mirror of
https://github.com/nasa/fprime.git
synced 2025-12-10 17:47:10 -06:00
Add Communication Aggregator Component (#4264)
* Add aggregator component * Add com aggregator to subtopology * Fix aggregator issues * Format and SDD * Add basic UTs * Fix not-empty check, test * sp * Fix author tag * Bump GDS for aggregation; timeout aggregator * Bump comQueue event size * Increase timeout for integration tests * Update Fw/Buffer/Buffer.hpp Co-authored-by: Thomas Boyer-Chammard <49786685+thomas-bc@users.noreply.github.com> * Update Svc/ComAggregator/CMakeLists.txt Co-authored-by: Thomas Boyer-Chammard <49786685+thomas-bc@users.noreply.github.com> * Update Svc/ComAggregator/docs/sdd.md Co-authored-by: Thomas Boyer-Chammard <49786685+thomas-bc@users.noreply.github.com> * Update Svc/ComAggregator/docs/sdd.md Co-authored-by: Thomas Boyer-Chammard <49786685+thomas-bc@users.noreply.github.com> * Remove unused variable 'good' in action doClear * A usage note about APID. --------- Co-authored-by: Thomas Boyer-Chammard <49786685+thomas-bc@users.noreply.github.com>
This commit is contained in:
parent
9a7123c190
commit
2ee27f82f3
@ -47,6 +47,15 @@ namespace Fw {
|
|||||||
class Buffer : public Fw::Serializable {
|
class Buffer : public Fw::Serializable {
|
||||||
friend class Fw::BufferTester;
|
friend class Fw::BufferTester;
|
||||||
|
|
||||||
|
public:
|
||||||
|
//! Buffer ownership state
|
||||||
|
//!
|
||||||
|
//! A convenience enumeration to help users implement ownership tracking of buffers.
|
||||||
|
enum class OwnershipState {
|
||||||
|
NOT_OWNED, //!< The buffer is currently not owned
|
||||||
|
OWNED, //!< The buffer is currently owned
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//! The size type for a buffer - for backwards compatibility
|
//! The size type for a buffer - for backwards compatibility
|
||||||
using SizeType = FwSizeType;
|
using SizeType = FwSizeType;
|
||||||
|
|||||||
@ -88,6 +88,7 @@ module Ref {
|
|||||||
rateGroup1Comp.RateGroupMemberOut[4] -> systemResources.run
|
rateGroup1Comp.RateGroupMemberOut[4] -> systemResources.run
|
||||||
rateGroup1Comp.RateGroupMemberOut[5] -> ComCcsds.comQueue.run
|
rateGroup1Comp.RateGroupMemberOut[5] -> ComCcsds.comQueue.run
|
||||||
rateGroup1Comp.RateGroupMemberOut[6] -> CdhCore.cmdDisp.run
|
rateGroup1Comp.RateGroupMemberOut[6] -> CdhCore.cmdDisp.run
|
||||||
|
rateGroup1Comp.RateGroupMemberOut[7] -> ComCcsds.aggregator.timeout
|
||||||
|
|
||||||
# Rate group 2
|
# Rate group 2
|
||||||
rateGroupDriverComp.CycleOut[Ports_RateGroups.rateGroup2] -> rateGroup2Comp.CycleIn
|
rateGroupDriverComp.CycleOut[Ports_RateGroups.rateGroup2] -> rateGroup2Comp.CycleIn
|
||||||
|
|||||||
@ -71,3 +71,4 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/LinuxTimer/")
|
|||||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Version/")
|
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Version/")
|
||||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FpySequencer/")
|
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FpySequencer/")
|
||||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Ccsds/")
|
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Ccsds/")
|
||||||
|
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ComAggregator/")
|
||||||
|
|||||||
34
Svc/ComAggregator/CMakeLists.txt
Normal file
34
Svc/ComAggregator/CMakeLists.txt
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
####
|
||||||
|
# F Prime CMakeLists.txt:
|
||||||
|
#
|
||||||
|
# SOURCES: list of source files (to be compiled)
|
||||||
|
# AUTOCODER_INPUTS: list of files to be passed to the autocoders
|
||||||
|
# DEPENDS: list of libraries that this module depends on
|
||||||
|
#
|
||||||
|
# More information in the F´ CMake API documentation:
|
||||||
|
# https://fprime.jpl.nasa.gov/latest/docs/reference/api/cmake/API/
|
||||||
|
#
|
||||||
|
####
|
||||||
|
|
||||||
|
# Module names are derived from the path from the nearest project/library/framework
|
||||||
|
# root when not specifically overridden by the developer. i.e. The module defined by
|
||||||
|
# `Ref/SignalGen/CMakeLists.txt` will be named `Ref_SignalGen`.
|
||||||
|
|
||||||
|
register_fprime_library(
|
||||||
|
AUTOCODER_INPUTS
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/ComAggregator.fpp"
|
||||||
|
SOURCES
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/ComAggregator.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
### Unit Tests ###
|
||||||
|
register_fprime_ut(
|
||||||
|
AUTOCODER_INPUTS
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/ComAggregator.fpp"
|
||||||
|
SOURCES
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/test/ut/ComAggregatorTestMain.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/test/ut/ComAggregatorTester.cpp"
|
||||||
|
DEPENDS
|
||||||
|
STest # For rules-based testing
|
||||||
|
UT_AUTO_HELPERS
|
||||||
|
)
|
||||||
120
Svc/ComAggregator/ComAggregator.cpp
Normal file
120
Svc/ComAggregator/ComAggregator.cpp
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
// ======================================================================
|
||||||
|
// \title ComAggregator.cpp
|
||||||
|
// \author lestarch
|
||||||
|
// \brief cpp file for ComAggregator component implementation class
|
||||||
|
// ======================================================================
|
||||||
|
|
||||||
|
#include "Svc/ComAggregator/ComAggregator.hpp"
|
||||||
|
|
||||||
|
namespace Svc {
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Component construction and destruction
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
ComAggregator ::ComAggregator(const char* const compName)
|
||||||
|
: ComAggregatorComponentBase(compName),
|
||||||
|
m_bufferState(Fw::Buffer::OwnershipState::OWNED),
|
||||||
|
m_frameBuffer(m_frameBufferStore, sizeof(m_frameBufferStore)),
|
||||||
|
m_frameSerializer(m_frameBuffer.getSerializer()) {}
|
||||||
|
|
||||||
|
ComAggregator ::~ComAggregator() {}
|
||||||
|
|
||||||
|
void ComAggregator ::preamble() {
|
||||||
|
Fw::Success good = Fw::Success::SUCCESS;
|
||||||
|
this->comStatusOut_out(0, good);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Handler implementations for typed input ports
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void ComAggregator ::comStatusIn_handler(FwIndexType portNum, Fw::Success& condition) {
|
||||||
|
this->aggregationMachine_sendSignal_status(condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComAggregator ::dataIn_handler(FwIndexType portNum, Fw::Buffer& data, const ComCfg::FrameContext& context) {
|
||||||
|
Svc::ComDataContextPair pair(data, context);
|
||||||
|
this->aggregationMachine_sendSignal_fill(pair);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComAggregator ::dataReturnIn_handler(FwIndexType portNum, Fw::Buffer& data, const ComCfg::FrameContext& context) {
|
||||||
|
FW_ASSERT(this->m_bufferState == Fw::Buffer::OwnershipState::NOT_OWNED);
|
||||||
|
this->m_bufferState = Fw::Buffer::OwnershipState::OWNED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComAggregator ::timeout_handler(FwIndexType portNum, U32 context) {
|
||||||
|
this->aggregationMachine_sendSignal_timeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Implementations for internal state machine actions
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void ComAggregator ::Svc_AggregationMachine_action_doClear(SmId smId, Svc_AggregationMachine::Signal signal) {
|
||||||
|
this->m_frameSerializer.resetSer();
|
||||||
|
this->m_frameBuffer.setSize(sizeof(this->m_frameBufferStore));
|
||||||
|
if (this->m_held.get_data().isValid()) {
|
||||||
|
// Fill the held data
|
||||||
|
this->Svc_AggregationMachine_action_doFill(smId, signal, this->m_held);
|
||||||
|
this->m_held = Svc::ComDataContextPair();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComAggregator ::Svc_AggregationMachine_action_doFill(SmId smId,
|
||||||
|
Svc_AggregationMachine::Signal signal,
|
||||||
|
const Svc::ComDataContextPair& value) {
|
||||||
|
Fw::SerializeStatus status = this->m_frameSerializer.serializeFrom(
|
||||||
|
value.get_data().getData(), value.get_data().getSize(), Fw::Serialization::OMIT_LENGTH);
|
||||||
|
FW_ASSERT(status == Fw::SerializeStatus::FW_SERIALIZE_OK);
|
||||||
|
this->m_lastContext = value.get_context();
|
||||||
|
Fw::Success good = Fw::Success::SUCCESS;
|
||||||
|
// Return port does not alter data and thus const-cast is safe
|
||||||
|
this->dataReturnOut_out(0, const_cast<Fw::Buffer&>(value.get_data()), value.get_context());
|
||||||
|
this->comStatusOut_out(0, good);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComAggregator ::Svc_AggregationMachine_action_doSend(SmId smId, Svc_AggregationMachine::Signal signal) {
|
||||||
|
// Send only when the buffer will be valid
|
||||||
|
if (this->m_frameSerializer.getBuffLength() > 0) {
|
||||||
|
this->m_bufferState = Fw::Buffer::OwnershipState::NOT_OWNED;
|
||||||
|
this->m_frameBuffer.setSize(this->m_frameSerializer.getBuffLength());
|
||||||
|
this->dataOut_out(0, this->m_frameBuffer, this->m_lastContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComAggregator ::Svc_AggregationMachine_action_doHold(SmId smId,
|
||||||
|
Svc_AggregationMachine::Signal signal,
|
||||||
|
const Svc::ComDataContextPair& value) {
|
||||||
|
FW_ASSERT(not this->m_held.get_data().isValid());
|
||||||
|
this->m_held = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComAggregator ::Svc_AggregationMachine_action_assertNoStatus(SmId smId, Svc_AggregationMachine::Signal signal) {
|
||||||
|
// Status is not possible in this state, confirm by assertion
|
||||||
|
FW_ASSERT(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Implementations for internal state machine guards
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool ComAggregator ::Svc_AggregationMachine_guard_isFull(SmId smId,
|
||||||
|
Svc_AggregationMachine::Signal signal,
|
||||||
|
const Svc::ComDataContextPair& value) const {
|
||||||
|
FW_ASSERT(value.get_data().getSize() <= ComCfg::AggregationSize);
|
||||||
|
const FwSizeType remaining = this->m_frameSerializer.getBuffCapacity() - this->m_frameSerializer.getBuffLength();
|
||||||
|
return (remaining <= value.get_data().getSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComAggregator ::Svc_AggregationMachine_guard_isNotEmpty(SmId smId, Svc_AggregationMachine::Signal signal) const {
|
||||||
|
return this->m_frameSerializer.getBuffLength() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComAggregator ::Svc_AggregationMachine_guard_isGood(SmId smId,
|
||||||
|
Svc_AggregationMachine::Signal signal,
|
||||||
|
const Fw::Success& value) const {
|
||||||
|
return value == Fw::Success::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Svc
|
||||||
82
Svc/ComAggregator/ComAggregator.fpp
Normal file
82
Svc/ComAggregator/ComAggregator.fpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
module Svc {
|
||||||
|
@ Aggregation state machine
|
||||||
|
state machine AggregationMachine {
|
||||||
|
@ Initial state: WAIT_STATUS
|
||||||
|
@ Wait for initial 'status' to start the process
|
||||||
|
initial enter WAIT_STATUS
|
||||||
|
|
||||||
|
@ Rate-group driven timeout signal
|
||||||
|
signal timeout
|
||||||
|
|
||||||
|
@ Fill buffer signal
|
||||||
|
signal fill: Svc.ComDataContextPair
|
||||||
|
|
||||||
|
@ Status return
|
||||||
|
signal status: Fw.Success
|
||||||
|
|
||||||
|
@ Check if full
|
||||||
|
guard isFull: Svc.ComDataContextPair
|
||||||
|
|
||||||
|
@ Check if not empty
|
||||||
|
guard isNotEmpty
|
||||||
|
|
||||||
|
@ Check if last status is good
|
||||||
|
guard isGood: Fw.Success
|
||||||
|
|
||||||
|
@ Clear the buffer fill state, last status
|
||||||
|
action doClear
|
||||||
|
|
||||||
|
@ Fill the buffer with data
|
||||||
|
action doFill: Svc.ComDataContextPair
|
||||||
|
|
||||||
|
@ Send the buffer data
|
||||||
|
action doSend
|
||||||
|
|
||||||
|
@ Hold a buffer
|
||||||
|
action doHold: Svc.ComDataContextPair
|
||||||
|
|
||||||
|
|
||||||
|
@ Assert no status when in fill state
|
||||||
|
action assertNoStatus
|
||||||
|
|
||||||
|
@ The IS_FULL_THEN_SEND choice
|
||||||
|
choice IS_FULL_THEN_SEND {
|
||||||
|
if isFull do { doHold, doSend } enter WAIT_STATUS \
|
||||||
|
else do { doFill } enter FILL
|
||||||
|
}
|
||||||
|
|
||||||
|
@ The IS_GOOD_STATUS choice
|
||||||
|
choice IS_GOOD_STATUS {
|
||||||
|
if isGood do { doClear } enter FILL \
|
||||||
|
else enter WAIT_STATUS
|
||||||
|
}
|
||||||
|
|
||||||
|
@ Wait for com status from downstream
|
||||||
|
state WAIT_STATUS {
|
||||||
|
# ASSERT: fill cannot happen before initial 'status'
|
||||||
|
on fill do { doHold }
|
||||||
|
# IGNORE: 'timeout', this signal is irrelevant
|
||||||
|
# On status, move to IS_GOOD_STATUS choice
|
||||||
|
on status enter IS_GOOD_STATUS
|
||||||
|
}
|
||||||
|
|
||||||
|
@ Buffer aggregation in-progress
|
||||||
|
state FILL {
|
||||||
|
# Fill buffer, check if full then send or fill
|
||||||
|
on fill enter IS_FULL_THEN_SEND
|
||||||
|
# Timeout, send buffer
|
||||||
|
on timeout if isNotEmpty do { doSend } enter WAIT_STATUS
|
||||||
|
# ASSERT: status cannot happen while filling
|
||||||
|
on status do { assertNoStatus }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ Aggregates com buffers
|
||||||
|
active component ComAggregator {
|
||||||
|
import Svc.Framer
|
||||||
|
sync input port timeout: Svc.Sched
|
||||||
|
|
||||||
|
@ State machine instance for aggregation state machine
|
||||||
|
state machine instance aggregationMachine: AggregationMachine
|
||||||
|
}
|
||||||
|
}
|
||||||
146
Svc/ComAggregator/ComAggregator.hpp
Normal file
146
Svc/ComAggregator/ComAggregator.hpp
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
// ======================================================================
|
||||||
|
// \title ComAggregator.hpp
|
||||||
|
// \author lestarch
|
||||||
|
// \brief hpp file for ComAggregator component implementation class
|
||||||
|
// ======================================================================
|
||||||
|
|
||||||
|
#ifndef Svc_ComAggregator_HPP
|
||||||
|
#define Svc_ComAggregator_HPP
|
||||||
|
|
||||||
|
#include "Os/Mutex.hpp"
|
||||||
|
#include "Svc/ComAggregator/ComAggregatorComponentAc.hpp"
|
||||||
|
|
||||||
|
namespace Svc {
|
||||||
|
|
||||||
|
class ComAggregator final : public ComAggregatorComponentBase {
|
||||||
|
friend class ComAggregatorTester; // Allow unit test access to private members
|
||||||
|
public:
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Component construction and destruction
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
//! Construct ComAggregator object
|
||||||
|
ComAggregator(const char* const compName //!< The component name
|
||||||
|
);
|
||||||
|
|
||||||
|
//! Destroy ComAggregator object
|
||||||
|
~ComAggregator();
|
||||||
|
|
||||||
|
void preamble() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Handler implementations for typed input ports
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
//! Handler implementation for comStatusIn
|
||||||
|
//!
|
||||||
|
//! Port receiving the general status from the downstream component
|
||||||
|
//! indicating it is ready or not-ready for more input
|
||||||
|
void comStatusIn_handler(FwIndexType portNum, //!< The port number
|
||||||
|
Fw::Success& condition //!< Condition success/failure
|
||||||
|
) override;
|
||||||
|
|
||||||
|
//! Handler implementation for dataIn
|
||||||
|
//!
|
||||||
|
//! Port to receive data to frame, in a Fw::Buffer with optional context
|
||||||
|
void dataIn_handler(FwIndexType portNum, //!< The port number
|
||||||
|
Fw::Buffer& data,
|
||||||
|
const ComCfg::FrameContext& context) override;
|
||||||
|
|
||||||
|
//! Handler implementation for dataReturnIn
|
||||||
|
//!
|
||||||
|
//! Buffer coming from a deallocate call in a ComDriver component
|
||||||
|
void dataReturnIn_handler(FwIndexType portNum, //!< The port number
|
||||||
|
Fw::Buffer& data,
|
||||||
|
const ComCfg::FrameContext& context) override;
|
||||||
|
|
||||||
|
//! Handler implementation for timeout
|
||||||
|
void timeout_handler(FwIndexType portNum, //!< The port number
|
||||||
|
U32 context //!< The call order
|
||||||
|
) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Implementations for internal state machine actions
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
//! Implementation for action doClear of state machine Svc_AggregationMachine
|
||||||
|
//!
|
||||||
|
//! Clear the buffer fill state, last status
|
||||||
|
void Svc_AggregationMachine_action_doClear(SmId smId, //!< The state machine id
|
||||||
|
Svc_AggregationMachine::Signal signal //!< The signal
|
||||||
|
) override;
|
||||||
|
|
||||||
|
//! Implementation for action doFill of state machine Svc_AggregationMachine
|
||||||
|
//!
|
||||||
|
//! Fill the buffer with data
|
||||||
|
void Svc_AggregationMachine_action_doFill(SmId smId, //!< The state machine id
|
||||||
|
Svc_AggregationMachine::Signal signal, //!< The signal
|
||||||
|
const Svc::ComDataContextPair& value //!< The value
|
||||||
|
) override;
|
||||||
|
|
||||||
|
//! Implementation for action doSend of state machine Svc_AggregationMachine
|
||||||
|
//!
|
||||||
|
//! Send the buffer data
|
||||||
|
void Svc_AggregationMachine_action_doSend(SmId smId, //!< The state machine id
|
||||||
|
Svc_AggregationMachine::Signal signal //!< The signal
|
||||||
|
) override;
|
||||||
|
|
||||||
|
//! Implementation for action doHold of state machine Svc_AggregationMachine
|
||||||
|
//!
|
||||||
|
//! Hold a buffer
|
||||||
|
void Svc_AggregationMachine_action_doHold(SmId smId, //!< The state machine id
|
||||||
|
Svc_AggregationMachine::Signal signal, //!< The signal
|
||||||
|
const Svc::ComDataContextPair& value //!< The value
|
||||||
|
) override;
|
||||||
|
|
||||||
|
//! Implementation for action assertNoStatus of state machine Svc_AggregationMachine
|
||||||
|
//!
|
||||||
|
//! Assert no status when in fill state
|
||||||
|
void Svc_AggregationMachine_action_assertNoStatus(SmId smId, //!< The state machine id
|
||||||
|
Svc_AggregationMachine::Signal signal //!< The signal
|
||||||
|
) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Implementations for internal state machine guards
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
//! Implementation for guard isFull of state machine Svc_AggregationMachine
|
||||||
|
//!
|
||||||
|
//! Check if full
|
||||||
|
bool Svc_AggregationMachine_guard_isFull(SmId smId, //!< The state machine id
|
||||||
|
Svc_AggregationMachine::Signal signal, //!< The signal
|
||||||
|
const Svc::ComDataContextPair& value //!< The value
|
||||||
|
) const override;
|
||||||
|
|
||||||
|
//! Implementation for guard isNotEmpty of state machine Svc_AggregationMachine
|
||||||
|
//!
|
||||||
|
//! Check if not empty
|
||||||
|
bool Svc_AggregationMachine_guard_isNotEmpty(SmId smId, //!< The state machine id
|
||||||
|
Svc_AggregationMachine::Signal signal //!< The signal
|
||||||
|
) const override;
|
||||||
|
|
||||||
|
//! Implementation for guard isGood of state machine Svc_AggregationMachine
|
||||||
|
//!
|
||||||
|
//! Check if last status is good
|
||||||
|
bool Svc_AggregationMachine_guard_isGood(SmId smId, //!< The state machine id
|
||||||
|
Svc_AggregationMachine::Signal signal, //!< The signal
|
||||||
|
const Fw::Success& value //!< The value
|
||||||
|
) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
U8 m_frameBufferStore[ComCfg::AggregationSize]; //!< Buffer to hold the frame data
|
||||||
|
Fw::Buffer::OwnershipState m_bufferState =
|
||||||
|
Fw::Buffer::OwnershipState::OWNED; //!< whether m_frameBuffer is owned by TmFramer
|
||||||
|
Fw::Buffer m_frameBuffer;
|
||||||
|
Fw::ExternalSerializeBufferWithMemberCopy m_frameSerializer; //!< Serializer for m_frameBuffer
|
||||||
|
ComCfg::FrameContext m_lastContext; //!< Context for the current frame
|
||||||
|
|
||||||
|
Svc::ComDataContextPair m_held; //!< Held data while waiting for send
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Svc
|
||||||
|
|
||||||
|
#endif
|
||||||
1
Svc/ComAggregator/docs/img/diagram.svg
Normal file
1
Svc/ComAggregator/docs/img/diagram.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" id="fppDiagrams_0_root" class="sprotty-graph" style="border-block-end-color:rgb(204, 204, 204);border-block-start-color:rgb(204, 204, 204);border-bottom-color:rgb(204, 204, 204);border-inline-end-color:rgb(204, 204, 204);border-inline-start-color:rgb(204, 204, 204);border-left-color:rgb(204, 204, 204);border-right-color:rgb(204, 204, 204);border-top-color:rgb(204, 204, 204);caret-color:rgb(204, 204, 204);color:rgb(204, 204, 204);column-rule-color:rgb(204, 204, 204);flex-basis:0%;flex-grow:1;font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;font-size:13px;outline-color:rgb(204, 204, 204);perspective-origin:0px 75px;scrollbar-color:rgba(121, 121, 121, 0.4) rgb(31, 31, 31);text-decoration:none solid rgb(204, 204, 204);text-decoration-color:rgb(204, 204, 204);text-emphasis-color:rgb(204, 204, 204);transform-origin:0px 75px;-webkit-locale:"en";-webkit-text-fill-color:rgb(204, 204, 204);-webkit-text-stroke-color:rgb(204, 204, 204);" version="1.1" viewBox="12.000007629394531 12 284.5657653808594 90" width="284.5657653808594" height="90"><g transform="scale(1) translate(0,0)"><g id="fppDiagrams_0_uninstantiatedComponent" class="component" transform="translate(98.13125610351562, 12)"><rect rx="10" class="sprotty-node task component-active" width="102.19701385498047" height="90" style="fill:rgb(0, 120, 212);stroke:rgb(63, 185, 132);stroke-width:3px;"/><text class="node:component sprotty-label" id="fppDiagrams_0_uninstantiatedComponent.label.0" style="fill:rgb(204, 204, 204);"/><text class="node:component sprotty-label" id="fppDiagrams_0_uninstantiatedComponent.label.1" transform="translate(5, 37.799999713897705) translate(0, 12)" style="fill:rgb(204, 204, 204);">ComAggregator</text><g id="fppDiagrams_0_uninstantiatedComponent.dataIn.0" transform="translate(-10, 30)"><rect width="10" height="10" class="sprotty-port port-sync" style="fill:rgb(254, 146, 168);stroke:rgb(123, 51, 125);"/><text class="port sprotty-label" id="fppDiagrams_0_uninstantiatedComponent.dataIn.0.label.0" transform="translate(-37.13125228881836, -2.200000286102295) translate(0, 12)" style="fill:rgb(204, 204, 204);">dataIn</text></g><g id="fppDiagrams_0_uninstantiatedComponent.dataOut.0" transform="translate(102.19701385498047, 15)"><rect width="10" height="10" class="sprotty-port" style="fill:rgb(204, 204, 204);stroke:rgb(123, 51, 125);"/><text class="port sprotty-label" id="fppDiagrams_0_uninstantiatedComponent.dataOut.0.label.0" transform="translate(11, -2.200000286102295) translate(0, 12)" style="fill:rgb(204, 204, 204);">dataOut</text></g><g id="fppDiagrams_0_uninstantiatedComponent.dataReturnOut.0" transform="translate(102.19701385498047, 40)"><rect width="10" height="10" class="sprotty-port" style="fill:rgb(204, 204, 204);stroke:rgb(123, 51, 125);"/><text class="port sprotty-label" id="fppDiagrams_0_uninstantiatedComponent.dataReturnOut.0.label.0" transform="translate(11, -2.200000286102295) translate(0, 12)" style="fill:rgb(204, 204, 204);">dataReturnOut</text></g><g id="fppDiagrams_0_uninstantiatedComponent.dataReturnIn.0" transform="translate(-10, 50)"><rect width="10" height="10" class="sprotty-port port-sync" style="fill:rgb(254, 146, 168);stroke:rgb(123, 51, 125);"/><text class="port sprotty-label" id="fppDiagrams_0_uninstantiatedComponent.dataReturnIn.0.label.0" transform="translate(-76.13125610351562, -2.200000286102295) translate(0, 12)" style="fill:rgb(204, 204, 204);">dataReturnIn</text></g><g id="fppDiagrams_0_uninstantiatedComponent.comStatusIn.0" transform="translate(-10, 70)"><rect width="10" height="10" class="sprotty-port port-sync" style="fill:rgb(254, 146, 168);stroke:rgb(123, 51, 125);"/><text class="port sprotty-label" id="fppDiagrams_0_uninstantiatedComponent.comStatusIn.0.label.0" transform="translate(-73.22500610351562, -2.200000286102295) translate(0, 12)" style="fill:rgb(204, 204, 204);">comStatusIn</text></g><g id="fppDiagrams_0_uninstantiatedComponent.comStatusOut.0" transform="translate(102.19701385498047, 65)"><rect width="10" height="10" class="sprotty-port" style="fill:rgb(204, 204, 204);stroke:rgb(123, 51, 125);"/><text class="port sprotty-label" id="fppDiagrams_0_uninstantiatedComponent.comStatusOut.0.label.0" transform="translate(11, -2.200000286102295) translate(0, 12)" style="fill:rgb(204, 204, 204);">comStatusOut</text></g><g id="fppDiagrams_0_uninstantiatedComponent.timeout.0" transform="translate(-10, 10)"><rect width="10" height="10" class="sprotty-port port-sync" style="fill:rgb(254, 146, 168);stroke:rgb(123, 51, 125);"/><text class="port sprotty-label" id="fppDiagrams_0_uninstantiatedComponent.timeout.0.label.0" transform="translate(-43.618751525878906, -2.200000286102295) translate(0, 12)" style="fill:rgb(204, 204, 204);">timeout</text></g></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 4.7 KiB |
25
Svc/ComAggregator/docs/sdd.md
Normal file
25
Svc/ComAggregator/docs/sdd.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Svc::ComAggregator
|
||||||
|
|
||||||
|
Aggregates buffers in the downlink chain. This is for use with systems that have fixed size frames (e.g. CCSDS TM) that needed internal aggregation.
|
||||||
|
|
||||||
|
> [!CAUTION]
|
||||||
|
> `Svc::ComAggregator` does not preserve context.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
| ID | Description | Verification |
|
||||||
|
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ |
|
||||||
|
| Svc-ComAggregator-001 | ComAggregator shall accept incoming downlink data as `Fw::Buffer`, `ComCfg::FrameContext` pairs and append the buffer into the aggregate space permitting | Unit-Test |
|
||||||
|
| Svc-ComAggregator-002 | ComAggregator shall hold the incoming buffer when there is insufficient space in the aggregate buffer. | Unit-Test |
|
||||||
|
| Svc-ComAggregator-003 | ComAggregator shall send the current aggregate buffer when the incoming buffer is held due to overflow. | Unit-Test |
|
||||||
|
| Svc-ComAggregator-004 | ComAggregator shall send the current aggregate buffer when it receives a timeout trigger if and only if the aggregate is non-empty. | Unit-Test |
|
||||||
|
| Svc-ComAggregator-005 | ComAggregator shall clear aggregation state when a SUCCESS communication status is received back. | Unit-Test |
|
||||||
|
| Svc-ComAggregator-006 | ComAggregator shall preserve the order of received buffers when forming each aggregate and across aggregate sends. | Unit-Test |
|
||||||
|
| Svc-ComAggregator-007 | ComAggregator shall inter operate with the [Communication Adapter Interface comStatus protocol](../../../docs/reference/communication-adapter-interface.md) | Unit-Test |
|
||||||
|
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
`Svc.ComAggregator` implements `Svc.Framer`. Additionally, it has a `Svc.Sched` timeout port enabling timeout to be driven via a rate group.
|
||||||
69
Svc/ComAggregator/test/ut/ComAggregatorTestMain.cpp
Normal file
69
Svc/ComAggregator/test/ut/ComAggregatorTestMain.cpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// ======================================================================
|
||||||
|
// \title ComAggregatorTestMain.cpp
|
||||||
|
// \author lestarch
|
||||||
|
// \brief cpp file for ComAggregator component test main function
|
||||||
|
// ======================================================================
|
||||||
|
|
||||||
|
#include "ComAggregatorTester.hpp"
|
||||||
|
|
||||||
|
TEST(Nominal, Initial) {
|
||||||
|
Svc::ComAggregatorTester tester;
|
||||||
|
tester.test_initial();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Nominal, Fill) {
|
||||||
|
Svc::ComAggregatorTester tester;
|
||||||
|
tester.test_initial();
|
||||||
|
tester.test_fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Nominal, MultiFill) {
|
||||||
|
Svc::ComAggregatorTester tester;
|
||||||
|
tester.test_initial();
|
||||||
|
tester.test_fill_multi();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Nominal, Full) {
|
||||||
|
Svc::ComAggregatorTester tester;
|
||||||
|
tester.test_initial();
|
||||||
|
tester.test_fill_multi();
|
||||||
|
tester.test_full();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Nominal, Timeout) {
|
||||||
|
Svc::ComAggregatorTester tester;
|
||||||
|
tester.test_initial();
|
||||||
|
tester.test_fill_multi();
|
||||||
|
tester.test_timeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OffNominal, TimeoutEmpty) {
|
||||||
|
Svc::ComAggregatorTester tester;
|
||||||
|
tester.test_initial();
|
||||||
|
tester.test_timeout_zero();
|
||||||
|
tester.test_fill_multi();
|
||||||
|
tester.test_full();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Nominal, HoldWhileWaiting) {
|
||||||
|
Svc::ComAggregatorTester tester;
|
||||||
|
tester.test_initial();
|
||||||
|
tester.test_fill_multi();
|
||||||
|
tester.test_hold_while_waiting();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Nominal, Clear) {
|
||||||
|
Svc::ComAggregatorTester tester;
|
||||||
|
tester.test_initial();
|
||||||
|
tester.test_fill_multi();
|
||||||
|
tester.test_full();
|
||||||
|
tester.test_fill_multi();
|
||||||
|
tester.test_timeout();
|
||||||
|
tester.test_fill_multi();
|
||||||
|
tester.test_full();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
||||||
225
Svc/ComAggregator/test/ut/ComAggregatorTester.cpp
Normal file
225
Svc/ComAggregator/test/ut/ComAggregatorTester.cpp
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
// ======================================================================
|
||||||
|
// \title ComAggregatorTester.cpp
|
||||||
|
// \author lestarch
|
||||||
|
// \brief cpp file for ComAggregator component test harness implementation class
|
||||||
|
// ======================================================================
|
||||||
|
|
||||||
|
#include "ComAggregatorTester.hpp"
|
||||||
|
#include <STest/Pick/Pick.hpp>
|
||||||
|
#include <vector>
|
||||||
|
#include "config/FppConstantsAc.hpp"
|
||||||
|
|
||||||
|
namespace Svc {
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Construction and destruction
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
ComAggregatorTester ::ComAggregatorTester()
|
||||||
|
: ComAggregatorGTestBase("ComAggregatorTester", ComAggregatorTester::MAX_HISTORY_SIZE), component("ComAggregator") {
|
||||||
|
this->initComponents();
|
||||||
|
this->connectPorts();
|
||||||
|
}
|
||||||
|
|
||||||
|
ComAggregatorTester ::~ComAggregatorTester() {}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Tests
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Fw::Buffer ComAggregatorTester ::fill_buffer(U32 size) {
|
||||||
|
EXPECT_GT(size, 0);
|
||||||
|
U8* data = new U8[size];
|
||||||
|
for (U32 i = 0; i < size; i++) {
|
||||||
|
data[i] = static_cast<U8>(STest::Pick::lowerUpper(0, 255));
|
||||||
|
}
|
||||||
|
Fw::Buffer buffer(data, size);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Shadow aggregate a buffer for validation
|
||||||
|
void ComAggregatorTester ::shadow_aggregate(const Fw::Buffer& buffer) {
|
||||||
|
for (FwSizeType i = 0; i < buffer.getSize(); i++) {
|
||||||
|
this->m_aggregation.push_back(buffer.getData()[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Validate against shadow aggregation
|
||||||
|
void ComAggregatorTester ::validate_aggregation(const Fw::Buffer& buffer) {
|
||||||
|
ASSERT_EQ(buffer.getSize(), this->m_aggregation.size());
|
||||||
|
for (FwSizeType i = 0; i < this->m_aggregation.size(); i++) {
|
||||||
|
ASSERT_EQ(buffer.getData()[i], this->m_aggregation[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComAggregatorTester ::validate_buffer_aggregated(const Fw::Buffer& buffer, const ComCfg::FrameContext& context) {
|
||||||
|
FwSizeType start = this->component.m_frameSerializer.getBuffLength() - buffer.getSize();
|
||||||
|
for (FwSizeType i = 0; i < buffer.getSize(); i++) {
|
||||||
|
ASSERT_EQ(buffer.getData()[i], this->component.m_frameBuffer.getData()[start + i]);
|
||||||
|
}
|
||||||
|
ASSERT_EQ(context, this->component.m_lastContext);
|
||||||
|
this->shadow_aggregate(buffer);
|
||||||
|
delete[] buffer.getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComAggregatorTester ::test_initial() {
|
||||||
|
// Initial state should have empty buffer
|
||||||
|
ASSERT_EQ(this->component.m_frameSerializer.getBuffLength(), 0);
|
||||||
|
ASSERT_EQ(this->component.m_bufferState, Fw::Buffer::OwnershipState::OWNED);
|
||||||
|
this->component.preamble();
|
||||||
|
ASSERT_from_comStatusOut(0, Fw::Success::SUCCESS);
|
||||||
|
Fw::Success good = Fw::Success::SUCCESS;
|
||||||
|
this->invoke_to_comStatusIn(0, good);
|
||||||
|
ASSERT_EQ(this->dispatchOne(this->component),
|
||||||
|
Svc::ComAggregatorComponentBase::MsgDispatchStatus::MSG_DISPATCH_OK); // Dispatch the state machine
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Tests fill operation
|
||||||
|
Fw::Buffer ComAggregatorTester ::test_fill(bool expect_hold) {
|
||||||
|
// Precondition: initial has run
|
||||||
|
const FwSizeType ORIGINAL_LENGTH = this->component.m_frameSerializer.getBuffLength();
|
||||||
|
if (ORIGINAL_LENGTH == ComCfg::AggregationSize) {
|
||||||
|
// Nothing to fill
|
||||||
|
return Fw::Buffer();
|
||||||
|
}
|
||||||
|
const U32 BUFFER_LENGTH = STest::Pick::lowerUpper(1, static_cast<U32>(ComCfg::AggregationSize - ORIGINAL_LENGTH));
|
||||||
|
Fw::Buffer buffer = fill_buffer(BUFFER_LENGTH);
|
||||||
|
ComCfg::FrameContext context;
|
||||||
|
|
||||||
|
this->invoke_to_dataIn(0, buffer, context);
|
||||||
|
EXPECT_EQ(this->dispatchOne(this->component),
|
||||||
|
Svc::ComAggregatorComponentBase::MsgDispatchStatus::MSG_DISPATCH_OK); // Dispatch the state machine
|
||||||
|
if (expect_hold) {
|
||||||
|
EXPECT_EQ(ORIGINAL_LENGTH, this->component.m_frameSerializer.getBuffLength());
|
||||||
|
} else {
|
||||||
|
EXPECT_EQ(ORIGINAL_LENGTH + BUFFER_LENGTH, this->component.m_frameSerializer.getBuffLength());
|
||||||
|
this->validate_buffer_aggregated(buffer, context);
|
||||||
|
}
|
||||||
|
this->clearHistory();
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComAggregatorTester ::test_fill_multi() {
|
||||||
|
U32 count = STest::Pick::lowerUpper(1, 5);
|
||||||
|
for (U32 i = 0; i < count; i++) {
|
||||||
|
(void)this->test_fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Tests full operation
|
||||||
|
void ComAggregatorTester ::test_full() {
|
||||||
|
// Precondition: fill has run
|
||||||
|
// Chose a buffer that will be too large to fit but still will fit after being aggregated
|
||||||
|
const FwSizeType ORIGINAL_LENGTH = this->component.m_frameSerializer.getBuffLength();
|
||||||
|
const U32 BUFFER_LENGTH = STest::Pick::lowerUpper(static_cast<U32>(ComCfg::AggregationSize - ORIGINAL_LENGTH + 1),
|
||||||
|
static_cast<U32>(ComCfg::AggregationSize));
|
||||||
|
Fw::Buffer buffer = fill_buffer(BUFFER_LENGTH);
|
||||||
|
ComCfg::FrameContext context;
|
||||||
|
|
||||||
|
// Send the overflow buffer and ensure the current aggregation comes out
|
||||||
|
this->invoke_to_dataIn(0, buffer, context);
|
||||||
|
ASSERT_EQ(this->dispatchOne(this->component),
|
||||||
|
Svc::ComAggregatorComponentBase::MsgDispatchStatus::MSG_DISPATCH_OK); // Dispatch the state machine
|
||||||
|
ASSERT_from_dataOut_SIZE(1);
|
||||||
|
this->validate_aggregation(this->fromPortHistory_dataOut->at(0).data);
|
||||||
|
|
||||||
|
// Invoke some number of failures
|
||||||
|
for (U32 i = 0; i < STest::Pick::lowerUpper(1, 5); i++) {
|
||||||
|
Fw::Success bad = Fw::Success::FAILURE;
|
||||||
|
this->invoke_to_comStatusIn(0, bad);
|
||||||
|
ASSERT_EQ(this->dispatchOne(this->component),
|
||||||
|
Svc::ComAggregatorComponentBase::MsgDispatchStatus::MSG_DISPATCH_OK); // Dispatch the state machine
|
||||||
|
// Should be no change
|
||||||
|
this->validate_aggregation(this->component.m_frameBuffer);
|
||||||
|
ASSERT_from_dataOut_SIZE(1);
|
||||||
|
}
|
||||||
|
// Const cast is safe as data is not altered
|
||||||
|
this->invoke_to_dataReturnIn(0, const_cast<Fw::Buffer&>(this->fromPortHistory_dataOut->at(0).data),
|
||||||
|
this->fromPortHistory_dataOut->at(0).context);
|
||||||
|
Fw::Success good = Fw::Success::SUCCESS;
|
||||||
|
this->invoke_to_comStatusIn(0, good);
|
||||||
|
this->m_aggregation.clear();
|
||||||
|
ASSERT_EQ(this->dispatchOne(this->component),
|
||||||
|
Svc::ComAggregatorComponentBase::MsgDispatchStatus::MSG_DISPATCH_OK); // Dispatch the state machine
|
||||||
|
// Validate that the new buffer has been aggregated
|
||||||
|
this->validate_buffer_aggregated(buffer, context);
|
||||||
|
this->clearHistory();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Tests timeout operation
|
||||||
|
void ComAggregatorTester ::test_timeout() {
|
||||||
|
// Precondition: fill has run
|
||||||
|
this->invoke_to_timeout(0, 0);
|
||||||
|
ASSERT_EQ(this->dispatchOne(this->component),
|
||||||
|
Svc::ComAggregatorComponentBase::MsgDispatchStatus::MSG_DISPATCH_OK); // Dispatch the state machine
|
||||||
|
ASSERT_from_dataOut_SIZE(1);
|
||||||
|
this->validate_aggregation(this->fromPortHistory_dataOut->at(0).data);
|
||||||
|
|
||||||
|
// Invoke some number of failures
|
||||||
|
for (U32 i = 0; i < STest::Pick::lowerUpper(1, 5); i++) {
|
||||||
|
Fw::Success bad = Fw::Success::FAILURE;
|
||||||
|
this->invoke_to_comStatusIn(0, bad);
|
||||||
|
ASSERT_EQ(this->dispatchOne(this->component),
|
||||||
|
Svc::ComAggregatorComponentBase::MsgDispatchStatus::MSG_DISPATCH_OK); // Dispatch the state machine
|
||||||
|
// Should be no change
|
||||||
|
this->validate_aggregation(this->component.m_frameBuffer);
|
||||||
|
ASSERT_from_dataOut_SIZE(1);
|
||||||
|
}
|
||||||
|
// Const cast is safe as data is not altered
|
||||||
|
this->invoke_to_dataReturnIn(0, const_cast<Fw::Buffer&>(this->fromPortHistory_dataOut->at(0).data),
|
||||||
|
this->fromPortHistory_dataOut->at(0).context);
|
||||||
|
Fw::Success good = Fw::Success::SUCCESS;
|
||||||
|
this->invoke_to_comStatusIn(0, good);
|
||||||
|
this->m_aggregation.clear();
|
||||||
|
ASSERT_EQ(this->dispatchOne(this->component),
|
||||||
|
Svc::ComAggregatorComponentBase::MsgDispatchStatus::MSG_DISPATCH_OK); // Dispatch the state machine
|
||||||
|
this->clearHistory();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComAggregatorTester ::test_timeout_zero() {
|
||||||
|
// Precondition: initialize has run
|
||||||
|
this->invoke_to_timeout(0, 0);
|
||||||
|
ASSERT_EQ(this->dispatchOne(this->component),
|
||||||
|
Svc::ComAggregatorComponentBase::MsgDispatchStatus::MSG_DISPATCH_OK); // Dispatch the state machine
|
||||||
|
ASSERT_from_dataOut_SIZE(0);
|
||||||
|
this->clearHistory();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Tests hold while waiting on data return
|
||||||
|
void ComAggregatorTester ::test_hold_while_waiting() {
|
||||||
|
// Precondition: fill has run
|
||||||
|
ComCfg::FrameContext context;
|
||||||
|
this->invoke_to_timeout(0, 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(this->dispatchOne(this->component),
|
||||||
|
Svc::ComAggregatorComponentBase::MsgDispatchStatus::MSG_DISPATCH_OK); // Dispatch the state machine
|
||||||
|
ASSERT_from_dataOut_SIZE(1);
|
||||||
|
this->validate_aggregation(this->fromPortHistory_dataOut->at(0).data);
|
||||||
|
Fw::Buffer major_buffer = this->fromPortHistory_dataOut->at(0).data;
|
||||||
|
|
||||||
|
// Invoke some number of failures
|
||||||
|
for (U32 i = 0; i < STest::Pick::lowerUpper(1, 5); i++) {
|
||||||
|
Fw::Success bad = Fw::Success::FAILURE;
|
||||||
|
this->invoke_to_comStatusIn(0, bad);
|
||||||
|
ASSERT_EQ(this->dispatchOne(this->component),
|
||||||
|
Svc::ComAggregatorComponentBase::MsgDispatchStatus::MSG_DISPATCH_OK); // Dispatch the state machine
|
||||||
|
// Should be no change
|
||||||
|
this->validate_aggregation(this->component.m_frameBuffer);
|
||||||
|
ASSERT_from_dataOut_SIZE(1);
|
||||||
|
}
|
||||||
|
// Force a hold
|
||||||
|
Fw::Buffer minor_buffer = this->test_fill(true);
|
||||||
|
|
||||||
|
// Const cast is safe as data is not altered
|
||||||
|
this->invoke_to_dataReturnIn(0, major_buffer, context);
|
||||||
|
Fw::Success good = Fw::Success::SUCCESS;
|
||||||
|
this->invoke_to_comStatusIn(0, good);
|
||||||
|
this->m_aggregation.clear();
|
||||||
|
ASSERT_EQ(this->dispatchOne(this->component),
|
||||||
|
Svc::ComAggregatorComponentBase::MsgDispatchStatus::MSG_DISPATCH_OK); // Dispatch the state machine
|
||||||
|
// Validate that the new buffer has been aggregated
|
||||||
|
this->validate_buffer_aggregated(minor_buffer, context);
|
||||||
|
this->clearHistory();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Svc
|
||||||
110
Svc/ComAggregator/test/ut/ComAggregatorTester.hpp
Normal file
110
Svc/ComAggregator/test/ut/ComAggregatorTester.hpp
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
// ======================================================================
|
||||||
|
// \title ComAggregatorTester.hpp
|
||||||
|
// \author lestarch
|
||||||
|
// \brief hpp file for ComAggregator component test harness implementation class
|
||||||
|
// ======================================================================
|
||||||
|
|
||||||
|
#ifndef Svc_ComAggregatorTester_HPP
|
||||||
|
#define Svc_ComAggregatorTester_HPP
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "Svc/ComAggregator/ComAggregator.hpp"
|
||||||
|
#include "Svc/ComAggregator/ComAggregatorGTestBase.hpp"
|
||||||
|
|
||||||
|
namespace Svc {
|
||||||
|
|
||||||
|
class ComAggregatorTester final : public ComAggregatorGTestBase {
|
||||||
|
public:
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Constants
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Maximum size of histories storing events, telemetry, and port outputs
|
||||||
|
static const FwSizeType MAX_HISTORY_SIZE = 20;
|
||||||
|
|
||||||
|
// Instance ID supplied to the component instance under test
|
||||||
|
static const FwEnumStoreType TEST_INSTANCE_ID = 0;
|
||||||
|
|
||||||
|
// Queue depth supplied to the component instance under test
|
||||||
|
static const FwSizeType TEST_INSTANCE_QUEUE_DEPTH = 20;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Construction and destruction
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
//! Construct object ComAggregatorTester
|
||||||
|
ComAggregatorTester();
|
||||||
|
|
||||||
|
//! Destroy object ComAggregatorTester
|
||||||
|
~ComAggregatorTester();
|
||||||
|
|
||||||
|
public:
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Tests
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
//! Tests initial operation
|
||||||
|
void test_initial();
|
||||||
|
|
||||||
|
//! Tests fill operation
|
||||||
|
Fw::Buffer test_fill(bool expect_hold = false);
|
||||||
|
|
||||||
|
//! Tests fill operation
|
||||||
|
void test_fill_multi();
|
||||||
|
|
||||||
|
//! Tests full operation
|
||||||
|
void test_full();
|
||||||
|
|
||||||
|
//! Tests timeout operation
|
||||||
|
void test_timeout();
|
||||||
|
|
||||||
|
//! Tests timeout operation sends no empty buffer
|
||||||
|
void test_timeout_zero();
|
||||||
|
|
||||||
|
//! Tests hold while waiting on data return
|
||||||
|
void test_hold_while_waiting();
|
||||||
|
|
||||||
|
//! Tests clear operation
|
||||||
|
void test_clear();
|
||||||
|
|
||||||
|
//! Tests clear operation with held data
|
||||||
|
void test_clear_with_hold();
|
||||||
|
|
||||||
|
//! Helper to fill a buffer with random data
|
||||||
|
Fw::Buffer fill_buffer(U32 size);
|
||||||
|
|
||||||
|
//! Shadow aggregate a buffer for validation
|
||||||
|
void shadow_aggregate(const Fw::Buffer& buffer);
|
||||||
|
|
||||||
|
//! Validate against shadow aggregation
|
||||||
|
void validate_aggregation(const Fw::Buffer& buffer);
|
||||||
|
|
||||||
|
//! Helper to validate a buffer has been aggregated correctly
|
||||||
|
void validate_buffer_aggregated(const Fw::Buffer& buffer, const ComCfg::FrameContext& context);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Helper functions
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
//! Connect ports
|
||||||
|
void connectPorts();
|
||||||
|
|
||||||
|
//! Initialize components
|
||||||
|
void initComponents();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Member variables
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
//! The component under test
|
||||||
|
ComAggregator component;
|
||||||
|
//! Shadow aggregation for validation
|
||||||
|
std::vector<U8> m_aggregation;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Svc
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -6,6 +6,12 @@
|
|||||||
#####
|
#####
|
||||||
|
|
||||||
module Svc {
|
module Svc {
|
||||||
|
@ Struct representing a communications data buffer along with context information
|
||||||
|
@ for use storing the inputs ComDataWithContext port
|
||||||
|
struct ComDataContextPair {
|
||||||
|
data: Fw.Buffer
|
||||||
|
context: ComCfg.FrameContext
|
||||||
|
}
|
||||||
|
|
||||||
@ Port for sending communications data (frames) buffer along with context information
|
@ Port for sending communications data (frames) buffer along with context information
|
||||||
port ComDataWithContext(ref data: Fw.Buffer, context: ComCfg.FrameContext)
|
port ComDataWithContext(ref data: Fw.Buffer, context: ComCfg.FrameContext)
|
||||||
|
|||||||
@ -95,14 +95,19 @@ module ComCcsds {
|
|||||||
instance tcDeframer: Svc.Ccsds.TcDeframer base id ComCcsdsConfig.BASE_ID + 0x04000
|
instance tcDeframer: Svc.Ccsds.TcDeframer base id ComCcsdsConfig.BASE_ID + 0x04000
|
||||||
|
|
||||||
instance spacePacketDeframer: Svc.Ccsds.SpacePacketDeframer base id ComCcsdsConfig.BASE_ID + 0x05000
|
instance spacePacketDeframer: Svc.Ccsds.SpacePacketDeframer base id ComCcsdsConfig.BASE_ID + 0x05000
|
||||||
|
|
||||||
|
instance aggregator: Svc.ComAggregator base id ComCcsdsConfig.BASE_ID + 0x06000 \
|
||||||
|
queue size ComCcsdsConfig.QueueSizes.aggregator \
|
||||||
|
stack size ComCcsdsConfig.StackSizes.aggregator
|
||||||
|
|
||||||
# NOTE: name 'framer' is used for the framer that connects to the Com Adapter Interface for better subtopology interoperability
|
# NOTE: name 'framer' is used for the framer that connects to the Com Adapter Interface for better subtopology interoperability
|
||||||
instance framer: Svc.Ccsds.TmFramer base id ComCcsdsConfig.BASE_ID + 0x06000
|
instance framer: Svc.Ccsds.TmFramer base id ComCcsdsConfig.BASE_ID + 0x07000
|
||||||
|
|
||||||
instance spacePacketFramer: Svc.Ccsds.SpacePacketFramer base id ComCcsdsConfig.BASE_ID + 0x07000
|
instance spacePacketFramer: Svc.Ccsds.SpacePacketFramer base id ComCcsdsConfig.BASE_ID + 0x08000
|
||||||
|
|
||||||
instance apidManager: Svc.Ccsds.ApidManager base id ComCcsdsConfig.BASE_ID + 0x08000
|
instance apidManager: Svc.Ccsds.ApidManager base id ComCcsdsConfig.BASE_ID + 0x09000
|
||||||
|
|
||||||
instance comStub: Svc.ComStub base id ComCcsdsConfig.BASE_ID + 0x09000
|
instance comStub: Svc.ComStub base id ComCcsdsConfig.BASE_ID + 0x0A000
|
||||||
|
|
||||||
topology FramingSubtopology {
|
topology FramingSubtopology {
|
||||||
# Usage Note:
|
# Usage Note:
|
||||||
@ -131,6 +136,7 @@ module ComCcsds {
|
|||||||
instance framer
|
instance framer
|
||||||
instance spacePacketFramer
|
instance spacePacketFramer
|
||||||
instance apidManager
|
instance apidManager
|
||||||
|
instance aggregator
|
||||||
|
|
||||||
connections Downlink {
|
connections Downlink {
|
||||||
# ComQueue <-> SpacePacketFramer
|
# ComQueue <-> SpacePacketFramer
|
||||||
@ -141,10 +147,15 @@ module ComCcsds {
|
|||||||
spacePacketFramer.bufferDeallocate -> commsBufferManager.bufferSendIn
|
spacePacketFramer.bufferDeallocate -> commsBufferManager.bufferSendIn
|
||||||
spacePacketFramer.getApidSeqCount -> apidManager.getApidSeqCountIn
|
spacePacketFramer.getApidSeqCount -> apidManager.getApidSeqCountIn
|
||||||
# SpacePacketFramer <-> TmFramer
|
# SpacePacketFramer <-> TmFramer
|
||||||
spacePacketFramer.dataOut -> framer.dataIn
|
spacePacketFramer.dataOut -> aggregator.dataIn
|
||||||
framer.dataReturnOut -> spacePacketFramer.dataReturnIn
|
aggregator.dataOut -> framer.dataIn
|
||||||
|
|
||||||
|
framer.dataReturnOut -> aggregator.dataReturnIn
|
||||||
|
aggregator.dataReturnOut -> spacePacketFramer.dataReturnIn
|
||||||
|
|
||||||
# ComStatus
|
# ComStatus
|
||||||
framer.comStatusOut -> spacePacketFramer.comStatusIn
|
framer.comStatusOut -> aggregator.comStatusIn
|
||||||
|
aggregator.comStatusOut -> spacePacketFramer.comStatusIn
|
||||||
spacePacketFramer.comStatusOut -> comQueue.comStatusIn
|
spacePacketFramer.comStatusOut -> comQueue.comStatusIn
|
||||||
# (Outgoing) Framer <-> ComInterface connections shall be established by the user
|
# (Outgoing) Framer <-> ComInterface connections shall be established by the user
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,19 +4,22 @@ module ComCcsdsConfig {
|
|||||||
|
|
||||||
module QueueSizes {
|
module QueueSizes {
|
||||||
constant comQueue = 50
|
constant comQueue = 50
|
||||||
|
constant aggregator = 10
|
||||||
}
|
}
|
||||||
|
|
||||||
module StackSizes {
|
module StackSizes {
|
||||||
constant comQueue = 64 * 1024
|
constant comQueue = 64 * 1024
|
||||||
|
constant aggregator = 64 * 1024
|
||||||
}
|
}
|
||||||
|
|
||||||
module Priorities {
|
module Priorities {
|
||||||
constant comQueue = 101
|
constant comQueue = 101
|
||||||
|
constant aggregator = 100
|
||||||
}
|
}
|
||||||
|
|
||||||
# Queue configuration constants
|
# Queue configuration constants
|
||||||
module QueueDepths {
|
module QueueDepths {
|
||||||
constant events = 100
|
constant events = 200
|
||||||
constant tlm = 500
|
constant tlm = 500
|
||||||
constant file = 100
|
constant file = 100
|
||||||
}
|
}
|
||||||
|
|||||||
@ -108,7 +108,7 @@ function integration_test_run {
|
|||||||
TIMEOUT="gtimeout" # macOS homebrew "coreutils"
|
TIMEOUT="gtimeout" # macOS homebrew "coreutils"
|
||||||
fi
|
fi
|
||||||
TOP_CONFIG_ARGS="--deployment-config ${WORKDIR}/test/int/int_config.json"
|
TOP_CONFIG_ARGS="--deployment-config ${WORKDIR}/test/int/int_config.json"
|
||||||
${TIMEOUT} --kill-after=10s 180s pytest ${DICTIONARY_ARGS} ${TOP_CONFIG_ARGS}
|
${TIMEOUT} --kill-after=10s 300s pytest ${DICTIONARY_ARGS} ${TOP_CONFIG_ARGS}
|
||||||
)
|
)
|
||||||
RET_PYTEST=$?
|
RET_PYTEST=$?
|
||||||
pkill -P $GDS_PID
|
pkill -P $GDS_PID
|
||||||
|
|||||||
@ -17,6 +17,7 @@ module ComCfg {
|
|||||||
# - potentially APID enum ?
|
# - potentially APID enum ?
|
||||||
constant SpacecraftId = 0x0044 # Spacecraft ID (10 bits)
|
constant SpacecraftId = 0x0044 # Spacecraft ID (10 bits)
|
||||||
constant TmFrameFixedSize = 1024 # Needs to be at least COM_BUFFER_MAX_SIZE + (2 * SpacePacketHeaderSize) + 1
|
constant TmFrameFixedSize = 1024 # Needs to be at least COM_BUFFER_MAX_SIZE + (2 * SpacePacketHeaderSize) + 1
|
||||||
|
constant AggregationSize = TmFrameFixedSize - 6 - 6 - 1 - 2 # 2 header (6) + 1 idle byte + 2 trailer bytes
|
||||||
|
|
||||||
@ APIDs are 11 bits in the Space Packet protocol, so we use U16. Max value 7FF
|
@ APIDs are 11 bits in the Space Packet protocol, so we use U16. Max value 7FF
|
||||||
enum Apid : FwPacketDescriptorType {
|
enum Apid : FwPacketDescriptorType {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user