STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_sensor_state_machine.cpp
Go to the documentation of this file.
1/**
2 * Sensor state machine unit tests
3 *
4 * Tests the sensor state machine transitions, packet processing, and
5 * heartbeat state encoding.
6 *
7 * Rather than including SensorHotfireCore.h directly (which pulls in
8 * heavy ESP32 dependencies), we extract the testable pure-logic functions
9 * here. This mirrors the approach used for the actuator tests.
10 */
11#include <unity.h>
12
13#include <cstring>
14#include <vector>
15
16// DAQv2-Comms
17#include "DiabloEnums.h"
18#include "DiabloPacketUtils.cpp"
19#include "DiabloPacketUtils.h"
20#include "DiabloPackets.h"
21
22// ---------------------------------------------------------------------------
23// Replicate the sensor state machine enums and transition logic
24// (extracted from SensorHotfireCore.h for testability)
25// ---------------------------------------------------------------------------
26
27namespace SensorSM {
28
29enum class State : uint8_t {
31 Active = 2,
34};
35
43
44// Extracted config for testing
46 bool valid = false;
47 uint8_t num_sensors = 0;
48 uint8_t sensor_ids[10] = {};
49 uint8_t reference_voltage = 0;
50 bool necessary_for_abort = false;
53};
54
55// Replicate applyPacketTransition from SensorHotfireCore.h
57 bool necessary_for_abort,
58 bool run_self_test) {
59 switch (state) {
62 if (run_self_test) {
63 return State::SelfTest;
64 } else {
65 return State::Active;
66 }
67 }
68 break;
69 case State::Active:
71 necessary_for_abort) {
73 }
74 break;
77 return State::Active;
78 }
79 break;
80 }
81 return state; // No transition
82}
83
84// Replicate readPacketHeader from SensorHotfireCore.h
85static IncomingPacketKind readPacketHeader(const uint8_t* buf, size_t len) {
86 if (len < sizeof(daq::PacketHeader))
88 daq::PacketHeader hdr;
89 memcpy(&hdr, buf, sizeof(hdr));
90 switch (hdr.packet_type) {
97 default:
99 }
100}
101
102// Replicate processIncomingPacket (simplified, testing-focused)
104 const uint8_t* buf,
105 size_t len) {
106 IncomingPacketKind kind = readPacketHeader(buf, len);
107
109 // Parse the config packet
110 daq::PacketHeader hdr_out;
111 std::vector<uint8_t> ids;
112 uint8_t ref;
113 bool abort_needed;
114 uint32_t controller_ip;
115 uint8_t serial;
116 if (daq::parse_sensor_config_packet(buf, len, hdr_out, ids, ref,
117 abort_needed, controller_ip,
118 serial)) {
119 stored_config.valid = true;
120 stored_config.num_sensors = ids.size();
121 for (size_t i = 0; i < ids.size() && i < 10; i++) {
122 stored_config.sensor_ids[i] = ids[i];
123 }
124 stored_config.reference_voltage = ref;
125 stored_config.necessary_for_abort = abort_needed;
126 stored_config.actuator_controller_ip = controller_ip;
127 stored_config.enable_serial_printing = serial;
128 }
129 }
130
131 return kind;
132}
133
134// Replicate the heartbeat state→BoardState mapping from loop()
149
150} // namespace SensorSM
151
152// ===========================================================================
153// State transition tests
154// ===========================================================================
155
160
167
174
181
188
195
202
209
216
217// ===========================================================================
218// processIncomingPacket — parse raw buffers into packet kinds
219// ===========================================================================
220
222 daq::PacketHeader hdr;
223 hdr.packet_type = daq::PacketType::SERVER_HEARTBEAT;
224 hdr.version = 0;
225 hdr.timestamp = 1000;
226 daq::ServerHeartbeatPacket srv;
227 srv.engine_state = daq::EngineState::SAFE;
228
229 uint8_t buf[64];
230 memcpy(buf, &hdr, sizeof(hdr));
231 memcpy(buf + sizeof(hdr), &srv, sizeof(srv));
232 size_t total = sizeof(hdr) + sizeof(srv);
233
235 auto kind = SensorSM::processIncomingPacket(cfg, buf, total);
237 (int)kind);
238}
239
241 std::vector<uint8_t> sensor_ids = {1, 2, 3};
242 uint8_t buf[512];
243 size_t n = daq::create_sensor_config_packet(sensor_ids, 0, false, 0, 1,
244 9001u, buf, sizeof(buf));
245 TEST_ASSERT_GREATER_THAN(0, n);
246
248 auto kind = SensorSM::processIncomingPacket(cfg, buf, n);
249 TEST_ASSERT_EQUAL((int)SensorSM::IncomingPacketKind::SensorConfig,
250 (int)kind);
251
252 TEST_ASSERT_TRUE(cfg.valid);
253 TEST_ASSERT_EQUAL(3, cfg.num_sensors);
254 TEST_ASSERT_EQUAL(1, cfg.sensor_ids[0]);
255 TEST_ASSERT_EQUAL(2, cfg.sensor_ids[1]);
256 TEST_ASSERT_EQUAL(3, cfg.sensor_ids[2]);
257}
258
260 std::vector<uint8_t> sensor_ids = {5, 10};
261 uint32_t controller_ip = 0xC0A80232;
262 uint8_t buf[512];
264 sensor_ids, 1, true, controller_ip, 1, 9002u, buf, sizeof(buf));
265 TEST_ASSERT_GREATER_THAN(0, n);
266
268 auto kind = SensorSM::processIncomingPacket(cfg, buf, n);
269 TEST_ASSERT_EQUAL((int)SensorSM::IncomingPacketKind::SensorConfig,
270 (int)kind);
271
272 TEST_ASSERT_TRUE(cfg.valid);
273 TEST_ASSERT_TRUE(cfg.necessary_for_abort);
274 TEST_ASSERT_EQUAL(1, cfg.reference_voltage);
275 TEST_ASSERT_EQUAL_HEX32(0xC0A80232, cfg.actuator_controller_ip);
276 TEST_ASSERT_EQUAL(1, cfg.enable_serial_printing);
277}
278
280 daq::PacketHeader hdr;
281 hdr.packet_type = daq::PacketType::CLEAR_ABORT;
282 hdr.version = 0;
283 hdr.timestamp = 0;
284 uint8_t buf[16];
285 memcpy(buf, &hdr, sizeof(hdr));
286
288 auto kind = SensorSM::processIncomingPacket(cfg, buf, sizeof(hdr));
289 TEST_ASSERT_EQUAL((int)SensorSM::IncomingPacketKind::ClearAbort, (int)kind);
290}
291
293 uint8_t buf[2] = {0, 0};
295 auto kind = SensorSM::processIncomingPacket(cfg, buf, 2);
296 TEST_ASSERT_EQUAL((int)SensorSM::IncomingPacketKind::None, (int)kind);
297}
298
299// ===========================================================================
300// Heartbeat state encoding — verify state→BoardState mapping
301// ===========================================================================
302
304 struct {
306 daq::BoardState expected;
307 } mappings[] = {
312 };
313
314 for (auto& m : mappings) {
316 TEST_ASSERT_EQUAL_MESSAGE(
317 (int)m.expected, (int)got,
318 "State mapping mismatch in getBoardStateForHeartbeat");
319
320 // Also create → parse heartbeat to verify wire encoding
321 daq::BoardHeartbeatPacket hb{};
322 hb.board_id = 42;
323 hb.engine_state = daq::EngineState::SAFE;
324 hb.board_state = got;
325
326 uint8_t buf[512];
327 size_t n =
328 daq::create_board_heartbeat_packet(hb, 9003u, buf, sizeof(buf));
329 TEST_ASSERT_GREATER_THAN(0, n);
330
331 daq::PacketHeader hdr_out;
332 daq::BoardHeartbeatPacket hb_out;
333 TEST_ASSERT_TRUE(
334 daq::parse_board_heartbeat_packet(buf, n, hdr_out, hb_out));
335 TEST_ASSERT_EQUAL((int)m.expected, (int)hb_out.board_state);
336 TEST_ASSERT_EQUAL(42, hb_out.board_id);
337 TEST_ASSERT_EQUAL((int)daq::EngineState::SAFE,
338 (int)hb_out.engine_state);
339 }
340}
341
342// ===========================================================================
343// Unity runner
344// ===========================================================================
345
346void setUp() {
347}
348void tearDown() {
349}
350
351int main(int argc, char** argv) {
352 UNITY_BEGIN();
353
354 // State transitions
364
365 // processIncomingPacket
371
372 // Heartbeat state encoding
374
375 return UNITY_END();
376}
static StoredConfig stored_config
static ActuatorControllerState state
Definition main.cpp:57
static daq::BoardState getBoardStateForHeartbeat()
Definition main.cpp:167
Sensor state machine unit tests.
static daq::BoardState getBoardStateForHeartbeat(State state)
static IncomingPacketKind readPacketHeader(const uint8_t *buf, size_t len)
static State applyPacketTransition(State state, IncomingPacketKind kind, bool necessary_for_abort, bool run_self_test)
static IncomingPacketKind processIncomingPacket(StoredConfig &stored_config, const uint8_t *buf, size_t len)
bool parse_sensor_config_packet(const uint8_t *buffer, size_t buffer_size, PacketHeader &header_out, std::vector< uint8_t > &sensor_ids_out, uint8_t &reference_voltage_out, bool &necessary_for_abort_out, uint32_t &controller_ip_out, uint8_t &enable_serial_printing_out)
Parses a Sensor Config packet from buffer.
bool parse_board_heartbeat_packet(const uint8_t *buffer, size_t buffer_size, PacketHeader &header_out, BoardHeartbeatPacket &data_out)
Parses a Board Heartbeat packet from buffer.
size_t create_board_heartbeat_packet(const BoardHeartbeatPacket &data, uint32_t timestamp_ms, uint8_t *buffer, size_t buffer_size)
Creates a complete Board Heartbeat packet in the provided buffer.
BoardState
Defines the operational state of a board.
Definition DiabloEnums.h:31
size_t create_sensor_config_packet(const std::vector< uint8_t > &sensor_ids, uint8_t reference_voltage, bool necessary_for_abort, uint32_t controller_ip, uint8_t enable_serial_printing, uint32_t timestamp_ms, uint8_t *buffer, size_t buffer_size)
Creates a complete Sensor Config packet in the provided buffer.
*brief user defined string literal for JSON values *sa std::size_t n
Definition json.hpp:24474
int main()
void test_sensor_noconn_abort_with_necessary()
void test_sensor_initial_state()
void test_sensor_clear_abort_in_active_stays()
void test_sensor_heartbeat_stays_waiting()
void test_sensor_process_sensor_config_packet()
void test_sensor_process_clear_abort_packet()
void test_sensor_process_too_short_returns_none()
void test_sensor_config_transitions_to_active()
void test_sensor_process_sensor_config_with_abort()
void test_sensor_process_server_heartbeat_packet()
void test_sensor_noconn_abort_without_necessary()
void test_sensor_none_packet_doesnt_transition()
void test_sensor_clear_abort_returns_to_active()
void test_sensor_config_transitions_to_selftest()
void test_sensor_heartbeat_state_mapping_table()