Safe API Framework
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:
  • c
  • 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
  • } sapi_ipc_config_shm_t;
  • User Configuration Example:
  • c
  • sapi_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
  • };
  • sapi_ipc_handle_t shm_channel;
  • sapi_ipc_create_shm(&shm_channel, &shm_config); // OS backend implements this
  • Implementation by OS Integrator (NOT in framework):
  • c
  • // POSIX backend implementation (example, not in framework)
  • sapi_status_t sapi_ipc_create_shm(sapi_ipc_handle_t *handle, * const sapi_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) {
  • return SAPI_STATUS_HARDWARE_FAULT;
  • }
  • // 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 = (sapi_ipc_handle_t)q;
  • return SAPI_STATUS_OK;
  • }
  • ### 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:
  • c
  • 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
  • } sapi_ipc_config_fifo_t;
  • User Configuration Example:
  • c
  • sapi_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
  • };
  • sapi_ipc_handle_t fifo_channel;
  • sapi_ipc_create_fifo(&fifo_channel, &fifo_config); // OS backend implements
  • Implementation by OS Integrator (example):
  • c
  • // POSIX backend
  • sapi_status_t sapi_ipc_create_fifo(sapi_ipc_handle_t *handle, * const sapi_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) {
  • return SAPI_STATUS_HARDWARE_FAULT;
  • }
  • }
  • // Open FIFO
  • int flags = config->blocking ? 0 : O_NONBLOCK;
  • int fd = open(config->fifo_path, O_RDWR | flags);
  • if (fd < 0) {
  • return SAPI_STATUS_HARDWARE_FAULT;
  • }
  • 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 = (sapi_ipc_handle_t)ch;
  • return SAPI_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:
  • c
  • 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
  • } sapi_ipc_config_tcp_t;
  • User Configuration Example (Client/Connect):
  • c
  • sapi_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
  • };
  • sapi_ipc_handle_t tcp_channel;
  • sapi_ipc_create_tcp(&tcp_channel, &tcp_config); // OS backend implements
  • User Configuration Example (Server/Listen):
  • c
  • sapi_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
  • };
  • sapi_ipc_handle_t tcp_server;
  • sapi_ipc_create_tcp(&tcp_server, &tcp_server_config);
  • Implementation by OS Integrator (example):
  • c
  • // POSIX/Linux backend
  • sapi_status_t sapi_ipc_create_tcp(sapi_ipc_handle_t *handle, * const sapi_ipc_config_tcp_t *config)
  • {
  • int sock = socket(AF_INET, SOCK_STREAM, 0);
  • if (sock < 0) {
  • return SAPI_STATUS_HARDWARE_FAULT;
  • }
  • 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);
  • return SAPI_STATUS_INVALID_PARAM;
  • }
  • if (config->server_mode) {
  • // Server: bind and listen
  • if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
  • return SAPI_STATUS_HARDWARE_FAULT;
  • }
  • listen(sock, 1);
  • } else {
  • // Client: connect
  • if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
  • return SAPI_STATUS_HARDWARE_FAULT;
  • }
  • }
  • tcp_channel_t *ch = malloc(sizeof(tcp_channel_t));
  • ch->socket = sock;
  • ch->timeout_ms = config->timeout_ms;
  • *handle = (sapi_ipc_handle_t)ch;
  • return SAPI_STATUS_OK;
  • }
  • ### 4. UDP Channels
  • Use: Low-latency network communication, best-effort
  • Configuration Structure:
  • c
  • 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
  • } sapi_ipc_config_udp_t;
  • User Configuration Example:
  • c
  • sapi_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
  • };
  • sapi_ipc_handle_t udp_channel;
  • sapi_ipc_create_udp(&udp_channel, &udp_config);

Creating Channels for Vital Communication

  • ### Example: Online/Standby via TCP/IP
  • Online Host Configuration:
  • c
  • // Primary channel: TCP to standby (reliable)
  • sapi_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)
  • sapi_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
  • sapi_ipc_handle_t tcp_ch, udp_ch;
  • sapi_ipc_create_tcp(&tcp_ch, &tcp_config);
  • sapi_ipc_create_udp(&udp_ch, &udp_config);
  • // Wrap in vital channel for 2oo2 voting
  • sapi_channel_config_t vital_cfg = {
  • .voting_strategy = SAPI_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 };
  • sapi_channel_init(&vital, &vital_cfg, channels, 2);
  • // Now ready for voting communication!
  • Standby Host Configuration:
  • c
  • // Listen for online connections
  • sapi_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
  • sapi_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)
  • sapi_ipc_handle_t tcp_ch, udp_ch;
  • sapi_ipc_create_tcp(&tcp_ch, &tcp_server_config);
  • sapi_ipc_create_udp(&udp_ch, &udp_server_config);
  • // Same vital channel config (but roles reversed in logic)
  • sapi_channel_init(&vital, &vital_cfg, channels, 2);

Dispatcher Callback Pattern

  • Since users might mix different transports, they implement a dispatcher:
  • c
  • // 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 {
  • sapi_ipc_handle_t tcp;
  • sapi_ipc_handle_t udp;
  • sapi_ipc_handle_t shm;
  • sapi_ipc_handle_t fifo;
  • } handle;
  • } channel_wrapper_t;
  • // Dispatcher for send (vital_channel will call this)
  • sapi_status_t app_backend_send(void *ch, const void *data, size_t size) {
  • channel_wrapper_t *wrapper = (channel_wrapper_t *)ch;
  • switch (wrapper->type) {
  • case CHANNEL_TYPE_TCP:
  • return sapi_ipc_send_tcp(wrapper->handle.tcp, data, size, 1000);
  • case CHANNEL_TYPE_UDP:
  • return sapi_ipc_send_udp(wrapper->handle.udp, data, size, 500);
  • case CHANNEL_TYPE_SHM:
  • return sapi_ipc_send_shm(wrapper->handle.shm, data, size, 100);
  • case CHANNEL_TYPE_FIFO:
  • return sapi_ipc_send_fifo(wrapper->handle.fifo, data, size, 500);
  • default:
  • return SAPI_STATUS_INVALID_PARAM;
  • }
  • }
  • // Same pattern for receive
  • sapi_status_t app_backend_recv(void *ch, void *data, size_t size, uint32_t timeout) {
  • channel_wrapper_t *wrapper = (channel_wrapper_t *)ch;
  • switch (wrapper->type) {
  • case CHANNEL_TYPE_TCP:
  • return sapi_ipc_recv_tcp(wrapper->handle.tcp, data, size, timeout);
  • case CHANNEL_TYPE_UDP:
  • return sapi_ipc_recv_udp(wrapper->handle.udp, data, size, timeout);
  • // ... etc
  • }
  • }

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:
  • - sapi_ipc_create_shm() - Create shared memory channel
  • - sapi_ipc_create_fifo() - Create FIFO channel
  • - sapi_ipc_create_tcp() - Create TCP channel
  • - sapi_ipc_create_udp() - Create UDP channel
  • - sapi_ipc_send_*() and sapi_ipc_recv_*() - I/O operations
  • - Backend 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
  • */