Add Framer FPP interface, implement FprimeFramer and adapt ComQueue (#3486)

* Initial FprimeFramer and FprimePacketizer

* Code clarity + set up UTs

* Rework ComQueue and ComStub to use DataWithContext

* Add packets to RefPackets.fppi

* Fix ComQueue tests

* Add hotfix to FileDownlink instead of ComQueue

* Fix cancelPacket as well

* Fix ComQueue UTs by removing hotfix

* Refactor DataWithContext to use an FPP object for context instead of Fw.Buffer

* Touch up testing

* Add docs

* more docs

* More docs

* Rework buffer deallocation pattern to pass-through ComQueue

* Update ComStub UTs

* Restore original FileDownlink.cpp

* Formatting tweak

* Update deprecated getSerializeRepr() calls

* deserialization methods

* Fix spelling

* add cast for safety

* CMakefile change

* Bump ComQueue depth

* Update RPI deployment with new Downlink stack

* Rename comQueueIn port to comPktQueueIn

* Fix comQueueIn to comPktQueueIn change

* Remove legacy Svc.Framer

* Fix CMake UTs

* Fix RPI topology config

* Fix FprimeProtocol.fpp module

* Fix namespacing

* Use const reference for FrameContext port

* Review comments EXCEPT port passback refactor

* Rework ComStub with new ByteStream

* New ByteStream - ComInterface model

* Rework TcpClient / TcpServer with new bytestream

* Adapt UDP component for new ByteStream

* Adapt FrameAccumulator for new ByteStream

* Adapt FprimeFramer for new ByteStream

* Update Ref topology with new ByteStream model

* Remove all legacy deallocates from Drivers; reintroduce DEPRECATED model types

* Fix spelling and include error

* More spelling....

* RPI and RpiDemo fixes

* Fix conversion warning on RPI

* static_cast for short int on RPI

* Standardize port names

* Remove legacy Drv types and merge RECV/SEND enum type, delete StreamCrossover

* Update SDDs

* Update SDDs

* Fix ComInterface <-> Framer interfaction, clarify comments and fix annotations

* Switch ComStub from ASSERT to log failure and return buffer

* Add history size check + clarify test handler overrides

* Fix RPI topology to wire comStub on Uplink

* Rename comm to comDriver in RPI topology

* Update communication adapter interface docs
This commit is contained in:
Thomas Boyer-Chammard 2025-04-29 16:40:36 -07:00 committed by GitHub
parent a9da1b9955
commit d0246f148b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
130 changed files with 1367 additions and 2619 deletions

View File

@ -86,9 +86,9 @@ BUFFERALLOCATIONFAILED
BUFFERGETOUT
BUFFERMANAGERCOMPONENTIMPLCFG
BUFFERMGR
BUFFERQUEUEIN
BUFFERTOOSMALLFORDATA
BUFFERTOOSMALLFORPACKET
BUFFQUEUEIN
buffsize
BUGLIST
bugprone
@ -156,6 +156,7 @@ COMMANDDISPATCHERIMPLCFG
commandline
commasepitem
COMPACKET
COMPACKETQUEUEIN
COMPONENTTESTERIMPL
COMQUEUE
COMQUEUEIN

View File

@ -137,3 +137,12 @@ TeX/AMS
# ignore long runs of a single character:
\b([A-Za-z])\g{-1}{3,}\b
##########################################
###### F Prime specific patterns ######
##########################################
# .get...() .set...() autocoded functions
\.get\w+\(
\.set\w+\(

View File

@ -1,40 +1,22 @@
module Drv {
@ Status returned by the send call
enum SendStatus {
SEND_OK = 0 @< Send worked as expected
SEND_RETRY = 1 @< Data send should be retried
SEND_ERROR = 2 @< Send error occurred retrying may succeed
enum ByteStreamStatus {
OP_OK @< Operation worked as expected
SEND_RETRY @< Data send should be retried
RECV_NO_DATA @< Receive worked, but there was no data
OTHER_ERROR @< Error occurred, retrying may succeed
}
@ Send data out through the byte stream
port ByteStreamSend(
ref sendBuffer: Fw.Buffer @< Data to send
) -> SendStatus
@ Status associated with the received data
enum RecvStatus {
RECV_OK = 0 @< Receive worked as expected
RECV_NO_DATA = 1 @< Receive worked, but there was no data
RECV_ERROR = 2 @< Receive error occurred retrying may succeed
}
@ Carries the received bytes stream driver
port ByteStreamRecv(
ref recvBuffer: Fw.Buffer
recvStatus: RecvStatus
@ Port to exchange buffer and status with the ByteStreamDriver model
@ This port is used for receiving data from the driver as well as on
@ callback of a send call
port ByteStreamData(
ref buffer: Fw.Buffer,
status: ByteStreamStatus
)
enum PollStatus {
POLL_OK = 0 @< Poll successfully received data
POLL_RETRY = 1 @< No data available, retry later
POLL_ERROR = 2 @< Error received when polling
}
port ByteStreamPoll(
ref pollBuffer: Fw.Buffer
) -> PollStatus
@ Signal indicating the driver is ready to send and received data
port ByteStreamReady()
}

View File

@ -1,26 +0,0 @@
Old Symbol
New Symbol
Drv::POLL_OK
Drv::PollStatus::POLL_OK
Drv::POLL_RETRY
Drv::PollStatus::POLL_RETRY
Drv::POLL_ERROR
Drv::PollStatus::POLL_ERROR
Drv::RECV_OK
Drv::RecvStatus::RECV_OK
Drv::RECV_ERROR
Drv::RecvStatus::RECV_ERROR
Drv::SEND_OK
Drv::SendStatus::SEND_OK
Drv::SEND_RETRY
Drv::SendStatus::SEND_RETRY
Drv::SEND_ERROR
Drv::SendStatus::SEND_ERROR

View File

@ -1,57 +1,40 @@
# Drv::ByteStreamDriverModel Byte Stream Driver Model
The byte stream driver is a generic model for drivers implementing a "stream of bytes" interface. Typically these
drivers operate with an outgoing stream and an incoming stream. The outgoing stream is represented by the "send" port
and the incoming stream is either polled using the "poll" port or return asynchronously via the "readCallback" port.
The byte stream driver is a generic model for drivers implementing a "stream of bytes" interface. Typically these drivers operate with an outgoing stream and an incoming stream.
The outgoing stream is represented by the input `send` port; other components can invoke this port to send data through the driver. The incoming stream is represented by the output `recv` port; the driver will call this port to send data to the component that is receiving data from the driver.
## Design
The manager component (typically the ground interface) initiates the transfer of send data by calling the "send" port.
The caller will provide a `Fw::Buffer` containing the data to send and the port call will return a status of that send.
### Send
The manager component (for example a radio manager) initiates the transfer of send data by calling the "send" port.
The caller will provide a `Fw::Buffer` containing the data to send. The driver component **must** perform a callback on its `dataReturnOut` port to return the status of that send as well as returning ownership of the `Fw::Buffer` to the caller.
These responses are an enumeration whose values are described in the following table:
| Value | Description | Buffer Ownership |
|---|---|---|
| Drv::SEND_OK | Send functioned normally. | Ownership of the `Fw::Buffer` passes to the byte stream driver. |
| Drv::SEND_RETRY | Send should be retried, but a subsequent send should return SEND_OK. | The caller retains ownership of the `Fw::Buffer`. |
| Drv::SEND_ERROR | Send produced an error, future sends likely to fail. | Ownership of the `Fw::Buffer` passes to the byte stream driver. |
| ByteStreamStatus::OP_OK | Send functioned normally. | Ownership of the `Fw::Buffer` passes to the byte stream driver. |
| ByteStreamStatus::SEND_RETRY | Send should be retried, but a subsequent send should return OP_OK. | The caller retains ownership of the `Fw::Buffer`. |
| ByteStreamStatus::OTHER_ERROR | Send produced an error, future sends likely to fail. | Ownership of the `Fw::Buffer` passes to the byte stream driver. |
**Note:** in either formation described below, send will operate as described here.
### Callback Formation
### Receive
![Callback](./img/canvas-callback.png)
In the callback formation, the byte stream driver component initiates the transfer of received data by calling the
"readCallback" output port. This port transfers any read data in a `Fw::Buffer` along with a status for the receive.
"recv" output port. This port transfers any read data in a `Fw::Buffer` along with a status for the receive.
This status is an enumeration whose values are described in the following table:
| Value | Description |
|---|---|
| Drv::RECV_OK | Receive functioned normally buffer contains valid data. |
| Drv::RECV_ERROR | Receive produced an error and buffer contains no valid data. |
| ByteStreamStatus::OP_OK | Receive functioned normally and buffer contains valid data. |
| ByteStreamStatus::RECV_NO_DATA | Receive worked, but there was no data |
| ByteStreamStatus::OTHER_ERROR | Receive produced an error and buffer contains no valid data. |
The following components implement the byte stream model using a callback formation:
- `DrvTcpClient`: a F´ component wrapper of the tcp client
- `DrvTcpServer`: a F´ component wrapper of the tcp server
- `DrvUdp`: a F´ component wrapper of the udp
### Polling Formation
![Poll](./img/canvas-poll.png)
In the polling formation, the manager component (typically the ground interface) initiates the transfer of received
data by calling the "poll" input port. This port fills in the provided `Fw::Buffer` along with a status for the poll.
This status is an enumeration whose values are described in the following table:
| Value | Description |
|---|---|
| Drv::POLL_OK | Poll functioned normally buffer contains valid data. |
| Drv::POLL_RETRY | Poll should be retried and a subsequent send should return POLL_OK. |
| Drv::POLL_ERROR | Poll produced an error and buffer contains no valid data. |
**Note:** there are no known implementers of the polling formation, although this formation is best suited for
implementations running on baremetal machines.
- [`Drv::TcpClient`](../../TcpClient/docs/sdd.md): a F´ component wrapper of the tcp client
- [`Drv::TcpServer`](../../TcpServer/docs/sdd.md): a F´ component wrapper of the tcp server
- [`Drv::Udp`](../../Udp/docs/sdd.md): a F´ component wrapper of the udp
## Class Diagram
![classdiagram](./img/class_diagram.png)
@ -61,5 +44,4 @@ implementations running on baremetal machines.
| Name | Description | Validation |
|---|---|---|
| BYTEDRV-001 | The ByteStreamDriverModel shall provide the capability to send bytes | inspection |
| BYTEDRV-002 | The ByteStreamDriverModel shall provide the capability to poll for bytes | inspection |
| BYTEDRV-003 | The ByteStreamDriverModel shall provide the capability to produce bytes | inspection |
| BYTEDRV-002 | The ByteStreamDriverModel shall provide the capability to produce bytes | inspection |

View File

@ -10,7 +10,6 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/LinuxGpioDriver/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/LinuxUartDriver/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/LinuxSpiDriver/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/LinuxI2cDriver/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/StreamCrossover/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Ip/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TcpClient/")

View File

@ -1,8 +1,11 @@
@ Port invoked when the driver is ready to send/receive data
output port ready: Drv.ByteStreamReady
@ Port invoked when driver has received data
output port $recv: Drv.ByteStreamRecv
@ Port invoked by the driver when it receives data
output port $recv: Drv.ByteStreamData
@ Port invoked to send data out the driver
guarded input port $send: Drv.ByteStreamSend
@ Invoke this port to send data out the driver
guarded input port $send: Fw.BufferSend
@ Port invoked to return ownership of sent data back to the sender
output port dataReturnOut: Drv.ByteStreamData

View File

@ -292,10 +292,10 @@ LinuxUartDriver ::~LinuxUartDriver() {
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------
Drv::SendStatus LinuxUartDriver ::send_handler(const FwIndexType portNum, Fw::Buffer& serBuffer) {
Drv::SendStatus status = Drv::SendStatus::SEND_OK;
void LinuxUartDriver ::send_handler(const FwIndexType portNum, Fw::Buffer& serBuffer) {
Drv::ByteStreamStatus status = Drv::ByteStreamStatus::OP_OK;
if (this->m_fd == -1 || serBuffer.getData() == nullptr || serBuffer.getSize() == 0) {
status = Drv::SendStatus::SEND_ERROR;
status = Drv::ByteStreamStatus::OTHER_ERROR;
} else {
unsigned char *data = serBuffer.getData();
FW_ASSERT(static_cast<size_t>(serBuffer.getSize()) <= std::numeric_limits<size_t>::max(),
@ -307,28 +307,25 @@ Drv::SendStatus LinuxUartDriver ::send_handler(const FwIndexType portNum, Fw::Bu
if (-1 == stat || static_cast<size_t>(stat) != xferSize) {
Fw::LogStringArg _arg = this->m_device;
this->log_WARNING_HI_WriteError(_arg, static_cast<I32>(stat));
status = Drv::SendStatus::SEND_ERROR;
status = Drv::ByteStreamStatus::OTHER_ERROR;
}
}
// Deallocate when necessary
if (isConnected_deallocate_OutputPort(0)) {
deallocate_out(0, serBuffer);
}
return status;
// Return the buffer back to the caller
dataReturnOut_out(0, serBuffer, status);
}
void LinuxUartDriver ::serialReadTaskEntry(void* ptr) {
FW_ASSERT(ptr != nullptr);
Drv::RecvStatus status = RecvStatus::RECV_ERROR; // added by m.chase 03.06.2017
Drv::ByteStreamStatus status = ByteStreamStatus::OTHER_ERROR; // added by m.chase 03.06.2017
LinuxUartDriver* comp = reinterpret_cast<LinuxUartDriver*>(ptr);
while (!comp->m_quitReadThread) {
Fw::Buffer buff = comp->allocate_out(0,comp->m_allocationSize);
// On failed allocation, error and deallocate
// On failed allocation, error
if (buff.getData() == nullptr) {
Fw::LogStringArg _arg = comp->m_device;
comp->log_WARNING_HI_NoBuffers(_arg);
status = RecvStatus::RECV_ERROR;
status = ByteStreamStatus::OTHER_ERROR;
comp->recv_out(0, buff, status);
// to avoid spinning, wait 50 ms
Os::Task::delay(Fw::TimeInterval(0, 50000));
@ -350,12 +347,12 @@ void LinuxUartDriver ::serialReadTaskEntry(void* ptr) {
if (stat == -1) {
Fw::LogStringArg _arg = comp->m_device;
comp->log_WARNING_HI_ReadError(_arg, stat);
status = RecvStatus::RECV_ERROR;
status = ByteStreamStatus::OTHER_ERROR;
} else if (stat > 0) {
buff.setSize(static_cast<U32>(stat));
status = RecvStatus::RECV_OK; // added by m.chase 03.06.2017
status = ByteStreamStatus::OP_OK; // added by m.chase 03.06.2017
} else {
status = RecvStatus::RECV_ERROR; // Simply to return the buffer
status = ByteStreamStatus::OTHER_ERROR; // Simply to return the buffer
}
comp->recv_out(0, buff, status); // added by m.chase 03.06.2017
}

View File

@ -11,9 +11,6 @@ module Drv {
@ Allocation port used for allocating memory in the receive task
output port allocate: Fw.BufferGet
@ Deallocates buffers passed to the "send" port
output port deallocate: Fw.BufferSend
# ----------------------------------------------------------------------
# Special ports
# ----------------------------------------------------------------------

View File

@ -93,7 +93,7 @@ class LinuxUartDriver final : public LinuxUartDriverComponentBase {
//! Handler implementation for serialSend
//!
Drv::SendStatus send_handler(FwIndexType portNum, /*!< The port number*/
void send_handler(FwIndexType portNum, /*!< The port number*/
Fw::Buffer& serBuffer);

View File

@ -1,22 +0,0 @@
####
# F prime CMakeLists.txt:
#
# SOURCE_FILES: combined list of source and autocoding files
# MOD_DEPS: (optional) module dependencies
#
####
set(SOURCE_FILES
"${CMAKE_CURRENT_LIST_DIR}/StreamCrossover.fpp"
"${CMAKE_CURRENT_LIST_DIR}/StreamCrossover.cpp"
)
register_fprime_module()
# Register the unit test build
set(UT_SOURCE_FILES
"${CMAKE_CURRENT_LIST_DIR}/StreamCrossover.fpp"
"${CMAKE_CURRENT_LIST_DIR}/test/ut/StreamCrossoverTester.cpp"
"${CMAKE_CURRENT_LIST_DIR}/test/ut/StreamCrossoverTestMain.cpp"
)
set(UT_AUTO_HELPERS ON)
register_fprime_ut()

View File

@ -1,57 +0,0 @@
// ======================================================================
// \title StreamCrossover.cpp
// \author ethanchee
// \brief cpp file for StreamCrossover component implementation class
// ======================================================================
#include <Drv/StreamCrossover/StreamCrossover.hpp>
#include <Fw/FPrimeBasicTypes.hpp>
namespace Drv {
// ----------------------------------------------------------------------
// Construction, initialization, and destruction
// ----------------------------------------------------------------------
StreamCrossover ::
StreamCrossover(
const char *const compName
) : StreamCrossoverComponentBase(compName)
{
}
StreamCrossover ::
~StreamCrossover()
{
}
// ----------------------------------------------------------------------
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------
void StreamCrossover ::
streamIn_handler(
const FwIndexType portNum,
Fw::Buffer &recvBuffer,
const Drv::RecvStatus &recvStatus
)
{
if(recvStatus == Drv::RecvStatus::RECV_ERROR || recvBuffer.getSize() == 0)
{
this->log_WARNING_HI_StreamOutError(Drv::SendStatus::SEND_ERROR);
this->errorDeallocate_out(0, recvBuffer);
return;
}
Drv::SendStatus sendStatus = this->streamOut_out(0, recvBuffer);
if(sendStatus != Drv::SendStatus::SEND_OK)
{
this->log_WARNING_HI_StreamOutError(sendStatus);
}
}
} // end namespace Drv

View File

@ -1,28 +0,0 @@
module Drv {
passive component StreamCrossover {
output port streamOut: Drv.ByteStreamSend
sync input port streamIn: Drv.ByteStreamRecv
@ Indicates buffer failed to send to streamOut.
event StreamOutError(sendStatus: Drv.SendStatus) \
severity warning high \
format "StreamCrossover StreamOut Error: {}"
@ Allows for deallocation after bad receive status
output port errorDeallocate: Fw.BufferSend
@ Port for requesting the current time
time get port timeCaller
@ Port for sending textual representation of events
text event port logTextOut
@ Port for sending events to downlink
event port logOut
}
}

View File

@ -1,53 +0,0 @@
// ======================================================================
// \title StreamCrossover.hpp
// \author ethanchee
// \brief hpp file for StreamCrossover component implementation class
// ======================================================================
#ifndef StreamCrossover_HPP
#define StreamCrossover_HPP
#include "Drv/StreamCrossover/StreamCrossoverComponentAc.hpp"
namespace Drv {
class StreamCrossover final :
public StreamCrossoverComponentBase
{
public:
// ----------------------------------------------------------------------
// Construction, initialization, and destruction
// ----------------------------------------------------------------------
//! Construct object StreamCrossover
//!
StreamCrossover(
const char *const compName /*!< The component name*/
);
//! Destroy object StreamCrossover
//!
~StreamCrossover();
PRIVATE:
// ----------------------------------------------------------------------
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------
//! Handler implementation for streamIn
//!
void streamIn_handler(
const FwIndexType portNum, /*!< The port number*/
Fw::Buffer &recvBuffer,
const Drv::RecvStatus &recvStatus
);
};
} // end namespace Drv
#endif

View File

@ -1,29 +0,0 @@
# Drv::StreamCrossover Stream Crossover Component
The Stream Crossover component allows a connection of byte stream driver model ports of type ByteStreamRecv and
ByteStreamSend.
## Design
The Drv::StreamCrossover utilizes the byte stream driver model to handle the incoming stream of bytes. Upon calling
the "streamIn" port, the `Fw::Buffer` containing the data will be forwarded to the "streamOut" port. This enables a
connection from a ByteStreamRecv port to a ByteStreamSend port.
## Port Descriptions
| Name | Description |
|---|---|
| streamOut | A ByteStreamSend port for outgoing data stored in `Fw::Buffer` |
| streamIn | A ByteStreamRecv port for incoming data stored in `Fw::Buffer` |
| errorDeallocate | Deallocate a `Fw::Buffer` on error |
## Requirements
Add requirements in the chart below
| Name | Description | Validation |
|---|---|---|
| STREAM-CROSSOVER-COMP-001 | The stream crossover component shall provide the capability to forward bytes | Unit Test |
## Change Log
| Date | Description |
|---|---|
| 2023-06-05 | Initial Draft |
| 2023-06-09 | Implement Error Handling |

View File

@ -1,20 +0,0 @@
// ----------------------------------------------------------------------
// TestMain.cpp
// ----------------------------------------------------------------------
#include "StreamCrossoverTester.hpp"
TEST(Nominal, TestBuffer) {
Drv::StreamCrossoverTester tester;
tester.sendTestBuffer();
}
TEST(Nominal, TestFail) {
Drv::StreamCrossoverTester tester;
tester.testFail();
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -1,101 +0,0 @@
// ======================================================================
// \title StreamCrossover.hpp
// \author ethanchee
// \brief cpp file for StreamCrossover test harness implementation class
// ======================================================================
#include "StreamCrossoverTester.hpp"
namespace Drv {
// ----------------------------------------------------------------------
// Construction and destruction
// ----------------------------------------------------------------------
StreamCrossoverTester ::
StreamCrossoverTester() :
StreamCrossoverGTestBase("Tester", StreamCrossoverTester::MAX_HISTORY_SIZE),
component("StreamCrossover")
{
this->initComponents();
this->connectPorts();
}
StreamCrossoverTester ::
~StreamCrossoverTester()
{
}
// ----------------------------------------------------------------------
// Tests
// ----------------------------------------------------------------------
void StreamCrossoverTester ::
sendTestBuffer()
{
U8 testStr[6] = "test\n";
Fw::Buffer sendBuffer(testStr, sizeof(testStr));
this->invoke_to_streamIn(0, sendBuffer, Drv::RecvStatus::RECV_OK);
// Ensure only one buffer was sent to streamOut
ASSERT_from_streamOut_SIZE(1);
// Ensure the sendBuffer was sent
ASSERT_from_streamOut(0, sendBuffer);
}
void StreamCrossoverTester ::
testFail()
{
U8 testStr[6] = "test\n";
Fw::Buffer sendBuffer(testStr, sizeof(testStr));
this->invoke_to_streamIn(0, sendBuffer, Drv::RecvStatus::RECV_ERROR);
// Ensure only one buffer was sent to errorDeallocate port on RECV_ERROR
ASSERT_from_errorDeallocate_SIZE(1);
// Ensure the sendBuffer was sent
ASSERT_from_errorDeallocate(0, sendBuffer);
// Ensure the error event was sent
ASSERT_EVENTS_StreamOutError_SIZE(1);
// Ensure the error is SEND_ERROR
ASSERT_EVENTS_StreamOutError(0, Drv::SendStatus::SEND_ERROR);
}
// ----------------------------------------------------------------------
// Handlers for typed from ports
// ----------------------------------------------------------------------
Drv::SendStatus StreamCrossoverTester ::
from_streamOut_handler(
const FwIndexType portNum,
Fw::Buffer &sendBuffer
)
{
this->pushFromPortEntry_streamOut(sendBuffer);
U8 testStr[6] = "test\n";
Fw::Buffer cmpBuffer(testStr, sizeof(testStr));
if(!(cmpBuffer == sendBuffer))
{
return Drv::SendStatus::SEND_ERROR;
}
return Drv::SendStatus::SEND_OK;
}
void StreamCrossoverTester ::
from_errorDeallocate_handler(
const FwIndexType portNum,
Fw::Buffer &fwBuffer
)
{
this->pushFromPortEntry_errorDeallocate(fwBuffer);
}
} // end namespace Drv

View File

@ -1,99 +0,0 @@
// ======================================================================
// \title StreamCrossover/test/ut/Tester.hpp
// \author ethanchee
// \brief hpp file for StreamCrossover test harness implementation class
// ======================================================================
#ifndef TESTER_HPP
#define TESTER_HPP
#include "StreamCrossoverGTestBase.hpp"
#include "Drv/StreamCrossover/StreamCrossover.hpp"
namespace Drv {
class StreamCrossoverTester :
public StreamCrossoverGTestBase
{
// ----------------------------------------------------------------------
// Construction and destruction
// ----------------------------------------------------------------------
public:
// Maximum size of histories storing events, telemetry, and port outputs
static const U32 MAX_HISTORY_SIZE = 10;
// Instance ID supplied to the component instance under test
static const FwEnumStoreType TEST_INSTANCE_ID = 0;
//! Construct object StreamCrossoverTester
//!
StreamCrossoverTester();
//! Destroy object StreamCrossoverTester
//!
~StreamCrossoverTester();
public:
// ----------------------------------------------------------------------
// Tests
// ----------------------------------------------------------------------
//! Send a test buffer to streamOut from streamIn
//!
void sendTestBuffer();
//! Send a fail RECV_STATUS to test error
//!
void testFail();
private:
// ----------------------------------------------------------------------
// Handlers for typed from ports
// ----------------------------------------------------------------------
//! Handler for from_streamOut
//!
Drv::SendStatus from_streamOut_handler(
const FwIndexType portNum, /*!< The port number*/
Fw::Buffer &sendBuffer
);
//! Handler for from_deallocate
//!
void from_errorDeallocate_handler(
const FwIndexType portNum, /*!< The port number*/
Fw::Buffer &fwBuffer
);
private:
// ----------------------------------------------------------------------
// Helper methods
// ----------------------------------------------------------------------
//! Connect ports
//!
void connectPorts();
//! Initialize components
//!
void initComponents();
private:
// ----------------------------------------------------------------------
// Variables
// ----------------------------------------------------------------------
//! The component under test
//!
StreamCrossover component;
};
} // end namespace Drv
#endif

View File

@ -3,9 +3,8 @@ module Drv {
include "../Interfaces/ByteStreamDriverInterface.fppi"
@ Allocation for received data
output port allocate: Fw.BufferGet
output port deallocate: Fw.BufferSend
}
}

View File

@ -52,15 +52,15 @@ Fw::Buffer TcpClientComponentImpl::getBuffer() {
}
void TcpClientComponentImpl::sendBuffer(Fw::Buffer buffer, SocketIpStatus status) {
Drv::RecvStatus recvStatus = RecvStatus::RECV_ERROR;
Drv::ByteStreamStatus recvStatus = ByteStreamStatus::OTHER_ERROR;
if (status == SOCK_SUCCESS) {
recvStatus = RecvStatus::RECV_OK;
recvStatus = ByteStreamStatus::OP_OK;
}
else if (status == SOCK_NO_DATA_AVAILABLE) {
recvStatus = RecvStatus::RECV_NO_DATA;
recvStatus = ByteStreamStatus::RECV_NO_DATA;
}
else {
recvStatus = RecvStatus::RECV_ERROR;
recvStatus = ByteStreamStatus::OTHER_ERROR;
}
this->recv_out(0, buffer, recvStatus);
}
@ -76,17 +76,22 @@ void TcpClientComponentImpl::connected() {
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------
Drv::SendStatus TcpClientComponentImpl::send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) {
void TcpClientComponentImpl::send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) {
Drv::SocketIpStatus status = send(fwBuffer.getData(), fwBuffer.getSize());
// Only deallocate buffer when the caller is not asked to retry
if (status == SOCK_INTERRUPTED_TRY_AGAIN) {
return SendStatus::SEND_RETRY;
} else if (status != SOCK_SUCCESS) {
deallocate_out(0, fwBuffer);
return SendStatus::SEND_ERROR;
Drv::ByteStreamStatus returnStatus;
switch (status) {
case SOCK_INTERRUPTED_TRY_AGAIN:
returnStatus = ByteStreamStatus::SEND_RETRY;
break;
case SOCK_SUCCESS:
returnStatus = ByteStreamStatus::OP_OK;
break;
default:
returnStatus = ByteStreamStatus::OTHER_ERROR;
break;
}
deallocate_out(0, fwBuffer);
return SendStatus::SEND_OK;
// Return the buffer and status to the caller
this->dataReturnOut_out(0, fwBuffer, returnStatus);
}
} // end namespace Drv

View File

@ -76,7 +76,7 @@ class TcpClientComponentImpl final : public TcpClientComponentBase, public Socke
*
* \return IpSocket reference
*/
IpSocket& getSocketHandler();
IpSocket& getSocketHandler() override;
/**
* \brief returns a buffer to fill with data
@ -86,7 +86,7 @@ class TcpClientComponentImpl final : public TcpClientComponentBase, public Socke
*
* \return Fw::Buffer to fill with data
*/
Fw::Buffer getBuffer();
Fw::Buffer getBuffer() override;
/**
* \brief sends a buffer to be filled with data
@ -96,12 +96,12 @@ class TcpClientComponentImpl final : public TcpClientComponentBase, public Socke
*
* \return Fw::Buffer filled with data to send out
*/
void sendBuffer(Fw::Buffer buffer, SocketIpStatus status);
void sendBuffer(Fw::Buffer buffer, SocketIpStatus status) override;
/**
* \brief called when the IPv4 system has been connected
*/
void connected();
void connected() override;
PRIVATE:
@ -115,7 +115,7 @@ class TcpClientComponentImpl final : public TcpClientComponentBase, public Socke
*
* Passing data to this port will send data from the TcpClient to whatever TCP server this component has connected
* to. Should the socket not be opened or was disconnected, then this port call will return SEND_RETRY and critical
* transmissions should be retried. SEND_ERROR indicates an unresolvable error. SEND_OK is returned when the data
* transmissions should be retried. OTHER_ERROR indicates an unresolvable error. OP_OK is returned when the data
* has been sent.
*
* Note: this component delegates the reopening of the socket to the read thread and thus the caller should retry
@ -123,9 +123,8 @@ class TcpClientComponentImpl final : public TcpClientComponentBase, public Socke
*
* \param portNum: fprime port number of the incoming port call
* \param fwBuffer: buffer containing data to be sent
* \return SEND_OK on success, SEND_RETRY when critical data should be retried and SEND_ERROR upon error
*/
Drv::SendStatus send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer);
void send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) override;
Drv::TcpClientSocket m_socket; //!< Socket implementation

View File

@ -5,38 +5,14 @@ connects and sends/receives bytes. It implements the callback formation (shown b
and producing the callback port call.
For more information on the supporting TCP implementation see: Drv::TcpClientSocket.
For more information on the ByteStreamModelDriver see: Drv::ByteStreamDriverModel.
For more information on the ByteStreamModelDriver see: [`Drv::ByteStreamDriverModel`](../../ByteStreamDriverModel/docs/sdd.md).
## Design
The manager component (typically the ground interface) initiates the transfer of send data by calling the "send" port.
The caller will provide a `Fw::Buffer` containing the data to send and the port call will return a status of that send.
These responses are an enumeration whose values are described in the following table:
| Value | Description |
|---|---|
| Drv::SEND_OK | Send functioned normally. |
| Drv::SEND_RETRY | Send should be retried, but a subsequent send should return SEND_OK. |
| Drv::SEND_ERROR | Send produced an error, future sends likely to fail. |
This data is immediately sent out to the remote tcp server with a configured send timeout. See Usage described below.
**Callback Formation**
![Callback](../../ByteStreamDriverModel/docs/img/canvas-callback.png)
In the callback formation, the byte stream driver component initiates the transfer of received data by calling the
"readCallback" output port. This port transfers any read data in a `Fw::Buffer` along with a status for the receive.
This status is an enumeration whose values are described in the following table:
| Value | Description |
|---|---|
| Drv::RECV_OK | Receive functioned normally buffer contains valid data. |
| Drv::RECV_ERROR | Receive produced an error and buffer contains no valid data. |
The TcpClient component implements the design specified by the [`Drv::ByteStreamDriverModel`](../../ByteStreamDriverModel/docs/sdd.md).
## Usage
The Drv::TcpClientComponentImpl must be configured with the address of the remote connection using the `configure` method.
The sockets must also be opened to send and receive data using `open`. When the component is set to automatically open,
`open` is called via the first send or receive. Users declining to use automatic opening or who wish to control when open

View File

@ -79,8 +79,10 @@ void TcpClientTester ::test_with_loop(U32 iterations, bool recv_thread) {
Drv::Test::force_recv_timeout(server_fd.serverFd, server);
m_data_buffer.setSize(sizeof(m_data_storage));
size = Drv::Test::fill_random_buffer(m_data_buffer);
Drv::SendStatus status = invoke_to_send(0, m_data_buffer);
EXPECT_EQ(status, SendStatus::SEND_OK);
invoke_to_send(0, m_data_buffer);
ASSERT_from_dataReturnOut_SIZE(i + 1);
Drv::ByteStreamStatus status = this->fromPortHistory_dataReturnOut->at(i).status;
EXPECT_EQ(status, ByteStreamStatus::OP_OK);
Drv::Test::receive_all(server, server_fd, buffer, size);
Drv::Test::validate_random_buffer(m_data_buffer, buffer);
// If receive thread is live, try the other way
@ -89,7 +91,6 @@ void TcpClientTester ::test_with_loop(U32 iterations, bool recv_thread) {
m_data_buffer.setSize(sizeof(m_data_storage));
status2 = server.send(server_fd, m_data_buffer.getData(), m_data_buffer.getSize());
EXPECT_EQ(status2, Drv::SOCK_SUCCESS);
from_deallocate_handler(0, m_data_buffer);
while (not m_spinner) {}
}
}
@ -180,18 +181,18 @@ void TcpClientTester ::test_no_automatic_recv_connection() {
}
// ----------------------------------------------------------------------
// Handlers for typed from ports
// Handler overrides for typed from ports
// ----------------------------------------------------------------------
void TcpClientTester ::
from_recv_handler(
const FwIndexType portNum,
Fw::Buffer &recvBuffer,
const RecvStatus &recvStatus
const ByteStreamStatus &ByteStreamStatus
)
{
this->pushFromPortEntry_recv(recvBuffer, recvStatus);
if (recvStatus == RecvStatus::RECV_OK){
this->pushFromPortEntry_recv(recvBuffer, ByteStreamStatus);
if (ByteStreamStatus == ByteStreamStatus::OP_OK){
// Make sure we can get to unblocking the spinner
EXPECT_EQ(m_data_buffer.getSize(), recvBuffer.getSize()) << "Invalid transmission size";
Drv::Test::validate_random_buffer(m_data_buffer, recvBuffer.getData());
@ -201,10 +202,6 @@ void TcpClientTester ::test_no_automatic_recv_connection() {
delete[] recvBuffer.getData();
}
void TcpClientTester ::from_ready_handler(const FwIndexType portNum) {
this->pushFromPortEntry_ready();
}
Fw::Buffer TcpClientTester ::
from_allocate_handler(
const FwIndexType portNum,
@ -217,13 +214,4 @@ Fw::Buffer TcpClientTester ::
return buffer;
}
void TcpClientTester ::
from_deallocate_handler(
const FwIndexType portNum,
Fw::Buffer &fwBuffer
)
{
this->pushFromPortEntry_deallocate(fwBuffer);
}
} // end namespace Drv

View File

@ -70,7 +70,7 @@ namespace Drv {
private:
// ----------------------------------------------------------------------
// Handlers for typed from ports
// Handler overrides for typed from ports
// ----------------------------------------------------------------------
//! Handler for from_recv
@ -78,28 +78,15 @@ namespace Drv {
void from_recv_handler(
const FwIndexType portNum, /*!< The port number*/
Fw::Buffer &recvBuffer,
const RecvStatus &recvStatus
);
//! Handler for from_ready
//!
void from_ready_handler(
const FwIndexType portNum /*!< The port number*/
);
const ByteStreamStatus &ByteStreamStatus
) override;
//! Handler for from_allocate
//!
Fw::Buffer from_allocate_handler(
const FwIndexType portNum, /*!< The port number*/
U32 size
);
//! Handler for from_deallocate
//!
void from_deallocate_handler(
const FwIndexType portNum, /*!< The port number*/
Fw::Buffer &fwBuffer
);
) override;
private:

View File

@ -3,9 +3,8 @@ module Drv {
include "../Interfaces/ByteStreamDriverInterface.fppi"
@ Allocation for received data
output port allocate: Fw.BufferGet
output port deallocate: Fw.BufferSend
}
}

View File

@ -59,15 +59,15 @@ Fw::Buffer TcpServerComponentImpl::getBuffer() {
}
void TcpServerComponentImpl::sendBuffer(Fw::Buffer buffer, SocketIpStatus status) {
Drv::RecvStatus recvStatus = RecvStatus::RECV_ERROR;
Drv::ByteStreamStatus recvStatus = ByteStreamStatus::OTHER_ERROR;
if (status == SOCK_SUCCESS) {
recvStatus = RecvStatus::RECV_OK;
recvStatus = ByteStreamStatus::OP_OK;
}
else if (status == SOCK_NO_DATA_AVAILABLE) {
recvStatus = RecvStatus::RECV_NO_DATA;
recvStatus = ByteStreamStatus::RECV_NO_DATA;
}
else {
recvStatus = RecvStatus::RECV_ERROR;
recvStatus = ByteStreamStatus::OTHER_ERROR;
}
this->recv_out(0, buffer, recvStatus);
}
@ -124,17 +124,22 @@ void TcpServerComponentImpl::readLoop() {
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------
Drv::SendStatus TcpServerComponentImpl::send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) {
void TcpServerComponentImpl::send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) {
Drv::SocketIpStatus status = this->send(fwBuffer.getData(), fwBuffer.getSize());
// Only deallocate buffer when the caller is not asked to retry
if (status == SOCK_INTERRUPTED_TRY_AGAIN) {
return SendStatus::SEND_RETRY;
} else if (status != SOCK_SUCCESS) {
deallocate_out(0, fwBuffer);
return SendStatus::SEND_ERROR;
Drv::ByteStreamStatus returnStatus;
switch (status) {
case SOCK_INTERRUPTED_TRY_AGAIN:
returnStatus = ByteStreamStatus::SEND_RETRY;
break;
case SOCK_SUCCESS:
returnStatus = ByteStreamStatus::OP_OK;
break;
default:
returnStatus = ByteStreamStatus::OTHER_ERROR;
break;
}
deallocate_out(0, fwBuffer);
return SendStatus::SEND_OK;
// Return the buffer and status to the caller
this->dataReturnOut_out(0, fwBuffer, returnStatus);
}
} // end namespace Drv

View File

@ -150,7 +150,7 @@ class TcpServerComponentImpl final : public TcpServerComponentBase, public Socke
*
* Passing data to this port will send data from the TcpServer to whatever TCP client this component has connected
* to. Should the socket not be opened or was disconnected, then this port call will return SEND_RETRY and critical
* transmissions should be retried. SEND_ERROR indicates an unresolvable error. SEND_OK is returned when the data
* transmissions should be retried. OTHER_ERROR indicates an unresolvable error. OP_OK is returned when the data
* has been sent.
*
* Note: this component delegates the reopening of the socket to the read thread and thus the caller should retry
@ -158,9 +158,8 @@ class TcpServerComponentImpl final : public TcpServerComponentBase, public Socke
*
* \param portNum: fprime port number of the incoming port call
* \param fwBuffer: buffer containing data to be sent
* \return SEND_OK on success, SEND_RETRY when critical data should be retried and SEND_ERROR upon error
*/
Drv::SendStatus send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) override;
void send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) override;
Drv::TcpServerSocket m_socket; //!< Socket implementation

View File

@ -6,34 +6,11 @@ and producing the callback port call. Since it is a server, it must startup and
for single client communication, it does not permit a queue of connecting clients.
For more information on the supporting TCP implementation see: [Drv::TcpServerSocket](../../Ip/docs/sdd.md#drvtcpserversocket-class).
For more information on the ByteStreamModelDriver see: [Drv::ByteStreamDriverModel](../..//ByteStreamDriverModel/docs/sdd.md).
For more information on the ByteStreamModelDriver see: [Drv::ByteStreamDriverModel](../../ByteStreamDriverModel/docs/sdd.md).
## Design
The manager component (typically the ground interface) initiates the transfer of send data by calling the "send" port.
The caller will provide a `Fw::Buffer` containing the data to send and the port call will return a status of that send.
These responses are an enumeration whose values are described in the following table:
| Value | Description |
|---|---|
| Drv::SEND_OK | Send functioned normally. |
| Drv::SEND_RETRY | Send should be retried, but a subsequent send should return SEND_OK. |
| Drv::SEND_ERROR | Send produced an error, future sends likely to fail. |
This data is immediately sent out to the remote tcp server with a configured send timeout. See Usage described below.
**Callback Formation**
![Callback](../../ByteStreamDriverModel/docs/img/canvas-callback.png)
In the callback formation, the byte stream driver component initiates the transfer of received data by calling the
"readCallback" output port. This port transfers any read data in a `Fw::Buffer` along with a status for the receive.
This status is an enumeration whose values are described in the following table:
| Value | Description |
|---|---|
| Drv::RECV_OK | Receive functioned normally buffer contains valid data. |
| Drv::RECV_ERROR | Receive produced an error and buffer contains no valid data. |
The TcpClient component implements the design specified by the [`Drv::ByteStreamDriverModel`](../../ByteStreamDriverModel/docs/sdd.md).
## Usage

View File

@ -80,8 +80,10 @@ void TcpServerTester ::test_with_loop(U32 iterations, bool recv_thread) {
Drv::Test::force_recv_timeout(client_fd.fd, client);
m_data_buffer.setSize(sizeof(m_data_storage));
size = Drv::Test::fill_random_buffer(m_data_buffer);
Drv::SendStatus status = invoke_to_send(0, m_data_buffer);
EXPECT_EQ(status, SendStatus::SEND_OK) <<
invoke_to_send(0, m_data_buffer);
ASSERT_from_dataReturnOut_SIZE(i + 1);
Drv::ByteStreamStatus status = this->fromPortHistory_dataReturnOut->at(i).status;
EXPECT_EQ(status, ByteStreamStatus::OP_OK) <<
"On iteration: " << i << " and receive thread: " << recv_thread;
Drv::Test::receive_all(client, client_fd, buffer, size);
EXPECT_EQ(status2, Drv::SOCK_SUCCESS) <<
@ -219,10 +221,10 @@ void TcpServerTester ::test_no_automatic_recv_connection() {
// Handlers for typed from ports
// ----------------------------------------------------------------------
void TcpServerTester ::from_recv_handler(const FwIndexType portNum, Fw::Buffer& recvBuffer, const RecvStatus& recvStatus) {
void TcpServerTester ::from_recv_handler(const FwIndexType portNum, Fw::Buffer& recvBuffer, const ByteStreamStatus& recvStatus) {
// this function will still receive a status of error because the recv port is always called
this->pushFromPortEntry_recv(recvBuffer, recvStatus);
if (recvStatus == RecvStatus::RECV_OK) {
if (recvStatus == ByteStreamStatus::OP_OK) {
// Make sure we can get to unblocking the spinner
EXPECT_EQ(m_data_buffer.getSize(), recvBuffer.getSize()) << "Invalid transmission size";
Drv::Test::validate_random_buffer(m_data_buffer, recvBuffer.getData());
@ -231,10 +233,6 @@ void TcpServerTester ::from_recv_handler(const FwIndexType portNum, Fw::Buffer&
delete[] recvBuffer.getData();
}
void TcpServerTester ::from_ready_handler(const FwIndexType portNum) {
this->pushFromPortEntry_ready();
}
Fw::Buffer TcpServerTester ::
from_allocate_handler(
const FwIndexType portNum,
@ -246,12 +244,4 @@ Fw::Buffer TcpServerTester ::
return buffer;
}
void TcpServerTester ::
from_deallocate_handler(
const FwIndexType portNum,
Fw::Buffer &fwBuffer
)
{
this->pushFromPortEntry_deallocate(fwBuffer);
}
} // end namespace Drv

View File

@ -82,7 +82,7 @@ namespace Drv {
private:
// ----------------------------------------------------------------------
// Handlers for typed from ports
// Handlers overrides for typed from ports
// ----------------------------------------------------------------------
//! Handler for from_recv
@ -90,28 +90,15 @@ namespace Drv {
void from_recv_handler(
const FwIndexType portNum, /*!< The port number*/
Fw::Buffer &recvBuffer,
const RecvStatus &recvStatus
);
//! Handler for from_ready
//!
void from_ready_handler(
const FwIndexType portNum /*!< The port number*/
);
const ByteStreamStatus &recvStatus
) override;
//! Handler for from_allocate
//!
Fw::Buffer from_allocate_handler(
const FwIndexType portNum, /*!< The port number*/
U32 size
);
//! Handler for from_deallocate
//!
void from_deallocate_handler(
const FwIndexType portNum, /*!< The port number*/
Fw::Buffer &fwBuffer
);
) override;
private:

View File

@ -5,7 +5,5 @@ module Drv {
output port allocate: Fw.BufferGet
output port deallocate: Fw.BufferSend
}
}

View File

@ -59,15 +59,15 @@ Fw::Buffer UdpComponentImpl::getBuffer() {
}
void UdpComponentImpl::sendBuffer(Fw::Buffer buffer, SocketIpStatus status) {
Drv::RecvStatus recvStatus = RecvStatus::RECV_ERROR;
Drv::ByteStreamStatus recvStatus = ByteStreamStatus::OTHER_ERROR;
if (status == SOCK_SUCCESS) {
recvStatus = RecvStatus::RECV_OK;
recvStatus = ByteStreamStatus::OP_OK;
}
else if (status == SOCK_NO_DATA_AVAILABLE) {
recvStatus = RecvStatus::RECV_NO_DATA;
recvStatus = ByteStreamStatus::RECV_NO_DATA;
}
else {
recvStatus = RecvStatus::RECV_ERROR;
recvStatus = ByteStreamStatus::OTHER_ERROR;
}
this->recv_out(0, buffer, recvStatus);
}
@ -82,16 +82,25 @@ void UdpComponentImpl::connected() {
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------
Drv::SendStatus UdpComponentImpl::send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) {
void UdpComponentImpl::send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) {
Drv::SocketIpStatus status = send(fwBuffer.getData(), fwBuffer.getSize());
// Always return the buffer
deallocate_out(0, fwBuffer);
if ((status == SOCK_DISCONNECTED) || (status == SOCK_INTERRUPTED_TRY_AGAIN)) {
return SendStatus::SEND_RETRY;
} else if (status != SOCK_SUCCESS) {
return SendStatus::SEND_ERROR;
Drv::ByteStreamStatus returnStatus;
switch (status) {
case SOCK_INTERRUPTED_TRY_AGAIN:
returnStatus = ByteStreamStatus::SEND_RETRY;
break;
case SOCK_DISCONNECTED:
returnStatus = ByteStreamStatus::SEND_RETRY;
break;
case SOCK_SUCCESS:
returnStatus = ByteStreamStatus::OP_OK;
break;
default:
returnStatus = ByteStreamStatus::OTHER_ERROR;
break;
}
return SendStatus::SEND_OK;
// Return the buffer and status to the caller
this->dataReturnOut_out(0, fwBuffer, returnStatus);
}
} // end namespace Drv

View File

@ -137,7 +137,7 @@ PROTECTED:
*
* Passing data to this port will send data from the TcpClient to whatever TCP server this component has connected
* to. Should the socket not be opened or was disconnected, then this port call will return SEND_RETRY and critical
* transmissions should be retried. SEND_ERROR indicates an unresolvable error. SEND_OK is returned when the data
* transmissions should be retried. OTHER_ERROR indicates an unresolvable error. OP_OK is returned when the data
* has been sent.
*
* Note: this component delegates the reopening of the socket to the read thread and thus the caller should retry
@ -145,9 +145,8 @@ PROTECTED:
*
* \param portNum: fprime port number of the incoming port call
* \param fwBuffer: buffer containing data to be sent
* \return SEND_OK on success, SEND_RETRY when critical data should be retried and SEND_ERROR upon error
*/
Drv::SendStatus send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer);
void send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer);
Drv::UdpSocket m_socket; //!< Socket implementation

View File

@ -9,30 +9,7 @@ For more information on the ByteStreamModelDriver see: Drv::ByteStreamDriverMode
## Design
The manager component (typically the ground interface) initiates the transfer of send data by calling the "send" port.
The caller will provide a `Fw::Buffer` containing the data to send and the port call will return a status of that send.
These responses are an enumeration whose values are described in the following table:
| Value | Description |
|---|---|
| Drv::SEND_OK | Send functioned normally. |
| Drv::SEND_RETRY | Send should be retried, but a subsequent send should return SEND_OK. |
| Drv::SEND_ERROR | Send produced an error, future sends likely to fail. |
This data is immediately sent out to the remote UDP server with a configured send timeout. See Usage described below.
**Callback Formation**
![Callback](../../ByteStreamDriverModel/docs/img/canvas-callback.png)
In the callback formation, the byte stream driver component initiates the transfer of received data by calling the
"readCallback" output port. This port transfers any read data in a `Fw::Buffer` along with a status for the receive.
This status is an enumeration whose values are described in the following table:
| Value | Description |
|---|---|
| Drv::RECV_OK | Receive functioned normally buffer contains valid data. |
| Drv::RECV_ERROR | Receive produced an error and buffer contains no valid data. |
The TcpClient component implements the design specified by the [`Drv::ByteStreamDriverModel`](../../ByteStreamDriverModel/docs/sdd.md).
## Usage

View File

@ -91,8 +91,10 @@ void UdpTester::test_with_loop(U32 iterations, bool recv_thread) {
Drv::Test::force_recv_timeout(udp2_fd.fd, udp2);
m_data_buffer.setSize(sizeof(m_data_storage));
size = Drv::Test::fill_random_buffer(m_data_buffer);
Drv::SendStatus status = invoke_to_send(0, m_data_buffer);
EXPECT_EQ(status, SendStatus::SEND_OK);
invoke_to_send(0, m_data_buffer);
ASSERT_from_dataReturnOut_SIZE(i + 1);
Drv::ByteStreamStatus status = this->fromPortHistory_dataReturnOut->at(i).status;
EXPECT_EQ(status, ByteStreamStatus::OP_OK);
Drv::Test::receive_all(udp2, udp2_fd, buffer, size);
Drv::Test::validate_random_buffer(m_data_buffer, buffer);
// If receive thread is live, try the other way
@ -160,10 +162,10 @@ void UdpTester ::test_advanced_reconnect() {
// Handlers for typed from ports
// ----------------------------------------------------------------------
void UdpTester ::from_recv_handler(const FwIndexType portNum, Fw::Buffer& recvBuffer, const RecvStatus& recvStatus) {
void UdpTester ::from_recv_handler(const FwIndexType portNum, Fw::Buffer& recvBuffer, const ByteStreamStatus& recvStatus) {
this->pushFromPortEntry_recv(recvBuffer, recvStatus);
// Make sure we can get to unblocking the spinner
if (recvStatus == RecvStatus::RECV_OK){
if (recvStatus == ByteStreamStatus::OP_OK){
EXPECT_EQ(m_data_buffer.getSize(), recvBuffer.getSize()) << "Invalid transmission size";
Drv::Test::validate_random_buffer(m_data_buffer, recvBuffer.getData());
m_spinner = true;
@ -171,10 +173,6 @@ void UdpTester ::from_recv_handler(const FwIndexType portNum, Fw::Buffer& recvBu
delete[] recvBuffer.getData();
}
void UdpTester ::from_ready_handler(const FwIndexType portNum) {
this->pushFromPortEntry_ready();
}
Fw::Buffer UdpTester ::
from_allocate_handler(
const FwIndexType portNum,
@ -187,13 +185,4 @@ Fw::Buffer UdpTester ::
return buffer;
}
void UdpTester ::
from_deallocate_handler(
const FwIndexType portNum,
Fw::Buffer &fwBuffer
)
{
this->pushFromPortEntry_deallocate(fwBuffer);
}
} // end namespace Drv

View File

@ -77,7 +77,7 @@ namespace Drv {
private:
// ----------------------------------------------------------------------
// Handlers for typed from ports
// Handler overrides for typed from ports
// ----------------------------------------------------------------------
//! Handler for from_recv
@ -85,28 +85,15 @@ namespace Drv {
void from_recv_handler(
const FwIndexType portNum, /*!< The port number*/
Fw::Buffer &recvBuffer,
const RecvStatus &recvStatus
);
//! Handler for from_ready
//!
void from_ready_handler(
const FwIndexType portNum /*!< The port number*/
);
const ByteStreamStatus &recvStatus
) override;
//! Handler for from_allocate
//!
Fw::Buffer from_allocate_handler(
const FwIndexType portNum, /*!< The port number*/
U32 size
);
//! Handler for from_deallocate
//!
void from_deallocate_handler(
const FwIndexType portNum, /*!< The port number*/
Fw::Buffer &fwBuffer
);
) override;
private:

View File

@ -16,8 +16,4 @@ module Fw {
$size: U32
) -> Fw.Buffer
@ Port for sending data buffer along with context buffer
@ This is useful for passing data that needs context to be interpreted
port DataWithContext(ref data: Fw.Buffer, ref context: Fw.Buffer)
}

View File

@ -30,7 +30,7 @@ module RPI {
async input port Run: Svc.Sched
@ Input port for receiving UART data
async input port UartRead: Drv.ByteStreamRecv
async input port UartRead: Drv.ByteStreamData
@ Output Port for reading GPIO values
output port GpioRead: [2] Drv.GpioRead
@ -42,7 +42,10 @@ module RPI {
output port GpioWrite: [3] Drv.GpioWrite
@ Output Port for writing UART data
output port UartWrite: Drv.ByteStreamSend
output port UartWrite: Fw.BufferSend
@ Input port for getting back buffer ownership and status when using UartWrite
sync input port UartWriteReturn: Drv.ByteStreamData
@ Output port for sending UART buffers to use for reading
output port UartBuffers: Fw.BufferSend

View File

@ -106,10 +106,10 @@ namespace RPI {
UartRead_handler(
const FwIndexType portNum,
Fw::Buffer &serBuffer,
const Drv::RecvStatus &status
const Drv::ByteStreamStatus &status
)
{
if (Drv::RecvStatus::RECV_OK == status.e) {
if (Drv::ByteStreamStatus::OP_OK == status.e) {
// convert incoming data to string. If it is not printable, set character to '*'
char uMsg[serBuffer.getSize() + 1];
char *bPtr = reinterpret_cast<char *>(serBuffer.getData());
@ -142,14 +142,16 @@ namespace RPI {
Fw::Buffer txt;
txt.setSize(text.length());
txt.setData(reinterpret_cast<U8*>(const_cast<char*>(text.toChar())));
Drv::SendStatus status = this->UartWrite_out(0, txt);
if (Drv::SendStatus::SEND_OK == status.e) {
this->m_uartWriteBytes += text.length();
this->UartWrite_out(0, txt);
this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
}
Fw::LogStringArg arg = text;
void RpiDemoComponentImpl ::UartWriteReturn_handler(FwIndexType portNum, Fw::Buffer& buffer, const Drv::ByteStreamStatus& status) {
if (Drv::ByteStreamStatus::OP_OK == status.e) {
this->m_uartWriteBytes += buffer.getSize();
Fw::LogStringArg arg(reinterpret_cast<char *>(buffer.getData()));
this->log_ACTIVITY_HI_RD_UartMsgOut(arg);
}
this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
}
void RpiDemoComponentImpl ::

View File

@ -68,9 +68,16 @@ namespace RPI {
void UartRead_handler(
const FwIndexType portNum, /*!< The port number*/
Fw::Buffer &serBuffer, /*!< Buffer containing data*/
const Drv::RecvStatus &status /*!< Status of read*/
const Drv::ByteStreamStatus &status /*!< Status of read*/
) override;
//! Handler implementation for UartWriteReturn
//!
//! Input port for getting back buffer ownership and status when using UartWrite
void UartWriteReturn_handler(FwIndexType portNum, //!< The port number
Fw::Buffer& buffer,
const Drv::ByteStreamStatus& status) override;
PRIVATE:
// ----------------------------------------------------------------------

View File

@ -4,7 +4,6 @@
#include "Fw/Types/MallocAllocator.hpp"
#include "Os/Console.hpp"
#include "RPI/Top/FppConstantsAc.hpp"
#include "Svc/FramingProtocol/FprimeProtocol.hpp"
#include "Svc/LinuxTimer/LinuxTimer.hpp"
#include <Svc/FrameAccumulator/FrameDetector/FprimeFrameDetector.hpp>

View File

@ -148,6 +148,30 @@ module RPI {
stack size Default.stackSize \
priority 30
instance comQueue: Svc.ComQueue base id 0x1100 \
queue size 50 \
stack size Default.stackSize \
priority 100 \
{
phase Fpp.ToCpp.Phases.configObjects """
Svc::ComQueue::QueueConfigurationTable configurationTable;
"""
phase Fpp.ToCpp.Phases.configComponents """
// Events (highest-priority)
ConfigObjects::RPI_comQueue::configurationTable.entries[0].depth = 100;
ConfigObjects::RPI_comQueue::configurationTable.entries[0].priority = 0;
// Telemetry
ConfigObjects::RPI_comQueue::configurationTable.entries[1].depth = 500;
ConfigObjects::RPI_comQueue::configurationTable.entries[1].priority = 2;
// File Downlink
ConfigObjects::RPI_comQueue::configurationTable.entries[2].depth = 100;
ConfigObjects::RPI_comQueue::configurationTable.entries[2].priority = 1;
RPI::comQueue.configure(ConfigObjects::RPI_comQueue::configurationTable, 0, Allocation::mallocator);
"""
}
# ----------------------------------------------------------------------
# Queued component instances
# ----------------------------------------------------------------------
@ -214,22 +238,11 @@ module RPI {
instance fatalAdapter: Svc.AssertFatalAdapter base id 1000
instance downlink: Svc.Framer base id 1220 \
{
phase Fpp.ToCpp.Phases.configObjects """
Svc::FprimeFraming framing;
"""
phase Fpp.ToCpp.Phases.configComponents """
RPI::downlink.setup(ConfigObjects::RPI_downlink::framing);
"""
}
instance framer: Svc.FprimeFramer base id 1220
instance deframer: Svc.FprimeDeframer base id 1240
instance comm: Drv.TcpClient base id 1260 \
instance comDriver: Drv.TcpClient base id 1260 \
{
phase Fpp.ToCpp.Phases.configConstants """
@ -242,7 +255,7 @@ module RPI {
phase Fpp.ToCpp.Phases.configComponents """
// Configure socket server if and only if there is a valid specification
if (state.hostName != nullptr && state.portNumber != 0) {
RPI::comm.configure(state.hostName, state.portNumber);
RPI::comDriver.configure(state.hostName, state.portNumber);
}
"""
@ -251,20 +264,20 @@ module RPI {
if (state.hostName != nullptr && state.portNumber != 0) {
// Uplink is configured for receive so a socket task is started
Os::TaskString name("ReceiveTask");
RPI::comm.start(
RPI::comDriver.start(
name,
ConfigConstants::RPI_comm::PRIORITY,
ConfigConstants::RPI_comm::STACK_SIZE
ConfigConstants::RPI_comDriver::PRIORITY,
ConfigConstants::RPI_comDriver::STACK_SIZE
);
}
"""
phase Fpp.ToCpp.Phases.stopTasks """
RPI::comm.stop();
RPI::comDriver.stop();
"""
phase Fpp.ToCpp.Phases.freeThreads """
(void) RPI::comm.join();
(void) RPI::comDriver.join();
"""
}
@ -474,4 +487,7 @@ module RPI {
instance fprimeRouter: Svc.FprimeRouter base id 3000
instance comStub: Svc.ComStub base id 3100
}

View File

@ -10,9 +10,11 @@ module RPI {
instance chanTlm
instance cmdDisp
instance cmdSeq
instance comm
instance comQueue
instance comDriver
instance comStub
instance deframer
instance downlink
instance framer
instance eventLogger
instance fatalAdapter
instance fatalHandler
@ -61,11 +63,26 @@ module RPI {
# ----------------------------------------------------------------------
connections Downlink {
chanTlm.PktSend -> downlink.comIn
downlink.bufferDeallocate -> fileDownlink.bufferReturn
downlink.framedOut -> comm.$send
eventLogger.PktSend -> downlink.comIn
fileDownlink.bufferSendOut -> downlink.bufferIn
eventLogger.PktSend -> comQueue.comPacketQueueIn[0]
chanTlm.PktSend -> comQueue.comPacketQueueIn[1]
fileDownlink.bufferSendOut -> comQueue.bufferQueueIn[0]
comQueue.queueSend -> framer.dataIn
comQueue.bufferReturnOut[0] -> fileDownlink.bufferReturn
framer.dataReturnOut -> comQueue.bufferReturnIn
framer.bufferAllocate -> commsBufferManager.bufferGetCallee
framer.bufferDeallocate -> commsBufferManager.bufferSendIn
framer.dataOut -> comStub.comDataIn
comStub.dataReturnOut -> framer.dataReturnIn
comDriver.dataReturnOut -> comStub.dataReturnIn
comDriver.ready -> comStub.drvConnected
comStub.drvDataOut -> comDriver.$send
comStub.comStatusOut -> framer.comStatusIn
framer.comStatusOut -> comQueue.comStatusIn
}
connections FaultProtection {
@ -109,9 +126,7 @@ module RPI {
}
connections MemoryAllocations {
comm.allocate -> commsBufferManager.bufferGetCallee
comm.deallocate -> commsBufferManager.bufferSendIn
downlink.framedAllocate -> commsBufferManager.bufferGetCallee
comDriver.allocate -> commsBufferManager.bufferGetCallee
fileUplink.bufferSendOut -> commsBufferManager.bufferSendIn
frameAccumulator.bufferAllocate -> commsBufferManager.bufferGetCallee
frameAccumulator.bufferDeallocate -> commsBufferManager.bufferSendIn
@ -124,10 +139,12 @@ module RPI {
rpiDemo.UartWrite -> uartDrv.$send
uartDrv.$recv -> rpiDemo.UartRead
uartDrv.allocate -> uartBufferManager.bufferGetCallee
uartDrv.dataReturnOut -> rpiDemo.UartWriteReturn
}
connections Uplink {
comm.$recv -> frameAccumulator.dataIn
comDriver.$recv -> comStub.drvDataIn
comStub.comDataOut -> frameAccumulator.dataIn
frameAccumulator.frameOut -> deframer.framedIn
deframer.deframedOut -> fprimeRouter.dataIn

View File

@ -15,10 +15,6 @@ set(SOURCE_FILES
set(MOD_DEPS
Fw/Logger
Svc/PosixTime
# Communication Implementations
Drv/Udp
Drv/TcpClient
Drv/TcpServer
)
register_fprime_module()

View File

@ -17,6 +17,8 @@ telemetry packets RefPackets {
Ref.fileDownlink.FilesSent
Ref.fileDownlink.PacketsSent
Ref.fileManager.CommandsExecuted
Ref.comQueue.comQueueDepth
Ref.comQueue.buffQueueDepth
# Ref.tlmSend.SendLevel
}

View File

@ -14,8 +14,8 @@
// Necessary project-specified types
#include <Fw/Types/MallocAllocator.hpp>
#include <Os/Console.hpp>
#include <Svc/FramingProtocol/FprimeProtocol.hpp>
#include <Svc/FrameAccumulator/FrameDetector/FprimeFrameDetector.hpp>
#include <Ref/Top/Ports_ComPacketQueueEnumAc.hpp>
// Used for 1Hz synthetic cycling
#include <Os/Mutex.hpp>
@ -32,7 +32,6 @@ Fw::MallocAllocator mallocator;
// The reference topology uses the F´ packet protocol when communicating with the ground and therefore uses the F´
// framing and deframing implementations.
Svc::FprimeFraming framing;
Svc::FrameDetectors::FprimeFrameDetector frameDetector;
@ -46,6 +45,8 @@ U32 rateGroup1Context[Svc::ActiveRateGroup::CONNECTION_COUNT_MAX] = {};
U32 rateGroup2Context[Svc::ActiveRateGroup::CONNECTION_COUNT_MAX] = {};
U32 rateGroup3Context[Svc::ActiveRateGroup::CONNECTION_COUNT_MAX] = {};
Svc::ComQueue::QueueConfigurationTable configurationTable;
// A number of constants are needed for construction of the topology. These are specified here.
enum TopologyConstants {
CMD_SEQ_BUFFER_SIZE = 5 * 1024,
@ -113,8 +114,6 @@ void configureTopology() {
dpBuffMgrBins.bins[0].numBuffers = DP_BUFFER_MANAGER_STORE_COUNT;
dpBufferManager.setup(DP_BUFFER_MANAGER_ID, 0, mallocator, dpBuffMgrBins);
// Framer and Deframer components need to be passed a protocol handler
framer.setup(framing);
frameAccumulator.configure(frameDetector, 1, mallocator, 2048);
Fw::FileNameString dpDir("./DpCat");
@ -126,6 +125,19 @@ void configureTopology() {
dpCat.configure(&dpDir,1,dpState,0,mallocator);
dpWriter.configure(dpDir);
// ComQueue configuration
// Events (highest-priority)
configurationTable.entries[Ref::Ports_ComPacketQueue::EVENTS].depth = 100;
configurationTable.entries[Ref::Ports_ComPacketQueue::EVENTS].priority = 0;
// Telemetry
configurationTable.entries[Ref::Ports_ComPacketQueue::TELEMETRY].depth = 500;
configurationTable.entries[Ref::Ports_ComPacketQueue::TELEMETRY].priority = 2;
// File Downlink (first entry after the ComPacket queues = NUM_CONSTANTS)
configurationTable.entries[Ref::Ports_ComPacketQueue::NUM_CONSTANTS].depth = 100;
configurationTable.entries[Ref::Ports_ComPacketQueue::NUM_CONSTANTS].priority = 1;
// Allocation identifier is 0 as the MallocAllocator discards it
comQueue.configure(configurationTable, 0, mallocator);
// Note: Uncomment when using Svc:TlmPacketizer
// tlmSend.setPacketList(Ref::Ref_RefPacketsTlmPackets::packetList, Ref::Ref_RefPacketsTlmPackets::omittedChannels, 1);
}
@ -144,7 +156,7 @@ void setupTopology(const TopologyState& state) {
// Autocoded configuration. Function provided by autocoder.
configComponents(state);
if (state.hostname != nullptr && state.port != 0) {
comm.configure(state.hostname, state.port);
comDriver.configure(state.hostname, state.port);
}
// Project-specific component configuration. Function provided above. May be inlined, if desired.
configureTopology();
@ -158,7 +170,7 @@ void setupTopology(const TopologyState& state) {
if (state.hostname != nullptr && state.port != 0) {
Os::TaskString name("ReceiveTask");
// Uplink is configured for receive so a socket task is started
comm.start(name, COMM_PRIORITY, Default::STACK_SIZE);
comDriver.start(name, COMM_PRIORITY, Default::STACK_SIZE);
}
}
@ -194,8 +206,8 @@ void teardownTopology(const TopologyState& state) {
freeThreads(state);
// Other task clean-up.
comm.stop();
(void)comm.join();
comDriver.stop();
(void)comDriver.join();
// Resource deallocation
cmdSeq.deallocateBuffer(mallocator);

View File

@ -14,7 +14,6 @@
#include "Drv/BlockDriver/BlockDriver.hpp"
#include "Fw/Types/MallocAllocator.hpp"
#include "Ref/Top/FppConstantsAc.hpp"
#include "Svc/FramingProtocol/FprimeProtocol.hpp"
#include "Svc/Health/Health.hpp"
// Definitions are placed within a namespace named after the deployment

View File

@ -102,8 +102,14 @@ module Ref {
stack size Default.STACK_SIZE \
priority 96
# ComQueue has a deeper queue to be resilient to spikes in com throughput
instance comQueue: Svc.ComQueue base id 0x1100 \
queue size 50 \
stack size Default.STACK_SIZE \
priority 100
instance typeDemo: Ref.TypeDemo base id 0x1100
instance typeDemo: Ref.TypeDemo base id 0x1200
# ----------------------------------------------------------------------
# Queued component instances
@ -137,35 +143,37 @@ module Ref {
# ----------------------------------------------------------------------
@ Communications driver. May be swapped with other comm drivers like UART
instance comm: Drv.TcpClient base id 0x4000
instance comDriver: Drv.TcpClient base id 0x4000
instance framer: Svc.Framer base id 0x4100
instance fatalAdapter: Svc.AssertFatalAdapter base id 0x4100
instance fatalAdapter: Svc.AssertFatalAdapter base id 0x4200
instance fatalHandler: Svc.FatalHandler base id 0x4200
instance fatalHandler: Svc.FatalHandler base id 0x4300
instance commsBufferManager: Svc.BufferManager base id 0x4300
instance commsBufferManager: Svc.BufferManager base id 0x4400
instance posixTime: Svc.PosixTime base id 0x4400
instance posixTime: Svc.PosixTime base id 0x4500
instance rateGroupDriverComp: Svc.RateGroupDriver base id 0x4500
instance rateGroupDriverComp: Svc.RateGroupDriver base id 0x4600
instance recvBuffComp: Ref.RecvBuff base id 0x4600
instance recvBuffComp: Ref.RecvBuff base id 0x4700
instance version: Svc.Version base id 0x4700
instance version: Svc.Version base id 0x4800
instance textLogger: Svc.PassiveTextLogger base id 0x4800
instance textLogger: Svc.PassiveTextLogger base id 0x4900
instance systemResources: Svc.SystemResources base id 0x4900
instance systemResources: Svc.SystemResources base id 0x4A00
instance dpBufferManager: Svc.BufferManager base id 0x4A00
instance dpBufferManager: Svc.BufferManager base id 0x4B00
instance frameAccumulator: Svc.FrameAccumulator base id 0x4B00
instance frameAccumulator: Svc.FrameAccumulator base id 0x4C00
instance deframer: Svc.FprimeDeframer base id 0x4C00
instance deframer: Svc.FprimeDeframer base id 0x4D00
instance fprimeRouter: Svc.FprimeRouter base id 0x4D00
instance fprimeRouter: Svc.FprimeRouter base id 0x4E00
instance fprimeFramer: Svc.FprimeFramer base id 0x4E00
instance comStub: Svc.ComStub base id 0x4F00
}

View File

@ -10,6 +10,11 @@ module Ref {
rateGroup3
}
enum Ports_ComPacketQueue {
EVENTS,
TELEMETRY
}
topology Ref {
# ----------------------------------------------------------------------
@ -26,7 +31,9 @@ module Ref {
instance tlmSend
instance cmdDisp
instance cmdSeq
instance comm
instance comDriver
instance comStub
instance comQueue
instance deframer
instance eventLogger
instance fatalAdapter
@ -36,7 +43,7 @@ module Ref {
instance fileUplink
instance commsBufferManager
instance frameAccumulator
instance framer
instance fprimeFramer
instance posixTime
instance pingRcvr
instance prmDb
@ -85,20 +92,28 @@ module Ref {
# ----------------------------------------------------------------------
connections Downlink {
tlmSend.PktSend -> framer.comIn
eventLogger.PktSend -> framer.comIn
fileDownlink.bufferSendOut -> framer.bufferIn
framer.framedAllocate -> commsBufferManager.bufferGetCallee
framer.framedOut -> comm.$send
framer.bufferDeallocate -> fileDownlink.bufferReturn
comm.deallocate -> commsBufferManager.bufferSendIn
dpCat.fileOut -> fileDownlink.SendFile
fileDownlink.FileComplete -> dpCat.fileDone
eventLogger.PktSend -> comQueue.comPacketQueueIn[Ports_ComPacketQueue.EVENTS]
tlmSend.PktSend -> comQueue.comPacketQueueIn[Ports_ComPacketQueue.TELEMETRY]
fileDownlink.bufferSendOut -> comQueue.bufferQueueIn[0]
comQueue.bufferReturnOut[0] -> fileDownlink.bufferReturn
comQueue.queueSend -> fprimeFramer.dataIn
fprimeFramer.dataReturnOut -> comQueue.bufferReturnIn
fprimeFramer.comStatusOut -> comQueue.comStatusIn
fprimeFramer.bufferAllocate -> commsBufferManager.bufferGetCallee
fprimeFramer.bufferDeallocate -> commsBufferManager.bufferSendIn
fprimeFramer.dataOut -> comStub.comDataIn
comStub.dataReturnOut -> fprimeFramer.dataReturnIn
comStub.comStatusOut -> fprimeFramer.comStatusIn
comStub.drvDataOut -> comDriver.$send
comDriver.dataReturnOut -> comStub.dataReturnIn
comDriver.ready -> comStub.drvConnected
}
connections FaultProtection {
@ -117,6 +132,7 @@ module Ref {
rateGroup1Comp.RateGroupMemberOut[2] -> tlmSend.Run
rateGroup1Comp.RateGroupMemberOut[3] -> fileDownlink.Run
rateGroup1Comp.RateGroupMemberOut[4] -> systemResources.run
rateGroup1Comp.RateGroupMemberOut[5] -> comQueue.run
# Rate group 2
rateGroupDriverComp.CycleOut[Ports_RateGroups.rateGroup2] -> rateGroup2Comp.CycleIn
@ -148,8 +164,9 @@ module Ref {
connections Uplink {
comm.allocate -> commsBufferManager.bufferGetCallee
comm.$recv -> frameAccumulator.dataIn
comDriver.allocate -> commsBufferManager.bufferGetCallee
comDriver.$recv -> comStub.drvDataIn
comStub.comDataOut -> frameAccumulator.dataIn
frameAccumulator.frameOut -> deframer.framedIn
frameAccumulator.bufferAllocate -> commsBufferManager.bufferGetCallee

View File

@ -8,6 +8,7 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/PolyIf/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Sched/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Seq/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/WatchDog/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Ports/")
# Components
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ActiveLogger/")
@ -35,10 +36,10 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FileDownlink/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FileManager/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FileUplink/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FprimeDeframer/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FprimeFramer/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FprimeProtocol/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FprimeRouter/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FrameAccumulator/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Framer/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FramingProtocol/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/GenericHub/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Health/")
@ -52,8 +53,6 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/StaticMemory/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TlmChan/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TlmPacketizer/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/SystemResources/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Ports/VersionPorts")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Ports/OsTimeEpoch")
# Text logger components included by default,
# but can be disabled if FW_ENABLE_TEXT_LOGGING=0 is desired.

View File

@ -6,6 +6,7 @@
#include <Fw/Types/Assert.hpp>
#include <Svc/ComQueue/ComQueue.hpp>
#include <Fw/Com/ComPacket.hpp>
#include "Fw/Types/BasicTypes.hpp"
namespace Svc {
@ -130,22 +131,22 @@ void ComQueue::configure(QueueConfigurationTable queueConfig,
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------
void ComQueue::comQueueIn_handler(const FwIndexType portNum, Fw::ComBuffer& data, U32 context) {
// Ensure that the port number of comQueueIn is consistent with the expectation
void ComQueue::comPacketQueueIn_handler(const FwIndexType portNum, Fw::ComBuffer& data, U32 context) {
// Ensure that the port number of comPacketQueueIn is consistent with the expectation
FW_ASSERT(portNum >= 0 && portNum < COM_PORT_COUNT, static_cast<FwAssertArgType>(portNum));
(void)this->enqueue(portNum, QueueType::COM_QUEUE, reinterpret_cast<const U8*>(&data), sizeof(Fw::ComBuffer));
}
void ComQueue::buffQueueIn_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) {
void ComQueue::bufferQueueIn_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) {
FW_ASSERT(std::numeric_limits<FwIndexType>::max() - COM_PORT_COUNT > portNum);
const FwIndexType queueNum = static_cast<FwIndexType>(portNum + COM_PORT_COUNT);
// Ensure that the port number of buffQueueIn is consistent with the expectation
// Ensure that the port number of bufferQueueIn is consistent with the expectation
FW_ASSERT(portNum >= 0 && portNum < BUFFER_PORT_COUNT, static_cast<FwAssertArgType>(portNum));
FW_ASSERT(queueNum < TOTAL_PORT_COUNT);
bool status =
bool success =
this->enqueue(queueNum, QueueType::BUFFER_QUEUE, reinterpret_cast<const U8*>(&fwBuffer), sizeof(Fw::Buffer));
if (!status) {
this->deallocate_out(portNum, fwBuffer);
if (!success) {
this->bufferReturnOut_out(portNum, fwBuffer);
}
}
@ -188,13 +189,32 @@ void ComQueue::run_handler(const FwIndexType portNum, U32 context) {
this->tlmWrite_buffQueueDepth(buffQueueDepth);
}
void ComQueue ::bufferReturnIn_handler(FwIndexType portNum,
Fw::Buffer& data,
const ComCfg::FrameContext& context) {
static_assert(std::numeric_limits<FwIndexType>::is_signed, "FwIndexType must be signed");
// For the buffer queues, the index of the queue is portNum offset by COM_PORT_COUNT since
// the first COM_PORT_COUNT queues are for ComBuffer. So we have for buffer queues:
// queueNum = portNum + COM_PORT_COUNT
// Since queueNum is used as APID, we can retrieve the original portNum like such:
FwIndexType bufferReturnPortNum = static_cast<FwIndexType>(context.getcomQueueIndex() - ComQueue::COM_PORT_COUNT);
// Failing this assert means that context.apid was modified since ComQueue set it, which should not happen
FW_ASSERT(bufferReturnPortNum < BUFFER_PORT_COUNT, static_cast<FwAssertArgType>(bufferReturnPortNum));
if (bufferReturnPortNum >= 0) {
// It is a coding error not to connect the associated bufferReturnOut port for each bufferReturnIn port
FW_ASSERT(this->isConnected_bufferReturnOut_OutputPort(bufferReturnPortNum), static_cast<FwAssertArgType>(bufferReturnPortNum));
// If this is a buffer port, return the buffer to the BufferDownlink
this->bufferReturnOut_out(bufferReturnPortNum, data);
}
}
// ----------------------------------------------------------------------
// Hook implementations for typed async input ports
// ----------------------------------------------------------------------
void ComQueue::buffQueueIn_overflowHook(FwIndexType portNum, Fw::Buffer& fwBuffer) {
void ComQueue::bufferQueueIn_overflowHook(FwIndexType portNum, Fw::Buffer& fwBuffer) {
FW_ASSERT(portNum >= 0 && portNum < BUFFER_PORT_COUNT, static_cast<FwAssertArgType>(portNum));
this->deallocate_out(portNum, fwBuffer);
this->bufferReturnOut_out(portNum, fwBuffer);
}
// ----------------------------------------------------------------------
@ -231,16 +251,29 @@ bool ComQueue::enqueue(const FwIndexType queueNum, QueueType queueType, const U8
return rvStatus;
}
void ComQueue::sendComBuffer(Fw::ComBuffer& comBuffer) {
void ComQueue::sendComBuffer(Fw::ComBuffer& comBuffer, FwIndexType queueIndex) {
FW_ASSERT(this->m_state == READY);
this->comQueueSend_out(0, comBuffer, 0);
Fw::Buffer outBuffer(comBuffer.getBuffAddr(), static_cast<Fw::Buffer::SizeType>(comBuffer.getBuffLength()));
// Context APID is set to the queue index for now. A future implementation may want this to be configurable
ComCfg::FrameContext context;
context.setcomQueueIndex(queueIndex);
this->queueSend_out(0, outBuffer, context);
// Set state to WAITING for the status to come back
this->m_state = WAITING;
}
void ComQueue::sendBuffer(Fw::Buffer& buffer) {
void ComQueue::sendBuffer(Fw::Buffer& buffer, FwIndexType queueIndex) {
// Retry buffer expected to be cleared as we are either transferring ownership or have already deallocated it.
FW_ASSERT(this->m_state == READY);
this->buffQueueSend_out(0, buffer);
// Context APID is set to the queue index for now. A future implementation may want this to be configurable
ComCfg::FrameContext context;
context.setcomQueueIndex(queueIndex);
this->queueSend_out(0, buffer, context);
// Set state to WAITING for the status to come back
this->m_state = WAITING;
}
@ -265,11 +298,11 @@ void ComQueue::processQueue() {
if (entry.index < COM_PORT_COUNT) {
Fw::ComBuffer comBuffer;
queue.dequeue(reinterpret_cast<U8*>(&comBuffer), sizeof(comBuffer));
this->sendComBuffer(comBuffer);
this->sendComBuffer(comBuffer, entry.index);
} else {
Fw::Buffer buffer;
queue.dequeue(reinterpret_cast<U8*>(&buffer), sizeof(buffer));
this->sendBuffer(buffer);
this->sendBuffer(buffer, entry.index);
}
// Update the throttle and the index that was just sent

View File

@ -16,23 +16,24 @@ module Svc {
# General ports
# ----------------------------------------------------------------------
@ Fw::ComBuffer output port
output port comQueueSend: Fw.Com
@ Fw::Buffer output port
output port buffQueueSend: Fw.BufferSend
@ Port for deallocating Fw::Buffer on queue overflow
output port deallocate: Fw.BufferSend
@ Port for emitting data ready to be sent
output port queueSend: Svc.ComDataWithContext
@ Port for receiving the status signal
async input port comStatusIn: Fw.SuccessCondition
@ Port array for receiving Fw::ComBuffers
async input port comQueueIn: [ComQueueComPorts] Fw.Com drop
async input port comPacketQueueIn: [ComQueueComPorts] Fw.Com drop
@ Port array for receiving Fw::Buffers
async input port buffQueueIn: [ComQueueBufferPorts] Fw.BufferSend hook
async input port bufferQueueIn: [ComQueueBufferPorts] Fw.BufferSend hook
@ Port array for returning ownership of Fw::Buffer to its original sender
output port bufferReturnOut: [ComQueueBufferPorts] Fw.BufferSend
# It is appropriate for this port to be sync since it is just a passthrough
@ Port for receiving Fw::Buffer whose ownership needs to be handed back
sync input port bufferReturnIn: Svc.ComDataWithContext
@ Port for scheduling telemetry output
async input port run: Svc.Sched drop

View File

@ -24,10 +24,10 @@ namespace Svc {
class ComQueue final : public ComQueueComponentBase {
public:
//!< Count of Fw::Com input ports and thus Fw::Com queues
static const FwIndexType COM_PORT_COUNT = ComQueueComponentBase::NUM_COMQUEUEIN_INPUT_PORTS;
static const FwIndexType COM_PORT_COUNT = ComQueueComponentBase::NUM_COMPACKETQUEUEIN_INPUT_PORTS;
//!< Count of Fw::Buffer input ports and thus Fw::Buffer queues
static const FwIndexType BUFFER_PORT_COUNT = ComQueueComponentBase::NUM_BUFFQUEUEIN_INPUT_PORTS;
static const FwIndexType BUFFER_PORT_COUNT = ComQueueComponentBase::NUM_BUFFERQUEUEIN_INPUT_PORTS;
static_assert((COM_PORT_COUNT + BUFFER_PORT_COUNT) <= std::numeric_limits<FwIndexType>::max(),
"FwIndexType not large enough to hold com and buffer ports");
@ -126,27 +126,35 @@ class ComQueue final : public ComQueueComponentBase {
//! Receive and queue a Fw::Buffer
//!
void buffQueueIn_handler(const FwIndexType portNum, /*!< The port number*/
Fw::Buffer& fwBuffer /*!< Buffer containing packet data*/);
void bufferQueueIn_handler(const FwIndexType portNum, /*!< The port number*/
Fw::Buffer& fwBuffer /*!< Buffer containing packet data*/) override;
//! Receive and queue a Fw::ComBuffer
//!
void comQueueIn_handler(const FwIndexType portNum, /*!< The port number*/
void comPacketQueueIn_handler(const FwIndexType portNum, /*!< The port number*/
Fw::ComBuffer& data, /*!< Buffer containing packet data*/
U32 context /*!< Call context value; meaning chosen by user*/
);
) override;
//! Handle the status of the last sent message
//!
void comStatusIn_handler(const FwIndexType portNum, /*!< The port number*/
Fw::Success& condition /*!<Status of communication state*/
);
) override;
//! Schedules the transmission of telemetry
//!
void run_handler(const FwIndexType portNum, /*!< The port number*/
U32 context /*!<The call order*/
);
) override;
//! Handler implementation for bufferReturnIn
//!
//! Port for returning ownership of Fw::Buffer to its sender
void bufferReturnIn_handler(FwIndexType portNum, //!< The port number
Fw::Buffer& data,
const ComCfg::FrameContext& context) override;
// ----------------------------------------------------------------------
// Hook implementations for typed async input ports
@ -154,9 +162,9 @@ class ComQueue final : public ComQueueComponentBase {
//! Queue overflow hook method that deallocates the fwBuffer
//!
void buffQueueIn_overflowHook(FwIndexType portNum, //!< The port number
void bufferQueueIn_overflowHook(FwIndexType portNum, //!< The port number
Fw::Buffer& fwBuffer //!< The buffer
);
) override;
// ----------------------------------------------------------------------
// Helper Functions
@ -172,17 +180,22 @@ class ComQueue final : public ComQueueComponentBase {
//! Send a chosen Fw::ComBuffer
//!
void sendComBuffer(Fw::ComBuffer& comBuffer //!< Reference to buffer to send
void sendComBuffer(Fw::ComBuffer& comBuffer, //!< Reference to buffer to send
FwIndexType queueIndex //!< Index of the queue emitting the message
);
//! Send a chosen Fw::Buffer
//!
void sendBuffer(Fw::Buffer& buffer //!< Reference to buffer to send
void sendBuffer(Fw::Buffer& buffer, //!< Reference to buffer to send
FwIndexType queueIndex //!< Index of the queue emitting the message
);
//! Process the queues to select the next priority message
//!
void processQueue();
PRIVATE:
// ----------------------------------------------------------------------
// Member variables
// ----------------------------------------------------------------------
@ -195,6 +208,7 @@ class ComQueue final : public ComQueueComponentBase {
FwEnumStoreType m_allocationId; //!< Component's allocation ID
Fw::MemAllocator* m_allocator; //!< Pointer to Fw::MemAllocator instance for deallocation
void* m_allocation; //!< Pointer to allocated memory
};
} // end namespace Svc

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -2,18 +2,12 @@
## 1. Introduction
`Svc::ComQueue` is an F´ active component that functions as a priority queue of buffer types. Messages are dequeued and
forwarded when a `Fw::Success::SUCCESS` signal is received in order of priority. `Fw::Success::FAILURE` signals result
in the queues being paused until a following `Fw::Success::SUCCESS` signal.
`Svc::ComQueue` is an F´ active component that functions as a priority queue of buffer types. Messages are dequeued and forwarded in order of priority when a `Fw::Success::SUCCESS` signal is received. Receiving a `Fw::Success::FAILURE` results in the queues being paused until a following `Fw::Success::SUCCESS` is received.
`Svc::ComQueue` is configured with a queue depth and queue priority for each incoming `Fw::Com` and `Fw::Buffer` port by
passing in a configuration table at initialization. Queued messages from the highest priority source port are serviced
first and a round-robin algorithm is used to balance between ports of shared priority.
`Svc::ComQueue` is configured with a queue depth and queue priority for each incoming `Fw::Com` and `Fw::Buffer` port by passing in a configuration table at initialization.
Queued messages from the highest priority source port are serviced first and a round-robin algorithm is used to balance between ports of shared priority.
`Svc::ComQueue` is designed to act alongside instances of the
[communication adapter interface](../../../docs/reference/communication-adapter-interface.md) and
implements the communication queue
[protocol](../../../docs/reference/communication-adapter-interface.md#communication-queue-protocol).
`Svc::ComQueue` is designed to act alongside instances of the [communication adapter interface](../../../docs/reference/communication-adapter-interface.md) and implements the communication queue [protocol](../../../docs/reference/communication-adapter-interface.md#communication-queue-protocol).
## 2. Assumptions
@ -30,7 +24,7 @@ implements the communication queue
| Requirement | Description | Rationale | Verification Method |
|------------------|-----------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------|---------------------|
| SVC-COMQUEUE-001 | `Svc::ComQueue` shall queue `Fw::Buffer` and `Fw::ComBuffer` received on incoming ports. | The purpose of the queue is to store messages. | Unit Test |
| SVC-COMQUEUE-002 | `Svc::ComQueue` shall output exactly one `Fw::Buffer` or `Fw::ComBuffer` message on a received `Fw::Success::SUCCESS` signal. | `Svc::ComQueue` obeys the communication adapter interface protocol. | Unit Test |
| SVC-COMQUEUE-002 | `Svc::ComQueue` shall output exactly one `Fw::Buffer` (wrapping the queued data units) on a received `Fw::Success::SUCCESS` signal. | `Svc::ComQueue` obeys the communication adapter interface protocol. | Unit Test |
| SVC-COMQUEUE-003 | `Svc::ComQueue` shall pause sending on the `Fw::Success::FAILURE` and restart on the next `Fw::Success::SUCCESS` signal. | `Svc::ComQueue` should not sent to a failing communication adapter. | Unit Test |
| SVC-COMQUEUE-004 | `Svc::ComQueue` shall have a configurable number of `Fw::Com` and `Fw::Buffer` input ports. | `Svc::ComQueue` should be adaptable for a number of projects. | Inspection |
| SVC-COMQUEUE-005 | `Svc::ComQueue` shall select and send the next priority `Fw::Buffer` and `Fw::ComBuffer` message in response to `Fw::Success::SUCCESS`. | `Svc::ComQueue` obeys the communication adapter interface protocol. | Unit test |
@ -38,7 +32,7 @@ implements the communication queue
| SVC-COMQUEUE-007 | `Svc::ComQueue` shall emit a queue overflow event for a given port when the configured depth is exceeded. Messages shall be discarded. | `Svc::ComQueue` needs to indicate off-nominal events. | Unit Test |
| SVC-COMQUEUE-008 | `Svc::ComQueue` shall implement a round robin approach to balance between ports of the same priority. | Allows projects to balance between a set of queues of similar priority. | Unit Test |
| SVC-COMQUEUE-009 | `Svc::ComQueue` shall keep track and throttle queue overflow events per port. | Prevents a flood of queue overflow events. | Unit test |
comPacketQueueIn
## 4. Design
The diagram below shows the `Svc::ComQueue` component.
@ -48,19 +42,16 @@ The diagram below shows the `Svc::ComQueue` component.
`Svc::ComQueue` has the following ports:
| Kind | Name | Port Type | Usage |
|---------------|-------------------|---------------------------------------|--------------------------------------------------------|
| `output` | `comQueueSend` | `Fw.Com` | Fw::ComBuffer output port |
| `output` | `buffQueueSend` | `Fw.BufferSend` | Fw::Buffer output port |
| `output` | `deallocate` | `Fw.BufferSend` | Port for deallocating Fw::Buffer on queue overflow |
|---------------|-------------------|---------------------------------------|----------------------------------------------------------|
| `output` | `queueSend` | `Svc.ComDataWithContext` | Port emitting queued messages |
| `async input` | `comStatusIn` | `Fw.SuccessCondition` | Port for receiving the status signal |
| `async input` | `comQueueIn` | `[ComQueueComPorts] Fw.Com` | Port array for receiving Fw::ComBuffers |
| `async input` | `buffQueueIn` | `[ComQueueBufferPorts] Fw.BufferSend` | Port array for receiving Fw::Buffers |
| `async input` | `run` | `Svc.Sched` | Port for scheduling telemetry output |
| `event` | `Log` | `Fw.Log` | Port for emitting events |
| `text event` | `LogText` | `Fw.LogText` | Port for emitting text events |
| `time get` | `Time` | `Fw.Time` | Port for getting the time |
| `telemetry` | `Tlm` | `Fw.Tlm` | Port for emitting telemetry |
| `async input` | `comPacketQueueIn`| `[ComQueueComPorts] Fw.Com` | Port array for receiving Fw::ComBuffers |
| `async input` | `bufferQueueIn` | `[ComQueueBufferPorts] Fw.BufferSend`| Port array for receiving Fw::Buffers |
| `sync input` | `bufferReturnIn` | `Svc.ComDataWithContext` | Port for deallocating Fw::Buffer on queue overflow |
| `output` | `bufferReturnOut` | `Svc.ComDataWithContext` | Port for deallocating Fw::Buffer on queue overflow |
> [!NOTE]
> ComQueue also has the port instances for autocoded functionality for events, telemetry and time.
### 4.2. State
`Svc::ComQueue` maintains the following state:
@ -87,8 +78,8 @@ Buffers are queued when in `WAITING` state.
### 4.3 Model Configuration
`Svc::ComQueue` has the following constants, that are configured in `AcConstants.fpp`:
1. `ComQueueComPorts`: number of ports of `Fw.Com` type in the `comQueueIn` port array.
2. `ComQueueBufferPorts`: number of ports of `Fw.BufferSend` type in the `buffQueueIn` port array.
1. `ComQueueComPorts`: number of ports of `Fw.Com` type in the `comPacketQueueIn` port array.
2. `ComQueueBufferPorts`: number of ports of `Fw.BufferSend` type in the `bufferQueueIn` port array.
### 4.4 Runtime Setup
To set up an instance of `ComQueue`, the following needs to be done:
@ -104,8 +95,8 @@ and an allocator of `Fw::MemAllocator`. The `configure` method foes the followin
### 4.5 Port Handlers
#### 4.5.1 buffQueueIn
The `buffQueueIn` port handler receives an `Fw::Buffer` data type and a port number.
#### 4.5.1 bufferQueueIn
The `bufferQueueIn` port handler receives an `Fw::Buffer` data type and a port number.
It does the following:
1. Ensures that the port number is between zero and the value of the buffer size
2. Enqueue the buffer onto the `m_queues` instance
@ -114,8 +105,8 @@ It does the following:
In the case where the component is already in `READY` state, this will process the queue immediately after the buffer
is added to the queue.
#### 4.5.2 comQueueIn
The `comQueueIn` port handler receives an `Fw::ComBuffer` data type and a port number.
#### 4.5.2 comPacketQueueIn
The `comPacketQueueIn` port handler receives an `Fw::ComBuffer` data type and a port number.
It does the following:
1. Ensures that the port number is between zero and the value of the com buffer size
2. Enqueue the com buffer onto the `m_queues` instance

View File

@ -34,6 +34,16 @@ TEST(Nominal, ReadyFirst) {
tester.testReadyFirst();
}
TEST(Nominal, ContextData) {
Svc::ComQueueTester tester;
tester.testContextData();
}
TEST(Nominal, testBufferQueueReturn) {
Svc::ComQueueTester tester;
tester.testBufferQueueReturn();
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();

View File

@ -49,11 +49,11 @@ void ComQueueTester ::sendByQueueNumber(Fw::Buffer& buffer,
Fw::ComBuffer comBuffer(buffer.getData(), buffer.getSize());
portNum = queueNum;
queueType = QueueType::COM_QUEUE;
invoke_to_comQueueIn(portNum, comBuffer, 0);
invoke_to_comPacketQueueIn(portNum, comBuffer, 0);
} else {
portNum = queueNum - ComQueue::COM_PORT_COUNT;
queueType = QueueType::BUFFER_QUEUE;
invoke_to_buffQueueIn(portNum, buffer);
invoke_to_bufferQueueIn(portNum, buffer);
}
}
@ -64,15 +64,14 @@ void ComQueueTester ::emitOne() {
}
void ComQueueTester ::emitOneAndCheck(FwIndexType expectedIndex,
QueueType expectedType,
Fw::ComBuffer& expectedCom,
Fw::Buffer& expectedBuff) {
U8* expectedData,
FwSizeType expectedSize) {
emitOne();
if (expectedType == QueueType::COM_QUEUE) {
ASSERT_from_comQueueSend(expectedIndex, expectedCom, 0);
} else {
ASSERT_from_buffQueueSend(expectedIndex, expectedBuff);
// Check that the data buffers are identical (size + data)
Fw::Buffer emittedBuffer = this->fromPortHistory_queueSend->at(expectedIndex).data;
ASSERT_EQ(expectedSize, emittedBuffer.getSize());
for (FwSizeType i = 0; i < expectedSize; i++) {
ASSERT_EQ(emittedBuffer.getData()[i], expectedData[i]);
}
}
@ -87,14 +86,14 @@ void ComQueueTester ::testQueueSend() {
configure();
for(FwIndexType portNum = 0; portNum < ComQueue::COM_PORT_COUNT; portNum++){
invoke_to_comQueueIn(portNum, comBuffer, 0);
emitOneAndCheck(portNum, QueueType::COM_QUEUE, comBuffer, buffer);
invoke_to_comPacketQueueIn(portNum, comBuffer, 0);
emitOneAndCheck(portNum, comBuffer.getBuffAddr(), comBuffer.getBuffLength());
}
clearFromPortHistory();
for(FwIndexType portNum = 0; portNum < ComQueue::BUFFER_PORT_COUNT; portNum++){
invoke_to_buffQueueIn(portNum, buffer);
emitOneAndCheck(portNum, QueueType::BUFFER_QUEUE, comBuffer, buffer);
invoke_to_bufferQueueIn(portNum, buffer);
emitOneAndCheck(portNum, buffer.getData(), buffer.getSize());
}
clearFromPortHistory();
component.cleanup();
@ -107,24 +106,24 @@ void ComQueueTester ::testQueuePause() {
configure();
for(FwIndexType portNum = 0; portNum < ComQueue::COM_PORT_COUNT; portNum++){
invoke_to_comQueueIn(portNum, comBuffer, 0);
invoke_to_comPacketQueueIn(portNum, comBuffer, 0);
// Send a bunch of failures
Fw::Success state = Fw::Success::FAILURE;
invoke_to_comStatusIn(0, state);
invoke_to_comStatusIn(0, state);
invoke_to_comStatusIn(0, state);
emitOneAndCheck(portNum, QueueType::COM_QUEUE, comBuffer, buffer);
emitOneAndCheck(portNum, comBuffer.getBuffAddr(), comBuffer.getBuffLength());
}
clearFromPortHistory();
for(FwIndexType portNum = 0; portNum < ComQueue::BUFFER_PORT_COUNT; portNum++){
invoke_to_buffQueueIn(portNum, buffer);
invoke_to_bufferQueueIn(portNum, buffer);
// Send a bunch of failures
Fw::Success state = Fw::Success::FAILURE;
invoke_to_comStatusIn(0, state);
invoke_to_comStatusIn(0, state);
invoke_to_comStatusIn(0, state);
emitOneAndCheck(portNum, QueueType::BUFFER_QUEUE, comBuffer, buffer);
emitOneAndCheck(portNum, buffer.getData(), buffer.getSize());
}
clearFromPortHistory();
component.cleanup();
@ -150,37 +149,26 @@ void ComQueueTester ::testPrioritySend() {
for(FwIndexType portNum = 0; portNum < ComQueue::COM_PORT_COUNT; portNum++){
Fw::ComBuffer comBuffer(&data[portNum][0], BUFFER_LENGTH);
invoke_to_comQueueIn(portNum, comBuffer, 0);
invoke_to_comPacketQueueIn(portNum, comBuffer, 0);
}
for (FwIndexType portNum = 0; portNum < ComQueue::BUFFER_PORT_COUNT; portNum++) {
Fw::Buffer buffer(&data[portNum + ComQueue::COM_PORT_COUNT][0], BUFFER_LENGTH);
invoke_to_buffQueueIn(portNum, buffer);
invoke_to_bufferQueueIn(portNum, buffer);
}
// Check that nothing has yet been sent
ASSERT_from_buffQueueSend_SIZE(0);
ASSERT_from_comQueueSend_SIZE(0);
ASSERT_from_queueSend_SIZE(0);
for (FwIndexType index = 0; index < ComQueue::TOTAL_PORT_COUNT; index++) {
U8 orderKey;
U32 previousComSize = fromPortHistory_comQueueSend->size();
U32 previousBufSize = fromPortHistory_buffQueueSend->size();
U32 previousSize = fromPortHistory_queueSend->size();
emitOne();
ASSERT_EQ(fromPortHistory_comQueueSend->size() + fromPortHistory_buffQueueSend->size(), (index + 1));
ASSERT_EQ(fromPortHistory_queueSend->size(), (index + 1));
// Check that the size changed by exactly one
ASSERT_EQ(fromPortHistory_queueSend->size(), (previousSize + 1));
// Check that the sizes changed by exactly one
ASSERT_TRUE((previousComSize == fromPortHistory_comQueueSend->size()) ^
(previousBufSize == fromPortHistory_buffQueueSend->size()));
// Look for which type had arrived
if (fromPortHistory_comQueueSend->size() > previousComSize) {
orderKey = fromPortHistory_comQueueSend->at(fromPortHistory_comQueueSend->size() - 1).data.getBuffAddr()[0];
} else {
orderKey =
fromPortHistory_buffQueueSend->at(fromPortHistory_buffQueueSend->size() - 1).fwBuffer.getData()[0];
}
orderKey = fromPortHistory_queueSend->at(index).data.getData()[0];
ASSERT_EQ(orderKey, index);
}
clearFromPortHistory();
@ -188,6 +176,8 @@ void ComQueueTester ::testPrioritySend() {
}
void ComQueueTester::testExternalQueueOverflow() {
// "External" queue is ComQueue's managed queue for input Com/Buffers
// as opposed to the "internal" message queue for async input ports
ComQueue::QueueConfigurationTable configurationTable;
ComQueueDepth expectedComDepth;
BuffQueueDepth expectedBuffDepth;
@ -212,21 +202,22 @@ void ComQueueTester::testExternalQueueOverflow() {
for (FwIndexType queueNum = 0; queueNum < ComQueue::TOTAL_PORT_COUNT; queueNum++) {
QueueType overflow_type;
FwIndexType portNum;
// queue[portNum].depth + 2 to deliberately cause overflow and check throttle of exactly 1
// queue[portNum].depth + 2 to deliberately cause overflow of 2, in order to also test the throttle
for (FwSizeType msgCount = 0; msgCount < configurationTable.entries[queueNum].depth + 2; msgCount++) {
sendByQueueNumber(buffer, queueNum, portNum, overflow_type);
dispatchAll();
}
if (QueueType::BUFFER_QUEUE == overflow_type) {
ASSERT_from_deallocate_SIZE(2);
ASSERT_from_deallocate(0, buffer);
ASSERT_from_deallocate(1, buffer);
}
// Throttle should make it that we emitted only 1 event, even though we overflowed twice
ASSERT_EVENTS_QueueOverflow_SIZE(1);
ASSERT_EVENTS_QueueOverflow(0, overflow_type, portNum);
if (QueueType::BUFFER_QUEUE == overflow_type) {
// Two messages overflowed, so two buffers should be returned
ASSERT_from_bufferReturnOut_SIZE(2);
ASSERT_from_bufferReturnOut(0, buffer);
ASSERT_from_bufferReturnOut(1, buffer);
}
// Drain a message, and see if throttle resets
emitOne();
@ -236,10 +227,12 @@ void ComQueueTester::testExternalQueueOverflow() {
dispatchAll();
if (QueueType::BUFFER_QUEUE == overflow_type) {
ASSERT_from_deallocate_SIZE(3);
ASSERT_from_deallocate(2, buffer);
// Third message overflowed, so third bufferReturnOut
ASSERT_from_bufferReturnOut_SIZE(3);
ASSERT_from_bufferReturnOut(2, buffer);
}
// emitOne() reset the throttle, then overflow again. So expect a second overflow event
ASSERT_EVENTS_QueueOverflow_SIZE(2);
ASSERT_EVENTS_QueueOverflow(1, overflow_type, portNum);
@ -260,6 +253,7 @@ void ComQueueTester::testExternalQueueOverflow() {
}
void ComQueueTester::testInternalQueueOverflow() {
// Internal queue is the message queue for async input ports
U8 data[BUFFER_LENGTH] = {0xde, 0xad, 0xbe};
Fw::Buffer buffer(data, sizeof(data));
@ -277,15 +271,15 @@ void ComQueueTester::testInternalQueueOverflow() {
// send one more to overflow the queue
sendByQueueNumber(buffer, queueNum, portNum, overflow_type);
ASSERT_from_deallocate_SIZE(1);
ASSERT_from_deallocate(0, buffer);
ASSERT_from_bufferReturnOut_SIZE(1);
ASSERT_from_bufferReturnOut(0, buffer);
// send another
sendByQueueNumber(buffer, queueNum, portNum, overflow_type);
ASSERT_from_deallocate_SIZE(2);
ASSERT_from_deallocate(0, buffer);
ASSERT_from_deallocate(1, buffer);
ASSERT_from_bufferReturnOut_SIZE(2);
ASSERT_from_bufferReturnOut(0, buffer);
ASSERT_from_bufferReturnOut(1, buffer);
component.cleanup();
}
@ -298,36 +292,79 @@ void ComQueueTester ::testReadyFirst() {
for(FwIndexType portNum = 0; portNum < ComQueue::COM_PORT_COUNT; portNum++){
emitOne();
invoke_to_comQueueIn(portNum, comBuffer, 0);
invoke_to_comPacketQueueIn(portNum, comBuffer, 0);
dispatchAll();
ASSERT_from_comQueueSend(portNum, comBuffer, 0);
Fw::Buffer emittedBuffer = this->fromPortHistory_queueSend->at(portNum).data;
ASSERT_EQ(emittedBuffer.getSize(), comBuffer.getBuffLength());
for (FwSizeType i = 0; i < emittedBuffer.getSize(); i++) {
ASSERT_EQ(emittedBuffer.getData()[i], comBuffer.getBuffAddr()[i]);
}
}
clearFromPortHistory();
for(FwIndexType portNum = 0; portNum < ComQueue::BUFFER_PORT_COUNT; portNum++){
emitOne();
invoke_to_buffQueueIn(portNum, buffer);
invoke_to_bufferQueueIn(portNum, buffer);
dispatchAll();
ASSERT_from_buffQueueSend(portNum, buffer);
Fw::Buffer emittedBuffer = this->fromPortHistory_queueSend->at(portNum).data;
ASSERT_EQ(emittedBuffer.getSize(), buffer.getSize());
for (FwSizeType i = 0; i < buffer.getSize(); i++) {
ASSERT_EQ(buffer.getData()[i], emittedBuffer.getData()[i]);
}
}
clearFromPortHistory();
component.cleanup();
}
// ----------------------------------------------------------------------
// Handlers for typed from ports
// ----------------------------------------------------------------------
void ComQueueTester ::testContextData() {
U8 data[BUFFER_LENGTH] = {0xde, 0xad, 0xbe};
Fw::ComBuffer comBuffer(&data[0], sizeof(data));
Fw::Buffer buffer(&data[0], sizeof(data));
configure();
void ComQueueTester ::from_buffQueueSend_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) {
this->pushFromPortEntry_buffQueueSend(fwBuffer);
for(FwIndexType portNum = 0; portNum < ComQueue::COM_PORT_COUNT; portNum++){
invoke_to_comPacketQueueIn(portNum, comBuffer, 0);
emitOne();
// Currently, the APID is set to the queue index, which is the same as the port number for COM ports
FwIndexType expectedApid = portNum;
auto emittedContext = this->fromPortHistory_queueSend->at(portNum).context;
ASSERT_EQ(expectedApid, emittedContext.getcomQueueIndex());
}
clearFromPortHistory();
for(FwIndexType portNum = 0; portNum < ComQueue::BUFFER_PORT_COUNT; portNum++){
invoke_to_bufferQueueIn(portNum, buffer);
emitOne();
// APID is queue index, which is COM_PORT_COUNT + portNum for BUFFER ports
FwIndexType expectedApid = portNum + ComQueue::COM_PORT_COUNT;
auto emittedContext = this->fromPortHistory_queueSend->at(portNum).context;
ASSERT_EQ(expectedApid, emittedContext.getcomQueueIndex());
}
clearFromPortHistory();
component.cleanup();
}
void ComQueueTester ::from_comQueueSend_handler(const FwIndexType portNum, Fw::ComBuffer& data, U32 context) {
this->pushFromPortEntry_comQueueSend(data, context);
}
void ComQueueTester ::testBufferQueueReturn() {
U8 data[BUFFER_LENGTH] = {0xde, 0xad, 0xbe};
Fw::Buffer buffer(&data[0], sizeof(data));
ComCfg::FrameContext context;
configure();
// ----------------------------------------------------------------------
// Helper methods
// ----------------------------------------------------------------------
for(FwIndexType portNum = 0; portNum < ComQueue::TOTAL_PORT_COUNT; portNum++){
clearFromPortHistory();
context.setcomQueueIndex(portNum);
invoke_to_bufferReturnIn(0, buffer, context);
// APIDs that correspond to an buffer originating from a Fw.Com port
// do no get deallocated APIDs that correspond to a Fw.Buffer do
if (portNum < ComQueue::COM_PORT_COUNT) {
ASSERT_from_bufferReturnOut_SIZE(0);
} else {
ASSERT_from_bufferReturnOut_SIZE(1);
ASSERT_from_bufferReturnOut(0, buffer);
}
}
component.cleanup();
}
} // end namespace Svc

View File

@ -59,9 +59,8 @@ class ComQueueTester : public ComQueueGTestBase {
void emitOne();
void emitOneAndCheck(FwIndexType expectedIndex,
QueueType expectedType,
Fw::ComBuffer& expectedCom,
Fw::Buffer& expectedBuff);
U8* expectedData,
FwSizeType expectedDataSize);
// ----------------------------------------------------------------------
// Tests
@ -79,22 +78,9 @@ class ComQueueTester : public ComQueueGTestBase {
void testReadyFirst();
private:
// ----------------------------------------------------------------------
// Handlers for typed from ports
// ----------------------------------------------------------------------
void testContextData();
//! Handler for from_buffQueueSend
//!
void from_buffQueueSend_handler(const FwIndexType portNum, /*!< The port number*/
Fw::Buffer& fwBuffer);
//! Handler for from_comQueueSend
//!
void from_comQueueSend_handler(const FwIndexType portNum, /*!< The port number*/
Fw::ComBuffer& data, /*!< Buffer containing packet data*/
U32 context /*!< Call context value; meaning chosen by user*/
);
void testBufferQueueReturn();
private:
// ----------------------------------------------------------------------
@ -117,6 +103,7 @@ class ComQueueTester : public ComQueueGTestBase {
//! The component under test
//!
ComQueue component;
};
} // end namespace Svc

View File

@ -10,6 +10,9 @@ set(SOURCE_FILES
"${CMAKE_CURRENT_LIST_DIR}/ComStub.fpp"
"${CMAKE_CURRENT_LIST_DIR}/ComStub.cpp"
)
set(MOD_DEPS
Fw/Logger
)
register_fprime_module()
set(UT_SOURCE_FILES
@ -20,4 +23,5 @@ set(UT_SOURCE_FILES
set(UT_MOD_DEPS
STest
)
set(UT_AUTO_HELPERS ON)
register_fprime_ut()

View File

@ -5,6 +5,7 @@
// ======================================================================
#include <Svc/ComStub/ComStub.hpp>
#include <Fw/Logger/Logger.hpp>
#include "Fw/Types/Assert.hpp"
#include "Fw/Types/BasicTypes.hpp"
@ -14,7 +15,7 @@ namespace Svc {
// Construction, initialization, and destruction
// ----------------------------------------------------------------------
ComStub::ComStub(const char* const compName) : ComStubComponentBase(compName), m_reinitialize(true) {}
ComStub::ComStub(const char* const compName) : ComStubComponentBase(compName), m_reinitialize(true), m_retry_count(0) {}
ComStub::~ComStub() {}
@ -22,33 +23,51 @@ ComStub::~ComStub() {}
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------
Drv::SendStatus ComStub::comDataIn_handler(const FwIndexType portNum, Fw::Buffer& sendBuffer) {
FW_ASSERT(!this->m_reinitialize || !this->isConnected_comStatus_OutputPort(0)); // A message should never get here if we need to reinitialize is needed
Drv::SendStatus driverStatus = Drv::SendStatus::SEND_RETRY;
for (FwIndexType i = 0; driverStatus == Drv::SendStatus::SEND_RETRY && i < RETRY_LIMIT; i++) {
driverStatus = this->drvDataOut_out(0, sendBuffer);
}
FW_ASSERT(driverStatus != Drv::SendStatus::SEND_RETRY); // If it is still in retry state, there is no good answer
Fw::Success comSuccess = (driverStatus.e == Drv::SendStatus::SEND_OK) ? Fw::Success::SUCCESS : Fw::Success::FAILURE;
this->m_reinitialize = driverStatus.e != Drv::SendStatus::SEND_OK;
if (this->isConnected_comStatus_OutputPort(0)) {
this->comStatus_out(0, comSuccess);
}
return Drv::SendStatus::SEND_OK; // Always send ok to deframer as it does not handle this anyway
void ComStub::comDataIn_handler(const FwIndexType portNum, Fw::Buffer& sendBuffer, const ComCfg::FrameContext& context) {
FW_ASSERT(!this->m_reinitialize || !this->isConnected_comStatusOut_OutputPort(0)); // A message should never get here if we need to reinitialize is needed
this->m_storedContext = context; // Store the context of the current message
this->drvDataOut_out(0, sendBuffer);
}
void ComStub::drvConnected_handler(const FwIndexType portNum) {
Fw::Success radioSuccess = Fw::Success::SUCCESS;
if (this->isConnected_comStatus_OutputPort(0) && m_reinitialize) {
if (this->isConnected_comStatusOut_OutputPort(0) && m_reinitialize) {
this->m_reinitialize = false;
this->comStatus_out(0, radioSuccess);
this->comStatusOut_out(0, radioSuccess);
}
}
void ComStub::drvDataIn_handler(const FwIndexType portNum,
Fw::Buffer& recvBuffer,
const Drv::RecvStatus& recvStatus) {
this->comDataOut_out(0, recvBuffer, recvStatus);
const Drv::ByteStreamStatus& recvStatus) {
if (recvStatus.e == Drv::ByteStreamStatus::OP_OK) {
this->comDataOut_out(0, recvBuffer);
}
}
void ComStub ::dataReturnIn_handler(FwIndexType portNum, //!< The port number
Fw::Buffer& fwBuffer, //!< The buffer
const Drv::ByteStreamStatus& sendStatus) {
if (sendStatus != Drv::ByteStreamStatus::SEND_RETRY) {
// Not retrying - return buffer ownership and send status
this->dataReturnOut_out(0, fwBuffer, this->m_storedContext);
this->m_reinitialize = sendStatus.e != Drv::ByteStreamStatus::OP_OK;
this->m_retry_count = 0; // Reset the retry count
Fw::Success comSuccess = (sendStatus.e == Drv::ByteStreamStatus::OP_OK) ? Fw::Success::SUCCESS : Fw::Success::FAILURE;
this->comStatusOut_out(0, comSuccess);
} else {
// Driver indicates we should retry (SEND_RETRY)
if (this->m_retry_count < this->RETRY_LIMIT) {
// If we have not yet retried more than the retry limit, attempt to retry
this->m_retry_count++;
this->drvDataOut_out(0, fwBuffer);
} else {
// If retried too many times, return buffer and log failure
this->dataReturnOut_out(0, fwBuffer, this->m_storedContext);
Fw::Logger::log("ComStub RETRY_LIMIT exceeded, skipped sending data");
this->m_retry_count = 0; // Reset the retry count
}
}
}
} // end namespace Svc

View File

@ -10,10 +10,14 @@ module Svc {
@ Ready signal when driver is connected
sync input port drvConnected: Drv.ByteStreamReady
@ Data received from driver
sync input port drvDataIn: Drv.ByteStreamRecv
@ Receive (read) data from driver. This gets forwarded to comDataOut
sync input port drvDataIn: Drv.ByteStreamData
@ Send (write) data to the driver. This gets invoked on comDataIn invocation
output port drvDataOut: Fw.BufferSend
@ Callback from drvDataOut (retrieving status and ownership of sent buffer)
sync input port dataReturnIn: Drv.ByteStreamData
@ Data going to the underlying driver
output port drvDataOut: Drv.ByteStreamSend
}
}

View File

@ -7,11 +7,14 @@
#ifndef Svc_ComStub_HPP
#define Svc_ComStub_HPP
#include "Drv/ByteStreamDriverModel/ByteStreamStatusEnumAc.hpp"
#include "Svc/ComStub/ComStubComponentAc.hpp"
namespace Svc {
class ComStub final : public ComStubComponentBase {
friend class ComStubTester; //!< Allow UT Tester to access private members
public:
const FwIndexType RETRY_LIMIT = 10;
// ----------------------------------------------------------------------
@ -34,8 +37,11 @@ class ComStub final : public ComStubComponentBase {
//! Handler implementation for comDataIn
//!
Drv::SendStatus comDataIn_handler(const FwIndexType portNum, /*!< The port number*/
Fw::Buffer& sendBuffer) override;
//! Comms data is coming in meaning there is a request for ComStub to send data on the wire
//! For ComStub, this means we send the data to the underlying driver (e.g. TCP/UDP/UART)
void comDataIn_handler(const FwIndexType portNum, /*!< The port number*/
Fw::Buffer& sendBuffer,
const ComCfg::FrameContext& context) override;
//! Handler implementation for drvConnected
//!
@ -43,11 +49,22 @@ class ComStub final : public ComStubComponentBase {
//! Handler implementation for drvDataIn
//!
//! Data is coming in from the driver (meaning it has been read from the wire).
//! ComStub forwards this to the comDataOut port
void drvDataIn_handler(const FwIndexType portNum,
/*!< The port number*/ Fw::Buffer& recvBuffer,
const Drv::RecvStatus& recvStatus) override;
const Drv::ByteStreamStatus& recvStatus) override;
//! Handler implementation for dataReturnIn
//!
//! Buffer ownership and status returning from a Driver "send" operation
void dataReturnIn_handler(FwIndexType portNum, //!< The port number
Fw::Buffer& fwBuffer, //!< The buffer
const Drv::ByteStreamStatus& recvStatus) override;
bool m_reinitialize; //!< Stores if a ready signal is needed on connection
ComCfg::FrameContext m_storedContext; //!< Stores the context of the current message
FwIndexType m_retry_count; //!< Counts the number of retries of the current message
};
} // end namespace Svc

View File

@ -55,8 +55,8 @@ be useful
| Kind | Name | Port Type | Usage |
|--------------|----------------|-----------------------|-----------------------------------------------------------------------------------|
| `sync input` | `comDataIn` | `Drv.ByteStreamSend` | Port receiving `Fw::Buffer`s for transmission out `drvDataOut` |
| `output` | `comStatus` | `Svc.ComStatus` | Port indicating success or failure to attached `Svc::ComQueue` |
| `sync input` | `comDataIn` | `Svc.ComDataWithContext` | Port receiving `Fw::Buffer`s for transmission out `drvDataOut` |
| `output` | `comStatusOut` | `Svc.ComStatus` | Port indicating success or failure to attached `Svc::ComQueue` |
| `output` | `comDataOut` | `Drv.ByteStreamRecv` | Port providing received `Fw::Buffers` to a potential `Svc::Deframer` |
**Byte Stream Driver Model Ports**
@ -81,13 +81,13 @@ response to a driver reconnection event. This is to implement the Communication
The `comDataIn` port handler receives an `Fw::Buffer` from the F´ system for transmission to the ground. Typically, it
is connected to the output of the `Svc::Framer` component. In this `Svc::ComStub` implementation, it passes this
`Fw::Buffer` directly to the `drvDataOut` port. It will retry when that port responds with a `RETRY` request. Otherwise,
the `comStatus` port will be invoked to indicate success or failure. Retries attempts are limited before the port
the `comStatusOut` port will be invoked to indicate success or failure. Retries attempts are limited before the port
asserts.
#### 4.3.1 drvConnected
This port receives the connected signal from the driver and responds with exactly one `READY` invocation to the
`comStatus` port. This starts downlink. This occurs each time the driver reconnects.
`comStatusOut` port. This starts downlink. This occurs each time the driver reconnects.
#### 4.3.1 drvDataIn

View File

@ -25,6 +25,11 @@ TEST(OffNominal, Retry) {
tester.test_retry();
}
TEST(OffNominal, RetryReset) {
Svc::ComStubTester tester;
tester.test_retry_reset();
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();

View File

@ -7,12 +7,6 @@
#include "ComStubTester.hpp"
#include <STest/Pick/Pick.hpp>
#define INSTANCE 0
#define MAX_HISTORY_SIZE 100
#define RETRIES 3
U8 storage[RETRIES][10240];
namespace Svc {
// ----------------------------------------------------------------------
@ -21,8 +15,8 @@ namespace Svc {
ComStubTester ::ComStubTester()
: ComStubGTestBase("Tester", MAX_HISTORY_SIZE),
m_component("ComStub"),
m_send_mode(Drv::SendStatus::SEND_OK),
component("ComStub"),
m_send_mode(Drv::ByteStreamStatus::OP_OK),
m_retries(0) {
this->initComponents();
this->connectPorts();
@ -47,74 +41,112 @@ void ComStubTester ::fill(Fw::Buffer& buffer_to_fill) {
void ComStubTester ::test_initial() {
Fw::Success condition = Fw::Success::SUCCESS;
invoke_to_drvConnected(0);
ASSERT_from_comStatus_SIZE(1);
ASSERT_from_comStatus(0, condition);
this->fromPortHistory_comStatus->clear();
ASSERT_from_comStatusOut_SIZE(1);
ASSERT_from_comStatusOut(0, condition);
this->fromPortHistory_comStatusOut->clear();
}
void ComStubTester ::test_basic() {
this->test_initial();
Fw::Buffer buffer(storage[0], sizeof(storage[0]));
U8 storage[8];
Fw::Buffer buffer(storage, sizeof(storage));
Fw::Success condition = Fw::Success::SUCCESS;
ComCfg::FrameContext context;
this->fill(buffer);
// Downlink
ASSERT_EQ(invoke_to_comDataIn(0, buffer), Drv::SendStatus::SEND_OK);
invoke_to_comDataIn(0, buffer, context);
ASSERT_from_drvDataOut_SIZE(1);
ASSERT_from_drvDataOut(0, buffer);
ASSERT_from_comStatus(0, condition);
// Uplink
Drv::RecvStatus status = Drv::RecvStatus::RECV_OK;
invoke_to_drvDataIn(0, buffer, status);
invoke_to_drvDataIn(0, buffer, Drv::ByteStreamStatus::OP_OK);
ASSERT_from_comDataOut_SIZE(1);
ASSERT_from_comDataOut(0, buffer, status);
ASSERT_from_comDataOut(0, buffer);
}
void ComStubTester ::test_fail() {
this->test_initial();
Fw::Buffer buffer(storage[0], sizeof(storage[0]));
U8 storage[8];
Fw::Buffer buffer(storage, sizeof(storage));
this->fill(buffer);
Fw::Success condition = Fw::Success::FAILURE;
m_send_mode = Drv::SendStatus::SEND_ERROR;
m_send_mode = Drv::ByteStreamStatus::OTHER_ERROR;
ComCfg::FrameContext context;
// Downlink
ASSERT_EQ(invoke_to_comDataIn(0, buffer), Drv::SendStatus::SEND_OK);
invoke_to_comDataIn(0, buffer, context);
ASSERT_from_drvDataOut_SIZE(1);
ASSERT_from_drvDataOut(0, buffer);
ASSERT_from_drvDataOut_SIZE(1);
ASSERT_from_comStatus(0, condition);
// Uplink
Drv::RecvStatus status = Drv::RecvStatus::RECV_ERROR;
invoke_to_drvDataIn(0, buffer, status);
ASSERT_from_comDataOut_SIZE(1);
ASSERT_from_comDataOut(0, buffer, status);
invoke_to_drvDataIn(0, buffer, Drv::ByteStreamStatus::OTHER_ERROR);
ASSERT_from_comDataOut_SIZE(0); // receiving failure should not send anything
}
void ComStubTester ::test_retry() {
this->test_initial();
Fw::Buffer buffers[RETRIES];
Fw::Success condition = Fw::Success::SUCCESS;
m_send_mode = Drv::SendStatus::SEND_RETRY;
FwIndexType MAX_ITERS = this->component.RETRY_LIMIT + 1;
for (U32 i = 0; i < RETRIES; i++) {
// Make small individual buffers for testing
U8 storage[MAX_ITERS][8];
Fw::Buffer buffers[MAX_ITERS];
for (FwIndexType i = 0; i < MAX_ITERS; i++) {
buffers[i].setData(storage[i]);
buffers[i].setSize(sizeof(storage[i]));
buffers[i].setContext(i);
buffers[i].setContext(static_cast<U32>(i));
this->fill(buffers[i]);
invoke_to_comDataIn(0, buffers[i]);
ASSERT_from_drvDataOut_SIZE((i + 1) * RETRIES);
m_retries = 0;
}
ASSERT_from_drvDataOut_SIZE(RETRIES * RETRIES);
ASSERT_from_comStatus_SIZE(3);
for (U32 i = 0; i < RETRIES; i++) {
for (U32 j = 0; j < RETRIES; j++) {
ASSERT_from_drvDataOut((i * RETRIES) + j, buffers[i]);
// Retrying for as many times as the RETRY_LIMIT should be ok
for (FwIndexType i = 0; i < this->component.RETRY_LIMIT; i++) {
invoke_to_dataReturnIn(0, buffers[i], Drv::ByteStreamStatus::SEND_RETRY);
// Test we have indeed retried (data sent on drvDataOut)
ASSERT_from_drvDataOut_SIZE(static_cast<U32>(i + 1));
ASSERT_from_drvDataOut(static_cast<U32>(i), buffers[i]);
}
ASSERT_from_comStatus(i, condition);
ASSERT_from_drvDataOut_SIZE(static_cast<U32>(this->component.RETRY_LIMIT));
ASSERT_EQ(this->component.m_retry_count, this->component.RETRY_LIMIT);
// Retry one more time should block from retrying and reset retry count
invoke_to_dataReturnIn(0, buffers[MAX_ITERS - 1], Drv::ByteStreamStatus::SEND_RETRY);
ASSERT_from_drvDataOut_SIZE(static_cast<U32>(this->component.RETRY_LIMIT)); // no drvDataOut sent when SEND_RETRY
ASSERT_from_dataReturnOut_SIZE(1); // buffer ownership was returned
ASSERT_EQ(this->component.m_retry_count, 0);
}
void ComStubTester ::test_retry_reset() {
this->test_initial();
FwIndexType MAX_ITERS = this->component.RETRY_LIMIT + 1;
U32 expected_drvDataOut_count = 0;
// Make small individual buffers for testing
U8 storage[MAX_ITERS][8];
Fw::Buffer buffers[MAX_ITERS];
for (FwIndexType i = 0; i < MAX_ITERS; i++) {
buffers[i].setData(storage[i]);
buffers[i].setSize(sizeof(storage[i]));
buffers[i].setContext(static_cast<U32>(i));
this->fill(buffers[i]);
}
// Retrying for as many times as the RETRY_LIMIT should be ok
for (FwIndexType i = 0; i < this->component.RETRY_LIMIT; i++) {
invoke_to_dataReturnIn(0, buffers[i], Drv::ByteStreamStatus::SEND_RETRY);
ASSERT_from_drvDataOut(expected_drvDataOut_count, buffers[i]);
expected_drvDataOut_count++; // trick: increment now to use as index prior and size after
ASSERT_from_drvDataOut_SIZE(expected_drvDataOut_count);
}
// Now, we receive a OP_OK, which should not retry (drvDataOut should not be called) and reset the retry count
ASSERT_from_drvDataOut_SIZE(expected_drvDataOut_count); // no drvDataOut sent when OP_OK
invoke_to_dataReturnIn(0, buffers[0], Drv::ByteStreamStatus::OP_OK);
ASSERT_from_drvDataOut_SIZE(expected_drvDataOut_count); // no drvDataOut sent when OP_OK
// Now that retry count is reset, we can retry again without a problem
for (FwIndexType i = 0; i < this->component.RETRY_LIMIT; i++) {
invoke_to_dataReturnIn(0, buffers[i], Drv::ByteStreamStatus::SEND_RETRY);
ASSERT_from_drvDataOut(expected_drvDataOut_count, buffers[i]);
expected_drvDataOut_count++; // trick: increment now to use as index prior and size after
ASSERT_from_drvDataOut_SIZE(expected_drvDataOut_count);
}
ASSERT_from_drvDataOut_SIZE(expected_drvDataOut_count); // no drvDataOut sent when OP_OK
}
// ----------------------------------------------------------------------
@ -122,51 +154,17 @@ void ComStubTester ::test_retry() {
// ----------------------------------------------------------------------
void ComStubTester ::from_comDataOut_handler(const FwIndexType portNum,
Fw::Buffer& recvBuffer,
const Drv::RecvStatus& recvStatus) {
this->pushFromPortEntry_comDataOut(recvBuffer, recvStatus);
Fw::Buffer& recvBuffer) {
this->pushFromPortEntry_comDataOut(recvBuffer);
}
void ComStubTester ::from_comStatus_handler(const FwIndexType portNum, Fw::Success& condition) {
this->pushFromPortEntry_comStatus(condition);
void ComStubTester ::from_comStatusOut_handler(const FwIndexType portNum, Fw::Success& condition) {
this->pushFromPortEntry_comStatusOut(condition);
}
Drv::SendStatus ComStubTester ::from_drvDataOut_handler(const FwIndexType portNum, Fw::Buffer& sendBuffer) {
void ComStubTester ::from_drvDataOut_handler(const FwIndexType portNum, Fw::Buffer& sendBuffer) {
this->pushFromPortEntry_drvDataOut(sendBuffer);
m_retries = (m_send_mode == Drv::SendStatus::SEND_RETRY) ? (m_retries + 1) : m_retries;
if (m_retries < RETRIES) {
return m_send_mode;
}
return Drv::SendStatus::SEND_OK;
}
// ----------------------------------------------------------------------
// Helper methods
// ----------------------------------------------------------------------
void ComStubTester ::connectPorts() {
// comDataIn
this->connect_to_comDataIn(0, this->m_component.get_comDataIn_InputPort(0));
// drvConnected
this->connect_to_drvConnected(0, this->m_component.get_drvConnected_InputPort(0));
// drvDataIn
this->connect_to_drvDataIn(0, this->m_component.get_drvDataIn_InputPort(0));
// comDataOut
this->m_component.set_comDataOut_OutputPort(0, this->get_from_comDataOut(0));
// comStatus
this->m_component.set_comStatus_OutputPort(0, this->get_from_comStatus(0));
// drvDataOut
this->m_component.set_drvDataOut_OutputPort(0, this->get_from_drvDataOut(0));
}
void ComStubTester ::initComponents() {
this->init();
this->m_component.init(INSTANCE);
}
} // end namespace Svc

View File

@ -13,6 +13,12 @@
namespace Svc {
class ComStubTester : public ComStubGTestBase {
// Maximum size of histories storing events, telemetry, and port outputs
static const FwSizeType MAX_HISTORY_SIZE = 30;
// Instance ID supplied to the component instance under test
static const FwEnumStoreType TEST_INSTANCE_ID = 0;
// ----------------------------------------------------------------------
// Construction and destruction
// ----------------------------------------------------------------------
@ -50,6 +56,10 @@ class ComStubTester : public ComStubGTestBase {
//!
void test_retry();
//! Tests the retry -> reset -> retry again
//!
void test_retry_reset();
private:
// ----------------------------------------------------------------------
// Handlers for typed from ports
@ -58,18 +68,17 @@ class ComStubTester : public ComStubGTestBase {
//! Handler for from_comDataOut
//!
void from_comDataOut_handler(const FwIndexType portNum, //!< The port number
Fw::Buffer& recvBuffer,
const Drv::RecvStatus& recvStatus);
Fw::Buffer& recvBuffer);
//! Handler for from_comStatus
//! Handler for from_comStatusOut
//!
void from_comStatus_handler(const FwIndexType portNum, //!< The port number
void from_comStatusOut_handler(const FwIndexType portNum, //!< The port number
Fw::Success& condition //!< Status of communication state
);
//! Handler for from_drvDataOut
//!
Drv::SendStatus from_drvDataOut_handler(const FwIndexType portNum, //!< The port number
void from_drvDataOut_handler(const FwIndexType portNum, //!< The port number
Fw::Buffer& sendBuffer);
private:
@ -92,8 +101,8 @@ class ComStubTester : public ComStubGTestBase {
//! The component under test
//!
ComStub m_component;
Drv::SendStatus m_send_mode; //! Send mode
ComStub component;
Drv::ByteStreamStatus m_send_mode; //! Send mode
U32 m_retries; //! Number of retries to test
};

View File

@ -25,7 +25,7 @@ FprimeDeframer ::~FprimeDeframer() {}
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------
void FprimeDeframer ::framedIn_handler(FwIndexType portNum, Fw::Buffer& data, Fw::Buffer& context) {
void FprimeDeframer ::framedIn_handler(FwIndexType portNum, Fw::Buffer& data, const ComCfg::FrameContext& context) {
if (data.getSize() < FprimeProtocol::FrameHeader::SERIALIZED_SIZE + FprimeProtocol::FrameTrailer::SERIALIZED_SIZE) {
// Incoming buffer is not long enough to contain a valid frame (header+trailer)
this->log_WARNING_HI_InvalidBufferReceived();

View File

@ -38,7 +38,7 @@ class FprimeDeframer final : public FprimeDeframerComponentBase {
//! and pass the deframed data to the deframed output port.
void framedIn_handler(FwIndexType portNum, //!< The port number
Fw::Buffer& data,
Fw::Buffer& context) override;
const ComCfg::FrameContext& context) override;
};
} // namespace Svc

View File

@ -16,7 +16,7 @@ The `Svc::FprimeDeframer` does not support deframing multiple packets in a singl
### Frame validation
The passed-in `data` field (of type `Fw::Buffer`) of the `Fw.DataWithContext` input port is validated for the following conditions:
The passed-in `data` field (of type `Fw::Buffer`) of the `Svc.ComDataWithContext` input port is validated for the following conditions:
- The buffer is large enough to contain the header and trailer
- The buffer starts with the F´ start word
- The buffer length is equal to (or larger than) the packet length field in the frame header
@ -41,7 +41,7 @@ The below diagram shows a typical configuration in which the `Svc::FprimeDeframe
```mermaid
classDiagram
class FprimeDeframer~PassiveComponent~ {
+ void framedIn_handler(FwIndexType portNum, Fw::Buffer& data, Fw::Buffer& context)
+ void framedIn_handler(FwIndexType portNum, Fw::Buffer& data, const ComCfg::FrameContext& context)
}
```
@ -57,6 +57,6 @@ SVC-DEFRAMER-002 | `Svc::FprimeDeframer` shall deallocate input buffers that are
| Kind | Name | Type | Description |
|---|---|---|---|
| `guarded input` | framedIn | `Fw.DataWithContext` | Receives a frame with optional context data |
| `output` | deframedOut | `Fw.DataWithContext` | Receives a frame with optional context data |
| `guarded input` | framedIn | `Svc.ComDataWithContext` | Receives a frame with optional context data |
| `output` | deframedOut | `Svc.ComDataWithContext` | Receives a frame with optional context data |
| `output` | bufferDeallocate | `Fw.BufferSend` | Port for deallocating dropped frames |

View File

@ -122,7 +122,7 @@ void FprimeDeframerTester::injectChecksum(U8* data, FwSizeType size) {
}
void FprimeDeframerTester::mockReceiveData(U8* data, FwSizeType size) {
Fw::Buffer nullContext;
ComCfg::FrameContext nullContext;
Fw::Buffer buffer(data, static_cast<Fw::Buffer::SizeType>(size));
this->invoke_to_framedIn(0, buffer, nullContext);
}

View File

@ -0,0 +1,32 @@
####
# FPrime CMakeLists.txt:
#
# SOURCE_FILES: combined list of source and autocoding files
# MOD_DEPS: (optional) module dependencies
# UT_SOURCE_FILES: list of source files for unit tests
#
# More information in the F´ CMake API documentation:
# https://fprime.jpl.nasa.gov/latest/documentation/reference
#
####
set(SOURCE_FILES
"${CMAKE_CURRENT_LIST_DIR}/FprimeFramer.fpp"
"${CMAKE_CURRENT_LIST_DIR}/FprimeFramer.cpp"
)
set(MOD_DEPS
Svc/FprimeProtocol
)
register_fprime_module()
### Unit Tests ###
set(UT_SOURCE_FILES
"${CMAKE_CURRENT_LIST_DIR}/FprimeFramer.fpp"
"${CMAKE_CURRENT_LIST_DIR}/test/ut/FprimeFramerTestMain.cpp"
"${CMAKE_CURRENT_LIST_DIR}/test/ut/FprimeFramerTester.cpp"
)
set(UT_AUTO_HELPERS ON)
register_fprime_ut()

View File

@ -0,0 +1,75 @@
// ======================================================================
// \title FprimeFramer.cpp
// \author thomas-bc
// \brief cpp file for FprimeFramer component implementation class
// ======================================================================
#include "Svc/FprimeFramer/FprimeFramer.hpp"
#include "Svc/FprimeProtocol/FrameHeaderSerializableAc.hpp"
#include "Svc/FprimeProtocol/FrameTrailerSerializableAc.hpp"
#include "Utils/Hash/Hash.hpp"
namespace Svc {
// ----------------------------------------------------------------------
// Component construction and destruction
// ----------------------------------------------------------------------
FprimeFramer ::FprimeFramer(const char* const compName) : FprimeFramerComponentBase(compName) {}
FprimeFramer ::~FprimeFramer() {}
// ----------------------------------------------------------------------
// Handler implementations for typed input ports
// ----------------------------------------------------------------------
void FprimeFramer ::dataIn_handler(FwIndexType portNum, Fw::Buffer& data, const ComCfg::FrameContext& context) {
FprimeProtocol::FrameHeader header;
FprimeProtocol::FrameTrailer trailer;
// Full size of the frame will be size of header + data + trailer
FwSizeType frameSize =
FprimeProtocol::FrameHeader::SERIALIZED_SIZE + data.getSize() + FprimeProtocol::FrameTrailer::SERIALIZED_SIZE;
FW_ASSERT(data.getSize() <= std::numeric_limits<FprimeProtocol::TokenType>::max(), static_cast<FwAssertArgType>(frameSize));
FW_ASSERT(frameSize <= std::numeric_limits<Fw::Buffer::SizeType>::max(), static_cast<FwAssertArgType>(frameSize));
// Allocate frame buffer
Fw::Buffer frameBuffer = this->bufferAllocate_out(0, static_cast<Fw::Buffer::SizeType>(frameSize));
auto frameSerializer = frameBuffer.getSerializer();
Fw::SerializeStatus status;
// Serialize the header
// 0xDEADBEEF is already set as the default value for the header startWord field in the FPP type definition
header.setlengthField(data.getSize());
status = frameSerializer.serialize(header);
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
// Serialize the data
status = frameSerializer.serialize(data.getData(), data.getSize(), Fw::Serialization::OMIT_LENGTH);
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
// Serialize the trailer (with CRC computation)
Utils::HashBuffer hashBuffer;
Utils::Hash::hash(frameBuffer.getData(), frameSize - HASH_DIGEST_LENGTH, hashBuffer);
trailer.setcrcField(hashBuffer.asBigEndianU32());
status = frameSerializer.serialize(trailer);
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
// Send the full frame out - this port shall always be connected
this->dataOut_out(0, frameBuffer, context);
// Return original (unframed) data buffer ownership back to its sender - always connected
this->dataReturnOut_out(0, data, context);
}
void FprimeFramer ::comStatusIn_handler(FwIndexType portNum, Fw::Success& condition) {
if (this->isConnected_comStatusOut_OutputPort(portNum)) {
this->comStatusOut_out(portNum, condition);
}
}
void FprimeFramer ::dataReturnIn_handler(FwIndexType portNum, Fw::Buffer& frameBuffer, const ComCfg::FrameContext& context) {
// dataReturnIn is the allocated buffer coming back from the ComManager (e.g. ComStub) component
this->bufferDeallocate_out(0, frameBuffer);
}
} // namespace Svc

View File

@ -0,0 +1,24 @@
module Svc {
@ Framer implementation for the F Prime protocol
passive component FprimeFramer {
include "../Interfaces/FramerInterface.fppi"
# ----------------------------------------------------------------------
# Allocation of buffers
# ----------------------------------------------------------------------
@ Port for allocating buffers to hold framed data
output port bufferAllocate: Fw.BufferGet
@ Port for deallocating buffers allocated for framed data
output port bufferDeallocate: Fw.BufferSend
# ----------------------------------------------------------------------
# Standard AC Ports
# ----------------------------------------------------------------------
@ Port for requesting the current time
time get port timeCaller
}
}

View File

@ -0,0 +1,66 @@
// ======================================================================
// \title FprimeFramer.hpp
// \author thomas-bc
// \brief hpp file for FprimeFramer component implementation class
// ======================================================================
#ifndef Svc_FprimeFramer_HPP
#define Svc_FprimeFramer_HPP
#include "Svc/FprimeFramer/FprimeFramerComponentAc.hpp"
namespace Svc {
class FprimeFramer final : public FprimeFramerComponentBase {
public:
// ----------------------------------------------------------------------
// Component construction and destruction
// ----------------------------------------------------------------------
//! Construct FprimeFramer object
FprimeFramer(const char* const compName //!< The component name
);
//! Destroy FprimeFramer object
~FprimeFramer();
PRIVATE:
// ----------------------------------------------------------------------
// Handler implementations for typed input ports
// ----------------------------------------------------------------------
//! Handler implementation for comStatusIn
//!
//! Port receiving the general status from the downstream component
//! indicating it is ready or not-ready for more input
void comStatusIn_handler(FwIndexType portNum, //!< The port number
Fw::Success& condition //!< Condition success/failure
) override;
//! Handler implementation for dataIn
//!
//! Port to receive data to frame, in a Fw::Buffer with optional context
void dataIn_handler(FwIndexType portNum, //!< The port number
Fw::Buffer& data,
const ComCfg::FrameContext& context) override;
//! Handler implementation for dataReturnIn
//!
//! Buffer coming from a deallocate call in a ComDriver component
void dataReturnIn_handler(FwIndexType portNum, //!< The port number
Fw::Buffer& data,
const ComCfg::FrameContext& context) override;
// ----------------------------------------------------------------------
// Helpers
// ----------------------------------------------------------------------
//! Helper function to send the framed data out of the component
//! This sequentially calls both frameDataOut and frameStreamOut ports if connected
void framedOut_helper(Fw::Buffer& frameBuffer,
const ComCfg::FrameContext& context);
};
} // namespace Svc
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -0,0 +1,42 @@
# Svc::FprimeFramer
The `Svc::FprimeFramer` is an implementation of the [FramerInterface](../../Interfaces/docs/sdd.md) for the F Prime protocol.
It receives data (an F´ packet) on input and produces an [F´ frame](../../FprimeProtocol/docs/sdd.md) on its output port as a result. Please refer to the [F Prime frame specification](../../FprimeProtocol/docs/sdd.md) for details on the frame format.
### Diagrams
Below is the common configuration in which the `Svc::FprimeFramer` can be used. It is receiving packets from a [`Svc::ComQueue`](../../ComQueue/docs/sdd.md) and passes frames to a [Communications Adapter](../../Interfaces/docs/sdd.md), such as a Radio manager component (or a [`Svc::ComStub`](../../ComStub/docs/sdd.md)), for transmission.
![./img/framer-topology.png](./img/framer-topology.png)
## Internals
The `Svc::FprimeFramer` receives data packets of type `Svc.ComDataWithContext`. This type contains both a `Fw::Buffer` containing the packet data, and a `context: FrameContext` that contains contextual information about the data packet (such as an APID). In the default configuration (using Svc::ComQueue), the `context` is used to determine whether a packet is coming from the ComQueue's Fw::Buffer queue (as opposed to ComPacket queue). If it is, the original data packet `Fw::Buffer` is returned back to its original sender.
On receiving a data packet, the `Svc::FprimeFramer` performs the following actions:
1. Allocates a new _`outBuffer`_ (of type `Fw::Buffer`) to hold the F´ frame, of size _`size(dataPacket) + size(FprimeHeader) + size(FprimeTrailer)`_
2. Serializes the F´ start word (`0xDEADBEEF`) and length token (`size(dataPacket)`) into _`outBuffer`_
3. Serializes the F´ packet data into _`outBuffer`_
4. Computes and serializes a CRC32 checksum into _`outBuffer`_
5. Emits the _`outBuffer`_ on the `dataOut` output port. Ownership of _`outBuffer`_ is handed to the receiver
5. Transfer ownership of input _`dataPacket`_ to the `dataReturnOut` port. This usually should be connected to the same component that sent the original packet to `dataIn`.
## Port Descriptions
| Kind | Name | Port Type | Usage |
|---|---|---|---|
| `guarded input` | `dataIn` | `Svc.ComDataWithContext` | Port to receive data to frame, in a Fw::Buffer with optional context|
| `output` | `dataOut` | `Svc.ComDataWithContext` | Port to output framed data, with optional context, for follow-up framing|
| `sync input` | `comStatusIn` | `Fw.SuccessCondition` | Port receiving the general status from the downstream component|
| `output` | `comStatusOut` | `Fw.SuccessCondition` | Port receiving indicating the status of framer for receiving more data|
## Requirements
| Name | Description | Validation |
|---|---|---|
| SVC-FPRIME_FRAMER-001 | `Svc::FprimeFramer` shall accept data buffers (packets) stored in `Fw::Buffer` through its `dataIn` input port | Unit Test |
| SVC-FPRIME_FRAMER-002 | `Svc::FprimeFramer` shall emit one F Prime frame on its `framedOut` output port for each packet received on `dataIn` input port | Unit Test |
| SVC-FPRIME_FRAMER-003 | `Svc::FprimeFramer` shall emit F Prime frames that conforms to the [F´ frame specification](../../FprimeProtocol/docs/sdd.md) | Unit Test |

View File

@ -0,0 +1,27 @@
// ======================================================================
// \title FprimeFramerTestMain.cpp
// \author thomas-bc
// \brief cpp file for FprimeFramer component test main function
// ======================================================================
#include "FprimeFramerTester.hpp"
TEST(Nominal, testComStatusPassThrough) {
Svc::FprimeFramerTester tester;
tester.testComStatusPassThrough();
}
TEST(Nominal, testFrameDeallocation) {
Svc::FprimeFramerTester tester;
tester.testFrameDeallocation();
}
TEST(Nominal, testNominalFraming) {
Svc::FprimeFramerTester tester;
tester.testNominalFraming();
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,93 @@
// ======================================================================
// \title FprimeFramerTester.cpp
// \author thomas-bc
// \brief cpp file for FprimeFramer component test harness implementation class
// ======================================================================
#include "FprimeFramerTester.hpp"
#include "Svc/FprimeProtocol/FrameHeaderSerializableAc.hpp"
#include "Svc/FprimeProtocol/FrameTrailerSerializableAc.hpp"
namespace Svc {
// ----------------------------------------------------------------------
// Construction and destruction
// ----------------------------------------------------------------------
FprimeFramerTester ::FprimeFramerTester()
: FprimeFramerGTestBase("FprimeFramerTester", FprimeFramerTester::MAX_HISTORY_SIZE), component("FprimeFramer") {
this->initComponents();
this->connectPorts();
}
FprimeFramerTester ::~FprimeFramerTester() {}
// ----------------------------------------------------------------------
// Tests
// ----------------------------------------------------------------------
void FprimeFramerTester ::testFrameDeallocation() {
// When receiving a buffer on dataReturnIn, the buffer should be deallocated
Fw::Buffer buffer;
ComCfg::FrameContext context;
this->invoke_to_dataReturnIn(0, buffer, context);
ASSERT_from_bufferDeallocate_SIZE(1);
ASSERT_from_bufferDeallocate(0, buffer);
}
void FprimeFramerTester ::testComStatusPassThrough() {
// Send a status message to the component
Fw::Success inputStatus = Fw::Success::SUCCESS;
this->invoke_to_comStatusIn(0, inputStatus);
ASSERT_from_comStatusOut_SIZE(1);
ASSERT_from_comStatusOut(0, inputStatus); // at index 0, received SUCCESS
inputStatus = Fw::Success::FAILURE;
this->invoke_to_comStatusIn(0, inputStatus);
ASSERT_from_comStatusOut_SIZE(2);
ASSERT_from_comStatusOut(1, inputStatus); // at index 1, received FAILURE
}
void FprimeFramerTester ::testNominalFraming() {
U8 bufferData[100];
Fw::Buffer buffer(bufferData, sizeof(bufferData));
ComCfg::FrameContext context;
// Fill the buffer with some data
for (U32 i = 0; i < sizeof(bufferData); ++i) {
bufferData[i] = static_cast<U8>(i);
}
// Send the buffer to the component
this->invoke_to_dataIn(0, buffer, context);
ASSERT_from_dataOut_SIZE(1); // One frame emitted
ASSERT_from_dataReturnOut_SIZE(1); // Original data buffer ownership returned
Fw::Buffer outputBuffer = this->fromPortHistory_dataOut->at(0).data;
// Check the size of the output buffer
ASSERT_EQ(outputBuffer.getSize(), sizeof(bufferData) + FprimeProtocol::FrameHeader::SERIALIZED_SIZE + FprimeProtocol::FrameTrailer::SERIALIZED_SIZE);
// Check header
FprimeProtocol::FrameHeader defaultHeader;
FprimeProtocol::FrameHeader outputHeader;
outputBuffer.getDeserializer().deserialize(outputHeader);
ASSERT_EQ(outputHeader.getstartWord(), defaultHeader.getstartWord());
ASSERT_EQ(outputHeader.getlengthField(), sizeof(bufferData));
// Check data
for (U32 i = 0; i < sizeof(bufferData); ++i) {
ASSERT_EQ(outputBuffer.getData()[i + FprimeProtocol::FrameHeader::SERIALIZED_SIZE], bufferData[i]);
}
}
// ----------------------------------------------------------------------
// Test Harness: Handler implementations for output ports
// ----------------------------------------------------------------------
Fw::Buffer FprimeFramerTester::from_bufferAllocate_handler(FwIndexType portNum, U32 size){
this->pushFromPortEntry_bufferAllocate(size);
this->m_buffer.setData(this->m_buffer_slot);
this->m_buffer.setSize(size);
::memset(this->m_buffer.getData(), 0, size);
return this->m_buffer;
}
} // namespace Svc

View File

@ -0,0 +1,83 @@
// ======================================================================
// \title FprimeFramerTester.hpp
// \author thomas-bc
// \brief hpp file for FprimeFramer component test harness implementation class
// ======================================================================
#ifndef Svc_FprimeFramerTester_HPP
#define Svc_FprimeFramerTester_HPP
#include "Svc/FprimeFramer/FprimeFramer.hpp"
#include "Svc/FprimeFramer/FprimeFramerGTestBase.hpp"
namespace Svc {
class FprimeFramerTester final : public FprimeFramerGTestBase {
public:
// ----------------------------------------------------------------------
// Constants
// ----------------------------------------------------------------------
// Maximum size of histories storing events, telemetry, and port outputs
static const FwSizeType MAX_HISTORY_SIZE = 10;
// Instance ID supplied to the component instance under test
static const FwEnumStoreType TEST_INSTANCE_ID = 0;
public:
// ----------------------------------------------------------------------
// Construction and destruction
// ----------------------------------------------------------------------
//! Construct object FprimeFramerTester
FprimeFramerTester();
//! Destroy object FprimeFramerTester
~FprimeFramerTester();
public:
// ----------------------------------------------------------------------
// Tests
// ----------------------------------------------------------------------
//! Test pass through of comStatusIn to comStatusOut
void testComStatusPassThrough();
//! Test deallocation of data
void testFrameDeallocation();
//! Test framing of data
void testNominalFraming();
private:
// ----------------------------------------------------------------------
// Helper functions
// ----------------------------------------------------------------------
//! Connect ports
void connectPorts();
//! Initialize components
void initComponents();
// ----------------------------------------------------------------------
// Test Harness: Handler implementations for output ports
// ----------------------------------------------------------------------
Fw::Buffer from_bufferAllocate_handler(FwIndexType portNum, U32 size) override;
private:
// ----------------------------------------------------------------------
// Member variables
// ----------------------------------------------------------------------
//! The component under test
FprimeFramer component;
U8 m_buffer_slot[2048];
Fw::Buffer m_buffer; // buffer to be returned by mocked allocate call
};
} // namespace Svc
#endif

View File

@ -1,9 +1,12 @@
module Svc {
module FprimeProtocol {
type TokenType = U32
@ Describes the frame header format for the F Prime communications protocol
struct FrameHeader {
startWord: U32,
lengthField: U32,
startWord: TokenType,
lengthField: TokenType,
} default {
startWord = 0xdeadbeef
}
@ -14,3 +17,4 @@ module FprimeProtocol {
}
}
}

View File

@ -23,7 +23,7 @@ FprimeRouter ::~FprimeRouter() {}
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------
void FprimeRouter ::dataIn_handler(FwIndexType portNum, Fw::Buffer& packetBuffer, Fw::Buffer& contextBuffer) {
void FprimeRouter ::dataIn_handler(FwIndexType portNum, Fw::Buffer& packetBuffer, const ComCfg::FrameContext& context) {
// Read the packet type from the packet buffer
FwPacketDescriptorType packetType = Fw::ComPacket::FW_PACKET_UNKNOWN;
Fw::SerializeStatus status = Fw::FW_SERIALIZE_OK;
@ -71,7 +71,7 @@ void FprimeRouter ::dataIn_handler(FwIndexType portNum, Fw::Buffer& packetBuffer
// Packet type is not known to the F Prime protocol. If the unknownDataOut port is
// connected, forward packet and context for further processing
if (this->isConnected_unknownDataOut_OutputPort(0)) {
this->unknownDataOut_out(0, packetBuffer, contextBuffer);
this->unknownDataOut_out(0, packetBuffer, context);
// Transfer ownership of the packetBuffer to the receiver
deallocate = false;
}

View File

@ -8,7 +8,7 @@ module Svc {
include "../Interfaces/RouterInterface.fppi"
@ Port for forwarding non-recognized packet types
output port unknownDataOut: Fw.DataWithContext
output port unknownDataOut: Svc.ComDataWithContext
@ Port for deallocating buffers
output port bufferDeallocate: Fw.BufferSend

View File

@ -33,7 +33,7 @@ class FprimeRouter final : public FprimeRouterComponentBase {
//! Receiving Fw::Buffer from Deframer
void dataIn_handler(FwIndexType portNum, //!< The port number
Fw::Buffer& packetBuffer, //!< The packet buffer
Fw::Buffer& contextBuffer //!< The context buffer
const ComCfg::FrameContext& context //!< The context object
) override;
// ! Handler for input port cmdResponseIn

View File

@ -2,7 +2,7 @@
The `Svc::FprimeRouter` component routes F´ packets (such as command or file packets) to other components.
The `Svc::FprimeRouter` component receives F´ packets (as [Fw::Buffer](../../../Fw/Buffer/docs/sdd.md) objects) and routes them to other components through synchronous port calls. The input port of type `Fw.DataWithContext` passes this Fw.Buffer object along with optional context data which can help for routing. The current F Prime protocol does not use this context data, but is nevertheless present in the interface for compatibility with other protocols which may for example pass APIDs in the frame headers.
The `Svc::FprimeRouter` component receives F´ packets (as [Fw::Buffer](../../../Fw/Buffer/docs/sdd.md) objects) and routes them to other components through synchronous port calls. The input port of type `Svc.ComDataWithContext` passes this Fw.Buffer object along with optional context data which can help for routing. The current F Prime protocol does not use this context data, but is nevertheless present in the interface for compatibility with other protocols which may for example pass APIDs in the frame headers.
The `Svc::FprimeRouter` component supports `Fw::ComPacket::FW_PACKET_COMMAND` and `Fw::ComPacket::FW_PACKET_FILE` packet types. Unknown packet types are forwarded on the `unknownDataOut` port, which a project-specific component can connect to for custom routing. In the case of unknown data being forwarded, the ownership of the packet data `Fw::Buffer` object is passed to the receiver.
@ -31,10 +31,10 @@ classDiagram
| Name | Description | Type |
|---|---|---|
| `dataIn: Fw.DataWithContext` | Receiving Fw::Buffer with context buffer from Deframer | `guarded input` |
| `dataIn: Svc.ComDataWithContext` | Receiving Fw::Buffer with context buffer from Deframer | `guarded input` |
| `commandOut: Fw.Com` | Port for sending command packets as Fw::ComBuffers | `output` |
| `fileOut: Fw.BufferSend` | Port for sending file packets as Fw::Buffer (ownership passed to receiver) | `output` |
| `unknownDataOut: Fw.DataWithContext` | Port forwarding unknown data (useful for adding custom routing rules with a project-defined router) | `output` |
| `unknownDataOut: Svc.ComDataWithContext` | Port forwarding unknown data (useful for adding custom routing rules with a project-defined router) | `output` |
| `output`| bufferDeallocate | `Fw.BufferSend` | Port for deallocating buffers once routed |
## Requirements

View File

@ -76,10 +76,9 @@ void FprimeRouterTester::mockReceivePacketType(Fw::ComPacket::ComPacketType pack
const FwPacketDescriptorType descriptorType = packetType;
U8 data[sizeof descriptorType];
Fw::Buffer buffer(data, sizeof(data));
auto esb = buffer.getSerializer();
Fw::SerializeStatus status = esb.serialize(descriptorType);
ComCfg::FrameContext nullContext;
Fw::SerializeStatus status = buffer.getSerializer().serialize(descriptorType);
FW_ASSERT(status == Fw::FW_SERIALIZE_OK);
Fw::Buffer nullContext;
this->invoke_to_dataIn(0, buffer, nullContext);
}

View File

@ -50,12 +50,12 @@ void FrameAccumulator ::cleanup() {
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------
void FrameAccumulator ::dataIn_handler(FwIndexType portNum, Fw::Buffer& buffer, const Drv::RecvStatus& status) {
void FrameAccumulator ::dataIn_handler(FwIndexType portNum, Fw::Buffer& buffer) {
// Check whether there is data to process
if (status.e == Drv::RecvStatus::RECV_OK) {
// There is: process the data
if (buffer.isValid()) {
this->processBuffer(buffer);
}
// TODO: rework the uplink deallocation logic to use the bufferReturn chaining pattern
// Deallocate the buffer
this->bufferDeallocate_out(0, buffer);
}
@ -135,8 +135,8 @@ void FrameAccumulator ::processRing() {
FW_ASSERT(m_inRing.get_allocated_size() == remaining - size_out,
static_cast<FwAssertArgType>(m_inRing.get_allocated_size()),
static_cast<FwAssertArgType>(remaining), static_cast<FwAssertArgType>(size_out));
Fw::Buffer nullContext;
this->frameOut_out(0, buffer, nullContext);
ComCfg::FrameContext context;
this->frameOut_out(0, buffer, context);
} else {
// No buffer is available, we need to exit and try again later
this->log_WARNING_HI_NoBufferAvailable();

View File

@ -50,8 +50,7 @@ class FrameAccumulator final : public FrameAccumulatorComponentBase {
//!
//! Receives raw data from a ByteStreamDriver, ComStub, or other buffer producing component
void dataIn_handler(FwIndexType portNum, //!< The port number
Fw::Buffer& recvBuffer,
const Drv::RecvStatus& recvStatus) override;
Fw::Buffer& recvBuffer) override;
PRIVATE:
//! \brief process raw buffer

View File

@ -66,7 +66,7 @@ The `Svc::FrameAccumulator` component is used in the uplink stack of many refere
classDiagram
class FrameAccumulator~PassiveComponent~ {
+ void configure(FrameDetector& detector, FwEnumStoreType allocationId, Fw::MemAllocator& allocator, FwSizeType store_size)
+ void dataIn_handler(FwIndexType portNum, Fw::Buffer& recvBuffer, const Drv::RecvStatus& recvStatus)
+ void dataIn_handler(FwIndexType portNum, Fw::Buffer& recvBuffer, const Drv::ByteStreamStatus& recvStatus)
+ void processBuffer(Fw::Buffer& buffer)
+ void processRing()
}
@ -86,6 +86,6 @@ SVC-FRAME-ACCUMULATOR-004 | `Svc::FrameAccumulator` shall accept byte buffers co
| Kind | Name | Type | Description |
|---|---|---|---|
| `guarded input` | dataIn | `Drv.ByteStreamRecv` | Receives raw data from a ByteStreamDriver, ComStub, or other buffer producing component |
| `output` | frameOut | `Fw.DataWithContext` | Port for sending an extracted frame out |
| `output` | frameOut | `Svc.ComDataWithContext` | Port for sending an extracted frame out |
| `output` | bufferAllocate | `Fw.BufferGet` | Port for allocating buffer to hold extracted frame |
| `output`| bufferDeallocate | `Fw.BufferSend` | Port for deallocating buffers received on dataIn. |

View File

@ -40,7 +40,7 @@ void FrameAccumulatorTester ::testFrameDetected() {
// Set the mock detector to report success of size_out = buffer_size
this->mockDetector.set_next_result(FrameDetector::Status::FRAME_DETECTED, buffer_size);
// Receive the buffer on dataIn
this->invoke_to_dataIn(0, buffer, Drv::RecvStatus::RECV_OK);
this->invoke_to_dataIn(0, buffer);
// Checks
ASSERT_from_bufferDeallocate_SIZE(1); // input buffer was deallocated
ASSERT_from_frameOut_SIZE(1); // frame was sent
@ -56,7 +56,7 @@ void FrameAccumulatorTester ::testMoreDataNeeded() {
// Set the mock detector to report more data needed
this->mockDetector.set_next_result(FrameDetector::Status::MORE_DATA_NEEDED, buffer_size + 1);
// Receive the buffer on dataIn
this->invoke_to_dataIn(0, buffer, Drv::RecvStatus::RECV_OK);
this->invoke_to_dataIn(0, buffer);
// Checks
ASSERT_from_bufferDeallocate_SIZE(1); // input buffer was deallocated
ASSERT_from_frameOut_SIZE(0); // frame was not sent (waiting on more data)
@ -71,7 +71,7 @@ void FrameAccumulatorTester ::testNoFrameDetected() {
// Set the mock detector
this->mockDetector.set_next_result(FrameDetector::Status::NO_FRAME_DETECTED, 0);
// Receive the buffer on dataIn
this->invoke_to_dataIn(0, buffer, Drv::RecvStatus::RECV_OK);
this->invoke_to_dataIn(0, buffer);
// Checks
ASSERT_from_bufferDeallocate_SIZE(1); // input buffer was deallocated
ASSERT_from_frameOut_SIZE(0); // No frame was sent out
@ -84,7 +84,7 @@ void FrameAccumulatorTester ::testReceiveZeroSizeBuffer() {
U8 data[1] = {0};
Fw::Buffer buffer(data, 0);
// Receive the buffer on dataIn
this->invoke_to_dataIn(0, buffer, Drv::RecvStatus::RECV_OK);
this->invoke_to_dataIn(0, buffer);
// Checks
ASSERT_from_bufferDeallocate_SIZE(1); // input buffer was deallocated
ASSERT_from_frameOut_SIZE(0); // No frame was sent out
@ -102,11 +102,11 @@ void FrameAccumulatorTester ::testAccumulateTwoBuffers() {
this->mockDetector.set_next_result(FrameDetector::Status::MORE_DATA_NEEDED, buffer2_size);
// Receive the buffer on dataIn
this->invoke_to_dataIn(0, buffer1, Drv::RecvStatus::RECV_OK);
this->invoke_to_dataIn(0, buffer1);
// Next result is detection of a full frame, size = buffer1_size + buffer2_size
this->mockDetector.set_next_result(FrameDetector::Status::FRAME_DETECTED, buffer1_size + buffer2_size );
// Receive the buffer on dataIn
this->invoke_to_dataIn(0, buffer2, Drv::RecvStatus::RECV_OK);
this->invoke_to_dataIn(0, buffer2);
// Checks
ASSERT_from_bufferDeallocate_SIZE(2); // both input buffers deallocated
@ -171,7 +171,7 @@ void FrameAccumulatorTester ::mockAccumulateFullFrame(U32& frame_size, U32& buff
buffer.setSize(buffer_size);
// Detector reports MORE_DATA_NEEDED and size needed bigger than accumulated size so far
this->mockDetector.set_next_result(FrameDetector::Status::MORE_DATA_NEEDED, accumulated_size + 1);
this->invoke_to_dataIn(0, buffer, Drv::RecvStatus::RECV_OK);
this->invoke_to_dataIn(0, buffer);
}
// Send last buffer with FRAME_DETECTED
@ -181,7 +181,7 @@ void FrameAccumulatorTester ::mockAccumulateFullFrame(U32& frame_size, U32& buff
// Send last buffer with finally FRAME_DETECTED and total accumulated + last buffer
this->mockDetector.set_next_result(FrameDetector::Status::FRAME_DETECTED, accumulated_size);
// Receive the last buffer on dataIn
this->invoke_to_dataIn(0, buffer, Drv::RecvStatus::RECV_OK);
this->invoke_to_dataIn(0, buffer);
frame_size = accumulated_size;
buffer_count = iters + 1;
}

View File

@ -76,7 +76,7 @@ TEST(FprimeFrameDetector, TestBufferTooSmall) {
// Anything smaller than the size of header + trailer is invalid
U32 minimum_valid_size =
FprimeProtocol::FrameHeader::SERIALIZED_SIZE + FprimeProtocol::FrameTrailer::SERIALIZED_SIZE;
Svc::FprimeProtocol::FrameHeader::SERIALIZED_SIZE + Svc::FprimeProtocol::FrameTrailer::SERIALIZED_SIZE;
U32 invalid_size = STest::Random::lowerUpper(1, minimum_valid_size - 1);
// Set the circular buffer to hold data of invalid size
circular_buffer.serialize(buffer, invalid_size);

View File

@ -1,26 +0,0 @@
####
# F prime CMakeLists.txt:
#
# SOURCE_FILES: combined list of source and autocoding files
# MOD_DEPS: (optional) module dependencies
#
# Note: using PROJECT_NAME as EXECUTABLE_NAME
####
set(SOURCE_FILES
"${CMAKE_CURRENT_LIST_DIR}/Framer.fpp"
"${CMAKE_CURRENT_LIST_DIR}/Framer.cpp"
)
set(MOD_DEPS
Svc/FramingProtocol
)
register_fprime_module()
#### UTS ###
set(UT_SOURCE_FILES
"${CMAKE_CURRENT_LIST_DIR}/Framer.fpp"
"${CMAKE_CURRENT_LIST_DIR}/test/ut/FramerTester.cpp"
"${CMAKE_CURRENT_LIST_DIR}/test/ut/FramerTestMain.cpp"
)
register_fprime_ut()

View File

@ -1,87 +0,0 @@
// ======================================================================
// \title Framer.cpp
// \author mstarch
// \brief cpp file for Framer component implementation class
//
// \copyright
// Copyright 2009-2022, by the California Institute of Technology.
// ALL RIGHTS RESERVED. United States Government Sponsorship
// acknowledged.
//
// ======================================================================
#include <Fw/FPrimeBasicTypes.hpp>
#include <Svc/Framer/Framer.hpp>
#include "Fw/Logger/Logger.hpp"
#include "Utils/Hash/Hash.hpp"
namespace Svc {
// ----------------------------------------------------------------------
// Construction, initialization, and destruction
// ----------------------------------------------------------------------
Framer ::Framer(const char* const compName)
: FramerComponentBase(compName), FramingProtocolInterface(), m_protocol(nullptr), m_frame_sent(false) {}
Framer ::~Framer() {}
void Framer ::setup(FramingProtocol& protocol) {
FW_ASSERT(this->m_protocol == nullptr);
this->m_protocol = &protocol;
protocol.setup(*this);
}
void Framer ::handle_framing(const U8* const data, const U32 size, Fw::ComPacket::ComPacketType packet_type) {
FW_ASSERT(this->m_protocol != nullptr);
this->m_frame_sent = false; // Clear the flag to detect if frame was sent
this->m_protocol->frame(data, size, packet_type);
// If no frame was sent, Framer has the obligation to report success
if (this->isConnected_comStatusOut_OutputPort(0) && (!this->m_frame_sent)) {
Fw::Success status = Fw::Success::SUCCESS;
this->comStatusOut_out(0, status);
}
}
// ----------------------------------------------------------------------
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------
void Framer ::comIn_handler(const FwIndexType portNum, Fw::ComBuffer& data, U32 context) {
FW_ASSERT(data.getBuffLength() < std::numeric_limits<U32>::max(), static_cast<FwAssertArgType>(data.getBuffLength()));
this->handle_framing(data.getBuffAddr(), static_cast<U32>(data.getBuffLength()), Fw::ComPacket::FW_PACKET_UNKNOWN);
}
void Framer ::bufferIn_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) {
this->handle_framing(fwBuffer.getData(), fwBuffer.getSize(), Fw::ComPacket::FW_PACKET_FILE);
// Deallocate the buffer after it was processed by the framing protocol
this->bufferDeallocate_out(0, fwBuffer);
}
void Framer ::comStatusIn_handler(const FwIndexType portNum, Fw::Success& condition) {
if (this->isConnected_comStatusOut_OutputPort(portNum)) {
this->comStatusOut_out(portNum, condition);
}
}
// ----------------------------------------------------------------------
// Framing protocol implementations
// ----------------------------------------------------------------------
void Framer ::send(Fw::Buffer& outgoing) {
FW_ASSERT(!this->m_frame_sent); // Prevent multiple sends per-packet
const Drv::SendStatus sendStatus = this->framedOut_out(0, outgoing);
if (sendStatus.e != Drv::SendStatus::SEND_OK) {
// Note: if there is a data sending problem, an EVR likely wouldn't
// make it down. Log the issue in hopes that
// someone will see it.
Fw::Logger::log("[ERROR] Failed to send framed data: %d\n", sendStatus.e);
}
this->m_frame_sent = true; // A frame was sent
}
Fw::Buffer Framer ::allocate(const U32 size) {
return this->framedAllocate_out(0, size);
}
} // end namespace Svc

View File

@ -1,50 +0,0 @@
module Svc {
@ A component for framing input for transmission to the ground
passive component Framer {
# ----------------------------------------------------------------------
# Receiving packets
# ----------------------------------------------------------------------
@ Port for receiving data packets of any type stored in statically-sized
@ Fw::Com buffers
guarded input port comIn: Fw.Com
@ Port for receiving file packets stored in dynamically-sized
@ Fw::Buffer objects
guarded input port bufferIn: Fw.BufferSend
# ----------------------------------------------------------------------
# Allocation and deallocation of buffers
# ----------------------------------------------------------------------
@ Port for deallocating buffers received on bufferIn, after
@ copying packet data to the frame buffer
output port bufferDeallocate: Fw.BufferSend
@ Port for allocating buffers to hold framed data
output port framedAllocate: Fw.BufferGet
# ----------------------------------------------------------------------
# Sending frame data
# ----------------------------------------------------------------------
@ Port for sending buffers containing framed data. Ownership of the
@ buffer passes to the receiver.
output port framedOut: Drv.ByteStreamSend
# ----------------------------------------------------------------------
# Handling of of ready signals
# ----------------------------------------------------------------------
@ Port receiving the general status from the downstream component
@ indicating it is ready or not-ready for more input
sync input port comStatusIn: Fw.SuccessCondition
@ Port receiving indicating the status of framer for receiving more data
output port comStatusOut: Fw.SuccessCondition
}
}

View File

@ -1,113 +0,0 @@
// ======================================================================
// \title Framer.hpp
// \author mstarch, bocchino
// \brief hpp file for Framer component implementation class
//
// \copyright
// Copyright 2009-2022, by the California Institute of Technology.
// ALL RIGHTS RESERVED. United States Government Sponsorship
// acknowledged.
//
// ======================================================================
#ifndef Svc_Framer_HPP
#define Svc_Framer_HPP
#include "Svc/Framer/FramerComponentAc.hpp"
#include "Svc/FramingProtocol/FramingProtocol.hpp"
#include "Svc/FramingProtocol/FramingProtocolInterface.hpp"
namespace Svc {
/**
* \brief Generic framing component using FramingProtocol implementation for actual framing
*
* Framing component used to take Com and File packets and frame serialize them using a
* framing protocol specified in a FramingProtocol instance. The instance must be supplied
* using the `setup` method.
*
* Using this component, projects can implement and supply a fresh FramingProtocol implementation
* without changing the reference topology.
*/
class Framer final : public FramerComponentBase, public FramingProtocolInterface {
public:
// ----------------------------------------------------------------------
// Construction, initialization, and destruction
// ----------------------------------------------------------------------
//! Construct object Framer
//!
Framer(const char* const compName /*!< The component name*/
);
//! \brief Setup this component with a supplied framing protocol
//!
void setup(FramingProtocol& protocol /*!< Protocol used in framing */);
//! Destroy object Framer
//!
~Framer();
PRIVATE:
// ----------------------------------------------------------------------
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------
//! Handler implementation for comIn
//!
void comIn_handler(const FwIndexType portNum, /*!< The port number*/
Fw::ComBuffer& data, /*!< Buffer containing packet data*/
U32 context /*!< Call context value; meaning chosen by user*/
);
//! Handler implementation for bufferIn
//!
void bufferIn_handler(const FwIndexType portNum, /*!< The port number*/
Fw::Buffer& fwBuffer /*!< The buffer*/
);
//! Handler implementation for comStatusIn
//!
void comStatusIn_handler(const FwIndexType portNum, /*!< The port number*/
Fw::Success& condition /*!< The condition*/);
// ----------------------------------------------------------------------
// Implementation of FramingProtocolInterface
// ----------------------------------------------------------------------
//! \brief Allocation callback used to request memory for the framer
//!
//! Method used by the FramingProtocol to allocate memory for the framed buffer. Framing
//! typically adds tokens on the beginning and end of the raw data so it must allocate new space
//! to place those and a copy of the data in.
//!
//! \param size: size of allocation
//! \return Fw::Buffer containing allocation to write into
Fw::Buffer allocate(const U32 size);
//! Send implementation
//!
void send(Fw::Buffer& outgoing //!< The buffer to send
);
// ----------------------------------------------------------------------
// Helper functions
// ----------------------------------------------------------------------
//! \brief helper function to handle framing of the raw data
//!
void handle_framing(const U8* const data, const U32 size, Fw::ComPacket::ComPacketType packet_type);
// ----------------------------------------------------------------------
// Member variables
// ----------------------------------------------------------------------
//! The FramingProtocol implementation
FramingProtocol* m_protocol;
//! Flag determining if at least one frame was sent during framing
bool m_frame_sent;
};
} // end namespace Svc
#endif

View File

@ -1 +0,0 @@
DATA_FOLDER=top/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

View File

@ -1,48 +0,0 @@
{
"columns" : [
[
{
"instanceName" : "eventLogger",
"inputPorts" : [],
"outputPorts" : [
{
"name" : "PktSend",
"portNumbers" : [
0
]
}
]
}
],
[
{
"instanceName" : "framer",
"inputPorts" : [
{
"name" : "comIn",
"portNumbers" : [
0
]
}
],
"outputPorts" : []
}
]
],
"connections" : [
[
[
0,
0,
0,
0
],
[
1,
0,
0,
0
]
]
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

View File

@ -1,6 +0,0 @@
eventLogger
PktSend
0
framer
comIn
0

View File

@ -1,109 +0,0 @@
{
"columns" : [
[
{
"instanceName" : "framer",
"inputPorts" : [],
"outputPorts" : [
{
"name" : "framedOut",
"portNumbers" : [
0
]
},
{
"name" : "framedAllocate",
"portNumbers" : [
0
]
}
]
}
],
[
{
"instanceName" : "comm",
"inputPorts" : [
{
"name" : "send",
"portNumbers" : [
0
]
}
],
"outputPorts" : [
{
"name" : "deallocate",
"portNumbers" : [
0
]
}
]
}
],
[
{
"instanceName" : "buffMgr",
"inputPorts" : [
{
"name" : "bufferSendIn",
"portNumbers" : [
0
]
},
{
"name" : "bufferGetCallee",
"portNumbers" : [
0
]
}
],
"outputPorts" : []
}
]
],
"connections" : [
[
[
0,
0,
1,
0
],
[
2,
0,
1,
0
]
],
[
[
1,
0,
0,
0
],
[
2,
0,
0,
0
]
],
[
[
0,
0,
0,
0
],
[
1,
0,
0,
0
]
]
]
}

Some files were not shown because too many files have changed in this diff Show More