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
rte_cross_comparator.c
Go to the documentation of this file.
1
7#include <string.h>
8
11#include "rte/oal/log/rte_log.h"
13
14static bool cross_comparator_data_equal(const rte_cross_comparator_t *cmp,
15 const void *a, const void *b, size_t size)
16{
17 bool equal;
18
19 if (cmp->config.compare != NULL)
20 {
21 equal = cmp->config.compare(a, b, size, cmp->config.compare_context);
22 }
23 else
24 {
25 equal = (memcmp(a, b, size) == 0);
26 }
27 return equal;
28}
29
32{
33 rte_status_t lifecycle_status;
34
35 if ((storage == NULL) || (config == NULL))
36 {
38 }
39 /* REQ-LIFECYCLE-001 (ADR-026): a cross-comparator is a setup-only resource - refuse once
40 * the application's setup phase has been locked. */
41 lifecycle_status = rte_lifecycle_check_setup_allowed();
42 if (lifecycle_status != RTE_STATUS_OK)
43 {
44 return lifecycle_status;
45 }
46
47 storage->config = *config;
48 storage->channel_a = NULL;
49 storage->channel_b = NULL;
50 storage->registered_count = 0U;
51 storage->total_disagreements = 0U;
52 storage->initialized = true;
53
54 return RTE_STATUS_OK;
55}
56
58 rte_channel_t *channel)
59{
60 rte_status_t lifecycle_status;
61
62 if ((cmp == NULL) || (channel == NULL))
63 {
65 }
66 if (!cmp->initialized)
67 {
69 }
70 /* REQ-LIFECYCLE-001 (ADR-026): registering a channel into a cross-comparator is
71 * setup-only - refuse once the application's setup phase has been
72 * locked. */
73 lifecycle_status = rte_lifecycle_check_setup_allowed();
74 if (lifecycle_status != RTE_STATUS_OK)
75 {
76 return lifecycle_status;
77 }
78
79 if (cmp->registered_count == 0U)
80 {
81 cmp->channel_a = channel;
82 cmp->registered_count = 1U;
83 }
84 else if (cmp->registered_count == 1U)
85 {
86 cmp->channel_b = channel;
87 cmp->registered_count = 2U;
88 }
89 else
90 {
92 }
93 return RTE_STATUS_OK;
94}
95
103 const void *agreed_data, size_t data_size,
104 rte_voting_result_t *result, void *out_data, size_t *out_size)
105{
106 if (result != NULL)
107 {
108 *result = local_result;
109 }
110
111 if (local_result == RTE_VOTING_AGREED)
112 {
113 if (out_data != NULL)
114 {
115 (void)memcpy(out_data, agreed_data, data_size);
116 }
117 if (out_size != NULL)
118 {
119 *out_size = data_size;
120 }
121 return RTE_STATUS_OK;
122 }
123
124 if (out_size != NULL)
125 {
126 *out_size = 0U;
127 }
128
129 if (local_result == RTE_VOTING_DISAGREED)
130 {
131 cmp->total_disagreements++;
132 if (cmp->config.log_disagreements)
133 {
134 rte_log_write(RTE_LOG_LEVEL_ERROR, "cross_comparator", "channels disagreed");
135 }
136 /* Application reacts (cleanup, alarms, closing its own links)
137 * BEFORE the Platform enters safe state below - it gets no
138 * "after". See this module's own RCA/OCORA PI-API compatibility
139 * note (rte_cross_comparator.h) for why the Platform, not the
140 * application, now unconditionally owns this transition. */
141 if (cmp->config.on_disagreement != NULL)
142 {
143 cmp->config.on_disagreement(cmp->config.disagreement_context, local_result);
144 }
145 RTE_SAFESTATE(cmp->config.safestate_level, cmp->config.safestate_reason);
146 }
147 else if (cmp->config.on_disagreement != NULL)
148 {
149 /* Non-DISAGREED non-AGREED results (TIMEOUT/INSUFFICIENT_QUORUM)
150 * still get the callback, same as before this change, but do
151 * NOT trigger a safestate transition here - only a genuine
152 * DISAGREE does. */
153 cmp->config.on_disagreement(cmp->config.disagreement_context, local_result);
154 }
155 else
156 {
157 /* Nothing further to do for a non-DISAGREED result with no
158 * callback registered. */
159 }
160
162}
163
165 rte_voting_result_t *result,
166 void *out_data, size_t *out_size)
167{
168 uint8_t buf_a[RTE_CROSS_COMPARATOR_MAX_MESSAGE_SIZE] = { 0 };
169 rte_channel_health_t health_a;
170 rte_channel_health_t health_b;
171 rte_voting_result_t local_result;
172
173 if ((cmp == NULL) || (data_size == 0U))
174 {
176 }
177 if (!cmp->initialized)
178 {
180 }
182 {
184 }
185 if (cmp->registered_count != 2U)
186 {
188 }
189
190 (void)rte_channel_get_health(cmp->channel_a, &health_a);
191 (void)rte_channel_get_health(cmp->channel_b, &health_b);
192
193 if ((!health_a.is_healthy) || (!health_b.is_healthy))
194 {
195 local_result = RTE_VOTING_INSUFFICIENT_QUORUM;
196 }
197 else
198 {
200 rte_status_t st_a = rte_channel_receive(cmp->channel_a, buf_a, data_size, cmp->config.channel_timeout_ms);
201 rte_status_t st_b = rte_channel_receive(cmp->channel_b, buf_b, data_size, cmp->config.channel_timeout_ms);
202
203 if ((st_a == RTE_STATUS_TIMEOUT) || (st_b == RTE_STATUS_TIMEOUT))
204 {
205 local_result = RTE_VOTING_TIMEOUT;
206 }
207 else if ((st_a != RTE_STATUS_OK) || (st_b != RTE_STATUS_OK))
208 {
209 local_result = RTE_VOTING_INSUFFICIENT_QUORUM;
210 }
211 else if (cross_comparator_data_equal(cmp, buf_a, buf_b, data_size))
212 {
213 local_result = RTE_VOTING_AGREED;
214 }
215 else
216 {
217 local_result = RTE_VOTING_DISAGREED;
218 }
219 }
220
221 return cross_comparator_finish(cmp, local_result, buf_a, data_size, result, out_data, out_size);
222}
223
225 const void *local_data, const void *peer_data,
226 size_t data_size,
227 rte_voting_result_t *result,
228 void *out_data, size_t *out_size)
229{
230 rte_voting_result_t local_result;
231
232 if ((cmp == NULL) || (local_data == NULL) || (peer_data == NULL) || (data_size == 0U))
233 {
235 }
236 if (!cmp->initialized)
237 {
239 }
241 {
243 }
244
245 local_result = cross_comparator_data_equal(cmp, local_data, peer_data, data_size) ? RTE_VOTING_AGREED
247
248 return cross_comparator_finish(cmp, local_result, local_data, data_size, result, out_data, out_size);
249}
250
252 uint32_t *healthy_count,
253 uint32_t *total_disagreements)
254{
255 uint32_t healthy;
257
258 if (cmp == NULL)
259 {
261 }
262
263 healthy = 0U;
264 if (cmp->registered_count >= 1U)
265 {
266 (void)rte_channel_get_health(cmp->channel_a, &h);
267 if (h.is_healthy)
268 {
269 healthy++;
270 }
271 }
272 if (cmp->registered_count >= 2U)
273 {
274 (void)rte_channel_get_health(cmp->channel_b, &h);
275 if (h.is_healthy)
276 {
277 healthy++;
278 }
279 }
280
281 if (healthy_count != NULL)
282 {
283 *healthy_count = healthy;
284 }
285 if (total_disagreements != NULL)
286 {
287 *total_disagreements = cmp->total_disagreements;
288 }
289 return RTE_STATUS_OK;
290}
291
293{
294 if (cmp == NULL)
295 {
296 return RTE_STATUS_OK;
297 }
298 cmp->initialized = false;
299 return RTE_STATUS_OK;
300}
rte_status_t rte_lifecycle_check_setup_allowed(void)
Convenience check for a setup-only constructor: call this as one of the first checks in any function ...
void rte_log_write(rte_log_level_t level, const char *tag, const char *message)
Emits one log message. Non-blocking; never fails the caller's control flow even if the message is dro...
Definition rte_log.c:108
@ RTE_LOG_LEVEL_ERROR
Definition rte_log.h:38
#define RTE_SAFESTATE(level, reason)
Explicitly enters the given safe-state level with a reason code, capturing the call site automaticall...
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_NOT_INITIALIZED
Definition rte_status.h:30
@ RTE_STATUS_HARDWARE_FAULT
Definition rte_status.h:36
@ RTE_STATUS_RESOURCE_EXHAUSTED
Definition rte_status.h:33
@ RTE_STATUS_TIMEOUT
Definition rte_status.h:32
rte_status_t rte_cross_comparator_register_channel(rte_cross_comparator_t *cmp, rte_channel_t *channel)
Registers one already-initialized channel (channel A, then B).
rte_status_t rte_cross_comparator_execute(rte_cross_comparator_t *cmp, size_t data_size, rte_voting_result_t *result, void *out_data, size_t *out_size)
Receives from both registered channels and compares them.
rte_status_t rte_cross_comparator_init(rte_cross_comparator_storage_t *storage, const rte_cross_comparator_config_t *config)
Initializes a cross-comparator with zero registered channels.
rte_cross_comparator_storage_t rte_cross_comparator_t
Opaque handle to a cross-comparator instance.
#define RTE_CROSS_COMPARATOR_MAX_MESSAGE_SIZE
Maximum payload size rte_cross_comparator_execute() supports.
rte_status_t rte_cross_comparator_get_aggregated_health(const rte_cross_comparator_t *cmp, uint32_t *healthy_count, uint32_t *total_disagreements)
Aggregated health across both registered channels.
rte_status_t rte_cross_comparator_execute_buffers(rte_cross_comparator_t *cmp, const void *local_data, const void *peer_data, size_t data_size, rte_voting_result_t *result, void *out_data, size_t *out_size)
Compares two already-in-hand data buffers directly, with the same on_disagreement-then-unconditional-...
rte_status_t rte_cross_comparator_destroy(rte_cross_comparator_t *cmp)
Destroys a cross-comparator instance.
rte_voting_result_t
Outcome of one rte_voter_receive() call.
Definition rte_voter.h:54
@ RTE_VOTING_AGREED
Definition rte_voter.h:56
@ RTE_VOTING_INSUFFICIENT_QUORUM
Definition rte_voter.h:62
@ RTE_VOTING_DISAGREED
Definition rte_voter.h:58
@ RTE_VOTING_TIMEOUT
Definition rte_voter.h:60
static rte_status_t cross_comparator_finish(rte_cross_comparator_t *cmp, rte_voting_result_t local_result, const void *agreed_data, size_t data_size, rte_voting_result_t *result, void *out_data, size_t *out_size)
Pairwise comparison between exactly 2 registered rte_channel_t links (ADR-025).
Process-wide application setup-phase lock (ADR-026).
OS Abstraction Layer - Logging/diagnostics service.
Safe-state transitions and checked assertions (ADR-004).
Health statistics for one vital channel.
Definition rte_channel.h:39
Configuration for rte_cross_comparator_init().
rte_safestate_reason_t safestate_reason
void(*) on_disagreement(void *context, rte_voting_result_t result)
rte_safestate_level_t safestate_level
Storage for one cross-comparator instance (opaque to caller). No dynamic memory.