mirror of
https://github.com/nasa/fprime.git
synced 2025-12-10 17:47:10 -06:00
Add sequence dispatcher component (#2731)
* Add sequence dispatcher component * Add seq start port to cmd sequencer * Update author names and some include paths * Get fully compiling, move consts/enums to correct places, check for connections on init * Add spelling exceptions * Get unit tests almost compiling... * Fix string type in port, call component init in test * Fix unit test compilation errors and assertions * Switch back to using StringBase * Switch to FwIndexType, remove textLogIn * UpperCamel events, add warning for unexpected seq start * remove init method, add check for connected to getNextAvailableIdx * Update sdd, change event from low to high, static cast a portnum * Add state diagram, add more warnings, fix wrong header types, use assert instead of warning for runSeq --------- Co-authored-by: Zimri Leisher <zimri.leisher@fireflyspace.com>
This commit is contained in:
parent
672538de32
commit
b0716ad605
3
.github/actions/spelling/expect.txt
vendored
3
.github/actions/spelling/expect.txt
vendored
@ -152,6 +152,7 @@ CMDPACKET
|
||||
CMDREG
|
||||
cmds
|
||||
CMDSEQ
|
||||
cmdsequencer
|
||||
cnt
|
||||
cntx
|
||||
cobj
|
||||
@ -548,6 +549,7 @@ lammertbies
|
||||
LASTLOG
|
||||
LBLOCK
|
||||
LCHILD
|
||||
leisher
|
||||
lemstarch
|
||||
lestarch
|
||||
levelname
|
||||
@ -1212,4 +1214,5 @@ xsh
|
||||
xsltproc
|
||||
xxxx
|
||||
yacgen
|
||||
zimri
|
||||
zmq
|
||||
@ -42,6 +42,7 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/PassiveRateGroup")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/PolyDb/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/PrmDb/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/RateGroupDriver/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/SeqDispatcher/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/StaticMemory/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TlmChan/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TlmPacketizer/")
|
||||
|
||||
@ -85,6 +85,9 @@ module Svc {
|
||||
@ Schedule in port
|
||||
async input port schedIn: Svc.Sched
|
||||
|
||||
@ Notifies that a sequence has started running
|
||||
output port seqStartOut: Svc.CmdSeqIn
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Commands
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
@ -122,6 +122,9 @@ namespace Svc {
|
||||
// Check the step mode. If it is auto, start the sequence
|
||||
if (AUTO == this->m_stepMode) {
|
||||
this->m_runMode = RUNNING;
|
||||
if(this->isConnected_seqStartOut_OutputPort(0)) {
|
||||
this->seqStartOut_out(0, this->m_sequence->getStringFileName());
|
||||
}
|
||||
this->performCmd_Step();
|
||||
}
|
||||
|
||||
@ -159,7 +162,7 @@ namespace Svc {
|
||||
//! Handler for input port seqRunIn
|
||||
void CmdSequencerComponentImpl::seqRunIn_handler(
|
||||
NATIVE_INT_TYPE portNum,
|
||||
Fw::String &filename
|
||||
const Fw::StringBase& filename
|
||||
) {
|
||||
|
||||
if (!this->requireRunMode(STOPPED)) {
|
||||
@ -190,6 +193,9 @@ namespace Svc {
|
||||
// Check the step mode. If it is auto, start the sequence
|
||||
if (AUTO == this->m_stepMode) {
|
||||
this->m_runMode = RUNNING;
|
||||
if(this->isConnected_seqStartOut_OutputPort(0)) {
|
||||
this->seqStartOut_out(0, this->m_sequence->getStringFileName());
|
||||
}
|
||||
this->performCmd_Step();
|
||||
}
|
||||
|
||||
@ -359,6 +365,9 @@ namespace Svc {
|
||||
this->m_runMode = RUNNING;
|
||||
this->performCmd_Step();
|
||||
this->log_ACTIVITY_HI_CS_CmdStarted(this->m_sequence->getLogFileName());
|
||||
if(this->isConnected_seqStartOut_OutputPort(0)) {
|
||||
this->seqStartOut_out(0, this->m_sequence->getStringFileName());
|
||||
}
|
||||
this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
|
||||
}
|
||||
|
||||
|
||||
@ -235,6 +235,10 @@ namespace Svc {
|
||||
//! \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;
|
||||
|
||||
@ -277,6 +281,9 @@ namespace Svc {
|
||||
//! 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;
|
||||
|
||||
@ -582,7 +589,7 @@ namespace Svc {
|
||||
//! Handler for input port seqRunIn
|
||||
void seqRunIn_handler(
|
||||
NATIVE_INT_TYPE portNum, //!< The port number
|
||||
Fw::String &filename //!< The sequence file
|
||||
const Fw::StringBase& filename //!< The sequence file
|
||||
);
|
||||
|
||||
//! Handler for ping port
|
||||
|
||||
@ -112,6 +112,7 @@ namespace Svc {
|
||||
{
|
||||
this->m_fileName = fileName;
|
||||
this->m_logFileName = fileName;
|
||||
this->m_stringFileName = fileName;
|
||||
}
|
||||
|
||||
Fw::CmdStringArg& CmdSequencerComponentImpl::Sequence ::
|
||||
@ -126,5 +127,11 @@ namespace Svc {
|
||||
return this->m_logFileName;
|
||||
}
|
||||
|
||||
Fw::String& CmdSequencerComponentImpl::Sequence ::
|
||||
getStringFileName()
|
||||
{
|
||||
return this->m_stringFileName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ module Svc {
|
||||
|
||||
@ Port to request a sequence be run
|
||||
port CmdSeqIn(
|
||||
ref filename: Fw.String @< The sequence file
|
||||
filename: string size 240 @< The sequence file
|
||||
)
|
||||
|
||||
@ Port to cancel a sequence
|
||||
|
||||
24
Svc/SeqDispatcher/CMakeLists.txt
Normal file
24
Svc/SeqDispatcher/CMakeLists.txt
Normal file
@ -0,0 +1,24 @@
|
||||
####
|
||||
# F prime CMakeLists.txt:
|
||||
#
|
||||
# SOURCE_FILES: combined list of source and autocoding files
|
||||
# MOD_DEPS: (optional) module dependencies
|
||||
# UT_SOURCE_FILES: list of source files for unit tests
|
||||
#
|
||||
####
|
||||
set(SOURCE_FILES
|
||||
"${CMAKE_CURRENT_LIST_DIR}/SeqDispatcher.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/SeqDispatcher.cpp"
|
||||
)
|
||||
|
||||
register_fprime_module()
|
||||
|
||||
### UTS ###
|
||||
set(UT_AUTO_HELPERS ON)
|
||||
|
||||
set(UT_SOURCE_FILES
|
||||
"${FPRIME_FRAMEWORK_PATH}/Svc/SeqDispatcher/SeqDispatcher.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/test/ut/SeqDispatcherTester.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/test/ut/SeqDispatcherTestMain.cpp"
|
||||
)
|
||||
register_fprime_ut()
|
||||
186
Svc/SeqDispatcher/SeqDispatcher.cpp
Normal file
186
Svc/SeqDispatcher/SeqDispatcher.cpp
Normal file
@ -0,0 +1,186 @@
|
||||
// ======================================================================
|
||||
// \title SeqDispatcher.cpp
|
||||
// \author zimri.leisher
|
||||
// \brief cpp file for SeqDispatcher component implementation class
|
||||
// ======================================================================
|
||||
|
||||
#include <Svc/SeqDispatcher/SeqDispatcher.hpp>
|
||||
|
||||
namespace Svc {
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction, initialization, and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
SeqDispatcher ::SeqDispatcher(const char* const compName)
|
||||
: SeqDispatcherComponentBase(compName) {}
|
||||
|
||||
SeqDispatcher ::~SeqDispatcher() {}
|
||||
|
||||
FwIndexType SeqDispatcher::getNextAvailableSequencerIdx() {
|
||||
for (FwIndexType i = 0; i < SeqDispatcherSequencerPorts; i++) {
|
||||
if (this->isConnected_seqRunOut_OutputPort(i) &&
|
||||
this->m_entryTable[i].state == SeqDispatcher_CmdSequencerState::AVAILABLE) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void SeqDispatcher::runSequence(FwIndexType sequencerIdx,
|
||||
const Fw::StringBase& fileName,
|
||||
Fw::Wait block) {
|
||||
// this function is only designed for internal usage
|
||||
// we can guarantee it cannot be called with input that would fail
|
||||
FW_ASSERT(sequencerIdx >= 0 && sequencerIdx < SeqDispatcherSequencerPorts,
|
||||
sequencerIdx);
|
||||
FW_ASSERT(this->isConnected_seqRunOut_OutputPort(sequencerIdx));
|
||||
FW_ASSERT(this->m_entryTable[sequencerIdx].state ==
|
||||
SeqDispatcher_CmdSequencerState::AVAILABLE,
|
||||
this->m_entryTable[sequencerIdx].state);
|
||||
|
||||
if (block == Fw::Wait::NO_WAIT) {
|
||||
this->m_entryTable[sequencerIdx].state =
|
||||
SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_NO_BLOCK;
|
||||
} else {
|
||||
this->m_entryTable[sequencerIdx].state =
|
||||
SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_BLOCK;
|
||||
}
|
||||
|
||||
this->m_sequencersAvailable--;
|
||||
this->tlmWrite_sequencersAvailable(this->m_sequencersAvailable);
|
||||
this->m_entryTable[sequencerIdx].sequenceRunning = fileName;
|
||||
|
||||
this->m_dispatchedCount++;
|
||||
this->tlmWrite_dispatchedCount(this->m_dispatchedCount);
|
||||
this->seqRunOut_out(sequencerIdx,
|
||||
this->m_entryTable[sequencerIdx].sequenceRunning);
|
||||
}
|
||||
|
||||
void SeqDispatcher::seqStartIn_handler(
|
||||
NATIVE_INT_TYPE portNum, //!< The port number
|
||||
const Fw::StringBase& fileName //!< The sequence file name
|
||||
) {
|
||||
FW_ASSERT(portNum >= 0 && portNum < SeqDispatcherSequencerPorts, portNum);
|
||||
if (this->m_entryTable[portNum].state ==
|
||||
SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_BLOCK ||
|
||||
this->m_entryTable[portNum].state ==
|
||||
SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_NO_BLOCK) {
|
||||
// we were aware of this sequencer running a sequence
|
||||
if (this->m_entryTable[portNum].sequenceRunning != fileName) {
|
||||
// uh oh. entry table is wrong
|
||||
// let's just update it to be correct. nothing we can do about
|
||||
// it except raise a warning and update our state
|
||||
this->log_WARNING_HI_ConflictingSequenceStarted(static_cast<U16>(portNum), fileName, this->m_entryTable[portNum].sequenceRunning);
|
||||
this->m_entryTable[portNum].sequenceRunning = fileName;
|
||||
}
|
||||
} else {
|
||||
// we were not aware that this sequencer was running. ground must have
|
||||
// directly commanded that specific sequencer
|
||||
|
||||
// warn because this may be unintentional
|
||||
this->log_WARNING_LO_UnexpectedSequenceStarted(static_cast<U16>(portNum), fileName);
|
||||
|
||||
// update the state
|
||||
this->m_entryTable[portNum].state =
|
||||
SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_NO_BLOCK;
|
||||
this->m_entryTable[portNum].sequenceRunning = fileName;
|
||||
this->m_sequencersAvailable--;
|
||||
this->tlmWrite_sequencersAvailable(this->m_sequencersAvailable);
|
||||
}
|
||||
}
|
||||
|
||||
void SeqDispatcher::seqDoneIn_handler(
|
||||
NATIVE_INT_TYPE portNum, //!< The port number
|
||||
FwOpcodeType opCode, //!< Command Op Code
|
||||
U32 cmdSeq, //!< Command Sequence
|
||||
const Fw::CmdResponse& response //!< The command response argument
|
||||
) {
|
||||
FW_ASSERT(portNum >= 0 && portNum < SeqDispatcherSequencerPorts, portNum);
|
||||
if (this->m_entryTable[portNum].state !=
|
||||
SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_BLOCK &&
|
||||
this->m_entryTable[portNum].state !=
|
||||
SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_NO_BLOCK) {
|
||||
// this sequencer was not running a sequence that we were aware of.
|
||||
|
||||
// we should have caught this in seqStartIn and updated the state
|
||||
// accordingly, but somehow we didn't? very sad and shouldn't happen
|
||||
|
||||
// anyways, don't have to do anything cuz now that this seq we didn't know
|
||||
// about is done, the sequencer is available again (which is its current
|
||||
// state in our internal entry table already)
|
||||
this->log_WARNING_LO_UnknownSequenceFinished(static_cast<U16>(portNum));
|
||||
} else {
|
||||
// ok, a sequence has finished that we knew about
|
||||
if (this->m_entryTable[portNum].state ==
|
||||
SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_BLOCK) {
|
||||
// we need to give a cmd response cuz some other sequence is being blocked
|
||||
// by this
|
||||
this->cmdResponse_out(this->m_entryTable[portNum].opCode,
|
||||
this->m_entryTable[portNum].cmdSeq, response);
|
||||
|
||||
if (response == Fw::CmdResponse::EXECUTION_ERROR) {
|
||||
// dispatched sequence errored
|
||||
this->m_errorCount++;
|
||||
this->tlmWrite_errorCount(this->m_errorCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// all command responses mean the sequence is no longer running
|
||||
// so component should be available
|
||||
this->m_entryTable[portNum].state = SeqDispatcher_CmdSequencerState::AVAILABLE;
|
||||
this->m_entryTable[portNum].sequenceRunning = "<no seq>";
|
||||
this->m_sequencersAvailable++;
|
||||
this->tlmWrite_sequencersAvailable(this->m_sequencersAvailable);
|
||||
}
|
||||
|
||||
//! Handler for input port seqRunIn
|
||||
void SeqDispatcher::seqRunIn_handler(NATIVE_INT_TYPE portNum,
|
||||
const Fw::StringBase& fileName) {
|
||||
FwIndexType idx = this->getNextAvailableSequencerIdx();
|
||||
// no available sequencers
|
||||
if (idx == -1) {
|
||||
this->log_WARNING_HI_NoAvailableSequencers();
|
||||
return;
|
||||
}
|
||||
|
||||
this->runSequence(idx, fileName, Fw::Wait::NO_WAIT);
|
||||
}
|
||||
// ----------------------------------------------------------------------
|
||||
// Command handler implementations
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void SeqDispatcher ::RUN_cmdHandler(const FwOpcodeType opCode,
|
||||
const U32 cmdSeq,
|
||||
const Fw::CmdStringArg& fileName,
|
||||
Fw::Wait block) {
|
||||
FwIndexType idx = this->getNextAvailableSequencerIdx();
|
||||
// no available sequencers
|
||||
if (idx == -1) {
|
||||
this->log_WARNING_HI_NoAvailableSequencers();
|
||||
this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
this->runSequence(idx, fileName, block);
|
||||
|
||||
if (block == Fw::Wait::NO_WAIT) {
|
||||
// return instantly
|
||||
this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
|
||||
} else {
|
||||
// otherwise don't return a response yet. just save the opCode and cmdSeq
|
||||
// so we can return a response later
|
||||
this->m_entryTable[idx].opCode = opCode;
|
||||
this->m_entryTable[idx].cmdSeq = cmdSeq;
|
||||
}
|
||||
}
|
||||
|
||||
void SeqDispatcher::LOG_STATUS_cmdHandler(
|
||||
const FwOpcodeType opCode, /*!< The opcode*/
|
||||
const U32 cmdSeq) { /*!< The command sequence number*/
|
||||
for(FwIndexType idx = 0; idx < SeqDispatcherSequencerPorts; idx++) {
|
||||
this->log_ACTIVITY_LO_LogSequencerStatus(static_cast<U16>(idx), this->m_entryTable[idx].state, Fw::LogStringArg(this->m_entryTable[idx].sequenceRunning));
|
||||
}
|
||||
}
|
||||
} // end namespace components
|
||||
55
Svc/SeqDispatcher/SeqDispatcher.fpp
Normal file
55
Svc/SeqDispatcher/SeqDispatcher.fpp
Normal file
@ -0,0 +1,55 @@
|
||||
module Svc {
|
||||
@ Dispatches command sequences to available command sequencers
|
||||
active component SeqDispatcher {
|
||||
|
||||
enum CmdSequencerState {
|
||||
AVAILABLE = 0
|
||||
RUNNING_SEQUENCE_BLOCK = 1
|
||||
RUNNING_SEQUENCE_NO_BLOCK = 2
|
||||
}
|
||||
|
||||
include "SeqDispatcherCommands.fppi"
|
||||
include "SeqDispatcherTelemetry.fppi"
|
||||
include "SeqDispatcherEvents.fppi"
|
||||
|
||||
@ Dispatches a sequence to the first available command sequencer
|
||||
async input port seqRunIn: Svc.CmdSeqIn
|
||||
|
||||
output port seqRunOut: [SeqDispatcherSequencerPorts] Svc.CmdSeqIn
|
||||
|
||||
@ Called by a command sequencer whenever it has finished any sequence
|
||||
async input port seqDoneIn: [SeqDispatcherSequencerPorts] Fw.CmdResponse
|
||||
|
||||
@ Called by cmdsequencer whenever it starts any sequence
|
||||
async input port seqStartIn: [SeqDispatcherSequencerPorts] Svc.CmdSeqIn
|
||||
|
||||
match seqRunOut with seqDoneIn
|
||||
|
||||
match seqRunOut with seqStartIn
|
||||
|
||||
###############################################################################
|
||||
# Standard AC Ports: Required for Channels, Events, Commands, and Parameters #
|
||||
###############################################################################
|
||||
@ Port for requesting the current time
|
||||
time get port timeCaller
|
||||
|
||||
@ Port for sending command registrations
|
||||
command reg port cmdRegOut
|
||||
|
||||
@ Port for receiving commands
|
||||
command recv port cmdIn
|
||||
|
||||
@ Port for sending command responses
|
||||
command resp port cmdResponseOut
|
||||
|
||||
@ Port for sending textual representation of events
|
||||
text event port logTextOut
|
||||
|
||||
@ Port for sending events to downlink
|
||||
event port logOut
|
||||
|
||||
@ Port for sending telemetry channels to downlink
|
||||
telemetry port tlmOut
|
||||
|
||||
}
|
||||
}
|
||||
95
Svc/SeqDispatcher/SeqDispatcher.hpp
Normal file
95
Svc/SeqDispatcher/SeqDispatcher.hpp
Normal file
@ -0,0 +1,95 @@
|
||||
// ======================================================================
|
||||
// \title SeqDispatcher.hpp
|
||||
// \author zimri.leisher
|
||||
// \brief hpp file for SeqDispatcher component implementation class
|
||||
// ======================================================================
|
||||
|
||||
#ifndef SeqDispatcher_HPP
|
||||
#define SeqDispatcher_HPP
|
||||
|
||||
#include "Svc/SeqDispatcher/SeqDispatcherComponentAc.hpp"
|
||||
#include "Svc/SeqDispatcher/SeqDispatcher_CmdSequencerStateEnumAc.hpp"
|
||||
#include "FppConstantsAc.hpp"
|
||||
#include "Fw/Types/WaitEnumAc.hpp"
|
||||
#include "Fw/Types/StringBase.hpp"
|
||||
|
||||
namespace Svc {
|
||||
|
||||
class SeqDispatcher : public SeqDispatcherComponentBase {
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction, initialization, and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Construct object SeqDispatcher
|
||||
//!
|
||||
SeqDispatcher(const char* const compName /*!< The component name*/
|
||||
);
|
||||
|
||||
//! Destroy object SeqDispatcher
|
||||
//!
|
||||
~SeqDispatcher();
|
||||
|
||||
PROTECTED:
|
||||
|
||||
//! Handler for input port seqDoneIn
|
||||
void
|
||||
seqDoneIn_handler(NATIVE_INT_TYPE portNum, //!< The port number
|
||||
FwOpcodeType opCode, //!< Command Op Code
|
||||
U32 cmdSeq, //!< Command Sequence
|
||||
const Fw::CmdResponse& response //!< The command response argument
|
||||
);
|
||||
|
||||
//! Handler for input port seqStartIn
|
||||
void seqStartIn_handler(NATIVE_INT_TYPE portNum, //!< The port number
|
||||
const Fw::StringBase& fileName //!< The sequence file
|
||||
);
|
||||
|
||||
//! Handler for input port seqRunIn
|
||||
void seqRunIn_handler(NATIVE_INT_TYPE portNum, //!< The port number
|
||||
const Fw::StringBase& fileName //!< The sequence file
|
||||
);
|
||||
|
||||
PRIVATE:
|
||||
|
||||
// number of sequences dispatched (successful or otherwise)
|
||||
U32 m_dispatchedCount = 0;
|
||||
// number of errors from dispatched sequences (CmdResponse::EXECUTION_ERROR)
|
||||
U32 m_errorCount = 0;
|
||||
// number of sequencers in state AVAILABLE
|
||||
U32 m_sequencersAvailable = SeqDispatcherSequencerPorts;
|
||||
|
||||
struct DispatchEntry {
|
||||
FwOpcodeType opCode; //!< opcode of entry
|
||||
U32 cmdSeq;
|
||||
// store the state of each sequencer
|
||||
SeqDispatcher_CmdSequencerState state;
|
||||
// store the sequence currently running for each sequencer
|
||||
Fw::String sequenceRunning = "<no seq>";
|
||||
} m_entryTable[SeqDispatcherSequencerPorts]; //!< table of dispatch
|
||||
//!< entries
|
||||
|
||||
FwIndexType getNextAvailableSequencerIdx();
|
||||
|
||||
void runSequence(FwIndexType sequencerIdx,
|
||||
const Fw::StringBase& fileName,
|
||||
Fw::Wait block);
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Command handler implementations
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Implementation for RUN command handler
|
||||
//!
|
||||
void RUN_cmdHandler(const FwOpcodeType opCode, /*!< The opcode*/
|
||||
const U32 cmdSeq, /*!< The command sequence number*/
|
||||
const Fw::CmdStringArg& fileName, /*!< The name of the sequence file*/
|
||||
Fw::Wait block);
|
||||
|
||||
void LOG_STATUS_cmdHandler(const FwOpcodeType opCode, /*!< The opcode*/
|
||||
const U32 cmdSeq); /*!< The command sequence number*/
|
||||
};
|
||||
|
||||
} // end namespace components
|
||||
|
||||
#endif
|
||||
9
Svc/SeqDispatcher/SeqDispatcherCommands.fppi
Normal file
9
Svc/SeqDispatcher/SeqDispatcherCommands.fppi
Normal file
@ -0,0 +1,9 @@
|
||||
@ Dispatches a sequence to the first available sequencer
|
||||
async command RUN(
|
||||
fileName: string size 240 @< The name of the sequence file
|
||||
$block: Fw.Wait @< Return command status when complete or not
|
||||
) \
|
||||
opcode 0
|
||||
|
||||
@ Logs via Events the state of each connected command sequencer
|
||||
async command LOG_STATUS() opcode 1
|
||||
38
Svc/SeqDispatcher/SeqDispatcherEvents.fppi
Normal file
38
Svc/SeqDispatcher/SeqDispatcherEvents.fppi
Normal file
@ -0,0 +1,38 @@
|
||||
event InvalidSequencer(
|
||||
idx: U16
|
||||
) \
|
||||
severity warning high \
|
||||
format "Invalid sequence index {}"
|
||||
|
||||
event NoAvailableSequencers() \
|
||||
severity warning high \
|
||||
format "No available cmd sequencers to dispatch a sequence to"
|
||||
|
||||
event UnknownSequenceFinished(
|
||||
idx: U16
|
||||
) \
|
||||
severity warning low \
|
||||
format "Sequencer {} completed a sequence with no matching start notification"
|
||||
|
||||
event ConflictingSequenceStarted(
|
||||
idx: U16,
|
||||
newSequence: string size 240,
|
||||
sequenceInInternalState: string size 240
|
||||
) \
|
||||
severity warning high \
|
||||
format "Sequencer {} started a sequence {} while still running {}"
|
||||
|
||||
event UnexpectedSequenceStarted(
|
||||
idx: U16,
|
||||
newSequence: string size 240
|
||||
) \
|
||||
severity warning low \
|
||||
format "Sequencer {} was externally commanded to start a sequence {}"
|
||||
|
||||
event LogSequencerStatus(
|
||||
idx: U16
|
||||
state: CmdSequencerState
|
||||
filename: string size 240
|
||||
) \
|
||||
severity activity low \
|
||||
format "Sequencer {} with state {} is running file {}"
|
||||
8
Svc/SeqDispatcher/SeqDispatcherTelemetry.fppi
Normal file
8
Svc/SeqDispatcher/SeqDispatcherTelemetry.fppi
Normal file
@ -0,0 +1,8 @@
|
||||
@ Number of sequences dispatched
|
||||
telemetry dispatchedCount: U32
|
||||
@ Number of sequences dispatched that returned an error. Note: if a sequence
|
||||
@ was run in non-blocking mode, even if the sequence errors out, this error
|
||||
@ count will never increase
|
||||
telemetry errorCount: U32
|
||||
@ Number of sequencers in an available state
|
||||
telemetry sequencersAvailable: U32
|
||||
51
Svc/SeqDispatcher/docs/sdd.md
Normal file
51
Svc/SeqDispatcher/docs/sdd.md
Normal file
@ -0,0 +1,51 @@
|
||||
# components::SeqDispatcher
|
||||
|
||||
Dispatches command sequences to available command sequencers, allowing the spacecraft controllers to run multiple sequences at once without having to manually manage which `CmdSequencer`s those sequences run on.
|
||||
|
||||
### Usage
|
||||
* Call the `RUN` command just like you would call it on a `CmdSequencer`
|
||||
* If any connected `CmdSequencer` is available, it will route the sequence to the first one it finds
|
||||
* `RUN` can be made blocking or non-blocking, just like `CmdSequencer`'s `RUN`
|
||||
|
||||
## State diagram
|
||||

|
||||
|
||||
## Port Descriptions
|
||||
|Type| Name | Description |
|
||||
|async input|seqRunIn|Equivalent to the RUN cmd, dispatches a sequence to the first available sequencer|
|
||||
|output|seqRunOut|This is used by the SeqDispatcher to send sequence run calls to sequencers|
|
||||
|async input|seqDoneIn|Called by a command sequencer whenever it has finished any sequence|
|
||||
|async input|seqStartIn|Called by a command sequencer whenever it starts any sequence|
|
||||
|
||||
## Commands
|
||||
| Name | Description |
|
||||
|RUN|Dispatches a sequence to the first available sequencer|
|
||||
|LOG_STATUS|Logs via Events the state of each connected command sequencer|
|
||||
|
||||
## Events
|
||||
| Name | Description |
|
||||
|InvalidSequencer|The given sequencer index is invalid for an unspecified reason|
|
||||
|NoAvailableSequencers|There are no available sequencers to dispatch a sequence to|
|
||||
|UnknownSequenceFinished|We received a call to seqDoneIn that didn't have a corresponding seqStartIn call|
|
||||
|UnexpectedSequenceStarted|We received a call to seqStartIn but we didn't receive a call to seqDoneIn before that|
|
||||
|LogSequencerStatus|Shows the current state and sequence filename for a particular sequencer. Produced by the LOG_STATUS command|
|
||||
|
||||
|
||||
|
||||
## Telemetry
|
||||
| Name | Description |
|
||||
|dispatchedCount|Number of sequences dispatched|
|
||||
|errorCount|Number of sequences dispatched that returned an error. Note: if a sequence was run in non-blocking mode, even if the sequence errors out, this error count will never increase|
|
||||
|sequencersAvailable|Number of sequencers ready to run a sequence|
|
||||
|
||||
## Unit Tests
|
||||
Add unit test descriptions in the chart below
|
||||
| Name | Description |
|
||||
|testDispatch|Tests the basic dispatch functionality of the `SeqDispatcher`|
|
||||
|testLogStatus|Tests the LOG_STATUS command|
|
||||
|
||||
## Requirements
|
||||
Add requirements in the chart below
|
||||
| Name | Description | Validation |
|
||||
|---|---|---|
|
||||
|---|---|---|
|
||||
BIN
Svc/SeqDispatcher/docs/seq_dispatcher_model.png
Normal file
BIN
Svc/SeqDispatcher/docs/seq_dispatcher_model.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
21
Svc/SeqDispatcher/test/ut/SeqDispatcherTestMain.cpp
Normal file
21
Svc/SeqDispatcher/test/ut/SeqDispatcherTestMain.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
// ----------------------------------------------------------------------
|
||||
// TestMain.cpp
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
#include "SeqDispatcherTester.hpp"
|
||||
|
||||
TEST(Nominal, testDispatch) {
|
||||
Svc::SeqDispatcherTester tester;
|
||||
tester.testDispatch();
|
||||
}
|
||||
|
||||
TEST(Nominal, testLogStatus) {
|
||||
Svc::SeqDispatcherTester tester;
|
||||
tester.testLogStatus();
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
92
Svc/SeqDispatcher/test/ut/SeqDispatcherTester.cpp
Normal file
92
Svc/SeqDispatcher/test/ut/SeqDispatcherTester.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
// ======================================================================
|
||||
// \title SeqDispatcher.hpp
|
||||
// \author zimri.leisher
|
||||
// \brief cpp file for SeqDispatcher test harness implementation class
|
||||
// ======================================================================
|
||||
|
||||
#include "SeqDispatcherTester.hpp"
|
||||
|
||||
namespace Svc{
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
SeqDispatcherTester ::SeqDispatcherTester()
|
||||
: SeqDispatcherGTestBase("SeqDispatcherTester", SeqDispatcherTester::MAX_HISTORY_SIZE),
|
||||
component("SeqDispatcher") {
|
||||
this->connectPorts();
|
||||
this->initComponents();
|
||||
}
|
||||
|
||||
SeqDispatcherTester ::~SeqDispatcherTester() {}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Tests
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void SeqDispatcherTester ::testDispatch() {
|
||||
// test that it fails when we dispatch too many sequences
|
||||
for (int i = 0; i < SeqDispatcherSequencerPorts; i++) {
|
||||
sendCmd_RUN(0, 0, Fw::String("test"), Fw::Wait::WAIT);
|
||||
this->component.doDispatch();
|
||||
// no response cuz blocking
|
||||
ASSERT_CMD_RESPONSE_SIZE(0);
|
||||
ASSERT_EVENTS_SIZE(0);
|
||||
}
|
||||
ASSERT_TLM_sequencersAvailable(SeqDispatcherSequencerPorts - 1, 0);
|
||||
this->clearHistory();
|
||||
// all sequencers should be busy
|
||||
sendCmd_RUN(0, 0, Fw::String("test"), Fw::Wait::WAIT);
|
||||
this->component.doDispatch();
|
||||
ASSERT_CMD_RESPONSE_SIZE(1);
|
||||
ASSERT_CMD_RESPONSE(0, SeqDispatcher::OPCODE_RUN, 0,
|
||||
Fw::CmdResponse::EXECUTION_ERROR);
|
||||
|
||||
this->clearHistory();
|
||||
|
||||
this->invoke_to_seqDoneIn(0, 0, 0, Fw::CmdResponse::OK);
|
||||
this->component.doDispatch();
|
||||
ASSERT_EVENTS_SIZE(0);
|
||||
// we should have gotten a cmd response now
|
||||
ASSERT_CMD_RESPONSE_SIZE(1);
|
||||
ASSERT_CMD_RESPONSE(0, SeqDispatcher::OPCODE_RUN, 0, Fw::CmdResponse::OK);
|
||||
|
||||
this->clearHistory();
|
||||
// ok now we should be able to send another sequence
|
||||
// let's test non blocking now
|
||||
sendCmd_RUN(0, 0, Fw::String("test"), Fw::Wait::NO_WAIT);
|
||||
this->component.doDispatch();
|
||||
|
||||
// should immediately return
|
||||
ASSERT_EVENTS_SIZE(0);
|
||||
ASSERT_CMD_RESPONSE_SIZE(1);
|
||||
ASSERT_CMD_RESPONSE(0, SeqDispatcher::OPCODE_RUN, 0, Fw::CmdResponse::OK);
|
||||
this->clearHistory();
|
||||
|
||||
// ok now check that if a sequence errors on block it will return error
|
||||
this->invoke_to_seqDoneIn(1, 0, 0, Fw::CmdResponse::EXECUTION_ERROR);
|
||||
this->component.doDispatch();
|
||||
ASSERT_EVENTS_SIZE(0);
|
||||
ASSERT_CMD_RESPONSE_SIZE(1);
|
||||
ASSERT_CMD_RESPONSE(0, SeqDispatcher::OPCODE_RUN, 0, Fw::CmdResponse::EXECUTION_ERROR);
|
||||
|
||||
}
|
||||
|
||||
void SeqDispatcherTester::testLogStatus() {
|
||||
this->sendCmd_RUN(0,0, Fw::String("test"), Fw::Wait::WAIT);
|
||||
this->component.doDispatch();
|
||||
this->sendCmd_LOG_STATUS(0,0);
|
||||
this->component.doDispatch();
|
||||
ASSERT_EVENTS_SIZE(SeqDispatcherSequencerPorts);
|
||||
ASSERT_EVENTS_LogSequencerStatus(0, 0, SeqDispatcher_CmdSequencerState::RUNNING_SEQUENCE_BLOCK, "test");
|
||||
}
|
||||
|
||||
void SeqDispatcherTester::seqRunOut_handler(
|
||||
FwIndexType portNum, //!< The port number
|
||||
const Fw::StringBase& filename //!< The sequence file
|
||||
) {
|
||||
this->pushFromPortEntry_seqRunOut(filename);
|
||||
}
|
||||
|
||||
} // end namespace components
|
||||
79
Svc/SeqDispatcher/test/ut/SeqDispatcherTester.hpp
Normal file
79
Svc/SeqDispatcher/test/ut/SeqDispatcherTester.hpp
Normal file
@ -0,0 +1,79 @@
|
||||
// ======================================================================
|
||||
// \title SeqDispatcher/test/ut/Tester.hpp
|
||||
// \author zimri.leisher
|
||||
// \brief hpp file for SeqDispatcher test harness implementation class
|
||||
// ======================================================================
|
||||
|
||||
#ifndef TESTER_HPP
|
||||
#define TESTER_HPP
|
||||
|
||||
#include "SeqDispatcherGTestBase.hpp"
|
||||
#include "Svc/SeqDispatcher/SeqDispatcher.hpp"
|
||||
|
||||
namespace Svc{
|
||||
|
||||
class SeqDispatcherTester : public SeqDispatcherGTestBase {
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public:
|
||||
// Maximum size of histories storing events, telemetry, and port outputs
|
||||
static const NATIVE_INT_TYPE MAX_HISTORY_SIZE = 10;
|
||||
// Instance ID supplied to the component instance under test
|
||||
static const NATIVE_INT_TYPE TEST_INSTANCE_ID = 0;
|
||||
// Queue depth supplied to component instance under test
|
||||
static const NATIVE_INT_TYPE TEST_INSTANCE_QUEUE_DEPTH = 10;
|
||||
|
||||
//! Construct object SeqDispatcherTester
|
||||
//!
|
||||
SeqDispatcherTester();
|
||||
|
||||
//! Destroy object SeqDispatcherTester
|
||||
//!
|
||||
~SeqDispatcherTester();
|
||||
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Tests
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void testDispatch();
|
||||
void testLogStatus();
|
||||
|
||||
private:
|
||||
// ----------------------------------------------------------------------
|
||||
// Handlers for typed from ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void seqRunOut_handler(
|
||||
FwIndexType portNum, //!< The port number
|
||||
const Fw::StringBase& filename //!< The sequence file
|
||||
);
|
||||
|
||||
private:
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Connect ports
|
||||
//!
|
||||
void connectPorts();
|
||||
|
||||
//! Initialize components
|
||||
//!
|
||||
void initComponents();
|
||||
|
||||
private:
|
||||
// ----------------------------------------------------------------------
|
||||
// Variables
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! The component under test
|
||||
//!
|
||||
SeqDispatcher component;
|
||||
};
|
||||
|
||||
} // end namespace components
|
||||
|
||||
#endif
|
||||
@ -18,6 +18,9 @@ constant CmdDispatcherComponentCommandPorts = 30
|
||||
@ Used for uplink/sequencer buffer/response ports
|
||||
constant CmdDispatcherSequencePorts = 5
|
||||
|
||||
@ Used for dispatching sequences to command sequencers
|
||||
constant SeqDispatcherSequencerPorts = 2
|
||||
|
||||
@ Used for sizing the command splitter input arrays
|
||||
constant CmdSplitterPorts = CmdDispatcherSequencePorts
|
||||
|
||||
|
||||
@ -53,7 +53,7 @@ number of components.
|
||||
| CmdDispatcherSequencePorts | Number of incoming ports to command dispatcher, e.g. uplink and command sequencer | 5 | Positive integer |
|
||||
| RateGroupDriverRateGroupPorts | Number of rate group driver output ports. Limits total number of different rate groups | 3 | Positive integer |
|
||||
| HealthPingPorts | Number of health ping output ports. Limits number of components attached to health component | 25 | Positive integer |
|
||||
|
||||
| SeqDispatcherSequencerPorts | Number of CmdSequencers that the SeqDispatcher can dispatch sequences to | 2 | Positive integer
|
||||
|
||||
## FpConfig.h
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user