GCC Code Coverage Report


Directory: ./
File: Svc/CmdSequencer/CmdSequencerImpl.cpp
Date: 2026-11-03 22:10:14
Exec Total Coverage
Lines: 119 341 34.9%
Functions: 17 36 47.2%
Branches: 63 257 24.5%

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 1 CmdSequencerComponentImpl::CmdSequencerComponentImpl(const char* name)
25 : CmdSequencerComponentBase(name),
26 1 m_FPrimeSequence(*this),
27 1 m_sequence(&this->m_FPrimeSequence),
28 1 m_loadCmdCount(0),
29 1 m_cancelCmdCount(0),
30 1 m_errorCount(0),
31 1 m_runMode(STOPPED),
32 1 m_stepMode(AUTO),
33 1 m_executedCount(0),
34 1 m_totalExecutedCount(0),
35 1 m_sequencesCompletedCount(0),
36 1 m_timeout(0),
37 1 m_blockState(Svc::BlockState::NO_BLOCK),
38 1 m_opCode(0),
39 1 m_cmdSeq(0),
40 1 m_join_waiting(false),
41
2/2
✓ Branch 0 taken 240 times.
✓ Branch 1 taken 1 times.
241 m_sandboxDir(),
42
5/5
✓ Branch 2 taken 1 times.
✓ Branch 5 taken 1 times.
✓ Branch 8 taken 1 times.
✓ Branch 11 taken 1 times.
✓ Branch 14 taken 1 times.
2 m_sandboxConfigured(false) {}
43
44 ✗ void CmdSequencerComponentImpl::setTimeout(const U32 timeout) {
45 ✗ this->m_timeout = timeout;
46 ✗ }
47
48 ✗ void CmdSequencerComponentImpl ::setSequenceFormat(Sequence& sequence) {
49 ✗ this->m_sequence = &sequence;
50 ✗ }
51
52 ✗ void CmdSequencerComponentImpl ::configureSandbox(const char* directory) {
53 ✗ FW_ASSERT(directory != nullptr);
54 const Os::FilePathUtils::Status status =
55 ✗ Os::FilePathUtils::resolveDirectoryFromCwd(directory, this->m_sandboxDir, sizeof(this->m_sandboxDir));
56 ✗ FW_ASSERT(status == Os::FilePathUtils::VALID, static_cast<FwAssertArgType>(status));
57 ✗ this->m_sandboxConfigured = true;
58 ✗ }
59
60 2 bool CmdSequencerComponentImpl ::checkSandbox(const Fw::ConstStringBase& fileName) {
61
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (not this->m_sandboxConfigured) {
62 2 return true;
63 }
64 char resolved[Os::FilePathUtils::MAX_PATH_LENGTH];
65 const bool contained =
66 ✗ (Os::FilePathUtils::resolveFromCwd(fileName.toChar(), resolved, sizeof(resolved)) ==
67 ✗ Os::FilePathUtils::VALID) &&
68 ✗ (Os::FilePathUtils::checkContainment(resolved, this->m_sandboxDir) == Os::FilePathUtils::VALID);
69 ✗ if (not contained) {
70 ✗ this->log_WARNING_HI_CS_FileOutsideSandbox(Fw::LogStringArg(fileName.toChar()),
71 ✗ Fw::LogStringArg(this->m_sandboxDir));
72 ✗ this->error();
73 }
74 ✗ return contained;
75 }
76
77 1 void CmdSequencerComponentImpl ::allocateBuffer(const FwEnumStoreType identifier,
78 Fw::MemAllocator& allocator,
79 const FwSizeType bytes) {
80 1 this->m_sequence->allocateBuffer(identifier, allocator, bytes);
81 1 }
82
83 ✗ void CmdSequencerComponentImpl ::loadSequence(const Fw::ConstStringBase& fileName) {
84 ✗ FW_ASSERT(this->m_runMode == STOPPED, this->m_runMode);
85 ✗ if (not this->loadFile(fileName)) {
86 ✗ this->m_sequence->clear();
87 }
88 ✗ }
89
90 1 void CmdSequencerComponentImpl ::deallocateBuffer(Fw::MemAllocator& allocator) {
91 1 this->m_sequence->deallocateBuffer(allocator);
92 1 }
93
94 2 CmdSequencerComponentImpl::~CmdSequencerComponentImpl() {}
95
96 // ----------------------------------------------------------------------
97 // Handler implementations
98 // ----------------------------------------------------------------------
99
100 2 void CmdSequencerComponentImpl::CS_RUN_cmdHandler(FwOpcodeType opCode,
101 U32 cmdSeq,
102 const Fw::CmdStringArg& fileName,
103 const Svc::BlockState& block) {
104
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (not this->requireRunMode(STOPPED)) {
105 ✗ if (m_join_waiting) {
106 // Inform user previous seq file is not complete
107 ✗ this->log_WARNING_HI_CS_JoinWaitingNotComplete();
108 }
109 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
110 ✗ return;
111 }
112
113
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 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 2 this->m_blockState = block.e;
121 2 this->m_cmdSeq = cmdSeq;
122 2 this->m_opCode = opCode;
123
124 // load commands
125
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 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 ✗ this->m_blockState = Svc::BlockState::NO_BLOCK;
129 ✗ this->m_opCode = 0;
130 ✗ this->m_cmdSeq = 0;
131 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
132 ✗ return;
133 }
134
135 2 this->m_executedCount = 0;
136
137 // Check the step mode. If it is auto, start the sequence
138
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (AUTO == this->m_stepMode) {
139 2 this->m_runMode = RUNNING;
140
3/3
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
2 this->tlmWrite_CS_CurrentSequence(this->m_sequence->getStringFileName());
141
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 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 2 this->performCmd_Step();
148 }
149
150
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (Svc::BlockState::NO_BLOCK == this->m_blockState) {
151 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
152 }
153 }
154
155 ✗ void CmdSequencerComponentImpl::CS_VALIDATE_cmdHandler(FwOpcodeType opCode,
156 U32 cmdSeq,
157 const Fw::CmdStringArg& fileName) {
158 ✗ FW_ASSERT(this->m_sequence != nullptr);
159 ✗ if (!this->requireRunMode(STOPPED)) {
160 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
161 ✗ return;
162 }
163
164 // load commands
165 ✗ if (not this->loadFile(fileName)) {
166 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
167 ✗ return;
168 }
169
170 // clear the buffer
171 ✗ this->m_sequence->clear();
172
173 ✗ this->log_ACTIVITY_HI_CS_SequenceValid(this->m_sequence->getLogFileName());
174
175 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
176 }
177
178 //! Shared body of the seqRunIn and seqDispatchIn handlers
179 ✗ void CmdSequencerComponentImpl::doSequenceRun(const Fw::StringBase& filename) {
180 ✗ if (MANUAL == this->m_stepMode) {
181 // In MANUAL mode nothing executes until CS_STEP, so a port-driven run would wedge
182 ✗ this->log_WARNING_HI_CS_InvalidMode();
183 ✗ this->rejectSequenceRun();
184 ✗ return;
185 }
186 ✗ if (!this->requireRunMode(STOPPED)) {
187 ✗ this->rejectSequenceRun();
188 ✗ return;
189 }
190
191 // If file name is non-empty, load a file.
192 // Empty file name means don't load.
193 ✗ if (filename != "") {
194 ✗ const bool status = this->loadFile(filename);
195 ✗ if (!status) {
196 ✗ this->rejectSequenceRun();
197 ✗ return;
198 }
199 ✗ } else if (not this->m_sequence->hasMoreRecords()) {
200 // No sequence loaded
201 ✗ this->log_WARNING_LO_CS_NoSequenceActive();
202 ✗ this->error();
203 ✗ this->rejectSequenceRun();
204 ✗ return;
205 }
206
207 ✗ this->m_executedCount = 0;
208
209 // Check the step mode. If it is auto, start the sequence
210 ✗ if (AUTO == this->m_stepMode) {
211 ✗ this->m_runMode = RUNNING;
212 ✗ this->tlmWrite_CS_CurrentSequence(this->m_sequence->getStringFileName());
213 ✗ 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 ✗ this->performCmd_Step();
220 }
221
222 ✗ this->log_ACTIVITY_HI_CS_PortSequenceStarted(this->m_sequence->getLogFileName());
223 }
224
225 ✗ void CmdSequencerComponentImpl::rejectSequenceRun() {
226 ✗ if (this->isConnected_seqDone_OutputPort(0)) {
227 ✗ this->seqDone_out(0, 0, 0, Fw::CmdResponse::EXECUTION_ERROR);
228 }
229 ✗ }
230
231 ✗ void CmdSequencerComponentImpl::seqRunIn_handler(FwIndexType portNum,
232 const Fw::StringBase& filename,
233 const Svc::SeqArgs& args) {
234 (void)args; // Suppress unused parameter warning
235 ✗ this->doSequenceRun(filename);
236 ✗ }
237
238 ✗ void CmdSequencerComponentImpl::seqDispatchIn_handler(FwIndexType portNum, Fw::StringBase& file_name) {
239 ✗ this->doSequenceRun(file_name);
240 ✗ }
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 ✗ void CmdSequencerComponentImpl::CS_CANCEL_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
254 ✗ if (RUNNING == this->m_runMode) {
255 ✗ this->performCmd_Cancel();
256 ✗ this->log_ACTIVITY_HI_CS_SequenceCanceled(this->m_sequence->getLogFileName());
257 ✗ ++this->m_cancelCmdCount;
258 ✗ this->tlmWrite_CS_CancelCommands(this->m_cancelCmdCount);
259 } else {
260 ✗ this->log_WARNING_LO_CS_NoSequenceActive();
261 }
262 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
263 ✗ }
264
265 ✗ void CmdSequencerComponentImpl::CS_JOIN_WAIT_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq) {
266 // If there is no running sequence do not wait
267 ✗ if (m_runMode != RUNNING) {
268 ✗ this->log_WARNING_LO_CS_NoSequenceActive();
269 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
270 ✗ return;
271 ✗ } 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 ✗ this->log_WARNING_HI_CS_JoinWaitingNotComplete();
276 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
277 } else {
278 ✗ m_join_waiting = true;
279 ✗ Fw::LogStringArg& logFileName = this->m_sequence->getLogFileName();
280 ✗ this->log_ACTIVITY_HI_CS_JoinWaiting(logFileName, m_cmdSeq, CmdDispatcherCfg::getEventOpcode(m_opCode));
281 ✗ m_cmdSeq = cmdSeq;
282 ✗ m_opCode = opCode;
283 }
284 }
285
286 // ----------------------------------------------------------------------
287 // Private helper methods
288 // ----------------------------------------------------------------------
289
290 2 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
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 const bool status = this->checkSandbox(fileName) and this->m_sequence->loadFile(fileName);
294
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (status) {
295 2 Fw::LogStringArg& logFileName = this->m_sequence->getLogFileName();
296 2 this->log_ACTIVITY_LO_CS_SequenceLoaded(logFileName);
297 2 ++this->m_loadCmdCount;
298
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
2 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 ✗ this->m_sequence->clear();
309 }
310 2 return status;
311 }
312
313 ✗ void CmdSequencerComponentImpl::error() {
314 ✗ ++this->m_errorCount;
315 ✗ this->tlmWrite_CS_Errors(m_errorCount);
316 ✗ }
317
318 ✗ void CmdSequencerComponentImpl::performCmd_Cancel() {
319 ✗ FW_ASSERT(this->m_sequence != nullptr);
320 ✗ this->m_sequence->reset();
321 ✗ this->m_runMode = STOPPED;
322 ✗ this->m_cmdTimer.clear();
323 ✗ this->m_cmdTimeoutTimer.clear();
324 ✗ this->m_executedCount = 0;
325 // write sequence done port with error, if connected
326 ✗ if (this->isConnected_seqDone_OutputPort(0)) {
327 ✗ this->seqDone_out(0, 0, 0, Fw::CmdResponse::EXECUTION_ERROR);
328 }
329
330 ✗ 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 ✗ this->m_blockState = Svc::BlockState::NO_BLOCK;
337 ✗ }
338
339 5 void CmdSequencerComponentImpl ::cmdResponseIn_handler(FwIndexType portNum,
340 FwOpcodeType opcode,
341 U32 cmdSeq,
342 const Fw::CmdResponse& response) {
343
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (this->m_runMode == STOPPED) {
344 // Sequencer is not running
345 ✗ this->log_WARNING_HI_CS_UnexpectedCompletion(CmdDispatcherCfg::getEventOpcode(opcode));
346 } else {
347 // clear command timeout
348 5 this->m_cmdTimeoutTimer.clear();
349
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 if (response != Fw::CmdResponse::OK) {
350 ✗ this->commandError(this->m_executedCount, opcode, response.e);
351 ✗ this->performCmd_Cancel();
352
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 } else if (this->m_runMode == RUNNING && this->m_stepMode == AUTO) {
353 // Auto mode
354 5 this->commandComplete(opcode);
355
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 3 times.
10 if (not this->m_sequence->hasMoreRecords()) {
356 // No data left
357 2 this->m_runMode = STOPPED;
358 2 this->sequenceComplete();
359 } else {
360 3 this->performCmd_Step();
361 }
362 } else {
363 // Manual step mode
364 ✗ this->commandComplete(opcode);
365 ✗ if (not this->m_sequence->hasMoreRecords()) {
366 ✗ this->m_runMode = STOPPED;
367 ✗ this->sequenceComplete();
368 }
369 }
370 }
371 5 }
372
373 131 void CmdSequencerComponentImpl ::schedIn_handler(FwIndexType portNum, U32 order) {
374
1/1
✓ Branch 1 taken 131 times.
131 Fw::Time currTime = this->getTime();
375 // check to see if a command time is pending
376
4/4
✓ Branch 1 taken 131 times.
✓ Branch 4 taken 131 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 130 times.
131 if (this->m_cmdTimer.isExpiredAt(currTime)) {
377
1/1
✓ Branch 1 taken 1 times.
1 this->comCmdOut_out(0, m_record.m_command, 0);
378 1 this->m_cmdTimer.clear();
379 // start command timeout timer
380
1/1
✓ Branch 1 taken 1 times.
1 this->setCmdTimeout(currTime);
381
3/4
✓ Branch 1 taken 130 times.
✓ Branch 4 taken 130 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 130 times.
130 } else if (this->m_cmdTimeoutTimer.isExpiredAt(this->getTime())) { // check for command timeout
382 ✗ this->log_WARNING_HI_CS_SequenceTimeout(m_sequence->getLogFileName(), this->m_executedCount);
383 // If there is a command timeout, cancel the sequence
384 ✗ this->performCmd_Cancel();
385 }
386 131 }
387
388 ✗ void CmdSequencerComponentImpl ::CS_START_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
389 ✗ if (not this->m_sequence->hasMoreRecords()) {
390 // No sequence loaded
391 ✗ this->log_WARNING_LO_CS_NoSequenceActive();
392 ✗ this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
393 ✗ return;
394 }
395 ✗ if (!this->requireRunMode(STOPPED)) {
396 ✗ this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
397 ✗ return;
398 }
399
400 ✗ this->m_blockState = Svc::BlockState::NO_BLOCK;
401 ✗ this->m_runMode = RUNNING;
402 ✗ this->tlmWrite_CS_CurrentSequence(this->m_sequence->getStringFileName());
403 ✗ this->log_ACTIVITY_HI_CS_CmdStarted(this->m_sequence->getLogFileName());
404 ✗ this->performCmd_Step();
405 ✗ 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 ✗ this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
411 }
412
413 ✗ void CmdSequencerComponentImpl ::CS_STEP_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
414 ✗ FW_ASSERT(this->m_sequence != nullptr);
415 ✗ if (this->requireRunMode(RUNNING)) {
416 ✗ 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 ✗ 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 ✗ const Fw::Time pendingTime = this->m_cmdTimer.getExpirationTime();
426 ✗ this->log_WARNING_LO_CS_StepRecordPending(this->m_sequence->getLogFileName(), pendingTime.getSeconds(),
427 pendingTime.getUSeconds());
428 ✗ this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
429 ✗ return;
430 ✗ }
431 ✗ 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 ✗ this->performCmd_Step();
439 // check for special case where end of sequence entry was encountered
440 ✗ if (this->m_runMode != STOPPED) {
441 ✗ this->log_ACTIVITY_HI_CS_CmdStepped(this->m_sequence->getLogFileName(), this->m_executedCount);
442 }
443 ✗ this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
444 } else {
445 ✗ this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
446 }
447 }
448
449 ✗ void CmdSequencerComponentImpl ::CS_AUTO_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
450 ✗ if (this->requireRunMode(STOPPED)) {
451 ✗ this->m_stepMode = AUTO;
452 ✗ this->log_ACTIVITY_HI_CS_ModeSwitched(CmdSequencer_SeqMode::AUTO);
453 ✗ this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
454 } else {
455 ✗ this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
456 }
457 ✗ }
458
459 ✗ void CmdSequencerComponentImpl ::CS_MANUAL_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
460 ✗ if (this->requireRunMode(STOPPED)) {
461 ✗ this->m_stepMode = MANUAL;
462 ✗ this->log_ACTIVITY_HI_CS_ModeSwitched(CmdSequencer_SeqMode::STEP);
463 ✗ this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
464 } else {
465 ✗ this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
466 }
467 ✗ }
468
469 // ----------------------------------------------------------------------
470 // Helper methods
471 // ----------------------------------------------------------------------
472
473 2 bool CmdSequencerComponentImpl::requireRunMode(RunMode mode) {
474
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (this->m_runMode == mode) {
475 2 return true;
476 } else {
477 ✗ this->log_WARNING_HI_CS_InvalidMode();
478 ✗ return false;
479 }
480 }
481
482 ✗ void CmdSequencerComponentImpl ::commandError(const U32 number, const FwOpcodeType opCode, const U32 error) {
483 ✗ this->log_WARNING_HI_CS_CommandError(this->m_sequence->getLogFileName(), number,
484 CmdDispatcherCfg::getEventOpcode(opCode), error);
485 ✗ this->error();
486 ✗ }
487
488 5 void CmdSequencerComponentImpl::performCmd_Step() {
489
1/1
✓ Branch 1 taken 5 times.
5 this->m_sequence->nextRecord(m_record);
490 // set clock time base and context from value set when sequence was loaded
491
1/1
✓ Branch 1 taken 5 times.
5 const Sequence::Header& header = this->m_sequence->getHeader();
492
2/2
✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
5 this->m_record.m_timeTag.setTimeBase(header.m_timeBase);
493
1/1
✓ Branch 1 taken 5 times.
5 this->m_record.m_timeTag.setTimeContext(header.m_timeContext);
494
495
1/1
✓ Branch 1 taken 5 times.
5 Fw::Time currentTime = this->getTime();
496
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
5 switch (this->m_record.m_descriptor) {
497 ✗ case Sequence::Record::END_OF_SEQUENCE:
498 ✗ this->m_runMode = STOPPED;
499 ✗ this->sequenceComplete();
500 ✗ break;
501 5 case Sequence::Record::RELATIVE:
502
1/1
✓ Branch 1 taken 5 times.
5 this->performCmd_Step_RELATIVE(currentTime);
503 5 break;
504 ✗ case Sequence::Record::ABSOLUTE:
505 ✗ this->performCmd_Step_ABSOLUTE(currentTime);
506 ✗ break;
507 ✗ default:
508 ✗ FW_ASSERT(false, m_record.m_descriptor);
509 }
510 5 }
511
512 2 void CmdSequencerComponentImpl::sequenceComplete() {
513 2 FW_ASSERT(this->m_sequence != nullptr);
514 2 ++this->m_sequencesCompletedCount;
515 // reset buffer
516 2 this->m_sequence->clear();
517 2 this->log_ACTIVITY_HI_CS_SequenceComplete(this->m_sequence->getLogFileName());
518
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
2 this->tlmWrite_CS_SequencesCompleted(this->m_sequencesCompletedCount);
519 2 this->m_executedCount = 0;
520 // write sequence done port, if connected
521
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (this->isConnected_seqDone_OutputPort(0)) {
522 ✗ this->seqDone_out(0, 0, 0, Fw::CmdResponse::OK);
523 }
524
525
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 if (Svc::BlockState::BLOCK == this->m_blockState || m_join_waiting) {
526
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
2 this->cmdResponse_out(this->m_opCode, this->m_cmdSeq, Fw::CmdResponse::OK);
527 }
528
529 2 m_join_waiting = false;
530 2 this->m_blockState = Svc::BlockState::NO_BLOCK;
531
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
2 this->tlmWrite_CS_CurrentSequence(NO_SEQ);
532 2 }
533
534 5 void CmdSequencerComponentImpl::commandComplete(const FwOpcodeType opcode) {
535 5 this->log_ACTIVITY_LO_CS_CommandComplete(this->m_sequence->getLogFileName(), this->m_executedCount,
536 CmdDispatcherCfg::getEventOpcode(opcode));
537 5 ++this->m_executedCount;
538 5 ++this->m_totalExecutedCount;
539
2/2
✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
5 this->tlmWrite_CS_CommandsExecuted(this->m_totalExecutedCount);
540 5 }
541
542 5 void CmdSequencerComponentImpl ::performCmd_Step_RELATIVE(Fw::Time& currentTime) {
543 5 this->m_record.m_timeTag.add(currentTime.getSeconds(), currentTime.getUSeconds());
544 5 this->performCmd_Step_ABSOLUTE(currentTime);
545 5 }
546
547 5 void CmdSequencerComponentImpl ::performCmd_Step_ABSOLUTE(Fw::Time& currentTime) {
548
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 times.
5 if (currentTime >= this->m_record.m_timeTag) {
549 4 this->comCmdOut_out(0, m_record.m_command, 0);
550 4 this->setCmdTimeout(currentTime);
551 } else {
552
2/2
✓ Branch 1 taken 1 times.
✓ Branch 4 taken 1 times.
1 this->m_cmdTimer.set(this->m_record.m_timeTag);
553 }
554 5 }
555
556 66 void CmdSequencerComponentImpl ::pingIn_handler(FwIndexType portNum, /*!< The port number*/
557 U32 key /*!< Value to return to pinger*/
558 ) {
559 // send ping response
560 66 this->pingOut_out(0, key);
561 66 }
562
563 5 void CmdSequencerComponentImpl ::setCmdTimeout(const Fw::Time& currentTime) {
564 // start timeout timer if enabled and not in step mode
565
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
5 if ((this->m_timeout > 0) and (AUTO == this->m_stepMode)) {
566 ✗ Fw::Time expTime = currentTime;
567 ✗ expTime.add(this->m_timeout, 0);
568 ✗ this->m_cmdTimeoutTimer.set(expTime);
569 ✗ }
570 5 }
571
572 } // namespace Svc
573