Motion Master 6.0.0-alpha.86
Next-generation motion control software
Loading...
Searching...
No Matches
mm::node::Device Class Reference

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::FoeErrorreadFile (const std::string &filename) const
 Reads a file from this device via File over EtherCAT (FoE).
 
std::expected< void, mm::comm::FoeErrorwriteFile (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 FlatPdoMappingflatPdoMapping () 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< DeviceParameterparametersOrdered () const
 Returns all parameters sorted ascending by (index, subindex).
 
std::optional< DeviceParameterparameter (uint16_t index, uint8_t subindex) const
 Looks up one parameter by (index, subindex) and returns a copy of it. O(1).
 
std::optional< DeviceParameterValueparameterValue (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.
 
DeviceParameterfindParameter (uint16_t index, uint8_t subindex)
 Parameter lookup by (index, subindex). O(1); nullptr if absent. Takes no lock.
 
const DeviceParameterfindParameter (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.
 

Detailed Description

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.

  • identity, no bus access: slavePosition, name, vendorId, productCode, revisionNumber, serialNumber, supportsCoe, mailboxActive
  • the parameter index: initializeParameters, hasParameters, findParameter, parameter, parametersOrdered, parameterValue
  • one parameter over the bus: readParameter, writeParameter, readObjectComplete, readAllParameters
  • inside a cycle, no lock: value<T>, setValue<T>, exchangesProcessData
  • process data: readFlatPdoMapping, flatPdoMapping, readPdoMapping, writePdoMapping
  • files and EEPROM: readFile, writeFile, readSii, writeSii
  • ESC registers: 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.

Constructor & Destructor Documentation

◆ Device()

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.

Parameters
slavePosition1-based position on the fieldbus (0 is reserved for the master).
driverFieldbus driver; lifetime must exceed that of this object.
processDataLive 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.
parameterCacheOn-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.

Member Function Documentation

◆ dataType()

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.

Parameters
indexCoE object index.
subindexCoE object subindex.
Returns
The data-type code, or nullopt if the parameter is unknown.

◆ exchangesProcessData()

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.

◆ findParameter() [1/2]

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.

◆ findParameter() [2/2]

const DeviceParameter * mm::node::Device::findParameter ( uint16_t  index,
uint8_t  subindex 
) const

Const overload of findParameter. Same contract.

◆ flatPdoMapping()

const FlatPdoMapping & mm::node::Device::flatPdoMapping ( ) const

Returns the device's PDO mapping. Empty until readFlatPdoMapping() succeeds.

◆ hasParameters()

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.

◆ initializeParameters()

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.

Parameters
readValuesWhen true, follow up each entry with an SDO upload.
useCompleteAccessWhen true, use CoE Complete Access for multi-subindex objects.
Returns
Void on success, or an error string if the object dictionary enumeration itself fails (the slave does not support SDO Info, or all retries timed out).

◆ isCia402()

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.

◆ mailboxActive()

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.

◆ name()

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.

◆ parameter()

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.

Parameters
indexCoE object index.
subindexCoE object subindex.
Returns
A copy of the parameter, or nullopt if the parameter is unknown.

◆ parametersOrdered()

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.

◆ parametersUnavailable()

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.

◆ parameterValue()

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.

Parameters
indexCoE object index.
subindexCoE object subindex.
Returns
The cached value, or nullopt if the parameter is unknown.

◆ productCode()

uint32_t mm::node::Device::productCode ( ) const

Product code from EEPROM.

◆ productName()

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.

◆ readAllParameters()

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.

Parameters
useCompleteAccessWhen true, use CoE Complete Access for multi-subindex objects read over the mailbox.
Returns
Void on success (the always-taken best-effort path), or an error string if the device has no parameters loaded yet (call initializeParameters first).

◆ readCachedValue()

template<typename T >
std::expected< T, std::string > mm::node::Device::readCachedValue ( uint16_t  index,
uint8_t  subindex 
)
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.

Warning
Only this accessor caches. For objects that change (statusword, actual values, error/status objects) call 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.
Template Parameters
TThe expected variant alternative.
Parameters
indexCoE object index.
subindexCoE object subindex.
Returns
The value as T, or an error string if the parameter is unknown, the (first) read fails, or the stored value is not a T.

◆ readFile()

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).

Parameters
filenameFoE filename as recognised by the slave firmware.
Returns
File bytes on success, or a 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.

◆ readFlatPdoMapping()

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.

Returns
Void on success, or an error string if a mapping object referenced by an assignment cannot be read (an inconsistent mapping the caller must not exchange).

◆ readObject()

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.

Parameters
indexCoE object index.
useCompleteAccessWhen true, attempt Complete Access for eligible objects.
Returns
The decoded values per readable subindex, or an error string if the object is unknown (or has no readable sub-entries) or a read fails.

◆ readParameter()

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:

  • When the device is exchanging (SAFE-OP/OP) and process-image access was injected, the live PDO value is taken from the process image (if the object is PDO-mapped and the bus is healthy), decoded, stored (marking it SyncState::Synced) and returned — no bus I/O.
  • Otherwise, when mailboxActive(), uploads via SDO, decodes, stores and returns it.
  • Otherwise (no mailbox) returns the cached value without touching the bus.

The parameter must already exist in the map (populated by initializeParameters).

Parameters
indexCoE object index.
subindexCoE object subindex.
Returns
The (possibly freshly read) value, or an error string if the parameter is unknown or, when online, the SDO upload / decode fails.

◆ readPdoMapping()

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).

Returns
The grouped mapping, or an error string if a referenced mapping object cannot be read.

◆ readRegister()

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.

Parameters
addressESC register address (e.g. 0x0130 for DL Status).
dataOutput buffer; its size determines how many bytes are read.
Returns
Void on success, or an error string on failure.

◆ readSdo()

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.

Parameters
indexCoE object index.
subindexCoE object subindex.
Returns
The bytes transferred on success, or an error string if the mailbox transfer fails.

◆ readSii()

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.

Returns
The raw SII image on success, or an error string on failure.

◆ readValue() [1/2]

template<typename T >
std::expected< T, std::string > mm::node::Device::readValue ( ObjectAddress< T >  address)
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.

◆ readValue() [2/2]

template<typename T >
std::expected< T, std::string > mm::node::Device::readValue ( uint16_t  index,
uint8_t  subindex 
)
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.

Template Parameters
TThe expected variant alternative.
Parameters
indexCoE object index.
subindexCoE object subindex.
Returns
The value as T, or an error string if the parameter is unknown, the read fails, or the stored value is not a T.

◆ revisionNumber()

uint32_t mm::node::Device::revisionNumber ( ) const

Revision number from EEPROM.

◆ serialNumber()

uint32_t mm::node::Device::serialNumber ( ) const

Serial number from EEPROM.

◆ setValue() [1/2]

template<typename T >
bool mm::node::Device::setValue ( ObjectAddress< T >  address,
newValue 
)
inline

setValue<T>() addressed by an ObjectAddress. Lock-free, non-allocating.

◆ setValue() [2/2]

template<typename T >
bool mm::node::Device::setValue ( uint16_t  index,
uint8_t  subindex,
newValue 
)
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.

Warning
An object that is not output-mapped is stored and never transmitted. Deciding otherwise would mean scanning the process image on every call, which is what this accessor exists to avoid. Use 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.

Warning
Call it from a cyclic task's execute, for the reason value<T>() gives.
Template Parameters
TThe arithmetic type the parameter's data type maps to.
Parameters
indexCoE object index.
subindexCoE object subindex.
newValueValue to store.
Returns
true if the cell was written.

◆ setValueFromBytes()

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).

Parameters
indexCoE object index.
subindexCoE object subindex.
bytesRaw LSB-aligned little-endian value bytes.
Returns
The decoded value, or an error string if the parameter is unknown or decoding fails.

◆ slavePosition()

uint16_t mm::node::Device::slavePosition ( ) const

Returns the 1-based position of this node on the fieldbus.

◆ supportsCoe()

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.

◆ value() [1/2]

template<typename T >
std::optional< T > mm::node::Device::value ( ObjectAddress< T >  address) const
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.

◆ value() [2/2]

template<typename T >
std::optional< T > mm::node::Device::value ( uint16_t  index,
uint8_t  subindex 
) const
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.

Warning
Call it from a cyclic task's 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.
Template Parameters
TThe arithmetic type the parameter's data type maps to.
Parameters
indexCoE object index.
subindexCoE object subindex.
Returns
The value, or nullopt (see above).

◆ vendorId()

uint32_t mm::node::Device::vendorId ( ) const

Vendor ID from EEPROM.

◆ writeFile()

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).

Parameters
filenameFoE filename as recognised by the slave firmware.
dataFile bytes to write.
Returns
Void on success, or a mm::comm::FoeError (see readFile).

◆ writeParameter()

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:

  • exchanging (SAFE-OP/OP) with process-image access injected and the object output-mapped: the value is staged into the output image (sent next cycle), marked SyncState::Synced, and the call returns — no SDO download.
  • online: the value is encoded and downloaded via SDO. On success the parameter is marked SyncState::Synced; on download failure it is marked SyncState::Pending and the error is returned.
  • offline: the parameter is marked SyncState::Pending and the call succeeds — the edit lives in the cache until the device comes back online and it is re-written.
Parameters
indexCoE object index.
subindexCoE object subindex.
valueValue to set; coerced to the parameter's type.
Returns
Void on success (including the offline cache-only case), or an error string if the parameter is unknown, value cannot be coerced, or an online download fails.

◆ writePdoMapping()

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.

Parameters
mappingThe desired output (RxPDO) and input (TxPDO) mapping objects, in assignment order. An empty direction clears that sync manager's assignment.
Returns
Void on success (the device's mapping matches 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.

◆ writeRegister()

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.

Parameters
addressESC register address.
dataBytes to write.
Returns
Void on success, or an error string on failure.

◆ writeSdo()

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).

Parameters
indexCoE object index.
subindexCoE object subindex.
dataBytes to write; size must match the object's length.
Returns
Void on success, or an error string if the mailbox transfer fails.

◆ writeSii()

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.

Parameters
dataRaw SII image to write (even length).
Returns
Void on success, or an error string on failure.

◆ writeValue() [1/2]

template<typename T >
std::expected< void, std::string > mm::node::Device::writeValue ( ObjectAddress< T >  address,
newValue 
)
inline

writeValue<T>() addressed by an ObjectAddress. Blocking; off the RT thread only.

◆ writeValue() [2/2]

template<typename T >
std::expected< void, std::string > mm::node::Device::writeValue ( uint16_t  index,
uint8_t  subindex,
newValue 
)
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.

Template Parameters
TAny type a DeviceParameterValue can hold (integers, floats, std::string, std::vector<uint8_t>).
Parameters
indexCoE object index.
subindexCoE object subindex.
newValueValue to write; coerced to the parameter's type.
Returns
Void on success (including the offline cache-only case), or an error string.

The documentation for this class was generated from the following files: