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
process_a.c
1
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <stdint.h>
20#include <unistd.h>
21#include <time.h>
22
23/* ============================================================================
24 * Command Structure
25 * ========================================================================== */
26
27typedef struct {
28 uint32_t train_id;
29 uint32_t position;
30 uint8_t signal_state;
31 uint8_t emergency_stop;
32 uint16_t speed_limit;
33 uint32_t timestamp_ms;
34 uint32_t checksum;
35 uint32_t sequence_number;
37
38/* ============================================================================
39 * Shared Memory
40 * ========================================================================== */
41
42typedef struct {
43 train_command_t cmd_a;
44 train_command_t cmd_b;
45 train_command_t cmd_a_out;
46 train_command_t cmd_b_out;
47 volatile uint32_t a_ready;
48 volatile uint32_t b_ready;
49 volatile uint32_t a_compared;
50 volatile uint32_t b_compared;
51 volatile uint8_t outputs_match;
52 uint32_t mismatch_count;
53 uint32_t sequence;
54 uint32_t timestamp_ms;
56
57__attribute__((aligned(64)))
58static shared_state_t shared_mem = {0};
59
60/* ============================================================================
61 * Train Database
62 * ========================================================================== */
63
64typedef struct {
65 uint32_t train_id;
66 uint32_t position;
68
69static train_db_entry_t trains[] = {
70 { .train_id = 101, .position = 500 },
71 { .train_id = 102, .position = 2500 },
72 { .train_id = 103, .position = 7500 },
73};
74
75#define TRAIN_COUNT (sizeof(trains) / sizeof(trains[0]))
76
77/* ============================================================================
78 * Computation
79 * ========================================================================== */
80
81static uint32_t crc32(const uint8_t *data, uint32_t len)
82{
83 uint32_t crc = 0xffffffff;
84 for (uint32_t i = 0; i < len; i++) {
85 uint8_t byte = data[i];
86 crc = (crc >> 8) ^ (uint32_t)(crc ^ byte);
87 }
88 return crc ^ 0xffffffff;
89}
90
91static train_command_t compute_command(uint32_t train_id, uint32_t position)
92{
93 train_command_t cmd = {
94 .train_id = train_id,
95 .position = position,
96 .emergency_stop = 0,
97 .timestamp_ms = (uint32_t)(time(NULL) * 1000) & 0xffffffff,
98 .sequence_number = 0,
99 };
100
101 if (position < 1000) {
102 cmd.signal_state = 2;
103 cmd.speed_limit = 80;
104 } else if (position < 5000) {
105 cmd.signal_state = 1;
106 cmd.speed_limit = 40;
107 } else if (position < 9000) {
108 cmd.signal_state = 0;
109 cmd.speed_limit = 0;
110 } else {
111 cmd.emergency_stop = 1;
112 cmd.signal_state = 0;
113 cmd.speed_limit = 0;
114 }
115
116 uint8_t *cmd_bytes = (uint8_t *)&cmd;
117 cmd.checksum = crc32(cmd_bytes, sizeof(cmd) - sizeof(cmd.checksum));
118 return cmd;
119}
120
121/* ============================================================================
122 * Cross-Comparison Logic (Identical in A and B)
123 * ========================================================================== */
124
125static train_command_t safe_state(uint32_t train_id)
126{
127 train_command_t safe = {
128 .train_id = train_id,
129 .position = 0,
130 .signal_state = 0,
131 .emergency_stop = 1,
132 .speed_limit = 0,
133 .timestamp_ms = 0,
134 .checksum = 0,
135 .sequence_number = 0,
136 };
137 return safe;
138}
139
140static void compare_and_decide(const train_command_t *my_cmd,
141 const train_command_t *other_cmd,
142 train_command_t *output,
143 uint8_t *match_status)
144{
145 /* Cross-comparison logic (SIL 4) */
146 if (my_cmd->train_id == other_cmd->train_id &&
147 my_cmd->position == other_cmd->position &&
148 my_cmd->signal_state == other_cmd->signal_state &&
149 my_cmd->speed_limit == other_cmd->speed_limit) {
150
151 /* MATCH: Both agree, output joint command */
152 *output = *my_cmd;
153 *match_status = 1;
154
155 } else {
156
157 /* MISMATCH: Both output safe state */
158 *output = safe_state(my_cmd->train_id);
159 *match_status = 0;
160 }
161}
162
163/* ============================================================================
164 * Main Loop
165 * ========================================================================== */
166
167int main(int argc, char *argv[])
168{
169 (void)argc;
170 (void)argv;
171
172 uint32_t cycle_count = 0;
173 uint32_t sequence = 0;
174
175 fprintf(stderr,
176 "┌────────────────────────────────────────────────────────┐\n");
177 fprintf(stderr,
178 "│ Process A - Cross-Comparison (Peer-to-Peer 2oo2) │\n");
179 fprintf(stderr,
180 "│ Computes independently, compares with B, co-decides │\n");
181 fprintf(stderr,
182 "└────────────────────────────────────────────────────────┘\n");
183 fprintf(stderr, "\n");
184
185 while (1) {
186 cycle_count++;
187 sequence++;
188
189 /* PHASE 1: Compute independently */
190 for (uint32_t i = 0; i < TRAIN_COUNT; i++) {
191 trains[i].position += (i + 1) * 3;
192 if (trains[i].position > 10000) {
193 trains[i].position = 10000;
194 }
195 }
196
197 for (uint32_t i = 0; i < TRAIN_COUNT; i++) {
198 train_command_t cmd = compute_command(
199 trains[i].train_id,
200 trains[i].position
201 );
202 cmd.sequence_number = sequence;
203
204 shared_mem.cmd_a = cmd;
205 shared_mem.sequence = sequence;
206 __sync_synchronize();
207 shared_mem.a_ready = 1;
208
209 if (cycle_count % 10 == 0) {
210 fprintf(stderr,
211 "[WEST:A] Cycle %u: Computed train=%u, pos=%u, sig=%u, speed=%u\n",
212 cycle_count, cmd.train_id, cmd.position,
213 cmd.signal_state, cmd.speed_limit);
214 }
215 }
216
217 /* PHASE 2: Wait for B's computation */
218 uint32_t timeout_ms = 0;
219 while (!shared_mem.b_ready && timeout_ms < 25) {
220 usleep(1000);
221 timeout_ms++;
222 }
223
224 if (!shared_mem.b_ready) {
225 fprintf(stderr, "[WEST:A] ERROR: Process B timeout\n");
226 continue;
227 }
228
229 /* PHASE 3: Cross-comparison (local decision) */
230 train_command_t output;
231 uint8_t match_status;
232
233 compare_and_decide(
234 &shared_mem.cmd_a,
235 &shared_mem.cmd_b,
236 &output,
237 &match_status
238 );
239
240 shared_mem.cmd_a_out = output;
241 __sync_synchronize();
242 shared_mem.a_compared = 1;
243
244 if (cycle_count % 10 == 0) {
245 fprintf(stderr,
246 "[WEST:A] Received B's: train=%u, sig=%u, speed=%u\n",
247 shared_mem.cmd_b.train_id,
248 shared_mem.cmd_b.signal_state,
249 shared_mem.cmd_b.speed_limit);
250
251 fprintf(stderr,
252 "[WEST:A] Comparison: %s\n",
253 match_status ? "MATCH ✓" : "MISMATCH ✗");
254
255 fprintf(stderr,
256 "[WEST:A] Output: sig=%u, speed=%u\n",
257 output.signal_state, output.speed_limit);
258 }
259
260 /* PHASE 4: Verify B reached same decision */
261 timeout_ms = 0;
262 while (!shared_mem.b_compared && timeout_ms < 10) {
263 usleep(1000);
264 timeout_ms++;
265 }
266
267 if (shared_mem.b_compared) {
268 if (shared_mem.cmd_a_out.signal_state == shared_mem.cmd_b_out.signal_state &&
269 shared_mem.cmd_a_out.speed_limit == shared_mem.cmd_b_out.speed_limit) {
270
271 shared_mem.outputs_match = 1;
272
273 } else {
274
275 shared_mem.outputs_match = 0;
276 shared_mem.mismatch_count++;
277
278 fprintf(stderr,
279 "[WEST:A] ⚠️ ALERT: B's output differs!\n");
280 fprintf(stderr,
281 " A output: sig=%u, speed=%u\n",
282 shared_mem.cmd_a_out.signal_state,
283 shared_mem.cmd_a_out.speed_limit);
284 fprintf(stderr,
285 " B output: sig=%u, speed=%u\n",
286 shared_mem.cmd_b_out.signal_state,
287 shared_mem.cmd_b_out.speed_limit);
288 }
289 }
290
291 /* Reset for next cycle */
292 shared_mem.a_ready = 0;
293 shared_mem.a_compared = 0;
294
295 usleep(25000); /* 25ms cycle */
296 }
297
298 return EXIT_SUCCESS;
299}
Definition process_a.c:64