GCC Code Coverage Report


Directory: Svc/FpySequencer/
File: FpySequencer.cpp
Date: 2026-11-03 21:18:57
Exec Total Coverage
Lines: 238 290 82.1%
Functions: 27 29 93.1%
Branches: 246 279 88.2%

Line Branch Exec Source
1 // ======================================================================
2 // \title FpySequencer.cpp
3 // \author zimri.leisher
4 // \brief cpp file for FpySequencer component implementation class
5 // ======================================================================
6
7 #include <Fw/Prm/ParamValid.hpp>
8 #include <Svc/FpySequencer/FpySequencer.hpp>
9 #include <config/CommandDispatcherImplCfg.hpp>
10 #include <new>
11
12 namespace Svc {
13
14 // ----------------------------------------------------------------------
15 // Construction, initialization, and destruction
16 // ----------------------------------------------------------------------
17
18 347 FpySequencer ::FpySequencer(const char* const compName)
19 : FpySequencerComponentBase(compName),
20
1/1
✓ Branch 4 taken 347 times.
347 m_sequenceBuffer(),
21 347 m_allocatorId(0),
22
1/1
✓ Branch 4 taken 347 times.
347 m_sequenceFilePath("<invalid_seq>"),
23
1/1
✓ Branch 5 taken 347 times.
347 m_sequenceObj(),
24
1/1
✓ Branch 5 taken 347 times.
347 m_computedCRC(),
25 347 m_totalExpectedArgSize(0),
26
1/1
✓ Branch 5 taken 347 times.
347 m_sequenceBlockState(),
27 347 m_savedOpCode(0),
28 347 m_savedCmdSeq(0),
29
1/1
✓ Branch 4 taken 347 times.
347 m_sequenceArgs(0, 0),
30
1/1
✓ Branch 5 taken 347 times.
347 m_goalState(),
31 347 m_sequencesStarted(0),
32 347 m_statementsDispatched(0),
33 347 m_checkTimersQueued(false),
34 347 m_tlmWriteQueued(false),
35
1/1
✓ Branch 7 taken 347 times.
347 m_runtime(),
36 347 m_breakpoint(),
37 347 m_debug(),
38
1/1
✓ Branch 7 taken 347 times.
347 m_tlm(),
39
2/2
✓ Branch 4 taken 83280 times.
✓ Branch 5 taken 347 times.
83627 m_sandboxDir(),
40
1/1
✓ Branch 11 taken 347 times.
1041 m_sandboxConfigured(false) {}
41
42 694 FpySequencer ::~FpySequencer() {}
43
44 4 void FpySequencer::configureSandbox(const char* directory) {
45 4 FW_ASSERT(directory != nullptr);
46 const Os::FilePathUtils::Status status =
47 4 Os::FilePathUtils::resolveDirectoryFromCwd(directory, this->m_sandboxDir, sizeof(this->m_sandboxDir));
48 4 FW_ASSERT(status == Os::FilePathUtils::VALID, static_cast<FwAssertArgType>(status));
49 4 this->m_sandboxConfigured = true;
50 4 }
51
52 81 bool FpySequencer::checkSandbox(const Fw::StringBase& fileName) {
53
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 81 times.
✓ Branch 5 taken 76 times.
✓ Branch 6 taken 5 times.
81 if (!this->m_sandboxConfigured) {
54 76 return true;
55 }
56 5 char resolved[Os::FilePathUtils::MAX_PATH_LENGTH];
57 const bool contained =
58
2/2
✓ Branch 11 taken 5 times.
✓ Branch 14 taken 5 times.
5 (Os::FilePathUtils::resolveFromCwd(fileName.toChar(), resolved, sizeof(resolved)) ==
59
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
10 Os::FilePathUtils::VALID) &&
60
3/3
✓ Branch 3 taken 5 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 3 times.
5 (Os::FilePathUtils::checkContainment(resolved, this->m_sandboxDir) == Os::FilePathUtils::VALID);
61
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 if (!contained) {
62
2/2
✓ Branch 8 taken 3 times.
✓ Branch 12 taken 3 times.
3 this->log_WARNING_HI_FileOutsideSandbox(fileName, Fw::LogStringArg(this->m_sandboxDir));
63 }
64 5 return contained;
65 }
66
67 //! Handler for command RUN
68 //!
69 //! Loads, validates and runs a sequence
70 47 void FpySequencer::RUN_cmdHandler(FwOpcodeType opCode, //!< The opcode
71 U32 cmdSeq, //!< The command sequence number
72 const Fw::CmdStringArg& fileName, //!< The name of the sequence file
73 const BlockState& block //!< Return command status when complete or not
74 ) {
75 // Empty args and delegate to RUN_ARGS handler
76
2/2
✓ Branch 8 taken 47 times.
✓ Branch 12 taken 47 times.
47 this->RUN_ARGS_cmdHandler(opCode, cmdSeq, fileName, block, Svc::SeqArgs{0, 0});
77 47 }
78
79 50 void FpySequencer ::RUN_ARGS_cmdHandler(FwOpcodeType opCode, //!< The opcode
80 U32 cmdSeq, //!< The command sequence number
81 const Fw::CmdStringArg& fileName, //!< The name of the sequence file
82 const Svc::BlockState& block, //!< Return command status when complete or not
83 const Svc::SeqArgs& args //!< Arguments to pass to the sequencer
84 ) {
85 // can only run a seq while in idle
86
3/3
✓ Branch 3 taken 50 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 49 times.
50 if (sequencer_getState() != State::IDLE) {
87
2/2
✓ Branch 7 taken 1 times.
✓ Branch 14 taken 1 times.
1 this->log_WARNING_HI_InvalidCommand(static_cast<I32>(sequencer_getState()));
88
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
89 1 return;
90 }
91
92
2/2
✓ Branch 4 taken 48 times.
✓ Branch 5 taken 1 times.
49 if (block == BlockState::BLOCK) {
93 // save the opCode and cmdSeq so we can respond later
94 48 this->m_savedOpCode = opCode;
95 48 this->m_savedCmdSeq = cmdSeq;
96 }
97
98 // Store args for pushArgsToStack action
99
2/2
✓ Branch 13 taken 49 times.
✓ Branch 17 taken 49 times.
49 this->sequencer_sendSignal_cmd_RUN(FpySequencer_SequenceExecutionArgs(fileName, block, args));
100
101 // only respond if the user doesn't want us to block further execution
102
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 48 times.
49 if (block == BlockState::NO_BLOCK) {
103
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
104 }
105 }
106
107 //! Handler for command VALIDATE
108 //!
109 //! Loads and validates a sequence
110 10 void FpySequencer::VALIDATE_cmdHandler(FwOpcodeType opCode, //!< The opcode
111 U32 cmdSeq, //!< The command sequence number
112 const Fw::CmdStringArg& fileName //!< The name of the sequence file
113 ) {
114
2/2
✓ Branch 8 taken 10 times.
✓ Branch 12 taken 10 times.
10 this->VALIDATE_ARGS_cmdHandler(opCode, cmdSeq, fileName, Svc::SeqArgs{0, 0});
115 10 }
116
117 //! Handler implementation for command VALIDATE_ARGS
118 //!
119 //! Loads and validates a sequence with arguments
120 18 void FpySequencer ::VALIDATE_ARGS_cmdHandler(FwOpcodeType opCode,
121 U32 cmdSeq,
122 const Fw::CmdStringArg& fileName,
123 const Svc::SeqArgs& buffer) {
124 // can only validate a seq while in idle
125
3/3
✓ Branch 3 taken 18 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 17 times.
18 if (sequencer_getState() != State::IDLE) {
126
2/2
✓ Branch 7 taken 1 times.
✓ Branch 14 taken 1 times.
1 this->log_WARNING_HI_InvalidCommand(static_cast<I32>(sequencer_getState()));
127
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
128 1 return;
129 }
130
131 // validate always blocks until finished, so save opcode/cmdseq
132 // so we can respond once done
133 17 this->m_savedOpCode = opCode;
134 17 this->m_savedCmdSeq = cmdSeq;
135
136 // VALIDATE_ARGS receives args via command interface
137 // Store args for pushArgsToStack action when RUN_VALIDATED is called
138
2/2
✓ Branch 9 taken 17 times.
✓ Branch 13 taken 17 times.
17 this->sequencer_sendSignal_cmd_VALIDATE(FpySequencer_SequenceExecutionArgs(fileName, BlockState::BLOCK, buffer));
139 }
140
141 5 void FpySequencer::RUN_VALIDATED_cmdHandler(FwOpcodeType opCode, //!< The opcode
142 U32 cmdSeq, //!< The command sequence number
143 const BlockState& block //!< Return command status when complete or not
144 ) {
145 // can only RUN_VALIDATED if we have validated and are awaiting this exact cmd
146
3/3
✓ Branch 3 taken 5 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 4 times.
5 if (sequencer_getState() != State::AWAITING_CMD_RUN_VALIDATED) {
147
2/2
✓ Branch 7 taken 1 times.
✓ Branch 14 taken 1 times.
1 this->log_WARNING_HI_InvalidCommand(static_cast<I32>(sequencer_getState()));
148
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
149 1 return;
150 }
151
152
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 2 times.
4 if (block == BlockState::BLOCK) {
153 // save the opCode and cmdSeq so we can respond later
154 2 this->m_savedOpCode = opCode;
155 2 this->m_savedCmdSeq = cmdSeq;
156 }
157
158
1/1
✓ Branch 5 taken 4 times.
8 this->sequencer_sendSignal_cmd_RUN_VALIDATED(
159
1/1
✓ Branch 12 taken 4 times.
8 FpySequencer_SequenceExecutionArgs(this->m_sequenceFilePath, block, this->m_sequenceArgs));
160
161 // only respond if the user doesn't want us to block further execution
162
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 2 times.
4 if (block == BlockState::NO_BLOCK) {
163
2/2
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
2 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
164 }
165 }
166
167 //! Handler for command CANCEL
168 //!
169 //! Cancels a running or validated sequence
170 5 void FpySequencer::CANCEL_cmdHandler(FwOpcodeType opCode, //!< The opcode
171 U32 cmdSeq //!< The command sequence number
172 ) {
173 // only state you can't cancel in is IDLE
174
3/3
✓ Branch 3 taken 5 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 4 times.
5 if (sequencer_getState() == State::IDLE) {
175
2/2
✓ Branch 7 taken 1 times.
✓ Branch 14 taken 1 times.
1 this->log_WARNING_HI_InvalidCommand(static_cast<I32>(sequencer_getState()));
176
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
177 1 return;
178 }
179
180 4 this->sequencer_sendSignal_cmd_CANCEL();
181
182 // cancel returns immediately and always succeeds
183
2/2
✓ Branch 6 taken 4 times.
✓ Branch 9 taken 4 times.
4 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
184 }
185
186 //! Handler for command SET_BREAKPOINT
187 //!
188 //! Sets the breakpoint which will pause the execution of the sequencer when
189 //! reached, until unpaused by the CONTINUE command. Will pause just before
190 //! dispatching the specified statement. This command is valid in all states. Breakpoint
191 //! settings are cleared after a sequence ends execution.
192 1 void FpySequencer::SET_BREAKPOINT_cmdHandler(FwOpcodeType opCode, //!< The opcode
193 U32 cmdSeq, //!< The command sequence number
194 U32 stmtIdx, //!< The statement index to pause execution before.
195 bool breakOnce //!< Whether or not to break only once at this breakpoint
196 ) {
197
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sequencer_sendSignal_cmd_SET_BREAKPOINT(FpySequencer_BreakpointArgs(true, breakOnce, stmtIdx));
198
199
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
200 1 }
201
202 //! Handler for command BREAK
203 //!
204 //! Pauses the execution of the sequencer, just before it is about to dispatch the next statement,
205 //! until unpaused by the CONTINUE command, or stepped by the STEP command. This command is only valid
206 //! in substates of the RUNNING state that are not RUNNING.PAUSED.
207 3 void FpySequencer::BREAK_cmdHandler(FwOpcodeType opCode, //!< The opcode
208 U32 cmdSeq //!< The command sequence number
209 ) {
210
14/24
✓ Branch 6 taken 3 times.
✓ Branch 9 taken 2 times.
✓ Branch 10 taken 1 times.
✓ Branch 14 taken 2 times.
✓ Branch 20 taken 1 times.
✓ Branch 21 taken 1 times.
✓ Branch 22 taken 2 times.
✓ Branch 23 taken 1 times.
✓ Branch 26 taken 2 times.
✓ Branch 27 taken 1 times.
✓ Branch 29 taken 3 times.
✗ Branch 30 not taken.
✓ Branch 33 taken 3 times.
✗ Branch 34 not taken.
✓ Branch 36 taken 2 times.
✓ Branch 37 taken 1 times.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
3 if (!this->isRunningState(this->sequencer_getState()) || this->sequencer_getState() == State::RUNNING_PAUSED) {
211 // can only break while running, and not paused
212
2/2
✓ Branch 7 taken 2 times.
✓ Branch 14 taken 2 times.
2 this->log_WARNING_HI_InvalidCommand(static_cast<I32>(sequencer_getState()));
213
2/2
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
2 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
214 2 return;
215 }
216 1 this->sequencer_sendSignal_cmd_BREAK();
217
218
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
219 }
220
221 //! Handler for command CONTINUE
222 //!
223 //! Continues the automatic execution of the sequence after it has been paused. If a breakpoint is still
224 //! set, it may pause again on that breakpoint. This command is only valid in the RUNNING.PAUSED state.
225 2 void FpySequencer::CONTINUE_cmdHandler(FwOpcodeType opCode, //!< The opcode
226 U32 cmdSeq //!< The command sequence number
227 ) {
228
3/3
✓ Branch 3 taken 2 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 1 times.
2 if (this->sequencer_getState() != State::RUNNING_PAUSED) {
229
2/2
✓ Branch 7 taken 1 times.
✓ Branch 14 taken 1 times.
1 this->log_WARNING_HI_InvalidCommand(static_cast<I32>(sequencer_getState()));
230
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
231 1 return;
232 }
233
234 1 this->sequencer_sendSignal_cmd_CONTINUE();
235
236
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
237 }
238
239 //! Handler for command CLEAR_BREAKPOINT
240 //!
241 //! Clears the breakpoint, but does not continue executing the sequence. This command
242 //! is valid in all states. This happens automatically when a sequence ends execution.
243 1 void FpySequencer::CLEAR_BREAKPOINT_cmdHandler(FwOpcodeType opCode, //!< The opcode
244 U32 cmdSeq //!< The command sequence number
245 ) {
246 1 this->sequencer_sendSignal_cmd_CLEAR_BREAKPOINT();
247 1 this->log_ACTIVITY_HI_BreakpointCleared();
248
249
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
250 1 }
251
252 //! Handler for command STEP
253 //!
254 //! Dispatches and awaits the result of the next directive, or ends the sequence if no more directives remain.
255 //! Returns to the RUNNING.PAUSED state if the directive executes successfully. This command is only valid in the
256 //! RUNNING.PAUSED state.
257 4 void FpySequencer::STEP_cmdHandler(FwOpcodeType opCode, //!< The opcode
258 U32 cmdSeq //!< The command sequence number
259 ) {
260
3/3
✓ Branch 3 taken 4 times.
✓ Branch 12 taken 2 times.
✓ Branch 13 taken 2 times.
4 if (this->sequencer_getState() != State::RUNNING_PAUSED) {
261
2/2
✓ Branch 7 taken 2 times.
✓ Branch 14 taken 2 times.
2 this->log_WARNING_HI_InvalidCommand(static_cast<I32>(sequencer_getState()));
262
2/2
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
2 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
263 2 return;
264 }
265
266 2 this->sequencer_sendSignal_cmd_STEP();
267
2/2
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
2 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
268 }
269
270 //! Handler for command DUMP_STACK_TO_FILE
271 //!
272 //! Writes the contents of the stack to a file. This command is only valid in the RUNNING.PAUSED state.
273 5 void FpySequencer::DUMP_STACK_TO_FILE_cmdHandler(FwOpcodeType opCode, //!< The opcode
274 U32 cmdSeq, //!< The command sequence number
275 const Fw::CmdStringArg& fileName //!< The name of the output file
276 ) {
277
3/3
✓ Branch 3 taken 5 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 4 times.
5 if (this->sequencer_getState() != State::RUNNING_PAUSED) {
278
2/2
✓ Branch 7 taken 1 times.
✓ Branch 14 taken 1 times.
1 this->log_WARNING_HI_InvalidCommand(static_cast<I32>(sequencer_getState()));
279
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
280 1 return;
281 }
282
3/3
✓ Branch 7 taken 4 times.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 3 times.
4 if (!this->checkSandbox(fileName)) {
283
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
284 1 return;
285 }
286
1/1
✓ Branch 2 taken 3 times.
3 Os::File sequenceFile;
287
1/1
✓ Branch 6 taken 3 times.
3 Os::File::Status status = sequenceFile.open(fileName.toChar(), Os::File::OPEN_WRITE);
288
289
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (status != Os::File::Status::OP_OK) {
290
1/1
✓ Branch 8 taken 1 times.
1 this->log_WARNING_HI_FileOpenError(fileName, static_cast<I32>(status));
291
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
292 1 return;
293 }
294
295 2 FwSizeType writeSize = static_cast<FwSizeType>(this->m_runtime.stack.size);
296
1/1
✓ Branch 4 taken 2 times.
2 status = sequenceFile.write(this->m_runtime.stack.bytes, writeSize);
297
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
2 if (status != Os::File::Status::OP_OK || writeSize != this->m_runtime.stack.size) {
298 ✗ this->log_WARNING_HI_FileWriteError(writeSize, fileName, static_cast<I32>(status));
299 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
300 ✗ return;
301 }
302
2/2
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
2 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
303 2 return;
304 3 }
305 //! Handler for input port checkTimers
306 9 void FpySequencer::checkTimers_handler(FwIndexType portNum, //!< The port number
307 U32 context //!< The call order
308 ) {
309 // only enqueue if a checkTimersInternal message is not already on the queue
310 9 bool expected = false;
311
2/2
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 3 times.
9 if (this->m_checkTimersQueued.compare_exchange_strong(expected, true)) {
312
1/1
✓ Branch 5 taken 6 times.
6 this->checkTimersInternal_internalInterfaceInvoke();
313 }
314 9 }
315
316 //! Internal interface handler for checkTimersInternal
317 5 void FpySequencer::checkTimersInternal_internalInterfaceHandler() {
318 5 FW_ASSERT(this->m_checkTimersQueued);
319 5 this->m_checkTimersQueued = false;
320 5 this->sequencer_sendSignal_checkTimersIn();
321 5 }
322
323 1 void FpySequencer::pingIn_handler(FwIndexType portNum, /*!< The port number*/
324 U32 key /*!< Value to return to pinger*/
325 ) {
326 // send ping response
327 1 this->pingOut_out(0, key);
328 1 }
329
330 //! Handler for input port cmdResponseIn
331 8 void FpySequencer::cmdResponseIn_handler(FwIndexType portNum, //!< The port number
332 FwOpcodeType opCode, //!< Command Op Code
333 U32 cmdSeq, //!< Command Sequence
334 const Fw::CmdResponse& response //!< The command response argument
335 ) {
336 // if we aren't in the RUNNING state:
337
3/3
✓ Branch 6 taken 8 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 7 times.
8 if (!this->isRunningState(sequencer_getState())) {
338 // must be a coding error from an outside component (off nom), or due to CANCEL while running a command (nom).
339 // because we can't be sure that it wasn't a nominal sequence of events leading to this, don't fail the
340 // sequence, just report it
341
2/2
✓ Branch 8 taken 1 times.
✓ Branch 15 taken 1 times.
1 this->log_WARNING_LO_CmdResponseWhileNotRunningSequence(static_cast<I32>(this->sequencer_getState()),
342 CmdDispatcherCfg::getEventOpcode(opCode), response);
343 1 return;
344 }
345
346 // okay, we're running a sequence. now let's use the cmdUid to check if the response was for a cmd
347 // from this sequence
348
349 // the cmdSeq arg is confusingly not the cmdSeq in this case, according to the current implementation
350 // of the CmdDisp. instead, it is the context that we passed in when we originally sent the cmd out.
351 // this context is in turn the cmdUid that we calculated just before sending it. rename the variable for
352 // clarity's sake
353 7 U32 cmdUid = cmdSeq;
354
355 // pull the sequence index (modulo 2^16) out of the cmdUid. see the comment in FpySequencer::dispatchCommand
356 // for info on the binary format of this cmdUid. as a reminder, this should be equal to the first 16 bits of
357 // the m_sequencesStarted variable
358 7 U16 sequenceIndex = static_cast<U16>((cmdUid & 0xFFFF0000) >> 16);
359 7 U16 currentSequenceIndex = static_cast<U16>(this->m_sequencesStarted & 0xFFFF);
360
361 // if it was from a different sequence:
362
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
7 if (sequenceIndex != currentSequenceIndex) {
363 1 this->log_WARNING_LO_CmdResponseFromOldSequence(CmdDispatcherCfg::getEventOpcode(opCode), response,
364 sequenceIndex, currentSequenceIndex);
365 1 return;
366 }
367
368 // okay, it was from this sequence. now if anything's wrong from this point on we should fail the sequence
369
370 // first, make sure we're actually awaiting a statement response
371
3/3
✓ Branch 3 taken 6 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 5 times.
6 if (this->sequencer_getState() != State::RUNNING_AWAITING_STATEMENT_RESPONSE) {
372 // okay, crap. something from this sequence responded, and we weren't awaiting anything. end it all
373 1 this->log_WARNING_HI_CmdResponseWhileNotAwaiting(CmdDispatcherCfg::getEventOpcode(opCode), response);
374 1 this->sequencer_sendSignal_stmtResponse_unexpected();
375 1 return;
376 }
377
378
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 4 times.
5 if (this->m_runtime.currentStatementOpcode != Fpy::DirectiveId::CONST_CMD &&
379
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
1 this->m_runtime.currentStatementOpcode != Fpy::DirectiveId::STACK_CMD) {
380 // we were not awaiting a cmd response, we were waiting for a directive
381 ✗ this->log_WARNING_HI_CmdResponseWhileAwaitingDirective(CmdDispatcherCfg::getEventOpcode(opCode), response,
382 ✗ this->m_runtime.currentStatementOpcode);
383 ✗ this->sequencer_sendSignal_stmtResponse_unexpected();
384 ✗ return;
385 }
386
387 // okay, we were awaiting a cmd response. were we awaiting this opcode?
388
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 4 times.
5 if (opCode != this->m_runtime.currentCmdOpcode) {
389 // we were not awaiting this opcode. coding error, likely on the part of the responding component or cmd
390 // dispatcher
391 2 this->log_WARNING_HI_WrongCmdResponseOpcode(CmdDispatcherCfg::getEventOpcode(opCode), response,
392 1 CmdDispatcherCfg::getEventOpcode(this->m_runtime.currentCmdOpcode));
393 1 this->sequencer_sendSignal_stmtResponse_unexpected();
394 1 return;
395 }
396
397 // okay, we were awaiting this opcode. but was it from this exact statement, or a different one with the same opcode
398 // in the same file?
399
400 // pull the cmd index (modulo 2^16) out of cmdUid. this should be equal to the first 16 bits of the
401 // m_statementsDispatched variable
402 4 U16 cmdIndex = static_cast<U16>(cmdUid & 0xFFFF);
403 // check for coding errors. at this point in the function, we have definitely dispatched a stmt
404 4 FW_ASSERT(this->m_statementsDispatched > 0);
405 4 U16 currentCmdIndex = static_cast<U16>((this->m_statementsDispatched) & 0xFFFF);
406
407
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (cmdIndex != currentCmdIndex) {
408 // we were not awaiting this exact statement, it was a different one with the same opcode. coding error
409 2 this->log_WARNING_HI_WrongCmdResponseIndex(CmdDispatcherCfg::getEventOpcode(opCode), response, cmdIndex,
410 currentCmdIndex);
411 2 this->sequencer_sendSignal_stmtResponse_unexpected();
412 2 return;
413 }
414
415 // okay, got the right cmd back. we have verified:
416 // 1) we are in the RUNNING state
417 // 2) the response is from this sequence
418 // 3) the response is from the correct opcode
419 // 4) the response is from the correct instance of that opcode in the sequence
420
421 // always succeed; the cmd response value is pushed to the stack so the sequence
422 // can branch on it if desired
423 2 this->sequencer_sendSignal_stmtResponse_success();
424
425 // push the cmd response to the stack so we can branch off of it
426 // the constCmd/stackCmd directive handlers guarantee there is room for this push
427
1/2
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
2 this->m_runtime.stack.push(static_cast<Fw::CmdResponse::SerialType>(response.e));
428 }
429
430 2 void FpySequencer ::seqCancelIn_handler(FwIndexType portNum) {
431 // only state you can't cancel in is IDLE
432
3/3
✓ Branch 3 taken 2 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 1 times.
2 if (sequencer_getState() == State::IDLE) {
433
2/2
✓ Branch 7 taken 1 times.
✓ Branch 14 taken 1 times.
1 this->log_WARNING_HI_InvalidSeqCancelCall(static_cast<I32>(sequencer_getState()));
434 1 return;
435 }
436 1 this->sequencer_sendSignal_cmd_CANCEL();
437 }
438
439 //! Handler for input port seqRunIn
440 3 void FpySequencer::seqRunIn_handler(FwIndexType portNum, const Fw::StringBase& filename, const Svc::SeqArgs& args) {
441 // can only run a seq while in idle
442
3/3
✓ Branch 3 taken 3 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 2 times.
3 if (sequencer_getState() != State::IDLE) {
443
2/2
✓ Branch 7 taken 1 times.
✓ Branch 14 taken 1 times.
1 this->log_WARNING_HI_InvalidSeqRunCall(static_cast<I32>(sequencer_getState()));
444 1 return;
445 }
446
447 // seqRunIn is never blocking - store args for pushArgsToStack action
448 // Args must be serialized in F' big-endian format by the caller before being sent
449
2/2
✓ Branch 6 taken 2 times.
✓ Branch 10 taken 2 times.
2 this->sequencer_sendSignal_cmd_RUN(FpySequencer_SequenceExecutionArgs(filename, BlockState::NO_BLOCK, args));
450 }
451
452 //! Handler for input port tlmWrite
453 5 void FpySequencer::tlmWrite_handler(FwIndexType portNum, //!< The port number
454 U32 context //!< The call order
455 ) {
456 // only enqueue if a tlmWriteInternal message is not already on the queue
457 5 bool expected = false;
458
2/2
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 2 times.
5 if (this->m_tlmWriteQueued.compare_exchange_strong(expected, true)) {
459
1/1
✓ Branch 5 taken 3 times.
3 this->tlmWriteInternal_internalInterfaceInvoke();
460 }
461 5 }
462
463 //! Overflow hook for tlmWriteInternal
464 ✗ void FpySequencer::tlmWriteInternal_overflowHook() {
465 ✗ this->m_tlmWriteQueued = false;
466 ✗ }
467
468 //! Internal interface handler for tlmWriteInternal
469 3 void FpySequencer::tlmWriteInternal_internalInterfaceHandler() {
470 3 FW_ASSERT(this->m_tlmWriteQueued);
471 3 this->m_tlmWriteQueued = false;
472
3/3
✓ Branch 6 taken 3 times.
✓ Branch 11 taken 3 times.
✓ Branch 18 taken 3 times.
3 this->tlmWrite_State(static_cast<FwEnumStoreType>(this->sequencer_getState()));
473
2/2
✓ Branch 6 taken 3 times.
✓ Branch 13 taken 3 times.
3 this->tlmWrite_StatementsDispatched(this->m_statementsDispatched);
474
2/2
✓ Branch 6 taken 3 times.
✓ Branch 13 taken 3 times.
3 this->tlmWrite_StatementsFailed(this->m_tlm.statementsFailed);
475
2/2
✓ Branch 6 taken 3 times.
✓ Branch 13 taken 3 times.
3 this->tlmWrite_SequencesCancelled(this->m_tlm.sequencesCancelled);
476
2/2
✓ Branch 6 taken 3 times.
✓ Branch 13 taken 3 times.
3 this->tlmWrite_SequencesSucceeded(this->m_tlm.sequencesSucceeded);
477
2/2
✓ Branch 6 taken 3 times.
✓ Branch 13 taken 3 times.
3 this->tlmWrite_SequencesFailed(this->m_tlm.sequencesFailed);
478
2/2
✓ Branch 6 taken 3 times.
✓ Branch 12 taken 3 times.
3 this->tlmWrite_LastDirectiveError(this->m_tlm.lastDirectiveError);
479
2/2
✓ Branch 6 taken 3 times.
✓ Branch 13 taken 3 times.
3 this->tlmWrite_DirectiveErrorIndex(this->m_tlm.directiveErrorIndex);
480
2/2
✓ Branch 6 taken 3 times.
✓ Branch 12 taken 3 times.
3 this->tlmWrite_DirectiveErrorId(this->m_tlm.directiveErrorId);
481
2/2
✓ Branch 6 taken 3 times.
✓ Branch 12 taken 3 times.
3 this->tlmWrite_SeqPath(this->m_sequenceFilePath);
482
483
2/2
✓ Branch 6 taken 3 times.
✓ Branch 13 taken 3 times.
3 this->tlmWrite_BreakpointIndex(this->m_breakpoint.breakpointIndex);
484
3/4
✓ Branch 6 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 3 times.
✓ Branch 14 taken 3 times.
3 this->tlmWrite_BreakOnlyOnceOnBreakpoint(this->m_breakpoint.breakOnlyOnceOnBreakpoint);
485
3/4
✓ Branch 6 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 3 times.
✓ Branch 14 taken 3 times.
3 this->tlmWrite_BreakBeforeNextLine(this->m_breakpoint.breakBeforeNextLine);
486
3/4
✓ Branch 6 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 3 times.
✓ Branch 14 taken 3 times.
3 this->tlmWrite_BreakpointInUse(this->m_breakpoint.breakpointInUse);
487
488 3 this->updateDebugTelemetryStruct();
489
2/2
✓ Branch 6 taken 3 times.
✓ Branch 13 taken 3 times.
3 this->tlmWrite_Debug_NextCmdOpcode(this->m_debug.nextCmdOpcode);
490
2/2
✓ Branch 6 taken 3 times.
✓ Branch 13 taken 3 times.
3 this->tlmWrite_Debug_NextStatementOpcode(this->m_debug.nextStatementOpcode);
491
3/4
✓ Branch 6 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 3 times.
✓ Branch 14 taken 3 times.
3 this->tlmWrite_Debug_NextStatementReadSuccess(this->m_debug.nextStatementReadSuccess);
492
2/2
✓ Branch 6 taken 3 times.
✓ Branch 13 taken 3 times.
3 this->tlmWrite_Debug_NextStatementIndex(this->m_debug.nextStatementIndex);
493
3/4
✓ Branch 6 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 3 times.
✓ Branch 14 taken 3 times.
3 this->tlmWrite_Debug_ReachedEndOfFile(this->m_debug.reachedEndOfFile);
494
2/2
✓ Branch 6 taken 3 times.
✓ Branch 13 taken 3 times.
3 this->tlmWrite_Debug_StackSize(this->m_debug.stackSize);
495 3 }
496
497 3 void FpySequencer::updateDebugTelemetryStruct() {
498 // only send debug tlm when we are paused
499
2/3
✓ Branch 3 taken 3 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 3 times.
3 if (this->sequencer_getState() == State::RUNNING_PAUSED) {
500 ✗ if (this->m_runtime.nextStatementIndex >= this->m_sequenceObj.get_header().get_statementCount()) {
501 // reached end of file, turn on EOF flag and otherwise send some default tlm
502 ✗ this->m_debug.reachedEndOfFile = true;
503 ✗ this->m_debug.nextStatementReadSuccess = false;
504 ✗ this->m_debug.nextStatementOpcode = 0;
505 ✗ this->m_debug.nextCmdOpcode = 0;
506 ✗ this->m_debug.nextStatementIndex = this->m_runtime.nextStatementIndex;
507 ✗ this->m_debug.stackSize = this->m_runtime.stack.size;
508 ✗ return;
509 }
510
511 ✗ FW_ASSERT(this->m_runtime.nextStatementIndex < Fpy::MAX_SEQUENCE_STATEMENT_COUNT,
512 static_cast<FwAssertArgType>(this->m_runtime.nextStatementIndex));
513 ✗ const Fpy::Statement& nextStmt = this->m_sequenceObj.get_statements()[this->m_runtime.nextStatementIndex];
514 ✗ DirectiveUnion directiveUnion;
515 ✗ Fw::Success status = this->deserializeDirective(nextStmt, directiveUnion);
516
517 ✗ if (status != Fw::Success::SUCCESS) {
518 ✗ this->m_debug.reachedEndOfFile = false;
519 ✗ this->m_debug.nextStatementReadSuccess = false;
520 ✗ this->m_debug.nextStatementOpcode = nextStmt.get_opCode();
521 ✗ this->m_debug.nextCmdOpcode = 0;
522 ✗ this->m_debug.nextStatementIndex = this->m_runtime.nextStatementIndex;
523 ✗ this->m_debug.stackSize = this->m_runtime.stack.size;
524 ✗ return;
525 }
526
527 ✗ if (nextStmt.get_opCode() == Fpy::DirectiveId::CONST_CMD) {
528 // send opcode of the cmd to the ground
529 ✗ this->m_debug.reachedEndOfFile = false;
530 ✗ this->m_debug.nextStatementReadSuccess = true;
531 ✗ this->m_debug.nextStatementOpcode = nextStmt.get_opCode();
532 ✗ this->m_debug.nextCmdOpcode = directiveUnion.constCmd.get_opCode();
533 ✗ this->m_debug.nextStatementIndex = this->m_runtime.nextStatementIndex;
534 ✗ this->m_debug.stackSize = this->m_runtime.stack.size;
535 ✗ return;
536 }
537
538 ✗ this->m_debug.reachedEndOfFile = false;
539 ✗ this->m_debug.nextStatementReadSuccess = true;
540 ✗ this->m_debug.nextStatementOpcode = nextStmt.get_opCode();
541 ✗ this->m_debug.nextCmdOpcode = 0;
542 ✗ this->m_debug.nextStatementIndex = this->m_runtime.nextStatementIndex;
543 ✗ this->m_debug.stackSize = this->m_runtime.stack.size;
544 ✗ return;
545 ✗ }
546 // send some default tlm when we aren't in debug break
547 3 this->m_debug.reachedEndOfFile = false;
548 3 this->m_debug.nextStatementReadSuccess = false;
549 3 this->m_debug.nextStatementOpcode = 0;
550 3 this->m_debug.nextCmdOpcode = 0;
551 3 this->m_debug.nextStatementIndex = 0;
552 3 this->m_debug.stackSize = 0;
553 }
554
555 ✗ void FpySequencer::parametersLoaded() {
556 ✗ parameterUpdated(PARAMID_STATEMENT_TIMEOUT_SECS);
557 ✗ parameterUpdated(PARAMID_SEQ_BASE_DIR);
558 ✗ }
559
560 14 void FpySequencer::parameterUpdated(FwPrmIdType id) {
561
1/1
✓ Branch 2 taken 14 times.
14 Fw::ParamValid valid;
562
2/3
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
14 switch (id) {
563 7 case PARAMID_STATEMENT_TIMEOUT_SECS: {
564
3/3
✓ Branch 6 taken 7 times.
✓ Branch 13 taken 7 times.
✓ Branch 16 taken 7 times.
7 this->tlmWrite_PRM_STATEMENT_TIMEOUT_SECS(this->paramGet_STATEMENT_TIMEOUT_SECS(valid));
565 7 break;
566 }
567 7 case PARAMID_SEQ_BASE_DIR: {
568
3/3
✓ Branch 6 taken 7 times.
✓ Branch 11 taken 7 times.
✓ Branch 15 taken 7 times.
7 this->tlmWrite_PRM_SEQ_BASE_DIR(this->paramGet_SEQ_BASE_DIR(valid));
569 7 break;
570 }
571 ✗ default: {
572 ✗ FW_ASSERT(false, static_cast<FwAssertArgType>(id)); // coding error, forgot to include in switch statement
573 }
574 }
575
576 14 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
577 28 }
578
579 20 bool FpySequencer::isRunningState(State state) {
580 // TODO ask Rob if there's a better way to check if we're in a superstate. I don't want to have
581 // to update this every time I add a new substate to the RUNNING state.
582
583
4/4
✓ Branch 6 taken 11 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 9 times.
✓ Branch 9 taken 2 times.
43 return state == State::RUNNING_AWAITING_STATEMENT_RESPONSE || state == State::RUNNING_DISPATCH_STATEMENT ||
584
4/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 8 times.
✓ Branch 8 taken 3 times.
✓ Branch 9 taken 6 times.
43 state == State::RUNNING_PAUSED || state == State::RUNNING_SLEEPING;
585 }
586
587 } // namespace Svc
588