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) {
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) {
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()));
125  return;
126  }
127 
129 
130  // cancel returns immediately and always succeeds
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 
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()));
164  return;
165  }
167  FpySequencer_DebugBreakpointArgs(true, breakOnce, this->m_runtime.nextStatementIndex));
168 
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()));
182  return;
183  }
184 
186 
188 }
189 
194 void FpySequencer::DEBUG_CLEAR_BREAKPOINT_cmdHandler(FwOpcodeType opCode,
195  U32 cmdSeq
196 ) {
199 
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 statement response
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  if (this->m_runtime.currentStatementOpcode != Fpy::DirectiveId::CMD) {
265  // we were not awaiting a cmd response, we were waiting for a directive
266  this->log_WARNING_HI_CmdResponseWhileAwaitingDirective(opCode, response, this->m_runtime.currentStatementOpcode);
268  return;
269  }
270 
271  // okay, we were awaiting a cmd response. were we awaiting this opcode?
272  if (opCode != this->m_runtime.currentCmdOpcode) {
273  // we were not awaiting this opcode. coding error, likely on the part of the responding component or cmd
274  // dispatcher
275  this->log_WARNING_HI_WrongCmdResponseOpcode(opCode, response, this->m_runtime.currentCmdOpcode);
277  return;
278  }
279 
280  // okay, we were awaiting this opcode. but was it from this exact statement, or a different one with the same opcode
281  // in the same file?
282 
283  // pull the cmd index (modulo 2^16) out of cmdUid. this should be equal to the first 16 bits of the
284  // m_statementsDispatched variable
285  U16 cmdIndex = static_cast<U16>(cmdUid & 0xFFFF);
286  // check for coding errors. at this point in the function, we have definitely dispatched a stmt
287  FW_ASSERT(this->m_statementsDispatched > 0);
288  U16 currentCmdIndex = static_cast<U16>((this->m_statementsDispatched) & 0xFFFF);
289 
290  if (cmdIndex != currentCmdIndex) {
291  // we were not awaiting this exact statement, it was a different one with the same opcode. coding error
292  this->log_WARNING_HI_WrongCmdResponseIndex(opCode, response, cmdIndex, currentCmdIndex);
294  return;
295  }
296 
297  // okay, got the right cmd back. we have verified:
298  // 1) we are in the RUNNING state
299  // 2) the response is from this sequence
300  // 3) the response is from the correct opcode
301  // 4) the response is from the correct instance of that opcode in the sequence
302 
303  if (response == Fw::CmdResponse::OK) {
305  } else {
306  this->log_WARNING_HI_CommandFailed(opCode,
307  this->m_runtime.nextStatementIndex - 1, this->m_sequenceFilePath,
308  response);
310  }
311 }
312 
315  const Fw::StringBase& filename
316 ) {
317  // can only run a seq while in idle
318  if (sequencer_getState() != State::IDLE) {
319  this->log_WARNING_HI_InvalidSeqRunCall(static_cast<I32>(sequencer_getState()));
320  return;
321  }
322 
323  // seqRunIn is never blocking
325 }
326 
329  U32 context
330 ) {
331  this->tlmWrite_State(static_cast<I32>(this->sequencer_getState()));
332  this->tlmWrite_StatementsDispatched(this->m_statementsDispatched);
333  this->tlmWrite_StatementsFailed(this->m_tlm.statementsFailed);
334  this->tlmWrite_SequencesCancelled(this->m_tlm.sequencesCancelled);
335  this->tlmWrite_SequencesSucceeded(this->m_tlm.sequencesSucceeded);
336  this->tlmWrite_SequencesFailed(this->m_tlm.sequencesFailed);
337  this->tlmWrite_LastDirectiveError(this->m_tlm.lastDirectiveError);
338  this->tlmWrite_SeqPath(this->m_sequenceFilePath);
339  this->tlmWrite_DebugBreakpointIdx(this->m_debug.breakpointIndex);
340  this->tlmWrite_Debug(this->getDebugTelemetry());
341 }
342 
343 FpySequencer_DebugTelemetry FpySequencer::getDebugTelemetry() {
344  // only send debug tlm when we are paused in debug break
346  if (this->m_runtime.nextStatementIndex >= this->m_sequenceObj.getheader().getstatementCount()) {
347  // reached end of file, turn on EOF flag and otherwise send some default tlm
348  return FpySequencer_DebugTelemetry(true, false, 0, 0);
349  }
350 
351  const Fpy::Statement& nextStmt = this->m_sequenceObj.getstatements()[this->m_runtime.nextStatementIndex];
352  DirectiveUnion directiveUnion;
353  Fw::Success status = this->deserializeDirective(nextStmt, directiveUnion);
354  if (status != Fw::Success::SUCCESS) {
355  return FpySequencer_DebugTelemetry(false, false, nextStmt.getopCode(), 0);
356  }
357  if (nextStmt.getopCode() == Fpy::DirectiveId::CMD) {
358  // send opcode of the cmd to the ground
359  return FpySequencer_DebugTelemetry(false, true, nextStmt.getopCode(), directiveUnion.cmd.getopCode());
360  }
361 
362  return FpySequencer_DebugTelemetry(false, true, nextStmt.getopCode(), 0);
363  }
364  // send some default tlm when we aren't in debug break
365  return FpySequencer_DebugTelemetry(false, false, 0, 0);
366 }
367 
369  Fw::ParamValid valid;
370  this->paramGet_STATEMENT_TIMEOUT_SECS(valid);
371  // check for coding errors--should have a default
373 }
374 
376  Fw::ParamValid valid;
377  switch (id) {
380  break;
381  }
382  default: {
383  FW_ASSERT(0, static_cast<FwAssertArgType>(id)); // coding error, forgot to include in switch statement
384  }
385  }
386 }
387 
388 bool FpySequencer::isRunningState(State state) {
389  // TODO ask Rob if there's a better way to check if we're in a superstate. I don't want to have
390  // to update this every time I add a new substate to the RUNNING state.
391 
396 }
397 
398 } // namespace Svc
FwIdType FwOpcodeType
The type of a command opcode.
Representing success.
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 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 tlmWrite_PRM_STATEMENT_TIMEOUT_SECS(F32 arg, Fw::Time _tlmTime=Fw::Time())
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_CommandFailed(FwOpcodeType opCode, U32 stmtIdx, const Fw::StringBase &filePath, Fw::CmdResponse response) const
Log event CommandFailed.
void tlmWrite_LastDirectiveError(const Svc::FpySequencer_DirectiveErrorCode &arg, Fw::Time _tlmTime=Fw::Time())
void log_WARNING_HI_InvalidSeqRunCall(I32 state) const
Log event InvalidSeqRunCall.
Command successfully executed.
Type_of_statements & getstatements()
Get member statements.
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.
void log_WARNING_HI_CmdResponseWhileAwaitingDirective(FwOpcodeType opcode, Fw::CmdResponse response, U8 expectedDirectiveOpcode) const
Log event CmdResponseWhileAwaitingDirective.
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
Success/Failure.
FpySequencer(const char *const compName)