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 extern "C" {
17 }
18 
19 namespace Svc {
20 
21 // ----------------------------------------------------------------------
22 // Construction, initialization, and destruction
23 // ----------------------------------------------------------------------
24 
27  m_FPrimeSequence(*this),
28  m_sequence(&this->m_FPrimeSequence),
29  m_loadCmdCount(0),
30  m_cancelCmdCount(0),
31  m_errorCount(0),
32  m_runMode(STOPPED),
33  m_stepMode(AUTO),
34  m_executedCount(0),
35  m_totalExecutedCount(0),
36  m_sequencesCompletedCount(0),
37  m_timeout(0),
38  m_blockState(Svc::CmdSequencer_BlockState::NO_BLOCK),
39  m_opCode(0),
40  m_cmdSeq(0),
41  m_join_waiting(false) {}
42 
43 void CmdSequencerComponentImpl::setTimeout(const U32 timeout) {
44  this->m_timeout = timeout;
45 }
46 
48  this->m_sequence = &sequence;
49 }
50 
52  Fw::MemAllocator& allocator,
53  const FwSizeType bytes) {
54  this->m_sequence->allocateBuffer(identifier, allocator, bytes);
55 }
56 
58  FW_ASSERT(this->m_runMode == STOPPED, this->m_runMode);
59  if (not this->loadFile(fileName)) {
60  this->m_sequence->clear();
61  }
62 }
63 
65  this->m_sequence->deallocateBuffer(allocator);
66 }
67 
69 
70 // ----------------------------------------------------------------------
71 // Handler implementations
72 // ----------------------------------------------------------------------
73 
74 void CmdSequencerComponentImpl::CS_RUN_cmdHandler(FwOpcodeType opCode,
75  U32 cmdSeq,
76  const Fw::CmdStringArg& fileName,
78  if (not this->requireRunMode(STOPPED)) {
79  if (m_join_waiting) {
80  // Inform user previous seq file is not complete
82  }
84  return;
85  }
86 
87  this->m_blockState = block.e;
88  this->m_cmdSeq = cmdSeq;
89  this->m_opCode = opCode;
90 
91  // load commands
92  if (not this->loadFile(fileName)) {
94  return;
95  }
96 
97  this->m_executedCount = 0;
98 
99  // Check the step mode. If it is auto, start the sequence
100  if (AUTO == this->m_stepMode) {
101  this->m_runMode = RUNNING;
102  if (this->isConnected_seqStartOut_OutputPort(0)) {
103  // Create empty SeqArgs as placeholder
104  // Use parameterized constructor to ensure m_size is initialized to 0
105  Svc::SeqArgs emptyArgs{0, 0};
106  this->seqStartOut_out(0, this->m_sequence->getStringFileName(), emptyArgs);
107  }
108  this->performCmd_Step();
109  }
110 
111  if (Svc::CmdSequencer_BlockState::NO_BLOCK == this->m_blockState) {
112  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
113  }
114 }
115 
116 void CmdSequencerComponentImpl::CS_VALIDATE_cmdHandler(FwOpcodeType opCode,
117  U32 cmdSeq,
118  const Fw::CmdStringArg& fileName) {
119  if (!this->requireRunMode(STOPPED)) {
120  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
121  return;
122  }
123 
124  // load commands
125  if (not this->loadFile(fileName)) {
126  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
127  return;
128  }
129 
130  // clear the buffer
131  this->m_sequence->clear();
132 
133  this->log_ACTIVITY_HI_CS_SequenceValid(this->m_sequence->getLogFileName());
134 
135  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
136 }
137 
139 void CmdSequencerComponentImpl::doSequenceRun(const Fw::StringBase& filename) {
140  if (!this->requireRunMode(STOPPED)) {
142  return;
143  }
144 
145  // If file name is non-empty, load a file.
146  // Empty file name means don't load.
147  if (filename != "") {
148  Fw::CmdStringArg cmdStr(filename);
149  const bool status = this->loadFile(cmdStr);
150  if (!status) {
152  return;
153  }
154  } else if (not this->m_sequence->hasMoreRecords()) {
155  // No sequence loaded
157  this->error();
159  return;
160  }
161 
162  this->m_executedCount = 0;
163 
164  // Check the step mode. If it is auto, start the sequence
165  if (AUTO == this->m_stepMode) {
166  this->m_runMode = RUNNING;
167  if (this->isConnected_seqStartOut_OutputPort(0)) {
168  // Create empty SeqArgs as placeholder
169  // Use parameterized constructor to ensure m_size is initialized to 0
170  Svc::SeqArgs emptyArgs{0, 0};
171  this->seqStartOut_out(0, this->m_sequence->getStringFileName(), emptyArgs);
172  }
173  this->performCmd_Step();
174  }
175 
176  this->log_ACTIVITY_HI_CS_PortSequenceStarted(this->m_sequence->getLogFileName());
177 }
178 
179 void CmdSequencerComponentImpl::seqRunIn_handler(FwIndexType portNum,
180  const Fw::StringBase& filename,
181  const Svc::SeqArgs& args) {
182  (void)args; // Suppress unused parameter warning
183  this->doSequenceRun(filename);
184 }
185 
186 void CmdSequencerComponentImpl::seqDispatchIn_handler(FwIndexType portNum, Fw::StringBase& file_name) {
187  this->doSequenceRun(file_name);
188 }
189 
190 void CmdSequencerComponentImpl ::seqCancelIn_handler(const FwIndexType portNum) {
191  if (RUNNING == this->m_runMode) {
192  this->performCmd_Cancel();
193  this->log_ACTIVITY_HI_CS_SequenceCanceled(this->m_sequence->getLogFileName());
194  ++this->m_cancelCmdCount;
195  this->tlmWrite_CS_CancelCommands(this->m_cancelCmdCount);
196  } else {
198  }
199 }
200 
201 void CmdSequencerComponentImpl::CS_CANCEL_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
202  if (RUNNING == this->m_runMode) {
203  this->performCmd_Cancel();
204  this->log_ACTIVITY_HI_CS_SequenceCanceled(this->m_sequence->getLogFileName());
205  ++this->m_cancelCmdCount;
206  this->tlmWrite_CS_CancelCommands(this->m_cancelCmdCount);
207  } else {
209  }
210  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
211 }
212 
213 void CmdSequencerComponentImpl::CS_JOIN_WAIT_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq) {
214  // If there is no running sequence do not wait
215  if (m_runMode != RUNNING) {
217  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
218  return;
219  } else {
220  m_join_waiting = true;
221  Fw::LogStringArg& logFileName = this->m_sequence->getLogFileName();
222  this->log_ACTIVITY_HI_CS_JoinWaiting(logFileName, m_cmdSeq, m_opCode);
223  m_cmdSeq = cmdSeq;
224  m_opCode = opCode;
225  }
226 }
227 
228 // ----------------------------------------------------------------------
229 // Private helper methods
230 // ----------------------------------------------------------------------
231 
232 bool CmdSequencerComponentImpl ::loadFile(const Fw::ConstStringBase& fileName) {
233  const bool status = this->m_sequence->loadFile(fileName);
234  if (status) {
235  Fw::LogStringArg& logFileName = this->m_sequence->getLogFileName();
236  this->log_ACTIVITY_LO_CS_SequenceLoaded(logFileName);
237  ++this->m_loadCmdCount;
238  this->tlmWrite_CS_LoadCommands(this->m_loadCmdCount);
239  }
240  return status;
241 }
242 
243 void CmdSequencerComponentImpl::error() {
244  ++this->m_errorCount;
245  this->tlmWrite_CS_Errors(m_errorCount);
246 }
247 
248 void CmdSequencerComponentImpl::performCmd_Cancel() {
249  this->m_sequence->reset();
250  this->m_runMode = STOPPED;
251  this->m_cmdTimer.clear();
252  this->m_cmdTimeoutTimer.clear();
253  this->m_executedCount = 0;
254  // write sequence done port with error, if connected
255  if (this->isConnected_seqDone_OutputPort(0)) {
257  }
258 
259  if (Svc::CmdSequencer_BlockState::BLOCK == this->m_blockState || m_join_waiting) {
260  // Do not wait if sequence was canceled or a cmd failed
261  this->m_join_waiting = false;
262  this->cmdResponse_out(this->m_opCode, this->m_cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
263  }
264 
265  this->m_blockState = Svc::CmdSequencer_BlockState::NO_BLOCK;
266 }
267 
268 void CmdSequencerComponentImpl ::cmdResponseIn_handler(FwIndexType portNum,
269  FwOpcodeType opcode,
270  U32 cmdSeq,
271  const Fw::CmdResponse& response) {
272  if (this->m_runMode == STOPPED) {
273  // Sequencer is not running
275  } else {
276  // clear command timeout
277  this->m_cmdTimeoutTimer.clear();
278  if (response != Fw::CmdResponse::OK) {
279  this->commandError(this->m_executedCount, opcode, response.e);
280  this->performCmd_Cancel();
281  } else if (this->m_runMode == RUNNING && this->m_stepMode == AUTO) {
282  // Auto mode
283  this->commandComplete(opcode);
284  if (not this->m_sequence->hasMoreRecords()) {
285  // No data left
286  this->m_runMode = STOPPED;
287  this->sequenceComplete();
288  } else {
289  this->performCmd_Step();
290  }
291  } else {
292  // Manual step mode
293  this->commandComplete(opcode);
294  if (not this->m_sequence->hasMoreRecords()) {
295  this->m_runMode = STOPPED;
296  this->sequenceComplete();
297  }
298  }
299  }
300 }
301 
302 void CmdSequencerComponentImpl ::schedIn_handler(FwIndexType portNum, U32 order) {
303  Fw::Time currTime = this->getTime();
304  // check to see if a command time is pending
305  if (this->m_cmdTimer.isExpiredAt(currTime)) {
306  this->comCmdOut_out(0, m_record.m_command, 0);
307  this->m_cmdTimer.clear();
308  // start command timeout timer
309  this->setCmdTimeout(currTime);
310  } else if (this->m_cmdTimeoutTimer.isExpiredAt(this->getTime())) { // check for command timeout
311  this->log_WARNING_HI_CS_SequenceTimeout(m_sequence->getLogFileName(), this->m_executedCount);
312  // If there is a command timeout, cancel the sequence
313  this->performCmd_Cancel();
314  }
315 }
316 
317 void CmdSequencerComponentImpl ::CS_START_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
318  if (not this->m_sequence->hasMoreRecords()) {
319  // No sequence loaded
321  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
322  return;
323  }
324  if (!this->requireRunMode(STOPPED)) {
325  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
326  return;
327  }
328 
329  this->m_blockState = Svc::CmdSequencer_BlockState::NO_BLOCK;
330  this->m_runMode = RUNNING;
331  this->performCmd_Step();
332  this->log_ACTIVITY_HI_CS_CmdStarted(this->m_sequence->getLogFileName());
333  if (this->isConnected_seqStartOut_OutputPort(0)) {
334  // Create empty SeqArgs as placeholder
335  Svc::SeqArgs emptyArgs{0, 0};
336  this->seqStartOut_out(0, this->m_sequence->getStringFileName(), emptyArgs);
337  }
338  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
339 }
340 
341 void CmdSequencerComponentImpl ::CS_STEP_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
342  if (this->requireRunMode(RUNNING)) {
343  this->performCmd_Step();
344  // check for special case where end of sequence entry was encountered
345  if (this->m_runMode != STOPPED) {
346  this->log_ACTIVITY_HI_CS_CmdStepped(this->m_sequence->getLogFileName(), this->m_executedCount);
347  }
348  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
349  } else {
350  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
351  }
352 }
353 
354 void CmdSequencerComponentImpl ::CS_AUTO_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
355  if (this->requireRunMode(STOPPED)) {
356  this->m_stepMode = AUTO;
358  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
359  } else {
360  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
361  }
362 }
363 
364 void CmdSequencerComponentImpl ::CS_MANUAL_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
365  if (this->requireRunMode(STOPPED)) {
366  this->m_stepMode = MANUAL;
368  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
369  } else {
370  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
371  }
372 }
373 
374 // ----------------------------------------------------------------------
375 // Helper methods
376 // ----------------------------------------------------------------------
377 
378 bool CmdSequencerComponentImpl::requireRunMode(RunMode mode) {
379  if (this->m_runMode == mode) {
380  return true;
381  } else {
383  return false;
384  }
385 }
386 
387 void CmdSequencerComponentImpl ::commandError(const U32 number, const FwOpcodeType opCode, const U32 error) {
388  this->log_WARNING_HI_CS_CommandError(this->m_sequence->getLogFileName(), number, opCode, error);
389  this->error();
390 }
391 
392 void CmdSequencerComponentImpl::performCmd_Step() {
393  this->m_sequence->nextRecord(m_record);
394  // set clock time base and context from value set when sequence was loaded
395  const Sequence::Header& header = this->m_sequence->getHeader();
396  this->m_record.m_timeTag.setTimeBase(header.m_timeBase);
397  this->m_record.m_timeTag.setTimeContext(header.m_timeContext);
398 
399  Fw::Time currentTime = this->getTime();
400  switch (this->m_record.m_descriptor) {
402  this->m_runMode = STOPPED;
403  this->sequenceComplete();
404  break;
406  this->performCmd_Step_RELATIVE(currentTime);
407  break;
409  this->performCmd_Step_ABSOLUTE(currentTime);
410  break;
411  default:
412  FW_ASSERT(0, m_record.m_descriptor);
413  }
414 }
415 
416 void CmdSequencerComponentImpl::sequenceComplete() {
417  ++this->m_sequencesCompletedCount;
418  // reset buffer
419  this->m_sequence->clear();
420  this->log_ACTIVITY_HI_CS_SequenceComplete(this->m_sequence->getLogFileName());
421  this->tlmWrite_CS_SequencesCompleted(this->m_sequencesCompletedCount);
422  this->m_executedCount = 0;
423  // write sequence done port, if connected
424  if (this->isConnected_seqDone_OutputPort(0)) {
425  this->seqDone_out(0, 0, 0, Fw::CmdResponse::OK);
426  }
427 
428  if (Svc::CmdSequencer_BlockState::BLOCK == this->m_blockState || m_join_waiting) {
429  this->cmdResponse_out(this->m_opCode, this->m_cmdSeq, Fw::CmdResponse::OK);
430  }
431 
432  m_join_waiting = false;
433  this->m_blockState = Svc::CmdSequencer_BlockState::NO_BLOCK;
434 }
435 
436 void CmdSequencerComponentImpl::commandComplete(const FwOpcodeType opcode) {
437  this->log_ACTIVITY_LO_CS_CommandComplete(this->m_sequence->getLogFileName(), this->m_executedCount, opcode);
438  ++this->m_executedCount;
439  ++this->m_totalExecutedCount;
440  this->tlmWrite_CS_CommandsExecuted(this->m_totalExecutedCount);
441 }
442 
443 void CmdSequencerComponentImpl ::performCmd_Step_RELATIVE(Fw::Time& currentTime) {
444  this->m_record.m_timeTag.add(currentTime.getSeconds(), currentTime.getUSeconds());
445  this->performCmd_Step_ABSOLUTE(currentTime);
446 }
447 
448 void CmdSequencerComponentImpl ::performCmd_Step_ABSOLUTE(Fw::Time& currentTime) {
449  if (currentTime >= this->m_record.m_timeTag) {
450  this->comCmdOut_out(0, m_record.m_command, 0);
451  this->setCmdTimeout(currentTime);
452  } else {
453  this->m_cmdTimer.set(this->m_record.m_timeTag);
454  }
455 }
456 
457 void CmdSequencerComponentImpl ::pingIn_handler(FwIndexType portNum,
458  U32 key
459 ) {
460  // send ping response
461  this->pingOut_out(0, key);
462 }
463 
464 void CmdSequencerComponentImpl ::setCmdTimeout(const Fw::Time& currentTime) {
465  // start timeout timer if enabled and not in step mode
466  if ((this->m_timeout > 0) and (AUTO == this->m_stepMode)) {
467  Fw::Time expTime = currentTime;
468  expTime.add(this->m_timeout, 0);
469  this->m_cmdTimeoutTimer.set(expTime);
470  }
471 }
472 
473 } // 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
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
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