Safe API Framework 0.1.0 build 384e6ae
Layered API framework for safety-related applications (ERTMS RBC reference targeting CENELEC EN 50128 SIL 4)
Loading...
Searching...
No Matches
Channel Configuration Guide

Channel Configuration Pattern

The framework provides configuration structures for each channel type. Users fill in these configurations with OS-specific parameters, then the framework creates channels and integrates them with vital_channel.

Key Principle:** Framework defines the interface, user provides the parameters.

Supported Channel Types

1. Shared Memory Channels

Use:** Ultra-low latency, same CPU/board

OS-Specific Implementation Areas:**

  • POSIX: mmap() to shared memory region
  • QNX: mmap() or QNX shared memory
  • Linux: memfd_create() or /dev/shm files
  • RTOS: Memory mapping or inter-task memory

    Configuration Structure:**

    typedef struct {
    const char *name; // Diagnostic name
    const char *descriptor_path; // OS-specific path
    // POSIX: "/dev/shm/my_channel"
    // Linux: "/tmp/my_channel_shm"
    size_t message_size; // Size of each message
    size_t queue_depth; // How many messages to queue
    uint32_t timeout_ms; // Max wait time
    } rte_ipc_config_shm_t;

    User Configuration Example:**

    rte_ipc_config_shm_t shm_config = {
    .name = "vital_A_to_B",
    .descriptor_path = "/dev/shm/rail_vital_ab", // User provides path
    .message_size = sizeof(train_command_t), // 256 bytes
    .queue_depth = 10, // Buffer 10 messages
    .timeout_ms = 100, // 100ms timeout
    };
    rte_ipc_handle_t shm_channel;
    rte_ipc_create_shm(&shm_channel, &shm_config); // OS OSAdapter implements this
    struct rte_ipc_impl_s * rte_ipc_handle_t
    Opaque handle to a created IPC channel, returned by rte_ipc_create().
    Definition rte_ipc.h:34

    Implementation by OS Integrator (NOT in framework):**

    // POSIX OSAdapter implementation (example, not in framework)
    rte_status_t rte_ipc_create_shm(rte_ipc_handle_t *handle,
    const rte_ipc_config_shm_t *config)
    {
    // Create or open shared memory region
    int fd = shm_open(config->descriptor_path, O_CREAT | O_RDWR, 0666);
    if (fd < 0) {
    }
    // Set size and map
    size_t total_size = config->queue_depth * config->message_size;
    ftruncate(fd, total_size);
    void *mapped = mmap(NULL, total_size, PROT_READ | PROT_WRITE,
    MAP_SHARED, fd, 0);
    // Initialize queue structure and mutex
    shm_queue_t *q = (shm_queue_t *)mapped;
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(&q->mutex, &attr);
    *handle = (rte_ipc_handle_t)q;
    return RTE_STATUS_OK;
    }
    rte_status_t
    Common result/status codes.
    Definition rte_status.h:27
    @ RTE_STATUS_OK
    Definition rte_status.h:28
    @ RTE_STATUS_HARDWARE_FAULT
    Definition rte_status.h:36

2. FIFO (Named Pipes) Channels

Use:** Process isolation on same system, fallback transport

OS-Specific Implementation Areas:**

  • POSIX: mkfifo() and open()
  • Linux: /proc filesystem for FIFOs
  • Windows: Named pipes (CreateNamedPipe())
  • RTOS: Message queues (if no FIFO support)

    Configuration Structure:**

    typedef struct {
    const char *name; // Diagnostic name
    const char *fifo_path; // Full path to FIFO
    // POSIX: "/tmp/myfifo"
    // Windows: "\\\\.\\pipe\\myfifo"
    size_t message_size; // Size of each message
    uint32_t timeout_ms; // Max wait time
    bool blocking; // Blocking or non-blocking mode
    } rte_ipc_config_fifo_t;

    User Configuration Example:**

    rte_ipc_config_fifo_t fifo_config = {
    .name = "vital_A_to_B_fallback",
    .fifo_path = "/tmp/rail_vital_ab_fifo", // User provides path
    .message_size = sizeof(train_command_t),
    .timeout_ms = 500, // Longer timeout for FIFO
    .blocking = true, // Wait for data
    };
    rte_ipc_handle_t fifo_channel;
    rte_ipc_create_fifo(&fifo_channel, &fifo_config); // OS OSAdapter implements

    Implementation by OS Integrator (example):**

    // POSIX OSAdapter
    rte_status_t rte_ipc_create_fifo(rte_ipc_handle_t *handle,
    const rte_ipc_config_fifo_t *config)
    {
    // Create FIFO if it doesn't exist
    if (access(config->fifo_path, F_OK) != 0) {
    if (mkfifo(config->fifo_path, 0666) < 0) {
    }
    }
    // Open FIFO
    int flags = config->blocking ? 0 : O_NONBLOCK;
    int fd = open(config->fifo_path, O_RDWR | flags);
    if (fd < 0) {
    }
    fifo_channel_t *ch = malloc(sizeof(fifo_channel_t));
    ch->fd = fd;
    ch->message_size = config->message_size;
    ch->timeout_ms = config->timeout_ms;
    *handle = (rte_ipc_handle_t)ch;
    return RTE_STATUS_OK;
    }

3. TCP/IP Channels

Use:** Remote standby across network, reliable ordered delivery

OS-Specific Implementation Areas:**

  • POSIX/Linux: BSD sockets (socket(), connect(), send())
  • Windows: Winsock2
  • RTOS: Native TCP/IP stack or lwIP

    Configuration Structure:**

    typedef struct {
    const char *name; // Diagnostic name
    const char *remote_ip; // IP address of remote host
    // e.g., "192.168.1.100" or "standby.example.com"
    uint16_t remote_port; // Port number
    // e.g., 5000, 8080
    size_t message_size; // Max message size
    uint32_t timeout_ms; // Connect/send/receive timeout
    bool server_mode; // true = listen, false = connect
    } rte_ipc_config_tcp_t;

    User Configuration Example (Client/Connect):**

    rte_ipc_config_tcp_t tcp_config = {
    .name = "online_to_standby",
    .remote_ip = "192.168.1.100", // User provides standby IP
    .remote_port = 5000, // User chooses port
    .message_size = sizeof(train_command_t),
    .timeout_ms = 1000, // 1s timeout for network
    .server_mode = false, // Connect to remote
    };
    rte_ipc_handle_t tcp_channel;
    rte_ipc_create_tcp(&tcp_channel, &tcp_config); // OS OSAdapter implements

    User Configuration Example (Server/Listen):**

    rte_ipc_config_tcp_t tcp_server_config = {
    .name = "standby_listen",
    .remote_ip = "0.0.0.0", // Listen on all interfaces
    .remote_port = 5000, // User chooses port
    .message_size = sizeof(train_command_t),
    .timeout_ms = 1000,
    .server_mode = true, // Listen mode
    };
    rte_ipc_handle_t tcp_server;
    rte_ipc_create_tcp(&tcp_server, &tcp_server_config);

    Implementation by OS Integrator (example):**

    // POSIX/Linux OSAdapter
    rte_status_t rte_ipc_create_tcp(rte_ipc_handle_t *handle,
    const rte_ipc_config_tcp_t *config)
    {
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
    }
    struct sockaddr_in addr = {
    .sin_family = AF_INET,
    .sin_port = htons(config->remote_port),
    };
    if (inet_pton(AF_INET, config->remote_ip, &addr.sin_addr) <= 0) {
    close(sock);
    }
    if (config->server_mode) {
    // Server: bind and listen
    if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
    }
    listen(sock, 1);
    } else {
    // Client: connect
    if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
    }
    }
    tcp_channel_t *ch = malloc(sizeof(tcp_channel_t));
    ch->socket = sock;
    ch->timeout_ms = config->timeout_ms;
    *handle = (rte_ipc_handle_t)ch;
    return RTE_STATUS_OK;
    }
    @ RTE_STATUS_INVALID_PARAM
    Definition rte_status.h:29

4. UDP Channels

Use:** Low-latency network communication, best-effort

Configuration Structure:**

typedef struct {
const char *name; // Diagnostic name
const char *local_ip; // Bind to this IP (receiver)
// "0.0.0.0" = any interface
uint16_t local_port; // Local port to bind
const char *remote_ip; // Send to this IP
uint16_t remote_port; // Send to this port
size_t message_size; // Max datagram size
uint32_t timeout_ms; // Non-blocking typically
} rte_ipc_config_udp_t;

User Configuration Example:**

rte_ipc_config_udp_t udp_config = {
.name = "fast_heartbeat",
.local_ip = "192.168.1.50", // This host's IP
.local_port = 5001, // Listen on this port
.remote_ip = "192.168.1.100", // Send to standby
.remote_port = 5001, // Standby's port
.message_size = sizeof(heartbeat_t),
.timeout_ms = 0, // Non-blocking
};
rte_ipc_handle_t udp_channel;
rte_ipc_create_udp(&udp_channel, &udp_config);

Creating Channels for Vital Communication

Example: Online/Standby via TCP/IP

Online Host Configuration:**

// Primary channel: TCP to standby (reliable)
rte_ipc_config_tcp_t tcp_config = {
.name = "online_to_standby_tcp",
.remote_ip = "192.168.1.100", // Standby IP (user provides)
.remote_port = 5000, // Standby port (user chooses)
.message_size = 256,
.timeout_ms = 1000,
.server_mode = false, // Connect (client)
};
// Secondary channel: UDP (fast backup)
rte_ipc_config_udp_t udp_config = {
.name = "online_to_standby_udp",
.local_ip = "192.168.1.50", // Online IP (user provides)
.local_port = 5001,
.remote_ip = "192.168.1.100", // Standby IP
.remote_port = 5001,
.message_size = 256,
.timeout_ms = 500,
};
// Create channels
rte_ipc_handle_t tcp_ch, udp_ch;
rte_ipc_create_tcp(&tcp_ch, &tcp_config);
rte_ipc_create_udp(&udp_ch, &udp_config);
// Wrap in vital channel for 2oo2 voting
rte_channel_config_t vital_cfg = {
.voting_strategy = RTE_VOTING_2OO2,
.channel_timeout_ms = 1000,
.log_disagreements = true,
.backend_send = tcp_udp_dispatch_send, // Your dispatcher
.backend_recv = tcp_udp_dispatch_recv,
};
void *channels[2] = { &tcp_ch, &udp_ch };
rte_channel_init(&vital, &vital_cfg, channels, 2);
// Now ready for voting communication!
@ RTE_VOTING_2OO2
Definition rte_voter.h:44
Configuration for rte_channel_init().
Definition rte_channel.h:90

Standby Host Configuration:**

// Listen for online connections
rte_ipc_config_tcp_t tcp_server_config = {
.name = "standby_listen_tcp",
.remote_ip = "0.0.0.0", // Listen on all interfaces
.remote_port = 5000, // Must match online's config
.message_size = 256,
.timeout_ms = 1000,
.server_mode = true, // Listen (server)
};
// Listen for UDP
rte_ipc_config_udp_t udp_server_config = {
.name = "standby_listen_udp",
.local_ip = "192.168.1.100", // Standby IP (user provides)
.local_port = 5001,
.remote_ip = "192.168.1.50", // Send ACK back to online
.remote_port = 5001,
.message_size = 256,
.timeout_ms = 500,
};
// Create channels (same as online)
rte_ipc_handle_t tcp_ch, udp_ch;
rte_ipc_create_tcp(&tcp_ch, &tcp_server_config);
rte_ipc_create_udp(&udp_ch, &udp_server_config);
// Same vital channel config (but roles reversed in logic)
rte_channel_init(&vital, &vital_cfg, channels, 2);

Dispatcher Callback Pattern

Since users might mix different transports, they implement a dispatcher:

// Identify channel type (add to handle or use wrapper)
typedef enum {
CHANNEL_TYPE_TCP,
CHANNEL_TYPE_UDP,
CHANNEL_TYPE_SHM,
CHANNEL_TYPE_FIFO,
} channel_type_t;
typedef struct {
channel_type_t type;
union {
} handle;
// Dispatcher for send (vital_channel will call this)
rte_status_t app_backend_send(void *ch, const void *data, size_t size) {
switch (wrapper->type) {
case CHANNEL_TYPE_TCP:
return rte_ipc_send_tcp(wrapper->handle.tcp, data, size, 1000);
case CHANNEL_TYPE_UDP:
return rte_ipc_send_udp(wrapper->handle.udp, data, size, 500);
case CHANNEL_TYPE_SHM:
return rte_ipc_send_shm(wrapper->handle.shm, data, size, 100);
case CHANNEL_TYPE_FIFO:
return rte_ipc_send_fifo(wrapper->handle.fifo, data, size, 500);
default:
}
}
// Same pattern for receive
rte_status_t app_backend_recv(void *ch, void *data, size_t size, uint32_t timeout) {
switch (wrapper->type) {
case CHANNEL_TYPE_TCP:
return rte_ipc_recv_tcp(wrapper->handle.tcp, data, size, timeout);
case CHANNEL_TYPE_UDP:
return rte_ipc_recv_udp(wrapper->handle.udp, data, size, timeout);
// ... etc
}
}
static rte_status_t app_backend_send(void *channel, const void *data, size_t size)
Dispatcher send callback Routes to appropriate transport based on channel type.
static rte_status_t app_backend_recv(void *channel, void *data, size_t size, uint32_t timeout_ms)
Dispatcher receive callback Routes to appropriate transport based on channel type.

Configuration Parameter Summary

Shared Memory:**

  • descriptor_path: Where to create shared memory (user specifies)
  • message_size: Fixed message size (user specifies)
  • queue_depth: How many messages to buffer (user specifies)
  • timeout_ms: Wait timeout (user specifies)

    FIFO:**

  • fifo_path: Path to FIFO file (user specifies)
  • message_size: Fixed message size (user specifies)
  • timeout_ms: Wait timeout (user specifies)
  • blocking: true/false (user specifies)

    TCP/IP:**

  • remote_ip: Destination IP address (user specifies)
  • remote_port: Destination port (user specifies)
  • message_size: Max message size (user specifies)
  • timeout_ms: Connect/send/receive timeout (user specifies)
  • server_mode: Listen (true) or connect (false) (user specifies)

    UDP:**

  • local_ip: Bind address (user specifies)
  • local_port: Bind port (user specifies)
  • remote_ip: Send-to address (user specifies)
  • remote_port: Send-to port (user specifies)
  • message_size: Max datagram size (user specifies)
  • timeout_ms: Non-blocking if 0 (user specifies)

Implementation Responsibility

Framework provides:**

  • Configuration structures (what to configure)
  • Vital channel voting layer
  • Example dispatcher patterns
  • Interface definitions (what to implement)

    OS Integrator implements:**

  • rte_ipc_create_shm() - Create shared memory channel
  • rte_ipc_create_fifo() - Create FIFO channel
  • rte_ipc_create_tcp() - Create TCP channel
  • rte_ipc_create_udp() - Create UDP channel
  • rte_ipc_send_*() and rte_ipc_recv_*() - I/O operations
  • OSAdapter dispatcher callbacks (optional, user can write)

    User/Integrator provides:**

  • Configuration values (IPs, ports, paths)
  • Dispatcher implementation (which channel for which transport)
  • Channel lifecycle (create, use, destroy)

This separation ensures:

  • ✓ Framework stays OS-agnostic
  • ✓ Framework stays transport-agnostic
  • ✓ Users have full control over configuration
  • ✓ Easy to adapt to new OS/transports