diff --git a/Svc/CmdSplitter/CmdSplitter.cpp b/Svc/CmdSplitter/CmdSplitter.cpp index 6d850e33d6..003d7eb377 100644 --- a/Svc/CmdSplitter/CmdSplitter.cpp +++ b/Svc/CmdSplitter/CmdSplitter.cpp @@ -7,7 +7,8 @@ #include #include #include -#include +#include +#include namespace Svc { @@ -19,6 +20,10 @@ CmdSplitter ::CmdSplitter(const char* const compName) : CmdSplitterComponentBase CmdSplitter ::~CmdSplitter() {} +void CmdSplitter ::configure(const FwOpcodeType remoteBaseOpcode) { + this->m_remoteBase = remoteBaseOpcode; +} + // ---------------------------------------------------------------------- // Handler implementations for user-defined typed input ports // ---------------------------------------------------------------------- @@ -27,15 +32,16 @@ void CmdSplitter ::CmdBuff_handler(const NATIVE_INT_TYPE portNum, Fw::ComBuffer& Fw::CmdPacket cmdPkt; Fw::SerializeStatus stat = cmdPkt.deserialize(data); + FW_ASSERT(portNum < CmdSplitterPorts); if (stat != Fw::FW_SERIALIZE_OK) { // Let the local command dispatcher deal with it - this->LocalCmd_out(0, data, context); + this->LocalCmd_out(portNum, data, context); } else { // Check if local or remote - if (cmdPkt.getOpCode() < CMD_SPLITTER_REMOTE_OPCODE_BASE) { - this->LocalCmd_out(0, data, context); + if (cmdPkt.getOpCode() < this->m_remoteBase) { + this->LocalCmd_out(portNum, data, context); } else { - this->RemoteCmd_out(0, data, context); + this->RemoteCmd_out(portNum, data, context); } } } @@ -44,6 +50,7 @@ void CmdSplitter ::seqCmdStatus_handler(const NATIVE_INT_TYPE portNum, FwOpcodeType opCode, U32 cmdSeq, const Fw::CmdResponse& response) { + FW_ASSERT(portNum < CmdSplitterPorts); // Forward the command status this->forwardSeqCmdStatus_out(portNum, opCode, cmdSeq, response); } diff --git a/Svc/CmdSplitter/CmdSplitter.fpp b/Svc/CmdSplitter/CmdSplitter.fpp index 6d408f05af..f34f6a2708 100644 --- a/Svc/CmdSplitter/CmdSplitter.fpp +++ b/Svc/CmdSplitter/CmdSplitter.fpp @@ -5,23 +5,27 @@ module Svc { passive component CmdSplitter { # ---------------------------------------------------------------------- - # General ports + # Upstream connections: uplink commanding and command sequencers # ---------------------------------------------------------------------- @ Input port for local or remote commands - sync input port CmdBuff: Fw.Com - - @ Input port for receiving the command status - sync input port seqCmdStatus: Fw.CmdResponse + sync input port CmdBuff: [CmdSplitterPorts] Fw.Com @ Output port for forwarding the Command status - output port forwardSeqCmdStatus: Fw.CmdResponse + output port forwardSeqCmdStatus: [CmdSplitterPorts] Fw.CmdResponse + + # ---------------------------------------------------------------------- + # Downstream connections: local and remote command sequencers + # ---------------------------------------------------------------------- + + @ Input port for receiving the command status + sync input port seqCmdStatus: [CmdSplitterPorts] Fw.CmdResponse @ Output port for local commands - output port LocalCmd: Fw.Com + output port LocalCmd: [CmdSplitterPorts] Fw.Com @ Output port for remote commands - output port RemoteCmd: Fw.Com + output port RemoteCmd: [CmdSplitterPorts] Fw.Com } } \ No newline at end of file diff --git a/Svc/CmdSplitter/CmdSplitter.hpp b/Svc/CmdSplitter/CmdSplitter.hpp index ae76cba6b6..b582913bac 100644 --- a/Svc/CmdSplitter/CmdSplitter.hpp +++ b/Svc/CmdSplitter/CmdSplitter.hpp @@ -27,7 +27,11 @@ class CmdSplitter : public CmdSplitterComponentBase { //! ~CmdSplitter(); - PRIVATE : + //! Configure this splitter + //! + void configure(const FwOpcodeType remoteBaseOpcode /*!< Base remote opcode*/); + + PRIVATE: // ---------------------------------------------------------------------- // Handler implementations for user-defined typed input ports @@ -47,6 +51,8 @@ class CmdSplitter : public CmdSplitterComponentBase { U32 cmdSeq, /*!< Command Sequence */ const Fw::CmdResponse& response /*!< The command response argument */ ); + + FwOpcodeType m_remoteBase; // Opcodes greater than or equal than this value will route remotely }; } // end namespace Svc diff --git a/Svc/CmdSplitter/docs/sdd.md b/Svc/CmdSplitter/docs/sdd.md index 206b53fede..dfab1c9cfa 100644 --- a/Svc/CmdSplitter/docs/sdd.md +++ b/Svc/CmdSplitter/docs/sdd.md @@ -3,7 +3,9 @@ ## 1. Introduction -The `Svc::CmdSplitter` splits an uplinked command execution to two separate `Svc::CmdDispatcher` components: one "local" and the other "remote". This splitting is done by opcode where local commands are commands whose opcode is less than `Svc::CMD_SPLITTER_REMOTE_OPCODE_BASE` and remote commands are those opcodes equal to or larger than that configuration setting. `Svc::CmdSplitter` is intended to be used as part of the hub pattern to route command to a command dispatcher in the remote deployment. +The `Svc::CmdSplitter` splits an uplinked command execution to two separate `Svc::CmdDispatcher` components: one "local" and the other "remote". This splitting is done by opcode where local commands are commands whose opcode is less than a configured base and remote commands are those opcodes equal to or larger than that configurated base setting. `Svc::CmdSplitter` is intended to be used as part of the hub pattern to route command to a command dispatcher in the remote deployment. + +All input and output are done through port arrays that mirror the port array of input commands to the command dispatcher. In this way `Svc::CmdSplitter` may support multiple command sources in the same manner that `Svc::CmdDispatcher` does. ## 2. Requirements @@ -11,31 +13,34 @@ The requirements for `Svc::CmdSplitter` are as follows: | Requirement | Description | Verification Method | |----------------------|------------------------------------------------------------------------------------------------------|---------------------| +| SVC-CMD-SPLITTER-000 | The `Svc::CmdSplitter` component shall support multiple command sources. | Unit Test | | SVC-CMD-SPLITTER-001 | The `Svc::CmdSplitter` component shall accept incoming command buffers. | Unit Test | | SVC-CMD-SPLITTER-002 | The `Svc::CmdSplitter` component shall route commands under a configured value to the "local" port. | Unit Test | | SVC-CMD-SPLITTER-003 | The `Svc::CmdSplitter` component shall route commands under a configured value to the "remote" port. | Unit Test | | SVC-CMD-SPLITTER-004 | The `Svc::CmdSplitter` component shall route commands to the "local" port when an error occurs. | Unit Test | -| SVC-CMD-SPLITTER-005 | The `Svc::CmdSplitter` forward command status responses. | Unit Test | +| SVC-CMD-SPLITTER-005 | The `Svc::CmdSplitter` component shall forward command status responses. | Unit Test | ## 3. Design ### 3.1 Ports -| Name | Type | Kind | Description | -|---------------------|----------------|------------|-----------------------------------------------------------------| -| CmdBuff | Fw.Com | sync input | Incoming command buffer. | -| seqCmdStatus | Fw.CmdResponse | sync input | Incoming command status from both local and remote dispatchers. | -| LocalCmd | Fw.Com | sync input | Outgoing command buffer for local command dispatcher. | -| RemoteCmd | Fw.Com | sync input | Outgoing command buffer for remote command dispatcher. | -| forwardSeqCmdStatus | Fw.CmdResponse | sync input | Outgoing forwarded command status. | +| Name | Type | Kind | Description | +|---------------------|----------------|------------------|--------------------------------------------------------------------------| +| CmdBuff | Fw.Com | sync input array | Array of incoming command buffer. | +| seqCmdStatus | Fw.CmdResponse | sync input array | Array of incoming command status from both local and remote dispatchers. | +| LocalCmd | Fw.Com | output array | Array of outgoing command buffer for local command dispatcher. | +| RemoteCmd | Fw.Com | output array | Array of outgoing command buffer for remote command dispatcher. | +| forwardSeqCmdStatus | Fw.CmdResponse | output array | Array of outgoing forwarded command status. | + +> Port arrays mirror those of the command dispatcher in order to support multiple sources in the same fashion that command dispatcher does. ### 3.2 Functional Description -The `Svc::CmdSplitter` routes an incoming command buffer of type `Fw::ComBuffer` to a local or remote command dispatcher. This is done by comparing the command's opcode to the `Svc::CMD_SPLITTER_REMOTE_OPCODE_BASE` configuration value. All command responses are forwarded through. +The `Svc::CmdSplitter` routes an incoming command buffer of type `Fw::ComBuffer` to a local or remote command dispatcher. This is done by comparing the command's opcode to the configured base opcode value. All command responses are forwarded through. ### 3.1 State -`Svc::CmdSplitter` has no state machines nor internal state. +`Svc::CmdSplitter` has only one internal state variable: `m_remoteBase` the configured base opcode for remote opcodes. This is set by a call to the `configure()` method at system start-up. ### 3.2 Algorithms @@ -47,9 +52,7 @@ To see unit test coverage run `fprime-util check --coverage` in the `Svc::CmdSpl ## 5. Change Log -| Date | Description | -|------------|-------------| -| 2023-06-12 | Initial | - - - +| Date | Description | +|------------|---------------------------------| +| 2023-06-12 | Initial | +| 2023-09-27 | Support multiple command inputs | diff --git a/Svc/CmdSplitter/test/ut/Tester.cpp b/Svc/CmdSplitter/test/ut/Tester.cpp index 6a44e98daf..3be2b3a3e5 100644 --- a/Svc/CmdSplitter/test/ut/Tester.cpp +++ b/Svc/CmdSplitter/test/ut/Tester.cpp @@ -5,10 +5,10 @@ // ====================================================================== #include "Tester.hpp" -#include #include #include #include +#include "FppConstantsAc.hpp" namespace Svc { @@ -43,51 +43,71 @@ Fw::ComBuffer Tester ::build_command_around_opcode(FwOpcodeType opcode) { return comBuffer; } +FwOpcodeType Tester ::setup_and_pick_valid_opcode(bool for_local) { + const FwOpcodeType MAX_OPCODE = std::numeric_limits::max(); + if (for_local) { + FwOpcodeType base = STest::Pick::lowerUpper(1, MAX_OPCODE); + component.configure(base); + EXPECT_GT(base, 0); // Must leave some room for local commands + return static_cast( + STest::Pick::lowerUpper(0, FW_MIN(base - 1, MAX_OPCODE)) + ); + } + FwOpcodeType base = STest::Pick::lowerUpper(0, MAX_OPCODE); + component.configure(base); + return static_cast(STest::Pick::lowerUpper(base, MAX_OPCODE)); +} + void Tester ::test_local_routing() { + REQUIREMENT("SVC-CMD-SPLITTER-000"); REQUIREMENT("SVC-CMD-SPLITTER-001"); REQUIREMENT("SVC-CMD-SPLITTER-002"); - ASSERT_GT(Svc::CMD_SPLITTER_REMOTE_OPCODE_BASE, 0); // Must leave some room for local commands - FwOpcodeType local_opcode = static_cast(STest::Pick::lowerUpper( - 0, FW_MIN(Svc::CMD_SPLITTER_REMOTE_OPCODE_BASE - 1, std::numeric_limits::max()))); + FwOpcodeType local_opcode = this->setup_and_pick_valid_opcode(true); Fw::ComBuffer testBuffer = this->build_command_around_opcode(local_opcode); U32 context = static_cast(STest::Pick::any()); - this->invoke_to_CmdBuff(0, testBuffer, context); + this->active_command_source = static_cast(STest::Pick::lowerUpper( + 0, CmdSplitterPorts)); + this->invoke_to_CmdBuff(this->active_command_source, testBuffer, context); ASSERT_from_RemoteCmd_SIZE(0); ASSERT_from_LocalCmd_SIZE(1); ASSERT_from_LocalCmd(0, testBuffer, context); } void Tester ::test_remote_routing() { + REQUIREMENT("SVC-CMD-SPLITTER-000"); REQUIREMENT("SVC-CMD-SPLITTER-001"); REQUIREMENT("SVC-CMD-SPLITTER-003"); - ASSERT_LT(Svc::CMD_SPLITTER_REMOTE_OPCODE_BASE, - std::numeric_limits::max()); // Must leave some room for remote commands - FwOpcodeType local_opcode = static_cast( - STest::Pick::lowerUpper(Svc::CMD_SPLITTER_REMOTE_OPCODE_BASE, std::numeric_limits::max())); - Fw::ComBuffer testBuffer = this->build_command_around_opcode(local_opcode); + FwOpcodeType remote_opcode = this->setup_and_pick_valid_opcode(false); + Fw::ComBuffer testBuffer = this->build_command_around_opcode(remote_opcode); U32 context = static_cast(STest::Pick::any()); - this->invoke_to_CmdBuff(0, testBuffer, context); + this->active_command_source = static_cast(STest::Pick::lowerUpper( + 0, CmdSplitterPorts)); + this->invoke_to_CmdBuff(this->active_command_source, testBuffer, context); ASSERT_from_LocalCmd_SIZE(0); ASSERT_from_RemoteCmd_SIZE(1); ASSERT_from_RemoteCmd(0, testBuffer, context); } void Tester ::test_error_routing() { + REQUIREMENT("SVC-CMD-SPLITTER-000"); REQUIREMENT("SVC-CMD-SPLITTER-001"); REQUIREMENT("SVC-CMD-SPLITTER-004"); Fw::ComBuffer testBuffer; // Intentionally left empty U32 context = static_cast(STest::Pick::any()); - this->invoke_to_CmdBuff(0, testBuffer, context); + this->active_command_source = static_cast(STest::Pick::lowerUpper( + 0, CmdDispatcherSequencePorts)); + this->invoke_to_CmdBuff(this->active_command_source, testBuffer, context); ASSERT_from_RemoteCmd_SIZE(0); ASSERT_from_LocalCmd_SIZE(1); ASSERT_from_LocalCmd(0, testBuffer, context); } void Tester ::test_response_forwarding() { + REQUIREMENT("SVC-CMD-SPLITTER-000"); REQUIREMENT("SVC-CMD-SPLITTER-001"); REQUIREMENT("SVC-CMD-SPLITTER-005"); @@ -96,7 +116,10 @@ void Tester ::test_response_forwarding() { Fw::CmdResponse response; response.e = static_cast(STest::Pick::lowerUpper(0, Fw::CmdResponse::NUM_CONSTANTS)); U32 cmdSeq = static_cast(STest::Pick::any()); - this->invoke_to_seqCmdStatus(0, opcode, cmdSeq, response); + this->active_command_source = static_cast(STest::Pick::lowerUpper( + 0, CmdDispatcherSequencePorts)); + + this->invoke_to_seqCmdStatus(this->active_command_source, opcode, cmdSeq, response); ASSERT_from_forwardSeqCmdStatus_SIZE(1); ASSERT_from_forwardSeqCmdStatus(0, opcode, cmdSeq, response); } @@ -106,10 +129,12 @@ void Tester ::test_response_forwarding() { // ---------------------------------------------------------------------- void Tester ::from_LocalCmd_handler(const NATIVE_INT_TYPE portNum, Fw::ComBuffer& data, U32 context) { + EXPECT_EQ(this->active_command_source, portNum) << "Command source not respected"; this->pushFromPortEntry_LocalCmd(data, context); } void Tester ::from_RemoteCmd_handler(const NATIVE_INT_TYPE portNum, Fw::ComBuffer& data, U32 context) { + EXPECT_EQ(this->active_command_source, portNum) << "Command source not respected"; this->pushFromPortEntry_RemoteCmd(data, context); } @@ -117,6 +142,7 @@ void Tester ::from_forwardSeqCmdStatus_handler(const NATIVE_INT_TYPE portNum, FwOpcodeType opCode, U32 cmdSeq, const Fw::CmdResponse& response) { + EXPECT_EQ(this->active_command_source, portNum) << "Command source not respected"; this->pushFromPortEntry_forwardSeqCmdStatus(opCode, cmdSeq, response); } diff --git a/Svc/CmdSplitter/test/ut/Tester.hpp b/Svc/CmdSplitter/test/ut/Tester.hpp index 02ad1d07d5..56f60729b7 100644 --- a/Svc/CmdSplitter/test/ut/Tester.hpp +++ b/Svc/CmdSplitter/test/ut/Tester.hpp @@ -57,6 +57,10 @@ class Tester : public CmdSplitterGTestBase { //! Fw::ComBuffer build_command_around_opcode(FwOpcodeType opcode); + //! Helper to set opcode base and select a valid opcode + //! + FwOpcodeType setup_and_pick_valid_opcode(bool for_local /*!< Local command testing*/); + // ---------------------------------------------------------------------- // Handlers for typed from ports // ---------------------------------------------------------------------- @@ -104,6 +108,7 @@ class Tester : public CmdSplitterGTestBase { //! The component under test //! CmdSplitter component; + NATIVE_INT_TYPE active_command_source; }; } // end namespace Svc diff --git a/cmake/FPrime-Code.cmake b/cmake/FPrime-Code.cmake index 1a4708afb6..297c0257ac 100644 --- a/cmake/FPrime-Code.cmake +++ b/cmake/FPrime-Code.cmake @@ -28,6 +28,7 @@ endif() add_library(Fpp INTERFACE) set(FPRIME_CURRENT_MODULE config) add_subdirectory("${FPRIME_CONFIG_DIR}" "${CMAKE_BINARY_DIR}/config") +include_directories("${CMAKE_BINARY_DIR}/config") set(_FP_CORE_PACKAGES Fw Svc Os Drv CFDP Utils) foreach (_FP_PACKAGE_DIR IN LISTS _FP_CORE_PACKAGES) set(FPRIME_CURRENT_MODULE "${_FP_PACKAGE_DIR}") diff --git a/config/AcConstants.fpp b/config/AcConstants.fpp index a6d399d4a5..5315c63dab 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 sizing the command splitter input arrays +constant CmdSplitterPorts = CmdDispatcherSequencePorts + @ Number of static memory allocations constant StaticMemoryAllocations = 4 diff --git a/config/CmdSplitterCfg.hpp b/config/CmdSplitterCfg.hpp deleted file mode 100644 index e943d2a2e0..0000000000 --- a/config/CmdSplitterCfg.hpp +++ /dev/null @@ -1,18 +0,0 @@ -/* - * CmdSplitterCfg.hpp: - * - * Used to configure the Svc::CmdSplitter component's remote opcode base. - * - */ - -#ifndef CONFIG_CMD_SPLITTER_CFG_HPP_ -#define CONFIG_CMD_SPLITTER_CFG_HPP_ - -#include - -namespace Svc { -//!< Base value for remote opcodes used by Svc::CmdSplitter -static const FwOpcodeType CMD_SPLITTER_REMOTE_OPCODE_BASE = 0x10000000; -}; - -#endif /* CONFIG_CMD_SPLITTER_CFG_HPP_ */