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
voter.c
Go to the documentation of this file.
1
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <stdint.h>
19#include <unistd.h>
20#include <time.h>
21
22typedef struct {
23 uint32_t train_id;
24 uint32_t position;
25 uint8_t signal_state;
26 uint8_t emergency_stop;
27 uint16_t speed_limit;
28 uint32_t timestamp_ms;
29 uint32_t checksum;
30 uint32_t sequence_number;
32
33typedef struct {
34 train_command_t cmd_a;
35 train_command_t cmd_b;
36 uint32_t process_a_seq;
37 uint32_t process_b_seq;
38 volatile uint8_t ready_a;
39 volatile uint8_t ready_b;
40 volatile uint8_t process_a_healthy;
41 volatile uint8_t process_b_healthy;
42 uint32_t timestamp_ms;
44
45__attribute__((aligned(64)))
46extern shared_state_t shared_mem;
47
48typedef struct {
49 train_command_t voted_command;
50 uint8_t vote_result;
51 uint32_t voter_sequence;
52 char log_message[256];
53} voted_result_t;
54
55__attribute__((aligned(64)))
56extern voted_result_t voted_result;
57
58/* ============================================================================
59 * 2oo2 Voting Logic
60 * ========================================================================== */
61
62static int perform_2oo2_vote(const train_command_t *cmd_a,
63 const train_command_t *cmd_b,
64 train_command_t *result_out,
65 char *message_out,
66 uint32_t message_len)
67{
68 if (cmd_a == NULL || cmd_b == NULL || result_out == NULL) {
69 return 2; /* Timeout */
70 }
71
72 /* Compare vital fields */
73 if (cmd_a->train_id != cmd_b->train_id ||
74 cmd_a->position != cmd_b->position ||
75 cmd_a->signal_state != cmd_b->signal_state ||
76 cmd_a->speed_limit != cmd_b->speed_limit) {
77
78 /* MISMATCH */
79 snprintf(message_out, message_len,
80 "[VOTER] MISMATCH: A(train=%u,sig=%u,spd=%u) vs "
81 "B(train=%u,sig=%u,spd=%u) → SAFE_STATE",
82 cmd_a->train_id, cmd_a->signal_state, cmd_a->speed_limit,
83 cmd_b->train_id, cmd_b->signal_state, cmd_b->speed_limit);
84
85 /* Output safe state */
86 *result_out = *cmd_a;
87 result_out->signal_state = 0; /* Red */
88 result_out->speed_limit = 0; /* Stop */
89 result_out->emergency_stop = 1;
90
91 return 1; /* Mismatch */
92 }
93
94 /* MATCH */
95 snprintf(message_out, message_len,
96 "[VOTER] MATCH: train=%u, pos=%u, sig=%u, speed=%u",
97 cmd_a->train_id, cmd_a->position,
98 cmd_a->signal_state, cmd_a->speed_limit);
99
100 *result_out = *cmd_a;
101 return 0; /* Match */
102}
103
104/* ============================================================================
105 * Main Voter Loop
106 * ========================================================================== */
107
108int main(int argc, char *argv[])
109{
110 (void)argc;
111 (void)argv;
112
113 uint32_t voter_seq = 0;
114 uint32_t total_votes = 0;
115 uint32_t total_matches = 0;
116 uint32_t total_mismatches = 0;
117 uint32_t total_timeouts = 0;
118
119 printf("\n");
120 printf("╔════════════════════════════════════════════════════════════╗\n");
121 printf("║ Voter Process - 2oo2 Voting Logic (Deterministic) ║\n");
122 printf("║ Reads from Processes A & B, writes to Process C ║\n");
123 printf("╚════════════════════════════════════════════════════════════╝\n");
124 printf("\n");
125
126 while (1) {
127 voter_seq++;
128
129 /* Wait for both channels to be ready */
130 if (!shared_mem.ready_a || !shared_mem.ready_b) {
131 usleep(5000); /* Brief wait */
132 continue;
133 }
134
135 /* Check sequence synchronization */
136 if (shared_mem.process_a_seq != shared_mem.process_b_seq) {
137 fprintf(stderr, "[VOTER] Sequence mismatch: A_seq=%u vs B_seq=%u\n",
138 shared_mem.process_a_seq, shared_mem.process_b_seq);
139 usleep(1000);
140 continue;
141 }
142
143 /* Perform 2oo2 voting */
144 train_command_t voted_cmd;
145 char vote_message[256];
146 int vote_result = perform_2oo2_vote(
147 &shared_mem.cmd_a,
148 &shared_mem.cmd_b,
149 &voted_cmd,
150 vote_message,
151 sizeof(vote_message)
152 );
153
154 /* Update statistics */
155 total_votes++;
156 switch (vote_result) {
157 case 0:
158 total_matches++;
159 break;
160 case 1:
161 total_mismatches++;
162 break;
163 case 2:
164 total_timeouts++;
165 break;
166 }
167
168 /* Write result to shared memory for Process C */
169 voted_result.voted_command = voted_cmd;
170 voted_result.vote_result = (uint8_t)vote_result;
171 voted_result.voter_sequence = voter_seq;
172 strncpy(voted_result.log_message, vote_message, sizeof(voted_result.log_message) - 1);
173 voted_result.log_message[sizeof(voted_result.log_message) - 1] = '\0';
174
175 /* Heartbeat to Coordinator: voter is alive */
176 if ((total_votes % 10) == 0) {
177 fprintf(stderr,
178 "[VOTER] Cycle %u: votes=%u, matches=%u, mismatches=%u, timeouts=%u\n",
179 voter_seq, total_votes, total_matches, total_mismatches, total_timeouts);
180 }
181
182 /* Reset ready flags for next cycle */
183 shared_mem.ready_a = 0;
184 shared_mem.ready_b = 0;
185
186 /* Deterministic 25ms cycle (must match Process A & B) */
187 usleep(25000);
188 }
189
190 return EXIT_SUCCESS;
191}