Safe API Framework
Layered API framework for safety-related applications (ERTMS RBC reference targeting CENELEC EN 50128 SIL 4)
Loading...
Searching...
No Matches
sapi_cross_comparator.c
Go to the documentation of this file.
1
7#include <string.h>
8
13
14static bool cross_comparator_data_equal(const sapi_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 sapi_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 = sapi_lifecycle_check_setup_allowed();
42 if (lifecycle_status != SAPI_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 SAPI_STATUS_OK;
55}
56
58 sapi_channel_t *channel)
59{
60 sapi_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 = sapi_lifecycle_check_setup_allowed();
74 if (lifecycle_status != SAPI_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 SAPI_STATUS_OK;
94}
95
98 void *out_data, size_t *out_size)
99{
102 sapi_channel_health_t health_a;
103 sapi_channel_health_t health_b;
104 sapi_status_t st_a;
105 sapi_status_t st_b;
106 sapi_voting_result_t local_result;
107
108 if ((cmp == NULL) || (data_size == 0U))
109 {
111 }
112 if (!cmp->initialized)
113 {
115 }
117 {
119 }
120 if (cmp->registered_count != 2U)
121 {
123 }
124
125 (void)sapi_channel_get_health(cmp->channel_a, &health_a);
126 (void)sapi_channel_get_health(cmp->channel_b, &health_b);
127
128 if ((!health_a.is_healthy) || (!health_b.is_healthy))
129 {
130 local_result = SAPI_VOTING_INSUFFICIENT_QUORUM;
131 }
132 else
133 {
134 st_a = sapi_channel_receive(cmp->channel_a, buf_a, data_size, cmp->config.channel_timeout_ms);
135 st_b = sapi_channel_receive(cmp->channel_b, buf_b, data_size, cmp->config.channel_timeout_ms);
136
137 if ((st_a == SAPI_STATUS_TIMEOUT) || (st_b == SAPI_STATUS_TIMEOUT))
138 {
139 local_result = SAPI_VOTING_TIMEOUT;
140 }
141 else if ((st_a != SAPI_STATUS_OK) || (st_b != SAPI_STATUS_OK))
142 {
143 local_result = SAPI_VOTING_INSUFFICIENT_QUORUM;
144 }
145 else if (cross_comparator_data_equal(cmp, buf_a, buf_b, data_size))
146 {
147 local_result = SAPI_VOTING_AGREED;
148 }
149 else
150 {
151 local_result = SAPI_VOTING_DISAGREED;
152 }
153 }
154
155 if (result != NULL)
156 {
157 *result = local_result;
158 }
159
160 if (local_result == SAPI_VOTING_AGREED)
161 {
162 if (out_data != NULL)
163 {
164 (void)memcpy(out_data, buf_a, data_size);
165 }
166 if (out_size != NULL)
167 {
168 *out_size = data_size;
169 }
170 return SAPI_STATUS_OK;
171 }
172
173 if (out_size != NULL)
174 {
175 *out_size = 0U;
176 }
177
178 if (local_result == SAPI_VOTING_DISAGREED)
179 {
180 cmp->total_disagreements++;
181 if (cmp->config.log_disagreements)
182 {
183 sapi_log_write(SAPI_LOG_LEVEL_ERROR, "cross_comparator", "channels disagreed");
184 }
185 if (cmp->config.trigger_safestate_on_disagreement)
186 {
188 }
189 }
190
191 if (cmp->config.on_disagreement != NULL)
192 {
193 cmp->config.on_disagreement(cmp->config.disagreement_context, local_result);
194 }
195
197}
198
200 uint32_t *healthy_count,
201 uint32_t *total_disagreements)
202{
203 uint32_t healthy;
205
206 if (cmp == NULL)
207 {
209 }
210
211 healthy = 0U;
212 if (cmp->registered_count >= 1U)
213 {
214 (void)sapi_channel_get_health(cmp->channel_a, &h);
215 if (h.is_healthy)
216 {
217 healthy++;
218 }
219 }
220 if (cmp->registered_count >= 2U)
221 {
222 (void)sapi_channel_get_health(cmp->channel_b, &h);
223 if (h.is_healthy)
224 {
225 healthy++;
226 }
227 }
228
229 if (healthy_count != NULL)
230 {
231 *healthy_count = healthy;
232 }
233 if (total_disagreements != NULL)
234 {
235 *total_disagreements = cmp->total_disagreements;
236 }
237 return SAPI_STATUS_OK;
238}
239
241{
242 if (cmp == NULL)
243 {
244 return SAPI_STATUS_OK;
245 }
246 cmp->initialized = false;
247 return SAPI_STATUS_OK;
248}
sapi_status_t sapi_lifecycle_check_setup_allowed(void)
Convenience check for a setup-only constructor: call this as one of the first checks in any function ...
void sapi_log_write(sapi_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 sapi_log.c:108
@ SAPI_LOG_LEVEL_ERROR
Definition sapi_log.h:38
#define SAPI_SAFESTATE(level, reason)
Explicitly enters the given safe-state level with a reason code, capturing the call site automaticall...
#define SAPI_SAFESTATE_REASON_UNSPECIFIED
@ SAPI_SAFESTATE_LEVEL_SAFE
sapi_status_t
Common result/status codes.
Definition sapi_status.h:27
@ SAPI_STATUS_HARDWARE_FAULT
Definition sapi_status.h:36
@ SAPI_STATUS_TIMEOUT
Definition sapi_status.h:32
@ SAPI_STATUS_INVALID_PARAM
Definition sapi_status.h:29
@ SAPI_STATUS_RESOURCE_EXHAUSTED
Definition sapi_status.h:33
@ SAPI_STATUS_NOT_INITIALIZED
Definition sapi_status.h:30
@ SAPI_STATUS_OK
Definition sapi_status.h:28
sapi_status_t sapi_cross_comparator_get_aggregated_health(const sapi_cross_comparator_t *cmp, uint32_t *healthy_count, uint32_t *total_disagreements)
Aggregated health across both registered channels.
#define SAPI_CROSS_COMPARATOR_MAX_MESSAGE_SIZE
Maximum payload size sapi_cross_comparator_execute() supports.
sapi_status_t sapi_cross_comparator_init(sapi_cross_comparator_storage_t *storage, const sapi_cross_comparator_config_t *config)
Initializes a cross-comparator with zero registered channels.
sapi_cross_comparator_storage_t sapi_cross_comparator_t
Opaque handle to a cross-comparator instance.
sapi_status_t sapi_cross_comparator_destroy(sapi_cross_comparator_t *cmp)
Destroys a cross-comparator instance.
sapi_status_t sapi_cross_comparator_register_channel(sapi_cross_comparator_t *cmp, sapi_channel_t *channel)
Registers one already-initialized channel (channel A, then B).
sapi_status_t sapi_cross_comparator_execute(sapi_cross_comparator_t *cmp, size_t data_size, sapi_voting_result_t *result, void *out_data, size_t *out_size)
Receives from both registered channels and compares them.
sapi_voting_result_t
Outcome of one sapi_voter_receive() call.
Definition sapi_voter.h:45
@ SAPI_VOTING_DISAGREED
Definition sapi_voter.h:49
@ SAPI_VOTING_INSUFFICIENT_QUORUM
Definition sapi_voter.h:53
@ SAPI_VOTING_TIMEOUT
Definition sapi_voter.h:51
@ SAPI_VOTING_AGREED
Definition sapi_voter.h:47
Pairwise comparison between exactly 2 registered sapi_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.
Configuration for sapi_cross_comparator_init().
void(*) on_disagreement(void *context, sapi_voting_result_t result)
Storage for one cross-comparator instance (opaque to caller). No dynamic memory.