Device
Device Definition
A pyrogue.Device is the primary composition unit in a PyRogue tree.
Devices can contain:
child devices
variables (local, remote, link)
commands (local, remote)
Most user-facing hardware abstractions are implemented as Device subclasses.
At runtime, a device also participates in memory routing behavior through the
Rogue memory hub stack.
import pyrogue as pr
class MyDevice(pr.Device):
def __init__(self, **kwargs):
super().__init__(description='Example device', **kwargs)
self.add(pr.LocalVariable(name='Mode', mode='RW', value=0))
self.add(pr.LocalCommand(name='Reset', function=self._reset))
def _reset(self):
pass
Key Attributes
In addition to inherited Node attributes, common device-level attributes include:
offset/addressmemBaseenable
The enable variable allows tree-level logic to disable a full device subtree
for hardware access while keeping node metadata available.
Relationship to Hub
Conceptually, a device behaves as a hub in the memory routing stack:
variable/block transactions are addressed relative to the device base
child devices can inherit base/memory routing from parent devices
a child device can also be attached to an independent memory path when needed
Key Methods
Commonly used methods:
Managed Interfaces: addInterface() and addProtocol()
Devices can own stream/memory/protocol helper objects that need coordinated startup/shutdown with the device tree.
Use pyrogue.Device.addInterface() (or alias
pyrogue.Device.addProtocol()) to register:
Rogue memory or stream interface objects
protocol servers/clients
any custom object that implements
_start()and/or_stop()
At runtime:
pyrogue.Device._start()calls_start()on each managed object if the method existspyrogue.Device._stop()calls_stop()on each managed object if the method existsboth then recurse to child devices
This is why top-level interfaces are commonly added at root scope using
root.addInterface(...) (Root is a Device subclass).
Lifecycle Override Points for Subclasses
The following methods are intended override points when you need custom behavior around startup/shutdown or transaction sequencing.
Start/Stop hooks
pyrogue.Device._start(): Called recursively frompyrogue.Root.start(). Typical use: open sockets/threads/resources, then callsuper()._start().pyrogue.Device._stop(): Called recursively frompyrogue.Root.stop(). Typical use: stop custom resources and callsuper()._stop()to preserve managed interface and child-device shutdown.pyrogue.Device._rootAttached(): Called during root startup before_finishInitand before runtime threads. Typical use: finalize path/root-dependent setup after callingsuper()._rootAttached(parent, root).
Operational hooks
These are commonly overridden to implement device-specific control behavior.
Read/write sequencing hooks
Override these when the default transaction ordering needs pre/post side effects or custom sequencing.
Device Read/Write Operations
Bulk config/read/write operations traverse the tree and issue block transactions through device block APIs.
Typical write flow:
update variable shadow value
enqueue write transaction
optionally enqueue verify transaction
check completion and publish updates
Typical read flow:
enqueue read transaction
check completion
return/publish updated values
Implementation Boundary (Python and C++)
From a Python API perspective, you use pyrogue.Device methods such as
readBlocks and writeBlocks.
Under the hood, pyrogue.Device is built on the Rogue memory interface
hub/master/slave stack. In particular, a device participates in memory routing
as a Hub, and forwards block transactions toward downstream memory slaves.
Where Hub fits in transaction flow
The C++ Hub implementation (src/rogue/interfaces/memory/Hub.cpp):
applies local address offset when forwarding transactions downstream
can split large transactions into sub-transactions based on downstream max-access limits
allows custom transaction translation by overriding
_doTransactionin Python/C++ subclasses
Conceptual transaction path:
variable operation -> block transaction -> device/hub routing -> downstream slave access -> completion/check -> variable update notify
For deeper memory-stack behavior, see:
Custom Read/Write Operations
If a device needs sequencing around default block operations, override:
class SequencedDevice(pyrogue.Device):
def writeBlocks(self, *, force=False, recurse=True, variable=None, checkEach=False, index=-1):
# Pre-transaction behavior
super().writeBlocks(
force=force,
recurse=recurse,
variable=variable,
checkEach=checkEach,
index=index,
)
# Post-transaction behavior
Device Command Decorators
Device supports decorators that create pyrogue.LocalCommand nodes.
You can use decorators on local functions created in __init__.
@pyrogue.command(name='ReadConfig', value='', description='Load config file')
def _readConfig(self, arg):
self.root.loadYaml(name=arg, writeEach=False, modes=['RW', 'WO'])
Special Device Subclasses
There are several specialized device classes available:
Device Class Documentation
See Device for generated API details.