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
vital_channel_dual_transport.c
Go to the documentation of this file.
1
29
30#include <stdint.h>
31#include <stdbool.h>
32#include <string.h>
33#include <stdio.h>
34#include <stdlib.h>
35
36#include "rte/channel_link/rte_channel.h"
37#include "rte/voter/rte_voter.h"
38#include "rte/status/rte_status.h"
39
40/* ============================================================================
41 * Transport OSAdapter 1: Shared Memory Queue (Simulated)
42 * ========================================================================== */
43
44typedef struct {
45 uint32_t write_idx;
46 uint32_t read_idx;
47 uint32_t count;
48 uint8_t data[10][256]; /* Queue of 10 messages, each 256 bytes */
50
51/* For example purposes: one shared queue per channel */
52static shm_queue_t shm_queue_0 = {0};
53
54static rte_status_t shm_send(shm_queue_t *q, const void *data, size_t size)
55{
56 if (q->count >= 10) {
57 return RTE_STATUS_TIMEOUT; /* Queue full */
58 }
59
60 if (size > 256) {
62 }
63
64 memcpy(q->data[q->write_idx], data, size);
65 q->write_idx = (q->write_idx + 1) % 10;
66 q->count++;
67
68 return RTE_STATUS_OK;
69}
70
71static rte_status_t shm_recv(shm_queue_t *q, void *data, size_t size)
72{
73 if (q->count == 0) {
74 return RTE_STATUS_TIMEOUT; /* Queue empty */
75 }
76
77 if (size < 256) {
79 }
80
81 memcpy(data, q->data[q->read_idx], 256);
82 q->read_idx = (q->read_idx + 1) % 10;
83 q->count--;
84
85 return RTE_STATUS_OK;
86}
87
88/* ============================================================================
89 * Transport OSAdapter 2: TCP/IP Socket (Simulated)
90 * ========================================================================== */
91
92typedef struct {
93 int socket;
94 uint32_t packets_sent;
95 uint32_t packets_recv;
96 bool connected;
98
99static tcp_channel_t tcp_ch_0 = {.socket = 0, .connected = true};
100
101static rte_status_t tcp_send(tcp_channel_t *ch, const void *data, size_t size)
102{
103 if (ch->socket < 0 || !ch->connected) {
105 }
106
107 if (size > 256) {
109 }
110
111 /* In real implementation:
112 * - Add message framing header (length prefix)
113 * - Call socket send() with timeout
114 * - Check for EAGAIN (queue full)
115 */
116
117 ch->packets_sent++;
118 return RTE_STATUS_OK;
119}
120
121static rte_status_t tcp_recv(tcp_channel_t *ch, void *data, size_t size)
122{
123 if (ch->socket < 0 || !ch->connected) {
125 }
126
127 if (size < 256) {
129 }
130
131 /* In real implementation:
132 * - Call socket recv() with timeout
133 * - Parse message framing
134 * - Handle partial receives
135 */
136
137 ch->packets_recv++;
138 return RTE_STATUS_OK;
139}
140
141/* ============================================================================
142 * Multi-Transport OSAdapter Callbacks (each rte_channel_t uses one of these)
143 * ========================================================================== */
144
151typedef enum {
152 CHANNEL_TYPE_SHARED_MEMORY,
153 CHANNEL_TYPE_TCP
154} channel_type_t;
155
156typedef struct {
157 channel_type_t type;
158 union {
159 shm_queue_t *shm;
160 tcp_channel_t *tcp;
161 } impl;
163
164static multi_transport_channel_t g_channel_0 = {
165 .type = CHANNEL_TYPE_SHARED_MEMORY,
166 .impl.shm = &shm_queue_0,
167};
168
169static multi_transport_channel_t g_channel_1 = {
170 .type = CHANNEL_TYPE_TCP,
171 .impl.tcp = &tcp_ch_0,
172};
173
180static rte_status_t channel_backend_send(void *channel_handle, const void *data, size_t size)
181{
183
184 if (ch == NULL) {
186 }
187
188 switch (ch->type) {
189 case CHANNEL_TYPE_SHARED_MEMORY:
190 return shm_send(ch->impl.shm, data, size);
191
192 case CHANNEL_TYPE_TCP:
193 return tcp_send(ch->impl.tcp, data, size);
194
195 default:
197 }
198}
199
206static rte_status_t channel_backend_recv(void *channel_handle, void *data, size_t size,
207 uint32_t timeout_ms __attribute__((unused)))
208{
210
211 if (ch == NULL) {
213 }
214
215 switch (ch->type) {
216 case CHANNEL_TYPE_SHARED_MEMORY:
217 return shm_recv(ch->impl.shm, data, size);
218
219 case CHANNEL_TYPE_TCP:
220 return tcp_recv(ch->impl.tcp, data, size);
221
222 default:
224 }
225}
226
227/* ============================================================================
228 * Application Example: Train Command Distribution
229 * ========================================================================== */
230
234typedef struct {
235 uint32_t command_id;
236 uint32_t speed_limit_kmh;
237 uint32_t target_distance_m;
238 uint8_t emergency_stop;
240
241#define CHANNEL_COUNT 2U
242
252 rte_voter_t *voter)
253{
254 static void * const osadapter_handles[CHANNEL_COUNT] = { &g_channel_0, &g_channel_1 };
255 rte_voter_config_t voter_cfg = {0};
256 rte_status_t rc;
257 uint32_t i;
258
260 voter_cfg.channel_timeout_ms = 1000;
261 voter_cfg.log_disagreements = true;
262 voter_cfg.on_disagreement = NULL;
263 voter_cfg.disagreement_context = NULL;
264
265 rc = rte_voter_init(voter, &voter_cfg);
266 if (rc != RTE_STATUS_OK) {
267 printf("ERROR: Failed to initialize voter: %d\n", rc);
268 return rc;
269 }
270
271 for (i = 0; i < CHANNEL_COUNT; i++) {
272 rte_channel_config_t chan_cfg = {0};
273
274 chan_cfg.channel_handle = osadapter_handles[i];
275 chan_cfg.send = channel_backend_send;
276 chan_cfg.recv = channel_backend_recv;
277
278 rc = rte_channel_init(&channels[i], &chan_cfg);
279 if (rc != RTE_STATUS_OK) {
280 printf("ERROR: Failed to initialize channel %u: %d\n", i, rc);
281 return rc;
282 }
283
284 rc = rte_voter_register_channel(voter, &channels[i]);
285 if (rc != RTE_STATUS_OK) {
286 printf("ERROR: Failed to register channel %u: %d\n", i, rc);
287 return rc;
288 }
289 }
290
291 printf("Voter initialized with 2oo2 voting over 2 channels\n");
292 printf(" Channel 0: Shared Memory (local standby)\n");
293 printf(" Channel 1: TCP/IP (remote standby)\n");
294 return RTE_STATUS_OK;
295}
296
301{
302 printf("\n>>> Broadcasting train command:\n");
303 printf(" Command ID: %u\n", cmd->command_id);
304 printf(" Speed Limit: %u km/h\n", cmd->speed_limit_kmh);
305 printf(" Distance: %u m\n", cmd->target_distance_m);
306 printf(" E-Stop: %s\n", cmd->emergency_stop ? "YES" : "NO");
307
308 rte_status_t rc = rte_voter_send(voter, cmd, sizeof(*cmd));
309 if (rc != RTE_STATUS_OK) {
310 printf("Send failed: %d (channel fault detected)\n", rc);
311 return rc;
312 }
313
314 printf("Sent to both channels (atomic broadcast)\n");
315 return RTE_STATUS_OK;
316}
317
322{
323 rte_voting_result_t vote_result;
324 size_t bytes_received = 0;
325
326 printf("\n<<< Receiving train command (with voting)...\n");
327
328 rte_status_t rc = rte_voter_receive(voter, cmd_out, sizeof(*cmd_out), &vote_result,
329 &bytes_received);
330
331 if (rc != RTE_STATUS_OK) {
332 printf("Receive failed: %d (voting result: %d)\n", rc, vote_result);
333 return rc;
334 }
335
336 printf("Both channels agreed on data:\n");
337 printf(" Command ID: %u\n", cmd_out->command_id);
338 printf(" Speed Limit: %u km/h\n", cmd_out->speed_limit_kmh);
339 return RTE_STATUS_OK;
340}
341
345static void monitor_channel_health(rte_channel_t channels[CHANNEL_COUNT], rte_voter_t *voter)
346{
347 printf("\n=== Channel Health Report ===\n");
348
349 for (uint32_t i = 0; i < CHANNEL_COUNT; i++) {
351 rte_status_t rc = rte_channel_get_health(&channels[i], &health);
352
353 if (rc != RTE_STATUS_OK) {
354 printf(" Channel %u: Error reading health\n", i);
355 continue;
356 }
357
358 printf(" Channel %u:\n", i);
359 printf(" Healthy: %s\n", health.is_healthy ? "YES" : "NO");
360 printf(" Sends: %u (errors: %u)\n", health.send_count, health.send_error_count);
361 printf(" Recvs: %u (errors: %u)\n", health.receive_count, health.receive_error_count);
362 }
363
364 uint32_t healthy_count = 0;
365 uint32_t total_disagreements = 0;
366 rte_voter_get_aggregated_health(voter, &healthy_count, &total_disagreements);
367 printf(" Total healthy: %u/%u\n", healthy_count, CHANNEL_COUNT);
368 printf(" Total disagreements: %u\n", total_disagreements);
369
370 if (healthy_count < CHANNEL_COUNT) {
371 printf(" WARNING: Redundancy compromised!\n");
372 }
373}
374
378int main(void)
379{
380 printf("========================================\n");
381 printf("Voted Channel: Dual Transport Example\n");
382 printf("========================================\n");
383
384 rte_channel_t channels[CHANNEL_COUNT];
385 rte_voter_t voter;
386
387 if (example_setup_dual_redundancy(channels, &voter) != RTE_STATUS_OK) {
388 return EXIT_FAILURE;
389 }
390
391 /* Example 1: Normal operation (both channels working) */
392 printf("\n--- Test 1: Normal Operation ---\n");
393 train_command_t cmd = {
394 .command_id = 1001,
395 .speed_limit_kmh = 80,
396 .target_distance_m = 5000,
397 .emergency_stop = 0,
398 };
399
400 if (send_train_command(&voter, &cmd) != RTE_STATUS_OK) {
401 printf("Send failed\n");
402 }
403
404 monitor_channel_health(channels, &voter);
405
406 /* Example 2: Simulate channel degradation */
407 printf("\n--- Test 2: One Channel Healthy ---\n");
408 tcp_ch_0.connected = false; /* TCP connection lost */
409 printf("TCP connection lost\n");
410
411 cmd.command_id = 1002;
412 if (send_train_command(&voter, &cmd) != RTE_STATUS_OK) {
413 printf("Send failed (expected, channel down)\n");
414 }
415
416 monitor_channel_health(channels, &voter);
417
418 /* Example 3: Restore and verify recovery */
419 printf("\n--- Test 3: Recovery ---\n");
420 tcp_ch_0.connected = true; /* TCP reconnected */
421 printf("TCP connection restored\n");
422
423 cmd.command_id = 1003;
424 if (send_train_command(&voter, &cmd) != RTE_STATUS_OK) {
425 printf("Send failed\n");
426 }
427
428 monitor_channel_health(channels, &voter);
429
430 (void)receive_train_command; /* demonstrates the receive-side API; not driven in this example */
431
432 /* Cleanup */
433 rte_voter_destroy(&voter);
434 for (uint32_t i = 0; i < CHANNEL_COUNT; i++) {
435 rte_channel_destroy(&channels[i]);
436 }
437 printf("\n========================================\n");
438 printf("Example completed\n");
439 printf("========================================\n");
440
441 return EXIT_SUCCESS;
442}
rte_status_t
Common result/status codes.
Definition rte_status.h:27
@ RTE_STATUS_OK
Definition rte_status.h:28
@ RTE_STATUS_INVALID_PARAM
Definition rte_status.h:29
@ RTE_STATUS_HARDWARE_FAULT
Definition rte_status.h:36
@ RTE_STATUS_RESOURCE_EXHAUSTED
Definition rte_status.h:33
@ RTE_STATUS_TIMEOUT
Definition rte_status.h:32
rte_voter_storage_t rte_voter_t
Opaque handle to a voter instance.
Definition rte_voter.h:149
rte_status_t rte_voter_get_aggregated_health(const rte_voter_t *voter, uint32_t *healthy_count, uint32_t *total_disagreements)
Aggregated health across every registered channel.
Definition rte_voter.c:603
rte_status_t rte_voter_register_channel(rte_voter_t *voter, rte_channel_t *channel)
Registers one already-initialized channel with this voter.
Definition rte_voter.c:240
rte_status_t rte_voter_destroy(rte_voter_t *voter)
Destroys a voter instance.
Definition rte_voter.c:656
rte_voting_result_t
Outcome of one rte_voter_receive() call.
Definition rte_voter.h:54
rte_status_t rte_voter_send(rte_voter_t *voter, const void *data, size_t data_size)
Broadcasts data to every registered, healthy channel.
Definition rte_voter.c:269
rte_status_t rte_voter_init(rte_voter_storage_t *storage, const rte_voter_config_t *config)
Initializes a voter with zero registered channels.
Definition rte_voter.c:201
rte_status_t rte_voter_receive(rte_voter_t *voter, void *data, size_t data_size, rte_voting_result_t *result, size_t *bytes_received)
Receives from every registered, healthy channel and votes.
Definition rte_voter.c:331
@ RTE_VOTING_2OO2
Definition rte_voter.h:44
Configuration for rte_channel_init().
Definition rte_channel.h:90
rte_channel_send_fn send
Definition rte_channel.h:96
rte_channel_recv_fn recv
Definition rte_channel.h:98
Health statistics for one vital channel.
Definition rte_channel.h:39
uint32_t receive_error_count
Definition rte_channel.h:47
uint32_t send_error_count
Definition rte_channel.h:43
Configuration for rte_voter_init().
Definition rte_voter.h:87
void * disagreement_context
Definition rte_voter.h:128
uint32_t channel_timeout_ms
Definition rte_voter.h:94
void(*) on_disagreement(void *context, rte_voting_result_t result)
Definition rte_voter.h:126
rte_voting_strategy_t voting_strategy
Definition rte_voter.h:89
static rte_status_t receive_train_command(rte_voter_t *voter, train_command_t *cmd_out)
Receive a train command via the voter (with 2oo2 voting).
static rte_status_t example_setup_dual_redundancy(rte_channel_t channels[CHANNEL_COUNT], rte_voter_t *voter)
Example: Initialize dual-transport channels plus a 2oo2 voter.
channel_type_t
Opaque channel type that hides whether it's SHM or TCP.
static rte_status_t send_train_command(rte_voter_t *voter, const train_command_t *cmd)
Send a train command to every registered channel (atomic broadcast).
static void monitor_channel_health(rte_channel_t channels[CHANNEL_COUNT], rte_voter_t *voter)
Monitor channel health and detect faults.
static rte_status_t channel_backend_recv(void *channel_handle, void *data, size_t size, uint32_t timeout_ms __attribute__((unused)))
OSAdapter receive callback for rte_channel (dispatches to transport).
static rte_status_t channel_backend_send(void *channel_handle, const void *data, size_t size)
OSAdapter send callback for rte_channel (dispatches to transport).