Building block view

arc42 §5.

The framework decomposes into five workspace crates plus reuse of two existing taktora-executor crates. The decomposition is hierarchical: a level-1 view shows crate-level building blocks; level-2 zooms into the two crates that carry the most logic.

Building Block: taktora-connector-core BB_0001
status: open
is refined by: ARCH_0002
implements: REQ_0220, REQ_0221, REQ_0222
is implemented by: IMPL_0010
links incoming: REQ_0221

Pure trait definitions and shared types. No IPC, no protocol code. Public surface: Connector trait, PayloadCodec trait, Routing marker, ChannelDescriptor<R, const N: usize>, ConnectorHealth, HealthEvent, ReconnectPolicy, ExponentialBackoff, ConnectorError.

Building Block: taktora-connector-transport-iox BB_0002
status: open
is refined by: ARCH_0002
implements: REQ_0200, REQ_0205, REQ_0206
is implemented by: IMPL_0020
links incoming: RISK_0002, REQ_0205

Concrete envelope (ConnectorEnvelope<const N: usize>) and iceoryx2-backed channel handles (ChannelWriter<T, C, N>, ChannelReader<T, C, N>, ServiceFactory). Depends on taktora-connector-core, iceoryx2, taktora-executor.

Building Block: taktora-connector-codec BB_0003
status: open
is refined by: ARCH_0002, ARCH_0030
implements: REQ_0210, REQ_0212
is implemented by: IMPL_0030
links incoming: REQ_0210, REQ_0212

Concrete PayloadCodec implementations. JsonCodec ships default-on; MsgPackCodec and ProtoCodec are deferred behind cargo features.

Building Block: taktora-connector-mqtt BB_0004
status: open
is refined by: ARCH_0002
implements: REQ_0250, REQ_0251, REQ_0258

MQTT plugin (MqttConnector<C> implementing Connector) and gateway (MqttGateway exposing executable items). Hosts the tokio sidecar driving rumqttc::EventLoop and the bridge between taktora-executor and tokio.

Building Block: taktora-connector-host BB_0005
status: open
is refined by: ARCH_0002, ARCH_0032, ARCH_0013
implements: REQ_0270, REQ_0271, REQ_0272
is implemented by: IMPL_0040

Composition layer. Provides ConnectorHost::builder() and ConnectorGateway::builder() wrapping a taktora_executor::Executor. Optional Observer adapter to taktora-executor-tracing lives behind a tracing cargo feature.

Architecture View: Level-1 building block decomposition ARCH_0002
status: open

Crate-level building blocks and their dependency graph. All edges point from depender to dependee. The graph is acyclic; the host is the only consumer of every other new crate. The taktora-connector-ethercat crate (BB_0030) is a peer of taktora-connector-mqtt (BB_0004) — both depend on the same core / transport / codec triad and feed the host.

        flowchart TB
  subgraph existing_crates[existing crates]
    EX[taktora-executor]
    TR[taktora-executor-tracing]
  end
  subgraph new_crates["new crates (this spec)"]
    CO[taktora-connector-core<br/>BB_0001]
    TX[taktora-connector-transport-iox<br/>BB_0002]
    CD[taktora-connector-codec<br/>BB_0003]
    MQ[taktora-connector-mqtt<br/>BB_0004]
    EC[taktora-connector-ethercat<br/>BB_0030]
    ZE[taktora-connector-zenoh<br/>BB_0040]
    HO[taktora-connector-host<br/>BB_0005]
  end
  CO --> TX
  CO --> CD
  CO --> MQ
  CO --> EC
  TX --> MQ
  TX --> EC
  CD --> MQ
  CD --> EC
  EX --> TX
  EX --> MQ
  EX --> EC
  CO --> HO
  TX --> HO
  CD --> HO
  MQ --> HO
  EC --> HO
  CO --> ZE
  TX --> ZE
  CD --> ZE
  EX --> ZE
  ZE --> HO
  TR -.optional adapter.-> HO
    
Building Block: ConnectorEnvelope (sub-block of BB_0002) BB_0010
status: open
is verified by: TEST_0124
links incoming: RISK_0003, REQ_0202, REQ_0203, REQ_0204

The on-wire form. #[repr(C)] POD type with a fixed header (sequence number, timestamp, length, correlation id) and a const-generic-sized payload buffer.

#[repr(C)]
#[derive(Debug, Copy, Clone, ZeroCopySend)]
pub struct ConnectorEnvelope<const N: usize> {
    pub sequence_number: u64,
    pub timestamp_ns:    u64,
    pub payload_length:  u32,
    pub _reserved:       u32,
    pub correlation_id:  [u8; 32],
    pub payload:         [u8; N],
}

At plan stage, the implementation may substitute a small set of size-tier types (4 KB / 64 KB / 1 MB) for the const-generic variant. The external contract — fixed at service-creation time — is identical either way.

Building Block: ServiceFactory (sub-block of BB_0002) BB_0011
status: open
implements: REQ_0206
is verified by: TEST_0126
links incoming: REQ_0206

Derives iceoryx2 service names deterministically from a ChannelDescriptor and creates the publisher / subscriber / event-service pairs for each direction.

out service:    taktora.connector.<connector>.<channel>.out
in  service:    taktora.connector.<connector>.<channel>.in
out event:      taktora.connector.<connector>.<channel>.out.evt
in  event:      taktora.connector.<connector>.<channel>.in.evt
Building Block: MqttConnector (sub-block of BB_0004, plugin side) BB_0020
status: open
implements: REQ_0250, REQ_0251

MqttConnector<C: PayloadCodec>. Implements Connector with type Routing = MqttRouting. create_writer / create_reader build ServiceFactory-backed channel handles; health() reads the gateway’s status snapshot.

Building Block: MqttGateway (sub-block of BB_0004, gateway side) BB_0021
status: open
links incoming: RISK_0001, RISK_0005

Hosts rumqttc::AsyncClient + EventLoop on a tokio runtime, plus the bridge channels and the executable items (OutboundGatewayItem, InboundGatewayItem) registered with taktora-executor.

Building Block: Tokio bridge (sub-block of BB_0021) BB_0022
status: open
is refined by: ARCH_0010, ARCH_0011
implements: REQ_0259, REQ_0260, REQ_0261
is verified by: TEST_0160
links incoming: RISK_0004

Two bounded channel pairs that translate between taktora-executor’s thread (WaitSet driver) and the tokio runtime owning rumqttc. Outbound = tokio::sync::mpsc; inbound = crossbeam_channel wired as a taktora-executor signal source.

Building Block: taktora-connector-ethercat BB_0030
status: open
is refined by: ARCH_0002
is implemented by: IMPL_0050

EtherCAT plugin (EthercatConnector<C> implementing Connector) and gateway (EthercatGateway exposing executable items). Hosts the tokio sidecar driving ethercrab’s tx_rx_task and the bridge between taktora-executor and tokio. Depends on taktora-connector-core, taktora-connector-transport-iox, ethercrab, taktora-executor.

Building Block: EthercatConnector (sub-block of BB_0030, plugin side) BB_0031
status: open
implements: REQ_0310, REQ_0311
links incoming: REQ_0311

Plugin-side EthercatConnector<C: PayloadCodec>. Owns no I/O — produces ChannelWriter / ChannelReader handles whose EthercatRouting (SubDevice configured address, PDO direction, bit offset within the SubDevice’s process data, bit length of the mapped object) identifies one process-data slice. Acts as a compile-time-checked façade over the gateway’s SHM services.

Building Block: EthercatGateway (sub-block of BB_0030, gateway side) BB_0032
status: open
is refined by: ARCH_0040, ARCH_0041, ARCH_0042
implements: REQ_0312, REQ_0313, REQ_0325

Gateway-side executable item that owns the ethercrab MainDevice and PduStorage on one Linux network interface. Brings the bus from INIT through PRE-OP and SAFE-OP to OP via the typestate init_single_group / into_op API before serving plugin traffic. Opens the NIC via ethercrab::std::tx_rx_task; requires CAP_NET_RAW.

Building Block: PDO mapping (sub-block of BB_0030) BB_0033
status: open
is refined by: ARCH_0040
implements: REQ_0314, REQ_0315
links incoming: REQ_0315, REQ_0853

Module that accepts a static PDO-mapping description per SubDevice from EthercatConnectorOptions and applies it via SDO writes to the sync-manager assignment indices 0x1C12 (RxPDO) and 0x1C13 (TxPDO) during the PRE-OP → SAFE-OP transition. No ESI or EEPROM parsing.

Building Block: Tokio bridge for ethercrab (sub-block of BB_0030) BB_0034
status: open
is refined by: ARCH_0041
implements: REQ_0322, REQ_0323, REQ_0324
links incoming: REQ_0324

Two bounded channel pairs that translate between taktora-executor’s WaitSet thread and the tokio runtime owning ethercrab’s tx_rx_task. Outbound saturation surfaces as ConnectorError::BackPressure plus ConnectorHealth::Degraded; inbound saturation emits HealthEvent::DroppedInbound { count } and drops the inbound process image for the affected cycle.

Building Block: taktora-connector-zenoh BB_0040
status: open
is refined by: ARCH_0002
is implemented by: IMPL_0060
links incoming: REQ_0444, REQ_0445, REQ_0446

Zenoh plugin (ZenohConnector<C> implementing Connector) and gateway (ZenohGateway exposing executable items). Hosts the tokio sidecar driving zenoh::Session and the bridge between taktora-executor and tokio. Depends on taktora-connector-core, taktora-connector-transport-iox, taktora-connector-codec, taktora-executor, and (behind the zenoh-integration feature) zenoh.

Building Block: ZenohConnector (sub-block of BB_0040, plugin side) BB_0041
status: open
implements: REQ_0400, REQ_0401, REQ_0420

Plugin-side ZenohConnector<C: PayloadCodec>. Implements Connector with type Routing = ZenohRouting and adds concrete non-trait methods create_querier / create_queryable. Owns no I/O — produces ChannelWriter / ChannelReader / ZenohQuerier / ZenohQueryable handles whose ZenohRouting identifies a Zenoh key expression and the pub/sub QoS knobs. Acts as a compile-time-checked façade over the gateway’s SHM services.

Building Block: ZenohGateway (sub-block of BB_0040, gateway side) BB_0042
status: open
is refined by: ARCH_0043
links incoming: REQ_0403, REQ_0426, REQ_0440, REQ_0442

Gateway-side executable item that owns one zenoh::Session created via zenoh::open(config) (or a MockZenohSession when zenoh-integration is off — both implement the ZenohSessionLike trait). Maintains a per-channel routing registry mapping each open ChannelDescriptor to its declared Zenoh primitive (publisher / subscriber / queryable), and a correlation_id zenoh::Query map for in-flight queryable reply streams. Translates session-alive ↔ session-closed transitions into HealthEvent``s without using ``ReconnectPolicy.

Building Block: Zenoh query handles (sub-block of BB_0041) BB_0043
status: open
is refined by: ARCH_0043
links incoming: REQ_0420, REQ_0422, REQ_0423

ZenohQuerier<Q, R, C, N> and ZenohQueryable<Q, R, C, N>. The non-trait query handle types. ZenohQuerier::send mints a QueryId, encodes Q via the connector’s codec, and publishes on the channel’s {name}.query.out iceoryx2 service; try_recv drains {name}.reply.in and decodes the 1-byte frame discriminator (0x01=data, 0x02=EoS, 0x03=timeout) plus the codec-encoded R chunk. ZenohQueryable::try_recv surfaces (QueryId, Q); reply stamps the QueryId back onto a reply envelope and publishes on {name}.reply.out; terminate(id) publishes a 0x02 envelope finalising the upstream zenoh::Query.

Building Block: Tokio bridge for zenoh (sub-block of BB_0042) BB_0044
status: open
links incoming: REQ_0403

Two bounded channel pairs that translate between taktora-executor’s WaitSet thread and the tokio runtime owning zenoh::Session. Outbound saturation surfaces as ConnectorError::BackPressure plus ConnectorHealth::Degraded; inbound saturation emits HealthEvent::DroppedInbound { count } and drops the offending sample or reply chunk. Same shape as Tokio bridge for ethercrab ... (BB_0034) (EtherCAT) and Tokio bridge (sub-block of ... (BB_0022) (MQTT).

Building Block: taktora-connector-can crate BB_0070
status: open
is implemented by: IMPL_0080
links incoming: TEST_0511, TEST_0512

CAN plugin (CanConnector<C> implementing Connector) and gateway (CanGateway exposing executable items). Hosts the tokio sidecar driving N SocketCAN sockets and the bridges between taktora-executor and tokio. Depends on taktora-connector-core, taktora-connector-transport-iox, taktora-connector-codec, taktora-executor, and (behind the socketcan-integration feature) socketcan with its tokio feature enabled. Ships MockCanInterface unfeature-gated for layer-1 tests on any host OS.

Building Block: CanConnector (sub-block of BB_0070, plugin side) BB_0071
status: open

Plugin-side CanConnector<C: PayloadCodec>. Implements Connector with type Routing = CanRouting. Owns no I/O — produces ChannelWriter<T, C, N> / ChannelReader<T, C, N> handles whose CanRouting declares the target interface, CAN ID, mask, frame kind, and FD flags. Validates that CanRouting::iface belongs to the configured gateway’s interface set and that ChannelDescriptor::max_payload_size matches CanRouting::kind (8 for Classical, 64 for FD) before any iceoryx2 service is created. Acts as a compile-time-checked façade over the gateway’s SHM services.

Building Block: CanGateway (sub-block of BB_0070, gateway side) BB_0072
status: open

Gateway-side executable item that owns one CanInterfaceLike per configured interface (real socketcan::CanSocket / CanFdSocket when socketcan-integration is on, MockCanInterface otherwise — both implement CanInterfaceLike). For each interface, runs an RX task draining the socket and a TX drain consuming the outbound bridge. Maintains a per-interface routing registry mapping each open ChannelDescriptor to its CanRouting and direction. Aggregates per-interface sub-states into the externally-visible ConnectorHealth via worst-of (ConnectorHealth aggregates ... (REQ_0630)), enables CAN_ERR_FLAG on every owned socket, classifies error frames internally (Error frames consumed inter... (REQ_0631)), and never forwards error frames to plugin channels (Error frames not exposed to... (REQ_0636), NO plugin-visible error-fra... (REQ_0643)).

Building Block: Tokio bridge for CAN (sub-block of BB_0072) BB_0073
status: open
is refined by: ARCH_0060

Two bounded channel pairs per owned interface that translate between taktora-executor’s WaitSet thread and the tokio runtime owning the SocketCAN sockets. Outbound saturation surfaces as ConnectorError::BackPressure plus ConnectorHealth::Degraded; inbound saturation emits HealthEvent::DroppedInbound { count } and drops the offending CAN frame. Same shape as Tokio bridge for zenoh (sub... (BB_0044) (Zenoh), Tokio bridge for ethercrab ... (BB_0034) (EtherCAT), and Tokio bridge (sub-block of ... (BB_0022) (MQTT).

Building Block: Per-iface filter compiler (sub-block of BB_0072) BB_0074
status: open
is refined by: ARCH_0061
implements: REQ_0622, REQ_0623, REQ_0624

Pure-logic helper that maps the per-interface registry of inbound CanRouting entries to a single Vec<libc::can_filter> (or the socketcan crate’s equivalent newtype) and applies it via setsockopt(SOL_CAN_RAW, CAN_RAW_FILTER, …). Recomputed whenever a reader is created or dropped on the affected interface; the recompute does not require the socket to be re-opened or the bus to leave its current state. Symmetric counterpart for the inbound demux side: given a received frame, returns the list of registered readers whose (can_id, mask, extended) matches under kernel CAN_RAW_FILTER semantics so that every matching reader gets its own envelope copy (Inbound demux to all matchi... (REQ_0624)).

Building Block: MockCanInterface (sub-block of BB_0070) BB_0075
status: open
implements: REQ_0604

In-process loopback implementation of CanInterfaceLike, shipping in the default build (not gated by socketcan-integration). Sends queued for transmission on a mock interface are immediately delivered to any reader whose filter matches; programmable error-frame injection drives the CanGateway (sub-block of BB... (BB_0072) gateway’s health classifier under test. Exists so the Layer-1 test pyramid can exercise the full envelope ↔ interface ↔ envelope hop on Linux, macOS, and Windows without depending on the real socketcan crate or a Linux kernel CAN module. Mirrors taktora-connector-zenoh (BB_0040)’s MockZenohSession posture under MockZenohSession ships unfe... (REQ_0445).