fprime/Svc/CmdSequencer/CmdSequencerImpl.hpp
Ian Brault 2b65cc83cf
Add new Fw::ConstStringBase type for strings backed by immutable string literals (#4269)
* Add new Fw::StringBase type StaticString for strings backed my immutable literals

* Spellcheck fix

* Add disclaimer comment about use of StaticString

* Refactor the StringBase interface into an immutable ConstStringBase abstract base class and the now mutable StringBase class

* Rename StaticString to ConstExternalString and inherit from ConstStringBase

* Fix typo

* Change references from StringBase to ConstStringBase where applicable

* Updates following review meeting: add missing deserialize function and add new error status, move length function implementation into ConstStringBase so it is not pure virtual

* Clang format fix

* Additional clang-format fixes

* Fix the copy-assignment operator for StringBase not being correctly evaluated

* Clang format fix

* Explicitly delete the Serializable assignment operator and provide a skeleton implementation for RawTimeInterface to appease the compiler

* Revert "Explicitly delete the Serializable assignment operator and provide a skeleton implementation for RawTimeInterface to appease the compiler"

This reverts commit 086d7bcd3ca9c4f6e553d7fc34d0d126a69a165b.

* Move ConstStringBase to separate hpp/cpp files, plus other pull request feedback

* Clang format fix

* Update length implementation for ConstStringBase and ConstExternalString

* Improved asserts in ConstExternalString constructor

Co-authored-by: Rob Bocchino <bocchino@icloud.com>

* Fixed ConstStringBase length implementation

Co-authored-by: Rob Bocchino <bocchino@icloud.com>

* Clang format fix

* Add some UTs for ConstExternalString, fix non-overridden interfaces, and fix ConstStringBase::maxLength asserting for zero capacity strings

* Spell-check fix for ConstExternalString UTs

* Revise length implementation in ConstStringBase

If the capacity is zero, return zero

* Format

---------

Co-authored-by: Ian Brault <ian.r.brault@jpl.nasa.gov>
Co-authored-by: Rob Bocchino <bocchino@icloud.com>
Co-authored-by: Rob Bocchino <bocchino@jpl.nasa.gov>
Co-authored-by: M Starch <LeStarch@googlemail.com>
2025-11-07 09:50:05 -08:00

700 lines
22 KiB
C++

// ======================================================================
// \title CmdSequencerImpl.hpp
// \author Bocchino/Canham
// \brief hpp file for CmdSequencer component implementation class
//
// Copyright (C) 2009-2018 California Institute of Technology.
// ALL RIGHTS RESERVED. United States Government Sponsorship
// acknowledged.
// ======================================================================
#ifndef Svc_CmdSequencerImpl_HPP
#define Svc_CmdSequencerImpl_HPP
#include "Fw/Com/ComBuffer.hpp"
#include "Fw/Types/MemAllocator.hpp"
#include "Os/File.hpp"
#include "Os/ValidateFile.hpp"
#include "Svc/CmdSequencer/CmdSequencerComponentAc.hpp"
namespace Svc {
// Forward declaration for UTs
namespace ImmediateBase {
class CmdSequencerTester;
}
namespace Immediate {
class CmdSequencerTester;
}
namespace ImmediateEOS {
class CmdSequencerTester;
}
namespace Mixed {
class CmdSequencerTester;
}
namespace MixedRelativeBase {
class CmdSequencerTester;
}
namespace Relative {
class CmdSequencerTester;
}
namespace JoinWait {
class CmdSequencerTester;
}
class CmdSequencerComponentImpl final : public CmdSequencerComponentBase {
friend class CmdSequencerTester;
friend class Svc::ImmediateBase::CmdSequencerTester;
friend class Svc::Immediate::CmdSequencerTester;
friend class Svc::ImmediateEOS::CmdSequencerTester;
friend class Svc::Mixed::CmdSequencerTester;
friend class Svc::MixedRelativeBase::CmdSequencerTester;
friend class Svc::Relative::CmdSequencerTester;
friend class Svc::JoinWait::CmdSequencerTester;
private:
// ----------------------------------------------------------------------
// Private enumerations
// ----------------------------------------------------------------------
//! The run mode
enum RunMode { STOPPED, RUNNING };
//! The step mode
enum StepMode { AUTO, MANUAL };
public:
// ----------------------------------------------------------------------
// Public classes
// ----------------------------------------------------------------------
//! \class Sequence
//! \brief A sequence with unspecified binary format
class Sequence {
public:
//! \class Events
//! \brief Sequence event reporting
class Events {
public:
//! Construct an Events object
Events(Sequence& sequence //!< The enclosing sequence
);
public:
//! File CRC failure
void fileCRCFailure(const U32 storedCRC, //!< The CRC stored in the file
const U32 computedCRC //!< The CRC computed over the file
);
//! File invalid
void fileInvalid(const CmdSequencer_FileReadStage::t stage, //!< The file read stage
const I32 error //!< The error
);
//! File not found
void fileNotFound();
//! File read error
void fileReadError();
//! File size error
void fileSizeError(const U32 size //!< The size
);
//! Record invalid
void recordInvalid(const U32 recordNumber, //!< The record number
const I32 error //!< The error
);
//! Record mismatch
void recordMismatch(const U32 numRecords, //!< The number of records in the header
const U32 extraBytes //!< The number of bytes beyond last record
);
//! Time base mismatch
void timeBaseMismatch(const TimeBase currTimeBase, //!< The current time base
const TimeBase seqTimeBase //!< The sequence file time base
);
//! Time context mismatch
void timeContextMismatch(const FwTimeContextStoreType currTimeContext, //!< The current time context
const FwTimeContextStoreType seqTimeContext //!< The sequence file time context
);
// No Records
void noRecords();
private:
//! The enclosing component
Sequence& m_sequence;
};
public:
//! Construct a Sequence object
Sequence(CmdSequencerComponentImpl& component //!< The enclosing component
);
//! Destroy a Sequence object
virtual ~Sequence();
public:
//! \class Header
//! \brief A sequence header
class Header {
public:
enum Constants {
//! Serialized size of header
SERIALIZED_SIZE =
sizeof(U32) + sizeof(U32) + sizeof(FwTimeBaseStoreType) + sizeof(FwTimeContextStoreType)
};
public:
//! Construct a Header object
Header();
public:
//! Validate the time field of the sequence header
//! \return Success or failure
bool validateTime(CmdSequencerComponentImpl& component //!< Component for time and events
);
public:
//! The file size
U32 m_fileSize;
//! The number of records in the sequence
U32 m_numRecords;
//! The time base of the sequence
TimeBase m_timeBase;
//! The context of the sequence
FwTimeContextStoreType m_timeContext;
};
public:
//! \class Record
//! \brief A sequence record
class Record {
public:
enum Descriptor {
ABSOLUTE, //!< Absolute time
RELATIVE, //!< Relative time
END_OF_SEQUENCE //!< end of sequence
};
public:
//! Construct a Record object
Record() : m_descriptor(END_OF_SEQUENCE) {}
public:
//! The descriptor
Descriptor m_descriptor;
//! The time tag. NOTE: timeBase and context not filled in
Fw::Time m_timeTag;
//! The command
Fw::ComBuffer m_command;
};
public:
//! Give the sequence representation a memory buffer
void allocateBuffer(FwEnumStoreType identifier, //!< The identifier
Fw::MemAllocator& allocator, //!< The allocator
FwSizeType bytes //!< The number of bytes
);
//! Deallocate the buffer
void deallocateBuffer(Fw::MemAllocator& allocator //!< The allocator
);
//! Set the file name. Also sets the log file name.
void setFileName(const Fw::ConstStringBase& fileName);
//! Get the file name
//! \return The file name
Fw::CmdStringArg& getFileName();
//! Get the log file name
//! \return The log file name
Fw::LogStringArg& getLogFileName();
//! Get the normal string file name
//! \return The normal string file name
Fw::String& getStringFileName();
//! Get the sequence header
const Header& getHeader() const;
//! Load a sequence file
//! \return Success or failure
virtual bool loadFile(const Fw::ConstStringBase& fileName //!< The file name
) = 0;
//! Query whether the sequence has any more records
//! \return Yes or no
virtual bool hasMoreRecords() const = 0;
//! Get the next record in the sequence
//! Asserts on failure
virtual void nextRecord(Record& record //!< The returned record
) = 0;
//! Reset the sequence to the beginning.
//! After calling this, hasMoreRecords should return true,
//! unless the sequence has no records
virtual void reset() = 0;
//! Clear the sequence records.
//! After calling this, hasMoreRecords should return false
virtual void clear() = 0;
protected:
//! The enclosing component
CmdSequencerComponentImpl& m_component;
//! Event reporting
Events m_events;
//! The sequence file name
Fw::CmdStringArg m_fileName;
//! Copy of file name for events
Fw::LogStringArg m_logFileName;
//! Copy of file name for ports
Fw::String m_stringFileName;
//! Serialize buffer to hold the binary sequence data
Fw::ExternalSerializeBuffer m_buffer;
//! The allocator ID
FwEnumStoreType m_allocatorId;
//! The sequence header
Header m_header;
};
//! \class FPrimeSequence
//! \brief A sequence that uses the F Prime binary format
class FPrimeSequence : public Sequence {
private:
enum Constants { INITIAL_COMPUTED_VALUE = 0xFFFFFFFFU };
public:
//! \class CRC
//! \brief Container for computed and stored CRC values
struct CRC {
//! Construct a CRC
CRC();
//! Initialize computed CRC
void init();
//! Update computed CRC
void update(const BYTE* buffer, //!< The buffer
FwSizeType bufferSize //!< The buffer size
);
//! Finalize computed CRC
void finalize();
//! Computed CRC
U32 m_computed;
//! Stored CRC
U32 m_stored;
};
public:
//! Construct an FPrimeSequence
FPrimeSequence(CmdSequencerComponentImpl& component //!< The enclosing component
);
public:
//! Load a sequence file
//! \return Success or failure
bool loadFile(const Fw::ConstStringBase& fileName //!< The file name
);
//! Query whether the sequence has any more records
//! \return Yes or no
bool hasMoreRecords() const;
//! Get the next record in the sequence.
//! Asserts on failure
void nextRecord(Record& record //!< The returned record
);
//! Reset the sequence to the beginning.
//! After calling this, hasMoreRecords should return true, unless
//! the sequence has no records.
void reset();
//! Clear the sequence records.
//! After calling this, hasMoreRecords should return false.
void clear();
private:
//! Read a sequence file
//! \return Success or failure
bool readFile();
//! Read an open sequence file
//! \return Success or failure
bool readOpenFile();
//! Read a binary sequence header from the sequence file
//! into the buffer
//! \return Success or failure
bool readHeader();
//! Deserialize the binary sequence header from the buffer
//! \return Success or failure
bool deserializeHeader();
//! Read records and CRC into buffer
//! \return Success or failure
bool readRecordsAndCRC();
//! Extract CRC from record data
//! \return Success or failure
bool extractCRC();
//! Validate the CRC
//! \return Success or failure
bool validateCRC();
//! Deserialize a record from a buffer
//! \return Serialize status
Fw::SerializeStatus deserializeRecord(Record& record //!< The record
);
//! Deserialize a record descriptor
//! \return Serialize status
Fw::SerializeStatus deserializeDescriptor(Record::Descriptor& descriptor //!< The descriptor
);
//! Deserialize a time tag
//! \return Serialize status
Fw::SerializeStatus deserializeTimeTag(Fw::Time& timeTag //!< The time tag
);
//! Deserialize the record size
//! \return Serialize status
Fw::SerializeStatus deserializeRecordSize(U32& recordSize //!< The record size
);
//! Copy the serialized command into a com buffer
//! \return Serialize status
Fw::SerializeStatus copyCommand(Fw::ComBuffer& comBuffer, //!< The com buffer
const U32 recordSize //!< The record size
);
//! Validate the sequence records in the buffer
//! \return Success or failure
bool validateRecords();
private:
//! The CRC values
CRC m_crc;
//! The sequence file
Os::File m_sequenceFile;
};
private:
// ----------------------------------------------------------------------
// Private classes
// ----------------------------------------------------------------------
//! \class Timer
//! \brief A class representing a timer
class Timer {
friend class CmdSequencerTester;
friend class Svc::ImmediateBase::CmdSequencerTester;
friend class Svc::Immediate::CmdSequencerTester;
friend class Svc::ImmediateEOS::CmdSequencerTester;
friend class Svc::Mixed::CmdSequencerTester;
friend class Svc::MixedRelativeBase::CmdSequencerTester;
friend class Svc::Relative::CmdSequencerTester;
friend class Svc::JoinWait::CmdSequencerTester;
private:
//! The timer state
typedef enum { SET, CLEAR } State;
public:
//! Construct a Timer object
Timer() : m_state(CLEAR) {}
//! Set the expiration time
void set(Fw::Time time //!< The time
) {
this->m_state = SET;
this->expirationTime = time;
}
//! Clear the timer
void clear() { this->m_state = CLEAR; }
//! Determine whether the timer is expired at a given time
//! \return Yes or no
bool isExpiredAt(Fw::Time time //!< The time
) {
if (this->m_state == CLEAR) {
return false;
} else if (Fw::Time::compare(this->expirationTime, time) == Fw::Time::GT) {
return false;
}
return true;
}
private:
//! The timer state
State m_state;
//! The expiration time
Fw::Time expirationTime;
};
public:
// ----------------------------------------------------------------------
// Construction, initialization, and destruction
// ----------------------------------------------------------------------
//! Construct a CmdSequencer
CmdSequencerComponentImpl(const char* compName //!< The component name
);
//! (Optional) Set a timeout.
//! Sequence will quit if a command takes longer than the number of
//! seconds in the timeout value.
void setTimeout(const U32 seconds //!< The number of seconds
);
//! (Optional) Set the sequence format.
//! CmdSequencer will use the sequence object you pass in
//! to load and run sequences. By default, it uses an FPrimeSequence
//! object.
void setSequenceFormat(Sequence& sequence //!< The sequence object
);
//! Give the sequence a memory buffer.
//! Call this after constructor and init, and after setting
//! the sequence format, but before task is spawned.
void allocateBuffer(const FwEnumStoreType identifier, //!< The identifier
Fw::MemAllocator& allocator, //!< The allocator
const FwSizeType bytes //!< The number of bytes
);
//! (Optional) Load a sequence to run later.
//! When you call this function, the event ports must be connected.
void loadSequence(const Fw::ConstStringBase& fileName //!< The file name
);
//! Return allocated buffer. Call during shutdown.
void deallocateBuffer(Fw::MemAllocator& allocator //!< The allocator
);
//! Destroy a CmdDispatcherComponentBase
~CmdSequencerComponentImpl();
private:
// ----------------------------------------------------------------------
// Handler implementations for input ports
// ----------------------------------------------------------------------
//! Handler for input port cmdResponseIn
void cmdResponseIn_handler(FwIndexType portNum, //!< The port number
FwOpcodeType opcode, //!< The command opcode
U32 cmdSeq, //!< The command sequence number
const Fw::CmdResponse& response //!< The command response
);
//! Handler for input port schedIn
void schedIn_handler(FwIndexType portNum, //!< The port number
U32 order //!< The call order
);
//! Handler for input port seqRunIn
void seqRunIn_handler(FwIndexType portNum, //!< The port number
const Fw::StringBase& filename //!< The sequence file
);
//! Handler for ping port
void pingIn_handler(FwIndexType portNum, //!< The port number
U32 key //!< Value to return to pinger
);
//! Handler implementation for seqCancelIn
//!
void seqCancelIn_handler(const FwIndexType portNum /*!< The port number*/
);
private:
// ----------------------------------------------------------------------
// Command handler implementations
// ----------------------------------------------------------------------
//! Handler for command CS_AUTO
//! Set the run mode to AUTO.
void CS_AUTO_cmdHandler(FwOpcodeType opcode, //!< The opcode
U32 cmdSeq //!< The command sequence number
);
//! Handler for command CS_CANCEL
//! Validate a command sequence file
void CS_CANCEL_cmdHandler(FwOpcodeType opCode, //!< The opcode
U32 cmdSeq //!< The command sequence number
);
//! Handler for command CS_MANUAL
//! Set the run mode to MANUAL.
void CS_MANUAL_cmdHandler(FwOpcodeType opcode, //!< The opcode
U32 cmdSeq //!< The command sequence number
);
//! Handler for command CS_RUN
void CS_RUN_cmdHandler(FwOpcodeType opCode, //!< The opcode
U32 cmdSeq, //!< The command sequence number
const Fw::CmdStringArg& fileName, //!< The file name
Svc::CmdSequencer_BlockState block /*!< Return command status when complete or not*/
);
//! Handler for command CS_START
//! Start running a command sequence
void CS_START_cmdHandler(FwOpcodeType opcode, //!< The opcode
U32 cmdSeq //!< The command sequence number
);
//! Handler for command CS_STEP
//! Perform one step in a command sequence.
//! Valid only if SequenceRunner is in MANUAL run mode.
void CS_STEP_cmdHandler(FwOpcodeType opcode, //!< The opcode
U32 cmdSeq //!< The command sequence number
);
//! Handler for command CS_VALIDATE
//! Run a command sequence file
void CS_VALIDATE_cmdHandler(FwOpcodeType opCode, //!< The opcode
U32 cmdSeq, //!< The command sequence number
const Fw::CmdStringArg& fileName //!< The name of the sequence file
);
//! Implementation for CS_JOIN command handler
//! Wait for sequences that are running to finish.
//! Allow user to run multiple seq files in SEQ_NO_BLOCK mode
//! then wait for them to finish before allowing more seq run request.
void CS_JOIN_WAIT_cmdHandler(const FwOpcodeType opCode, /*!< The opcode*/
const U32 cmdSeq /*!< The command sequence number*/
);
private:
// ----------------------------------------------------------------------
// Private helper methods
// ----------------------------------------------------------------------
//! Load a sequence file
//! \return Success or failure
bool loadFile(const Fw::ConstStringBase& fileName //!< The file name
);
//! Perform a Cancel command
void performCmd_Cancel();
//! Perform a Step command
void performCmd_Step();
//! Perform a Step command with a relative time
void performCmd_Step_RELATIVE(Fw::Time& currentTime //!< The time
);
//! Perform a Step command with an absolute time
void performCmd_Step_ABSOLUTE(Fw::Time& currentTime //!< The time
);
//! Record a completed command
void commandComplete(const FwOpcodeType opCode //!< The opcode
);
//! Record a sequence complete event
void sequenceComplete();
//! Record an error
void error();
//! Record an error in executing a sequence command
void commandError(const U32 number, //!< The command number
const FwOpcodeType opCode, //!< The command opcode
const U32 error //!< The error code
);
//! Require a run mode
//! \return Whether we are in the correct mode
bool requireRunMode(RunMode mode //!< The required mode
);
//! Set command timeout timer
void setCmdTimeout(const Fw::Time& currentTime //!< The current time
);
private:
// ----------------------------------------------------------------------
// Private member variables
// ----------------------------------------------------------------------
//! The F Prime sequence
FPrimeSequence m_FPrimeSequence;
//! The abstract sequence
Sequence* m_sequence;
//! The number of Load commands executed
U32 m_loadCmdCount;
//! The number of Cancel commands executed
U32 m_cancelCmdCount;
//! The number of errors
U32 m_errorCount;
//! The run mode
RunMode m_runMode;
//! The step mode
StepMode m_stepMode;
//! The sequence record currently being processed
Sequence::Record m_record;
//! The command time timer
Timer m_cmdTimer;
//! The number of commands executed in this sequence
U32 m_executedCount;
//! The total number of commands executed across all sequences
U32 m_totalExecutedCount;
//! The total number of sequences completed
U32 m_sequencesCompletedCount;
//! timeout value
U32 m_timeout;
//! timeout timer
Timer m_cmdTimeoutTimer;
//! Block mode for command status
Svc::CmdSequencer_BlockState::t m_blockState;
FwOpcodeType m_opCode;
U32 m_cmdSeq;
bool m_join_waiting;
};
} // namespace Svc
#endif