STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
self_test.c
Go to the documentation of this file.
1#include "self_test.h"
2
3#include <stddef.h>
4#include <string.h>
5
6static bool interval_valid(uint32_t interval) {
7 return interval > 0U && interval <= INT32_MAX;
8}
9
10static bool elapsed(uint32_t now, uint32_t then, uint32_t interval) {
11 return (uint32_t)(now - then) >= interval;
12}
13
21
22static bool config_valid(const self_test_config_t* c) {
23 if (!c || c->device_count > SELF_TEST_MAX_DEVICES ||
32 c->probe_max_attempts == 0U || c->publication_max_attempts == 0U ||
34 return false;
35 }
36 for (uint8_t i = 0; i < c->device_count; ++i) {
37 for (uint8_t j = 0; j < i; ++j) {
38 if (c->devices[i].device_id == c->devices[j].device_id) {
39 return false;
40 }
41 }
42 }
43 return true;
44}
45
46static void aggregate(self_test_t* t) {
47 t->status.probes_complete = true;
48 for (uint8_t i = 0; i < t->config.device_count; ++i) {
49 if (!t->status.devices[i].complete) {
50 t->status.probes_complete = false;
51 }
52 }
53 bool required = false;
54 bool pending = false;
55 t->status.probes_outcome = SELF_TEST_NOT_CONFIGURED;
56 for (uint8_t i = 0; i < t->config.device_count; ++i) {
57 const self_test_device_status_t* d = &t->status.devices[i];
58 if (!d->required) {
59 continue;
60 }
61 required = true;
62 if (!d->complete) {
63 pending = true;
64 } else if (d->reply.result != SELF_TEST_PASS) {
65 t->status.probes_outcome = SELF_TEST_FAIL;
66 return;
67 }
68 }
69 if (required) {
70 t->status.probes_outcome = pending ? SELF_TEST_PENDING : SELF_TEST_PASS;
71 }
72}
73
74bool self_test_reset(self_test_t* t, uint32_t now_ms) {
75 if (!t || !t->initialized) {
76 return false;
77 }
78 memset(&t->status, 0, sizeof(t->status));
79 memset(t->probes, 0, sizeof(t->probes));
80 t->status.now_ms = now_ms;
81 t->status.device_count = t->config.device_count;
82 for (uint8_t i = 0; i < t->config.device_count; ++i) {
83 t->status.devices[i].device_id = t->config.devices[i].device_id;
84 t->status.devices[i].required = t->config.devices[i].required;
85 }
86 t->session_started_ms = now_ms;
87 t->next_probe = 0;
88 t->last_heartbeat_ms = now_ms;
89 t->last_battery_ms = now_ms;
90 t->last_publication_ms = now_ms;
91 t->heartbeat_started = false;
92 t->battery_started = false;
93 t->publication_retry = false;
94 aggregate(t);
95 return true;
96}
97
99 const self_test_port_t* port, void* context,
100 uint32_t now_ms) {
101 if (!t) {
102 return false;
103 }
104 memset(t, 0, sizeof(*t));
105 if (!port || !config_valid(config)) {
106 return false;
107 }
108 t->config = *config;
109 if (t->config.session_timeout_ms == 0U) {
110 t->config.session_timeout_ms = SELF_TEST_DEFAULT_SESSION_TIMEOUT_MS;
111 }
112 t->port = *port;
113 t->context = context;
114 t->initialized = true;
115 return self_test_reset(t, now_ms);
116}
117
118static void finish_probe(self_test_t* t, uint8_t index, uint32_t now_ms) {
119 self_test_device_status_t* d = &t->status.devices[index];
120 self_test_probe_state_t* p = &t->probes[index];
121 if ((d->reply.result == SELF_TEST_FAIL ||
123 d->attempts < t->config.probe_max_attempts) {
124 p->retry_wait = true;
125 p->retry_at_ms = now_ms;
126 } else {
127 d->complete = true;
128 }
129}
130
131static void probe_one(self_test_t* t, uint32_t now_ms) {
132 for (uint8_t n = 0; n < t->config.device_count; ++n) {
133 uint8_t i = t->next_probe;
134 t->next_probe = (uint8_t)((i + 1U) % t->config.device_count);
135 self_test_device_status_t* d = &t->status.devices[i];
136 self_test_probe_state_t* p = &t->probes[i];
137 if (d->complete) {
138 continue;
139 }
140 if (p->retry_wait) {
141 if (!elapsed(now_ms, p->retry_at_ms, t->config.retry_interval_ms)) {
142 continue;
143 }
144 p->retry_wait = false;
145 p->started = false;
146 }
147 if (!p->started) {
148 p->started = true;
149 p->started_at_ms = now_ms;
150 ++d->attempts;
151 } else if (elapsed(now_ms, p->started_at_ms,
152 t->config.probe_timeout_ms)) {
154 finish_probe(t, i, now_ms);
155 return;
156 }
157 if (t->port.probe) {
158 uint32_t remaining = t->config.probe_timeout_ms -
159 (uint32_t)(now_ms - p->started_at_ms);
160 uint32_t session_remaining =
161 t->config.session_timeout_ms -
162 (uint32_t)(now_ms - t->session_started_ms);
163 if (session_remaining < remaining) {
164 remaining = session_remaining;
165 }
166 uint32_t budget = t->config.callback_budget_ms;
167 if (remaining < budget) {
168 budget = remaining;
169 }
170 d->reply = checked_reply(t->port.probe(
171 t->context, d->device_id, d->attempts, now_ms, budget));
172 } else {
174 }
175 if (d->reply.result != SELF_TEST_PENDING) {
176 finish_probe(t, i, now_ms);
177 }
178 return;
179 }
180}
181
182static void sample_battery(self_test_t* t, uint32_t now_ms) {
183 if (!t->battery_started ||
184 elapsed(now_ms, t->last_battery_ms, t->config.battery_interval_ms)) {
185 t->battery_started = true;
186 t->last_battery_ms = now_ms;
187 if (t->port.read_battery) {
188 t->status.battery = t->port.read_battery(
189 t->context, now_ms, t->config.callback_budget_ms);
190 t->status.battery.reply = checked_reply(t->status.battery.reply);
191 if (t->status.battery.reply.result == SELF_TEST_PASS &&
192 !t->status.battery.valid) {
193 t->status.battery.reply = (self_test_reply_t){
195 }
196 } else {
197 t->status.battery =
199 }
200 t->status.battery.valid =
201 t->status.battery.valid &&
202 t->status.battery.reply.result == SELF_TEST_PASS;
203 }
204 if (t->status.battery.valid &&
205 elapsed(now_ms, t->status.battery.sampled_at_ms,
206 t->config.battery_max_age_ms)) {
207 t->status.battery.valid = false;
208 t->status.battery.reply = (self_test_reply_t){
210 }
211 if (!t->status.battery.valid) {
212 t->status.battery.raw_counts = 0;
213 }
214}
215
216static void publish(self_test_t* t, uint32_t now_ms) {
217 if (!t->heartbeat_started || elapsed(now_ms, t->last_heartbeat_ms,
218 t->config.heartbeat_interval_ms)) {
219 t->heartbeat_started = true;
220 t->last_heartbeat_ms = now_ms;
221 t->status.publication_attempts = 0;
222 t->publication_retry = true;
223 } else if (!t->publication_retry || !elapsed(now_ms, t->last_publication_ms,
224 t->config.retry_interval_ms)) {
225 return;
226 }
227 t->last_publication_ms = now_ms;
228 ++t->status.publication_attempts;
229 t->status.publication =
230 t->port.publish_status
231 ? checked_reply(t->port.publish_status(
232 t->context, &t->status, t->config.callback_budget_ms))
234 self_test_result_t result = t->status.publication.result;
235 t->publication_retry =
238 t->status.publication_attempts < t->config.publication_max_attempts;
239 if (result == SELF_TEST_PENDING && !t->publication_retry) {
240 t->status.publication.result = SELF_TEST_TIMEOUT;
241 }
242}
243
244void self_test_tick(self_test_t* t, uint32_t now_ms) {
245 if (!t || !t->initialized) {
246 return;
247 }
248 t->status.now_ms = now_ms;
249 if (!t->status.probes_complete) {
250 if (elapsed(now_ms, t->session_started_ms,
251 t->config.session_timeout_ms)) {
252 t->status.deadline_expired = true;
253 for (uint8_t i = 0; i < t->config.device_count; ++i) {
254 self_test_device_status_t* d = &t->status.devices[i];
255 if (!d->complete) {
258 d->complete = true;
259 }
260 }
261 } else {
262 probe_one(t, now_ms);
263 }
264 }
265 aggregate(t);
266 sample_battery(t, now_ms);
267 publish(t, now_ms);
268}
269
271 return t && t->initialized ? &t->status : NULL;
272}
static const emmc_port_t port
Definition emmc_stm32.c:210
static bool config_valid
Definition main.cpp:66
int32_t p
Definition flight_e2e.c:78
#define NULL
Definition usbd_def.h:44
static const idle_config_t config
Definition idle_task.c:17
json_pointer result
Definition json.hpp:14064
*brief user defined string literal for JSON values *sa std::size_t n
Definition json.hpp:24474
bool self_test_init(self_test_t *t, const self_test_config_t *config, const self_test_port_t *port, void *context, uint32_t now_ms)
Definition self_test.c:98
static void aggregate(self_test_t *t)
Definition self_test.c:46
void self_test_tick(self_test_t *t, uint32_t now_ms)
Definition self_test.c:244
bool self_test_reset(self_test_t *t, uint32_t now_ms)
Definition self_test.c:74
static self_test_reply_t checked_reply(self_test_reply_t reply)
Definition self_test.c:14
static void publish(self_test_t *t, uint32_t now_ms)
Definition self_test.c:216
static bool elapsed(uint32_t now, uint32_t then, uint32_t interval)
Definition self_test.c:10
const self_test_status_t * self_test_status(const self_test_t *t)
Definition self_test.c:270
static bool interval_valid(uint32_t interval)
Definition self_test.c:6
static void probe_one(self_test_t *t, uint32_t now_ms)
Definition self_test.c:131
static void finish_probe(self_test_t *t, uint8_t index, uint32_t now_ms)
Definition self_test.c:118
static void sample_battery(self_test_t *t, uint32_t now_ms)
Definition self_test.c:182
#define SELF_TEST_DEFAULT_SESSION_TIMEOUT_MS
Definition self_test.h:10
static self_test_reply_t reply(self_test_result_t result, int32_t diagnostic)
#define SELF_TEST_DIAG_BAD_REPLY
#define SELF_TEST_MAX_DEVICES
#define SELF_TEST_DIAG_STALE_BATTERY
#define SELF_TEST_DIAG_SESSION_TIMEOUT
self_test_result_t
@ SELF_TEST_FAIL
@ SELF_TEST_NOT_CONFIGURED
@ SELF_TEST_NOT_IMPLEMENTED
@ SELF_TEST_PENDING
@ SELF_TEST_PASS
@ SELF_TEST_TIMEOUT
uint32_t heartbeat_interval_ms
Definition self_test.h:22
uint32_t session_timeout_ms
Definition self_test.h:30
self_test_device_t devices[SELF_TEST_MAX_DEVICES]
Definition self_test.h:19
uint32_t callback_budget_ms
Definition self_test.h:25
uint32_t retry_interval_ms
Definition self_test.h:21
uint32_t battery_interval_ms
Definition self_test.h:23
uint32_t probe_timeout_ms
Definition self_test.h:20
uint8_t device_count
Definition self_test.h:18
uint8_t publication_max_attempts
Definition self_test.h:28
uint32_t battery_max_age_ms
Definition self_test.h:24
uint8_t probe_max_attempts
Definition self_test.h:27
uint32_t tick_budget_ms
Definition self_test.h:26
self_test_reply_t reply
uint16_t device_id
Definition self_test.h:13
self_test_result_t result