F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
CmdSequencerImpl.cpp
Go to the documentation of this file.
1 // ======================================================================
2 // \title CmdSequencerImpl.cpp
3 // \author Bocchino/Canham
4 // \brief cpp file for CmdDispatcherComponentBase component implementation class
5 //
6 // Copyright (C) 2009-2018 California Institute of Technology.
7 // ALL RIGHTS RESERVED. United States Government Sponsorship
8 // acknowledged.
9 
10 #include <Fw/Com/ComPacket.hpp>
11 #include <Fw/Types/Assert.hpp>
15 #include <Utils/Hash/Hash.hpp>
16 
17 namespace Svc {
18 
19 // ----------------------------------------------------------------------
20 // Construction, initialization, and destruction
21 // ----------------------------------------------------------------------
22 
25  m_FPrimeSequence(*this),
26  m_sequence(&this->m_FPrimeSequence),
27  m_loadCmdCount(0),
28  m_cancelCmdCount(0),
29  m_errorCount(0),
30  m_runMode(STOPPED),
31  m_stepMode(AUTO),
32  m_executedCount(0),
33  m_totalExecutedCount(0),
34  m_sequencesCompletedCount(0),
35  m_timeout(0),
36  m_blockState(Svc::BlockState::NO_BLOCK),
37  m_opCode(0),
38  m_cmdSeq(0),
39  m_join_waiting(false) {}
40 
41 void CmdSequencerComponentImpl::setTimeout(const U32 timeout) {
42  this->m_timeout = timeout;
43 }
44 
46  this->m_sequence = &sequence;
47 }
48 
50  Fw::MemAllocator& allocator,
51  const FwSizeType bytes) {
52  this->m_sequence->allocateBuffer(identifier, allocator, bytes);
53 }
54 
56  FW_ASSERT(this->m_runMode == STOPPED, this->m_runMode);
57  if (not this->loadFile(fileName)) {
58  this->m_sequence->clear();
59  }
60 }
61 
63  this->m_sequence->deallocateBuffer(allocator);
64 }
65 
67 
68 // ----------------------------------------------------------------------
69 // Handler implementations
70 // ----------------------------------------------------------------------
71 
72 void CmdSequencerComponentImpl::CS_RUN_cmdHandler(FwOpcodeType opCode,
73  U32 cmdSeq,
74  const Fw::CmdStringArg& fileName,
75  Svc::BlockState block) {
76  if (not this->requireRunMode(STOPPED)) {
77  if (m_join_waiting) {
78  // Inform user previous seq file is not complete
80  }
82  return;
83  }
84 
85  this->m_blockState = block.e;
86  this->m_cmdSeq = cmdSeq;
87  this->m_opCode = opCode;
88 
89  // load commands
90  if (not this->loadFile(fileName)) {
92  return;
93  }
94 
95  this->m_executedCount = 0;
96 
97  // Check the step mode. If it is auto, start the sequence
98  if (AUTO == this->m_stepMode) {
99  this->m_runMode = RUNNING;
100  if (this->isConnected_seqStartOut_OutputPort(0)) {
101  // Create empty SeqArgs as placeholder
102  // Use parameterized constructor to ensure m_size is initialized to 0
103  Svc::SeqArgs emptyArgs{0, 0};
104  this->seqStartOut_out(0, this->m_sequence->getStringFileName(), emptyArgs);
105  }
106  this->performCmd_Step();
107  }
108 
109  if (Svc::BlockState::NO_BLOCK == this->m_blockState) {
110  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
111  }
112 }
113 
114 void CmdSequencerComponentImpl::CS_VALIDATE_cmdHandler(FwOpcodeType opCode,
115  U32 cmdSeq,
116  const Fw::CmdStringArg& fileName) {
117  if (!this->requireRunMode(STOPPED)) {
118  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
119  return;
120  }
121 
122  // load commands
123  if (not this->loadFile(fileName)) {
124  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
125  return;
126  }
127 
128  // clear the buffer
129  this->m_sequence->clear();
130 
131  this->log_ACTIVITY_HI_CS_SequenceValid(this->m_sequence->getLogFileName());
132 
133  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
134 }
135 
137 void CmdSequencerComponentImpl::doSequenceRun(const Fw::StringBase& filename) {
138  if (!this->requireRunMode(STOPPED)) {
140  return;
141  }
142 
143  // If file name is non-empty, load a file.
144  // Empty file name means don't load.
145  if (filename != "") {
146  Fw::CmdStringArg cmdStr(filename);
147  const bool status = this->loadFile(cmdStr);
148  if (!status) {
150  return;
151  }
152  } else if (not this->m_sequence->hasMoreRecords()) {
153  // No sequence loaded
155  this->error();
157  return;
158  }
159 
160  this->m_executedCount = 0;
161 
162  // Check the step mode. If it is auto, start the sequence
163  if (AUTO == this->m_stepMode) {
164  this->m_runMode = RUNNING;
165  if (this->isConnected_seqStartOut_OutputPort(0)) {
166  // Create empty SeqArgs as placeholder
167  // Use parameterized constructor to ensure m_size is initialized to 0
168  Svc::SeqArgs emptyArgs{0, 0};
169  this->seqStartOut_out(0, this->m_sequence->getStringFileName(), emptyArgs);
170  }
171  this->performCmd_Step();
172  }
173 
174  this->log_ACTIVITY_HI_CS_PortSequenceStarted(this->m_sequence->getLogFileName());
175 }
176 
177 void CmdSequencerComponentImpl::seqRunIn_handler(FwIndexType portNum,
178  const Fw::StringBase& filename,
179  const Svc::SeqArgs& args) {
180  (void)args; // Suppress unused parameter warning
181  this->doSequenceRun(filename);
182 }
183 
184 void CmdSequencerComponentImpl::seqDispatchIn_handler(FwIndexType portNum, Fw::StringBase& file_name) {
185  this->doSequenceRun(file_name);
186 }
187 
188 void CmdSequencerComponentImpl ::seqCancelIn_handler(const FwIndexType portNum) {
189  if (RUNNING == this->m_runMode) {
190  this->performCmd_Cancel();
191  this->log_ACTIVITY_HI_CS_SequenceCanceled(this->m_sequence->getLogFileName());
192  ++this->m_cancelCmdCount;
193  this->tlmWrite_CS_CancelCommands(this->m_cancelCmdCount);
194  } else {
196  }
197 }
198 
199 void CmdSequencerComponentImpl::CS_CANCEL_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
200  if (RUNNING == this->m_runMode) {
201  this->performCmd_Cancel();
202  this->log_ACTIVITY_HI_CS_SequenceCanceled(this->m_sequence->getLogFileName());
203  ++this->m_cancelCmdCount;
204  this->tlmWrite_CS_CancelCommands(this->m_cancelCmdCount);
205  } else {
207  }
208  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
209 }
210 
211 void CmdSequencerComponentImpl::CS_JOIN_WAIT_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq) {
212  // If there is no running sequence do not wait
213  if (m_runMode != RUNNING) {
215  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
216  return;
217  } else {
218  m_join_waiting = true;
219  Fw::LogStringArg& logFileName = this->m_sequence->getLogFileName();
220  this->log_ACTIVITY_HI_CS_JoinWaiting(logFileName, m_cmdSeq, m_opCode);
221  m_cmdSeq = cmdSeq;
222  m_opCode = opCode;
223  }
224 }
225 
226 // ----------------------------------------------------------------------
227 // Private helper methods
228 // ----------------------------------------------------------------------
229 
230 bool CmdSequencerComponentImpl ::loadFile(const Fw::ConstStringBase& fileName) {
231  const bool status = this->m_sequence->loadFile(fileName);
232  if (status) {
233  Fw::LogStringArg& logFileName = this->m_sequence->getLogFileName();
234  this->log_ACTIVITY_LO_CS_SequenceLoaded(logFileName);
235  ++this->m_loadCmdCount;
236  this->tlmWrite_CS_LoadCommands(this->m_loadCmdCount);
237  } else {
238  // A partial load may have populated m_buffer before an intermediate
239  // validation step (e.g. CRC, time, record structure) failed. Without
240  // an explicit reset, FPrimeSequence::hasMoreRecords() still returns
241  // true because getDeserializeSizeLeft() > 0, so a subsequent CS_START
242  // bypasses the "no sequence active" guard and reaches
243  // FPrimeSequence::nextRecord, which asserts on the failed deserialize
244  // and aborts the FSW. Clearing the sequence on every load failure
245  // closes that window.
246  this->m_sequence->clear();
247  }
248  return status;
249 }
250 
251 void CmdSequencerComponentImpl::error() {
252  ++this->m_errorCount;
253  this->tlmWrite_CS_Errors(m_errorCount);
254 }
255 
256 void CmdSequencerComponentImpl::performCmd_Cancel() {
257  this->m_sequence->reset();
258  this->m_runMode = STOPPED;
259  this->m_cmdTimer.clear();
260  this->m_cmdTimeoutTimer.clear();
261  this->m_executedCount = 0;
262  // write sequence done port with error, if connected
263  if (this->isConnected_seqDone_OutputPort(0)) {
265  }
266 
267  if (Svc::BlockState::BLOCK == this->m_blockState || m_join_waiting) {
268  // Do not wait if sequence was canceled or a cmd failed
269  this->m_join_waiting = false;
270  this->cmdResponse_out(this->m_opCode, this->m_cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
271  }
272 
273  this->m_blockState = Svc::BlockState::NO_BLOCK;
274 }
275 
276 void CmdSequencerComponentImpl ::cmdResponseIn_handler(FwIndexType portNum,
277  FwOpcodeType opcode,
278  U32 cmdSeq,
279  const Fw::CmdResponse& response) {
280  if (this->m_runMode == STOPPED) {
281  // Sequencer is not running
283  } else {
284  // clear command timeout
285  this->m_cmdTimeoutTimer.clear();
286  if (response != Fw::CmdResponse::OK) {
287  this->commandError(this->m_executedCount, opcode, response.e);
288  this->performCmd_Cancel();
289  } else if (this->m_runMode == RUNNING && this->m_stepMode == AUTO) {
290  // Auto mode
291  this->commandComplete(opcode);
292  if (not this->m_sequence->hasMoreRecords()) {
293  // No data left
294  this->m_runMode = STOPPED;
295  this->sequenceComplete();
296  } else {
297  this->performCmd_Step();
298  }
299  } else {
300  // Manual step mode
301  this->commandComplete(opcode);
302  if (not this->m_sequence->hasMoreRecords()) {
303  this->m_runMode = STOPPED;
304  this->sequenceComplete();
305  }
306  }
307  }
308 }
309 
310 void CmdSequencerComponentImpl ::schedIn_handler(FwIndexType portNum, U32 order) {
311  Fw::Time currTime = this->getTime();
312  // check to see if a command time is pending
313  if (this->m_cmdTimer.isExpiredAt(currTime)) {
314  this->comCmdOut_out(0, m_record.m_command, 0);
315  this->m_cmdTimer.clear();
316  // start command timeout timer
317  this->setCmdTimeout(currTime);
318  } else if (this->m_cmdTimeoutTimer.isExpiredAt(this->getTime())) { // check for command timeout
319  this->log_WARNING_HI_CS_SequenceTimeout(m_sequence->getLogFileName(), this->m_executedCount);
320  // If there is a command timeout, cancel the sequence
321  this->performCmd_Cancel();
322  }
323 }
324 
325 void CmdSequencerComponentImpl ::CS_START_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
326  if (not this->m_sequence->hasMoreRecords()) {
327  // No sequence loaded
329  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
330  return;
331  }
332  if (!this->requireRunMode(STOPPED)) {
333  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
334  return;
335  }
336 
337  this->m_blockState = Svc::BlockState::NO_BLOCK;
338  this->m_runMode = RUNNING;
339  this->performCmd_Step();
340  this->log_ACTIVITY_HI_CS_CmdStarted(this->m_sequence->getLogFileName());
341  if (this->isConnected_seqStartOut_OutputPort(0)) {
342  // Create empty SeqArgs as placeholder
343  Svc::SeqArgs emptyArgs{0, 0};
344  this->seqStartOut_out(0, this->m_sequence->getStringFileName(), emptyArgs);
345  }
346  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
347 }
348 
349 void CmdSequencerComponentImpl ::CS_STEP_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
350  if (this->requireRunMode(RUNNING)) {
351  this->performCmd_Step();
352  // check for special case where end of sequence entry was encountered
353  if (this->m_runMode != STOPPED) {
354  this->log_ACTIVITY_HI_CS_CmdStepped(this->m_sequence->getLogFileName(), this->m_executedCount);
355  }
356  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
357  } else {
358  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
359  }
360 }
361 
362 void CmdSequencerComponentImpl ::CS_AUTO_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
363  if (this->requireRunMode(STOPPED)) {
364  this->m_stepMode = AUTO;
366  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
367  } else {
368  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
369  }
370 }
371 
372 void CmdSequencerComponentImpl ::CS_MANUAL_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
373  if (this->requireRunMode(STOPPED)) {
374  this->m_stepMode = MANUAL;
376  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
377  } else {
378  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
379  }
380 }
381 
382 // ----------------------------------------------------------------------
383 // Helper methods
384 // ----------------------------------------------------------------------
385 
386 bool CmdSequencerComponentImpl::requireRunMode(RunMode mode) {
387  if (this->m_runMode == mode) {
388  return true;
389  } else {
391  return false;
392  }
393 }
394 
395 void CmdSequencerComponentImpl ::commandError(const U32 number, const FwOpcodeType opCode, const U32 error) {
396  this->log_WARNING_HI_CS_CommandError(this->m_sequence->getLogFileName(), number, opCode, error);
397  this->error();
398 }
399 
400 void CmdSequencerComponentImpl::performCmd_Step() {
401  this->m_sequence->nextRecord(m_record);
402  // set clock time base and context from value set when sequence was loaded
403  const Sequence::Header& header = this->m_sequence->getHeader();
404  this->m_record.m_timeTag.setTimeBase(header.m_timeBase);
405  this->m_record.m_timeTag.setTimeContext(header.m_timeContext);
406 
407  Fw::Time currentTime = this->getTime();
408  switch (this->m_record.m_descriptor) {
410  this->m_runMode = STOPPED;
411  this->sequenceComplete();
412  break;
414  this->performCmd_Step_RELATIVE(currentTime);
415  break;
417  this->performCmd_Step_ABSOLUTE(currentTime);
418  break;
419  default:
420  FW_ASSERT(0, m_record.m_descriptor);
421  }
422 }
423 
424 void CmdSequencerComponentImpl::sequenceComplete() {
425  ++this->m_sequencesCompletedCount;
426  // reset buffer
427  this->m_sequence->clear();
428  this->log_ACTIVITY_HI_CS_SequenceComplete(this->m_sequence->getLogFileName());
429  this->tlmWrite_CS_SequencesCompleted(this->m_sequencesCompletedCount);
430  this->m_executedCount = 0;
431  // write sequence done port, if connected
432  if (this->isConnected_seqDone_OutputPort(0)) {
433  this->seqDone_out(0, 0, 0, Fw::CmdResponse::OK);
434  }
435 
436  if (Svc::BlockState::BLOCK == this->m_blockState || m_join_waiting) {
437  this->cmdResponse_out(this->m_opCode, this->m_cmdSeq, Fw::CmdResponse::OK);
438  }
439 
440  m_join_waiting = false;
441  this->m_blockState = Svc::BlockState::NO_BLOCK;
442 }
443 
444 void CmdSequencerComponentImpl::commandComplete(const FwOpcodeType opcode) {
445  this->log_ACTIVITY_LO_CS_CommandComplete(this->m_sequence->getLogFileName(), this->m_executedCount, opcode);
446  ++this->m_executedCount;
447  ++this->m_totalExecutedCount;
448  this->tlmWrite_CS_CommandsExecuted(this->m_totalExecutedCount);
449 }
450 
451 void CmdSequencerComponentImpl ::performCmd_Step_RELATIVE(Fw::Time& currentTime) {
452  this->m_record.m_timeTag.add(currentTime.getSeconds(), currentTime.getUSeconds());
453  this->performCmd_Step_ABSOLUTE(currentTime);
454 }
455 
456 void CmdSequencerComponentImpl ::performCmd_Step_ABSOLUTE(Fw::Time& currentTime) {
457  if (currentTime >= this->m_record.m_timeTag) {
458  this->comCmdOut_out(0, m_record.m_command, 0);
459  this->setCmdTimeout(currentTime);
460  } else {
461  this->m_cmdTimer.set(this->m_record.m_timeTag);
462  }
463 }
464 
465 void CmdSequencerComponentImpl ::pingIn_handler(FwIndexType portNum,
466  U32 key
467 ) {
468  // send ping response
469  this->pingOut_out(0, key);
470 }
471 
472 void CmdSequencerComponentImpl ::setCmdTimeout(const Fw::Time& currentTime) {
473  // start timeout timer if enabled and not in step mode
474  if ((this->m_timeout > 0) and (AUTO == this->m_stepMode)) {
475  Fw::Time expTime = currentTime;
476  expTime.add(this->m_timeout, 0);
477  this->m_cmdTimeoutTimer.set(expTime);
478  }
479 }
480 
481 } // namespace Svc
void loadSequence(const Fw::ConstStringBase &fileName)
void tlmWrite_CS_LoadCommands(U32 arg, Fw::Time _tlmTime=Fw::Time()) const
void setSequenceFormat(Sequence &sequence)
FwIdType FwOpcodeType
The type of a command opcode.
void log_ACTIVITY_HI_CS_ModeSwitched(Svc::CmdSequencer_SeqMode mode) const
PlatformSizeType FwSizeType
I32 FwEnumStoreType
CmdSequencerComponentImpl(const char *compName)
Construct a CmdSequencer.
void tlmWrite_CS_Errors(U32 arg, Fw::Time _tlmTime=Fw::Time()) const
void log_ACTIVITY_HI_CS_JoinWaiting(const Fw::StringBase &filename, U32 recordNumber, FwOpcodeType opCode) const
void log_WARNING_HI_CS_UnexpectedCompletion(FwOpcodeType opcode) const
void log_ACTIVITY_LO_CS_SequenceLoaded(const Fw::StringBase &fileName) const
enum T e
The raw enum value.
Fw::Time m_timeTag
The time tag. NOTE: timeBase and context not filled in.
void log_ACTIVITY_HI_CS_CmdStarted(const Fw::StringBase &filename) const
Enum representing a command response.
bool isConnected_seqDone_OutputPort(FwIndexType portNum) const
void log_WARNING_HI_CS_SequenceTimeout(const Fw::StringBase &filename, U32 command) const
bool isConnected_seqStartOut_OutputPort(FwIndexType portNum) const
void allocateBuffer(FwEnumStoreType identifier, Fw::MemAllocator &allocator, FwSizeType bytes)
Give the sequence representation a memory buffer.
Definition: Sequence.cpp:45
virtual bool hasMoreRecords() const =0
void tlmWrite_CS_CancelCommands(U32 arg, Fw::Time _tlmTime=Fw::Time()) const
void tlmWrite_CS_CommandsExecuted(U32 arg, Fw::Time _tlmTime=Fw::Time()) const
~CmdSequencerComponentImpl()
Destroy a CmdDispatcherComponentBase.
void log_ACTIVITY_HI_CS_PortSequenceStarted(const Fw::StringBase &filename) const
void seqStartOut_out(FwIndexType portNum, const Fw::StringBase &filename, const Svc::SeqArgs &args) const
Invoke output port seqStartOut.
void log_ACTIVITY_HI_CS_SequenceCanceled(const Fw::StringBase &fileName) const
void setTimeContext(FwTimeContextStoreType context)
Definition: Time.cpp:252
void allocateBuffer(const FwEnumStoreType identifier, Fw::MemAllocator &allocator, const FwSizeType bytes)
A sequence with unspecified binary format.
void deallocateBuffer(Fw::MemAllocator &allocator)
Deallocate the buffer.
Definition: Sequence.cpp:55
U32 getSeconds() const
Definition: Time.cpp:118
Sequencer blocking state.
void setTimeBase(TimeBase timeBase)
Definition: Time.cpp:248
void setTimeout(const U32 seconds)
Command successfully executed.
virtual bool loadFile(const Fw::ConstStringBase &fileName)=0
Command had execution error.
static Time add(const Time &a, const Time &b)
Definition: Time.cpp:164
void deallocateBuffer(Fw::MemAllocator &allocator)
Return allocated buffer. Call during shutdown.
Memory Allocation base class.
U32 getUSeconds() const
Definition: Time.cpp:122
A read-only abstract superclass for StringBase.
PlatformIndexType FwIndexType
Auto-generated base for CmdSequencer component.
void comCmdOut_out(FwIndexType portNum, Fw::ComBuffer &data, U32 context) const
Invoke output port comCmdOut.
RateGroupDivider component implementation.
virtual void nextRecord(Record &record)=0
void log_ACTIVITY_HI_CS_SequenceComplete(const Fw::StringBase &fileName) const
void seqDone_out(FwIndexType portNum, FwOpcodeType opCode, U32 cmdSeq, const Fw::CmdResponse &response) const
Invoke output port seqDone.
void log_ACTIVITY_LO_CS_CommandComplete(const Fw::StringBase &fileName, U32 recordNumber, FwOpcodeType opCode) const
void log_WARNING_HI_CS_CommandError(const Fw::StringBase &fileName, U32 recordNumber, FwOpcodeType opCode, U32 errorStatus) const
#define FW_ASSERT(...)
Definition: Assert.hpp:14
void pingOut_out(FwIndexType portNum, U32 key) const
Invoke output port pingOut.
const Header & getHeader() const
Get the sequence header.
Definition: Sequence.cpp:60
void log_ACTIVITY_HI_CS_SequenceValid(const Fw::StringBase &filename) const
enum T e
The raw enum value.
void cmdResponse_out(FwOpcodeType opCode, U32 cmdSeq, Fw::CmdResponse response)
Emit command response.
void tlmWrite_CS_SequencesCompleted(U32 arg, Fw::Time _tlmTime=Fw::Time()) const
void log_ACTIVITY_HI_CS_CmdStepped(const Fw::StringBase &filename, U32 command) const