GCC Code Coverage Report


Directory: /home/runner/work/fprime/fprime/Svc/CmdDispatcher/
File: CommandDispatcherImpl.cpp
Date: 2026-09-03 22:13:30
Exec Total Coverage
Lines: 59 110 53.6%
Functions: 9 12 75.0%
Branches: 47 113 41.6%

Line Branch Exec Source
1 /*
2 * CommandDispatcherImpl.cpp
3 *
4 * Created on: May 13, 2014
5 * Author: Timothy Canham
6 */
7
8 #include <Fw/Cmd/CmdPacket.hpp>
9 #include <Fw/Types/Assert.hpp>
10 #include <Svc/CmdDispatcher/CommandDispatcherImpl.hpp>
11 #include <cstdio>
12 #include <cstring>
13
14 // Check the CMD_DISPATCHER_DISPATCH_TABLE_SIZE and CMD_DISPATCHER_SEQUENCER_TABLE_SIZE for overflow
15 static_assert(CMD_DISPATCHER_DISPATCH_TABLE_SIZE <= std::numeric_limits<FwOpcodeType>::max(),
16 "Opcode table limited to opcode range");
17 static_assert(CMD_DISPATCHER_SEQUENCER_TABLE_SIZE <= std::numeric_limits<U32>::max(),
18 "Sequencer table limited to range of U32");
19
20 namespace Svc {
21 1 CommandDispatcherImpl::CommandDispatcherImpl(const char* name)
22
2/2
✓ Branch 2 taken 1 times.
✓ Branch 5 taken 1 times.
1 : CommandDispatcherComponentBase(name), m_seq(0), m_numCmdsDispatched(0), m_numCmdErrors(0), m_numCmdsDropped(0) {}
23
24 2 CommandDispatcherImpl::~CommandDispatcherImpl() {}
25
26 104 void CommandDispatcherImpl::compCmdReg_handler(FwIndexType portNum, FwOpcodeType opCode) {
27 FwIndexType existingPort;
28
2/3
✓ Branch 1 taken 104 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 104 times.
104 if (this->m_entryTable.find(opCode, existingPort) == Fw::Success::SUCCESS) {
29 // Opcode already present — must be the same port (re-registration)
30 FW_ASSERT(existingPort == portNum, static_cast<FwAssertArgType>(opCode));
31 this->log_DIAGNOSTIC_OpCodeReregistered(CmdDispatcherCfg::getEventOpcode(opCode), portNum);
32 } else {
33
1/1
✓ Branch 1 taken 104 times.
104 const I32 slot = static_cast<I32>(this->m_entryTable.getSize());
34
1/1
✓ Branch 1 taken 104 times.
104 const Fw::Success status = this->m_entryTable.insert(opCode, portNum);
35 104 FW_ASSERT(status == Fw::Success::SUCCESS, static_cast<FwAssertArgType>(opCode));
36
1/1
✓ Branch 2 taken 104 times.
104 this->log_DIAGNOSTIC_OpCodeRegistered(CmdDispatcherCfg::getEventOpcode(opCode), portNum, slot);
37 104 }
38 104 }
39
40 145 void CommandDispatcherImpl::compCmdStat_handler(FwIndexType portNum,
41 FwOpcodeType opCode,
42 U32 cmdSeq,
43 const Fw::CmdResponse& response) {
44 // check response and log
45
1/2
✓ Branch 0 taken 145 times.
✗ Branch 1 not taken.
145 if (Fw::CmdResponse::OK == response.e) {
46
1/1
✓ Branch 2 taken 145 times.
145 this->log_COMMAND_OpCodeCompleted(CmdDispatcherCfg::getEventOpcode(opCode));
47 } else {
48 this->m_numCmdErrors++;
49 FW_ASSERT(response.e != Fw::CmdResponse::OK);
50 this->log_COMMAND_OpCodeError(CmdDispatcherCfg::getEventOpcode(opCode), response);
51 }
52 // look for command source
53 SequenceTrackerEntry trackedCmd;
54
1/1
✓ Branch 1 taken 145 times.
145 const Fw::Success removeStatus = this->m_sequenceTracker.remove(cmdSeq, trackedCmd);
55
1/2
✓ Branch 1 taken 145 times.
✗ Branch 2 not taken.
145 if (removeStatus == Fw::Success::SUCCESS) {
56 145 const FwIndexType portToCall = trackedCmd.callerPort;
57 145 const U32 context = trackedCmd.context;
58 145 FW_ASSERT(opCode == trackedCmd.opCode);
59 145 FW_ASSERT(portToCall < this->getNum_seqCmdStatus_OutputPorts());
60
61 // call port to report status
62
2/3
✓ Branch 1 taken 145 times.
✓ Branch 3 taken 145 times.
✗ Branch 4 not taken.
145 if (this->isConnected_seqCmdStatus_OutputPort(portToCall)) {
63 // NOTE: seqCmdStatus port forwards three arguments: (opCode, cmdSeq, response).
64 // However, the cmdSeq value has no meaning for the calling sequencer.
65 // Instead, the context value is forwarded to allow the caller to utilize it if needed.
66
1/1
✓ Branch 1 taken 145 times.
145 this->seqCmdStatus_out(portToCall, opCode, context, response);
67 }
68 }
69 145 }
70
71 145 void CommandDispatcherImpl::seqCmdBuff_handler(FwIndexType portNum, Fw::ComBuffer& data, U32 context) {
72
1/1
✓ Branch 1 taken 145 times.
145 Fw::CmdPacket cmdPkt;
73
1/1
✓ Branch 1 taken 145 times.
145 Fw::SerializeStatus stat = cmdPkt.deserializeFrom(data);
74
75
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 145 times.
145 if (stat != Fw::FW_SERIALIZE_OK) {
76 Fw::DeserialStatus serErr(static_cast<Fw::DeserialStatus::t>(stat));
77 this->log_WARNING_HI_MalformedCommand(serErr);
78 if (this->isConnected_seqCmdStatus_OutputPort(portNum)) {
79 this->seqCmdStatus_out(portNum, cmdPkt.getOpCode(), context, Fw::CmdResponse::VALIDATION_ERROR);
80 }
81 return;
82 }
83
84 // look up opcode in dispatch map
85 FwIndexType entryPort;
86
2/2
✓ Branch 1 taken 145 times.
✓ Branch 4 taken 145 times.
145 Fw::Success findStatus = this->m_entryTable.find(cmdPkt.getOpCode(), entryPort);
87
4/7
✓ Branch 1 taken 145 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 145 times.
✓ Branch 6 taken 145 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 145 times.
✗ Branch 9 not taken.
145 if (findStatus == Fw::Success::SUCCESS and this->isConnected_compCmdSend_OutputPort(entryPort)) {
88 // register command in command tracker only if response port is connect
89
2/3
✓ Branch 1 taken 145 times.
✓ Branch 3 taken 145 times.
✗ Branch 4 not taken.
145 if (this->isConnected_seqCmdStatus_OutputPort(portNum)) {
90 SequenceTrackerEntry pendingCmd;
91
1/1
✓ Branch 1 taken 145 times.
145 pendingCmd.opCode = cmdPkt.getOpCode();
92 145 pendingCmd.context = context;
93 145 pendingCmd.callerPort = portNum;
94
95
1/1
✓ Branch 1 taken 145 times.
145 const Fw::Success pendingInsertStatus = this->m_sequenceTracker.insert(this->m_seq, pendingCmd);
96
97 // if we couldn't find a slot to track the command, quit
98
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 145 times.
145 if (pendingInsertStatus != Fw::Success::SUCCESS) {
99 this->log_WARNING_HI_TooManyCommands(CmdDispatcherCfg::getEventOpcode(cmdPkt.getOpCode()));
100 if (this->isConnected_seqCmdStatus_OutputPort(portNum)) {
101 this->seqCmdStatus_out(portNum, cmdPkt.getOpCode(), context, Fw::CmdResponse::EXECUTION_ERROR);
102 }
103 return;
104 }
105 145 } // end if status port connected
106 // pass arguments to argument buffer
107
3/3
✓ Branch 1 taken 145 times.
✓ Branch 4 taken 145 times.
✓ Branch 7 taken 145 times.
145 this->compCmdSend_out(entryPort, cmdPkt.getOpCode(), this->m_seq, cmdPkt.getArgBuffer());
108 // log dispatched command
109
2/2
✓ Branch 1 taken 145 times.
✓ Branch 5 taken 145 times.
145 this->log_COMMAND_OpCodeDispatched(CmdDispatcherCfg::getEventOpcode(cmdPkt.getOpCode()), entryPort);
110
111 // increment command count
112 145 this->m_numCmdsDispatched++;
113 } else {
114 this->log_WARNING_HI_InvalidCommand(CmdDispatcherCfg::getEventOpcode(cmdPkt.getOpCode()));
115 this->m_numCmdErrors++;
116 // Fail command back to port, if connected
117 if (this->isConnected_seqCmdStatus_OutputPort(portNum)) {
118 this->seqCmdStatus_out(portNum, cmdPkt.getOpCode(), context, Fw::CmdResponse::INVALID_OPCODE);
119 }
120 }
121
122 // increment sequence number
123 145 this->m_seq++;
124 145 }
125
126 235 void CommandDispatcherImpl ::run_handler(FwIndexType portNum, U32 context) {
127
2/2
✓ Branch 1 taken 235 times.
✓ Branch 4 taken 235 times.
235 this->tlmWrite_CommandsDropped(this->m_numCmdsDropped);
128
2/2
✓ Branch 1 taken 235 times.
✓ Branch 4 taken 235 times.
235 this->tlmWrite_CommandErrors(this->m_numCmdErrors);
129
2/2
✓ Branch 1 taken 235 times.
✓ Branch 4 taken 235 times.
235 this->tlmWrite_CommandsDispatched(this->m_numCmdsDispatched);
130 235 }
131
132 107 void CommandDispatcherImpl::CMD_NO_OP_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
133
1/1
✓ Branch 1 taken 107 times.
107 Fw::LogStringArg no_op_string("Hello, World!");
134 // Log event for NO_OP here.
135
1/1
✓ Branch 1 taken 107 times.
107 this->log_ACTIVITY_HI_NoOpReceived();
136
2/2
✓ Branch 1 taken 107 times.
✓ Branch 4 taken 107 times.
107 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
137 107 }
138
139 3 void CommandDispatcherImpl::CMD_NO_OP_STRING_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, const Fw::CmdStringArg& arg1) {
140
1/1
✓ Branch 2 taken 3 times.
3 Fw::LogStringArg msg(arg1.toChar());
141 // Echo the NO_OP_STRING args here.
142
1/1
✓ Branch 1 taken 3 times.
3 this->log_ACTIVITY_HI_NoOpStringReceived(msg);
143
2/2
✓ Branch 1 taken 3 times.
✓ Branch 4 taken 3 times.
3 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
144 3 }
145
146 void CommandDispatcherImpl::CMD_TEST_CMD_1_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, I32 arg1, F32 arg2, U8 arg3) {
147 this->log_ACTIVITY_HI_TestCmd1Args(arg1, arg2, arg3);
148 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
149 }
150
151 void CommandDispatcherImpl::CMD_CLEAR_TRACKING_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
152 // Preserve this command's own entry so its OK status still reaches the caller
153 SequenceTrackerEntry selfEntry = {};
154 const bool selfTracked = (this->m_sequenceTracker.find(cmdSeq, selfEntry) == Fw::Success::SUCCESS);
155
156 // Notify every other caller that its pending status will never arrive
157 for (const auto& entry : this->m_sequenceTracker) {
158 if (entry.getKey() == cmdSeq) {
159 continue;
160 }
161 const SequenceTrackerEntry& trackedCmd = entry.getValue();
162 FW_ASSERT(trackedCmd.callerPort < this->getNum_seqCmdStatus_OutputPorts());
163 if (this->isConnected_seqCmdStatus_OutputPort(trackedCmd.callerPort)) {
164 this->seqCmdStatus_out(trackedCmd.callerPort, trackedCmd.opCode, trackedCmd.context,
165 Fw::CmdResponse::CLEARED);
166 }
167 }
168
169 this->m_sequenceTracker.clear();
170 if (selfTracked) {
171 const Fw::Success status = this->m_sequenceTracker.insert(cmdSeq, selfEntry);
172 FW_ASSERT(status == Fw::Success::SUCCESS);
173 }
174 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
175 }
176
177 59 void CommandDispatcherImpl::pingIn_handler(FwIndexType portNum, U32 key) {
178 // respond to ping
179 59 this->pingOut_out(0, key);
180 59 }
181
182 void CommandDispatcherImpl::seqCmdBuff_overflowHook(FwIndexType portNum, Fw::ComBuffer& data, U32 context) {
183 FwOpcodeType opcode = 0; // Note: 0 = Reserved opcode
184 if (CmdDispatcherCfg::IncludeCommandOpcodesInEvents) {
185 Fw::CmdPacket cmdPkt;
186 const Fw::SerializeStatus stat = cmdPkt.deserializeFrom(data);
187 if (stat == Fw::FW_SERIALIZE_OK) {
188 opcode = cmdPkt.getOpCode();
189 }
190 }
191
192 this->log_WARNING_HI_CommandDroppedQueueOverflow(CmdDispatcherCfg::getEventOpcode(opcode), context);
193 // Increment CommandsDroppedBufOverflow counter
194 this->m_numCmdsDropped++;
195 }
196
197 } // namespace Svc
198