Routing detailed design

Each routing message uses an 8-byte header followed by its payload:

  • 4 bytes: message ID (big endian)

  • 4 bytes: payload length in bytes (big endian)

  • N bytes: payload

RxAdapter or PduTransportRxAdapter extracts PDU(s) from a payload according to its RxAdapterTable and implements the IReader interface. The RxAdapterTable determines which PDUs a payload contains with the message ID and the payload length. For each PDU, it defines a length and an offset in bytes, i.e., the position of the PDU in the payload.

// The sizes below are expressed as a function of:
//  - N: the number of known PDUs (>= P)
//  - P: the number of known messages
struct RxAdapterTable
{
    // the first internal PDU ID of this channel. All other PDU IDs are derived from it.
    ::etl::be_uint32_t firstId = {};
    // The message IDs that can be received.
    // The index is called messageIndex. It is shared with messageLengths and pduLengthsOffsets.
    // size: P
    ::etl::span<::etl::be_uint32_t const> messageIds;
    // The lengths of the messages that can be received. If the length of a message
    // does not match the expected one, no PDU is extracted. If empty, it is ignored.
    // size: P
    ::etl::span<::etl::be_uint32_t const> messageLengths;
    // A pointer into the PDULengths and PDUOffsets tables.
    // size: P + 1
    ::etl::span<::etl::be_uint32_t const> pduLengthsOffsets;
    // The length of each PDU in each frame.
    // The index is called channelSpecificPduId. It is shared with pduOffsets and
    // used to derive the internal PDU ID by adding it to firstId.
    // size: N
    ::etl::span<::etl::be_uint32_t const> pduLengths;
    // The offset in each frame of each PDU.
    // size: N
    ::etl::span<::etl::be_uint32_t const> pduOffsets;
};

Router reads PDUs from channels using IReader interfaces and writes them to channels using IWriter interfaces. Its PduRoutingTable specifies to which channel(s) a PDU should be forwarded as well as the outgoing message ID.

/**
 * The input of the routing is the output of the adapters, i.e. Internal PDU IDs.
 *
 * The routing table consists of two parts:
 *
 * 1) Routing to destinations.
 *    The internal PDU ID is used as index into destinationOffsets. The range
 *    destinations[destinationOffsets[pduId], destinationOffsets[pduId + 1][ are the
 *    destination channel indices to which the PDU is routed.
 *
 * 2) Translation from internal PDU ID to destination channel specific addressing.
 *    The index of a routing in the destinations table is also used in the outputMessageIds table.
 *    This contains the message ID to output on the channel for this message.
 *
 * The sizes of the spans are expressed according to:
 *  - N: the number of known PDUs
 *  - M: the number of channels
 */
struct PduRoutingTable
{
    // size: [N,(M*N)]
    ::etl::span<uint8_t const> destinations;

    // size: N+1
    ::etl::span<::etl::be_uint32_t const> destinationOffsets;

    // size: [N, (M*N)]
    ::etl::span<::etl::be_uint32_t const> outputMessageIds;
};

LegacyTxAdapter or PduTransportTxAdapter creates outgoing messages out of PDUs according to its TxAdapterTable and implements the IWriter interface. For each message, the TxAdapterTable determines the payload length and the PDU offset within it.

// The sizes below are expresed as a function of:
//  - N: the number of known PDUs
struct TxAdapterTable
{
    // The message IDs which can be sent by this adapter
    // size: N
    ::etl::span<::etl::be_uint32_t const> messageIds;
    // The length of each message to be sent, achieved using padding with 0xFF.
    // size: N
    ::etl::span<::etl::be_uint32_t const> messageLengths;
    // The offset of the PDU to be sent in the message.
    // size: N
    ::etl::span<::etl::be_uint32_t const> pduOffsets;
};

Routing example

The diagram below shows a concrete routing path through the tables. A message arriving on PDU_TRANSPORT0 with ID 18 is routed to CAN0 (outgoing ID 1), which in turn routes to PDU_TRANSPORT1 (outgoing ID 4096), which loops back to PDU_TRANSPORT0 (outgoing ID 256). This illustrates how the routing module chains multi-hop routes across CAN and PDU transport channels using only ID-based table lookups.

The diagram is generated with:

cd tools
python -m blob.routing visualize --channel PDU_TRANSPORT0 --id 18 \
    libs/bsw/routing/test/routing.jsonl
digraph Routing {
  graph[fontname="Arial" fontsize="10" ranksep=1.0 nodesep=1.0 label="routing graph for input of Id: 18 on Channel: PDU_TRANSPORT0"]
    "Input" -> "PDU_TRANSPORT0" [label = "Id: 18"]
    "PDU_TRANSPORT0" -> "CAN0" [label = "Id: 1"]
    "CAN0" -> "PDU_TRANSPORT1" [label = "Id: 4096"]
    "PDU_TRANSPORT1" -> "PDU_TRANSPORT0" [label = "Id: 256"]
}