43 uint32_t last_position;
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 },
52#define TRAIN_COUNT (sizeof(trains) / sizeof(trains[0]))
61 .train_id = train->train_id,
62 .position = train->position,
64 .timestamp_ms = (uint32_t)(time(NULL) * 1000) & 0xffffffff,
69 if (train->position < 1000) {
72 }
else if (train->position < 5000) {
75 }
else if (train->position < 9000) {
79 cmd.emergency_stop = 1;
85 uint8_t *cmd_bytes = (uint8_t *)&cmd;
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);
92 cmd.checksum = crc ^ 0xffffffff;
109 static uint32_t call_count = 0;
112 if ((call_count % 25) == 0) {
113 cmd.signal_state = (cmd.signal_state + 1) % 3;
115 uint8_t *cmd_bytes = (uint8_t *)&cmd;
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);
122 cmd.checksum = crc ^ 0xffffffff;
134 train->last_position = train->position;
136 switch (train->train_id) {
138 train->position += 5;
141 train->position += 8;
144 train->position += 3;
148 if (train->position > 10000) {
149 train->position = 10000;
157int main(
int argc,
char *argv[])
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)",
174 printf(
"╔═══════════════════════════════════════════════════════════════╗\n");
175 printf(
"║ Multi-Site Geographic Redundancy Example ║\n");
176 printf(
"║ ERTMS Radio Block Centre with Active/Standby Failover ║\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");
182 printf(
"║ Safety: EN 50128 SIL 4 / EN 50129 ║\n");
183 printf(
"╚═══════════════════════════════════════════════════════════════╝\n");
188 fprintf(stderr,
"ERROR: Failed to initialize voter\n");
193 uint32_t total_cycles = 50;
194 uint32_t failover_trigger_cycle = 25;
196 for (uint32_t cycle = 0; cycle < total_cycles; cycle++) {
197 printf(
"┌─ Cycle %2u (%s) ─────────────────────────────────────────┐\n",
199 voter.active_site == SITE_WEST ?
"WEST active" :
"EAST active");
202 for (uint32_t t = 0; t < TRAIN_COUNT; t++) {
203 simulate_movement(&trains[t]);
207 for (uint32_t t = 0; t < TRAIN_COUNT; t++) {
221 if (voter.active_site == SITE_WEST) {
222 printf(
"│ WEST: %s\n", voter.west.channels.service_output);
224 printf(
"│ EAST: %s\n", voter.east.channels.service_output);
231 printf(
"└────────────────────────────────────────────────────────────┘\n");
234 if (cycle == failover_trigger_cycle) {
236 printf(
"⚠️ INJECTING FAULT: WEST site lost heartbeat (simulating failure)\n");
237 printf(
" Heartbeat miss counter will trigger automatic failover...\n");
241 voter.west.heartbeat_timestamp_ms = 0;
254 printf(
"╔═══════════════════════════════════════════════════════════════╗\n");
255 printf(
"║ Multi-Site Example Completed Successfully ║\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");
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.