diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index b0f11982a82..bfa7022c9de 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -362,14 +362,17 @@ genshi getblocks getchildren getcontext +getcrc getdata getdefaultencoding getextern getf geti getinput +getlength getm getquaternion +getstart gett gettime getty @@ -517,6 +520,7 @@ listdir Listst LJR lld +llu LOCALSTATEDIR LOGGERRULES loglvl diff --git a/.gitignore b/.gitignore index 717e7e67e7f..b3e44b6939c 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ RemoteSystemsTempFiles Dict *.core +**/coverage/ *.gcov !**/test/ut/output/*.gcov GTestBase.* diff --git a/Fw/Buffer/Buffer.fpp b/Fw/Buffer/Buffer.fpp index 48dd549caf8..fab0b958950 100644 --- a/Fw/Buffer/Buffer.fpp +++ b/Fw/Buffer/Buffer.fpp @@ -16,4 +16,8 @@ module Fw { $size: U32 ) -> Fw.Buffer + + @ Port for sending data buffer along with context buffer + @ This is useful for passing data that needs context to be interpreted + port DataWithContext(ref data: Fw.Buffer, ref context: Fw.Buffer) } diff --git a/RPI/Top/RPITopologyDefs.hpp b/RPI/Top/RPITopologyDefs.hpp index 50ec1d1c841..5040f33ff63 100644 --- a/RPI/Top/RPITopologyDefs.hpp +++ b/RPI/Top/RPITopologyDefs.hpp @@ -6,6 +6,7 @@ #include "RPI/Top/FppConstantsAc.hpp" #include "Svc/FramingProtocol/FprimeProtocol.hpp" #include "Svc/LinuxTimer/LinuxTimer.hpp" +#include namespace RPI { diff --git a/RPI/Top/instances.fpp b/RPI/Top/instances.fpp index 047a61ad5bb..1014ebce698 100644 --- a/RPI/Top/instances.fpp +++ b/RPI/Top/instances.fpp @@ -178,13 +178,13 @@ module RPI { instance fatalHandler: Svc.FatalHandler base id 100 - instance fileUplinkBufferManager: Svc.BufferManager base id 900 \ + instance commsBufferManager: Svc.BufferManager base id 900 \ { phase Fpp.ToCpp.Phases.configConstants """ enum { STORE_SIZE = 3000, - QUEUE_SIZE = 30, + STORE_COUNT = 30, MGR_ID = 200 }; """ @@ -193,10 +193,10 @@ module RPI { { Svc::BufferManager::BufferBins bufferBins; memset(&bufferBins, 0, sizeof(bufferBins)); - using namespace ConfigConstants::RPI_fileUplinkBufferManager; + using namespace ConfigConstants::RPI_commsBufferManager; bufferBins.bins[0].bufferSize = STORE_SIZE; - bufferBins.bins[0].numBuffers = QUEUE_SIZE; - RPI::fileUplinkBufferManager.setup( + bufferBins.bins[0].numBuffers = STORE_COUNT; + RPI::commsBufferManager.setup( MGR_ID, 0, Allocation::mallocator, @@ -207,15 +207,13 @@ module RPI { """ phase Fpp.ToCpp.Phases.tearDownComponents """ - RPI::fileUplinkBufferManager.cleanup(); + RPI::commsBufferManager.cleanup(); """ } instance fatalAdapter: Svc.AssertFatalAdapter base id 1000 - instance staticMemory: Svc.StaticMemory base id 1200 - instance downlink: Svc.Framer base id 1220 \ { @@ -229,18 +227,7 @@ module RPI { } - instance uplink: Svc.Deframer base id 1240 \ - { - - phase Fpp.ToCpp.Phases.configObjects """ - Svc::FprimeDeframing deframing; - """ - - phase Fpp.ToCpp.Phases.configComponents """ - RPI::uplink.setup(ConfigObjects::RPI_uplink::deframing); - """ - - } + instance deframer: Svc.FprimeDeframer base id 1240 instance comm: Drv.TcpClient base id 1260 \ { @@ -465,5 +452,20 @@ module RPI { """ } + instance frameAccumulator: Svc.FrameAccumulator base id 2900 \ + { + phase Fpp.ToCpp.Phases.configObjects """ + Svc::FrameDetectors::FprimeFrameDetector fprimeFrameDetector; + """ + + phase Fpp.ToCpp.Phases.configComponents """ + { + frameAccumulator.configure(ConfigObjects::RPI_frameAccumulator::fprimeFrameDetector, 1, Allocation::mallocator, 2048); + } + + """ + } + + instance uplinkRouter: Svc.Router base id 3000 } diff --git a/RPI/Top/topology.fpp b/RPI/Top/topology.fpp index 3d842a1828c..5b7f27890bc 100644 --- a/RPI/Top/topology.fpp +++ b/RPI/Top/topology.fpp @@ -11,13 +11,15 @@ module RPI { instance cmdDisp instance cmdSeq instance comm + instance deframer instance downlink instance eventLogger instance fatalAdapter instance fatalHandler instance fileDownlink instance fileUplink - instance fileUplinkBufferManager + instance frameAccumulator + instance commsBufferManager instance gpio17Drv instance gpio23Drv instance gpio24Drv @@ -31,11 +33,10 @@ module RPI { instance rateGroupDriverComp instance rpiDemo instance spiDrv - instance staticMemory instance textLogger instance uartDrv - instance uplink instance uartBufferManager + instance uplinkRouter # ---------------------------------------------------------------------- # Pattern graph specifiers @@ -71,13 +72,6 @@ module RPI { eventLogger.FatalAnnounce -> fatalHandler.FatalReceive } - connections FileUplinkBuffers { - fileUplink.bufferSendOut -> fileUplinkBufferManager.bufferSendIn - uplink.bufferAllocate -> fileUplinkBufferManager.bufferGetCallee - uplink.bufferDeallocate -> fileUplinkBufferManager.bufferSendIn - uplink.bufferOut -> fileUplink.bufferSendIn - } - connections GPIO { rpiDemo.GpioRead -> gpio25Drv.gpioRead rpiDemo.GpioRead -> gpio17Drv.gpioRead @@ -114,11 +108,14 @@ module RPI { rpiDemo.SpiReadWrite -> spiDrv.SpiReadWrite } - connections StaticMemory { - comm.allocate -> staticMemory.bufferAllocate[0] - comm.deallocate -> staticMemory.bufferDeallocate[1] - downlink.framedAllocate -> staticMemory.bufferAllocate[1] - uplink.framedDeallocate -> staticMemory.bufferDeallocate[0] + connections MemoryAllocations { + comm.allocate -> commsBufferManager.bufferGetCallee + comm.deallocate -> commsBufferManager.bufferSendIn + downlink.framedAllocate -> commsBufferManager.bufferGetCallee + fileUplink.bufferSendOut -> commsBufferManager.bufferSendIn + frameAccumulator.bufferAllocate -> commsBufferManager.bufferGetCallee + frameAccumulator.bufferDeallocate -> commsBufferManager.bufferSendIn + uplinkRouter.bufferDeallocate -> commsBufferManager.bufferSendIn } connections UART { @@ -129,9 +126,15 @@ module RPI { } connections Uplink { - cmdDisp.seqCmdStatus -> uplink.cmdResponseIn - comm.$recv -> uplink.framedIn - uplink.comOut -> cmdDisp.seqCmdBuff + comm.$recv -> frameAccumulator.dataIn + + frameAccumulator.frameOut -> deframer.framedIn + deframer.deframedOut -> uplinkRouter.dataIn + + uplinkRouter.commandOut -> cmdDisp.seqCmdBuff + uplinkRouter.fileOut -> fileUplink.bufferSendIn + + cmdDisp.seqCmdStatus -> uplinkRouter.cmdResponseIn } } diff --git a/Ref/Top/RefPackets.xml b/Ref/Top/RefPackets.xml index e525794244f..69cc1e8af98 100644 --- a/Ref/Top/RefPackets.xml +++ b/Ref/Top/RefPackets.xml @@ -13,9 +13,9 @@ - - - + + + @@ -32,8 +32,8 @@ - - + + diff --git a/Ref/Top/RefTopology.cpp b/Ref/Top/RefTopology.cpp index 8c6694c5609..9d989a5d644 100644 --- a/Ref/Top/RefTopology.cpp +++ b/Ref/Top/RefTopology.cpp @@ -16,6 +16,7 @@ #include #include #include +#include // Used for 1Hz synthetic cycling #include @@ -33,7 +34,8 @@ Fw::MallocAllocator mallocator; // The reference topology uses the F´ packet protocol when communicating with the ground and therefore uses the F´ // framing and deframing implementations. Svc::FprimeFraming framing; -Svc::FprimeDeframing deframing; +Svc::FrameDetectors::FprimeFrameDetector frameDetector; + // The reference topology divides the incoming clock signal (1Hz) into sub-signals: 1Hz, 1/2Hz, and 1/4Hz and // zero offset for all the dividers @@ -54,12 +56,16 @@ enum TopologyConstants { FILE_DOWNLINK_FILE_QUEUE_DEPTH = 10, HEALTH_WATCHDOG_CODE = 0x123, COMM_PRIORITY = 100, - UPLINK_BUFFER_MANAGER_STORE_SIZE = 3000, - UPLINK_BUFFER_MANAGER_QUEUE_SIZE = 30, - UPLINK_BUFFER_MANAGER_ID = 200, + // Buffer manager for Uplink/Downlink + COMMS_BUFFER_MANAGER_STORE_SIZE = 2048, + COMMS_BUFFER_MANAGER_STORE_COUNT = 20, + COMMS_BUFFER_MANAGER_FILE_STORE_SIZE = 3000, + COMMS_BUFFER_MANAGER_FILE_QUEUE_SIZE = 30, + COMMS_BUFFER_MANAGER_ID = 200, + // Buffer manager for Data Products DP_BUFFER_MANAGER_STORE_SIZE = 10000, - DP_BUFFER_MANAGER_QUEUE_SIZE = 10, - DP_BUFFER_MANAGER_ID = 300 + DP_BUFFER_MANAGER_STORE_COUNT = 10, + DP_BUFFER_MANAGER_ID = 300, }; /** @@ -94,21 +100,23 @@ void configureTopology() { FW_NUM_ARRAY_ELEMENTS(ConfigObjects::Ref_health::pingEntries), HEALTH_WATCHDOG_CODE); // Buffer managers need a configured set of buckets and an allocator used to allocate memory for those buckets. - Svc::BufferManager::BufferBins upBuffMgrBins; - memset(&upBuffMgrBins, 0, sizeof(upBuffMgrBins)); - upBuffMgrBins.bins[0].bufferSize = UPLINK_BUFFER_MANAGER_STORE_SIZE; - upBuffMgrBins.bins[0].numBuffers = UPLINK_BUFFER_MANAGER_QUEUE_SIZE; - fileUplinkBufferManager.setup(UPLINK_BUFFER_MANAGER_ID, 0, mallocator, upBuffMgrBins); + Svc::BufferManager::BufferBins commsBuffMgrBins; + memset(&commsBuffMgrBins, 0, sizeof(commsBuffMgrBins)); + commsBuffMgrBins.bins[0].bufferSize = COMMS_BUFFER_MANAGER_STORE_SIZE; + commsBuffMgrBins.bins[0].numBuffers = COMMS_BUFFER_MANAGER_STORE_COUNT; + commsBuffMgrBins.bins[1].bufferSize = COMMS_BUFFER_MANAGER_FILE_STORE_SIZE; + commsBuffMgrBins.bins[1].numBuffers = COMMS_BUFFER_MANAGER_FILE_QUEUE_SIZE; + commsBufferManager.setup(COMMS_BUFFER_MANAGER_ID, 0, mallocator, commsBuffMgrBins); Svc::BufferManager::BufferBins dpBuffMgrBins; memset(&dpBuffMgrBins, 0, sizeof(dpBuffMgrBins)); dpBuffMgrBins.bins[0].bufferSize = DP_BUFFER_MANAGER_STORE_SIZE; - dpBuffMgrBins.bins[0].numBuffers = DP_BUFFER_MANAGER_QUEUE_SIZE; + dpBuffMgrBins.bins[0].numBuffers = DP_BUFFER_MANAGER_STORE_COUNT; dpBufferManager.setup(DP_BUFFER_MANAGER_ID, 0, mallocator, dpBuffMgrBins); // Framer and Deframer components need to be passed a protocol handler - downlink.setup(framing); - uplink.setup(deframing); + framer.setup(framing); + frameAccumulator.configure(frameDetector, 1, mallocator, 2048); Fw::FileNameString dpDir("./DpCat"); Fw::FileNameString dpState("./DpCat/DpState.dat"); @@ -192,6 +200,6 @@ void teardownTopology(const TopologyState& state) { // Resource deallocation cmdSeq.deallocateBuffer(mallocator); - fileUplinkBufferManager.cleanup(); + commsBufferManager.cleanup(); } }; // namespace Ref diff --git a/Ref/Top/instances.fpp b/Ref/Top/instances.fpp index 33399937b92..a821e7f6276 100644 --- a/Ref/Top/instances.fpp +++ b/Ref/Top/instances.fpp @@ -137,16 +137,15 @@ module Ref { # ---------------------------------------------------------------------- @ Communications driver. May be swapped with other comm drivers like UART - @ Note: Here we have TCP reliable uplink and UDP (low latency) downlink instance comm: Drv.TcpClient base id 0x4000 - instance downlink: Svc.Framer base id 0x4100 + instance framer: Svc.Framer base id 0x4100 instance fatalAdapter: Svc.AssertFatalAdapter base id 0x4200 instance fatalHandler: Svc.FatalHandler base id 0x4300 - instance fileUplinkBufferManager: Svc.BufferManager base id 0x4400 + instance commsBufferManager: Svc.BufferManager base id 0x4400 instance posixTime: Svc.PosixTime base id 0x4500 @@ -154,16 +153,19 @@ module Ref { instance recvBuffComp: Ref.RecvBuff base id 0x4700 - instance staticMemory: Svc.StaticMemory base id 0x4800 + instance version: Svc.Version base id 0x4800 instance textLogger: Svc.PassiveTextLogger base id 0x4900 - instance uplink: Svc.Deframer base id 0x4A00 + instance systemResources: Svc.SystemResources base id 0x4A00 - instance systemResources: Svc.SystemResources base id 0x4B00 + instance dpBufferManager: Svc.BufferManager base id 0x4B00 - instance dpBufferManager: Svc.BufferManager base id 0x4C00 - - instance version: Svc.Version base id 0x4D00 + instance frameAccumulator: Svc.FrameAccumulator base id 0x4C00 + + instance deframer: Svc.FprimeDeframer base id 0x4D00 + + instance uplinkRouter: Svc.Router base id 0x4E00 } + diff --git a/Ref/Top/topology.fpp b/Ref/Top/topology.fpp index d890604f4ae..155cb78a45f 100644 --- a/Ref/Top/topology.fpp +++ b/Ref/Top/topology.fpp @@ -13,6 +13,7 @@ module Ref { enum Ports_StaticMemory { downlink uplink + accumulator } topology Ref { @@ -32,14 +33,16 @@ module Ref { instance cmdDisp instance cmdSeq instance comm - instance downlink + instance deframer instance eventLogger instance fatalAdapter instance fatalHandler instance fileDownlink instance fileManager instance fileUplink - instance fileUplinkBufferManager + instance commsBufferManager + instance frameAccumulator + instance framer instance posixTime instance pingRcvr instance prmDb @@ -48,11 +51,10 @@ module Ref { instance rateGroup3Comp instance rateGroupDriverComp instance recvBuffComp + instance uplinkRouter instance sendBuffComp - instance staticMemory instance textLogger instance typeDemo - instance uplink instance systemResources instance dpCat instance dpMgr @@ -84,15 +86,15 @@ module Ref { connections Downlink { - tlmSend.PktSend -> downlink.comIn - eventLogger.PktSend -> downlink.comIn - fileDownlink.bufferSendOut -> downlink.bufferIn + tlmSend.PktSend -> framer.comIn + eventLogger.PktSend -> framer.comIn + fileDownlink.bufferSendOut -> framer.bufferIn - downlink.framedAllocate -> staticMemory.bufferAllocate[Ports_StaticMemory.downlink] - downlink.framedOut -> comm.$send - downlink.bufferDeallocate -> fileDownlink.bufferReturn + framer.framedAllocate -> commsBufferManager.bufferGetCallee + framer.framedOut -> comm.$send + framer.bufferDeallocate -> fileDownlink.bufferReturn - comm.deallocate -> staticMemory.bufferDeallocate[Ports_StaticMemory.downlink] + comm.deallocate -> commsBufferManager.bufferSendIn dpCat.fileOut -> fileDownlink.SendFile fileDownlink.FileComplete -> dpCat.fileDone @@ -128,7 +130,7 @@ module Ref { rateGroup3Comp.RateGroupMemberOut[0] -> $health.Run rateGroup3Comp.RateGroupMemberOut[1] -> SG5.schedIn rateGroup3Comp.RateGroupMemberOut[2] -> blockDrv.Sched - rateGroup3Comp.RateGroupMemberOut[3] -> fileUplinkBufferManager.schedIn + rateGroup3Comp.RateGroupMemberOut[3] -> commsBufferManager.schedIn rateGroup3Comp.RateGroupMemberOut[4] -> dpBufferManager.schedIn rateGroup3Comp.RateGroupMemberOut[5] -> dpWriter.schedIn rateGroup3Comp.RateGroupMemberOut[6] -> dpMgr.schedIn @@ -146,17 +148,21 @@ module Ref { connections Uplink { - comm.allocate -> staticMemory.bufferAllocate[Ports_StaticMemory.uplink] - comm.$recv -> uplink.framedIn - uplink.framedDeallocate -> staticMemory.bufferDeallocate[Ports_StaticMemory.uplink] + comm.allocate -> commsBufferManager.bufferGetCallee + comm.$recv -> frameAccumulator.dataIn - uplink.comOut -> cmdDisp.seqCmdBuff - cmdDisp.seqCmdStatus -> uplink.cmdResponseIn + frameAccumulator.frameOut -> deframer.framedIn + frameAccumulator.bufferAllocate -> commsBufferManager.bufferGetCallee + frameAccumulator.bufferDeallocate -> commsBufferManager.bufferSendIn + deframer.deframedOut -> uplinkRouter.dataIn - uplink.bufferAllocate -> fileUplinkBufferManager.bufferGetCallee - uplink.bufferOut -> fileUplink.bufferSendIn - uplink.bufferDeallocate -> fileUplinkBufferManager.bufferSendIn - fileUplink.bufferSendOut -> fileUplinkBufferManager.bufferSendIn + uplinkRouter.commandOut -> cmdDisp.seqCmdBuff + uplinkRouter.fileOut -> fileUplink.bufferSendIn + uplinkRouter.bufferDeallocate -> commsBufferManager.bufferSendIn + + cmdDisp.seqCmdStatus -> uplinkRouter.cmdResponseIn + + fileUplink.bufferSendOut -> commsBufferManager.bufferSendIn } diff --git a/Svc/CMakeLists.txt b/Svc/CMakeLists.txt index 49de3f43aad..6b43259b229 100644 --- a/Svc/CMakeLists.txt +++ b/Svc/CMakeLists.txt @@ -25,7 +25,6 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ComStub/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/CmdDispatcher/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/CmdSequencer/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/CmdSplitter/") -add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Deframer/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/DpCatalog/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/DpManager/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/DpPorts/") @@ -35,7 +34,9 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FileDownlinkPorts/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FileDownlink/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FileManager/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FileUplink/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FprimeDeframer/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/GenericHub/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FrameAccumulator/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Framer/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FramingProtocol/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Health/") @@ -43,6 +44,7 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/PassiveRateGroup") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/PolyDb/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/PrmDb/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/RateGroupDriver/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Router/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/SeqDispatcher/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/StaticMemory/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TlmChan/") diff --git a/Svc/Deframer/CMakeLists.txt b/Svc/Deframer/CMakeLists.txt deleted file mode 100644 index 6df9e8c344a..00000000000 --- a/Svc/Deframer/CMakeLists.txt +++ /dev/null @@ -1,45 +0,0 @@ -#### -# F prime CMakeLists.txt: -# -# SOURCE_FILES: combined list of source and autocoding files -# MOD_DEPS: (optional) module dependencies -# -# Note: using PROJECT_NAME as EXECUTABLE_NAME -#### -set(SOURCE_FILES - "${CMAKE_CURRENT_LIST_DIR}/Deframer.fpp" - "${CMAKE_CURRENT_LIST_DIR}/Deframer.cpp" -) - -set(MOD_DEPS - Svc/FramingProtocol - Utils/Types -) - -register_fprime_module() - -#### UTS ### - -# These tests verify the Deframer component against a mock -# deframing protocol -set(UT_SOURCE_FILES - "${CMAKE_CURRENT_LIST_DIR}/Deframer.fpp" - "${CMAKE_CURRENT_LIST_DIR}/test/ut/DeframerTester.cpp" - "${CMAKE_CURRENT_LIST_DIR}/test/ut/DeframerTestMain.cpp" -) -register_fprime_ut() - -# These tests verify the Deframer component against the -# F Prime deframing protocol located at Svc/FramingProtocol. -# They also verify the deframing part of the F Prime protocol -# implementation. -set(UT_SOURCE_FILES - "${CMAKE_CURRENT_LIST_DIR}/Deframer.fpp" - "${CMAKE_CURRENT_LIST_DIR}/test/ut-fprime-protocol/GenerateFrames.cpp" - "${CMAKE_CURRENT_LIST_DIR}/test/ut-fprime-protocol/SendBuffer.cpp" - "${CMAKE_CURRENT_LIST_DIR}/test/ut-fprime-protocol/DeframerTester.cpp" - "${CMAKE_CURRENT_LIST_DIR}/test/ut-fprime-protocol/DeframerTestMain.cpp" - "${CMAKE_CURRENT_LIST_DIR}/test/ut-fprime-protocol/UplinkFrame.cpp" -) -set(UT_MOD_DEPS STest) -register_fprime_ut(Svc_Deframer_fprime_protocol) diff --git a/Svc/Deframer/Deframer.cpp b/Svc/Deframer/Deframer.cpp deleted file mode 100644 index 56b7a187c0d..00000000000 --- a/Svc/Deframer/Deframer.cpp +++ /dev/null @@ -1,312 +0,0 @@ -// ====================================================================== -// \title Deframer.cpp -// \author mstarch, bocchino -// \brief cpp file for Deframer component implementation class -// -// \copyright -// Copyright 2009-2022, by the California Institute of Technology. -// ALL RIGHTS RESERVED. United States Government Sponsorship -// acknowledged. -// -// ====================================================================== - -#include - -#include "Fw/Com/ComPacket.hpp" -#include "Fw/Logger/Logger.hpp" -#include -#include "Svc/Deframer/Deframer.hpp" - -namespace Svc { - -// ---------------------------------------------------------------------- -// Static assertions -// ---------------------------------------------------------------------- - -static_assert( - DeframerCfg::POLL_BUFFER_SIZE > 0, - "poll buffer size must be greater than zero" -); -static_assert( - DeframerCfg::RING_BUFFER_SIZE > 0, - "ring buffer size must be greater than zero" -); - -// ---------------------------------------------------------------------- -// Construction, initialization, and destruction -// ---------------------------------------------------------------------- - -Deframer ::Deframer(const char* const compName) : - DeframerComponentBase(compName), - DeframingProtocolInterface(), - m_protocol(nullptr), - m_inRing(m_ringBuffer, sizeof m_ringBuffer) -{ - (void) memset(m_pollBuffer, 0, sizeof m_pollBuffer); -} - -Deframer ::~Deframer() {} - -void Deframer ::setup(DeframingProtocol& protocol) { - // Check that this is the first time we are calling setup - FW_ASSERT(m_protocol == nullptr); - // Assign the protocol passed in to m_protocol - m_protocol = &protocol; - // Pass *this as the DeframingProtocolInstance to protocol setup - // Deframer is derived from and implements DeframingProtocolInterface - protocol.setup(*this); -} - -// ---------------------------------------------------------------------- -// Handler implementations for user-defined typed input ports -// ---------------------------------------------------------------------- - -void Deframer ::cmdResponseIn_handler( - FwIndexType portNum, - FwOpcodeType opcode, - U32 cmdSeq, - const Fw::CmdResponse& response -) { - // Nothing to do -} - -void Deframer ::framedIn_handler( - const FwIndexType portNum, - Fw::Buffer& recvBuffer, - const Drv::RecvStatus& recvStatus -) { - // Check whether there is data to process - if (recvStatus.e == Drv::RecvStatus::RECV_OK) { - // There is: process the data - processBuffer(recvBuffer); - } - // Deallocate the buffer - framedDeallocate_out(0, recvBuffer); -} - -void Deframer ::schedIn_handler( - const FwIndexType portNum, - U32 context -) { - // Check for data - Fw::Buffer buffer(m_pollBuffer, sizeof(m_pollBuffer)); - const Drv::PollStatus status = framedPoll_out(0, buffer); - if (status.e == Drv::PollStatus::POLL_OK) { - // Data exists: process it - processBuffer(buffer); - } -} - -// ---------------------------------------------------------------------- -// Implementation of DeframingProtocolInterface -// ---------------------------------------------------------------------- - -Fw::Buffer Deframer ::allocate(const U32 size) { - return bufferAllocate_out(0, size); -} - -void Deframer ::route(Fw::Buffer& packetBuffer) { - - // Read the packet type from the packet buffer - FwPacketDescriptorType packetType = Fw::ComPacket::FW_PACKET_UNKNOWN; - Fw::SerializeStatus status = Fw::FW_SERIALIZE_OK; - { - Fw::SerializeBufferBase& serial = packetBuffer.getSerializeRepr(); - status = serial.setBuffLen(packetBuffer.getSize()); - FW_ASSERT(status == Fw::FW_SERIALIZE_OK); - status = serial.deserialize(packetType); - } - - // Whether to deallocate the packet buffer - bool deallocate = true; - - // Process the packet - if (status == Fw::FW_SERIALIZE_OK) { - U8 *const packetData = packetBuffer.getData(); - const U32 packetSize = packetBuffer.getSize(); - switch (packetType) { - // Handle a command packet - case Fw::ComPacket::FW_PACKET_COMMAND: { - // Allocate a com buffer on the stack - Fw::ComBuffer com; - // Copy the contents of the packet buffer into the com buffer - status = com.setBuff(packetData, packetSize); - if (status == Fw::FW_SERIALIZE_OK) { - // Send the com buffer - comOut_out(0, com, 0); - } - else { - Fw::Logger::log( - "[ERROR] Serializing com buffer failed with status %d\n", - status - ); - } - break; - } - // Handle a file packet - case Fw::ComPacket::FW_PACKET_FILE: { - // If the file uplink output port is connected, - // send the file packet. Otherwise take no action. - if (isConnected_bufferOut_OutputPort(0)) { - // Shift the packet buffer to skip the packet type - // The FileUplink component does not expect the packet - // type to be there. - packetBuffer.setData(packetData + sizeof(packetType)); - packetBuffer.setSize(static_cast(packetSize - sizeof(packetType))); - // Send the packet buffer - bufferOut_out(0, packetBuffer); - // Transfer ownership of the buffer to the receiver - deallocate = false; - } - break; - } - // Take no action for other packet types - default: - break; - } - } - else { - Fw::Logger::log( - "[ERROR] Deserializing packet type failed with status %d\n", - status - ); - } - - if (deallocate) { - // Deallocate the packet buffer - bufferDeallocate_out(0, packetBuffer); - } - -} - -// ---------------------------------------------------------------------- -// Helper methods -// ---------------------------------------------------------------------- - -void Deframer ::processBuffer(Fw::Buffer& buffer) { - - const U32 bufferSize = buffer.getSize(); - U8 *const bufferData = buffer.getData(); - // Current offset into buffer - U32 offset = 0; - // Remaining data in buffer - U32 remaining = bufferSize; - - for (U32 i = 0; i < bufferSize; ++i) { - // If there is no data left, exit the loop - if (remaining == 0) { - break; - } - // Compute the size of data to serialize - const NATIVE_UINT_TYPE ringFreeSize = m_inRing.get_free_size(); - const NATIVE_UINT_TYPE serSize = (ringFreeSize <= remaining) ? - ringFreeSize : static_cast(remaining); - // Serialize data into the ring buffer - const Fw::SerializeStatus status = - m_inRing.serialize(&bufferData[offset], serSize); - // If data does not fit, there is a coding error - FW_ASSERT( - status == Fw::FW_SERIALIZE_OK, - static_cast(status), - static_cast(offset), - static_cast(serSize)); - // Process the data - processRing(); - // Update buffer offset and remaining - offset += serSize; - remaining -= serSize; - } - - // In every iteration, either remaining == 0 and we break out - // of the loop, or we consume at least one byte from the buffer. - // So there should be no data left in the buffer. - FW_ASSERT(remaining == 0, static_cast(remaining)); - -} - -void Deframer ::processRing() { - - FW_ASSERT(m_protocol != nullptr); - - // The number of remaining bytes in the ring buffer - U32 remaining = 0; - // The protocol status - DeframingProtocol::DeframingStatus status = - DeframingProtocol::DEFRAMING_STATUS_SUCCESS; - // The ring buffer capacity - const NATIVE_UINT_TYPE ringCapacity = m_inRing.get_capacity(); - - // Process the ring buffer looking for at least the header - for (U32 i = 0; i < ringCapacity; i++) { - // Get the number of bytes remaining in the ring buffer - remaining = m_inRing.get_allocated_size(); - // If there are none, we are done - if (remaining == 0) { - break; - } - // Needed is an out-only variable - // Initialize it to zero - U32 needed = 0; - // Call the deframe method of the protocol, getting - // needed and status - status = m_protocol->deframe(m_inRing, needed); - // Deframing protocol must not consume data in the ring buffer - FW_ASSERT( - m_inRing.get_allocated_size() == remaining, - static_cast(m_inRing.get_allocated_size()), - static_cast(remaining) - ); - // On successful deframing, consume data from the ring buffer now - if (status == DeframingProtocol::DEFRAMING_STATUS_SUCCESS) { - // If deframing succeeded, protocol should set needed - // to a non-zero value - FW_ASSERT(needed != 0); - FW_ASSERT( - needed <= remaining, - static_cast(needed), - static_cast(remaining)); - m_inRing.rotate(needed); - FW_ASSERT( - m_inRing.get_allocated_size() == remaining - needed, - static_cast(m_inRing.get_allocated_size()), - static_cast(remaining), - static_cast(needed) - ); - } - // More data needed - else if (status == DeframingProtocol::DEFRAMING_MORE_NEEDED) { - // Deframing protocol should not report "more is needed" - // unless more is needed - FW_ASSERT( - needed > remaining, - static_cast(needed), - static_cast(remaining)); - // Break out of loop: suspend deframing until we receive - // another buffer - break; - } - // Error occurred - else { - // Skip one byte of bad data - m_inRing.rotate(1); - FW_ASSERT( - m_inRing.get_allocated_size() == remaining - 1, - static_cast(m_inRing.get_allocated_size()), - static_cast(remaining) - ); - // Log checksum errors - // This is likely a real error, not an artifact of other data corruption - if (status == DeframingProtocol::DEFRAMING_INVALID_CHECKSUM) { - Fw::Logger::log("[ERROR] Deframing checksum validation failed\n"); - } - } - } - - // If more not needed, circular buffer should be empty - if (status != DeframingProtocol::DEFRAMING_MORE_NEEDED) { - FW_ASSERT(remaining == 0, static_cast(remaining)); - } - -} - -} // end namespace Svc diff --git a/Svc/Deframer/Deframer.fpp b/Svc/Deframer/Deframer.fpp deleted file mode 100644 index 3d7323a77cf..00000000000 --- a/Svc/Deframer/Deframer.fpp +++ /dev/null @@ -1,75 +0,0 @@ -module Svc { - - @ A component for deframing input received from the ground - @ via a byte stream driver, which may be active or passive - passive component Deframer { - - # ---------------------------------------------------------------------- - # Receiving framed data via push - # ---------------------------------------------------------------------- - - @ Port for receiving frame buffers FB pushed from the byte stream driver. - @ After using a buffer FB received on this port, Deframer deallocates it - @ by invoking framedDeallocate. - guarded input port framedIn: Drv.ByteStreamRecv - - @ Port for deallocating buffers received on framedIn. - output port framedDeallocate: Fw.BufferSend - - # ---------------------------------------------------------------------- - # Receiving framed data via poll - # ---------------------------------------------------------------------- - - @ Schedule in port, driven by a rate group. - guarded input port schedIn: Svc.Sched - - @ Port that polls for data from the byte stream driver. - @ Deframer invokes this port on its schedIn cycle, if it is connected. - @ No allocation or occurs when invoking this port. - @ The data transfer uses a pre-allocated frame buffer - @ owned by Deframer. - output port framedPoll: Drv.ByteStreamPoll - - # ---------------------------------------------------------------------- - # Memory management for deframing and for sending file packets - # ---------------------------------------------------------------------- - - @ Port for allocating Fw::Buffer objects from a buffer manager. - @ When Deframer invokes this port, it receives a packet buffer PB and - @ takes ownership of it. It uses PB internally for deframing. - @ Then one of two things happens: - @ - @ 1. PB contains a file packet, which Deframer sends on bufferOut. - @ In this case ownership of PB passes to the receiver. - @ - @ 2. PB does not contain a file packet, or bufferOut is unconnected. - @ In this case Deframer deallocates PB on bufferDeallocate. - output port bufferAllocate: Fw.BufferGet - - @ Port for sending file packets (case 1 above). - @ The file packets are wrapped in Fw::Buffer objects allocated with - @ bufferAllocate. - @ Ownership of the Fw::Buffer passes to the receiver, which is - @ responsible for the deallocation. - output port bufferOut: Fw.BufferSend - - @ Port for deallocating temporary buffers allocated with - @ bufferAllocate (case 2 above). Deallocation occurs here - @ when there is nothing to send on bufferOut. - output port bufferDeallocate: Fw.BufferSend - - # ---------------------------------------------------------------------- - # Sending command packets and receiving command responses - # ---------------------------------------------------------------------- - - @ Port for sending command packets as Com buffers. - output port comOut: Fw.Com - - @ Port for receiving command responses from a command dispatcher. - @ Invoking this port does nothing. The port exists to allow the matching - @ connection in the topology. - sync input port cmdResponseIn: Fw.CmdResponse - - } - -} diff --git a/Svc/Deframer/Deframer.hpp b/Svc/Deframer/Deframer.hpp deleted file mode 100644 index f946d407934..00000000000 --- a/Svc/Deframer/Deframer.hpp +++ /dev/null @@ -1,138 +0,0 @@ -// ====================================================================== -// \title Deframer.hpp -// \author mstarch, bocchino -// \brief hpp file for Deframer component implementation class -// -// \copyright -// Copyright 2009-2022, by the California Institute of Technology. -// ALL RIGHTS RESERVED. United States Government Sponsorship -// acknowledged. -// -// ====================================================================== - -#ifndef Svc_Deframer_HPP -#define Svc_Deframer_HPP - -#include - -#include "Svc/Deframer/DeframerComponentAc.hpp" -#include "Svc/FramingProtocol/DeframingProtocol.hpp" -#include "Svc/FramingProtocol/DeframingProtocolInterface.hpp" -#include "Utils/Types/CircularBuffer.hpp" - -namespace Svc { - -/** - * \brief Generic deframing component using DeframingProtocol implementation for actual deframing - * - * Deframing component used to take byte streams and expand them into Com/File buffers. This is - * done using a deframing protocol specified in a DeframingProtocol instance. The instance must be - * supplied using the `setup` method. - * - * Using this component, projects can implement and supply a fresh DeframingProtocol implementation - * without changing the reference topology. - * - * Implementation uses a circular buffer to store incoming data, which is drained one framed packet - * at a time into buffers dispatched to the rest of the system. - */ -class Deframer : - public DeframerComponentBase, - public DeframingProtocolInterface -{ - public: - - // ---------------------------------------------------------------------- - // Construction, initialization, and destruction - // ---------------------------------------------------------------------- - - //! Construct Deframer instance - Deframer( - const char* const compName //!< The component name - ); - - //! Destroy Deframer instance - ~Deframer(); - - //! Set up the instance - void setup( - DeframingProtocol& protocol //!< Deframing protocol instance - ); - - PRIVATE: - - // ---------------------------------------------------------------------- - // Handler implementations for user-defined typed input ports - // ---------------------------------------------------------------------- - - //! Handler for input port cmdResponseIn - 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 - ); - - //! Handler implementation for framedIn - void framedIn_handler( - const FwIndexType portNum, //!< The port number - Fw::Buffer& recvBuffer, //!< Buffer containing framed data - const Drv::RecvStatus& recvStatus //!< Status of the bytes - ); - - //! Handler implementation for schedIn - void schedIn_handler( - const FwIndexType portNum, //!< The port number - U32 context //!< The call order - ); - - // ---------------------------------------------------------------------- - // Implementation of DeframingProtocolInterface - // ---------------------------------------------------------------------- - - //! The implementation of DeframingProtocolInterface::route - //! Send a data packet - void route( - Fw::Buffer& packetBuffer //!< The packet buffer - ); - - //! The implementation of DeframingProtocolInterface::allocate - //! Allocate a packet buffer - //! \return The packet buffer - Fw::Buffer allocate( - const U32 size //!< The number of bytes to request - ); - - // ---------------------------------------------------------------------- - // Helper methods - // ---------------------------------------------------------------------- - - //! Copy data from an incoming frame buffer into the internal - //! circular buffer - void processBuffer( - Fw::Buffer& buffer //!< The frame buffer - ); - - //! Process data in the circular buffer - void processRing(); - - // ---------------------------------------------------------------------- - // Member variables - // ---------------------------------------------------------------------- - - //! The DeframingProtocol implementation - DeframingProtocol* m_protocol; - - //! The circular buffer - Types::CircularBuffer m_inRing; - - //! Memory for the circular buffer - U8 m_ringBuffer[DeframerCfg::RING_BUFFER_SIZE]; - - //! Memory for the polling buffer - U8 m_pollBuffer[DeframerCfg::POLL_BUFFER_SIZE]; - -}; - -} // end namespace Svc - -#endif diff --git a/Svc/Deframer/docs/img/.fpv-env b/Svc/Deframer/docs/img/.fpv-env deleted file mode 100644 index ff60caacf45..00000000000 --- a/Svc/Deframer/docs/img/.fpv-env +++ /dev/null @@ -1 +0,0 @@ -DATA_FOLDER=top/ diff --git a/Svc/Deframer/docs/img/Deframer.png b/Svc/Deframer/docs/img/Deframer.png deleted file mode 100644 index b6d54cdc326..00000000000 Binary files a/Svc/Deframer/docs/img/Deframer.png and /dev/null differ diff --git a/Svc/Deframer/docs/img/deframer_example_1.png b/Svc/Deframer/docs/img/deframer_example_1.png deleted file mode 100644 index 30a83a48632..00000000000 Binary files a/Svc/Deframer/docs/img/deframer_example_1.png and /dev/null differ diff --git a/Svc/Deframer/docs/img/deframer_example_2.png b/Svc/Deframer/docs/img/deframer_example_2.png deleted file mode 100644 index aae87e69415..00000000000 Binary files a/Svc/Deframer/docs/img/deframer_example_2.png and /dev/null differ diff --git a/Svc/Deframer/docs/img/top/cmd.json b/Svc/Deframer/docs/img/top/cmd.json deleted file mode 100644 index e079481d128..00000000000 --- a/Svc/Deframer/docs/img/top/cmd.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "columns" : [ - [ - { - "instanceName" : "deframer", - "inputPorts" : [ - { - "name" : "cmdResponseIn", - "portNumbers" : [ - 0 - ] - } - ], - "outputPorts" : [ - { - "name" : "comOut", - "portNumbers" : [ - 0 - ] - } - ] - } - ], - [ - { - "instanceName" : "cmdDisp", - "inputPorts" : [ - { - "name" : "seqCmdBuff", - "portNumbers" : [ - 0 - ] - } - ], - "outputPorts" : [ - { - "name" : "compCmdStat", - "portNumbers" : [ - 0 - ] - } - ] - } - ] - ], - "connections" : [ - [ - [ - 0, - 0, - 0, - 0 - ], - [ - 1, - 0, - 0, - 0 - ] - ], - [ - [ - 1, - 0, - 0, - 0 - ], - [ - 0, - 0, - 0, - 0 - ] - ] - ] -} diff --git a/Svc/Deframer/docs/img/top/cmd.png b/Svc/Deframer/docs/img/top/cmd.png deleted file mode 100644 index 39b36b97101..00000000000 Binary files a/Svc/Deframer/docs/img/top/cmd.png and /dev/null differ diff --git a/Svc/Deframer/docs/img/top/cmd.txt b/Svc/Deframer/docs/img/top/cmd.txt deleted file mode 100644 index ea505a43cc7..00000000000 --- a/Svc/Deframer/docs/img/top/cmd.txt +++ /dev/null @@ -1,13 +0,0 @@ -deframer -comOut -0 -cmdDisp -seqCmdBuff -0 - -cmdDisp -compCmdStat -0 -deframer -cmdResponseIn -0 diff --git a/Svc/Deframer/docs/img/top/deframer-file.json b/Svc/Deframer/docs/img/top/deframer-file.json deleted file mode 100644 index 2b1ef18172a..00000000000 --- a/Svc/Deframer/docs/img/top/deframer-file.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "columns" : [ - [ - { - "instanceName" : "deframer", - "inputPorts" : [], - "outputPorts" : [ - { - "name" : "bufferOut", - "portNumbers" : [ - 0 - ] - }, - { - "name" : "bufferDeallocate", - "portNumbers" : [ - 0 - ] - }, - { - "name" : "bufferAllocate", - "portNumbers" : [ - 0 - ] - } - ] - } - ], - [ - { - "instanceName" : "fileUplink", - "inputPorts" : [ - { - "name" : "bufferSendIn", - "portNumbers" : [ - 0 - ] - } - ], - "outputPorts" : [ - { - "name" : "bufferSendOut", - "portNumbers" : [ - 0 - ] - } - ] - } - ], - [ - { - "instanceName" : "buffMgr", - "inputPorts" : [ - { - "name" : "bufferSendIn", - "portNumbers" : [ - 0 - ] - }, - { - "name" : "bufferGetCallee", - "portNumbers" : [ - 0 - ] - } - ], - "outputPorts" : [] - } - ] - ], - "connections" : [ - [ - [ - 0, - 0, - 0, - 0 - ], - [ - 1, - 0, - 0, - 0 - ] - ], - [ - [ - 1, - 0, - 0, - 0 - ], - [ - 2, - 0, - 0, - 0 - ] - ], - [ - [ - 0, - 0, - 2, - 0 - ], - [ - 2, - 0, - 1, - 0 - ] - ], - [ - [ - 0, - 0, - 1, - 0 - ], - [ - 2, - 0, - 0, - 0 - ] - ] - ] -} diff --git a/Svc/Deframer/docs/img/top/deframer-file.png b/Svc/Deframer/docs/img/top/deframer-file.png deleted file mode 100644 index 1c4713605fb..00000000000 Binary files a/Svc/Deframer/docs/img/top/deframer-file.png and /dev/null differ diff --git a/Svc/Deframer/docs/img/top/deframer-file.txt b/Svc/Deframer/docs/img/top/deframer-file.txt deleted file mode 100644 index 40f6369e006..00000000000 --- a/Svc/Deframer/docs/img/top/deframer-file.txt +++ /dev/null @@ -1,28 +0,0 @@ -deframer -bufferOut -0 -fileUplink -bufferSendIn -0 - -fileUplink -bufferSendOut -0 -buffMgr -bufferSendIn -0 - -deframer -bufferAllocate -0 -buffMgr -bufferGetCallee -0 - -deframer -bufferDeallocate -0 -buffMgr -bufferSendIn -0 - diff --git a/Svc/Deframer/docs/img/top/framed-active.json b/Svc/Deframer/docs/img/top/framed-active.json deleted file mode 100644 index 965f9cc8d55..00000000000 --- a/Svc/Deframer/docs/img/top/framed-active.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "columns" : [ - [ - { - "instanceName" : "activeComm", - "inputPorts" : [], - "outputPorts" : [ - { - "name" : "recv", - "portNumbers" : [ - 0 - ] - }, - { - "name" : "allocate", - "portNumbers" : [ - 0 - ] - } - ] - } - ], - [ - { - "instanceName" : "deframer", - "inputPorts" : [ - { - "name" : "framedIn", - "portNumbers" : [ - 0 - ] - } - ], - "outputPorts" : [ - { - "name" : "framedDeallocate", - "portNumbers" : [ - 0 - ] - } - ] - } - ], - [ - { - "instanceName" : "buffMgr", - "inputPorts" : [ - { - "name" : "bufferSendIn", - "portNumbers" : [ - 0 - ] - }, - { - "name" : "bufferGetCallee", - "portNumbers" : [ - 0 - ] - } - ], - "outputPorts" : [] - } - ] - ], - "connections" : [ - [ - [ - 0, - 0, - 1, - 0 - ], - [ - 2, - 0, - 1, - 0 - ] - ], - [ - [ - 0, - 0, - 0, - 0 - ], - [ - 1, - 0, - 0, - 0 - ] - ], - [ - [ - 1, - 0, - 0, - 0 - ], - [ - 2, - 0, - 0, - 0 - ] - ] - ] -} diff --git a/Svc/Deframer/docs/img/top/framed-active.png b/Svc/Deframer/docs/img/top/framed-active.png deleted file mode 100644 index d941a771747..00000000000 Binary files a/Svc/Deframer/docs/img/top/framed-active.png and /dev/null differ diff --git a/Svc/Deframer/docs/img/top/framed-active.txt b/Svc/Deframer/docs/img/top/framed-active.txt deleted file mode 100644 index ec3f5c37797..00000000000 --- a/Svc/Deframer/docs/img/top/framed-active.txt +++ /dev/null @@ -1,20 +0,0 @@ -activeComm -allocate -0 -buffMgr -bufferGetCallee -0 - -activeComm -recv -0 -deframer -framedIn -0 - -deframer -framedDeallocate -0 -buffMgr -bufferSendIn -0 diff --git a/Svc/Deframer/docs/img/top/framed-passive.json b/Svc/Deframer/docs/img/top/framed-passive.json deleted file mode 100644 index 428a67f0a4f..00000000000 --- a/Svc/Deframer/docs/img/top/framed-passive.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "columns" : [ - [ - { - "instanceName" : "rateGroup", - "inputPorts" : [], - "outputPorts" : [ - { - "name" : "RateGroupMemberOut", - "portNumbers" : [ - 0 - ] - } - ] - } - ], - [ - { - "instanceName" : "deframer", - "inputPorts" : [ - { - "name" : "schedIn", - "portNumbers" : [ - 0 - ] - } - ], - "outputPorts" : [ - { - "name" : "framedPoll", - "portNumbers" : [ - 0 - ] - } - ] - } - ], - [ - { - "instanceName" : "passiveComm", - "inputPorts" : [ - { - "name" : "poll", - "portNumbers" : [ - 0 - ] - } - ], - "outputPorts" : [] - } - ] - ], - "connections" : [ - [ - [ - 0, - 0, - 0, - 0 - ], - [ - 1, - 0, - 0, - 0 - ] - ], - [ - [ - 1, - 0, - 0, - 0 - ], - [ - 2, - 0, - 0, - 0 - ] - ] - ] -} diff --git a/Svc/Deframer/docs/img/top/framed-passive.png b/Svc/Deframer/docs/img/top/framed-passive.png deleted file mode 100644 index 6254b39090a..00000000000 Binary files a/Svc/Deframer/docs/img/top/framed-passive.png and /dev/null differ diff --git a/Svc/Deframer/docs/img/top/framed-passive.txt b/Svc/Deframer/docs/img/top/framed-passive.txt deleted file mode 100644 index 8dbdf5c0eca..00000000000 --- a/Svc/Deframer/docs/img/top/framed-passive.txt +++ /dev/null @@ -1,13 +0,0 @@ -rateGroup -RateGroupMemberOut -0 -deframer -schedIn -0 - -deframer -framedPoll -0 -passiveComm -poll -0 diff --git a/Svc/Deframer/docs/img/top/hub-cmd.json b/Svc/Deframer/docs/img/top/hub-cmd.json deleted file mode 100644 index 173ba2411f8..00000000000 --- a/Svc/Deframer/docs/img/top/hub-cmd.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "columns" : [ - [ - { - "instanceName" : "deframer", - "inputPorts" : [], - "outputPorts" : [ - { - "name" : "comOut", - "portNumbers" : [ - 0 - ] - } - ] - } - ], - [ - { - "instanceName" : "hub", - "inputPorts" : [ - { - "name" : "portIn", - "portNumbers" : [ - 0 - ] - } - ], - "outputPorts" : [] - } - ] - ], - "connections" : [ - [ - [ - 0, - 0, - 0, - 0 - ], - [ - 1, - 0, - 0, - 0 - ] - ] - ] -} diff --git a/Svc/Deframer/docs/img/top/hub-cmd.png b/Svc/Deframer/docs/img/top/hub-cmd.png deleted file mode 100644 index ed34b1c7de7..00000000000 Binary files a/Svc/Deframer/docs/img/top/hub-cmd.png and /dev/null differ diff --git a/Svc/Deframer/docs/img/top/hub-cmd.txt b/Svc/Deframer/docs/img/top/hub-cmd.txt deleted file mode 100644 index 55302ba996c..00000000000 --- a/Svc/Deframer/docs/img/top/hub-cmd.txt +++ /dev/null @@ -1,6 +0,0 @@ -deframer -comOut -0 -hub -portIn -0 diff --git a/Svc/Deframer/docs/img/top/hub-file.json b/Svc/Deframer/docs/img/top/hub-file.json deleted file mode 100644 index e3a14136c5f..00000000000 --- a/Svc/Deframer/docs/img/top/hub-file.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "columns" : [ - [ - { - "instanceName" : "deframer", - "inputPorts" : [], - "outputPorts" : [ - { - "name" : "bufferOut", - "portNumbers" : [ - 0 - ] - }, - { - "name" : "bufferDeallocate", - "portNumbers" : [ - 0 - ] - }, - { - "name" : "bufferAllocate", - "portNumbers" : [ - 0 - ] - } - ] - } - ], - [ - { - "instanceName" : "hub", - "inputPorts" : [ - { - "name" : "buffersIn", - "portNumbers" : [ - 0 - ] - } - ], - "outputPorts" : [ - { - "name" : "bufferDeallocate", - "portNumbers" : [ - 0 - ] - } - ] - } - ], - [ - { - "instanceName" : "buffMgr", - "inputPorts" : [ - { - "name" : "bufferSendIn", - "portNumbers" : [ - 0 - ] - }, - { - "name" : "bufferGetCallee", - "portNumbers" : [ - 0 - ] - } - ], - "outputPorts" : [] - } - ] - ], - "connections" : [ - [ - [ - 0, - 0, - 0, - 0 - ], - [ - 1, - 0, - 0, - 0 - ] - ], - [ - [ - 1, - 0, - 0, - 0 - ], - [ - 2, - 0, - 0, - 0 - ] - ], - [ - [ - 0, - 0, - 2, - 0 - ], - [ - 2, - 0, - 1, - 0 - ] - ], - [ - [ - 0, - 0, - 1, - 0 - ], - [ - 2, - 0, - 0, - 0 - ] - ] - ] -} diff --git a/Svc/Deframer/docs/img/top/hub-file.png b/Svc/Deframer/docs/img/top/hub-file.png deleted file mode 100644 index c73171779bf..00000000000 Binary files a/Svc/Deframer/docs/img/top/hub-file.png and /dev/null differ diff --git a/Svc/Deframer/docs/img/top/hub-file.txt b/Svc/Deframer/docs/img/top/hub-file.txt deleted file mode 100644 index d2d83484b91..00000000000 --- a/Svc/Deframer/docs/img/top/hub-file.txt +++ /dev/null @@ -1,28 +0,0 @@ -deframer -bufferOut -0 -hub -buffersIn -0 - -hub -bufferDeallocate -0 -buffMgr -bufferSendIn -0 - -deframer -bufferAllocate -0 -buffMgr -bufferGetCallee -0 - -deframer -bufferDeallocate -0 -buffMgr -bufferSendIn -0 - diff --git a/Svc/Deframer/docs/sdd.md b/Svc/Deframer/docs/sdd.md deleted file mode 100644 index 5c4acf2719f..00000000000 --- a/Svc/Deframer/docs/sdd.md +++ /dev/null @@ -1,519 +0,0 @@ -# Svc::Deframer (Passive Component) - -## 1. Introduction - -`Svc::Deframer` is a passive component. -It accepts as input a sequence of byte buffers, which -typically come from a ground data system via a -[byte stream driver](../../../Drv/ByteStreamDriverModel/docs/sdd.md). -It interprets the concatenated data of the buffers -as a sequence of uplink frames. -The uplink frames need not be aligned on the -buffer boundaries, and each frame may span one or more buffers. -`Deframer` extracts the frames from the sequence of buffers. -For each complete frame _F_ received, `Deframer` -validates _F_ and extracts a data packet from _F_. -It sends the data packet to another component in the service layer, e.g., -an instance of [`Svc::CommandDispatcher`](../../CmdDispatcher/docs/sdd.md), -[`Svc::FileUplink`](../../FileUplink/docs/sdd.md), -or [`Svc::GenericHub`](../../GenericHub/docs/sdd.md). - -When instantiating Deframer, you must provide an implementation -of [`Svc::DeframingProtocol`](../../FramingProtocol/docs/sdd.md). -This implementation specifies exactly what is -in each frame; typically it is a frame header, a data packet, and a hash value. - -On receiving a buffer _FB_ containing framed data, `Deframer` -(1) copies the data from _FB_ into a circular buffer _CB_ owned by `Deframer` and (2) -calls the `deframe` method of the `Svc::DeframingProtocol` implementation, -passing a reference to _CB_ as input. -If _FB_ holds more data than will fit in _CB_, -then `Deframer` repeats this process until _FB_ is empty. -If the protocol implementation reports that the data in _CB_ -represents an incomplete frame, then `Deframer` postpones deframing -until the next buffer _FB_ becomes available. - -Deframer supports two configurations for streaming data: - -1. **Poll:** This configuration works with a passive byte stream driver. - In this configuration, `Deframer` polls the driver for buffers - on its `schedIn` cycle. - No buffer allocation occurs when polling. - _FB_ is a buffer owned by `Deframer`. - -2. **Push:** This configuration works with an active byte stream driver. - In this configuration the driver invokes a guarded port of `Deframer` to - send a buffer _FB_ to `Deframer`. - The invocation transfers ownership of _FB_ from the driver to `Deframer`. - Deframing occurs on the thread of the byte stream driver. - `Deframer` deallocates _FB_ before it returns from - the guarded port call. - -## 2. Assumptions - -1. For any deployment _D_ that uses an instance _I_ of `Deframer`, the - deframing protocol used with _I_ matches the uplink protocol of - any ground system that sends frames to _I_. - -1. In any topology _T_, for any instance _I_ of `Deframer` in _T_, - at any one time, framed data arrives on the poll interface of _I_ or on - the push interface of _I_, but not on both concurrently. - The push and poll interfaces are guarded by a mutual exclusion lock, - so there is no concurrency safety issue. - However, ordinarily it does not make sense to interleave framed data - concurrently on two different interfaces. - -1. Each frame received by `Deframer` contains an F Prime command packet - or file packet _P_. - The first _n_ bytes of the packet hold the packet descriptor value - `Fw::ComPacket::FW_PACKET_COMMAND` (for a command packet) or - `Fw::ComPacket::FW_PACKET_FILE` (for a file packet), - serialized as an unsigned integer in big-endian byte order. - The number of bytes _n_ matches the size of the type defined by the - C preprocessor symbol `FwPacketDescriptorType` in the F Prime FSW. - -## 3. Requirements - -Requirement | Description | Rationale | Verification Method ------------ | ----------- | ----------| ------------------- -SVC-DEFRAMER-001 | `Svc::Deframer` shall accept a sequence of byte buffers and interpret their concatenated data as a sequence of uplink frames. | The purpose of the component is to do uplink deframing. | Unit test -SVC-DEFRAMER-002 | `Svc::Deframer` shall accept byte buffers containing uplink frames that are not aligned on a buffer boundary. | For flexibility, we do not require that the frames be aligned on a buffer boundary. | Unit test -SVC-DEFRAMER-003 | `Svc::Deframer` shall accept byte buffers containing uplink frames that span one or more buffers. | For flexibility, we do not require each frame to fit in a single buffer. | Unit test -SVC-DEFRAMER-004 | `Svc::Deframer` shall provide a port interface that a threaded driver can use to push byte buffers to be deframed. | This interface supports applications in which the byte stream driver has its own thread. | Unit test -SVC-DEFRAMER-005 | `Svc::Deframer` shall provide a port interface that Deframer can use to poll for byte buffers to be deframed. | This interface supports applications in which byte stream driver does not have its own thread. | Unit test -SVC-DEFRAMER-006 | If the polling interface is connected, then `Svc::Deframer` shall poll for byte buffers on its `schedIn` port. | This requirement allows the system scheduler to drive the periodic polling. | Unit test -SVC-DEFRAMER-007 | `Svc::Deframer` shall use an instance of `Svc::DeframingProtocol`, supplied when the component is instantiated, to validate the frames and extract their packet data. | Using the `Svc::DeframingProtocol` interface allows the same Deframer component to operate with different protocols. | Unit test -SVC-DEFRAMER-008 | `Svc::Deframer` shall interpret the initial bytes of the packet data as a value of type `FwPacketDescriptorType`. | `FwPacketDescriptorType` is the type of an F Prime packet descriptor. The size of the type is configurable in the F Prime framework. | Test -SVC-DEFRAMER-009 | `Svc::Deframer` shall extract and send packets with the following types: `Fw::ComPacket::FW_PACKET_COMMAND`, `Fw::ComPacket::FW_PACKET_FILE`. | These are the packet types used for uplink. | Unit test -SVC-DEFRAMER-010 | `Svc::Deframer` shall send command packets and file packets on separate ports. | Command packets and file packets are typically handled by different components. | Unit test -SVC-DEFRAMER-011 | `Svc::Deframer` shall operate nominally when its port for sending file packets is unconnected, even if it receives a frame containing a file packet. | Some applications do not use file uplink. Sending a file uplink packet to `Deframer` should not crash the application because of an unconnected port. | Unit test - -## 4. Design - -### 4.1. Component Diagram - -The diagram below shows the `Deframer` component. - -![Deframer](img/Deframer.png) - -### 4.2. Ports - -`Deframer` has the following ports: - -| Kind | Name | Port Type | Usage | -|------|------|-----------|-------| -| `guarded input` | `framedIn` | `Drv.ByteStreamRecv` | Port for receiving frame buffers FB pushed from the byte stream driver. After using a buffer FB received on this port, Deframer deallocates it by invoking framedDeallocate.| -| `output` | `framedDeallocate` | `Fw.BufferSend` | Port for deallocating buffers received on framedIn. | -| `guarded input` | `schedIn` | `Svc.Sched` | Schedule in port, driven by a rate group.| -| `output` | `framedPoll` | `Drv.ByteStreamPoll` | Port that polls for data from the byte stream driver. Deframer invokes this port on its schedIn cycle, if it is connected. No allocation or occurs when invoking this port. The data transfer uses a pre-allocated frame buffer owned by Deframer. | -| `output` | `bufferAllocate` | `Fw.BufferGet` | Port for allocating Fw::Buffer objects from a buffer manager. When Deframer invokes this port, it receives a packet buffer PB and takes ownership of it. It uses PB internally for deframing. Then one of two things happens: 1. PB contains a file packet, which Deframer sends on bufferOut. In this case ownership of PB passes to the receiver. 2. PB does not contain a file packet, or bufferOut is unconnected. In this case Deframer deallocates PB on bufferDeallocate. | -| `output` | `bufferOut` | `Fw.BufferSend` | Port for sending file packets (case 1 above). The file packets are wrapped in Fw::Buffer objects allocated with bufferAllocate. Ownership of the Fw::Buffer passes to the receiver, which is responsible for the deallocation. | -| `output` | `bufferDeallocate` | `Fw.BufferSend` | Port for deallocating temporary buffers allocated with bufferAllocate (case 2 above). Deallocation occurs here when there is nothing to send on bufferOut. | -| `output` | `comOut` | `Fw.Com` | Port for sending command packets as Com buffers. | -| `sync input` | `cmdResponseIn` | `Fw.CmdResponse` | Port for receiving command responses from a command dispatcher. Invoking this port does nothing. The port exists to allow the matching connection in the topology. | - - -### 4.3. Derived Classes - -`Deframer` is derived from `DeframerComponentBase` as usual. -It is also derived (via C++ multiple inheritance) from -[`Svc::DeframingProtocolInterface`](../../FramingProtocol/docs/sdd.md). -The multiple inheritance makes the `Deframer` instance into the -instance of `Svc::DeframingProtocolInterface` that is required -to use `Svc::DeframingProtocol`. -See below for a description of how `Deframer` implements -`DeframingProtocolInterface`. - -Here is a class diagram for `Deframer`: - -```mermaid -classDiagram - ObjBase <|-- PassiveComponentBase - PassiveComponentBase <|-- DeframerComponentBase - DeframerComponentBase <|-- Deframer - DeframingProtocolInterface <|-- Deframer -``` - -### 4.4. State - -`Deframer` maintains the following state: - -1. `m_protocol`: A pointer to the implementation of `DeframingProtocol` - used for deframing. - -1. `m_inRing`: An instance of `Types::CircularBuffer` for storing data to be deframed. - -1. `m_ringBuffer`: The storage backing the circular buffer: an array of `RING_BUFFER_SIZE` -`U8` values. - -1. `m_pollBuffer`: The buffer used for polling input: an array of 1024 `POLL_BUFFER_SIZE` -values. - -### 4.5. Header File Configuration - -The `Deframer` header file provides the following configurable constants: - -1. `Svc::Deframer::RING_BUFFER_SIZE`: The size of the circular buffer. -The capacity of the circular buffer must be large enough to hold a -complete frame. - -1. `Svc::Deframer::POLL_BUFFER_SIZE`: The size of the buffer used for polling data. - -### 4.6. Runtime Setup - -To set up an instance of `Deframer`, you do the following: - -1. Call the constructor and the `init` method in the usual way -for an F Prime passive component. - -1. Call the `setup` method, passing in an instance _P_ of `Svc::DeframingProtocol`. -The `setup` method does the following: - - 1. Store a pointer to _P_ in `m_protocol`. - - 1. Pass `*this` into the setup method for _P_. - As noted above, `*this` - is the instance of `Svc::DeframingProtocolInterface` - used by _P_. - -For an example of setting up a `Deframer` instance, see the -`uplink` instance in [`Ref/Top/instances.fpp`](../../../Ref/Top/instances.fpp). - -### 4.7. Port Handlers - -#### 4.7.1. framedIn - -The `framedIn` port handler receives an `Fw::Buffer` _FB_ and a receive status _S_. -It does the following: - -1. If _S_ = `RECV_OK`, then call - `processBuffer`, passing in _FB_. - -2. Deallocate _FB_ by invoking `framedDeallocate`. - -#### 4.7.2. schedIn - -The `schedIn` port handler does the following: - -1. Construct an `Fw::Buffer` _FB_ that wraps `m_pollBuffer`. - -1. If `framedPoll` is connected, then - - 1. Invoke `framedPollOut`, passing in _FB_, to poll for new data. - - 1. If new data is available, then call - `processBuffer`, passing in _FB_. - -#### 4.7.3. cmdResponseIn - -The `cmdResponseIn` handler does nothing. -It exists to provide the necessary symmetry in the topology -(every component that sends a command to the dispatcher should -accept a matching response). - - -### 4.8. Implementation of Svc::DeframingProtocolInterface - - -#### 4.8.1. allocate - -The implementation of `allocate` invokes `bufferAllocate`. - - -#### 4.8.2. route - -The implementation of `route` takes a reference to an -`Fw::Buffer` _PB_ (a packet buffer) and does the following: - -1. Set `deallocate = true`. - -1. Let _N_ = `sizeof(FwPacketDescriptorType)`. -Deserialize the first _N_ bytes of _PB_ as a value of type -`FwPacketDescriptorType`. - -1. If the deserialization succeeds, then switch on the packet type _T_. - - 1. If _T_ = `FW_PACKET_COMMAND`, then send the contents - of _PB_ as a Com buffer on `comOut`. - - 1. Otherwise if _T_ = `FW_PACKET_FILE` and `bufferOut` is connected, - then - - 1. Shift the pointer of _PB_ _N_ bytes forward and - reduce the size of _PB_ by _N_ to skip the packet type. - This step is necessary to accommodate the `FileUplink` component. - - 1. Send _B_ on `bufferOut`. - - 1. Set `deallocate = false`. This step causes ownership - of the buffer to pass to the receiver. - -1. If `deallocate = true`, then invoke `bufferDeallocate` - to deallocate _PB_. - -### 4.9. Helper Functions - - -#### 4.9.1. processBuffer - -`processBuffer` accepts a reference to an `Fw::Buffer` _FB_ -(a frame buffer). -It does the following: - -1. Set `buffer_offset` = 0. - -1. Set _S_ = `buffer.getSize()`. - -1. In a bounded loop, while `buffer_offset` < _S_, do: - - 1. Compute the amount of remaining data in _FB_. - This is _R_ = _S_ - `buffer_offset`. - - 1. Compute _C_, the number of bytes to copy from _FB_ into the - circular buffer `m_inRing`. - - 1. Let _F_ be the number of free bytes in `m_inRing`. - - 1. If _R_ < _F_, then _C_ = _R_. - - 1. Otherwise _C_ = _F_. - - 1. Copy _C_ bytes from _FB_ starting at `buffer_offset` - into `m_inRing`. - - 1. Advance `buffer_offset` by _C_. - - 1. Call `processRing` - to process the data stored in `m_inRing`. - - -#### 4.9.2. processRing - -In a bounded loop, while there is data remaining in `m_inRing`, do: - -1. Call the `deframe` method of `m_protocol` on `m_inRing`. - The `deframe` method calls `allocate` and - `route` as necessary. - It returns a status value _S_ and the number _N_ of bytes - needed for successful deframing. - -1. If _S_ = `SUCCESS`, then _N_ represents the number of bytes - used in a successful deframing. Rotate `m_inRing` by _N_ bytes (i.e., - deallocate _N_ bytes from the head of `m_inRing`). - -1. Otherwise if _S_ = `MORE_NEEDED`, then do nothing. - Further processing will occur on the next call, after more - data goes into `m_inRing`. - -1. Otherwise something is wrong. - Rotate `m_inRing` by one byte, to skip byte by byte over - bad data until we find a valid frame. - -## 5. Ground Interface - -None. - -## 6. Example Uses - - -### 6.1. Topology Diagrams - -The following topology diagrams show how to connect `Svc::Deframer` -to a byte stream driver, a command dispatcher, and a file uplink component. -The diagrams use the following instances: - -* `activeComm`: An active instance of -[`Drv::ByteStreamDriverModel`](../../../Drv/ByteStreamDriverModel/docs/sdd.md), for example, -[`Drv::TcpClient`](../../../Drv/TcpClient/docs/sdd.md). - -* `buffMgr`: An instance of [`Svc::BufferManager`](../../BufferManager/docs/sdd.md) - -* `cmdDisp`: An instance of [`Svc::CommandDispatcher`](../../CmdDispatcher/docs/sdd.md) - -* `deframer`: An instance of `Svc::Deframer`. - -* `fileUplink`: An instance of [`Svc::FileUplink`](../../FileUplink/docs/sdd.md). - -* `passiveComm`: A passive instance of -[`Drv::ByteStreamDriverModel`](../../../Drv/ByteStreamDriverModel/docs/sdd.md). - -* `rateGroup`: An instance of [`Svc::ActiveRateGroup`](../../ActiveRateGroup/docs/sdd.md). - -Topologies 1a and 1b are alternate topologies. -You should use one or the other. -In topology 3, the `fileUplink` instance and its connections are -optional. - -**Topology 1a: Buffers containing framed data (active byte stream driver):** - -![active](img/top/framed-active.png) - -**Topology 1b: Buffers containing framed data (passive byte stream driver):** - -![passive](img/top/framed-passive.png) - -Revise the port number of `rateGroup.RateGroupMemberOut` as -appropriate for your application. - -**Topology 2: Command packets and command responses:** - -![cmd](img/top/cmd.png) - -Revise the port numbers of `cmdDisp.seqCmdBuff` and -`cmdDisp.compCmdStat` as appropriate for your application. -If you model your topology in FPP, then FPP can automatically -assign these numbers. - -**Topology 3: Buffers containing packet data:** - -![file](img/top/deframer-file.png) - -### 6.2. Sequence Diagrams - -#### 6.2.1. Active Byte Stream Driver - -**Sending a command packet:** -The following sequence diagram shows what happens when `activeComm` -sends data to `deframer`, and `deframer` -decodes the data into a command packet. -Open vertical rectangles represent threads. -Vertical dashed lines represent component code. -Solid horizontal arrows represent synchronous port invocations, and open -horizontal arrows represent asynchronous port invocations. - -```mermaid -sequenceDiagram - activate activeComm - activeComm->>buffMgr: Allocate frame buffer FB - buffMgr-->>activeComm: Return FB - activeComm->>activeComm: Fill FB with framed data - activeComm->>deframer: Send FB[framedIn] - deframer->>buffMgr: Allocate packet buffer PB [bufferAllocate] - buffMgr-->>deframer: Return PB - deframer->>deframer: Deframe FB into PB - deframer->>deframer: Copy PB into a command packet C - deframer-)cmdDisp: Send C [comOut] - deframer->>buffMgr: Deallocate PB [bufferDeallocate] - buffMgr-->>deframer: - deframer->>buffMgr: Deallocate FB [framedDeallocate] - buffMgr-->>deframer: - deframer-->>activeComm: - deactivate activeComm - activate cmdDisp - cmdDisp->>deframer: Send cmd response [cmdResponseIn] - deframer-->>cmdDisp: - deactivate cmdDisp -``` - -**Sending a file packet:** -The following sequence diagram shows what happens when `activeComm` -sends data to `deframer`, and `deframer` decodes the data into a file packet. - -```mermaid -sequenceDiagram - activate activeComm - activeComm->>buffMgr: Allocate frame buffer FB - buffMgr-->>activeComm: Return FB - activeComm->>activeComm: Fill FB with framed data - activeComm->>deframer: Send FB [framedIn] - deframer->>buffMgr: Allocate packet buffer PB [bufferAllocate] - buffMgr-->>deframer: Return PB - deframer->>deframer: Deframe FB into PB - deframer-)fileUplink: Send PB [bufferOut] - deframer->>buffMgr: Deallocate FB [framedDeallocate] - buffMgr-->>deframer: - deframer-->>activeComm: - deactivate activeComm - activate fileUplink - fileUplink->>buffMgr: Deallocate PB - buffMgr-->>fileUplink: - deactivate fileUplink -``` - -#### 6.2.2. Passive Byte Stream Driver - -**Sending a command packet:** The following sequence diagram shows what -happens when `passiveComm` sends data to `deframer`, and -`deframer` decodes the data into a command packet. - -```mermaid -sequenceDiagram - activate rateGroup - rateGroup->>deframer: Send schedule tick [schedIn] - deframer->>passiveComm: Poll for data [framedPoll] - passiveComm-->>deframer: Return status - deframer->>buffMgr: Allocate packet buffer PB [bufferAllocate] - buffMgr-->>deframer: Return PB - deframer->>deframer: Deframe data into PB - deframer->>deframer: Copy PB into a command packet C - deframer-)cmdDisp: Send C [comOut] - deframer->>buffMgr: Deallocate PB [bufferDeallocate] - buffMgr-->>deframer: - deframer-->>rateGroup: - deactivate rateGroup - activate cmdDisp - cmdDisp->>deframer: Send cmd response [cmdResponseIn] - deframer-->>cmdDisp: - deactivate cmdDisp -``` - -**Sending a file packet:** The following sequence diagram shows what -happens when `passiveComm` sends data to `deframer`, and -`Deframer` decodes the data into a file packet. - -```mermaid -sequenceDiagram - activate rateGroup - rateGroup->>deframer: Send schedule tick [schedIn] - deframer->>passiveComm: Poll for data [framedPoll] - passiveComm-->>deframer: Return status - deframer->>buffMgr: Allocate packet buffer PB [bufferAllocate] - buffMgr-->>deframer: Return PB - deframer->>deframer: Deframe data into PB - deframer-)fileUplink: Send PB [bufferOut] - deframer-->>rateGroup: - deactivate rateGroup - activate fileUplink - fileUplink->>buffMgr: Deallocate PB - buffMgr-->>fileUplink: - deactivate fileUplink -``` - - -### 6.3. Using Svc::GenericHub - -You can use `Deframer` with an instance of -[`Svc::GenericHub`](../../GenericHub/docs/sdd.md) to send deframed -command packets and file packets across a network connection, instead of -directly to a command dispatcher or file uplink component. -To send deframed packets this way, do the following: - -1. In the topology described above, -instead of the `cmdDisp` and `fileUplink` instances, use an -instance `hub` of type `Svc::GenericHub`. - -1. Revise topologies 2 and 3 as shown below. - -**Topology 2: Command packets** - -![hub-cmd](img/top/hub-cmd.png) - -Revise the port number of `hub.portIn` as appropriate for your application. - -**Topology 3: Buffers containing packet data** - -![file](img/top/hub-file.png) - -Revise the port number of `hub.buffersIn` as appropriate for your application. -When `hub` receives a buffer on `buffersIn`, it copies the data across -the connection to the other hub and deallocates the buffer. - -If you don't need to transmit file packets across the hub, then you can -omit the `hub` connections shown in this topology. - -## 7. Change Log - -| Date | Description | -|---|---| -| 2021-01-30 | Initial Draft | -| 2022-04-04 | Revised | diff --git a/Svc/Deframer/test/ut-fprime-protocol/DeframerTestMain.cpp b/Svc/Deframer/test/ut-fprime-protocol/DeframerTestMain.cpp deleted file mode 100644 index da7091a50e9..00000000000 --- a/Svc/Deframer/test/ut-fprime-protocol/DeframerTestMain.cpp +++ /dev/null @@ -1,132 +0,0 @@ -// ---------------------------------------------------------------------- -// TestMain.cpp -// ---------------------------------------------------------------------- - -#include - -#include "Fw/Test/UnitTest.hpp" -#include "GenerateFrames.hpp" -#include "Os/Console.hpp" -#include "STest/Scenario/BoundedScenario.hpp" -#include "STest/Scenario/RandomScenario.hpp" -#include "STest/Scenario/Scenario.hpp" -#include "SendBuffer.hpp" -#include "DeframerTester.hpp" - -#define STEP_COUNT 10000 - -// Uncomment the following line to turn on OS logging -//Os::Log logger; - -// ---------------------------------------------------------------------- -// Static helper functions -// ---------------------------------------------------------------------- - -//! Run a random scenario -static void runRandomScenario(Svc::DeframerTester::InputMode::t inputMode) { - Svc::DeframerTester tester(Svc::DeframerTester::InputMode::PUSH); - - // Create rules, and assign them into the array - Svc::GenerateFrames generateFrames; - Svc::SendBuffer sendBuffer; - - // Setup a list of rules to choose from - STest::Rule* rules[] = { - &generateFrames, - &sendBuffer - }; - // Construct the random scenario and run it with the defined bounds - STest::RandomScenario random( - "Random Rules", - rules, - FW_NUM_ARRAY_ELEMENTS(rules) - ); - - // Setup a bounded scenario to run rules a set number of times - STest::BoundedScenario bounded( - "Bounded Random Rules Scenario", - random, - STEP_COUNT - ); - // Run! - const U32 numSteps = bounded.run(tester); - printf("Ran %u steps.\n", numSteps); -} - -// ---------------------------------------------------------------------- -// Tests -// ---------------------------------------------------------------------- - -TEST(Nominal, BasicPush) { - COMMENT("Send one buffer to the deframer, simulating an active driver (push)"); - REQUIREMENT("SVC-DEFRAMER-001"); - REQUIREMENT("SVC-DEFRAMER-002"); - REQUIREMENT("SVC-DEFRAMER-003"); - REQUIREMENT("SVC-DEFRAMER-004"); - REQUIREMENT("SVC-DEFRAMER-007"); - REQUIREMENT("SVC-DEFRAMER-008"); - REQUIREMENT("SVC-DEFRAMER-009"); - REQUIREMENT("SVC-DEFRAMER-010"); - Svc::DeframerTester tester(Svc::DeframerTester::InputMode::PUSH); - Svc::GenerateFrames().apply(tester); - Svc::SendBuffer().apply(tester); -} - -TEST(Nominal, BasicPoll) { - COMMENT("Send one buffer to the deframer, simulating a passive driver (poll)"); - REQUIREMENT("SVC-DEFRAMER-001"); - REQUIREMENT("SVC-DEFRAMER-002"); - REQUIREMENT("SVC-DEFRAMER-003"); - REQUIREMENT("SVC-DEFRAMER-005"); - REQUIREMENT("SVC-DEFRAMER-006"); - REQUIREMENT("SVC-DEFRAMER-007"); - REQUIREMENT("SVC-DEFRAMER-008"); - REQUIREMENT("SVC-DEFRAMER-009"); - REQUIREMENT("SVC-DEFRAMER-010"); - Svc::DeframerTester tester(Svc::DeframerTester::InputMode::POLL); - Svc::GenerateFrames().apply(tester); - Svc::SendBuffer().apply(tester); -} - -TEST(Nominal, RandomPush) { - COMMENT("Send random buffers to the deframer, simulating an active driver (push)"); - REQUIREMENT("SVC-DEFRAMER-001"); - REQUIREMENT("SVC-DEFRAMER-002"); - REQUIREMENT("SVC-DEFRAMER-003"); - REQUIREMENT("SVC-DEFRAMER-004"); - REQUIREMENT("SVC-DEFRAMER-007"); - REQUIREMENT("SVC-DEFRAMER-008"); - REQUIREMENT("SVC-DEFRAMER-009"); - REQUIREMENT("SVC-DEFRAMER-010"); - runRandomScenario(Svc::DeframerTester::InputMode::PUSH); -} - -TEST(Nominal, RandomPoll) { - COMMENT("Send random buffers to the deframer, simulating a passive driver (poll)"); - REQUIREMENT("SVC-DEFRAMER-001"); - REQUIREMENT("SVC-DEFRAMER-002"); - REQUIREMENT("SVC-DEFRAMER-003"); - REQUIREMENT("SVC-DEFRAMER-005"); - REQUIREMENT("SVC-DEFRAMER-006"); - REQUIREMENT("SVC-DEFRAMER-007"); - REQUIREMENT("SVC-DEFRAMER-008"); - REQUIREMENT("SVC-DEFRAMER-009"); - REQUIREMENT("SVC-DEFRAMER-010"); - runRandomScenario(Svc::DeframerTester::InputMode::POLL); -} - -TEST(Error, SizeOverflow) { - COMMENT("Test handling of size overflow in F Prime deframing protocol"); - Svc::DeframerTester tester(Svc::DeframerTester::InputMode::PUSH); - tester.sizeOverflow(); -} - -// ---------------------------------------------------------------------- -// Main function -// ---------------------------------------------------------------------- - -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - STest::Random::seed(); - return RUN_ALL_TESTS(); -} diff --git a/Svc/Deframer/test/ut-fprime-protocol/DeframerTester.cpp b/Svc/Deframer/test/ut-fprime-protocol/DeframerTester.cpp deleted file mode 100644 index c4262372b0c..00000000000 --- a/Svc/Deframer/test/ut-fprime-protocol/DeframerTester.cpp +++ /dev/null @@ -1,261 +0,0 @@ -// ====================================================================== -// \title DeframerTester.cpp -// \brief Implementation file for Deframer test with F Prime protocol -// \author mstarch, bocchino -// -// \copyright -// Copyright 2009-2022, by the California Institute of Technology. -// ALL RIGHTS RESERVED. United States Government Sponsorship -// acknowledged. -// ====================================================================== - -#include -#include - -#include "Fw/Types/Assert.hpp" -#include "DeframerTester.hpp" -#include "Utils/Hash/Hash.hpp" -#include "Utils/Hash/HashBuffer.hpp" - -#define INSTANCE 0 -#define MAX_HISTORY_SIZE 10000 - -namespace Svc { - - // ---------------------------------------------------------------------- - // Constructor - // ---------------------------------------------------------------------- - - DeframerTester ::DeframerTester(InputMode::t inputMode) - : DeframerGTestBase("Tester", MAX_HISTORY_SIZE), - component("Deframer"), - m_inputMode(inputMode) - { - this->initComponents(); - this->connectPorts(); - component.setup(protocol); - memset(m_incomingBufferBytes, 0, sizeof m_incomingBufferBytes); - } - - // ---------------------------------------------------------------------- - // Tests - // ---------------------------------------------------------------------- - - void DeframerTester ::sizeOverflow() { - U8 data[FpFrameHeader::SIZE]; - Fw::Buffer buffer(data, sizeof data); - Fw::SerializeBufferBase& serialRepr = buffer.getSerializeRepr(); - Fw::SerializeStatus status = serialRepr.serialize(FpFrameHeader::START_WORD); - ASSERT_EQ(status, Fw::FW_SERIALIZE_OK); - FpFrameHeader::TokenType size = std::numeric_limits::max(); - status = serialRepr.serialize(size); - ASSERT_EQ(status, Fw::FW_SERIALIZE_OK); - this->component.processBuffer(buffer); - // Assert no output - ASSERT_FROM_PORT_HISTORY_SIZE(0); - } - - // ---------------------------------------------------------------------- - // Public instance methods - // ---------------------------------------------------------------------- - - void DeframerTester ::setUpIncomingBuffer() { - const U32 bufferSize = STest::Pick::lowerUpper( - 1, - sizeof m_incomingBufferBytes - ); - ASSERT_LE(bufferSize, sizeof m_incomingBufferBytes); - m_incomingBuffer = Fw::Buffer( - m_incomingBufferBytes, - bufferSize - ); - } - - void DeframerTester ::sendIncomingBuffer() { - switch (m_inputMode) { - case InputMode::PUSH: - // Push buffer to framedIn - invoke_to_framedIn( - 0, - m_incomingBuffer, - Drv::RecvStatus::RECV_OK - ); - break; - case InputMode::POLL: - // Call schedIn handler, which polls for buffer - invoke_to_schedIn(0, 0); - break; - default: - FW_ASSERT(0); - break; - } - } - - // ---------------------------------------------------------------------- - // Handlers for typed from ports - // ---------------------------------------------------------------------- - - void DeframerTester ::from_comOut_handler( - const FwIndexType portNum, - Fw::ComBuffer& data, - U32 context - ) { - // Check that a received frame is expected - ASSERT_GT(m_framesToReceive.size(), 0) << - "Queue of frames to receive is empty" << std::endl; - // Get the frame at the front - UplinkFrame frame = m_framesToReceive.front(); - m_framesToReceive.pop_front(); - // Check the packet type - ASSERT_EQ(frame.packetType, Fw::ComPacket::FW_PACKET_COMMAND); - // Check the packet data - for (U32 i = 0; i < data.getBuffLength(); i++) { - EXPECT_EQ( - (data.getBuffAddr())[i], - (frame.getData())[FpFrameHeader::SIZE + i] - ); - } - // Push the history entry - this->pushFromPortEntry_comOut(data, context); - } - - void DeframerTester ::from_bufferOut_handler( - const FwIndexType portNum, - Fw::Buffer& fwBuffer - ) { - // Check that a received frame is expected - ASSERT_GT(m_framesToReceive.size(), 0) << - "Queue of frames to receive is empty" << std::endl; - // Get the frame at the front - UplinkFrame frame = m_framesToReceive.front(); - m_framesToReceive.pop_front(); - // Check the packet type - ASSERT_EQ(frame.packetType, Fw::ComPacket::FW_PACKET_FILE); - // Check the packet data - for (U32 i = 0; i < fwBuffer.getSize(); i++) { - // Deframer strips type before sending to FileUplink - const U32 frameOffset = - FpFrameHeader::SIZE + sizeof(FwPacketDescriptorType) + i; - ASSERT_EQ( - (fwBuffer.getData())[i], - (frame.getData())[frameOffset] - ); - } - // Buffers received on this port are owned by the receiver - // So delete the allocation now - // Before deallocating, undo the deframer's adjustment of the pointer - // by the size of the packet type - delete[](fwBuffer.getData() - sizeof(FwPacketDescriptorType)); - // Push the history entry - this->pushFromPortEntry_bufferOut(fwBuffer); - } - - Fw::Buffer DeframerTester ::from_bufferAllocate_handler( - const FwIndexType portNum, - U32 size - ) { - this->pushFromPortEntry_bufferAllocate(size); - U8 *const data = new U8[size]; - memset(data, 0, size); - Fw::Buffer buffer(data, size); - return buffer; - } - - void DeframerTester ::from_bufferDeallocate_handler( - const FwIndexType portNum, - Fw::Buffer& fwBuffer - ) { - delete[] fwBuffer.getData(); - this->pushFromPortEntry_bufferDeallocate(fwBuffer); - } - - void DeframerTester ::from_framedDeallocate_handler( - const FwIndexType portNum, - Fw::Buffer& fwBuffer - ) { - this->pushFromPortEntry_framedDeallocate(fwBuffer); - } - - Drv::PollStatus DeframerTester ::from_framedPoll_handler( - const FwIndexType portNum, - Fw::Buffer& pollBuffer - ) { - this->pushFromPortEntry_framedPoll(pollBuffer); - U8* incoming = m_incomingBuffer.getData(); - const U32 size = m_incomingBuffer.getSize(); - U8* outgoing = pollBuffer.getData(); - const U32 maxSize = pollBuffer.getSize(); - FW_ASSERT(size <= maxSize, size, maxSize); - memcpy(outgoing, incoming, size); - pollBuffer.setSize(size); - return Drv::PollStatus::POLL_OK; - } - - // ---------------------------------------------------------------------- - // Helper methods - // ---------------------------------------------------------------------- - - void DeframerTester ::connectPorts() { - - // bufferAllocate - this->component.set_bufferAllocate_OutputPort( - 0, - this->get_from_bufferAllocate(0) - ); - - // bufferDeallocate - this->component.set_bufferDeallocate_OutputPort( - 0, - this->get_from_bufferDeallocate(0) - ); - - // bufferOut - this->component.set_bufferOut_OutputPort( - 0, - this->get_from_bufferOut(0) - ); - - // cmdResponseIn - this->connect_to_cmdResponseIn( - 0, - this->component.get_cmdResponseIn_InputPort(0) - ); - - // comOut - this->component.set_comOut_OutputPort( - 0, - this->get_from_comOut(0) - ); - - // framedDeallocate - this->component.set_framedDeallocate_OutputPort( - 0, - this->get_from_framedDeallocate(0) - ); - - // framedIn - this->connect_to_framedIn( - 0, - this->component.get_framedIn_InputPort(0) - ); - - // framedPoll - this->component.set_framedPoll_OutputPort( - 0, - this->get_from_framedPoll(0) - ); - - // schedIn - this->connect_to_schedIn( - 0, - this->component.get_schedIn_InputPort(0) - ); - - } - - void DeframerTester ::initComponents() { - this->init(); - this->component.init(INSTANCE); - } - -} diff --git a/Svc/Deframer/test/ut-fprime-protocol/DeframerTester.hpp b/Svc/Deframer/test/ut-fprime-protocol/DeframerTester.hpp deleted file mode 100644 index 858ffd441a0..00000000000 --- a/Svc/Deframer/test/ut-fprime-protocol/DeframerTester.hpp +++ /dev/null @@ -1,325 +0,0 @@ -// ====================================================================== -// \title DeframerTester.hpp -// \brief Header file for Deframer test with F Prime protocol -// \author mstarch, bocchino -// -// \copyright -// Copyright 2009-2022, by the California Institute of Technology. -// ALL RIGHTS RESERVED. United States Government Sponsorship -// acknowledged. -// ====================================================================== - -#ifndef SVC_TESTER_HPP -#define SVC_TESTER_HPP - -#include -#include - -#include "DeframerGTestBase.hpp" -#include "Fw/Com/ComPacket.hpp" -#include "Fw/Types/SerialBuffer.hpp" -#include "STest/STest/Pick/Pick.hpp" -#include "Svc/Deframer/Deframer.hpp" -#include "Svc/FramingProtocol/FprimeProtocol.hpp" -#include "Utils/Hash/Hash.hpp" - -namespace Svc { - - class DeframerTester : public DeframerGTestBase { - - // ---------------------------------------------------------------------- - // Friend classes - // ---------------------------------------------------------------------- - - friend struct GenerateFrames; - friend class SendBuffer; - - // ---------------------------------------------------------------------- - // Types - // ---------------------------------------------------------------------- - - public: - - //! Enumerated constants - enum Constants { - //! The maximum valid frame size - //! Every valid frame must fit in the ring buffer - MAX_VALID_FRAME_SIZE = DeframerCfg::RING_BUFFER_SIZE, - //! The max frame size that will fit in the test buffer - //! Larger than max valid size, to test bad sizes - MAX_FRAME_SIZE = MAX_VALID_FRAME_SIZE + 1, - //! The size of the part of the frame that is outside the packet - NON_PACKET_SIZE = FpFrameHeader::SIZE + HASH_DIGEST_LENGTH, - //! The offset of the start word in an F Prime protocol frame - START_WORD_OFFSET = 0, - //! The offset of the packet size in an F Prime protocol frame - PACKET_SIZE_OFFSET = START_WORD_OFFSET + - sizeof FpFrameHeader::START_WORD, - //! The offset of the packet type in an F Prime protocol frame - PACKET_TYPE_OFFSET = FpFrameHeader::SIZE, - }; - - //! The type of the input mode - struct InputMode { - typedef enum { - //! Push data from another thread - PUSH, - //! Poll for data on the schedIn thread - POLL - } t; - }; - - //! An uplink frame for testing - class UplinkFrame { - - // ---------------------------------------------------------------------- - // Types - // ---------------------------------------------------------------------- - - //! The type of frame data - typedef U8 FrameData[MAX_FRAME_SIZE]; - - public: - - // ---------------------------------------------------------------------- - // Constructor - // ---------------------------------------------------------------------- - - //! Construct an uplink frame - UplinkFrame( - Fw::ComPacket::ComPacketType packetType, //!< The packet type - U32 packetSize //!< The packet size - ); - - public: - - // ---------------------------------------------------------------------- - // Public instance methods - // ---------------------------------------------------------------------- - - //! Copy data from the frame, advancing the copy offset - void copyDataOut( - Fw::SerialBuffer& serialBuffer, //!< The serial buffer to copy to - U32 size //!< The number of bytes to copy - ); - - //! Get a constant reference to the frame data - const FrameData& getData() const; - - //! Get the frame size - U32 getSize() const; - - //! Get the size of data that remains for copying - U32 getRemainingCopySize() const; - - //! Report whether the frame is valid - bool isValid() const; - - public: - - // ---------------------------------------------------------------------- - // Public static methods - // ---------------------------------------------------------------------- - - //! Construct a random frame - static UplinkFrame random(); - - //! Get the max packet size that will fit in the test buffer - //! This is an invalid size for the deframer - static U32 getInvalidPacketSize(); - - //! Get the max valid command packet size - static U32 getMaxValidCommandPacketSize(); - - //! Get the max valid file packet size - static U32 getMaxValidFilePacketSize(); - - //! Get the min packet size - static U32 getMinPacketSize(); - - private: - - // ---------------------------------------------------------------------- - // Private instance methods - // ---------------------------------------------------------------------- - - //! Randomly invalidate a valid frame, or leave it alone - //! If the frame is already invalid, leave it alone - void randomlyInvalidate(); - - //! Update the frame header - void updateHeader(); - - //! Update the hash value - void updateHash(); - - //! Write an arbitrary packet size - void writePacketSize( - FpFrameHeader::TokenType ps //!< The packet size - ); - - //! Write an arbitrary packet type - void writePacketType( - FwPacketDescriptorType pt //!< The packet type - ); - - //! Write an arbitrary start word - void writeStartWord( - FpFrameHeader::TokenType sw //!< The start word - ); - - public: - - // ---------------------------------------------------------------------- - // Public member variables - // ---------------------------------------------------------------------- - - //! The packet type - const Fw::ComPacket::ComPacketType packetType; - - //! The packet size - const U32 packetSize; - - private: - - // ---------------------------------------------------------------------- - // Private member variables - // ---------------------------------------------------------------------- - - //! The frame data, including header, packet data, and hash. - //! The array is big enough to hold a frame larger than the - //! max valid frame size for the deframer. - U8 data[MAX_FRAME_SIZE]; - - //! The amount of frame data already copied out into a buffer - U32 copyOffset; - - //! Whether the frame is valid - bool valid; - - }; - - public: - - // ---------------------------------------------------------------------- - // Constructor - // ---------------------------------------------------------------------- - - //! Construct a DeframerTester - DeframerTester(InputMode::t inputMode); - - public: - - // ---------------------------------------------------------------------- - // Tests - // ---------------------------------------------------------------------- - - //! Size would cause integer overflow - void sizeOverflow(); - - public: - - // ---------------------------------------------------------------------- - // Public instance methods - // ---------------------------------------------------------------------- - - //! Set up the incoming buffer - void setUpIncomingBuffer(); - - //! Send the incoming buffer - void sendIncomingBuffer(); - - private: - - // ---------------------------------------------------------------------- - // Handlers for typed from ports - // ---------------------------------------------------------------------- - - //! Handler for from_comOut - void from_comOut_handler( - const FwIndexType portNum, //!< The port number - Fw::ComBuffer& data, //!< Buffer containing packet data - U32 context //!< Call context value; meaning chosen by user - ); - - //! Handler for from_bufferOut - void from_bufferOut_handler( - const FwIndexType portNum, //!< The port number - Fw::Buffer& fwBuffer //!< The buffer - ); - - //! Handler for from_bufferAllocate - Fw::Buffer from_bufferAllocate_handler( - const FwIndexType portNum, //!< The port number - U32 size //!< The size - ); - - //! Handler for from_bufferDeallocate - void from_bufferDeallocate_handler( - const FwIndexType portNum, //!< The port number - Fw::Buffer& fwBuffer //!< The buffer - ); - - //! Handler for from_framedDeallocate - //! - void from_framedDeallocate_handler( - const FwIndexType portNum, //!< The port number - Fw::Buffer& fwBuffer //!< The buffer - ); - - //! Handler for from_framedPoll - Drv::PollStatus from_framedPoll_handler( - const FwIndexType portNum, //!< The port number - Fw::Buffer& pollBuffer //!< The poll buffer - ); - - private: - - // ---------------------------------------------------------------------- - // Private helper methods - // ---------------------------------------------------------------------- - - //! Connect ports - void connectPorts(); - - //! Initialize components - void initComponents(); - - //! Allocate a packet buffer - Fw::Buffer allocatePacketBuffer( - U32 size //!< The buffer size - ); - - private: - - // ---------------------------------------------------------------------- - // Private member variables - // ---------------------------------------------------------------------- - - //! The component under test - Deframer component; - - //! The deframing protocol - Svc::FprimeDeframing protocol; - - //! Frames that the DeframerTester should send to the Deframer - std::deque m_framesToSend; - - //! Frames that the DeframerTester should receive from the Deframer - std::deque m_framesToReceive; - - //! Byte store for the incoming buffer - //! In polling mode, the incoming buffer must fit in the poll buffer - U8 m_incomingBufferBytes[DeframerCfg::POLL_BUFFER_SIZE]; - - //! Serialized frame data to send to the Deframer - Fw::Buffer m_incomingBuffer; - - //! The input mode - InputMode::t m_inputMode; - - }; - -} - -#endif diff --git a/Svc/Deframer/test/ut-fprime-protocol/GenerateFrames.cpp b/Svc/Deframer/test/ut-fprime-protocol/GenerateFrames.cpp deleted file mode 100644 index 76d9710ed86..00000000000 --- a/Svc/Deframer/test/ut-fprime-protocol/GenerateFrames.cpp +++ /dev/null @@ -1,40 +0,0 @@ -//! ====================================================================== -//! \title GenerateFrames.cpp -//! \brief Implementation file for GenerateFrames rule -//! \author mstarch, bocchino -//! ====================================================================== - -#include "GenerateFrames.hpp" -#include "Printing.hpp" -#include "STest/Pick/Pick.hpp" -#include "Utils/Hash/Hash.hpp" - -namespace Svc { - - GenerateFrames :: GenerateFrames() : - STest::Rule("GenerateFrames") - { - - } - - bool GenerateFrames :: precondition(const Svc::DeframerTester &state) { - return state.m_framesToSend.size() == 0; - } - - void GenerateFrames :: action(Svc::DeframerTester &state) { - PRINT("----------------------------------------------------------------------"); - PRINT("GenerateFrames action"); - PRINT("----------------------------------------------------------------------"); - // Generate 1-100 frames - const U32 numFrames = STest::Pick::lowerUpper(1, 100); - PRINT_ARGS("Generating %d frames", numFrames) - for (U32 i = 0; i < numFrames; i++) { - // Generate a random frame - DeframerTester::UplinkFrame frame = DeframerTester::UplinkFrame::random(); - // Push it on the sending list - state.m_framesToSend.push_back(frame); - } - PRINT_ARGS("frameToSend.size()=%lu", state.m_framesToSend.size()) - } - -} diff --git a/Svc/Deframer/test/ut-fprime-protocol/GenerateFrames.hpp b/Svc/Deframer/test/ut-fprime-protocol/GenerateFrames.hpp deleted file mode 100644 index 170a11a8832..00000000000 --- a/Svc/Deframer/test/ut-fprime-protocol/GenerateFrames.hpp +++ /dev/null @@ -1,46 +0,0 @@ -//! ====================================================================== -//! \title DeframerRules.hpp -//! \brief Header file for GenerateFrames rule -//! \author lestarch, bocchino -//! ====================================================================== - -#ifndef SVC_GENERATE_FRAMES_HPP -#define SVC_GENERATE_FRAMES_HPP - -#include -#include "Fw/Types/StringType.hpp" -#include "STest/STest/Pick/Pick.hpp" -#include "STest/STest/Rule/Rule.hpp" -#include "DeframerTester.hpp" - -namespace Svc { - - //! Generate frames to send - struct GenerateFrames : public STest::Rule { - - // ---------------------------------------------------------------------- - // Construction - // ---------------------------------------------------------------------- - - //! Constructor - GenerateFrames(); - - // ---------------------------------------------------------------------- - // Public member functions - // ---------------------------------------------------------------------- - - //! Precondition - bool precondition( - const DeframerTester& state //!< The test state - ); - - //! Action - void action( - DeframerTester& state //!< The test state - ); - - }; - -} - -#endif diff --git a/Svc/Deframer/test/ut-fprime-protocol/Printing.hpp b/Svc/Deframer/test/ut-fprime-protocol/Printing.hpp deleted file mode 100644 index 7dd24e5039e..00000000000 --- a/Svc/Deframer/test/ut-fprime-protocol/Printing.hpp +++ /dev/null @@ -1,18 +0,0 @@ -//! ====================================================================== -//! \title Printing.hpp -//! \brief Print macros for deframer unit tests -//! \author bocchino -//! ====================================================================== - -// Uncomment the following line to turn on printing -//#define PRINTING - -#ifdef PRINTING -#include - -#define PRINT(S) printf("[Deframer Tests] " S "\n"); -#define PRINT_ARGS(S, ...) printf("[Deframer Tests] " S "\n", __VA_ARGS__); -#else -#define PRINT(S) -#define PRINT_ARGS(S, ...) -#endif diff --git a/Svc/Deframer/test/ut-fprime-protocol/SendBuffer.cpp b/Svc/Deframer/test/ut-fprime-protocol/SendBuffer.cpp deleted file mode 100644 index 4fc2b6ee503..00000000000 --- a/Svc/Deframer/test/ut-fprime-protocol/SendBuffer.cpp +++ /dev/null @@ -1,158 +0,0 @@ -//! ====================================================================== -//! \title SendBuffer.cpp -//! \brief Implementation file for SendBuffer rule -//! \author mstarch, bocchino -//! ====================================================================== - -#include "Printing.hpp" -#include "STest/Pick/Pick.hpp" -#include "SendBuffer.hpp" -#include "Utils/Hash/Hash.hpp" - -namespace Svc { - - SendBuffer :: SendBuffer() : - STest::Rule("SendBuffer"), - expectedComCount(0), - expectedBuffCount(0) - { - - } - - bool SendBuffer :: precondition(const Svc::DeframerTester &state) { - return state.m_framesToSend.size() > 0; - } - - void SendBuffer :: action(Svc::DeframerTester &state) { - - PRINT("----------------------------------------------------------------------"); - PRINT("SendBuffer action"); - PRINT("----------------------------------------------------------------------"); - - // Clear the test history - state.clearHistory(); - - // Set up the incoming buffer - state.setUpIncomingBuffer(); - - // Fill the incoming buffer with frame data - fillIncomingBuffer(state); - - // Send the buffer - state.sendIncomingBuffer(); - - // Check the counts - state.assert_from_comOut_size(__FILE__, __LINE__, expectedComCount); - PRINT_ARGS("expectedComCount=%d", expectedComCount) - state.assert_from_bufferOut_size(__FILE__, __LINE__, expectedBuffCount); - PRINT_ARGS("expectedBuffCount=%d", expectedBuffCount) - - } - - void SendBuffer :: fillIncomingBuffer(Svc::DeframerTester &state) { - - // Get the size of the incoming buffer - const U32 incomingBufferSize = state.m_incomingBuffer.getSize(); - - // Set up a serial buffer for data transfer - Fw::SerialBuffer serialBuffer( - state.m_incomingBufferBytes, - incomingBufferSize - ); - - // Reset the expected com count - expectedComCount = 0; - // Reset the expected buff count - expectedBuffCount = 0; - - // The number of bytes copied into the buffer - U32 copiedSize = 0; - // The size of available data in the buffer - U32 buffAvailable = incomingBufferSize; - - // Fill the incoming buffer as much as possible with available frames - for (U32 i = 0; i < incomingBufferSize; ++i) { - - // Check if there is any room left in the buffer - if (buffAvailable == 0) { - break; - } - - // Check if there are any frames to send - if (state.m_framesToSend.size() == 0) { - break; - } - - // Get the frame from the head of the sending queue - DeframerTester::UplinkFrame& frame = state.m_framesToSend.front(); - - // Compute the amount to copy - const U32 frameAvailable = frame.getRemainingCopySize(); - const U32 copyAmt = std::min(frameAvailable, buffAvailable); - - // Copy the frame data into the serial buffer - frame.copyDataOut(serialBuffer, copyAmt); - - // Update buffAvailable - ASSERT_GE(buffAvailable, copyAmt); - buffAvailable -= copyAmt; - - // Update copiedSize - copiedSize += copyAmt; - ASSERT_LE(copiedSize, incomingBufferSize); - - // If we have copied a complete frame F, then (1) remove F from - // the send queue; and (2) if F is valid, then record F as - // received - if (frame.getRemainingCopySize() == 0) { - // If F is valid, then record it as received - recordReceivedFrame(state, frame); - // Remove F from the send queue - state.m_framesToSend.pop_front(); - PRINT_ARGS( - "frameToSend.size()=%lu", - state.m_framesToSend.size() - ) - } - - } - - // Update the buffer - ASSERT_LE(copiedSize, incomingBufferSize); - state.m_incomingBuffer.setSize(copiedSize); - - } - - void SendBuffer :: recordReceivedFrame( - Svc::DeframerTester& state, - Svc::DeframerTester::UplinkFrame& frame - ) { - if (frame.isValid()) { - // Push frame F on the received queue - state.m_framesToReceive.push_back(frame); - // Update the count of expected frames - switch (frame.packetType) { - case Fw::ComPacket::FW_PACKET_COMMAND: - PRINT("popped valid command frame") - // If F contains a command packet, then increment - // the expected com count - ++expectedComCount; - break; - case Fw::ComPacket::FW_PACKET_FILE: - PRINT("popped valid file frame") - // If F contains a file packet, then increment - // the expected buffer count - ++expectedBuffCount; - break; - default: - // This should not happen for a valid frame - FW_ASSERT(0, frame.packetType); - break; - } - } - else { - PRINT("popped invalid frame") - } - } - -}; diff --git a/Svc/Deframer/test/ut-fprime-protocol/SendBuffer.hpp b/Svc/Deframer/test/ut-fprime-protocol/SendBuffer.hpp deleted file mode 100644 index fc91d893649..00000000000 --- a/Svc/Deframer/test/ut-fprime-protocol/SendBuffer.hpp +++ /dev/null @@ -1,80 +0,0 @@ -//! ====================================================================== -//! \title SendBuffer.hpp -//! \brief Header file for SendBuffer rule -//! \author lestarch, bocchino -//! ====================================================================== - -#ifndef SVC_SEND_BUFFER_HPP -#define SVC_SEND_BUFFER_HPP - -#include -#include "Fw/Types/StringType.hpp" -#include "STest/STest/Pick/Pick.hpp" -#include "STest/STest/Rule/Rule.hpp" -#include "DeframerTester.hpp" - -namespace Svc { - - //! Pack generated frames into a buffer - //! Send the buffer - class SendBuffer : public STest::Rule { - - public: - - // ---------------------------------------------------------------------- - // Construction - // ---------------------------------------------------------------------- - - //! Constructor - SendBuffer(); - - public: - - // ---------------------------------------------------------------------- - // Public functions - // ---------------------------------------------------------------------- - - //! Precondition - bool precondition( - const DeframerTester& state //!< The test state - ); - - //! Action - void action( - Svc::DeframerTester &state //!< The test state - ); - - private: - - // ---------------------------------------------------------------------- - // Private helper functions - // ---------------------------------------------------------------------- - - //! Fill the incoming buffer with frame data - void fillIncomingBuffer( - Svc::DeframerTester &state //!< The test state - ); - - //! Record a received frame - void recordReceivedFrame( - Svc::DeframerTester& state, //!< The test state - Svc::DeframerTester::UplinkFrame& frame //!< The frame - ); - - private: - - // ---------------------------------------------------------------------- - // Private member variables - // ---------------------------------------------------------------------- - - //! The expected number of com buffers emitted - U32 expectedComCount; - - //! The expected number of file packet buffers emitted - U32 expectedBuffCount; - - }; - -}; - -#endif diff --git a/Svc/Deframer/test/ut-fprime-protocol/UplinkFrame.cpp b/Svc/Deframer/test/ut-fprime-protocol/UplinkFrame.cpp deleted file mode 100644 index 8110ea42a55..00000000000 --- a/Svc/Deframer/test/ut-fprime-protocol/UplinkFrame.cpp +++ /dev/null @@ -1,259 +0,0 @@ -// ====================================================================== -// \title UplinkFrame.cpp -// \author mstarch, bocchino -// \brief Implementation file for DeframerTester::UplinkFrame -// -// \copyright -// Copyright 2009-2022, by the California Institute of Technology. -// ALL RIGHTS RESERVED. United States Government Sponsorship -// acknowledged. -// ====================================================================== - -#include "DeframerTester.hpp" - -namespace Svc { - - // ---------------------------------------------------------------------- - // Constructor - // ---------------------------------------------------------------------- - - DeframerTester::UplinkFrame::UplinkFrame( - Fw::ComPacket::ComPacketType packetType, - U32 packetSize - ) : - packetType(packetType), - packetSize(packetSize), - copyOffset(0), - valid(false) - { - // Fill in random data - for (U32 i = 0; i < sizeof data; ++i) { - data[i] = STest::Pick::lowerUpper(0, 0xFF); - } - // Update the frame header - this->updateHeader(); - // Update the hash value - this->updateHash(); - // Check validity of packet type and size - if ( - (packetType == Fw::ComPacket::FW_PACKET_COMMAND) && - (packetSize <= getMaxValidCommandPacketSize()) - ) { - valid = true; - } - if ( - (packetType == Fw::ComPacket::FW_PACKET_FILE) && - (packetSize <= getMaxValidFilePacketSize()) - ) { - valid = true; - } - } - - // ---------------------------------------------------------------------- - // Public instance methods - // ---------------------------------------------------------------------- - - U32 DeframerTester::UplinkFrame::getRemainingCopySize() const { - const U32 frameSize = getSize(); - FW_ASSERT(frameSize >= copyOffset, frameSize, copyOffset); - return frameSize - copyOffset; - } - - U32 DeframerTester::UplinkFrame::getSize() const { - return NON_PACKET_SIZE + packetSize; - } - - bool DeframerTester::UplinkFrame::isValid() const { - return valid; - } - - const DeframerTester::UplinkFrame::FrameData& - DeframerTester::UplinkFrame::getData() const - { - return data; - } - - void DeframerTester::UplinkFrame::copyDataOut( - Fw::SerialBuffer& serialBuffer, - U32 size - ) { - ASSERT_LE(copyOffset + size, getSize()); - const Fw::SerializeStatus status = serialBuffer.pushBytes( - &data[copyOffset], - size - ); - ASSERT_EQ(status, Fw::FW_SERIALIZE_OK); - copyOffset += size; - } - - // ---------------------------------------------------------------------- - // Public static methods - // ---------------------------------------------------------------------- - - DeframerTester::UplinkFrame DeframerTester::UplinkFrame::random() { - // Randomly set the packet type - Fw::ComPacket::ComPacketType packetType = - Fw::ComPacket::FW_PACKET_UNKNOWN; - const U32 packetSelector = STest::Pick::lowerUpper(0,1); - U32 maxValidPacketSize = 0; - switch (packetSelector) { - case 0: - packetType = Fw::ComPacket::FW_PACKET_COMMAND; - maxValidPacketSize = getMaxValidCommandPacketSize(); - break; - case 1: - packetType = Fw::ComPacket::FW_PACKET_FILE; - maxValidPacketSize = getMaxValidFilePacketSize(); - break; - default: - FW_ASSERT(0); - break; - } - // Randomly set the packet size - U32 packetSize = 0; - // Invalidate 1 in 100 packet sizes - const U32 invalidSizeIndex = STest::Pick::startLength(0, 100); - if (invalidSizeIndex == 0) { - // This packet size fits in the test buffer, - // but is invalid for the deframer - packetSize = getInvalidPacketSize(); - } - else { - // Choose a valid packet size - // This packet size fits in the test buffer and is - // valid for the deframer - packetSize = STest::Pick::lowerUpper( - getMinPacketSize(), - maxValidPacketSize - ); - } - // Construct the frame - UplinkFrame frame = UplinkFrame(packetType, packetSize); - // Randomly invalidate the frame, or leave it alone - frame.randomlyInvalidate(); - // Return the frame - return frame; - } - - U32 DeframerTester::UplinkFrame::getInvalidPacketSize() { - FW_ASSERT( - MAX_FRAME_SIZE >= NON_PACKET_SIZE, - MAX_FRAME_SIZE, - NON_PACKET_SIZE - ); - const U32 result = MAX_FRAME_SIZE - NON_PACKET_SIZE; - // Make sure the size is invalid - FW_ASSERT( - result > getMaxValidCommandPacketSize(), - result, - getMaxValidCommandPacketSize() - ); - FW_ASSERT( - result > getMaxValidFilePacketSize(), - result, - getMaxValidFilePacketSize() - ); - return result; - } - - U32 DeframerTester::UplinkFrame::getMaxValidCommandPacketSize() { - return std::min( - // Packet must fit into a com buffer - static_cast(FW_COM_BUFFER_MAX_SIZE), - // Frame must fit into the ring buffer - getMaxValidFilePacketSize() - ); - } - - U32 DeframerTester::UplinkFrame::getMaxValidFilePacketSize() { - FW_ASSERT( - MAX_VALID_FRAME_SIZE >= NON_PACKET_SIZE, - MAX_VALID_FRAME_SIZE, - NON_PACKET_SIZE - ); - return MAX_VALID_FRAME_SIZE - NON_PACKET_SIZE; - } - - U32 DeframerTester::UplinkFrame::getMinPacketSize() { - // Packet must hold the packet type - return sizeof(FwPacketDescriptorType); - } - - // ---------------------------------------------------------------------- - // Private instance methods - // ---------------------------------------------------------------------- - - void DeframerTester::UplinkFrame::randomlyInvalidate() { - if (valid) { - // Invalidation cases occur out of 100 samples - const U32 invalidateIndex = STest::Pick::startLength(0, 100); - switch (invalidateIndex) { - case 0: { - // Invalidate the start word - const FpFrameHeader::TokenType badStartWord = - FpFrameHeader::START_WORD + 1; - writeStartWord(badStartWord); - valid = false; - break; - } - case 1: - // Invalidate the packet type - writePacketType(Fw::ComPacket::FW_PACKET_UNKNOWN); - valid = false; - break; - case 2: - // Invalidate the hash value - ++data[getSize() - 1]; - valid = false; - break; - default: - // Stay valid - break; - } - } - } - - void DeframerTester::UplinkFrame::updateHash() { - Utils::Hash hash; - Utils::HashBuffer hashBuffer; - const U32 dataSize = FpFrameHeader::SIZE + packetSize; - hash.update(data, dataSize); - hash.final(hashBuffer); - const U8 *const hashAddr = hashBuffer.getBuffAddr(); - memcpy(&data[dataSize], hashAddr, HASH_DIGEST_LENGTH); - } - - void DeframerTester::UplinkFrame::updateHeader() { - // Write the correct start word - writeStartWord(FpFrameHeader::START_WORD); - // Write the correct packet size - writePacketSize(packetSize); - // Write the correct packet type - writePacketType(packetType); - } - - void DeframerTester::UplinkFrame::writePacketSize( - FpFrameHeader::TokenType ps - ) { - Fw::SerialBuffer sb(&data[PACKET_SIZE_OFFSET], sizeof ps); - const Fw::SerializeStatus status = sb.serialize(packetSize); - ASSERT_EQ(status, Fw::FW_SERIALIZE_OK); - } - - void DeframerTester::UplinkFrame::writePacketType( - FwPacketDescriptorType pt - ) { - Fw::SerialBuffer sb(&data[PACKET_TYPE_OFFSET], sizeof pt); - const Fw::SerializeStatus status = sb.serialize(pt); - ASSERT_EQ(status, Fw::FW_SERIALIZE_OK); - } - - void DeframerTester::UplinkFrame::writeStartWord( - FpFrameHeader::TokenType sw - ) { - Fw::SerialBuffer sb(data, sizeof sw); - const Fw::SerializeStatus status = sb.serialize(sw); - ASSERT_EQ(status, Fw::FW_SERIALIZE_OK); - } - -} diff --git a/Svc/Deframer/test/ut/DeframerTestMain.cpp b/Svc/Deframer/test/ut/DeframerTestMain.cpp deleted file mode 100644 index 26ebf1a619a..00000000000 --- a/Svc/Deframer/test/ut/DeframerTestMain.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// ---------------------------------------------------------------------- -// TestMain.cpp -// ---------------------------------------------------------------------- - -#include "DeframerTester.hpp" -#include -#include - -TEST(Deframer, TestMoreNeeded) { - COMMENT("Send a frame buffer to the mock protocol, expecting more needed"); - REQUIREMENT("SVC-DEFRAMER-001"); - REQUIREMENT("SVC-DEFRAMER-002"); - REQUIREMENT("SVC-DEFRAMER-003"); - REQUIREMENT("SVC-DEFRAMER-007"); - Svc::DeframerTester tester; - tester.test_incoming_frame(Svc::DeframingProtocol::DEFRAMING_MORE_NEEDED); -} -TEST(Deframer, TestSuccess) { - COMMENT("Send a frame buffer to the mock protocol, expecting success"); - REQUIREMENT("SVC-DEFRAMER-001"); - REQUIREMENT("SVC-DEFRAMER-002"); - REQUIREMENT("SVC-DEFRAMER-003"); - REQUIREMENT("SVC-DEFRAMER-007"); - Svc::DeframerTester tester; - tester.test_incoming_frame(Svc::DeframingProtocol::DEFRAMING_STATUS_SUCCESS); -} -TEST(Deframer, TestBadChecksum) { - COMMENT("Send a frame buffer to the mock protocol, expecting bad checksum"); - REQUIREMENT("SVC-DEFRAMER-001"); - REQUIREMENT("SVC-DEFRAMER-002"); - REQUIREMENT("SVC-DEFRAMER-003"); - REQUIREMENT("SVC-DEFRAMER-007"); - Svc::DeframerTester tester; - tester.test_incoming_frame(Svc::DeframingProtocol::DEFRAMING_INVALID_CHECKSUM); -} -TEST(Deframer, TestBadSize) { - COMMENT("Send a frame buffer to the mock protocol, expecting bad size"); - REQUIREMENT("SVC-DEFRAMER-001"); - REQUIREMENT("SVC-DEFRAMER-002"); - REQUIREMENT("SVC-DEFRAMER-003"); - REQUIREMENT("SVC-DEFRAMER-007"); - Svc::DeframerTester tester; - tester.test_incoming_frame(Svc::DeframingProtocol::DEFRAMING_INVALID_SIZE); -} -TEST(Deframer, TestBadFormat) { - COMMENT("Send a frame buffer to the mock protocol, expecting bad format"); - REQUIREMENT("SVC-DEFRAMER-001"); - REQUIREMENT("SVC-DEFRAMER-002"); - REQUIREMENT("SVC-DEFRAMER-003"); - REQUIREMENT("SVC-DEFRAMER-007"); - Svc::DeframerTester tester; - tester.test_incoming_frame(Svc::DeframingProtocol::DEFRAMING_INVALID_FORMAT); -} -TEST(Deframer, TestComInterface) { - COMMENT("Route a com packet"); - REQUIREMENT("SVC-DEFRAMER-008"); - REQUIREMENT("SVC-DEFRAMER-009"); - REQUIREMENT("SVC-DEFRAMER-010"); - Svc::DeframerTester tester; - tester.test_com_interface(); -} -TEST(Deframer, TestFileInterface) { - COMMENT("Route a file packet"); - REQUIREMENT("SVC-DEFRAMER-008"); - REQUIREMENT("SVC-DEFRAMER-009"); - REQUIREMENT("SVC-DEFRAMER-010"); - Svc::DeframerTester tester; - tester.test_file_interface(); -} -TEST(Deframer, TestUnknownInterface) { - COMMENT("Attempt to route a packet of unknown type"); - REQUIREMENT("SVC-DEFRAMER-008"); - REQUIREMENT("SVC-DEFRAMER-009"); - Svc::DeframerTester tester; - tester.test_unknown_interface(); -} -TEST(Deframer, TestCommandResponse) { - COMMENT("Handle a command response (no-op)"); - Svc::DeframerTester tester; - tester.testCommandResponse(); -} -TEST(Deframer, TestCommandPacketTooLarge) { - COMMENT("Attempt to route a command packet that is too large"); - REQUIREMENT("SVC-DEFRAMER-008"); - REQUIREMENT("SVC-DEFRAMER-009"); - Svc::DeframerTester tester; - tester.testCommandPacketTooLarge(); -} -TEST(Deframer, TestPacketBufferTooSmall) { - COMMENT("Attempt to route a packet that is too small"); - REQUIREMENT("SVC-DEFRAMER-008"); - REQUIREMENT("SVC-DEFRAMER-009"); - Svc::DeframerTester tester; - tester.testPacketBufferTooSmall(); -} - -TEST(Deframer, TestBufferOutUnconnected) { - COMMENT("Test routing a file packet when bufferOut is unconnected"); - REQUIREMENT("SVC-DEFRAMER-011"); - Svc::DeframerTester tester(Svc::DeframerTester::ConnectStatus::UNCONNECTED); - tester.testBufferOutUnconnected(); -} - -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/Svc/Deframer/test/ut/DeframerTester.cpp b/Svc/Deframer/test/ut/DeframerTester.cpp deleted file mode 100644 index ca555a15a8c..00000000000 --- a/Svc/Deframer/test/ut/DeframerTester.cpp +++ /dev/null @@ -1,238 +0,0 @@ -// ====================================================================== -// \title Deframer.hpp -// \author janamian, bocchino -// \brief cpp file for Deframer test harness implementation class -// -// \copyright -// Copyright 2009-2021, by the California Institute of Technology. -// ALL RIGHTS RESERVED. United States Government Sponsorship -// acknowledged. -// -// ====================================================================== - -#include - -#include "DeframerTester.hpp" - -#define INSTANCE 0 -#define MAX_HISTORY_SIZE 1000 - -namespace Svc { - -// ---------------------------------------------------------------------- -// Construction and destruction -// ---------------------------------------------------------------------- -DeframerTester::MockDeframer::MockDeframer(DeframerTester& parent) : m_status(DeframingProtocol::DEFRAMING_STATUS_SUCCESS) {} - -DeframerTester::MockDeframer::DeframingStatus DeframerTester::MockDeframer::deframe(Types::CircularBuffer& ring_buffer, U32& needed) { - needed = ring_buffer.get_allocated_size(); - if (m_status == DeframingProtocol::DEFRAMING_MORE_NEEDED) { - needed = ring_buffer.get_allocated_size() + 1; // Obey the rules - } - return m_status; -} - -void DeframerTester::MockDeframer::test_interface(Fw::ComPacket::ComPacketType com_packet_type) { - const FwPacketDescriptorType descriptorType = com_packet_type; - U8 chars[sizeof descriptorType]; - m_interface->allocate(3042); - Fw::Buffer buffer(chars, sizeof(chars)); - buffer.getSerializeRepr().serialize(descriptorType); - m_interface->route(buffer); -} - - -DeframerTester ::DeframerTester(ConnectStatus::t bufferOutStatus) - : DeframerGTestBase("Tester", MAX_HISTORY_SIZE), - component("Deframer"), - m_mock(*this), - bufferOutStatus(bufferOutStatus) { - this->initComponents(); - this->connectPorts(); - component.setup(this->m_mock); -} - -DeframerTester ::~DeframerTester() {} - -// ---------------------------------------------------------------------- -// Tests -// ---------------------------------------------------------------------- -void DeframerTester ::test_incoming_frame(DeframerTester::MockDeframer::DeframingStatus status) { - U32 buffer_size = 512; - U8 data[buffer_size]; - ::memset(data, 0, buffer_size); - Fw::Buffer recvBuffer(data, buffer_size); - m_mock.m_status = status; - Drv::RecvStatus recvStatus = Drv::RecvStatus::RECV_OK; - invoke_to_framedIn(0, recvBuffer, recvStatus); - // Check remaining size - if (status == DeframingProtocol::DEFRAMING_MORE_NEEDED) { - ASSERT_EQ(component.m_inRing.get_allocated_size(), buffer_size); - } else if (status == DeframingProtocol::DEFRAMING_STATUS_SUCCESS) { - ASSERT_EQ(component.m_inRing.get_allocated_size(), 0); - } else { - ASSERT_EQ(component.m_inRing.get_allocated_size(), 0); - } - ASSERT_from_framedDeallocate(0, recvBuffer); -} - -void DeframerTester ::test_com_interface() { - m_mock.test_interface(Fw::ComPacket::FW_PACKET_COMMAND); - ASSERT_from_comOut_SIZE(1); - ASSERT_from_bufferAllocate(0, 3042); - ASSERT_from_bufferOut_SIZE(0); - ASSERT_from_bufferDeallocate_SIZE(1); -} - -void DeframerTester ::test_file_interface() { - m_mock.test_interface(Fw::ComPacket::FW_PACKET_FILE); - ASSERT_from_comOut_SIZE(0); - ASSERT_from_bufferAllocate(0, 3042); - ASSERT_from_bufferOut_SIZE(1); - ASSERT_from_bufferDeallocate_SIZE(0); -} - -void DeframerTester ::test_unknown_interface() { - m_mock.test_interface(Fw::ComPacket::FW_PACKET_UNKNOWN); - ASSERT_from_comOut_SIZE(0); - ASSERT_from_bufferAllocate(0, 3042); - ASSERT_from_bufferOut_SIZE(0); - ASSERT_from_bufferDeallocate_SIZE(1); -} - -void DeframerTester ::testCommandResponse() { - const U32 portNum = 0; - const U32 opcode = 0; - const U32 cmdSeq = 0; - const Fw::CmdResponse cmdResp(Fw::CmdResponse::OK); - this->invoke_to_cmdResponseIn(portNum, opcode, cmdSeq, cmdResp); - ASSERT_FROM_PORT_HISTORY_SIZE(0); -} - -void DeframerTester ::testCommandPacketTooLarge() { - // Allocate a large packet buffer - enum { - BUFFER_SIZE = 2 * FW_COM_BUFFER_MAX_SIZE - }; - U8 bytes[BUFFER_SIZE]; - ::memset(bytes, 0, sizeof bytes); - Fw::Buffer buffer(bytes, sizeof bytes); - // Serialize the packet type - Fw::SerializeBufferBase& serialRepr = buffer.getSerializeRepr(); - const FwPacketDescriptorType descriptorType = - Fw::ComPacket::FW_PACKET_COMMAND; - const Fw::SerializeStatus status = - serialRepr.serialize(descriptorType); - ASSERT_EQ(status, Fw::FW_SERIALIZE_OK); - // Call the route method - this->component.route(buffer); - // Assert buffer deallocated, no packet output - ASSERT_FROM_PORT_HISTORY_SIZE(1); - ASSERT_from_bufferDeallocate_SIZE(1); -} - -void DeframerTester ::testPacketBufferTooSmall() { - // Allocate a small packet buffer - U8 byte = 0; - Fw::Buffer buffer(&byte, sizeof byte); - // Call the route method - this->component.route(buffer); - // Assert buffer deallocated, no packet output - ASSERT_FROM_PORT_HISTORY_SIZE(1); - ASSERT_from_bufferDeallocate_SIZE(1); -} - -void DeframerTester ::testBufferOutUnconnected() { - ASSERT_EQ(this->bufferOutStatus, ConnectStatus::UNCONNECTED); - enum { - BUFFER_SIZE = 256 - }; - U8 bytes[BUFFER_SIZE]; - ::memset(bytes, 0, sizeof bytes); - Fw::Buffer buffer(bytes, sizeof bytes); - // Serialize the packet type - Fw::SerializeBufferBase& serialRepr = buffer.getSerializeRepr(); - const FwPacketDescriptorType descriptorType = - Fw::ComPacket::FW_PACKET_FILE; - const Fw::SerializeStatus status = - serialRepr.serialize(descriptorType); - ASSERT_EQ(status, Fw::FW_SERIALIZE_OK); - // Call the route method - this->component.route(buffer); - // Assert buffer deallocated, no packet output - ASSERT_FROM_PORT_HISTORY_SIZE(1); - ASSERT_from_bufferDeallocate_SIZE(1); -} - -// ---------------------------------------------------------------------- -// Handlers for typed from ports -// ---------------------------------------------------------------------- - -void DeframerTester ::from_comOut_handler(const FwIndexType portNum, Fw::ComBuffer& data, U32 context) { - this->pushFromPortEntry_comOut(data, context); -} - -void DeframerTester ::from_bufferOut_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) { - this->pushFromPortEntry_bufferOut(fwBuffer); -} - -Fw::Buffer DeframerTester ::from_bufferAllocate_handler(const FwIndexType portNum, U32 size) { - this->pushFromPortEntry_bufferAllocate(size); - Fw::Buffer buffer(nullptr, size); - return buffer; -} - -void DeframerTester ::from_bufferDeallocate_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) { - this->pushFromPortEntry_bufferDeallocate(fwBuffer); -} - -void DeframerTester ::from_framedDeallocate_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) { - this->pushFromPortEntry_framedDeallocate(fwBuffer); -} - -Drv::PollStatus DeframerTester ::from_framedPoll_handler(const FwIndexType portNum, Fw::Buffer& pollBuffer) { - this->pushFromPortEntry_framedPoll(pollBuffer); - return Drv::PollStatus::POLL_OK; -} - -// ---------------------------------------------------------------------- -// Helper methods -// ---------------------------------------------------------------------- - -void DeframerTester ::connectPorts() { - // bufferAllocate - this->component.set_bufferAllocate_OutputPort(0, this->get_from_bufferAllocate(0)); - - // bufferDeallocate - this->component.set_bufferDeallocate_OutputPort(0, this->get_from_bufferDeallocate(0)); - - // bufferOut - if (this->bufferOutStatus == ConnectStatus::CONNECTED) { - this->component.set_bufferOut_OutputPort(0, this->get_from_bufferOut(0)); - } - - // cmdResponseIn - this->connect_to_cmdResponseIn(0, this->component.get_cmdResponseIn_InputPort(0)); - - // comOut - this->component.set_comOut_OutputPort(0, this->get_from_comOut(0)); - - // framedDeallocate - this->component.set_framedDeallocate_OutputPort(0, this->get_from_framedDeallocate(0)); - - // framedIn - this->connect_to_framedIn(0, this->component.get_framedIn_InputPort(0)); - - // framedPoll - this->component.set_framedPoll_OutputPort(0, this->get_from_framedPoll(0)); - - // schedIn - this->connect_to_schedIn(0, this->component.get_schedIn_InputPort(0)); -} - -void DeframerTester ::initComponents() { - this->init(); - this->component.init(INSTANCE); -} - -} // end namespace Svc diff --git a/Svc/Deframer/test/ut/DeframerTester.hpp b/Svc/Deframer/test/ut/DeframerTester.hpp deleted file mode 100644 index e2b4a85742d..00000000000 --- a/Svc/Deframer/test/ut/DeframerTester.hpp +++ /dev/null @@ -1,155 +0,0 @@ -// ====================================================================== -// \title Deframer/test/ut/Tester.hpp -// \author janamian, bocchino -// \brief hpp file for Deframer test harness implementation class -// -// \copyright -// Copyright 2009-2021, by the California Institute of Technology. -// ALL RIGHTS RESERVED. United States Government Sponsorship -// acknowledged. -// -// ====================================================================== - -#ifndef TESTER_HPP -#define TESTER_HPP - -#include "DeframerGTestBase.hpp" -#include "Svc/Deframer/Deframer.hpp" - -namespace Svc { - -class DeframerTester : public DeframerGTestBase { - public: - // ---------------------------------------------------------------------- - // Types - // ---------------------------------------------------------------------- - - struct ConnectStatus { - //! Whether a port is connected - typedef enum { - CONNECTED, - UNCONNECTED - } t; - }; - - // ---------------------------------------------------------------------- - // Construction and destruction - // ---------------------------------------------------------------------- - class MockDeframer : public DeframingProtocol { - public: - MockDeframer(DeframerTester& parent); - DeframingStatus deframe(Types::CircularBuffer& ring_buffer, U32& needed); - //! Test the implementation of DeframingProtocolInterface provided - //! by the Deframer component - void test_interface(Fw::ComPacket::ComPacketType com_type); - DeframingStatus m_status; - }; - - public: - //! Construct object DeframerTester - //! - DeframerTester(ConnectStatus::t bufferOutStatus = ConnectStatus::CONNECTED); - - //! Destroy object DeframerTester - //! - ~DeframerTester(); - - public: - // ---------------------------------------------------------------------- - // Tests - // ---------------------------------------------------------------------- - - //! Send a frame to framedIn - void test_incoming_frame( - DeframingProtocol::DeframingStatus status //!< The status that the mock deframer will return - ); - - //! Route a com packet - void test_com_interface(); - - //! Route a file packet - void test_file_interface(); - - //! Route a packet of unknown type - void test_unknown_interface(); - - //! Invoke the command response input port - void testCommandResponse(); - - //! Attempt to route a command packet that is too large - void testCommandPacketTooLarge(); - - //! Attempt to route a packet buffer that is too small - void testPacketBufferTooSmall(); - - //! Route a file packet with bufferOutUnconnected - void testBufferOutUnconnected(); - - private: - // ---------------------------------------------------------------------- - // Handlers for typed from ports - // ---------------------------------------------------------------------- - - //! Handler for from_comOut - //! - void from_comOut_handler(const FwIndexType portNum, /*!< The port number*/ - Fw::ComBuffer& data, /*!< Buffer containing packet data*/ - U32 context /*!< Call context value; meaning chosen by user*/ - ); - - //! Handler for from_bufferOut - //! - void from_bufferOut_handler(const FwIndexType portNum, /*!< The port number*/ - Fw::Buffer& fwBuffer); - - //! Handler for from_bufferAllocate - //! - Fw::Buffer from_bufferAllocate_handler(const FwIndexType portNum, /*!< The port number*/ - U32 size); - - //! Handler for from_bufferDeallocate - //! - void from_bufferDeallocate_handler(const FwIndexType portNum, /*!< The port number*/ - Fw::Buffer& fwBuffer); - - //! Handler for from_framedDeallocate - //! - void from_framedDeallocate_handler(const FwIndexType portNum, /*!< The port number*/ - Fw::Buffer& fwBuffer); - - //! Handler for from_framedPoll - //! - Drv::PollStatus from_framedPoll_handler(const FwIndexType portNum, /*!< The port number*/ - Fw::Buffer& pollBuffer); - - private: - // ---------------------------------------------------------------------- - // Helper methods - // ---------------------------------------------------------------------- - - //! Connect ports - //! - void connectPorts(); - - //! Initialize components - //! - void initComponents(); - - private: - // ---------------------------------------------------------------------- - // Variables - // ---------------------------------------------------------------------- - - //! The component under test - //! - Deframer component; - - Fw::Buffer m_buffer; - MockDeframer m_mock; - // Whether the bufferOut port is connected - ConnectStatus::t bufferOutStatus; -}; - -} // end namespace Svc - -#endif diff --git a/Svc/FprimeDeframer/CMakeLists.txt b/Svc/FprimeDeframer/CMakeLists.txt new file mode 100644 index 00000000000..d95a7141828 --- /dev/null +++ b/Svc/FprimeDeframer/CMakeLists.txt @@ -0,0 +1,33 @@ +#### +# F prime CMakeLists.txt: +# +# SOURCE_FILES: combined list of source and autocoding files +# MOD_DEPS: (optional) module dependencies +# +# Note: using PROJECT_NAME as EXECUTABLE_NAME +#### +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/FprimeDeframer.fpp" + "${CMAKE_CURRENT_LIST_DIR}/FprimeDeframer.cpp" +) + +set(MOD_DEPS + Svc/FramingProtocol + Utils/Types +) + +register_fprime_module() + + +#### UTs #### +set(UT_SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/FprimeDeframer.fpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/FprimeDeframerTester.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/FprimeDeframerTestMain.cpp" +) +set(UT_MOD_DEPS + STest +) +set(UT_AUTO_HELPERS ON) + +register_fprime_ut() diff --git a/Svc/FprimeDeframer/FprimeDeframer.cpp b/Svc/FprimeDeframer/FprimeDeframer.cpp new file mode 100644 index 00000000000..886fa2fdaef --- /dev/null +++ b/Svc/FprimeDeframer/FprimeDeframer.cpp @@ -0,0 +1,41 @@ +// ====================================================================== +// \title FprimeDeframer.cpp +// \author thomas-bc +// \brief cpp file for FprimeDeframer component implementation class +// ====================================================================== + +#include "Svc/FprimeDeframer/FprimeDeframer.hpp" +#include "FpConfig.hpp" +#include "Fw/Types/Assert.hpp" + +#include "config/FrameHeaderSerializableAc.hpp" +#include "config/FrameTrailerSerializableAc.hpp" + + +namespace Svc { + +// ---------------------------------------------------------------------- +// Component construction and destruction +// ---------------------------------------------------------------------- + +FprimeDeframer ::FprimeDeframer(const char* const compName) : FprimeDeframerComponentBase(compName) {} + +FprimeDeframer ::~FprimeDeframer() {} + +// ---------------------------------------------------------------------- +// Handler implementations for user-defined typed input ports +// ---------------------------------------------------------------------- + +void FprimeDeframer ::framedIn_handler(FwIndexType portNum, Fw::Buffer& data, Fw::Buffer& context) { + + FW_ASSERT(data.getSize() >= FprimeProtocol::FrameHeader::SERIALIZED_SIZE + FprimeProtocol::FrameTrailer::SERIALIZED_SIZE); + + // Shift data pointer to effectively remove the header + data.setData(data.getData() + FprimeProtocol::FrameHeader::SERIALIZED_SIZE); + // Shrunk size to effectively remove the trailer (also removes the header) + data.setSize(data.getSize() - FprimeProtocol::FrameHeader::SERIALIZED_SIZE - FprimeProtocol::FrameTrailer::SERIALIZED_SIZE); + + this->deframedOut_out(0, data, context); +} + +} // namespace Svc diff --git a/Svc/FprimeDeframer/FprimeDeframer.fpp b/Svc/FprimeDeframer/FprimeDeframer.fpp new file mode 100644 index 00000000000..68ca7807a51 --- /dev/null +++ b/Svc/FprimeDeframer/FprimeDeframer.fpp @@ -0,0 +1,15 @@ +module Svc { + + @ A component for deframing input received from the ground + @ via a FrameAccumulator + passive component FprimeDeframer { + + # ---------------------------------------------------------------------- + # Deframer interface + # ---------------------------------------------------------------------- + + include "../Interfaces/DeframerInterface.fppi" + + } + +} diff --git a/Svc/FprimeDeframer/FprimeDeframer.hpp b/Svc/FprimeDeframer/FprimeDeframer.hpp new file mode 100644 index 00000000000..c1b4f271530 --- /dev/null +++ b/Svc/FprimeDeframer/FprimeDeframer.hpp @@ -0,0 +1,46 @@ +// ====================================================================== +// \title FprimeDeframer.hpp +// \author thomas-bc +// \brief hpp file for FprimeDeframer component implementation class +// ====================================================================== + +#ifndef Svc_FprimeDeframer_HPP +#define Svc_FprimeDeframer_HPP + +#include "Svc/FprimeDeframer/FprimeDeframerComponentAc.hpp" +#include "Utils/Hash/Hash.hpp" + +namespace Svc { + +class FprimeDeframer : public FprimeDeframerComponentBase { + + + public: + // ---------------------------------------------------------------------- + // Component construction and destruction + // ---------------------------------------------------------------------- + + //! Construct FprimeDeframer object + FprimeDeframer(const char* const compName //!< The component name + ); + + //! Destroy FprimeDeframer object + ~FprimeDeframer(); + + PRIVATE: + // ---------------------------------------------------------------------- + // Handler implementations for user-defined typed input ports + // ---------------------------------------------------------------------- + + //! Handler implementation for frame + //! + //! Port to receive framed data. The handler will strip the header and trailer from the frame + //! and pass the deframed data to the deframed output port. + void framedIn_handler(FwIndexType portNum, //!< The port number + Fw::Buffer& data, + Fw::Buffer& context) override; +}; + +} // namespace Svc + +#endif diff --git a/Svc/FprimeDeframer/docs/img/deframer_uplink_stack.png b/Svc/FprimeDeframer/docs/img/deframer_uplink_stack.png new file mode 100644 index 00000000000..fb55878b152 Binary files /dev/null and b/Svc/FprimeDeframer/docs/img/deframer_uplink_stack.png differ diff --git a/Svc/FprimeDeframer/docs/sdd.md b/Svc/FprimeDeframer/docs/sdd.md new file mode 100644 index 00000000000..9d9c3f90c48 --- /dev/null +++ b/Svc/FprimeDeframer/docs/sdd.md @@ -0,0 +1,65 @@ +# Svc::FprimeDeframer + +The `Svc::FprimeDeframer` component receives F´ frames on its input port, takes off the header and trailer (or "footer"), and passes the encapsulated payload to other components of the system. + +## F Prime frame format + +```mermaid +--- +title: "F Prime Frame Format" +--- +packet-beta + 0-31: "Start word: 0xDEADBEEF [4 bytes]" + 32-63: "Packet length [4 bytes]" + 64-95: "Packet data [variable length]" + 96-127: "CRC [4 bytes]" + +``` + +Following this frame specification, the `Svc::FprimeDeframer` removes the 32-bit start word, the 32-bit packet length, and the 32-bit CRC from the frame, and outputs the encapsulated packet data. + +## Internals + +The `Svc::FprimeDeframer` component is an implementation of the [DeframerInterface](../../Interfaces/DeframerInterface.fppi) for the F´ communications protocol. It receives an F´ frame (in a [Fw::Buffer](../../../Fw/Buffer/docs/sdd.md) object) on its `framedIn` input port, modifies the input buffer to remove the header and trailer, and sends it out through its `deframedOut` output port. + +Ownership of the buffer is transferred to the component connected to the `deframedOut` output port. The input buffer is modified by subtracting the header and trailer size from the buffer's length, and offsetting the buffer's data pointer to point to the start of the packet data. + +The `Svc::FprimeDeframer` component does not perform any validation of the frame. It is expected that the frame is valid and well-formed. The validation should be performed by an upstream component, such as [`Svc::FrameAccumulator`](../../FrameAccumulator/docs/sdd.md). + +The `Svc::FprimeDeframer` does not support deframing multiple packets in a single frame (i.e. concatenated packets) as this is not supported by the F´ communications protocol. + + +## Usage Examples + +The `Svc::FprimeDeframer` component is used in the uplink stack of many reference F´ application such as [the tutorials source code](https://github.com/fprime-community#tutorials). + + +## Diagrams + +The below diagram shows a typical configuration in which the `Svc::FprimeDeframer` can be used. This is the configuration used in the [the tutorials source code](https://github.com/fprime-community#tutorials). It is receiving accumulated frames from a [Svc::FrameAccumulator](../../FrameAccumulator/docs/sdd.md) and passes packets to a [Svc::Router](../../Router/docs/sdd.md) for routing to other components. + +![./img/deframer_uplink_stack.png](./img/deframer_uplink_stack.png) + + +## Class Diagram + +```mermaid +classDiagram + class FprimeDeframer~PassiveComponent~ { + + void framedIn_handler(FwIndexType portNum, Fw::Buffer& data, Fw::Buffer& context) + } +``` + + +## Requirements + +Requirement | Description | Rationale | Verification Method +----------- | ----------- | ----------| ------------------- +SVC-DEFRAMER-001 | `Svc::FprimeDeframer` shall remove the header and trailer from and F´ frame | Purpose of the component | Unit test | + +## Port Descriptions + +| Kind | Name | Type | Description | +|---|---|---|---| +| `guarded input` | framedIn | `Fw.DataWithContext` | Receives a frame with optional context data | +| `output` | deframedOut | `Fw.DataWithContext` | Receives a frame with optional context data | diff --git a/Svc/FprimeDeframer/test/ut/FprimeDeframerTestMain.cpp b/Svc/FprimeDeframer/test/ut/FprimeDeframerTestMain.cpp new file mode 100644 index 00000000000..5d38ff17d69 --- /dev/null +++ b/Svc/FprimeDeframer/test/ut/FprimeDeframerTestMain.cpp @@ -0,0 +1,29 @@ +// ====================================================================== +// \title FprimeDeframerTestMain.cpp +// \author thomas-bc +// \brief cpp file for FprimeDeframer component test main function +// ====================================================================== + +#include "FprimeDeframerTester.hpp" +#include "STest/Random/Random.hpp" + +TEST(FprimeDeframer, NominalFrame) { + Svc::FprimeDeframerTester tester; + tester.testNominalFrame(); +} + +TEST(FprimeDeframer, TruncatedFrame) { + Svc::FprimeDeframerTester tester; + tester.testTruncatedFrame(); +} + +TEST(FprimeDeframer, ZeroSizeFrame) { + Svc::FprimeDeframerTester tester; + tester.testZeroSizeFrame(); +} + +int main(int argc, char** argv) { + STest::Random::seed(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/Svc/FprimeDeframer/test/ut/FprimeDeframerTester.cpp b/Svc/FprimeDeframer/test/ut/FprimeDeframerTester.cpp new file mode 100644 index 00000000000..67188120ec7 --- /dev/null +++ b/Svc/FprimeDeframer/test/ut/FprimeDeframerTester.cpp @@ -0,0 +1,69 @@ +// ====================================================================== +// \title FprimeDeframerTester.cpp +// \author thomas-bc +// \brief cpp file for FprimeDeframer component test harness implementation class +// ====================================================================== + +#include "FprimeDeframerTester.hpp" +#include "STest/Random/Random.hpp" + +namespace Svc { + +// ---------------------------------------------------------------------- +// Construction and destruction +// ---------------------------------------------------------------------- + +FprimeDeframerTester ::FprimeDeframerTester() + : FprimeDeframerGTestBase("FprimeDeframerTester", FprimeDeframerTester::MAX_HISTORY_SIZE), component("FprimeDeframer") { + this->initComponents(); + this->connectPorts(); +} + +FprimeDeframerTester ::~FprimeDeframerTester() {} + +// ---------------------------------------------------------------------- +// Tests +// ---------------------------------------------------------------------- + +void FprimeDeframerTester ::testNominalFrame() { + // TODO: make this test multiple times with different random bytes and lengths + // Get random byte of data + U8 randomByte = STest::Random::lowerUpper(0, 255); + // Note: the content of the frame header/footer doesn't actually matter in these tests + // | F´ start word | Length (= 1) | Data | Checksum (4 bytes) | + U8 data[13] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00, 0x00, 0x01, randomByte, 0x00, 0x00, 0x00, 0x00}; + this->mockReceiveData(data, sizeof(data)); + + // Assert that something was emitted on the deframedOut port + ASSERT_from_deframedOut_SIZE(1); + // Assert that the data that was emitted on deframedOut is equal to Data field above (randomByte) + ASSERT_EQ(this->fromPortHistory_deframedOut->at(0).data.getData()[0], randomByte); +} + +void FprimeDeframerTester::testTruncatedFrame() { + // Send a truncated frame, too short to be valid + U8 data[11] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + ASSERT_DEATH(this->mockReceiveData(data, sizeof(data)), "FprimeDeframer.cpp"); + ASSERT_from_deframedOut_SIZE(0); +} + +void FprimeDeframerTester::testZeroSizeFrame() { + // Send a null frame, too short to be valid + U8 data[0] = {}; + ASSERT_DEATH(this->mockReceiveData(data, sizeof(data)), "FprimeDeframer.cpp"); + ASSERT_from_deframedOut_SIZE(0); +} + +// ---------------------------------------------------------------------- +// Test Helpers +// ---------------------------------------------------------------------- + +void FprimeDeframerTester::mockReceiveData(U8* data, FwSizeType size) { + Fw::Buffer nullContext; + Fw::Buffer buffer; + buffer.setData(data); + buffer.setSize(size); + this->invoke_to_framedIn(0, buffer, nullContext); +} + +} // namespace Svc diff --git a/Svc/FprimeDeframer/test/ut/FprimeDeframerTester.hpp b/Svc/FprimeDeframer/test/ut/FprimeDeframerTester.hpp new file mode 100644 index 00000000000..e8a6394e8b9 --- /dev/null +++ b/Svc/FprimeDeframer/test/ut/FprimeDeframerTester.hpp @@ -0,0 +1,78 @@ +// ====================================================================== +// \title FprimeDeframerTester.hpp +// \author thomas-bc +// \brief hpp file for FprimeDeframer component test harness implementation class +// ====================================================================== + +#ifndef Svc_FprimeDeframerTester_HPP +#define Svc_FprimeDeframerTester_HPP + +#include "Svc/FprimeDeframer/FprimeDeframer.hpp" +#include "Svc/FprimeDeframer/FprimeDeframerGTestBase.hpp" + +namespace Svc { + +class FprimeDeframerTester : public FprimeDeframerGTestBase { + 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 FprimeDeframerTester + FprimeDeframerTester(); + + //! Destroy object FprimeDeframerTester + ~FprimeDeframerTester(); + + public: + // ---------------------------------------------------------------------- + // Tests + // ---------------------------------------------------------------------- + + //! Test receiving a nominal frame + void testNominalFrame(); + + //! Test receiving a truncated frame + void testTruncatedFrame(); + + //! Test receiving a zero size frame + void testZeroSizeFrame(); + + private: + // ---------------------------------------------------------------------- + // Helper functions + // ---------------------------------------------------------------------- + + //! Connect ports + void connectPorts(); + + //! Initialize components + void initComponents(); + + //! Sends a buffer of supplied data and size on the component input port + void mockReceiveData(U8* data, FwSizeType size); + + + private: + // ---------------------------------------------------------------------- + // Member variables + // ---------------------------------------------------------------------- + + //! The component under test + FprimeDeframer component; +}; + +} // namespace Svc + +#endif diff --git a/Svc/FrameAccumulator/CMakeLists.txt b/Svc/FrameAccumulator/CMakeLists.txt new file mode 100644 index 00000000000..4abc771cde5 --- /dev/null +++ b/Svc/FrameAccumulator/CMakeLists.txt @@ -0,0 +1,41 @@ +#### +# 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}/FrameAccumulator.fpp" + "${CMAKE_CURRENT_LIST_DIR}/FrameAccumulator.cpp" + "${CMAKE_CURRENT_LIST_DIR}/FrameDetector/FprimeFrameDetector.cpp" +) + +register_fprime_module() + +#### UTs #### +set(UT_SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/FrameAccumulator.fpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/FrameAccumulatorTester.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/FrameAccumulatorTestMain.cpp" +) +set(UT_MOD_DEPS + Utils/Types + STest +) +set(UT_AUTO_HELPERS ON) + +register_fprime_ut() + +#### FprimeFrameDetector tests #### + +set(UT_HEADER_FILES + "${CMAKE_CURRENT_LIST_DIR}/FrameDetector/FprimeFrameDetector.hpp" +) +set(UT_SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/test/ut/detectors/FprimeFrameDetectorTestMain.cpp" +) + +register_fprime_ut("Svc_FrameAccumulator_FprimeFrameDetector_test") diff --git a/Svc/FrameAccumulator/FrameAccumulator.cpp b/Svc/FrameAccumulator/FrameAccumulator.cpp new file mode 100644 index 00000000000..5aec92d6416 --- /dev/null +++ b/Svc/FrameAccumulator/FrameAccumulator.cpp @@ -0,0 +1,166 @@ +// ====================================================================== +// \title FrameAccumulator.cpp +// \author mstarch +// \brief cpp file for FrameAccumulator component implementation class +// ====================================================================== + +#include "Svc/FrameAccumulator/FrameAccumulator.hpp" +#include // required for placement new in configure() member function +#include "FpConfig.hpp" +#include "Fw/Types/Assert.hpp" + +namespace Svc { + +// ---------------------------------------------------------------------- +// Component construction and destruction +// ---------------------------------------------------------------------- + +FrameAccumulator ::FrameAccumulator(const char* const compName) + : FrameAccumulatorComponentBase(compName), + m_detector(nullptr), + m_memoryAllocator(nullptr), + m_memory(nullptr), + m_allocatorId(0) {} + +FrameAccumulator ::~FrameAccumulator() { + // If configuration happened, we must deallocate + if (this->m_memoryAllocator != nullptr) { + this->m_memoryAllocator->deallocate(this->m_allocatorId, this->m_memory); + this->m_memory = nullptr; + } +} + +void FrameAccumulator ::configure(const FrameDetector& detector, + NATIVE_UINT_TYPE allocationId, + Fw::MemAllocator& allocator, + FwSizeType store_size) { + bool recoverable = false; + FW_ASSERT(std::numeric_limits::max() >= store_size, static_cast(store_size)); + NATIVE_UINT_TYPE store_size_int = static_cast(store_size); + void* data_void = allocator.allocate(allocationId, store_size_int, recoverable); + U8* data = new (data_void) U8[store_size_int]; + FW_ASSERT(data != nullptr); + FW_ASSERT(store_size_int >= store_size); + m_inRing.setup(data, store_size_int); + + this->m_detector = &detector; + this->m_allocatorId = allocationId; + this->m_memoryAllocator = &allocator; + this->m_memory = data; +} + +// ---------------------------------------------------------------------- +// Handler implementations for user-defined typed input ports +// ---------------------------------------------------------------------- + +void FrameAccumulator ::dataIn_handler(FwIndexType portNum, Fw::Buffer& buffer, const Drv::RecvStatus& status) { + // Check whether there is data to process + if (status.e == Drv::RecvStatus::RECV_OK) { + // There is: process the data + this->processBuffer(buffer); + } + // Deallocate the buffer + this->bufferDeallocate_out(0, buffer); +} + +void FrameAccumulator ::processBuffer(Fw::Buffer& buffer) { + const U32 bufferSize = buffer.getSize(); + U8* const bufferData = buffer.getData(); + // Current offset into buffer + U32 offset = 0; + // Remaining data in buffer + U32 remaining = bufferSize; + + for (U32 i = 0; i < bufferSize; ++i) { + // If there is no data left or no space, exit the loop + if (remaining == 0 || this->m_inRing.get_free_size() == 0) { + break; + } + // Compute the size of data to serialize + const NATIVE_UINT_TYPE ringFreeSize = this->m_inRing.get_free_size(); + const NATIVE_UINT_TYPE serSize = + (ringFreeSize <= remaining) ? ringFreeSize : static_cast(remaining); + // Serialize data into the ring buffer + const Fw::SerializeStatus status = this->m_inRing.serialize(&bufferData[offset], serSize); + // If data does not fit, there is a coding error + FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast(status), + static_cast(offset), static_cast(serSize)); + // Process the data + this->processRing(); + // Update buffer offset and remaining + offset += serSize; + remaining -= serSize; + } + // Either all the bytes from the data buffer must be processed, or the ring must be full + FW_ASSERT(remaining == 0 || this->m_inRing.get_free_size() == 0, static_cast(remaining)); +} + +void FrameAccumulator ::processRing() { + FW_ASSERT(this->m_detector != nullptr); + + // The number of remaining bytes in the ring buffer + U32 remaining = 0; + // The protocol status + FrameDetector::Status status = FrameDetector::Status::FRAME_DETECTED; + // The ring buffer capacity + const NATIVE_UINT_TYPE ringCapacity = this->m_inRing.get_capacity(); + + // Process the ring buffer looking for at least the header + for (U32 i = 0; i < ringCapacity; i++) { + // Get the number of bytes remaining in the ring buffer + remaining = this->m_inRing.get_allocated_size(); + // If there are none, we are done + if (remaining == 0) { + break; + } + // size_out is a return variable we initialize to zero, but it should be overwritten + FwSizeType size_out = 0; + // Attempt to detect the frame without changing the circular buffer + status = this->m_detector->detect(this->m_inRing, size_out); + // Detect must not consume data in the ring buffer + FW_ASSERT(m_inRing.get_allocated_size() == remaining, + static_cast(m_inRing.get_allocated_size()), static_cast(remaining)); + // On successful detection, consume data from the ring buffer and place it into an allocated frame + if (status == FrameDetector::FRAME_DETECTED) { + // size_out must be set to the size of the buffer and must fit within the existing data + FW_ASSERT(size_out != 0); + FW_ASSERT(size_out <= remaining, static_cast(size_out), + static_cast(remaining)); + Fw::Buffer buffer = this->bufferAllocate_out(0, static_cast(size_out)); + if (buffer.isValid()) { + // Copy out data and rotate + FW_ASSERT(this->m_inRing.peek(buffer.getData(), static_cast(size_out)) == + Fw::SerializeStatus::FW_SERIALIZE_OK); + buffer.setSize(static_cast(size_out)); + m_inRing.rotate(static_cast(size_out)); + FW_ASSERT(m_inRing.get_allocated_size() == static_cast(remaining - size_out), + static_cast(m_inRing.get_allocated_size()), + static_cast(remaining), static_cast(size_out)); + Fw::Buffer nullContext; + this->frameOut_out(0, buffer, nullContext); + } else { + // No buffer is available, we need to exit and try again later + break; + } + } + // More data needed + else if (status == FrameDetector::MORE_DATA_NEEDED) { + // size_out can never be larger than the capacity of the ring. Otherwise all uplink will fail. + FW_ASSERT(size_out < m_inRing.get_capacity(), static_cast(size_out)); + // Detection should report "more is needed" and set size_out to something larger than available data + FW_ASSERT(size_out > remaining, static_cast(size_out), + static_cast(remaining)); + // Break out of loop: suspend detection until we receive another buffer + break; + } + // No frame was detected or an unknown status was received + else { + // Discard a single byte of data and start again + (void)m_inRing.rotate(1); + FW_ASSERT(m_inRing.get_allocated_size() == remaining - 1, + static_cast(m_inRing.get_allocated_size()), + static_cast(remaining)); + } + } +} +} // namespace Svc diff --git a/Svc/FrameAccumulator/FrameAccumulator.fpp b/Svc/FrameAccumulator/FrameAccumulator.fpp new file mode 100644 index 00000000000..146c2d1b8cc --- /dev/null +++ b/Svc/FrameAccumulator/FrameAccumulator.fpp @@ -0,0 +1,17 @@ +module Svc { + @ Accumulates data into frames + passive component FrameAccumulator { + + @ Receives raw data from a ByteStreamDriver, ComStub, or other buffer producing component + guarded input port dataIn: Drv.ByteStreamRecv + + @ Port for deallocating buffers received on dataIn. + output port bufferDeallocate: Fw.BufferSend + + @ Port for allocating buffer to hold extracted frame + output port bufferAllocate: Fw.BufferGet + + @ Port for sending an extracted frame out + output port frameOut: Fw.DataWithContext + } +} diff --git a/Svc/FrameAccumulator/FrameAccumulator.hpp b/Svc/FrameAccumulator/FrameAccumulator.hpp new file mode 100644 index 00000000000..23868f56b4e --- /dev/null +++ b/Svc/FrameAccumulator/FrameAccumulator.hpp @@ -0,0 +1,78 @@ +// ====================================================================== +// \title FrameAccumulator.hpp +// \author mstarch +// \brief hpp file for FrameAccumulator component implementation class +// ====================================================================== + +#ifndef Svc_FrameAccumulator_HPP +#define Svc_FrameAccumulator_HPP + +#include "Fw/Types/MemAllocator.hpp" +#include "Svc/FrameAccumulator/FrameAccumulatorComponentAc.hpp" +#include "Svc/FrameAccumulator/FrameDetector.hpp" +#include "Utils/Types/CircularBuffer.hpp" + +namespace Svc { + +class FrameAccumulator : public FrameAccumulatorComponentBase { + public: + // ---------------------------------------------------------------------- + // Component construction and destruction + // ---------------------------------------------------------------------- + + //! \brief Construct FrameAccumulator object + FrameAccumulator(const char* const compName //!< The component name + ); + + //! \brief Destroy FrameAccumulator object + ~FrameAccumulator(); + + //! \brief configure memory allocation for the circular buffer + //! + //! Takes in parameters used in the Fw::MemAllocator pattern and configures a memory allocation for storing the + //! circular buffer. + void configure(const FrameDetector& detector, //!< Frame detector helper instance + NATIVE_UINT_TYPE allocationId, //!< Identifier used when dealing with the Fw::MemAllocator + Fw::MemAllocator& allocator, //!< Fw::MemAllocator used to acquire memory + FwSizeType store_size //!< Size to request for circular buffer + ); + + PRIVATE: + // ---------------------------------------------------------------------- + // Handler implementations for user-defined typed input ports + // ---------------------------------------------------------------------- + + //! Handler implementation for dataIn + //! + //! Receives raw data from a ByteStreamDriver, ComStub, or other buffer producing component + void dataIn_handler(FwIndexType portNum, //!< The port number + Fw::Buffer& recvBuffer, + const Drv::RecvStatus& recvStatus) override; + + PRIVATE: + //! \brief process raw buffer + //! \return raw data buffer + void processBuffer(Fw::Buffer& buffer); + + //! \brief process circular buffer + void processRing(); + + //! Circular buffer for storing data + Types::CircularBuffer m_inRing; + + //! Pointer to helper class that detects frames + FrameDetector const* m_detector; + + //! Memory allocator instance used with deallocating + Fw::MemAllocator* m_memoryAllocator; + + //! Memory pointer for allocated memory + U8* m_memory; + + //! Identification used with the memory allocator + NATIVE_UINT_TYPE m_allocatorId; +}; + +} // namespace Svc + +#endif diff --git a/Svc/FrameAccumulator/FrameDetector.hpp b/Svc/FrameAccumulator/FrameDetector.hpp new file mode 100644 index 00000000000..08de8a0da6a --- /dev/null +++ b/Svc/FrameAccumulator/FrameDetector.hpp @@ -0,0 +1,53 @@ +// ====================================================================== +// \title FrameDetector.hpp +// \author mstarch +// \brief hpp interface specification for FrameDetector +// ====================================================================== +#ifndef SVC_FPRIME_FRAME_DETECTOR_HPP +#define SVC_FPRIME_FRAME_DETECTOR_HPP +#include +#include + +namespace Svc { + + //! \brief interface class used to codify what must be supported to allow frame detection + class FrameDetector { + public: + //! \brief status returned from the detection step + enum Status { + FRAME_DETECTED, //!< Frame detected. Extract frame and return with new data. + NO_FRAME_DETECTED, //!< No frame detected. Discard data and return with new data. + MORE_DATA_NEEDED //!< More data is needed to detect a frame. Keep current data and return with more. + }; + + //! \brief virtual destructor + virtual ~FrameDetector() = default; + + //! \brief detect if a frame is available within the circular buffer + //! + //! Function implemented by sub classes used to determine if a frame is available at the current position of the + //! circular buffer. Implementors should detect if a frame is available, set size_out, and return a status while + //! following these expectations: + //! + //! 1. FRAME_DETECTED status implies a frame is available at the current offset of the circular buffer. + //! size_out must be set to the size of the frame from that location. + //! + //! 2. NO_FRAME_DETECTED status implies no frame is possible at the current offset of the circular buffer. + //! e.g. no start word is found at the current offset. size_out is ignored. + //! + //! 3. MORE_DATA_NEEDED status implies that a frame might be possible but more data is needed before a + //! determination is possible. size_out must be set to the total amount of data needed. + //! + //! For example, if a frame start word is 4 bytes, and 3 bytes are available in the circular buffer then the + //! return status would be NO_FRAME_DETECTED and size_out must be set to 4 to ensure that at least the start + //! word is available. + //! + //! \param data: circular buffer with read-only access + //! \param size_out: set as output to caller indicating size when appropriate + //! \return status of the detection to be paired with size_out + virtual Status detect(const Types::CircularBuffer& data, FwSizeType& size_out) const = 0; + }; + +} // Svc + +#endif //SVC_FPRIME_FRAME_DETECTOR_HPP diff --git a/Svc/FrameAccumulator/FrameDetector/FprimeFrameDetector.cpp b/Svc/FrameAccumulator/FrameDetector/FprimeFrameDetector.cpp new file mode 100644 index 00000000000..13dad43a294 --- /dev/null +++ b/Svc/FrameAccumulator/FrameDetector/FprimeFrameDetector.cpp @@ -0,0 +1,102 @@ +// ====================================================================== +// \title FprimeFrameDetector.hpp +// \author thomas-bc +// \brief hpp file for fprime frame detector definitions +// ====================================================================== + +#include "Svc/FrameAccumulator/FrameDetector/FprimeFrameDetector.hpp" + +namespace Svc { +namespace FrameDetectors { + +FrameDetector::Status FprimeFrameDetector::detect(const Types::CircularBuffer& data, FwSizeType& size_out) const { + // If not enough data for header + trailer, report MORE_DATA_NEEDED + if (data.get_allocated_size() < + FprimeProtocol::FrameHeader::SERIALIZED_SIZE + FprimeProtocol::FrameTrailer::SERIALIZED_SIZE) { + size_out = FprimeProtocol::FrameHeader::SERIALIZED_SIZE + FprimeProtocol::FrameTrailer::SERIALIZED_SIZE; + return Status::MORE_DATA_NEEDED; + } + + // NOTE: it is understood and accepted that the following code is not as efficient as it could technically be + // We are leveraging the FPP autocoded types to do the deserialization for us. + // In its current implementation, CircularBuffer is not a SerializeBufferBase, which prevents us from deserializing + // directly from the CircularBuffer into FrameHeader/FrameTrailer. Instead, we have to copy the data into + // a temporary SerializeBuffer, and then deserialize from that buffer into the FrameHeader/FrameTrailer objects. + // A better implementation would be to have CircularBuffer implement a shared interface with SerializeBufferBase, + // and then we could pass the CircularBuffer directly into the FrameHeader/FrameTrailer deserializers. This is left + // as a TODO for future improvement as it is a significant refactor + + FprimeProtocol::FrameHeader header; + FprimeProtocol::FrameTrailer trailer; + + // ---------------- Frame Header ---------------- + // Copy CircularBuffer data into linear buffer, for serialization into FrameHeader object + U8 header_data[FprimeProtocol::FrameHeader::SERIALIZED_SIZE]; + Fw::SerializeStatus status = data.peek(header_data, FprimeProtocol::FrameHeader::SERIALIZED_SIZE, 0); + if (status != Fw::FW_SERIALIZE_OK) { + return Status::NO_FRAME_DETECTED; + } + Fw::ExternalSerializeBuffer header_ser_buffer(header_data, FprimeProtocol::FrameHeader::SERIALIZED_SIZE); + status = header_ser_buffer.setBuffLen(FprimeProtocol::FrameHeader::SERIALIZED_SIZE); + FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast(status)); + // Attempt to deserialize data into the FrameHeader object + status = header.deserialize(header_ser_buffer); + if (status != Fw::FW_SERIALIZE_OK) { + return Status::NO_FRAME_DETECTED; + } + // Check that deserialized start_word token matches expected value (default start_word value in the FPP object) + FprimeProtocol::FrameHeader default_value; + if (header.getstart_word() != default_value.getstart_word()) { + return Status::NO_FRAME_DETECTED; + } + // If the deserialized length token can't fit in current allocated size -> MORE_DATA_NEEDED + if (data.get_allocated_size() < FprimeProtocol::FrameHeader::SERIALIZED_SIZE + header.getlength() + + FprimeProtocol::FrameTrailer::SERIALIZED_SIZE) { + size_out = header.getlength() + FprimeProtocol::FrameHeader::SERIALIZED_SIZE + + FprimeProtocol::FrameTrailer::SERIALIZED_SIZE; + return Status::MORE_DATA_NEEDED; + } + + // ---------------- Frame Trailer ---------------- + U8 trailer_data[FprimeProtocol::FrameTrailer::SERIALIZED_SIZE]; + Fw::ExternalSerializeBuffer trailer_ser_buffer(trailer_data, FprimeProtocol::FrameTrailer::SERIALIZED_SIZE); + status = data.peek(trailer_data, FprimeProtocol::FrameTrailer::SERIALIZED_SIZE, + FprimeProtocol::FrameHeader::SERIALIZED_SIZE + header.getlength()); + if (status != Fw::FW_SERIALIZE_OK) { + return Status::NO_FRAME_DETECTED; + } + status = trailer_ser_buffer.setBuffLen(FprimeProtocol::FrameTrailer::SERIALIZED_SIZE); + FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast(status)); + // Deserialize trailer from circular buffer (peeked data) into trailer object + status = trailer.deserialize(trailer_ser_buffer); + if (status != Fw::FW_SERIALIZE_OK) { + return Status::NO_FRAME_DETECTED; + } + + Utils::Hash hash; + Utils::HashBuffer hashBuffer; + // Compute CRC over the transmitted data (header + body) + FwSizeType hash_field_size = header.getlength() + FprimeProtocol::FrameHeader::SERIALIZED_SIZE; + hash.init(); + for (U32 i = 0; i < hash_field_size; i++) { + U8 byte = 0; + status = data.peek(byte, i); + FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status); + hash.update(&byte, 1); + } + hash.final(hashBuffer); + + // Compare the transmitted CRC with the computed one + if (trailer.getcrc() != hashBuffer.asBigEndianU32()) { + // Note: CRC mismatch - there likely was data corruption + // Should there be an event / telemetry for frames dropped ?? + // Add Status::DATA_ERROR , and use it for deserialization errors as well? + return Status::NO_FRAME_DETECTED; + } + size_out = header.getlength() + FprimeProtocol::FrameHeader::SERIALIZED_SIZE + + FprimeProtocol::FrameTrailer::SERIALIZED_SIZE; + return Status::FRAME_DETECTED; +} + +} // namespace FrameDetectors +} // namespace Svc diff --git a/Svc/FrameAccumulator/FrameDetector/FprimeFrameDetector.hpp b/Svc/FrameAccumulator/FrameDetector/FprimeFrameDetector.hpp new file mode 100644 index 00000000000..042c0b590dc --- /dev/null +++ b/Svc/FrameAccumulator/FrameDetector/FprimeFrameDetector.hpp @@ -0,0 +1,62 @@ +// ====================================================================== +// \title FprimeFrameDetector.hpp +// \author thomas-bc +// \brief hpp file for fprime frame detector definitions +// ====================================================================== +#ifndef SVC_FRAME_ACCUMULATOR_FRAME_DETECTOR_FPRIME_FRAME_DETECTOR +#define SVC_FRAME_ACCUMULATOR_FRAME_DETECTOR_FPRIME_FRAME_DETECTOR + +#include "Svc/FrameAccumulator/FrameDetector.hpp" + +#include "Fw/Buffer/Buffer.hpp" +#include "Utils/Hash/Hash.hpp" +#include "config/FpConfig.h" + +#include "config/FrameHeaderSerializableAc.hpp" +#include "config/FrameTrailerSerializableAc.hpp" + +namespace Svc { +namespace FrameDetectors { + +//! \brief interface class used to codify what must be supported to allow frame detection +class FprimeFrameDetector : public FrameDetector { + public: + //! \brief detect if a frame is available within the circular buffer + //! + //! Function implemented by sub classes used to determine if a frame is available at the current position of the + //! circular buffer. Implementors should detect if a frame is available, set size_out, and return a status while + //! following these expectations: + //! + //! 1. FRAME_DETECTED status implies a frame is available at the current offset of the circular buffer. + //! size_out must be set to the size of the frame from that location. + //! + //! 2. NO_FRAME_DETECTED status implies no frame is possible at the current offset of the circular buffer. + //! e.g. no start word is found at the current offset. size_out is ignored. + //! + //! 3. MORE_DATA_NEEDED status implies that a frame might be possible but more data is needed before a + //! determination is possible. size_out must be set to the total amount of data needed. + //! + //! For example, if a frame start word is 4 bytes, and 3 bytes are available in the circular buffer then the + //! return status would be NO_FRAME_DETECTED and size_out must be set to 4 to ensure that at least the start + //! word is available. + //! + //! \param data: circular buffer with read-only access + //! \param size_out: set as output to caller indicating size when appropriate + //! \return status of the detection to be paired with size_out + Status detect(const Types::CircularBuffer& data, FwSizeType& size_out) const override; + +}; // class FprimeFrameDetector +} // namespace FrameDetectors +} // namespace Svc + +#endif // SVC_FRAME_ACCUMULATOR_FRAME_DETECTOR_FPRIME_FRAME_DETECTOR + +//! fprime framing start word is a configurable type and matched against 0xdeadbeef as cast into the appropriate type +// using FprimeStartWord = StartToken(0xdeadbeef)>; +// //! fprime framing length is a configurable type +// using FprimeLength = LengthToken; +// //! fprime uses a CRC32 checksum anchored at the end of the data +// using FprimeChecksum = CRC; + +// //! fprime frame detector is a start/length/crc detector using the configured fprime tokens +// using FprimeFrameDetector = StartLengthCrcDetector; diff --git a/Svc/FrameAccumulator/docs/sdd.md b/Svc/FrameAccumulator/docs/sdd.md new file mode 100644 index 00000000000..f4a15d969f8 --- /dev/null +++ b/Svc/FrameAccumulator/docs/sdd.md @@ -0,0 +1,87 @@ +# Svc::FrameAccumulator + +The `Svc::FrameAccumulator` component accumulates a stream of data (sequence of [Fw::Buffer](../../../Fw/Buffer/docs/sdd.md) objects) to extract full frames from. + +The `Svc::FrameAccumulator` accepts as input a sequence of byte buffers, which typically come from a ground data system via a [ByteStreamDriver](../../../Drv/ByteStreamDriverModel/docs/sdd.md). It extracts the frames from the sequence of buffers and emits them on the `frameOut` output port. + +## Internals + +### Overview and configuration + +The `Svc::FrameAccumulator` accumulates the [Fw::Buffer](../../../Fw/Buffer/docs/sdd.md) objects into a circular buffer ([Utils::CircularBuffer](../../../Utils/Types/CircularBuffer.hpp)). + +The component must be configured with a [`Svc::FrameDetector`](../FrameDetector.hpp) which is responsible for detecting frames in the circular buffer. An implementation of this for the F´ communications protocol is provided by `Svc::FrameDetectors::FprimeFrameDetector`. + +The uplink frames need not be aligned on the buffer boundaries, and each frame may span one or more buffers. + +### Frame detection + +The `Svc::FrameAccumulator` receives `Fw::Buffer` objects on its `dataIn` input port. These buffers are accumulated in a `Utils::CircularBuffer`. Every time a new buffer is accumulated into the circular buffer, the `Svc::FrameAccumulator` enters a loop to `detect()` a frame within the circular buffer, starting at the current head of the circular buffer. The `Svc::FrameDetector` returns one of three results: + +- `NO_FRAME_DETECTED`: indicates no valid frame is present at the head of the circular buffer (for example, start word does not match the current head of the circular buffer). The `Svc::FrameAccumulator` rotates the circular buffer one byte and loops over to `detect()` again, or break the loop if the circular buffer is exhausted. +- `FRAME_DETECTED`: indicates there is a frame at the current head of the circular buffer. The `Svc::FrameAccumulator` allocates a new `Fw::Buffer` object to hold the frame, copies the detected frame from the circular buffer into the new `Fw::Buffer` object, and emits the new `Fw::Buffer` object (containing the frame) on its `frameOut` output port. The `Svc::FrameAccumulator` then rotates the circular buffer to remove the data that was just extracted, and deallocates the original `Fw::Buffer` that was received on the `dataIn` input port. +- `MORE_DATA_NEEDED`: indicates that more data is needed to determine whether there is a valid frame. The `Svc::FrameAccumulator` deallocates the original `Fw::Buffer` that was received on the `dataIn` input port and halts execution, effectively waiting for the next `Fw::Buffer` to be received on the `dataIn` input port. + + +```mermaid +sequenceDiagram + participant I as Input + box Grey FrameAccumulator + participant A as Accumulator + participant D as Detector + end + + I-->>A: Fw::Buffer + activate A + A-->A: Serialize into RingBuffer + loop + A-->>D: detect() + alt MORE_DATA_NEEDED + A-->A: break + else NO_FRAME_DETECTED + A-->>A: ring.rotate() + else FRAME_DETECTED + create participant Z as Output + A-->>Z: Frame + end + end + deactivate A + destroy O +``` + + +## Usage Examples + +The `Svc::FrameAccumulator` component is used in the uplink stack of many reference F´ application such as [the tutorials source code](https://github.com/fprime-community#tutorials). The below diagram shows the canonical configuration in which it is used. + +![./img/deframer_uplink_stack.png](../../FprimeDeframer/docs/img/deframer_uplink_stack.png) + +## Class Diagram + +```mermaid +classDiagram + class FrameAccumulator~PassiveComponent~ { + + void configure(FrameDetector& detector, NATIVE_UINT_TYPE allocationId, Fw::MemAllocator& allocator, FwSizeType store_size) + + void dataIn_handler(FwIndexType portNum, Fw::Buffer& recvBuffer, const Drv::RecvStatus& recvStatus) + + void processBuffer(Fw::Buffer& buffer) + + void processRing() + } +``` + +## Requirements + +Requirement | Description | Rationale | Verification Method +----------- | ----------- | ----------| ------------------- +SVC-FRAME-ACCUMULATOR-001 | `Svc::FrameAccumulator` shall accumulate a sequence of byte buffers until a full frame is received | FrameAccumulator is designed to re-assemble frames from sequence of bytes | Unit test | +SVC-FRAME-ACCUMULATOR-002 | `Svc::FrameAccumulator` shall detect once the accumulated buffers form a full frame and emit said frame | Pass frames to other parts of the system | Unit test | +SVC-FRAME-ACCUMULATOR-003 | `Svc::FrameAccumulator` shall accept byte buffers containing frames that are not aligned on a buffer boundary. | For flexibility, we do not require that the frames be aligned on a buffer boundary. | Unit test | +SVC-FRAME-ACCUMULATOR-004 | `Svc::FrameAccumulator` shall accept byte buffers containing frames that span one or more buffers. | For flexibility, we do not require each frame to fit in a single buffer. | Unit test | + +## Port Descriptions + +| Kind | Name | Type | Description | +|---|---|---|---| +| `guarded input` | dataIn | `Drv.ByteStreamRecv` | Receives raw data from a ByteStreamDriver, ComStub, or other buffer producing component | +| `output` | frameOut | `Fw.DataWithContext` | Port for sending an extracted frame out | +| `output` | bufferAllocate | `Fw.BufferGet` | Port for allocating buffer to hold extracted frame | +| `output`| bufferDeallocate | `Fw.BufferSend` | Port for deallocating buffers received on dataIn. | diff --git a/Svc/FrameAccumulator/test/ut/FrameAccumulatorTestMain.cpp b/Svc/FrameAccumulator/test/ut/FrameAccumulatorTestMain.cpp new file mode 100644 index 00000000000..c84b984d42b --- /dev/null +++ b/Svc/FrameAccumulator/test/ut/FrameAccumulatorTestMain.cpp @@ -0,0 +1,50 @@ +// ====================================================================== +// \title FrameAccumulatorTestMain.cpp +// \author thomas-bc +// \brief cpp file for FrameAccumulator component test main function +// ====================================================================== + +#include "FrameAccumulatorTester.hpp" +#include "STest/Random/Random.hpp" + +TEST(FrameAccumulator, TestFrameDetected) { + Svc::FrameAccumulatorTester tester; + tester.testFrameDetected(); +} + +TEST(FrameAccumulator, TestMoreDataNeeded) { + Svc::FrameAccumulatorTester tester; + tester.testMoreDataNeeded(); +} + +TEST(FrameAccumulator, TestNoFrameDetected) { + Svc::FrameAccumulatorTester tester; + tester.testNoFrameDetected(); +} + +TEST(FrameAccumulator, TestReceiveZeroSizeBuffer) { + Svc::FrameAccumulatorTester tester; + tester.testReceiveZeroSizeBuffer(); +} + +TEST(FrameAccumulator, TestAccumulateTwoBuffers) { + Svc::FrameAccumulatorTester tester; + tester.testAccumulateTwoBuffers(); +} + +TEST(FrameAccumulator, testAccumulateBuffersEmitFrame) { + Svc::FrameAccumulatorTester tester; + tester.testAccumulateBuffersEmitFrame(); +} + +TEST(FrameAccumulator, testAccumulateBuffersEmitManyFrames) { + Svc::FrameAccumulatorTester tester; + tester.testAccumulateBuffersEmitManyFrames(); +} + + +int main(int argc, char** argv) { + STest::Random::seed(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/Svc/FrameAccumulator/test/ut/FrameAccumulatorTester.cpp b/Svc/FrameAccumulator/test/ut/FrameAccumulatorTester.cpp new file mode 100644 index 00000000000..03c21cba052 --- /dev/null +++ b/Svc/FrameAccumulator/test/ut/FrameAccumulatorTester.cpp @@ -0,0 +1,199 @@ +// ====================================================================== +// \title FrameAccumulatorTester.cpp +// \author thomas-bc +// \brief cpp file for FrameAccumulator component test harness implementation class +// ====================================================================== + +#include "FrameAccumulatorTester.hpp" +#include "STest/Random/Random.hpp" + + + +namespace Svc { + +// ---------------------------------------------------------------------- +// Construction and destruction +// ---------------------------------------------------------------------- + +FrameAccumulatorTester ::FrameAccumulatorTester() + : FrameAccumulatorGTestBase("FrameAccumulatorTester", FrameAccumulatorTester::MAX_HISTORY_SIZE), + component("FrameAccumulator") { + component.configure(this->mockDetector, 1, this->mallocator, 2048); + this->initComponents(); + this->connectPorts(); +} + +// ---------------------------------------------------------------------- +// Tests +// ---------------------------------------------------------------------- + +void FrameAccumulatorTester ::testFrameDetected() { + // Prepare a random size buffer + U32 buffer_size = STest::Random::lowerUpper(1, 1024); + U8 data[buffer_size]; + Fw::Buffer buffer(data, buffer_size); + // Set the mock detector to report success of size_out = buffer_size + this->mockDetector.set_next_result(FrameDetector::Status::FRAME_DETECTED, buffer_size); + // Receive the buffer on dataIn + this->invoke_to_dataIn(0, buffer, Drv::RecvStatus::RECV_OK); + // Checks + ASSERT_from_bufferDeallocate_SIZE(1); // input buffer was deallocated + ASSERT_from_frameOut_SIZE(1); // frame was sent + ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // no data left in ring buffer + ASSERT_EQ(this->fromPortHistory_frameOut->at(0).data.getSize(), buffer_size); // all data was sent out +} + +void FrameAccumulatorTester ::testMoreDataNeeded() { + // Prepare a random size buffer + U32 buffer_size = STest::Random::lowerUpper(1, 1024); + U8 data[buffer_size]; + Fw::Buffer buffer(data, buffer_size); + // Set the mock detector to report more data needed + this->mockDetector.set_next_result(FrameDetector::Status::MORE_DATA_NEEDED, buffer_size + 1); + // Receive the buffer on dataIn + this->invoke_to_dataIn(0, buffer, Drv::RecvStatus::RECV_OK); + // Checks + ASSERT_from_bufferDeallocate_SIZE(1); // input buffer was deallocated + ASSERT_from_frameOut_SIZE(0); // frame was not sent (waiting on more data) + ASSERT_EQ(this->component.m_inRing.get_allocated_size(), buffer_size); // data left in ring buffer +} + +void FrameAccumulatorTester ::testNoFrameDetected() { + // Prepare a random size buffer + U32 buffer_size = STest::Random::lowerUpper(1, 1024); + U8 data[buffer_size]; + Fw::Buffer buffer(data, buffer_size); + // Set the mock detector + this->mockDetector.set_next_result(FrameDetector::Status::NO_FRAME_DETECTED, 0); + // Receive the buffer on dataIn + this->invoke_to_dataIn(0, buffer, Drv::RecvStatus::RECV_OK); + // Checks + ASSERT_from_bufferDeallocate_SIZE(1); // input buffer was deallocated + ASSERT_from_frameOut_SIZE(0); // No frame was sent out + ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // all data was consumed and discarded +} + +void FrameAccumulatorTester ::testReceiveZeroSizeBuffer() { + + // Prepare a zero size buffer + U8 data[1] = {0}; + Fw::Buffer buffer(data, 0); + // Receive the buffer on dataIn + this->invoke_to_dataIn(0, buffer, Drv::RecvStatus::RECV_OK); + // Checks + ASSERT_from_bufferDeallocate_SIZE(1); // input buffer was deallocated + ASSERT_from_frameOut_SIZE(0); // No frame was sent out + ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // No data in ring buffer + ASSERT_EQ(this->component.m_inRing.m_head_idx, 0); +} + +void FrameAccumulatorTester ::testAccumulateTwoBuffers() { + FwSizeType buffer1_size = 10; + FwSizeType buffer2_size = 20; + U8 data1[buffer1_size]; + U8 data2[buffer2_size]; + Fw::Buffer buffer1(data1, buffer1_size); + Fw::Buffer buffer2(data2, buffer2_size); + + this->mockDetector.set_next_result(FrameDetector::Status::MORE_DATA_NEEDED, buffer2_size); + // Receive the buffer on dataIn + this->invoke_to_dataIn(0, buffer1, Drv::RecvStatus::RECV_OK); + // Next result is detection of a full frame, size = buffer1_size + buffer2_size + this->mockDetector.set_next_result(FrameDetector::Status::FRAME_DETECTED, buffer1_size + buffer2_size ); + // Receive the buffer on dataIn + this->invoke_to_dataIn(0, buffer2, Drv::RecvStatus::RECV_OK); + + // Checks + ASSERT_from_bufferDeallocate_SIZE(2); // both input buffers deallocated + ASSERT_from_frameOut_SIZE(1); // Exactly one frame was sent out + ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // No data in ring buffer +} + +void FrameAccumulatorTester ::testAccumulateBuffersEmitFrame() { + U32 frame_size = 0; + U32 buffer_count = 0; + this->mockAccumulateFullFrame(frame_size, buffer_count); + // Checks + ASSERT_from_bufferDeallocate_SIZE(buffer_count); // all input buffers deallocated + ASSERT_from_frameOut_SIZE(1); // Exactly one frame was sent out + ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // No data left in ring buffer + ASSERT_EQ(this->fromPortHistory_frameOut->at(0).data.getSize(), frame_size); // accumulated buffer size +} + +void FrameAccumulatorTester ::testAccumulateBuffersEmitManyFrames() { + U32 max_iters = STest::Random::lowerUpper(1, 10); + U32 total_buffer_received = 0; + + U32 frame_size = 0; + U32 buffer_count = 0; + + // Send frames successively, perform some checks after each frame + for (U32 i = 0; i < max_iters; i++) { + this->mockAccumulateFullFrame(frame_size, buffer_count); + total_buffer_received += buffer_count; + + ASSERT_from_bufferDeallocate_SIZE(total_buffer_received); // all input buffers deallocated + ASSERT_from_frameOut_SIZE(i+1); // Exactly one frame was sent out + ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // No data left in ring buffer + ASSERT_EQ(this->fromPortHistory_frameOut->at(i).data.getSize(), frame_size); // accumulated buffer size + } + // Final checks + ASSERT_from_bufferDeallocate_SIZE(total_buffer_received); // all input buffers deallocated + ASSERT_from_frameOut_SIZE(max_iters); // Exactly max_iters frames were sent out + ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // No data left in ring buffer +} + +// ---------------------------------------------------------------------- +// Helper functions +// ---------------------------------------------------------------------- + +void FrameAccumulatorTester ::mockAccumulateFullFrame(U32& frame_size, U32& buffer_count) { + // Constants need to be picked so that: + // - the worst case doesn't overflow the ring buffer size (max_size * iters < 2048) + // - iters < MAX_HISTORY_SIZE + const U32 buffer_max_size = 64; + const U32 iters = STest::Random::lowerUpper(0, 10); + + U8 data[buffer_max_size]; + U32 buffer_size; + Fw::Buffer buffer(data, 0); + U32 accumulated_size = 0; + + // Send multiple buffers with MORE_DATA_NEEDED + for (U32 i = 0; i < iters; i++) { + buffer_size = STest::Random::lowerUpper(1, buffer_max_size); + accumulated_size += buffer_size; + buffer.setSize(buffer_size); + // Detector reports MORE_DATA_NEEDED and size needed bigger than accumulated size so far + this->mockDetector.set_next_result(FrameDetector::Status::MORE_DATA_NEEDED, accumulated_size + 1); + this->invoke_to_dataIn(0, buffer, Drv::RecvStatus::RECV_OK); + } + + // Send last buffer with FRAME_DETECTED + buffer_size = STest::Random::lowerUpper(1, buffer_max_size); + buffer.setSize(buffer_size); + accumulated_size += buffer_size; // accumulate once more (sending last buffer below) + // Send last buffer with finally FRAME_DETECTED and total accumulated + last buffer + this->mockDetector.set_next_result(FrameDetector::Status::FRAME_DETECTED, accumulated_size); + // Receive the last buffer on dataIn + this->invoke_to_dataIn(0, buffer, Drv::RecvStatus::RECV_OK); + frame_size = accumulated_size; + buffer_count = iters + 1; +} + +// ---------------------------------------------------------------------- +// Port handler overrides +// ---------------------------------------------------------------------- +Fw::Buffer FrameAccumulatorTester ::from_bufferAllocate_handler( + FwIndexType portNum, + U32 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/Svc/FrameAccumulator/test/ut/FrameAccumulatorTester.hpp b/Svc/FrameAccumulator/test/ut/FrameAccumulatorTester.hpp new file mode 100644 index 00000000000..351a6ba799b --- /dev/null +++ b/Svc/FrameAccumulator/test/ut/FrameAccumulatorTester.hpp @@ -0,0 +1,126 @@ +// ====================================================================== +// \title FrameAccumulatorTester.hpp +// \author thomas-bc +// \brief hpp file for FrameAccumulator component test harness implementation class +// ====================================================================== + +#ifndef Svc_FrameAccumulatorTester_HPP +#define Svc_FrameAccumulatorTester_HPP + +#include "Fw/Types/MallocAllocator.hpp" +#include "Svc/FrameAccumulator/FrameAccumulator.hpp" +#include "Svc/FrameAccumulator/FrameAccumulatorGTestBase.hpp" +#include "Svc/FrameAccumulator/FrameDetector/FprimeFrameDetector.hpp" + +namespace Svc { + +class FrameAccumulatorTester : public FrameAccumulatorGTestBase { + public: + // ---------------------------------------------------------------------- + // Constants + // ---------------------------------------------------------------------- + + // Maximum size of histories storing events, telemetry, and port outputs + static const FwSizeType MAX_HISTORY_SIZE = 200; + + // Instance ID supplied to the component instance under test + static const FwEnumStoreType TEST_INSTANCE_ID = 0; + + public: + // ---------------------------------------------------------------------- + // Construction and destruction + // ---------------------------------------------------------------------- + + //! Construct object FrameAccumulatorTester + FrameAccumulatorTester(); + + //! Destroy object FrameAccumulatorTester + ~FrameAccumulatorTester() = default; + + public: + // ---------------------------------------------------------------------- + // Tests + // ---------------------------------------------------------------------- + + //! Test that frame is detected + void testFrameDetected(); + + //! More data needed + void testMoreDataNeeded(); + + //! No frame detected + void testNoFrameDetected(); + + //! Receive a zero size buffer + void testReceiveZeroSizeBuffer(); + + // Send two buffers successively and check that the frame is correctly accumulated + void testAccumulateTwoBuffers(); + + //! Test accumulation of multiple random-size buffer into a frame + void testAccumulateBuffersEmitFrame(); + + //! Test accumulation of multiple random-size buffer into frames successively + void testAccumulateBuffersEmitManyFrames(); + + private: + // ---------------------------------------------------------------------- + // Helper functions + // ---------------------------------------------------------------------- + + //! Send a series of random-size buffers, terminated by a buffer that + //! will be detected as a full frame by the MockDetector + //! (output) frame_size and buffer_count are updated with the size of the frame and the number of buffers sent + void mockAccumulateFullFrame(U32& frame_size, U32& buffer_count); + + //! Connect ports + void connectPorts(); + + //! Initialize components + void initComponents(); + + private: + // ---------------------------------------------------------------------- + // Port handler overrides + // ---------------------------------------------------------------------- + //! Overriding bufferAllocate handler to be able to request a buffer in component tests + Fw::Buffer from_bufferAllocate_handler(FwIndexType portNum, U32 size) override; + + private: + // ---------------------------------------------------------------------- + // Member variables + // ---------------------------------------------------------------------- + //! MockDetector is used to control the behavior of the component under test + //! by controlling what the FrameDetector will report without needing to craft + //! legitimate frames from specific to a protocol + class MockDetector : public FrameDetector { + public: + Status detect(const Types::CircularBuffer& data, FwSizeType& size_out) const override { + size_out = this->next_size_out; + return next_status; + } + + void set_next_result(Status status, FwSizeType size_out) { + this->next_size_out = size_out; + this->next_status = status; + } + + Status next_status = Status::FRAME_DETECTED; + U32 next_size_out = 0; + }; + + //! Instances required by the component under test + MockDetector mockDetector; + Fw::MallocAllocator mallocator; + + //! The component under test (should be listed after mallocator for safe destruction) + Svc::FrameAccumulator component; + + Fw::Buffer m_buffer; // buffer to be returned by mocked bufferAllocate call + U8 m_buffer_slot[2048]; +}; + + +} // namespace Svc + +#endif diff --git a/Svc/FrameAccumulator/test/ut/detectors/FprimeFrameDetectorTestMain.cpp b/Svc/FrameAccumulator/test/ut/detectors/FprimeFrameDetectorTestMain.cpp new file mode 100644 index 00000000000..1e4646a0c02 --- /dev/null +++ b/Svc/FrameAccumulator/test/ut/detectors/FprimeFrameDetectorTestMain.cpp @@ -0,0 +1,143 @@ +// ====================================================================== +// \title FprimeFrameDetectorTestMain.cpp +// \author thomas-bc +// \brief cpp file for FrameAccumulator component test main function +// ====================================================================== + +#include "gtest/gtest.h" +#include "Svc/FrameAccumulator/FrameDetector/FprimeFrameDetector.hpp" +#include "STest/Random/Random.hpp" +#include "Utils/Hash/Hash.hpp" + +constexpr U32 CIRCULAR_BUFFER_TEST_SIZE = 2048; + +//! \brief Create an F´ frame and serialize it into the supplied circular buffer +//! \param circular_buffer The circular buffer to serialize the frame into +//! \note The frame is generated with random data of random size +//! \return The size of the generated frame +FwSizeType generate_random_fprime_frame(Types::CircularBuffer& circular_buffer) { + constexpr FwSizeType FRAME_HEADER_SIZE = 8; + constexpr FwSizeType FRAME_FOOTER_SIZE = 4; + // Generate random packet size (1-1024 bytes; because 0 would trigger undefined behavior warnings) + // 1024 is max length as per FrameAccumulator/FrameDetector/FprimeFrameDetector @ LengthToken::MaximumLength + U32 packet_size = STest::Random::lowerUpper(1, 1024); + + U8 packet_data[packet_size]; + // Generate random packet_data of random size + for (FwSizeType i = 0; i < packet_size; i++) { + packet_data[i] = STest::Random::lowerUpper(0, 255); + } + // Frame header | Start Word 4 bytes | Length (4 bytes) | + U8 frame_header[FRAME_HEADER_SIZE] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00, 0x00, 0x00}; + // Serialize actual packet size into header + for (FwSizeType i = 0; i < 4; i++) { + frame_header[i + 4] = static_cast(packet_size >> (8 * (3 - i))); + } + + // Calculate CRC on header + packet_data + Utils::Hash crc_calculator; + Utils::HashBuffer crc_result; + crc_calculator.update(frame_header, FRAME_HEADER_SIZE); + crc_calculator.update(packet_data, packet_size); + crc_calculator.final(crc_result); + // printf("crc: %08X\n", crc); + + // Concatenate all packet_data to create the full frame (byte array) + FwSizeType fprime_frame_size = FRAME_HEADER_SIZE + packet_size + FRAME_FOOTER_SIZE; + U8 fprime_frame[fprime_frame_size]; + // Copy header, packet_data, and CRC into the full frame + for (FwIndexType i = 0; i < static_cast(FRAME_HEADER_SIZE); i++) { + fprime_frame[i] = frame_header[i]; + } + for (FwIndexType i = 0; i < static_cast(packet_size); i++) { + fprime_frame[i + FRAME_HEADER_SIZE] = packet_data[i]; + } + for (FwIndexType i = 0; i < static_cast(FRAME_FOOTER_SIZE); i++) { + // crc is a U32; unpack into 4 bytes (shift by 24->-16->8->0 bits, mask with 0xFF) + fprime_frame[i + FRAME_HEADER_SIZE + packet_size] = (crc_result.asBigEndianU32() >> (8 * (3 - i))) & 0xFF; + } + // Serialize frame into circular buffer + circular_buffer.serialize(fprime_frame, fprime_frame_size); + + // Uncomment for debugging + // printf("Serialized %llu bytes:\n", fprime_frame_size); + // for (FwIndexType i = 0; i < static_cast(fprime_frame_size); i++) { + // printf("%02X ", fprime_frame[i]); + // } + return fprime_frame_size; +} + +TEST(FprimeFrameDetector, TestFrameDetected) { + Svc::FrameDetectors::FprimeFrameDetector fprime_detector; + U8 buffer[CIRCULAR_BUFFER_TEST_SIZE]; + ::memset(buffer, 0, CIRCULAR_BUFFER_TEST_SIZE); + Types::CircularBuffer circular_buffer(buffer, CIRCULAR_BUFFER_TEST_SIZE); + + FwSizeType frame_size = generate_random_fprime_frame(circular_buffer); + + Svc::FrameDetector::Status status; + FwSizeType size_out = 0; + status = fprime_detector.detect(circular_buffer, size_out); + + EXPECT_EQ(status, Svc::FrameDetector::Status::FRAME_DETECTED); + EXPECT_EQ(size_out, frame_size); +} + +TEST(FprimeFrameDetector, TestManyFrameDetected) { + U32 MAX_ITERS = 1000; + Svc::FrameDetectors::FprimeFrameDetector fprime_detector; + U8 buffer[CIRCULAR_BUFFER_TEST_SIZE]; + ::memset(buffer, 0, CIRCULAR_BUFFER_TEST_SIZE); + Types::CircularBuffer circular_buffer(buffer, CIRCULAR_BUFFER_TEST_SIZE); + + for (U32 i = 0; i < MAX_ITERS; i++) { + FwSizeType frame_size = generate_random_fprime_frame(circular_buffer); + Svc::FrameDetector::Status status; + FwSizeType size_out = 0; + status = fprime_detector.detect(circular_buffer, size_out); + + EXPECT_EQ(status, Svc::FrameDetector::Status::FRAME_DETECTED); + EXPECT_EQ(size_out, frame_size); + circular_buffer.rotate(size_out); // clear up used data + } +} + +TEST(FprimeFrameDetector, TestNoFrameDetected) { + Svc::FrameDetectors::FprimeFrameDetector fprime_detector; + U8 buffer[CIRCULAR_BUFFER_TEST_SIZE]; + ::memset(buffer, 0, CIRCULAR_BUFFER_TEST_SIZE); + Types::CircularBuffer circular_buffer(buffer, CIRCULAR_BUFFER_TEST_SIZE); + + (void) generate_random_fprime_frame(circular_buffer); + // Remove 1 byte from the beginning of the frame, making it invalid + circular_buffer.rotate(1); + + Svc::FrameDetector::Status status; + FwSizeType unused = 0; + status = fprime_detector.detect(circular_buffer, unused); + + EXPECT_EQ(status, Svc::FrameDetector::Status::NO_FRAME_DETECTED); +} + +TEST(FprimeFrameDetector, TestMoreDataNeeded) { + Svc::FrameDetectors::FprimeFrameDetector fprime_detector; + U8 buffer[CIRCULAR_BUFFER_TEST_SIZE]; + ::memset(buffer, 0, CIRCULAR_BUFFER_TEST_SIZE); + Types::CircularBuffer circular_buffer(buffer, CIRCULAR_BUFFER_TEST_SIZE); + + (void) generate_random_fprime_frame(circular_buffer); + circular_buffer.m_allocated_size--; // Remove 1 byte from the end of the frame to trigger "more data needed" + + Svc::FrameDetector::Status status; + FwSizeType unused = 0; + status = fprime_detector.detect(circular_buffer, unused); + + EXPECT_EQ(status, Svc::FrameDetector::Status::MORE_DATA_NEEDED); +} + + +int main(int argc, char** argv) { + STest::Random::seed(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/Svc/Interfaces/DeframerInterface.fppi b/Svc/Interfaces/DeframerInterface.fppi new file mode 100644 index 00000000000..f32a8e756fb --- /dev/null +++ b/Svc/Interfaces/DeframerInterface.fppi @@ -0,0 +1,5 @@ +@ Port to receive framed data, with optional context +guarded input port framedIn: Fw.DataWithContext + +@ Port to output deframed data, with optional context +output port deframedOut: Fw.DataWithContext diff --git a/Svc/Interfaces/RouterInterface.fppi b/Svc/Interfaces/RouterInterface.fppi new file mode 100644 index 00000000000..9dd95aa3adf --- /dev/null +++ b/Svc/Interfaces/RouterInterface.fppi @@ -0,0 +1,8 @@ +@ Receiving data (Fw::Buffer) to be routed with optional context to help with routing +guarded input port dataIn: Fw.DataWithContext + +@ Port for sending file packets as Fw::Buffer (ownership passed to receiver) +output port fileOut: Fw.BufferSend + +@ Port for sending command packets as Fw::ComBuffers +output port commandOut: Fw.Com diff --git a/Svc/Router/CMakeLists.txt b/Svc/Router/CMakeLists.txt new file mode 100644 index 00000000000..fad11ded5d8 --- /dev/null +++ b/Svc/Router/CMakeLists.txt @@ -0,0 +1,25 @@ +#### +# 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}/Router.fpp" + "${CMAKE_CURRENT_LIST_DIR}/Router.cpp" +) +register_fprime_module() + + +#### UTS #### +set(UT_SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/Router.fpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/RouterTester.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/RouterTestMain.cpp" +) +set(UT_AUTO_HELPERS ON) + +register_fprime_ut() diff --git a/Svc/Router/Router.cpp b/Svc/Router/Router.cpp new file mode 100644 index 00000000000..cc83945f69a --- /dev/null +++ b/Svc/Router/Router.cpp @@ -0,0 +1,97 @@ +// ====================================================================== +// \title Router.cpp +// \author thomas-bc +// \brief cpp file for Router component implementation class +// ====================================================================== + +#include "Svc/Router/Router.hpp" +#include "FpConfig.hpp" +#include "Fw/Com/ComPacket.hpp" +#include "Fw/Logger/Logger.hpp" + +namespace Svc { + +// ---------------------------------------------------------------------- +// Component construction and destruction +// ---------------------------------------------------------------------- + +Router ::Router(const char* const compName) : RouterComponentBase(compName) {} + +Router ::~Router() {} + +// ---------------------------------------------------------------------- +// Handler implementations for user-defined typed input ports +// ---------------------------------------------------------------------- + +void Router ::dataIn_handler(NATIVE_INT_TYPE portNum, Fw::Buffer& packetBuffer, Fw::Buffer& contextBuffer) { + // Read the packet type from the packet buffer + FwPacketDescriptorType packetType = Fw::ComPacket::FW_PACKET_UNKNOWN; + Fw::SerializeStatus status = Fw::FW_SERIALIZE_OK; + { + Fw::SerializeBufferBase& serial = packetBuffer.getSerializeRepr(); + status = serial.setBuffLen(packetBuffer.getSize()); + FW_ASSERT(status == Fw::FW_SERIALIZE_OK); + status = serial.deserialize(packetType); + } + + // Whether to deallocate the packet buffer + bool deallocate = true; + + // Process the packet + if (status == Fw::FW_SERIALIZE_OK) { + U8* const packetData = packetBuffer.getData(); + const U32 packetSize = packetBuffer.getSize(); + switch (packetType) { + // Handle a command packet + case Fw::ComPacket::FW_PACKET_COMMAND: { + // Allocate a com buffer on the stack + Fw::ComBuffer com; + // Copy the contents of the packet buffer into the com buffer + status = com.setBuff(packetData, packetSize); + if (status == Fw::FW_SERIALIZE_OK) { + // Send the com buffer + commandOut_out(0, com, 0); + // REVIEW NOTE: Deframer did not check if the output port was connected, should it? + } else { + Fw::Logger::log("[ERROR] Serializing com buffer failed with status %d\n", status); + } + break; + } + // Handle a file packet + case Fw::ComPacket::FW_PACKET_FILE: { + // If the file uplink output port is connected, + // send the file packet. Otherwise take no action. + if (isConnected_fileOut_OutputPort(0)) { + // Shift the packet buffer to skip the packet type + // The FileUplink component does not expect the packet + // type to be there. + packetBuffer.setData(packetData + sizeof(packetType)); + packetBuffer.setSize(static_cast(packetSize - sizeof(packetType))); + // Send the packet buffer + fileOut_out(0, packetBuffer); + // Transfer ownership of the buffer to the receiver + deallocate = false; + } + break; + } + // Take no action for other packet types + default: + break; + } + } else { + Fw::Logger::log("[ERROR] Deserializing packet type failed with status %d\n", status); + } + + if (deallocate) { + // Deallocate the packet buffer + bufferDeallocate_out(0, packetBuffer); + } +} + +void Router ::cmdResponseIn_handler(NATIVE_INT_TYPE portNum, + FwOpcodeType opcode, + U32 cmdSeq, + const Fw::CmdResponse& response) { + // Nothing to do +} +} // namespace Svc diff --git a/Svc/Router/Router.fpp b/Svc/Router/Router.fpp new file mode 100644 index 00000000000..1aef4b1c27e --- /dev/null +++ b/Svc/Router/Router.fpp @@ -0,0 +1,34 @@ +module Svc { + @ Routes packets deframed by the Deframer to the rest of the system + passive component Router { + + # ---------------------------------------------------------------------- + # Router interface + # ---------------------------------------------------------------------- + include "../Interfaces/RouterInterface.fppi" + + @ Port for deallocating buffers + output port bufferDeallocate: Fw.BufferSend + + @ Port for receiving command responses from a command dispatcher. + @ Invoking this port does nothing. The port exists to allow the matching + @ connection in the topology. + sync input port cmdResponseIn: Fw.CmdResponse + + ############################################################################### + # Standard AC Ports: Required for Channels, Events, Commands, and Parameters # + ############################################################################### + @ 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 + + @ Port for sending telemetry channels to downlink + telemetry port tlmOut + + } +} diff --git a/Svc/Router/Router.hpp b/Svc/Router/Router.hpp new file mode 100644 index 00000000000..1b2aaa23426 --- /dev/null +++ b/Svc/Router/Router.hpp @@ -0,0 +1,50 @@ +// ====================================================================== +// \title Router.hpp +// \author thomas-bc +// \brief hpp file for Router component implementation class +// ====================================================================== + +#ifndef Svc_Router_HPP +#define Svc_Router_HPP + +#include "Svc/Router/RouterComponentAc.hpp" + +namespace Svc { + +class Router : public RouterComponentBase { + public: + // ---------------------------------------------------------------------- + // Component construction and destruction + // ---------------------------------------------------------------------- + + //! Construct Router object + Router(const char* const compName //!< The component name + ); + + //! Destroy Router object + ~Router(); + + PRIVATE: + // ---------------------------------------------------------------------- + // Handler implementations for user-defined typed input ports + // ---------------------------------------------------------------------- + + //! Handler implementation for bufferIn + //! Receiving Fw::Buffer from Deframer + void dataIn_handler(NATIVE_INT_TYPE portNum, //!< The port number + Fw::Buffer& packetBuffer, //!< The packet buffer + Fw::Buffer& contextBuffer //!< The context buffer + ) override; + + // ! Handler for input port cmdResponseIn + // ! This is a no-op because Router does not need to handle command responses + // ! but the port must be connected + void cmdResponseIn_handler(NATIVE_INT_TYPE portNum, //!< The port number + FwOpcodeType opcode, //!< The command opcode + U32 cmdSeq, //!< The command sequence number + const Fw::CmdResponse& response //!< The command response + ) override; +}; +} // namespace Svc + +#endif diff --git a/Svc/Router/docs/sdd.md b/Svc/Router/docs/sdd.md new file mode 100644 index 00000000000..9f0e4da1432 --- /dev/null +++ b/Svc/Router/docs/sdd.md @@ -0,0 +1,43 @@ +# Svc::Router + +The `Svc::Router` component routes F´ packets (such as command or file packets) to other components. + +The `Svc::Router` component receives F´ packets (as [Fw::Buffer](../../../Fw/Buffer/docs/sdd.md) objects) and routes them to other components through synchronous port calls. The `Svc::Router` component supports `Fw::ComPacket::FW_PACKET_COMMAND` and `Fw::ComPacket::FW_PACKET_FILE` packet types. + +## Usage Examples + +The `Svc::Router` 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::Router` 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) + +## Class Diagram + + +```mermaid +classDiagram + class Router~PassiveComponent~ { + + void dataIn_handler(portNum, packetBuffer, contextBuffer) + + void cmdResponseIn_handler(portNum, opcode, cmdSeq, response) + } +``` + +## Port Descriptions + +| Name | Description | Type | +|---|---|---| +| `dataIn: Fw.DataWithContext` | Receiving Fw::Buffer with context buffer from Deframer | `guarded input` | +| `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) | `output` | + + +## Requirements + +| Name | Description | Rationale | Validation | +|---|---|---|---| +SVC-ROUTER-001 | `Svc::Router` 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::Router` shall route packets with the following types: `Fw::ComPacket::FW_PACKET_COMMAND`, `Fw::ComPacket::FW_PACKET_FILE`. | These are the packet types used for uplink. | Unit test | +SVC-ROUTER-003 | `Svc::Router` shall route command and file packets to the `commandOut` and `fileOut` ports, respectively | Routing mechanism as dictated by the F´ point-to-point architecture. | Unit test | diff --git a/Svc/Router/test/ut/RouterTestMain.cpp b/Svc/Router/test/ut/RouterTestMain.cpp new file mode 100644 index 00000000000..875e6f09b14 --- /dev/null +++ b/Svc/Router/test/ut/RouterTestMain.cpp @@ -0,0 +1,35 @@ +// ====================================================================== +// \title RouterTestMain.cpp +// \author thomas-bc +// \brief cpp file for Router component test main function +// ====================================================================== + +#include "RouterTester.hpp" + +#include + +TEST(Router, TestComInterface) { + COMMENT("Route a com packet"); + Svc::RouterTester tester; + tester.testRouteComInterface(); +} +TEST(Router, TestFileInterface) { + COMMENT("Route a file packet"); + Svc::RouterTester tester; + tester.testRouteFileInterface(); +} +TEST(Router, TestUnknownInterface) { + COMMENT("Attempt to route a packet of unknown type"); + Svc::RouterTester tester; + tester.testRouteUnknownPacket(); +} +TEST(Router, TestCommandResponse) { + COMMENT("Handle a command response (no-op)"); + Svc::RouterTester tester; + tester.testCommandResponse(); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/Svc/Router/test/ut/RouterTester.cpp b/Svc/Router/test/ut/RouterTester.cpp new file mode 100644 index 00000000000..9657d4b2740 --- /dev/null +++ b/Svc/Router/test/ut/RouterTester.cpp @@ -0,0 +1,68 @@ +// ====================================================================== +// \title RouterTester.cpp +// \author thomas-bc +// \brief cpp file for Router component test harness implementation class +// ====================================================================== + +#include "RouterTester.hpp" + +namespace Svc { + +// ---------------------------------------------------------------------- +// Construction and destruction +// ---------------------------------------------------------------------- + +RouterTester ::RouterTester() : RouterGTestBase("RouterTester", RouterTester::MAX_HISTORY_SIZE), component("Router") { + this->initComponents(); + this->connectPorts(); +} + +RouterTester ::~RouterTester() {} + +// ---------------------------------------------------------------------- +// Test Cases +// ---------------------------------------------------------------------- + +void RouterTester ::testRouteComInterface() { + this->mockReceivePacketType(Fw::ComPacket::FW_PACKET_COMMAND); + ASSERT_from_commandOut_SIZE(1); // one command packet emitted + ASSERT_from_fileOut_SIZE(0); // no file packet emitted + ASSERT_from_bufferDeallocate_SIZE(1); // command packets are deallocated by the router +} + +void RouterTester ::testRouteFileInterface() { + this->mockReceivePacketType(Fw::ComPacket::FW_PACKET_FILE); + ASSERT_from_commandOut_SIZE(0); // no command packet emitted + ASSERT_from_fileOut_SIZE(1); // one file packet emitted + ASSERT_from_bufferDeallocate_SIZE(0); // no deallocation (file packets' ownership is transferred to the receiver) +} + +void RouterTester ::testRouteUnknownPacket() { + this->mockReceivePacketType(Fw::ComPacket::FW_PACKET_UNKNOWN); + ASSERT_from_commandOut_SIZE(0); // no command packet emitted + ASSERT_from_fileOut_SIZE(0); // no file packet emitted + ASSERT_from_bufferDeallocate_SIZE(1); // unknown packets are deallocated and dropped +} + +void RouterTester ::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 RouterTester::mockReceivePacketType(Fw::ComPacket::ComPacketType packetType) { + const FwPacketDescriptorType descriptorType = packetType; + U8 data[sizeof descriptorType]; + Fw::Buffer buffer(data, sizeof(data)); + buffer.getSerializeRepr().serialize(descriptorType); + Fw::Buffer nullContext; + this->invoke_to_dataIn(0, buffer, nullContext); +} + +} // namespace Svc diff --git a/Svc/Router/test/ut/RouterTester.hpp b/Svc/Router/test/ut/RouterTester.hpp new file mode 100644 index 00000000000..b7b5e3175d5 --- /dev/null +++ b/Svc/Router/test/ut/RouterTester.hpp @@ -0,0 +1,82 @@ +// ====================================================================== +// \title RouterTester.hpp +// \author thomas-bc +// \brief hpp file for Router component test harness implementation class +// ====================================================================== + +#ifndef Svc_RouterTester_HPP +#define Svc_RouterTester_HPP + +#include "Svc/Router/Router.hpp" +#include "Svc/Router/RouterGTestBase.hpp" + +#include + +namespace Svc { + +class RouterTester : public RouterGTestBase { + 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 RouterTester + RouterTester(); + + //! Destroy object RouterTester + ~RouterTester(); + + public: + // ---------------------------------------------------------------------- + // Tests + // ---------------------------------------------------------------------- + + //! Route a com packet + void testRouteComInterface(); + + //! Route a file packet + void testRouteFileInterface(); + + //! Route a packet of unknown type + void testRouteUnknownPacket(); + + //! Invoke the command response input port + void testCommandResponse(); + + private: + // ---------------------------------------------------------------------- + // Helper functions + // ---------------------------------------------------------------------- + + //! Connect ports + void connectPorts(); + + //! Initialize components + void initComponents(); + + //! Mock the reception of a packet of a specific type + void mockReceivePacketType(Fw::ComPacket::ComPacketType packetType); + + private: + // ---------------------------------------------------------------------- + // Member variables + // ---------------------------------------------------------------------- + + //! The component under test + Router component; +}; + +} // namespace Svc + +#endif diff --git a/Utils/Hash/libcrc/CRC32.cpp b/Utils/Hash/libcrc/CRC32.cpp index 81e94ada5dc..0c49f9b390f 100644 --- a/Utils/Hash/libcrc/CRC32.cpp +++ b/Utils/Hash/libcrc/CRC32.cpp @@ -12,6 +12,9 @@ #include +static_assert(sizeof(unsigned long) >= sizeof(U32), "CRC32 cannot fit in CRC32 library chosen types"); + + namespace Utils { Hash :: diff --git a/cmake/test/src/settings.py b/cmake/test/src/settings.py index 2a901ec3238..4ad38f9cfa1 100644 --- a/cmake/test/src/settings.py +++ b/cmake/test/src/settings.py @@ -46,12 +46,12 @@ "Svc_CmdDispatcher", "Svc_CmdSequencer", "Svc_Cycle", - "Svc_Deframer", "Svc_Fatal", "Svc_FatalHandler", "Svc_FileDownlink", "Svc_FileManager", "Svc_FileUplink", + "Svc_FprimeDeframer", "Svc_Framer", "Svc_FramingProtocol", "Svc_Health", diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt index a63d68471da..a2a38d4d975 100644 --- a/config/CMakeLists.txt +++ b/config/CMakeLists.txt @@ -9,5 +9,6 @@ set(SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/FpConfig.fpp" "${CMAKE_CURRENT_LIST_DIR}/PolyDbCfg.fpp" "${CMAKE_CURRENT_LIST_DIR}/VersionCfg.fpp" + "${CMAKE_CURRENT_LIST_DIR}/FprimeProtocol.fpp" ) register_fprime_module(config) diff --git a/config/FpConfig.h b/config/FpConfig.h index ca65ff2226a..ffc4b5666db 100644 --- a/config/FpConfig.h +++ b/config/FpConfig.h @@ -119,6 +119,11 @@ typedef U32 FwDpIdType; typedef U32 FwDpPriorityType; #define PRI_FwDpPriorityType PRIu32 +// Type for start word and length word used in fprime framing +typedef U32 FwFramingTokenType; +#define PRI_FwFramingTokenType PRIu32 + + // ---------------------------------------------------------------------- // Derived type aliases // By default, these types are aliases of types defined above diff --git a/config/FprimeProtocol.fpp b/config/FprimeProtocol.fpp new file mode 100644 index 00000000000..d24bb7a61b7 --- /dev/null +++ b/config/FprimeProtocol.fpp @@ -0,0 +1,16 @@ +module FprimeProtocol { + + @ Describes the frame header format for the F Prime communications protocol + struct FrameHeader { + start_word: U32, + length: U32, + } default { + start_word = 0xdeadbeef + } + + @ Describes the frame trailer format for the F Prime communications protocol + struct FrameTrailer { + crc: U32 + } + +}