|
Motion Master 6.0.0-alpha.50
Next-generation motion control software
|
Represents a single node on the fieldbus. More...
#include <device.h>
Public Member Functions | |
| Device (uint16_t slavePosition, mm::comm::FieldbusDriver &driver, ProcessData *processData=nullptr, const ParameterCache *parameterCache=nullptr) | |
Constructs a device, reading identity from the driver at slavePosition. | |
| uint16_t | slavePosition () const |
| Returns the 1-based position of this node on the fieldbus. | |
| const std::string & | name () const |
| Human-readable node name from SII EEPROM. | |
| uint32_t | vendorId () const |
| Vendor ID from EEPROM. | |
| uint32_t | productCode () const |
| Product code from EEPROM. | |
| uint32_t | revisionNumber () const |
| Revision number from EEPROM. | |
| uint32_t | serialNumber () const |
| Serial number from EEPROM. | |
| bool | mailboxActive () const |
| Whether the device's CoE/SDO mailbox is currently active (AL state PRE-OP, SAFE-OP, or OP). | |
| bool | exchangesProcessData () const |
| Whether the device is in a process-data-exchanging state (SAFE-OP or OP, error bit clear). | |
| std::expected< std::vector< uint8_t >, std::string > | readSdo (uint16_t index, uint8_t subindex) const |
| Reads an object dictionary entry from the device (CoE SDO upload). | |
| std::expected< void, std::string > | writeSdo (uint16_t index, uint8_t subindex, std::span< const uint8_t > data) const |
| Writes an object dictionary entry to the device (CoE SDO download). | |
| std::expected< std::vector< uint8_t >, std::string > | readFile (const std::string &filename) const |
| Reads a file from this device via File over EtherCAT (FoE). | |
| std::expected< void, std::string > | writeFile (const std::string &filename, std::span< const uint8_t > data) const |
| Writes a file to this device via File over EtherCAT (FoE). | |
| std::expected< void, std::string > | readRegister (uint16_t address, std::span< uint8_t > data) const |
| Reads bytes from an ESC register on this device. | |
| std::expected< void, std::string > | writeRegister (uint16_t address, std::span< const uint8_t > data) const |
| Writes bytes to an ESC register on this device. | |
| std::expected< std::vector< uint8_t >, std::string > | readSii () const |
| Reads this device's raw Slave Information Interface (SII / EEPROM) image. | |
| std::expected< void, std::string > | writeSii (std::span< const uint8_t > data) const |
| Writes a raw SII (EEPROM) image to this device. | |
| std::expected< void, std::string > | initializeParameters (bool readValues=false, bool useCompleteAccess=true) |
Enumerates the device's CoE object dictionary and populates parameters(). | |
| std::expected< void, std::string > | readFlatPdoMapping () |
| Reads the device's PDO mapping from its assignment and mapping objects. | |
| const FlatPdoMapping & | flatPdoMapping () const |
Returns the device's PDO mapping. Empty until readFlatPdoMapping() succeeds. | |
| std::expected< PdoMapping, std::string > | readPdoMapping () |
Reads the device's PDO mapping grouped by mapping object (0x16xx / 0x1Axx). | |
| std::expected< void, std::string > | writePdoMapping (const PdoMapping &mapping) |
| Writes a new PDO mapping to the device via SDO, then reads it back to verify. | |
| std::expected< void, std::string > | setValue (uint16_t index, uint8_t subindex, DeviceParameterValue newValue) |
| Stores a parameter's value locally, without any bus access (the typed setter). | |
| std::expected< DeviceParameterValue, std::string > | setValueFromBytes (uint16_t index, uint8_t subindex, std::span< const uint8_t > bytes) |
| Sets the parameter's value from its raw on-the-wire bytes (the bytes-domain setter). | |
| std::expected< std::vector< uint8_t >, std::string > | valueAsBytes (uint16_t index, uint8_t subindex) const |
| Returns the parameter's value as its raw on-the-wire bytes (the bytes-domain getter). | |
| const std::unordered_map< uint32_t, DeviceParameter > & | parameters () const |
Returns the parameter map, keyed by makeParameterKey(index, subindex). Empty until initializeParameters() is called. | |
| std::vector< DeviceParameter > | parametersOrdered () const |
Returns all parameters sorted ascending by (index, subindex). | |
| const DeviceParameter * | parameter (uint16_t index, uint8_t subindex) const |
Looks up a parameter by (index, subindex). O(1). | |
| std::optional< DeviceParameterValue > | value (uint16_t index, uint8_t subindex) const |
| Returns a copy of a parameter's cached value (the typed cache getter), no bus access. | |
| std::optional< DeviceParameter > | parameterCopy (uint16_t index, uint8_t subindex) const |
| Returns a copy of a full parameter struct (value + metadata), no bus access. | |
| std::optional< uint16_t > | dataType (uint16_t index, uint8_t subindex) const |
| Returns a parameter's declared ETG.1020 data-type code, thread-safely (cache lock). | |
| std::expected< DeviceParameterValue, std::string > | readParameter (uint16_t index, uint8_t subindex) |
| Reads a parameter value, keeping the cached store in sync. | |
| std::expected< void, std::string > | readAllParameters (bool useCompleteAccess=true) |
| Refreshes the cached value of every readable parameter, keeping the list intact. | |
| std::expected< void, std::string > | writeParameter (uint16_t index, uint8_t subindex, DeviceParameterValue newValue) |
| Writes a parameter value, always updating the cache first. | |
| template<typename T > | |
| std::expected< void, std::string > | writeValue (uint16_t index, uint8_t subindex, T newValue) |
Typed convenience wrapper for writeParameter. | |
| template<typename T > | |
| std::expected< T, std::string > | readValue (uint16_t index, uint8_t subindex) |
Typed convenience wrapper for readParameter. | |
Represents a single node on the fieldbus.
Holds the node's bus position, immutable identity read from EEPROM, and a reference to the fieldbus driver for SDO and state operations.
| mm::node::Device::Device | ( | uint16_t | slavePosition, |
| mm::comm::FieldbusDriver & | driver, | ||
| ProcessData * | processData = nullptr, |
||
| const ParameterCache * | parameterCache = nullptr |
||
| ) |
Constructs a device, reading identity from the driver at slavePosition.
| slavePosition | 1-based position on the fieldbus (0 is reserved for the master). |
| driver | Fieldbus driver; lifetime must exceed that of this object. |
| processData | Live process-data runtime, or nullptr for SDO-only operation. When supplied (by DeviceManager), readParameter / writeParameter prefer the live PDO image while the device is exchanging and fall back to SDO otherwise. Lifetime must exceed that of this object. |
| parameterCache | On-disk cache of object-dictionary definitions, or nullptr to always enumerate live. When supplied (by DeviceManager), initializeParameters loads definitions from it on a hit and populates it on a miss. Lifetime must exceed that of this object. |
| std::optional< uint16_t > mm::node::Device::dataType | ( | uint16_t | index, |
| uint8_t | subindex | ||
| ) | const |
Returns a parameter's declared ETG.1020 data-type code, thread-safely (cache lock).
The data type is immutable once initializeParameters has populated the entry, but the map itself can be rebuilt off the caller's thread, so reading it under the lock is the safe way for an off-thread consumer (e.g. capturing a PDO decode spec) to obtain it.
| index | CoE object index. |
| subindex | CoE object subindex. |
nullopt if the parameter is unknown. | bool mm::node::Device::exchangesProcessData | ( | ) | const |
Whether the device is in a process-data-exchanging state (SAFE-OP or OP, error bit clear).
Derived live from the driver's cached AL status. When false (INIT / PRE-OP / BOOT) the device does not participate in the LRW cycle, so its region of the process image is stale — PDO-mapped parameter access must use SDO, not the shared buffers. Lets a partially-operational bus route each device correctly.
| const FlatPdoMapping & mm::node::Device::flatPdoMapping | ( | ) | const |
Returns the device's PDO mapping. Empty until readFlatPdoMapping() succeeds.
| std::expected< void, std::string > mm::node::Device::initializeParameters | ( | bool | readValues = false, |
| bool | useCompleteAccess = true |
||
| ) |
Enumerates the device's CoE object dictionary and populates parameters().
Requires the device to be in PRE-OP, SAFE-OP, or OP (mailbox communication active). One DeviceParameter is created per (index, subindex) pair returned by the SDO Info service, with value pre-initialised to a type-appropriate zero. When readValues is true each entry is additionally read and the decoded value stored on the parameter; entries that fail to read keep their default value and the call still succeeds (per-entry errors are logged).
When useCompleteAccess is true, multi-subindex objects (ARRAY/RECORD) are read with a single CoE Complete Access upload instead of one upload per subindex — far fewer mailbox round-trips. Support is probed once: if the slave rejects the first CA read, the whole pass falls back to per-subindex reads. It has no effect unless readValues is true.
Calling this method again replaces the existing parameter map.
| readValues | When true, follow up each entry with an SDO upload. |
| useCompleteAccess | When true, use CoE Complete Access for multi-subindex objects. |
| bool mm::node::Device::mailboxActive | ( | ) | const |
Whether the device's CoE/SDO mailbox is currently active (AL state PRE-OP, SAFE-OP, or OP).
Mailbox communication is available in PRE-OP and above per the EtherCAT state machine, independent of the AL error indicator — a device in SAFE-OP+error still answers mailbox requests. INIT has no mailbox and BOOT's is FoE-only, so both report false. Derived live from the fieldbus driver's cached AL status (FieldbusDriver::slaveState) — no copy is stored here. When false, readParameter / writeParameter operate on the cached value only and never touch the bus. Reflects the last state the driver read; call DeviceManager::getDeviceStates to refresh from the hardware.
| const std::string & mm::node::Device::name | ( | ) | const |
Human-readable node name from SII EEPROM.
| const DeviceParameter * mm::node::Device::parameter | ( | uint16_t | index, |
| uint8_t | subindex | ||
| ) | const |
Looks up a parameter by (index, subindex). O(1).
nullptr if no such entry exists.value / dataType getters below, which return copies under the lock. | std::optional< DeviceParameter > mm::node::Device::parameterCopy | ( | uint16_t | index, |
| uint8_t | subindex | ||
| ) | const |
Returns a copy of a full parameter struct (value + metadata), no bus access.
The struct-level analogue of value: a deep copy taken under the cache lock, so it is safe to call off the control-plane thread (unlike parameter, which hands out a raw pointer). Reflects the last value stored by a read/refresh; it does not itself touch the bus.
| index | CoE object index. |
| subindex | CoE object subindex. |
nullopt if the parameter is unknown. | const std::unordered_map< uint32_t, DeviceParameter > & mm::node::Device::parameters | ( | ) | const |
Returns the parameter map, keyed by makeParameterKey(index, subindex). Empty until initializeParameters() is called.
| std::vector< DeviceParameter > mm::node::Device::parametersOrdered | ( | ) | const |
Returns all parameters sorted ascending by (index, subindex).
Copies the map into a vector and sorts on the packed key. O(N log N) — call when you need stable iteration order (e.g. JSON serialisation, UI listings) rather than O(1) lookup.
| uint32_t mm::node::Device::productCode | ( | ) | const |
Product code from EEPROM.
| std::expected< void, std::string > mm::node::Device::readAllParameters | ( | bool | useCompleteAccess = true | ) |
Refreshes the cached value of every readable parameter, keeping the list intact.
Re-reads each entry already in the map (it does not re-enumerate the object dictionary — use initializeParameters for that). PDO-mapped objects are read from the live process image when exchanging; everything else is read over the mailbox — as one CoE Complete Access upload per multi-subindex ARRAY/RECORD when useCompleteAccess is true (probed once, with per-object fallback), or one SDO upload per subindex otherwise. The objects are snapshotted under the lock first, then read with the lock released between objects (each transfer holds it for one round-trip), so a concurrent cached read of this device never waits for the whole — potentially multi-second — sweep.
Write-only objects are skipped (an SDO upload of one would abort). Best-effort, like initializeParameters(readValues=true): an entry that fails to read keeps its cached value and is logged, and the call still succeeds so one bad object never blocks the rest.
| useCompleteAccess | When true, use CoE Complete Access for multi-subindex objects read over the mailbox. |
initializeParameters first). | std::expected< std::vector< uint8_t >, std::string > mm::node::Device::readFile | ( | const std::string & | filename | ) | const |
Reads a file from this device via File over EtherCAT (FoE).
| filename | FoE filename as recognised by the slave firmware. |
| std::expected< void, std::string > mm::node::Device::readFlatPdoMapping | ( | ) |
Reads the device's PDO mapping from its assignment and mapping objects.
Walks the PDO assignment objects (0x1C12 for outputs/RxPDO, 0x1C13 for inputs/TxPDO) and the mapping objects they reference (0x16xx / 0x1Axx) via SDO upload, producing one PdoMappingEntry per mapped object with its bit offset and width within the slave's process-data window. Requires the device to be in PRE-OP, SAFE-OP, or OP (mailbox communication active).
A direction whose assignment object is absent or assigns nothing yields an empty list for that direction (the device simply has no PDOs in it) and is not an error. Calling this again replaces the existing mapping.
| std::expected< DeviceParameterValue, std::string > mm::node::Device::readParameter | ( | uint16_t | index, |
| uint8_t | subindex | ||
| ) |
Reads a parameter value, keeping the cached store in sync.
Routing, in order:
SyncState::Synced) and returned — no bus I/O.mailboxActive(), uploads via SDO, decodes, stores and returns it.The parameter must already exist in the map (populated by initializeParameters).
| index | CoE object index. |
| subindex | CoE object subindex. |
| std::expected< PdoMapping, std::string > mm::node::Device::readPdoMapping | ( | ) |
Reads the device's PDO mapping grouped by mapping object (0x16xx / 0x1Axx).
The grouped counterpart of readFlatPdoMapping: instead of one flat list per direction, each mapping object keeps its pdoIndex and its own entries (with derived bitOffset), so the result round-trips into writePdoMapping. Reads fresh over SDO; does not touch the cached flatPdoMapping(). Requires the device to be in PRE-OP, SAFE-OP, or OP (mailbox active).
| std::expected< void, std::string > mm::node::Device::readRegister | ( | uint16_t | address, |
| std::span< uint8_t > | data | ||
| ) | const |
Reads bytes from an ESC register on this device.
Delegates to the fieldbus driver's readRegister using this device's slave position.
| address | ESC register address (e.g. 0x0130 for DL Status). |
| data | Output buffer; its size determines how many bytes are read. |
| std::expected< std::vector< uint8_t >, std::string > mm::node::Device::readSdo | ( | uint16_t | index, |
| uint8_t | subindex | ||
| ) | const |
Reads an object dictionary entry from the device (CoE SDO upload).
Raw SDO read with no cache or PDO awareness — for the typed, PDO-aware path use readParameter instead.
| index | CoE object index. |
| subindex | CoE object subindex. |
| std::expected< std::vector< uint8_t >, std::string > mm::node::Device::readSii | ( | ) | const |
Reads this device's raw Slave Information Interface (SII / EEPROM) image.
Delegates to the fieldbus driver's readSii using this device's slave position. Decode the returned bytes with mm::comm::parseSii. EEPROM access is most reliable while the device is in INIT or PRE-OP.
|
inline |
Typed convenience wrapper for readParameter.
Refreshes the value from the device when online (otherwise serves the cache) and returns it as T — e.g. device.readValue<int32_t>(0x6064, 0). The request is type-exact; use parameter(index, subindex)->numeric() when you only need a number.
| T | The expected variant alternative. |
| index | CoE object index. |
| subindex | CoE object subindex. |
T, or an error string if the parameter is unknown, the read fails, or the stored value is not a T. | uint32_t mm::node::Device::revisionNumber | ( | ) | const |
Revision number from EEPROM.
| uint32_t mm::node::Device::serialNumber | ( | ) | const |
Serial number from EEPROM.
| std::expected< void, std::string > mm::node::Device::setValue | ( | uint16_t | index, |
| uint8_t | subindex, | ||
| DeviceParameterValue | newValue | ||
| ) |
Stores a parameter's value locally, without any bus access (the typed setter).
Coerces value into the parameter's declared type, stores it, and marks it SyncState::Synced. Used to reflect a value obtained out-of-band — e.g. one decoded from the process image by DeviceManager — so DeviceParameter stays the source of truth. The parameter must already exist. Unlike writeParameter this never touches the wire; it is the typed counterpart of setValueFromBytes.
| index | CoE object index. |
| subindex | CoE object subindex. |
| value | Value to store; coerced to the parameter's declared type. |
value cannot be coerced to its type. | std::expected< DeviceParameterValue, std::string > mm::node::Device::setValueFromBytes | ( | uint16_t | index, |
| uint8_t | subindex, | ||
| std::span< const uint8_t > | bytes | ||
| ) |
Sets the parameter's value from its raw on-the-wire bytes (the bytes-domain setter).
The byte-input counterpart of setValue: decodes bytes with the parameter's declared data type, stores the result (marking it SyncState::Synced), and returns the decoded value — all under parametersMutex_ so the data-type lookup, decode, and store are one atomic step. bytes are the LSB-aligned little-endian encoding of the object's value; the source is irrelevant (a slice of the process image, an SDO upload, a test fixture).
| index | CoE object index. |
| subindex | CoE object subindex. |
| bytes | Raw LSB-aligned little-endian value bytes. |
| uint16_t mm::node::Device::slavePosition | ( | ) | const |
Returns the 1-based position of this node on the fieldbus.
| std::optional< DeviceParameterValue > mm::node::Device::value | ( | uint16_t | index, |
| uint8_t | subindex | ||
| ) | const |
Returns a copy of a parameter's cached value (the typed cache getter), no bus access.
The read counterpart of setValue, and thread-safe (taken under the cache lock) so the monitoring sampler can read it concurrently with refresher/control-plane writes. Returns the last value stored by a read/refresh; it does not itself touch the bus.
| index | CoE object index. |
| subindex | CoE object subindex. |
nullopt if the parameter is unknown. | std::expected< std::vector< uint8_t >, std::string > mm::node::Device::valueAsBytes | ( | uint16_t | index, |
| uint8_t | subindex | ||
| ) | const |
Returns the parameter's value as its raw on-the-wire bytes (the bytes-domain getter).
Reads the stored value and its declared data type under parametersMutex_ and encodes them, so a caller can stage the current setpoint into the output image without reaching into the parameter map itself. The encode counterpart of setValueFromBytes.
| index | CoE object index. |
| subindex | CoE object subindex. |
| uint32_t mm::node::Device::vendorId | ( | ) | const |
Vendor ID from EEPROM.
| std::expected< void, std::string > mm::node::Device::writeFile | ( | const std::string & | filename, |
| std::span< const uint8_t > | data | ||
| ) | const |
Writes a file to this device via File over EtherCAT (FoE).
| filename | FoE filename as recognised by the slave firmware. |
| data | File bytes to write. |
| std::expected< void, std::string > mm::node::Device::writeParameter | ( | uint16_t | index, |
| uint8_t | subindex, | ||
| DeviceParameterValue | newValue | ||
| ) |
Writes a parameter value, always updating the cache first.
value is coerced into the parameter's declared data type and stored in the cache (the cache is the source of truth). Then, in order:
SyncState::Synced, and the call returns — no SDO download.SyncState::Synced; on download failure it is marked SyncState::Pending and the error is returned.SyncState::Pending and the call succeeds — the edit lives in the cache until the device comes back online and it is re-written.| index | CoE object index. |
| subindex | CoE object subindex. |
| value | Value to set; coerced to the parameter's type. |
value cannot be coerced, or an online download fails. | std::expected< void, std::string > mm::node::Device::writePdoMapping | ( | const PdoMapping & | mapping | ) |
Writes a new PDO mapping to the device via SDO, then reads it back to verify.
Reconfigures both directions' sync-manager PDO assignment (0x1C12 outputs / 0x1C13 inputs) and the mapping objects (0x16xx / 0x1Axx) they reference, following the CoE ordering rule (ETG.1000.6 §5.6.7.4.9): a sync manager's PDO assignment is cleared to zero (which makes its mapping objects writable), each mapping object's entry count is cleared, its entries are written as packed uint32 words (index<<16 | subindex<<8 | bitLength), the entry count is restored, and finally the assignment lists the mapping objects and its own count is written. A mapping object present in the previous configuration but absent from mapping is simply left unassigned — its contents are irrelevant once it is off the sync manager, so it needs no explicit clear.
Requires the device to be in PRE-OP. The mapping and assignment objects are writable only in PRE-OP — INIT/BOOT have no CoE mailbox, and in SAFE-OP/OP the sync managers are active and the slave aborts the write. This is the "drop to PRE-OP, remap, climb back" flow: the caller takes the device to PRE-OP, calls this, then transitions it back to SAFE-OP/OP, at which point DeviceManager re-reads the mapping and rebuilds the whole-bus process image.
After writing, the mapping is read back (via readFlatPdoMapping, which also refreshes flatPdoMapping()) and compared against mapping; a mismatch, or a transient SDO failure mid-sequence, is retried up to a small fixed number of whole-mapping attempts before failing, because a single dropped mailbox frame would otherwise leave the object dictionary half-configured. The apply is idempotent, so a retry is safe.
| mapping | The desired output (RxPDO) and input (TxPDO) mapping objects, in assignment order. An empty direction clears that sync manager's assignment. |
mapping), or an error string if the device is not in PRE-OP, an entry is malformed (bit length or count out of range), an SDO write/read-back fails after all retries, or the read-back does not match. | std::expected< void, std::string > mm::node::Device::writeRegister | ( | uint16_t | address, |
| std::span< const uint8_t > | data | ||
| ) | const |
Writes bytes to an ESC register on this device.
Delegates to the fieldbus driver's writeRegister using this device's slave position.
| address | ESC register address. |
| data | Bytes to write. |
| std::expected< void, std::string > mm::node::Device::writeSdo | ( | uint16_t | index, |
| uint8_t | subindex, | ||
| std::span< const uint8_t > | data | ||
| ) | const |
Writes an object dictionary entry to the device (CoE SDO download).
Raw SDO write with no cache or PDO awareness — for the typed, PDO-aware path use writeParameter instead. Requires the device to be in PRE-OP, SAFE-OP, or OP (mailbox communication active).
| index | CoE object index. |
| subindex | CoE object subindex. |
| data | Bytes to write; size must match the object's length. |
| std::expected< void, std::string > mm::node::Device::writeSii | ( | std::span< const uint8_t > | data | ) | const |
Writes a raw SII (EEPROM) image to this device.
Delegates to the fieldbus driver's writeSii using this device's slave position. Destructive: a malformed image can leave the device unidentifiable until re-flashed. The device adopts the new contents only after a power cycle. Most reliable while the device is in INIT or PRE-OP.
| data | Raw SII image to write (even length). |
|
inline |
Typed convenience wrapper for writeParameter.
Lets callers pass a bare value without constructing a DeviceParameterValue — e.g. device.writeValue(0x2030, 1, 123). newValue is coerced into the parameter's declared type, so the literal's own type need not match the object's width. Online / offline behaviour is exactly that of writeParameter.
| T | Any type a DeviceParameterValue can hold (integers, floats, std::string, std::vector<uint8_t>). |
| index | CoE object index. |
| subindex | CoE object subindex. |
| newValue | Value to write; coerced to the parameter's type. |