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:
Zimri Leisher 2024-09-17 19:55:09 -05:00 committed by GitHub
parent 672538de32
commit b0716ad605
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 695 additions and 4 deletions

View File

@ -152,6 +152,7 @@ CMDPACKET
CMDREG CMDREG
cmds cmds
CMDSEQ CMDSEQ
cmdsequencer
cnt cnt
cntx cntx
cobj cobj
@ -548,6 +549,7 @@ lammertbies
LASTLOG LASTLOG
LBLOCK LBLOCK
LCHILD LCHILD
leisher
lemstarch lemstarch
lestarch lestarch
levelname levelname
@ -1212,4 +1214,5 @@ xsh
xsltproc xsltproc
xxxx xxxx
yacgen yacgen
zimri
zmq zmq

View File

@ -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}/PolyDb/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/PrmDb/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/PrmDb/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/RateGroupDriver/") 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}/StaticMemory/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TlmChan/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TlmChan/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TlmPacketizer/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TlmPacketizer/")

View File

@ -85,6 +85,9 @@ module Svc {
@ Schedule in port @ Schedule in port
async input port schedIn: Svc.Sched async input port schedIn: Svc.Sched
@ Notifies that a sequence has started running
output port seqStartOut: Svc.CmdSeqIn
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# Commands # Commands
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------

View File

@ -122,6 +122,9 @@ namespace Svc {
// Check the step mode. If it is auto, start the sequence // Check the step mode. If it is auto, start the sequence
if (AUTO == this->m_stepMode) { if (AUTO == this->m_stepMode) {
this->m_runMode = RUNNING; this->m_runMode = RUNNING;
if(this->isConnected_seqStartOut_OutputPort(0)) {
this->seqStartOut_out(0, this->m_sequence->getStringFileName());
}
this->performCmd_Step(); this->performCmd_Step();
} }
@ -159,7 +162,7 @@ namespace Svc {
//! Handler for input port seqRunIn //! Handler for input port seqRunIn
void CmdSequencerComponentImpl::seqRunIn_handler( void CmdSequencerComponentImpl::seqRunIn_handler(
NATIVE_INT_TYPE portNum, NATIVE_INT_TYPE portNum,
Fw::String &filename const Fw::StringBase& filename
) { ) {
if (!this->requireRunMode(STOPPED)) { if (!this->requireRunMode(STOPPED)) {
@ -190,6 +193,9 @@ namespace Svc {
// Check the step mode. If it is auto, start the sequence // Check the step mode. If it is auto, start the sequence
if (AUTO == this->m_stepMode) { if (AUTO == this->m_stepMode) {
this->m_runMode = RUNNING; this->m_runMode = RUNNING;
if(this->isConnected_seqStartOut_OutputPort(0)) {
this->seqStartOut_out(0, this->m_sequence->getStringFileName());
}
this->performCmd_Step(); this->performCmd_Step();
} }
@ -359,6 +365,9 @@ namespace Svc {
this->m_runMode = RUNNING; this->m_runMode = RUNNING;
this->performCmd_Step(); this->performCmd_Step();
this->log_ACTIVITY_HI_CS_CmdStarted(this->m_sequence->getLogFileName()); 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); this->cmdResponse_out(opcode, cmdSeq, Fw::CmdResponse::OK);
} }

View File

@ -235,6 +235,10 @@ namespace Svc {
//! \return The log file name //! \return The log file name
Fw::LogStringArg& getLogFileName(); Fw::LogStringArg& getLogFileName();
//! Get the normal string file name
//! \return The normal string file name
Fw::String& getStringFileName();
//! Get the sequence header //! Get the sequence header
const Header& getHeader() const; const Header& getHeader() const;
@ -277,6 +281,9 @@ namespace Svc {
//! Copy of file name for events //! Copy of file name for events
Fw::LogStringArg m_logFileName; Fw::LogStringArg m_logFileName;
//! Copy of file name for ports
Fw::String m_stringFileName;
//! Serialize buffer to hold the binary sequence data //! Serialize buffer to hold the binary sequence data
Fw::ExternalSerializeBuffer m_buffer; Fw::ExternalSerializeBuffer m_buffer;
@ -582,7 +589,7 @@ namespace Svc {
//! Handler for input port seqRunIn //! Handler for input port seqRunIn
void seqRunIn_handler( void seqRunIn_handler(
NATIVE_INT_TYPE portNum, //!< The port number NATIVE_INT_TYPE portNum, //!< The port number
Fw::String &filename //!< The sequence file const Fw::StringBase& filename //!< The sequence file
); );
//! Handler for ping port //! Handler for ping port

View File

@ -112,6 +112,7 @@ namespace Svc {
{ {
this->m_fileName = fileName; this->m_fileName = fileName;
this->m_logFileName = fileName; this->m_logFileName = fileName;
this->m_stringFileName = fileName;
} }
Fw::CmdStringArg& CmdSequencerComponentImpl::Sequence :: Fw::CmdStringArg& CmdSequencerComponentImpl::Sequence ::
@ -126,5 +127,11 @@ namespace Svc {
return this->m_logFileName; return this->m_logFileName;
} }
Fw::String& CmdSequencerComponentImpl::Sequence ::
getStringFileName()
{
return this->m_stringFileName;
}
} }

View File

@ -2,7 +2,7 @@ module Svc {
@ Port to request a sequence be run @ Port to request a sequence be run
port CmdSeqIn( port CmdSeqIn(
ref filename: Fw.String @< The sequence file filename: string size 240 @< The sequence file
) )
@ Port to cancel a sequence @ Port to cancel a sequence

View 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()

View 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

View 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
}
}

View 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

View 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

View 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 {}"

View 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

View 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
![State diagram of the SeqDispatcher](seq_dispatcher_model.png "SeqDispatcher model")
## 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 |
|---|---|---|
|---|---|---|

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View 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();
}

View 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

View 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

View File

@ -18,6 +18,9 @@ constant CmdDispatcherComponentCommandPorts = 30
@ Used for uplink/sequencer buffer/response ports @ Used for uplink/sequencer buffer/response ports
constant CmdDispatcherSequencePorts = 5 constant CmdDispatcherSequencePorts = 5
@ Used for dispatching sequences to command sequencers
constant SeqDispatcherSequencerPorts = 2
@ Used for sizing the command splitter input arrays @ Used for sizing the command splitter input arrays
constant CmdSplitterPorts = CmdDispatcherSequencePorts constant CmdSplitterPorts = CmdDispatcherSequencePorts

View File

@ -53,7 +53,7 @@ number of components.
| CmdDispatcherSequencePorts | Number of incoming ports to command dispatcher, e.g. uplink and command sequencer | 5 | Positive integer | | 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 | | 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 | | 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 ## FpConfig.h