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
main.c
Go to the documentation of this file.
1
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <unistd.h>
32#include <time.h>
33
35
36/* ============================================================================
37 * Simulated Trains
38 * ========================================================================== */
39
40typedef struct {
41 uint32_t train_id;
42 uint32_t position;
43 uint32_t last_position;
45
46static train_state_t trains[] = {
47 { .train_id = 101, .position = 500, .last_position = 500 },
48 { .train_id = 102, .position = 2500, .last_position = 2500 },
49 { .train_id = 103, .position = 7500, .last_position = 7500 },
50};
51
52#define TRAIN_COUNT (sizeof(trains) / sizeof(trains[0]))
53
54/* ============================================================================
55 * Simulated Channel A (CPU0)
56 * ========================================================================== */
57
58static train_command_t channel_a_compute(const train_state_t *train)
59{
60 train_command_t cmd = {
61 .train_id = train->train_id,
62 .position = train->position,
63 .emergency_stop = 0,
64 .timestamp_ms = (uint32_t)(time(NULL) * 1000) & 0xffffffff,
65 .sequence_number = 0,
66 };
67
68 /* Signal logic */
69 if (train->position < 1000) {
70 cmd.signal_state = 2;
71 cmd.speed_limit = 80;
72 } else if (train->position < 5000) {
73 cmd.signal_state = 1;
74 cmd.speed_limit = 40;
75 } else if (train->position < 9000) {
76 cmd.signal_state = 0;
77 cmd.speed_limit = 0;
78 } else {
79 cmd.emergency_stop = 1;
80 cmd.signal_state = 0;
81 cmd.speed_limit = 0;
82 }
83
84 /* Compute checksum */
85 uint8_t *cmd_bytes = (uint8_t *)&cmd;
86 cmd.checksum = 0;
87 uint32_t crc = 0xffffffff;
88 for (uint32_t i = 0; i < sizeof(cmd) - sizeof(cmd.checksum); i++) {
89 uint8_t byte = cmd_bytes[i];
90 crc = (crc >> 8) ^ (uint32_t)(crc ^ byte);
91 }
92 cmd.checksum = crc ^ 0xffffffff;
93
94 return cmd;
95}
96
97/* ============================================================================
98 * Simulated Channel B (CPU1) - Verifies Channel A
99 * ========================================================================== */
100
101static train_command_t channel_b_compute(const train_state_t *train)
102{
103 /* In real system: independent verification
104 * In simulation: mostly same as A, with controlled disagreements
105 */
106 train_command_t cmd = channel_a_compute(train);
107
108 /* Occasionally introduce mismatch for testing */
109 static uint32_t call_count = 0;
110 call_count++;
111
112 if ((call_count % 25) == 0) {
113 cmd.signal_state = (cmd.signal_state + 1) % 3;
114 /* Recompute checksum */
115 uint8_t *cmd_bytes = (uint8_t *)&cmd;
116 cmd.checksum = 0;
117 uint32_t crc = 0xffffffff;
118 for (uint32_t i = 0; i < sizeof(cmd) - sizeof(cmd.checksum); i++) {
119 uint8_t byte = cmd_bytes[i];
120 crc = (crc >> 8) ^ (uint32_t)(crc ^ byte);
121 }
122 cmd.checksum = crc ^ 0xffffffff;
123 }
124
125 return cmd;
126}
127
128/* ============================================================================
129 * Simulate Train Movement
130 * ========================================================================== */
131
132static void simulate_movement(train_state_t *train)
133{
134 train->last_position = train->position;
135
136 switch (train->train_id) {
137 case 101:
138 train->position += 5;
139 break;
140 case 102:
141 train->position += 8;
142 break;
143 case 103:
144 train->position += 3;
145 break;
146 }
147
148 if (train->position > 10000) {
149 train->position = 10000;
150 }
151}
152
153/* ============================================================================
154 * Main Simulation
155 * ========================================================================== */
156
157int main(int argc, char *argv[])
158{
159 (void)argc;
160 (void)argv;
161
162 multi_site_voter_t voter;
163 multi_site_config_t config = {
164 .site_heartbeat_interval_ms = 50,
165 .heartbeat_timeout_ms = 150,
166 .channel_timeout_ms = 25,
167 .max_heartbeat_misses = 3,
168 .max_voting_errors = 10,
169 .west_site_name = "ERTMS-RBC-West (Madrid)",
170 .east_site_name = "ERTMS-RBC-East (Barcelona)",
171 };
172
173 printf("\n");
174 printf("╔═══════════════════════════════════════════════════════════════╗\n");
175 printf("║ Multi-Site Geographic Redundancy Example ║\n");
176 printf("║ ERTMS Radio Block Centre with Active/Standby Failover ║\n");
177 printf("║ ║\n");
178 printf("║ Architecture: ║\n");
179 printf("║ WEST (Madrid): ONLINE - Vital channels A,B + Svc C ║\n");
180 printf("║ EAST (Barcelona): STANDBY - Mirrors, ready to failover ║\n");
181 printf("║ ║\n");
182 printf("║ Safety: EN 50128 SIL 4 / EN 50129 ║\n");
183 printf("╚═══════════════════════════════════════════════════════════════╝\n");
184 printf("\n");
185
186 /* Initialize */
187 if (multi_site_voter_init(&voter, &config) != 0) {
188 fprintf(stderr, "ERROR: Failed to initialize voter\n");
189 return EXIT_FAILURE;
190 }
191
192 /* Run simulation: 50 cycles with failover at cycle 25 */
193 uint32_t total_cycles = 50;
194 uint32_t failover_trigger_cycle = 25;
195
196 for (uint32_t cycle = 0; cycle < total_cycles; cycle++) {
197 printf("┌─ Cycle %2u (%s) ─────────────────────────────────────────┐\n",
198 cycle + 1,
199 voter.active_site == SITE_WEST ? "WEST active" : "EAST active");
200
201 /* Simulate train movement */
202 for (uint32_t t = 0; t < TRAIN_COUNT; t++) {
203 simulate_movement(&trains[t]);
204 }
205
206 /* Each site independently votes */
207 for (uint32_t t = 0; t < TRAIN_COUNT; t++) {
208 train_state_t *train = &trains[t];
209
210 /* Compute channels A and B */
211 train_command_t cmd_a = channel_a_compute(train);
212 train_command_t cmd_b = channel_b_compute(train);
213
214 /* WEST votes */
215 multi_site_vote_cycle(&voter, SITE_WEST, &cmd_a, &cmd_b);
216
217 /* EAST votes (mirrored) */
218 multi_site_vote_cycle(&voter, SITE_EAST, &cmd_a, &cmd_b);
219
220 /* Display active site output (Channel C: Service/printf) */
221 if (voter.active_site == SITE_WEST) {
222 printf("│ WEST: %s\n", voter.west.channels.service_output);
223 } else {
224 printf("│ EAST: %s\n", voter.east.channels.service_output);
225 }
226 }
227
228 /* Check heartbeat and failover */
229 multi_site_heartbeat(&voter);
230
231 printf("└────────────────────────────────────────────────────────────┘\n");
232
233 /* Inject fault at specific cycle */
234 if (cycle == failover_trigger_cycle) {
235 printf("\n");
236 printf("⚠️ INJECTING FAULT: WEST site lost heartbeat (simulating failure)\n");
237 printf(" Heartbeat miss counter will trigger automatic failover...\n");
238 printf("\n");
239
240 /* Simulate WEST losing heartbeat by advancing its last timestamp */
241 voter.west.heartbeat_timestamp_ms = 0;
242 }
243
244 usleep(50000); /* 50ms between cycles */
245 }
246
247 /* Final statistics */
248 printf("\n");
250
251 /* Shutdown */
253
254 printf("╔═══════════════════════════════════════════════════════════════╗\n");
255 printf("║ Multi-Site Example Completed Successfully ║\n");
256 printf("║ ║\n");
257 printf("║ Key Features Demonstrated: ║\n");
258 printf("║ ✓ Two geographic sites (WEST/EAST) ║\n");
259 printf("║ ✓ Active/Standby role switching ║\n");
260 printf("║ ✓ Automatic failover on heartbeat loss ║\n");
261 printf("║ ✓ 2oo2 vital voting (channels A, B) ║\n");
262 printf("║ ✓ Service channel (C) for output/logging ║\n");
263 printf("║ ✓ Dual redundancy across sites ║\n");
264 printf("╚═══════════════════════════════════════════════════════════════╝\n");
265 printf("\n");
266
267 return EXIT_SUCCESS;
268}
void multi_site_print_stats(const multi_site_voter_t *voter)
Print multi-site statistics for diagnostics.
int multi_site_voter_shutdown(multi_site_voter_t *voter)
Shutdown multi-site voter.
int multi_site_voter_init(multi_site_voter_t *voter, const multi_site_config_t *config)
Initialize multi-site voter with West online, East standby.
int multi_site_vote_cycle(multi_site_voter_t *voter, site_id_t site, const train_command_t *cmd_a, const train_command_t *cmd_b)
Process voting cycle on one site.
site_id_t multi_site_heartbeat(multi_site_voter_t *voter)
Check heartbeat between sites and trigger failover if needed.
Multi-Site 2oo2 Redundancy with Active/Standby Failover.