diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index a55382cce5..d608eeac1f 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -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 \ No newline at end of file diff --git a/CFDP/Checksum/Checksum.hpp b/CFDP/Checksum/Checksum.hpp index 711c3af6de..d12357354a 100644 --- a/CFDP/Checksum/Checksum.hpp +++ b/CFDP/Checksum/Checksum.hpp @@ -18,8 +18,38 @@ namespace CFDP { //! \class Checksum - //! \brief Class representing a CFDP checksum + //! \brief Class representing a 32-bit checksum as mandated by the CCSDS File + //! Delivery Protocol. //! + //! This checksum is calculated by update of an existing 32-bit value + //! with the "next" 32-bit string drawn from the file data. Beginning + //! at the start of the file, a 4-byte window moves up the file by four + //! bytes per update. The update itself replaces the existing checksum + //! with the byte-wise sum of the existing checksum and the file data + //! contained in the window. Overflows in the addition are permitted + //! and the carry discarded. + //! + //! If an update is to be made beginning at an offset into the file + //! which is not aligned to a 4-byte boundary, the window is treated + //! as beginning at the last 4-byte boundary, but is left-zero-padded. + //! Similarly, where the file data for an update ends on an unaligned + //! byte, the window extends up to the next boundary and is + //! right-zero-padded. + //! + //! ## Example + //! + //! For buffer 0xDE 0xAD 0xBE 0xEF 0xCA 0xFE and initial zero checksum: + //! + //! ------------------------------------ Update 1 + //! Window 0xDE 0xAD 0xBE 0xEF + //! Checksum 0xDEADBEEF + //! + //! ------------------------------------ Update 2 + //! Window 0xCA 0xFE + //! Checksum 0xDEADBEEF+ + //! 0xCAFE0000 + //! ---------- + //! 0xA8ABBEEF <- Final value class Checksum { public: @@ -34,16 +64,16 @@ namespace CFDP { // Construction and destruction // ---------------------------------------------------------------------- - //! Construct a fresh Checksum object + //! Construct a fresh Checksum object. Checksum(); - //! Construct a Checksum object and initialize it with a value + //! Construct a Checksum object and initialize it with a value. Checksum(const U32 value); - //! Copy a Checksum object + //! Copy a Checksum object. Checksum(const Checksum &original); - //! Destroy a Checksum object + //! Destroy a Checksum object. ~Checksum(); public: @@ -52,20 +82,25 @@ namespace CFDP { // Public instance methods // ---------------------------------------------------------------------- - //! Assign checksum to this + //! Assign checksum to this. Checksum& operator=(const Checksum& checksum); - //! Compare checksum and this for equality + //! Compare checksum and this for equality. bool operator==(const Checksum& checksum) const; - //! Compare checksum and this for inequality + //! Compare checksum and this for inequality. bool operator!=(const Checksum& checksum) const; - //! Update the checksum value by accumulating the words in the data - void update( - const U8 *const data, //!< The data - const U32 offset, //!< The offset of the start of the data, relative to the start of the file - const U32 length //!< The length of the data in bytes + //! Update the checksum value by accumulating words in the given data. + //! + //! \important The data and data-length passed to this method are specifically + //! those over which the update is made, rather than the entire + //! file. Typically, therefore, `data` will be a pointer to the + //! byte given by the offset, e.g. `&file_buffer[offset]`. + //! + void update(const U8* const data, //!< Beginning of the data over which to update. + const U32 offset, //!< Offset into the file at which the data begins. + const U32 length //!< Length of the update data in bytes. ); //! Get the checksum value diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 57c348b9f1..408a242e16 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -74,7 +74,7 @@ Contributors to the [fprime](https://github.com/nasa/fprime) repository should u F´ follows a standard git flow development model. Developers should start with a [fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) of one of the F´ repositories and then develop according to [git flow](https://docs.github.com/en/get-started/quickstart/github-flow). Remember to add an -upstream remote to your fork such that you may fetch the latest changes. +[upstream remote](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/configuring-a-remote-repository-for-a-fork) to your fork such that you may fetch the latest changes. For each contribution, developers should first fetch the latest changes from upstream. Then create a new branch off `devel` and submit back to F´ using a pull request as described above. diff --git a/Os/Posix/test/ut/PosixFileTests.cpp b/Os/Posix/test/ut/PosixFileTests.cpp index d75c5bc40f..55cc7dfc11 100644 --- a/Os/Posix/test/ut/PosixFileTests.cpp +++ b/Os/Posix/test/ut/PosixFileTests.cpp @@ -33,8 +33,8 @@ bool check_permissions(const char* path, int permission) { //! std::shared_ptr get_test_filename(bool random) { const char* filename = TEST_FILE; - char full_buffer[PATH_MAX]; - char buffer[PATH_MAX - sizeof(BASE_PATH)]; + char full_buffer[_POSIX_PATH_MAX]; + char buffer[_POSIX_PATH_MAX - sizeof(BASE_PATH)]; // When random, select random characters if (random) { filename = buffer; @@ -47,7 +47,7 @@ std::shared_ptr get_test_filename(bool random) { } buffer[i] = 0; // Terminate random string } - (void) snprintf(full_buffer, PATH_MAX, "%s/%s", BASE_PATH, filename); + (void) snprintf(full_buffer, _POSIX_PATH_MAX, "%s/%s", BASE_PATH, filename); // Create a shared pointer wrapping our filename buffer std::shared_ptr pointer(new std::string(full_buffer), std::default_delete()); return pointer; diff --git a/Os/test/ut/file/FileRules.cpp b/Os/test/ut/file/FileRules.cpp index e708ec9569..39b0399b61 100644 --- a/Os/test/ut/file/FileRules.cpp +++ b/Os/test/ut/file/FileRules.cpp @@ -231,7 +231,7 @@ void Os::Test::File::Tester::assert_file_seek(const FwSignedSizeType original_po ASSERT_EQ(this->m_shadow.position(shadow_position), Os::File::Status::OP_OK); const FwSignedSizeType expected_offset = (absolute) ? seek_desired : (original_position + seek_desired); - if (expected_offset > 0) { + if (expected_offset >= 0) { ASSERT_EQ(new_position, expected_offset); } else { ASSERT_EQ(new_position, original_position); @@ -497,7 +497,7 @@ void Os::Test::File::Tester::Preallocate::action( state.assert_file_consistent(); FileState original_file_state = state.current_file_state(); FwSignedSizeType offset = static_cast(STest::Pick::lowerUpper(0, FILE_DATA_MAXIMUM)); - FwSignedSizeType length = static_cast(STest::Pick::lowerUpper(0, FILE_DATA_MAXIMUM)); + FwSignedSizeType length = static_cast(STest::Pick::lowerUpper(1, FILE_DATA_MAXIMUM)); Os::File::Status status = state.m_file.preallocate(offset, length); ASSERT_EQ(Os::File::Status::OP_OK, status); state.shadow_preallocate(offset, length); diff --git a/Os/test/ut/file/SyntheticFileSystem.cpp b/Os/test/ut/file/SyntheticFileSystem.cpp index b81f7c6eae..5bcb59cbf4 100644 --- a/Os/test/ut/file/SyntheticFileSystem.cpp +++ b/Os/test/ut/file/SyntheticFileSystem.cpp @@ -191,7 +191,7 @@ Os::File::Status SyntheticFile::seek(const FwSignedSizeType offset, const SeekTy status = Os::File::Status::NOT_OPENED; } else { FwSignedSizeType new_offset = (absolute) ? offset : (offset + this->m_data->m_pointer); - if (new_offset > 0) { + if (new_offset >= 0) { this->m_data->m_pointer = new_offset; } else { status = Os::File::Status::INVALID_ARGUMENT; diff --git a/Ref/Top/RefPackets.xml b/Ref/Top/RefPackets.xml index a8e5204119..898a72bf16 100644 --- a/Ref/Top/RefPackets.xml +++ b/Ref/Top/RefPackets.xml @@ -72,12 +72,7 @@ - - - - - - + diff --git a/Svc/CMakeLists.txt b/Svc/CMakeLists.txt index 37de2e0110..2ab95efc03 100644 --- a/Svc/CMakeLists.txt +++ b/Svc/CMakeLists.txt @@ -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/") diff --git a/Svc/CmdSequencer/CmdSequencer.fpp b/Svc/CmdSequencer/CmdSequencer.fpp index 52df683856..df55efaa74 100644 --- a/Svc/CmdSequencer/CmdSequencer.fpp +++ b/Svc/CmdSequencer/CmdSequencer.fpp @@ -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 # ---------------------------------------------------------------------- diff --git a/Svc/CmdSequencer/CmdSequencerImpl.cpp b/Svc/CmdSequencer/CmdSequencerImpl.cpp index 7e1c84d25b..88e2018a62 100644 --- a/Svc/CmdSequencer/CmdSequencerImpl.cpp +++ b/Svc/CmdSequencer/CmdSequencerImpl.cpp @@ -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); } diff --git a/Svc/CmdSequencer/CmdSequencerImpl.hpp b/Svc/CmdSequencer/CmdSequencerImpl.hpp index 6e595000b4..655cebd4f3 100644 --- a/Svc/CmdSequencer/CmdSequencerImpl.hpp +++ b/Svc/CmdSequencer/CmdSequencerImpl.hpp @@ -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 diff --git a/Svc/CmdSequencer/Sequence.cpp b/Svc/CmdSequencer/Sequence.cpp index 235de84c13..30cc2ccb50 100644 --- a/Svc/CmdSequencer/Sequence.cpp +++ b/Svc/CmdSequencer/Sequence.cpp @@ -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; + } + } diff --git a/Svc/ComQueue/CMakeLists.txt b/Svc/ComQueue/CMakeLists.txt index c1733c668b..5e3d657fd3 100644 --- a/Svc/ComQueue/CMakeLists.txt +++ b/Svc/ComQueue/CMakeLists.txt @@ -23,4 +23,6 @@ set(UT_SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/test/ut/ComQueueTestMain.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/ComQueueTester.cpp" ) + +set(UT_AUTO_HELPERS ON) register_fprime_ut() diff --git a/Svc/ComQueue/ComQueue.cpp b/Svc/ComQueue/ComQueue.cpp index a1fb1c40c2..1a0ff4d4c8 100644 --- a/Svc/ComQueue/ComQueue.cpp +++ b/Svc/ComQueue/ComQueue.cpp @@ -131,7 +131,7 @@ void ComQueue::configure(QueueConfigurationTable queueConfig, void ComQueue::comQueueIn_handler(const NATIVE_INT_TYPE portNum, Fw::ComBuffer& data, U32 context) { // Ensure that the port number of comQueueIn is consistent with the expectation FW_ASSERT(portNum >= 0 && portNum < COM_PORT_COUNT, portNum); - this->enqueue(portNum, QueueType::COM_QUEUE, reinterpret_cast(&data), sizeof(Fw::ComBuffer)); + (void)this->enqueue(portNum, QueueType::COM_QUEUE, reinterpret_cast(&data), sizeof(Fw::ComBuffer)); } void ComQueue::buffQueueIn_handler(const NATIVE_INT_TYPE portNum, Fw::Buffer& fwBuffer) { @@ -139,7 +139,11 @@ void ComQueue::buffQueueIn_handler(const NATIVE_INT_TYPE portNum, Fw::Buffer& fw // Ensure that the port number of buffQueueIn is consistent with the expectation FW_ASSERT(portNum >= 0 && portNum < BUFFER_PORT_COUNT, portNum); FW_ASSERT(queueNum < TOTAL_PORT_COUNT); - this->enqueue(queueNum, QueueType::BUFFER_QUEUE, reinterpret_cast(&fwBuffer), sizeof(Fw::Buffer)); + bool status = + this->enqueue(queueNum, QueueType::BUFFER_QUEUE, reinterpret_cast(&fwBuffer), sizeof(Fw::Buffer)); + if (!status) { + this->deallocate_out(portNum, fwBuffer); + } } void ComQueue::comStatusIn_handler(const NATIVE_INT_TYPE portNum, Fw::Success& condition) { @@ -181,29 +185,45 @@ void ComQueue::run_handler(const NATIVE_INT_TYPE portNum, U32 context) { this->tlmWrite_buffQueueDepth(buffQueueDepth); } +// ---------------------------------------------------------------------- +// Hook implementations for typed async input ports +// ---------------------------------------------------------------------- + +void ComQueue::buffQueueIn_overflowHook(FwIndexType portNum, Fw::Buffer& fwBuffer) { + FW_ASSERT(portNum >= 0 && portNum < BUFFER_PORT_COUNT, portNum); + this->deallocate_out(portNum, fwBuffer); +} + // ---------------------------------------------------------------------- // Private helper methods // ---------------------------------------------------------------------- -void ComQueue::enqueue(const FwIndexType queueNum, QueueType queueType, const U8* data, const FwSizeType size) { +bool ComQueue::enqueue(const FwIndexType queueNum, QueueType queueType, const U8* data, const FwSizeType size) { // Enqueue the given message onto the matching queue. When no space is available then emit the queue overflow event, // set the appropriate throttle, and move on. Will assert if passed a message for a depth 0 queue. const FwSizeType expectedSize = (queueType == QueueType::COM_QUEUE) ? sizeof(Fw::ComBuffer) : sizeof(Fw::Buffer); const FwIndexType portNum = queueNum - ((queueType == QueueType::COM_QUEUE) ? 0 : COM_PORT_COUNT); + bool rvStatus = true; FW_ASSERT( expectedSize == size, static_cast(size), static_cast(expectedSize)); FW_ASSERT(portNum >= 0, portNum); Fw::SerializeStatus status = this->m_queues[queueNum].enqueue(data, size); - if (status == Fw::FW_SERIALIZE_NO_ROOM_LEFT && !this->m_throttle[queueNum]) { - this->log_WARNING_HI_QueueOverflow(queueType, static_cast(portNum)); - this->m_throttle[queueNum] = true; + if (status == Fw::FW_SERIALIZE_NO_ROOM_LEFT) { + if (!this->m_throttle[queueNum]) { + this->log_WARNING_HI_QueueOverflow(queueType, static_cast(portNum)); + this->m_throttle[queueNum] = true; + } + + rvStatus = false; } // When the component is already in READY state process the queue to send out the next available message immediately if (this->m_state == READY) { this->processQueue(); } + + return rvStatus; } void ComQueue::sendComBuffer(Fw::ComBuffer& comBuffer) { diff --git a/Svc/ComQueue/ComQueue.fpp b/Svc/ComQueue/ComQueue.fpp index 43cda64368..a45ec081a5 100644 --- a/Svc/ComQueue/ComQueue.fpp +++ b/Svc/ComQueue/ComQueue.fpp @@ -4,7 +4,7 @@ module Svc { @ Array of queue depths for Fw::Com types array ComQueueDepth = [ComQueueComPorts] U32 - + @ Array of queue depths for Fw::Buffer types array BuffQueueDepth = [ComQueueBufferPorts] U32 @@ -22,6 +22,9 @@ module Svc { @ Fw::Buffer output port output port buffQueueSend: Fw.BufferSend + @ Port for deallocating Fw::Buffer on queue overflow + output port deallocate: Fw.BufferSend + @ Port for receiving the status signal async input port comStatusIn: Fw.SuccessCondition @@ -29,7 +32,7 @@ module Svc { async input port comQueueIn: [ComQueueComPorts] Fw.Com drop @ Port array for receiving Fw::Buffers - async input port buffQueueIn: [ComQueueBufferPorts] Fw.BufferSend drop + async input port buffQueueIn: [ComQueueBufferPorts] Fw.BufferSend hook @ Port for scheduling telemetry output async input port run: Svc.Sched drop diff --git a/Svc/ComQueue/ComQueue.hpp b/Svc/ComQueue/ComQueue.hpp index 550be00c98..40e1e0bd7d 100644 --- a/Svc/ComQueue/ComQueue.hpp +++ b/Svc/ComQueue/ComQueue.hpp @@ -151,13 +151,23 @@ class ComQueue : public ComQueueComponentBase { U32 context /*!component.m_queue.getQueueSize(); + QueueType overflow_type; + NATIVE_INT_TYPE portNum; + + // fill the queue + for (NATIVE_INT_TYPE msgCount = 0; msgCount < msgCountMax; msgCount++) { + sendByQueueNumber(buffer, queueNum, portNum, overflow_type); + ASSERT_EQ(overflow_type, QueueType::BUFFER_QUEUE); + } + + // send one more to overflow the queue + sendByQueueNumber(buffer, queueNum, portNum, overflow_type); + + ASSERT_from_deallocate_SIZE(1); + ASSERT_from_deallocate(0, buffer); + + // send another + sendByQueueNumber(buffer, queueNum, portNum, overflow_type); + + ASSERT_from_deallocate_SIZE(2); + ASSERT_from_deallocate(0, buffer); + ASSERT_from_deallocate(1, buffer); + + component.cleanup(); +} + void ComQueueTester ::testReadyFirst() { U8 data[BUFFER_LENGTH] = {0xde, 0xad, 0xbe}; Fw::ComBuffer comBuffer(&data[0], sizeof(data)); @@ -283,45 +330,4 @@ void ComQueueTester ::from_comQueueSend_handler(const NATIVE_INT_TYPE portNum, F // Helper methods // ---------------------------------------------------------------------- -void ComQueueTester ::connectPorts() { - // buffQueueIn - for (NATIVE_INT_TYPE i = 0; i < ComQueue::BUFFER_PORT_COUNT; ++i) { - this->connect_to_buffQueueIn(i, this->component.get_buffQueueIn_InputPort(i)); - } - - // comQueueIn - for (NATIVE_INT_TYPE i = 0; i < ComQueue::COM_PORT_COUNT; ++i) { - this->connect_to_comQueueIn(i, this->component.get_comQueueIn_InputPort(i)); - } - - // comStatusIn - this->connect_to_comStatusIn(0, this->component.get_comStatusIn_InputPort(0)); - - // run - this->connect_to_run(0, this->component.get_run_InputPort(0)); - - // Log - this->component.set_Log_OutputPort(0, this->get_from_Log(0)); - - // LogText - this->component.set_LogText_OutputPort(0, this->get_from_LogText(0)); - - // Time - this->component.set_Time_OutputPort(0, this->get_from_Time(0)); - - // Tlm - this->component.set_Tlm_OutputPort(0, this->get_from_Tlm(0)); - - // buffQueueSend - this->component.set_buffQueueSend_OutputPort(0, this->get_from_buffQueueSend(0)); - - // comQueueSend - this->component.set_comQueueSend_OutputPort(0, this->get_from_comQueueSend(0)); -} - -void ComQueueTester ::initComponents() { - this->init(); - this->component.init(QUEUE_DEPTH, INSTANCE); -} - } // end namespace Svc diff --git a/Svc/ComQueue/test/ut/ComQueueTester.hpp b/Svc/ComQueue/test/ut/ComQueueTester.hpp index 5149edf8c8..41da0db4fb 100644 --- a/Svc/ComQueue/test/ut/ComQueueTester.hpp +++ b/Svc/ComQueue/test/ut/ComQueueTester.hpp @@ -14,6 +14,19 @@ namespace Svc { class ComQueueTester : public ComQueueGTestBase { + + public: + + // ---------------------------------------------------------------------- + // Constants + // ---------------------------------------------------------------------- + + // Instance ID supplied to the component instance under test + static const NATIVE_INT_TYPE TEST_INSTANCE_ID = 0; + + // Queue depth supplied to the component instance under test + static const NATIVE_INT_TYPE TEST_INSTANCE_QUEUE_DEPTH = 10; + private: // ---------------------------------------------------------------------- // Construction and destruction @@ -38,7 +51,10 @@ class ComQueueTester : public ComQueueGTestBase { // ---------------------------------------------------------------------- void configure(); - void sendByQueueNumber(NATIVE_INT_TYPE queueNumber, NATIVE_INT_TYPE& portNum, QueueType& queueType); + void sendByQueueNumber(Fw::Buffer& buffer, + NATIVE_INT_TYPE queueNumber, + NATIVE_INT_TYPE& portNum, + QueueType& queueType); void emitOne(); @@ -57,7 +73,9 @@ class ComQueueTester : public ComQueueGTestBase { void testPrioritySend(); - void testQueueOverflow(); + void testExternalQueueOverflow(); + + void testInternalQueueOverflow(); void testReadyFirst(); diff --git a/Svc/Seq/Seq.fpp b/Svc/Seq/Seq.fpp index 66a5d9888c..1caf1ce037 100644 --- a/Svc/Seq/Seq.fpp +++ b/Svc/Seq/Seq.fpp @@ -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 diff --git a/Svc/SeqDispatcher/CMakeLists.txt b/Svc/SeqDispatcher/CMakeLists.txt new file mode 100644 index 0000000000..bb92108919 --- /dev/null +++ b/Svc/SeqDispatcher/CMakeLists.txt @@ -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() diff --git a/Svc/SeqDispatcher/SeqDispatcher.cpp b/Svc/SeqDispatcher/SeqDispatcher.cpp new file mode 100644 index 0000000000..c55bda62c0 --- /dev/null +++ b/Svc/SeqDispatcher/SeqDispatcher.cpp @@ -0,0 +1,186 @@ +// ====================================================================== +// \title SeqDispatcher.cpp +// \author zimri.leisher +// \brief cpp file for SeqDispatcher component implementation class +// ====================================================================== + +#include + +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(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(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(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 = ""; + 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(idx), this->m_entryTable[idx].state, Fw::LogStringArg(this->m_entryTable[idx].sequenceRunning)); + } +} +} // end namespace components diff --git a/Svc/SeqDispatcher/SeqDispatcher.fpp b/Svc/SeqDispatcher/SeqDispatcher.fpp new file mode 100644 index 0000000000..3fc6d9702f --- /dev/null +++ b/Svc/SeqDispatcher/SeqDispatcher.fpp @@ -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 + + } +} \ No newline at end of file diff --git a/Svc/SeqDispatcher/SeqDispatcher.hpp b/Svc/SeqDispatcher/SeqDispatcher.hpp new file mode 100644 index 0000000000..9c8b9f6806 --- /dev/null +++ b/Svc/SeqDispatcher/SeqDispatcher.hpp @@ -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 = ""; + } 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 diff --git a/Svc/SeqDispatcher/SeqDispatcherCommands.fppi b/Svc/SeqDispatcher/SeqDispatcherCommands.fppi new file mode 100644 index 0000000000..378c2f6ec7 --- /dev/null +++ b/Svc/SeqDispatcher/SeqDispatcherCommands.fppi @@ -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 \ No newline at end of file diff --git a/Svc/SeqDispatcher/SeqDispatcherEvents.fppi b/Svc/SeqDispatcher/SeqDispatcherEvents.fppi new file mode 100644 index 0000000000..9b92eca254 --- /dev/null +++ b/Svc/SeqDispatcher/SeqDispatcherEvents.fppi @@ -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 {}" \ No newline at end of file diff --git a/Svc/SeqDispatcher/SeqDispatcherTelemetry.fppi b/Svc/SeqDispatcher/SeqDispatcherTelemetry.fppi new file mode 100644 index 0000000000..3728c9a063 --- /dev/null +++ b/Svc/SeqDispatcher/SeqDispatcherTelemetry.fppi @@ -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 \ No newline at end of file diff --git a/Svc/SeqDispatcher/docs/sdd.md b/Svc/SeqDispatcher/docs/sdd.md new file mode 100644 index 0000000000..86fa11160f --- /dev/null +++ b/Svc/SeqDispatcher/docs/sdd.md @@ -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 | +|---|---|---| +|---|---|---| \ No newline at end of file diff --git a/Svc/SeqDispatcher/docs/seq_dispatcher_model.png b/Svc/SeqDispatcher/docs/seq_dispatcher_model.png new file mode 100644 index 0000000000..a85d7c7f45 Binary files /dev/null and b/Svc/SeqDispatcher/docs/seq_dispatcher_model.png differ diff --git a/Svc/SeqDispatcher/test/ut/SeqDispatcherTestMain.cpp b/Svc/SeqDispatcher/test/ut/SeqDispatcherTestMain.cpp new file mode 100644 index 0000000000..b4362ef055 --- /dev/null +++ b/Svc/SeqDispatcher/test/ut/SeqDispatcherTestMain.cpp @@ -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(); +} diff --git a/Svc/SeqDispatcher/test/ut/SeqDispatcherTester.cpp b/Svc/SeqDispatcher/test/ut/SeqDispatcherTester.cpp new file mode 100644 index 0000000000..261f5a275c --- /dev/null +++ b/Svc/SeqDispatcher/test/ut/SeqDispatcherTester.cpp @@ -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 diff --git a/Svc/SeqDispatcher/test/ut/SeqDispatcherTester.hpp b/Svc/SeqDispatcher/test/ut/SeqDispatcherTester.hpp new file mode 100644 index 0000000000..f1f9aee162 --- /dev/null +++ b/Svc/SeqDispatcher/test/ut/SeqDispatcherTester.hpp @@ -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 diff --git a/Svc/SystemResources/CMakeLists.txt b/Svc/SystemResources/CMakeLists.txt index 6fb9cfa7ea..c4a34de837 100644 --- a/Svc/SystemResources/CMakeLists.txt +++ b/Svc/SystemResources/CMakeLists.txt @@ -12,7 +12,6 @@ set(SOURCE_FILES ) set(MOD_DEPS Os - version ) register_fprime_module() ### UTs ### diff --git a/Svc/SystemResources/SystemResources.cpp b/Svc/SystemResources/SystemResources.cpp index 1c2bd04299..f0c01884aa 100644 --- a/Svc/SystemResources/SystemResources.cpp +++ b/Svc/SystemResources/SystemResources.cpp @@ -12,7 +12,6 @@ #include //isnan() #include -#include #include namespace Svc { @@ -73,7 +72,6 @@ void SystemResources ::run_handler(const NATIVE_INT_TYPE portNum, U32 tick_time_ Cpu(); Mem(); PhysMem(); - Version(); } } @@ -88,15 +86,6 @@ void SystemResources ::ENABLE_cmdHandler(const FwOpcodeType opCode, this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); } -void SystemResources ::VERSION_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq) { - Fw::LogStringArg version_string(Project::Version::FRAMEWORK_VERSION); - this->log_ACTIVITY_LO_FRAMEWORK_VERSION(version_string); - - version_string = Project::Version::PROJECT_VERSION; - this->log_ACTIVITY_LO_PROJECT_VERSION(version_string); - this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); -} - F32 SystemResources::compCpuUtil(Os::SystemResources::CpuTicks current, Os::SystemResources::CpuTicks previous) { F32 util = 100.0f; // Prevent divide by zero on fast-sample @@ -151,11 +140,4 @@ void SystemResources::PhysMem() { } } -void SystemResources::Version() { - Fw::TlmString version_string(Project::Version::FRAMEWORK_VERSION); - this->tlmWrite_FRAMEWORK_VERSION(version_string); - - version_string= Project::Version::PROJECT_VERSION; - this->tlmWrite_PROJECT_VERSION(version_string); -} } // end namespace Svc diff --git a/Svc/SystemResources/SystemResources.fpp b/Svc/SystemResources/SystemResources.fpp index fa7d271dd7..1c2d0f4ad5 100644 --- a/Svc/SystemResources/SystemResources.fpp +++ b/Svc/SystemResources/SystemResources.fpp @@ -42,26 +42,6 @@ module Svc { ) \ opcode 0 - @ Report version as EVR - guarded command VERSION \ - opcode 1 - - @ Version of the git repository. - event FRAMEWORK_VERSION( - version: string size 40 @< version string - ) \ - severity activity low \ - id 0 \ - format "Framework Version: [{}]" - - @ Version of the git repository. - event PROJECT_VERSION( - version: string size 40 @< version string - ) \ - severity activity low \ - id 1 \ - format "Project Version: [{}]" - @ Total system memory in KB telemetry MEMORY_TOTAL: U64 id 0 \ format "{} KB" @@ -129,12 +109,6 @@ module Svc { @ System's CPU Percentage telemetry CPU_15: F32 id 20 format "{.2f} percent" - @ Software framework version - telemetry FRAMEWORK_VERSION: string size 40 id 21 - - @ Software project version - telemetry PROJECT_VERSION: string size 40 id 22 - } } diff --git a/Svc/SystemResources/SystemResources.hpp b/Svc/SystemResources/SystemResources.hpp index 7807a7e067..5a8a599a4c 100644 --- a/Svc/SystemResources/SystemResources.hpp +++ b/Svc/SystemResources/SystemResources.hpp @@ -67,17 +67,11 @@ class SystemResources : public SystemResourcesComponentBase { SystemResourceEnabled enable /*!< whether or not system resource telemetry is enabled*/ ); - //! Implementation for VERSION command handler - //! Report version as EVR - void VERSION_cmdHandler(const FwOpcodeType opCode, /*!< The opcode*/ - const U32 cmdSeq /*!< The command sequence number*/ - ); private: void Cpu(); void Mem(); void PhysMem(); - void Version(); F32 compCpuUtil(Os::SystemResources::CpuTicks current, Os::SystemResources::CpuTicks previous); diff --git a/Svc/SystemResources/docs/sdd.md b/Svc/SystemResources/docs/sdd.md index db0160c203..37b2757a1b 100644 --- a/Svc/SystemResources/docs/sdd.md +++ b/Svc/SystemResources/docs/sdd.md @@ -3,10 +3,9 @@ The system resources component downlinks information about the running F´ system. This information includes: -1. System and Software version -2. Free Memory -3. CPU load -4. Disk space +1. Free Memory +2. CPU load +3. Disk space These items are downlinked as telemetry channels in response to a rate group port invocation. diff --git a/Svc/SystemResources/test/ut/SystemResourcesTestMain.cpp b/Svc/SystemResources/test/ut/SystemResourcesTestMain.cpp index 36c992659d..37558da3f1 100644 --- a/Svc/SystemResources/test/ut/SystemResourcesTestMain.cpp +++ b/Svc/SystemResources/test/ut/SystemResourcesTestMain.cpp @@ -14,11 +14,6 @@ TEST(OffNominal, Disabled) { tester.test_disable_enable(); } -TEST(Nominal, Events) { - Svc::SystemResourcesTester tester; - tester.test_version_evr(); -} - int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/Svc/SystemResources/test/ut/SystemResourcesTester.cpp b/Svc/SystemResources/test/ut/SystemResourcesTester.cpp index 476a66d6a6..fe9b1adb12 100644 --- a/Svc/SystemResources/test/ut/SystemResourcesTester.cpp +++ b/Svc/SystemResources/test/ut/SystemResourcesTester.cpp @@ -11,7 +11,6 @@ // ====================================================================== #include "SystemResourcesTester.hpp" -#include "versions/version.hpp" #define INSTANCE 0 #define MAX_HISTORY_SIZE 100 @@ -111,13 +110,7 @@ void SystemResourcesTester ::test_tlm(bool enabled) { ASSERT_TLM_NON_VOLATILE_FREE_SIZE(0); ASSERT_TLM_NON_VOLATILE_TOTAL_SIZE(0); } - ASSERT_TLM_FRAMEWORK_VERSION_SIZE((enabled) ? 1 : 0); - ASSERT_TLM_PROJECT_VERSION_SIZE((enabled) ? 1 : 0); - if (enabled) { - ASSERT_TLM_FRAMEWORK_VERSION(0, Project::Version::FRAMEWORK_VERSION); - ASSERT_TLM_PROJECT_VERSION(0, Project::Version::PROJECT_VERSION); - } - ASSERT_TLM_SIZE((enabled) ? (count + 3) : 0); // CPU count channels + avg + 2 ver + ASSERT_TLM_SIZE((enabled) ? (count + 1) : 0); // CPU count channels + avg + 2 ver break; } } @@ -130,14 +123,6 @@ void SystemResourcesTester ::test_disable_enable() { this->test_tlm(true); } -void SystemResourcesTester ::test_version_evr() { - this->sendCmd_VERSION(0, 0); - ASSERT_EVENTS_FRAMEWORK_VERSION_SIZE(1); - ASSERT_EVENTS_FRAMEWORK_VERSION(0, Project::Version::FRAMEWORK_VERSION); - ASSERT_EVENTS_PROJECT_VERSION_SIZE(1); - ASSERT_EVENTS_PROJECT_VERSION(0, Project::Version::FRAMEWORK_VERSION); -} - // ---------------------------------------------------------------------- // Helper methods // ---------------------------------------------------------------------- diff --git a/Svc/SystemResources/test/ut/SystemResourcesTester.hpp b/Svc/SystemResources/test/ut/SystemResourcesTester.hpp index 6c3fcff571..392d70eddd 100644 --- a/Svc/SystemResources/test/ut/SystemResourcesTester.hpp +++ b/Svc/SystemResources/test/ut/SystemResourcesTester.hpp @@ -45,10 +45,6 @@ class SystemResourcesTester : public SystemResourcesGTestBase { //! void test_disable_enable(); - //! Test version EVR - //! - void test_version_evr(); - private: // ---------------------------------------------------------------------- // Helper methods diff --git a/Svc/Version/Version.cpp b/Svc/Version/Version.cpp index c659ce688d..70f94f449d 100644 --- a/Svc/Version/Version.cpp +++ b/Svc/Version/Version.cpp @@ -112,7 +112,7 @@ void Version ::VERSION_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, Svc::VersionT // ---------------------------------------------------------------------- // implementations for internal functions // ---------------------------------------------------------------------- -// functions to log tlm on versions +// Send both events and tlm for framework version void Version ::fwVersion_tlm() { Fw::LogStringArg fw_event = (Project::Version::FRAMEWORK_VERSION); this->log_ACTIVITY_LO_FrameworkVersion(fw_event); @@ -120,6 +120,7 @@ void Version ::fwVersion_tlm() { this->tlmWrite_FrameworkVersion(fw_tlm); } +// Send both events and tlm for project version void Version ::projectVersion_tlm() { Fw::LogStringArg proj_event = Project::Version::PROJECT_VERSION; this->log_ACTIVITY_LO_ProjectVersion(proj_event); @@ -127,6 +128,7 @@ void Version ::projectVersion_tlm() { this->tlmWrite_ProjectVersion(proj_tlm); } +// Send both events and tlm for library versions void Version ::libraryVersion_tlm() { // Process libraries array @@ -173,6 +175,7 @@ void Version ::libraryVersion_tlm() { } } +// Send all events and tlm (if verbosity is enabled) for custom versions void Version ::customVersion_tlm_all() { for (U8 i = 0; (m_enable == true) && (m_num_custom_elements != 0) && (i < Svc::VersionCfg::VersionEnum::NUM_CONSTANTS); i++) { @@ -180,52 +183,55 @@ void Version ::customVersion_tlm_all() { } } +// Send events and tlm (if verbosity is enabled) for custom versions void Version ::customVersion_tlm(VersionSlot custom_slot) { // Process custom version TLM only if verbosity is enabled and there are any valid writes to it; // it doesn't necessarily have to be consecutive - if ((this->verId_db[custom_slot].getversion_value() != "no_ver") && m_enable == true && + if ((this->verId_db[custom_slot].getversion_value() != "no_ver") && (this->m_num_custom_elements > 0)) { // Write TLM for valid writes - // Emit Events/TLM on library versions + // Emit Events/TLM on custom versions this->log_ACTIVITY_LO_CustomVersions(this->verId_db[custom_slot].getversion_enum(), this->verId_db[custom_slot].getversion_value()); + if (m_enable == true) { //Send TLM only if verbosity is enabled // Write to TLM - switch (custom_slot) { - case VER_SLOT_00: - this->tlmWrite_CustomVersion01(verId_db[custom_slot]); - break; - case VER_SLOT_01: - this->tlmWrite_CustomVersion02(verId_db[custom_slot]); - break; - case VER_SLOT_02: - this->tlmWrite_CustomVersion03(verId_db[custom_slot]); - break; - case VER_SLOT_03: - this->tlmWrite_CustomVersion04(verId_db[custom_slot]); - break; - case VER_SLOT_04: - this->tlmWrite_CustomVersion05(verId_db[custom_slot]); - break; - case VER_SLOT_05: - this->tlmWrite_CustomVersion06(verId_db[custom_slot]); - break; - case VER_SLOT_06: - this->tlmWrite_CustomVersion07(verId_db[custom_slot]); - break; - case VER_SLOT_07: - this->tlmWrite_CustomVersion08(verId_db[custom_slot]); - break; - case VER_SLOT_08: - this->tlmWrite_CustomVersion09(verId_db[custom_slot]); - break; - case VER_SLOT_09: - this->tlmWrite_CustomVersion10(verId_db[custom_slot]); - break; - default: - // There are only 10 custom slots available - FW_ASSERT(0, custom_slot); - break; + switch (custom_slot) { + case VER_SLOT_00: + this->tlmWrite_CustomVersion01(verId_db[custom_slot]); + break; + case VER_SLOT_01: + this->tlmWrite_CustomVersion02(verId_db[custom_slot]); + break; + case VER_SLOT_02: + this->tlmWrite_CustomVersion03(verId_db[custom_slot]); + break; + case VER_SLOT_03: + this->tlmWrite_CustomVersion04(verId_db[custom_slot]); + break; + case VER_SLOT_04: + this->tlmWrite_CustomVersion05(verId_db[custom_slot]); + break; + case VER_SLOT_05: + this->tlmWrite_CustomVersion06(verId_db[custom_slot]); + break; + case VER_SLOT_06: + this->tlmWrite_CustomVersion07(verId_db[custom_slot]); + break; + case VER_SLOT_07: + this->tlmWrite_CustomVersion08(verId_db[custom_slot]); + break; + case VER_SLOT_08: + this->tlmWrite_CustomVersion09(verId_db[custom_slot]); + break; + case VER_SLOT_09: + this->tlmWrite_CustomVersion10(verId_db[custom_slot]); + break; + default: + // There are only 10 custom slots available + FW_ASSERT(0, custom_slot); + break; + } } } } diff --git a/Svc/Version/docs/sdd.md b/Svc/Version/docs/sdd.md index 6fe1b50bd1..1388c2bd07 100644 --- a/Svc/Version/docs/sdd.md +++ b/Svc/Version/docs/sdd.md @@ -9,18 +9,18 @@ Tracks versions for framework,project, libraries and user defined project specif |SVC-VERSION-001|`Svc::Version` upon startup shall generate an event and a telemetry channel with version for framework.| This is to provide transparency on framework version being used| |SVC-VERSION-002|`Svc::Version` upon startup shall generate an event and a telemetry channel with version for project | This is to provide transparency on project version being used| |SVC-VERSION-003|`Svc::Version` upon startup shall generate events and telemetry channels (upto 10) with versions for library.| Transparency on different library versions| -|SVC-VERSION-004|`Svc::Version` upon startup shall make verbosity on custom versions configurable.| Transparency on different library versions| +|SVC-VERSION-004|`Svc::Version` upon startup shall make verbosity on custom versions configurable.| Verbosity will enable/disable the channel generation but will not effect the event generation| |SVC-VERSION-005|`Svc::Version` shall provide a ground command to request events and telemetry on framework version| Accessibility on demand| |SVC-VERSION-006|`Svc::Version` shall provide a ground command to request events and telemetry on project version| Accessibility on demand| |SVC-VERSION-007|`Svc::Version` shall provide a ground command to request events and telemetry channels (upto 10) on library versions| Accessibility on demand| -|SVC-VERSION-008|`Svc::Version` shall provide a ground command to request events and telemetry channels (upto 10) on custom versions| Accessibility on demand| +|SVC-VERSION-008|`Svc::Version` shall provide a ground command to request events and telemetry channels (upto 10) on custom versions| Accessibility on demand. Verbosity configuration will determine the channel generation| |SVC-VERSION-009|`Svc::Version` shall provide a ground command to enable/disable verbosity on custom versions| Accessibility on demand| |SVC-VERSION-010|`Svc::Version` shall provide a telemetry channel on framework version| Accessibility to versions being used| |SVC-VERSION-011|`Svc::Version` shall provide a telemetry channel on project version| Accessibility to versions being used| |SVC-VERSION-012|`Svc::Version` shall provide upto 10 telemetry channels on library versions| Accessibility to versions being used| -|SVC-VERSION-013|`Svc::Version` shall provide upto 10 telemetry channels on custom versions| Accessibility to versions being used| -|SVC-VERSION-014|`Svc::Version` shall provide an interface for other components to set custom versions.| Enables projects to set hardware and FPGA versions, say, as needed. Also generates Events/TLM| -|SVC-VERSION-015|`Svc::Version` shall provide an interface for other components to get custom versions.| Also generates Events/TLM| +|SVC-VERSION-013|`Svc::Version` shall provide upto 10 telemetry channels on custom versions| Accessibility to versions being used. Only accessible if verbosity is enabled| +|SVC-VERSION-014|`Svc::Version` shall provide an interface for other components to set custom versions.| Enables projects to set hardware and FPGA versions, say, as needed. Also generates Events/TLM based on verbosity configuration| +|SVC-VERSION-015|`Svc::Version` shall provide an interface for other components to get custom versions.| Also generates Events/TLM based on verbosity configuration| ## Emitting Versions on Start-Up diff --git a/Svc/Version/test/ut/VersionTester.cpp b/Svc/Version/test/ut/VersionTester.cpp index 7fa4f40f01..edc114128d 100644 --- a/Svc/Version/test/ut/VersionTester.cpp +++ b/Svc/Version/test/ut/VersionTester.cpp @@ -85,13 +85,35 @@ void VersionTester ::test_enable() { this->sendCmd_ENABLE(0, cmd_seq, VersionEnabled::DISABLED); ASSERT_CMD_RESPONSE(0, 0, 9, Fw::CmdResponse::OK); VersionTester::test_setVer(false); - ASSERT_EVENTS_CustomVersions_SIZE(0); + //When verbosity is disabled, events are still generated but not EHAs + ASSERT_EVENTS_CustomVersions_SIZE(10); + ASSERT_TLM_CustomVersion01_SIZE(0); + ASSERT_TLM_CustomVersion02_SIZE(0); + ASSERT_TLM_CustomVersion03_SIZE(0); + ASSERT_TLM_CustomVersion04_SIZE(0); + ASSERT_TLM_CustomVersion05_SIZE(0); + ASSERT_TLM_CustomVersion06_SIZE(0); + ASSERT_TLM_CustomVersion07_SIZE(0); + ASSERT_TLM_CustomVersion08_SIZE(0); + ASSERT_TLM_CustomVersion09_SIZE(0); + ASSERT_TLM_CustomVersion10_SIZE(0); cmd_seq = 9; this->sendCmd_ENABLE(0, cmd_seq, VersionEnabled::ENABLED); ASSERT_CMD_RESPONSE(0, 0, 9, Fw::CmdResponse::OK); VersionTester::test_setVer(true); + //When verbosity is enabled, events and EHAs are generated ASSERT_EVENTS_CustomVersions_SIZE(10); + ASSERT_TLM_CustomVersion01_SIZE(1); + ASSERT_TLM_CustomVersion02_SIZE(1); + ASSERT_TLM_CustomVersion03_SIZE(1); + ASSERT_TLM_CustomVersion04_SIZE(1); + ASSERT_TLM_CustomVersion05_SIZE(1); + ASSERT_TLM_CustomVersion06_SIZE(1); + ASSERT_TLM_CustomVersion07_SIZE(1); + ASSERT_TLM_CustomVersion08_SIZE(1); + ASSERT_TLM_CustomVersion09_SIZE(1); + ASSERT_TLM_CustomVersion10_SIZE(1); } void VersionTester ::test_versions() { diff --git a/config/AcConstants.fpp b/config/AcConstants.fpp index 2f0f10706c..3ddcff26e6 100644 --- a/config/AcConstants.fpp +++ b/config/AcConstants.fpp @@ -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 diff --git a/docs/UsersGuide/dev/configuring-fprime.md b/docs/UsersGuide/dev/configuring-fprime.md index 6464fb8732..dc9ee6ea55 100644 --- a/docs/UsersGuide/dev/configuring-fprime.md +++ b/docs/UsersGuide/dev/configuring-fprime.md @@ -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 diff --git a/requirements.txt b/requirements.txt index 00fc186b57..dfc036df53 100644 --- a/requirements.txt +++ b/requirements.txt @@ -31,7 +31,7 @@ fprime-fpp-to-dict==2.2.0a3 fprime-fpp-to-json==2.2.0a3 fprime-fpp-to-xml==2.2.0a3 fprime-gds==3.4.4a3 -fprime-tools==3.4.4 +fprime-tools==v3.4.5a1 fprime-visual==1.0.2 gcovr==6.0 idna==3.4