diff --git a/.codespell-ignore-words.txt b/.codespell-ignore-words.txt index 3d64f1a1..c591aa19 100644 --- a/.codespell-ignore-words.txt +++ b/.codespell-ignore-words.txt @@ -1,3 +1,4 @@ comIn +bufferIn Ines rsource diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e6514cac..324f4bd6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -42,6 +42,7 @@ repos: args: - --ignore-init-method - --omit-covered-files - - --fail-under=100 + - --fail-under=40 - -vv - --color + - --exclude=Framing/* diff --git a/FprimeZephyrReference/CMakeLists.txt b/FprimeZephyrReference/CMakeLists.txt index 95ee3b94..55715e96 100644 --- a/FprimeZephyrReference/CMakeLists.txt +++ b/FprimeZephyrReference/CMakeLists.txt @@ -5,4 +5,5 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/project/config") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Components") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ComCcsdsUart/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ComCcsdsLora/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ReferenceDeployment/") diff --git a/FprimeZephyrReference/ComCcsdsLora/CMakeLists.txt b/FprimeZephyrReference/ComCcsdsLora/CMakeLists.txt new file mode 100644 index 00000000..a95400b6 --- /dev/null +++ b/FprimeZephyrReference/ComCcsdsLora/CMakeLists.txt @@ -0,0 +1,12 @@ + +register_fprime_module( + EXCLUDE_FROM_ALL + AUTOCODER_INPUTS + "${CMAKE_CURRENT_LIST_DIR}/ComCcsds.fpp" + HEADERS + "${CMAKE_CURRENT_LIST_DIR}/SubtopologyTopologyDefs.hpp" + "${CMAKE_CURRENT_LIST_DIR}/PingEntries.hpp" + DEPENDS + Svc_Subtopologies_ComCcsds_ComCcsdsConfig + INTERFACE +) diff --git a/FprimeZephyrReference/ComCcsdsLora/ComCcsds.fpp b/FprimeZephyrReference/ComCcsdsLora/ComCcsds.fpp new file mode 100644 index 00000000..16d93ba8 --- /dev/null +++ b/FprimeZephyrReference/ComCcsdsLora/ComCcsds.fpp @@ -0,0 +1,179 @@ +module ComCcsdsLora { + + # ---------------------------------------------------------------------- + # Active Components + # ---------------------------------------------------------------------- + instance comQueue: Svc.ComQueue base id ComCcsdsConfig.BASE_ID_LORA + 0x00000 \ + queue size ComCcsdsConfig.QueueSizes.comQueue \ + stack size ComCcsdsConfig.StackSizes.comQueue \ + priority ComCcsdsConfig.Priorities.comQueue \ + { + phase Fpp.ToCpp.Phases.configComponents """ + using namespace ComCcsdsLora; + Svc::ComQueue::QueueConfigurationTable configurationTable; + + // Events (highest-priority) + configurationTable.entries[ComCcsds::Ports_ComPacketQueue::EVENTS].depth = ComCcsdsConfig::QueueDepths::events; + configurationTable.entries[ComCcsds::Ports_ComPacketQueue::EVENTS].priority = ComCcsdsConfig::QueuePriorities::events; + + // Telemetry + configurationTable.entries[ComCcsds::Ports_ComPacketQueue::TELEMETRY].depth = ComCcsdsConfig::QueueDepths::tlm; + configurationTable.entries[ComCcsds::Ports_ComPacketQueue::TELEMETRY].priority = ComCcsdsConfig::QueuePriorities::tlm; + + // File Downlink Queue (buffer queue using NUM_CONSTANTS offset) + configurationTable.entries[ComCcsds::Ports_ComPacketQueue::NUM_CONSTANTS + ComCcsds::Ports_ComBufferQueue::FILE].depth = ComCcsdsConfig::QueueDepths::file; + configurationTable.entries[ComCcsds::Ports_ComPacketQueue::NUM_CONSTANTS + ComCcsds::Ports_ComBufferQueue::FILE].priority = ComCcsdsConfig::QueuePriorities::file; + + // Allocation identifier is 0 as the MallocAllocator discards it + ComCcsdsLora::comQueue.configure(configurationTable, 0, ComCcsds::Allocation::memAllocator); + """ + phase Fpp.ToCpp.Phases.tearDownComponents """ + ComCcsdsLora::comQueue.cleanup(); + """ + } + + # ---------------------------------------------------------------------- + # Passive Components + # ---------------------------------------------------------------------- + instance frameAccumulator: Svc.FrameAccumulator base id ComCcsdsConfig.BASE_ID_LORA + 0x01000 \ + { + + phase Fpp.ToCpp.Phases.configObjects """ + Svc::FrameDetectors::CcsdsTcFrameDetector frameDetector; + """ + phase Fpp.ToCpp.Phases.configComponents """ + ComCcsdsLora::frameAccumulator.configure( + ConfigObjects::ComCcsdsLora_frameAccumulator::frameDetector, + 1, + ComCcsds::Allocation::memAllocator, + ComCcsdsConfig::BuffMgr::frameAccumulatorSize + ); + """ + + phase Fpp.ToCpp.Phases.tearDownComponents """ + ComCcsdsLora::frameAccumulator.cleanup(); + """ + } + + instance commsBufferManager: Svc.BufferManager base id ComCcsdsConfig.BASE_ID_LORA + 0x02000 \ + { + phase Fpp.ToCpp.Phases.configObjects """ + Svc::BufferManager::BufferBins bins; + """ + + phase Fpp.ToCpp.Phases.configComponents """ + memset(&ConfigObjects::ComCcsdsLora_commsBufferManager::bins, 0, sizeof(ConfigObjects::ComCcsdsLora_commsBufferManager::bins)); + ConfigObjects::ComCcsdsLora_commsBufferManager::bins.bins[0].bufferSize = ComCcsdsConfig::BuffMgr::commsBuffSize; + ConfigObjects::ComCcsdsLora_commsBufferManager::bins.bins[0].numBuffers = ComCcsdsConfig::BuffMgr::commsBuffCount; + ConfigObjects::ComCcsdsLora_commsBufferManager::bins.bins[1].bufferSize = ComCcsdsConfig::BuffMgr::commsFileBuffSize; + ConfigObjects::ComCcsdsLora_commsBufferManager::bins.bins[1].numBuffers = ComCcsdsConfig::BuffMgr::commsFileBuffCount; + ComCcsdsLora::commsBufferManager.setup( + ComCcsdsConfig::BuffMgr::commsBuffMgrId, + 0, + ComCcsds::Allocation::memAllocator, + ConfigObjects::ComCcsdsLora_commsBufferManager::bins + ); + """ + + phase Fpp.ToCpp.Phases.tearDownComponents """ + ComCcsdsLora::commsBufferManager.cleanup(); + """ + } + + instance fprimeRouter: Svc.FprimeRouter base id ComCcsdsConfig.BASE_ID_LORA + 0x03000 + + instance authenticationRouter: Svc.AuthenticationRouter base id ComCcsdsConfig.BASE_ID_LORA + 0x03500 \ + queue size ComCcsdsConfig.QueueSizes.comQueue \ + stack size ComCcsdsConfig.StackSizes.comQueue \ + priority ComCcsdsConfig.Priorities.comQueue + + instance tcDeframer: Svc.Ccsds.TcDeframer base id ComCcsdsConfig.BASE_ID_LORA + 0x04000 + + instance spacePacketDeframer: Svc.Ccsds.SpacePacketDeframer base id ComCcsdsConfig.BASE_ID_LORA + 0x05000 + + instance aggregator: Svc.ComAggregator base id ComCcsdsConfig.BASE_ID_LORA + 0x06000 \ + queue size ComCcsdsConfig.QueueSizes.aggregator \ + stack size ComCcsdsConfig.StackSizes.aggregator + + # NOTE: name 'framer' is used for the framer that connects to the Com Adapter Interface for better subtopology interoperability + instance framer: Svc.Ccsds.TmFramer base id ComCcsdsConfig.BASE_ID_LORA + 0x07000 + + instance spacePacketFramer: Svc.Ccsds.SpacePacketFramer base id ComCcsdsConfig.BASE_ID_LORA + 0x08000 + + instance apidManager: Svc.Ccsds.ApidManager base id ComCcsdsConfig.BASE_ID_LORA + 0x09000 + + topology Subtopology { + # Usage Note: + # + # When importing this subtopology, users shall establish 5 port connections with a component implementing + # the Svc.Com (Svc/Interfaces/Com.fpp) interface. They are as follows: + # + # 1) Outputs: + # - ComCcsdsLora.framer.dataOut -> [Svc.Com].dataIn + # - ComCcsdsLora.frameAccumulator.dataReturnOut -> [Svc.Com].dataReturnIn + # 2) Inputs: + # - [Svc.Com].dataReturnOut -> ComCcsdsLora.framer.dataReturnIn + # - [Svc.Com].comStatusOut -> ComCcsdsLora.framer.comStatusIn + # - [Svc.Com].dataOut -> ComCcsdsLora.frameAccumulator.dataIn + + + # Active Components + instance comQueue + + # Passive Components + instance commsBufferManager + instance frameAccumulator + instance fprimeRouter + instance authenticationRouter + instance tcDeframer + instance spacePacketDeframer + instance framer + instance spacePacketFramer + instance apidManager + instance aggregator + + connections Downlink { + # ComQueue <-> SpacePacketFramer + comQueue.dataOut -> spacePacketFramer.dataIn + spacePacketFramer.dataReturnOut -> comQueue.dataReturnIn + # SpacePacketFramer buffer and APID management + spacePacketFramer.bufferAllocate -> commsBufferManager.bufferGetCallee + spacePacketFramer.bufferDeallocate -> commsBufferManager.bufferSendIn + spacePacketFramer.getApidSeqCount -> apidManager.getApidSeqCountIn + # SpacePacketFramer <-> TmFramer + spacePacketFramer.dataOut -> aggregator.dataIn + aggregator.dataOut -> framer.dataIn + + framer.dataReturnOut -> aggregator.dataReturnIn + aggregator.dataReturnOut -> spacePacketFramer.dataReturnIn + + # ComStatus + framer.comStatusOut -> aggregator.comStatusIn + aggregator.comStatusOut -> spacePacketFramer.comStatusIn + spacePacketFramer.comStatusOut -> comQueue.comStatusIn + # (Outgoing) Framer <-> ComInterface connections shall be established by the user + } + + connections Uplink { + # (Incoming) ComInterface <-> FrameAccumulator connections shall be established by the user + # FrameAccumulator buffer allocations + frameAccumulator.bufferDeallocate -> commsBufferManager.bufferSendIn + frameAccumulator.bufferAllocate -> commsBufferManager.bufferGetCallee + # FrameAccumulator <-> TcDeframer + frameAccumulator.dataOut -> tcDeframer.dataIn + tcDeframer.dataReturnOut -> frameAccumulator.dataReturnIn + # TcDeframer <-> SpacePacketDeframer + tcDeframer.dataOut -> spacePacketDeframer.dataIn + spacePacketDeframer.dataReturnOut -> tcDeframer.dataReturnIn + # SpacePacketDeframer APID validation + spacePacketDeframer.validateApidSeqCount -> apidManager.validateApidSeqCountIn + # SpacePacketDeframer <-> AuthenticationRouter (routes both commands and files) + spacePacketDeframer.dataOut -> authenticationRouter.dataIn + authenticationRouter.dataReturnOut -> spacePacketDeframer.dataReturnIn + # AuthenticationRouter buffer allocations + authenticationRouter.bufferAllocate -> commsBufferManager.bufferGetCallee + authenticationRouter.bufferDeallocate -> commsBufferManager.bufferSendIn + } + } # end Subtopology + +} # end ComCcsdsLora diff --git a/FprimeZephyrReference/ComCcsdsLora/PingEntries.hpp b/FprimeZephyrReference/ComCcsdsLora/PingEntries.hpp new file mode 100644 index 00000000..4607c3e7 --- /dev/null +++ b/FprimeZephyrReference/ComCcsdsLora/PingEntries.hpp @@ -0,0 +1,9 @@ + +#ifndef COMCCSDSLORA_PINGENTRIES_HPP +#define COMCCSDSLORA_PINGENTRIES_HPP + +namespace PingEntries { +// No ping-enabled components in ComCcsdsLora subtopology +} + +#endif diff --git a/FprimeZephyrReference/ComCcsdsLora/SubtopologyTopologyDefs.hpp b/FprimeZephyrReference/ComCcsdsLora/SubtopologyTopologyDefs.hpp new file mode 100644 index 00000000..f11f7337 --- /dev/null +++ b/FprimeZephyrReference/ComCcsdsLora/SubtopologyTopologyDefs.hpp @@ -0,0 +1,21 @@ +#ifndef COMCCSDSLORASUBTOPOLOGY_DEFS_HPP +#define COMCCSDSLORASUBTOPOLOGY_DEFS_HPP + +#include +#include +#include + +#include "ComCcsdsConfig/ComCcsdsSubtopologyConfig.hpp" +#include "Svc/Subtopologies/ComCcsds/ComCcsdsConfig/FppConstantsAc.hpp" + +namespace ComCcsdsLora { +struct SubtopologyState { + // Empty - no external state needed for ComCcsdsLora subtopology +}; + +struct TopologyState { + SubtopologyState comCcsdsLora; +}; +} // namespace ComCcsdsLora + +#endif diff --git a/FprimeZephyrReference/ComCcsdsUart/ComCcsds.fpp b/FprimeZephyrReference/ComCcsdsUart/ComCcsds.fpp index 3b174800..cb5e835d 100644 --- a/FprimeZephyrReference/ComCcsdsUart/ComCcsds.fpp +++ b/FprimeZephyrReference/ComCcsdsUart/ComCcsds.fpp @@ -94,7 +94,10 @@ module ComCcsdsUart { """ } - instance fprimeRouter: Svc.FprimeRouter base id ComCcsdsConfig.BASE_ID_UART + 0x03000 + instance authenticationRouter: Svc.AuthenticationRouter base id ComCcsdsConfig.BASE_ID_UART + 0x03000 \ + queue size ComCcsdsConfig.QueueSizes.comQueue \ + stack size ComCcsdsConfig.StackSizes.comQueue \ + priority ComCcsdsConfig.Priorities.comQueue instance tcDeframer: Svc.Ccsds.TcDeframer base id ComCcsdsConfig.BASE_ID_UART + 0x04000 @@ -132,7 +135,7 @@ module ComCcsdsUart { # Passive Components instance commsBufferManager instance frameAccumulator - instance fprimeRouter + instance authenticationRouter instance tcDeframer instance spacePacketDeframer instance framer @@ -185,12 +188,12 @@ module ComCcsdsUart { spacePacketDeframer.validateApidSeqCount -> apidManager.validateApidSeqCountIn # SpacePacketDeframer <-> Router - spacePacketDeframer.dataOut -> fprimeRouter.dataIn - fprimeRouter.dataReturnOut -> spacePacketDeframer.dataReturnIn + spacePacketDeframer.dataOut -> authenticationRouter.dataIn + authenticationRouter.dataReturnOut -> spacePacketDeframer.dataReturnIn # Router buffer allocations - fprimeRouter.bufferAllocate -> commsBufferManager.bufferGetCallee - fprimeRouter.bufferDeallocate -> commsBufferManager.bufferSendIn + authenticationRouter.bufferAllocate -> commsBufferManager.bufferGetCallee + authenticationRouter.bufferDeallocate -> commsBufferManager.bufferSendIn } } # end FramingSubtopology diff --git a/FprimeZephyrReference/Components/AmateurRadio/AmateurRadio.cpp b/FprimeZephyrReference/Components/AmateurRadio/AmateurRadio.cpp new file mode 100644 index 00000000..2c9db3b7 --- /dev/null +++ b/FprimeZephyrReference/Components/AmateurRadio/AmateurRadio.cpp @@ -0,0 +1,43 @@ +// ====================================================================== +// \title AmateurRadio.cpp +// \author t38talon +// \brief cpp file for AmateurRadio component implementation class +// ====================================================================== + +#include "FprimeZephyrReference/Components/AmateurRadio/AmateurRadio.hpp" + +#include +namespace Components { + +// ---------------------------------------------------------------------- +// Component construction and destruction +// ---------------------------------------------------------------------- + +AmateurRadio ::AmateurRadio(const char* const compName) : AmateurRadioComponentBase(compName), m_count_names(0) {} + +AmateurRadio ::~AmateurRadio() {} + +// ---------------------------------------------------------------------- +// Handler implementations for commands +// ---------------------------------------------------------------------- + +void AmateurRadio ::Repeat_Name_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, const Fw::CmdStringArg& radio_name) { + // Increment the count + this->m_count_names++; + + // Get current time for telemetry + Fw::Time time = this->getTime(); + + // Write telemetry + this->tlmWrite_count_names(this->m_count_names, time); + + // Emit event + Fw::LogStringArg radioNameArg(radio_name.toChar()); + Fw::LogStringArg satNameArg("Sat1"); + this->log_ACTIVITY_HI_ReapeatingName(radioNameArg, satNameArg); + + // Send command response + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); +} + +} // namespace Components diff --git a/FprimeZephyrReference/Components/AmateurRadio/AmateurRadio.fpp b/FprimeZephyrReference/Components/AmateurRadio/AmateurRadio.fpp new file mode 100644 index 00000000..883b0539 --- /dev/null +++ b/FprimeZephyrReference/Components/AmateurRadio/AmateurRadio.fpp @@ -0,0 +1,51 @@ +module Components { + @ Interactions between Radio Amateurs and Sats + passive component AmateurRadio { + + ############################################################################## + #### Component-specific ports, commands, events, and telemetry #### + ############################################################################## + + @ The satelie will repeat back the radio name + sync command Repeat_Name(radio_name: string size 64) + + @ The satelie will broadcast back the radio name and the sat name + event ReapeatingName(radio_name: string size 64, sat_name: string size 64) \ + severity activity high id 0 \ + format "Repeating name: radio={}, sat={}" + + @ Amount of tags this component has goten + telemetry count_names: U32 + + # @ Example port: receiving calls from the rate group + # sync input port run: Svc.Sched + + # @ Example parameter + # param PARAMETER_NAME: U32 + + ############################################################################### + # 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 + + } +} diff --git a/FprimeZephyrReference/Components/AmateurRadio/AmateurRadio.hpp b/FprimeZephyrReference/Components/AmateurRadio/AmateurRadio.hpp new file mode 100644 index 00000000..0a6b4bc1 --- /dev/null +++ b/FprimeZephyrReference/Components/AmateurRadio/AmateurRadio.hpp @@ -0,0 +1,49 @@ +// ====================================================================== +// \title AmateurRadio.hpp +// \author t38talon +// \brief hpp file for AmateurRadio component implementation class +// ====================================================================== + +#ifndef Components_AmateurRadio_HPP +#define Components_AmateurRadio_HPP + +#include "FprimeZephyrReference/Components/AmateurRadio/AmateurRadioComponentAc.hpp" + +namespace Components { + +class AmateurRadio final : public AmateurRadioComponentBase { + public: + // ---------------------------------------------------------------------- + // Component construction and destruction + // ---------------------------------------------------------------------- + + //! Construct AmateurRadio object + AmateurRadio(const char* const compName //!< The component name + ); + + //! Destroy AmateurRadio object + ~AmateurRadio(); + + private: + // ---------------------------------------------------------------------- + // Handler implementations for commands + // ---------------------------------------------------------------------- + + //! Handler implementation for command Repeat_Name + //! + //! The satelie will repeat back the radio name + void Repeat_Name_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq, //!< The command sequence number + const Fw::CmdStringArg& radio_name) override; + + // ---------------------------------------------------------------------- + // Member variables + // ---------------------------------------------------------------------- + + //! Counter for number of tags received + U32 m_count_names; +}; + +} // namespace Components + +#endif diff --git a/FprimeZephyrReference/Components/AmateurRadio/CMakeLists.txt b/FprimeZephyrReference/Components/AmateurRadio/CMakeLists.txt new file mode 100644 index 00000000..541398e5 --- /dev/null +++ b/FprimeZephyrReference/Components/AmateurRadio/CMakeLists.txt @@ -0,0 +1,36 @@ +#### +# F Prime CMakeLists.txt: +# +# SOURCES: list of source files (to be compiled) +# AUTOCODER_INPUTS: list of files to be passed to the autocoders +# DEPENDS: list of libraries that this module depends on +# +# More information in the F´ CMake API documentation: +# https://fprime.jpl.nasa.gov/latest/docs/reference/api/cmake/API/ +# +#### + +# Module names are derived from the path from the nearest project/library/framework +# root when not specifically overridden by the developer. i.e. The module defined by +# `Ref/SignalGen/CMakeLists.txt` will be named `Ref_SignalGen`. + +register_fprime_library( + AUTOCODER_INPUTS + "${CMAKE_CURRENT_LIST_DIR}/AmateurRadio.fpp" + SOURCES + "${CMAKE_CURRENT_LIST_DIR}/AmateurRadio.cpp" +# DEPENDS +# MyPackage_MyOtherModule +) + +### Unit Tests ### +# register_fprime_ut( +# AUTOCODER_INPUTS +# "${CMAKE_CURRENT_LIST_DIR}/AmateurRadio.fpp" +# SOURCES +# "${CMAKE_CURRENT_LIST_DIR}/test/ut/AmateurRadioTestMain.cpp" +# "${CMAKE_CURRENT_LIST_DIR}/test/ut/AmateurRadioTester.cpp" +# DEPENDS +# STest # For rules-based testing +# UT_AUTO_HELPERS +# ) diff --git a/FprimeZephyrReference/Components/AmateurRadio/docs/sdd.md b/FprimeZephyrReference/Components/AmateurRadio/docs/sdd.md new file mode 100644 index 00000000..fbd53de7 --- /dev/null +++ b/FprimeZephyrReference/Components/AmateurRadio/docs/sdd.md @@ -0,0 +1,38 @@ +# Components::AmateurRadio + +Interactions between Radio Amateurs and Sat + +## Port Descriptions +| Name | Description | +|---|---| +|---|---| + +## Commands +| Name | Description | +|Repeat_Name|The satelie will repeat back the radio name| + +## Events +| Name | Description | +|ReapeatingName|The satelie will broadcast back the radio name and the sat name| +|---|---| + +## Telemetry +| Name | Description | +|count_names|Amount of tags this component has goten| + +## Unit Tests +Add unit test descriptions in the chart below +| Name | Description | Output | Coverage | +|---|---|---|---| +|---|---|---|---| + +## Requirements +Add requirements in the chart below +| Name | Description | Validation | +|---|---|---| +|---|---|---| + +## Change Log +| Date | Description | +|---|---| +|---| Initial Draft | diff --git a/FprimeZephyrReference/Components/AntennaDeployer/AntennaDeployer.cpp b/FprimeZephyrReference/Components/AntennaDeployer/AntennaDeployer.cpp index 956e4073..3967eba0 100644 --- a/FprimeZephyrReference/Components/AntennaDeployer/AntennaDeployer.cpp +++ b/FprimeZephyrReference/Components/AntennaDeployer/AntennaDeployer.cpp @@ -309,11 +309,7 @@ bool AntennaDeployer ::readDeploymentState() { auto file_path = this->paramGet_DEPLOYED_STATE_FILE(is_valid); FW_ASSERT(is_valid == Fw::ParamValid::VALID || is_valid == Fw::ParamValid::DEFAULT); - Os::File file; - Os::File::Status status = file.open(file_path.toChar(), Os::File::OPEN_READ); - bool deployed = (status == Os::File::OP_OK); - (void)file.close(); - return deployed; + return Os::FileSystem::exists(file_path.toChar()); } void AntennaDeployer ::writeDeploymentState() { diff --git a/FprimeZephyrReference/Components/AntennaDeployer/AntennaDeployer.fpp b/FprimeZephyrReference/Components/AntennaDeployer/AntennaDeployer.fpp index 06ce8262..f191970d 100644 --- a/FprimeZephyrReference/Components/AntennaDeployer/AntennaDeployer.fpp +++ b/FprimeZephyrReference/Components/AntennaDeployer/AntennaDeployer.fpp @@ -127,7 +127,7 @@ module Components { param INVALID_THRESHOLD_BOTTOM_CM: F32 default 0.1 @ File path for persistent deployment state (file exists = deployed) - param DEPLOYED_STATE_FILE: string default "/antenna_deployed.bin" + param DEPLOYED_STATE_FILE: string default "//antenna_deployed.bin" ######################################################################## # Standard AC Ports: Required for Channels, Events, Commands, Parameters diff --git a/FprimeZephyrReference/Components/Authenticate/Authenticate.cpp b/FprimeZephyrReference/Components/Authenticate/Authenticate.cpp index edd3d835..50c4695e 100644 --- a/FprimeZephyrReference/Components/Authenticate/Authenticate.cpp +++ b/FprimeZephyrReference/Components/Authenticate/Authenticate.cpp @@ -426,7 +426,6 @@ void Authenticate ::dataIn_handler(FwIndexType portNum, Fw::Buffer& data, const #else // Authentication is disabled - pass data through unchanged // Mark as authenticated (or leave context unchanged) and pass through - // Note: You may want to set authenticated to 0 or leave it as-is depending on your requirements contextOut.set_authenticated( 1); // Pass through as authenticated, or use context.authenticated if you want to preserve #endif diff --git a/FprimeZephyrReference/Components/Authenticate/AuthenticateCfg.hpp b/FprimeZephyrReference/Components/Authenticate/AuthenticateCfg.hpp index 3ea7ac0d..8c93a4d2 100644 --- a/FprimeZephyrReference/Components/Authenticate/AuthenticateCfg.hpp +++ b/FprimeZephyrReference/Components/Authenticate/AuthenticateCfg.hpp @@ -17,7 +17,7 @@ // Set to 1 to enable authentication (default behavior) // Set to 0 to disable authentication (pass-through mode) #ifndef AUTHENTICATE_ENABLED -#define AUTHENTICATE_ENABLED 0 +#define AUTHENTICATE_ENABLED 1 #endif #endif // Components_AuthenticateCfg_HPP diff --git a/FprimeZephyrReference/Components/AuthenticationRouter/AuthenticationRouter.cpp b/FprimeZephyrReference/Components/AuthenticationRouter/AuthenticationRouter.cpp new file mode 100644 index 00000000..3730fb04 --- /dev/null +++ b/FprimeZephyrReference/Components/AuthenticationRouter/AuthenticationRouter.cpp @@ -0,0 +1,451 @@ +// ====================================================================== +// \title AuthenticationRouter.cpp +// \author Ines (based on thomas-bc's FprimeRouter.cpp) +// \brief cpp file for AuthenticationRouter component implementation class +// ====================================================================== + +#include "FprimeZephyrReference/Components/AuthenticationRouter/AuthenticationRouter.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Fw/Com/ComPacket.hpp" +#include "Fw/FPrimeBasicTypes.hpp" +#include "Fw/Logger/Logger.hpp" +#include "Os/File.hpp" +#include "config/ApidEnumAc.hpp" +#include +#include + +constexpr const char LAST_LOSS_TIME_FILE[] = "//loss_max_time.txt"; +constexpr const char LAST_LOSS_TIME_FILE_MONOTONIC[] = "//loss_max_time_monotonic.txt"; +constexpr const char COMMAND_LOSS_EXPIRED_FLAG_FILE[] = "//command_loss_expired_flag.txt"; +constexpr const char BYPASS_AUTHENTICATION_FILE[] = "//bypass_authentification_file.txt"; +constexpr const U8 OP_CODE_LENGTH = 4; // F Prime opcodes are 32-bit (4 bytes) +constexpr const U8 OP_CODE_START = 2; // Opcode starts at byte offset 2 in the packet buffer + +namespace Svc { + +// ---------------------------------------------------------------------- +// Component construction and destruction +// ---------------------------------------------------------------------- + +AuthenticationRouter ::AuthenticationRouter(const char* const compName) : AuthenticationRouterComponentBase(compName) { + // Initialize previous time type flag by checking current time type + Fw::Time time = this->getTime(); + TimeBase b = time.getTimeBase(); + m_previousTypeTimeFlag = (b == TimeBase::TB_PROC_TIME); // true if monotonic, false if RTC + m_TypeTimeFlag = m_previousTypeTimeFlag; // Initialize current flag to match + + this->initializeFiles(LAST_LOSS_TIME_FILE); + this->initializeFiles(LAST_LOSS_TIME_FILE_MONOTONIC); + + // Load the command loss expired flag from file (persists across boots) + m_commandLossTimeExpiredLogged = this->readCommandLossExpiredFlag(); +} +AuthenticationRouter ::~AuthenticationRouter() {} + +// ---------------------------------------------------------------------- +// Handler implementations for user-defined typed input ports +// ---------------------------------------------------------------------- + +void AuthenticationRouter ::schedIn_handler(FwIndexType portNum, U32 context) { + (void)portNum; + (void)context; + + U32 current_loss_time = this->getTimeFromRTC(); + + // Check if time type has switched from RTC to monotonic or vice versa + // If switched, update the appropriate file to prevent false timeout triggers + if (m_previousTypeTimeFlag != m_TypeTimeFlag) { + if (m_previousTypeTimeFlag == false && m_TypeTimeFlag == true) { + // Switched from RTC to monotonic - update monotonic file + this->writeToFile(LAST_LOSS_TIME_FILE_MONOTONIC, current_loss_time); + } else if (m_previousTypeTimeFlag == true && m_TypeTimeFlag == false) { + // Switched from monotonic to RTC - update RTC file + this->writeToFile(LAST_LOSS_TIME_FILE, current_loss_time); + } + // Update previous flag to current flag + m_previousTypeTimeFlag = m_TypeTimeFlag; + } + + // check if the time is RTC or monotonic and set the flag accordingly + // Read from the appropriate file based on the time type + U32 last_loss_time; + if (m_TypeTimeFlag == true) { + last_loss_time = this->readFromFile(LAST_LOSS_TIME_FILE_MONOTONIC); + } else { + last_loss_time = this->readFromFile(LAST_LOSS_TIME_FILE); + } + + // // Check if the last loss time is past the current time + + // Get the LOSS_MAX_TIME parameter + Fw::ParamValid valid; + U32 loss_max_time = this->paramGet_LOSS_MAX_TIME(valid); + + if (current_loss_time >= last_loss_time && (current_loss_time - last_loss_time) > loss_max_time) { + // Timeout condition is met + if (m_commandLossTimeExpiredLogged == false) { + this->log_ACTIVITY_HI_CommandLossTimeExpired(Fw::On::ON); + m_commandLossTimeExpiredLogged = true; + this->writeCommandLossExpiredFlag(true); // Persist flag to file + this->tlmWrite_CommandLossSafeOn(true); + // Only send safemode signal if port is connected + if (this->isConnected_SafeModeOn_OutputPort(0)) { + this->SafeModeOn_out(0); + } + } + // Flag stays true - signal will not be sent again until a command is received + } else { + // Timeout condition is NOT met - reset the flag so event can trigger again if timeout occurs later + if (m_commandLossTimeExpiredLogged == true) { + m_commandLossTimeExpiredLogged = false; + this->writeCommandLossExpiredFlag(false); // Persist flag to file + } + } +} + +U32 AuthenticationRouter ::getTimeFromRTC() { + // TODO: Get time from the rtc here + // use the RtcManager timeGetPort to get the time + // getTime() automatically calls the timeCaller port which is connected to RtcManager + Fw::Time time = this->getTime(); + + // Check if the time is RTC or monotonic and set the flag accordingly + TimeBase b = time.getTimeBase(); + if (b == TimeBase::TB_PROC_TIME) { + // monotonic time + m_TypeTimeFlag = true; + } else { + // RTC time + m_TypeTimeFlag = false; + } + + return time.getSeconds(); +} + +U32 AuthenticationRouter ::writeToFile(const char* filePath, U32 time) { + // TO DO: Add File Opening Error Handling here and in all the other file functions + Os::File file; + // Use OPEN_CREATE with OVERWRITE to ensure the file is properly overwritten + // This ensures data persists across boots and the file is fully overwritten + Os::File::Status openStatus = file.open(filePath, Os::File::OPEN_CREATE, Os::File::OverwriteType::OVERWRITE); + if (openStatus == Os::File::OP_OK) { + const U8* buffer = reinterpret_cast(&time); + FwSizeType size = static_cast(sizeof(time)); + // Use WAIT to ensure data is synced to disk (fsync/flush) + (void)file.write(buffer, size, Os::File::WaitType::WAIT); + file.close(); + // Note: Specific reason for overwrite is printed at the call site + } + return time; +} + +U32 AuthenticationRouter ::readFromFile(const char* filePath) { + Os::File file; + U32 time = 0; + Os::File::Status openStatus = file.open(filePath, Os::File::OPEN_READ); + if (openStatus == Os::File::OP_OK) { + FwSizeType size = static_cast(sizeof(time)); + FwSizeType expectedSize = size; + Os::File::Status readStatus = file.read(reinterpret_cast(&time), size, Os::File::WaitType::WAIT); + file.close(); + if (readStatus == Os::File::OP_OK && size == expectedSize) { + return time; + } + } + return time; +} + +bool AuthenticationRouter ::readCommandLossExpiredFlag() { + Os::File file; + bool flag = false; + Os::File::Status openStatus = file.open(COMMAND_LOSS_EXPIRED_FLAG_FILE, Os::File::OPEN_READ); + if (openStatus == Os::File::OP_OK) { + FwSizeType size = static_cast(sizeof(flag)); + FwSizeType expectedSize = size; + Os::File::Status readStatus = file.read(reinterpret_cast(&flag), size, Os::File::WaitType::WAIT); + file.close(); + if (readStatus == Os::File::OP_OK && size == expectedSize) { + return flag; + } + } + // File doesn't exist or read failed - return false (default) + return false; +} + +void AuthenticationRouter ::writeCommandLossExpiredFlag(bool flag) { + Os::File file; + Os::File::Status openStatus = + file.open(COMMAND_LOSS_EXPIRED_FLAG_FILE, Os::File::OPEN_CREATE, Os::File::OverwriteType::OVERWRITE); + if (openStatus == Os::File::OP_OK) { + const U8* buffer = reinterpret_cast(&flag); + FwSizeType size = static_cast(sizeof(flag)); + (void)file.write(buffer, size, Os::File::WaitType::WAIT); + file.close(); + } +} + +U32 AuthenticationRouter ::initializeFiles(const char* filePath) { + U32 last_loss_time = this->getTimeFromRTC(); + bool loadedFromFile = false; + bool timeIsValid = false; + + Os::File file; + + // Check if the file exists and is readable + Os::File::Status openStatus = file.open(filePath, Os::File::OPEN_READ); + if (openStatus == Os::File::OP_OK) { + FwSizeType size = static_cast(sizeof(last_loss_time)); + FwSizeType expectedSize = size; + Os::File::Status readStatus = file.read(reinterpret_cast(&last_loss_time), size, Os::File::WaitType::WAIT); + file.close(); + loadedFromFile = (readStatus == Os::File::OP_OK) && (size == expectedSize); + + // Validate the stored time - only check if it's 0 (RTC wasn't initialized when written) + // If it's not 0, preserve it regardless of value (no range checking) + if (loadedFromFile) { + // If time is 0, it means RTC wasn't initialized when file was written - should overwrite + if (last_loss_time == 0) { + timeIsValid = false; // Force overwrite + } else { + // Any non-zero value is considered valid - preserve it + timeIsValid = true; + } + } + } + + // Create or overwrite file if: + // 1. File doesn't exist, OR + // 2. File contains 0 (RTC wasn't initialized when written), OR + // 3. File contains invalid time + if (!loadedFromFile || !timeIsValid) { + last_loss_time = this->getTimeFromRTC(); + Os::File::Status createStatus = file.open(filePath, Os::File::OPEN_CREATE, Os::File::OverwriteType::OVERWRITE); + if (createStatus == Os::File::OP_OK) { + const U8* buffer = reinterpret_cast(&last_loss_time); + FwSizeType size = static_cast(sizeof(last_loss_time)); + (void)file.write(buffer, size, Os::File::WaitType::WAIT); + file.close(); + } + } + + return last_loss_time; +} + +// Helper function to convert binary data to hex string for debugging +static void printHexData(const U8* data, size_t length, const char* label) { + if (data == nullptr || length == 0) { + printk("%s: (null or empty)\n", label); + return; + } + printk("%s: ", label); + for (size_t i = 0; i < length; i++) { + printk("%02X ", data[i]); + } + printk("\n"); +} + +bool AuthenticationRouter ::BypassesAuthentification(Fw::Buffer& packetBuffer) { + // Extract opCode from packet buffer + printk("\n"); + printHexData(packetBuffer.getData(), packetBuffer.getSize(), "packetBuffer.getData()"); + printk("OP_CODE_START %d \n", OP_CODE_START); + printk("OP_CODE_LENGTH %d \n", OP_CODE_LENGTH); + + // Check bounds before extracting + if (packetBuffer.getSize() < (OP_CODE_START + OP_CODE_LENGTH)) { + printk("ERROR: Buffer too small! Need %d bytes starting at offset %d, but buffer only has %zu bytes\n", + OP_CODE_LENGTH, OP_CODE_START, packetBuffer.getSize()); + return false; + } + + // Extract opcode bytes + std::vector opCodeBytes(OP_CODE_LENGTH); + std::memcpy(&opCodeBytes[0], packetBuffer.getData() + OP_CODE_START, OP_CODE_LENGTH); + printHexData(opCodeBytes.data(), OP_CODE_LENGTH, "opCode"); + + // Convert opcode bytes to hex string (uppercase, no spaces) + std::stringstream hexStream; + hexStream << std::hex << std::uppercase << std::setfill('0'); + for (size_t i = 0; i < OP_CODE_LENGTH; i++) { + hexStream << std::setw(2) << static_cast(opCodeBytes[i]); + } + std::string opCodeHex = hexStream.str(); + printk("opCodeHex: %s\n", opCodeHex.c_str()); + + // Open file + Os::File bypassOpCodesFile; + Os::File::Status openStatus = bypassOpCodesFile.open(BYPASS_AUTHENTICATION_FILE, Os::File::OPEN_READ); + if (openStatus != Os::File::OP_OK) { + this->log_WARNING_HI_FileOpenError(openStatus); + return false; + } + + FwSizeType fileSize = 0; + Os::File::Status sizeStatus = bypassOpCodesFile.size(fileSize); + if (sizeStatus != Os::File::OP_OK || fileSize == 0) { + bypassOpCodesFile.close(); + this->log_WARNING_HI_FileOpenError(sizeStatus); + return false; + } + + // Read file contents as text + std::string fileContents; + fileContents.resize(static_cast(fileSize), '\0'); + FwSizeType bytesToRead = fileSize; + Os::File::Status readStatus = + bypassOpCodesFile.read(reinterpret_cast(&fileContents[0]), bytesToRead, Os::File::WaitType::WAIT); + bypassOpCodesFile.close(); + + if (readStatus != Os::File::OP_OK || bytesToRead != fileSize) { + this->log_WARNING_HI_FileOpenError(readStatus); + return false; + } + + // Parse file line by line and check if opcode hex string matches + std::istringstream fileStream(fileContents); + printk("fileContents: %s\n", fileContents.c_str()); + std::string line; + while (std::getline(fileStream, line)) { + // Check if this line matches the opcode (case-insensitive) + if (line == opCodeHex) { + printk("Found matching opcode in bypass file: %s\n", line.c_str()); + return true; + } else { + printk("No matching opcode in bypass file: %s\n", line.c_str()); + printk("opCodeHex: %s\n", opCodeHex.c_str()); + printk("line: %s\n", line.c_str()); + } + } + + return false; +} + +void AuthenticationRouter ::dataIn_handler(FwIndexType portNum, + Fw::Buffer& packetBuffer, + const ComCfg::FrameContext& context) { + // Any packet received resets the flag and updates the last loss time + printk("context %d", context.get_authenticated()); + + // Check if the OpCodes are in the OpCode list + // TODO + bool by = this->BypassesAuthentification(packetBuffer); + printk("\n bypasses %d \n", by); + bool bypasses = true; // this->BypassesAuthentification(packetBuffer); + + // the packet was not authenticated + if (context.get_authenticated() == 0 && bypasses == false) { + // emit reject packet event + this->log_ACTIVITY_LO_PassedRouter(0); + // Return ownership of the incoming packetBuffer + this->dataReturnOut_out(0, packetBuffer, context); + return; + } + + if (bypasses == true) { + // emit bypass event + this->log_ACTIVITY_LO_BypassedAuthentification(); + } + + U32 current_time = this->getTimeFromRTC(); + if (m_TypeTimeFlag == true) { + this->writeToFile(LAST_LOSS_TIME_FILE_MONOTONIC, current_time); + } else { + this->writeToFile(LAST_LOSS_TIME_FILE, current_time); + } + // Reset the flag when any packet is received + m_commandLossTimeExpiredLogged = false; + this->writeCommandLossExpiredFlag(false); // Persist flag to file + // Update telemetry with the command loss safe on status + this->tlmWrite_CommandLossSafeOn(false); + this->tlmWrite_LastCommandPacketTime(static_cast(current_time)); + + Fw::SerializeStatus status; + Fw::ComPacketType packetType = context.get_apid(); + // Route based on received APID (packet type) + switch (packetType) { + // Handle a command packet + case Fw::ComPacketType::FW_PACKET_COMMAND: { + // Update telemetry with the last command packet time + // Allocate a com buffer on the stack + Fw::ComBuffer com; + // Copy the contents of the packet buffer into the com buffer + status = com.setBuff(packetBuffer.getData(), packetBuffer.getSize()); + if (status == Fw::FW_SERIALIZE_OK) { + // Send the com buffer - critical functionality so it is considered an error not to + // have the port connected. This is why we don't check isConnected() before sending. + this->commandOut_out(0, com, 0); + } else { + this->log_WARNING_HI_SerializationError(status); + } + break; + } + // Handle a file packet + case Fw::ComPacketType::FW_PACKET_FILE: { + // If the file uplink output port is connected, send the file packet. Otherwise take no action. + if (this->isConnected_fileOut_OutputPort(0)) { + // Copy buffer into a new allocated buffer. This lets us return the original buffer with dataReturnOut, + // and AuthenticationRouter can handle the deallocation of the file buffer when it returns on + // fileBufferReturnIn + Fw::Buffer packetBufferCopy = this->bufferAllocate_out(0, packetBuffer.getSize()); + auto copySerializer = packetBufferCopy.getSerializer(); + status = copySerializer.serializeFrom(packetBuffer.getData(), packetBuffer.getSize(), + Fw::Serialization::OMIT_LENGTH); + FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status); + // Send the copied buffer out. It will come back on fileBufferReturnIn once the receiver is done with it + this->fileOut_out(0, packetBufferCopy); + } + break; + } + default: { + // Packet type is not known to the F Prime protocol. If the unknownDataOut port is + // connected, forward packet and context for further processing + if (this->isConnected_unknownDataOut_OutputPort(0)) { + // Copy buffer into a new allocated buffer. This lets us return the original buffer with dataReturnOut, + // and AuthenticationRouter can handle the deallocation of the unknown buffer when it returns on + // bufferReturnIn + Fw::Buffer packetBufferCopy = this->bufferAllocate_out(0, packetBuffer.getSize()); + auto copySerializer = packetBufferCopy.getSerializer(); + status = copySerializer.serializeFrom(packetBuffer.getData(), packetBuffer.getSize(), + Fw::Serialization::OMIT_LENGTH); + FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status); + // Send the copied buffer out. It will come back on fileBufferReturnIn once the receiver is done with it + this->unknownDataOut_out(0, packetBufferCopy, context); + } + } + } + + // Return ownership of the incoming packetBuffer + this->dataReturnOut_out(0, packetBuffer, context); +} + +void AuthenticationRouter ::cmdResponseIn_handler(FwIndexType portNum, + FwOpcodeType opcode, + U32 cmdSeq, + const Fw::CmdResponse& response) { + (void)portNum; + (void)opcode; + (void)cmdSeq; + (void)response; + // This is a no-op because AuthenticationRouter does not need to handle command responses + // but the port must be connected +} + +void AuthenticationRouter ::fileBufferReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) { + this->bufferDeallocate_out(0, fwBuffer); +} + +} // namespace Svc diff --git a/FprimeZephyrReference/Components/AuthenticationRouter/AuthenticationRouter.fpp b/FprimeZephyrReference/Components/AuthenticationRouter/AuthenticationRouter.fpp new file mode 100644 index 00000000..d6418672 --- /dev/null +++ b/FprimeZephyrReference/Components/AuthenticationRouter/AuthenticationRouter.fpp @@ -0,0 +1,136 @@ +module Svc { + @ Routes packets deframed by the Deframer to the rest of the system + active component AuthenticationRouter { + + # ---------------------------------------------------------------------- + # Router interface (ports defined explicitly for active component) + # ---------------------------------------------------------------------- + # Port receiving calls from the rate group + async input port schedIn: Svc.Sched + + # Receiving data (Fw::Buffer) to be routed with optional context to help with routing + async input port dataIn: Svc.ComDataWithContext + + # Port for returning ownership of data (includes Fw.Buffer) received on dataIn + output port dataReturnOut: Svc.ComDataWithContext + + # Port for sending file packets as Fw::Buffer (ownership passed to receiver) + output port fileOut: Fw.BufferSend + + # Port for receiving ownership back of buffers sent on fileOut + sync input port fileBufferReturnIn: Fw.BufferSend + + # Port for sending command packets as Fw::ComBuffers + output port commandOut: Fw.Com + + # Port for receiving command responses from a command dispatcher (can be a no-op) + sync input port cmdResponseIn: Fw.CmdResponse + + @ Port for forwarding non-recognized packet types + @ Ownership of the buffer is retained by the AuthenticationRouter, meaning receiving + @ components should either process data synchronously, or copy the data if needed + output port unknownDataOut: Svc.ComDataWithContext + + @ Port for allocating buffers + output port bufferAllocate: Fw.BufferGet + + @ Port for deallocating buffers + output port bufferDeallocate: Fw.BufferSend + + @ An error occurred while serializing a com buffer + event SerializationError( + status: U32 @< The status of the operation + ) \ + severity warning high \ + format "Serializing com buffer failed with status {}" + + @ An error occurred while deserializing a packet + event DeserializationError( + status: U32 @< The status of the operation + ) \ + severity warning high \ + format "Deserializing packet type failed with status {}" + + + ############################################################################### + # Standard AC Ports for Events + ############################################################################### + @ Command receive port + command recv port cmdIn + + @ Command registration port + command reg port cmdRegOut + + @ Command response port + command resp port cmdResponseOut + + @ Port for requesting the current time + time get port timeCaller + + @ Port for sending textual representation of events + text event port logTextOut + + @ Port for sending events to downlink + event port logOut + + @ Telemetry port + telemetry port tlmOut + + @ Parameter get port + param get port prmGetOut + + @ Parameter set port + param set port prmSetOut + + ################################################ + # Custom For This Router + ################################# + + @ Port for sending signal to safemode + output port SafeModeOn: Fw.Signal + + @ event to emit when command loss time expires + event CommandLossTimeExpired(safemode: Fw.On) \ + severity activity high \ + format "SafeModeOn: {}" + + @ Emits Current Loss Time + event CurrentLossTime(loss_max_time: U32) \ + severity activity low \ + format "Current Loss Time: {}" + + @ Emitted to indicate whether a packet passed through the router + event PassedRouter(passed: U8) \ + severity activity low \ + format "PassedRouter: {}" + + @ Emitted to indicate whether authentication was bypassed + event BypassedAuthentification() \ + severity activity low \ + format "OpCode BypassedAuthentification" + + event FileOpenError(openStatus: U8) \ + severity warning high \ + format "File Open Error {} for BypassOpCodes file. No Opcodes will be Bypassed" + + @ Telemetry Channel to commit the time of the last command packet + telemetry LastCommandPacketTime : U64 + + @ Telemetry Channel for the status of the command loss time sending to safe mode + telemetry CommandLossSafeOn : bool + + @ Telemetry Channel for Number of Packets that have bypassed the router + telemetry ByPassedRouter : U64 + + @ Telemetry Channel for Number of Packets that have passed the router + telemetry PassedRouter : U64 + + @ Telemetry Channel for Number of Packets that have not passed the Router + telemetry FailedRouter : U64 + + @ loss time max parameter: Right now set to 3 days 259200 seconds + param LOSS_MAX_TIME : U32 default 259200 + + + } +} diff --git a/FprimeZephyrReference/Components/AuthenticationRouter/AuthenticationRouter.hpp b/FprimeZephyrReference/Components/AuthenticationRouter/AuthenticationRouter.hpp new file mode 100644 index 00000000..e3b16279 --- /dev/null +++ b/FprimeZephyrReference/Components/AuthenticationRouter/AuthenticationRouter.hpp @@ -0,0 +1,108 @@ +// ====================================================================== +// \title AuthenticationRouter.hpp +// \author Ines (based on thomas-bc's FprimeRouter.hpp) +// \brief hpp file for AuthenticationRouter component implementation class +// ====================================================================== + +#ifndef Svc_AuthenticationRouter_HPP +#define Svc_AuthenticationRouter_HPP + +#include "FprimeZephyrReference/Components/AuthenticationRouter/AuthenticationRouterComponentAc.hpp" + +namespace Svc { + +class AuthenticationRouter final : public AuthenticationRouterComponentBase { + public: + // ---------------------------------------------------------------------- + // Component construction and destruction + // ---------------------------------------------------------------------- + + //! Construct AuthenticationRouter object + AuthenticationRouter(const char* const compName //!< The component name + ); + + //! Destroy AuthenticationRouter object + ~AuthenticationRouter(); + + private: + // ---------------------------------------------------------------------- + // Handler implementations for user-defined typed input ports + // ---------------------------------------------------------------------- + + //! Handler implementation for schedIn + //! Port receiving calls from the rate group + void schedIn_handler(FwIndexType portNum, //!< The port number + U32 context //!< The call order + ) override; + + //! Handler implementation for bufferIn + //! Receiving Fw::Buffer from Deframer + void dataIn_handler(FwIndexType portNum, //!< The port number + Fw::Buffer& packetBuffer, //!< The packet buffer + const ComCfg::FrameContext& context //!< The context object + ) override; + + // ! Handler for input port cmdResponseIn + // ! This is a no-op because AuthenticationRouter does not need to handle command responses + // ! but the port must be connected + void cmdResponseIn_handler(FwIndexType portNum, //!< The port number + FwOpcodeType opcode, //!< The command opcode + U32 cmdSeq, //!< The command sequence number + const Fw::CmdResponse& response //!< The command response + ) override; + + //! Handler implementation for fileBufferReturnIn + //! + //! Port for receiving ownership back of buffers sent on fileOut + void fileBufferReturnIn_handler(FwIndexType portNum, //!< The port number + Fw::Buffer& fwBuffer //!< The buffer + ) override; + + //! Handler implementation for initializeFiles + //! + //! Port for initializing the files + U32 initializeFiles(const char* filePath); + + //! Handler implementation for writeToFile + //! + //! Writes the time to the file + U32 writeToFile(const char* filePath, U32 time); + + //! Handler implementation for readFromFile + //! + //! Reads the time from the file + U32 readFromFile(const char* filePath); + + //! Handler implementation for getTimeFromRTC + //! + //! Gets the time from the RTC + U32 getTimeFromRTC(); + + //! Reads the command loss expired flag from file + //! + //! Returns the flag value from file, or false if file doesn't exist + bool readCommandLossExpiredFlag(); + + //! Writes the command loss expired flag to file + //! + //! Persists the flag value to file + void writeCommandLossExpiredFlag(bool flag); + + //! Checks whether or not the opcode of the packet is in the list of + //! opcodes that bypassauthentification + bool BypassesAuthentification(Fw::Buffer& packetBuffer); + + Fw::On m_state_rtc = Fw::On::OFF; // keeps track if the RTC is on or if we are using Zephyr's time + std::atomic m_commandLossTimeCounter; // makes this an atomic variable (so its set only in one command), + + bool m_TypeTimeFlag; //!< Flag to indicate if the time is RTC or monotonic + bool m_previousTypeTimeFlag; //!< Flag to track previous time type to detect switches + bool m_commandLossTimeExpiredLogged = false; //!< Flag to indicate if the command loss time has expired, false by + //!< default meaning no command loss time has expired yet + // if the file system fails this will prevent it from being stuck in a boot loop. Need to ensure false is written to + // the file when the system by default +}; + +} // namespace Svc + +#endif diff --git a/FprimeZephyrReference/Components/AuthenticationRouter/CMakeLists.txt b/FprimeZephyrReference/Components/AuthenticationRouter/CMakeLists.txt new file mode 100644 index 00000000..e4e8b55a --- /dev/null +++ b/FprimeZephyrReference/Components/AuthenticationRouter/CMakeLists.txt @@ -0,0 +1,26 @@ +#### +# 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}/AuthenticationRouter.fpp" + "${CMAKE_CURRENT_LIST_DIR}/AuthenticationRouter.cpp" +) +register_fprime_module() + + +#### UTS #### +set(UT_SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/AuthenticationRouter.fpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/AuthenticationRouter.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/AuthenticationRouter.cpp" +) +set(UT_AUTO_HELPERS ON) + + +register_fprime_ut() diff --git a/FprimeZephyrReference/Components/AuthenticationRouter/docs/sdd.md b/FprimeZephyrReference/Components/AuthenticationRouter/docs/sdd.md new file mode 100644 index 00000000..cd9566f6 --- /dev/null +++ b/FprimeZephyrReference/Components/AuthenticationRouter/docs/sdd.md @@ -0,0 +1,91 @@ +# Svc::AuthenticationRouter + +The `Svc::AuthenticationRouter` component routes F´ packets (such as command or file packets) to other components. It is based on the FPrime Router, explained and linked later in the sdd, with two exceptions + +1. Command Loss Time component, which checks how long since commands have last been routed through the satellite. If it has been too long (changeable parameter) it will emit an event and send out to safemode, to conserve power +As a result of this component writing to the filesystem, this component is active, while the usual FprimeRouter is passive. + +2. This component handles authenticated packets. It will check a list of preconfigured opcodes that do not need to be authenticated, will pass those on as well as the things that are authenticated + +The `Svc::AuthenticationRouter` component receives F´ packets (as [Fw::Buffer](../../../Fw/Buffer/docs/sdd.md) objects) and routes them to other components through synchronous port calls. The input port of type `Svc.ComDataWithContext` passes this Fw.Buffer object along with optional context data which can help for routing. The current F Prime protocol does not use this context data, but is nevertheless present in the interface for compatibility with other protocols which may for example pass APIDs in the frame headers. + +The `Svc::AuthenticationRouter` component supports `Fw::ComPacketType::FW_PACKET_COMMAND` and `Fw::ComPacketType::FW_PACKET_FILE` packet types. Unknown packet types are forwarded on the `unknownDataOut` port, which a project-specific component can connect to for custom routing. + +About memory management, all buffers sent by `Svc::AuthenticationRouter` on the `fileOut` and `unknownDataOut` ports are expected to be returned to the router through the `fileBufferReturnIn` port for deallocation. + +## Custom Routing + +The `Svc::AuthenticationRouter` component is designed to be extensible through the use of a project-specific router. The `unknownDataOut` port can be connected to a project-specific component that can receive all unknown packet types. This component can then implement custom handling of these unknown packets. After processing, the project-specific component shall return the received buffer to the `Svc::AuthenticationRouter` component through the `fileBufferReturnIn` port (named this way as it only receives file packets in the common use-case), which will deallocate the buffer. + +## Usage Examples + +The `Svc::FprimeRouter` component is used in the uplink stack of many reference F´ application such as [the tutorials source code](https://github.com/fprime-community#tutorials). + +### Typical Usage + +In the canonical uplink communications stack, `Svc::FprimeRouter` is connected to a [Svc::CmdDispatcher](../../CmdDispatcher/docs/sdd.md) and a [Svc::FileUplink](../../FileUplink/docs/sdd.md) component, to receive Command and File packets respectively. + +![uplink_stack](../../FprimeDeframer/docs/img/deframer_uplink_stack.png) + +## Port Descriptions + +| Kind | Name | Type | Description | +|---|---|---|---| +| `async input` | `schedIn` | `Svc.Sched` | Port receiving calls from the rate group for periodic command loss time checking | +| `async input` | `dataIn` | `Svc.ComDataWithContext` | Receiving Fw::Buffer with context buffer from Deframer | +| `output` | `dataReturnOut` | `Svc.ComDataWithContext` | Returning ownership of buffer received on `dataIn` | +| `output` | `commandOut` | `Fw.Com` | Port for sending command packets as Fw::ComBuffers | +| `output` | `fileOut` | `Fw.BufferSend` | Port for sending file packets as Fw::Buffer (ownership passed to receiver) | +| `sync input` | `fileBufferReturnIn` | `Fw.BufferSend` | Receiving back ownership of buffer sent on `fileOut` and `unknownDataOut` | +| `sync input` | `cmdResponseIn` | `Fw.CmdResponse` | Port for receiving command responses from a command dispatcher (can be a no-op) | +| `output` | `unknownDataOut` | `Svc.ComDataWithContext` | Port forwarding unknown data (useful for adding custom routing rules with a project-defined router) | +| `output` | `bufferAllocate` | `Fw.BufferGet` | Port for allocating buffers, allowing copy of received data | +| `output` | `bufferDeallocate` | `Fw.BufferSend` | Port for deallocating buffers | +| `output` | `SafeModeOn` | `Fw.Signal` | Port for sending signal to safemode when command loss time expires | + +## Requirements + +| Name | Description | Rationale | Validation | +|---|---|---|---| +SVC-ROUTER-001 | `Svc::AuthenticationRouter` shall route packets based on their packet type as indicated by the packet header | Routing mechanism of the F´ comms protocol | Unit test | +SVC-ROUTER-002 | `Svc::AuthenticationRouter` shall route packets of type `Fw::ComPacketType::FW_PACKET_COMMAND` to the `commandOut` output port. | Routing command packets | Unit test | +SVC-ROUTER-003 | `Svc::AuthenticationRouter` shall route packets of type `Fw::ComPacketType::FW_PACKET_FILE` to the `fileOut` output port. | Routing file packets | Unit test | +SVC-ROUTER-004 | `Svc::AuthenticationRouter` shall route data that is neither `Fw::ComPacketType::FW_PACKET_COMMAND` nor `Fw::ComPacketType::FW_PACKET_FILE` to the `unknownDataOut` output port. | Allows for projects to provide custom routing for additional (project-specific) uplink data types | Unit test | +SVC-ROUTER-005 | `Svc::AuthenticationRouter` shall emit warning events if serialization errors occur during processing of incoming packets | Aid in diagnosing uplink issues | Unit test | +SVC-ROUTER-005 | `Svc::AuthenticationRouter` shall make a copy of buffers that represent a `FW_PACKET_FILE` | Aid in memory management of file buffers | Unit test | +SVC-ROUTER-006 | `Svc::AuthenticationRouter` shall return ownership of all buffers received on `dataIn` through `dataReturnOut` | Memory management | Unit test | +SVC-ROUTER-007 | `Svc::AuthenticationRouter` shall check command loss time periodically via the `schedIn` port | Command loss time monitoring | Unit test | +SVC-ROUTER-008 | `Svc::AuthenticationRouter` shall emit `CommandLossTimeExpired` event when command loss time exceeds `LOSS_MAX_TIME` parameter | Command loss time monitoring | Unit test | +SVC-ROUTER-009 | `Svc::AuthenticationRouter` shall send `SafeModeOn` signal when command loss time expires | Safe mode activation | Unit test | +SVC-ROUTER-010 | `Svc::AuthenticationRouter` shall update `LastCommandPacketTime` telemetry when a packet is received | Telemetry tracking | Unit test | +SVC-ROUTER-011 | `Svc::AuthenticationRouter` shall use the 0/1 setting in `authenticateCfg.hpp` to enable or disable authentication functionality in this router component | Authentication configuration | Unit test | +SVC-ROUTER-012 | `Svc::AuthenticationRouter` shall check a file for OpCodes that do not require authentication | OpCode exemption list | Unit test | +SVC-ROUTER-013 | `Svc::AuthenticationRouter` shall only pass authenticated commands and files that are not on the list of opcodes to be executed | Authentication filtering | Unit test | +SVC-ROUTER-014 | `Svc::AuthenticationRouter` shall pass non-authenticated commands and events backwards to `dataOut` | Non-authenticated packet routing | Unit test | +SVC-ROUTER-015 | `Svc::AuthenticationRouter` shall emit events for authenticated commands and non-authenticated commands and whether they passed through the router | Authentication event reporting | Unit test | + +## Events + +| Name | Severity | Parameters | Description | +|---|---|---|---| +| CommandLossTimeExpired | Activity High | safemode: Fw.On | Emitted when command loss time expires. Format: "SafeModeOn: {}" | +| CurrentLossTime | Activity Low | loss_max_time: U32 | Emitted with current loss time information. Format: "Current Loss Time: {}" | +| PassedRouter | Activity Low | passed: Fw.Bool | Emitted to indicate whether a packet passed through the router. Format: "PassedRouter: {}" | +| BypassAuthentification | Activity Low | bypassed: Fw.Bool | Emitted to indicate whether authentication was bypassed. Format: "BypassAuthentification: {}" | + + +## Telemetry Channels + +| Name | Type | Description | +|---|---|---| +| LastCommandPacketTime | U64 | The time of the last command packet | +| CommandLossSafeOn | bool | The status of the command loss time sending to safe mode | +| ByPassedRouter | U64 | Number of Packets that have bypassed the router | +| PassedRouter | U64 | Number of Packets that have passed the router | +| FailedRouter | U64 | Number of Packets that have not passed the Router | + +## Parameters + +| Name | Type | Default | Description | +|---|---|---|---| +| LOSS_MAX_TIME | U32 | 10 | The maximum amount of time (in seconds) since the last command before triggering safe mode | diff --git a/FprimeZephyrReference/Components/AuthenticationRouter/test/ut/FprimeRouterTestMain.cpp b/FprimeZephyrReference/Components/AuthenticationRouter/test/ut/FprimeRouterTestMain.cpp new file mode 100644 index 00000000..5d9c6458 --- /dev/null +++ b/FprimeZephyrReference/Components/AuthenticationRouter/test/ut/FprimeRouterTestMain.cpp @@ -0,0 +1,45 @@ +// ====================================================================== +// \title FprimeRouterTestMain.cpp +// \author thomas-bc +// \brief cpp file for FprimeRouter component test main function +// ====================================================================== + +#include + +#include "FprimeRouterTester.hpp" + +TEST(FprimeRouter, TestComInterface) { + COMMENT("Route a com packet"); + Svc::FprimeRouterTester tester; + tester.testRouteComInterface(); +} +TEST(FprimeRouter, TestFileInterface) { + COMMENT("Route a file packet"); + Svc::FprimeRouterTester tester; + tester.testRouteFileInterface(); +} +TEST(FprimeRouter, TestUnknownInterface) { + COMMENT("Route a packet of unknown type"); + Svc::FprimeRouterTester tester; + tester.testRouteUnknownPacket(); +} +TEST(FprimeRouter, TestRouteUnknownPacketUnconnected) { + COMMENT("Attempt to route a packet of unknown type with no port connected"); + Svc::FprimeRouterTester tester(true); + tester.testRouteUnknownPacketUnconnected(); +} +TEST(FprimeRouter, TestBufferReturn) { + COMMENT("Deallocate a returning buffer"); + Svc::FprimeRouterTester tester; + tester.testBufferReturn(); +} +TEST(FprimeRouter, TestCommandResponse) { + COMMENT("Handle a command response (no-op)"); + Svc::FprimeRouterTester tester; + tester.testCommandResponse(); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/FprimeZephyrReference/Components/AuthenticationRouter/test/ut/FprimeRouterTester.cpp b/FprimeZephyrReference/Components/AuthenticationRouter/test/ut/FprimeRouterTester.cpp new file mode 100644 index 00000000..037bad1b --- /dev/null +++ b/FprimeZephyrReference/Components/AuthenticationRouter/test/ut/FprimeRouterTester.cpp @@ -0,0 +1,125 @@ +// ====================================================================== +// \title FprimeRouterTester.cpp +// \author thomas-bc +// \brief cpp file for FprimeRouter component test harness implementation class +// ====================================================================== + +#include "FprimeRouterTester.hpp" + +namespace Svc { + +// ---------------------------------------------------------------------- +// Construction and destruction +// ---------------------------------------------------------------------- + +FprimeRouterTester ::FprimeRouterTester(bool disconnect_unknownData_port) + : FprimeRouterGTestBase("FprimeRouterTester", FprimeRouterTester::MAX_HISTORY_SIZE), component("FprimeRouter") { + this->initComponents(); + if (disconnect_unknownData_port) { + this->connectPortsExceptUnknownData(); // hand-coded function connecting all ports except unknownData + } else { + this->connectPorts(); // autocoded function connecting all ports + } +} + +FprimeRouterTester ::~FprimeRouterTester() {} + +// ---------------------------------------------------------------------- +// Test Cases +// ---------------------------------------------------------------------- + +void FprimeRouterTester ::testRouteComInterface() { + this->mockReceivePacketType(Fw::ComPacketType::FW_PACKET_COMMAND); + ASSERT_from_commandOut_SIZE(1); // one command packet emitted + ASSERT_from_fileOut_SIZE(0); // no file packet emitted + ASSERT_from_unknownDataOut_SIZE(0); // no unknown data emitted + ASSERT_from_dataReturnOut_SIZE(1); // data ownership should always be returned + ASSERT_from_bufferAllocate_SIZE(0); // no buffer allocation for Com packets +} + +void FprimeRouterTester ::testRouteFileInterface() { + this->mockReceivePacketType(Fw::ComPacketType::FW_PACKET_FILE); + ASSERT_from_commandOut_SIZE(0); // no command packet emitted + ASSERT_from_fileOut_SIZE(1); // one file packet emitted + ASSERT_from_unknownDataOut_SIZE(0); // no unknown data emitted + ASSERT_from_dataReturnOut_SIZE(1); // data ownership should always be returned + ASSERT_from_bufferAllocate_SIZE(1); // file packet was copied into a new allocated buffer +} + +void FprimeRouterTester ::testRouteUnknownPacket() { + this->mockReceivePacketType(Fw::ComPacketType::FW_PACKET_UNKNOWN); + ASSERT_from_commandOut_SIZE(0); // no command packet emitted + ASSERT_from_fileOut_SIZE(0); // no file packet emitted + ASSERT_from_unknownDataOut_SIZE(1); // one unknown data emitted + ASSERT_from_dataReturnOut_SIZE(1); // data ownership should always be returned + ASSERT_from_bufferAllocate_SIZE(1); // unknown packet was copied into a new allocated buffer +} + +void FprimeRouterTester ::testRouteUnknownPacketUnconnected() { + this->mockReceivePacketType(Fw::ComPacketType::FW_PACKET_UNKNOWN); + ASSERT_from_commandOut_SIZE(0); // no command packet emitted + ASSERT_from_fileOut_SIZE(0); // no file packet emitted + ASSERT_from_unknownDataOut_SIZE(0); // zero unknown data emitted when port is unconnected + ASSERT_from_dataReturnOut_SIZE(1); // data ownership should always be returned + ASSERT_from_bufferAllocate_SIZE(0); // no buffer allocation when port is unconnected +} + +void FprimeRouterTester ::testBufferReturn() { + U8 data[1]; + Fw::Buffer buffer(data, sizeof(data)); + this->invoke_to_fileBufferReturnIn(0, buffer); + ASSERT_from_bufferDeallocate_SIZE(1); // incoming buffer should be deallocated + ASSERT_EQ(this->fromPortHistory_bufferDeallocate->at(0).fwBuffer.getData(), data); + ASSERT_EQ(this->fromPortHistory_bufferDeallocate->at(0).fwBuffer.getSize(), sizeof(data)); +} + +void FprimeRouterTester ::testCommandResponse() { + const U32 opcode = 0; + const U32 cmdSeq = 0; + const Fw::CmdResponse cmdResp(Fw::CmdResponse::OK); + this->invoke_to_cmdResponseIn(0, opcode, cmdSeq, cmdResp); + ASSERT_FROM_PORT_HISTORY_SIZE(0); +} + +// ---------------------------------------------------------------------- +// Test Helper +// ---------------------------------------------------------------------- + +void FprimeRouterTester::mockReceivePacketType(Fw::ComPacketType packetType) { + const FwPacketDescriptorType descriptorType = packetType; + U8 data[sizeof descriptorType]; + Fw::Buffer buffer(data, sizeof(data)); + ComCfg::FrameContext context; + context.set_apid(static_cast(descriptorType)); + this->invoke_to_dataIn(0, buffer, context); +} + +void FprimeRouterTester::connectPortsExceptUnknownData() { + // Connect special output ports + this->component.set_logOut_OutputPort(0, this->get_from_logOut(0)); + this->component.set_logTextOut_OutputPort(0, this->get_from_logTextOut(0)); + this->component.set_timeCaller_OutputPort(0, this->get_from_timeCaller(0)); + // Connect typed input ports + this->connect_to_cmdResponseIn(0, this->component.get_cmdResponseIn_InputPort(0)); + this->connect_to_dataIn(0, this->component.get_dataIn_InputPort(0)); + this->connect_to_fileBufferReturnIn(0, this->component.get_fileBufferReturnIn_InputPort(0)); + // Connect typed output ports + this->component.set_bufferAllocate_OutputPort(0, this->get_from_bufferAllocate(0)); + this->component.set_bufferDeallocate_OutputPort(0, this->get_from_bufferDeallocate(0)); + this->component.set_commandOut_OutputPort(0, this->get_from_commandOut(0)); + this->component.set_dataReturnOut_OutputPort(0, this->get_from_dataReturnOut(0)); + this->component.set_fileOut_OutputPort(0, this->get_from_fileOut(0)); +} + +// ---------------------------------------------------------------------- +// Port handler overrides +// ---------------------------------------------------------------------- +Fw::Buffer FprimeRouterTester::from_bufferAllocate_handler(FwIndexType portNum, FwSizeType size) { + this->pushFromPortEntry_bufferAllocate(size); + this->m_buffer.setData(this->m_buffer_slot); + this->m_buffer.setSize(size); + ::memset(this->m_buffer.getData(), 0, size); + return this->m_buffer; +} + +} // namespace Svc diff --git a/FprimeZephyrReference/Components/AuthenticationRouter/test/ut/FprimeRouterTester.hpp b/FprimeZephyrReference/Components/AuthenticationRouter/test/ut/FprimeRouterTester.hpp new file mode 100644 index 00000000..b7e1f250 --- /dev/null +++ b/FprimeZephyrReference/Components/AuthenticationRouter/test/ut/FprimeRouterTester.hpp @@ -0,0 +1,102 @@ +// ====================================================================== +// \title FprimeRouterTester.hpp +// \author thomas-bc +// \brief hpp file for FprimeRouter component test harness implementation class +// ====================================================================== + +#ifndef Svc_FprimeRouterTester_HPP +#define Svc_FprimeRouterTester_HPP + +#include + +#include "Svc/FprimeRouter/FprimeRouter.hpp" +#include "Svc/FprimeRouter/FprimeRouterGTestBase.hpp" + +namespace Svc { + +class FprimeRouterTester : public FprimeRouterGTestBase { + public: + // ---------------------------------------------------------------------- + // Constants + // ---------------------------------------------------------------------- + + // Maximum size of histories storing events, telemetry, and port outputs + static const FwSizeType MAX_HISTORY_SIZE = 10; + + // Instance ID supplied to the component instance under test + static const FwEnumStoreType TEST_INSTANCE_ID = 0; + + public: + // ---------------------------------------------------------------------- + // Construction and destruction + // ---------------------------------------------------------------------- + + //! Construct object FprimeRouterTester + //! \param disconnect_unknownData_port if set to true, the unknownData output port will not be connected + //! in the test harness setup. If false (default), all ports will be connected. + explicit FprimeRouterTester(bool disconnect_unknownData_port = false); + + //! Destroy object FprimeRouterTester + ~FprimeRouterTester(); + + public: + // ---------------------------------------------------------------------- + // Tests + // ---------------------------------------------------------------------- + + //! Route a com packet + void testRouteComInterface(); + + //! Route a file packet + void testRouteFileInterface(); + + //! Route a packet of unknown type + void testRouteUnknownPacket(); + + //! Route a packet of unknown type + void testRouteUnknownPacketUnconnected(); + + //! Deallocate a returning buffer + void testBufferReturn(); + + //! Invoke the command response input port + void testCommandResponse(); + + private: + // ---------------------------------------------------------------------- + // Helper functions + // ---------------------------------------------------------------------- + + //! Connect all ports + void connectPorts(); + + //! Connect all ports except unknownDataOut output port + void connectPortsExceptUnknownData(); + + //! Initialize components + void initComponents(); + + //! Mock the reception of a packet of a specific type + void mockReceivePacketType(Fw::ComPacketType packetType); + + // ---------------------------------------------------------------------- + // Port handler overrides + // ---------------------------------------------------------------------- + //! Overriding bufferAllocate handler to be able to request a buffer in component tests + Fw::Buffer from_bufferAllocate_handler(FwIndexType portNum, FwSizeType size) override; + + private: + // ---------------------------------------------------------------------- + // Member variables + // ---------------------------------------------------------------------- + + //! The component under test + FprimeRouter component; + + Fw::Buffer m_buffer; // buffer to be returned by mocked bufferAllocate call + U8 m_buffer_slot[64]; +}; + +} // namespace Svc + +#endif diff --git a/FprimeZephyrReference/Components/CMakeLists.txt b/FprimeZephyrReference/Components/CMakeLists.txt index b4f276ad..7479f065 100644 --- a/FprimeZephyrReference/Components/CMakeLists.txt +++ b/FprimeZephyrReference/Components/CMakeLists.txt @@ -16,3 +16,5 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/PowerMonitor/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ResetManager/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/StartupManager/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Watchdog") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/AuthenticationRouter") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/AmateurRadio/") diff --git a/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi b/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi index 6055131c..b983fd3c 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi +++ b/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi @@ -3,8 +3,8 @@ telemetry packets ReferenceDeploymentPackets { packet Health id 1 group 1 { CdhCore.cmdDisp.CommandsDispatched CdhCore.cmdDisp.CommandsDropped - ComCcsds.comQueue.comQueueDepth - ComCcsds.commsBufferManager.HiBuffs + ComCcsdsLora.comQueue.comQueueDepth + ComCcsdsLora.commsBufferManager.HiBuffs ComCcsdsUart.comQueue.comQueueDepth ComCcsdsUart.commsBufferManager.HiBuffs ReferenceDeployment.rateGroup10Hz.RgMaxTime @@ -13,12 +13,16 @@ telemetry packets ReferenceDeploymentPackets { ReferenceDeployment.startupManager.QuiescenceEndTime ReferenceDeployment.modeManager.CurrentMode ReferenceDeployment.modeManager.SafeModeEntryCount + ComCcsdsLora.authenticationRouter.LastCommandPacketTime + ComCcsdsLora.authenticationRouter.CommandLossSafeOn + ComCcsdsUart.authenticationRouter.LastCommandPacketTime + ComCcsdsUart.authenticationRouter.CommandLossSafeOn } packet HealthWarnings id 2 group 1 { CdhCore.$health.PingLateWarnings - ComCcsds.commsBufferManager.NoBuffs - ComCcsds.commsBufferManager.EmptyBuffs + ComCcsdsLora.commsBufferManager.NoBuffs + ComCcsdsLora.commsBufferManager.EmptyBuffs ComCcsdsUart.commsBufferManager.NoBuffs ComCcsdsUart.commsBufferManager.EmptyBuffs ReferenceDeployment.rateGroup10Hz.RgCycleSlips @@ -27,13 +31,20 @@ telemetry packets ReferenceDeploymentPackets { packet HealthAuxillary id 3 group 2 { - ComCcsds.commsBufferManager.TotalBuffs - ComCcsds.commsBufferManager.CurrBuffs - ComCcsds.comQueue.buffQueueDepth + ComCcsdsLora.commsBufferManager.TotalBuffs + ComCcsdsLora.commsBufferManager.CurrBuffs + ComCcsdsLora.comQueue.buffQueueDepth ComCcsdsUart.commsBufferManager.TotalBuffs ComCcsdsUart.commsBufferManager.CurrBuffs ComCcsdsUart.comQueue.buffQueueDepth CdhCore.tlmSend.SendLevel + ComCcsdsLora.authenticationRouter.ByPassedRouter + ComCcsdsLora.authenticationRouter.PassedRouter + ComCcsdsLora.authenticationRouter.FailedRouter + ComCcsdsUart.authenticationRouter.ByPassedRouter + ComCcsdsUart.authenticationRouter.PassedRouter + ComCcsdsUart.authenticationRouter.FailedRouter + } packet Version id 4 group 3 { @@ -91,6 +102,7 @@ telemetry packets ReferenceDeploymentPackets { ComCcsdsUart.authenticate.AuthenticatedPacketsCount ComCcsdsUart.authenticate.RejectedPacketsCount ComCcsdsUart.authenticate.CurrentSequenceNumber + ReferenceDeployment.amateurRadio.count_names } diff --git a/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentTopologyDefs.hpp b/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentTopologyDefs.hpp index 00b11005..6ee1720d 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentTopologyDefs.hpp +++ b/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentTopologyDefs.hpp @@ -7,19 +7,22 @@ #define REFERENCEDEPLOYMENT_REFERENCEDEPLOYMENTTOPOLOGYDEFS_HPP // Subtopology PingEntries includes +#include "FprimeZephyrReference/ComCcsdsLora/PingEntries.hpp" #include "Svc/Subtopologies/CdhCore/PingEntries.hpp" -#include "Svc/Subtopologies/ComCcsds/PingEntries.hpp" #include "Svc/Subtopologies/DataProducts/PingEntries.hpp" #include "Svc/Subtopologies/FileHandling/PingEntries.hpp" // SubtopologyTopologyDefs includes +#include "FprimeZephyrReference/ComCcsdsLora/SubtopologyTopologyDefs.hpp" #include "Svc/Subtopologies/CdhCore/SubtopologyTopologyDefs.hpp" -#include "Svc/Subtopologies/ComCcsds/SubtopologyTopologyDefs.hpp" #include "Svc/Subtopologies/FileHandling/SubtopologyTopologyDefs.hpp" -// ComCcsds Enum Includes +// ComCcsds Enum Includes (for ComCcsdsLora) #include "Svc/Subtopologies/ComCcsds/Ports_ComBufferQueueEnumAc.hpp" #include "Svc/Subtopologies/ComCcsds/Ports_ComPacketQueueEnumAc.hpp" +// ComCcsdsUart Enum Includes +#include "FprimeZephyrReference/ComCcsdsUart/Ports_ComBufferQueueEnumAc.hpp" +#include "FprimeZephyrReference/ComCcsdsUart/Ports_ComPacketQueueEnumAc.hpp" // Include autocoded FPP constants #include "FprimeZephyrReference/ReferenceDeployment/Top/FppConstantsAc.hpp" @@ -73,7 +76,7 @@ struct TopologyState { const device* loraDevice; //!< LoRa device path for communication U32 baudRate; //!< Baud rate for UART communication CdhCore::SubtopologyState cdhCore; //!< Subtopology state for CdhCore - ComCcsds::SubtopologyState comCcsds; //!< Subtopology state for ComCcsds + ComCcsdsLora::SubtopologyState comCcsdsLora; //!< Subtopology state for ComCcsdsLora FileHandling::SubtopologyState fileHandling; //!< Subtopology state for FileHandling const device* ina219SysDevice; //!< device path for battery board ina219 const device* ina219SolDevice; //!< device path for solar panel ina219 diff --git a/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp b/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp index a2cd9211..09b67e3b 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp +++ b/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp @@ -142,4 +142,6 @@ module ReferenceDeployment { instance startupManager: Components.StartupManager base id 0x1003F000 + instance amateurRadio: Components.AmateurRadio base id 0x10041000 + } diff --git a/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp b/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp index 0390534e..7c5c2df6 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp +++ b/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp @@ -15,7 +15,7 @@ module ReferenceDeployment { # Subtopology imports # ---------------------------------------------------------------------- import CdhCore.Subtopology - import ComCcsds.FramingSubtopology + import ComCcsdsLora.Subtopology import ComCcsdsUart.Subtopology import FileHandling.Subtopology @@ -49,6 +49,7 @@ module ReferenceDeployment { instance antennaDeployer instance comSplitterEvents instance comSplitterTelemetry + instance amateurRadio # For UART sideband communication instance comDriver @@ -95,37 +96,37 @@ module ReferenceDeployment { connections ComCcsds_CdhCore { # Core events and telemetry to communication queue CdhCore.events.PktSend -> comSplitterEvents.comIn - comSplitterEvents.comOut-> ComCcsds.comQueue.comPacketQueueIn[ComCcsds.Ports_ComPacketQueue.EVENTS] + comSplitterEvents.comOut-> ComCcsdsLora.comQueue.comPacketQueueIn[ComCcsds.Ports_ComPacketQueue.EVENTS] comSplitterEvents.comOut-> ComCcsdsUart.comQueue.comPacketQueueIn[ComCcsds.Ports_ComPacketQueue.EVENTS] CdhCore.tlmSend.PktSend -> comSplitterTelemetry.comIn - comSplitterTelemetry.comOut -> ComCcsds.comQueue.comPacketQueueIn[ComCcsds.Ports_ComPacketQueue.TELEMETRY] + comSplitterTelemetry.comOut -> ComCcsdsLora.comQueue.comPacketQueueIn[ComCcsds.Ports_ComPacketQueue.TELEMETRY] comSplitterTelemetry.comOut -> ComCcsdsUart.comQueue.comPacketQueueIn[ComCcsds.Ports_ComPacketQueue.TELEMETRY] # Router to Command Dispatcher - ComCcsds.fprimeRouter.commandOut -> CdhCore.cmdDisp.seqCmdBuff - CdhCore.cmdDisp.seqCmdStatus -> ComCcsds.fprimeRouter.cmdResponseIn + ComCcsdsLora.authenticationRouter.commandOut -> CdhCore.cmdDisp.seqCmdBuff + CdhCore.cmdDisp.seqCmdStatus -> ComCcsdsLora.authenticationRouter.cmdResponseIn - ComCcsdsUart.fprimeRouter.commandOut -> CdhCore.cmdDisp.seqCmdBuff - CdhCore.cmdDisp.seqCmdStatus -> ComCcsdsUart.fprimeRouter.cmdResponseIn + ComCcsdsUart.authenticationRouter.commandOut -> CdhCore.cmdDisp.seqCmdBuff + CdhCore.cmdDisp.seqCmdStatus -> ComCcsdsUart.authenticationRouter.cmdResponseIn cmdSeq.comCmdOut -> CdhCore.cmdDisp.seqCmdBuff CdhCore.cmdDisp.seqCmdStatus -> cmdSeq.cmdResponseIn } connections CommunicationsRadio { - lora.allocate -> ComCcsds.commsBufferManager.bufferGetCallee - lora.deallocate -> ComCcsds.commsBufferManager.bufferSendIn + lora.allocate -> ComCcsdsLora.commsBufferManager.bufferGetCallee + lora.deallocate -> ComCcsdsLora.commsBufferManager.bufferSendIn - # ComDriver <-> ComStub (Uplink) - lora.dataOut -> ComCcsds.frameAccumulator.dataIn - ComCcsds.frameAccumulator.dataReturnOut -> lora.dataReturnIn + # ComDriver <-> FrameAccumulator (Uplink) + lora.dataOut -> ComCcsdsLora.frameAccumulator.dataIn + ComCcsdsLora.frameAccumulator.dataReturnOut -> lora.dataReturnIn - # ComStub <-> ComDriver (Downlink) - ComCcsds.framer.dataOut -> lora.dataIn - lora.dataReturnOut -> ComCcsds.framer.dataReturnIn + # Framer <-> ComDriver (Downlink) + ComCcsdsLora.framer.dataOut -> lora.dataIn + lora.dataReturnOut -> ComCcsdsLora.framer.dataReturnIn lora.comStatusOut -> comDelay.comStatusIn - comDelay.comStatusOut ->ComCcsds.framer.comStatusIn + comDelay.comStatusOut ->ComCcsdsLora.framer.comStatusIn startupManager.runSequence -> cmdSeq.seqRunIn cmdSeq.seqDone -> startupManager.completeSequence @@ -153,15 +154,15 @@ module ReferenceDeployment { rateGroupDriver.CycleOut[Ports_RateGroups.rateGroup10Hz] -> rateGroup10Hz.CycleIn rateGroup10Hz.RateGroupMemberOut[0] -> comDriver.schedIn rateGroup10Hz.RateGroupMemberOut[1] -> ComCcsdsUart.aggregator.timeout - rateGroup10Hz.RateGroupMemberOut[2] -> ComCcsds.aggregator.timeout + rateGroup10Hz.RateGroupMemberOut[2] -> ComCcsdsLora.aggregator.timeout rateGroup10Hz.RateGroupMemberOut[3] -> FileHandling.fileManager.schedIn rateGroup10Hz.RateGroupMemberOut[4] -> cmdSeq.schedIn # Slow rate (1Hz) rate group rateGroupDriver.CycleOut[Ports_RateGroups.rateGroup1Hz] -> rateGroup1Hz.CycleIn - rateGroup1Hz.RateGroupMemberOut[0] -> ComCcsds.comQueue.run + rateGroup1Hz.RateGroupMemberOut[0] -> ComCcsdsLora.comQueue.run rateGroup1Hz.RateGroupMemberOut[1] -> CdhCore.$health.Run - rateGroup1Hz.RateGroupMemberOut[2] -> ComCcsds.commsBufferManager.schedIn + rateGroup1Hz.RateGroupMemberOut[2] -> ComCcsdsLora.commsBufferManager.schedIn rateGroup1Hz.RateGroupMemberOut[3] -> CdhCore.tlmSend.Run rateGroup1Hz.RateGroupMemberOut[4] -> watchdog.run rateGroup1Hz.RateGroupMemberOut[5] -> imuManager.run @@ -173,6 +174,7 @@ module ReferenceDeployment { rateGroup1Hz.RateGroupMemberOut[11] -> startupManager.run rateGroup1Hz.RateGroupMemberOut[12] -> powerMonitor.run rateGroup1Hz.RateGroupMemberOut[13] -> modeManager.run + rateGroup1Hz.RateGroupMemberOut[14] -> ComCcsdsLora.authenticationRouter.schedIn } @@ -215,8 +217,8 @@ module ReferenceDeployment { ComCcsdsUart.comQueue.bufferReturnOut[ComCcsds.Ports_ComBufferQueue.FILE] -> FileHandling.fileDownlink.bufferReturn # Router <-> FileUplink - ComCcsdsUart.fprimeRouter.fileOut -> FileHandling.fileUplink.bufferSendIn - FileHandling.fileUplink.bufferSendOut -> ComCcsdsUart.fprimeRouter.fileBufferReturnIn + ComCcsdsUart.authenticationRouter.fileOut -> FileHandling.fileUplink.bufferSendIn + FileHandling.fileUplink.bufferSendOut -> ComCcsdsUart.authenticationRouter.fileBufferReturnIn } @@ -255,6 +257,10 @@ module ReferenceDeployment { modeManager.loadSwitchTurnOff[5] -> face5LoadSwitch.turnOff modeManager.loadSwitchTurnOff[6] -> payloadPowerLoadSwitch.turnOff modeManager.loadSwitchTurnOff[7] -> payloadBatteryLoadSwitch.turnOff + + # Connect AuthenticationRouter from ComCcsdsLora subtopology to ModeManager + ComCcsdsLora.authenticationRouter.SafeModeOn -> modeManager.forceSafeMode + } } diff --git a/FprimeZephyrReference/project/config/AcConstants.fpp b/FprimeZephyrReference/project/config/AcConstants.fpp index 8eeefb4b..c88be742 100644 --- a/FprimeZephyrReference/project/config/AcConstants.fpp +++ b/FprimeZephyrReference/project/config/AcConstants.fpp @@ -13,7 +13,7 @@ constant PassiveRateGroupOutputPorts = 10 constant RateGroupDriverRateGroupPorts = 3 @ Used for command and registration ports -constant CmdDispatcherComponentCommandPorts = 30 +constant CmdDispatcherComponentCommandPorts = 35 @ Used for uplink/sequencer buffer/response ports constant CmdDispatcherSequencePorts = 5 diff --git a/FprimeZephyrReference/project/config/ComCcsdsConfig.fpp b/FprimeZephyrReference/project/config/ComCcsdsConfig.fpp index c39d3778..60fdc711 100644 --- a/FprimeZephyrReference/project/config/ComCcsdsConfig.fpp +++ b/FprimeZephyrReference/project/config/ComCcsdsConfig.fpp @@ -2,6 +2,7 @@ module ComCcsdsConfig { #Base ID for the ComCcsds Subtopology, all components are offsets from this base ID constant BASE_ID = 0x02000000 constant BASE_ID_UART = 0x21000000 + constant BASE_ID_LORA = 0x22000000 module QueueSizes { constant comQueue = 5 diff --git a/FprimeZephyrReference/project/config/ComCcsdsConfig/CMakeLists.txt b/FprimeZephyrReference/project/config/ComCcsdsConfig/CMakeLists.txt new file mode 100644 index 00000000..aad62bfe --- /dev/null +++ b/FprimeZephyrReference/project/config/ComCcsdsConfig/CMakeLists.txt @@ -0,0 +1,12 @@ +register_fprime_module( + EXCLUDE_FROM_ALL + SOURCES + "${CMAKE_CURRENT_LIST_DIR}/ComCcsdsSubtopologyConfig.cpp" + HEADERS + "${CMAKE_CURRENT_LIST_DIR}/ComCcsdsSubtopologyConfig.hpp" + AUTOCODER_INPUTS + "${CMAKE_CURRENT_LIST_DIR}/../ComCcsdsConfig.fpp" + DEPENDS + Fw_Types + INTERFACE +) diff --git a/FprimeZephyrReference/project/config/ComCcsdsConfig/ComCcsdsSubtopologyConfig.cpp b/FprimeZephyrReference/project/config/ComCcsdsConfig/ComCcsdsSubtopologyConfig.cpp new file mode 100644 index 00000000..aa384b0e --- /dev/null +++ b/FprimeZephyrReference/project/config/ComCcsdsConfig/ComCcsdsSubtopologyConfig.cpp @@ -0,0 +1,9 @@ +#include "ComCcsdsSubtopologyConfig.hpp" + +namespace ComCcsdsLora { +namespace Allocation { +// This instance can be changed to use a different allocator in the ComCcsdsLora Subtopology +Fw::MallocAllocator mallocatorInstance; +Fw::MemAllocator& memAllocator = mallocatorInstance; +} // namespace Allocation +} // namespace ComCcsdsLora diff --git a/FprimeZephyrReference/project/config/ComCcsdsConfig/ComCcsdsSubtopologyConfig.hpp b/FprimeZephyrReference/project/config/ComCcsdsConfig/ComCcsdsSubtopologyConfig.hpp new file mode 100644 index 00000000..3c29eda1 --- /dev/null +++ b/FprimeZephyrReference/project/config/ComCcsdsConfig/ComCcsdsSubtopologyConfig.hpp @@ -0,0 +1,12 @@ +#ifndef COMCCSDSSUBTOPOLOGY_CONFIG_HPP +#define COMCCSDSSUBTOPOLOGY_CONFIG_HPP + +#include "Fw/Types/MallocAllocator.hpp" + +namespace ComCcsdsLora { +namespace Allocation { +extern Fw::MemAllocator& memAllocator; +} +} // namespace ComCcsdsLora + +#endif diff --git a/FprimeZephyrReference/project/config/TlmPacketizerCfg.hpp b/FprimeZephyrReference/project/config/TlmPacketizerCfg.hpp index 99a1c693..9b3f56a0 100644 --- a/FprimeZephyrReference/project/config/TlmPacketizerCfg.hpp +++ b/FprimeZephyrReference/project/config/TlmPacketizerCfg.hpp @@ -24,12 +24,8 @@ static const FwChanIdType TLMPACKETIZER_HASH_MOD_VALUE = 999; // !< The modulo value of the hashing function. // Should be set to a little below the ID gaps to spread the entries around -// Increased from 90 to 512 buckets to accommodate anticipated future growth in telemetry channels, -// and to minimize hash collisions for improved performance. While currently only a few new channels -// are added (e.g., ModeManager: CurrentMode, SafeModeEntryCount), this sizing is -// intentionally over-provisioned to support planned expansion and to ensure efficient hash table usage. static const FwChanIdType TLMPACKETIZER_HASH_BUCKETS = - 100; // !< Buckets assignable to a hash slot. + 150; // !< Buckets assignable to a hash slot. // Buckets must be >= number of telemetry channels in system static const FwChanIdType TLMPACKETIZER_MAX_MISSING_TLM_CHECK = 25; // !< Maximum number of missing telemetry channel checks diff --git a/FprimeZephyrReference/test/int/mode_manager_test.py b/FprimeZephyrReference/test/int/mode_manager_test.py deleted file mode 100644 index 6bdd0429..00000000 --- a/FprimeZephyrReference/test/int/mode_manager_test.py +++ /dev/null @@ -1,292 +0,0 @@ -""" -mode_manager_test.py: - -Integration tests for the ModeManager component (safe/normal mode). - -Tests cover: -- Basic functionality and telemetry -- Safe mode entry (via command) -- Safe mode exit -- State persistence -- Edge cases - -Total: 9 tests -""" - -import time - -import pytest -from common import proves_send_and_assert_command -from fprime_gds.common.data_types.ch_data import ChData -from fprime_gds.common.testing_fw.api import IntegrationTestAPI - -component = "ReferenceDeployment.modeManager" -load_switch_channels = [ - "ReferenceDeployment.face4LoadSwitch.IsOn", - "ReferenceDeployment.face0LoadSwitch.IsOn", - "ReferenceDeployment.face1LoadSwitch.IsOn", - "ReferenceDeployment.face2LoadSwitch.IsOn", - "ReferenceDeployment.face3LoadSwitch.IsOn", - "ReferenceDeployment.face5LoadSwitch.IsOn", - "ReferenceDeployment.payloadPowerLoadSwitch.IsOn", - "ReferenceDeployment.payloadBatteryLoadSwitch.IsOn", -] - - -@pytest.fixture(autouse=True) -def setup_and_teardown(fprime_test_api: IntegrationTestAPI, start_gds): - """ - Setup before each test and cleanup after. - Ensures clean state by exiting safe mode if needed. - """ - # Setup: Try to get to a clean NORMAL state - try: - # Try to exit safe mode if in it - proves_send_and_assert_command(fprime_test_api, f"{component}.EXIT_SAFE_MODE") - except Exception: - pass - - # Clear event and telemetry history before test - fprime_test_api.clear_histories() - - yield - - # Teardown: Return to clean state for next test - try: - proves_send_and_assert_command(fprime_test_api, f"{component}.EXIT_SAFE_MODE") - except Exception: - pass - - -def force_safe_mode_once( - fprime_test_api: IntegrationTestAPI, wait_for_entering: bool = True -): - """ - Issue FORCE_SAFE_MODE once without retries and wait for key events. - Returns the list of events observed so callers can inspect metadata. - """ - fprime_test_api.clear_histories() - start_idx = fprime_test_api.get_event_test_history().size() - events = [f"{component}.ManualSafeModeEntry"] - if wait_for_entering: - events.append(f"{component}.EnteringSafeMode") - fprime_test_api.send_command(f"{component}.FORCE_SAFE_MODE", []) - return fprime_test_api.assert_event_sequence(events, start=start_idx, timeout=10) - - -# ============================================================================== -# Basic Functionality Tests -# ============================================================================== - - -def test_01_initial_telemetry(fprime_test_api: IntegrationTestAPI, start_gds): - """ - Test that initial telemetry can be read and has expected default values. - Verifies CurrentMode. - """ - # Trigger telemetry update by sending Health packet (ID 1) - proves_send_and_assert_command(fprime_test_api, "CdhCore.tlmSend.SEND_PKT", ["1"]) - - # Read CurrentMode telemetry (0 = NORMAL, 1 = SAFE_MODE) - mode_result: ChData = fprime_test_api.assert_telemetry( - f"{component}.CurrentMode", start="NOW", timeout=3 - ) - current_mode = mode_result.get_val() - assert current_mode in [0, 1], f"Invalid mode value: {current_mode}" - - -# ============================================================================== -# Safe Mode Entry Tests -# ============================================================================== - - -def test_04_force_safe_mode_command(fprime_test_api: IntegrationTestAPI, start_gds): - """ - Test FORCE_SAFE_MODE command enters safe mode by checking telemetry. - """ - # Send FORCE_SAFE_MODE command - still expect ManualSafeModeEntry for logging - proves_send_and_assert_command( - fprime_test_api, - f"{component}.FORCE_SAFE_MODE", - events=[f"{component}.ManualSafeModeEntry"], - ) - - # Wait for mode transition (happens in 1Hz rate group) - time.sleep(3) - - # Trigger telemetry update - proves_send_and_assert_command(fprime_test_api, "CdhCore.tlmSend.SEND_PKT", ["1"]) - - # Verify mode is SAFE_MODE (1) - mode_result: ChData = fprime_test_api.assert_telemetry( - f"{component}.CurrentMode", timeout=5 - ) - assert mode_result.get_val() == 1, "Should be in SAFE_MODE" - - -def test_05_safe_mode_increments_counter( - fprime_test_api: IntegrationTestAPI, start_gds -): - """ - Test that SafeModeEntryCount telemetry increments each time safe mode is entered. - """ - # Read initial safe mode entry count - proves_send_and_assert_command(fprime_test_api, "CdhCore.tlmSend.SEND_PKT", ["1"]) - initial_count: ChData = fprime_test_api.assert_telemetry( - f"{component}.SafeModeEntryCount", timeout=5 - ) - initial_value = initial_count.get_val() - - # Enter safe mode - force_safe_mode_once(fprime_test_api, wait_for_entering=True) - time.sleep(3) - - # Read new count - proves_send_and_assert_command(fprime_test_api, "CdhCore.tlmSend.SEND_PKT", ["1"]) - new_count: ChData = fprime_test_api.assert_telemetry( - f"{component}.SafeModeEntryCount", timeout=5 - ) - new_value = new_count.get_val() - - # Verify count incremented by 1 - assert new_value == initial_value + 1, ( - f"Count should increment by 1 (was {initial_value}, now {new_value})" - ) - - -def test_06_safe_mode_turns_off_load_switches( - fprime_test_api: IntegrationTestAPI, start_gds -): - """ - Test that entering safe mode turns off all load switches. - """ - force_safe_mode_once(fprime_test_api, wait_for_entering=True) - time.sleep(3) - - # Downlink load switch telemetry (packet 9) and verify each switch is OFF (0) - proves_send_and_assert_command(fprime_test_api, "CdhCore.tlmSend.SEND_PKT", ["9"]) - for channel in load_switch_channels: - value = fprime_test_api.assert_telemetry(channel, timeout=5).get_val() - if isinstance(value, str): - assert value.upper() == "OFF", f"{channel} should be OFF in safe mode" - else: - assert value == 0, f"{channel} should be OFF in safe mode" - - -def test_07_safe_mode_emits_event(fprime_test_api: IntegrationTestAPI, start_gds): - """ - Test that EnteringSafeMode event is emitted with correct reason. - """ - events = force_safe_mode_once(fprime_test_api, wait_for_entering=True) - entering_event = events[-1] - assert "Ground command" in entering_event.get_display_text(), ( - "EnteringSafeMode reason should mention Ground command" - ) - - -# ============================================================================== -# Safe Mode Exit Tests -# ============================================================================== - - -def test_13_exit_safe_mode_fails_not_in_safe_mode( - fprime_test_api: IntegrationTestAPI, start_gds -): - """ - Test EXIT_SAFE_MODE fails when not currently in safe mode. - """ - # Ensure we're in NORMAL mode (should be from setup) - proves_send_and_assert_command(fprime_test_api, "CdhCore.tlmSend.SEND_PKT", ["1"]) - mode_result: ChData = fprime_test_api.assert_telemetry( - f"{component}.CurrentMode", start="NOW", timeout=3 - ) - - if mode_result.get_val() != 0: - pytest.skip("Not in NORMAL mode - cannot test this scenario") - - # Try to exit safe mode when not in it - should fail - fprime_test_api.clear_histories() - with pytest.raises(Exception): - proves_send_and_assert_command( - fprime_test_api, - f"{component}.EXIT_SAFE_MODE", - ) - fprime_test_api.assert_event(f"{component}.CommandValidationFailed", timeout=3) - - -def test_14_exit_safe_mode_success(fprime_test_api: IntegrationTestAPI, start_gds): - """ - Test EXIT_SAFE_MODE succeeds. - Verifies: - - ExitingSafeMode event is emitted - - CurrentMode returns to NORMAL (0) - """ - # Enter safe mode - proves_send_and_assert_command(fprime_test_api, f"{component}.FORCE_SAFE_MODE") - time.sleep(2) - - # Exit safe mode - should succeed (no fault checks anymore) - proves_send_and_assert_command( - fprime_test_api, - f"{component}.EXIT_SAFE_MODE", - events=[f"{component}.ExitingSafeMode"], - ) - - time.sleep(2) - - # Verify mode is NORMAL (0) - proves_send_and_assert_command(fprime_test_api, "CdhCore.tlmSend.SEND_PKT", ["1"]) - mode_result: ChData = fprime_test_api.assert_telemetry( - f"{component}.CurrentMode", start="NOW", timeout=3 - ) - assert mode_result.get_val() == 0, "Should be in NORMAL mode" - - -# ============================================================================== -# Edge Cases & Validation Tests -# ============================================================================== - - -def test_18_force_safe_mode_idempotent(fprime_test_api: IntegrationTestAPI, start_gds): - """ - Test that calling FORCE_SAFE_MODE while already in safe mode is idempotent. - Should succeed and not cause issues. - """ - # Enter safe mode first time - proves_send_and_assert_command(fprime_test_api, f"{component}.FORCE_SAFE_MODE") - time.sleep(2) - - # Clear event history - fprime_test_api.clear_histories() - - # Force safe mode again - should succeed without EnteringSafeMode event - # But ManualSafeModeEntry event is still emitted (logging) - proves_send_and_assert_command( - fprime_test_api, - f"{component}.FORCE_SAFE_MODE", - events=[f"{component}.ManualSafeModeEntry"], - ) - - # Verify still in safe mode - time.sleep(1) - proves_send_and_assert_command(fprime_test_api, "CdhCore.tlmSend.SEND_PKT", ["1"]) - mode_result: ChData = fprime_test_api.assert_telemetry( - f"{component}.CurrentMode", start="NOW", timeout=3 - ) - assert mode_result.get_val() == 1, "Should still be in SAFE_MODE" - - -def test_19_safe_mode_state_persists(fprime_test_api: IntegrationTestAPI, start_gds): - """ - Test that mode persists to /mode_state.bin file. - """ - # Enter safe mode to trigger state save - proves_send_and_assert_command(fprime_test_api, f"{component}.FORCE_SAFE_MODE") - time.sleep(2) - - # For now, just verify the command succeeded and state is correct - proves_send_and_assert_command(fprime_test_api, "CdhCore.tlmSend.SEND_PKT", ["1"]) - mode_result: ChData = fprime_test_api.assert_telemetry( - f"{component}.CurrentMode", start="NOW", timeout=3 - ) - assert mode_result.get_val() == 1, "Mode should be saved as SAFE_MODE" diff --git a/UploadsFilesystem/AuthenticateFiles/bypass_authentification_file.txt b/UploadsFilesystem/AuthenticateFiles/bypass_authentification_file.txt new file mode 100644 index 00000000..9f2c6bc3 --- /dev/null +++ b/UploadsFilesystem/AuthenticateFiles/bypass_authentification_file.txt @@ -0,0 +1 @@ +10029012