GCC Code Coverage Report


Directory: Svc/SeqDispatcher/
File: SeqDispatcher.cpp
Date: 2026-09-03 21:18:22
Exec Total Coverage
Lines: 82 106 77.4%
Functions: 10 12 83.3%
Branches: 84 109 77.1%

Line Branch Exec Source
1 // ======================================================================
2 // \title SeqDispatcher.cpp
3 // \author zimri.leisher
4 // \brief cpp file for SeqDispatcher component implementation class
5 // ======================================================================
6
7 #include <Svc/SeqDispatcher/SeqDispatcher.hpp>
8
9 namespace Svc {
10
11 // ----------------------------------------------------------------------
12 // Construction, initialization, and destruction
13 // ----------------------------------------------------------------------
14
15
3/7
✓ Branch 26 taken 20 times.
✓ Branch 29 taken 20 times.
✓ Branch 30 taken 10 times.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
30 SeqDispatcher ::SeqDispatcher(const char* const compName) : SeqDispatcherComponentBase(compName) {}
16
17
3/4
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 11 taken 20 times.
✓ Branch 12 taken 10 times.
60 SeqDispatcher ::~SeqDispatcher() {}
18
19 16 FwIndexType SeqDispatcher::getNextAvailableSequencerIdx() {
20
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 2 times.
23 for (FwIndexType i = 0; i < SeqDispatcherSequencerPorts; i++) {
21
5/6
✓ Branch 5 taken 21 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 14 times.
✓ Branch 8 taken 7 times.
✓ Branch 9 taken 14 times.
✓ Branch 10 taken 7 times.
42 if (this->isConnected_seqRunOut_OutputPort(i) &&
22 21 this->m_entryTable[i].state == SeqDispatcher_CmdSequencerState::AVAILABLE) {
23 14 return i;
24 }
25 }
26 2 return -1;
27 }
28
29 14 void SeqDispatcher::runSequence(FwIndexType sequencerIdx,
30 const Fw::ConstStringBase& fileName,
31 BlockState block,
32 const Svc::SeqArgs& args) {
33 // this function is only designed for internal usage
34 // we can guarantee it cannot be called with input that would fail
35 14 FW_ASSERT(sequencerIdx >= 0 && sequencerIdx < SeqDispatcherSequencerPorts,
36 static_cast<FwAssertArgType>(sequencerIdx));
37 14 FW_ASSERT(this->isConnected_seqRunOut_OutputPort(sequencerIdx));
38 14 FW_ASSERT(this->m_entryTable[sequencerIdx].state == SeqDispatcher_CmdSequencerState::AVAILABLE,
39 static_cast<FwAssertArgType>(this->m_entryTable[sequencerIdx].state));
40
41
2/2
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 7 times.
14 if (block == BlockState::NO_BLOCK) {
42 7 this->m_entryTable[sequencerIdx].state = SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_NO_BLOCK;
43 } else {
44 7 this->m_entryTable[sequencerIdx].state = SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_BLOCK;
45 }
46
47 14 this->m_sequencersAvailable--;
48
2/2
✓ Branch 6 taken 14 times.
✓ Branch 13 taken 14 times.
14 this->tlmWrite_sequencersAvailable(this->m_sequencersAvailable);
49 14 this->m_entryTable[sequencerIdx].sequenceRunning = fileName;
50
51 14 this->m_dispatchedCount++;
52
2/2
✓ Branch 6 taken 14 times.
✓ Branch 13 taken 14 times.
14 this->tlmWrite_dispatchedCount(this->m_dispatchedCount);
53 14 this->seqRunOut_out(sequencerIdx, this->m_entryTable[sequencerIdx].sequenceRunning, args);
54 14 }
55
56 void SeqDispatcher::seqStartIn_handler(FwIndexType portNum, //!< The port number
57 const Fw::StringBase& fileName, //!< The sequence file name
58 const Svc::SeqArgs& args //!< Sequence arguments (not currently used)
59 ) {
60 (void)args; // Suppress unused parameter warning
61 FW_ASSERT(portNum >= 0 && portNum < SeqDispatcherSequencerPorts, static_cast<FwAssertArgType>(portNum));
62 if (this->m_entryTable[portNum].state == SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_BLOCK ||
63 this->m_entryTable[portNum].state == SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_NO_BLOCK) {
64 // we were aware of this sequencer running a sequence
65 if (this->m_entryTable[portNum].sequenceRunning != fileName) {
66 // uh oh. entry table is wrong
67 // let's just update it to be correct. nothing we can do about
68 // it except raise a warning and update our state
69 this->log_WARNING_HI_ConflictingSequenceStarted(static_cast<U16>(portNum), fileName,
70 this->m_entryTable[portNum].sequenceRunning);
71 this->m_entryTable[portNum].sequenceRunning = fileName;
72 }
73 } else {
74 // we were not aware that this sequencer was running. ground must have
75 // directly commanded that specific sequencer
76
77 // warn because this may be unintentional
78 this->log_WARNING_LO_UnexpectedSequenceStarted(static_cast<U16>(portNum), fileName);
79
80 // update the state
81 this->m_entryTable[portNum].state = SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_NO_BLOCK;
82 this->m_entryTable[portNum].sequenceRunning = fileName;
83 this->m_sequencersAvailable--;
84 this->tlmWrite_sequencersAvailable(this->m_sequencersAvailable);
85 }
86 }
87
88 8 void SeqDispatcher::seqDoneIn_handler(FwIndexType portNum, //!< The port number
89 FwOpcodeType opCode, //!< Command Op Code
90 U32 cmdSeq, //!< Command Sequence
91 const Fw::CmdResponse& response //!< The command response argument
92 ) {
93 8 FW_ASSERT(portNum >= 0 && portNum < SeqDispatcherSequencerPorts, static_cast<FwAssertArgType>(portNum));
94
4/6
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 4 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 8 times.
12 if (this->m_entryTable[portNum].state != SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_BLOCK &&
95 4 this->m_entryTable[portNum].state != SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_NO_BLOCK) {
96 // this sequencer was not running a sequence that we were aware of.
97
98 // we should have caught this in seqStartIn and updated the state
99 // accordingly, but somehow we didn't? very sad and shouldn't happen
100
101 // anyways, don't have to do anything cuz now that this seq we didn't know
102 // about is done, the sequencer is available again (which is its current
103 // state in our internal entry table already)
104 this->log_WARNING_LO_UnknownSequenceFinished(static_cast<U16>(portNum));
105 // sequencer was already counted available; don't increment again
106 this->m_entryTable[portNum].state = SeqDispatcher_CmdSequencerState::AVAILABLE;
107 this->m_entryTable[portNum].sequenceRunning = "<no seq>";
108 return;
109 } else {
110 // ok, a sequence has finished that we knew about
111
2/2
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 4 times.
8 if (this->m_entryTable[portNum].state == SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_BLOCK) {
112 // we need to give a cmd response cuz some other sequence is being blocked
113 // by this
114
2/2
✓ Branch 6 taken 4 times.
✓ Branch 17 taken 4 times.
4 this->cmdResponse_out(this->m_entryTable[portNum].opCode, this->m_entryTable[portNum].cmdSeq, response);
115
116
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 2 times.
4 if (response == Fw::CmdResponse::EXECUTION_ERROR) {
117 // dispatched sequence errored
118 2 this->m_errorCount++;
119
2/2
✓ Branch 6 taken 2 times.
✓ Branch 13 taken 2 times.
2 this->tlmWrite_errorCount(this->m_errorCount);
120 }
121 }
122 }
123
124 // all command responses mean the sequence is no longer running
125 // so component should be available
126 8 this->m_entryTable[portNum].state = SeqDispatcher_CmdSequencerState::AVAILABLE;
127 8 this->m_entryTable[portNum].sequenceRunning = "<no seq>";
128 8 this->m_sequencersAvailable++;
129
2/2
✓ Branch 6 taken 8 times.
✓ Branch 13 taken 8 times.
8 this->tlmWrite_sequencersAvailable(this->m_sequencersAvailable);
130 }
131
132 //! Handler for input port seqRunIn
133 void SeqDispatcher::seqRunIn_handler(FwIndexType portNum, const Fw::StringBase& fileName, const Svc::SeqArgs& args) {
134 FwIndexType idx = this->getNextAvailableSequencerIdx();
135 // no available sequencers
136 if (idx == -1) {
137 this->log_WARNING_HI_NoAvailableSequencers();
138 return;
139 }
140
141 this->runSequence(idx, fileName, BlockState::NO_BLOCK, args);
142 }
143 // ----------------------------------------------------------------------
144 // Command handler implementations
145 // ----------------------------------------------------------------------
146
147 // RUN command delegates to RUN_ARGS with empty arguments for backward compatibility
148 5 void SeqDispatcher ::RUN_cmdHandler(const FwOpcodeType opCode,
149 const U32 cmdSeq,
150 const Fw::CmdStringArg& fileName,
151 const BlockState& block) {
152 // Create empty args and delegate to RUN_ARGS handler
153
1/1
✓ Branch 2 taken 5 times.
5 Svc::SeqArgs emptyArgs{0, 0};
154
1/1
✓ Branch 4 taken 5 times.
5 this->RUN_ARGS_cmdHandler(opCode, cmdSeq, fileName, block, emptyArgs);
155 10 }
156
157 // RUN_ARGS command dispatches a sequence with optional arguments to the first available sequencer
158 16 void SeqDispatcher ::RUN_ARGS_cmdHandler(const FwOpcodeType opCode,
159 const U32 cmdSeq,
160 const Fw::CmdStringArg& fileName,
161 const BlockState& block,
162 const Svc::SeqArgs& buffer) {
163 16 FwIndexType idx = this->getNextAvailableSequencerIdx();
164 // no available sequencers
165
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 14 times.
16 if (idx == -1) {
166 2 this->log_WARNING_HI_NoAvailableSequencers();
167
2/2
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
2 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
168 2 return;
169 }
170
171
2/2
✓ Branch 5 taken 14 times.
✓ Branch 11 taken 14 times.
14 this->runSequence(idx, fileName, block, buffer);
172
173
2/2
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 7 times.
14 if (block == BlockState::NO_BLOCK) {
174 // return instantly
175
2/2
✓ Branch 6 taken 7 times.
✓ Branch 9 taken 7 times.
7 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
176 } else {
177 // otherwise don't return a response yet. just save the opCode and cmdSeq
178 // so we can return a response later
179 7 this->m_entryTable[idx].opCode = opCode;
180 7 this->m_entryTable[idx].cmdSeq = cmdSeq;
181 }
182 }
183
184 1 void SeqDispatcher::LOG_STATUS_cmdHandler(const FwOpcodeType opCode, /*!< The opcode*/
185 const U32 cmdSeq) { /*!< The command sequence number*/
186
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (FwIndexType idx = 0; idx < SeqDispatcherSequencerPorts; idx++) {
187
1/1
✓ Branch 10 taken 2 times.
4 this->log_ACTIVITY_LO_LogSequencerStatus(static_cast<U16>(idx), this->m_entryTable[idx].state,
188
1/1
✓ Branch 4 taken 2 times.
4 Fw::LogStringArg(this->m_entryTable[idx].sequenceRunning));
189 }
190
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
191 1 }
192
193 2 void SeqDispatcher::CANCEL_NAME_cmdHandler(
194 const FwOpcodeType opCode, /*!< The opcode*/
195 const U32 cmdSeq, /*!< The command sequence number*/
196 const Fw::CmdStringArg& fileName) /*!< The name of the sequence file to cancel*/ {
197 2 bool canceled = false;
198
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (FwIndexType idx = 0; idx < SeqDispatcherSequencerPorts; idx++) {
199 // only slots actively running the named sequence are candidates
200 4 const bool running = this->m_entryTable[idx].state != SeqDispatcher_CmdSequencerState::AVAILABLE;
201
5/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 3 times.
4 if (running && this->m_entryTable[idx].sequenceRunning == fileName) {
202
1/2
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
1 if (this->isConnected_seqCancelOut_OutputPort(idx)) {
203 1 this->seqCancelOut_out(idx);
204 // Entry table is cleared via seqDoneIn_handler
205
1/1
✓ Branch 7 taken 1 times.
2 this->log_ACTIVITY_HI_SequenceCanceled(static_cast<U16>(idx),
206
1/1
✓ Branch 4 taken 1 times.
2 Fw::LogStringArg(this->m_entryTable[idx].sequenceRunning));
207
2/2
✓ Branch 6 taken 1 times.
✓ Branch 17 taken 1 times.
1 this->tlmWrite_canceledCount(++this->m_canceledCount);
208 1 canceled = true;
209 }
210 }
211 }
212
213
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (canceled) {
214
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
215 } else {
216
2/2
✓ Branch 9 taken 1 times.
✓ Branch 13 taken 1 times.
1 this->log_WARNING_LO_CancelSequenceNotFound(Fw::LogStringArg(fileName));
217
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
218 }
219 2 }
220
221 //! Broadcast a cancel to every running sequencer.
222 //! This does not exclude the caller!
223 //! A sequence issuing CANCEL_ALL will cancel itself is connected to this seqDispatcher.
224 2 void SeqDispatcher::CANCEL_ALL_cmdHandler(const FwOpcodeType opCode, /*!< The opcode*/
225 const U32 cmdSeq) { /*!< The command sequence number*/
226
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (FwIndexType idx = 0; idx < SeqDispatcherSequencerPorts; idx++) {
227 4 const bool running = this->m_entryTable[idx].state != SeqDispatcher_CmdSequencerState::AVAILABLE;
228
5/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 2 times.
✓ Branch 10 taken 2 times.
4 if (running && this->isConnected_seqCancelOut_OutputPort(idx)) {
229 2 this->seqCancelOut_out(idx);
230 // Entry table is cleared via seqDoneIn_handler
231
1/1
✓ Branch 7 taken 2 times.
4 this->log_ACTIVITY_HI_SequenceCanceled(static_cast<U16>(idx),
232
1/1
✓ Branch 4 taken 2 times.
4 Fw::LogStringArg(this->m_entryTable[idx].sequenceRunning));
233
2/2
✓ Branch 6 taken 2 times.
✓ Branch 17 taken 2 times.
2 this->tlmWrite_canceledCount(++this->m_canceledCount);
234 }
235 }
236
2/2
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
2 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
237 2 }
238 } // namespace Svc
239