mirror of
https://github.com/nasa/fprime.git
synced 2025-12-10 00:44:37 -06:00
Refactor FrameAccumulator not to assert on incorrect size_out (#4340)
* Refactor FrameAccumulator not to assert on incorrect size_out * spelling * revert port names * Update check logic
This commit is contained in:
parent
6bdd79f290
commit
c0b75fb06a
@ -116,19 +116,31 @@ void FrameAccumulator ::processRing() {
|
|||||||
// Detect must not consume data in the ring buffer
|
// Detect must not consume data in the ring buffer
|
||||||
FW_ASSERT(m_inRing.get_allocated_size() == remaining,
|
FW_ASSERT(m_inRing.get_allocated_size() == remaining,
|
||||||
static_cast<FwAssertArgType>(m_inRing.get_allocated_size()), static_cast<FwAssertArgType>(remaining));
|
static_cast<FwAssertArgType>(m_inRing.get_allocated_size()), static_cast<FwAssertArgType>(remaining));
|
||||||
|
|
||||||
|
// Drop frames that are too large to handle (can't fit in the accumulation circular buffer)
|
||||||
|
if (size_out > ringCapacity) {
|
||||||
|
// Detector reports a size_out larger than the accumulation buffer capacity, we will never be able
|
||||||
|
// to process it. Log a warning and discard a byte, then keep iterating to look for a new frame
|
||||||
|
this->log_WARNING_HI_FrameDetectionSizeError(size_out);
|
||||||
|
// Discard a single byte of data and start again
|
||||||
|
(void)this->m_inRing.rotate(1);
|
||||||
|
FW_ASSERT(m_inRing.get_allocated_size() == remaining - 1,
|
||||||
|
static_cast<FwAssertArgType>(m_inRing.get_allocated_size()),
|
||||||
|
static_cast<FwAssertArgType>(remaining));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// On successful detection, consume data from the ring buffer and place it into an allocated frame
|
// On successful detection, consume data from the ring buffer and place it into an allocated frame
|
||||||
if (status == FrameDetector::FRAME_DETECTED) {
|
if (status == FrameDetector::FRAME_DETECTED) {
|
||||||
// size_out must be set to the size of the buffer and must fit within the existing data
|
// size_out must be set (non-zero) and must fit within the remaining data
|
||||||
FW_ASSERT(size_out != 0);
|
FW_ASSERT(size_out != 0);
|
||||||
FW_ASSERT(size_out <= remaining, static_cast<FwAssertArgType>(size_out),
|
FW_ASSERT(size_out <= remaining, static_cast<FwAssertArgType>(size_out),
|
||||||
static_cast<FwAssertArgType>(remaining));
|
static_cast<FwAssertArgType>(remaining));
|
||||||
// check for overflow before casting down to U32
|
Fw::Buffer buffer = this->bufferAllocate_out(0, size_out);
|
||||||
FW_ASSERT(size_out <= std::numeric_limits<U32>::max());
|
|
||||||
Fw::Buffer buffer = this->bufferAllocate_out(0, static_cast<U32>(size_out));
|
|
||||||
if (buffer.isValid()) {
|
if (buffer.isValid()) {
|
||||||
// Copy data out of ring buffer into the allocated buffer
|
// Copy data out of ring buffer into the allocated buffer
|
||||||
Fw::SerializeStatus serialize_status = this->m_inRing.peek(buffer.getData(), size_out);
|
Fw::SerializeStatus serialize_status = this->m_inRing.peek(buffer.getData(), size_out);
|
||||||
buffer.setSize(static_cast<Fw::Buffer::SizeType>(size_out));
|
buffer.setSize(size_out);
|
||||||
FW_ASSERT(serialize_status == Fw::SerializeStatus::FW_SERIALIZE_OK);
|
FW_ASSERT(serialize_status == Fw::SerializeStatus::FW_SERIALIZE_OK);
|
||||||
// Consume (rotate) the data from the ring buffer
|
// Consume (rotate) the data from the ring buffer
|
||||||
serialize_status = this->m_inRing.rotate(size_out);
|
serialize_status = this->m_inRing.rotate(size_out);
|
||||||
@ -146,8 +158,6 @@ void FrameAccumulator ::processRing() {
|
|||||||
}
|
}
|
||||||
// More data needed
|
// More data needed
|
||||||
else if (status == FrameDetector::MORE_DATA_NEEDED) {
|
else if (status == FrameDetector::MORE_DATA_NEEDED) {
|
||||||
// size_out can never be larger than the capacity of the ring. Otherwise all uplink will fail.
|
|
||||||
FW_ASSERT(size_out < m_inRing.get_capacity(), static_cast<FwAssertArgType>(size_out));
|
|
||||||
// Detection should report "more is needed" and set size_out to something larger than available data
|
// Detection should report "more is needed" and set size_out to something larger than available data
|
||||||
FW_ASSERT(size_out > remaining, static_cast<FwAssertArgType>(size_out),
|
FW_ASSERT(size_out > remaining, static_cast<FwAssertArgType>(size_out),
|
||||||
static_cast<FwAssertArgType>(remaining));
|
static_cast<FwAssertArgType>(remaining));
|
||||||
|
|||||||
@ -5,7 +5,7 @@ module Svc {
|
|||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
# FrameAccumulator interface
|
# FrameAccumulator interface
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
import FrameAccumulator
|
import Svc.FrameAccumulator
|
||||||
|
|
||||||
@ Port for deallocating buffers holding extracted frames
|
@ Port for deallocating buffers holding extracted frames
|
||||||
output port bufferDeallocate: Fw.BufferSend
|
output port bufferDeallocate: Fw.BufferSend
|
||||||
@ -18,6 +18,11 @@ module Svc {
|
|||||||
severity warning high \
|
severity warning high \
|
||||||
format "Could not allocate a valid buffer to fit the detected frame"
|
format "Could not allocate a valid buffer to fit the detected frame"
|
||||||
|
|
||||||
|
@ A frame was detected whose size exceeds the internal accumulation buffer
|
||||||
|
@ capacity. No choice but to drop the frame.
|
||||||
|
event FrameDetectionSizeError(size_out: FwSizeType) \
|
||||||
|
severity warning high \
|
||||||
|
format "Reported size_out={} exceeds accumulation buffer capacity"
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Standard AC Ports for Events
|
# Standard AC Ports for Events
|
||||||
@ -25,11 +30,8 @@ module Svc {
|
|||||||
@ Port for requesting the current time
|
@ Port for requesting the current time
|
||||||
time get port timeCaller
|
time get port timeCaller
|
||||||
|
|
||||||
@ Port for sending textual representation of events
|
@ Ports for logging events
|
||||||
text event port logTextOut
|
import Fw.Event
|
||||||
|
|
||||||
@ Port for sending events to downlink
|
|
||||||
event port logOut
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,9 @@ The `Svc::FrameAccumulator` receives `Fw::Buffer` objects on its `dataIn` input
|
|||||||
- `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.
|
- `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.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> If the `Svc::FrameDetector` reports a `size_out` that is larger than the capacity of the internal accumulation buffer, the frame will cannot be processed. The `Svc::FrameAccumulator` logs a warning event, and continues searching for a new frame.
|
||||||
|
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
|
|||||||
@ -47,6 +47,11 @@ TEST(FrameAccumulator, testBufferReturnDeallocation) {
|
|||||||
tester.testBufferReturnDeallocation();
|
tester.testBufferReturnDeallocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(FrameAccumulator, testDetectionErrorHandling) {
|
||||||
|
Svc::FrameAccumulatorTester tester;
|
||||||
|
tester.testDetectionErrorHandling();
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|||||||
@ -160,6 +160,49 @@ void FrameAccumulatorTester ::testBufferReturnDeallocation() {
|
|||||||
ASSERT_EQ(this->fromPortHistory_bufferDeallocate->at(0).fwBuffer.getSize(), sizeof(data));
|
ASSERT_EQ(this->fromPortHistory_bufferDeallocate->at(0).fwBuffer.getSize(), sizeof(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FrameAccumulatorTester ::testDetectionErrorHandling() {
|
||||||
|
FwSizeType too_large_size = this->component.m_inRing.get_capacity() + 1;
|
||||||
|
// Using buffer_size=1 to simplify test since otherwise Accumulator will loop `buffer_size` times
|
||||||
|
Fw::Buffer::SizeType buffer_size = 1;
|
||||||
|
U8 data[buffer_size];
|
||||||
|
Fw::Buffer buffer(data, buffer_size);
|
||||||
|
ComCfg::FrameContext context;
|
||||||
|
|
||||||
|
// Too large size reported by detector should emit event and continue
|
||||||
|
this->mockDetector.set_next_result(FrameDetector::Status::FRAME_DETECTED, too_large_size);
|
||||||
|
this->invoke_to_dataIn(0, buffer, context);
|
||||||
|
// Checks
|
||||||
|
ASSERT_from_dataReturnOut_SIZE(1); // input buffer ownership was returned
|
||||||
|
ASSERT_from_dataOut_SIZE(0); // No frame was sent out
|
||||||
|
ASSERT_EVENTS_SIZE(1); // One event should be logged:
|
||||||
|
ASSERT_EVENTS_FrameDetectionSizeError_SIZE(1); // FrameDetectionSizeError
|
||||||
|
ASSERT_EVENTS_FrameDetectionSizeError(0, too_large_size); // with expected size_out
|
||||||
|
|
||||||
|
this->clearHistory();
|
||||||
|
|
||||||
|
// Too large size reported by detector should emit event and continue
|
||||||
|
this->mockDetector.set_next_result(FrameDetector::Status::MORE_DATA_NEEDED, too_large_size);
|
||||||
|
this->invoke_to_dataIn(0, buffer, context);
|
||||||
|
// Checks
|
||||||
|
ASSERT_from_dataReturnOut_SIZE(1); // input buffer ownership was returned
|
||||||
|
ASSERT_from_dataOut_SIZE(0); // No frame was sent out
|
||||||
|
ASSERT_EVENTS_SIZE(1); // One event should be logged:
|
||||||
|
ASSERT_EVENTS_FrameDetectionSizeError_SIZE(1); // FrameDetectionSizeError
|
||||||
|
ASSERT_EVENTS_FrameDetectionSizeError(0, too_large_size); // with expected size_out
|
||||||
|
|
||||||
|
this->clearHistory();
|
||||||
|
|
||||||
|
// Too large size reported by detector should emit event and continue
|
||||||
|
this->mockDetector.set_next_result(FrameDetector::Status::NO_FRAME_DETECTED, too_large_size);
|
||||||
|
this->invoke_to_dataIn(0, buffer, context);
|
||||||
|
// Checks
|
||||||
|
ASSERT_from_dataReturnOut_SIZE(1); // input buffer ownership was returned
|
||||||
|
ASSERT_from_dataOut_SIZE(0); // No frame was sent out
|
||||||
|
ASSERT_EVENTS_SIZE(1); // One event should be logged:
|
||||||
|
ASSERT_EVENTS_FrameDetectionSizeError_SIZE(1); // FrameDetectionSizeError
|
||||||
|
ASSERT_EVENTS_FrameDetectionSizeError(0, too_large_size); // with expected size_out
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Helper functions
|
// Helper functions
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|||||||
@ -66,6 +66,9 @@ class FrameAccumulatorTester : public FrameAccumulatorGTestBase {
|
|||||||
//! Test returning ownership of a buffer
|
//! Test returning ownership of a buffer
|
||||||
void testBufferReturnDeallocation();
|
void testBufferReturnDeallocation();
|
||||||
|
|
||||||
|
//! Test handling of errors from the FrameDetector (too large size_out)
|
||||||
|
void testDetectionErrorHandling();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Helper functions
|
// Helper functions
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user