GCC Code Coverage Report


Directory: ./
File: Svc/CmdSequencer/CmdSequencerImpl.cpp
Date: 2026-11-03 21:15:54
Exec Total Coverage
Lines: 311 343 90.7%
Functions: 35 36 97.2%
Branches: 245 311 78.8%

Line Branch Exec Source
1 // ======================================================================
2 // \title CmdSequencerImpl.cpp
3 // \author Bocchino/Canham
4 // \brief cpp file for CmdDispatcherComponentBase component implementation class
5 //
6 // Copyright (C) 2009-2018 California Institute of Technology.
7 // ALL RIGHTS RESERVED. United States Government Sponsorship
8 // acknowledged.
9
10 #include <Fw/Com/ComPacket.hpp>
11 #include <Fw/Types/Assert.hpp>
12 #include <Fw/Types/SerialBuffer.hpp>
13 #include <Fw/Types/Serializable.hpp>
14 #include <Svc/CmdSequencer/CmdSequencerImpl.hpp>
15 #include <Utils/Hash/Hash.hpp>
16 #include <config/CommandDispatcherImplCfg.hpp>
17
18 namespace Svc {
19
20 // ----------------------------------------------------------------------
21 // Construction, initialization, and destruction
22 // ----------------------------------------------------------------------
23
24 93 CmdSequencerComponentImpl::CmdSequencerComponentImpl(const char* name)
25 : CmdSequencerComponentBase(name),
26
1/1
✓ Branch 4 taken 93 times.
93 m_FPrimeSequence(*this),
27 93 m_sequence(&this->m_FPrimeSequence),
28 93 m_loadCmdCount(0),
29 93 m_cancelCmdCount(0),
30 93 m_errorCount(0),
31 93 m_runMode(STOPPED),
32 93 m_stepMode(AUTO),
33 93 m_executedCount(0),
34 93 m_totalExecutedCount(0),
35 93 m_sequencesCompletedCount(0),
36 93 m_timeout(0),
37 93 m_blockState(Svc::BlockState::NO_BLOCK),
38 93 m_opCode(0),
39 93 m_cmdSeq(0),
40 93 m_join_waiting(false),
41
2/2
✓ Branch 4 taken 22320 times.
✓ Branch 5 taken 93 times.
22413 m_sandboxDir(),
42
4/4
✓ Branch 10 taken 93 times.
✓ Branch 16 taken 93 times.
✓ Branch 22 taken 93 times.
✓ Branch 33 taken 93 times.
372 m_sandboxConfigured(false) {}
43
44 87 void CmdSequencerComponentImpl::setTimeout(const U32 timeout) {
45 87 this->m_timeout = timeout;
46 87 }
47
48 30 void CmdSequencerComponentImpl ::setSequenceFormat(Sequence& sequence) {
49 30 this->m_sequence = &sequence;
50 30 }
51
52 6 void CmdSequencerComponentImpl ::configureSandbox(const char* directory) {
53 6 FW_ASSERT(directory != nullptr);
54 const Os::FilePathUtils::Status status =
55 6 Os::FilePathUtils::resolveDirectoryFromCwd(directory, this->m_sandboxDir, sizeof(this->m_sandboxDir));
56 6 FW_ASSERT(status == Os::FilePathUtils::VALID, static_cast<FwAssertArgType>(status));
57 6 this->m_sandboxConfigured = true;
58 6 }
59
60 143 bool CmdSequencerComponentImpl ::checkSandbox(const Fw::ConstStringBase& fileName) {
61
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 143 times.
✓ Branch 5 taken 135 times.
✓ Branch 6 taken 8 times.
143 if (not this->m_sandboxConfigured) {
62 135 return true;
63 }
64 8 char resolved[Os::FilePathUtils::MAX_PATH_LENGTH];
65 const bool contained =
66
2/2
✓ Branch 7 taken 8 times.
✓ Branch 10 taken 8 times.
8 (Os::FilePathUtils::resolveFromCwd(fileName.toChar(), resolved, sizeof(resolved)) ==
67
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
16 Os::FilePathUtils::VALID) &&
68
3/3
✓ Branch 3 taken 8 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 6 times.
8 (Os::FilePathUtils::checkContainment(resolved, this->m_sandboxDir) == Os::FilePathUtils::VALID);
69
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 if (not contained) {
70
3/3
✓ Branch 14 taken 6 times.
✓ Branch 17 taken 6 times.
✓ Branch 21 taken 6 times.
12 this->log_WARNING_HI_CS_FileOutsideSandbox(Fw::LogStringArg(fileName.toChar()),
71
1/1
✓ Branch 3 taken 6 times.
12 Fw::LogStringArg(this->m_sandboxDir));
72
1/1
✓ Branch 4 taken 6 times.
6 this->error();
73 }
74 8 return contained;
75 }
76
77 88 void CmdSequencerComponentImpl ::allocateBuffer(const FwEnumStoreType identifier,
78 Fw::MemAllocator& allocator,
79 const FwSizeType bytes) {
80 88 this->m_sequence->allocateBuffer(identifier, allocator, bytes);
81 88 }
82
83 4 void CmdSequencerComponentImpl ::loadSequence(const Fw::ConstStringBase& fileName) {
84 4 FW_ASSERT(this->m_runMode == STOPPED, this->m_runMode);
85
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
4 if (not this->loadFile(fileName)) {
86 ✗ this->m_sequence->clear();
87 }
88 4 }
89
90 88 void CmdSequencerComponentImpl ::deallocateBuffer(Fw::MemAllocator& allocator) {
91 88 this->m_sequence->deallocateBuffer(allocator);
92 88 }
93
94 186 CmdSequencerComponentImpl::~CmdSequencerComponentImpl() {}
95
96 // ----------------------------------------------------------------------
97 // Handler implementations
98 // ----------------------------------------------------------------------
99
100 55 void CmdSequencerComponentImpl::CS_RUN_cmdHandler(FwOpcodeType opCode,
101 U32 cmdSeq,
102 const Fw::CmdStringArg& fileName,
103 const Svc::BlockState& block) {
104
2/2
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 52 times.
55 if (not this->requireRunMode(STOPPED)) {
105
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
3 if (m_join_waiting) {
106 // Inform user previous seq file is not complete
107 ✗ this->log_WARNING_HI_CS_JoinWaitingNotComplete();
108 }
109
2/2
✓ Branch 6 taken 3 times.
✓ Branch 9 taken 3 times.
3 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
110 3 return;
111 }
112
113
5/8
✗ Branch 3 not taken.
✓ Branch 4 taken 52 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 51 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
52 if ((Svc::BlockState::BLOCK == block.e) && (MANUAL == this->m_stepMode)) {
114 // In MANUAL mode nothing executes until CS_STEP, so a BLOCK response could never be sent
115 ✗ this->log_WARNING_HI_CS_InvalidMode();
116 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
117 ✗ return;
118 }
119
120
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 52 times.
52 this->m_blockState = block.e;
121 52 this->m_cmdSeq = cmdSeq;
122 52 this->m_opCode = opCode;
123
124 // load commands
125
2/2
✓ Branch 7 taken 20 times.
✓ Branch 8 taken 32 times.
52 if (not this->loadFile(fileName)) {
126 // Clear the recorded command state so a later port-driven run cannot
127 // emit a duplicate response for this already-answered command
128 20 this->m_blockState = Svc::BlockState::NO_BLOCK;
129 20 this->m_opCode = 0;
130 20 this->m_cmdSeq = 0;
131
2/2
✓ Branch 6 taken 20 times.
✓ Branch 9 taken 20 times.
20 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
132 20 return;
133 }
134
135 32 this->m_executedCount = 0;
136
137 // Check the step mode. If it is auto, start the sequence
138
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 32 times.
✓ Branch 5 taken 28 times.
✓ Branch 6 taken 4 times.
32 if (AUTO == this->m_stepMode) {
139 28 this->m_runMode = RUNNING;
140
3/3
✓ Branch 6 taken 28 times.
✓ Branch 16 taken 28 times.
✓ Branch 21 taken 28 times.
28 this->tlmWrite_CS_CurrentSequence(this->m_sequence->getStringFileName());
141
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 28 times.
28 if (this->isConnected_seqStartOut_OutputPort(0)) {
142 // Create empty SeqArgs as placeholder
143 // Use parameterized constructor to ensure m_size is initialized to 0
144 ✗ Svc::SeqArgs emptyArgs{0, 0};
145 ✗ this->seqStartOut_out(0, this->m_sequence->getStringFileName(), emptyArgs);
146 ✗ }
147 28 this->performCmd_Step();
148 }
149
150
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 32 times.
✓ Branch 5 taken 31 times.
✓ Branch 6 taken 1 times.
32 if (Svc::BlockState::NO_BLOCK == this->m_blockState) {
151
2/2
✓ Branch 6 taken 31 times.
✓ Branch 9 taken 31 times.
31 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
152 }
153 }
154
155 79 void CmdSequencerComponentImpl::CS_VALIDATE_cmdHandler(FwOpcodeType opCode,
156 U32 cmdSeq,
157 const Fw::CmdStringArg& fileName) {
158 79 FW_ASSERT(this->m_sequence != nullptr);
159
2/2
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 76 times.
79 if (!this->requireRunMode(STOPPED)) {
160
2/2
✓ Branch 6 taken 3 times.
✓ Branch 9 taken 3 times.
3 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
161 3 return;
162 }
163
164 // load commands
165
2/2
✓ Branch 7 taken 31 times.
✓ Branch 8 taken 45 times.
76 if (not this->loadFile(fileName)) {
166
2/2
✓ Branch 6 taken 31 times.
✓ Branch 9 taken 31 times.
31 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
167 31 return;
168 }
169
170 // clear the buffer
171 45 this->m_sequence->clear();
172
173 45 this->log_ACTIVITY_HI_CS_SequenceValid(this->m_sequence->getLogFileName());
174
175
2/2
✓ Branch 6 taken 45 times.
✓ Branch 9 taken 45 times.
45 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
176 }
177
178 //! Shared body of the seqRunIn and seqDispatchIn handlers
179 21 void CmdSequencerComponentImpl::doSequenceRun(const Fw::StringBase& filename) {
180
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 21 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 20 times.
21 if (MANUAL == this->m_stepMode) {
181 // In MANUAL mode nothing executes until CS_STEP, so a port-driven run would wedge
182 1 this->log_WARNING_HI_CS_InvalidMode();
183 1 this->rejectSequenceRun();
184 1 return;
185 }
186
2/2
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 16 times.
20 if (!this->requireRunMode(STOPPED)) {
187 4 this->rejectSequenceRun();
188 4 return;
189 }
190
191 // If file name is non-empty, load a file.
192 // Empty file name means don't load.
193
2/2
✓ Branch 7 taken 11 times.
✓ Branch 8 taken 5 times.
16 if (filename != "") {
194 11 const bool status = this->loadFile(filename);
195
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 8 times.
11 if (!status) {
196 3 this->rejectSequenceRun();
197 3 return;
198 }
199
2/2
✓ Branch 23 taken 3 times.
✓ Branch 24 taken 2 times.
5 } else if (not this->m_sequence->hasMoreRecords()) {
200 // No sequence loaded
201 3 this->log_WARNING_LO_CS_NoSequenceActive();
202 3 this->error();
203 3 this->rejectSequenceRun();
204 3 return;
205 }
206
207 10 this->m_executedCount = 0;
208
209 // Check the step mode. If it is auto, start the sequence
210
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
✓ Branch 5 taken 10 times.
✗ Branch 6 not taken.
10 if (AUTO == this->m_stepMode) {
211 10 this->m_runMode = RUNNING;
212
3/3
✓ Branch 6 taken 10 times.
✓ Branch 16 taken 10 times.
✓ Branch 21 taken 10 times.
10 this->tlmWrite_CS_CurrentSequence(this->m_sequence->getStringFileName());
213
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 10 times.
10 if (this->isConnected_seqStartOut_OutputPort(0)) {
214 // Create empty SeqArgs as placeholder
215 // Use parameterized constructor to ensure m_size is initialized to 0
216 ✗ Svc::SeqArgs emptyArgs{0, 0};
217 ✗ this->seqStartOut_out(0, this->m_sequence->getStringFileName(), emptyArgs);
218 ✗ }
219 10 this->performCmd_Step();
220 }
221
222 10 this->log_ACTIVITY_HI_CS_PortSequenceStarted(this->m_sequence->getLogFileName());
223 }
224
225 11 void CmdSequencerComponentImpl::rejectSequenceRun() {
226
2/2
✓ Branch 5 taken 7 times.
✓ Branch 6 taken 4 times.
11 if (this->isConnected_seqDone_OutputPort(0)) {
227
2/2
✓ Branch 6 taken 7 times.
✓ Branch 10 taken 7 times.
7 this->seqDone_out(0, 0, 0, Fw::CmdResponse::EXECUTION_ERROR);
228 }
229 11 }
230
231 15 void CmdSequencerComponentImpl::seqRunIn_handler(FwIndexType portNum,
232 const Fw::StringBase& filename,
233 const Svc::SeqArgs& args) {
234 (void)args; // Suppress unused parameter warning
235 15 this->doSequenceRun(filename);
236 15 }
237
238 2 void CmdSequencerComponentImpl::seqDispatchIn_handler(FwIndexType portNum, Fw::StringBase& file_name) {
239 2 this->doSequenceRun(file_name);
240 2 }
241
242 ✗ void CmdSequencerComponentImpl ::seqCancelIn_handler(const FwIndexType portNum) {
243 ✗ if (RUNNING == this->m_runMode) {
244 ✗ this->performCmd_Cancel();
245 ✗ this->log_ACTIVITY_HI_CS_SequenceCanceled(this->m_sequence->getLogFileName());
246 ✗ ++this->m_cancelCmdCount;
247 ✗ this->tlmWrite_CS_CancelCommands(this->m_cancelCmdCount);
248 } else {
249 ✗ this->log_WARNING_LO_CS_NoSequenceActive();
250 }
251 ✗ }
252
253 5 void CmdSequencerComponentImpl::CS_CANCEL_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
254
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 2 times.
5 if (RUNNING == this->m_runMode) {
255 3 this->performCmd_Cancel();
256 3 this->log_ACTIVITY_HI_CS_SequenceCanceled(this->m_sequence->getLogFileName());
257 3 ++this->m_cancelCmdCount;
258
2/2
✓ Branch 6 taken 3 times.
✓ Branch 13 taken 3 times.
3 this->tlmWrite_CS_CancelCommands(this->m_cancelCmdCount);
259 } else {
260 2 this->log_WARNING_LO_CS_NoSequenceActive();
261 }
262
2/2
✓ Branch 6 taken 5 times.
✓ Branch 9 taken 5 times.
5 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
263 5 }
264
265 5 void CmdSequencerComponentImpl::CS_JOIN_WAIT_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq) {
266 // If there is no running sequence do not wait
267
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 4 times.
5 if (m_runMode != RUNNING) {
268 1 this->log_WARNING_LO_CS_NoSequenceActive();
269
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
270 1 return;
271
6/8
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 3 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 2 times.
4 } else if ((Svc::BlockState::BLOCK == this->m_blockState) || this->m_join_waiting) {
272 // A command response is already owed to a BLOCK-mode CS_RUN caller or a
273 // previous CS_JOIN_WAIT caller. Reject rather than overwrite that state,
274 // which would leave the original caller without a completion response.
275 2 this->log_WARNING_HI_CS_JoinWaitingNotComplete();
276
2/2
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
2 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
277 } else {
278 2 m_join_waiting = true;
279 2 Fw::LogStringArg& logFileName = this->m_sequence->getLogFileName();
280 2 this->log_ACTIVITY_HI_CS_JoinWaiting(logFileName, m_cmdSeq, CmdDispatcherCfg::getEventOpcode(m_opCode));
281 2 m_cmdSeq = cmdSeq;
282 2 m_opCode = opCode;
283 }
284 }
285
286 // ----------------------------------------------------------------------
287 // Private helper methods
288 // ----------------------------------------------------------------------
289
290 143 bool CmdSequencerComponentImpl ::loadFile(const Fw::ConstStringBase& fileName) {
291 // Checked here, before any sequence format opens the file, so the restriction applies to
292 // every format (including a custom one set with setSequenceFormat)
293
4/4
✓ Branch 4 taken 137 times.
✓ Branch 5 taken 6 times.
✓ Branch 29 taken 89 times.
✓ Branch 30 taken 48 times.
143 const bool status = this->checkSandbox(fileName) and this->m_sequence->loadFile(fileName);
294
2/2
✓ Branch 0 taken 89 times.
✓ Branch 1 taken 54 times.
143 if (status) {
295 89 Fw::LogStringArg& logFileName = this->m_sequence->getLogFileName();
296 89 this->log_ACTIVITY_LO_CS_SequenceLoaded(logFileName);
297 89 ++this->m_loadCmdCount;
298
2/2
✓ Branch 6 taken 89 times.
✓ Branch 13 taken 89 times.
89 this->tlmWrite_CS_LoadCommands(this->m_loadCmdCount);
299 } else {
300 // A partial load may have populated m_buffer before an intermediate
301 // validation step (e.g. CRC, time, record structure) failed. Without
302 // an explicit reset, FPrimeSequence::hasMoreRecords() still returns
303 // true because getDeserializeSizeLeft() > 0, so a subsequent CS_START
304 // bypasses the "no sequence active" guard and reaches
305 // FPrimeSequence::nextRecord, which asserts on the failed deserialize
306 // and aborts the FSW. Clearing the sequence on every load failure
307 // closes that window.
308 54 this->m_sequence->clear();
309 }
310 143 return status;
311 }
312
313 59 void CmdSequencerComponentImpl::error() {
314 59 ++this->m_errorCount;
315
2/2
✓ Branch 6 taken 59 times.
✓ Branch 13 taken 59 times.
59 this->tlmWrite_CS_Errors(m_errorCount);
316 59 }
317
318 8 void CmdSequencerComponentImpl::performCmd_Cancel() {
319 8 FW_ASSERT(this->m_sequence != nullptr);
320 8 this->m_sequence->reset();
321 8 this->m_runMode = STOPPED;
322 8 this->m_cmdTimer.clear();
323 8 this->m_cmdTimeoutTimer.clear();
324 8 this->m_executedCount = 0;
325 // write sequence done port with error, if connected
326
1/2
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
8 if (this->isConnected_seqDone_OutputPort(0)) {
327
2/2
✓ Branch 6 taken 8 times.
✓ Branch 10 taken 8 times.
8 this->seqDone_out(0, 0, 0, Fw::CmdResponse::EXECUTION_ERROR);
328 }
329
330
4/8
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 8 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 8 times.
8 if (Svc::BlockState::BLOCK == this->m_blockState || m_join_waiting) {
331 // Do not wait if sequence was canceled or a cmd failed
332 ✗ this->m_join_waiting = false;
333 ✗ this->cmdResponse_out(this->m_opCode, this->m_cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
334 }
335
336 8 this->m_blockState = Svc::BlockState::NO_BLOCK;
337 8 }
338
339 140 void CmdSequencerComponentImpl ::cmdResponseIn_handler(FwIndexType portNum,
340 FwOpcodeType opcode,
341 U32 cmdSeq,
342 const Fw::CmdResponse& response) {
343
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 140 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 137 times.
140 if (this->m_runMode == STOPPED) {
344 // Sequencer is not running
345 3 this->log_WARNING_HI_CS_UnexpectedCompletion(CmdDispatcherCfg::getEventOpcode(opcode));
346 } else {
347 // clear command timeout
348 137 this->m_cmdTimeoutTimer.clear();
349
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 135 times.
137 if (response != Fw::CmdResponse::OK) {
350
1/2
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
2 this->commandError(this->m_executedCount, opcode, response.e);
351 2 this->performCmd_Cancel();
352
5/8
✗ Branch 3 not taken.
✓ Branch 4 taken 135 times.
✓ Branch 5 taken 135 times.
✗ Branch 6 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 135 times.
✓ Branch 12 taken 121 times.
✓ Branch 13 taken 14 times.
135 } else if (this->m_runMode == RUNNING && this->m_stepMode == AUTO) {
353 // Auto mode
354 121 this->commandComplete(opcode);
355
2/2
✓ Branch 23 taken 22 times.
✓ Branch 24 taken 99 times.
242 if (not this->m_sequence->hasMoreRecords()) {
356 // No data left
357 22 this->m_runMode = STOPPED;
358 22 this->sequenceComplete();
359 } else {
360 99 this->performCmd_Step();
361 }
362 } else {
363 // Manual step mode
364 14 this->commandComplete(opcode);
365
2/2
✓ Branch 23 taken 2 times.
✓ Branch 24 taken 12 times.
14 if (not this->m_sequence->hasMoreRecords()) {
366 2 this->m_runMode = STOPPED;
367 2 this->sequenceComplete();
368 }
369 }
370 }
371 140 }
372
373 27 void CmdSequencerComponentImpl ::schedIn_handler(FwIndexType portNum, U32 order) {
374
1/1
✓ Branch 3 taken 27 times.
27 Fw::Time currTime = this->getTime();
375 // check to see if a command time is pending
376
4/4
✓ Branch 5 taken 27 times.
✓ Branch 8 taken 27 times.
✓ Branch 13 taken 11 times.
✓ Branch 14 taken 16 times.
27 if (this->m_cmdTimer.isExpiredAt(currTime)) {
377
1/1
✓ Branch 8 taken 11 times.
11 this->comCmdOut_out(0, m_record.m_command, 0);
378 11 this->m_cmdTimer.clear();
379 // start command timeout timer
380
1/1
✓ Branch 4 taken 11 times.
11 this->setCmdTimeout(currTime);
381
4/4
✓ Branch 6 taken 16 times.
✓ Branch 9 taken 16 times.
✓ Branch 14 taken 3 times.
✓ Branch 15 taken 13 times.
16 } else if (this->m_cmdTimeoutTimer.isExpiredAt(this->getTime())) { // check for command timeout
382
2/2
✓ Branch 16 taken 3 times.
✓ Branch 21 taken 3 times.
3 this->log_WARNING_HI_CS_SequenceTimeout(m_sequence->getLogFileName(), this->m_executedCount);
383 // If there is a command timeout, cancel the sequence
384
1/1
✓ Branch 4 taken 3 times.
3 this->performCmd_Cancel();
385 }
386 54 }
387
388 10 void CmdSequencerComponentImpl ::CS_START_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
389
2/2
✓ Branch 23 taken 3 times.
✓ Branch 24 taken 7 times.
10 if (not this->m_sequence->hasMoreRecords()) {
390 // No sequence loaded
391 3 this->log_WARNING_LO_CS_NoSequenceActive();
392
2/2
✓ Branch 6 taken 3 times.
✓ Branch 9 taken 3 times.
3 this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
393 3 return;
394 }
395
2/2
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 4 times.
7 if (!this->requireRunMode(STOPPED)) {
396
2/2
✓ Branch 6 taken 3 times.
✓ Branch 9 taken 3 times.
3 this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
397 3 return;
398 }
399
400 4 this->m_blockState = Svc::BlockState::NO_BLOCK;
401 4 this->m_runMode = RUNNING;
402
3/3
✓ Branch 6 taken 4 times.
✓ Branch 16 taken 4 times.
✓ Branch 21 taken 4 times.
4 this->tlmWrite_CS_CurrentSequence(this->m_sequence->getStringFileName());
403 4 this->log_ACTIVITY_HI_CS_CmdStarted(this->m_sequence->getLogFileName());
404 4 this->performCmd_Step();
405
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
4 if (this->isConnected_seqStartOut_OutputPort(0)) {
406 // Create empty SeqArgs as placeholder
407 ✗ Svc::SeqArgs emptyArgs{0, 0};
408 ✗ this->seqStartOut_out(0, this->m_sequence->getStringFileName(), emptyArgs);
409 ✗ }
410
2/2
✓ Branch 6 taken 4 times.
✓ Branch 9 taken 4 times.
4 this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
411 }
412
413 17 void CmdSequencerComponentImpl ::CS_STEP_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
414 17 FW_ASSERT(this->m_sequence != nullptr);
415
2/2
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 3 times.
17 if (this->requireRunMode(RUNNING)) {
416
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 14 times.
14 if (MANUAL != this->m_stepMode) {
417 // CS_STEP is valid only in MANUAL step mode
418 ✗ this->log_WARNING_HI_CS_InvalidMode();
419 ✗ this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
420 ✗ return;
421 }
422
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 13 times.
14 if (this->m_cmdTimer.isSet()) {
423 // The previously stepped record is still waiting for its time tag; stepping again
424 // would overwrite it before it is dispatched
425
1/1
✓ Branch 4 taken 1 times.
1 const Fw::Time pendingTime = this->m_cmdTimer.getExpirationTime();
426
4/4
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 19 taken 1 times.
✓ Branch 24 taken 1 times.
2 this->log_WARNING_LO_CS_StepRecordPending(this->m_sequence->getLogFileName(), pendingTime.getSeconds(),
427 1 pendingTime.getUSeconds());
428
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
429 1 return;
430 1 }
431
1/2
✗ Branch 23 not taken.
✓ Branch 24 taken 13 times.
13 if (not this->m_sequence->hasMoreRecords()) {
432 // A sequence with no end-of-sequence record leaves nothing to step; stepping anyway
433 // asserts in the sequence reader
434 ✗ this->log_WARNING_LO_CS_NoSequenceActive();
435 ✗ this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
436 ✗ return;
437 }
438 13 this->performCmd_Step();
439 // check for special case where end of sequence entry was encountered
440
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
✓ Branch 5 taken 12 times.
✓ Branch 6 taken 1 times.
13 if (this->m_runMode != STOPPED) {
441 12 this->log_ACTIVITY_HI_CS_CmdStepped(this->m_sequence->getLogFileName(), this->m_executedCount);
442 }
443
2/2
✓ Branch 6 taken 13 times.
✓ Branch 9 taken 13 times.
13 this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
444 } else {
445
2/2
✓ Branch 6 taken 3 times.
✓ Branch 9 taken 3 times.
3 this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
446 }
447 }
448
449 6 void CmdSequencerComponentImpl ::CS_AUTO_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
450
2/2
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 3 times.
6 if (this->requireRunMode(STOPPED)) {
451 3 this->m_stepMode = AUTO;
452
2/2
✓ Branch 6 taken 3 times.
✓ Branch 10 taken 3 times.
3 this->log_ACTIVITY_HI_CS_ModeSwitched(CmdSequencer_SeqMode::AUTO);
453
2/2
✓ Branch 6 taken 3 times.
✓ Branch 9 taken 3 times.
3 this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
454 } else {
455
2/2
✓ Branch 6 taken 3 times.
✓ Branch 9 taken 3 times.
3 this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
456 }
457 6 }
458
459 7 void CmdSequencerComponentImpl ::CS_MANUAL_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
460
2/2
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 3 times.
7 if (this->requireRunMode(STOPPED)) {
461 4 this->m_stepMode = MANUAL;
462
2/2
✓ Branch 6 taken 4 times.
✓ Branch 10 taken 4 times.
4 this->log_ACTIVITY_HI_CS_ModeSwitched(CmdSequencer_SeqMode::STEP);
463
2/2
✓ Branch 6 taken 4 times.
✓ Branch 9 taken 4 times.
4 this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
464 } else {
465
2/2
✓ Branch 6 taken 3 times.
✓ Branch 9 taken 3 times.
3 this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
466 }
467 7 }
468
469 // ----------------------------------------------------------------------
470 // Helper methods
471 // ----------------------------------------------------------------------
472
473 191 bool CmdSequencerComponentImpl::requireRunMode(RunMode mode) {
474
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 191 times.
✓ Branch 5 taken 169 times.
✓ Branch 6 taken 22 times.
191 if (this->m_runMode == mode) {
475 169 return true;
476 } else {
477 22 this->log_WARNING_HI_CS_InvalidMode();
478 22 return false;
479 }
480 }
481
482 2 void CmdSequencerComponentImpl ::commandError(const U32 number, const FwOpcodeType opCode, const U32 error) {
483 2 this->log_WARNING_HI_CS_CommandError(this->m_sequence->getLogFileName(), number,
484 CmdDispatcherCfg::getEventOpcode(opCode), error);
485 2 this->error();
486 2 }
487
488 154 void CmdSequencerComponentImpl::performCmd_Step() {
489
1/1
✓ Branch 26 taken 154 times.
154 this->m_sequence->nextRecord(m_record);
490 // set clock time base and context from value set when sequence was loaded
491
1/1
✓ Branch 8 taken 154 times.
154 const Sequence::Header& header = this->m_sequence->getHeader();
492
2/2
✓ Branch 12 taken 154 times.
✓ Branch 15 taken 154 times.
154 this->m_record.m_timeTag.setTimeBase(header.m_timeBase);
493
1/1
✓ Branch 8 taken 154 times.
154 this->m_record.m_timeTag.setTimeContext(header.m_timeContext);
494
495
1/1
✓ Branch 3 taken 154 times.
154 Fw::Time currentTime = this->getTime();
496
4/6
✗ Branch 3 not taken.
✓ Branch 4 taken 154 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 147 times.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
154 switch (this->m_record.m_descriptor) {
497 5 case Sequence::Record::END_OF_SEQUENCE:
498 5 this->m_runMode = STOPPED;
499
1/1
✓ Branch 4 taken 5 times.
5 this->sequenceComplete();
500 5 break;
501 147 case Sequence::Record::RELATIVE:
502
1/1
✓ Branch 4 taken 147 times.
147 this->performCmd_Step_RELATIVE(currentTime);
503 147 break;
504 2 case Sequence::Record::ABSOLUTE:
505
1/1
✓ Branch 4 taken 2 times.
2 this->performCmd_Step_ABSOLUTE(currentTime);
506 2 break;
507 ✗ default:
508 ✗ FW_ASSERT(false, m_record.m_descriptor);
509 }
510 308 }
511
512 29 void CmdSequencerComponentImpl::sequenceComplete() {
513 29 FW_ASSERT(this->m_sequence != nullptr);
514 29 ++this->m_sequencesCompletedCount;
515 // reset buffer
516 29 this->m_sequence->clear();
517 29 this->log_ACTIVITY_HI_CS_SequenceComplete(this->m_sequence->getLogFileName());
518
2/2
✓ Branch 6 taken 29 times.
✓ Branch 13 taken 29 times.
29 this->tlmWrite_CS_SequencesCompleted(this->m_sequencesCompletedCount);
519 29 this->m_executedCount = 0;
520 // write sequence done port, if connected
521
1/2
✓ Branch 5 taken 29 times.
✗ Branch 6 not taken.
29 if (this->isConnected_seqDone_OutputPort(0)) {
522
2/2
✓ Branch 6 taken 29 times.
✓ Branch 10 taken 29 times.
29 this->seqDone_out(0, 0, 0, Fw::CmdResponse::OK);
523 }
524
525
6/8
✗ Branch 3 not taken.
✓ Branch 4 taken 29 times.
✓ Branch 5 taken 28 times.
✓ Branch 6 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 28 times.
✓ Branch 12 taken 2 times.
✓ Branch 13 taken 26 times.
29 if (Svc::BlockState::BLOCK == this->m_blockState || m_join_waiting) {
526
2/2
✓ Branch 6 taken 3 times.
✓ Branch 17 taken 3 times.
3 this->cmdResponse_out(this->m_opCode, this->m_cmdSeq, Fw::CmdResponse::OK);
527 }
528
529 29 m_join_waiting = false;
530 29 this->m_blockState = Svc::BlockState::NO_BLOCK;
531
2/2
✓ Branch 6 taken 29 times.
✓ Branch 14 taken 29 times.
29 this->tlmWrite_CS_CurrentSequence(NO_SEQ);
532 29 }
533
534 135 void CmdSequencerComponentImpl::commandComplete(const FwOpcodeType opcode) {
535 135 this->log_ACTIVITY_LO_CS_CommandComplete(this->m_sequence->getLogFileName(), this->m_executedCount,
536 CmdDispatcherCfg::getEventOpcode(opcode));
537 135 ++this->m_executedCount;
538 135 ++this->m_totalExecutedCount;
539
2/2
✓ Branch 6 taken 135 times.
✓ Branch 13 taken 135 times.
135 this->tlmWrite_CS_CommandsExecuted(this->m_totalExecutedCount);
540 135 }
541
542 147 void CmdSequencerComponentImpl ::performCmd_Step_RELATIVE(Fw::Time& currentTime) {
543 147 this->m_record.m_timeTag.add(currentTime.getSeconds(), currentTime.getUSeconds());
544 147 this->performCmd_Step_ABSOLUTE(currentTime);
545 147 }
546
547 149 void CmdSequencerComponentImpl ::performCmd_Step_ABSOLUTE(Fw::Time& currentTime) {
548
2/2
✓ Branch 7 taken 134 times.
✓ Branch 8 taken 15 times.
149 if (currentTime >= this->m_record.m_timeTag) {
549 134 this->comCmdOut_out(0, m_record.m_command, 0);
550 134 this->setCmdTimeout(currentTime);
551 } else {
552
2/2
✓ Branch 8 taken 15 times.
✓ Branch 11 taken 15 times.
15 this->m_cmdTimer.set(this->m_record.m_timeTag);
553 }
554 149 }
555
556 1 void CmdSequencerComponentImpl ::pingIn_handler(FwIndexType portNum, /*!< The port number*/
557 U32 key /*!< Value to return to pinger*/
558 ) {
559 // send ping response
560 1 this->pingOut_out(0, key);
561 1 }
562
563 145 void CmdSequencerComponentImpl ::setCmdTimeout(const Fw::Time& currentTime) {
564 // start timeout timer if enabled and not in step mode
565
4/6
✓ Branch 4 taken 145 times.
✗ Branch 5 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 145 times.
✓ Branch 11 taken 130 times.
✓ Branch 12 taken 15 times.
145 if ((this->m_timeout > 0) and (AUTO == this->m_stepMode)) {
566
1/1
✓ Branch 2 taken 130 times.
130 Fw::Time expTime = currentTime;
567
1/1
✓ Branch 6 taken 130 times.
130 expTime.add(this->m_timeout, 0);
568
2/2
✓ Branch 5 taken 130 times.
✓ Branch 8 taken 130 times.
130 this->m_cmdTimeoutTimer.set(expTime);
569 130 }
570 145 }
571
572 } // namespace Svc
573