F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
FpySequencer.cpp
Go to the documentation of this file.
1 // ======================================================================
2 // \title FpySequencer.cpp
3 // \author zimri.leisher
4 // \brief cpp file for FpySequencer component implementation class
5 // ======================================================================
6 
8 
9 namespace Svc {
10 
11 // ----------------------------------------------------------------------
12 // Construction, initialization, and destruction
13 // ----------------------------------------------------------------------
14 
15 FpySequencer ::FpySequencer(const char* const compName)
16  : FpySequencerComponentBase(compName),
17  m_sequenceBuffer(),
18  m_allocatorId(0),
19  m_sequenceFilePath("<invalid_seq>"),
20  m_sequenceObj(),
21  m_computedCRC(0),
22  m_sequenceBlockState(),
23  m_savedOpCode(0),
24  m_savedCmdSeq(0),
25  m_goalState(),
26  m_sequencesStarted(0),
27  m_statementsDispatched(0),
28  m_runtime(),
29  m_debug(),
30  m_tlm() {}
31 
33 
37 void FpySequencer::RUN_cmdHandler(FwOpcodeType opCode,
38  U32 cmdSeq,
39  const Fw::CmdStringArg& fileName,
41 ) {
42  // can only run a seq while in idle
44  this->log_WARNING_HI_InvalidCommand(static_cast<I32>(sequencer_getState()));
46  return;
47  }
48 
49  if (block == FpySequencer_BlockState::BLOCK) {
50  // save the opCode and cmdSeq so we can respond later
51  this->m_savedOpCode = opCode;
52  this->m_savedCmdSeq = cmdSeq;
53  }
54 
55  this->sequencer_sendSignal_cmd_RUN(FpySequencer_SequenceExecutionArgs(fileName, block));
56 
57  // only respond if the user doesn't want us to block further execution
58  if (block == FpySequencer_BlockState::NO_BLOCK) {
59  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
60  }
61 }
62 
66 void FpySequencer::VALIDATE_cmdHandler(FwOpcodeType opCode,
67  U32 cmdSeq,
68  const Fw::CmdStringArg& fileName
69 ) {
70  // can only validate a seq while in idle
72  this->log_WARNING_HI_InvalidCommand(static_cast<I32>(sequencer_getState()));
74  return;
75  }
76 
77  // validate always blocks until finished, so save opcode/cmdseq
78  // so we can respond once done
79  this->m_savedOpCode = opCode;
80  this->m_savedCmdSeq = cmdSeq;
81 
83  FpySequencer_SequenceExecutionArgs(fileName, FpySequencer_BlockState::BLOCK));
84 }
85 
89 void FpySequencer::RUN_VALIDATED_cmdHandler(
90  FwOpcodeType opCode,
91  U32 cmdSeq,
92  FpySequencer_BlockState block
93 ) {
94  // can only RUN_VALIDATED if we have validated and are awaiting this exact cmd
96  this->log_WARNING_HI_InvalidCommand(static_cast<I32>(sequencer_getState()));
98  return;
99  }
100 
101  if (block == FpySequencer_BlockState::BLOCK) {
102  // save the opCode and cmdSeq so we can respond later
103  this->m_savedOpCode = opCode;
104  this->m_savedCmdSeq = cmdSeq;
105  }
106 
107  this->sequencer_sendSignal_cmd_RUN_VALIDATED(FpySequencer_SequenceExecutionArgs(this->m_sequenceFilePath, block));
108 
109  // only respond if the user doesn't want us to block further execution
110  if (block == FpySequencer_BlockState::NO_BLOCK) {
111  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
112  }
113 }
114 
118 void FpySequencer::CANCEL_cmdHandler(FwOpcodeType opCode,
119  U32 cmdSeq
120 ) {
121  // only state you can't cancel in is IDLE
122  if (sequencer_getState() == State::IDLE) {
123  this->log_WARNING_HI_InvalidCommand(static_cast<I32>(sequencer_getState()));
124  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
125  return;
126  }
127 
129 
130  // cancel returns immediately and always succeeds
131  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
132 }
133 
140 void FpySequencer::DEBUG_SET_BREAKPOINT_cmdHandler(
141  FwOpcodeType opCode,
142  U32 cmdSeq,
143  U32 stmtIdx,
144  bool breakOnce
145 ) {
146  this->sequencer_sendSignal_cmd_DEBUG_SET_BREAKPOINT(FpySequencer_DebugBreakpointArgs(true, breakOnce, stmtIdx));
147 
148  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
149 }
150 
156 void FpySequencer::DEBUG_BREAK_cmdHandler(FwOpcodeType opCode,
157  U32 cmdSeq,
158  bool breakOnce
159 ) {
160  if (!this->isRunningState(this->sequencer_getState())) {
161  // can only break while running
162  this->log_WARNING_HI_InvalidCommand(static_cast<I32>(sequencer_getState()));
163  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
164  return;
165  }
167  FpySequencer_DebugBreakpointArgs(true, breakOnce, this->m_runtime.nextStatementIndex));
168 
169  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
170 }
171 
176 void FpySequencer::DEBUG_CONTINUE_cmdHandler(FwOpcodeType opCode,
177  U32 cmdSeq
178 ) {
180  this->log_WARNING_HI_InvalidCommand(static_cast<I32>(sequencer_getState()));
181  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
182  return;
183  }
184 
186 
187  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
188 }
189 
194 void FpySequencer::DEBUG_CLEAR_BREAKPOINT_cmdHandler(FwOpcodeType opCode,
195  U32 cmdSeq
196 ) {
199 
200  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
201 }
202 
205  U32 context
206 ) {
208 }
209 
211  U32 key
212 ) {
213  // send ping response
214  this->pingOut_out(0, key);
215 }
216 
219  FwOpcodeType opCode,
220  U32 cmdSeq,
221  const Fw::CmdResponse& response
222 ) {
223  // if we aren't in the RUNNING state:
224  if (!this->isRunningState(sequencer_getState())) {
225  // must be a coding error from an outside component (off nom), or due to CANCEL while running a command (nom).
226  // because we can't be sure that it wasn't a nominal sequence of events leading to this, don't fail the
227  // sequence, just report it
228  this->log_WARNING_LO_CmdResponseWhileNotRunningSequence(static_cast<I32>(this->sequencer_getState()), opCode,
229  response);
230  return;
231  }
232 
233  // okay, we're running a sequence. now let's use the cmdUid to check if the response was for a cmd
234  // from this sequence
235 
236  // the cmdSeq arg is confusingly not the cmdSeq in this case, according to the current implementation
237  // of the CmdDisp. instead, it is the context that we passed in when we originally sent the cmd out.
238  // this context is in turn the cmdUid that we calculated just before sending it. rename the variable for
239  // clarity's sake
240  U32 cmdUid = cmdSeq;
241 
242  // pull the sequence index (modulo 2^16) out of the cmdUid. see the comment in FpySequencer::dispatchCommand
243  // for info on the binary format of this cmdUid. as a reminder, this should be equal to the first 16 bits of
244  // the m_sequencesStarted variable
245  U16 sequenceIndex = static_cast<U16>((cmdUid & 0xFFFF0000) >> 16);
246  U16 currentSequenceIndex = static_cast<U16>(this->m_sequencesStarted & 0xFFFF);
247 
248  // if it was from a different sequence:
249  if (sequenceIndex != currentSequenceIndex) {
250  this->log_WARNING_LO_CmdResponseFromOldSequence(opCode, response, sequenceIndex, currentSequenceIndex);
251  return;
252  }
253 
254  // okay, it was from this sequence. now if anything's wrong from this point on we should fail the sequence
255 
256  // first, make sure we're actually awaiting a command
258  // okay, crap. something from this sequence responded, and we weren't awaiting anything. end it all
259  this->log_WARNING_HI_CmdResponseWhileNotAwaiting(opCode, response);
261  return;
262  }
263 
264  // okay, we were awaiting a command. were we awaiting this opcode?
265  if (opCode != this->m_runtime.currentStatementOpcode ||
266  this->m_runtime.currentStatementType != Fpy::StatementType::COMMAND) {
267  // we were not awaiting this opcode. coding error, likely on the part of the responding component or cmd
268  // dispatcher
269  this->log_WARNING_HI_WrongCmdResponseOpcode(opCode, response, this->m_runtime.currentStatementOpcode);
271  return;
272  }
273 
274  // okay, we were awaiting this opcode. but was it from this exact statement, or a different one with the same opcode
275  // in the same file?
276 
277  // pull the cmd index (modulo 2^16) out of cmdUid. this should be equal to the first 16 bits of the
278  // m_statementsDispatched variable - 1. the -1 is because the count gets incremented immediately
279  // after we dispatch the cmd.
280  U16 cmdIndex = static_cast<U16>(cmdUid & 0xFFFF);
281  // check for coding errors. at this point in the function, we have definitely dispatched a stmt
282  FW_ASSERT(this->m_statementsDispatched > 0);
283  U16 currentCmdIndex = static_cast<U16>((this->m_statementsDispatched - 1) & 0xFFFF);
284 
285  if (cmdIndex != currentCmdIndex) {
286  // we were not awaiting this exact statement, it was a different one with the same opcode. coding error
287  this->log_WARNING_HI_WrongCmdResponseIndex(opCode, response, cmdIndex, currentCmdIndex);
289  return;
290  }
291 
292  // okay, got the right cmd back. we have verified:
293  // 1) we are in the RUNNING state
294  // 2) the response is from this sequence
295  // 3) the response is from the correct opcode
296  // 4) the response is from the correct instance of that opcode in the sequence
297 
298  if (response == Fw::CmdResponse::OK) {
300  } else {
302  this->m_runtime.nextStatementIndex - 1, this->m_sequenceFilePath,
303  response);
305  }
306 }
307 
310  const Fw::StringBase& filename
311 ) {
312  // can only run a seq while in idle
313  if (sequencer_getState() != State::IDLE) {
314  this->log_WARNING_HI_InvalidSeqRunCall(static_cast<I32>(sequencer_getState()));
315  return;
316  }
317 
318  // seqRunIn is never blocking
320 }
321 
324  U32 context
325 ) {
326  this->tlmWrite_State(static_cast<I32>(this->sequencer_getState()));
327  this->tlmWrite_StatementsDispatched(this->m_statementsDispatched);
328  this->tlmWrite_StatementsFailed(this->m_tlm.statementsFailed);
329  this->tlmWrite_SequencesCancelled(this->m_tlm.sequencesCancelled);
330  this->tlmWrite_SequencesSucceeded(this->m_tlm.sequencesSucceeded);
331  this->tlmWrite_SequencesFailed(this->m_tlm.sequencesFailed);
332  this->tlmWrite_SeqPath(this->m_sequenceFilePath);
333  this->tlmWrite_DebugBreakpointIdx(this->m_debug.breakpointIndex);
334  this->tlmWrite_Debug(this->getDebugTelemetry());
335 }
336 
337 FpySequencer_DebugTelemetry FpySequencer::getDebugTelemetry() {
338  // only send debug tlm when we are paused in debug break
340  if (this->m_runtime.nextStatementIndex >= this->m_sequenceObj.getheader().getstatementCount()) {
341  // reached end of file, turn on EOF flag and otherwise send some default tlm
343  } else {
344  const Fpy::Statement& nextStmt = this->m_sequenceObj.getstatements()[this->m_runtime.nextStatementIndex];
345  return FpySequencer_DebugTelemetry(false, nextStmt.getopCode(), nextStmt.gettype());
346  }
347  }
348  // send some default tlm when we aren't in debug break
349  return FpySequencer_DebugTelemetry(false, 0, Fpy::StatementType::COMMAND);
350 }
351 
353  Fw::ParamValid valid;
354  this->paramGet_STATEMENT_TIMEOUT_SECS(valid);
355  // check for coding errors--should have a default
357 }
358 
360  Fw::ParamValid valid;
361  switch (id) {
364  break;
365  }
366  default: {
367  FW_ASSERT(0, static_cast<FwAssertArgType>(id)); // coding error, forgot to include in switch statement
368  }
369  }
370 }
371 
372 bool FpySequencer::isRunningState(State state) {
373  // TODO ask Rob if there's a better way to check if we're in a superstate. I don't want to have
374  // to update this every time I add a new substate to the RUNNING state.
375 
380 }
381 
382 } // namespace Svc
FwIdType FwOpcodeType
The type of a command opcode.
void pingIn_handler(FwIndexType portNum, U32 key) override
Handler for input port pingIn.
void sequencer_sendSignal_cmd_DEBUG_SET_BREAKPOINT(const Svc::FpySequencer_DebugBreakpointArgs &value)
Send signal cmd_DEBUG_SET_BREAKPOINT to state machine sequencer.
FwIdType FwPrmIdType
The type of a parameter identifier.
void tlmWrite_State(I32 arg, Fw::Time _tlmTime=Fw::Time())
void log_WARNING_HI_StatementFailed(Svc::Fpy::StatementType stmtType, FwOpcodeType stmtOpcode, U32 stmtIdx, const Fw::StringBase &filePath, Fw::CmdResponse response) const
Log event StatementFailed.
void sequencer_sendSignal_stmtResponse_success()
Send signal stmtResponse_success to state machine sequencer.
void log_WARNING_LO_CmdResponseWhileNotRunningSequence(I32 state, FwOpcodeType opcode, Fw::CmdResponse response) const
Log event CmdResponseWhileNotRunningSequence.
void sequencer_sendSignal_cmd_VALIDATE(const Svc::FpySequencer_SequenceExecutionArgs &value)
Send signal cmd_VALIDATE to state machine sequencer.
Enum representing a command response.
void tlmWrite_SequencesSucceeded(U64 arg, Fw::Time _tlmTime=Fw::Time())
void sequencer_sendSignal_stmtResponse_unexpected()
Send signal stmtResponse_unexpected to state machine sequencer.
void seqRunIn_handler(FwIndexType portNum, const Fw::StringBase &filename) override
Handler for input port seqRunIn.
Svc_FpySequencer_SequencerStateMachine::State sequencer_getState() const
Get the state of state machine instance sequencer.
void log_WARNING_HI_WrongCmdResponseOpcode(FwOpcodeType opcode, Fw::CmdResponse response, FwOpcodeType expectedOpcode) const
Log event WrongCmdResponseOpcode.
void tlmWrite_StatementsFailed(U64 arg, Fw::Time _tlmTime=Fw::Time())
void parametersLoaded() override
Called whenever parameters are loaded.
F32 paramGet_STATEMENT_TIMEOUT_SECS(Fw::ParamValid &valid)
void cmdResponse_out(FwOpcodeType opCode, U32 cmdSeq, Fw::CmdResponse response)
Emit command response.
void tlmWrite_handler(FwIndexType portNum, U32 context) override
Handler for input port tlmWrite.
void tlmWrite_DebugBreakpointIdx(U32 arg, Fw::Time _tlmTime=Fw::Time())
void sequencer_sendSignal_cmd_DEBUG_CLEAR_BREAKPOINT()
Send signal cmd_DEBUG_CLEAR_BREAKPOINT to state machine sequencer.
void sequencer_sendSignal_cmd_RUN_VALIDATED(const Svc::FpySequencer_SequenceExecutionArgs &value)
Send signal cmd_RUN_VALIDATED to state machine sequencer.
void log_WARNING_LO_CmdResponseFromOldSequence(FwOpcodeType opcode, Fw::CmdResponse response, U16 oldSequenceIdx, U16 currentSequenceIdx) const
Log event CmdResponseFromOldSequence.
void sequencer_sendSignal_checkTimersIn()
Send signal checkTimersIn to state machine sequencer.
void tlmWrite_StatementsDispatched(U64 arg, Fw::Time _tlmTime=Fw::Time())
void log_ACTIVITY_HI_DebugBreakpointCleared() const
Log event DebugBreakpointCleared.
sequencer is not taking any action, waiting for a time in the future to continue
void parameterUpdated(FwPrmIdType id) override
Called whenever a parameter is updated.
sequencer is ready to load, validate and run a sequence
void sequencer_sendSignal_stmtResponse_failure()
Send signal stmtResponse_failure to state machine sequencer.
void log_WARNING_HI_InvalidSeqRunCall(I32 state) const
Log event InvalidSeqRunCall.
Command successfully executed.
Type_of_statements & getstatements()
Get member statements.
void tlmWrite_STATEMENT_TIMEOUT_SECS(F32 arg, Fw::Time _tlmTime=Fw::Time())
Command had execution error.
void sequencer_sendSignal_cmd_CANCEL()
Send signal cmd_CANCEL to state machine sequencer.
void log_WARNING_HI_InvalidCommand(I32 state) const
Log event InvalidCommand.
void cmdResponseIn_handler(FwIndexType portNum, FwOpcodeType opCode, U32 cmdSeq, const Fw::CmdResponse &response) override
Handler for input port cmdResponseIn.
void pingOut_out(FwIndexType portNum, U32 key)
Invoke output port pingOut.
void tlmWrite_SequencesFailed(U64 arg, Fw::Time _tlmTime=Fw::Time())
void checkTimers_handler(FwIndexType portNum, U32 context) override
Handler for input port checkTimers.
void sequencer_sendSignal_cmd_DEBUG_BREAK(const Svc::FpySequencer_DebugBreakpointArgs &value)
Send signal cmd_DEBUG_BREAK to state machine sequencer.
void tlmWrite_SeqPath(const Fw::StringBase &arg, Fw::Time _tlmTime=Fw::Time())
PlatformIndexType FwIndexType
sequencer is stepping into a single statement and dispatching it
void tlmWrite_Debug(const Svc::FpySequencer_DebugTelemetry &arg, Fw::Time _tlmTime=Fw::Time())
RateGroupDivider component implementation.
void log_WARNING_HI_CmdResponseWhileNotAwaiting(FwOpcodeType opcode, Fw::CmdResponse response) const
Log event CmdResponseWhileNotAwaiting.
Enum representing parameter validity.
Auto-generated base for FpySequencer component.
void sequencer_sendSignal_cmd_RUN(const Svc::FpySequencer_SequenceExecutionArgs &value)
Send signal cmd_RUN to state machine sequencer.
sequencer has validated the sequence and is waiting for a command to run it
void log_WARNING_HI_WrongCmdResponseIndex(FwOpcodeType opcode, Fw::CmdResponse response, U16 actualCmdIdx, U16 expectedCmdIdx) const
Log event WrongCmdResponseIndex.
void sequencer_sendSignal_cmd_DEBUG_CONTINUE()
Send signal cmd_DEBUG_CONTINUE to state machine sequencer.
void tlmWrite_SequencesCancelled(U64 arg, Fw::Time _tlmTime=Fw::Time())
#define FW_ASSERT(...)
Definition: Assert.hpp:14
FpySequencer(const char *const compName)