Motion Master 6.0.0-alpha.50
Next-generation motion control software
Loading...
Searching...
No Matches
mm::comm::FieldbusDriver Class Referenceabstract

Abstract interface for an EtherCAT fieldbus driver. More...

#include <fieldbus_driver.h>

Inheritance diagram for mm::comm::FieldbusDriver:
[legend]
Collaboration diagram for mm::comm::FieldbusDriver:
[legend]

Classes

struct  SlaveStateRaw
 One slave's AL Status and AL Status Code registers — the "where" and the "why". More...
 

Public Member Functions

virtual ~FieldbusDriver ()=default
 
virtual std::expected< void, std::string > init ()=0
 Opens the network interface and initialises the master context.
 
virtual std::expected< int, std::string > scan ()=0
 Scans the bus for slaves and configures their sync managers and FMMUs.
 
virtual SlaveInfo slaveInfo (uint16_t position) const =0
 Returns the immutable identity fields for the slave at position.
 
virtual uint16_t slaveState (uint16_t position) const =0
 Returns the last-known AL status for a slave without any bus I/O.
 
virtual std::expected< void, std::string > configureProcessData ()=0
 Maps the process data and lays out the IOmap (one-time, control-plane).
 
virtual PdoLayout processDataLayout ()=0
 Returns the current process-data layout established by configureProcessData.
 
virtual std::vector< SlaveConfigbusConfig () const
 Returns the static ESC configuration of every slave, as last programmed by the master.
 
virtual int exchangeProcessData (std::span< const uint8_t > outputs, std::span< uint8_t > inputs)=0
 Exchanges one cycle of process data: sends outputs, receives into inputs.
 
virtual void stop ()=0
 Closes the network interface, releasing the master context.
 
virtual std::expected< std::vector< SlaveStateRaw >, std::string > readStates (const std::vector< uint16_t > &positions)=0
 Reads the current AL Status for each slave in positions.
 
virtual std::expected< std::vector< SlaveDiagnostics >, std::string > readDiagnostics (const std::vector< uint16_t > &)
 Reads live link-quality and watchdog diagnostics for each slave in positions.
 
virtual std::expected< std::vector< DcSyncDiagnostics >, std::string > readDcSync (const std::vector< uint16_t > &)
 Reads live distributed-clock synchronisation status for each slave in positions.
 
virtual std::expected< std::vector< uint8_t >, std::string > readSdo (uint16_t slavePosition, uint16_t index, uint8_t subindex)=0
 Reads an object dictionary entry via CoE SDO upload.
 
virtual std::expected< std::vector< uint8_t >, std::string > readSdoComplete (uint16_t, uint16_t)
 Reads an entire object in one CoE SDO upload using Complete Access.
 
virtual std::expected< void, std::string > writeSdo (uint16_t slavePosition, uint16_t index, uint8_t subindex, std::span< const uint8_t > data)=0
 Writes an object dictionary entry to a slave (CoE SDO download).
 
virtual std::expected< std::vector< OdEntry >, std::string > readObjectDictionary (uint16_t slavePosition)=0
 Enumerates the entire CoE object dictionary of a slave via SDO Info.
 
virtual std::expected< std::vector< uint8_t >, std::string > readSii (uint16_t)
 Reads the raw Slave Information Interface (SII / EEPROM) image of a slave.
 
virtual std::expected< void, std::string > writeSii (uint16_t, std::span< const uint8_t >)
 Writes a raw SII (EEPROM) image to a slave.
 
virtual std::expected< std::vector< uint8_t >, std::string > readFile (uint16_t slavePosition, const std::string &filename)=0
 Reads a file from the slave via File over EtherCAT (FoE).
 
virtual std::expected< void, std::string > writeFile (uint16_t slavePosition, const std::string &filename, std::span< const uint8_t > data)=0
 Writes a file to the slave via File over EtherCAT (FoE).
 
virtual std::expected< void, std::string > readRegister (uint16_t slavePosition, uint16_t address, std::span< uint8_t > data)=0
 Reads bytes from an ESC register via a Configured-Address Read (FPRD) datagram.
 
virtual std::expected< void, std::string > writeRegister (uint16_t slavePosition, uint16_t address, std::span< const uint8_t > data)=0
 Writes bytes to an ESC register via a Configured-Address Write (FPWR) datagram.
 
virtual std::expected< ProcessDataWatchdogConfig, std::string > processDataWatchdog (uint16_t)
 Reads the process-data (sync-manager) watchdog configuration of one slave.
 
virtual std::expected< ProcessDataWatchdogConfig, std::string > setProcessDataWatchdog (uint16_t, std::chrono::nanoseconds)
 Sets the process-data (sync-manager) watchdog timeout of one slave.
 
virtual void transitionToState (const std::vector< uint16_t > &positions, std::optional< EtherCatState > requiredState, EtherCatState targetState, std::chrono::steady_clock::duration timeout, std::chrono::steady_clock::duration resendInterval=std::chrono::seconds(2), std::function< void()> tick=nullptr, std::function< bool()> shouldAbort=nullptr)=0
 Commands a set of devices to targetState and blocks until all arrive or timeout elapses.
 

Protected Attributes

std::mutex controlPlaneMutex_
 

Detailed Description

Abstract interface for an EtherCAT fieldbus driver.

Concrete implementations: SoemFieldbusDriver (SOEM), SpoeFieldbusDriver (SPoE, planned). The composition root (main.cc) constructs exactly one and injects it into DeviceManager via DeviceManager::init. GameLoop never references the driver — it runs CyclicTasks (e.g. ProcessDataCyclicTask) that reach the bus only through DeviceManager.

The driver owns controlPlaneMutex_, which serialises the control-plane operations (mailbox/SDO, FoE, ESC register, and state access) amongst non-RT callers. The real-time PDO path (exchangeProcessData) runs lock-free: SOEM's port layer is internally thread-safe (per-datagram index allocation plus tx/rx mutexes held only for a single non-blocking poll), and PDO touches disjoint state (the process-data IOmap) from the control plane (mailbox pool, slave state). Keeping PDO out of the mutex keeps the RT cycle unbounded by a slow SDO or object-dictionary enumeration.

Note
Precondition on slavePosition/position: every slave-indexed method here trusts its position argument and indexes a fixed-size slave table without bounds-checking. Callers must pass a valid, discovered bus position (1..number-of-slaves) — an unknown or out-of-range value is undefined behaviour (an out-of-bounds access, and for a write a datagram to a bogus station). This is deliberate: the driver is a thin transport, and validating the position is the caller's responsibility. DeviceManager does this for every call (it rejects an unknown position with a 404 before reaching the driver); a program using a driver directly must validate positions itself. The same "caller owns the preconditions" contract applies to lifecycle ordering (initscanconfigureProcessData) and slave AL state.

Constructor & Destructor Documentation

◆ ~FieldbusDriver()

virtual mm::comm::FieldbusDriver::~FieldbusDriver ( )
virtualdefault

Member Function Documentation

◆ busConfig()

virtual std::vector< SlaveConfig > mm::comm::FieldbusDriver::busConfig ( ) const
inlinevirtual

Returns the static ESC configuration of every slave, as last programmed by the master.

A diagnostic snapshot (Sync Managers, FMMUs, mailbox, distributed clock, addresses) of what the master programmed into each slave's EtherCAT Slave Controller. Read from cached state — no bus I/O — so it mirrors what the master believes it programmed, not a live ESC read. It is built up across control-plane operations, not one: scan() sets identity, addresses, the mailbox SMs and DC capability; configureProcessData() adds the process-data SMs, FMMUs and DC timing (and rebuilds the FMMUs on a re-map); and a BOOT state transition transiently reprograms the mailbox SMs (reset on the return to PRE-OP). A meaningful snapshot therefore needs scan(), plus configureProcessData() for the process-data SMs/FMMUs. Optional capability: the default returns an empty vector for transports without an ESC (e.g. SPoE); the SOEM driver overrides it.

Returns
Per-slave configuration in bus order, or an empty vector if unsupported / unscanned.

Reimplemented in mm::comm::soem::SoemFieldbusDriver.

◆ configureProcessData()

virtual std::expected< void, std::string > mm::comm::FieldbusDriver::configureProcessData ( )
pure virtual

Maps the process data and lays out the IOmap (one-time, control-plane).

Reads each slave's active PDO assignment, computes the process image, programs the FMMUs, and fills the driver-owned IOmap. Slaves must be in PRE-OP (mailbox active) so the assignment can be read. Run again to re-map after the slave set changes (e.g. a device returning from a firmware download) — the previous PdoLayout is invalidated. Serialised with other control-plane operations via the socket mutex. This is the one operation that must not overlap exchangeProcessData — it rewrites the IOmap the RT cycle reads — and, since the lock-free PDO path does not take the socket mutex, that exclusion is enforced by the caller (DeviceManager drains exchange and unpublishes the process image before re-mapping), not by the mutex.

Returns
Void on success, or an error string if no driver is initialised, no process data is mapped, or the image exceeds kMaxProcessImageBytes.

Implemented in mm::comm::soem::SoemFieldbusDriver.

◆ exchangeProcessData()

virtual int mm::comm::FieldbusDriver::exchangeProcessData ( std::span< const uint8_t >  outputs,
std::span< uint8_t >  inputs 
)
pure virtual

Exchanges one cycle of process data: sends outputs, receives into inputs.

The caller owns both images; the driver translates them to and from its transport (an IOmap copy for SOEM/IgH, message (de)serialisation for SPoE). outputs.size() must equal PdoLayout::outputBytes and inputs.size() must equal PdoLayout::inputBytes. Called once per GameLoop cycle. Runs lock-free (does not take the socket mutex) — see the class-level note. Must complete within the cycle budget; timing jitter here propagates directly to control latency. Must not be called before configureProcessData or after stop.

Parameters
outputsOutput image to send (master→slave); not retained past the call.
inputsBuffer that receives the input image (slave→master).
Returns
The working counter for the transaction. Compare against PdoLayout::expectedWkc: a lower value means one or more slaves did not contribute this cycle. Returns 0 when no driver is initialised.

Implemented in mm::comm::soem::SoemFieldbusDriver.

◆ init()

virtual std::expected< void, std::string > mm::comm::FieldbusDriver::init ( )
pure virtual

Opens the network interface and initialises the master context.

Must be called before any other driver method.

Returns
Void on success, or an error string describing the failure.

Implemented in mm::comm::soem::SoemFieldbusDriver.

◆ processDataLayout()

virtual PdoLayout mm::comm::FieldbusDriver::processDataLayout ( )
pure virtual

Returns the current process-data layout established by configureProcessData.

The returned spans alias the driver-owned IOmap and remain valid until the next configureProcessData or stop. Before a successful configureProcessData the layout is empty (zero-sized spans, no slaves).

Implemented in mm::comm::soem::SoemFieldbusDriver.

◆ processDataWatchdog()

virtual std::expected< ProcessDataWatchdogConfig, std::string > mm::comm::FieldbusDriver::processDataWatchdog ( uint16_t  )
inlinevirtual

Reads the process-data (sync-manager) watchdog configuration of one slave.

FPRD-reads the watchdog divider (0x0400), process-data watchdog time (0x0420), and process-data watchdog status (0x0440) ESC registers, decoding the timeout as ticks × 40 ns × (divider + 2) plus the live running/expired status. A zero time register means the watchdog is disabled. Unlike readDiagnostics (which returns the expiration counter), this returns the configured timeout and its current run state. Serialised via the socket mutex; runs concurrently with the lock-free exchangeProcessData.

Optional capability: the default returns an error for transports without an ESC (e.g. SPoE); the SOEM driver overrides it.

Parameters
slavePosition1-based slave position on the bus.
Returns
The decoded watchdog configuration, or an error string if the transport has no ESC or a register read fails.

Reimplemented in mm::comm::soem::SoemFieldbusDriver.

◆ readDcSync()

virtual std::expected< std::vector< DcSyncDiagnostics >, std::string > mm::comm::FieldbusDriver::readDcSync ( const std::vector< uint16_t > &  )
inlinevirtual

Reads live distributed-clock synchronisation status for each slave in positions.

Performs FPRD reads of the DC system-time delay (0x0928) and system-time difference (0x092C) registers for every DC-capable slave and decodes the signed deviation of each slave's local clock from the bus reference. The reference clock is the first DC-capable slave; its own difference is zero. Values are meaningful only while the bus is exchanging process data in SAFE-OP/OP (the reference time is distributed in the cyclic frame), and the difference converges toward zero as the slaves' drift-compensation loops settle — poll this and watch for one that stays large or grows. Serialised with other control-plane operations via the socket mutex; runs concurrently with the lock-free exchangeProcessData and never blocks the RT cycle.

Optional capability: the default returns an error for transports without an ESC (e.g. SPoE); the SOEM driver overrides it.

Parameters
positions1-based slave positions to read.
Returns
Decoded DC sync status per position in the same order as positions, or an error string if the transport has no ESC or a register read fails.

Reimplemented in mm::comm::soem::SoemFieldbusDriver.

◆ readDiagnostics()

virtual std::expected< std::vector< SlaveDiagnostics >, std::string > mm::comm::FieldbusDriver::readDiagnostics ( const std::vector< uint16_t > &  )
inlinevirtual

Reads live link-quality and watchdog diagnostics for each slave in positions.

Performs FPRD reads of the DL Status (0x0110), error-counter (0x0300–0x0313), and watchdog (0x0440–0x0443) ESC register blocks for every requested slave and returns the decoded counters. The counters are monotonic since the last clear (power cycle / explicit write), so callers compare successive snapshots and alert on a rising delta — a single non-zero value is historical, a climbing one is an active fault. Serialised with other control-plane operations via the socket mutex; runs concurrently with the lock-free exchangeProcessData and never blocks the RT cycle.

Optional capability: the default returns an error for transports without an ESC (e.g. SPoE); the SOEM driver overrides it.

Parameters
positions1-based slave positions to read.
Returns
Decoded diagnostics per position in the same order as positions, or an error string if the transport has no ESC or a register read fails.

Reimplemented in mm::comm::soem::SoemFieldbusDriver.

◆ readFile()

virtual std::expected< std::vector< uint8_t >, std::string > mm::comm::FieldbusDriver::readFile ( uint16_t  slavePosition,
const std::string &  filename 
)
pure virtual

Reads a file from the slave via File over EtherCAT (FoE).

Sends an FoE read request for filename and collects all data packets from the slave. FoE is available in Boot, Pre-Op, Safe-Op, and Op states (device-dependent); the caller is responsible for ensuring the device is in a suitable state. Called from HTTP handler threads; serialised with other control-plane operations via the socket mutex, and runs concurrently with the lock-free exchangeProcessData.

Parameters
slavePosition1-based slave position on the bus.
filenameFoE filename as recognised by the slave firmware.
Returns
File bytes on success, or an error string if the transfer fails.

Implemented in mm::comm::soem::SoemFieldbusDriver.

◆ readObjectDictionary()

virtual std::expected< std::vector< OdEntry >, std::string > mm::comm::FieldbusDriver::readObjectDictionary ( uint16_t  slavePosition)
pure virtual

Enumerates the entire CoE object dictionary of a slave via SDO Info.

Performs the "Get Object List" → "Get Object Description" → "Get Entry Description" sequence (ETG.1000.6 §5.6) and returns one OdEntry per (index, subindex) pair. The slave must be in PRE-OP, SAFE-OP, or OP — i.e. mailbox communication enabled. Many transfers are issued; expect the call to take seconds on a fully populated drive.

Slaves that do not implement the SDO Info service return an error.

Parameters
slavePosition1-based slave position on the bus.
Returns
All OD entries on success, or an error string if any phase of the enumeration fails after the driver's internal retries.

Implemented in mm::comm::soem::SoemFieldbusDriver.

◆ readRegister()

virtual std::expected< void, std::string > mm::comm::FieldbusDriver::readRegister ( uint16_t  slavePosition,
uint16_t  address,
std::span< uint8_t >  data 
)
pure virtual

Reads bytes from an ESC register via a Configured-Address Read (FPRD) datagram.

data.size() bytes are read from register address of the slave at slavePosition. Called from HTTP handler threads; serialised with other control-plane operations via the socket mutex, and runs concurrently with the lock-free exchangeProcessData.

Parameters
slavePosition1-based slave position on the bus.
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 if no slave responded.

Implemented in mm::comm::soem::SoemFieldbusDriver.

◆ readSdo()

virtual std::expected< std::vector< uint8_t >, std::string > mm::comm::FieldbusDriver::readSdo ( uint16_t  slavePosition,
uint16_t  index,
uint8_t  subindex 
)
pure virtual

Reads an object dictionary entry via CoE SDO upload.

Allocates up to 4096 bytes, performs a mailbox SDO upload, and returns the exact bytes the slave sent (resized to the actual transfer size). Called from HTTP handler threads; serialised with other control-plane operations via the socket mutex, but runs concurrently with the lock-free exchangeProcessData — a slow SDO never blocks the RT cycle (the reason the PDO path stays out of the mutex).

Returns an owning std::vector, not a std::span, deliberately: the bytes are produced by this call and their ownership must transfer to the caller, which a non-owning view cannot do (a span would have to alias driver state that the next call clobbers, or storage nothing frees). Nor can the caller lend a sized buffer the way readRegister does — an SDO object's size is unknown until the transfer completes (expedited vs. segmented), which is exactly why the implementation over-allocates and resizes. The return is moved out, so this costs no copy. (std::span is used elsewhere in this interface only for borrowed inputs — e.g. writeSdo / exchangeProcessData — never to hand back freshly produced data.)

Parameters
slavePosition1-based slave position on the bus.
indexCoE object index.
subindexCoE object subindex.
Returns
The bytes transferred on success, or an error string if the mailbox transfer fails.

Implemented in mm::comm::soem::SoemFieldbusDriver.

◆ readSdoComplete()

virtual std::expected< std::vector< uint8_t >, std::string > mm::comm::FieldbusDriver::readSdoComplete ( uint16_t  ,
uint16_t   
)
inlinevirtual

Reads an entire object in one CoE SDO upload using Complete Access.

Uploads every subindex of index in a single mailbox transfer (SDO command with the complete-access bit set, starting at subindex 0), replacing one readSdo per subindex. The returned blob is what the slave sends: subindex 0 as a 16-bit value (1 data byte + 1 alignment pad), then subindices 1..N concatenated at their native bit lengths. The caller slices it back into per-subindex values from the object's known entry layout.

Complete access is optional in CoE — a slave, or an individual object, that does not support it answers with an SDO abort. Callers must therefore treat any error as "fall back to per-subindex @c readSdo", not as a hard failure. The default implementation reports it unsupported; drivers backed by a real CoE mailbox override this.

Parameters
slavePosition1-based slave position on the bus.
indexCoE object index.
Returns
The raw complete-access blob on success, or an error string (including when the driver or the slave does not support complete access).

Reimplemented in mm::comm::soem::SoemFieldbusDriver.

◆ readSii()

virtual std::expected< std::vector< uint8_t >, std::string > mm::comm::FieldbusDriver::readSii ( uint16_t  )
inlinevirtual

Reads the raw Slave Information Interface (SII / EEPROM) image of a slave.

Reads the slave's EEPROM through the ESC's EEPROM-control registers and returns the raw byte image — a fixed 128-byte header followed by the self-describing category section (strings, general info, FMMU/Sync-Manager and PDO defaults, distributed-clock settings). Decode it with mm::comm::parseSii. EEPROM access is a control-plane operation serialised on the socket mutex per transaction (it runs concurrently with the lock-free exchangeProcessData); the real constraint is slave state — it is most reliable while the slave is in INIT or PRE-OP. The driver hands EEPROM control back to the slave's PDI before returning.

Optional capability: the default returns an error for transports without an ESC EEPROM (e.g. SPoE); the SOEM driver overrides it.

Parameters
slavePosition1-based slave position on the bus.
Returns
The raw SII image on success, or an error string if the transport has no EEPROM, the position is out of range, or the driver is not initialised.

Reimplemented in mm::comm::soem::SoemFieldbusDriver.

◆ readStates()

virtual std::expected< std::vector< SlaveStateRaw >, std::string > mm::comm::FieldbusDriver::readStates ( const std::vector< uint16_t > &  positions)
pure virtual

Reads the current AL Status for each slave in positions.

Refreshes slave state from the hardware in one pass, then returns the raw AL Status register and AL Status Code register for each requested position. AL Status bits 3:0 encode the current state (1=Init, 2=PreOp, 3=Boot, 4=SafeOp, 8=Op); bit 4 is the error indicator. AL Status Code is non-zero when an error is present and identifies the cause (ETG.1000.6 §6.4.1).

Parameters
positions1-based slave positions to read.
Returns
Raw state per position in the same order as positions, or an error string if the hardware read fails.

Implemented in mm::comm::soem::SoemFieldbusDriver.

◆ scan()

virtual std::expected< int, std::string > mm::comm::FieldbusDriver::scan ( )
pure virtual

Scans the bus for slaves and configures their sync managers and FMMUs.

Must be called after a successful init(). Slaves remain in INIT state — state transitions are left entirely to the caller.

Returns
Number of slaves found (0 is a valid result — an empty/unpowered bus, which the master cannot distinguish from a disconnected one), or an error string if the scan fails.

Implemented in mm::comm::soem::SoemFieldbusDriver.

◆ setProcessDataWatchdog()

virtual std::expected< ProcessDataWatchdogConfig, std::string > mm::comm::FieldbusDriver::setProcessDataWatchdog ( uint16_t  ,
std::chrono::nanoseconds   
)
inlinevirtual

Sets the process-data (sync-manager) watchdog timeout of one slave.

Reads the slave's current watchdog divider (0x0400) — left untouched, since it is shared with the PDI watchdog — computes the nearest tick count for timeout, and FPWR-writes the process-data watchdog time register (0x0420). A timeout of zero disables the watchdog. Because the tick resolution is the divider's time base, the achieved timeout is rounded; the returned config reports what was actually programmed. Fails if timeout exceeds what the current divider can represent (more than 65535 ticks) — the error names the maximum.

The write persists across re-maps and re-scans (SOEM never reprograms these registers) until the ESC reloads EEPROM (power cycle / explicit reload). Serialised via the socket mutex; runs concurrently with the lock-free exchangeProcessData.

Optional capability: the default returns an error for transports without an ESC (e.g. SPoE); the SOEM driver overrides it.

Parameters
slavePosition1-based slave position on the bus.
timeoutDesired watchdog timeout; zero disables the watchdog.
Returns
The watchdog configuration actually programmed (timeout rounded to the tick base), or an error string if the transport has no ESC, timeout is unrepresentable, or a register access fails.

Reimplemented in mm::comm::soem::SoemFieldbusDriver.

◆ slaveInfo()

virtual SlaveInfo mm::comm::FieldbusDriver::slaveInfo ( uint16_t  position) const
pure virtual

Returns the immutable identity fields for the slave at position.

Parameters
position1-based slave position on the bus.

Implemented in mm::comm::soem::SoemFieldbusDriver.

◆ slaveState()

virtual uint16_t mm::comm::FieldbusDriver::slaveState ( uint16_t  position) const
pure virtual

Returns the last-known AL status for a slave without any bus I/O.

Returns the cached AL Status register (bits 3:0 = state, bit 4 = error) from the most recent readStates() or state transition — no network round trip. Returns 0 before any state is known. Call readStates() to refresh the cache from the hardware. Lets callers (e.g. Device) derive online / exchanging status without a redundant cached copy.

Implemented in mm::comm::soem::SoemFieldbusDriver.

◆ stop()

virtual void mm::comm::FieldbusDriver::stop ( )
pure virtual

Closes the network interface, releasing the master context.

Does not command any AL state change — it only closes the transport (for SOEM, ecx_close, which just destroys the mailbox-pool mutex and closes the raw socket; nothing is sent on the wire). Slaves left in SAFE-OP/OP are not driven back to INIT by the master; once process-data frames stop arriving, each slave's own sync-manager watchdog trips and it drops out of OP on its own (the safety guarantee for comms loss, independent of any graceful shutdown). After stop() returns, exchangeProcessData() must not be called again.

Implemented in mm::comm::soem::SoemFieldbusDriver.

◆ transitionToState()

virtual void mm::comm::FieldbusDriver::transitionToState ( const std::vector< uint16_t > &  positions,
std::optional< EtherCatState requiredState,
EtherCatState  targetState,
std::chrono::steady_clock::duration  timeout,
std::chrono::steady_clock::duration  resendInterval = std::chrono::seconds(2),
std::function< void()>  tick = nullptr,
std::function< bool()>  shouldAbort = nullptr 
)
pure virtual

Commands a set of devices to targetState and blocks until all arrive or timeout elapses.

Devices whose current state (error bit masked) does not match requiredState are skipped; pass std::nullopt to command all positions unconditionally.

The call polls at ~100 ms intervals, re-sending the command to lagging devices every resendInterval. Devices that do not arrive in time are logged at error level; no exception is thrown. The outcome per device is not returned here — read it back via readStates after the call (this is what DeviceManager::transitionToState does).

Parameters
positions1-based device positions to target.
requiredStatePre-filter: only command devices whose current state equals this value. std::nullopt skips filtering and commands all positions.
targetStateDesired state.
timeoutMaximum time to wait for all devices.
resendIntervalHow often to re-send the command to lagging devices.
tickOptional callback invoked at ~1 ms intervals while waiting. Pass a PDO sender when targeting EtherCatState::Op so the sync-manager watchdog does not fire during the wait.
shouldAbortOptional predicate; when it returns true the wait is abandoned early without logging failures for pending devices.

Implemented in mm::comm::soem::SoemFieldbusDriver.

◆ writeFile()

virtual std::expected< void, std::string > mm::comm::FieldbusDriver::writeFile ( uint16_t  slavePosition,
const std::string &  filename,
std::span< const uint8_t >  data 
)
pure virtual

Writes a file to the slave via File over EtherCAT (FoE).

Sends an FoE write request for filename and streams data to the slave. FoE is available in Boot, Pre-Op, Safe-Op, and Op states (device-dependent); the caller is responsible for ensuring the device is in a suitable state. Called from non-RT (HTTP handler) threads; serialized with other control-plane operations via the driver's socket mutex.

Parameters
slavePosition1-based slave position on the bus.
filenameFoE filename as recognised by the slave firmware.
dataFile bytes to write.
Returns
Void on success, or an error string if the transfer fails.

Implemented in mm::comm::soem::SoemFieldbusDriver.

◆ writeRegister()

virtual std::expected< void, std::string > mm::comm::FieldbusDriver::writeRegister ( uint16_t  slavePosition,
uint16_t  address,
std::span< const uint8_t >  data 
)
pure virtual

Writes bytes to an ESC register via a Configured-Address Write (FPWR) datagram.

data.size() bytes are written to register address of the slave at slavePosition. Called from HTTP handler threads; serialised with other control-plane operations via the socket mutex, and runs concurrently with the lock-free exchangeProcessData.

Parameters
slavePosition1-based slave position on the bus.
addressESC register address.
dataBytes to write.
Returns
Void on success, or an error string if no slave responded.

Implemented in mm::comm::soem::SoemFieldbusDriver.

◆ writeSdo()

virtual std::expected< void, std::string > mm::comm::FieldbusDriver::writeSdo ( uint16_t  slavePosition,
uint16_t  index,
uint8_t  subindex,
std::span< const uint8_t >  data 
)
pure virtual

Writes an object dictionary entry to a slave (CoE SDO download).

Performs a mailbox SDO download of data to (index, subindex). The slave must be in PRE-OP, SAFE-OP, or OP (mailbox communication active). Called from non-RT (HTTP handler) threads; serialized with other control-plane operations via the driver's socket mutex, and runs concurrently with the lock-free exchangeProcessData (never blocks the RT cycle).

Parameters
slavePosition1-based slave position on the bus.
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.

Implemented in mm::comm::soem::SoemFieldbusDriver.

◆ writeSii()

virtual std::expected< void, std::string > mm::comm::FieldbusDriver::writeSii ( uint16_t  ,
std::span< const uint8_t >   
)
inlinevirtual

Writes a raw SII (EEPROM) image to a slave.

Writes data to the slave's EEPROM through the ESC's EEPROM-control registers, one 16-bit word at a time from word address 0. data must be a whole number of 16-bit words (even length). This is a destructive control-plane operation: a malformed image can leave the slave unidentifiable until re-flashed. EEPROM access takes the socket mutex (running concurrently with the lock-free exchangeProcessData) and is only safe while the slave is in INIT or PRE-OP. The slave does not adopt the new contents until its ESC reloads the EEPROM — i.e. after a power cycle.

Optional capability: the default returns an error for transports without an ESC EEPROM (e.g. SPoE); the SOEM driver overrides it.

Parameters
slavePosition1-based slave position on the bus.
dataRaw SII image to write (even length).
Returns
Void on success, or an error string if the transport has no EEPROM, the position is out of range, data has an odd length, or a word write fails.

Reimplemented in mm::comm::soem::SoemFieldbusDriver.

Member Data Documentation

◆ controlPlaneMutex_

std::mutex mm::comm::FieldbusDriver::controlPlaneMutex_
mutableprotected

Serialises control-plane access to the underlying fieldbus context (mailbox pool, slave state, manual mailbox sequence counters) amongst non-RT callers.

"Control plane" vs "data plane" is the networking split, and it maps exactly onto this driver. The data plane is the high-rate PDO cycle (exchangeProcessData, run every tick on the RT thread) that moves process data — it runs lock-free and does not take this mutex. The control plane is everything that configures or commands the bus occasionally and off the RT path — SDO/mailbox, FoE, ESC registers, AL-state transitions, and pure reads of the cached results (processDataLayout, busConfig) — all of which share the one fieldbus context and are serialised here. So a slow SDO or object-dictionary walk never blocks the RT cycle: the two planes touch disjoint state (control-plane context vs. the process-data IOmap).

Held only for the duration of a single socket transaction — never across a sleep, a blocking wait, or a user callback. mutable so the const accessors (slaveInfo, slaveCount) can lock.


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