Connector framework¶
This page captures the requirements for taktora-connector: a framework that
connects taktora-executor applications to external protocols (MQTT, OPC UA,
gRPC, fieldbus) through a controlled boundary, so messy network code lives
outside the application’s deterministic core.
The decomposition is two-tier:
Top-level umbrella feature — Connector framework (FEAT_0030) — peer to PLC runtime heart on iceoryx2 (FEAT_0010). Taktora-connector is a general-purpose framework usable by any taktora-executor consumer; it is not bound to the PLC use case.
Capability-cluster sub-features — one per architectural concern, each
:satisfies:Connector framework (FEAT_0030).Requirements — concrete shall-clauses that
:satisfies:a capability-cluster feature.
This round covers the framework core plus an MQTT reference connector
(rumqttc-backed). OPC UA, gRPC, and Beckhoff ADS connectors are
deferred to follow-on specs that will reuse the same five contracts.
Top-level umbrella¶
A Rust framework that bridges taktora-executor applications to external
protocols through a typed envelope carried over iceoryx2 shared memory.
The framework provides five contracts — envelope, codec, routing, health,
lifecycle — that every protocol connector instantiates as a plugin
(in-app side) and a gateway (out-of-app side). Both halves are
taktora-executor Deployment chooses whether the gateway runs as a tokio task in-process alongside the plugin host, or as a separate gateway binary. The envelope contract is identical either way; only process-startup wiring differs. This umbrella is a peer of PLC runtime heart on iceoryx2 (FEAT_0010) “PLC runtime heart”; the
connector framework is a general-purpose mechanism, not PLC-specific.
Fieldbus integration interface (FEAT_0023) “Fieldbus integration interface” is later expected to
|
Capability clusters¶
The umbrella decomposes into seven capability clusters. Each cluster is a
sub-feature :satisfies: Connector framework (FEAT_0030), with concrete shall-clauses
underneath.
Envelope transport¶
The on-wire form of every message crossing the plugin↔gateway boundary and the iceoryx2 service shape that carries it. Defines header fields, per-channel sizing, and the zero-copy publish path. |
The framework shall define |
The framework shall allow each channel to declare its maximum payload
size at service-creation time, carried in |
For each (publisher, channel) pair, the framework shall populate
|
The framework shall populate |
The framework shall carry the 32-byte |
The framework shall publish envelopes via |
For each logical channel direction (outbound app→gateway, inbound
gateway→app), the framework shall create a separate iceoryx2
publish-subscribe service whose name is derived deterministically from
|
Codec abstraction¶
How typed values become payload bytes, and back. Codec selection is a compile-time decision via a generic parameter on the connector type; no runtime codec dispatch. |
The framework shall define a |
Each |
The framework shall ship a |
When |
When |
Connector trait and routing¶
The plugin-side public API: a |
The framework shall define a |
|
The |
|
Each connector crate ( |
Connection lifecycle¶
The observable health state of every connector and the policy by which a connector retries after a stack-level disconnect. Both surfaces are uniform across protocols, regardless of which protocol stack owns the reconnect mechanism. |
The framework shall define |
|
The framework shall define a |
The framework shall ship an |
Every transition between |
Connectors whose underlying protocol stack manages reconnect internally
(e.g. tonic-managed gRPC channels) shall not be required to use
|
Process boundary¶
The framework supports two deployment shapes — gateway as an in-process tokio task or as a separate gateway binary — using the same envelope contract on both sides. |
The framework shall use the same |
The framework shall support running the gateway as a tokio task spawned
by |
The framework shall support running the gateway as a self-contained binary in its own OS process, communicating with the plugin only through iceoryx2 shared memory. |
Both the plugin host and a separate gateway binary shall return cleanly
from |
The framework shall not introduce envelopes carrying control-plane
semantics (“ping”, “version”, “shutdown handshake”) on the SHM channel.
Health is observed via |
MQTT reference connector¶
The first concrete connector instantiating the framework’s contracts:
|
The connector crate shall expose |
The |
The connector shall support MQTT QoS levels |
When |
The connector shall accept inbound subscriptions whose topic includes
the MQTT wildcards |
The connector shall accept username and password credentials in
|
The connector shall provide TLS support via |
The connector shall target MQTT protocol version 3.1.1. MQTT 5.0 features (user properties, shared subscriptions, response topic) are deferred to a follow-on spec. |
The MQTT gateway shall host |
The outbound (taktora-executor → tokio) and inbound (tokio →
taktora-executor) bridges shall be bounded channels with configurable
capacity in |
When the outbound bridge channel is full, |
When the inbound bridge channel is full, the gateway shall emit
|
EtherCAT reference connector¶
A second concrete connector instantiating the framework’s contracts:
|
The connector crate shall expose |
The |
A single |
The gateway shall transition the EtherCAT bus to the OP state before accepting envelope traffic from the plugin side. |
The connector shall accept a static PDO-mapping description per
SubDevice at build time, declared by the application crate via
|
The gateway shall apply the configured PDO mapping by issuing SDO writes
to the sync-manager assignment indices |
The gateway shall accept a configurable cycle duration via
|
When the gateway misses one or more cycle ticks, it shall skip the missed ticks rather than queue them for catch-up execution. |
The connector shall perform Distributed Clocks bring-up only when
|
The gateway shall report |
When the working counter on a completed cycle is below the expected
value, the gateway shall transition |
The EtherCAT gateway shall host the ethercrab TX/RX task on a tokio
runtime contained inside |
The outbound (taktora-executor → tokio) and inbound (tokio →
taktora-executor) bridges between the plugin and the gateway sidecar
shall be bounded channels with configurable capacity in
|
When the outbound bridge channel is full, |
When the inbound bridge channel is full, the gateway shall emit
|
The gateway shall open the EtherCAT network interface via a Linux raw
socket, requiring the |
When a plugin publishes a value through |
After each cycle’s |
The gateway shall maintain a registry mapping each open
|
Host wiring¶
The composition layer that wraps a |
|
|
|
Behind a default-off |
Zenoh reference connector¶
A third concrete connector instantiating the framework’s contracts:
|
The pub/sub half of the Zenoh connector. |
The connector crate shall expose |
The |
The Zenoh connector shall accept any |
The Zenoh gateway shall host the |
The outbound (taktora-executor → tokio) and inbound (tokio →
taktora-executor) bridges between the plugin and the Zenoh gateway
sidecar shall be bounded channels with capacities configurable
via |
When the outbound bridge channel is full, |
When the inbound bridge channel is full, the gateway shall emit
|
|
On the inbound leg (Zenoh peer → plugin), the gateway shall
publish the raw payload bytes received from the Zenoh subscriber
or reply callback onto the channel’s inbound iceoryx2 service as
a |
The query half of the Zenoh connector — Zenoh’s signature
request/response primitive, layered on top of the same
|
|
|
|
|
The end of a reply stream shall be signalled by a one-byte
Zenoh-private frame discriminator at the start of the reply
envelope’s payload: |
The default per-query timeout shall be sourced from
|
When the gateway observes a |
|
When the inbound bridge for the reply path (gateway → plugin
on a querier channel) saturates, the gateway shall emit
|
The Zenoh-specific session and observability surface — peer-vs-
client mode configuration, scout/locator wiring, and the
stack-internal reconnect posture. Health-event semantics inherit
from Connection lifecycle (FEAT_0034) and re-affirm Stack-internal-reconnect co... (REQ_0235) (stack-
internal reconnect emits health events without
|
|
The Zenoh connector shall not use
ReconnectPolicy trait (REQ_0232) |
Every transition of the Zenoh session between alive and closed
states observed by the gateway (including the initial
|
|
The real |
|
The Zenoh connector shall support Linux, macOS, and Windows as
host operating systems for both plugin and gateway (broader than
Linux raw socket required o... (REQ_0325)’s Linux-only EtherCAT posture, because Zenoh has
no OS-specific socket requirement comparable to |
CAN (SocketCAN) reference connector¶
A fourth concrete connector instantiating the framework’s
contracts: |
The on-wire form of CAN traffic crossing the plugin↔gateway
boundary. |
The gateway-side multiplexer: one gateway instance can own
multiple Linux CAN interfaces (broader than Single MainDevice per gatew... (REQ_0312)’s
single-MainDevice EtherCAT posture). Per-channel CAN ID and mask
are compiled into one |
The CAN-specific health surface: per-interface state aggregated
into the connector’s single externally-visible
|
The connector crate shall expose |
The |
The CAN gateway shall open SocketCAN interfaces via the Linux
|
The |
|
The CAN gateway shall host its RX/TX tasks on a tokio runtime
contained inside |
The outbound (taktora-executor → tokio) and inbound (tokio →
taktora-executor) bridges between the plugin and the CAN gateway
sidecar shall be bounded channels with capacities configurable
via |
When the outbound bridge channel is full, |
When the inbound bridge channel is full, the gateway shall emit
|
For channels declared with |
For channels declared with |
|
When a plugin publishes a value through |
On the inbound leg (CAN bus → plugin), the gateway shall
publish the raw frame data bytes received from the SocketCAN
read onto the matching channel’s inbound iceoryx2 service as a
|
The |
A single |
|
For each owned interface, the gateway shall compute the union
of |
The per-interface filter (per Per-interface filter is the... (REQ_0622)) shall be
recomputed and re-applied whenever a |
When a CAN frame arrives on an interface, the gateway shall
publish the frame’s data bytes (per Inbound gateway is byte-onl... (REQ_0614)) onto the
inbound iceoryx2 service of every registered channel whose
|
The gateway shall maintain a per-interface routing registry
mapping each open |
The single externally-visible |
The gateway shall enable the |
When an interface reports an error-passive or error-warning
condition via an error frame, the gateway shall transition
that interface’s sub-state to |
When an interface reports a bus-off condition via an error
frame, the gateway shall transition that interface’s sub-state
to |
The CAN connector shall use the framework-level
|
Every transition between |
No |
Anti-goals¶
The following requirements are explicitly rejected — captured for the
record so that future readers see what the framework deliberately does
not do, and why. Each rejected requirement :satisfies: Connector framework (FEAT_0030)
to keep the umbrella’s traceability complete.
The framework shall not match requests to responses using
|
The framework shall not introduce envelopes carrying |
The framework shall not persist outbound envelopes on disk or in
any durable store when the gateway is |
The framework shall not verify that plugin and gateway agree on
the channel’s payload type |
The framework shall not offer a channel type that is portable
between protocols (“write the same plugin code, swap MQTT for OPC UA
without code changes”). Plugin code imports its connector’s
|
A single |
The framework shall not catch panics from the tokio task or any protocol-stack worker. A panic shall propagate and abort the gateway process; restart policy is the host’s responsibility, matching taktora-executor’s existing posture. |
The CAN connector shall not parse Vector DBC files or perform bit-/signal-level extraction from CAN payloads. The connector is a raw-frame transport; typed signal codecs are a separate concern for a future feature layered on top. |
The CAN connector shall not implement ISO-TP (ISO 15765-2)
segmentation or J1939 (PGN, transport protocol, address claim).
Applications needing higher-layer CAN protocols shall either
layer them above |
The CAN connector shall not transport CAN-XL (CiA 610-1)
frames. The first cut targets classical CAN and CAN-FD only;
CAN-XL is deferred to a follow-on spec once the underlying
|
The CAN connector shall not expose CAN error frames as a
plugin-readable |
The CAN connector shall not set the kernel’s
|
Cross-cutting traceability¶
Every requirement on this page (excluding rejected anti-goals) carries a
:satisfies: link to its capability-cluster feat; every cluster feat
:satisfies: Connector framework (FEAT_0030). Architectural specifications
(spec directives) refining these requirements are emitted in
Connector framework — architecture (arc42). Verification artefacts (test
directives) are emitted in Connector framework — verification.
ID |
Title |
Status |
Satisfies |
|---|---|---|---|
Connector framework |
open |
||
Envelope transport |
open |
||
Codec abstraction |
open |
||
Connector trait and routing |
open |
||
Connection lifecycle |
open |
||
Process boundary deployments |
open |
||
MQTT reference connector |
open |
||
Host wiring and builder |
open |
||
EtherCAT reference connector |
open |
||
Zenoh reference connector |
open |
||
Zenoh pub/sub |
open |
||
Zenoh queries |
open |
||
Zenoh session topology and health |
open |
||
CAN (SocketCAN) reference connector |
open |
||
CAN frame transport (classical + FD) |
open |
||
Multi-interface gateway and per-channel filtering |
open |
||
Bus health, error frames, and reconnect |
open |
ID |
Title |
Status |
Satisfies |
|---|---|---|---|
ConnectorEnvelope is a POD type |
open |
||
Per-channel max payload size |
open |
||
Sequence number monotonically increasing |
open |
||
Timestamp recorded at send |
open |
||
Correlation id is a passive carrier |
open |
||
Zero-copy publish via iceoryx2 loan |
open |
||
One iceoryx2 service per channel direction |
open |
||
PayloadCodec trait |
open |
||
Codec is a generic parameter on connectors |
open |
||
JsonCodec is the default codec |
open |
||
Codec encode error variant |
open |
||
Codec decode error variant |
open |
||
Connector trait |
open |
||
ChannelDescriptor carries typed routing |
open |
||
Routing is a marker trait with bounds |
open |
||
create_writer / create_reader return concrete handles |
open |
||
Connector ships its own routing struct |
open |
||
ConnectorHealth state machine |
open |
||
subscribe_health returns a Channel of HealthEvent |
open |
||
ReconnectPolicy trait |
open |
||
ExponentialBackoff default policy |
open |
||
HealthEvent emitted on every transition |
open |
||
Stack-internal-reconnect connectors emit health uniformly |
open |
||
Same envelope contract for both deployments |
open |
||
In-process gateway is a tokio task |
open |
||
Separate-process gateway is a self-contained binary |
open |
||
Clean exit on SIGINT / SIGTERM on both sides |
open |
||
No app↔gateway control-plane envelopes |
open |
||
MqttConnector implements Connector |
open |
||
MqttRouting carries topic, qos, retained |
open |
||
QoS 0 and 1 supported |
open |
||
Retained-message publish supported |
open |
||
Wildcard subscriptions supported |
open |
||
Username/password authentication |
open |
||
TLS is optional via cargo feature |
open |
||
MQTT 3.1.1 baseline |
open |
||
Tokio sidecar inside the gateway crate |
open |
||
Bridge channels are bounded |
open |
||
Outbound bridge saturation surfaces as BackPressure |
open |
||
Inbound bridge saturation surfaces as DroppedInbound HealthEvent |
open |
||
ConnectorHost builder API |
open |
||
ConnectorGateway builder API |
open |
||
Host registers connector items with the executor |
open |
||
Optional Observer adapter for tracing |
open |
||
NO request/response matching by the framework |
rejected |
||
NO app↔gateway control plane |
rejected |
||
NO persistent outbox or durable buffering |
rejected |
||
NO schema/contract enforcement across the boundary |
rejected |
||
NO protocol-portable Channel<T> |
rejected |
||
NO multi-broker / multi-tenant gateway |
rejected |
||
NO supervision / panic recovery |
rejected |
||
EthercatConnector implements Connector |
open |
||
EthercatRouting carries SubDevice and PDO addressing |
open |
||
Single MainDevice per gateway instance |
open |
||
Bus reaches OP before serving traffic |
open |
||
Static PDO mapping per SubDevice |
open |
||
PDO mapping applied during PRE-OP to SAFE-OP transition |
open |
||
Cycle time configurable with millisecond resolution |
open |
||
Missed cycle ticks are skipped not queued |
open |
||
Distributed Clocks bring-up is opt-in |
open |
||
Working-counter-based health policy |
open |
||
Working-counter mismatch degrades health |
open |
||
Tokio sidecar contained inside the connector crate |
open |
||
Bridge channels are bounded |
open |
||
Outbound bridge saturation surfaces as BackPressure |
open |
||
Inbound bridge saturation surfaces as DroppedInbound HealthEvent |
open |
||
Linux raw socket required on gateway host |
open |
||
Outbound payload written to PDI bit slice per routing |
open |
||
Inbound payload read from PDI bit slice per routing |
open |
||
Per-channel routing registry on the gateway |
open |
||
ZenohConnector implements Connector |
open |
||
ZenohRouting carries key_expr and pub/sub QoS fields |
open |
||
JsonCodec is the default codec for Zenoh |
open |
||
Tokio sidecar contained inside the Zenoh connector crate |
implemented |
||
Zenoh bridge channels are bounded |
open |
||
Outbound bridge saturation surfaces as BackPressure |
open |
||
Inbound bridge saturation surfaces as DroppedInbound |
open |
||
Zenoh zero-copy publish via iceoryx2 loan |
open |
||
Zenoh gateway is byte-only on the inbound publish path |
open |
||
ZenohConnector exposes create_querier and create_queryable |
open |
||
ZenohQuerier maps QueryId to envelope correlation_id |
open |
||
ZenohQueryable correlates replies via correlation_id |
open |
||
Multi-reply per query supported |
open |
||
Reply stream end-of-stream framed in payload |
open |
||
Query timeout sourced from options, overridable per-querier |
open |
||
terminate(id) finalizes the upstream zenoh::Query |
open |
||
Codec applied to Q on send and to R on reply |
open |
||
Reply-side inbound saturation emits DroppedInbound |
open |
||
Zenoh session mode is a config knob |
open |
||
NO ReconnectPolicy on Zenoh session loss |
rejected |
||
HealthEvent emitted on every Zenoh session transition |
implemented |
||
Connect and listen locators surfaced to zenoh::Config |
open |
||
zenoh-integration cargo feature gates the real zenoh dep |
implemented |
||
MockZenohSession ships unfeature-gated |
implemented |
||
Linux, macOS, and Windows are supported host operating systems |
implemented |
||
Pure parse function with no I/O |
open |
||
no_std + alloc compatible |
open |
||
quick-xml + serde backend |
open |
||
Parser does not depend on ethercrab or codegen |
open |
||
IR carries identity, PDO maps, mailbox, DC, and OD |
open |
||
Vendor-specific extensions captured as opaque blobs |
open |
||
Parse errors carry line and column |
open |
||
CodegenBackend trait shape |
open |
||
Naming policy is owned by codegen, not the backend |
open |
||
Revision collision handled deterministically |
open |
||
Common PDO entry types deduplicated |
open |
||
Emission target is proc_macro2 TokenStream |
open |
||
Backend crate is the sole ethercrab dependency |
open |
||
One device struct per ESI device entry |
open |
||
SubDeviceIdentity const emitted per device |
open |
||
PDO assignment alternatives emitted as sum type |
open |
||
One PDO struct per assignment alternative |
open |
||
Generated module root exposes a registry |
open |
||
Generated code compiles under no_std + alloc |
open |
||
EsiDevice trait shape |
open |
||
EsiConfigurable trait shape for preop bring-up |
open |
||
Traits live in ethercat-esi-rt, not taktora-connector |
open |
||
Object dictionary emission is a default-off cargo feature |
open |
||
Process image access via bitvec BitSlice |
open |
||
Builder API shape |
open |
||
Output written to OUT_DIR |
open |
||
Cargo rerun-if directives emitted per ESI input |
open |
||
Generated output passes through prettyplease |
open |
||
cargo esi expand emits one device's generated code |
open |
||
cargo esi list enumerates devices in a glob |
open |
||
CLI shares the parser and codegen crates |
open |
||
Verifier ingests ESI XML plus SII binary |
open |
||
Diagnostic output names the differing field |
open |
||
Verifier reuses the parser |
open |
||
Verifier exits non-zero on mismatch |
open |
||
NO CAN / CANopen / EDS support in this round |
rejected |
||
NO proc-macro front-end |
rejected |
||
NO unification of EtherCAT and CANopen runtime traits |
rejected |
||
NO runtime XML parsing |
rejected |
||
NO modification of taktora-connector-ethercat runtime |
rejected |
||
NO automatic vendor library scraping |
rejected |
Safety refinements¶
The connector framework carries five TSRs from the SEooC safety concept (see Technical Safety Concept — TSRs):
Compile-time channel direct... (TSR_0005) (compile-time channel directionality) — implemented by taktora-connector-core (BB_0001), taktora-connector-host (BB_0005).
Bounded health-event latency (TSR_0006) (bounded health-event latency) — implemented by Zenoh session mode is a con... (REQ_0440), NO ReconnectPolicy on Zenoh... (REQ_0441), HealthEvent emitted on ever... (REQ_0442), Connect and listen locators... (REQ_0443), zenoh-integration cargo fea... (REQ_0444).
Single-publisher iceoryx2 t... (TSR_0007) (single-publisher iceoryx2 topology for SC channels) — implemented (iceoryx2 default).
Envelope sequence + CRC int... (TSR_0008) (envelope sequence + CRC integrity) — draft; current
ConnectorEnvelope<N>carries aCorrelationIdbut no sequence or CRC.Cross-process hosting mode (TSR_0009) (cross-process hosting mode) — draft; requires per-process iceoryx2 segment capability wiring at the
ConnectorGatewaylayer. See Process boundary as spatial... (ADR_0050).