mirror of
https://github.com/nasa/fprime.git
synced 2025-12-10 00:44:37 -06:00
Use data return pattern on Uplink and standardize port names (#3546)
* First pass at Svc + TcpClient implementation * Revert FileUplink changes * Add copy (with allocation/deallocation) to FprimeRouter to simplify buffer management * Update FprimeRouter UTs * Update FprimeDeframer UTs * Update FrameAccumulator UTs * Update ComStub UTs * Update missing Drv and UTs * Update ComInterface to use ComDataWithContext on output * Update Ref/RPI topology * Fix spelling * Fix test typo * Update Udp component and UTs * Rename data ports and standardize "Return" naming pattern * Fix variable name * Adapt UTs * Update Communication Adapter Interface docs * Full SDD updates * Spelling & nits and details * Put formatting back to original * Update Deframer interface to include bufferReturn * Address review comments
This commit is contained in:
parent
53320cd6a5
commit
c3b2e04880
@ -8,7 +8,7 @@ The outgoing stream is represented by the input `send` port; other components ca
|
|||||||
### Send
|
### Send
|
||||||
|
|
||||||
The manager component (for example a radio manager) initiates the transfer of send data by calling the "send" port.
|
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.
|
The caller will provide a `Fw::Buffer` containing the data to send. The driver component **must** perform a callback on its `sendReturnOut` 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:
|
These responses are an enumeration whose values are described in the following table:
|
||||||
|
|
||||||
| Value | Description | Buffer Ownership |
|
| Value | Description | Buffer Ownership |
|
||||||
|
|||||||
@ -7,5 +7,8 @@
|
|||||||
@ Invoke this port to send data out the driver
|
@ Invoke this port to send data out the driver
|
||||||
guarded input port $send: Fw.BufferSend
|
guarded input port $send: Fw.BufferSend
|
||||||
|
|
||||||
@ Port invoked to return ownership of sent data back to the sender
|
@ Port returning ownership of data received on $send port
|
||||||
output port dataReturnOut: Drv.ByteStreamData
|
output port sendReturnOut: Drv.ByteStreamData
|
||||||
|
|
||||||
|
@ Port receiving back ownership of data sent out on $recv port
|
||||||
|
guarded input port recvReturnIn: Fw.BufferSend
|
||||||
|
|||||||
@ -311,7 +311,12 @@ void LinuxUartDriver ::send_handler(const FwIndexType portNum, Fw::Buffer& serBu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Return the buffer back to the caller
|
// Return the buffer back to the caller
|
||||||
dataReturnOut_out(0, serBuffer, status);
|
sendReturnOut_out(0, serBuffer, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LinuxUartDriver::recvReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
|
||||||
|
this->deallocate_out(0, fwBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LinuxUartDriver ::serialReadTaskEntry(void* ptr) {
|
void LinuxUartDriver ::serialReadTaskEntry(void* ptr) {
|
||||||
|
|||||||
@ -11,6 +11,9 @@ module Drv {
|
|||||||
@ Allocation port used for allocating memory in the receive task
|
@ Allocation port used for allocating memory in the receive task
|
||||||
output port allocate: Fw.BufferGet
|
output port allocate: Fw.BufferGet
|
||||||
|
|
||||||
|
@ Deallocation of allocated buffers
|
||||||
|
output port deallocate: Fw.BufferSend
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
# Special ports
|
# Special ports
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|||||||
@ -94,8 +94,14 @@ class LinuxUartDriver final : public LinuxUartDriverComponentBase {
|
|||||||
//! Handler implementation for serialSend
|
//! Handler implementation for serialSend
|
||||||
//!
|
//!
|
||||||
void send_handler(FwIndexType portNum, /*!< The port number*/
|
void send_handler(FwIndexType portNum, /*!< The port number*/
|
||||||
Fw::Buffer& serBuffer);
|
Fw::Buffer& serBuffer) override;
|
||||||
|
|
||||||
|
//! Handler implementation for recvReturnIn
|
||||||
|
//!
|
||||||
|
//! Port receiving back ownership of data sent out on $recv port
|
||||||
|
void recvReturnIn_handler(FwIndexType portNum, //!< The port number
|
||||||
|
Fw::Buffer& fwBuffer //!< The buffer
|
||||||
|
) override;
|
||||||
|
|
||||||
PlatformIntType m_fd; //!< file descriptor returned for I/O device
|
PlatformIntType m_fd; //!< file descriptor returned for I/O device
|
||||||
U32 m_allocationSize; //!< size of allocation request to memory manager
|
U32 m_allocationSize; //!< size of allocation request to memory manager
|
||||||
|
|||||||
@ -6,5 +6,8 @@ module Drv {
|
|||||||
@ Allocation for received data
|
@ Allocation for received data
|
||||||
output port allocate: Fw.BufferGet
|
output port allocate: Fw.BufferGet
|
||||||
|
|
||||||
|
@ Deallocation of allocated buffers
|
||||||
|
output port deallocate: Fw.BufferSend
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -91,7 +91,11 @@ void TcpClientComponentImpl::send_handler(const FwIndexType portNum, Fw::Buffer&
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Return the buffer and status to the caller
|
// Return the buffer and status to the caller
|
||||||
this->dataReturnOut_out(0, fwBuffer, returnStatus);
|
this->sendReturnOut_out(0, fwBuffer, returnStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TcpClientComponentImpl::recvReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
|
||||||
|
this->deallocate_out(0, fwBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namespace Drv
|
} // end namespace Drv
|
||||||
|
|||||||
@ -126,6 +126,14 @@ class TcpClientComponentImpl final : public TcpClientComponentBase, public Socke
|
|||||||
*/
|
*/
|
||||||
void send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) override;
|
void send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) override;
|
||||||
|
|
||||||
|
//! Handler implementation for recvReturnIn
|
||||||
|
//!
|
||||||
|
//! Port receiving back ownership of data sent out on $recv port
|
||||||
|
void recvReturnIn_handler(FwIndexType portNum, //!< The port number
|
||||||
|
Fw::Buffer& fwBuffer //!< The buffer
|
||||||
|
) override;
|
||||||
|
|
||||||
|
|
||||||
Drv::TcpClientSocket m_socket; //!< Socket implementation
|
Drv::TcpClientSocket m_socket; //!< Socket implementation
|
||||||
|
|
||||||
// Member variable to store the buffer size
|
// Member variable to store the buffer size
|
||||||
|
|||||||
@ -15,6 +15,11 @@ TEST(Nominal, TcpClientBasicReceiveThread) {
|
|||||||
tester.test_receive_thread();
|
tester.test_receive_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Nominal, TcpClientBufferDeallocation) {
|
||||||
|
Drv::TcpClientTester tester;
|
||||||
|
tester.test_buffer_deallocation();
|
||||||
|
}
|
||||||
|
|
||||||
TEST(Reconnect, TcpClientMultiMessaging) {
|
TEST(Reconnect, TcpClientMultiMessaging) {
|
||||||
Drv::TcpClientTester tester;
|
Drv::TcpClientTester tester;
|
||||||
tester.test_multiple_messaging();
|
tester.test_multiple_messaging();
|
||||||
|
|||||||
@ -80,8 +80,8 @@ void TcpClientTester ::test_with_loop(U32 iterations, bool recv_thread) {
|
|||||||
m_data_buffer.setSize(sizeof(m_data_storage));
|
m_data_buffer.setSize(sizeof(m_data_storage));
|
||||||
size = Drv::Test::fill_random_buffer(m_data_buffer);
|
size = Drv::Test::fill_random_buffer(m_data_buffer);
|
||||||
invoke_to_send(0, m_data_buffer);
|
invoke_to_send(0, m_data_buffer);
|
||||||
ASSERT_from_dataReturnOut_SIZE(i + 1);
|
ASSERT_from_sendReturnOut_SIZE(i + 1);
|
||||||
Drv::ByteStreamStatus status = this->fromPortHistory_dataReturnOut->at(i).status;
|
Drv::ByteStreamStatus status = this->fromPortHistory_sendReturnOut->at(i).status;
|
||||||
EXPECT_EQ(status, ByteStreamStatus::OP_OK);
|
EXPECT_EQ(status, ByteStreamStatus::OP_OK);
|
||||||
Drv::Test::receive_all(server, server_fd, buffer, size);
|
Drv::Test::receive_all(server, server_fd, buffer, size);
|
||||||
Drv::Test::validate_random_buffer(m_data_buffer, buffer);
|
Drv::Test::validate_random_buffer(m_data_buffer, buffer);
|
||||||
@ -180,6 +180,15 @@ void TcpClientTester ::test_no_automatic_recv_connection() {
|
|||||||
server.terminate(server_fd);
|
server.terminate(server_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TcpClientTester ::test_buffer_deallocation() {
|
||||||
|
U8 data[1];
|
||||||
|
Fw::Buffer buffer(data, sizeof(data));
|
||||||
|
this->invoke_to_recvReturnIn(0, buffer);
|
||||||
|
ASSERT_from_deallocate_SIZE(1); // incoming buffer should be deallocated
|
||||||
|
ASSERT_EQ(this->fromPortHistory_deallocate->at(0).fwBuffer.getData(), data);
|
||||||
|
ASSERT_EQ(this->fromPortHistory_deallocate->at(0).fwBuffer.getSize(), sizeof(data));
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Handler overrides for typed from ports
|
// Handler overrides for typed from ports
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|||||||
@ -67,6 +67,8 @@ namespace Drv {
|
|||||||
|
|
||||||
void test_with_loop(U32 iterations, bool recv_thread=false);
|
void test_with_loop(U32 iterations, bool recv_thread=false);
|
||||||
|
|
||||||
|
void test_buffer_deallocation();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|||||||
@ -6,5 +6,8 @@ module Drv {
|
|||||||
@ Allocation for received data
|
@ Allocation for received data
|
||||||
output port allocate: Fw.BufferGet
|
output port allocate: Fw.BufferGet
|
||||||
|
|
||||||
|
@ Deallocation of allocated buffers
|
||||||
|
output port deallocate: Fw.BufferSend
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -139,7 +139,11 @@ void TcpServerComponentImpl::send_handler(const FwIndexType portNum, Fw::Buffer&
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Return the buffer and status to the caller
|
// Return the buffer and status to the caller
|
||||||
this->dataReturnOut_out(0, fwBuffer, returnStatus);
|
this->sendReturnOut_out(0, fwBuffer, returnStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TcpServerComponentImpl::recvReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
|
||||||
|
this->deallocate_out(0, fwBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namespace Drv
|
} // end namespace Drv
|
||||||
|
|||||||
@ -161,6 +161,13 @@ class TcpServerComponentImpl final : public TcpServerComponentBase, public Socke
|
|||||||
*/
|
*/
|
||||||
void send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) override;
|
void send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) override;
|
||||||
|
|
||||||
|
//! Handler implementation for recvReturnIn
|
||||||
|
//!
|
||||||
|
//! Port receiving back ownership of data sent out on $recv port
|
||||||
|
void recvReturnIn_handler(FwIndexType portNum, //!< The port number
|
||||||
|
Fw::Buffer& fwBuffer //!< The buffer
|
||||||
|
) override;
|
||||||
|
|
||||||
Drv::TcpServerSocket m_socket; //!< Socket implementation
|
Drv::TcpServerSocket m_socket; //!< Socket implementation
|
||||||
|
|
||||||
FwSizeType m_allocation_size; //!< Member variable to store the buffer size
|
FwSizeType m_allocation_size; //!< Member variable to store the buffer size
|
||||||
|
|||||||
@ -4,6 +4,11 @@
|
|||||||
|
|
||||||
#include "TcpServerTester.hpp"
|
#include "TcpServerTester.hpp"
|
||||||
|
|
||||||
|
TEST(Nominal, TcpServerBufferDeallocation) {
|
||||||
|
Drv::TcpServerTester tester;
|
||||||
|
tester.test_buffer_deallocation();
|
||||||
|
}
|
||||||
|
|
||||||
TEST(Nominal, TcpServerBasicMessaging) {
|
TEST(Nominal, TcpServerBasicMessaging) {
|
||||||
Drv::TcpServerTester tester;
|
Drv::TcpServerTester tester;
|
||||||
tester.test_basic_messaging();
|
tester.test_basic_messaging();
|
||||||
|
|||||||
@ -81,8 +81,8 @@ void TcpServerTester ::test_with_loop(U32 iterations, bool recv_thread) {
|
|||||||
m_data_buffer.setSize(sizeof(m_data_storage));
|
m_data_buffer.setSize(sizeof(m_data_storage));
|
||||||
size = Drv::Test::fill_random_buffer(m_data_buffer);
|
size = Drv::Test::fill_random_buffer(m_data_buffer);
|
||||||
invoke_to_send(0, m_data_buffer);
|
invoke_to_send(0, m_data_buffer);
|
||||||
ASSERT_from_dataReturnOut_SIZE(i + 1);
|
ASSERT_from_sendReturnOut_SIZE(i + 1);
|
||||||
Drv::ByteStreamStatus status = this->fromPortHistory_dataReturnOut->at(i).status;
|
Drv::ByteStreamStatus status = this->fromPortHistory_sendReturnOut->at(i).status;
|
||||||
EXPECT_EQ(status, ByteStreamStatus::OP_OK) <<
|
EXPECT_EQ(status, ByteStreamStatus::OP_OK) <<
|
||||||
"On iteration: " << i << " and receive thread: " << recv_thread;
|
"On iteration: " << i << " and receive thread: " << recv_thread;
|
||||||
Drv::Test::receive_all(client, client_fd, buffer, size);
|
Drv::Test::receive_all(client, client_fd, buffer, size);
|
||||||
@ -217,6 +217,15 @@ void TcpServerTester ::test_no_automatic_recv_connection() {
|
|||||||
this->component.terminate();
|
this->component.terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TcpServerTester ::test_buffer_deallocation() {
|
||||||
|
U8 data[1];
|
||||||
|
Fw::Buffer buffer(data, sizeof(data));
|
||||||
|
this->invoke_to_recvReturnIn(0, buffer);
|
||||||
|
ASSERT_from_deallocate_SIZE(1); // incoming buffer should be deallocated
|
||||||
|
ASSERT_EQ(this->fromPortHistory_deallocate->at(0).fwBuffer.getData(), data);
|
||||||
|
ASSERT_EQ(this->fromPortHistory_deallocate->at(0).fwBuffer.getSize(), sizeof(data));
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Handlers for typed from ports
|
// Handlers for typed from ports
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|||||||
@ -75,6 +75,8 @@ namespace Drv {
|
|||||||
void test_no_automatic_send_connection();
|
void test_no_automatic_send_connection();
|
||||||
|
|
||||||
void test_no_automatic_recv_connection();
|
void test_no_automatic_recv_connection();
|
||||||
|
|
||||||
|
void test_buffer_deallocation();
|
||||||
|
|
||||||
bool wait_on_change(bool open, U32 iterations);
|
bool wait_on_change(bool open, U32 iterations);
|
||||||
bool wait_on_started(bool open, U32 iterations);
|
bool wait_on_started(bool open, U32 iterations);
|
||||||
|
|||||||
@ -5,5 +5,7 @@ module Drv {
|
|||||||
|
|
||||||
output port allocate: Fw.BufferGet
|
output port allocate: Fw.BufferGet
|
||||||
|
|
||||||
|
output port deallocate: Fw.BufferSend
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -100,7 +100,11 @@ void UdpComponentImpl::send_handler(const FwIndexType portNum, Fw::Buffer& fwBuf
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Return the buffer and status to the caller
|
// Return the buffer and status to the caller
|
||||||
this->dataReturnOut_out(0, fwBuffer, returnStatus);
|
this->sendReturnOut_out(0, fwBuffer, returnStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UdpComponentImpl::recvReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
|
||||||
|
this->deallocate_out(0, fwBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namespace Drv
|
} // end namespace Drv
|
||||||
|
|||||||
@ -99,7 +99,7 @@ PROTECTED:
|
|||||||
*
|
*
|
||||||
* \return IpSocket reference
|
* \return IpSocket reference
|
||||||
*/
|
*/
|
||||||
IpSocket& getSocketHandler();
|
IpSocket& getSocketHandler() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief returns a buffer to fill with data
|
* \brief returns a buffer to fill with data
|
||||||
@ -109,7 +109,7 @@ PROTECTED:
|
|||||||
*
|
*
|
||||||
* \return Fw::Buffer to fill with data
|
* \return Fw::Buffer to fill with data
|
||||||
*/
|
*/
|
||||||
Fw::Buffer getBuffer();
|
Fw::Buffer getBuffer() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief sends a buffer to be filled with data
|
* \brief sends a buffer to be filled with data
|
||||||
@ -119,12 +119,12 @@ PROTECTED:
|
|||||||
*
|
*
|
||||||
* \return Fw::Buffer filled with data to send out
|
* \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
|
* \brief called when the IPv4 system has been connected
|
||||||
*/
|
*/
|
||||||
void connected();
|
void connected() override;
|
||||||
|
|
||||||
PRIVATE:
|
PRIVATE:
|
||||||
|
|
||||||
@ -146,7 +146,15 @@ PROTECTED:
|
|||||||
* \param portNum: fprime port number of the incoming port call
|
* \param portNum: fprime port number of the incoming port call
|
||||||
* \param fwBuffer: buffer containing data to be sent
|
* \param fwBuffer: buffer containing data to be sent
|
||||||
*/
|
*/
|
||||||
void send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer);
|
void send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) override;
|
||||||
|
|
||||||
|
|
||||||
|
//! Handler implementation for recvReturnIn
|
||||||
|
//!
|
||||||
|
//! Port receiving back ownership of data sent out on $recv port
|
||||||
|
void recvReturnIn_handler(FwIndexType portNum, //!< The port number
|
||||||
|
Fw::Buffer& fwBuffer //!< The buffer
|
||||||
|
) override;
|
||||||
|
|
||||||
Drv::UdpSocket m_socket; //!< Socket implementation
|
Drv::UdpSocket m_socket; //!< Socket implementation
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,11 @@ TEST(Reconnect, UdpReceiveThreadReconnect) {
|
|||||||
tester.test_advanced_reconnect();
|
tester.test_advanced_reconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Reconnect, UdpBufferDeallocation) {
|
||||||
|
Drv::UdpTester tester;
|
||||||
|
tester.test_buffer_deallocation();
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
return RUN_ALL_TESTS();
|
return RUN_ALL_TESTS();
|
||||||
|
|||||||
@ -92,8 +92,8 @@ void UdpTester::test_with_loop(U32 iterations, bool recv_thread) {
|
|||||||
m_data_buffer.setSize(sizeof(m_data_storage));
|
m_data_buffer.setSize(sizeof(m_data_storage));
|
||||||
size = Drv::Test::fill_random_buffer(m_data_buffer);
|
size = Drv::Test::fill_random_buffer(m_data_buffer);
|
||||||
invoke_to_send(0, m_data_buffer);
|
invoke_to_send(0, m_data_buffer);
|
||||||
ASSERT_from_dataReturnOut_SIZE(i + 1);
|
ASSERT_from_sendReturnOut_SIZE(i + 1);
|
||||||
Drv::ByteStreamStatus status = this->fromPortHistory_dataReturnOut->at(i).status;
|
Drv::ByteStreamStatus status = this->fromPortHistory_sendReturnOut->at(i).status;
|
||||||
EXPECT_EQ(status, ByteStreamStatus::OP_OK);
|
EXPECT_EQ(status, ByteStreamStatus::OP_OK);
|
||||||
Drv::Test::receive_all(udp2, udp2_fd, buffer, size);
|
Drv::Test::receive_all(udp2, udp2_fd, buffer, size);
|
||||||
Drv::Test::validate_random_buffer(m_data_buffer, buffer);
|
Drv::Test::validate_random_buffer(m_data_buffer, buffer);
|
||||||
@ -158,6 +158,15 @@ void UdpTester ::test_advanced_reconnect() {
|
|||||||
test_with_loop(10, true); // Up to 10 * RECONNECT_MS
|
test_with_loop(10, true); // Up to 10 * RECONNECT_MS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UdpTester ::test_buffer_deallocation() {
|
||||||
|
U8 data[1];
|
||||||
|
Fw::Buffer buffer(data, sizeof(data));
|
||||||
|
this->invoke_to_recvReturnIn(0, buffer);
|
||||||
|
ASSERT_from_deallocate_SIZE(1); // incoming buffer should be deallocated
|
||||||
|
ASSERT_EQ(this->fromPortHistory_deallocate->at(0).fwBuffer.getData(), data);
|
||||||
|
ASSERT_EQ(this->fromPortHistory_deallocate->at(0).fwBuffer.getSize(), sizeof(data));
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Handlers for typed from ports
|
// Handlers for typed from ports
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|||||||
@ -69,6 +69,9 @@ namespace Drv {
|
|||||||
//!
|
//!
|
||||||
void test_advanced_reconnect();
|
void test_advanced_reconnect();
|
||||||
|
|
||||||
|
//! Test buffer deallocation
|
||||||
|
void test_buffer_deallocation();
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
void test_with_loop(U32 iterations, bool recv_thread=false);
|
void test_with_loop(U32 iterations, bool recv_thread=false);
|
||||||
|
|
||||||
|
|||||||
@ -63,26 +63,26 @@ module RPI {
|
|||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
connections Downlink {
|
connections Downlink {
|
||||||
eventLogger.PktSend -> comQueue.comPacketQueueIn[0]
|
eventLogger.PktSend -> comQueue.comPacketQueueIn[0]
|
||||||
chanTlm.PktSend -> comQueue.comPacketQueueIn[1]
|
chanTlm.PktSend -> comQueue.comPacketQueueIn[1]
|
||||||
fileDownlink.bufferSendOut -> comQueue.bufferQueueIn[0]
|
fileDownlink.bufferSendOut -> comQueue.bufferQueueIn[0]
|
||||||
|
|
||||||
comQueue.queueSend -> framer.dataIn
|
|
||||||
comQueue.bufferReturnOut[0] -> fileDownlink.bufferReturn
|
comQueue.bufferReturnOut[0] -> fileDownlink.bufferReturn
|
||||||
framer.dataReturnOut -> comQueue.bufferReturnIn
|
|
||||||
|
|
||||||
framer.bufferAllocate -> commsBufferManager.bufferGetCallee
|
comQueue.dataOut -> framer.dataIn
|
||||||
|
framer.dataReturnOut -> comQueue.dataReturnIn
|
||||||
|
|
||||||
|
framer.bufferAllocate -> commsBufferManager.bufferGetCallee
|
||||||
framer.bufferDeallocate -> commsBufferManager.bufferSendIn
|
framer.bufferDeallocate -> commsBufferManager.bufferSendIn
|
||||||
|
|
||||||
framer.dataOut -> comStub.comDataIn
|
framer.dataOut -> comStub.dataIn
|
||||||
comStub.dataReturnOut -> framer.dataReturnIn
|
comStub.dataReturnOut -> framer.dataReturnIn
|
||||||
comDriver.dataReturnOut -> comStub.dataReturnIn
|
|
||||||
|
|
||||||
comDriver.ready -> comStub.drvConnected
|
comStub.drvSendOut -> comDriver.$send
|
||||||
comStub.drvDataOut -> comDriver.$send
|
comDriver.sendReturnOut -> comStub.drvSendReturnIn
|
||||||
|
comDriver.ready -> comStub.drvConnected
|
||||||
|
|
||||||
comStub.comStatusOut -> framer.comStatusIn
|
comStub.comStatusOut -> framer.comStatusIn
|
||||||
framer.comStatusOut -> comQueue.comStatusIn
|
framer.comStatusOut -> comQueue.comStatusIn
|
||||||
}
|
}
|
||||||
|
|
||||||
connections FaultProtection {
|
connections FaultProtection {
|
||||||
@ -125,34 +125,41 @@ module RPI {
|
|||||||
rpiDemo.SpiReadWrite -> spiDrv.SpiReadWrite
|
rpiDemo.SpiReadWrite -> spiDrv.SpiReadWrite
|
||||||
}
|
}
|
||||||
|
|
||||||
connections MemoryAllocations {
|
|
||||||
comDriver.allocate -> commsBufferManager.bufferGetCallee
|
|
||||||
fileUplink.bufferSendOut -> commsBufferManager.bufferSendIn
|
|
||||||
frameAccumulator.bufferAllocate -> commsBufferManager.bufferGetCallee
|
|
||||||
frameAccumulator.bufferDeallocate -> commsBufferManager.bufferSendIn
|
|
||||||
fprimeRouter.bufferDeallocate -> commsBufferManager.bufferSendIn
|
|
||||||
deframer.bufferDeallocate -> commsBufferManager.bufferSendIn
|
|
||||||
}
|
|
||||||
|
|
||||||
connections UART {
|
connections UART {
|
||||||
rpiDemo.UartBuffers -> uartBufferManager.bufferSendIn
|
rpiDemo.UartBuffers -> uartBufferManager.bufferSendIn
|
||||||
rpiDemo.UartWrite -> uartDrv.$send
|
rpiDemo.UartWrite -> uartDrv.$send
|
||||||
uartDrv.$recv -> rpiDemo.UartRead
|
uartDrv.$recv -> rpiDemo.UartRead
|
||||||
uartDrv.allocate -> uartBufferManager.bufferGetCallee
|
uartDrv.allocate -> uartBufferManager.bufferGetCallee
|
||||||
uartDrv.dataReturnOut -> rpiDemo.UartWriteReturn
|
uartDrv.sendReturnOut -> rpiDemo.UartWriteReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
connections Uplink {
|
connections Uplink {
|
||||||
comDriver.$recv -> comStub.drvDataIn
|
# ComDriver buffer allocations
|
||||||
comStub.comDataOut -> frameAccumulator.dataIn
|
comDriver.allocate -> commsBufferManager.bufferGetCallee
|
||||||
|
comDriver.deallocate -> commsBufferManager.bufferSendIn
|
||||||
frameAccumulator.frameOut -> deframer.framedIn
|
# ComDriver <-> ComStub
|
||||||
deframer.deframedOut -> fprimeRouter.dataIn
|
comDriver.$recv -> comStub.drvReceiveIn
|
||||||
|
comStub.drvReceiveReturnOut -> comDriver.recvReturnIn
|
||||||
fprimeRouter.commandOut -> cmdDisp.seqCmdBuff
|
# ComStub <-> FrameAccumulator
|
||||||
fprimeRouter.fileOut -> fileUplink.bufferSendIn
|
comStub.dataOut -> frameAccumulator.dataIn
|
||||||
|
frameAccumulator.dataReturnOut -> comStub.dataReturnIn
|
||||||
cmdDisp.seqCmdStatus -> fprimeRouter.cmdResponseIn
|
# FrameAccumulator buffer allocations
|
||||||
|
frameAccumulator.bufferDeallocate -> commsBufferManager.bufferSendIn
|
||||||
|
frameAccumulator.bufferAllocate -> commsBufferManager.bufferGetCallee
|
||||||
|
# FrameAccumulator <-> Deframer
|
||||||
|
frameAccumulator.dataOut -> deframer.dataIn
|
||||||
|
deframer.dataReturnOut -> frameAccumulator.dataReturnIn
|
||||||
|
# Deframer <-> Router
|
||||||
|
deframer.dataOut -> fprimeRouter.dataIn
|
||||||
|
fprimeRouter.dataReturnOut -> deframer.dataReturnIn
|
||||||
|
# Router buffer allocations
|
||||||
|
fprimeRouter.bufferAllocate -> commsBufferManager.bufferGetCallee
|
||||||
|
fprimeRouter.bufferDeallocate -> commsBufferManager.bufferSendIn
|
||||||
|
# Router <-> CmdDispatcher/FileUplink
|
||||||
|
fprimeRouter.commandOut -> cmdDisp.seqCmdBuff
|
||||||
|
cmdDisp.seqCmdStatus -> fprimeRouter.cmdResponseIn
|
||||||
|
fprimeRouter.fileOut -> fileUplink.bufferSendIn
|
||||||
|
fileUplink.bufferSendOut -> fprimeRouter.fileBufferReturnIn
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,10 @@ module Ref {
|
|||||||
TELEMETRY
|
TELEMETRY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Ports_ComBufferQueue {
|
||||||
|
FILE_DOWNLINK
|
||||||
|
}
|
||||||
|
|
||||||
topology Ref {
|
topology Ref {
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
@ -92,28 +96,30 @@ module Ref {
|
|||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
connections Downlink {
|
connections Downlink {
|
||||||
dpCat.fileOut -> fileDownlink.SendFile
|
# Data Products
|
||||||
|
dpCat.fileOut -> fileDownlink.SendFile
|
||||||
fileDownlink.FileComplete -> dpCat.fileDone
|
fileDownlink.FileComplete -> dpCat.fileDone
|
||||||
|
# Inputs to ComQueue (events, telemetry, file)
|
||||||
eventLogger.PktSend -> comQueue.comPacketQueueIn[Ports_ComPacketQueue.EVENTS]
|
eventLogger.PktSend -> comQueue.comPacketQueueIn[Ports_ComPacketQueue.EVENTS]
|
||||||
tlmSend.PktSend -> comQueue.comPacketQueueIn[Ports_ComPacketQueue.TELEMETRY]
|
tlmSend.PktSend -> comQueue.comPacketQueueIn[Ports_ComPacketQueue.TELEMETRY]
|
||||||
fileDownlink.bufferSendOut -> comQueue.bufferQueueIn[0]
|
fileDownlink.bufferSendOut -> comQueue.bufferQueueIn[Ports_ComBufferQueue.FILE_DOWNLINK]
|
||||||
comQueue.bufferReturnOut[0] -> fileDownlink.bufferReturn
|
comQueue.bufferReturnOut[Ports_ComBufferQueue.FILE_DOWNLINK] -> fileDownlink.bufferReturn
|
||||||
|
# ComQueue <-> Framer
|
||||||
comQueue.queueSend -> fprimeFramer.dataIn
|
comQueue.dataOut -> fprimeFramer.dataIn
|
||||||
fprimeFramer.dataReturnOut -> comQueue.bufferReturnIn
|
fprimeFramer.dataReturnOut -> comQueue.dataReturnIn
|
||||||
fprimeFramer.comStatusOut -> comQueue.comStatusIn
|
# Buffer Management for Framer
|
||||||
|
fprimeFramer.bufferAllocate -> commsBufferManager.bufferGetCallee
|
||||||
fprimeFramer.bufferAllocate -> commsBufferManager.bufferGetCallee
|
|
||||||
fprimeFramer.bufferDeallocate -> commsBufferManager.bufferSendIn
|
fprimeFramer.bufferDeallocate -> commsBufferManager.bufferSendIn
|
||||||
|
# Framer <-> ComStub
|
||||||
fprimeFramer.dataOut -> comStub.comDataIn
|
fprimeFramer.dataOut -> comStub.dataIn
|
||||||
comStub.dataReturnOut -> fprimeFramer.dataReturnIn
|
comStub.dataReturnOut -> fprimeFramer.dataReturnIn
|
||||||
comStub.comStatusOut -> fprimeFramer.comStatusIn
|
# ComStub <-> ComDriver
|
||||||
|
comStub.drvSendOut -> comDriver.$send
|
||||||
comStub.drvDataOut -> comDriver.$send
|
comDriver.sendReturnOut -> comStub.drvSendReturnIn
|
||||||
comDriver.dataReturnOut -> comStub.dataReturnIn
|
comDriver.ready -> comStub.drvConnected
|
||||||
comDriver.ready -> comStub.drvConnected
|
# ComStatus
|
||||||
|
comStub.comStatusOut -> fprimeFramer.comStatusIn
|
||||||
|
fprimeFramer.comStatusOut -> comQueue.comStatusIn
|
||||||
}
|
}
|
||||||
|
|
||||||
connections FaultProtection {
|
connections FaultProtection {
|
||||||
@ -163,25 +169,32 @@ module Ref {
|
|||||||
}
|
}
|
||||||
|
|
||||||
connections Uplink {
|
connections Uplink {
|
||||||
|
# ComDriver buffer allocations
|
||||||
comDriver.allocate -> commsBufferManager.bufferGetCallee
|
comDriver.allocate -> commsBufferManager.bufferGetCallee
|
||||||
comDriver.$recv -> comStub.drvDataIn
|
comDriver.deallocate -> commsBufferManager.bufferSendIn
|
||||||
comStub.comDataOut -> frameAccumulator.dataIn
|
# ComDriver <-> ComStub
|
||||||
|
comDriver.$recv -> comStub.drvReceiveIn
|
||||||
frameAccumulator.frameOut -> deframer.framedIn
|
comStub.drvReceiveReturnOut -> comDriver.recvReturnIn
|
||||||
frameAccumulator.bufferAllocate -> commsBufferManager.bufferGetCallee
|
# ComStub <-> FrameAccumulator
|
||||||
|
comStub.dataOut -> frameAccumulator.dataIn
|
||||||
|
frameAccumulator.dataReturnOut -> comStub.dataReturnIn
|
||||||
|
# FrameAccumulator buffer allocations
|
||||||
frameAccumulator.bufferDeallocate -> commsBufferManager.bufferSendIn
|
frameAccumulator.bufferDeallocate -> commsBufferManager.bufferSendIn
|
||||||
deframer.bufferDeallocate -> commsBufferManager.bufferSendIn
|
frameAccumulator.bufferAllocate -> commsBufferManager.bufferGetCallee
|
||||||
deframer.deframedOut -> fprimeRouter.dataIn
|
# FrameAccumulator <-> Deframer
|
||||||
|
frameAccumulator.dataOut -> deframer.dataIn
|
||||||
fprimeRouter.commandOut -> cmdDisp.seqCmdBuff
|
deframer.dataReturnOut -> frameAccumulator.dataReturnIn
|
||||||
fprimeRouter.fileOut -> fileUplink.bufferSendIn
|
# Deframer <-> Router
|
||||||
|
deframer.dataOut -> fprimeRouter.dataIn
|
||||||
|
fprimeRouter.dataReturnOut -> deframer.dataReturnIn
|
||||||
|
# Router buffer allocations
|
||||||
|
fprimeRouter.bufferAllocate -> commsBufferManager.bufferGetCallee
|
||||||
fprimeRouter.bufferDeallocate -> commsBufferManager.bufferSendIn
|
fprimeRouter.bufferDeallocate -> commsBufferManager.bufferSendIn
|
||||||
|
# Router <-> CmdDispatcher/FileUplink
|
||||||
cmdDisp.seqCmdStatus -> fprimeRouter.cmdResponseIn
|
fprimeRouter.commandOut -> cmdDisp.seqCmdBuff
|
||||||
|
cmdDisp.seqCmdStatus -> fprimeRouter.cmdResponseIn
|
||||||
fileUplink.bufferSendOut -> commsBufferManager.bufferSendIn
|
fprimeRouter.fileOut -> fileUplink.bufferSendIn
|
||||||
|
fileUplink.bufferSendOut -> fprimeRouter.fileBufferReturnIn
|
||||||
}
|
}
|
||||||
|
|
||||||
connections DataProducts {
|
connections DataProducts {
|
||||||
|
|||||||
@ -193,7 +193,7 @@ void ComQueue::run_handler(const FwIndexType portNum, U32 context) {
|
|||||||
this->tlmWrite_buffQueueDepth(buffQueueDepth);
|
this->tlmWrite_buffQueueDepth(buffQueueDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComQueue ::bufferReturnIn_handler(FwIndexType portNum,
|
void ComQueue ::dataReturnIn_handler(FwIndexType portNum,
|
||||||
Fw::Buffer& data,
|
Fw::Buffer& data,
|
||||||
const ComCfg::FrameContext& context) {
|
const ComCfg::FrameContext& context) {
|
||||||
static_assert(std::numeric_limits<FwIndexType>::is_signed, "FwIndexType must be signed");
|
static_assert(std::numeric_limits<FwIndexType>::is_signed, "FwIndexType must be signed");
|
||||||
@ -205,7 +205,7 @@ void ComQueue ::bufferReturnIn_handler(FwIndexType portNum,
|
|||||||
// Failing this assert means that context.apid was modified since ComQueue set it, which should not happen
|
// 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));
|
FW_ASSERT(bufferReturnPortNum < BUFFER_PORT_COUNT, static_cast<FwAssertArgType>(bufferReturnPortNum));
|
||||||
if (bufferReturnPortNum >= 0) {
|
if (bufferReturnPortNum >= 0) {
|
||||||
// It is a coding error not to connect the associated bufferReturnOut port for each bufferReturnIn port
|
// It is a coding error not to connect the associated bufferReturnOut port for each dataReturnIn port
|
||||||
FW_ASSERT(this->isConnected_bufferReturnOut_OutputPort(bufferReturnPortNum), static_cast<FwAssertArgType>(bufferReturnPortNum));
|
FW_ASSERT(this->isConnected_bufferReturnOut_OutputPort(bufferReturnPortNum), static_cast<FwAssertArgType>(bufferReturnPortNum));
|
||||||
// If this is a buffer port, return the buffer to the BufferDownlink
|
// If this is a buffer port, return the buffer to the BufferDownlink
|
||||||
this->bufferReturnOut_out(bufferReturnPortNum, data);
|
this->bufferReturnOut_out(bufferReturnPortNum, data);
|
||||||
@ -263,7 +263,7 @@ void ComQueue::sendComBuffer(Fw::ComBuffer& comBuffer, FwIndexType queueIndex) {
|
|||||||
// Context APID is set to the queue index for now. A future implementation may want this to be configurable
|
// Context APID is set to the queue index for now. A future implementation may want this to be configurable
|
||||||
ComCfg::FrameContext context;
|
ComCfg::FrameContext context;
|
||||||
context.setcomQueueIndex(queueIndex);
|
context.setcomQueueIndex(queueIndex);
|
||||||
this->queueSend_out(0, outBuffer, context);
|
this->dataOut_out(0, outBuffer, context);
|
||||||
// Set state to WAITING for the status to come back
|
// Set state to WAITING for the status to come back
|
||||||
this->m_state = WAITING;
|
this->m_state = WAITING;
|
||||||
}
|
}
|
||||||
@ -275,7 +275,7 @@ void ComQueue::sendBuffer(Fw::Buffer& buffer, FwIndexType queueIndex) {
|
|||||||
// Context APID is set to the queue index for now. A future implementation may want this to be configurable
|
// Context APID is set to the queue index for now. A future implementation may want this to be configurable
|
||||||
ComCfg::FrameContext context;
|
ComCfg::FrameContext context;
|
||||||
context.setcomQueueIndex(queueIndex);
|
context.setcomQueueIndex(queueIndex);
|
||||||
this->queueSend_out(0, buffer, context);
|
this->dataOut_out(0, buffer, context);
|
||||||
|
|
||||||
// Set state to WAITING for the status to come back
|
// Set state to WAITING for the status to come back
|
||||||
this->m_state = WAITING;
|
this->m_state = WAITING;
|
||||||
|
|||||||
@ -17,7 +17,7 @@ module Svc {
|
|||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
@ Port for emitting data ready to be sent
|
@ Port for emitting data ready to be sent
|
||||||
output port queueSend: Svc.ComDataWithContext
|
output port dataOut: Svc.ComDataWithContext
|
||||||
|
|
||||||
@ Port for receiving the status signal
|
@ Port for receiving the status signal
|
||||||
async input port comStatusIn: Fw.SuccessCondition
|
async input port comStatusIn: Fw.SuccessCondition
|
||||||
@ -31,9 +31,8 @@ module Svc {
|
|||||||
@ Port array for returning ownership of Fw::Buffer to its original sender
|
@ Port array for returning ownership of Fw::Buffer to its original sender
|
||||||
output port bufferReturnOut: [ComQueueBufferPorts] Fw.BufferSend
|
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
|
@ Port for receiving Fw::Buffer whose ownership needs to be handed back
|
||||||
sync input port bufferReturnIn: Svc.ComDataWithContext
|
sync input port dataReturnIn: Svc.ComDataWithContext
|
||||||
|
|
||||||
@ Port for scheduling telemetry output
|
@ Port for scheduling telemetry output
|
||||||
async input port run: Svc.Sched drop
|
async input port run: Svc.Sched drop
|
||||||
|
|||||||
@ -148,10 +148,10 @@ class ComQueue final : public ComQueueComponentBase {
|
|||||||
U32 context /*!<The call order*/
|
U32 context /*!<The call order*/
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
//! Handler implementation for bufferReturnIn
|
//! Handler implementation for dataReturnIn
|
||||||
//!
|
//!
|
||||||
//! Port for returning ownership of Fw::Buffer to its sender
|
//! Port for returning ownership of Fw::Buffer to its sender
|
||||||
void bufferReturnIn_handler(FwIndexType portNum, //!< The port number
|
void dataReturnIn_handler(FwIndexType portNum, //!< The port number
|
||||||
Fw::Buffer& data,
|
Fw::Buffer& data,
|
||||||
const ComCfg::FrameContext& context) override;
|
const ComCfg::FrameContext& context) override;
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,7 @@ Queued messages from the highest priority source port are serviced first and a r
|
|||||||
| Requirement | Description | Rationale | Verification Method |
|
| 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-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` (wrapping the queued data units) 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-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-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 |
|
| 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 |
|
||||||
@ -32,7 +32,8 @@ Queued messages from the highest priority source port are serviced first and a r
|
|||||||
| 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-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-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 |
|
| 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
|
| SVC-COMQUEUE-010 | `Svc::ComQueue` shall return ownership of incoming buffers once they have been enqueued. | Memory management | Unit test |
|
||||||
|
|
||||||
## 4. Design
|
## 4. Design
|
||||||
The diagram below shows the `Svc::ComQueue` component.
|
The diagram below shows the `Svc::ComQueue` component.
|
||||||
|
|
||||||
@ -43,12 +44,12 @@ The diagram below shows the `Svc::ComQueue` component.
|
|||||||
|
|
||||||
| Kind | Name | Port Type | Usage |
|
| Kind | Name | Port Type | Usage |
|
||||||
|---------------|-------------------|---------------------------------------|----------------------------------------------------------|
|
|---------------|-------------------|---------------------------------------|----------------------------------------------------------|
|
||||||
| `output` | `queueSend` | `Svc.ComDataWithContext` | Port emitting queued messages |
|
| `output` | `dataOut` | `Svc.ComDataWithContext` | Port emitting queued messages |
|
||||||
|
| `sync input` | `dataReturnIn` | `Svc.ComDataWithContext` | Port retrieving back ownership buffers sent on dataOut |
|
||||||
| `async input` | `comStatusIn` | `Fw.SuccessCondition` | Port for receiving the status signal |
|
| `async input` | `comStatusIn` | `Fw.SuccessCondition` | Port for receiving the status signal |
|
||||||
| `async input` | `comPacketQueueIn`| `[ComQueueComPorts] Fw.Com` | Port array for receiving Fw::ComBuffers |
|
| `async input` | `comPacketQueueIn`| `[ComQueueComPorts] Fw.Com` | Port array for receiving Fw::ComBuffers |
|
||||||
| `async input` | `bufferQueueIn` | `[ComQueueBufferPorts] Fw.BufferSend`| Port array for receiving Fw::Buffers |
|
| `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` | `[ComQueueBufferPorts] Fw.BufferSend` | Port array returning ownership of buffers received on bufferQueueIn |
|
||||||
| `output` | `bufferReturnOut` | `Svc.ComDataWithContext` | Port for deallocating Fw::Buffer on queue overflow |
|
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> ComQueue also has the port instances for autocoded functionality for events, telemetry and time.
|
> ComQueue also has the port instances for autocoded functionality for events, telemetry and time.
|
||||||
|
|||||||
@ -68,7 +68,7 @@ void ComQueueTester ::emitOneAndCheck(FwIndexType expectedIndex,
|
|||||||
FwSizeType expectedSize) {
|
FwSizeType expectedSize) {
|
||||||
emitOne();
|
emitOne();
|
||||||
// Check that the data buffers are identical (size + data)
|
// Check that the data buffers are identical (size + data)
|
||||||
Fw::Buffer emittedBuffer = this->fromPortHistory_queueSend->at(expectedIndex).data;
|
Fw::Buffer emittedBuffer = this->fromPortHistory_dataOut->at(expectedIndex).data;
|
||||||
ASSERT_EQ(expectedSize, emittedBuffer.getSize());
|
ASSERT_EQ(expectedSize, emittedBuffer.getSize());
|
||||||
for (FwSizeType i = 0; i < expectedSize; i++) {
|
for (FwSizeType i = 0; i < expectedSize; i++) {
|
||||||
ASSERT_EQ(emittedBuffer.getData()[i], expectedData[i]);
|
ASSERT_EQ(emittedBuffer.getData()[i], expectedData[i]);
|
||||||
@ -158,17 +158,17 @@ void ComQueueTester ::testPrioritySend() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check that nothing has yet been sent
|
// Check that nothing has yet been sent
|
||||||
ASSERT_from_queueSend_SIZE(0);
|
ASSERT_from_dataOut_SIZE(0);
|
||||||
|
|
||||||
for (FwIndexType index = 0; index < ComQueue::TOTAL_PORT_COUNT; index++) {
|
for (FwIndexType index = 0; index < ComQueue::TOTAL_PORT_COUNT; index++) {
|
||||||
U8 orderKey;
|
U8 orderKey;
|
||||||
U32 previousSize = fromPortHistory_queueSend->size();
|
U32 previousSize = fromPortHistory_dataOut->size();
|
||||||
emitOne();
|
emitOne();
|
||||||
ASSERT_EQ(fromPortHistory_queueSend->size(), (index + 1));
|
ASSERT_EQ(fromPortHistory_dataOut->size(), (index + 1));
|
||||||
// Check that the size changed by exactly one
|
// Check that the size changed by exactly one
|
||||||
ASSERT_EQ(fromPortHistory_queueSend->size(), (previousSize + 1));
|
ASSERT_EQ(fromPortHistory_dataOut->size(), (previousSize + 1));
|
||||||
|
|
||||||
orderKey = fromPortHistory_queueSend->at(index).data.getData()[0];
|
orderKey = fromPortHistory_dataOut->at(index).data.getData()[0];
|
||||||
ASSERT_EQ(orderKey, index);
|
ASSERT_EQ(orderKey, index);
|
||||||
}
|
}
|
||||||
clearFromPortHistory();
|
clearFromPortHistory();
|
||||||
@ -295,7 +295,7 @@ void ComQueueTester ::testReadyFirst() {
|
|||||||
invoke_to_comPacketQueueIn(portNum, comBuffer, 0);
|
invoke_to_comPacketQueueIn(portNum, comBuffer, 0);
|
||||||
dispatchAll();
|
dispatchAll();
|
||||||
|
|
||||||
Fw::Buffer emittedBuffer = this->fromPortHistory_queueSend->at(portNum).data;
|
Fw::Buffer emittedBuffer = this->fromPortHistory_dataOut->at(portNum).data;
|
||||||
ASSERT_EQ(emittedBuffer.getSize(), comBuffer.getBuffLength());
|
ASSERT_EQ(emittedBuffer.getSize(), comBuffer.getBuffLength());
|
||||||
for (FwSizeType i = 0; i < emittedBuffer.getSize(); i++) {
|
for (FwSizeType i = 0; i < emittedBuffer.getSize(); i++) {
|
||||||
ASSERT_EQ(emittedBuffer.getData()[i], comBuffer.getBuffAddr()[i]);
|
ASSERT_EQ(emittedBuffer.getData()[i], comBuffer.getBuffAddr()[i]);
|
||||||
@ -307,7 +307,7 @@ void ComQueueTester ::testReadyFirst() {
|
|||||||
emitOne();
|
emitOne();
|
||||||
invoke_to_bufferQueueIn(portNum, buffer);
|
invoke_to_bufferQueueIn(portNum, buffer);
|
||||||
dispatchAll();
|
dispatchAll();
|
||||||
Fw::Buffer emittedBuffer = this->fromPortHistory_queueSend->at(portNum).data;
|
Fw::Buffer emittedBuffer = this->fromPortHistory_dataOut->at(portNum).data;
|
||||||
ASSERT_EQ(emittedBuffer.getSize(), buffer.getSize());
|
ASSERT_EQ(emittedBuffer.getSize(), buffer.getSize());
|
||||||
for (FwSizeType i = 0; i < buffer.getSize(); i++) {
|
for (FwSizeType i = 0; i < buffer.getSize(); i++) {
|
||||||
ASSERT_EQ(buffer.getData()[i], emittedBuffer.getData()[i]);
|
ASSERT_EQ(buffer.getData()[i], emittedBuffer.getData()[i]);
|
||||||
@ -328,7 +328,7 @@ void ComQueueTester ::testContextData() {
|
|||||||
emitOne();
|
emitOne();
|
||||||
// Currently, the APID is set to the queue index, which is the same as the port number for COM ports
|
// Currently, the APID is set to the queue index, which is the same as the port number for COM ports
|
||||||
FwIndexType expectedApid = portNum;
|
FwIndexType expectedApid = portNum;
|
||||||
auto emittedContext = this->fromPortHistory_queueSend->at(portNum).context;
|
auto emittedContext = this->fromPortHistory_dataOut->at(portNum).context;
|
||||||
ASSERT_EQ(expectedApid, emittedContext.getcomQueueIndex());
|
ASSERT_EQ(expectedApid, emittedContext.getcomQueueIndex());
|
||||||
}
|
}
|
||||||
clearFromPortHistory();
|
clearFromPortHistory();
|
||||||
@ -338,7 +338,7 @@ void ComQueueTester ::testContextData() {
|
|||||||
emitOne();
|
emitOne();
|
||||||
// APID is queue index, which is COM_PORT_COUNT + portNum for BUFFER ports
|
// APID is queue index, which is COM_PORT_COUNT + portNum for BUFFER ports
|
||||||
FwIndexType expectedApid = portNum + ComQueue::COM_PORT_COUNT;
|
FwIndexType expectedApid = portNum + ComQueue::COM_PORT_COUNT;
|
||||||
auto emittedContext = this->fromPortHistory_queueSend->at(portNum).context;
|
auto emittedContext = this->fromPortHistory_dataOut->at(portNum).context;
|
||||||
ASSERT_EQ(expectedApid, emittedContext.getcomQueueIndex());
|
ASSERT_EQ(expectedApid, emittedContext.getcomQueueIndex());
|
||||||
}
|
}
|
||||||
clearFromPortHistory();
|
clearFromPortHistory();
|
||||||
@ -354,7 +354,7 @@ void ComQueueTester ::testBufferQueueReturn() {
|
|||||||
for(FwIndexType portNum = 0; portNum < ComQueue::TOTAL_PORT_COUNT; portNum++){
|
for(FwIndexType portNum = 0; portNum < ComQueue::TOTAL_PORT_COUNT; portNum++){
|
||||||
clearFromPortHistory();
|
clearFromPortHistory();
|
||||||
context.setcomQueueIndex(portNum);
|
context.setcomQueueIndex(portNum);
|
||||||
invoke_to_bufferReturnIn(0, buffer, context);
|
invoke_to_dataReturnIn(0, buffer, context);
|
||||||
// APIDs that correspond to an buffer originating from a Fw.Com port
|
// APIDs that correspond to an buffer originating from a Fw.Com port
|
||||||
// do no get deallocated – APIDs that correspond to a Fw.Buffer do
|
// do no get deallocated – APIDs that correspond to a Fw.Buffer do
|
||||||
if (portNum < ComQueue::COM_PORT_COUNT) {
|
if (portNum < ComQueue::COM_PORT_COUNT) {
|
||||||
|
|||||||
@ -23,10 +23,10 @@ ComStub::~ComStub() {}
|
|||||||
// Handler implementations for user-defined typed input ports
|
// Handler implementations for user-defined typed input ports
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
void ComStub::comDataIn_handler(const FwIndexType portNum, Fw::Buffer& sendBuffer, const ComCfg::FrameContext& context) {
|
void ComStub::dataIn_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
|
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->m_storedContext = context; // Store the context of the current message
|
||||||
this->drvDataOut_out(0, sendBuffer);
|
this->drvSendOut_out(0, sendBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComStub::drvConnected_handler(const FwIndexType portNum) {
|
void ComStub::drvConnected_handler(const FwIndexType portNum) {
|
||||||
@ -37,15 +37,16 @@ void ComStub::drvConnected_handler(const FwIndexType portNum) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComStub::drvDataIn_handler(const FwIndexType portNum,
|
void ComStub::drvReceiveIn_handler(const FwIndexType portNum,
|
||||||
Fw::Buffer& recvBuffer,
|
Fw::Buffer& recvBuffer,
|
||||||
const Drv::ByteStreamStatus& recvStatus) {
|
const Drv::ByteStreamStatus& recvStatus) {
|
||||||
if (recvStatus.e == Drv::ByteStreamStatus::OP_OK) {
|
if (recvStatus.e == Drv::ByteStreamStatus::OP_OK) {
|
||||||
this->comDataOut_out(0, recvBuffer);
|
ComCfg::FrameContext emptyContext; // ComStub knows nothing about the received bytes, so use an empty context
|
||||||
|
this->dataOut_out(0, recvBuffer, emptyContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComStub ::dataReturnIn_handler(FwIndexType portNum, //!< The port number
|
void ComStub ::drvSendReturnIn_handler(FwIndexType portNum, //!< The port number
|
||||||
Fw::Buffer& fwBuffer, //!< The buffer
|
Fw::Buffer& fwBuffer, //!< The buffer
|
||||||
const Drv::ByteStreamStatus& sendStatus) {
|
const Drv::ByteStreamStatus& sendStatus) {
|
||||||
if (sendStatus != Drv::ByteStreamStatus::SEND_RETRY) {
|
if (sendStatus != Drv::ByteStreamStatus::SEND_RETRY) {
|
||||||
@ -60,7 +61,7 @@ void ComStub ::dataReturnIn_handler(FwIndexType portNum, //!< The port number
|
|||||||
if (this->m_retry_count < this->RETRY_LIMIT) {
|
if (this->m_retry_count < this->RETRY_LIMIT) {
|
||||||
// If we have not yet retried more than the retry limit, attempt to retry
|
// If we have not yet retried more than the retry limit, attempt to retry
|
||||||
this->m_retry_count++;
|
this->m_retry_count++;
|
||||||
this->drvDataOut_out(0, fwBuffer);
|
this->drvSendOut_out(0, fwBuffer);
|
||||||
} else {
|
} else {
|
||||||
// If retried too many times, return buffer and log failure
|
// If retried too many times, return buffer and log failure
|
||||||
this->dataReturnOut_out(0, fwBuffer, this->m_storedContext);
|
this->dataReturnOut_out(0, fwBuffer, this->m_storedContext);
|
||||||
@ -70,4 +71,8 @@ void ComStub ::dataReturnIn_handler(FwIndexType portNum, //!< The port number
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ComStub ::dataReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer, const ComCfg::FrameContext& context) {
|
||||||
|
this->drvReceiveReturnOut_out(0, fwBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
} // end namespace Svc
|
} // end namespace Svc
|
||||||
|
|||||||
@ -10,14 +10,17 @@ module Svc {
|
|||||||
@ Ready signal when driver is connected
|
@ Ready signal when driver is connected
|
||||||
sync input port drvConnected: Drv.ByteStreamReady
|
sync input port drvConnected: Drv.ByteStreamReady
|
||||||
|
|
||||||
@ Receive (read) data from driver. This gets forwarded to comDataOut
|
@ Receive (read) data from driver. This gets forwarded to dataOut
|
||||||
sync input port drvDataIn: Drv.ByteStreamData
|
sync input port drvReceiveIn: Drv.ByteStreamData
|
||||||
|
|
||||||
@ Send (write) data to the driver. This gets invoked on comDataIn invocation
|
@ Send (write) data to the driver. This gets invoked on dataIn invocation
|
||||||
output port drvDataOut: Fw.BufferSend
|
output port drvSendOut: Fw.BufferSend
|
||||||
|
|
||||||
@ Callback from drvDataOut (retrieving status and ownership of sent buffer)
|
@ Callback from drvSendOut (retrieving status and ownership of sent buffer)
|
||||||
sync input port dataReturnIn: Drv.ByteStreamData
|
sync input port drvSendReturnIn: Drv.ByteStreamData
|
||||||
|
|
||||||
|
@ Returning ownership of buffer that came in on drvReceiveIn
|
||||||
|
output port drvReceiveReturnOut: Fw.BufferSend
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,11 +35,11 @@ class ComStub final : public ComStubComponentBase {
|
|||||||
// Handler implementations for user-defined typed input ports
|
// Handler implementations for user-defined typed input ports
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
//! Handler implementation for comDataIn
|
//! Handler implementation for dataIn
|
||||||
//!
|
//!
|
||||||
//! Comms data is coming in meaning there is a request for ComStub to send data on the wire
|
//! 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)
|
//! 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*/
|
void dataIn_handler(const FwIndexType portNum, /*!< The port number*/
|
||||||
Fw::Buffer& sendBuffer,
|
Fw::Buffer& sendBuffer,
|
||||||
const ComCfg::FrameContext& context) override;
|
const ComCfg::FrameContext& context) override;
|
||||||
|
|
||||||
@ -47,18 +47,25 @@ class ComStub final : public ComStubComponentBase {
|
|||||||
//!
|
//!
|
||||||
void drvConnected_handler(const FwIndexType portNum) override;
|
void drvConnected_handler(const FwIndexType portNum) override;
|
||||||
|
|
||||||
//! Handler implementation for drvDataIn
|
//! Handler implementation for drvReceiveIn
|
||||||
//!
|
//!
|
||||||
//! Data is coming in from the driver (meaning it has been read from the wire).
|
//! Data is coming in from the driver (meaning it has been read from the wire).
|
||||||
//! ComStub forwards this to the comDataOut port
|
//! ComStub forwards this to the dataOut port
|
||||||
void drvDataIn_handler(const FwIndexType portNum,
|
void drvReceiveIn_handler(const FwIndexType portNum,
|
||||||
/*!< The port number*/ Fw::Buffer& recvBuffer,
|
/*!< The port number*/ Fw::Buffer& recvBuffer,
|
||||||
const Drv::ByteStreamStatus& recvStatus) override;
|
const Drv::ByteStreamStatus& recvStatus) override;
|
||||||
|
|
||||||
//! Handler implementation for dataReturnIn
|
//! Handler implementation for dataReturnIn
|
||||||
//!
|
//!
|
||||||
|
//! Port receiving back ownership of buffer sent out on dataOut
|
||||||
|
void dataReturnIn_handler(FwIndexType portNum, //!< The port number
|
||||||
|
Fw::Buffer& fwBuffer, //!< The buffer
|
||||||
|
const ComCfg::FrameContext& context) override;
|
||||||
|
|
||||||
|
//! Handler implementation for drvSendReturnIn
|
||||||
|
//!
|
||||||
//! Buffer ownership and status returning from a Driver "send" operation
|
//! Buffer ownership and status returning from a Driver "send" operation
|
||||||
void dataReturnIn_handler(FwIndexType portNum, //!< The port number
|
void drvSendReturnIn_handler(FwIndexType portNum, //!< The port number
|
||||||
Fw::Buffer& fwBuffer, //!< The buffer
|
Fw::Buffer& fwBuffer, //!< The buffer
|
||||||
const Drv::ByteStreamStatus& recvStatus) override;
|
const Drv::ByteStreamStatus& recvStatus) override;
|
||||||
|
|
||||||
|
|||||||
@ -55,32 +55,37 @@ be useful
|
|||||||
|
|
||||||
| Kind | Name | Port Type | Usage |
|
| Kind | Name | Port Type | Usage |
|
||||||
|--------------|----------------|-----------------------|-----------------------------------------------------------------------------------|
|
|--------------|----------------|-----------------------|-----------------------------------------------------------------------------------|
|
||||||
| `sync input` | `comDataIn` | `Svc.ComDataWithContext` | Port receiving `Fw::Buffer`s for transmission out `drvDataOut` |
|
| `sync input` | `dataIn` | `Svc.ComDataWithContext` | Port receiving `Fw::Buffer`s for transmission out `drvSendOut` |
|
||||||
| `output` | `comStatusOut` | `Svc.ComStatus` | Port indicating success or failure to attached `Svc::ComQueue` |
|
| `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` |
|
| `output` | `dataOut` | `Svc.ComDataWithContext` | Port providing received `Fw::Buffers` to the broader application (typically a Deframer) |
|
||||||
|
| `output` | `dataReturnOut` | `Svc.ComDataWithContext` | Port returning ownership of data that came in on `dataIn` |
|
||||||
|
| `sync input` | `dataReturnIn` | `Svc.ComDataWithContext` | Port receiving back ownership of buffer sent out on `dataOut` |
|
||||||
|
|
||||||
**Byte Stream Driver Model Ports**
|
**Byte Stream Driver Model Ports**
|
||||||
|
|
||||||
| Kind | Name | Port Type | Usage |
|
| Kind | Name | Port Type | Usage |
|
||||||
|--------------|----------------|-----------------------|-----------------------------------------------------------------------------------|
|
|--------------|----------------|-----------------------|-----------------------------------------------------------------------------------|
|
||||||
| `sync input` | `drvConnected` | `Drv.ByteStreamReady` | Port called when the underlying driver has connected |
|
| `sync input` | `drvConnected` | `Drv.ByteStreamReady` | Port called when the underlying driver has connected |
|
||||||
| `sync input` | `drvDataIn` | `Drv.ByteStreamRecv` | Port receiving `Fw::Buffers` from underlying communications bus driver |
|
| `sync input` | `drvReceiveIn` | `Drv.ByteStreamRecv` | Port receiving `Fw::Buffers` from underlying communications bus driver |
|
||||||
| `output` | `drvDataOut` | `Drv.ByteStreamSend` | Port providing received `Fw::Buffers` to the underlying communications bus driver |
|
| `output` | `drvSendOut` | `Drv.ByteStreamSend` | Port providing received `Fw::Buffers` to the underlying communications bus driver |
|
||||||
|
| `sync input` | `drvSendReturnIn` | `Drv.ByteStreamData` | Port receiving status and ownership of buffer sent out on `drvSendOut` |
|
||||||
|
| `output` | `drvReceiveReturnOut` | `Fw.BufferSend` | Port returning ownership of buffer that came in on `drvReceiveIn` |
|
||||||
|
|
||||||
|
|
||||||
### 4.2. State, Configuration, and Runtime Setup
|
### 4.2. State, Configuration, and Runtime Setup
|
||||||
|
|
||||||
`Svc::ComStub` has only stores a boolean `m_reinitialize` indicating when it should send `Fw::Success::SUCCESS` in
|
`Svc::ComStub` stores a boolean `m_reinitialize` indicating when it should send `Fw::Success::SUCCESS` in
|
||||||
response to a driver reconnection event. This is to implement the Communication Adapter Protocol of a
|
response to a driver reconnection event. This is to implement the Communication Adapter Protocol of a
|
||||||
[communication adapter interface](../../../docs/reference/communication-adapter-interface.md).
|
[communication adapter interface](../../../docs/reference/communication-adapter-interface.md). It also keeps
|
||||||
|
track of a `m_retry_count` to limit the number of retries on an attempt to send data.
|
||||||
|
|
||||||
### 4.3. Port Handlers
|
### 4.3. Port Handlers
|
||||||
|
|
||||||
#### 4.3.1 comDataIn
|
#### 4.3.1 dataIn
|
||||||
|
|
||||||
The `comDataIn` port handler receives an `Fw::Buffer` from the F´ system for transmission to the ground. Typically, it
|
The `dataIn` 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
|
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,
|
`Fw::Buffer` directly to the `drvSendOut` port. It will retry when that port responds with a `RETRY` request. Otherwise,
|
||||||
the `comStatusOut` 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.
|
asserts.
|
||||||
|
|
||||||
@ -89,7 +94,7 @@ asserts.
|
|||||||
This port receives the connected signal from the driver and responds with exactly one `READY` invocation to the
|
This port receives the connected signal from the driver and responds with exactly one `READY` invocation to the
|
||||||
`comStatusOut` 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
|
#### 4.3.1 drvReceiveIn
|
||||||
|
|
||||||
The `drvDataIn` handler receives data read from the driver and supplies it out the `comDataOut` port. It is usually
|
The `drvReceiveIn` handler receives data read from the driver and supplies it out the `dataOut` port. It is usually
|
||||||
connected to the `Svc::Deframer` component
|
connected to the `Svc::Deframer` component
|
||||||
|
|||||||
@ -20,6 +20,11 @@ TEST(Nominal, Fail) {
|
|||||||
tester.test_fail();
|
tester.test_fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Nominal, BufferReturn) {
|
||||||
|
Svc::ComStubTester tester;
|
||||||
|
tester.test_buffer_return();
|
||||||
|
}
|
||||||
|
|
||||||
TEST(OffNominal, Retry) {
|
TEST(OffNominal, Retry) {
|
||||||
Svc::ComStubTester tester;
|
Svc::ComStubTester tester;
|
||||||
tester.test_retry();
|
tester.test_retry();
|
||||||
|
|||||||
@ -55,14 +55,15 @@ void ComStubTester ::test_basic() {
|
|||||||
this->fill(buffer);
|
this->fill(buffer);
|
||||||
|
|
||||||
// Downlink
|
// Downlink
|
||||||
invoke_to_comDataIn(0, buffer, context);
|
invoke_to_dataIn(0, buffer, context);
|
||||||
ASSERT_from_drvDataOut_SIZE(1);
|
ASSERT_from_drvSendOut_SIZE(1);
|
||||||
ASSERT_from_drvDataOut(0, buffer);
|
ASSERT_from_drvSendOut(0, buffer);
|
||||||
|
|
||||||
// Uplink
|
// Uplink
|
||||||
invoke_to_drvDataIn(0, buffer, Drv::ByteStreamStatus::OP_OK);
|
ComCfg::FrameContext emptyContext;
|
||||||
ASSERT_from_comDataOut_SIZE(1);
|
invoke_to_drvReceiveIn(0, buffer, Drv::ByteStreamStatus::OP_OK);
|
||||||
ASSERT_from_comDataOut(0, buffer);
|
ASSERT_from_dataOut_SIZE(1);
|
||||||
|
ASSERT_from_dataOut(0, buffer, emptyContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComStubTester ::test_fail() {
|
void ComStubTester ::test_fail() {
|
||||||
@ -75,13 +76,13 @@ void ComStubTester ::test_fail() {
|
|||||||
ComCfg::FrameContext context;
|
ComCfg::FrameContext context;
|
||||||
|
|
||||||
// Downlink
|
// Downlink
|
||||||
invoke_to_comDataIn(0, buffer, context);
|
invoke_to_dataIn(0, buffer, context);
|
||||||
ASSERT_from_drvDataOut_SIZE(1);
|
ASSERT_from_drvSendOut_SIZE(1);
|
||||||
ASSERT_from_drvDataOut(0, buffer);
|
ASSERT_from_drvSendOut(0, buffer);
|
||||||
|
|
||||||
// Uplink
|
// Uplink
|
||||||
invoke_to_drvDataIn(0, buffer, Drv::ByteStreamStatus::OTHER_ERROR);
|
invoke_to_drvReceiveIn(0, buffer, Drv::ByteStreamStatus::OTHER_ERROR);
|
||||||
ASSERT_from_comDataOut_SIZE(0); // receiving failure should not send anything
|
ASSERT_from_dataOut_SIZE(0); // receiving failure should not send anything
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComStubTester ::test_retry() {
|
void ComStubTester ::test_retry() {
|
||||||
@ -99,16 +100,16 @@ void ComStubTester ::test_retry() {
|
|||||||
}
|
}
|
||||||
// Retrying for as many times as the RETRY_LIMIT should be ok
|
// Retrying for as many times as the RETRY_LIMIT should be ok
|
||||||
for (FwIndexType i = 0; i < this->component.RETRY_LIMIT; i++) {
|
for (FwIndexType i = 0; i < this->component.RETRY_LIMIT; i++) {
|
||||||
invoke_to_dataReturnIn(0, buffers[i], Drv::ByteStreamStatus::SEND_RETRY);
|
invoke_to_drvSendReturnIn(0, buffers[i], Drv::ByteStreamStatus::SEND_RETRY);
|
||||||
// Test we have indeed retried (data sent on drvDataOut)
|
// Test we have indeed retried (data sent on drvSendOut)
|
||||||
ASSERT_from_drvDataOut_SIZE(static_cast<U32>(i + 1));
|
ASSERT_from_drvSendOut_SIZE(static_cast<U32>(i + 1));
|
||||||
ASSERT_from_drvDataOut(static_cast<U32>(i), buffers[i]);
|
ASSERT_from_drvSendOut(static_cast<U32>(i), buffers[i]);
|
||||||
}
|
}
|
||||||
ASSERT_from_drvDataOut_SIZE(static_cast<U32>(this->component.RETRY_LIMIT));
|
ASSERT_from_drvSendOut_SIZE(static_cast<U32>(this->component.RETRY_LIMIT));
|
||||||
ASSERT_EQ(this->component.m_retry_count, 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
|
// Retry one more time should block from retrying and reset retry count
|
||||||
invoke_to_dataReturnIn(0, buffers[MAX_ITERS - 1], Drv::ByteStreamStatus::SEND_RETRY);
|
invoke_to_drvSendReturnIn(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_drvSendOut_SIZE(static_cast<U32>(this->component.RETRY_LIMIT)); // no drvSendOut sent when SEND_RETRY
|
||||||
ASSERT_from_dataReturnOut_SIZE(1); // buffer ownership was returned
|
ASSERT_from_dataReturnOut_SIZE(1); // buffer ownership was returned
|
||||||
ASSERT_EQ(this->component.m_retry_count, 0);
|
ASSERT_EQ(this->component.m_retry_count, 0);
|
||||||
}
|
}
|
||||||
@ -116,7 +117,7 @@ void ComStubTester ::test_retry() {
|
|||||||
void ComStubTester ::test_retry_reset() {
|
void ComStubTester ::test_retry_reset() {
|
||||||
this->test_initial();
|
this->test_initial();
|
||||||
FwIndexType MAX_ITERS = static_cast<FwIndexType>(this->component.RETRY_LIMIT + 1);
|
FwIndexType MAX_ITERS = static_cast<FwIndexType>(this->component.RETRY_LIMIT + 1);
|
||||||
U32 expected_drvDataOut_count = 0;
|
U32 expected_drvSendOut_count = 0;
|
||||||
|
|
||||||
// Make small individual buffers for testing
|
// Make small individual buffers for testing
|
||||||
U8 storage[MAX_ITERS][8];
|
U8 storage[MAX_ITERS][8];
|
||||||
@ -130,40 +131,51 @@ void ComStubTester ::test_retry_reset() {
|
|||||||
|
|
||||||
// Retrying for as many times as the RETRY_LIMIT should be ok
|
// Retrying for as many times as the RETRY_LIMIT should be ok
|
||||||
for (FwIndexType i = 0; i < this->component.RETRY_LIMIT; i++) {
|
for (FwIndexType i = 0; i < this->component.RETRY_LIMIT; i++) {
|
||||||
invoke_to_dataReturnIn(0, buffers[i], Drv::ByteStreamStatus::SEND_RETRY);
|
invoke_to_drvSendReturnIn(0, buffers[i], Drv::ByteStreamStatus::SEND_RETRY);
|
||||||
ASSERT_from_drvDataOut(expected_drvDataOut_count, buffers[i]);
|
ASSERT_from_drvSendOut(expected_drvSendOut_count, buffers[i]);
|
||||||
expected_drvDataOut_count++; // trick: increment now to use as index prior and size after
|
expected_drvSendOut_count++; // trick: increment now to use as index prior and size after
|
||||||
ASSERT_from_drvDataOut_SIZE(expected_drvDataOut_count);
|
ASSERT_from_drvSendOut_SIZE(expected_drvSendOut_count);
|
||||||
}
|
}
|
||||||
// Now, we receive a OP_OK, which should not retry (drvDataOut should not be called) and reset the retry count
|
// Now, we receive a OP_OK, which should not retry (drvSendOut should not be called) and reset the retry count
|
||||||
ASSERT_from_drvDataOut_SIZE(expected_drvDataOut_count); // no drvDataOut sent when OP_OK
|
ASSERT_from_drvSendOut_SIZE(expected_drvSendOut_count); // no drvSendOut sent when OP_OK
|
||||||
invoke_to_dataReturnIn(0, buffers[0], Drv::ByteStreamStatus::OP_OK);
|
invoke_to_drvSendReturnIn(0, buffers[0], Drv::ByteStreamStatus::OP_OK);
|
||||||
ASSERT_from_drvDataOut_SIZE(expected_drvDataOut_count); // no drvDataOut sent when OP_OK
|
ASSERT_from_drvSendOut_SIZE(expected_drvSendOut_count); // no drvSendOut sent when OP_OK
|
||||||
// Now that retry count is reset, we can retry again without a problem
|
// Now that retry count is reset, we can retry again without a problem
|
||||||
for (FwIndexType i = 0; i < this->component.RETRY_LIMIT; i++) {
|
for (FwIndexType i = 0; i < this->component.RETRY_LIMIT; i++) {
|
||||||
invoke_to_dataReturnIn(0, buffers[i], Drv::ByteStreamStatus::SEND_RETRY);
|
invoke_to_drvSendReturnIn(0, buffers[i], Drv::ByteStreamStatus::SEND_RETRY);
|
||||||
ASSERT_from_drvDataOut(expected_drvDataOut_count, buffers[i]);
|
ASSERT_from_drvSendOut(expected_drvSendOut_count, buffers[i]);
|
||||||
expected_drvDataOut_count++; // trick: increment now to use as index prior and size after
|
expected_drvSendOut_count++; // trick: increment now to use as index prior and size after
|
||||||
ASSERT_from_drvDataOut_SIZE(expected_drvDataOut_count);
|
ASSERT_from_drvSendOut_SIZE(expected_drvSendOut_count);
|
||||||
}
|
}
|
||||||
ASSERT_from_drvDataOut_SIZE(expected_drvDataOut_count); // no drvDataOut sent when OP_OK
|
ASSERT_from_drvSendOut_SIZE(expected_drvSendOut_count); // no drvSendOut sent when OP_OK
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComStubTester ::test_buffer_return() {
|
||||||
|
U8 data[1];
|
||||||
|
Fw::Buffer buffer(data, sizeof(data));
|
||||||
|
ComCfg::FrameContext context;
|
||||||
|
this->invoke_to_dataReturnIn(0, buffer, context);
|
||||||
|
ASSERT_from_drvReceiveReturnOut_SIZE(1); // incoming buffer should be returned
|
||||||
|
ASSERT_EQ(this->fromPortHistory_drvReceiveReturnOut->at(0).fwBuffer.getData(), data);
|
||||||
|
ASSERT_EQ(this->fromPortHistory_drvReceiveReturnOut->at(0).fwBuffer.getSize(), sizeof(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Handlers for typed from ports
|
// Handlers for typed from ports
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
void ComStubTester ::from_comDataOut_handler(const FwIndexType portNum,
|
void ComStubTester ::from_dataOut_handler(const FwIndexType portNum,
|
||||||
Fw::Buffer& recvBuffer) {
|
Fw::Buffer& recvBuffer,
|
||||||
this->pushFromPortEntry_comDataOut(recvBuffer);
|
const ComCfg::FrameContext& context) {
|
||||||
|
this->pushFromPortEntry_dataOut(recvBuffer, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComStubTester ::from_comStatusOut_handler(const FwIndexType portNum, Fw::Success& condition) {
|
void ComStubTester ::from_comStatusOut_handler(const FwIndexType portNum, Fw::Success& condition) {
|
||||||
this->pushFromPortEntry_comStatusOut(condition);
|
this->pushFromPortEntry_comStatusOut(condition);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComStubTester ::from_drvDataOut_handler(const FwIndexType portNum, Fw::Buffer& sendBuffer) {
|
void ComStubTester ::from_drvSendOut_handler(const FwIndexType portNum, Fw::Buffer& sendBuffer) {
|
||||||
this->pushFromPortEntry_drvDataOut(sendBuffer);
|
this->pushFromPortEntry_drvSendOut(sendBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -60,15 +60,20 @@ class ComStubTester : public ComStubGTestBase {
|
|||||||
//!
|
//!
|
||||||
void test_retry_reset();
|
void test_retry_reset();
|
||||||
|
|
||||||
|
//! Tests buffer is returned
|
||||||
|
//!
|
||||||
|
void test_buffer_return();
|
||||||
private:
|
private:
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Handlers for typed from ports
|
// Handlers for typed from ports
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
//! Handler for from_comDataOut
|
//! Handler for from_dataOut
|
||||||
//!
|
//!
|
||||||
void from_comDataOut_handler(const FwIndexType portNum, //!< The port number
|
void from_dataOut_handler(const FwIndexType portNum, //!< The port number
|
||||||
Fw::Buffer& recvBuffer);
|
Fw::Buffer& recvBuffer,
|
||||||
|
const ComCfg::FrameContext& context //!< The context
|
||||||
|
);
|
||||||
|
|
||||||
//! Handler for from_comStatusOut
|
//! Handler for from_comStatusOut
|
||||||
//!
|
//!
|
||||||
@ -76,9 +81,9 @@ class ComStubTester : public ComStubGTestBase {
|
|||||||
Fw::Success& condition //!< Status of communication state
|
Fw::Success& condition //!< Status of communication state
|
||||||
);
|
);
|
||||||
|
|
||||||
//! Handler for from_drvDataOut
|
//! Handler for from_drvSendOut
|
||||||
//!
|
//!
|
||||||
void from_drvDataOut_handler(const FwIndexType portNum, //!< The port number
|
void from_drvSendOut_handler(const FwIndexType portNum, //!< The port number
|
||||||
Fw::Buffer& sendBuffer);
|
Fw::Buffer& sendBuffer);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@ -25,11 +25,11 @@ FprimeDeframer ::~FprimeDeframer() {}
|
|||||||
// Handler implementations for user-defined typed input ports
|
// Handler implementations for user-defined typed input ports
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
void FprimeDeframer ::framedIn_handler(FwIndexType portNum, Fw::Buffer& data, const ComCfg::FrameContext& context) {
|
void FprimeDeframer ::dataIn_handler(FwIndexType portNum, Fw::Buffer& data, const ComCfg::FrameContext& context) {
|
||||||
if (data.getSize() < FprimeProtocol::FrameHeader::SERIALIZED_SIZE + FprimeProtocol::FrameTrailer::SERIALIZED_SIZE) {
|
if (data.getSize() < FprimeProtocol::FrameHeader::SERIALIZED_SIZE + FprimeProtocol::FrameTrailer::SERIALIZED_SIZE) {
|
||||||
// Incoming buffer is not long enough to contain a valid frame (header+trailer)
|
// Incoming buffer is not long enough to contain a valid frame (header+trailer)
|
||||||
this->log_WARNING_HI_InvalidBufferReceived();
|
this->log_WARNING_HI_InvalidBufferReceived();
|
||||||
this->bufferDeallocate_out(0, data); // drop the frame
|
this->dataReturnOut_out(0, data, context); // drop the frame
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ void FprimeDeframer ::framedIn_handler(FwIndexType portNum, Fw::Buffer& data, co
|
|||||||
const FprimeProtocol::FrameHeader defaultValue;
|
const FprimeProtocol::FrameHeader defaultValue;
|
||||||
if (header.getstartWord() != defaultValue.getstartWord()) {
|
if (header.getstartWord() != defaultValue.getstartWord()) {
|
||||||
this->log_WARNING_HI_InvalidStartWord();
|
this->log_WARNING_HI_InvalidStartWord();
|
||||||
this->bufferDeallocate_out(0, data);
|
this->dataReturnOut_out(0, data, context); // drop the frame
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// We expect the frame size to be size of header + body (of size specified in header) + trailer
|
// We expect the frame size to be size of header + body (of size specified in header) + trailer
|
||||||
@ -54,7 +54,7 @@ void FprimeDeframer ::framedIn_handler(FwIndexType portNum, Fw::Buffer& data, co
|
|||||||
FprimeProtocol::FrameTrailer::SERIALIZED_SIZE;
|
FprimeProtocol::FrameTrailer::SERIALIZED_SIZE;
|
||||||
if (data.getSize() < expectedFrameSize) {
|
if (data.getSize() < expectedFrameSize) {
|
||||||
this->log_WARNING_HI_InvalidLengthReceived();
|
this->log_WARNING_HI_InvalidLengthReceived();
|
||||||
this->bufferDeallocate_out(0, data);
|
this->dataReturnOut_out(0, data, context); // drop the frame
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ void FprimeDeframer ::framedIn_handler(FwIndexType portNum, Fw::Buffer& data, co
|
|||||||
// Check that the CRC in the trailer of the frame matches the computed CRC
|
// Check that the CRC in the trailer of the frame matches the computed CRC
|
||||||
if (trailer.getcrcField() != computedCrc.asBigEndianU32()) {
|
if (trailer.getcrcField() != computedCrc.asBigEndianU32()) {
|
||||||
this->log_WARNING_HI_InvalidChecksum();
|
this->log_WARNING_HI_InvalidChecksum();
|
||||||
this->bufferDeallocate_out(0, data);
|
this->dataReturnOut_out(0, data, context); // drop the frame
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +88,12 @@ void FprimeDeframer ::framedIn_handler(FwIndexType portNum, Fw::Buffer& data, co
|
|||||||
data.setSize(data.getSize() - FprimeProtocol::FrameHeader::SERIALIZED_SIZE -
|
data.setSize(data.getSize() - FprimeProtocol::FrameHeader::SERIALIZED_SIZE -
|
||||||
FprimeProtocol::FrameTrailer::SERIALIZED_SIZE);
|
FprimeProtocol::FrameTrailer::SERIALIZED_SIZE);
|
||||||
// Emit the deframed data
|
// Emit the deframed data
|
||||||
this->deframedOut_out(0, data, context);
|
this->dataOut_out(0, data, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FprimeDeframer ::dataReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer, const ComCfg::FrameContext& context) {
|
||||||
|
this->dataReturnOut_out(0, fwBuffer, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace Svc
|
} // namespace Svc
|
||||||
|
|||||||
@ -10,9 +10,6 @@ module Svc {
|
|||||||
|
|
||||||
include "../Interfaces/DeframerInterface.fppi"
|
include "../Interfaces/DeframerInterface.fppi"
|
||||||
|
|
||||||
@ Port for deallocating dropped frames
|
|
||||||
output port bufferDeallocate: Fw.BufferSend
|
|
||||||
|
|
||||||
@ An invalid frame was received (too short to be a frame)
|
@ An invalid frame was received (too short to be a frame)
|
||||||
event InvalidBufferReceived \
|
event InvalidBufferReceived \
|
||||||
severity warning high \
|
severity warning high \
|
||||||
|
|||||||
@ -36,9 +36,18 @@ class FprimeDeframer final : public FprimeDeframerComponentBase {
|
|||||||
//!
|
//!
|
||||||
//! Port to receive framed data. The handler will strip the header and trailer from the frame
|
//! Port to receive framed data. The handler will strip the header and trailer from the frame
|
||||||
//! and pass the deframed data to the deframed output port.
|
//! and pass the deframed data to the deframed output port.
|
||||||
void framedIn_handler(FwIndexType portNum, //!< The port number
|
void dataIn_handler(FwIndexType portNum, //!< The port number
|
||||||
Fw::Buffer& data,
|
Fw::Buffer& data,
|
||||||
const ComCfg::FrameContext& context) override;
|
const ComCfg::FrameContext& context) override;
|
||||||
|
|
||||||
|
//! Handler implementation for dataReturnIn
|
||||||
|
//!
|
||||||
|
//! Port receiving back ownership of sent frame buffers
|
||||||
|
void dataReturnIn_handler(FwIndexType portNum, //!< The port number
|
||||||
|
Fw::Buffer& data, //!< The buffer
|
||||||
|
const ComCfg::FrameContext& context) override;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Svc
|
} // namespace Svc
|
||||||
|
|||||||
@ -2,15 +2,13 @@
|
|||||||
|
|
||||||
The `Svc::FprimeDeframer` component receives F´ frames on its input port, takes off the header and trailer (sometimes referred to as "footer"), and passes the encapsulated payload to a downstream component (usually the [`Svc.FprimeRouter`](../../FprimeRouter/docs/sdd.md)).
|
The `Svc::FprimeDeframer` component receives F´ frames on its input port, takes off the header and trailer (sometimes referred to as "footer"), and passes the encapsulated payload to a downstream component (usually the [`Svc.FprimeRouter`](../../FprimeRouter/docs/sdd.md)).
|
||||||
|
|
||||||
Following the [F Prime Protocol frame specification](../../FprimeProtocol/docs/sdd.md), the `Svc::FprimeDeframer` validates the passed in `Fw.Buffer` to ensure it represents a valid frame (see [Frame validation](#frame-validation)), extract the payload from the frame, and outputs the payload on the `deframedOut` output port.
|
Following the [F Prime Protocol frame specification](../../FprimeProtocol/docs/sdd.md), the `Svc::FprimeDeframer` validates the passed in `Fw.Buffer` to ensure it represents a valid frame (see [Frame validation](#frame-validation)), extract the payload from the frame, and outputs the payload on the `dataOut` output port.
|
||||||
|
|
||||||
## Internals
|
## Internals
|
||||||
|
|
||||||
The `Svc::FprimeDeframer` component is an implementation of the [DeframerInterface](../../Interfaces/DeframerInterface.fppi) for the F´ communications protocol. It receives an F´ frame (in a [Fw::Buffer](../../../Fw/Buffer/docs/sdd.md) object) on its `framedIn` input port, modifies the input buffer to remove the header and trailer, and sends it out through its `deframedOut` output port.
|
The `Svc::FprimeDeframer` component is an implementation of the [DeframerInterface](../../Interfaces/DeframerInterface.fppi) for the F´ communications protocol. It receives an F´ frame (in a [Fw::Buffer](../../../Fw/Buffer/docs/sdd.md) object) on its `dataIn` input port, modifies the input buffer to remove the header and trailer, and sends it out through its `dataOut` output port.
|
||||||
|
|
||||||
Ownership of the buffer is transferred to the component connected to the `deframedOut` output port. The input buffer is modified by subtracting the header and trailer size from the buffer's length, and offsetting the buffer's data pointer to point to the start of the packet data.
|
Ownership of the buffer is transferred to the component connected to the `dataOut` output port. The input buffer is modified by subtracting the header and trailer size from the buffer's length, and offsetting the buffer's data pointer to point to the start of the packet data.
|
||||||
|
|
||||||
The `Svc::FprimeDeframer` component does not perform any validation of the frame. It is expected that the frame is valid and well-formed. The validation should be performed by an upstream component, such as [`Svc::FrameAccumulator`](../../FrameAccumulator/docs/sdd.md).
|
|
||||||
|
|
||||||
The `Svc::FprimeDeframer` does not support deframing multiple packets in a single frame (i.e. concatenated packets) as this is not supported by the F´ communications protocol.
|
The `Svc::FprimeDeframer` does not support deframing multiple packets in a single frame (i.e. concatenated packets) as this is not supported by the F´ communications protocol.
|
||||||
|
|
||||||
@ -28,35 +26,25 @@ If any of these conditions are not met, the frame is dropped meaning no payload
|
|||||||
|
|
||||||
The `Svc::FprimeDeframer` component is used in the uplink stack of many reference F´ application such as [the tutorials source code](https://github.com/fprime-community#tutorials).
|
The `Svc::FprimeDeframer` component is used in the uplink stack of many reference F´ application such as [the tutorials source code](https://github.com/fprime-community#tutorials).
|
||||||
|
|
||||||
|
|
||||||
## Diagrams
|
## Diagrams
|
||||||
|
|
||||||
The below diagram shows a typical configuration in which the `Svc::FprimeDeframer` can be used. This is the configuration used in the [the tutorials source code](https://github.com/fprime-community#tutorials). It is receiving accumulated frames from a [Svc::FrameAccumulator](../../FrameAccumulator/docs/sdd.md) and passes packets to a [Svc::FprimeRouter](../../FprimeRouter/docs/sdd.md) for routing to other components.
|
The below diagram shows a typical configuration in which the `Svc::FprimeDeframer` can be used. This is the configuration used in the [the tutorials source code](https://github.com/fprime-community#tutorials). It is receiving accumulated frames from a [Svc::FrameAccumulator](../../FrameAccumulator/docs/sdd.md) and passes packets to a [Svc::FprimeRouter](../../FprimeRouter/docs/sdd.md) for routing to other components.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## Class Diagram
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
classDiagram
|
|
||||||
class FprimeDeframer~PassiveComponent~ {
|
|
||||||
+ void framedIn_handler(FwIndexType portNum, Fw::Buffer& data, const ComCfg::FrameContext& context)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
Requirement | Description | Rationale | Verification Method
|
Requirement | Description | Rationale | Verification Method
|
||||||
----------- | ----------- | ----------| -------------------
|
----------- | ----------- | ----------| -------------------
|
||||||
SVC-DEFRAMER-001 | `Svc::FprimeDeframer` shall extract the payload field from input buffers that represent a valid F Prime frame as specified by the [F Prime Protocol](../../FprimeProtocol/docs/sdd.md) | Deframe valid frames and extract payload | Unit test |
|
SVC-DEFRAMER-001 | `Svc::FprimeDeframer` shall extract the payload field from input buffers that represent a valid F Prime frame as specified by the [F Prime Protocol](../../FprimeProtocol/docs/sdd.md) | Deframe valid frames and extract payload | Unit test |
|
||||||
SVC-DEFRAMER-002 | `Svc::FprimeDeframer` shall deallocate input buffers that are not a valid F Prime frame as specified by the [F Prime Protocol](../../FprimeProtocol/docs/sdd.md) | Drop invalid frames | Unit test |
|
SVC-DEFRAMER-002 | `Svc::FprimeDeframer` shall return ownership of input buffers that are not a valid F Prime frame as specified by the [F Prime Protocol](../../FprimeProtocol/docs/sdd.md) | Drop invalid frames | Unit test |
|
||||||
|
|
||||||
## Port Descriptions
|
## Port Descriptions
|
||||||
|
|
||||||
| Kind | Name | Type | Description |
|
| Kind | Name | Type | Description |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `guarded input` | framedIn | `Svc.ComDataWithContext` | Receives a frame with optional context data |
|
| `guarded input` | `dataIn` | `Svc.ComDataWithContext` | Receives a frame for deframing |
|
||||||
| `output` | deframedOut | `Svc.ComDataWithContext` | Receives a frame with optional context data |
|
| `output` | `dataOut` | `Svc.ComDataWithContext` | Emits deframed data (F´ packets) |
|
||||||
| `output` | bufferDeallocate | `Fw.BufferSend` | Port for deallocating dropped frames |
|
| `sync input` | `dataReturnIn` | `Svc.ComDataWithContext` | Receives ownership of the emitted data back |
|
||||||
|
| `output` | `dataReturnOut` | `Svc.ComDataWithContext` | Returns ownership of the input buffer back to the sender |
|
||||||
|
|
||||||
|
|||||||
@ -37,6 +37,11 @@ TEST(FprimeDeframer, testIncorrectCrc) {
|
|||||||
tester.testIncorrectCrc();
|
tester.testIncorrectCrc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(FprimeDeframer, testDataReturn) {
|
||||||
|
Svc::FprimeDeframerTester tester;
|
||||||
|
tester.testDataReturn();
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
STest::Random::seed();
|
STest::Random::seed();
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
|||||||
@ -34,10 +34,10 @@ void FprimeDeframerTester ::testNominalFrame() {
|
|||||||
this->injectChecksum(data, sizeof(data));
|
this->injectChecksum(data, sizeof(data));
|
||||||
this->mockReceiveData(data, sizeof(data));
|
this->mockReceiveData(data, sizeof(data));
|
||||||
|
|
||||||
ASSERT_from_deframedOut_SIZE(1); // something emitted on deframedOut
|
ASSERT_from_dataOut_SIZE(1); // something emitted on dataOut
|
||||||
ASSERT_from_bufferDeallocate_SIZE(0); // nothing emitted on bufferDeallocate
|
ASSERT_from_dataReturnOut_SIZE(0); // nothing emitted on dataReturnOut
|
||||||
// Assert that the data that was emitted on deframedOut is equal to Data field above (randomByte)
|
// Assert that the data that was emitted on dataOut is equal to Data field above (randomByte)
|
||||||
ASSERT_EQ(this->fromPortHistory_deframedOut->at(0).data.getData()[0], randomByte);
|
ASSERT_EQ(this->fromPortHistory_dataOut->at(0).data.getData()[0], randomByte);
|
||||||
ASSERT_EVENTS_SIZE(0); // no events emitted
|
ASSERT_EVENTS_SIZE(0); // no events emitted
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,8 +48,8 @@ void FprimeDeframerTester ::testIncorrectLengthToken() {
|
|||||||
this->injectChecksum(data, sizeof(data));
|
this->injectChecksum(data, sizeof(data));
|
||||||
this->mockReceiveData(data, sizeof(data));
|
this->mockReceiveData(data, sizeof(data));
|
||||||
|
|
||||||
ASSERT_from_deframedOut_SIZE(0); // nothing emitted on deframedOut
|
ASSERT_from_dataOut_SIZE(0); // nothing emitted on dataOut
|
||||||
ASSERT_from_bufferDeallocate_SIZE(1); // invalid buffer was deallocated
|
ASSERT_from_dataReturnOut_SIZE(1); // invalid buffer was deallocated
|
||||||
// Check which event was emitted
|
// Check which event was emitted
|
||||||
ASSERT_EVENTS_SIZE(1); // exactly 1 event emitted
|
ASSERT_EVENTS_SIZE(1); // exactly 1 event emitted
|
||||||
ASSERT_EVENTS_InvalidLengthReceived_SIZE(1); // event was emitted for invalid length
|
ASSERT_EVENTS_InvalidLengthReceived_SIZE(1); // event was emitted for invalid length
|
||||||
@ -62,8 +62,8 @@ void FprimeDeframerTester ::testIncorrectStartWord() {
|
|||||||
this->injectChecksum(data, sizeof(data));
|
this->injectChecksum(data, sizeof(data));
|
||||||
this->mockReceiveData(data, sizeof(data));
|
this->mockReceiveData(data, sizeof(data));
|
||||||
|
|
||||||
ASSERT_from_deframedOut_SIZE(0); // nothing emitted on deframedOut
|
ASSERT_from_dataOut_SIZE(0); // nothing emitted on dataOut
|
||||||
ASSERT_from_bufferDeallocate_SIZE(1); // invalid buffer was deallocated
|
ASSERT_from_dataReturnOut_SIZE(1); // invalid buffer was deallocated
|
||||||
// Check which event was emitted
|
// Check which event was emitted
|
||||||
ASSERT_EVENTS_SIZE(1); // exactly 1 event emitted
|
ASSERT_EVENTS_SIZE(1); // exactly 1 event emitted
|
||||||
ASSERT_EVENTS_InvalidStartWord_SIZE(1); // event was emitted for invalid start word
|
ASSERT_EVENTS_InvalidStartWord_SIZE(1); // event was emitted for invalid start word
|
||||||
@ -73,8 +73,8 @@ void FprimeDeframerTester ::testIncorrectCrc() {
|
|||||||
// Frame: | F´ start word | Length = 1 | Data | INCORRECT Checksum |
|
// Frame: | F´ start word | Length = 1 | Data | INCORRECT Checksum |
|
||||||
U8 data[13] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
|
U8 data[13] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
this->mockReceiveData(data, sizeof(data));
|
this->mockReceiveData(data, sizeof(data));
|
||||||
ASSERT_from_deframedOut_SIZE(0); // nothing emitted on deframedOut
|
ASSERT_from_dataOut_SIZE(0); // nothing emitted on dataOut
|
||||||
ASSERT_from_bufferDeallocate_SIZE(1); // invalid buffer was deallocated
|
ASSERT_from_dataReturnOut_SIZE(1); // invalid buffer was deallocated
|
||||||
// Check which event was emitted
|
// Check which event was emitted
|
||||||
ASSERT_EVENTS_SIZE(1); // exactly 1 event emitted
|
ASSERT_EVENTS_SIZE(1); // exactly 1 event emitted
|
||||||
ASSERT_EVENTS_InvalidChecksum_SIZE(1); // event was emitted for invalid checksum
|
ASSERT_EVENTS_InvalidChecksum_SIZE(1); // event was emitted for invalid checksum
|
||||||
@ -84,8 +84,8 @@ void FprimeDeframerTester::testTruncatedFrame() {
|
|||||||
// Send a truncated frame, too short to be valid
|
// Send a truncated frame, too short to be valid
|
||||||
U8 data[11] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
U8 data[11] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
this->mockReceiveData(data, sizeof(data));
|
this->mockReceiveData(data, sizeof(data));
|
||||||
ASSERT_from_deframedOut_SIZE(0); // nothing emitted on deframedOut
|
ASSERT_from_dataOut_SIZE(0); // nothing emitted on dataOut
|
||||||
ASSERT_from_bufferDeallocate_SIZE(1); // invalid buffer was deallocated
|
ASSERT_from_dataReturnOut_SIZE(1); // invalid buffer was deallocated
|
||||||
// Check which event was emitted
|
// Check which event was emitted
|
||||||
ASSERT_EVENTS_SIZE(1); // exactly 1 event emitted
|
ASSERT_EVENTS_SIZE(1); // exactly 1 event emitted
|
||||||
ASSERT_EVENTS_InvalidBufferReceived_SIZE(1); // event was emitted for invalid buffer
|
ASSERT_EVENTS_InvalidBufferReceived_SIZE(1); // event was emitted for invalid buffer
|
||||||
@ -94,13 +94,23 @@ void FprimeDeframerTester::testTruncatedFrame() {
|
|||||||
void FprimeDeframerTester::testZeroSizeFrame() {
|
void FprimeDeframerTester::testZeroSizeFrame() {
|
||||||
// Send an empty frame, too short to be valid
|
// Send an empty frame, too short to be valid
|
||||||
this->mockReceiveData(nullptr, 0);
|
this->mockReceiveData(nullptr, 0);
|
||||||
ASSERT_from_deframedOut_SIZE(0); // nothing emitted on deframedOut
|
ASSERT_from_dataOut_SIZE(0); // nothing emitted on dataOut
|
||||||
ASSERT_from_bufferDeallocate_SIZE(1); // invalid buffer was deallocated
|
ASSERT_from_dataReturnOut_SIZE(1); // invalid buffer was deallocated
|
||||||
// Check which event was emitted
|
// Check which event was emitted
|
||||||
ASSERT_EVENTS_SIZE(1); // exactly 1 event emitted
|
ASSERT_EVENTS_SIZE(1); // exactly 1 event emitted
|
||||||
ASSERT_EVENTS_InvalidBufferReceived_SIZE(1); // event was emitted for invalid buffer
|
ASSERT_EVENTS_InvalidBufferReceived_SIZE(1); // event was emitted for invalid buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FprimeDeframerTester::testDataReturn() {
|
||||||
|
U8 data[1];
|
||||||
|
Fw::Buffer buffer(data, sizeof(data));
|
||||||
|
ComCfg::FrameContext nullContext;
|
||||||
|
this->invoke_to_dataReturnIn(0, buffer, nullContext);
|
||||||
|
ASSERT_from_dataReturnOut_SIZE(1); // incoming buffer should be deallocated
|
||||||
|
ASSERT_EQ(this->fromPortHistory_dataReturnOut->at(0).data.getData(), data);
|
||||||
|
ASSERT_EQ(this->fromPortHistory_dataReturnOut->at(0).data.getSize(), sizeof(data));
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Test Helpers
|
// Test Helpers
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
@ -124,7 +134,7 @@ void FprimeDeframerTester::injectChecksum(U8* data, FwSizeType size) {
|
|||||||
void FprimeDeframerTester::mockReceiveData(U8* data, FwSizeType size) {
|
void FprimeDeframerTester::mockReceiveData(U8* data, FwSizeType size) {
|
||||||
ComCfg::FrameContext nullContext;
|
ComCfg::FrameContext nullContext;
|
||||||
Fw::Buffer buffer(data, static_cast<Fw::Buffer::SizeType>(size));
|
Fw::Buffer buffer(data, static_cast<Fw::Buffer::SizeType>(size));
|
||||||
this->invoke_to_framedIn(0, buffer, nullContext);
|
this->invoke_to_dataIn(0, buffer, nullContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Svc
|
} // namespace Svc
|
||||||
|
|||||||
@ -58,6 +58,9 @@ class FprimeDeframerTester : public FprimeDeframerGTestBase {
|
|||||||
//! Test receiving a frame with an incorrect Crc field
|
//! Test receiving a frame with an incorrect Crc field
|
||||||
void testIncorrectCrc();
|
void testIncorrectCrc();
|
||||||
|
|
||||||
|
//! Test bufferReturn passthrough
|
||||||
|
void testDataReturn();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Helper functions
|
// Helper functions
|
||||||
|
|||||||
@ -4,11 +4,11 @@ The `Svc::FprimeFramer` is an implementation of the [FramerInterface](../../Inte
|
|||||||
|
|
||||||
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.
|
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
|
It is designed to receive 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 [`Svc::ComStub`](../../ComStub/docs/sdd.md), for transmission on the wire.
|
||||||
|
|
||||||
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.
|
## Usage Examples
|
||||||
|
|
||||||

|
The `Svc::FprimeFramer` component is used in the uplink stack of many reference F´ application such as [the tutorials source code](https://github.com/fprime-community#tutorials).
|
||||||
|
|
||||||
## Internals
|
## Internals
|
||||||
|
|
||||||
@ -25,12 +25,14 @@ On receiving a data packet, the `Svc::FprimeFramer` performs the following actio
|
|||||||
|
|
||||||
## Port Descriptions
|
## Port Descriptions
|
||||||
|
|
||||||
| Kind | Name | Port Type | Usage |
|
| Kind | Name | Port Type | Usage |
|
||||||
|---|---|---|---|
|
|-----------------|-----------------|--------------------------|--------------------------------------------------------------------------|
|
||||||
| `guarded input` | `dataIn` | `Svc.ComDataWithContext` | Port to receive data to frame, in a Fw::Buffer with optional context|
|
| `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|
|
| `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|
|
| `sync input` | `dataReturnIn` | `Svc.ComDataWithContext` | Port to receive back ownership of buffer sent out of `dataOut` |
|
||||||
| `output` | `comStatusOut` | `Fw.SuccessCondition` | Port receiving indicating the status of framer for receiving more data|
|
| `output` | `dataReturnOut` | `Svc.ComDataWithContext` | Port to return ownership of buffer received on `dataIn` |
|
||||||
|
| `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
|
## Requirements
|
||||||
|
|
||||||
@ -39,4 +41,5 @@ On receiving a data packet, the `Svc::FprimeFramer` performs the following actio
|
|||||||
| 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-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-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 |
|
| SVC-FPRIME_FRAMER-003 | `Svc::FprimeFramer` shall emit F Prime frames that conforms to the [F´ frame specification](../../FprimeProtocol/docs/sdd.md) | Unit Test |
|
||||||
|
| SVC-FPRIME_FRAMER-004 | `Svc::FprimeFramer` shall pass through all `Fw.SuccessCondition` received on `comStatusIn` to `comStatusOut` | Unit Test |
|
||||||
|
|
||||||
|
|||||||
@ -32,9 +32,6 @@ void FprimeRouter ::dataIn_handler(FwIndexType portNum, Fw::Buffer& packetBuffer
|
|||||||
status = esb.deserialize(packetType);
|
status = esb.deserialize(packetType);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whether to deallocate the packet buffer
|
|
||||||
bool deallocate = true;
|
|
||||||
|
|
||||||
// Process the packet
|
// Process the packet
|
||||||
if (status == Fw::FW_SERIALIZE_OK) {
|
if (status == Fw::FW_SERIALIZE_OK) {
|
||||||
U8* const packetData = packetBuffer.getData();
|
U8* const packetData = packetBuffer.getData();
|
||||||
@ -57,13 +54,16 @@ void FprimeRouter ::dataIn_handler(FwIndexType portNum, Fw::Buffer& packetBuffer
|
|||||||
}
|
}
|
||||||
// Handle a file packet
|
// Handle a file packet
|
||||||
case Fw::ComPacket::FW_PACKET_FILE: {
|
case Fw::ComPacket::FW_PACKET_FILE: {
|
||||||
// If the file uplink output port is connected,
|
// If the file uplink output port is connected, send the file packet. Otherwise take no action.
|
||||||
// send the file packet. Otherwise take no action.
|
|
||||||
if (this->isConnected_fileOut_OutputPort(0)) {
|
if (this->isConnected_fileOut_OutputPort(0)) {
|
||||||
// Send the packet buffer
|
// Copy buffer into a new allocated buffer. This lets us return the original buffer with dataReturnOut,
|
||||||
this->fileOut_out(0, packetBuffer);
|
// and FprimeRouter can handle the deallocation of the file buffer when it returns on fileBufferReturnIn
|
||||||
// Transfer ownership of the packetBuffer to the receiver
|
Fw::Buffer packetBufferCopy = this->bufferAllocate_out(0, packetBuffer.getSize());
|
||||||
deallocate = false;
|
auto copySerializer = packetBufferCopy.getSerializer();
|
||||||
|
status = copySerializer.serialize(packetBuffer.getData(), packetBuffer.getSize(), Fw::Serialization::OMIT_LENGTH);
|
||||||
|
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
|
||||||
|
// Send the copied buffer out. It will come back on fileBufferReturnIn once the receiver is done with it
|
||||||
|
this->fileOut_out(0, packetBufferCopy);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -71,9 +71,14 @@ void FprimeRouter ::dataIn_handler(FwIndexType portNum, Fw::Buffer& packetBuffer
|
|||||||
// Packet type is not known to the F Prime protocol. If the unknownDataOut port is
|
// Packet type is not known to the F Prime protocol. If the unknownDataOut port is
|
||||||
// connected, forward packet and context for further processing
|
// connected, forward packet and context for further processing
|
||||||
if (this->isConnected_unknownDataOut_OutputPort(0)) {
|
if (this->isConnected_unknownDataOut_OutputPort(0)) {
|
||||||
this->unknownDataOut_out(0, packetBuffer, context);
|
// Copy buffer into a new allocated buffer. This lets us return the original buffer with dataReturnOut,
|
||||||
// Transfer ownership of the packetBuffer to the receiver
|
// and FprimeRouter can handle the deallocation of the unknown buffer when it returns on bufferReturnIn
|
||||||
deallocate = false;
|
Fw::Buffer packetBufferCopy = this->bufferAllocate_out(0, packetBuffer.getSize());
|
||||||
|
auto copySerializer = packetBufferCopy.getSerializer();
|
||||||
|
status = copySerializer.serialize(packetBuffer.getData(), packetBuffer.getSize(), Fw::Serialization::OMIT_LENGTH);
|
||||||
|
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
|
||||||
|
// Send the copied buffer out. It will come back on fileBufferReturnIn once the receiver is done with it
|
||||||
|
this->unknownDataOut_out(0, packetBufferCopy, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,10 +86,8 @@ void FprimeRouter ::dataIn_handler(FwIndexType portNum, Fw::Buffer& packetBuffer
|
|||||||
this->log_WARNING_HI_DeserializationError(status);
|
this->log_WARNING_HI_DeserializationError(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deallocate) {
|
// Return ownership of the incoming packetBuffer
|
||||||
// Deallocate the packet buffer
|
this->dataReturnOut_out(0, packetBuffer, context);
|
||||||
this->bufferDeallocate_out(0, packetBuffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FprimeRouter ::cmdResponseIn_handler(FwIndexType portNum,
|
void FprimeRouter ::cmdResponseIn_handler(FwIndexType portNum,
|
||||||
@ -93,4 +96,9 @@ void FprimeRouter ::cmdResponseIn_handler(FwIndexType portNum,
|
|||||||
const Fw::CmdResponse& response) {
|
const Fw::CmdResponse& response) {
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FprimeRouter ::fileBufferReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
|
||||||
|
this->bufferDeallocate_out(0, fwBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Svc
|
} // namespace Svc
|
||||||
|
|||||||
@ -10,6 +10,9 @@ module Svc {
|
|||||||
@ Port for forwarding non-recognized packet types
|
@ Port for forwarding non-recognized packet types
|
||||||
output port unknownDataOut: Svc.ComDataWithContext
|
output port unknownDataOut: Svc.ComDataWithContext
|
||||||
|
|
||||||
|
@ Port for allocating buffers
|
||||||
|
output port bufferAllocate: Fw.BufferGet
|
||||||
|
|
||||||
@ Port for deallocating buffers
|
@ Port for deallocating buffers
|
||||||
output port bufferDeallocate: Fw.BufferSend
|
output port bufferDeallocate: Fw.BufferSend
|
||||||
|
|
||||||
|
|||||||
@ -44,6 +44,14 @@ class FprimeRouter final : public FprimeRouterComponentBase {
|
|||||||
U32 cmdSeq, //!< The command sequence number
|
U32 cmdSeq, //!< The command sequence number
|
||||||
const Fw::CmdResponse& response //!< The command response
|
const Fw::CmdResponse& response //!< The command response
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
|
//! Handler implementation for fileBufferReturnIn
|
||||||
|
//!
|
||||||
|
//! Port for receiving ownership back of buffers sent on fileOut
|
||||||
|
void fileBufferReturnIn_handler(FwIndexType portNum, //!< The port number
|
||||||
|
Fw::Buffer& fwBuffer //!< The buffer
|
||||||
|
) override;
|
||||||
|
|
||||||
};
|
};
|
||||||
} // namespace Svc
|
} // namespace Svc
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,13 @@ The `Svc::FprimeRouter` component routes F´ packets (such as command or file pa
|
|||||||
|
|
||||||
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 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.
|
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.
|
||||||
|
|
||||||
|
About memory management, all buffers sent by `Svc::FprimeRouter` on the `fileOut` and `unknownDataOut` ports are expected to be returned to the router through the `fileBufferReturnIn` port for deallocation.
|
||||||
|
|
||||||
|
## Custom Routing
|
||||||
|
|
||||||
|
The `Svc::FprimeRouter` component is designed to be extensible through the use of a project-specific router. The `unknownDataOut` port can be connected to a project-specific component that can receive all unknown packet types. This component can then implement custom handling of these unknown packets. After processing, the project-specific component shall return the received buffer to the `Svc::FprimeRouter` component through the `fileBufferReturnIn` port (named this way as it only receives file packets in the common use-case), which will deallocate the buffer.
|
||||||
|
|
||||||
## Usage Examples
|
## Usage Examples
|
||||||
|
|
||||||
@ -16,26 +22,18 @@ In the canonical uplink communications stack, `Svc::FprimeRouter` is connected t
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Class Diagram
|
|
||||||
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
classDiagram
|
|
||||||
class FprimeRouter~PassiveComponent~ {
|
|
||||||
+ void dataIn_handler(portNum, packetBuffer, contextBuffer)
|
|
||||||
+ void cmdResponseIn_handler(portNum, opcode, cmdSeq, response)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Port Descriptions
|
## Port Descriptions
|
||||||
|
|
||||||
| Name | Description | Type |
|
| Kind | Name | Type | Description |
|
||||||
|---|---|---|
|
|---|---|---|---|
|
||||||
| `dataIn: Svc.ComDataWithContext` | Receiving Fw::Buffer with context buffer from Deframer | `guarded input` |
|
| `guarded input` | `dataIn` | `Svc.ComDataWithContext` | Receiving Fw::Buffer with context buffer from Deframer
|
||||||
| `commandOut: Fw.Com` | Port for sending command packets as Fw::ComBuffers | `output` |
|
| `guarded input` | `dataReturnOut` | `Svc.ComDataWithContext` | Returning ownership of buffer received on `dataIn`
|
||||||
| `fileOut: Fw.BufferSend` | Port for sending file packets as Fw::Buffer (ownership passed to receiver) | `output` |
|
| `output` | `commandOut` | `Fw.Com` | Port for sending command packets as Fw::ComBuffers |
|
||||||
| `unknownDataOut: Svc.ComDataWithContext` | Port forwarding unknown data (useful for adding custom routing rules with a project-defined router) | `output` |
|
| `output` | `fileOut` | `Fw.BufferSend` | Port for sending file packets as Fw::Buffer (ownership passed to receiver) |
|
||||||
| `output`| bufferDeallocate | `Fw.BufferSend` | Port for deallocating buffers once routed |
|
| `sync input` | `fileBufferReturnIn` | `Fw.BufferSend` | Receiving back ownership of buffer sent on `fileOut` and `unknownDataOut` |
|
||||||
|
| `output` | `unknownDataOut` | `Svc.ComDataWithContext` | Port forwarding unknown data (useful for adding custom routing rules with a project-defined router) |
|
||||||
|
| `output`| `bufferAllocate` | `Fw.BufferGet` | Port for allocating buffers, allowing copy of received data |
|
||||||
|
| `output`| `bufferDeallocate` | `Fw.BufferSend` | Port for deallocating buffers |
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
@ -46,3 +44,5 @@ SVC-ROUTER-002 | `Svc::FprimeRouter` shall route packets of type `Fw::ComPacket:
|
|||||||
SVC-ROUTER-003 | `Svc::FprimeRouter` shall route packets of type `Fw::ComPacket::FW_PACKET_FILE` to the `fileOut` output port. | Routing file packets | Unit test |
|
SVC-ROUTER-003 | `Svc::FprimeRouter` shall route packets of type `Fw::ComPacket::FW_PACKET_FILE` to the `fileOut` output port. | Routing file packets | Unit test |
|
||||||
SVC-ROUTER-004 | `Svc::FprimeRouter` shall route data that is neither `Fw::ComPacket::FW_PACKET_COMMAND` nor `Fw::ComPacket::FW_PACKET_FILE` to the `unknownDataOut` output port. | Allows for projects to provide custom routing for additional (project-specific) uplink data types | Unit test |
|
SVC-ROUTER-004 | `Svc::FprimeRouter` shall route data that is neither `Fw::ComPacket::FW_PACKET_COMMAND` nor `Fw::ComPacket::FW_PACKET_FILE` to the `unknownDataOut` output port. | Allows for projects to provide custom routing for additional (project-specific) uplink data types | Unit test |
|
||||||
SVC-ROUTER-005 | `Svc::FprimeRouter` shall emit warning events if serialization errors occur during processing of incoming packets | Aid in diagnosing uplink issues | Unit test |
|
SVC-ROUTER-005 | `Svc::FprimeRouter` shall emit warning events if serialization errors occur during processing of incoming packets | Aid in diagnosing uplink issues | Unit test |
|
||||||
|
SVC-ROUTER-005 | `Svc::FprimeRouter` shall make a copy of buffers that represent a `FW_PACKET_FILE` | Aid in memory management of file buffers | Unit test |
|
||||||
|
SVC-ROUTER-005 | `Svc::FprimeRouter` shall return ownership of all buffers received on `dataIn` through `dataReturnOut` | Memory management | Unit test |
|
||||||
|
|||||||
@ -28,6 +28,11 @@ TEST(FprimeRouter, TestRouteUnknownPacketUnconnected) {
|
|||||||
Svc::FprimeRouterTester tester(true);
|
Svc::FprimeRouterTester tester(true);
|
||||||
tester.testRouteUnknownPacketUnconnected();
|
tester.testRouteUnknownPacketUnconnected();
|
||||||
}
|
}
|
||||||
|
TEST(FprimeRouter, TestBufferReturn) {
|
||||||
|
COMMENT("Deallocate a returning buffer");
|
||||||
|
Svc::FprimeRouterTester tester;
|
||||||
|
tester.testBufferReturn();
|
||||||
|
}
|
||||||
TEST(FprimeRouter, TestCommandResponse) {
|
TEST(FprimeRouter, TestCommandResponse) {
|
||||||
COMMENT("Handle a command response (no-op)");
|
COMMENT("Handle a command response (no-op)");
|
||||||
Svc::FprimeRouterTester tester;
|
Svc::FprimeRouterTester tester;
|
||||||
|
|||||||
@ -33,7 +33,8 @@ void FprimeRouterTester ::testRouteComInterface() {
|
|||||||
ASSERT_from_commandOut_SIZE(1); // one command packet emitted
|
ASSERT_from_commandOut_SIZE(1); // one command packet emitted
|
||||||
ASSERT_from_fileOut_SIZE(0); // no file packet emitted
|
ASSERT_from_fileOut_SIZE(0); // no file packet emitted
|
||||||
ASSERT_from_unknownDataOut_SIZE(0); // no unknown data emitted
|
ASSERT_from_unknownDataOut_SIZE(0); // no unknown data emitted
|
||||||
ASSERT_from_bufferDeallocate_SIZE(1); // command packets are deallocated by the router
|
ASSERT_from_dataReturnOut_SIZE(1); // data ownership should always be returned
|
||||||
|
ASSERT_from_bufferAllocate_SIZE(0); // no buffer allocation for Com packets
|
||||||
}
|
}
|
||||||
|
|
||||||
void FprimeRouterTester ::testRouteFileInterface() {
|
void FprimeRouterTester ::testRouteFileInterface() {
|
||||||
@ -41,7 +42,8 @@ void FprimeRouterTester ::testRouteFileInterface() {
|
|||||||
ASSERT_from_commandOut_SIZE(0); // no command packet emitted
|
ASSERT_from_commandOut_SIZE(0); // no command packet emitted
|
||||||
ASSERT_from_fileOut_SIZE(1); // one file packet emitted
|
ASSERT_from_fileOut_SIZE(1); // one file packet emitted
|
||||||
ASSERT_from_unknownDataOut_SIZE(0); // no unknown data emitted
|
ASSERT_from_unknownDataOut_SIZE(0); // no unknown data emitted
|
||||||
ASSERT_from_bufferDeallocate_SIZE(0); // no deallocation (file packets' ownership is transferred to the receiver)
|
ASSERT_from_dataReturnOut_SIZE(1); // data ownership should always be returned
|
||||||
|
ASSERT_from_bufferAllocate_SIZE(1); // file packet was copied into a new allocated buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
void FprimeRouterTester ::testRouteUnknownPacket() {
|
void FprimeRouterTester ::testRouteUnknownPacket() {
|
||||||
@ -49,15 +51,26 @@ void FprimeRouterTester ::testRouteUnknownPacket() {
|
|||||||
ASSERT_from_commandOut_SIZE(0); // no command packet emitted
|
ASSERT_from_commandOut_SIZE(0); // no command packet emitted
|
||||||
ASSERT_from_fileOut_SIZE(0); // no file packet emitted
|
ASSERT_from_fileOut_SIZE(0); // no file packet emitted
|
||||||
ASSERT_from_unknownDataOut_SIZE(1); // one unknown data emitted
|
ASSERT_from_unknownDataOut_SIZE(1); // one unknown data emitted
|
||||||
ASSERT_from_bufferDeallocate_SIZE(0); // no deallocation (unknown data ownership is transferred to the receiver)
|
ASSERT_from_dataReturnOut_SIZE(1); // data ownership should always be returned
|
||||||
|
ASSERT_from_bufferAllocate_SIZE(1); // unknown packet was copied into a new allocated buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
void FprimeRouterTester ::testRouteUnknownPacketUnconnected() {
|
void FprimeRouterTester ::testRouteUnknownPacketUnconnected() {
|
||||||
this->mockReceivePacketType(Fw::ComPacket::FW_PACKET_UNKNOWN);
|
this->mockReceivePacketType(Fw::ComPacket::FW_PACKET_UNKNOWN);
|
||||||
ASSERT_from_commandOut_SIZE(0); // no command packet emitted
|
ASSERT_from_commandOut_SIZE(0); // no command packet emitted
|
||||||
ASSERT_from_fileOut_SIZE(0); // no file packet emitted
|
ASSERT_from_fileOut_SIZE(0); // no file packet emitted
|
||||||
ASSERT_from_unknownDataOut_SIZE(0); // zero unknown data emitted
|
ASSERT_from_unknownDataOut_SIZE(0); // zero unknown data emitted when port is unconnected
|
||||||
ASSERT_from_bufferDeallocate_SIZE(1); // test that buffer is deallocated when output port is not connected
|
ASSERT_from_dataReturnOut_SIZE(1); // data ownership should always be returned
|
||||||
|
ASSERT_from_bufferAllocate_SIZE(0); // no buffer allocation when port is unconnected
|
||||||
|
}
|
||||||
|
|
||||||
|
void FprimeRouterTester ::testBufferReturn() {
|
||||||
|
U8 data[1];
|
||||||
|
Fw::Buffer buffer(data, sizeof(data));
|
||||||
|
this->invoke_to_fileBufferReturnIn(0, buffer);
|
||||||
|
ASSERT_from_bufferDeallocate_SIZE(1); // incoming buffer should be deallocated
|
||||||
|
ASSERT_EQ(this->fromPortHistory_bufferDeallocate->at(0).fwBuffer.getData(), data);
|
||||||
|
ASSERT_EQ(this->fromPortHistory_bufferDeallocate->at(0).fwBuffer.getSize(), sizeof(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FprimeRouterTester ::testCommandResponse() {
|
void FprimeRouterTester ::testCommandResponse() {
|
||||||
@ -87,15 +100,27 @@ void FprimeRouterTester::connectPortsExceptUnknownData() {
|
|||||||
this->component.set_logOut_OutputPort(0, this->get_from_logOut(0));
|
this->component.set_logOut_OutputPort(0, this->get_from_logOut(0));
|
||||||
this->component.set_logTextOut_OutputPort(0, this->get_from_logTextOut(0));
|
this->component.set_logTextOut_OutputPort(0, this->get_from_logTextOut(0));
|
||||||
this->component.set_timeCaller_OutputPort(0, this->get_from_timeCaller(0));
|
this->component.set_timeCaller_OutputPort(0, this->get_from_timeCaller(0));
|
||||||
|
|
||||||
// Connect typed input ports
|
// Connect typed input ports
|
||||||
this->connect_to_cmdResponseIn(0, this->component.get_cmdResponseIn_InputPort(0));
|
this->connect_to_cmdResponseIn(0, this->component.get_cmdResponseIn_InputPort(0));
|
||||||
this->connect_to_dataIn(0, this->component.get_dataIn_InputPort(0));
|
this->connect_to_dataIn(0, this->component.get_dataIn_InputPort(0));
|
||||||
|
this->connect_to_fileBufferReturnIn(0, this->component.get_fileBufferReturnIn_InputPort(0));
|
||||||
// Connect typed output ports
|
// Connect typed output ports
|
||||||
|
this->component.set_bufferAllocate_OutputPort(0, this->get_from_bufferAllocate(0));
|
||||||
this->component.set_bufferDeallocate_OutputPort(0, this->get_from_bufferDeallocate(0));
|
this->component.set_bufferDeallocate_OutputPort(0, this->get_from_bufferDeallocate(0));
|
||||||
this->component.set_commandOut_OutputPort(0, this->get_from_commandOut(0));
|
this->component.set_commandOut_OutputPort(0, this->get_from_commandOut(0));
|
||||||
|
this->component.set_dataReturnOut_OutputPort(0, this->get_from_dataReturnOut(0));
|
||||||
this->component.set_fileOut_OutputPort(0, this->get_from_fileOut(0));
|
this->component.set_fileOut_OutputPort(0, this->get_from_fileOut(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Port handler overrides
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
Fw::Buffer FprimeRouterTester::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
|
} // namespace Svc
|
||||||
|
|||||||
@ -56,6 +56,9 @@ class FprimeRouterTester : public FprimeRouterGTestBase {
|
|||||||
//! Route a packet of unknown type
|
//! Route a packet of unknown type
|
||||||
void testRouteUnknownPacketUnconnected();
|
void testRouteUnknownPacketUnconnected();
|
||||||
|
|
||||||
|
//! Deallocate a returning buffer
|
||||||
|
void testBufferReturn();
|
||||||
|
|
||||||
//! Invoke the command response input port
|
//! Invoke the command response input port
|
||||||
void testCommandResponse();
|
void testCommandResponse();
|
||||||
|
|
||||||
@ -76,6 +79,12 @@ class FprimeRouterTester : public FprimeRouterGTestBase {
|
|||||||
//! Mock the reception of a packet of a specific type
|
//! Mock the reception of a packet of a specific type
|
||||||
void mockReceivePacketType(Fw::ComPacket::ComPacketType packetType);
|
void mockReceivePacketType(Fw::ComPacket::ComPacketType packetType);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Port handler overrides
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
//! Overriding bufferAllocate handler to be able to request a buffer in component tests
|
||||||
|
Fw::Buffer from_bufferAllocate_handler(FwIndexType portNum, U32 size) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Member variables
|
// Member variables
|
||||||
@ -83,6 +92,9 @@ class FprimeRouterTester : public FprimeRouterGTestBase {
|
|||||||
|
|
||||||
//! The component under test
|
//! The component under test
|
||||||
FprimeRouter component;
|
FprimeRouter component;
|
||||||
|
|
||||||
|
Fw::Buffer m_buffer; // buffer to be returned by mocked bufferAllocate call
|
||||||
|
U8 m_buffer_slot[64];
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Svc
|
} // namespace Svc
|
||||||
|
|||||||
@ -50,14 +50,14 @@ void FrameAccumulator ::cleanup() {
|
|||||||
// Handler implementations for user-defined typed input ports
|
// Handler implementations for user-defined typed input ports
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
void FrameAccumulator ::dataIn_handler(FwIndexType portNum, Fw::Buffer& buffer) {
|
void FrameAccumulator ::dataIn_handler(FwIndexType portNum, Fw::Buffer& buffer, const ComCfg::FrameContext& context) {
|
||||||
// Check whether there is data to process
|
// Check whether there is data to process
|
||||||
if (buffer.isValid()) {
|
if (buffer.isValid()) {
|
||||||
|
// The buffer is not necessarily a full frame, so the attached context has no meaning and we ignore it
|
||||||
this->processBuffer(buffer);
|
this->processBuffer(buffer);
|
||||||
}
|
}
|
||||||
// TODO: rework the uplink deallocation logic to use the bufferReturn chaining pattern
|
// Return ownership of the incoming data
|
||||||
// Deallocate the buffer
|
this->dataReturnOut_out(0, buffer, context);
|
||||||
this->bufferDeallocate_out(0, buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameAccumulator ::processBuffer(Fw::Buffer& buffer) {
|
void FrameAccumulator ::processBuffer(Fw::Buffer& buffer) {
|
||||||
@ -136,7 +136,7 @@ void FrameAccumulator ::processRing() {
|
|||||||
static_cast<FwAssertArgType>(m_inRing.get_allocated_size()),
|
static_cast<FwAssertArgType>(m_inRing.get_allocated_size()),
|
||||||
static_cast<FwAssertArgType>(remaining), static_cast<FwAssertArgType>(size_out));
|
static_cast<FwAssertArgType>(remaining), static_cast<FwAssertArgType>(size_out));
|
||||||
ComCfg::FrameContext context;
|
ComCfg::FrameContext context;
|
||||||
this->frameOut_out(0, buffer, context);
|
this->dataOut_out(0, buffer, context);
|
||||||
} else {
|
} else {
|
||||||
// No buffer is available, we need to exit and try again later
|
// No buffer is available, we need to exit and try again later
|
||||||
this->log_WARNING_HI_NoBufferAvailable();
|
this->log_WARNING_HI_NoBufferAvailable();
|
||||||
@ -163,4 +163,11 @@ void FrameAccumulator ::processRing() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FrameAccumulator ::dataReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer, const ComCfg::FrameContext& context) {
|
||||||
|
// Frame buffer ownership is returned to the component. Component had allocated with a buffer manager,
|
||||||
|
// so we return it to the buffer manager for deallocation
|
||||||
|
this->bufferDeallocate_out(0, fwBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Svc
|
} // namespace Svc
|
||||||
|
|||||||
@ -7,8 +7,7 @@ module Svc {
|
|||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
include "../Interfaces/FrameAccumulatorInterface.fppi"
|
include "../Interfaces/FrameAccumulatorInterface.fppi"
|
||||||
|
|
||||||
|
@ Port for deallocating buffers holding extracted frames
|
||||||
@ Port for deallocating buffers received on dataIn.
|
|
||||||
output port bufferDeallocate: Fw.BufferSend
|
output port bufferDeallocate: Fw.BufferSend
|
||||||
|
|
||||||
@ Port for allocating buffer to hold extracted frame
|
@ Port for allocating buffer to hold extracted frame
|
||||||
|
|||||||
@ -48,9 +48,18 @@ class FrameAccumulator final : public FrameAccumulatorComponentBase {
|
|||||||
|
|
||||||
//! Handler implementation for dataIn
|
//! Handler implementation for dataIn
|
||||||
//!
|
//!
|
||||||
//! Receives raw data from a ByteStreamDriver, ComStub, or other buffer producing component
|
//! Receive stream of bytes from a ComInterface component
|
||||||
void dataIn_handler(FwIndexType portNum, //!< The port number
|
void dataIn_handler(FwIndexType portNum, //!< The port number
|
||||||
Fw::Buffer& recvBuffer) override;
|
Fw::Buffer& recvBuffer,
|
||||||
|
const ComCfg::FrameContext& context) override;
|
||||||
|
|
||||||
|
//! Handler implementation for bufferReturnIn
|
||||||
|
//!
|
||||||
|
//! Port receiving ownership back of buffers sent on dataOut
|
||||||
|
void dataReturnIn_handler(FwIndexType portNum, //!< The port number
|
||||||
|
Fw::Buffer& fwBuffer, //!< The buffer
|
||||||
|
const ComCfg::FrameContext& context //!< The context object
|
||||||
|
) override;
|
||||||
|
|
||||||
PRIVATE:
|
PRIVATE:
|
||||||
//! \brief process raw buffer
|
//! \brief process raw buffer
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
The `Svc::FrameAccumulator` component accumulates a stream of data (sequence of [Fw::Buffer](../../../Fw/Buffer/docs/sdd.md) objects) to extract full frames from.
|
The `Svc::FrameAccumulator` component accumulates a stream of data (sequence of [Fw::Buffer](../../../Fw/Buffer/docs/sdd.md) objects) to extract full frames from.
|
||||||
|
|
||||||
The `Svc::FrameAccumulator` accepts as input a sequence of byte buffers, which typically come from a ground data system via a [ByteStreamDriver](../../../Drv/ByteStreamDriverModel/docs/sdd.md). It extracts the frames from the sequence of buffers and emits them on the `frameOut` output port.
|
The `Svc::FrameAccumulator` accepts as input a sequence of byte buffers, which typically come from a ground data system via a [ByteStreamDriver](../../../Drv/ByteStreamDriverModel/docs/sdd.md). It extracts the frames from the sequence of buffers and emits them on the `dataOut` output port.
|
||||||
|
|
||||||
## Internals
|
## Internals
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ The uplink frames need not be aligned on the buffer boundaries, and each frame m
|
|||||||
The `Svc::FrameAccumulator` receives `Fw::Buffer` objects on its `dataIn` input port. These buffers are accumulated in a `Utils::CircularBuffer`. Every time a new buffer is accumulated into the circular buffer, the `Svc::FrameAccumulator` enters a loop to `detect()` a frame within the circular buffer, starting at the current head of the circular buffer. The `Svc::FrameDetector` returns one of three results:
|
The `Svc::FrameAccumulator` receives `Fw::Buffer` objects on its `dataIn` input port. These buffers are accumulated in a `Utils::CircularBuffer`. Every time a new buffer is accumulated into the circular buffer, the `Svc::FrameAccumulator` enters a loop to `detect()` a frame within the circular buffer, starting at the current head of the circular buffer. The `Svc::FrameDetector` returns one of three results:
|
||||||
|
|
||||||
- `NO_FRAME_DETECTED`: indicates no valid frame is present at the head of the circular buffer (for example, start word does not match the current head of the circular buffer). The `Svc::FrameAccumulator` rotates the circular buffer one byte and loops over to `detect()` again, or break the loop if the circular buffer is exhausted.
|
- `NO_FRAME_DETECTED`: indicates no valid frame is present at the head of the circular buffer (for example, start word does not match the current head of the circular buffer). The `Svc::FrameAccumulator` rotates the circular buffer one byte and loops over to `detect()` again, or break the loop if the circular buffer is exhausted.
|
||||||
- `FRAME_DETECTED`: indicates there is a frame at the current head of the circular buffer. The `Svc::FrameAccumulator` allocates a new `Fw::Buffer` object to hold the frame, copies the detected frame from the circular buffer into the new `Fw::Buffer` object, and emits the new `Fw::Buffer` object (containing the frame) on its `frameOut` output port. The `Svc::FrameAccumulator` then rotates the circular buffer to remove the data that was just extracted, and deallocates the original `Fw::Buffer` that was received on the `dataIn` input port.
|
- `FRAME_DETECTED`: indicates there is a frame at the current head of the circular buffer. The `Svc::FrameAccumulator` allocates a new `Fw::Buffer` object to hold the frame, copies the detected frame from the circular buffer into the new `Fw::Buffer` object, and emits the new `Fw::Buffer` object (containing the frame) on its `dataOut` output port. The `Svc::FrameAccumulator` then rotates the circular buffer to remove the data that was just extracted, and deallocates the original `Fw::Buffer` that was received on the `dataIn` input port.
|
||||||
- `MORE_DATA_NEEDED`: indicates that more data is needed to determine whether there is a valid frame. The `Svc::FrameAccumulator` deallocates the original `Fw::Buffer` that was received on the `dataIn` input port and halts execution, effectively waiting for the next `Fw::Buffer` to be received on the `dataIn` input port.
|
- `MORE_DATA_NEEDED`: indicates that more data is needed to determine whether there is a valid frame. The `Svc::FrameAccumulator` deallocates the original `Fw::Buffer` that was received on the `dataIn` input port and halts execution, effectively waiting for the next `Fw::Buffer` to be received on the `dataIn` input port.
|
||||||
|
|
||||||
|
|
||||||
@ -85,7 +85,9 @@ SVC-FRAME-ACCUMULATOR-004 | `Svc::FrameAccumulator` shall accept byte buffers co
|
|||||||
|
|
||||||
| Kind | Name | Type | Description |
|
| Kind | Name | Type | Description |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `guarded input` | dataIn | `Drv.ByteStreamRecv` | Receives raw data from a ByteStreamDriver, ComStub, or other buffer producing component |
|
| `guarded input` | dataIn | `Svc.ComDataWithContext` | Receives a stream of byte buffers from a [Communication Adapter](../../Interfaces/docs/sdd.md) |
|
||||||
| `output` | frameOut | `Svc.ComDataWithContext` | Port for sending an extracted frame out |
|
| `output` | dataOut | `Svc.ComDataWithContext` | Port for sending an extracted frame out |
|
||||||
| `output` | bufferAllocate | `Fw.BufferGet` | Port for allocating buffer to hold extracted frame |
|
| `output` | bufferAllocate | `Fw.BufferGet` | Port for allocating buffer to hold extracted frame |
|
||||||
| `output`| bufferDeallocate | `Fw.BufferSend` | Port for deallocating buffers received on dataIn. |
|
| `output`| bufferDeallocate | `Fw.BufferSend` | Port for deallocating buffers received on dataIn. |
|
||||||
|
| `output` | dataReturnOut | `Svc.ComDataWithContext` | Port for returning ownership of buffers received on dataIn |
|
||||||
|
| `sync input` | dataReturnIn | `Svc.ComDataWithContext` | Receiving back ownership of buffers sent on dataOut |
|
||||||
|
|||||||
@ -42,6 +42,10 @@ TEST(FrameAccumulator, testAccumulateBuffersEmitManyFrames) {
|
|||||||
tester.testAccumulateBuffersEmitManyFrames();
|
tester.testAccumulateBuffersEmitManyFrames();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(FrameAccumulator, testBufferReturnDeallocation) {
|
||||||
|
Svc::FrameAccumulatorTester tester;
|
||||||
|
tester.testBufferReturnDeallocation();
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
STest::Random::seed();
|
STest::Random::seed();
|
||||||
|
|||||||
@ -37,15 +37,16 @@ void FrameAccumulatorTester ::testFrameDetected() {
|
|||||||
U32 buffer_size = STest::Random::lowerUpper(1, 1024);
|
U32 buffer_size = STest::Random::lowerUpper(1, 1024);
|
||||||
U8 data[buffer_size];
|
U8 data[buffer_size];
|
||||||
Fw::Buffer buffer(data, buffer_size);
|
Fw::Buffer buffer(data, buffer_size);
|
||||||
|
ComCfg::FrameContext context;
|
||||||
// Set the mock detector to report success of size_out = buffer_size
|
// Set the mock detector to report success of size_out = buffer_size
|
||||||
this->mockDetector.set_next_result(FrameDetector::Status::FRAME_DETECTED, buffer_size);
|
this->mockDetector.set_next_result(FrameDetector::Status::FRAME_DETECTED, buffer_size);
|
||||||
// Receive the buffer on dataIn
|
// Receive the buffer on dataIn
|
||||||
this->invoke_to_dataIn(0, buffer);
|
this->invoke_to_dataIn(0, buffer, context);
|
||||||
// Checks
|
// Checks
|
||||||
ASSERT_from_bufferDeallocate_SIZE(1); // input buffer was deallocated
|
ASSERT_from_dataReturnOut_SIZE(1); // input buffer ownership was returned
|
||||||
ASSERT_from_frameOut_SIZE(1); // frame was sent
|
ASSERT_from_dataOut_SIZE(1); // frame was sent
|
||||||
ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // no data left in ring buffer
|
ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // no data left in ring buffer
|
||||||
ASSERT_EQ(this->fromPortHistory_frameOut->at(0).data.getSize(), buffer_size); // all data was sent out
|
ASSERT_EQ(this->fromPortHistory_dataOut->at(0).data.getSize(), buffer_size); // all data was sent out
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameAccumulatorTester ::testMoreDataNeeded() {
|
void FrameAccumulatorTester ::testMoreDataNeeded() {
|
||||||
@ -53,13 +54,14 @@ void FrameAccumulatorTester ::testMoreDataNeeded() {
|
|||||||
U32 buffer_size = STest::Random::lowerUpper(1, 1024);
|
U32 buffer_size = STest::Random::lowerUpper(1, 1024);
|
||||||
U8 data[buffer_size];
|
U8 data[buffer_size];
|
||||||
Fw::Buffer buffer(data, buffer_size);
|
Fw::Buffer buffer(data, buffer_size);
|
||||||
|
ComCfg::FrameContext context;
|
||||||
// Set the mock detector to report more data needed
|
// Set the mock detector to report more data needed
|
||||||
this->mockDetector.set_next_result(FrameDetector::Status::MORE_DATA_NEEDED, buffer_size + 1);
|
this->mockDetector.set_next_result(FrameDetector::Status::MORE_DATA_NEEDED, buffer_size + 1);
|
||||||
// Receive the buffer on dataIn
|
// Receive the buffer on dataIn
|
||||||
this->invoke_to_dataIn(0, buffer);
|
this->invoke_to_dataIn(0, buffer, context);
|
||||||
// Checks
|
// Checks
|
||||||
ASSERT_from_bufferDeallocate_SIZE(1); // input buffer was deallocated
|
ASSERT_from_dataReturnOut_SIZE(1); // input buffer ownership was returned
|
||||||
ASSERT_from_frameOut_SIZE(0); // frame was not sent (waiting on more data)
|
ASSERT_from_dataOut_SIZE(0); // frame was not sent (waiting on more data)
|
||||||
ASSERT_EQ(this->component.m_inRing.get_allocated_size(), buffer_size); // data left in ring buffer
|
ASSERT_EQ(this->component.m_inRing.get_allocated_size(), buffer_size); // data left in ring buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,13 +70,14 @@ void FrameAccumulatorTester ::testNoFrameDetected() {
|
|||||||
U32 buffer_size = STest::Random::lowerUpper(1, 1024);
|
U32 buffer_size = STest::Random::lowerUpper(1, 1024);
|
||||||
U8 data[buffer_size];
|
U8 data[buffer_size];
|
||||||
Fw::Buffer buffer(data, buffer_size);
|
Fw::Buffer buffer(data, buffer_size);
|
||||||
|
ComCfg::FrameContext context;
|
||||||
// Set the mock detector
|
// Set the mock detector
|
||||||
this->mockDetector.set_next_result(FrameDetector::Status::NO_FRAME_DETECTED, 0);
|
this->mockDetector.set_next_result(FrameDetector::Status::NO_FRAME_DETECTED, 0);
|
||||||
// Receive the buffer on dataIn
|
// Receive the buffer on dataIn
|
||||||
this->invoke_to_dataIn(0, buffer);
|
this->invoke_to_dataIn(0, buffer, context);
|
||||||
// Checks
|
// Checks
|
||||||
ASSERT_from_bufferDeallocate_SIZE(1); // input buffer was deallocated
|
ASSERT_from_dataReturnOut_SIZE(1); // input buffer ownership was returned
|
||||||
ASSERT_from_frameOut_SIZE(0); // No frame was sent out
|
ASSERT_from_dataOut_SIZE(0); // No frame was sent out
|
||||||
ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // all data was consumed and discarded
|
ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // all data was consumed and discarded
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,11 +86,12 @@ void FrameAccumulatorTester ::testReceiveZeroSizeBuffer() {
|
|||||||
// Prepare a zero size buffer
|
// Prepare a zero size buffer
|
||||||
U8 data[1] = {0};
|
U8 data[1] = {0};
|
||||||
Fw::Buffer buffer(data, 0);
|
Fw::Buffer buffer(data, 0);
|
||||||
|
ComCfg::FrameContext context;
|
||||||
// Receive the buffer on dataIn
|
// Receive the buffer on dataIn
|
||||||
this->invoke_to_dataIn(0, buffer);
|
this->invoke_to_dataIn(0, buffer, context);
|
||||||
// Checks
|
// Checks
|
||||||
ASSERT_from_bufferDeallocate_SIZE(1); // input buffer was deallocated
|
ASSERT_from_dataReturnOut_SIZE(1); // input buffer ownership was returned
|
||||||
ASSERT_from_frameOut_SIZE(0); // No frame was sent out
|
ASSERT_from_dataOut_SIZE(0); // No frame was sent out
|
||||||
ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // No data in ring buffer
|
ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // No data in ring buffer
|
||||||
ASSERT_EQ(this->component.m_inRing.m_head_idx, 0);
|
ASSERT_EQ(this->component.m_inRing.m_head_idx, 0);
|
||||||
}
|
}
|
||||||
@ -99,18 +103,19 @@ void FrameAccumulatorTester ::testAccumulateTwoBuffers() {
|
|||||||
U8 data2[buffer2_size];
|
U8 data2[buffer2_size];
|
||||||
Fw::Buffer buffer1(data1, buffer1_size);
|
Fw::Buffer buffer1(data1, buffer1_size);
|
||||||
Fw::Buffer buffer2(data2, buffer2_size);
|
Fw::Buffer buffer2(data2, buffer2_size);
|
||||||
|
ComCfg::FrameContext context;
|
||||||
|
|
||||||
this->mockDetector.set_next_result(FrameDetector::Status::MORE_DATA_NEEDED, buffer2_size);
|
this->mockDetector.set_next_result(FrameDetector::Status::MORE_DATA_NEEDED, buffer2_size);
|
||||||
// Receive the buffer on dataIn
|
// Receive the buffer on dataIn
|
||||||
this->invoke_to_dataIn(0, buffer1);
|
this->invoke_to_dataIn(0, buffer1, context);
|
||||||
// Next result is detection of a full frame, size = buffer1_size + buffer2_size
|
// 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 );
|
this->mockDetector.set_next_result(FrameDetector::Status::FRAME_DETECTED, buffer1_size + buffer2_size );
|
||||||
// Receive the buffer on dataIn
|
// Receive the buffer on dataIn
|
||||||
this->invoke_to_dataIn(0, buffer2);
|
this->invoke_to_dataIn(0, buffer2, context);
|
||||||
|
|
||||||
// Checks
|
// Checks
|
||||||
ASSERT_from_bufferDeallocate_SIZE(2); // both input buffers deallocated
|
ASSERT_from_dataReturnOut_SIZE(2); // both input buffers ownership were returned
|
||||||
ASSERT_from_frameOut_SIZE(1); // Exactly one frame was sent out
|
ASSERT_from_dataOut_SIZE(1); // Exactly one frame was sent out
|
||||||
ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // No data in ring buffer
|
ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // No data in ring buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,10 +124,10 @@ void FrameAccumulatorTester ::testAccumulateBuffersEmitFrame() {
|
|||||||
U32 buffer_count = 0;
|
U32 buffer_count = 0;
|
||||||
this->mockAccumulateFullFrame(frame_size, buffer_count);
|
this->mockAccumulateFullFrame(frame_size, buffer_count);
|
||||||
// Checks
|
// Checks
|
||||||
ASSERT_from_bufferDeallocate_SIZE(buffer_count); // all input buffers deallocated
|
ASSERT_from_dataReturnOut_SIZE(buffer_count); // all input buffers ownership were returned
|
||||||
ASSERT_from_frameOut_SIZE(1); // Exactly one frame was sent out
|
ASSERT_from_dataOut_SIZE(1); // Exactly one frame was sent out
|
||||||
ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // No data left in ring buffer
|
ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // No data left in ring buffer
|
||||||
ASSERT_EQ(this->fromPortHistory_frameOut->at(0).data.getSize(), frame_size); // accumulated buffer size
|
ASSERT_EQ(this->fromPortHistory_dataOut->at(0).data.getSize(), frame_size); // accumulated buffer size
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameAccumulatorTester ::testAccumulateBuffersEmitManyFrames() {
|
void FrameAccumulatorTester ::testAccumulateBuffersEmitManyFrames() {
|
||||||
@ -137,17 +142,27 @@ void FrameAccumulatorTester ::testAccumulateBuffersEmitManyFrames() {
|
|||||||
this->mockAccumulateFullFrame(frame_size, buffer_count);
|
this->mockAccumulateFullFrame(frame_size, buffer_count);
|
||||||
total_buffer_received += buffer_count;
|
total_buffer_received += buffer_count;
|
||||||
|
|
||||||
ASSERT_from_bufferDeallocate_SIZE(total_buffer_received); // all input buffers deallocated
|
ASSERT_from_dataReturnOut_SIZE(total_buffer_received); // all input buffers returned
|
||||||
ASSERT_from_frameOut_SIZE(i+1); // Exactly one frame was sent out
|
ASSERT_from_dataOut_SIZE(i+1); // Exactly one frame was sent out
|
||||||
ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // No data left in ring buffer
|
ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // No data left in ring buffer
|
||||||
ASSERT_EQ(this->fromPortHistory_frameOut->at(i).data.getSize(), frame_size); // accumulated buffer size
|
ASSERT_EQ(this->fromPortHistory_dataOut->at(i).data.getSize(), frame_size); // accumulated buffer size
|
||||||
}
|
}
|
||||||
// Final checks
|
// Final checks
|
||||||
ASSERT_from_bufferDeallocate_SIZE(total_buffer_received); // all input buffers deallocated
|
ASSERT_from_dataReturnOut_SIZE(total_buffer_received); // all input buffers returned
|
||||||
ASSERT_from_frameOut_SIZE(max_iters); // Exactly max_iters frames were sent out
|
ASSERT_from_dataOut_SIZE(max_iters); // Exactly max_iters frames were sent out
|
||||||
ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // No data left in ring buffer
|
ASSERT_EQ(this->component.m_inRing.get_allocated_size(), 0); // No data left in ring buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FrameAccumulatorTester ::testBufferReturnDeallocation() {
|
||||||
|
U8 data[1];
|
||||||
|
Fw::Buffer buffer(data, sizeof(data));
|
||||||
|
ComCfg::FrameContext context;
|
||||||
|
this->invoke_to_dataReturnIn(0, buffer, context);
|
||||||
|
ASSERT_from_bufferDeallocate_SIZE(1); // incoming buffer should be deallocated
|
||||||
|
ASSERT_EQ(this->fromPortHistory_bufferDeallocate->at(0).fwBuffer.getData(), data);
|
||||||
|
ASSERT_EQ(this->fromPortHistory_bufferDeallocate->at(0).fwBuffer.getSize(), sizeof(data));
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Helper functions
|
// Helper functions
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
@ -163,6 +178,7 @@ void FrameAccumulatorTester ::mockAccumulateFullFrame(U32& frame_size, U32& buff
|
|||||||
U32 buffer_size;
|
U32 buffer_size;
|
||||||
Fw::Buffer buffer(data, 0);
|
Fw::Buffer buffer(data, 0);
|
||||||
U32 accumulated_size = 0;
|
U32 accumulated_size = 0;
|
||||||
|
ComCfg::FrameContext context;
|
||||||
|
|
||||||
// Send multiple buffers with MORE_DATA_NEEDED
|
// Send multiple buffers with MORE_DATA_NEEDED
|
||||||
for (U32 i = 0; i < iters; i++) {
|
for (U32 i = 0; i < iters; i++) {
|
||||||
@ -171,7 +187,7 @@ void FrameAccumulatorTester ::mockAccumulateFullFrame(U32& frame_size, U32& buff
|
|||||||
buffer.setSize(buffer_size);
|
buffer.setSize(buffer_size);
|
||||||
// Detector reports MORE_DATA_NEEDED and size needed bigger than accumulated size so far
|
// 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->mockDetector.set_next_result(FrameDetector::Status::MORE_DATA_NEEDED, accumulated_size + 1);
|
||||||
this->invoke_to_dataIn(0, buffer);
|
this->invoke_to_dataIn(0, buffer, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send last buffer with FRAME_DETECTED
|
// Send last buffer with FRAME_DETECTED
|
||||||
@ -181,7 +197,7 @@ void FrameAccumulatorTester ::mockAccumulateFullFrame(U32& frame_size, U32& buff
|
|||||||
// Send last buffer with finally FRAME_DETECTED and total accumulated + last buffer
|
// Send last buffer with finally FRAME_DETECTED and total accumulated + last buffer
|
||||||
this->mockDetector.set_next_result(FrameDetector::Status::FRAME_DETECTED, accumulated_size);
|
this->mockDetector.set_next_result(FrameDetector::Status::FRAME_DETECTED, accumulated_size);
|
||||||
// Receive the last buffer on dataIn
|
// Receive the last buffer on dataIn
|
||||||
this->invoke_to_dataIn(0, buffer);
|
this->invoke_to_dataIn(0, buffer, context);
|
||||||
frame_size = accumulated_size;
|
frame_size = accumulated_size;
|
||||||
buffer_count = iters + 1;
|
buffer_count = iters + 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,6 +63,9 @@ class FrameAccumulatorTester : public FrameAccumulatorGTestBase {
|
|||||||
//! Test accumulation of multiple random-size buffer into frames successively
|
//! Test accumulation of multiple random-size buffer into frames successively
|
||||||
void testAccumulateBuffersEmitManyFrames();
|
void testAccumulateBuffersEmitManyFrames();
|
||||||
|
|
||||||
|
//! Test returning ownership of a buffer
|
||||||
|
void testBufferReturnDeallocation();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Helper functions
|
// Helper functions
|
||||||
|
|||||||
@ -3,10 +3,10 @@
|
|||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
@ Data to be sent on the wire (coming in to the component)
|
@ Data to be sent on the wire (coming in to the component)
|
||||||
sync input port comDataIn: Svc.ComDataWithContext
|
sync input port dataIn: Svc.ComDataWithContext
|
||||||
|
|
||||||
@ Data received from the wire (coming out of the component)
|
@ Data received from the wire (going out of the component)
|
||||||
output port comDataOut: Fw.BufferSend
|
output port dataOut: Svc.ComDataWithContext
|
||||||
|
|
||||||
@ Status of the last transmission
|
@ Status of the last transmission
|
||||||
output port comStatusOut: Fw.SuccessCondition
|
output port comStatusOut: Fw.SuccessCondition
|
||||||
@ -15,5 +15,8 @@ output port comStatusOut: Fw.SuccessCondition
|
|||||||
# Memory management
|
# Memory management
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
@ Returning ownership of data that came in on comDataIn
|
@ Port returning ownership of data that came in on dataIn
|
||||||
output port dataReturnOut: Svc.ComDataWithContext
|
output port dataReturnOut: Svc.ComDataWithContext
|
||||||
|
|
||||||
|
@ Port receiving back ownership of buffer sent out on dataOut
|
||||||
|
sync input port dataReturnIn: Svc.ComDataWithContext
|
||||||
|
|||||||
@ -1,5 +1,11 @@
|
|||||||
@ Port to receive framed data, with optional context
|
@ Port to receive framed data, with optional context
|
||||||
guarded input port framedIn: Svc.ComDataWithContext
|
guarded input port dataIn: Svc.ComDataWithContext
|
||||||
|
|
||||||
@ Port to output deframed data, with optional context
|
@ Port to output deframed data, with optional context
|
||||||
output port deframedOut: Svc.ComDataWithContext
|
output port dataOut: Svc.ComDataWithContext
|
||||||
|
|
||||||
|
@ Port for returning ownership of received buffers to deframe
|
||||||
|
output port dataReturnOut: Svc.ComDataWithContext
|
||||||
|
|
||||||
|
@ Port receiving back ownership of sent buffers
|
||||||
|
sync input port dataReturnIn: Svc.ComDataWithContext
|
||||||
|
|||||||
@ -1,5 +1,11 @@
|
|||||||
@ Receive raw bytes from a ComInterface (e.g. ComStub)
|
@ Receive raw bytes from a ComInterface (e.g. ComStub)
|
||||||
guarded input port dataIn: Fw.BufferSend
|
guarded input port dataIn: Svc.ComDataWithContext
|
||||||
|
|
||||||
@ Port for sending an extracted frame out
|
@ Port for sending an extracted frame out
|
||||||
output port frameOut: Svc.ComDataWithContext
|
output port dataOut: Svc.ComDataWithContext
|
||||||
|
|
||||||
|
@ Port for returning ownership of buffers received on dataIn
|
||||||
|
output port dataReturnOut: Svc.ComDataWithContext
|
||||||
|
|
||||||
|
@ Port receiving back ownership of buffers sent on frameOut
|
||||||
|
sync input port dataReturnIn: Svc.ComDataWithContext
|
||||||
|
|||||||
@ -1,9 +1,21 @@
|
|||||||
|
# ---------------------------------------------
|
||||||
|
# Router <-> Deframers
|
||||||
|
# ---------------------------------------------
|
||||||
@ Receiving data (Fw::Buffer) to be routed with optional context to help with routing
|
@ Receiving data (Fw::Buffer) to be routed with optional context to help with routing
|
||||||
sync input port dataIn: Svc.ComDataWithContext
|
sync input port dataIn: Svc.ComDataWithContext
|
||||||
|
|
||||||
|
@ Port for returning ownership of data (includes Fw.Buffer) received on dataIn
|
||||||
|
output port dataReturnOut: Svc.ComDataWithContext
|
||||||
|
|
||||||
|
# ---------------------------------------------
|
||||||
|
# Router <-> CmdDispatch/FileUplink
|
||||||
|
# ---------------------------------------------
|
||||||
@ Port for sending file packets as Fw::Buffer (ownership passed to receiver)
|
@ Port for sending file packets as Fw::Buffer (ownership passed to receiver)
|
||||||
output port fileOut: Fw.BufferSend
|
output port fileOut: Fw.BufferSend
|
||||||
|
|
||||||
|
@ Port for receiving ownership back of buffers sent on fileOut
|
||||||
|
sync input port fileBufferReturnIn: Fw.BufferSend
|
||||||
|
|
||||||
@ Port for sending command packets as Fw::ComBuffers
|
@ Port for sending command packets as Fw::ComBuffers
|
||||||
output port commandOut: Fw.Com
|
output port commandOut: Fw.Com
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ The Svc interfaces are a set of `.fppi` files that define FPP interfaces for com
|
|||||||
|
|
||||||
## Svc/ComInterface
|
## Svc/ComInterface
|
||||||
|
|
||||||
The `Svc/ComInterface` is an interface for implementing the [Communications Adapter Interface](../../../docs/reference/communication-adapter-interface.md).
|
The `Svc/ComInterface` is an interface for implementing the [Communications Adapter Interface](../../../docs/reference/communication-adapter-interface.md). [`Svc::ComStub`](../../ComStub/docs/sdd.md) implements this interface and uses a ByteStream driver to send and receive data on a TCP/UDP/UART link, and is often used in development and early testing.
|
||||||
|
|
||||||
## Svc/DeframerInterface
|
## Svc/DeframerInterface
|
||||||
|
|
||||||
|
|||||||
@ -13,50 +13,51 @@ protocol to ensure that data messages do not overload a communication interface.
|
|||||||
|
|
||||||
## Ports
|
## Ports
|
||||||
|
|
||||||
The communication adapter interface is composed of three ports. These ports are used to transmit outgoing data through
|
The `Svc.ComDataWithContext` port type is used to transmit comms data between components of the F´ system. This port type is composed of a `Fw::Buffer` object and a `ComCfg::FrameContext` object. The `Fw::Buffer` object is used to transmit the data, while the `ComCfg::FrameContext` object is used to provide context for the data being transmitted, such as header information once the header has been consumed.
|
||||||
|
|
||||||
|
The communication adapter interface is composed of five ports. These ports are used to transmit outgoing data through
|
||||||
some communication hardware and receive incoming data from that same hardware. These ports share types with the byte
|
some communication hardware and receive incoming data from that same hardware. These ports share types with the byte
|
||||||
stream driver model for backwards compatibility.
|
stream driver model for backwards compatibility.
|
||||||
|
|
||||||
| Kind | Suggested Name | Port Type | Usage |
|
| Kind | Suggested Name | Port Type | Description |
|
||||||
|----------|----------------|-----------------------|----------------------------------------------------------------|
|
|----------|----------------|--------------------------|----------------------------------------------------------------|
|
||||||
| `input` | `comDataIn` | `Svc.ComDataWithContext` | Port receiving `Fw::Buffer` objects for outgoing transmission (to be sent on the wire) |
|
| `input` | `dataIn` | `Svc.ComDataWithContext` | Port receiving data for outgoing transmission (to be sent on the wire) |
|
||||||
| `output` | `comDataOut` | `Fw.BufferSend` | Port producing incoming `Fw::Buffer` objects (received on the wire) |
|
| `output` | `dataOut` | `Svc.ComDataWithContext` | Port producing data from incoming transmissions (received on the wire) |
|
||||||
| `output` | `dataReturnOut` | `Svc.ComDataWithContext` | Port returning ownership of the `Fw::Buffer` sent on the `comDataIn` port |
|
| `output` | `dataReturnOut`| `Svc.ComDataWithContext` | Port for sending back ownership of the `Fw::Buffer` received on the `dataIn` port |
|
||||||
| `output` | `comStatusOut` | `Fw.SuccessCondition` | Port indicating status of outgoing transmission. See protocol. |
|
| `input` | `dataReturnIn` | `Svc.ComDataWithContext` | Port for receiving back ownership of the `Fw::Buffer` sent on the `dataOut` port |
|
||||||
|
| `output` | `comStatusOut` | `Fw.SuccessCondition` | Port indicating status of outgoing transmission. See protocol. |
|
||||||
|
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> About buffer management: after receiving a buffer on the `comDataIn` port, the communication component must return ownership of said buffer through the `dataReturnOut` port. The common scenario is to connect `comDataIn` and `dataReturnOut` to the same component, so that the sender can handle deallocation.
|
> About buffer management: after receiving a buffer on the `dataIn` port, the communication component must return ownership of said buffer through the `dataReturnOut` port. The common scenario is to connect `dataIn` and `dataReturnOut` to the same component, so that the sender can handle deallocation. This is done with a callback so that `dataIn` can be an asynchronous port if needed.
|
||||||
> This is done with a callback so that `comDataIn` can be an asynchronous port if needed.
|
> Similarly, the buffer sent out on `dataOut` is expected to be returned through the `dataReturnIn` port.
|
||||||
|
|
||||||
### comDataIn Description
|
### dataIn Description
|
||||||
|
|
||||||
This port receives data from an F´ application in the form of an argument of `Fw::Buffer` type. This data is intended to
|
This port receives data from an F´ application in the form of an argument of `Fw::Buffer` type and a `ComCfg::FrameContext` context. This data is intended to be sent out the communications interface managed by this component (often referred to as _the wire_). From the perspective of the application this is the _outgoing data_ port.
|
||||||
be sent out the communications interface managed by this component (often referred to as _the wire_). From the perspective of the application this is
|
|
||||||
the outgoing data port.
|
|
||||||
|
|
||||||
### comDataOut Description
|
### dataOut Description
|
||||||
|
|
||||||
This port receives data from the communication interface managed by this component and provides it to the F´ application
|
This port produces data to the F´ application once it is received on the wire. From the perspective of the application this is the _incoming data_ port.
|
||||||
in the form of an argument of `Fw::Buffer` type. From the perspective of the application this is the incoming data port.
|
|
||||||
|
|
||||||
### dataReturnOut Description
|
### dataReturnOut Description
|
||||||
|
|
||||||
This port is used to receive a callback returning ownership of the `Fw::Buffer` object that was sent on the `comDataIn` port. Ownership is typically returned to the sender. A callback is used so that `comDataIn` may be an asynchronous port if needed.
|
This port is used to send a callback returning ownership of the `Fw::Buffer` object that was received on the `dataIn` port. Ownership is typically returned to the sender. A callback is used so that `dataIn` may be an asynchronous port if needed.
|
||||||
|
|
||||||
|
### dataReturnIn Description
|
||||||
|
|
||||||
|
This port is used to receive a callback returning ownership of the `Fw::Buffer` object that was sent on the `dataOut` port. Ownership is typically returned from the receiver of dataOut. A callback is used so that `dataOut` may be an asynchronous call if needed.
|
||||||
|
|
||||||
### comStatusOut Description
|
### comStatusOut Description
|
||||||
|
|
||||||
This port carries a status of `Fw::Success::SUCCESS` or `Fw::Success::FAILURE` typically in response to a call to the
|
This port carries a status of `Fw::Success::SUCCESS` or `Fw::Success::FAILURE` typically in response to a call to the `dataIn` port described above.
|
||||||
`comDataIn` port described above.
|
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> it is critical to obey the protocol as described in the protocol section below.
|
> it is critical to obey the protocol as described in the protocol section below.
|
||||||
|
|
||||||
## Communication Queue Protocol
|
## Communication Queue Protocol
|
||||||
|
|
||||||
`Svc::ComQueue` queues messages until the communication adapter is ready to receive these messages. For each
|
`Svc::ComQueue` queues messages until the communication adapter is ready to receive these messages. For each `Fw::Success::SUCCESS` message received, `Svc::ComQueue` will emit one message. `Svc::ComQueue` will not emit messages at any other time. This implies several things:
|
||||||
Fw::Success::SUCCESS message received, `Svc::ComQueue` will emit one message. `Svc::ComQueue` will not emit messages
|
|
||||||
at any other time. This implies several things:
|
|
||||||
|
|
||||||
1. An initial `Fw::Success::SUCCESS` message must be sent to `Svc::ComQueue` to initiate data flow
|
1. An initial `Fw::Success::SUCCESS` message must be sent to `Svc::ComQueue` to initiate data flow
|
||||||
2. A `Fw::Success::SUCCESS` must be eventually received in response to each message for data to continue to flow
|
2. A `Fw::Success::SUCCESS` must be eventually received in response to each message for data to continue to flow
|
||||||
@ -65,22 +66,19 @@ These implications are discussed in more depth in the appropriate protocol secti
|
|||||||
|
|
||||||
## Framer Status Protocol
|
## Framer Status Protocol
|
||||||
|
|
||||||
Framing typically happens between `Svc::ComQueue` and a communications adapter. The `Svc::Framer` component implements
|
Framing typically happens between `Svc::ComQueue` and a communications adapter. The action taken by this protocol is dependent on the number of framed messages (frames) sent to the communication adapter in response to each message received from `Svc::ComQueue`.
|
||||||
this protocol in order to interoperate with these other components. The action taken by this protocol is dependent on
|
|
||||||
the number of framed messages (frames) sent to the communication adapter in response to each message received from
|
The `Svc::FprimeFramer` implements the Framer status protocol by emitting a frame for each message received from `Svc::ComQueue`. Therefore, the `Svc::FprimeFramer` receives a status from the communication adapter on each sent frame and forwards it back to `Svc::ComQueue`. This allows the data flow to continue, or pause if the communication adapter is unable to receive more data.
|
||||||
`Svc::ComQueue`.
|
|
||||||
|
Framer implementations may choose to implement a different behavior. For example, a framer may choose to accept multiple messages from `Svc::ComQueue` and concatenate them in a single frame. In this case, the framer implementation must manage the flow of statuses accordingly. This is summarized in the table below.
|
||||||
|
|
||||||
| Produced Frames | Action | Rationale |
|
| Produced Frames | Action | Rationale |
|
||||||
|-----------------|----------------------------------------|----------------------------------------------------------|
|
|-----------------|----------------------------------------|----------------------------------------------------------|
|
||||||
| 0 | Send one `Fw::Success::SUCCESS` status | Data must continue to flow to produce a frame |
|
| 0 | Send one `Fw::Success::SUCCESS` status | Data must continue to flow to produce a frame |
|
||||||
| 1 | Pass through received statuses | Frame produced and communication adapter produces status |
|
| 1 | Pass through received status from ComQueue | Frame produced and communication adapter produces status |
|
||||||
|
|
||||||
When a frame is produced the communication adapter is responsible for determining the status of the message and its
|
> [!IMPORTANT]
|
||||||
statuses must be passed through. When no frame is produced, the communication adapter does not receive a frame and is
|
> All framer implementations must pass through the initiation message from the communication adapter to start data flow.
|
||||||
incapable of producing a status. `Svc::Framer` must act appropriately in this case.
|
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> `Svc::Framer` must also pass through the initiation message from the communication adapter to start data flow.
|
|
||||||
|
|
||||||
## Communication Adapter Protocol
|
## Communication Adapter Protocol
|
||||||
|
|
||||||
@ -89,16 +87,16 @@ transmissions. This is done with the `comStatus` port. A communication status is
|
|||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|----------------------|-----------------------------------------------------------------------------------|
|
|----------------------|-----------------------------------------------------------------------------------|
|
||||||
| Fw::Success::SUCCESS | *Communication adapter* transmission succeeded and is ready for more data. |
|
| Fw::Success::SUCCESS | *Communication adapter* transmission succeeded and is ready for more data. |
|
||||||
| Fw::Success::FAILURE | Last transmission failed; *communication adapter* is unable to receive more data. |
|
| Fw::Success::FAILURE | Last transmission failed; *communication adapter* is unable to receive more data. |
|
||||||
|
|
||||||
* Fw::Success::SUCCESS may also indicate a connection/reconnection success when data flow must be initiated.
|
* Fw::Success::SUCCESS may also indicate a connection/reconnection success when data flow must be initiated.
|
||||||
|
|
||||||
A *Communication Adapter* shall emit either Fw::Success::SUCCESS or Fw::Success::FAILURE via the `comStatus` port once
|
A *Communication Adapter* shall emit either Fw::Success::SUCCESS or Fw::Success::FAILURE via the `comStatusOut` port once
|
||||||
for each call received on `comDataIn`. Additionally, a *Communication Adapter* shall emit Fw::Success::SUCCESS once at
|
for each call received on `dataIn`. Additionally, a *Communication Adapter* shall emit Fw::Success::SUCCESS once at
|
||||||
startup to indicate communication is initially ready and once after each Fw::Success::FAILURE event to indicate that
|
startup to indicate communication is initially ready and once after each Fw::Success::FAILURE event to indicate that
|
||||||
communication has been restored. By emitting Fw::Success::SUCCESS after any failure, the communication adapter ensures
|
communication has been restored. By emitting Fw::Success::SUCCESS after any failure, the communication adapter ensures
|
||||||
that each received message eventually results in a Fw::Success::SUCCESS.
|
that each received message eventually results in a Fw::Success::SUCCESS.
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> It is imperative that *Communication Adapters* implement the `comStatus` protocol correctly.
|
> It is imperative that *Communication Adapters* implement the `comStatusOut` protocol correctly.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user