mirror of
https://github.com/nasa/fprime.git
synced 2025-12-10 00:44:37 -06:00
Data product framework support (#2485)
* Pull in framework changes from data-products * Pull in framework changes from data-products * Pull in updates from data-products branch * Update fpp version * Fix spelling * Update spell check * Add log archiving to FppTest CI * Update fpp version * Revise DpContainer Remove unused code * Revise data products design * Revise Fw/Dp Fix mistake in built-in Python autocoder types * Revise dp container members Add _m prefix * Update fpp version * Update fpp version * Update fpp version * Revise DpContainer and unit tests Don't fail an assertion on bad serial input * Pull in changes from data-products branch * Revise DpContainer Revise handling of serialize status --------- Co-authored-by: thomas-bc <thomas.boyerchammard@gmail.com>
This commit is contained in:
parent
75b30ad8bb
commit
c6f8e7bbba
5
.github/actions/spelling/expect.txt
vendored
5
.github/actions/spelling/expect.txt
vendored
@ -217,6 +217,7 @@ dawbarton
|
||||
DDDTHH
|
||||
ddl
|
||||
ddmm
|
||||
dealloc
|
||||
Debian
|
||||
deconstructor
|
||||
Deerin
|
||||
@ -265,6 +266,8 @@ doxyrules
|
||||
doxysearch
|
||||
Doxywizard
|
||||
dpi
|
||||
DPMANAGER
|
||||
DPWRITER
|
||||
DRAINBUFFERS
|
||||
drv
|
||||
dsdl
|
||||
@ -285,6 +288,7 @@ EGB
|
||||
EHAs
|
||||
elist
|
||||
ELOG
|
||||
Elts
|
||||
emoji
|
||||
endcode
|
||||
endcond
|
||||
@ -389,6 +393,7 @@ getquaternion
|
||||
gettime
|
||||
gettimeofday
|
||||
getty
|
||||
getu
|
||||
ghprb
|
||||
gitmodules
|
||||
gmock
|
||||
|
||||
7
.github/workflows/fpp-tests.yml
vendored
7
.github/workflows/fpp-tests.yml
vendored
@ -41,3 +41,10 @@ jobs:
|
||||
run: |
|
||||
fprime-util check
|
||||
shell: bash
|
||||
- name: "Archive Logs"
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: FppTest-Logs
|
||||
path: ./FppTest/build-fprime-automatic-native-ut/Testing/Temporary/*.log
|
||||
retention-days: 5
|
||||
|
||||
@ -178,6 +178,8 @@ class PortHVisitor(AbstractVisitor.AbstractVisitor):
|
||||
"bool",
|
||||
"FwBuffSizeType",
|
||||
"FwChanIdType",
|
||||
"FwDpIdType",
|
||||
"FwDpPriorityType",
|
||||
"FwEnumStoreType",
|
||||
"FwEventIdType",
|
||||
"FwIndexType",
|
||||
|
||||
@ -18,6 +18,8 @@ types_list = [
|
||||
port_types_list = [
|
||||
"FwBuffSizeType",
|
||||
"FwChanIdType",
|
||||
"FwDpIdType",
|
||||
"FwDpPriorityType",
|
||||
"FwEnumStoreType",
|
||||
"FwEventIdType",
|
||||
"FwIndexType",
|
||||
|
||||
@ -11,17 +11,16 @@ project(FppTest C CXX)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/FPrime.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/FPrime-Code.cmake")
|
||||
|
||||
if (BUILD_TESTING AND NOT __FPRIME_NO_UT_GEN__)
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/enum/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/array/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/struct/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/component/")
|
||||
endif()
|
||||
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/array/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/component/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/dp/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/enum/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/struct/")
|
||||
set(SOURCE_FILES "source.cpp")
|
||||
set(MOD_DEPS
|
||||
${PROJECT_NAME}/enum
|
||||
${PROJECT_NAME}/array
|
||||
${PROJECT_NAME}/dp
|
||||
${PROJECT_NAME}/enum
|
||||
${PROJECT_NAME}/struct
|
||||
${PROJECT_NAME}/component/empty
|
||||
${PROJECT_NAME}/component/active
|
||||
|
||||
15
FppTest/dp/CMakeLists.txt
Normal file
15
FppTest/dp/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
set(SOURCE_FILES
|
||||
"${CMAKE_CURRENT_LIST_DIR}/DpTest.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/DpTest.fpp"
|
||||
)
|
||||
|
||||
register_fprime_module()
|
||||
|
||||
set(UT_SOURCE_FILES
|
||||
"${CMAKE_CURRENT_LIST_DIR}/DpTest.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/test/ut/TestMain.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/test/ut/Tester.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/test/ut/TesterHelpers.cpp"
|
||||
)
|
||||
set(UT_MOD_DEPS STest)
|
||||
register_fprime_ut()
|
||||
188
FppTest/dp/DpTest.cpp
Normal file
188
FppTest/dp/DpTest.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
// ======================================================================
|
||||
// \title DpTest.cpp
|
||||
// \author bocchino
|
||||
// \brief cpp file for DpTest component implementation class
|
||||
// ======================================================================
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "FppTest/dp/DpTest.hpp"
|
||||
#include "Fw/Types/Assert.hpp"
|
||||
|
||||
namespace FppTest {
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction, initialization, and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
DpTest ::DpTest(const char* const compName,
|
||||
U32 u32RecordData,
|
||||
U16 dataRecordData,
|
||||
const U8ArrayRecordData& u8ArrayRecordData,
|
||||
const U32ArrayRecordData& u32ArrayRecordData,
|
||||
const DataArrayRecordData& dataArrayRecordData)
|
||||
: DpTestComponentBase(compName),
|
||||
u32RecordData(u32RecordData),
|
||||
dataRecordData(dataRecordData),
|
||||
u8ArrayRecordData(u8ArrayRecordData),
|
||||
u32ArrayRecordData(u32ArrayRecordData),
|
||||
dataArrayRecordData(dataArrayRecordData),
|
||||
sendTime(Fw::ZERO_TIME) {}
|
||||
|
||||
void DpTest ::init(const NATIVE_INT_TYPE queueDepth, const NATIVE_INT_TYPE instance) {
|
||||
DpTestComponentBase::init(queueDepth, instance);
|
||||
}
|
||||
|
||||
DpTest ::~DpTest() {}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for user-defined typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void DpTest::schedIn_handler(const NATIVE_INT_TYPE portNum, NATIVE_UINT_TYPE context) {
|
||||
// Request a buffer for Container 1
|
||||
this->dpRequest_Container1(CONTAINER_1_DATA_SIZE);
|
||||
// Request a buffer for Container 2
|
||||
this->dpRequest_Container2(CONTAINER_2_DATA_SIZE);
|
||||
// Request a buffer for Container 3
|
||||
this->dpRequest_Container3(CONTAINER_3_DATA_SIZE);
|
||||
// Request a buffer for Container 4
|
||||
this->dpRequest_Container4(CONTAINER_4_DATA_SIZE);
|
||||
// Request a buffer for Container 5
|
||||
this->dpRequest_Container5(CONTAINER_5_DATA_SIZE);
|
||||
// Get a buffer for Container 1
|
||||
{
|
||||
DpContainer container;
|
||||
Fw::Success status = this->dpGet_Container1(CONTAINER_1_DATA_SIZE, container);
|
||||
FW_ASSERT(status == Fw::Success::SUCCESS, status);
|
||||
// Check the container
|
||||
this->checkContainer(container, ContainerId::Container1, CONTAINER_1_PACKET_SIZE);
|
||||
}
|
||||
// Get a buffer for Container 2
|
||||
{
|
||||
DpContainer container;
|
||||
Fw::Success status = this->dpGet_Container2(CONTAINER_2_DATA_SIZE, container);
|
||||
FW_ASSERT(status == Fw::Success::SUCCESS);
|
||||
// Check the container
|
||||
this->checkContainer(container, ContainerId::Container2, CONTAINER_2_PACKET_SIZE);
|
||||
}
|
||||
// Get a buffer for Container 3
|
||||
{
|
||||
DpContainer container;
|
||||
Fw::Success status = this->dpGet_Container3(CONTAINER_3_DATA_SIZE, container);
|
||||
// This one should fail
|
||||
FW_ASSERT(status == Fw::Success::FAILURE);
|
||||
}
|
||||
// Get a buffer for Container 4
|
||||
{
|
||||
DpContainer container;
|
||||
Fw::Success status = this->dpGet_Container4(CONTAINER_4_DATA_SIZE, container);
|
||||
FW_ASSERT(status == Fw::Success::SUCCESS);
|
||||
// Check the container
|
||||
this->checkContainer(container, ContainerId::Container4, CONTAINER_4_PACKET_SIZE);
|
||||
}
|
||||
// Get a buffer for Container 5
|
||||
{
|
||||
DpContainer container;
|
||||
Fw::Success status = this->dpGet_Container5(CONTAINER_5_DATA_SIZE, container);
|
||||
FW_ASSERT(status == Fw::Success::SUCCESS);
|
||||
// Check the container
|
||||
this->checkContainer(container, ContainerId::Container5, CONTAINER_5_PACKET_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Data product handler implementations
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void DpTest ::dpRecv_Container1_handler(DpContainer& container, Fw::Success::T status) {
|
||||
if (status == Fw::Success::SUCCESS) {
|
||||
auto serializeStatus = Fw::FW_SERIALIZE_OK;
|
||||
for (FwSizeType i = 0; i < CONTAINER_1_DATA_SIZE; ++i) {
|
||||
serializeStatus = container.serializeRecord_U32Record(this->u32RecordData);
|
||||
if (serializeStatus == Fw::FW_SERIALIZE_NO_ROOM_LEFT) {
|
||||
break;
|
||||
}
|
||||
FW_ASSERT(serializeStatus == Fw::FW_SERIALIZE_OK, status);
|
||||
}
|
||||
// Use the time stamp from the time get port
|
||||
this->dpSend(container);
|
||||
}
|
||||
}
|
||||
|
||||
void DpTest ::dpRecv_Container2_handler(DpContainer& container, Fw::Success::T status) {
|
||||
if (status == Fw::Success::SUCCESS) {
|
||||
const DpTest_Data dataRecord(this->dataRecordData);
|
||||
auto serializeStatus = Fw::FW_SERIALIZE_OK;
|
||||
for (FwSizeType i = 0; i < CONTAINER_2_DATA_SIZE; ++i) {
|
||||
serializeStatus = container.serializeRecord_DataRecord(dataRecord);
|
||||
if (serializeStatus == Fw::FW_SERIALIZE_NO_ROOM_LEFT) {
|
||||
break;
|
||||
}
|
||||
FW_ASSERT(serializeStatus == Fw::FW_SERIALIZE_OK, status);
|
||||
}
|
||||
// Provide an explicit time stamp
|
||||
this->dpSend(container, this->sendTime);
|
||||
}
|
||||
}
|
||||
|
||||
void DpTest ::dpRecv_Container3_handler(DpContainer& container, Fw::Success::T status) {
|
||||
if (status == Fw::Success::SUCCESS) {
|
||||
auto serializeStatus = Fw::FW_SERIALIZE_OK;
|
||||
for (FwSizeType i = 0; i < CONTAINER_3_DATA_SIZE; ++i) {
|
||||
serializeStatus =
|
||||
container.serializeRecord_U8ArrayRecord(this->u8ArrayRecordData.data(), this->u8ArrayRecordData.size());
|
||||
if (serializeStatus == Fw::FW_SERIALIZE_NO_ROOM_LEFT) {
|
||||
break;
|
||||
}
|
||||
FW_ASSERT(serializeStatus == Fw::FW_SERIALIZE_OK, status);
|
||||
}
|
||||
// Use the time stamp from the time get port
|
||||
this->dpSend(container);
|
||||
}
|
||||
}
|
||||
|
||||
void DpTest ::dpRecv_Container4_handler(DpContainer& container, Fw::Success::T status) {
|
||||
if (status == Fw::Success::SUCCESS) {
|
||||
auto serializeStatus = Fw::FW_SERIALIZE_OK;
|
||||
for (FwSizeType i = 0; i < CONTAINER_4_DATA_SIZE; ++i) {
|
||||
serializeStatus = container.serializeRecord_U32ArrayRecord(this->u32ArrayRecordData.data(),
|
||||
this->u32ArrayRecordData.size());
|
||||
if (serializeStatus == Fw::FW_SERIALIZE_NO_ROOM_LEFT) {
|
||||
break;
|
||||
}
|
||||
FW_ASSERT(serializeStatus == Fw::FW_SERIALIZE_OK, status);
|
||||
}
|
||||
// Use the time stamp from the time get port
|
||||
this->dpSend(container);
|
||||
}
|
||||
}
|
||||
|
||||
void DpTest ::dpRecv_Container5_handler(DpContainer& container, Fw::Success::T status) {
|
||||
if (status == Fw::Success::SUCCESS) {
|
||||
auto serializeStatus = Fw::FW_SERIALIZE_OK;
|
||||
for (FwSizeType i = 0; i < CONTAINER_5_DATA_SIZE; ++i) {
|
||||
serializeStatus = container.serializeRecord_DataArrayRecord(this->dataArrayRecordData.data(),
|
||||
this->dataArrayRecordData.size());
|
||||
if (serializeStatus == Fw::FW_SERIALIZE_NO_ROOM_LEFT) {
|
||||
break;
|
||||
}
|
||||
FW_ASSERT(serializeStatus == Fw::FW_SERIALIZE_OK, status);
|
||||
}
|
||||
// Use the time stamp from the time get port
|
||||
this->dpSend(container);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Private helper functions
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void DpTest::checkContainer(const DpContainer& container, FwDpIdType localId, FwSizeType size) const {
|
||||
FW_ASSERT(container.getBaseId() == this->getIdBase(), container.getBaseId(), this->getIdBase());
|
||||
FW_ASSERT(container.getId() == container.getBaseId() + localId, container.getId(), container.getBaseId(),
|
||||
ContainerId::Container1);
|
||||
FW_ASSERT(container.getBuffer().getSize() == size, container.getBuffer().getSize(), size);
|
||||
}
|
||||
|
||||
} // end namespace FppTest
|
||||
82
FppTest/dp/DpTest.fpp
Normal file
82
FppTest/dp/DpTest.fpp
Normal file
@ -0,0 +1,82 @@
|
||||
module FppTest {
|
||||
|
||||
@ A component for testing data product code gen
|
||||
active component DpTest {
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Types
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
@ Data for a DataRecord
|
||||
struct Data {
|
||||
@ A U16 field
|
||||
u16Field: U16
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Special ports
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
@ Data product get port
|
||||
product get port productGetOut
|
||||
|
||||
@ Data product request port
|
||||
product request port productRequestOut
|
||||
|
||||
@ Data product receive port
|
||||
async product recv port productRecvIn
|
||||
|
||||
@ Data product send port
|
||||
product send port productSendOut
|
||||
|
||||
@ Time get port
|
||||
time get port timeGetOut
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# General ports
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
@ A schedIn port to run the data product generation
|
||||
async input port schedIn: Svc.Sched
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Records
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
@ Record 1
|
||||
product record U32Record: U32 id 100
|
||||
|
||||
@ Record 2
|
||||
product record DataRecord: Data id 200
|
||||
|
||||
@ Record 3
|
||||
product record U8ArrayRecord: U8 array id 300
|
||||
|
||||
@ Record 4
|
||||
product record U32ArrayRecord: U32 array id 400
|
||||
|
||||
@ Record 5
|
||||
product record DataArrayRecord: Data array id 500
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Containers
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
@ Container 1
|
||||
product container Container1 id 100 default priority 10
|
||||
|
||||
@ Container 2
|
||||
product container Container2 id 200 default priority 20
|
||||
|
||||
@ Container 3
|
||||
product container Container3 id 300 default priority 30
|
||||
|
||||
@ Container 4
|
||||
product container Container4 id 400 default priority 40
|
||||
|
||||
@ Container 5
|
||||
product container Container5 id 500 default priority 50
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
154
FppTest/dp/DpTest.hpp
Normal file
154
FppTest/dp/DpTest.hpp
Normal file
@ -0,0 +1,154 @@
|
||||
// ======================================================================
|
||||
// \title DpTest.hpp
|
||||
// \author bocchino
|
||||
// \brief hpp file for DpTest component implementation class
|
||||
// ======================================================================
|
||||
|
||||
#ifndef FppTest_DpTest_HPP
|
||||
#define FppTest_DpTest_HPP
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "FppTest/dp/DpTestComponentAc.hpp"
|
||||
|
||||
namespace FppTest {
|
||||
|
||||
class DpTest : public DpTestComponentBase {
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Constants
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
static constexpr FwSizeType CONTAINER_1_DATA_SIZE = 100;
|
||||
static constexpr FwSizeType CONTAINER_1_PACKET_SIZE = DpContainer::getPacketSizeForDataSize(CONTAINER_1_DATA_SIZE);
|
||||
static constexpr FwSizeType CONTAINER_2_DATA_SIZE = 1000;
|
||||
static constexpr FwSizeType CONTAINER_2_PACKET_SIZE = DpContainer::getPacketSizeForDataSize(CONTAINER_2_DATA_SIZE);
|
||||
static constexpr FwSizeType CONTAINER_3_DATA_SIZE = 1000;
|
||||
static constexpr FwSizeType CONTAINER_3_PACKET_SIZE = DpContainer::getPacketSizeForDataSize(CONTAINER_3_DATA_SIZE);
|
||||
static constexpr FwSizeType CONTAINER_4_DATA_SIZE = 1000;
|
||||
static constexpr FwSizeType CONTAINER_4_PACKET_SIZE = DpContainer::getPacketSizeForDataSize(CONTAINER_4_DATA_SIZE);
|
||||
static constexpr FwSizeType CONTAINER_5_DATA_SIZE = 1000;
|
||||
static constexpr FwSizeType CONTAINER_5_PACKET_SIZE = DpContainer::getPacketSizeForDataSize(CONTAINER_5_DATA_SIZE);
|
||||
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Types
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
using U8ArrayRecordData = std::array<U8, 256>;
|
||||
using U32ArrayRecordData = std::array<U32, 100>;
|
||||
using DataArrayRecordData = std::array<DpTest_Data, 300>;
|
||||
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction, initialization, and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Construct object DpTest
|
||||
DpTest(const char* const compName, //!< The component name
|
||||
U32 u32RecordData, //!< The U32Record data
|
||||
U16 dataRecordData, //!< The DataRecord data
|
||||
const U8ArrayRecordData& u8ArrayRecordData, //!< The U8ArrayRecord data
|
||||
const U32ArrayRecordData& u32ArrayRecordData, //!< The U32ArrayRecord data
|
||||
const DataArrayRecordData& dataArrayRecordData //!< The DataArrayRecord data
|
||||
);
|
||||
|
||||
//! Initialize object DpTest
|
||||
void init(const NATIVE_INT_TYPE queueDepth, //!< The queue depth
|
||||
const NATIVE_INT_TYPE instance = 0 //!< The instance number
|
||||
);
|
||||
|
||||
//! Destroy object DpTest
|
||||
~DpTest();
|
||||
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Public interface methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Set the send time
|
||||
void setSendTime(Fw::Time time) { this->sendTime = time; }
|
||||
|
||||
PRIVATE:
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for user-defined typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Handler implementation for schedIn
|
||||
void schedIn_handler(const NATIVE_INT_TYPE portNum, //!< The port number
|
||||
NATIVE_UINT_TYPE context //!< The call order
|
||||
) override;
|
||||
|
||||
PRIVATE:
|
||||
// ----------------------------------------------------------------------
|
||||
// Data product handler implementations
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Receive a data product container of type Container1
|
||||
//! \return Serialize status
|
||||
void dpRecv_Container1_handler(DpContainer& container, //!< The container
|
||||
Fw::Success::T //!< The container status
|
||||
) override;
|
||||
|
||||
//! Receive a data product container of type Container2
|
||||
//! \return Serialize status
|
||||
void dpRecv_Container2_handler(DpContainer& container, //!< The container
|
||||
Fw::Success::T //!< The container status
|
||||
) override;
|
||||
|
||||
//! Receive a data product container of type Container3
|
||||
//! \return Serialize status
|
||||
void dpRecv_Container3_handler(DpContainer& container, //!< The container
|
||||
Fw::Success::T //!< The container status
|
||||
) override;
|
||||
|
||||
//! Receive a data product container of type Container4
|
||||
//! \return Serialize status
|
||||
void dpRecv_Container4_handler(DpContainer& container, //!< The container
|
||||
Fw::Success::T //!< The container status
|
||||
) override;
|
||||
|
||||
//! Receive a data product container of type Container5
|
||||
//! \return Serialize status
|
||||
void dpRecv_Container5_handler(DpContainer& container, //!< The container
|
||||
Fw::Success::T //!< The container status
|
||||
) override;
|
||||
|
||||
PRIVATE:
|
||||
// ----------------------------------------------------------------------
|
||||
// Private helper functions
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Check a container for validity
|
||||
void checkContainer(const DpContainer& container, //!< The container
|
||||
FwDpIdType localId, //!< The expected local id
|
||||
FwSizeType size //!< The expected size
|
||||
) const;
|
||||
|
||||
PRIVATE:
|
||||
// ----------------------------------------------------------------------
|
||||
// Private member variables
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! U32Record data
|
||||
const U32 u32RecordData;
|
||||
|
||||
//! DataRecord data
|
||||
const U16 dataRecordData;
|
||||
|
||||
//! U8ArrayRecord data
|
||||
const U8ArrayRecordData& u8ArrayRecordData;
|
||||
|
||||
//! U32ArrayRecord data
|
||||
const U32ArrayRecordData& u32ArrayRecordData;
|
||||
|
||||
//! DataArrayRecord data
|
||||
const DataArrayRecordData& dataArrayRecordData;
|
||||
|
||||
//! Send time for testing
|
||||
Fw::Time sendTime;
|
||||
};
|
||||
|
||||
} // end namespace FppTest
|
||||
|
||||
#endif
|
||||
81
FppTest/dp/test/ut/TestMain.cpp
Normal file
81
FppTest/dp/test/ut/TestMain.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
// ----------------------------------------------------------------------
|
||||
// TestMain.cpp
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
#include "FppTest/dp/test/ut/Tester.hpp"
|
||||
#include "Fw/Test/UnitTest.hpp"
|
||||
#include "STest/Random/Random.hpp"
|
||||
|
||||
using namespace FppTest;
|
||||
|
||||
TEST(schedIn, OK) {
|
||||
COMMENT("schedIn OK");
|
||||
Tester tester;
|
||||
tester.schedIn_OK();
|
||||
}
|
||||
|
||||
TEST(productRecvIn, Container1_SUCCESS) {
|
||||
COMMENT("Receive Container1 SUCCESS");
|
||||
Tester tester;
|
||||
tester.productRecvIn_Container1_SUCCESS();
|
||||
}
|
||||
|
||||
TEST(productRecvIn, Container1_FAILURE) {
|
||||
COMMENT("Receive Container1 FAILURE");
|
||||
Tester tester;
|
||||
tester.productRecvIn_Container1_FAILURE();
|
||||
}
|
||||
|
||||
TEST(productRecvIn, Container2_SUCCESS) {
|
||||
COMMENT("Receive Container2 SUCCESS");
|
||||
Tester tester;
|
||||
tester.productRecvIn_Container2_SUCCESS();
|
||||
}
|
||||
|
||||
TEST(productRecvIn, Container2_FAILURE) {
|
||||
COMMENT("Receive Container2 FAILURE");
|
||||
Tester tester;
|
||||
tester.productRecvIn_Container2_FAILURE();
|
||||
}
|
||||
|
||||
TEST(productRecvIn, Container3_SUCCESS) {
|
||||
COMMENT("Receive Container3 SUCCESS");
|
||||
Tester tester;
|
||||
tester.productRecvIn_Container3_SUCCESS();
|
||||
}
|
||||
|
||||
TEST(productRecvIn, Container3_FAILURE) {
|
||||
COMMENT("Receive Container3 FAILURE");
|
||||
Tester tester;
|
||||
tester.productRecvIn_Container3_FAILURE();
|
||||
}
|
||||
|
||||
TEST(productRecvIn, Container4_SUCCESS) {
|
||||
COMMENT("Receive Container4 SUCCESS");
|
||||
Tester tester;
|
||||
tester.productRecvIn_Container4_SUCCESS();
|
||||
}
|
||||
|
||||
TEST(productRecvIn, Container4_FAILURE) {
|
||||
COMMENT("Receive Container4 FAILURE");
|
||||
Tester tester;
|
||||
tester.productRecvIn_Container4_FAILURE();
|
||||
}
|
||||
|
||||
TEST(productRecvIn, Container5_SUCCESS) {
|
||||
COMMENT("Receive Container5 SUCCESS");
|
||||
Tester tester;
|
||||
tester.productRecvIn_Container5_SUCCESS();
|
||||
}
|
||||
|
||||
TEST(productRecvIn, Container5_FAILURE) {
|
||||
COMMENT("Receive Container5 FAILURE");
|
||||
Tester tester;
|
||||
tester.productRecvIn_Container5_FAILURE();
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
STest::Random::seed();
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
331
FppTest/dp/test/ut/Tester.cpp
Normal file
331
FppTest/dp/test/ut/Tester.cpp
Normal file
@ -0,0 +1,331 @@
|
||||
// ======================================================================
|
||||
// \title DpTest.hpp
|
||||
// \author bocchino
|
||||
// \brief cpp file for DpTest test harness implementation class
|
||||
// ======================================================================
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "FppTest/dp/test/ut/Tester.hpp"
|
||||
#include "STest/Pick/Pick.hpp"
|
||||
|
||||
namespace FppTest {
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
Tester::Tester()
|
||||
: DpTestGTestBase("Tester", Tester::MAX_HISTORY_SIZE),
|
||||
container1Data{},
|
||||
container1Buffer(this->container1Data, sizeof this->container1Data),
|
||||
container2Data{},
|
||||
container2Buffer(this->container2Data, sizeof this->container2Data),
|
||||
container3Data{},
|
||||
container3Buffer(this->container3Data, sizeof this->container3Data),
|
||||
container4Data{},
|
||||
container4Buffer(this->container4Data, sizeof this->container4Data),
|
||||
container5Data{},
|
||||
container5Buffer(this->container5Data, sizeof this->container5Data),
|
||||
component("DpTest",
|
||||
STest::Pick::any(),
|
||||
STest::Pick::any(),
|
||||
this->u8ArrayRecordData,
|
||||
this->u32ArrayRecordData,
|
||||
this->dataArrayRecordData) {
|
||||
this->initComponents();
|
||||
this->connectPorts();
|
||||
this->component.setIdBase(ID_BASE);
|
||||
// Fill in arrays with random data
|
||||
for (U8& elt : this->u8ArrayRecordData) {
|
||||
elt = static_cast<U8>(STest::Pick::any());
|
||||
}
|
||||
for (U32& elt : this->u32ArrayRecordData) {
|
||||
elt = static_cast<U8>(STest::Pick::any());
|
||||
}
|
||||
for (DpTest_Data& elt : this->dataArrayRecordData) {
|
||||
elt.set(static_cast<U16>(STest::Pick::any()));
|
||||
}
|
||||
}
|
||||
|
||||
Tester::~Tester() {}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Tests
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void Tester::schedIn_OK() {
|
||||
this->invoke_to_schedIn(0, 0);
|
||||
this->component.doDispatch();
|
||||
ASSERT_PRODUCT_REQUEST_SIZE(5);
|
||||
ASSERT_PRODUCT_REQUEST(0, ID_BASE + DpTest::ContainerId::Container1, FwSizeType(DpTest::CONTAINER_1_PACKET_SIZE));
|
||||
ASSERT_PRODUCT_REQUEST(1, ID_BASE + DpTest::ContainerId::Container2, FwSizeType(DpTest::CONTAINER_2_PACKET_SIZE));
|
||||
ASSERT_PRODUCT_REQUEST(2, ID_BASE + DpTest::ContainerId::Container3, FwSizeType(DpTest::CONTAINER_3_PACKET_SIZE));
|
||||
ASSERT_PRODUCT_REQUEST(3, ID_BASE + DpTest::ContainerId::Container4, FwSizeType(DpTest::CONTAINER_4_PACKET_SIZE));
|
||||
ASSERT_PRODUCT_REQUEST(4, ID_BASE + DpTest::ContainerId::Container5, FwSizeType(DpTest::CONTAINER_5_PACKET_SIZE));
|
||||
ASSERT_PRODUCT_GET_SIZE(5);
|
||||
ASSERT_PRODUCT_GET(0, ID_BASE + DpTest::ContainerId::Container1, FwSizeType(DpTest::CONTAINER_1_PACKET_SIZE));
|
||||
ASSERT_PRODUCT_GET(1, ID_BASE + DpTest::ContainerId::Container2, FwSizeType(DpTest::CONTAINER_2_PACKET_SIZE));
|
||||
ASSERT_PRODUCT_GET(2, ID_BASE + DpTest::ContainerId::Container3, FwSizeType(DpTest::CONTAINER_3_PACKET_SIZE));
|
||||
ASSERT_PRODUCT_GET(3, ID_BASE + DpTest::ContainerId::Container4, FwSizeType(DpTest::CONTAINER_4_PACKET_SIZE));
|
||||
ASSERT_PRODUCT_GET(4, ID_BASE + DpTest::ContainerId::Container5, FwSizeType(DpTest::CONTAINER_5_PACKET_SIZE));
|
||||
}
|
||||
|
||||
void Tester::productRecvIn_Container1_SUCCESS() {
|
||||
Fw::Buffer buffer;
|
||||
FwSizeType expectedNumElts;
|
||||
// Invoke the port and check the header
|
||||
this->productRecvIn_InvokeAndCheckHeader(DpTest::ContainerId::Container1, sizeof(U32),
|
||||
DpTest::ContainerPriority::Container1, this->container1Buffer, buffer,
|
||||
expectedNumElts);
|
||||
// Check the data
|
||||
Fw::SerializeBufferBase& serialRepr = buffer.getSerializeRepr();
|
||||
Fw::TestUtil::DpContainerHeader::checkDeserialAtOffset(serialRepr, Fw::DpContainer::DATA_OFFSET);
|
||||
for (FwSizeType i = 0; i < expectedNumElts; ++i) {
|
||||
FwDpIdType id;
|
||||
U32 elt;
|
||||
auto status = serialRepr.deserialize(id);
|
||||
ASSERT_EQ(status, Fw::FW_SERIALIZE_OK);
|
||||
const FwDpIdType expectedId = this->component.getIdBase() + DpTest::RecordId::U32Record;
|
||||
ASSERT_EQ(id, expectedId);
|
||||
status = serialRepr.deserialize(elt);
|
||||
ASSERT_EQ(status, Fw::FW_SERIALIZE_OK);
|
||||
ASSERT_EQ(elt, this->component.u32RecordData);
|
||||
}
|
||||
}
|
||||
|
||||
void Tester::productRecvIn_Container1_FAILURE() {
|
||||
productRecvIn_CheckFailure(DpTest::ContainerId::Container1, this->container1Buffer);
|
||||
}
|
||||
|
||||
void Tester::productRecvIn_Container2_SUCCESS() {
|
||||
Fw::Buffer buffer;
|
||||
FwSizeType expectedNumElts;
|
||||
// Invoke the port and check the header
|
||||
this->productRecvIn_InvokeAndCheckHeader(DpTest::ContainerId::Container2, DpTest_Data::SERIALIZED_SIZE,
|
||||
DpTest::ContainerPriority::Container2, this->container2Buffer, buffer,
|
||||
expectedNumElts);
|
||||
// Check the data
|
||||
Fw::SerializeBufferBase& serialRepr = buffer.getSerializeRepr();
|
||||
Fw::TestUtil::DpContainerHeader::checkDeserialAtOffset(serialRepr, Fw::DpContainer::DATA_OFFSET);
|
||||
for (FwSizeType i = 0; i < expectedNumElts; ++i) {
|
||||
FwDpIdType id;
|
||||
DpTest_Data elt;
|
||||
auto status = serialRepr.deserialize(id);
|
||||
ASSERT_EQ(status, Fw::FW_SERIALIZE_OK);
|
||||
const FwDpIdType expectedId = this->component.getIdBase() + DpTest::RecordId::DataRecord;
|
||||
ASSERT_EQ(id, expectedId);
|
||||
status = serialRepr.deserialize(elt);
|
||||
ASSERT_EQ(status, Fw::FW_SERIALIZE_OK);
|
||||
ASSERT_EQ(elt.getu16Field(), this->component.dataRecordData);
|
||||
}
|
||||
}
|
||||
|
||||
void Tester::productRecvIn_Container2_FAILURE() {
|
||||
productRecvIn_CheckFailure(DpTest::ContainerId::Container2, this->container2Buffer);
|
||||
}
|
||||
|
||||
void Tester::productRecvIn_Container3_SUCCESS() {
|
||||
Fw::Buffer buffer;
|
||||
FwSizeType expectedNumElts;
|
||||
const FwSizeType dataEltSize = sizeof(FwSizeType) + this->u8ArrayRecordData.size();
|
||||
// Invoke the port and check the header
|
||||
this->productRecvIn_InvokeAndCheckHeader(DpTest::ContainerId::Container3, dataEltSize,
|
||||
DpTest::ContainerPriority::Container3, this->container3Buffer, buffer,
|
||||
expectedNumElts);
|
||||
|
||||
// Check the data
|
||||
Fw::SerializeBufferBase& serialRepr = buffer.getSerializeRepr();
|
||||
Fw::TestUtil::DpContainerHeader::checkDeserialAtOffset(serialRepr, Fw::DpContainer::DATA_OFFSET);
|
||||
for (FwSizeType i = 0; i < expectedNumElts; ++i) {
|
||||
FwDpIdType id;
|
||||
auto status = serialRepr.deserialize(id);
|
||||
ASSERT_EQ(status, Fw::FW_SERIALIZE_OK);
|
||||
const FwDpIdType expectedId = this->component.getIdBase() + DpTest::RecordId::U8ArrayRecord;
|
||||
ASSERT_EQ(id, expectedId);
|
||||
FwSizeType size;
|
||||
status = serialRepr.deserialize(size);
|
||||
ASSERT_EQ(status, Fw::FW_SERIALIZE_OK);
|
||||
ASSERT_EQ(size, this->u8ArrayRecordData.size());
|
||||
const U8* const buffAddr = serialRepr.getBuffAddr();
|
||||
for (FwSizeType j = 0; j < size; ++j) {
|
||||
U8 byte;
|
||||
status = serialRepr.deserialize(byte);
|
||||
ASSERT_EQ(status, Fw::FW_SERIALIZE_OK);
|
||||
ASSERT_EQ(byte, this->u8ArrayRecordData.at(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tester::productRecvIn_Container3_FAILURE() {
|
||||
productRecvIn_CheckFailure(DpTest::ContainerId::Container3, this->container3Buffer);
|
||||
}
|
||||
|
||||
void Tester::productRecvIn_Container4_SUCCESS() {
|
||||
Fw::Buffer buffer;
|
||||
FwSizeType expectedNumElts;
|
||||
const FwSizeType dataEltSize = sizeof(FwSizeType) + this->u32ArrayRecordData.size() * sizeof(U32);
|
||||
// Invoke the port and check the header
|
||||
this->productRecvIn_InvokeAndCheckHeader(DpTest::ContainerId::Container4, dataEltSize,
|
||||
DpTest::ContainerPriority::Container4, this->container4Buffer, buffer,
|
||||
expectedNumElts);
|
||||
|
||||
// Check the data
|
||||
Fw::SerializeBufferBase& serialRepr = buffer.getSerializeRepr();
|
||||
Fw::TestUtil::DpContainerHeader::checkDeserialAtOffset(serialRepr, Fw::DpContainer::DATA_OFFSET);
|
||||
for (FwSizeType i = 0; i < expectedNumElts; ++i) {
|
||||
FwDpIdType id;
|
||||
auto status = serialRepr.deserialize(id);
|
||||
ASSERT_EQ(status, Fw::FW_SERIALIZE_OK);
|
||||
const FwDpIdType expectedId = this->component.getIdBase() + DpTest::RecordId::U32ArrayRecord;
|
||||
ASSERT_EQ(id, expectedId);
|
||||
FwSizeType size;
|
||||
status = serialRepr.deserialize(size);
|
||||
ASSERT_EQ(status, Fw::FW_SERIALIZE_OK);
|
||||
ASSERT_EQ(size, this->u32ArrayRecordData.size());
|
||||
const U8* const buffAddr = serialRepr.getBuffAddr();
|
||||
for (FwSizeType j = 0; j < size; ++j) {
|
||||
U32 elt;
|
||||
status = serialRepr.deserialize(elt);
|
||||
ASSERT_EQ(status, Fw::FW_SERIALIZE_OK);
|
||||
ASSERT_EQ(elt, this->u32ArrayRecordData.at(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tester::productRecvIn_Container4_FAILURE() {
|
||||
productRecvIn_CheckFailure(DpTest::ContainerId::Container4, this->container4Buffer);
|
||||
}
|
||||
|
||||
void Tester::productRecvIn_Container5_SUCCESS() {
|
||||
Fw::Buffer buffer;
|
||||
FwSizeType expectedNumElts;
|
||||
const FwSizeType dataEltSize = sizeof(FwSizeType) + this->dataArrayRecordData.size() * DpTest_Data::SERIALIZED_SIZE;
|
||||
// Invoke the port and check the header
|
||||
this->productRecvIn_InvokeAndCheckHeader(DpTest::ContainerId::Container5, dataEltSize,
|
||||
DpTest::ContainerPriority::Container5, this->container5Buffer, buffer,
|
||||
expectedNumElts);
|
||||
|
||||
// Check the data
|
||||
Fw::SerializeBufferBase& serialRepr = buffer.getSerializeRepr();
|
||||
Fw::TestUtil::DpContainerHeader::checkDeserialAtOffset(serialRepr, Fw::DpContainer::DATA_OFFSET);
|
||||
for (FwSizeType i = 0; i < expectedNumElts; ++i) {
|
||||
FwDpIdType id;
|
||||
auto status = serialRepr.deserialize(id);
|
||||
ASSERT_EQ(status, Fw::FW_SERIALIZE_OK);
|
||||
const FwDpIdType expectedId = this->component.getIdBase() + DpTest::RecordId::DataArrayRecord;
|
||||
ASSERT_EQ(id, expectedId);
|
||||
FwSizeType size;
|
||||
status = serialRepr.deserialize(size);
|
||||
ASSERT_EQ(status, Fw::FW_SERIALIZE_OK);
|
||||
ASSERT_EQ(size, this->dataArrayRecordData.size());
|
||||
const U8* const buffAddr = serialRepr.getBuffAddr();
|
||||
for (FwSizeType j = 0; j < size; ++j) {
|
||||
DpTest_Data elt;
|
||||
status = serialRepr.deserialize(elt);
|
||||
ASSERT_EQ(status, Fw::FW_SERIALIZE_OK);
|
||||
ASSERT_EQ(elt, this->dataArrayRecordData.at(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tester::productRecvIn_Container5_FAILURE() {
|
||||
productRecvIn_CheckFailure(DpTest::ContainerId::Container5, this->container5Buffer);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
Fw::Time Tester::randomizeTestTime() {
|
||||
const U32 seconds = STest::Pick::any();
|
||||
const U32 useconds = STest::Pick::startLength(0, 1000000);
|
||||
const Fw::Time time(seconds, useconds);
|
||||
this->setTestTime(time);
|
||||
this->component.setSendTime(time);
|
||||
return time;
|
||||
}
|
||||
|
||||
void Tester::productRecvIn_InvokeAndCheckHeader(FwDpIdType id,
|
||||
FwSizeType dataEltSize,
|
||||
FwDpPriorityType priority,
|
||||
Fw::Buffer inputBuffer,
|
||||
Fw::Buffer& outputBuffer,
|
||||
FwSizeType& expectedNumElts) {
|
||||
const auto globalId = ID_BASE + id;
|
||||
// Set the test time
|
||||
const Fw::Time timeTag = this->randomizeTestTime();
|
||||
// Invoke the productRecvIn port
|
||||
this->sendProductResponse(globalId, inputBuffer, Fw::Success::SUCCESS);
|
||||
this->component.doDispatch();
|
||||
// Check the port history size
|
||||
ASSERT_PRODUCT_SEND_SIZE(1);
|
||||
// Compute the expected data size
|
||||
const auto& entry = this->productSendHistory->at(0);
|
||||
const auto bufferSize = entry.buffer.getSize();
|
||||
FW_ASSERT(bufferSize >= Fw::DpContainer::MIN_PACKET_SIZE);
|
||||
const auto dataCapacity = bufferSize - Fw::DpContainer::MIN_PACKET_SIZE;
|
||||
const auto eltSize = sizeof(FwDpIdType) + dataEltSize;
|
||||
expectedNumElts = dataCapacity / eltSize;
|
||||
const auto expectedDataSize = expectedNumElts * eltSize;
|
||||
// DP state should be the default value
|
||||
Fw::DpState dpState;
|
||||
// Set up the expected user data
|
||||
Fw::DpContainer::Header::UserData userData;
|
||||
memset(&userData[0], 0, sizeof userData);
|
||||
// Check the history entry
|
||||
// This sets the output buffer and sets the deserialization pointer
|
||||
// to the start of the data payload
|
||||
ASSERT_PRODUCT_SEND(0, globalId, priority, timeTag, 0, userData, dpState, expectedDataSize, outputBuffer);
|
||||
}
|
||||
|
||||
void Tester::productRecvIn_CheckFailure(FwDpIdType id, Fw::Buffer buffer) {
|
||||
// Invoke the port
|
||||
const auto globalId = ID_BASE + id;
|
||||
this->sendProductResponse(globalId, buffer, Fw::Success::FAILURE);
|
||||
this->component.doDispatch();
|
||||
// Check the port history size
|
||||
ASSERT_PRODUCT_SEND_SIZE(0);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Handlers for typed from ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
Fw::Success::T Tester::productGet_handler(FwDpIdType id, FwSizeType size, Fw::Buffer& buffer) {
|
||||
this->pushProductGetEntry(id, size);
|
||||
Fw::Success status = Fw::Success::FAILURE;
|
||||
FW_ASSERT(id >= ID_BASE, id, ID_BASE);
|
||||
const FwDpIdType localId = id - ID_BASE;
|
||||
switch (localId) {
|
||||
case DpTest::ContainerId::Container1:
|
||||
FW_ASSERT(size == DpTest::CONTAINER_1_PACKET_SIZE);
|
||||
buffer = this->container1Buffer;
|
||||
status = Fw::Success::SUCCESS;
|
||||
break;
|
||||
case DpTest::ContainerId::Container2:
|
||||
FW_ASSERT(size == DpTest::CONTAINER_2_PACKET_SIZE);
|
||||
buffer = this->container2Buffer;
|
||||
status = Fw::Success::SUCCESS;
|
||||
break;
|
||||
case DpTest::ContainerId::Container3:
|
||||
// Make this one fail for testing purposes
|
||||
break;
|
||||
case DpTest::ContainerId::Container4:
|
||||
FW_ASSERT(size == DpTest::CONTAINER_4_PACKET_SIZE);
|
||||
buffer = this->container4Buffer;
|
||||
status = Fw::Success::SUCCESS;
|
||||
break;
|
||||
case DpTest::ContainerId::Container5:
|
||||
FW_ASSERT(size == DpTest::CONTAINER_5_PACKET_SIZE);
|
||||
buffer = this->container5Buffer;
|
||||
status = Fw::Success::SUCCESS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
} // end namespace FppTest
|
||||
171
FppTest/dp/test/ut/Tester.hpp
Normal file
171
FppTest/dp/test/ut/Tester.hpp
Normal file
@ -0,0 +1,171 @@
|
||||
// ======================================================================
|
||||
// \title DpTest/test/ut/Tester.hpp
|
||||
// \author bocchino
|
||||
// \brief hpp file for DpTest test harness implementation class
|
||||
// ======================================================================
|
||||
|
||||
#ifndef FppTest_DpTest_Tester_HPP
|
||||
#define FppTest_DpTest_Tester_HPP
|
||||
|
||||
#include "DpTestGTestBase.hpp"
|
||||
#include "FppTest/dp/DpTest.hpp"
|
||||
#include "Fw/Dp/test/util/DpContainerHeader.hpp"
|
||||
#include "STest/Pick/Pick.hpp"
|
||||
|
||||
namespace FppTest {
|
||||
|
||||
class Tester : public DpTestGTestBase {
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public:
|
||||
// Maximum size of histories storing events, telemetry, and port outputs
|
||||
static constexpr FwSizeType MAX_HISTORY_SIZE = 10;
|
||||
// Instance ID supplied to the component instance under test
|
||||
static constexpr FwSizeType TEST_INSTANCE_ID = 0;
|
||||
// Queue depth supplied to component instance under test
|
||||
static constexpr FwSizeType TEST_INSTANCE_QUEUE_DEPTH = 10;
|
||||
// The component id base
|
||||
static constexpr FwDpIdType ID_BASE = 100;
|
||||
|
||||
//! Construct object Tester
|
||||
//!
|
||||
Tester();
|
||||
|
||||
//! Destroy object Tester
|
||||
//!
|
||||
~Tester();
|
||||
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Tests
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! schedIn OK
|
||||
void schedIn_OK();
|
||||
|
||||
//! productRecvIn with Container 1 (SUCCESS)
|
||||
void productRecvIn_Container1_SUCCESS();
|
||||
|
||||
//! productRecvIn with Container 1 (FAILURE)
|
||||
void productRecvIn_Container1_FAILURE();
|
||||
|
||||
//! productRecvIn with Container 2 (SUCCESS)
|
||||
void productRecvIn_Container2_SUCCESS();
|
||||
|
||||
//! productRecvIn with Container 2 (FAILURE)
|
||||
void productRecvIn_Container2_FAILURE();
|
||||
|
||||
//! productRecvIn with Container 3 (SUCCESS)
|
||||
void productRecvIn_Container3_SUCCESS();
|
||||
|
||||
//! productRecvIn with Container 3 (FAILURE)
|
||||
void productRecvIn_Container3_FAILURE();
|
||||
|
||||
//! productRecvIn with Container 4 (SUCCESS)
|
||||
void productRecvIn_Container4_SUCCESS();
|
||||
|
||||
//! productRecvIn with Container 4 (FAILURE)
|
||||
void productRecvIn_Container4_FAILURE();
|
||||
|
||||
//! productRecvIn with Container 5 (SUCCESS)
|
||||
void productRecvIn_Container5_SUCCESS();
|
||||
|
||||
//! productRecvIn with Container 5 (FAILURE)
|
||||
void productRecvIn_Container5_FAILURE();
|
||||
|
||||
PRIVATE:
|
||||
// ----------------------------------------------------------------------
|
||||
// Handlers for data product ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
Fw::Success::T productGet_handler(FwDpIdType id, //!< The container ID
|
||||
FwSizeType size, //!< The size of the requested buffer
|
||||
Fw::Buffer& buffer //!< The buffer
|
||||
) override;
|
||||
|
||||
PRIVATE:
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Connect ports
|
||||
//!
|
||||
void connectPorts();
|
||||
|
||||
//! Initialize components
|
||||
//!
|
||||
void initComponents();
|
||||
|
||||
//! Set and return a random time
|
||||
//! \return The time
|
||||
Fw::Time randomizeTestTime();
|
||||
|
||||
//! Invoke productRecvIn and check header
|
||||
//! This sets the output buffer to the received buffer and sets the
|
||||
//! deserialization pointer to the start of the data payload
|
||||
void productRecvIn_InvokeAndCheckHeader(FwDpIdType id, //!< The container id
|
||||
FwSizeType dataEltSize, //!< The data element size
|
||||
FwDpPriorityType priority, //!< The priority
|
||||
Fw::Buffer inputBuffer, //!< The buffer to send
|
||||
Fw::Buffer& outputBuffer, //!< The buffer received (output)
|
||||
FwSizeType& expectedNumElts //!< The expected number of elements (output)
|
||||
);
|
||||
|
||||
//! Check received buffer with failure status
|
||||
void productRecvIn_CheckFailure(FwDpIdType id, //!< The container id
|
||||
Fw::Buffer buffer //!< The buffer
|
||||
);
|
||||
|
||||
PRIVATE:
|
||||
// ----------------------------------------------------------------------
|
||||
// Variables
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Buffer data for Container 1
|
||||
U8 container1Data[DpTest::CONTAINER_1_PACKET_SIZE];
|
||||
|
||||
//! Buffer for Container 1
|
||||
const Fw::Buffer container1Buffer;
|
||||
|
||||
//! Buffer data for Container 2
|
||||
U8 container2Data[DpTest::CONTAINER_2_PACKET_SIZE];
|
||||
|
||||
//! Buffer for Container 2
|
||||
const Fw::Buffer container2Buffer;
|
||||
|
||||
//! Buffer data for Container 3
|
||||
U8 container3Data[DpTest::CONTAINER_3_PACKET_SIZE];
|
||||
|
||||
//! Buffer for Container 3
|
||||
const Fw::Buffer container3Buffer;
|
||||
|
||||
//! Buffer data for Container 4
|
||||
U8 container4Data[DpTest::CONTAINER_4_PACKET_SIZE];
|
||||
|
||||
//! Buffer for Container 4
|
||||
const Fw::Buffer container4Buffer;
|
||||
|
||||
//! Buffer data for Container 5
|
||||
U8 container5Data[DpTest::CONTAINER_5_PACKET_SIZE];
|
||||
|
||||
//! Buffer for Container 5
|
||||
const Fw::Buffer container5Buffer;
|
||||
|
||||
//! Data for U8 array record
|
||||
DpTest::U8ArrayRecordData u8ArrayRecordData;
|
||||
|
||||
//! Data for U32 array record
|
||||
DpTest::U32ArrayRecordData u32ArrayRecordData;
|
||||
|
||||
//! Data for Data array record
|
||||
DpTest::DataArrayRecordData dataArrayRecordData;
|
||||
|
||||
//! The component under test
|
||||
DpTest component;
|
||||
};
|
||||
|
||||
} // end namespace FppTest
|
||||
|
||||
#endif
|
||||
41
FppTest/dp/test/ut/TesterHelpers.cpp
Normal file
41
FppTest/dp/test/ut/TesterHelpers.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
// ======================================================================
|
||||
// \title DpTest/test/ut/TesterHelpers.cpp
|
||||
// \author Auto-generated
|
||||
// \brief cpp file for DpTest component test harness base class
|
||||
//
|
||||
// NOTE: this file was automatically generated
|
||||
//
|
||||
// ======================================================================
|
||||
#include "Tester.hpp"
|
||||
|
||||
namespace FppTest {
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void Tester ::connectPorts() {
|
||||
// productRecvIn
|
||||
this->connect_to_productRecvIn(0, this->component.get_productRecvIn_InputPort(0));
|
||||
|
||||
// schedIn
|
||||
this->connect_to_schedIn(0, this->component.get_schedIn_InputPort(0));
|
||||
|
||||
// productGetOut
|
||||
this->component.set_productGetOut_OutputPort(0, this->get_from_productGetOut(0));
|
||||
|
||||
// productRequestOut
|
||||
this->component.set_productRequestOut_OutputPort(0, this->get_from_productRequestOut(0));
|
||||
|
||||
// productSendOut
|
||||
this->component.set_productSendOut_OutputPort(0, this->get_from_productSendOut(0));
|
||||
|
||||
// timeGetOut
|
||||
this->component.set_timeGetOut_OutputPort(0, this->get_from_timeGetOut(0));
|
||||
}
|
||||
|
||||
void Tester ::initComponents() {
|
||||
this->init();
|
||||
this->component.init(Tester::TEST_INSTANCE_QUEUE_DEPTH, Tester::TEST_INSTANCE_ID);
|
||||
}
|
||||
|
||||
} // end namespace FppTest
|
||||
@ -1,12 +1,18 @@
|
||||
module Fw {
|
||||
|
||||
@ The buffer type
|
||||
type Buffer
|
||||
|
||||
@ Port for sending a buffer
|
||||
port BufferSend(
|
||||
@ The buffer
|
||||
ref fwBuffer: Fw.Buffer
|
||||
)
|
||||
|
||||
@ Port for getting a buffer
|
||||
@ Returns the buffer
|
||||
port BufferGet(
|
||||
@ The requested size
|
||||
$size: U32
|
||||
) -> Fw.Buffer
|
||||
|
||||
|
||||
@ -2,24 +2,25 @@
|
||||
set(FPRIME_FRAMEWORK_MODULES Fw_Prm Fw_Cmd Fw_Log Fw_Tlm Fw_Com Fw_Time Fw_Port Fw_Types Fw_Cfg CACHE INTERNAL "Fw mods")
|
||||
# Port subdirectories
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Buffer/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Com/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Cmd/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Com/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Dp/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Log/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Logger/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Prm/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Time/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Tlm/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Prm/")
|
||||
|
||||
# Framework subdirectories
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Cfg/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Comp/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FilePacket/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Obj/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Port/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Ports/SuccessCondition")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Types/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FilePacket/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/SerializableFile/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Test/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Types/")
|
||||
|
||||
# Setup an interface target for Fw for efficiency
|
||||
add_library(Fw INTERFACE)
|
||||
|
||||
@ -24,6 +24,7 @@ namespace Fw {
|
||||
FW_PACKET_LOG, // !< Log type - outgoing
|
||||
FW_PACKET_FILE, // !< File type - incoming and outgoing
|
||||
FW_PACKET_PACKETIZED_TLM, // !< Packetized telemetry packet type
|
||||
FW_PACKET_DP, //!< Data product packet
|
||||
FW_PACKET_IDLE, // !< Idle packet
|
||||
FW_PACKET_UNKNOWN = 0xFF // !< Unknown packet
|
||||
} ComPacketType;
|
||||
|
||||
13
Fw/Dp/CMakeLists.txt
Normal file
13
Fw/Dp/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
set(SOURCE_FILES
|
||||
"${CMAKE_CURRENT_LIST_DIR}/Dp.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/DpContainer.cpp"
|
||||
)
|
||||
set(MOD_DEPS Utils/Hash)
|
||||
register_fprime_module()
|
||||
|
||||
set(UT_SOURCE_FILES
|
||||
"${CMAKE_CURRENT_LIST_DIR}/Dp.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/test/ut/TestMain.cpp"
|
||||
)
|
||||
set(UT_MOD_DEPS STest)
|
||||
register_fprime_ut()
|
||||
62
Fw/Dp/Dp.fpp
Normal file
62
Fw/Dp/Dp.fpp
Normal file
@ -0,0 +1,62 @@
|
||||
module Fw {
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Types
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
enum DpState: U8 {
|
||||
@ The untransmitted state
|
||||
UNTRANSMITTED
|
||||
@ The transmitted state
|
||||
TRANSMITTED
|
||||
} default UNTRANSMITTED
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Ports
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
@ Port for synchronously getting a data product buffer
|
||||
@ Returns the status
|
||||
@
|
||||
@ On return, buffer should be set to a valid buffer large enough
|
||||
@ to hold a data product packet with the requested data size (if
|
||||
@ status is SUCCESS) or an invalid buffer (if status is FAILURE).
|
||||
port DpGet(
|
||||
@ The container ID (input)
|
||||
$id: FwDpIdType
|
||||
@ The data size of the requested buffer (input)
|
||||
dataSize: FwSizeType
|
||||
@ The buffer (output)
|
||||
ref buffer: Fw.Buffer
|
||||
) -> Fw.Success
|
||||
|
||||
@ Port for sending a request for a data product buffer to
|
||||
@ back a data product container. The request is for a buffer
|
||||
@ large enough to hold a data product packet with the requested
|
||||
@ data size.
|
||||
port DpRequest(
|
||||
@ The container ID
|
||||
$id: FwDpIdType
|
||||
@ The data size of the requested buffer
|
||||
dataSize: FwSizeType
|
||||
)
|
||||
|
||||
@ Port for receiving a response to a buffer request
|
||||
port DpResponse(
|
||||
@ The container ID
|
||||
$id: FwDpIdType
|
||||
@ The buffer
|
||||
buffer: Fw.Buffer
|
||||
@ The status
|
||||
status: Fw.Success
|
||||
)
|
||||
|
||||
@ Port for sending a data product buffer
|
||||
port DpSend(
|
||||
@ The container ID
|
||||
$id: FwDpIdType
|
||||
@ The buffer
|
||||
buffer: Fw.Buffer
|
||||
)
|
||||
|
||||
}
|
||||
174
Fw/Dp/DpContainer.cpp
Normal file
174
Fw/Dp/DpContainer.cpp
Normal file
@ -0,0 +1,174 @@
|
||||
// ======================================================================
|
||||
// \title DpContainer.cpp
|
||||
// \author bocchino
|
||||
// \brief cpp file for DpContainer
|
||||
// ======================================================================
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "Fw/Com/ComPacket.hpp"
|
||||
#include "Fw/Dp/DpContainer.hpp"
|
||||
#include "Fw/Types/Assert.hpp"
|
||||
|
||||
namespace Fw {
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
DpContainer::DpContainer(FwDpIdType id, const Fw::Buffer& buffer)
|
||||
: m_id(id), m_priority(0), m_timeTag(), m_procTypes(0), m_dpState(), m_dataSize(0), m_buffer(), m_dataBuffer() {
|
||||
// Initialize the user data field
|
||||
this->initUserDataField();
|
||||
// Set the packet buffer
|
||||
// This action also updates the data buffer
|
||||
this->setBuffer(buffer);
|
||||
}
|
||||
|
||||
DpContainer::DpContainer()
|
||||
: m_id(0), m_priority(0), m_timeTag(), m_procTypes(0), m_dataSize(0), m_buffer(), m_dataBuffer() {
|
||||
// Initialize the user data field
|
||||
this->initUserDataField();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Public member functions
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
Fw::SerializeStatus DpContainer::deserializeHeader() {
|
||||
FW_ASSERT(this->m_buffer.isValid());
|
||||
Fw::SerializeBufferBase& serializeRepr = this->m_buffer.getSerializeRepr();
|
||||
// Set buffer length
|
||||
Fw::SerializeStatus status = serializeRepr.setBuffLen(this->m_buffer.getSize());
|
||||
// Reset deserialization
|
||||
if (status == Fw::FW_SERIALIZE_OK) {
|
||||
status = serializeRepr.moveDeserToOffset(Header::PACKET_DESCRIPTOR_OFFSET);
|
||||
}
|
||||
// Deserialize the packet type
|
||||
if (status == Fw::FW_SERIALIZE_OK) {
|
||||
FwPacketDescriptorType packetDescriptor;
|
||||
status = serializeRepr.deserialize(packetDescriptor);
|
||||
if (packetDescriptor != Fw::ComPacket::FW_PACKET_DP) {
|
||||
status = Fw::FW_SERIALIZE_FORMAT_ERROR;
|
||||
}
|
||||
}
|
||||
// Deserialize the container id
|
||||
if (status == Fw::FW_SERIALIZE_OK) {
|
||||
status = serializeRepr.deserialize(this->m_id);
|
||||
}
|
||||
// Deserialize the priority
|
||||
if (status == Fw::FW_SERIALIZE_OK) {
|
||||
status = serializeRepr.deserialize(this->m_priority);
|
||||
}
|
||||
// Deserialize the time tag
|
||||
if (status == Fw::FW_SERIALIZE_OK) {
|
||||
status = serializeRepr.deserialize(this->m_timeTag);
|
||||
}
|
||||
// Deserialize the processing types
|
||||
if (status == Fw::FW_SERIALIZE_OK) {
|
||||
status = serializeRepr.deserialize(this->m_procTypes);
|
||||
}
|
||||
// Deserialize the user data
|
||||
if (status == Fw::FW_SERIALIZE_OK) {
|
||||
const bool omitLength = true;
|
||||
const FwSizeType requestedSize = sizeof this->m_userData;
|
||||
FwSizeType receivedSize = requestedSize;
|
||||
status = serializeRepr.deserialize(this->m_userData, receivedSize, omitLength);
|
||||
if (receivedSize != requestedSize) {
|
||||
status = Fw::FW_DESERIALIZE_SIZE_MISMATCH;
|
||||
}
|
||||
}
|
||||
// Deserialize the data product state
|
||||
if (status == Fw::FW_SERIALIZE_OK) {
|
||||
status = serializeRepr.deserialize(this->m_dpState);
|
||||
}
|
||||
// Deserialize the data size
|
||||
if (status == Fw::FW_SERIALIZE_OK) {
|
||||
status = serializeRepr.deserialize(this->m_dataSize);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void DpContainer::serializeHeader() {
|
||||
FW_ASSERT(this->m_buffer.isValid());
|
||||
Fw::SerializeBufferBase& serializeRepr = this->m_buffer.getSerializeRepr();
|
||||
// Reset serialization
|
||||
serializeRepr.resetSer();
|
||||
// Serialize the packet type
|
||||
Fw::SerializeStatus status =
|
||||
serializeRepr.serialize(static_cast<FwPacketDescriptorType>(Fw::ComPacket::FW_PACKET_DP));
|
||||
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status));
|
||||
// Serialize the container id
|
||||
status = serializeRepr.serialize(this->m_id);
|
||||
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status));
|
||||
// Serialize the priority
|
||||
status = serializeRepr.serialize(this->m_priority);
|
||||
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status));
|
||||
// Serialize the time tag
|
||||
status = serializeRepr.serialize(this->m_timeTag);
|
||||
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status));
|
||||
// Serialize the processing types
|
||||
status = serializeRepr.serialize(this->m_procTypes);
|
||||
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status));
|
||||
// Serialize the user data
|
||||
const bool omitLength = true;
|
||||
status = serializeRepr.serialize(this->m_userData, sizeof this->m_userData, omitLength);
|
||||
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status));
|
||||
// Serialize the data product state
|
||||
status = serializeRepr.serialize(this->m_dpState);
|
||||
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status));
|
||||
// Serialize the data size
|
||||
status = serializeRepr.serialize(this->m_dataSize);
|
||||
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status));
|
||||
// Update the header hash
|
||||
this->updateHeaderHash();
|
||||
}
|
||||
|
||||
void DpContainer::setBuffer(const Buffer& buffer) {
|
||||
// Set the buffer
|
||||
this->m_buffer = buffer;
|
||||
// Check that the buffer is large enough to hold a data product packet
|
||||
FW_ASSERT(buffer.getSize() >= MIN_PACKET_SIZE, buffer.getSize(), MIN_PACKET_SIZE);
|
||||
// Initialize the data buffer
|
||||
U8* const buffAddr = buffer.getData();
|
||||
const FwSizeType dataCapacity = buffer.getSize() - MIN_PACKET_SIZE;
|
||||
// Check that data buffer is in bounds for packet buffer
|
||||
FW_ASSERT(DATA_OFFSET + dataCapacity <= buffer.getSize());
|
||||
U8* const dataAddr = &buffAddr[DATA_OFFSET];
|
||||
this->m_dataBuffer.setExtBuffer(dataAddr, dataCapacity);
|
||||
}
|
||||
|
||||
void DpContainer::updateHeaderHash() {
|
||||
Utils::HashBuffer hashBuffer;
|
||||
U8* const buffAddr = this->m_buffer.getData();
|
||||
Utils::Hash::hash(buffAddr, Header::SIZE, hashBuffer);
|
||||
ExternalSerializeBuffer serialBuffer(&buffAddr[HEADER_HASH_OFFSET], HASH_DIGEST_LENGTH);
|
||||
const Fw::SerializeStatus status = hashBuffer.copyRaw(serialBuffer, HASH_DIGEST_LENGTH);
|
||||
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status));
|
||||
}
|
||||
|
||||
void DpContainer::updateDataHash() {
|
||||
Utils::HashBuffer hashBuffer;
|
||||
U8* const buffAddrBase = this->m_buffer.getData();
|
||||
const U8* const dataAddr = &buffAddrBase[DATA_OFFSET];
|
||||
const FwSizeType dataSize = this->getDataSize();
|
||||
const FwSizeType bufferSize = this->m_buffer.getSize();
|
||||
FW_ASSERT(DATA_OFFSET + dataSize <= bufferSize, DATA_OFFSET + dataSize, bufferSize);
|
||||
Utils::Hash::hash(dataAddr, dataSize, hashBuffer);
|
||||
const FwSizeType dataHashOffset = this->getDataHashOffset();
|
||||
U8* const dataHashAddr = &buffAddrBase[dataHashOffset];
|
||||
FW_ASSERT(dataHashOffset + HASH_DIGEST_LENGTH <= bufferSize, dataHashOffset + HASH_DIGEST_LENGTH, bufferSize);
|
||||
ExternalSerializeBuffer serialBuffer(dataHashAddr, HASH_DIGEST_LENGTH);
|
||||
const Fw::SerializeStatus status = hashBuffer.copyRaw(serialBuffer, HASH_DIGEST_LENGTH);
|
||||
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Private member functions
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void DpContainer::initUserDataField() {
|
||||
(void)::memset(this->m_userData, 0, sizeof this->m_userData);
|
||||
}
|
||||
|
||||
} // namespace Fw
|
||||
225
Fw/Dp/DpContainer.hpp
Normal file
225
Fw/Dp/DpContainer.hpp
Normal file
@ -0,0 +1,225 @@
|
||||
// ======================================================================
|
||||
// \title DpContainer.hpp
|
||||
// \author bocchino
|
||||
// \brief hpp file for DpContainer
|
||||
// ======================================================================
|
||||
|
||||
#ifndef Fw_DpContainer_HPP
|
||||
#define Fw_DpContainer_HPP
|
||||
|
||||
#include "Fw/Buffer/Buffer.hpp"
|
||||
#include "Fw/Dp/DpStateEnumAc.hpp"
|
||||
#include "Fw/Time/Time.hpp"
|
||||
#include "Utils/Hash/Hash.hpp"
|
||||
#include "config/FppConstantsAc.hpp"
|
||||
#include "config/ProcTypeEnumAc.hpp"
|
||||
|
||||
namespace Fw {
|
||||
|
||||
//! A data product Container
|
||||
class DpContainer {
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Constants and Types
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! A DpContainer packet header
|
||||
struct Header {
|
||||
//! The type of user data
|
||||
using UserData = U8[DpCfg::CONTAINER_USER_DATA_SIZE];
|
||||
//! The offset for the packet descriptor field
|
||||
static constexpr FwSizeType PACKET_DESCRIPTOR_OFFSET = 0;
|
||||
//! The offset for the id field
|
||||
static constexpr FwSizeType ID_OFFSET = PACKET_DESCRIPTOR_OFFSET + sizeof(FwPacketDescriptorType);
|
||||
//! The offset for the priority field
|
||||
static constexpr FwDpPriorityType PRIORITY_OFFSET = ID_OFFSET + sizeof(FwDpIdType);
|
||||
//! The offset for the time tag field
|
||||
static constexpr FwSizeType TIME_TAG_OFFSET = PRIORITY_OFFSET + sizeof(FwDpPriorityType);
|
||||
//! The offset for the processing types field
|
||||
static constexpr FwSizeType PROC_TYPES_OFFSET = TIME_TAG_OFFSET + Time::SERIALIZED_SIZE;
|
||||
//! The offset for the user data field
|
||||
static constexpr FwSizeType USER_DATA_OFFSET = PROC_TYPES_OFFSET + sizeof(DpCfg::ProcType::SerialType);
|
||||
//! The offset of the data product state field
|
||||
static constexpr FwSizeType DP_STATE_OFFSET = USER_DATA_OFFSET + DpCfg::CONTAINER_USER_DATA_SIZE;
|
||||
//! The offset for the data size field
|
||||
static constexpr FwSizeType DATA_SIZE_OFFSET = DP_STATE_OFFSET + DpState::SERIALIZED_SIZE;
|
||||
//! The header size
|
||||
static constexpr FwSizeType SIZE = DATA_SIZE_OFFSET + sizeof(FwSizeType);
|
||||
};
|
||||
|
||||
//! The header hash offset
|
||||
static constexpr FwSizeType HEADER_HASH_OFFSET = Header::SIZE;
|
||||
//! The data offset
|
||||
static constexpr FwSizeType DATA_OFFSET = HEADER_HASH_OFFSET + HASH_DIGEST_LENGTH;
|
||||
//! The minimum packet size
|
||||
//! Reserve space for the header, the header hash, and the data hash
|
||||
//! This is also the number of non-data bytes in the packet
|
||||
static constexpr FwSizeType MIN_PACKET_SIZE = Header::SIZE + 2 * HASH_DIGEST_LENGTH;
|
||||
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Constructor for initialized container
|
||||
DpContainer(FwDpIdType id, //!< The container id
|
||||
const Fw::Buffer& buffer //!< The buffer
|
||||
);
|
||||
|
||||
//! Constructor for container with default initialization
|
||||
DpContainer();
|
||||
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Public member functions
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Get the container id
|
||||
//! \return The id
|
||||
FwDpIdType getId() const { return this->m_id; }
|
||||
|
||||
//! Get the data size
|
||||
//! \return The data size
|
||||
FwSizeType getDataSize() const { return this->m_dataSize; }
|
||||
|
||||
//! Get the packet buffer
|
||||
//! \return The buffer
|
||||
Fw::Buffer getBuffer() const { return this->m_buffer; }
|
||||
|
||||
//! Get the packet size corresponding to the data size
|
||||
FwSizeType getPacketSize() const { return getPacketSizeForDataSize(this->m_dataSize); }
|
||||
|
||||
//! Get the priority
|
||||
//! \return The priority
|
||||
FwDpPriorityType getPriority() const { return this->m_priority; }
|
||||
|
||||
//! Get the time tag
|
||||
//! \return The time tag
|
||||
Fw::Time getTimeTag() const { return this->m_timeTag; }
|
||||
|
||||
//! Get the processing types
|
||||
//! \return The processing types
|
||||
DpCfg::ProcType::SerialType getProcTypes() const { return this->m_procTypes; }
|
||||
|
||||
//! Deserialize the header from the packet buffer
|
||||
//! Buffer must be valid and large enough to hold a DP container packet
|
||||
//! \return The serialize status
|
||||
Fw::SerializeStatus deserializeHeader();
|
||||
|
||||
//! Serialize the header into the packet buffer and update the header hash
|
||||
//! Buffer must be valid and large enough to hold a DP container packet
|
||||
void serializeHeader();
|
||||
|
||||
//! Set the id
|
||||
void setId(FwDpIdType id //!< The id
|
||||
) {
|
||||
this->m_id = id;
|
||||
}
|
||||
|
||||
//! Set the priority
|
||||
void setPriority(FwDpPriorityType priority //!< The priority
|
||||
) {
|
||||
this->m_priority = priority;
|
||||
}
|
||||
|
||||
//! Set the time tag
|
||||
void setTimeTag(Fw::Time timeTag //!< The time tag
|
||||
) {
|
||||
this->m_timeTag = timeTag;
|
||||
}
|
||||
|
||||
//! Set the processing types bit mask
|
||||
void setProcTypes(DpCfg::ProcType::SerialType procTypes //!< The processing types
|
||||
) {
|
||||
this->m_procTypes = procTypes;
|
||||
}
|
||||
|
||||
//! Set the data product state
|
||||
void setDpState(DpState dpState //!< The data product state
|
||||
) {
|
||||
this->m_dpState = dpState;
|
||||
}
|
||||
|
||||
//! Set the data size
|
||||
void setDataSize(FwSizeType dataSize //!< The data size
|
||||
) {
|
||||
this->m_dataSize = dataSize;
|
||||
}
|
||||
|
||||
//! Set the packet buffer
|
||||
void setBuffer(const Buffer& buffer //!< The packet buffer
|
||||
);
|
||||
|
||||
//! Update the header hash
|
||||
void updateHeaderHash();
|
||||
|
||||
//! Get the data hash offset
|
||||
FwSizeType getDataHashOffset() const {
|
||||
// Data hash goes after the header, the header hash, and the data
|
||||
return Header::SIZE + HASH_DIGEST_LENGTH + this->m_dataSize;
|
||||
}
|
||||
|
||||
//! Update the data hash
|
||||
void updateDataHash();
|
||||
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Public static functions
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Get the packet size for a given data size
|
||||
static constexpr FwSizeType getPacketSizeForDataSize(FwSizeType dataSize //!< The data size
|
||||
) {
|
||||
return Header::SIZE + dataSize + 2 * HASH_DIGEST_LENGTH;
|
||||
}
|
||||
|
||||
PRIVATE:
|
||||
// ----------------------------------------------------------------------
|
||||
// Private member functions
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Initialize the user data field
|
||||
void initUserDataField();
|
||||
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Public member variables
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! The user data
|
||||
Header::UserData m_userData;
|
||||
|
||||
PROTECTED:
|
||||
// ----------------------------------------------------------------------
|
||||
// Protected member variables
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! The container id
|
||||
//! This is a system-global id (component-local id + component base id)
|
||||
FwDpIdType m_id;
|
||||
|
||||
//! The priority
|
||||
FwDpPriorityType m_priority;
|
||||
|
||||
//! The time tag
|
||||
Time m_timeTag;
|
||||
|
||||
//! The processing types
|
||||
DpCfg::ProcType::SerialType m_procTypes;
|
||||
|
||||
//! The data product state
|
||||
DpState m_dpState;
|
||||
|
||||
//! The data size
|
||||
FwSizeType m_dataSize;
|
||||
|
||||
//! The packet buffer
|
||||
Buffer m_buffer;
|
||||
|
||||
//! The data buffer
|
||||
Fw::ExternalSerializeBuffer m_dataBuffer;
|
||||
};
|
||||
|
||||
} // end namespace Fw
|
||||
|
||||
#endif
|
||||
127
Fw/Dp/docs/sdd.md
Normal file
127
Fw/Dp/docs/sdd.md
Normal file
@ -0,0 +1,127 @@
|
||||
\page FwDp Framework Support for Data Products
|
||||
# Framework Support for Data Products
|
||||
|
||||
## 1. Introduction
|
||||
|
||||
This build module defines FPP ports and C++ classes that support
|
||||
the collection and storage of data products.
|
||||
For more information on data products and records, see the
|
||||
[data products documentation](../../../docs/Design/data-products.md).
|
||||
|
||||
## 2. Configuration
|
||||
|
||||
The following types and constants are configurable via the file
|
||||
[`config/DpCfg.fpp`](../../../config/DpCfg.fpp):
|
||||
|
||||
| Name | Kind | Description |
|
||||
| ---- | ---- | ---- |
|
||||
| `Fw::DpCfg::ProcType` | Type | The enumeration type that defines the bit mask for selecting a type of processing. The processing is applied to a container before writing it to disk. |
|
||||
| `Fw::DpCfg::CONTAINER_USER_DATA_SIZE` | Constant | The size of the user-configurable data in the container packet header. |
|
||||
|
||||
## 3. FPP Types
|
||||
|
||||
This build module defines the following FPP types:
|
||||
|
||||
1. `DpState`: An enumeration describing the state of a data product.
|
||||
|
||||
## 4. FPP Ports
|
||||
|
||||
This build module defines the following FPP ports:
|
||||
|
||||
1. `DpGet`: A port for synchronously getting a buffer to back
|
||||
a data product container.
|
||||
|
||||
1. `DpRequest`: A port for sending a request for a buffer to back
|
||||
a data product container.
|
||||
|
||||
1. `DpResponse`: A port for receiving a response to a buffer request.
|
||||
|
||||
1. `DpSend`: A port for sending a buffer holding data products.
|
||||
|
||||
For more information, see the file [`Dp.fpp`](../Dp.fpp) in the parent
|
||||
directory.
|
||||
|
||||
## 5. C++ Classes
|
||||
|
||||
This module defines a C++ class `DpContainer`.
|
||||
`DpContainer` is the base class for a data product container.
|
||||
When you specify a container _C_ in an FPP component model,
|
||||
the auto-generated C++ for the component defines a container
|
||||
class for _C_.
|
||||
The container class is derived from `DpContainer`.
|
||||
It provides all the generic operations defined in `DpContainer`
|
||||
plus the operations that are specific to _C_, for example
|
||||
serializing the specific types of data that _C_ can store.
|
||||
|
||||
<a name="serial-format"></a>
|
||||
### 5.1. Serialized Container Format
|
||||
|
||||
In serialized form, each data product container consists of the following
|
||||
elements: a header, a header hash, data, and a data hash.
|
||||
|
||||
#### 5.1.1. Header
|
||||
|
||||
The data product header has the following format.
|
||||
|
||||
|Field Name|Data Type|Serialized Size|Description|
|
||||
|----------|---------|---------------|-----------|
|
||||
|`PacketDescriptor`|`FwPacketDescriptorType`|`sizeof(FwPacketDescriptorType)`|The F Prime packet descriptor [`FW_PACKET_DP`](../../../Fw/Com/ComPacket.hpp)|
|
||||
|`Id`|`FwDpIdType`|`sizeof(FwDpIdType)`|The container ID. This is a system-global ID (component-local ID + component base ID)|
|
||||
|`Priority`|`FwDpPriorityType`|`sizeof(FwDpPriorityType)`|The container priority|
|
||||
|`TimeTag`|`Fw::Time`|`Fw::Time::SERIALIZED_SIZE`|The time tag associated with the container|
|
||||
|`ProcTypes`|`Fw::DpCfg::ProcType::SerialType`|`sizeof(Fw::DpCfg::ProcType::SerialType)`|The processing types, represented as a bit mask|
|
||||
|`UserData`|`Header::UserData`|`DpCfg::CONTAINER_USER_DATA_SIZE`|User-configurable data|
|
||||
|`DpState`|`DpState`|`DpState::SERIALIZED_SIZE`|The data product state
|
||||
|`DataSize`|`FwSizeType`|`sizeof(FwSizeType)`|The size of the data payload in bytes|
|
||||
|
||||
`Header::UserData` is an array of `U8` of size `Fw::DpCfg::CONTAINER_USER_DATA_SIZE`.
|
||||
|
||||
#### 5.1.2. Header Hash
|
||||
|
||||
The header hash has the following format.
|
||||
|
||||
|Field Name|Serialized Size|Description|
|
||||
|----------|---------------|-----------|
|
||||
|`Header Hash`|[`HASH_DIGEST_LENGTH`](../../../Utils/Hash/README.md)|The hash value guarding the header.|
|
||||
|
||||
#### 5.1.3. Data
|
||||
|
||||
The data is a sequence of records.
|
||||
The serialized format of each record _R_ depends on whether _R_ is a
|
||||
single-value record or an array record.
|
||||
|
||||
**Single-value records:**
|
||||
A single-value record is specified in FPP in the form `product record` _name_ `:` _type_.
|
||||
The record has name _name_ and represents one item of data of type _type_.
|
||||
The type may be any FPP type, including a struct or array type.
|
||||
Single-value records with _type = T_ have the following format:
|
||||
|
||||
|Field Name|Data Type|Serialized Size|Description|
|
||||
|----------|---------|---------------|-----------|
|
||||
|`Id`|`FwDpIdType`|`sizeof(FwDpIdType)`|The record ID|
|
||||
|`Data`|_T_|`sizeof(`_T_`)` if _T_ is a primitive type; otherwise _T_`::SERIALIZED_SIZE`|The serialized data|
|
||||
|
||||
**Array records:**
|
||||
An array record is specified in FPP in the form `product record` _name_ `:` _type_ `array`.
|
||||
The record has name _name_ and represents an array of items of type _type_.
|
||||
The type may be any FPP type, including a struct or array type.
|
||||
Array records with _type = T_ have the following format:
|
||||
|
||||
|Field Name|Data Type|Serialized Size|Description|
|
||||
|----------|---------|---------------|-----------|
|
||||
|`Id`|`FwDpIdType`|`sizeof(FwDpIdType)`|The record ID|
|
||||
|`Size`|`FwSizeType`|`sizeof(FwSizeType)`|The number _n_ of elements in the record|
|
||||
|`Data`|Array of _n_ _T_|_n_ * [`sizeof(`_T_`)` if _T_ is a primitive type; otherwise _T_`::SERIALIZED_SIZE`]|_n_ elements, each of type _T_|
|
||||
|
||||
#### 5.1.4. Data Hash
|
||||
|
||||
The data hash has the following format.
|
||||
|
||||
|Field Name|Serialized Size|Description|
|
||||
|----------|---------------|-----------|
|
||||
|`Data Hash`|[`HASH_DIGEST_LENGTH`](../../../Utils/Hash/README.md)|The hash value guarding the data.|
|
||||
|
||||
### 5.2. Further Information
|
||||
|
||||
For more information on the `DpContainer` class, see the file [`DpContainer.hpp`](../DpContainer.hpp) in
|
||||
the parent directory.
|
||||
147
Fw/Dp/test/ut/TestMain.cpp
Normal file
147
Fw/Dp/test/ut/TestMain.cpp
Normal file
@ -0,0 +1,147 @@
|
||||
// ----------------------------------------------------------------------
|
||||
// TestMain.cpp
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "Fw/Dp/DpContainer.hpp"
|
||||
#include "Fw/Dp/test/util/DpContainerHeader.hpp"
|
||||
#include "Fw/Test/UnitTest.hpp"
|
||||
#include "STest/Pick/Pick.hpp"
|
||||
#include "STest/Random/Random.hpp"
|
||||
|
||||
using namespace Fw;
|
||||
|
||||
constexpr FwSizeType DATA_SIZE = 100;
|
||||
constexpr FwSizeType PACKET_SIZE = DpContainer::getPacketSizeForDataSize(DATA_SIZE);
|
||||
U8 bufferData[PACKET_SIZE];
|
||||
DpContainer::Header::UserData userData;
|
||||
|
||||
void checkHeader(FwDpIdType id, Fw::Buffer& buffer, DpContainer& container) {
|
||||
// Check the packet size
|
||||
const FwSizeType expectedPacketSize = Fw::DpContainer::MIN_PACKET_SIZE;
|
||||
ASSERT_EQ(container.getPacketSize(), expectedPacketSize);
|
||||
// Set the priority
|
||||
const FwDpPriorityType priority = STest::Pick::lowerUpper(0, std::numeric_limits<FwDpPriorityType>::max());
|
||||
container.setPriority(priority);
|
||||
// Set the time tag
|
||||
const U32 seconds = STest::Pick::any();
|
||||
const U32 useconds = STest::Pick::startLength(0, 1000000);
|
||||
Fw::Time timeTag(seconds, useconds);
|
||||
container.setTimeTag(timeTag);
|
||||
// Set the processing types
|
||||
const FwSizeType numProcTypeStates = 1 << DpCfg::ProcType::NUM_CONSTANTS;
|
||||
const DpCfg::ProcType::SerialType procTypes = STest::Pick::startLength(0, numProcTypeStates);
|
||||
container.setProcTypes(procTypes);
|
||||
// Set the user data
|
||||
for (U8& data : userData) {
|
||||
data = static_cast<U8>(STest::Pick::any());
|
||||
}
|
||||
FW_ASSERT(sizeof userData == sizeof container.m_userData);
|
||||
(void)::memcpy(container.m_userData, userData, sizeof container.m_userData);
|
||||
// Set the DP state
|
||||
const DpState dpState(static_cast<DpState::T>(STest::Pick::startLength(0, DpState::NUM_CONSTANTS)));
|
||||
container.setDpState(dpState);
|
||||
// Set the data size
|
||||
container.setDataSize(DATA_SIZE);
|
||||
// Test serialization: Serialize the header
|
||||
container.serializeHeader();
|
||||
TestUtil::DpContainerHeader header;
|
||||
// Update the data hash
|
||||
container.updateDataHash();
|
||||
// Deserialize the header and check the hashes
|
||||
header.deserialize(__FILE__, __LINE__, buffer);
|
||||
// Check the deserialized header fields
|
||||
header.check(__FILE__, __LINE__, buffer, id, priority, timeTag, procTypes, userData, dpState, DATA_SIZE);
|
||||
// Test deserialization: Deserialize the header into a new container
|
||||
DpContainer deserContainer;
|
||||
deserContainer.setBuffer(container.getBuffer());
|
||||
const Fw::SerializeStatus serialStatus = deserContainer.deserializeHeader();
|
||||
ASSERT_EQ(serialStatus, Fw::FW_SERIALIZE_OK);
|
||||
// Clear out the header in the buffer
|
||||
FW_ASSERT(buffer.isValid());
|
||||
::memset(buffer.getData(), 0, DpContainer::Header::SIZE);
|
||||
// Serialize the header from the new container
|
||||
deserContainer.serializeHeader();
|
||||
// Deserialize and check the header
|
||||
header.deserialize(__FILE__, __LINE__, buffer);
|
||||
header.check(__FILE__, __LINE__, buffer, id, priority, timeTag, procTypes, userData, dpState, DATA_SIZE);
|
||||
}
|
||||
|
||||
void checkBuffers(DpContainer& container, FwSizeType bufferSize) {
|
||||
// Check the packet buffer
|
||||
ASSERT_EQ(container.m_buffer.getSize(), bufferSize);
|
||||
// Check the data buffer
|
||||
U8 *const buffPtr = container.m_buffer.getData();
|
||||
U8 *const dataPtr = &buffPtr[Fw::DpContainer::DATA_OFFSET];
|
||||
const FwSizeType dataCapacity = container.m_buffer.getSize() - Fw::DpContainer::MIN_PACKET_SIZE;
|
||||
ASSERT_EQ(container.m_dataBuffer.getBuffAddr(), dataPtr);
|
||||
ASSERT_EQ(container.m_dataBuffer.getBuffCapacity(), dataCapacity);
|
||||
}
|
||||
|
||||
void fillWithData(Fw::Buffer& buffer) {
|
||||
U8 *const buffAddrBase = buffer.getData();
|
||||
U8 *const dataAddr = &buffAddrBase[DpContainer::DATA_OFFSET];
|
||||
for (FwSizeType i = 0; i < DATA_SIZE; i++) {
|
||||
dataAddr[i] = static_cast<U8>(STest::Pick::any());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Header, BufferInConstructor) {
|
||||
COMMENT("Test header serialization with buffer in constructor");
|
||||
// Create a buffer
|
||||
Fw::Buffer buffer(bufferData, sizeof bufferData);
|
||||
// Fill with data
|
||||
fillWithData(buffer);
|
||||
// Use the buffer to create a container
|
||||
const FwDpIdType id = STest::Pick::lowerUpper(0, std::numeric_limits<FwDpIdType>::max());
|
||||
DpContainer container(id, buffer);
|
||||
// Check the header
|
||||
checkHeader(id, buffer, container);
|
||||
// Check the buffers
|
||||
checkBuffers(container, sizeof bufferData);
|
||||
}
|
||||
|
||||
TEST(Header, BufferSet) {
|
||||
COMMENT("Test header serialization with buffer set");
|
||||
// Create a buffer
|
||||
Fw::Buffer buffer(bufferData, sizeof bufferData);
|
||||
// Fill with data
|
||||
fillWithData(buffer);
|
||||
// Use the buffer to create a container
|
||||
const FwDpIdType id = STest::Pick::lowerUpper(0, std::numeric_limits<FwDpIdType>::max());
|
||||
DpContainer container;
|
||||
container.setId(id);
|
||||
container.setBuffer(buffer);
|
||||
// Check the header
|
||||
checkHeader(id, buffer, container);
|
||||
// Check the buffers
|
||||
checkBuffers(container, sizeof bufferData);
|
||||
}
|
||||
|
||||
TEST(Header, BadPacketDescriptor) {
|
||||
COMMENT("Test header serialization with bad packet descriptor");
|
||||
// Create a buffer
|
||||
Fw::Buffer buffer(bufferData, sizeof bufferData);
|
||||
// Set the packet descriptor to a bad value
|
||||
Fw::SerializeBufferBase& serialRepr = buffer.getSerializeRepr();
|
||||
const FwPacketDescriptorType badPacketDescriptor = Fw::ComPacket::FW_PACKET_DP + 1;
|
||||
Fw::SerializeStatus status = serialRepr.serialize(badPacketDescriptor);
|
||||
ASSERT_EQ(status, Fw::FW_SERIALIZE_OK);
|
||||
// Use the buffer to create a container
|
||||
DpContainer container;
|
||||
container.setBuffer(buffer);
|
||||
// Deserialize the header
|
||||
const Fw::SerializeStatus serialStatus = container.deserializeHeader();
|
||||
// Check the error
|
||||
ASSERT_EQ(serialStatus, Fw::FW_SERIALIZE_FORMAT_ERROR);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
STest::Random::seed();
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
200
Fw/Dp/test/util/DpContainerHeader.hpp
Normal file
200
Fw/Dp/test/util/DpContainerHeader.hpp
Normal file
@ -0,0 +1,200 @@
|
||||
// ======================================================================
|
||||
// \title DpContainerHeader.hpp
|
||||
// \author bocchino
|
||||
// \brief hpp file for DpContainer header test utility
|
||||
// ======================================================================
|
||||
|
||||
#ifndef Fw_TestUtil_DpContainerHeader_HPP
|
||||
#define Fw_TestUtil_DpContainerHeader_HPP
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "FpConfig.hpp"
|
||||
#include "Fw/Com/ComPacket.hpp"
|
||||
#include "Fw/Dp/DpContainer.hpp"
|
||||
|
||||
#define DP_CONTAINER_HEADER_ASSERT_MSG(actual, expected) \
|
||||
<< file << ":" << line << "\n" \
|
||||
<< " Actual value is " << actual << "\n" \
|
||||
<< " Expected value is " << expected
|
||||
#define DP_CONTAINER_HEADER_ASSERT_EQ(actual, expected) \
|
||||
ASSERT_EQ(actual, expected) DP_CONTAINER_HEADER_ASSERT_MSG(actual, expected)
|
||||
#define DP_CONTAINER_HEADER_ASSERT_GE(actual, expected) \
|
||||
ASSERT_GE(actual, expected) DP_CONTAINER_HEADER_ASSERT_MSG(actual, expected)
|
||||
|
||||
namespace Fw {
|
||||
namespace TestUtil {
|
||||
|
||||
//! A container packet header for testing
|
||||
struct DpContainerHeader {
|
||||
DpContainerHeader() : m_id(0), m_priority(0), m_timeTag(), m_procTypes(0), m_dpState(), m_dataSize(0) {}
|
||||
|
||||
//! Move the buffer deserialization to the specified offset
|
||||
static void moveDeserToOffset(const char* const file, //!< The call site file name
|
||||
const U32 line, //!< The call site line number
|
||||
Buffer& buffer, //!< The buffer
|
||||
FwSizeType offset //!< The offset
|
||||
) {
|
||||
Fw::SerializeBufferBase& serializeRepr = buffer.getSerializeRepr();
|
||||
// Reset deserialization
|
||||
Fw::SerializeStatus status = serializeRepr.setBuffLen(buffer.getSize());
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(status, FW_SERIALIZE_OK);
|
||||
status = serializeRepr.moveDeserToOffset(offset);
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(status, FW_SERIALIZE_OK);
|
||||
}
|
||||
|
||||
//! Deserialize a header from a packet buffer
|
||||
//! Check that the serialization succeeded at every step
|
||||
//! Check the header hash and the data hash
|
||||
void deserialize(const char* const file, //!< The call site file name
|
||||
const U32 line, //!< The call site line number
|
||||
Fw::Buffer& buffer //!< The packet buffer
|
||||
) {
|
||||
Fw::SerializeBufferBase& serializeRepr = buffer.getSerializeRepr();
|
||||
// Deserialize the packet descriptor
|
||||
FwPacketDescriptorType packetDescriptor = Fw::ComPacket::FW_PACKET_UNKNOWN;
|
||||
// Deserialize the packet descriptor
|
||||
DpContainerHeader::moveDeserToOffset(file, line, buffer, DpContainer::Header::PACKET_DESCRIPTOR_OFFSET);
|
||||
Fw::SerializeStatus status = serializeRepr.deserialize(packetDescriptor);
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(status, FW_SERIALIZE_OK);
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(packetDescriptor, Fw::ComPacket::FW_PACKET_DP);
|
||||
// Deserialize the container id
|
||||
DpContainerHeader::moveDeserToOffset(file, line, buffer, DpContainer::Header::ID_OFFSET);
|
||||
status = serializeRepr.deserialize(this->m_id);
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(status, FW_SERIALIZE_OK);
|
||||
// Deserialize the priority
|
||||
DpContainerHeader::moveDeserToOffset(file, line, buffer, DpContainer::Header::PRIORITY_OFFSET);
|
||||
status = serializeRepr.deserialize(this->m_priority);
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(status, FW_SERIALIZE_OK);
|
||||
// Deserialize the time tag
|
||||
DpContainerHeader::moveDeserToOffset(file, line, buffer, DpContainer::Header::TIME_TAG_OFFSET);
|
||||
status = serializeRepr.deserialize(this->m_timeTag);
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(status, FW_SERIALIZE_OK);
|
||||
// Deserialize the processing type
|
||||
DpContainerHeader::moveDeserToOffset(file, line, buffer, DpContainer::Header::PROC_TYPES_OFFSET);
|
||||
status = serializeRepr.deserialize(this->m_procTypes);
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(status, FW_SERIALIZE_OK);
|
||||
// Deserialize the user data
|
||||
DpContainerHeader::moveDeserToOffset(file, line, buffer, DpContainer::Header::USER_DATA_OFFSET);
|
||||
NATIVE_UINT_TYPE size = sizeof this->m_userData;
|
||||
const bool omitLength = true;
|
||||
status = serializeRepr.deserialize(this->m_userData, size, omitLength);
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(status, FW_SERIALIZE_OK);
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(size, sizeof this->m_userData);
|
||||
// Deserialize the data product state
|
||||
DpContainerHeader::moveDeserToOffset(file, line, buffer, DpContainer::Header::DP_STATE_OFFSET);
|
||||
status = serializeRepr.deserialize(this->m_dpState);
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(status, FW_SERIALIZE_OK);
|
||||
// Deserialize the data size
|
||||
DpContainerHeader::moveDeserToOffset(file, line, buffer, DpContainer::Header::DATA_SIZE_OFFSET);
|
||||
status = serializeRepr.deserialize(this->m_dataSize);
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(status, FW_SERIALIZE_OK);
|
||||
// After deserializing time, the deserialization index should be at
|
||||
// the header hash offset
|
||||
checkDeserialAtOffset(serializeRepr, DpContainer::HEADER_HASH_OFFSET);
|
||||
// Check the header hash
|
||||
checkHeaderHash(file, line, buffer);
|
||||
// Check the data hash
|
||||
this->checkDataHash(file, line, buffer);
|
||||
// Move the deserialization pointer to the data offset
|
||||
DpContainerHeader::moveDeserToOffset(file, line, buffer, DpContainer::DATA_OFFSET);
|
||||
}
|
||||
|
||||
//! Check the header hash
|
||||
static void checkHeaderHash(const char* const file, //!< The call site file name
|
||||
const U32 line, //!< The call site line number
|
||||
Fw::Buffer& buffer //!< The packet buffer
|
||||
) {
|
||||
Utils::HashBuffer computedHashBuffer;
|
||||
U8* const buffAddr = buffer.getData();
|
||||
Utils::Hash::hash(buffAddr, DpContainer::Header::SIZE, computedHashBuffer);
|
||||
Utils::HashBuffer storedHashBuffer(&buffAddr[DpContainer::HEADER_HASH_OFFSET], HASH_DIGEST_LENGTH);
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(computedHashBuffer, storedHashBuffer);
|
||||
}
|
||||
|
||||
//! Check the data hash
|
||||
void checkDataHash(const char* const file, //!< The call site file name
|
||||
const U32 line, //!< The call site line number
|
||||
Fw::Buffer& buffer //!< The packet buffer
|
||||
) {
|
||||
Utils::HashBuffer computedHashBuffer;
|
||||
U8* const buffAddrBase = buffer.getData();
|
||||
U8* const dataAddr = &buffAddrBase[DpContainer::DATA_OFFSET];
|
||||
Utils::Hash::hash(dataAddr, this->m_dataSize, computedHashBuffer);
|
||||
DpContainer container(this->m_id, buffer);
|
||||
container.setDataSize(this->m_dataSize);
|
||||
const FwSizeType dataHashOffset = container.getDataHashOffset();
|
||||
Utils::HashBuffer storedHashBuffer(&buffAddrBase[dataHashOffset], HASH_DIGEST_LENGTH);
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(computedHashBuffer, storedHashBuffer);
|
||||
}
|
||||
|
||||
//! Check a packet header against a buffer
|
||||
void check(const char* const file, //!< The call site file name
|
||||
const U32 line, //!< The call site line number
|
||||
const Fw::Buffer& buffer, //!< The buffer
|
||||
FwDpIdType id, //!< The expected id
|
||||
FwDpPriorityType priority, //!< The expected priority
|
||||
const Fw::Time& timeTag, //!< The expected time tag
|
||||
DpCfg::ProcType::SerialType procTypes, //!< The expected processing types
|
||||
const DpContainer::Header::UserData& userData, //!< The expected user data
|
||||
DpState dpState, //!< The expected dp state
|
||||
FwSizeType dataSize //!< The expected data size
|
||||
) const {
|
||||
// Check the buffer size
|
||||
const FwSizeType bufferSize = buffer.getSize();
|
||||
const FwSizeType minBufferSize = Fw::DpContainer::MIN_PACKET_SIZE;
|
||||
DP_CONTAINER_HEADER_ASSERT_GE(bufferSize, minBufferSize);
|
||||
// Check the container id
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(this->m_id, id);
|
||||
// Check the priority
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(this->m_priority, priority);
|
||||
// Check the time tag
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(this->m_timeTag, timeTag);
|
||||
// Check the deserialized processing types
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(this->m_procTypes, procTypes);
|
||||
// Check the user data
|
||||
for (FwSizeType i = 0; i < DpCfg::CONTAINER_USER_DATA_SIZE; ++i) {
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(this->m_userData[i], userData[i]);
|
||||
}
|
||||
// Check the deserialized data product state
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(this->m_dpState, dpState);
|
||||
// Check the data size
|
||||
DP_CONTAINER_HEADER_ASSERT_EQ(this->m_dataSize, dataSize);
|
||||
}
|
||||
|
||||
//! Check that the serialize repr is at the specified deserialization offset
|
||||
static void checkDeserialAtOffset(SerializeBufferBase& serialRepr, //!< The serialize repr
|
||||
FwSizeType offset //!< The offset
|
||||
) {
|
||||
const U8* buffAddr = serialRepr.getBuffAddr();
|
||||
const U8* buffAddrLeft = serialRepr.getBuffAddrLeft();
|
||||
ASSERT_EQ(buffAddrLeft, &buffAddr[offset]);
|
||||
}
|
||||
|
||||
//! The container id
|
||||
FwDpIdType m_id;
|
||||
|
||||
//! The priority
|
||||
FwDpPriorityType m_priority;
|
||||
|
||||
//! The time tag
|
||||
Time m_timeTag;
|
||||
|
||||
//! The processing types
|
||||
DpCfg::ProcType::SerialType m_procTypes;
|
||||
|
||||
//! The user data
|
||||
U8 m_userData[DpCfg::CONTAINER_USER_DATA_SIZE];
|
||||
|
||||
//! The data product state
|
||||
DpState m_dpState;
|
||||
|
||||
//! The data size
|
||||
FwSizeType m_dataSize;
|
||||
};
|
||||
|
||||
} // namespace TestUtil
|
||||
|
||||
} // end namespace Fw
|
||||
|
||||
#endif
|
||||
@ -581,7 +581,23 @@ namespace Fw {
|
||||
this->m_deserLoc = 0;
|
||||
}
|
||||
|
||||
SerializeStatus SerializeBufferBase::deserializeSkip(NATIVE_UINT_TYPE numBytesToSkip)
|
||||
SerializeStatus SerializeBufferBase::serializeSkip(FwSizeType numBytesToSkip)
|
||||
{
|
||||
Fw::SerializeStatus status = FW_SERIALIZE_OK;
|
||||
// compute new deser loc
|
||||
const FwSizeType newSerLoc = this->m_serLoc + numBytesToSkip;
|
||||
// check for room
|
||||
if (newSerLoc <= this->getBuffCapacity()) {
|
||||
// update deser loc
|
||||
this->m_serLoc = newSerLoc;
|
||||
}
|
||||
else {
|
||||
status = FW_SERIALIZE_NO_ROOM_LEFT;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
SerializeStatus SerializeBufferBase::deserializeSkip(FwSizeType numBytesToSkip)
|
||||
{
|
||||
// check for room
|
||||
if (this->getBuffLength() == this->m_deserLoc) {
|
||||
@ -594,6 +610,19 @@ namespace Fw {
|
||||
return FW_SERIALIZE_OK;
|
||||
}
|
||||
|
||||
SerializeStatus SerializeBufferBase::moveSerToOffset(FwSizeType offset) {
|
||||
// Reset serialization
|
||||
this->resetSer();
|
||||
// Advance to offset
|
||||
return this->serializeSkip(offset);
|
||||
}
|
||||
SerializeStatus SerializeBufferBase::moveDeserToOffset(FwSizeType offset) {
|
||||
// Reset deserialization
|
||||
this->resetDeser();
|
||||
// Advance to offset
|
||||
return this->deserializeSkip(offset);
|
||||
}
|
||||
|
||||
NATIVE_UINT_TYPE SerializeBufferBase::getBuffLength() const {
|
||||
return this->m_serLoc;
|
||||
}
|
||||
|
||||
@ -115,7 +115,11 @@ namespace Fw {
|
||||
void resetSer(); //!< reset to beginning of buffer to reuse for serialization
|
||||
void resetDeser(); //!< reset deserialization to beginning
|
||||
|
||||
SerializeStatus deserializeSkip(NATIVE_UINT_TYPE numBytesToSkip); //!< Skips the number of specified bytes for deserialization
|
||||
SerializeStatus moveSerToOffset(FwSizeType offset); //!< Moves serialization to the specified offset
|
||||
SerializeStatus moveDeserToOffset(FwSizeType offset); //!< Moves deserialization to the specified offset
|
||||
|
||||
SerializeStatus serializeSkip(FwSizeType numBytesToSkip); //!< Skips the number of specified bytes for serialization
|
||||
SerializeStatus deserializeSkip(FwSizeType numBytesToSkip); //!< Skips the number of specified bytes for deserialization
|
||||
virtual NATIVE_UINT_TYPE getBuffCapacity() const = 0; //!< returns capacity, not current size, of buffer
|
||||
NATIVE_UINT_TYPE getBuffLength() const; //!< returns current buffer size
|
||||
NATIVE_UINT_TYPE getBuffLeft() const; //!< returns how much deserialization buffer is left
|
||||
|
||||
44
Svc/DpCatalog/docs/sdd.md
Normal file
44
Svc/DpCatalog/docs/sdd.md
Normal file
@ -0,0 +1,44 @@
|
||||
\page SvcDpCatalogComponent Svc::DpCatalog Component
|
||||
# Svc::DpCatalog (Active Component)
|
||||
|
||||
## 1. Introduction
|
||||
|
||||
TODO
|
||||
## 2. Requirements
|
||||
|
||||
TODO
|
||||
|
||||
## 3. Design
|
||||
|
||||
### 3.1. Component Diagram
|
||||
|
||||
TODO
|
||||
|
||||
### 3.2. Ports
|
||||
|
||||
`DpCatalog` has the following ports:
|
||||
|
||||
TODO
|
||||
|
||||
### 3.3. State
|
||||
|
||||
`DpCatalog` maintains the following state:
|
||||
|
||||
TODO
|
||||
|
||||
### 3.4. Runtime Setup
|
||||
|
||||
TODO
|
||||
|
||||
### 3.5. Port Handlers
|
||||
|
||||
TODO
|
||||
|
||||
<a name="ground_interface"></a>
|
||||
## 4. Ground Interface
|
||||
|
||||
TODO
|
||||
|
||||
## 5. Example Uses
|
||||
|
||||
TODO
|
||||
BIN
Svc/DpManager/docs/img/DpManager.png
Normal file
BIN
Svc/DpManager/docs/img/DpManager.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 166 KiB |
BIN
Svc/DpManager/docs/img/top/buffer-get.png
Normal file
BIN
Svc/DpManager/docs/img/top/buffer-get.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 155 KiB |
13
Svc/DpManager/docs/img/top/buffer-get.txt
Normal file
13
Svc/DpManager/docs/img/top/buffer-get.txt
Normal file
@ -0,0 +1,13 @@
|
||||
producer
|
||||
productGetOut
|
||||
0
|
||||
dpManager
|
||||
productGetIn
|
||||
0
|
||||
|
||||
dpManager
|
||||
bufferGetOut
|
||||
0
|
||||
bufferManager
|
||||
bufferGetCallee
|
||||
0
|
||||
110
Svc/DpManager/docs/img/top/buffer-request.json
Normal file
110
Svc/DpManager/docs/img/top/buffer-request.json
Normal file
@ -0,0 +1,110 @@
|
||||
{
|
||||
"columns" : [
|
||||
[
|
||||
{
|
||||
"instanceName" : "client",
|
||||
"inputPorts" : [
|
||||
{
|
||||
"name" : "productRecvIn",
|
||||
"portNumbers" : [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputPorts" : [
|
||||
{
|
||||
"name" : "productRequestOut",
|
||||
"portNumbers" : [
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"instanceName" : "dpManager",
|
||||
"inputPorts" : [
|
||||
{
|
||||
"name" : "productRequestIn",
|
||||
"portNumbers" : [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputPorts" : [
|
||||
{
|
||||
"name" : "bufferGetOut",
|
||||
"portNumbers" : [
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "productResponseOut",
|
||||
"portNumbers" : [
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"instanceName" : "bufferManager",
|
||||
"inputPorts" : [
|
||||
{
|
||||
"name" : "bufferGetCallee",
|
||||
"portNumbers" : [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputPorts" : []
|
||||
}
|
||||
]
|
||||
],
|
||||
"connections" : [
|
||||
[
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
BIN
Svc/DpManager/docs/img/top/buffer-request.png
Normal file
BIN
Svc/DpManager/docs/img/top/buffer-request.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 219 KiB |
20
Svc/DpManager/docs/img/top/buffer-request.txt
Normal file
20
Svc/DpManager/docs/img/top/buffer-request.txt
Normal file
@ -0,0 +1,20 @@
|
||||
producer
|
||||
productRequestOut
|
||||
0
|
||||
dpManager
|
||||
productRequestIn
|
||||
0
|
||||
|
||||
dpManager
|
||||
bufferGetOut
|
||||
0
|
||||
bufferManager
|
||||
bufferGetCallee
|
||||
0
|
||||
|
||||
dpManager
|
||||
productResponseOut
|
||||
0
|
||||
producer
|
||||
productRecvIn
|
||||
0
|
||||
118
Svc/DpManager/docs/img/top/product-send.json
Normal file
118
Svc/DpManager/docs/img/top/product-send.json
Normal file
@ -0,0 +1,118 @@
|
||||
{
|
||||
"columns" : [
|
||||
[
|
||||
{
|
||||
"instanceName" : "client",
|
||||
"inputPorts" : [],
|
||||
"outputPorts" : [
|
||||
{
|
||||
"name" : "productSendOut",
|
||||
"portNumbers" : [
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"instanceName" : "dpManager",
|
||||
"inputPorts" : [
|
||||
{
|
||||
"name" : "productSendIn",
|
||||
"portNumbers" : [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputPorts" : [
|
||||
{
|
||||
"name" : "productSendOut",
|
||||
"portNumbers" : [
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"instanceName" : "dpWriter",
|
||||
"inputPorts" : [
|
||||
{
|
||||
"name" : "bufferSendIn",
|
||||
"portNumbers" : [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputPorts" : [
|
||||
{
|
||||
"name" : "bufferSendOut",
|
||||
"portNumbers" : [
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"instanceName" : "bufferManager",
|
||||
"inputPorts" : [
|
||||
{
|
||||
"name" : "bufferSendIn",
|
||||
"portNumbers" : [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputPorts" : []
|
||||
}
|
||||
]
|
||||
],
|
||||
"connections" : [
|
||||
[
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
3,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
BIN
Svc/DpManager/docs/img/top/product-send.png
Normal file
BIN
Svc/DpManager/docs/img/top/product-send.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 188 KiB |
20
Svc/DpManager/docs/img/top/product-send.txt
Normal file
20
Svc/DpManager/docs/img/top/product-send.txt
Normal file
@ -0,0 +1,20 @@
|
||||
producer
|
||||
productSendOut
|
||||
0
|
||||
dpManager
|
||||
productSendIn
|
||||
0
|
||||
|
||||
dpManager
|
||||
productSendOut
|
||||
0
|
||||
dpWriter
|
||||
bufferSendIn
|
||||
0
|
||||
|
||||
dpWriter
|
||||
bufferSendOut
|
||||
0
|
||||
bufferManager
|
||||
bufferSendIn
|
||||
0
|
||||
253
Svc/DpManager/docs/sdd.md
Normal file
253
Svc/DpManager/docs/sdd.md
Normal file
@ -0,0 +1,253 @@
|
||||
\page SvcDpManagerComponent Svc::DpManager Component
|
||||
# Svc::DpManager (Active Component)
|
||||
|
||||
## 1. Introduction
|
||||
|
||||
`Svc::DpManager` is an active component for managing data products.
|
||||
It does the following:
|
||||
|
||||
1. Receive requests for buffers to hold data products.
|
||||
|
||||
1. When a client component synchronously requests a data product buffer,
|
||||
request an [`Fw::Buffer`](../../../Fw/Buffer/docs/sdd.md)
|
||||
from a buffer manager.
|
||||
Return the buffer to the client component so the component can fill it.
|
||||
|
||||
1. When a client component asynchronously requests a data product buffer,
|
||||
request an [`Fw::Buffer`](../../../Fw/Buffer/docs/sdd.md)
|
||||
from a buffer manager.
|
||||
Send the buffer to the client component so the component can fill it.
|
||||
|
||||
1. Receive buffers filled with data products by
|
||||
client components.
|
||||
Upon receiving a buffer, send the buffer out on a port.
|
||||
Another component such as
|
||||
[`Svc::BufferAccumulator`](../../BufferAccumulator/docs/BufferAccumulator.md)
|
||||
or [`Svc::DpWriter`](../../DpWriter/docs/sdd.md)
|
||||
will process the buffer and then send it back to the buffer manager
|
||||
for deallocation.
|
||||
|
||||
## 2. Requirements
|
||||
|
||||
Requirement | Description | Rationale | Verification Method
|
||||
----------- | ----------- | ----------| -------------------
|
||||
SVC-DPMANAGER-001 | `Svc::DpManager` shall provide an array of ports for synchronously requesting and receiving data product buffers. | This capability supports the `product` `get` port in the auto-generated code for components that define data products. | Unit test
|
||||
SVC-DPMANAGER-002 | `Svc::DpManager` shall provide arrays of ports for receiving and asynchronously responding to requests for data product buffers. | This capability supports the `product` `request` and `product` `recv` ports in the auto-generated code for components that define data products. | Unit test
|
||||
SVC-DPMANAGER-003 | `Svc::DpManager` shall receive data product buffers and forward them for further processing. | This requirement provides a pass-through capability for sending data product buffers to downstream components. `Svc::DpManager` receives data product input on a port of type `Fw::DpSend`. This input consists of a container ID and an `Fw::Buffer` _B_. `Svc::DpManager` sends _B_ on a port of type `Fw::BufferSend`. This port type is used by the standard F Prime components for managing and logging data, e.g., `Svc::BufferAccumulator`, `Svc::DpWriter`. | Unit test
|
||||
SVC-DPMANAGER-004 | `Svc::DpManager` shall provide telemetry that reports the number of successful allocations, the number of failed allocations, and the volume of data handled. | This requirement establishes the telemetry interface for the component. | Unit test
|
||||
|
||||
## 3. Design
|
||||
|
||||
### 3.1. Component Diagram
|
||||
|
||||
The diagram below shows the `DpManager` component.
|
||||
|
||||
<div>
|
||||
<img src="img/DpManager.png" width=700/>
|
||||
</div>
|
||||
|
||||
### 3.2. Ports
|
||||
|
||||
`DpManager` has the following ports:
|
||||
|
||||
| Kind | Name | Port Type | Usage |
|
||||
|------|------|-----------|-------|
|
||||
| `async input` | `schedIn` | `Svc.Sched` | Schedule in port |
|
||||
| `sync input` | `productGetIn` | `[DpManagerNumPorts] Fw.DpGet` | Ports for responding to a data product get from a client component |
|
||||
| `async input` | `productRequestIn` | `[DpManagerNumPorts] Fw.DpRequest` | Ports for receiving data product buffer requests from a client component |
|
||||
| `output` | `productResponseOut` | `[DpManagerNumPorts] Fw.DpResponse` | Ports for sending requested data product buffers to a client component |
|
||||
| `output` | `bufferGetOut` | `[DpManagerNumPorts] Fw.BufferGet` | Ports for getting buffers from a Buffer Manager |
|
||||
| `async input` | `productSendIn` | `[DpManagerNumPorts] Fw.DpSend` | Ports for receiving filled data product buffers from a client component |
|
||||
| `output` | `productSendOut` | `[DpManagerNumPorts] Fw.BufferSend` | Ports for sending filled data product buffers to a downstream component |
|
||||
| `time get` | `timeGetOut` | `Fw.Time` | Time get port |
|
||||
| `telemetry` | `tlmOut` | `Fw.Tlm` | Telemetry port |
|
||||
| `event` | `eventOut` | `Fw.Log` | Event port |
|
||||
| `text event` | `textEventOut` | `Fw.LogText` | Text event port |
|
||||
|
||||
### 3.3. State
|
||||
|
||||
`DpManager` maintains the following state:
|
||||
|
||||
1. `numSuccessfulAllocations (U32)`: The number of successful buffer
|
||||
allocations.
|
||||
|
||||
1. `numFailedAllocations (U32)`: The number of failed buffer allocations.
|
||||
|
||||
1. `numDataProducts (U32)`: The number of data products handled.
|
||||
|
||||
1. `numBytes (U64)`: The number of bytes handled.
|
||||
|
||||
### 3.4. Compile-Time Setup
|
||||
|
||||
The configuration constant [`DpManagerNumPorts`](../../../config/AcConstants.fpp)
|
||||
specifies the number of ports for
|
||||
requesting data product buffers and for sending filled data products.
|
||||
|
||||
### 3.5. Runtime Setup
|
||||
|
||||
No special runtime setup is required.
|
||||
|
||||
### 3.6. Port Handlers
|
||||
|
||||
#### 3.6.1. schedIn
|
||||
|
||||
The handler for this port sends out the state variables as telemetry.
|
||||
|
||||
#### 3.6.2. productGetIn
|
||||
|
||||
This handler receives a port number `portNum`, a container ID `id`, a requested
|
||||
buffer size `size`, and a mutable reference to a buffer `B`.
|
||||
It does the following:
|
||||
|
||||
1. Set `status = getBuffer(portNum, id, size, B)`.
|
||||
|
||||
1. Return `status`.
|
||||
|
||||
#### 3.6.3. productRequestIn
|
||||
|
||||
This handler receives a port number `portNum`, a container ID `id` and a
|
||||
requested buffer size `size`.
|
||||
It does the following:
|
||||
|
||||
1. Initialize a local variable `B` with an invalid buffer.
|
||||
|
||||
1. Set `status = getBuffer(portNum id, size, B)`.
|
||||
|
||||
1. Send `(id, B, status)` on port `portNum` of `productResponseOut`.
|
||||
|
||||
#### 3.6.4. productSendIn
|
||||
|
||||
This handler receives a port number `portNum`, a data product ID `I` and a
|
||||
buffer `B`.
|
||||
It does the following:
|
||||
|
||||
1. Update `numDataProducts` and `numBytes`.
|
||||
|
||||
1. Send `B` on port `portNum` of `productSendOut`.
|
||||
|
||||
### 3.7. Helper Methods
|
||||
|
||||
<a name="getBuffer"></a>
|
||||
#### 3.7.1. getBuffer
|
||||
|
||||
This function receives a port number `portNum`, a container ID `id`, a
|
||||
requested buffer size `size`, and a mutable reference to a buffer `B`.
|
||||
It does the following:
|
||||
|
||||
1. Set `status = FAILURE`.
|
||||
|
||||
1. Set `B = bufferGetOut_out(portNum, size)`.
|
||||
|
||||
1. If `B` is valid, then atomically increment `numSuccessfulAllocations` and
|
||||
set `status = SUCCESS`.
|
||||
|
||||
1. Otherwise atomically increment `numFailedAllocations` and emit a warning event.
|
||||
|
||||
1. Return `status`.
|
||||
|
||||
<a name="ground_interface"></a>
|
||||
## 4. Ground Interface
|
||||
|
||||
### 4.1. Telemetry
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| `NumSuccessfulAllocations` | `U32` | The number of successful buffer allocations |
|
||||
| `NumFailedAllocations` | `U32` | The number of failed buffer allocations |
|
||||
| `NumDataProds` | `U32` | Number of data products handled |
|
||||
| `NumBytes` | `U32` | Number of bytes handled |
|
||||
|
||||
### 4.2. Events
|
||||
|
||||
| Name | Severity | Description |
|
||||
|------|----------|-------------|
|
||||
| `BufferAllocationFailed` | `warning high` | Buffer allocation failed |
|
||||
|
||||
## 5. Example Uses
|
||||
|
||||
<a name="top-diagrams"></a>
|
||||
### 5.1. Topology Diagrams
|
||||
|
||||
The following topology diagrams show how to connect `Svc::DpManager`
|
||||
to a client component, a buffer manager, and a data product writer.
|
||||
The diagrams use the following instances:
|
||||
|
||||
* `bufferManager`: An instance of [`Svc::BufferManager`](../../BufferManager/docs/sdd.md).
|
||||
|
||||
* `dpManager`: An instance of `Svc::DpManager`.
|
||||
|
||||
* `dpWriter`: An instance of [`Svc::DpWriter`](../../DpWriter/docs/sdd.md).
|
||||
|
||||
* `producer`: A client component that produces data products.
|
||||
`productRequestOut` is the special `product request` port.
|
||||
`productRecvIn` is the special `product recv` port.
|
||||
|
||||
The connections shown use port zero for requesting, receiving,
|
||||
and sending data product buffers.
|
||||
If `DpManagerNumPorts` is greater than one, then you can also use other ports,
|
||||
e.g., port one or port two.
|
||||
That way you can use one `DpManager` instance to support multiple sets of
|
||||
connections.
|
||||
|
||||
#### 5.1.1. Synchronously Getting Data Product Buffers
|
||||
|
||||
<div>
|
||||
<img src="img/top/buffer-get.png" width=800/>
|
||||
</div>
|
||||
|
||||
#### 5.1.2. Asynchronously Requesting Data Product Buffers
|
||||
|
||||
<div>
|
||||
<img src="img/top/buffer-request.png" width=800/>
|
||||
</div>
|
||||
|
||||
#### 5.1.3. Sending Data Products
|
||||
|
||||
<div>
|
||||
<img src="img/top/product-send.png" width=1000/>
|
||||
</div>
|
||||
|
||||
### 5.2. Sequence Diagrams
|
||||
|
||||
#### 5.2.1. Synchronously Getting a Data Product Buffer
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
activate producer
|
||||
producer->>dpManager: Request buffer [productGetIn]
|
||||
dpManager->>bufferManager: Request buffer B [bufferGetOut]
|
||||
bufferManager-->>dpManager: Return B
|
||||
dpManager->>dpManager: Store B into producer
|
||||
dpManager-->>producer: Return SUCCESS
|
||||
deactivate producer
|
||||
```
|
||||
|
||||
#### 5.2.2. Asynchronously Requesting a Data Product Buffer
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
activate producer
|
||||
activate dpManager
|
||||
producer-)dpManager: Request buffer [productRequestIn]
|
||||
dpManager->>bufferManager: Request buffer B [bufferGetOut]
|
||||
bufferManager-->>dpManager: Return B
|
||||
dpManager-)producer: Send B [productResponseOut]
|
||||
deactivate dpManager
|
||||
deactivate producer
|
||||
```
|
||||
|
||||
#### 5.2.3. Sending a Data Product
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
activate producer
|
||||
activate dpManager
|
||||
activate dpWriter
|
||||
producer-)dpManager: Send buffer B [productSendIn]
|
||||
dpManager-)dpWriter: Send B [productSendOut]
|
||||
dpWriter->>bufferManager: Deallocate B
|
||||
bufferManager-->>dpWriter: Return
|
||||
deactivate dpWriter
|
||||
deactivate dpManager
|
||||
deactivate producer
|
||||
```
|
||||
BIN
Svc/DpWriter/docs/img/DpWriter.png
Normal file
BIN
Svc/DpWriter/docs/img/DpWriter.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 102 KiB |
BIN
Svc/DpWriter/docs/img/top/product-write.png
Normal file
BIN
Svc/DpWriter/docs/img/top/product-write.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 342 KiB |
27
Svc/DpWriter/docs/img/top/product-write.txt
Normal file
27
Svc/DpWriter/docs/img/top/product-write.txt
Normal file
@ -0,0 +1,27 @@
|
||||
producer
|
||||
productSendOut
|
||||
0
|
||||
dpManager
|
||||
productSendIn
|
||||
0
|
||||
|
||||
dpManager
|
||||
productSendOut
|
||||
0
|
||||
dpWriter
|
||||
bufferSendIn
|
||||
0
|
||||
|
||||
dpWriter
|
||||
procBufferSendOut
|
||||
0
|
||||
dpProcessor
|
||||
bufferSendIn
|
||||
0
|
||||
|
||||
dpWriter
|
||||
deallocBufferSendOut
|
||||
0
|
||||
bufferManager
|
||||
bufferSendIn
|
||||
0
|
||||
192
Svc/DpWriter/docs/sdd.md
Normal file
192
Svc/DpWriter/docs/sdd.md
Normal file
@ -0,0 +1,192 @@
|
||||
\page SvcDpWriterComponent Svc::DpWriter Component
|
||||
# Svc::DpWriter (Active Component)
|
||||
|
||||
## 1. Introduction
|
||||
|
||||
`Svc::DpWriter` is an active component for writing data products to disk.
|
||||
It does the following:
|
||||
|
||||
1. Receive buffers containing filled data product containers.
|
||||
The buffers typically come from one or more components that produce
|
||||
data products.
|
||||
They typically pass through an instance of
|
||||
[`Svc::DpManager`](../../DpManager/docs/sdd.md), and possibly through
|
||||
an instance of
|
||||
[`Svc::BufferAccumulator`](../../BufferAccumulator/docs/BufferAccumulator.md),
|
||||
before reaching `DpWriter`.
|
||||
|
||||
1. For each buffer _B_ received in step 1:
|
||||
|
||||
1. Perform any requested processing, such as data compression, on _B_.
|
||||
|
||||
1. Write _B_ to disk.
|
||||
|
||||
## 2. Requirements
|
||||
|
||||
Requirement | Description | Rationale | Verification Method
|
||||
----------- | ----------- | ----------| -------------------
|
||||
SVC-DPWRITER-001 | `Svc::DpWriter` shall provide a port for receiving `Fw::Buffer` objects pointing to filled data product containers. | The purpose of `DpWriter` is to write the data products to disk. | Unit Test
|
||||
SVC-DPWRITER-002 | `Svc::DpWriter` shall provide an array of ports for sending `Fw::Buffer` objects for processing. | This requirement supports downstream processing of the data in the buffer. | Unit Test
|
||||
SVC-DPWRITER-003 | On receiving a data product container _C_, `Svc::DpWriter` shall use the processing type field of the header of _C_ to select zero or more processing ports to invoke, in port order. | The processing type field is a bit mask. A one in bit `2^n` in the bit mask selects port index `n`. | Unit Test
|
||||
SVC-DPWRITER-004 | On receiving an `Fw::Buffer` _B_, and after performing any requested processing on _B_, `Svc::DpWriter` shall write _B_ to disk. | The purpose of `DpWriter` is to write data products to the disk. | Unit Test
|
||||
SVC-DPWRITER-005 | `Svc::DpManager` shall provide telemetry that reports the number of data products written and the number of bytes written. | This requirement establishes the telemetry interface for the component. | Unit test
|
||||
|
||||
## 3. Design
|
||||
|
||||
### 3.1. Component Diagram
|
||||
|
||||
The diagram below shows the `DpWriter` component.
|
||||
|
||||
<div>
|
||||
<img src="img/DpWriter.png" width=700/>
|
||||
</div>
|
||||
|
||||
### 3.2. Ports
|
||||
|
||||
`DpWriter` has the following ports:
|
||||
|
||||
| Kind | Name | Port Type | Usage |
|
||||
|------|------|-----------|-------|
|
||||
| `async input` | `schedIn` | `Svc.Sched` | Schedule in port |
|
||||
| `async input` | `bufferSendIn` | `Fw.BufferSend` | Port for receiving data products to write to disk |
|
||||
| `output` | `procBufferSendOut` | `[DpWriterNumProcPorts] Fw.BufferSend` | Port for processing data products |
|
||||
| `output` | `deallocBufferSendOut` | `Fw.BufferSend` | Port for deallocating data product buffers |
|
||||
| `time get` | `timeGetOut` | `Fw.Time` | Time get port |
|
||||
| `telemetry` | `tlmOut` | `Fw.Tlm` | Telemetry port |
|
||||
| `event` | `eventOut` | `Fw.Log` | Event port |
|
||||
| `text event` | `textEventOut` | `Fw.LogText` | Text event port |
|
||||
|
||||
### 3.3. State
|
||||
|
||||
`DpWriter` maintains the following state:
|
||||
|
||||
1. `numDataProducts (U32)`: The number of data products written.
|
||||
|
||||
1. `numBytes (U64)`: The number of bytes written.
|
||||
|
||||
### 3.4. Compile-Time Setup
|
||||
|
||||
The configuration constant [`DpWriterNumProcPorts`](../../../config/AcConstants.fpp)
|
||||
specifies the number of ports for connecting components that perform
|
||||
processing.
|
||||
|
||||
### 3.5. Runtime Setup
|
||||
|
||||
The `config` function specifies the following constants:
|
||||
|
||||
1. `fileNamePrefix (string)`: The prefix to use for file names.
|
||||
|
||||
1. `fileNameSuffix (string)`: The suffix to use for file names.
|
||||
|
||||
### 3.6. Port Handlers
|
||||
|
||||
#### 3.6.1. schedIn
|
||||
|
||||
This handler sends out the state variables as telemetry.
|
||||
|
||||
#### 3.6.2. bufferSendIn
|
||||
|
||||
This handler receives a mutable reference to a buffer `B`.
|
||||
It does the following:
|
||||
|
||||
1. Check that `B` is valid and that the first `sizeof(FwPacketDescriptorType)`
|
||||
bytes of the memory referred to by `B` hold the serialized value
|
||||
[`Fw_PACKET_DP`](../../../Fw/Com/ComPacket.hpp).
|
||||
If not, emit a warning event.
|
||||
|
||||
1. If step 1 succeeded, then
|
||||
|
||||
1. Read the `ProcType` field out of the container header stored in the
|
||||
memory pointed to by `B`.
|
||||
If the value is a valid port number `N` for `procBufferSendOut`, then invoke
|
||||
`procBufferSendOut` at port number `N`, passing in `B`.
|
||||
This step updates the memory pointed to by `B` in place.
|
||||
|
||||
1. Write `B` to a file, using the format described in the [**File
|
||||
Format**](#file_format) section. For the time stamp, use the time
|
||||
provided by `timeGetOut`.
|
||||
|
||||
1. Send `B` on `deallocBufferSendOut`.
|
||||
|
||||
<a name="file_format"></a>
|
||||
## 4. File Format
|
||||
|
||||
### 4.1. Data Format
|
||||
|
||||
Each file stores a serialized data product record,
|
||||
with the format described in the
|
||||
[data products documentation](../../../Fw/Dp/docs/sdd.md#serial-format).
|
||||
|
||||
### 4.2. File Name
|
||||
|
||||
The name of each file consists of `fileNamePrefix` followed by an
|
||||
ID, a time stamp, and `fileNameSuffix`.
|
||||
The ID consists of an underscore character `_` followed by the container ID.
|
||||
The time stamp consists of an underscore character `_` followed by a seconds
|
||||
value, an underscore character, and a microseconds value.
|
||||
|
||||
For example, suppose that the file name prefix is `container_data` and the
|
||||
file name suffix is `.dat`.
|
||||
Suppose that container ID is 100, the seconds value is 100000,
|
||||
and the microseconds value is 1000.
|
||||
Then the file name is `container_data_100_100000_1000.dat`.
|
||||
|
||||
<a name="ground_interface"></a>
|
||||
## 5. Ground Interface
|
||||
|
||||
### 5.1. Telemetry
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| `NumDataProducts` | `U32` | The number of data products handled |
|
||||
| `NumBytes` | `U64` | The number of bytes handled |
|
||||
|
||||
### 5.2. Events
|
||||
|
||||
| Name | Severity | Description |
|
||||
|------|----------|-------------|
|
||||
| `BufferTooSmall` | `warning high` | Incoming buffer is too small to hold a data product container |
|
||||
| `InvalidPacketDescriptor` | `warning high` | Incoming buffer had an invalid packet descriptor |
|
||||
|
||||
## 6. Example Uses
|
||||
|
||||
<a name="top-diagrams"></a>
|
||||
### 6.1. Topology Diagrams
|
||||
|
||||
The following topology diagram shows how to connect `Svc::DpWriter`
|
||||
to a `DpManager` component and a processor component.
|
||||
The diagrams use the following instances:
|
||||
|
||||
* `dpManager`: An instance of [`Svc::DpManager`](../../DpManager/docs/sdd.md).
|
||||
|
||||
* `dpProcessor`: A component that processes data product containers.
|
||||
|
||||
* `dpWriter`: An instance of `Svc::DpWriter`.
|
||||
|
||||
* `producer`: A component that produces data products.
|
||||
|
||||
<div>
|
||||
<img src="img/top/product-write.png" width=800/>
|
||||
</div>
|
||||
|
||||
### 6.2. Sequence Diagrams
|
||||
|
||||
The following diagram shows what happens when a buffer is sent to `DpWriter`,
|
||||
is processed, and is written to disk.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
activate producer
|
||||
activate dpManager
|
||||
activate dpWriter
|
||||
producer-)dpManager: Send buffer
|
||||
dpManager-)dpWriter: Send buffer [bufferSendIn]
|
||||
dpWriter->>dpProcessor: Process buffer B [procBufferSendOut]
|
||||
dpProcessor-->>dpWriter: Return
|
||||
dpWriter->>dpWriter: Write B to disk
|
||||
dpWriter->>bufferManager: Deallocate B [deallocBufferSendOut]
|
||||
bufferManager-->>dpWriter: Return
|
||||
deactivate dpWriter
|
||||
deactivate dpManager
|
||||
deactivate producer
|
||||
```
|
||||
@ -39,6 +39,12 @@ constant ComQueueBufferPorts = 1
|
||||
@ Used for maximum number of connected buffer repeater consumers
|
||||
constant BufferRepeaterOutputPorts = 10
|
||||
|
||||
@ Size of port array for DpManager
|
||||
constant DpManagerNumPorts = 5
|
||||
|
||||
@ Size of processing port array for DpWriter
|
||||
constant DpWriterNumProcPorts = 5
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Hub connections. Connections on all deployments should mirror these settings.
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
@ -4,7 +4,8 @@
|
||||
# Sets a list of source files for cmake to process as part of autocoding.
|
||||
####
|
||||
set(SOURCE_FILES
|
||||
"${CMAKE_CURRENT_LIST_DIR}/FpConfig.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/AcConstants.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/DpCfg.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/FpConfig.fpp"
|
||||
)
|
||||
register_fprime_module(config)
|
||||
|
||||
26
config/DpCfg.fpp
Normal file
26
config/DpCfg.fpp
Normal file
@ -0,0 +1,26 @@
|
||||
# ======================================================================
|
||||
# FPP file for data products configuration
|
||||
# ======================================================================
|
||||
|
||||
module Fw {
|
||||
|
||||
module DpCfg {
|
||||
|
||||
@ The size in bytes of the user-configurable data in the container
|
||||
@ packet header
|
||||
constant CONTAINER_USER_DATA_SIZE = 32;
|
||||
|
||||
@ A bit mask for selecting the type of processing to perform on
|
||||
@ a container before writing it to disk.
|
||||
enum ProcType: U8 {
|
||||
@ Processing type 0
|
||||
PROC_TYPE_ZERO = 0x01
|
||||
@ Processing type 1
|
||||
PROC_TYPE_ONE = 0x02
|
||||
@ Processing type 2
|
||||
PROC_TYPE_TWO = 0x04
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
type FwBuffSizeType
|
||||
type FwChanIdType
|
||||
type FwDpIdType
|
||||
type FwDpPriorityType
|
||||
type FwEnumStoreType
|
||||
type FwEventIdType
|
||||
type FwIndexType
|
||||
|
||||
@ -68,6 +68,12 @@ typedef U32 FwPrmIdType;
|
||||
typedef U16 FwTlmPacketizeIdType;
|
||||
#define PRI_FwTlmPacketizeIdType PRIu16
|
||||
|
||||
typedef U32 FwDpIdType;
|
||||
#define PRI_FwDpIdType PRIu32
|
||||
|
||||
typedef U32 FwDpPriorityType;
|
||||
#define PRI_FwDpPriorityType PRIu32
|
||||
|
||||
// Boolean values for serialization
|
||||
#ifndef FW_SERIALIZE_TRUE_VALUE
|
||||
#define FW_SERIALIZE_TRUE_VALUE (0xFF) //!< Value encoded during serialization for boolean true
|
||||
|
||||
446
docs/Design/data-products.md
Normal file
446
docs/Design/data-products.md
Normal file
@ -0,0 +1,446 @@
|
||||
# Data Products
|
||||
|
||||
## 1. Introduction
|
||||
|
||||
F' provides several features for managing the generation, storage,
|
||||
and downlink of data products.
|
||||
In this section, we document those features.
|
||||
|
||||
## 2. Basic Concepts
|
||||
|
||||
First we explain some basic concepts.
|
||||
|
||||
### 2.1. Records, Containers, and Dictionaries
|
||||
|
||||
F' data products are based on **records** and **containers**.
|
||||
A record is a basic unit of data.
|
||||
For example, it may be a struct, an array of typed objects of
|
||||
statically known size, or an array of bytes of statically unknown size.
|
||||
A container has an identifier and a priority and stores records.
|
||||
In C++, a container is represented as a class object with member fields that
|
||||
(1) store header data and (2) store an `Fw::Buffer` object pointing
|
||||
to the memory that stores the records.
|
||||
|
||||
The set of all containers forms the **data product dictionary**.
|
||||
To manage the data product dictionary, F Prime uses the same general approach
|
||||
as for commands, telemetry, events, and parameters:
|
||||
|
||||
1. Each component _C_ defines records and containers.
|
||||
The container IDs are local to _C_.
|
||||
Typically they have the values 0, 1, 2, ... .
|
||||
|
||||
2. Each instance _I_ of _C_ contributes one container _I.c_ to the
|
||||
dictionary for each container _c_ defined in _C_.
|
||||
The global identifier for _I.c_ is the base identifier of _I_ plus
|
||||
the local identifier for _c_.
|
||||
For example, if the base identifier is 0x1000, then the global identifiers
|
||||
might be 0x1000, 0x1001, 0x1002, ... .
|
||||
|
||||
3. For any topology _T_, the global identifiers _I.c_ for all the instances _T_
|
||||
form the data product dictionary for _T_.
|
||||
|
||||
### 2.2. F' Components
|
||||
|
||||
Typically a data product system in an F' application consists of the following
|
||||
components:
|
||||
|
||||
1. One or more **data product producers**.
|
||||
These components produce data products and are typically mission-specific.
|
||||
For example, they may produce science data.
|
||||
|
||||
1. Standard F Prime components for managing data products.
|
||||
|
||||
1. A **data product manager**.
|
||||
This component allocates memory for empty containers.
|
||||
It also forwards filled containers to the data product writer.
|
||||
See [`Svc::DpManager`](../../Svc/DpManager/docs/sdd.md).
|
||||
|
||||
1. A **data product writer**.
|
||||
This component receives filled containers from data product
|
||||
producers. It writes the contents of the containers to non-volatile
|
||||
storage. See [`Svc::DpWriter`](../../Svc/DpWriter/docs/sdd.md).
|
||||
|
||||
1. A **data product catalog**.
|
||||
This component maintains a database of available data
|
||||
products. By command, it downlinks and deletes data products.
|
||||
See TODO.
|
||||
|
||||
1. A **data product processor**.
|
||||
This component performs in-memory processing on data
|
||||
product containers.
|
||||
See TODO.
|
||||
|
||||
Note that when using data products, you need to develop only the
|
||||
producer components. The other components are provided by F'.
|
||||
|
||||
## 3. Producer Components
|
||||
|
||||
In this section we provide more detail about producer components.
|
||||
|
||||
### 3.1. Activities
|
||||
|
||||
A producer component typically repeats the following activities,
|
||||
as often as necessary:
|
||||
|
||||
1. Request a container from a data manager component.
|
||||
|
||||
2. When the container is received, fill the container with
|
||||
data by serializing records into the container.
|
||||
|
||||
3. When the container is full, send the container to the
|
||||
data product manager, which forwards it to the data
|
||||
product writer.
|
||||
|
||||
The FPP model and the autocoded C++ have several features that
|
||||
support these activities.
|
||||
We discuss these features in the following sections.
|
||||
|
||||
### 3.2. FPP Modeling
|
||||
|
||||
In this section we summarize the features of the FPP modeling
|
||||
language used in constructing data product producer components.
|
||||
Each of these features is fully documented in _The FPP User's Guide_
|
||||
and _The FPP Language Specification_.
|
||||
|
||||
#### 3.2.1. Ports
|
||||
|
||||
FPP provides the following special ports for managing data products:
|
||||
|
||||
1. A **product get port** of type [`Fw::DpGet`](../../Fw/Dp/docs/sdd.md).
|
||||
This is an output port for synchronously requesting
|
||||
memory from a buffer manager.
|
||||
The request is served on the thread that invokes the port
|
||||
and causes a mutex lock to be taken on that thread.
|
||||
Example syntax:
|
||||
```
|
||||
product get port productGetOut
|
||||
```
|
||||
|
||||
1. A **product request port** of type [`Fw::DpRequest`](../../Fw/Dp/docs/sdd.md).
|
||||
This is an output port for asynchronously requesting memory
|
||||
from a data product manager.
|
||||
The request is served on the thread of the data product manager.
|
||||
This approach incurs the overhead of a separate thread, but it
|
||||
does not require the requesting thread to take a lock.
|
||||
Example syntax:
|
||||
```
|
||||
product request port productRequestOut
|
||||
```
|
||||
|
||||
1. A **product receive port** of type [`Fw::DpResponse`](../../Fw/Dp/docs/sdd.md).
|
||||
This is an input port for receiving an empty container in response
|
||||
to an asynchronous request. Example syntax:
|
||||
```
|
||||
async product recv port productRecvIn
|
||||
```
|
||||
|
||||
1. A **product send port** of type [`Fw::DpSend`](../../Fw/Dp/docs/sdd.md).
|
||||
This is an output port for sending a filled container
|
||||
to a data product writer. Example syntax:
|
||||
```
|
||||
product send port productSendOut
|
||||
```
|
||||
|
||||
Each data product producer component must have the following
|
||||
ports in its component model:
|
||||
|
||||
1. One or both of a `product` `get` port and a `product` `request` port.
|
||||
|
||||
1. A `product` `send` port.
|
||||
|
||||
A component that has a `product` `request` port must also have
|
||||
a `product` `receive` port.
|
||||
|
||||
#### 3.2.2. Records
|
||||
|
||||
A record is a unit of data.
|
||||
When defining a producer component, you can specify one or more
|
||||
records.
|
||||
A record specification consists of a name, a type specifier, and an optional identifier.
|
||||
The type specifier may be one of the following:
|
||||
|
||||
1. An FPP type _T_. In this case, the record contains a single value of type
|
||||
_T_. _T_ may be any FPP type, including a struct or array type.
|
||||
|
||||
1. An FPP type _T_ followed by the keyword `array`.
|
||||
In this case, the record is an array of values of type _T_
|
||||
of statically unknown size.
|
||||
The size of the array is stored in the record.
|
||||
|
||||
In either case, _T_ may be any FPP type, including a struct or array type.
|
||||
|
||||
Example syntax:
|
||||
```
|
||||
@ A struct with a fixed-size member array
|
||||
struct FixedSizeData {
|
||||
data: [1024] F32
|
||||
}
|
||||
@ A record containing fixed-size data
|
||||
product record FixedSizeDataRecord: FixedSizeData id 0x00
|
||||
@ A record containing a variable-size array
|
||||
product record F32ArrayRecord: F32 array id 0x01
|
||||
```
|
||||
|
||||
#### 3.2.3. Containers
|
||||
|
||||
A container is a data structure that stores records.
|
||||
When defining a producer component, you can specify one or more containers.
|
||||
Each container specified in a component can store
|
||||
any of the records specified in the component.
|
||||
|
||||
A container specification consists of a name, an optional
|
||||
identifier, and an optional default priority.
|
||||
The default priority is the priority to use if no
|
||||
other priority is specified for the container
|
||||
during operations.
|
||||
Example syntax:
|
||||
```
|
||||
product container C1
|
||||
product container C2 id 0x01 default priority 10
|
||||
```
|
||||
|
||||
### 3.3. Autocoded C++
|
||||
|
||||
The autocoded C++ base class for a producer component _C_ provides
|
||||
the following API elements:
|
||||
|
||||
1. Enumerations defining the available container IDs, container
|
||||
priorities, and record IDs.
|
||||
|
||||
1. A member class _C_ `::DpContainer`. This class is derived from
|
||||
[`Fw::DpContainer`](../../Fw/Dp/docs/sdd.md) and represents a container
|
||||
specialized to the data products defined in _C_.
|
||||
Each instance of _C_ `::DpContainer` is a wrapper for an `Fw::Buffer` _B_,
|
||||
which points to allocated memory.
|
||||
The class provides operations for serializing the records
|
||||
defined in _C_ into the memory pointed to by _B_.
|
||||
There is one operation _C_ `::DpContainer::serialize_` _R_
|
||||
for each record _R_ defined in _C_.
|
||||
For the serialized format of each record, see the documentation
|
||||
for [`Fw::DpContainer`](../../Fw/Dp/docs/sdd.md).
|
||||
|
||||
1. If _C_ has a `product` `get` port, a member function `dpGet_`
|
||||
_c_ for each container defined in _C_.
|
||||
This function takes a container ID, a data size, and a reference
|
||||
to a data product container _D_.
|
||||
It invokes `productGetOut`, which is typically connected
|
||||
to a data product manager component.
|
||||
In the nominal case, the invocation returns an `Fw::Buffer` _B_ large enough
|
||||
to store a data product packet with the requested data size.
|
||||
The `dpGet` function then uses the ID and _B_ to initialize _D_.
|
||||
It returns a status value indicating whether the buffer
|
||||
allocation succeeded.
|
||||
|
||||
1. If _C_ has a `product` `request` port, a member function
|
||||
`dpRequest_` _c_ for each container defined in _C_.
|
||||
This function takes a container ID and a data size.
|
||||
It sends out a request on `productRequestOut`, which is
|
||||
typically connected to a data product manager component.
|
||||
The request is for a buffer large enough to store a data
|
||||
product packet with the requested data size.
|
||||
|
||||
1. If _C_ has a `product` `recv` port, a pure virtual
|
||||
member function `dpRecv_` _c_ `_handler` for each container _c_
|
||||
defined in _C_.
|
||||
When a fresh container arrives in response to a
|
||||
`dpRequest` invocation, the autocoded C++ uses the container ID to
|
||||
select and invoke the appropriate `dpRecv` handler.
|
||||
The implementation of _C_ must override each handler
|
||||
to provide the mission-specific behavior for filling
|
||||
in the corresponding container.
|
||||
The arguments to `dpRecv_` _c_ `_handler` provide
|
||||
(1) a reference to the container, which the implementation can fill in;
|
||||
and (2) a status value indicating whether the container
|
||||
is valid. An invalid container can result if the buffer
|
||||
allocation fails.
|
||||
|
||||
1. A member function `dpSend` for sending a filled
|
||||
data product container.
|
||||
This function takes a reference to a container _c_ and an
|
||||
optional time tag.
|
||||
It does the following:
|
||||
|
||||
1. If no time tag is provided, then invoke `timeGetOut`
|
||||
to get the system time and use it to set the time tag.
|
||||
|
||||
1. Store the time tag into _c_.
|
||||
|
||||
1. Send _c_ on `productSendOut`.
|
||||
|
||||
### 3.4. Unit Test Support
|
||||
|
||||
In F Prime, each component _C_ comes with auto-generated
|
||||
classes _C_ `TesterBase` and _C_ `GTestBase` for writing
|
||||
unit tests against _C_.
|
||||
_C_ `GTestBase` is derived from _C_ `TesterBase`; it
|
||||
provides test macros based on the Google Test framework.
|
||||
|
||||
To write unit tests, you construct a class _C_ `Tester`.
|
||||
Typically _C_ `Tester` is derived from _C_ `GTestBase` and
|
||||
uses the Google Test framework macros.
|
||||
If for some reason you can't use the Google Test framework
|
||||
(e.g., because you are running on a platform that does not support it),
|
||||
then your _C_ `Tester` class can be derived from _C_ `TesterBase`.
|
||||
|
||||
This section documents the unit test support for producer components.
|
||||
|
||||
#### 3.4.1. The TesterBase Class
|
||||
|
||||
**History data structures:**
|
||||
The class _C_ `TesterBase` provides the following histories:
|
||||
|
||||
1. If _C_ has a product get port,
|
||||
then _C_ `TesterBase` has a history called `productGetHistory`.
|
||||
Each element in the history is of type `DpGet`.
|
||||
`DpGet` is a struct with fields storing the container ID and the
|
||||
size emitted on the product get port.
|
||||
|
||||
1. If _C_ has a product request port, then _C_ `TesterBase` has a
|
||||
corresponding history called `productRequestHistory`.
|
||||
Each element in the history is of type `DpRequest`.
|
||||
`DpRequest` is a struct with fields storing the container ID and the
|
||||
size emitted on the product request port.
|
||||
|
||||
1. _C_ `TesterBase` has a history called `productSendHistory`.
|
||||
Each element in the history is of type `DpSend`.
|
||||
`DpSend` is a struct with fields storing the container ID and
|
||||
a shallow copy of the buffer emitted on the product send port.
|
||||
|
||||
**History functions:**
|
||||
The class _C_ `TesterBase` provides the following functions
|
||||
for managing the histories:
|
||||
|
||||
1. If _C_ has a product get port, then _C_ `TesterBase` provides
|
||||
the following functions:
|
||||
|
||||
1. `pushProductGetEntry`: This function takes a container ID and
|
||||
a size. It constructs the corresponding `DpGet` history object
|
||||
and pushes it on `productGetHistory`. Typically this function is
|
||||
called by `productGet_handler` (see below).
|
||||
|
||||
1. `productGet_handler`: This function is called when the tester
|
||||
component receives data emitted on the product get port of the
|
||||
component under test. It takes a container ID, a size, and a
|
||||
mutable reference to a buffer _B_. By default it calls
|
||||
`pushProductGetEntry` with the ID and size and returns `FAILURE`,
|
||||
indicating that no memory was allocated and _B_ was not updated.
|
||||
This function is virtual, so you can override it with your own
|
||||
behavior. For example, your function could call `pushProductGetEntry`,
|
||||
allocate a buffer, store the allocated buffer into _B_, and return
|
||||
`SUCCESS`.
|
||||
|
||||
1. If _C_ has a product request port, then _C_ `TesterBase` provides
|
||||
the following functions:
|
||||
|
||||
1. `pushProductRequestEntry`: This function takes a container ID and
|
||||
a size. It constructs the corresponding `DpRequest` history object
|
||||
and pushes it on `productRequestHistory`. Typically this function is
|
||||
called by `productRequest_handler` (see below).
|
||||
|
||||
1. `productRequest_handler`: This function is called when the tester
|
||||
component receives data emitted on the product request port of the
|
||||
component under test. It takes a container ID and a size. By default
|
||||
it calls `pushProductRequestEntry` with the ID and size. This function
|
||||
is virtual, so you can override it with your own behavior.
|
||||
|
||||
1. _C_ `TesterBase` provides the following functions:
|
||||
|
||||
1. `pushProductSendEntry`: This function takes a container ID and a
|
||||
const reference to a buffer. It constructs the corresponding
|
||||
`DpSend` history object and pushes it on `productSendHistory`.
|
||||
Typically this function is called by `productSend_handler` (see below).
|
||||
|
||||
1. `productSend_handler`: This function is called when the tester
|
||||
component receives data emitted on the product send port of the
|
||||
component under test. It takes a container ID and a const reference
|
||||
to a buffer. By default it calls `pushProductSendEntry` with the
|
||||
ID and buffer. This function is virtual, so you can override it
|
||||
with your own behavior.
|
||||
|
||||
#### 3.4.2. The GTestBase Class
|
||||
|
||||
**Testing macros:**
|
||||
The class _C_ `GTestBase` provides the following macros for
|
||||
verifying the histories managed by _C_ `TesterBase`.
|
||||
|
||||
1. If _C_ defines data products and has a product get port, then _C_
|
||||
`GTestBase` provides the following macros:
|
||||
|
||||
1. `ASSERT_PRODUCT_GET_SIZE(size)`: This macro checks that `productGetHistory`
|
||||
has the specified size (number of entries).
|
||||
|
||||
1. `ASSERT_PRODUCT_GET(index, id, size)`: This macro checks that
|
||||
`productGetHistory` has the specified container ID and size
|
||||
at the specified history index.
|
||||
|
||||
1. If _C_ defines data products and has a product request port,
|
||||
then _C_ `GTestBase` provides the following macros:
|
||||
|
||||
1. `ASSERT_PRODUCT_REQUEST_SIZE(size)`: This macro checks that
|
||||
`productRequestHistory` has the specified size (number of entries).
|
||||
|
||||
1. `ASSERT_PRODUCT_REQUEST(index, id, size)`: This macro checks that
|
||||
`productRequestHistory` has the specified container ID and size
|
||||
at the specified history index.
|
||||
|
||||
1. If _C_ defines data products, then _C_ `GTestBase` provides
|
||||
the following macros:
|
||||
|
||||
1. `ASSERT_PRODUCT_SEND_SIZE(size)`: This macro checks that
|
||||
`productSendHistory` has the specified size (number of entries).
|
||||
|
||||
1. `ASSERT_PRODUCT_SEND(index, id, priority, timeTag, procType, userData, dataSize, buffer)`:
|
||||
All the arguments of this macro are inputs (read-only) except `buffer`, which is
|
||||
a by-reference output and must be a variable of type `Fw::Buffer&`.
|
||||
This macro verifies the entry `entry` stored at the specified
|
||||
index of `productSendHistory`. It does the following:
|
||||
|
||||
1. Check that `entry.id` matches the specified ID.
|
||||
|
||||
1. Deserialize the data product header stored in `entry.buffer`.
|
||||
|
||||
1. Check that the container ID, priority, time tag, processor type,
|
||||
user data, and data size stored in the deserialized header
|
||||
match the specified values.
|
||||
|
||||
1. Assign `entry.buffer` to `buffer`. After this macro runs,
|
||||
the deserialization pointer of `buffer` points into the start
|
||||
of the data payload of `entry.buffer`. You can write additional
|
||||
code to deserialize and check the data payload.
|
||||
|
||||
**Container IDs:**
|
||||
The container IDs emitted by the component under test are global
|
||||
IDs.
|
||||
Therefore, when constructing specified IDs you must add
|
||||
the ID base specified in the tester component to the local
|
||||
ID specified in the component under test.
|
||||
For example, for container `CONTAINER` in component `Component`,
|
||||
you would write
|
||||
```cpp
|
||||
ID_BASE + Component::ContainerId::CONTAINER
|
||||
```
|
||||
`ID_BASE` is a standard constant defined in each Tester implementation
|
||||
and provided to the Tester base classes in their constructors.
|
||||
|
||||
## 4. Use Cases
|
||||
|
||||
In this section we discuss several common use cases involving
|
||||
data products.
|
||||
|
||||
**Requesting and sending data products:**
|
||||
See the example uses in the documentation for
|
||||
[`Svc::DpManager`](../../Svc/DpManager/docs/sdd.md#5-example-uses).
|
||||
The component referred to as `producer` in that document
|
||||
is a data product producer.
|
||||
|
||||
**Writing data products to non-volatile storage:**
|
||||
See the example uses in the documentation for
|
||||
[`Svc::DpWriter`](../../Svc/DpWriter/docs/sdd.md#6-example-uses).
|
||||
The component referred to as `producer` in that document
|
||||
is a data product producer.
|
||||
|
||||
**Cataloging and downlinking data products:**
|
||||
TODO
|
||||
|
||||
**Processing data products:**
|
||||
TODO
|
||||
@ -18,17 +18,17 @@ fprime-fpl-convert-xml==1.0.3
|
||||
fprime-fpl-extract-xml==1.0.3
|
||||
fprime-fpl-layout==1.0.3
|
||||
fprime-fpl-write-pic==1.0.3
|
||||
fprime-fpp-check==2.0.2
|
||||
fprime-fpp-depend==2.0.2
|
||||
fprime-fpp-filenames==2.0.2
|
||||
fprime-fpp-format==2.0.2
|
||||
fprime-fpp-from-xml==2.0.2
|
||||
fprime-fpp-locate-defs==2.0.2
|
||||
fprime-fpp-locate-uses==2.0.2
|
||||
fprime-fpp-syntax==2.0.2
|
||||
fprime-fpp-to-cpp==2.0.2
|
||||
fprime-fpp-to-json==2.0.2
|
||||
fprime-fpp-to-xml==2.0.2
|
||||
fprime-fpp-check==2.1.0a3
|
||||
fprime-fpp-depend==2.1.0a3
|
||||
fprime-fpp-filenames==2.1.0a3
|
||||
fprime-fpp-format==2.1.0a3
|
||||
fprime-fpp-from-xml==2.1.0a3
|
||||
fprime-fpp-locate-defs==2.1.0a3
|
||||
fprime-fpp-locate-uses==2.1.0a3
|
||||
fprime-fpp-syntax==2.1.0a3
|
||||
fprime-fpp-to-cpp==2.1.0a3
|
||||
fprime-fpp-to-json==2.1.0a3
|
||||
fprime-fpp-to-xml==2.1.0a3
|
||||
fprime-gds==3.4.1
|
||||
fprime-tools==3.4.1
|
||||
fprime-visual==1.0.2
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user