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  this->seqStartOut_out(0, this->m_sequence->getStringFileName());
104  }
105  this->performCmd_Step();
106  }
107 
108  if (Svc::CmdSequencer_BlockState::NO_BLOCK == this->m_blockState) {
109  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
110  }
111 }
112 
113 void CmdSequencerComponentImpl::CS_VALIDATE_cmdHandler(FwOpcodeType opCode,
114  U32 cmdSeq,
115  const Fw::CmdStringArg& fileName) {
116  if (!this->requireRunMode(STOPPED)) {
117  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
118  return;
119  }
120 
121  // load commands
122  if (not this->loadFile(fileName)) {
123  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
124  return;
125  }
126 
127  // clear the buffer
128  this->m_sequence->clear();
129 
130  this->log_ACTIVITY_HI_CS_SequenceValid(this->m_sequence->getLogFileName());
131 
132  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
133 }
134 
136 void CmdSequencerComponentImpl::doSequenceRun(const Fw::StringBase& filename) {
137  if (!this->requireRunMode(STOPPED)) {
139  return;
140  }
141 
142  // If file name is non-empty, load a file.
143  // Empty file name means don't load.
144  if (filename != "") {
145  Fw::CmdStringArg cmdStr(filename);
146  const bool status = this->loadFile(cmdStr);
147  if (!status) {
149  return;
150  }
151  } else if (not this->m_sequence->hasMoreRecords()) {
152  // No sequence loaded
154  this->error();
156  return;
157  }
158 
159  this->m_executedCount = 0;
160 
161  // Check the step mode. If it is auto, start the sequence
162  if (AUTO == this->m_stepMode) {
163  this->m_runMode = RUNNING;
164  if (this->isConnected_seqStartOut_OutputPort(0)) {
165  this->seqStartOut_out(0, this->m_sequence->getStringFileName());
166  }
167  this->performCmd_Step();
168  }
169 
170  this->log_ACTIVITY_HI_CS_PortSequenceStarted(this->m_sequence->getLogFileName());
171 }
172 
173 void CmdSequencerComponentImpl::seqRunIn_handler(FwIndexType portNum, const Fw::StringBase& filename) {
174  this->doSequenceRun(filename);
175 }
176 
177 void CmdSequencerComponentImpl::seqDispatchIn_handler(FwIndexType portNum, Fw::StringBase& file_name) {
178  this->doSequenceRun(file_name);
179 }
180 
181 void CmdSequencerComponentImpl ::seqCancelIn_handler(const FwIndexType portNum) {
182  if (RUNNING == this->m_runMode) {
183  this->performCmd_Cancel();
184  this->log_ACTIVITY_HI_CS_SequenceCanceled(this->m_sequence->getLogFileName());
185  ++this->m_cancelCmdCount;
186  this->tlmWrite_CS_CancelCommands(this->m_cancelCmdCount);
187  } else {
189  }
190 }
191 
192 void CmdSequencerComponentImpl::CS_CANCEL_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
193  if (RUNNING == this->m_runMode) {
194  this->performCmd_Cancel();
195  this->log_ACTIVITY_HI_CS_SequenceCanceled(this->m_sequence->getLogFileName());
196  ++this->m_cancelCmdCount;
197  this->tlmWrite_CS_CancelCommands(this->m_cancelCmdCount);
198  } else {
200  }
201  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
202 }
203 
204 void CmdSequencerComponentImpl::CS_JOIN_WAIT_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq) {
205  // If there is no running sequence do not wait
206  if (m_runMode != RUNNING) {
208  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
209  return;
210  } else {
211  m_join_waiting = true;
212  Fw::LogStringArg& logFileName = this->m_sequence->getLogFileName();
213  this->log_ACTIVITY_HI_CS_JoinWaiting(logFileName, m_cmdSeq, m_opCode);
214  m_cmdSeq = cmdSeq;
215  m_opCode = opCode;
216  }
217 }
218 
219 // ----------------------------------------------------------------------
220 // Private helper methods
221 // ----------------------------------------------------------------------
222 
223 bool CmdSequencerComponentImpl ::loadFile(const Fw::ConstStringBase& fileName) {
224  const bool status = this->m_sequence->loadFile(fileName);
225  if (status) {
226  Fw::LogStringArg& logFileName = this->m_sequence->getLogFileName();
227  this->log_ACTIVITY_LO_CS_SequenceLoaded(logFileName);
228  ++this->m_loadCmdCount;
229  this->tlmWrite_CS_LoadCommands(this->m_loadCmdCount);
230  }
231  return status;
232 }
233 
234 void CmdSequencerComponentImpl::error() {
235  ++this->m_errorCount;
236  this->tlmWrite_CS_Errors(m_errorCount);
237 }
238 
239 void CmdSequencerComponentImpl::performCmd_Cancel() {
240  this->m_sequence->reset();
241  this->m_runMode = STOPPED;
242  this->m_cmdTimer.clear();
243  this->m_cmdTimeoutTimer.clear();
244  this->m_executedCount = 0;
245  // write sequence done port with error, if connected
246  if (this->isConnected_seqDone_OutputPort(0)) {
248  }
249 
250  if (Svc::CmdSequencer_BlockState::BLOCK == this->m_blockState || m_join_waiting) {
251  // Do not wait if sequence was canceled or a cmd failed
252  this->m_join_waiting = false;
253  this->cmdResponse_out(this->m_opCode, this->m_cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
254  }
255 
256  this->m_blockState = Svc::CmdSequencer_BlockState::NO_BLOCK;
257 }
258 
259 void CmdSequencerComponentImpl ::cmdResponseIn_handler(FwIndexType portNum,
260  FwOpcodeType opcode,
261  U32 cmdSeq,
262  const Fw::CmdResponse& response) {
263  if (this->m_runMode == STOPPED) {
264  // Sequencer is not running
266  } else {
267  // clear command timeout
268  this->m_cmdTimeoutTimer.clear();
269  if (response != Fw::CmdResponse::OK) {
270  this->commandError(this->m_executedCount, opcode, response.e);
271  this->performCmd_Cancel();
272  } else if (this->m_runMode == RUNNING && this->m_stepMode == AUTO) {
273  // Auto mode
274  this->commandComplete(opcode);
275  if (not this->m_sequence->hasMoreRecords()) {
276  // No data left
277  this->m_runMode = STOPPED;
278  this->sequenceComplete();
279  } else {
280  this->performCmd_Step();
281  }
282  } else {
283  // Manual step mode
284  this->commandComplete(opcode);
285  if (not this->m_sequence->hasMoreRecords()) {
286  this->m_runMode = STOPPED;
287  this->sequenceComplete();
288  }
289  }
290  }
291 }
292 
293 void CmdSequencerComponentImpl ::schedIn_handler(FwIndexType portNum, U32 order) {
294  Fw::Time currTime = this->getTime();
295  // check to see if a command time is pending
296  if (this->m_cmdTimer.isExpiredAt(currTime)) {
297  this->comCmdOut_out(0, m_record.m_command, 0);
298  this->m_cmdTimer.clear();
299  // start command timeout timer
300  this->setCmdTimeout(currTime);
301  } else if (this->m_cmdTimeoutTimer.isExpiredAt(this->getTime())) { // check for command timeout
302  this->log_WARNING_HI_CS_SequenceTimeout(m_sequence->getLogFileName(), this->m_executedCount);
303  // If there is a command timeout, cancel the sequence
304  this->performCmd_Cancel();
305  }
306 }
307 
308 void CmdSequencerComponentImpl ::CS_START_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
309  if (not this->m_sequence->hasMoreRecords()) {
310  // No sequence loaded
312  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
313  return;
314  }
315  if (!this->requireRunMode(STOPPED)) {
316  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
317  return;
318  }
319 
320  this->m_blockState = Svc::CmdSequencer_BlockState::NO_BLOCK;
321  this->m_runMode = RUNNING;
322  this->performCmd_Step();
323  this->log_ACTIVITY_HI_CS_CmdStarted(this->m_sequence->getLogFileName());
324  if (this->isConnected_seqStartOut_OutputPort(0)) {
325  this->seqStartOut_out(0, this->m_sequence->getStringFileName());
326  }
327  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
328 }
329 
330 void CmdSequencerComponentImpl ::CS_STEP_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
331  if (this->requireRunMode(RUNNING)) {
332  this->performCmd_Step();
333  // check for special case where end of sequence entry was encountered
334  if (this->m_runMode != STOPPED) {
335  this->log_ACTIVITY_HI_CS_CmdStepped(this->m_sequence->getLogFileName(), this->m_executedCount);
336  }
337  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
338  } else {
339  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
340  }
341 }
342 
343 void CmdSequencerComponentImpl ::CS_AUTO_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
344  if (this->requireRunMode(STOPPED)) {
345  this->m_stepMode = AUTO;
347  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
348  } else {
349  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
350  }
351 }
352 
353 void CmdSequencerComponentImpl ::CS_MANUAL_cmdHandler(FwOpcodeType opcode, U32 cmdSeq) {
354  if (this->requireRunMode(STOPPED)) {
355  this->m_stepMode = MANUAL;
357  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
358  } else {
359  this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
360  }
361 }
362 
363 // ----------------------------------------------------------------------
364 // Helper methods
365 // ----------------------------------------------------------------------
366 
367 bool CmdSequencerComponentImpl::requireRunMode(RunMode mode) {
368  if (this->m_runMode == mode) {
369  return true;
370  } else {
372  return false;
373  }
374 }
375 
376 void CmdSequencerComponentImpl ::commandError(const U32 number, const FwOpcodeType opCode, const U32 error) {
377  this->log_WARNING_HI_CS_CommandError(this->m_sequence->getLogFileName(), number, opCode, error);
378  this->error();
379 }
380 
381 void CmdSequencerComponentImpl::performCmd_Step() {
382  this->m_sequence->nextRecord(m_record);
383  // set clock time base and context from value set when sequence was loaded
384  const Sequence::Header& header = this->m_sequence->getHeader();
385  this->m_record.m_timeTag.setTimeBase(header.m_timeBase);
386  this->m_record.m_timeTag.setTimeContext(header.m_timeContext);
387 
388  Fw::Time currentTime = this->getTime();
389  switch (this->m_record.m_descriptor) {
391  this->m_runMode = STOPPED;
392  this->sequenceComplete();
393  break;
395  this->performCmd_Step_RELATIVE(currentTime);
396  break;
398  this->performCmd_Step_ABSOLUTE(currentTime);
399  break;
400  default:
401  FW_ASSERT(0, m_record.m_descriptor);
402  }
403 }
404 
405 void CmdSequencerComponentImpl::sequenceComplete() {
406  ++this->m_sequencesCompletedCount;
407  // reset buffer
408  this->m_sequence->clear();
409  this->log_ACTIVITY_HI_CS_SequenceComplete(this->m_sequence->getLogFileName());
410  this->tlmWrite_CS_SequencesCompleted(this->m_sequencesCompletedCount);
411  this->m_executedCount = 0;
412  // write sequence done port, if connected
413  if (this->isConnected_seqDone_OutputPort(0)) {
414  this->seqDone_out(0, 0, 0, Fw::CmdResponse::OK);
415  }
416 
417  if (Svc::CmdSequencer_BlockState::BLOCK == this->m_blockState || m_join_waiting) {
418  this->cmdResponse_out(this->m_opCode, this->m_cmdSeq, Fw::CmdResponse::OK);
419  }
420 
421  m_join_waiting = false;
422  this->m_blockState = Svc::CmdSequencer_BlockState::NO_BLOCK;
423 }
424 
425 void CmdSequencerComponentImpl::commandComplete(const FwOpcodeType opcode) {
426  this->log_ACTIVITY_LO_CS_CommandComplete(this->m_sequence->getLogFileName(), this->m_executedCount, opcode);
427  ++this->m_executedCount;
428  ++this->m_totalExecutedCount;
429  this->tlmWrite_CS_CommandsExecuted(this->m_totalExecutedCount);
430 }
431 
432 void CmdSequencerComponentImpl ::performCmd_Step_RELATIVE(Fw::Time& currentTime) {
433  this->m_record.m_timeTag.add(currentTime.getSeconds(), currentTime.getUSeconds());
434  this->performCmd_Step_ABSOLUTE(currentTime);
435 }
436 
437 void CmdSequencerComponentImpl ::performCmd_Step_ABSOLUTE(Fw::Time& currentTime) {
438  if (currentTime >= this->m_record.m_timeTag) {
439  this->comCmdOut_out(0, m_record.m_command, 0);
440  this->setCmdTimeout(currentTime);
441  } else {
442  this->m_cmdTimer.set(this->m_record.m_timeTag);
443  }
444 }
445 
446 void CmdSequencerComponentImpl ::pingIn_handler(FwIndexType portNum,
447  U32 key
448 ) {
449  // send ping response
450  this->pingOut_out(0, key);
451 }
452 
453 void CmdSequencerComponentImpl ::setCmdTimeout(const Fw::Time& currentTime) {
454  // start timeout timer if enabled and not in step mode
455  if ((this->m_timeout > 0) and (AUTO == this->m_stepMode)) {
456  Fw::Time expTime = currentTime;
457  expTime.add(this->m_timeout, 0);
458  this->m_cmdTimeoutTimer.set(expTime);
459  }
460 }
461 
462 } // 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
Fw::Time m_timeTag
The time tag. NOTE: timeBase and context not filled in.
void pingOut_out(FwIndexType portNum, U32 key)
Invoke output port pingOut.
void log_ACTIVITY_HI_CS_CmdStarted(const Fw::StringBase &filename) const
void comCmdOut_out(FwIndexType portNum, Fw::ComBuffer &data, U32 context)
Invoke output port comCmdOut.
void seqDone_out(FwIndexType portNum, FwOpcodeType opCode, U32 cmdSeq, const Fw::CmdResponse &response)
Invoke output port seqDone.
Enum representing a command response.
void log_WARNING_HI_CS_SequenceTimeout(const Fw::StringBase &filename, U32 command) const
void allocateBuffer(FwEnumStoreType identifier, Fw::MemAllocator &allocator, FwSizeType bytes)
Give the sequence representation a memory buffer.
Definition: Sequence.cpp:45
void seqStartOut_out(FwIndexType portNum, const Fw::StringBase &filename)
Invoke output port seqStartOut.
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 log_ACTIVITY_HI_CS_SequenceCanceled(const Fw::StringBase &fileName) const
void setTimeContext(FwTimeContextStoreType context)
Definition: Time.cpp:199
void allocateBuffer(const FwEnumStoreType identifier, Fw::MemAllocator &allocator, const FwSizeType bytes)
A sequence with unspecified binary format.
bool isConnected_seqStartOut_OutputPort(FwIndexType portNum)
void deallocateBuffer(Fw::MemAllocator &allocator)
Deallocate the buffer.
Definition: Sequence.cpp:55
U32 getSeconds() const
Definition: Time.cpp:88
T e
The raw enum value.
void setTimeBase(TimeBase timeBase)
Definition: Time.cpp:195
void setTimeout(const U32 seconds)
Command successfully executed.
bool isConnected_seqDone_OutputPort(FwIndexType portNum)
virtual bool loadFile(const Fw::ConstStringBase &fileName)=0
Command had execution error.
static Time add(const Time &a, const Time &b)
Definition: Time.cpp:134
void deallocateBuffer(Fw::MemAllocator &allocator)
Return allocated buffer. Call during shutdown.
Memory Allocation base class.
U32 getUSeconds() const
Definition: Time.cpp:92
A read-only abstract superclass for StringBase.
PlatformIndexType FwIndexType
Auto-generated base for CmdSequencer component.
RateGroupDivider component implementation.
virtual void nextRecord(Record &record)=0
void log_ACTIVITY_HI_CS_SequenceComplete(const Fw::StringBase &fileName) const
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
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