|
Motion Master 6.0.0-alpha.86
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. | |
| std::string | productName () const |
| Canonical product name, independent of the SII EEPROM contents. | |
| 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 | isCia402 () const |
| Whether this device implements the CiA402 drive profile. | |
| bool | mailboxActive () const |
| Whether the device's CoE/SDO mailbox is currently active (AL state PRE-OP, SAFE-OP, or OP). | |
| bool | supportsCoe () const |
| Whether the slave advertises a CoE mailbox — a fixed capability, not a live check. | |
| 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 >, mm::comm::FoeError > | readFile (const std::string &filename) const |
| Reads a file from this device via File over EtherCAT (FoE). | |
| std::expected< void, mm::comm::FoeError > | 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< 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). | |
| bool | hasParameters () const |
| Whether the object dictionary is enumerated (thread-safe; no bus access). | |
| bool | parametersUnavailable () const |
Whether any initializeParameters call on this device failed since it was scanned (thread-safe; no bus access). | |
| std::vector< DeviceParameter > | parametersOrdered () const |
Returns all parameters sorted ascending by (index, subindex). | |
| std::optional< DeviceParameter > | parameter (uint16_t index, uint8_t subindex) const |
Looks up one parameter by (index, subindex) and returns a copy of it. O(1). | |
| std::optional< DeviceParameterValue > | parameterValue (uint16_t index, uint8_t subindex) const |
| Returns a copy of a parameter's last known value. 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< ObjectValues, std::string > | readObject (uint16_t index, bool useCompleteAccess=true) |
| Reads every readable sub-entry of one object and returns the decoded values. | |
| std::expected< void, std::string > | writeParameter (uint16_t index, uint8_t subindex, const 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. | |
| template<typename T > | |
| std::expected< T, std::string > | readValue (ObjectAddress< T > address) |
readValue<T>() addressed by an ObjectAddress. Blocking; off the RT thread only. | |
| template<typename T > | |
| std::expected< void, std::string > | writeValue (ObjectAddress< T > address, T newValue) |
writeValue<T>() addressed by an ObjectAddress. Blocking; off the RT thread only. | |
| template<typename T > | |
| std::expected< T, std::string > | readCachedValue (uint16_t index, uint8_t subindex) |
| Read-once typed accessor for objects the caller knows are immutable. | |
| DeviceParameter * | findParameter (uint16_t index, uint8_t subindex) |
Parameter lookup by (index, subindex). O(1); nullptr if absent. Takes no lock. | |
| const DeviceParameter * | findParameter (uint16_t index, uint8_t subindex) const |
Const overload of findParameter. Same contract. | |
| template<typename T > | |
| std::optional< T > | value (uint16_t index, uint8_t subindex) const |
Reads a scalar parameter's current value as T. Lock-free, non-allocating. | |
| template<typename T > | |
| bool | setValue (uint16_t index, uint8_t subindex, T newValue) |
Sets a scalar parameter's value as T. Lock-free, non-allocating. | |
| template<typename T > | |
| std::optional< T > | value (ObjectAddress< T > address) const |
value<T>() addressed by an ObjectAddress. Lock-free, non-allocating. | |
| template<typename T > | |
| bool | setValue (ObjectAddress< T > address, T newValue) |
setValue<T>() addressed by an ObjectAddress. Lock-free, non-allocating. | |
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.
The surface, grouped — every member is documented at its declaration below. This map exists because that documentation is long: it records the traps, which is what makes it worth reading, and it makes the file hard to scan. Start here, then read the one you need.
slavePosition, name, vendorId, productCode, revisionNumber, serialNumber, supportsCoe, mailboxActive initializeParameters, hasParameters, findParameter, parameter, parametersOrdered, parameterValue readParameter, writeParameter, readObjectComplete, readAllParameters value<T>, setValue<T>, exchangesProcessData readFlatPdoMapping, flatPdoMapping, readPdoMapping, writePdoMapping readFile, writeFile, readSii, writeSii readRegister, writeRegister Two rules the whole class rests on, and both are stated again where they apply: parametersMutex_ is never held across bus input or output, and value<T> / setValue<T> take no lock at all because a cyclic task calls them.
| 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 populates 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.
| DeviceParameter * mm::node::Device::findParameter | ( | uint16_t | index, |
| uint8_t | subindex | ||
| ) |
Parameter lookup by (index, subindex). O(1); nullptr if absent. Takes no lock.
The pointer form, for a caller that works with an entry rather than a copy of it — a cyclic task resolves its signals this way each cycle. parameter is the copying counterpart for a one-off read.
Lifetime. The returned pointer is invalidated by initializeParameters, which replaces the whole map, and by DeviceManager::scan / reset, which destroy the Device. Never carry one across cycles or across a release of parametersMutex_: re-resolve where you use it. A cyclic task does its lookups inside the cycle GameLoop entered, which is what keeps the device (and therefore its map) alive for the body of the cycle.
Locking. A control-plane caller must hold parametersMutex_ for the lookup and for any access to value / syncState, which the refresher and the control plane both write.
| const DeviceParameter * mm::node::Device::findParameter | ( | uint16_t | index, |
| uint8_t | subindex | ||
| ) | const |
Const overload of findParameter. Same contract.
| const FlatPdoMapping & mm::node::Device::flatPdoMapping | ( | ) | const |
Returns the device's PDO mapping. Empty until readFlatPdoMapping() succeeds.
| bool mm::node::Device::hasParameters | ( | ) | const |
Whether the object dictionary is enumerated (thread-safe; no bus access).
The map itself is deliberately not exposed: handing out a reference to it would let a caller traverse it while initializeParameters replaces it wholesale under parametersMutex_. Callers that want the contents use parametersOrdered (a snapshot) or parameter (one entry), both of which copy under the lock.
| 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::isCia402 | ( | ) | const |
Whether this device implements the CiA402 drive profile.
True when both the controlword (0x6040) and statusword (0x6041) are present in the enumerated parameter map — the same offline-safe discriminator createCia402Drive uses (no bus I/O; requires an earlier initializeParameters). Surfaced on the device JSON so clients can gate CiA402-only UI without duplicating the object indices.
| 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::deviceStates to refresh from the hardware.
| const std::string & mm::node::Device::name | ( | ) | const |
Human-readable node name from SII EEPROM.
For SOMANET drives this is the group name "SOMANET" (not product-specific) — use productName() for a name that distinguishes products.
| std::optional< DeviceParameter > mm::node::Device::parameter | ( | uint16_t | index, |
| uint8_t | subindex | ||
| ) | const |
Looks up one parameter by (index, subindex) and returns a copy of it. O(1).
A copy, taken under parametersMutex_, rather than a pointer into the map: initializeParameters replaces the whole map, so any pointer handed out here would be invalidated by a re-enumeration on another thread — and the readers that want an entry (the process-image and dump headers, a profile view's capability probe) run on their own worker threads. Copying an entry is a handful of scalars plus two short strings, and these are point lookups rather than sweeps; parametersOrdered covers the whole-map case in one lock.
| index | CoE object index. |
| subindex | CoE object subindex. |
nullopt if the parameter is unknown. | 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.
| bool mm::node::Device::parametersUnavailable | ( | ) | const |
Whether any initializeParameters call on this device failed since it was scanned (thread-safe; no bus access).
Set so the automatic read on reaching a mailbox-active state is attempted once rather than repeated on every later AL transition. A bus that cannot answer the enumeration does not answer it three times either, and paying for the discovery once is the difference between a slow bring-up and a very slow one. An explicit read through the API is unaffected — it calls initializeParameters directly and clears this on success.
Served on the device's JSON so a client can offer that retry, since a device latched here otherwise presents as one with no parameters and no way to tell why.
| std::optional< DeviceParameterValue > mm::node::Device::parameterValue | ( | uint16_t | index, |
| uint8_t | subindex | ||
| ) | const |
Returns a copy of a parameter's last known value. No bus access.
The control-plane read, and the name says which one it is. It serves every data type, including strings and byte arrays, and it takes parametersMutex_ — so the monitoring sampler may call it while the refresher and the control plane write. It returns what the last read or refresh stored; it never touches the bus itself.
The real-time counterpart is value<T>(), which takes no lock and serves scalars only. The two deliberately do not share a name: they differed by one template argument until 2026-08-19, which made "did this call lock?" invisible at the call site.
| index | CoE object index. |
| subindex | CoE object subindex. |
nullopt if the parameter is unknown. | uint32_t mm::node::Device::productCode | ( | ) | const |
Product code from EEPROM.
| std::string mm::node::Device::productName | ( | ) | const |
Canonical product name, independent of the SII EEPROM contents.
For a recognised SOMANET product (vendor kSynapticonVendorId and a known product code) this is the product-specific name — "SOMANET Node", "SOMANET Circulo", etc. — where name() only reports the generic "SOMANET" group name. Falls back to name() for any other vendor or an unrecognised product code.
| 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).
|
inline |
Read-once typed accessor for objects the caller knows are immutable.
Serves the cached value as soon as it is SyncState::Synced (populated by a prior read, a write, or initializeParameters' value pass) and only touches the bus on the first access of a never-read object, delegating that one read to readValue. Use this — not readValue — for object-dictionary entries that do not change while the Device exists (device type 0x1000, identity, manufacturer name/version, ...): readValue re-issues an SDO on every call when online, which is wasted work for a constant object. There is no reliable object-dictionary flag for "constant" (0x1000 and the error register 0x1001 share the same read-only, non-mappable access bits), so the caller asserts immutability by choosing this.
readValue, which re-reads live on every call — using readCachedValue would keep returning the first reading. This is a per-call method choice, not a state on the object: readValue stays live even after a prior readCachedValue marked the entry Synced.| T | The expected variant alternative. |
| index | CoE object index. |
| subindex | CoE object subindex. |
T, or an error string if the parameter is unknown, the (first) read fails, or the stored value is not a T. | std::expected< std::vector< uint8_t >, mm::comm::FoeError > 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. |
mm::comm::FoeError — the one structured error on this class, because FoE callers branch on the failure (retry a transient one, treat a missing file as nothing to do). It keeps a string face, so a caller that only forwards the reason uses .error().message and is otherwise unaffected. | 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< ObjectValues, std::string > mm::node::Device::readObject | ( | uint16_t | index, |
| bool | useCompleteAccess = true |
||
| ) |
Reads every readable sub-entry of one object and returns the decoded values.
The grouped analogue of readParameter, for the multi-subindex ARRAY/RECORD objects a profile view reads as one unit (software position limit, gear ratio, restore parameters, ...). When the object is Complete-Access-decodable, it is fetched with a single CoE Complete Access upload instead of one mailbox round-trip per subindex — support is probed on the first grouped read and remembered for the device's lifetime (the EEPROM capability hint is not consulted; see CompleteAccessSupport). Any CA fallthrough — unsupported slave, ineligible layout, single-subindex object, useCompleteAccess off — reads subindex-by-subindex via readParameter. Like readAllParameters, a CA upload is skipped while any sub-entry is served by the live process image, so the per-subindex path can read it from the image instead of the mailbox.
Unlike the best-effort bulk sweep, this is all-or-nothing: the first sub-entry that cannot be read fails the whole call.
| index | CoE object index. |
| useCompleteAccess | When true, attempt Complete Access for eligible objects. |
| 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 |
readValue<T>() addressed by an ObjectAddress. Blocking; off the RT thread only.
Unlike value(), this serves every type an object can have — including the strings and byte arrays a cycle cannot read — because it goes through the variant rather than the scalar cell.
|
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.
|
inline |
setValue<T>() addressed by an ObjectAddress. Lock-free, non-allocating.
|
inline |
Sets a scalar parameter's value as T. Lock-free, non-allocating.
**The cyclic-task write, and the mirror of value<T>().** It stores into the parameter's cell and returns; it never touches the wire itself. For an object in the output image that is all that is needed — the RT loop composes the wire image from these cells, so the value goes out on the next cycle — and the next value<T>() of the same object returns what was set, not the last frame's reading.
writeParameter off the RT thread to reach such an object over SDO.false means the parameter is unknown to this device, is not a scalar, or does not hold a T. Nothing is coerced: unlike writeParameter, which accepts any number and casts it into the declared width, T must already be the parameter's type — an RT path has no room for the variant machinery that coercion needs.
execute, for the reason value<T>() gives.| T | The arithmetic type the parameter's data type maps to. |
| index | CoE object index. |
| subindex | CoE object subindex. |
| newValue | Value to store. |
true if the cell was written. | 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 bytes-in counterpart of value: 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.
| bool mm::node::Device::supportsCoe | ( | ) | const |
Whether the slave advertises a CoE mailbox — a fixed capability, not a live check.
Derived from the driver's cached EEPROM capability bits (FieldbusDriver::mailboxProtocols), independent of AL state — unlike mailboxActive(), which asks whether the mailbox is reachable now. false means CoE is impossible on this slave (an EtherCAT coupler / simple I/O terminal), so its PDO mapping is read from the SII EEPROM rather than the 0x1C12 / 0x1C13 CoE objects. No bus I/O.
|
inline |
value<T>() addressed by an ObjectAddress. Lock-free, non-allocating.
The form generated dictionaries are written for: the type comes from the address, so it cannot disagree with the object it names.
|
inline |
Reads a scalar parameter's current value as T. Lock-free, non-allocating.
The cyclic-task read. One hash lookup and one relaxed atomic load — no lock, no allocation, no bus access, and nothing in the call that distinguishes a PDO-mapped object from one polled over SDO in the background. Whether a value is in the process image is a commissioning decision, and it must not change how the control program is written.
nullopt means the parameter is unknown to this device, is not a scalar (a string or byte array), or does not hold a T — never "the value happens to be zero", and never "the device
is offline". A parameter whose device stopped exchanging keeps reporting its last known value: swapping a real number for nothing is how a control loop ends up acting on a fallback it never asked for. Use exchangesProcessData() and parameter()'s syncState to decide otherwise.
Distinct from the untyped value(index, subindex), which takes parametersMutex_ and returns a variant: that one serves every type and is for the control plane, this one serves scalars without a lock and is for a cycle.
execute, or from a control-plane caller holding parametersMutex_. The lookup walks a map that initializeParameters replaces, and the Device belongs to a set that scan / reset retire; the cycle GameLoop entered is what holds both still for the body of a cycle.| T | The arithmetic type the parameter's data type maps to. |
| index | CoE object index. |
| subindex | CoE object subindex. |
nullopt (see above). | uint32_t mm::node::Device::vendorId | ( | ) | const |
Vendor ID from EEPROM.
| std::expected< void, mm::comm::FoeError > 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. |
mm::comm::FoeError (see readFile). | std::expected< void, std::string > mm::node::Device::writeParameter | ( | uint16_t | index, |
| uint8_t | subindex, | ||
| const 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 |
writeValue<T>() addressed by an ObjectAddress. Blocking; off the RT thread only.
|
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. |