Epics PV Server (V7 Protocol)
EpicsPvServer is the EPICS V7 server-side bridge for exposing PyRogue tree
variables as EPICS records using softIocPVA (pythonSoftIOC).
What It Does
EpicsPvServer builds and runs a softIocPVA instance in a background thread,
creating real EPICS records for each served PyRogue Variable or Command.
Server Behavior
EpicsPvServer:
Requires a running
Rootbefore startup.Builds PV mappings either automatically (
base:path) or frompvMap.Supports include and exclude group filtering (default excludes
NoServe).Creates one
EpicsPvHolderper served variable and exposeslist()anddump()helpers for mapping inspection.Starts softIocPVA once via
iocInit()— the IOC is a process-wide singleton; multipleEpicsPvServerinstances share the same running IOC.Serves both CA and PVA protocols; any standard EPICS client can access the published records.
Constructor and Mapping Overview
EpicsPvServer(base=..., root=..., incGroups=..., excGroups=..., pvMap=...)
uses two mapping modes:
Automatic mode (
pvMap=None): serves variables that pass group filters using<base>:<path.with.colons>naming.Explicit mode (
pvMapdict): serves only mapped variable paths with user-defined PV names.
Default exclusion is ['NoServe'] when excGroups is not provided.
Note
Passing root=self to the constructor automatically registers the server
with the Root lifecycle via addProtocol. Do not call
self.addProtocol(self.epics) afterwards — doing so registers the server
twice and raises Exception: epicsV7: Duplicate _start() call at startup.
This differs from the EPICS V4 integration, where addProtocol must be
called explicitly. If you are migrating from V4, remove the
self.addProtocol(...) call.
Setup Example
import pyrogue as pr
import pyrogue.protocols.epicsV7 as pep7
class MyRoot(pr.Root):
def __init__(self):
super().__init__(name='MyRoot')
# Add variables/devices here as usual.
# Passing root=self auto-registers with the Root lifecycle.
# Do NOT also call self.addProtocol(self.epics).
self.epics = pep7.EpicsPvServer(
base='MyIoc',
root=self,
incGroups=None,
excGroups=['NoServe'],
pvMap=None,
)
with MyRoot() as root:
# Inspect active mapping.
root.epics.dump()
# Optionally write mapping to file for IOC/client integration.
root.epics.dump('epics_map.txt')
Typical Usage Pattern
The common setup follows this pattern:
Create and start a
Rootwith Local and Remote variables.Construct
EpicsPvServer(base=..., root=..., pvMap=...)— this automatically registers the server with the Root lifecycle.Do not call
root.addProtocol(...)— it is handled internally.Use any EPICS client (CA or PVA) to put and get values.
Invoke PyRogue Commands using a plain put to the command’s PV.
PV Name Length Handling
EPICS CA enforces a 60-character limit on PV names (PVNAME_STRINGSZ = 61
in EPICS Base). When the full name base:path would exceed 60 characters,
EpicsPvServer automatically shortens the softioc record name to a
deterministic hash of the form tail_XXXXXXXXXX (10 lowercase hex digits
derived from SHA-1 of the full name). CA clients use this hashed short name;
PVA clients can use the full human-readable name via a PVA alias — they never
need to know about the hash.
Names at or below 60 characters are published unchanged. Existing deployments are completely unaffected.
Two distinct variable paths that hash to the same short name cause
_start()to raiseRuntimeErrorimmediately before any record is created.list()always returns full long names regardless of whether a PV was hashed.dump()annotates each hashed PV with its CA short name in the form(CA: base:tail_XXXXXXXXXX).
Example output from dump() for a hashed PV:
MyIoc:LocalRoot:MyDevice:ThisIsAVeryLongVariableNameThatExceedsSixtyCharacterLimit (CA: MyIoc:tail_3f9a1b2c4d)
PVA Transparency for Long Names
For every PV whose CA record name was hashed, EpicsPvServer additionally
registers the full long name as a PVA-only channel backed by a
p4p.server.SharedPV. This means:
PVA clients connect using the full, human-readable name.
Reads on the long PVA name return the current PyRogue variable value.
Writes on the long PVA name update the same PyRogue variable as a CA write via the short name.
A CA write via the short name is immediately visible to PVA clients on the long name, and vice versa — with no feedback loops.
from p4p.client.thread import Context
ctxt = Context('pva')
# PVA clients always use the full name — no knowledge of the hash required.
full_name = 'MyIoc:LocalRoot:MyDevice:ThisIsAVeryLongVariableNameThatExceedsSixtyCharacterLimit'
value = ctxt.get(full_name)
ctxt.put(full_name, 42)
Live Hardware Values
softioc does not expose a Python callback that fires when a CA or PVA client
issues a caget / ctxt.get(). Both EPICS V4 and V7 integrations serve
the most recently pushed value from the EPICS record buffer. To ensure clients
receive up-to-date hardware values, the Rogue server process must execute
hardware reads itself — either via PyRogue auto-polling (pollInterval on
variables or pollEn=True on the Root) or by calling device-level read
commands explicitly.
Commands via caput
PyRogue Commands (LocalCommand and RemoteCommand) are exposed as
longOut EPICS records. Clients invoke them with a plain put:
Invoking a No-Arg Command
from p4p.client.thread import Context
ctxt = Context('pva')
pv_name = 'MyIoc:MyRoot:MyDevice:ResetCounter'
ctxt.put(pv_name, 0) # value=0 triggers no-arg command call
Invoking a Command With an Argument
from p4p.client.thread import Context
ctxt = Context('pva')
pv_name = 'MyIoc:MyRoot:MyDevice:SetThreshold'
ctxt.put(pv_name, 42) # value is forwarded to command as arg
The put value is forwarded directly to the PyRogue command as its argument.
This is different from the V4 integration, which uses ctxt.rpc() with
an NTURI wrapper.
Migration from EPICS V4
The EpicsPvServer constructor signature is identical in V4 and V7.
The key differences when migrating:
Replace
import pyrogue.protocols.epicsV4 as pepwithimport pyrogue.protocols.epicsV7 as pep7and update the class reference.Replace
ctxt.rpc()command invocations withctxt.put(pv_name, value).Install
softioc(pip install softioc) instead of or alongsidep4p. If you usep4pas a PVA client (forctxt.get()/ctxt.put()), keep it — only the server-side dependency changes.Remove any
self.addProtocol(self.epics)call. In V4 this was required; in V7 the constructor callsaddProtocolautomatically whenroot=selfis passed. Keeping the explicit call registers the server twice and raisesException: epicsV7: Duplicate _start() callat startup.
When To Use It
Your deployment requires CA compatibility alongside PVA.
You want to avoid the p4p SharedPV server in favor of standard softIocPVA.
Logging
EpicsPvServer uses Python logging.
Logger name:
pyrogue.EpicsPvServerConfiguration API:
logging.getLogger('pyrogue.EpicsPvServer').setLevel(logging.DEBUG)
What To Explore Next
Per-variable EPICS record behavior: Epics PV Holder (V7 Protocol)
API Reference
Generated API page: EpicsPvServer