Crate. crates/taktora-connector-ethercat. Default deps:
taktora-connector-core, taktora-connector-transport-iox,
taktora-connector-host, taktora-executor,
crossbeam-channel, tokio (rt +
rt-multi-thread + macros + sync). Optional
ethercrab dep behind the default-off bus-integration
cargo feature.
Status. C5a + C5b land the protocol-agnostic core:
routing, options builder, bridges, health monitor, tokio
runtime lifecycle, Connector trait impl, and the
pure-logic helpers (sdo / scheduler / wkc) that
carry the gateway’s load-bearing decision logic. C5c pulls
ethercrab 0.7 as an optional dep and ships the
forward-compatible declarations (bus::EthercatPduStorage
type alias + declare_pdu_storage! macro) every
application that wants real-bus deployment needs to declare
anyway. The cycle-loop wiring against
ethercrab::MainDevice was scoped to C5c but pulled back
when ethercrab 0.7’s actual API surface diverged from the
examples reachable via documentation search; writing 1000+
lines of speculative integration code against an API the
author can’t iterate against would have produced code that
compiles but whose runtime behaviour is unverified — exactly
the trust-me-but-untested posture the framework otherwise
avoids.
C5d takes the second path: defines the BusDriver trait
that abstracts over “the operations the cycle loop needs from a
real EtherCAT bus”, ships an in-tree MockBusDriver that
makes the cycle loop exhaustively testable without hardware,
and a CycleRunner that composes CycleScheduler,
BusDriver, evaluate_wkc, and EthercatHealthMonitor
into one cycle-driving unit.
C5e lands EthercrabBusDriver — a concrete BusDriver
wrapping ethercrab::MainDevice against ethercrab 0.7’s
API. The integration is compile-checked only: no EtherCAT
hardware is available at the time of authoring, so runtime
behaviour is unverified. The hardware-gated integration test
under tests/ethercrab_driver.rs (#[ignore]-marked,
gated on ETHERCAT_TEST_NIC) documents the bring-up + cycle
pattern and is one --ignored flag away from running on a
Linux gateway host with CAP_NET_RAW. End-to-end
verification waits on hardware arrival and a follow-on commit
to capture any API mismatches surfaced by the first real-bus
run.
Surface.
EthercatRouting — typed routing identifying one
process-data slice by SubDevice address, PDO direction, bit
offset, bit length. Implements Routing (REQ_0311).
EthercatConnectorOptions typed builder —
cycle_time (default 2 ms, min 1 ms clamp; REQ_0316),
distributed_clocks opt-in (REQ_0318), bounded
bridge capacities (REQ_0322), network interface name,
&'static [SubDeviceMap] PDO descriptor (REQ_0314,
``EthercatConnectorOptions`... (ADR_0027)), tokio worker-thread count
(Tokio runtime owned by ``Et... (ADR_0026)).
OutboundBridge<T> — bounded; saturation surfaces as
OutboundError::BackPressure(T) (REQ_0323).
InboundBridge<T> — bounded; saturation drops the
message and bumps a running count so the gateway can emit
HealthEvent::DroppedInbound { count } (REQ_0324).
EthercatHealthMonitor — thread-safe wrapper around
HealthMonitor that broadcasts every legal transition
over a crossbeam_channel.
EthercatGateway — owns its tokio runtime
(multi-thread, default 1 worker per Tokio runtime owned by ``Et... (ADR_0026)) and
joins it on Drop with a 5-second budget mirroring
Shutdown coordination (ARCH_0013) (REQ_0321).
EthercatConnector<D: BusDriver, C: PayloadCodec> —
implements the framework Connector trait (REQ_0310).
create_writer / create_reader open the plugin-side
iceoryx2 service named "{descriptor.name()}.out" /
".in", open the paired gateway-side raw port on the same
service, and register the channel in the shared
ChannelRegistry (REQ_0223 + REQ_0328).
register_with (C7b) takes the configured driver out of the
connector and spawns dispatcher_loop on the gateway’s
tokio runtime (REQ_0321); the framework still receives a
heartbeat ExecutableItem for REQ_0272.
sdo::pdo_sdo_writes — pure function producing the
ordered SDO write sequence (clear → entries → set-count
on indices 0x1C12 and 0x1C13) that the gateway
applies during the PRE-OP → SAFE-OP transition
(REQ_0315).
scheduler::CycleScheduler — pure-clock pacing decision
with skip-not-catch-up semantics; 10-cycle clock jump
produces exactly one fire (REQ_0317).
wkc::evaluate_wkc + WkcVerdict::degraded_reason —
working-counter health policy (REQ_0319, REQ_0320).
driver::BusDriver — async trait abstracting the
bring-up + per-cycle operations the runner needs from a
concrete back-end (REQ_0312 / REQ_0313 / REQ_0315
are encoded in the contract; concrete impls cover them).
C7a extends the trait with callback-shaped
with_subdevice_outputs_mut / with_subdevice_inputs
methods that expose one SubDevice’s PDI region; the
callback shape keeps ethercrab’s internal PdiWriteGuard
lifetime scoped to the impl (REQ_0326, REQ_0327).
mock::MockBusDriver — programmable test fixture: WKC
sequences, configurable bring-up response, bring-up
failure injection. C7a extends with per-SubDevice
PDI buffers (with_subdevice_outputs /
with_subdevice_inputs builders + Mutex-backed
interior storage for the callback methods).
runner::CycleRunner<D: BusDriver> — composes
CycleScheduler + BusDriver + evaluate_wkc +
EthercatHealthMonitor. End-to-end tested via
MockBusDriver.
pdi::write_routing / pdi::read_routing — pure-logic
bit-slice translation between a per-SubDevice PDI buffer
and a codec-encoded byte payload, honouring REQ_0311’s
bit_offset / bit_length. Read-modify-write on
partial leading / trailing bytes preserves adjacent
slices (REQ_0326, REQ_0327).
registry::ChannelRegistry — Vec-backed registry of
RegisteredChannel { descriptor_name, routing,
direction, binding }. C7b extends ChannelBinding with
Outbound(Box<dyn OutboundDrain>) and
Inbound(Box<dyn InboundPublish>) variants carrying the
gateway-side iceoryx2 ports (trait objects erase the
channel’s user-type T and codec C).
Insertion-order iteration verified by TEST_0219; per-cycle
iter() is allocation-free (verified via
CountingAllocator across 1 000 cycles × 16 channels —
REQ_0328).
dispatcher::dispatch_one_cycle /
dispatcher::dispatcher_loop (C7b) — gateway-side
byte-shovel composing pdi::write_routing /
pdi::read_routing + ChannelRegistry + the iceoryx2
raw pub/sub ports. dispatch_one_cycle is the
single-iteration synchronous form used by the
TEST_0220 / TEST_0221 / TEST_0222 integration
tests; dispatcher_loop is the long-running async fn
spawned by register_with. The trait-object wrappers
IoxOutboundDrain<N> / IoxInboundPublish<N> adapt the
raw iceoryx2 reader / writer to OutboundDrain /
InboundPublish (REQ_0326, REQ_0327, REQ_0328).
raw::RawChannelWriter<N> / raw::RawChannelReader<N>
in taktora-connector-transport-iox — byte-only iceoryx2
ports used by the dispatcher. send_raw_bytes /
try_recv_into bypass the codec entirely, keeping the
dispatcher hot path codec-free (REQ_0327 amended in
C7b).
Verification posture. Every REQ covered by IMPL_0050 has
a passing unit / integration test on every CI push.
EthercrabBusDriver provides the real-bus path for
REQ_0312 (single MainDevice — one PduStorage::try_split
per driver), REQ_0313 (bus reaches OP — group.into_op
fast path), REQ_0314 + REQ_0315 (PDO mapping applied via
pdo_sdo_writes + sdo_write during PRE-OP), and
REQ_0325 (Linux raw socket — tx_rx_task); those tests
await physical hardware under ETHERCAT_TEST_NIC.
REQ_0326 / REQ_0327 / REQ_0328’s end-to-end byte
hops are exercised against MockBusDriver via the C7b
integration tests TEST_0220 (outbound),
TEST_0221 (inbound), and TEST_0222 (loopback
round-trip), so the iceoryx2 ↔ PDI ↔ iceoryx2 pipeline is
green in every CI run without hardware.
Tests. Cases pass: TEST_0201 (routing round-trip),
TEST_0204 + TEST_0206 (options builder), TEST_0205-partial
(SDO write sequence shape), TEST_0207 (cycle scheduler
skip-not-catch-up), TEST_0208 (DC opt-in flag), TEST_0209 +
TEST_0210 (WKC policy), TEST_0211-partial (gateway tokio
runtime ownership and clean drop), TEST_0212-0214 (bridge
bounded capacity, BackPressure, DroppedInbound), TEST_0216-
0218 (PDI bit-slice byte-aligned / unaligned round-trips,
adjacent-slice preservation), TEST_0219 (registry
alloc-free iter), TEST_0220 (outbound end-to-end), TEST_0221
(inbound end-to-end), TEST_0222 (loopback round-trip via
mock), plus surface-shape checks for TEST_0200 (Connector
trait surface, create_writer / create_reader
registration semantics).
|