mirror of
https://github.com/nasa/fprime.git
synced 2025-12-10 17:47:10 -06:00
Instead of adding the name argument to a constructor when FW_OBJECT_NAMES is enabled, always supply a name argument. Add a marco that conditionally sets the name to "" if FW_OBJECT_NAMES is not set. This cleans up a lot of the conditional compilation switches that weren't being tested and would silently break, while still stripping the strings from the binary.
788 lines
22 KiB
C++
788 lines
22 KiB
C++
// ======================================================================
|
|
// \title CmdSequencerImpl.hpp
|
|
// \author Bocchino/Canham
|
|
// \brief hpp file for CmdSequencer component implementation class
|
|
//
|
|
// \copyright
|
|
// 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 {
|
|
|
|
class CmdSequencerComponentImpl :
|
|
public CmdSequencerComponentBase
|
|
{
|
|
|
|
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:
|
|
|
|
//! File read stage for error reporting
|
|
struct FileReadStage {
|
|
|
|
typedef enum {
|
|
READ_HEADER,
|
|
READ_HEADER_SIZE,
|
|
DESER_SIZE,
|
|
DESER_NUM_RECORDS,
|
|
DESER_TIME_BASE,
|
|
DESER_TIME_CONTEXT,
|
|
READ_SEQ_CRC,
|
|
READ_SEQ_DATA,
|
|
READ_SEQ_DATA_SIZE,
|
|
} t;
|
|
|
|
//! Convert FileReadStage::t to CmdSequencerComponentImpl::FileReadStage
|
|
static CmdSequencerComponentImpl::FileReadStage
|
|
toComponentEnum(
|
|
const t fileReadStage //!< The file read stage
|
|
);
|
|
|
|
};
|
|
|
|
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 FileReadStage::t stage, //!< The file read stage
|
|
const I32 error //!< The error
|
|
);
|
|
|
|
//! File not found
|
|
void fileNotFound(void);
|
|
|
|
//! File read error
|
|
void fileReadError(void);
|
|
|
|
//! 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 FwTimeBaseStoreType currTimeBase, //!< The current time base
|
|
const FwTimeBaseStoreType 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
|
|
);
|
|
|
|
PRIVATE:
|
|
|
|
//! The enclosing component
|
|
Sequence& m_sequence;
|
|
|
|
};
|
|
|
|
public:
|
|
|
|
//! Construct a Sequence object
|
|
Sequence(
|
|
CmdSequencerComponentImpl& component //!< The enclosing component
|
|
);
|
|
|
|
//! Destroy a Sequence object
|
|
virtual ~Sequence(void);
|
|
|
|
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(void);
|
|
|
|
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(void) :
|
|
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(
|
|
const NATIVE_INT_TYPE identifier, //!< The identifier
|
|
Fw::MemAllocator& allocator, //!< The allocator
|
|
const NATIVE_UINT_TYPE 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::CmdStringArg& fileName);
|
|
|
|
//! Get the file name
|
|
//! \return The file name
|
|
Fw::CmdStringArg& getFileName(void);
|
|
|
|
//! Get the log file name
|
|
//! \return The log file name
|
|
Fw::LogStringArg& getLogFileName(void);
|
|
|
|
//! Get the sequence header
|
|
const Header& getHeader(void) const;
|
|
|
|
//! Load a sequence file
|
|
//! \return Success or failure
|
|
virtual bool loadFile(
|
|
const Fw::CmdStringArg& fileName //!< The file name
|
|
) = 0;
|
|
|
|
//! Query whether the sequence has any more records
|
|
//! \return Yes or no
|
|
virtual bool hasMoreRecords(void) 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(void) = 0;
|
|
|
|
//! Clear the sequence records.
|
|
//! After calling this, hasMoreRecords should return false
|
|
virtual void clear(void) = 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;
|
|
|
|
//! Serialize buffer to hold the binary sequence data
|
|
Fw::ExternalSerializeBuffer m_buffer;
|
|
|
|
//! The allocator ID
|
|
NATIVE_INT_TYPE 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(void);
|
|
|
|
//! Initialize computed CRC
|
|
void init(void);
|
|
|
|
//! Update computed CRC
|
|
void update(
|
|
const BYTE* buffer, //!< The buffer
|
|
NATIVE_UINT_TYPE bufferSize //!< The buffer size
|
|
);
|
|
|
|
//! Finalize computed CRC
|
|
void finalize(void);
|
|
|
|
//! 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::CmdStringArg& fileName //!< The file name
|
|
);
|
|
|
|
//! Query whether the sequence has any more records
|
|
//! \return Yes or no
|
|
bool hasMoreRecords(void) 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(void);
|
|
|
|
//! Clear the sequence records.
|
|
//! After calling this, hasMoreRecords should return false.
|
|
void clear(void);
|
|
|
|
PRIVATE:
|
|
|
|
//! Read a sequence file
|
|
//! \return Success or failure
|
|
bool readFile(void);
|
|
|
|
//! Read an open sequence file
|
|
//! \return Success or failure
|
|
bool readOpenFile(void);
|
|
|
|
//! Read a binary sequence header from the sequence file
|
|
//! into the buffer
|
|
//! \return Success or failure
|
|
bool readHeader(void);
|
|
|
|
//! Deserialize the binary sequence header from the buffer
|
|
//! \return Success or failure
|
|
bool deserializeHeader(void);
|
|
|
|
//! Read records and CRC into buffer
|
|
//! \return Success or failure
|
|
bool readRecordsAndCRC(void);
|
|
|
|
//! Extract CRC from record data
|
|
//! \return Success or failure
|
|
bool extractCRC(void);
|
|
|
|
//! Validate the CRC
|
|
//! \return Success or failure
|
|
bool validateCRC(void);
|
|
|
|
//! 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(void);
|
|
|
|
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 {
|
|
|
|
PRIVATE:
|
|
|
|
//! The timer state
|
|
typedef enum {
|
|
SET, CLEAR
|
|
} State;
|
|
|
|
public:
|
|
|
|
//! Construct a Timer object
|
|
Timer(void) :
|
|
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(void) {
|
|
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
|
|
);
|
|
|
|
//! Initialize a CmdSequencer
|
|
void init(
|
|
const NATIVE_INT_TYPE queueDepth, //!< The queue depth
|
|
const NATIVE_INT_TYPE instance //!< The instance number
|
|
);
|
|
|
|
//! (Optional) Set a timeout.
|
|
//! Sequence will quit if a command takes longer than the number of
|
|
//! seconds in the timeout value.
|
|
void setTimeout(
|
|
NATIVE_UINT_TYPE 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 NATIVE_INT_TYPE identifier, //!< The identifier
|
|
Fw::MemAllocator& allocator, //!< The allocator
|
|
const NATIVE_UINT_TYPE 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::EightyCharString& fileName //!< The file name
|
|
);
|
|
|
|
//! Return allocated buffer. Call during shutdown.
|
|
void deallocateBuffer(
|
|
Fw::MemAllocator& allocator //!< The allocator
|
|
);
|
|
|
|
//! Destroy a CmdDispatcherComponentBase
|
|
~CmdSequencerComponentImpl(void);
|
|
|
|
PRIVATE:
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Handler implementations for input ports
|
|
// ----------------------------------------------------------------------
|
|
|
|
//! Handler for input port cmdResponseIn
|
|
void cmdResponseIn_handler(
|
|
NATIVE_INT_TYPE portNum, //!< The port number
|
|
FwOpcodeType opcode, //!< The command opcode
|
|
U32 cmdSeq, //!< The command sequence number
|
|
Fw::CommandResponse response //!< The command response
|
|
);
|
|
|
|
//! Handler for input port schedIn
|
|
void schedIn_handler(
|
|
NATIVE_INT_TYPE portNum, //!< The port number
|
|
NATIVE_UINT_TYPE order //!< The call order
|
|
);
|
|
|
|
//! Handler for input port seqRunIn
|
|
void seqRunIn_handler(
|
|
NATIVE_INT_TYPE portNum, //!< The port number
|
|
Fw::EightyCharString &filename //!< The sequence file
|
|
);
|
|
|
|
//! Handler for ping port
|
|
void pingIn_handler(
|
|
NATIVE_INT_TYPE portNum, //!< The port number
|
|
U32 key //!< Value to return to pinger
|
|
);
|
|
|
|
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
|
|
);
|
|
|
|
//! 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
|
|
);
|
|
|
|
PRIVATE:
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Private helper methods
|
|
// ----------------------------------------------------------------------
|
|
|
|
//! Load a sequence file
|
|
//! \return Success or failure
|
|
bool loadFile(
|
|
const Fw::CmdStringArg& fileName //!< The file name
|
|
);
|
|
|
|
//! Perform a Cancel command
|
|
void performCmd_Cancel(void);
|
|
|
|
//! Perform a Step command
|
|
void performCmd_Step(void);
|
|
|
|
//! 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 U32 opCode //!< The opcode
|
|
);
|
|
|
|
//! Record a sequence complete event
|
|
void sequenceComplete(void);
|
|
|
|
//! Record an error
|
|
void error(void);
|
|
|
|
//! Record an error in executing a sequence command
|
|
void commandError(
|
|
const U32 number, //!< The command number
|
|
const U32 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 ¤tTime //!< 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
|
|
NATIVE_UINT_TYPE m_timeout;
|
|
|
|
//! timeout timer
|
|
Timer m_cmdTimeoutTimer;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
#endif
|