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
geo_distributed_checkpoint_sync.c
Go to the documentation of this file.
1
35
36#include <setjmp.h>
37#include <stdio.h>
38#include <string.h>
39
40#include "rte/checkpoint/rte_checkpoint.h"
41#include "rte/checksum/rte_checksum.h"
42#include "rte/clocksync/rte_clocksync.h"
44#include "rte/safestate/rte_safestate.h"
45#include "rte/voter/rte_voter.h"
46
47/* ============================================================================
48 * Simulated transport: one in-memory mailbox per site (WEST=0, EAST=1)
49 * ========================================================================== */
50
51#define SITE_WEST 0
52#define SITE_EAST 1
53#define SITE_COUNT 2U
54
55static int g_site_index[SITE_COUNT] = { SITE_WEST, SITE_EAST };
56static void *g_channel_handles[SITE_COUNT] = { &g_site_index[SITE_WEST], &g_site_index[SITE_EAST] };
57
58static rte_vital_message_t g_mailbox[SITE_COUNT];
59static bool g_mail_ready[SITE_COUNT];
60/* Simulates a dead link to a given site: its backend_recv() times out
61 * regardless of what was sent, standing in for a message that was sent
62 * but never arrived (dropped/delayed network, not a local logic error -
63 * a real transport's recv() would time out the same way). */
64static bool g_link_down[SITE_COUNT];
65
66static rte_status_t mock_send(void *channel, const void *data, size_t data_size)
67{
68 int idx = *(const int *)channel;
69
70 if (data_size != sizeof(rte_vital_message_t))
71 {
73 }
74 /* Loopback echo for this single-process demo: in a real deployment
75 * this send() would travel over rte_ipc/a socket/a serial link to
76 * the actual peer process, which would send its own confirmation
77 * back independently - this mock just short-circuits that round
78 * trip for sites whose link is up. */
79 (void)memcpy(&g_mailbox[idx], data, data_size);
80 g_mail_ready[idx] = true;
81 return RTE_STATUS_OK;
82}
83
84static rte_status_t mock_recv(void *channel, void *data, size_t data_size, uint32_t timeout_ms)
85{
86 int idx = *(const int *)channel;
87
88 (void)timeout_ms; /* in-memory mailbox: either the mail is there or it isn't */
89 if (data_size != sizeof(rte_vital_message_t))
90 {
92 }
93 if (g_link_down[idx] || !g_mail_ready[idx])
94 {
95 return RTE_STATUS_TIMEOUT;
96 }
97 (void)memcpy(data, &g_mailbox[idx], data_size);
98 return RTE_STATUS_OK;
99}
100
101/* ============================================================================
102 * A minimal rte_clocksync OSAdapter - diagnostic only, per its own header's
103 * warning: never the basis for deciding whether results are comparable.
104 * ========================================================================== */
105
106static rte_status_t clocksync_get_offset_ms(int64_t *out_offset_ms)
107{
108 *out_offset_ms = 3; /* pretend WEST's clock is 3ms ahead of EAST's */
109 return RTE_STATUS_OK;
110}
111
112static rte_status_t clocksync_get_quality(rte_clocksync_quality_t *out_quality)
113{
114 *out_quality = RTE_CLOCKSYNC_SYNCHRONIZED;
115 return RTE_STATUS_OK;
116}
117
118/* ============================================================================
119 * Safe-state handler for this demo: print and unwind via longjmp instead
120 * of the production infinite loop, so the example can report the outcome
121 * and exit cleanly. A real integrator's SAFE handler drives outputs to
122 * their fail-safe state instead.
123 * ========================================================================== */
124
125static jmp_buf g_safestate_jmp;
126
127static void demo_safestate_handler(rte_safestate_level_t level, rte_safestate_reason_t reason, const char *file,
128 int32_t line, const char *message)
129{
130 (void)file;
131 (void)message;
132 printf(" [SAFE-STATE] level=%d reason=%u at line %d - withdrawing movement authority\n", (int)level,
133 (unsigned)reason, (int)line);
134 longjmp(g_safestate_jmp, 1);
135}
136
137int main(void)
138{
139 rte_channel_t sites[SITE_COUNT];
140 rte_voter_t voter;
141 rte_voter_config_t voter_config;
142 rte_osadapter_clocksync_t clock_osadapter;
143 int64_t offset_ms = 0;
145 uint32_t i;
146
148 (void)rte_safestate_register_handler(RTE_SAFESTATE_LEVEL_SAFE, demo_safestate_handler);
149
150 clock_osadapter.get_offset_ms = clocksync_get_offset_ms;
151 clock_osadapter.get_quality = clocksync_get_quality;
152 (void)rte_osadapter_clocksync_register(&clock_osadapter);
153 (void)rte_clocksync_get_offset_ms(&offset_ms);
154 (void)rte_clocksync_get_quality(&quality);
155 printf("Diagnostic clock offset WEST-vs-EAST: %lldms (quality=%d) - NOT used for the vote below\n",
156 (long long)offset_ms, (int)quality);
157
158 memset(&voter_config, 0, sizeof(voter_config));
159 voter_config.voting_strategy = RTE_VOTING_2OO2;
160 voter_config.channel_timeout_ms = 100U;
161 (void)rte_voter_init(&voter, &voter_config);
162
163 for (i = 0U; i < SITE_COUNT; i++)
164 {
165 rte_channel_config_t chan_config;
166
167 memset(&chan_config, 0, sizeof(chan_config));
168 chan_config.channel_handle = g_channel_handles[i];
169 chan_config.send = mock_send;
170 chan_config.recv = mock_recv;
171 (void)rte_channel_init(&sites[i], &chan_config);
172 (void)rte_voter_register_channel(&voter, &sites[i]);
173 }
174
175 /* ---- Cycle 1: both sites confirm in time ---- */
176 {
178 rte_status_t status;
179
180 g_mail_ready[SITE_WEST] = false;
181 g_mail_ready[SITE_EAST] = false;
182
183 ckpt.checkpoint_id = 1U;
184 ckpt.max_delay_ms = 200U;
185 ckpt.expected_node_count = SITE_COUNT;
186 ckpt.watchdog = NULL;
187
188 printf("\nCycle 1: WEST and EAST both reach checkpoint 1...\n");
189 status = rte_channel_checkpoint(&voter, &ckpt);
190 printf(" rte_channel_checkpoint() -> %s\n", rte_status_to_string(status));
191 }
192
193 /* ---- Cycle 2: EAST's confirmation never arrives (dropped/delayed) ---- */
194 if (setjmp(g_safestate_jmp) == 0)
195 {
197 rte_status_t status;
198
199 g_mail_ready[SITE_WEST] = false;
200 g_mail_ready[SITE_EAST] = false;
201 g_link_down[SITE_EAST] = true; /* EAST's confirmation will never arrive */
202
203 ckpt.checkpoint_id = 2U;
204 ckpt.max_delay_ms = 50U;
205 ckpt.expected_node_count = SITE_COUNT;
206 ckpt.watchdog = NULL;
207
208 printf("\nCycle 2: EAST's link is down - its confirmation never arrives...\n");
209 status = rte_channel_checkpoint(&voter, &ckpt);
210 /* Not reached on a real timeout: rte_channel_checkpoint() enters
211 * safe-state directly (REQ-CHECKPOINT-003) before returning. */
212 printf(" rte_channel_checkpoint() -> %s (unexpected - should have diverted)\n",
213 rte_status_to_string(status));
214 }
215 else
216 {
217 printf(" Cycle 2 correctly forced safe-state instead of voting on an unconfirmed cycle.\n");
218 }
219
220 (void)rte_voter_destroy(&voter);
221 for (i = 0U; i < SITE_COUNT; i++)
222 {
223 (void)rte_channel_destroy(&sites[i]);
224 }
225 return 0;
226}
rte_status_t rte_channel_checkpoint(rte_voter_t *voter, const rte_checkpoint_config_t *config)
Performs one bounded checkpoint rendezvous across every channel registered with a voter.
rte_status_t rte_checksum_crc64_init(rte_crc64_polynomial_t polynomial)
Initialize CRC-64 lookup tables for chosen polynomial.
@ RTE_CRC64_ERTMS
rte_clocksync_quality_t
Coarse quality/confidence of the current clock synchronization.
rte_status_t rte_clocksync_get_quality(rte_clocksync_quality_t *out_quality)
Reports the OSAdapter's current confidence in the clock offset.
rte_status_t rte_clocksync_get_offset_ms(int64_t *out_offset_ms)
Reports this node's estimated clock offset (diagnostic/timeout- sizing only - see the file-level note...
@ RTE_CLOCKSYNC_SYNCHRONIZED
@ RTE_CLOCKSYNC_UNSYNCHRONIZED
rte_safestate_level_t
Safe-state severity levels (ADR-004 section 2.1).
rte_status_t rte_safestate_register_handler(rte_safestate_level_t level, rte_safestate_handler_t handler)
Registers the reaction handler for one safe-state level. This is how an integrator supplies their own...
uint16_t rte_safestate_reason_t
Diagnostic reason code accompanying a safe-state transition.
@ RTE_SAFESTATE_LEVEL_SAFE
const char * rte_status_to_string(rte_status_t status)
Returns a short, static, human-readable string for a status code. Intended for diagnostics/logging on...
Definition rte_status.c:22
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_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_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_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_VOTING_2OO2
Definition rte_voter.h:44
OSAdapter interface for clock synchronization.
rte_status_t rte_osadapter_clocksync_register(const rte_osadapter_clocksync_t *adapter)
Registers the OSAdapter clock synchronization implementation.
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
Checkpoint configuration.
rte_duration_ms_t max_delay_ms
OSAdapter clock synchronization operations vtable.
Vital channel message with integrated CRC-64.
Configuration for rte_voter_init().
Definition rte_voter.h:87
uint32_t channel_timeout_ms
Definition rte_voter.h:94
rte_voting_strategy_t voting_strategy
Definition rte_voter.h:89