F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
CommandDispatcherImpl.cpp
Go to the documentation of this file.
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>
11 #include <cstdio>
12 #include <cstring>
13 #include <limits>
14 
15 // Check the CMD_DISPATCHER_DISPATCH_TABLE_SIZE and CMD_DISPATCHER_SEQUENCER_TABLE_SIZE for overflow
16 static_assert(CMD_DISPATCHER_DISPATCH_TABLE_SIZE <= std::numeric_limits<FwOpcodeType>::max(),
17  "Opcode table limited to opcode range");
18 static_assert(CMD_DISPATCHER_SEQUENCER_TABLE_SIZE <= std::numeric_limits<U32>::max(),
19  "Sequencer table limited to range of U32");
20 
21 namespace Svc {
24  m_seq(0),
25  m_seqWrapped(false),
26  m_executeWhenSequenceTableFull(CmdDispatcherCfg::EXECUTE_WHEN_SEQUENCE_TABLE_FULL_DEFAULT),
27  m_numCmdsDispatched(0),
28  m_numCmdErrors(0),
29  m_numCmdsDropped(0) {}
30 
32 
33 void CommandDispatcherImpl::configure(bool executeWhenSequenceTableFull) {
34  this->m_executeWhenSequenceTableFull = executeWhenSequenceTableFull;
35 }
36 
37 void CommandDispatcherImpl::advanceSequenceNumber() {
38  if (this->m_seq == std::numeric_limits<U32>::max()) {
39  this->m_seqWrapped = true;
40  }
41  ++this->m_seq;
42 }
43 
44 U32 CommandDispatcherImpl::allocateSequenceNumber() {
45  // Before the first wrap, m_seq is monotonic and cannot collide with a tracked key
46  if (this->m_seqWrapped) {
47  SequenceTrackerEntry trackedCmd;
48  const FwSizeType numTrackedCommands = this->m_sequenceTracker.getSize();
49 
50  // At most numTrackedCommands keys can collide, so the loop is bounded by the table size
51  for (FwSizeType i = 0; i < numTrackedCommands; ++i) {
52  if (this->m_sequenceTracker.find(this->m_seq, trackedCmd) != Fw::Success::SUCCESS) {
53  break;
54  }
55  this->advanceSequenceNumber();
56  }
57  }
58 
59  const U32 sequenceNumber = this->m_seq;
60  this->advanceSequenceNumber();
61  return sequenceNumber;
62 }
63 
64 void CommandDispatcherImpl::compCmdReg_handler(FwIndexType portNum, FwOpcodeType opCode) {
65  FwIndexType existingPort;
66  if (this->m_entryTable.find(opCode, existingPort) == Fw::Success::SUCCESS) {
67  // Opcode already present — must be the same port (re-registration)
68  FW_ASSERT(existingPort == portNum, static_cast<FwAssertArgType>(opCode));
70  } else {
71  const I32 slot = static_cast<I32>(this->m_entryTable.getSize());
72  const Fw::Success status = this->m_entryTable.insert(opCode, portNum);
73  FW_ASSERT(status == Fw::Success::SUCCESS, static_cast<FwAssertArgType>(opCode));
75  }
76 }
77 
78 void CommandDispatcherImpl::compCmdStat_handler(FwIndexType portNum,
79  FwOpcodeType opCode,
80  U32 cmdSeq,
81  const Fw::CmdResponse& response) {
82  // check response and log
83  if (Fw::CmdResponse::OK == response.e) {
85  } else {
86  this->m_numCmdErrors++;
87  FW_ASSERT(response.e != Fw::CmdResponse::OK);
89  }
90  // look for command source
91  SequenceTrackerEntry trackedCmd;
92  const Fw::Success removeStatus = this->m_sequenceTracker.remove(cmdSeq, trackedCmd);
93  if (removeStatus == Fw::Success::SUCCESS) {
94  const FwIndexType portToCall = trackedCmd.callerPort;
95  const U32 context = trackedCmd.context;
96  FW_ASSERT(opCode == trackedCmd.opCode);
97  FW_ASSERT(portToCall < this->getNum_seqCmdStatus_OutputPorts());
98 
99  // call port to report status
100  if (this->isConnected_seqCmdStatus_OutputPort(portToCall)) {
101  // NOTE: seqCmdStatus port forwards three arguments: (opCode, cmdSeq, response).
102  // However, the cmdSeq value has no meaning for the calling sequencer.
103  // Instead, the context value is forwarded to allow the caller to utilize it if needed.
104  this->seqCmdStatus_out(portToCall, opCode, context, response);
105  }
106  }
107 }
108 
109 void CommandDispatcherImpl::seqCmdBuff_handler(FwIndexType portNum, Fw::ComBuffer& data, U32 context) {
110  Fw::CmdPacket cmdPkt;
111  Fw::SerializeStatus stat = cmdPkt.deserializeFrom(data);
112  const bool portIsConnected = this->isConnected_seqCmdStatus_OutputPort(portNum);
113 
114  if (stat != Fw::FW_SERIALIZE_OK) {
115  Fw::DeserialStatus serErr(static_cast<Fw::DeserialStatus::t>(stat));
116  this->log_WARNING_HI_MalformedCommand(serErr);
117  if (portIsConnected) {
118  this->seqCmdStatus_out(portNum, cmdPkt.getOpCode(), context, Fw::CmdResponse::VALIDATION_ERROR);
119  }
120  return;
121  }
122 
123  // look up opcode in dispatch map
124  FwIndexType entryPort;
125  Fw::Success findStatus = this->m_entryTable.find(cmdPkt.getOpCode(), entryPort);
126  if (findStatus == Fw::Success::SUCCESS and this->isConnected_compCmdSend_OutputPort(entryPort)) {
127  Fw::Success pendingInsertStatus = Fw::Success::SUCCESS;
128  const U32 sequenceNumber = this->allocateSequenceNumber();
129 
130  // register command in command tracker only if response port is connect
131  if (portIsConnected) {
132  SequenceTrackerEntry pendingCmd;
133  pendingCmd.opCode = cmdPkt.getOpCode();
134  pendingCmd.context = context;
135  pendingCmd.callerPort = portNum;
136 
137  pendingInsertStatus = this->m_sequenceTracker.insert(sequenceNumber, pendingCmd);
138 
139  // if sequence table is full, reject here unless configured to dispatch untracked
140  if (not this->m_executeWhenSequenceTableFull and pendingInsertStatus != Fw::Success::SUCCESS) {
142  this->seqCmdStatus_out(portNum, cmdPkt.getOpCode(), context, Fw::CmdResponse::EXECUTION_ERROR);
143  return;
144  }
145  } // end if status port connected
146  // pass arguments to argument buffer
147  this->compCmdSend_out(entryPort, cmdPkt.getOpCode(), sequenceNumber, cmdPkt.getArgBuffer());
148  // log dispatched command
150 
151  // increment command count
152  this->m_numCmdsDispatched++;
153 
154  // pendingInsertStatus is only non-SUCCESS for a connected caller whose insert failed (see check above)
155  if (this->m_executeWhenSequenceTableFull and pendingInsertStatus != Fw::Success::SUCCESS) {
157  this->seqCmdStatus_out(portNum, cmdPkt.getOpCode(), context, Fw::CmdResponse::DISPATCHED_UNTRACKED);
158  }
159  } else {
161  this->m_numCmdErrors++;
162  // Fail command back to port, if connected
163  if (portIsConnected) {
164  this->seqCmdStatus_out(portNum, cmdPkt.getOpCode(), context, Fw::CmdResponse::INVALID_OPCODE);
165  }
166  // Preserve the existing behavior of consuming a sequence number for an invalid opcode.
167  this->advanceSequenceNumber();
168  }
169 }
170 
171 void CommandDispatcherImpl ::run_handler(FwIndexType portNum, U32 context) {
172  this->tlmWrite_CommandsDropped(this->m_numCmdsDropped.load(std::memory_order_relaxed));
173  this->tlmWrite_CommandErrors(this->m_numCmdErrors);
174  this->tlmWrite_CommandsDispatched(this->m_numCmdsDispatched);
175 }
176 
177 void CommandDispatcherImpl::CMD_NO_OP_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
178  Fw::LogStringArg no_op_string("Hello, World!");
179  // Log event for NO_OP here.
181  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
182 }
183 
184 void CommandDispatcherImpl::CMD_NO_OP_STRING_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, const Fw::CmdStringArg& arg1) {
185  Fw::LogStringArg msg(arg1.toChar());
186  // Echo the NO_OP_STRING args here.
188  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
189 }
190 
191 void CommandDispatcherImpl::CMD_TEST_CMD_1_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, I32 arg1, F32 arg2, U8 arg3) {
192  this->log_ACTIVITY_HI_TestCmd1Args(arg1, arg2, arg3);
193  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
194 }
195 
196 void CommandDispatcherImpl::CMD_CLEAR_TRACKING_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
197  // Preserve this command's own entry so its OK status still reaches the caller
198  SequenceTrackerEntry selfEntry = {};
199  const bool selfTracked = (this->m_sequenceTracker.find(cmdSeq, selfEntry) == Fw::Success::SUCCESS);
200 
201  // Notify every other caller that its pending status will never arrive
202  for (const auto& entry : this->m_sequenceTracker) {
203  if (entry.getKey() == cmdSeq) {
204  continue;
205  }
206  const SequenceTrackerEntry& trackedCmd = entry.getValue();
207  FW_ASSERT(trackedCmd.callerPort < this->getNum_seqCmdStatus_OutputPorts());
208  if (this->isConnected_seqCmdStatus_OutputPort(trackedCmd.callerPort)) {
209  this->seqCmdStatus_out(trackedCmd.callerPort, trackedCmd.opCode, trackedCmd.context,
211  }
212  }
213 
214  this->m_sequenceTracker.clear();
215  if (selfTracked) {
216  const Fw::Success status = this->m_sequenceTracker.insert(cmdSeq, selfEntry);
217  FW_ASSERT(status == Fw::Success::SUCCESS);
218  }
219  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
220 }
221 
222 void CommandDispatcherImpl::pingIn_handler(FwIndexType portNum, U32 key) {
223  // respond to ping
224  this->pingOut_out(0, key);
225 }
226 
227 void CommandDispatcherImpl::seqCmdBuff_overflowHook(FwIndexType portNum, Fw::ComBuffer& data, U32 context) {
228  FwOpcodeType opcode = 0; // Note: 0 = Reserved opcode
230  Fw::CmdPacket cmdPkt;
231  const Fw::SerializeStatus stat = cmdPkt.deserializeFrom(data);
232  if (stat == Fw::FW_SERIALIZE_OK) {
233  opcode = cmdPkt.getOpCode();
234  }
235  }
236 
238  // This hook runs on the caller's thread; the counter is atomic so no lock is needed
239  this->m_numCmdsDropped.fetch_add(1, std::memory_order_relaxed);
240 }
241 
242 } // namespace Svc
constexpr FwOpcodeType getEventOpcode(const FwOpcodeType opcode)
Serialization/Deserialization operation was successful.
void seqCmdStatus_out(FwIndexType portNum, FwOpcodeType opCode, U32 cmdSeq, const Fw::CmdResponse &response) const
Invoke output port seqCmdStatus.
FwIdType FwOpcodeType
The type of a command opcode.
constexpr bool IncludeCommandOpcodesInEvents
Representing success.
PlatformSizeType FwSizeType
void log_WARNING_HI_CommandDroppedQueueOverflow(FwOpcodeType OpCode, U32 Context)
void log_COMMAND_OpCodeError(FwOpcodeType Opcode, const Fw::CmdResponse &error) const
Invalid opcode dispatched.
Deserialization status.
enum T e
The raw enum value.
FwSizeType getSize() const override
void log_ACTIVITY_HI_TestCmd1Args(I32 arg1, F32 arg2, U8 arg3) const
void compCmdSend_out(FwIndexType portNum, FwOpcodeType opCode, U32 cmdSeq, Fw::CmdArgBuffer &args) const
Invoke output port compCmdSend.
Enum representing a command response.
Success find(const K &key, V &value) const override
void log_DIAGNOSTIC_OpCodeReregistered(FwOpcodeType Opcode, I32 port) const
Command tracking was cleared before the command completed.
SerializeStatus
forward declaration for string
float F32
32-bit floating point
Definition: BasicTypes.h:84
void log_COMMAND_OpCodeCompleted(FwOpcodeType Opcode) const
void log_ACTIVITY_HI_NoOpStringReceived(const Fw::StringBase &message) const
void tlmWrite_CommandsDispatched(U32 arg, Fw::Time _tlmTime=Fw::Time())
Success insert(const K &key, const V &value) override
Definition: ArrayMap.hpp:97
SerializeStatus deserializeFrom(SerialBufferBase &buffer, Fw::Endianness mode=Fw::Endianness::BIG) override
Deserialize the contents of this object from a buffer.
Definition: CmdPacket.cpp:27
Success remove(const K &key, V &value) override
Definition: ArrayMap.hpp:105
void tlmWrite_CommandsDropped(U32 arg, Fw::Time _tlmTime=Fw::Time())
Component responsible for dispatching incoming commands to registered components. ...
bool isConnected_compCmdSend_OutputPort(FwIndexType portNum) const
virtual ~CommandDispatcherImpl()
Component destructor.
void tlmWrite_CommandErrors(U32 arg, Fw::Time _tlmTime=Fw::Time())
CommandDispatcherImpl(const char *name)
Command Dispatcher constructor.
void pingOut_out(FwIndexType portNum, U32 key) const
Invoke output port pingOut.
const char * toChar() const
Convert to a C-style char*.
FwOpcodeType getOpCode() const
Definition: CmdPacket.cpp:57
void log_WARNING_HI_InvalidCommand(FwOpcodeType Opcode) const
Command successfully executed.
constexpr bool EXECUTE_WHEN_SEQUENCE_TABLE_FULL_DEFAULT
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:54
CmdArgBuffer & getArgBuffer()
Definition: CmdPacket.cpp:61
Command had execution error.
Success find(const K &key, V &value) const override
Definition: ArrayMap.hpp:81
PlatformIndexType FwIndexType
void log_DIAGNOSTIC_OpCodeRegistered(FwOpcodeType Opcode, I32 port, I32 slot) const
Log event OpCodeRegistered.
void log_WARNING_HI_MalformedCommand(const Fw::DeserialStatus &Status) const
bool isConnected_seqCmdStatus_OutputPort(FwIndexType portNum) const
Auto-generated base for CommandDispatcher component.
Command failed validation.
RateGroupDivider component implementation.
void configure(bool executeWhenSequenceTableFull=CmdDispatcherCfg::EXECUTE_WHEN_SEQUENCE_TABLE_FULL_DEFAULT)
Configure behavior when the sequence tracker table is full.
Command was dispatched but the response tracking failed, indicating unknown state.
FwSizeType getSize() const override
Definition: ArrayMap.hpp:93
Success insert(const K &key, const V &value) override
static constexpr FwIndexType getNum_seqCmdStatus_OutputPorts()
void log_WARNING_HI_TooManyCommands(FwOpcodeType Opcode) const
#define FW_ASSERT(...)
Definition: Assert.hpp:14
void cmdResponse_out(FwOpcodeType opCode, U32 cmdSeq, Fw::CmdResponse response)
Emit command response.
Success/Failure.
void log_COMMAND_OpCodeDispatched(FwOpcodeType Opcode, I32 port) const