Some EtherCAT devices keep part of their configuration only in RAM: it
is written by the master after every power-up and is gone after every
power cycle. The first such device in LinuxCNC-Ethercat is the
Leadshine R3EC/R2EC modular coupler, whose module list (0xF030), PDO
assignment (0x1C12/0x1C13) and the two SII feature bits that
allow the master to write that assignment (“Enable PDO Assignment”,
“Enable PDO Configuration”) are all reset on every boot.
The IgH master already handles a slave that drops off the bus and
returns: it re-scans the bus, walks the slave through INIT, PREOP,
SAFEOP and OP again and re-applies everything it knows about
(sync managers, FMMUs, PDO mapping, DC settings, startup SDOs
registered with ecrt_slave_config_sdo). What it cannot re-apply is
the configuration a LinuxCNC-Ethercat driver wrote in its _init
function with plain SDO or SII writes. Before this feature that
configuration was one-shot: a coupler power-cycled while LinuxCNC was
running came back to OP with missing or stale inputs, and nothing
short of a LinuxCNC restart fixed it.
Runtime re-initialization closes that gap. It needs the
linuxcnc-ethercat EtherCAT master fork (libethercat with
EC_HAVE_REINIT_HOLD, packaged from
https://github.com/linuxcnc-ethercat/ethercat); with a stock IgH
libethercat LinuxCNC-Ethercat still builds and runs, logs one warning
per affected slave at startup, and behaves as before.
For every slave whose driver provides a proc_reinit hook, LinuxCNC-Ethercat
sets the master’s per-slave ReinitHold feature flag at startup. From
then on:
slaves-responding count drops, the slave’s slave-online,
slave-oper and slave-state-* pins go to 0, and the domain
working counter (lcec.<m>.wkc-state) drops to “incomplete”.
Other slaves keep running.slave-state-preop = 1, and the new slave-reconfig pin goes to 1.
The master logs Holding in PREOP until the application confirms
its re-initialization.lcec component; the servo thread never blocks)
notices the hold, re-applies the XML <sdoConfig> entries of the
slave and then calls the driver’s proc_reinit. The log shows
slave <m>.<s> returned to the bus and is held in PREOP;
re-initializing.slave-reconfig returns to 0, slave-reconfig-count increments,
slave-oper and slave-state-op go back to 1 and process data is
live again.slave-reconfig-error goes to 1,
the failure is logged, and the attempt is retried every 2 seconds.A slave is never released to SAFEOP/OP with an unconfirmed
configuration. If nothing confirms within the master’s hold timeout
(30 s by default, ReinitHoldTimeoutMs feature flag) the master logs
Re-initialization hold timed out, sets the slave’s error flag
(ethercat slaves shows E) and leaves it in PREOP.
slave-reconfig-error stays at 1. LinuxCNC-Ethercat keeps retrying;
a later successful re-initialization clears the error, and the master
reconfigures the slave from scratch (it is held once more and
re-initialized once more, which is why proc_reinit must be
idempotent).
The first configuration at LinuxCNC startup is not affected: the
driver’s _init runs before the master is activated, and
LinuxCNC-Ethercat confirms it before activation, so the slave passes
straight through PREOP as it always has.
Every slave exports three additional pins, whether or not its driver supports re-initialization (they stay at 0 otherwise):
| Pin | Type | Meaning |
|---|---|---|
lcec.<m>.<s>.slave-reconfig |
bit | The master holds the slave in PREOP, or proc_reinit is running |
lcec.<m>.<s>.slave-reconfig-error |
bit | The last re-initialization failed, or the master’s hold timed out |
lcec.<m>.<s>.slave-reconfig-count |
u32 | Successful runtime re-initializations since LinuxCNC started |
Existing pins keep their meaning through the dip: slave-online /
slave-oper / slave-state-* follow the slave’s real AL state,
lcec.<m>.slaves-responding follows the bus. Configurations that gate
motion on all-op or a slave’s slave-oper behave correctly: the
machine is not enabled while the slave is being re-initialized.
An R3EC coupler with an encoder module sits on a 24 V bus that is independent from the PC. While LinuxCNC runs, that bus dips (thermal trip, maintenance switch, someone re-plugging the coupler):
LCEC: slave 0.io returned to the bus and is held in PREOP; re-initializing
LCEC: slave 0.io: updating SII CoE details 0x03 -> 0x0f (word 0x0044)
EtherCAT 0-0: SII general category re-parsed after write: PDO assign enabled, PDO configuration enabled.
LCEC: slave 0.io re-initialized (1 so far); released to the master
EtherCAT 0-0: Re-initialization confirmed; resuming configuration.
A few seconds after power returns the coupler is back in OP, the
encoder pins update again, and slave-reconfig-count reads 1. A rapid
double power cycle simply produces two rounds of the same sequence; a
re-initialization that was in flight when the coupler died fails on
the SDO writes and is retried on the new instance.
| Driver | Devices | What proc_reinit re-applies |
|---|---|---|
lcec_leadshine_ec |
R2EC, R3EC couplers | SII CoE feature bits, 0xF030 module list, per-slot modparams, 0x1C12/0x1C13 PDO assignment; warns if 0xF050 (detected modules) differs from the configured list |
For every supported slave the XML <sdoConfig> entries (except
complete-access ones, which the master owns) are re-applied as well.
Add a proc_reinit function to the driver’s types[] entry (it is
the last field, after sourcefile, so set it by name):
static int lcec_foo_reinit(lcec_slave_t *slave);
static lcec_typelist_t types[] = {
{.name = "FOO", .vid = LCEC_FOO_VID, .pid = 0x1234, .proc_init = lcec_foo_init, .proc_reinit = lcec_foo_reinit},
{NULL},
};
The hook runs in a non-realtime thread while the slave is in PREOP with its mailbox up. It must:
_init performed (module lists, PDO
assignment, feature bits, mode registers …), typically by calling
the same helper _init uses;lcec_pdo_init(), or change the sync
manager layout: the process image is fixed after activation;SII feature bits are handled with lcec_sii_update_coe_details()
(read-modify-write of the CoE details byte in the SII general
category, locating the category by walking the SII rather than
assuming a fixed word offset), and lcec_sii_read16() /
lcec_sii_write16() for anything else. See
lcec_leadshine_ec.c for a
complete example.