Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split Deframer into FrameAccumulator and Router #2900

Closed
wants to merge 33 commits into from

Conversation

thomas-bc
Copy link
Collaborator

@thomas-bc thomas-bc commented Sep 25, 2024

Related Issue(s)
Has Unit Tests (y/n)
Documentation Included (y/n)

Change Description

Split up Deframer into 3 components:

  • FrameAccumulator
  • Deframer
  • Router (rename UplinkRouter ?)

Using this new structure, adding in 2 new deframers for CCSDS SpacePacket and TM protocols.

Rationale

Better reusability and chaining of deframers, preliminary work for CCSDS integrations

TODO

!!! UPDATE fprime-util new --deployment

To discuss

class CRCWrapper {
protected:
//! \brief constructor setting initial value
CRCWrapper(TokenType initial) : m_crc(initial) {}

Check warning

Code scanning / CppCheck

Single-parameter constructors should be marked explicit. Warning

Single-parameter constructors should be marked explicit.
@thomas-bc thomas-bc added the Update Instructions Needed Need to add instructions in the release notes for updates. label Sep 25, 2024
Copy link

@github-advanced-security github-advanced-security bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check-spelling found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

class CRCWrapper {
protected:
//! \brief constructor setting initial value
CRCWrapper(TokenType initial) : m_crc(initial) {}

Check notice

Code scanning / CodeQL

Conditional compilation Note

Use of conditional compilation must be kept to a minimum.
@thomas-bc thomas-bc changed the title Add FrameAccumulator, Router and CCSDS deframers Split Deframer into FrameAccumulator and Router Sep 30, 2024
@LeStarch LeStarch added this to the Release v3.5.1 milestone Oct 7, 2024
@thomas-bc
Copy link
Collaborator Author

Will fix #2073

@ReggieMarr
Copy link

@thomas-bc @LeStarch I made a fork and kinda ran with it in my repo I found what I think is an issue with the frame detection here

Currently I'm workin to fix my TM framer impl but let me know if I'm stepping on anyones toes or going in a bad direction

Copy link
Contributor

@celskeggs celskeggs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for putting together these changes, @thomas-bc. I like the splitting out of the Router from the Deframer, but I'm not so sure I like splitting out the FrameAccumulator from the Deframer; this will actually make it less likely that I can use these stock components in my design. More details in individual comments.

remaining -= serSize;
}
// Either all the bytes from the data buffer must be processed, or the ring must be full
FW_ASSERT(remaining == 0 || this->m_inRing.get_free_size() == 0, static_cast<FwAssertArgType>(remaining));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that data will be silently dropped under some circumstances? Can we have a way to make it not silent (either a FATAL or some other kind of response)?

// No frame was detected or an unknown status was received
else {
// Discard a single byte of data and start again
m_inRing.rotate(1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we know how much data is invalid and should be discarded? Stepping through one byte at a time is likely to be needlessly slow. Yes, some frame detectors won't know how many bytes to discard, but some will.

output port frameAllocate: Fw.BufferGet

@ Port for sending an extracted frame out
output port frameOut: Fw.DataWithContext
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we send DataWithContext if there's always no context?

Svc/FrameAccumulator/FrameDetector.hpp Outdated Show resolved Hide resolved

protected:
TokenType m_value;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if this is my place to comment, since I won't be using the StartLengthCrcDetector, but I do want to say that I think all the templates in this file are needlessly convoluted. I don't see what benefit all of this abstraction adds. If you want to make it easy to implement a custom Start-Length-CRC detector, I'd just provide example code and tell people to copy it. It shouldn't be very complicated.

}
// TODO: add asserts?
data.setData(data.getData() + FrameConfig::HEADER_SIZE);
data.setSize(data.getSize() - FrameConfig::HEADER_SIZE - FrameConfig::CHECKSUM_SIZE);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect that this isn't going to be something you'll change, but I do want to note that modifying the buffer passed in as a reference seems dangerous. What if the caller didn't expect its buffer pointer and size to change? Yes, the caller should probably realize this could happen and not rely on it (especially since it should expect the data in the buffer to change anyway), but it still makes me nervous.

Svc/FrameAccumulator/FrameAccumulator.cpp Outdated Show resolved Hide resolved
@thomas-bc thomas-bc self-assigned this Nov 20, 2024
{
(void) memset(m_pollBuffer, 0, sizeof m_pollBuffer);
}
Deframer ::Deframer(const char* const compName) : DeframerComponentBase(compName) {}

Check notice

Code scanning / CodeQL

Use of basic integral type Note

compName uses the basic integral type char rather than a typedef with size and signedness.
DeframingProtocol::DEFRAMING_STATUS_SUCCESS;
// The ring buffer capacity
const NATIVE_UINT_TYPE ringCapacity = m_inRing.get_capacity();
void Deframer ::framedIn_handler(FwIndexType portNum, Fw::Buffer& data, Fw::Buffer& context) {

Check notice

Code scanning / CodeQL

Use of basic integral type Note

portNum uses the basic integral type int rather than a typedef with size and signedness.
// Component construction and destruction
// ----------------------------------------------------------------------

FrameAccumulator ::FrameAccumulator(const char* const compName) : FrameAccumulatorComponentBase(compName),

Check notice

Code scanning / CodeQL

Use of basic integral type Note

compName uses the basic integral type char rather than a typedef with size and signedness.
}
}

void FrameAccumulator ::configure(const FrameDetector& detector, NATIVE_UINT_TYPE allocationId,

Check notice

Code scanning / CodeQL

Use of basic integral type Note

allocationId uses the basic integral type unsigned int rather than a typedef with size and signedness.

void FrameAccumulator ::configure(const FrameDetector& detector, NATIVE_UINT_TYPE allocationId,
Fw::MemAllocator& allocator,
FwSizeType store_size

Check notice

Code scanning / CodeQL

Use of basic integral type Note

store_size uses the basic integral type unsigned long rather than a typedef with size and signedness.
U8* m_memory;

//! Identification used with the memory allocator
NATIVE_UINT_TYPE m_allocatorId;

Check notice

Code scanning / CodeQL

Use of basic integral type Note

m_allocatorId uses the basic integral type unsigned int rather than a typedef with size and signedness.
// Component construction and destruction
// ----------------------------------------------------------------------

Router ::Router(const char* const compName) : RouterComponentBase(compName) {}

Check notice

Code scanning / CodeQL

Use of basic integral type Note

compName uses the basic integral type char rather than a typedef with size and signedness.
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------

void Router ::dataIn_handler(NATIVE_INT_TYPE portNum, Fw::Buffer& packetBuffer, Fw::Buffer& contextBuffer) {

Check notice

Code scanning / CodeQL

Use of basic integral type Note

portNum uses the basic integral type int rather than a typedef with size and signedness.
}

// Whether to deallocate the packet buffer
bool deallocate = true;

Check notice

Code scanning / CodeQL

Use of basic integral type Note

deallocate uses the basic integral type bool rather than a typedef with size and signedness.
}
}

void Router ::cmdResponseIn_handler(NATIVE_INT_TYPE portNum,

Check notice

Code scanning / CodeQL

Use of basic integral type Note

portNum uses the basic integral type int rather than a typedef with size and signedness.

this->deframedOut_out(0, data, context);

Check warning

Code scanning / CodeQL

Unchecked function argument Warning

This use of parameter context has not been checked.
bool recoverable = false;
FW_ASSERT(std::numeric_limits<NATIVE_INT_TYPE>::max() >= store_size, static_cast<FwAssertArgType>(store_size));
NATIVE_UINT_TYPE store_size_int = static_cast<NATIVE_UINT_TYPE>(store_size);
U8* data = reinterpret_cast<U8*>(allocator.allocate(allocationId, store_size_int, recoverable));

Check warning

Code scanning / CodeQL

Unchecked function argument Warning

This use of parameter allocator has not been checked.
bool recoverable = false;
FW_ASSERT(std::numeric_limits<NATIVE_INT_TYPE>::max() >= store_size, static_cast<FwAssertArgType>(store_size));
NATIVE_UINT_TYPE store_size_int = static_cast<NATIVE_UINT_TYPE>(store_size);
U8* data = reinterpret_cast<U8*>(allocator.allocate(allocationId, store_size_int, recoverable));

Check warning

Code scanning / CodeQL

Unchecked function argument Warning

This use of parameter allocationId has not been checked.
FW_ASSERT(store_size_int >= store_size);
m_inRing.setup(data, store_size_int);

this->m_detector = &detector;

Check warning

Code scanning / CodeQL

Unchecked function argument Warning

This use of parameter detector has not been checked.
// Check whether there is data to process
if (status.e == Drv::RecvStatus::RECV_OK) {
// There is: process the data
this->processBuffer(buffer);

Check warning

Code scanning / CodeQL

Unchecked function argument Warning

This use of parameter buffer has not been checked.
this->processBuffer(buffer);
}
// Deallocate the buffer
this->dataDeallocate_out(0, buffer);

Check warning

Code scanning / CodeQL

Unchecked function argument Warning

This use of parameter buffer has not been checked.
}

void FrameAccumulator ::processBuffer(Fw::Buffer& buffer) {
const U32 bufferSize = buffer.getSize();

Check warning

Code scanning / CodeQL

Unchecked function argument Warning

This use of parameter buffer has not been checked.
// Copy out data and rotate
FW_ASSERT(this->m_inRing.peek(buffer.getData(), static_cast<NATIVE_UINT_TYPE>(size_out)) == Fw::SerializeStatus::FW_SERIALIZE_OK);
buffer.setSize(static_cast<U32>(size_out));
m_inRing.rotate(static_cast<U32>(size_out));

Check warning

Code scanning / CodeQL

Unchecked return value Warning

The return value of non-void function
rotate
is not checked.
// No frame was detected or an unknown status was received
else {
// Discard a single byte of data and start again
m_inRing.rotate(1);

Check warning

Code scanning / CodeQL

Unchecked return value Warning

The return value of non-void function
rotate
is not checked.
FwPacketDescriptorType packetType = Fw::ComPacket::FW_PACKET_UNKNOWN;
Fw::SerializeStatus status = Fw::FW_SERIALIZE_OK;
{
Fw::SerializeBufferBase& serial = packetBuffer.getSerializeRepr();

Check warning

Code scanning / CodeQL

Unchecked function argument Warning

This use of parameter packetBuffer has not been checked.

this->deframedOut_out(0, data, context);

Check warning

Code scanning / CodeQL

Unchecked function argument

This use of parameter context has not been checked.
bool recoverable = false;
FW_ASSERT(std::numeric_limits<NATIVE_INT_TYPE>::max() >= store_size, static_cast<FwAssertArgType>(store_size));
NATIVE_UINT_TYPE store_size_int = static_cast<NATIVE_UINT_TYPE>(store_size);
U8* data = reinterpret_cast<U8*>(allocator.allocate(allocationId, store_size_int, recoverable));

Check warning

Code scanning / CodeQL

Unchecked function argument

This use of parameter allocator has not been checked.
bool recoverable = false;
FW_ASSERT(std::numeric_limits<NATIVE_INT_TYPE>::max() >= store_size, static_cast<FwAssertArgType>(store_size));
NATIVE_UINT_TYPE store_size_int = static_cast<NATIVE_UINT_TYPE>(store_size);
U8* data = reinterpret_cast<U8*>(allocator.allocate(allocationId, store_size_int, recoverable));

Check notice

Code scanning / CodeQL

Conditional compilation Note

Use of conditional compilation must be kept to a minimum.
FW_ASSERT(store_size_int >= store_size);
m_inRing.setup(data, store_size_int);

this->m_detector = &detector;

Check warning

Code scanning / CodeQL

Unchecked function argument

This use of parameter detector has not been checked.
// Check whether there is data to process
if (status.e == Drv::RecvStatus::RECV_OK) {
// There is: process the data
this->processBuffer(buffer);

Check warning

Code scanning / CodeQL

Unchecked function argument

This use of parameter buffer has not been checked.
this->processBuffer(buffer);
}
// Deallocate the buffer
this->dataDeallocate_out(0, buffer);

Check warning

Code scanning / CodeQL

Unchecked function argument

This use of parameter buffer has not been checked.
}

void FrameAccumulator ::processBuffer(Fw::Buffer& buffer) {
const U32 bufferSize = buffer.getSize();

Check warning

Code scanning / CodeQL

Unchecked function argument

This use of parameter buffer has not been checked.
// Copy out data and rotate
FW_ASSERT(this->m_inRing.peek(buffer.getData(), static_cast<NATIVE_UINT_TYPE>(size_out)) == Fw::SerializeStatus::FW_SERIALIZE_OK);
buffer.setSize(static_cast<U32>(size_out));
m_inRing.rotate(static_cast<U32>(size_out));

Check warning

Code scanning / CodeQL

Unchecked return value

The return value of non-void function [rotate](1) is not checked.
// No frame was detected or an unknown status was received
else {
// Discard a single byte of data and start again
m_inRing.rotate(1);

Check warning

Code scanning / CodeQL

Unchecked return value

The return value of non-void function [rotate](1) is not checked.
FwPacketDescriptorType packetType = Fw::ComPacket::FW_PACKET_UNKNOWN;
Fw::SerializeStatus status = Fw::FW_SERIALIZE_OK;
{
Fw::SerializeBufferBase& serial = packetBuffer.getSerializeRepr();

Check warning

Code scanning / CodeQL

Unchecked function argument

This use of parameter packetBuffer has not been checked.
{
(void) memset(m_pollBuffer, 0, sizeof m_pollBuffer);
}
Deframer ::Deframer(const char* const compName) : DeframerComponentBase(compName) {}

Check notice

Code scanning / CodeQL

More than one statement per line Note

This line contains 2 statements; only one is allowed.
// ----------------------------------------------------------------------

FrameAccumulator ::FrameAccumulator(const char* const compName) : FrameAccumulatorComponentBase(compName),
m_detector(nullptr), m_memoryAllocator(nullptr), m_memory(nullptr), m_allocatorId(0) {}

Check notice

Code scanning / CodeQL

More than one statement per line Note

This line contains 2 statements; only one is allowed.
}
}

void FrameAccumulator ::configure(const FrameDetector& detector, NATIVE_UINT_TYPE allocationId,

Check notice

Code scanning / CodeQL

Long function without assertion Note

All functions of more than 10 lines should have at least one assertion.
this->dataDeallocate_out(0, buffer);
}

void FrameAccumulator ::processBuffer(Fw::Buffer& buffer) {

Check notice

Code scanning / CodeQL

Long function without assertion Note

All functions of more than 10 lines should have at least one assertion.
FW_ASSERT(remaining == 0 || this->m_inRing.get_free_size() == 0, static_cast<FwAssertArgType>(remaining));
}

void FrameAccumulator ::processRing() {

Check notice

Code scanning / CodeQL

Function too long Note

processRing has too many lines (81, while 60 are allowed).
FW_ASSERT(remaining == 0 || this->m_inRing.get_free_size() == 0, static_cast<FwAssertArgType>(remaining));
}

void FrameAccumulator ::processRing() {

Check notice

Code scanning / CodeQL

Long function without assertion Note

All functions of more than 10 lines should have at least one assertion.
// Component construction and destruction
// ----------------------------------------------------------------------

Router ::Router(const char* const compName) : RouterComponentBase(compName) {}

Check notice

Code scanning / CodeQL

More than one statement per line Note

This line contains 2 statements; only one is allowed.

Router ::Router(const char* const compName) : RouterComponentBase(compName) {}

Router ::~Router() {}

Check notice

Code scanning / CodeQL

More than one statement per line Note

This line contains 2 statements; only one is allowed.
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------

void Router ::dataIn_handler(NATIVE_INT_TYPE portNum, Fw::Buffer& packetBuffer, Fw::Buffer& contextBuffer) {

Check notice

Code scanning / CodeQL

Function too long Note

dataIn_handler has too many lines (64, while 60 are allowed).
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------

void Router ::dataIn_handler(NATIVE_INT_TYPE portNum, Fw::Buffer& packetBuffer, Fw::Buffer& contextBuffer) {

Check notice

Code scanning / CodeQL

Long function without assertion Note

All functions of more than 10 lines should have at least one assertion.
{
(void) memset(m_pollBuffer, 0, sizeof m_pollBuffer);
}
Deframer ::Deframer(const char* const compName) : DeframerComponentBase(compName) {}

Check notice

Code scanning / CodeQL

More than one statement per line

This line contains 2 statements; only one is allowed.
// ----------------------------------------------------------------------

FrameAccumulator ::FrameAccumulator(const char* const compName) : FrameAccumulatorComponentBase(compName),
m_detector(nullptr), m_memoryAllocator(nullptr), m_memory(nullptr), m_allocatorId(0) {}

Check notice

Code scanning / CodeQL

More than one statement per line

This line contains 2 statements; only one is allowed.
}
}

void FrameAccumulator ::configure(const FrameDetector& detector, NATIVE_UINT_TYPE allocationId,

Check notice

Code scanning / CodeQL

Long function without assertion

All functions of more than 10 lines should have at least one assertion.
this->dataDeallocate_out(0, buffer);
}

void FrameAccumulator ::processBuffer(Fw::Buffer& buffer) {

Check notice

Code scanning / CodeQL

Long function without assertion

All functions of more than 10 lines should have at least one assertion.
FW_ASSERT(remaining == 0 || this->m_inRing.get_free_size() == 0, static_cast<FwAssertArgType>(remaining));
}

void FrameAccumulator ::processRing() {

Check notice

Code scanning / CodeQL

Function too long

processRing has too many lines (81, while 60 are allowed).
FW_ASSERT(remaining == 0 || this->m_inRing.get_free_size() == 0, static_cast<FwAssertArgType>(remaining));
}

void FrameAccumulator ::processRing() {

Check notice

Code scanning / CodeQL

Long function without assertion

All functions of more than 10 lines should have at least one assertion.
// Component construction and destruction
// ----------------------------------------------------------------------

Router ::Router(const char* const compName) : RouterComponentBase(compName) {}

Check notice

Code scanning / CodeQL

More than one statement per line

This line contains 2 statements; only one is allowed.

Router ::Router(const char* const compName) : RouterComponentBase(compName) {}

Router ::~Router() {}

Check notice

Code scanning / CodeQL

Use of basic integral type Note

compName uses the basic integral type char rather than a typedef with size and signedness.
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------

void Router ::dataIn_handler(NATIVE_INT_TYPE portNum, Fw::Buffer& packetBuffer, Fw::Buffer& contextBuffer) {

Check notice

Code scanning / CodeQL

Function too long

dataIn_handler has too many lines (64, while 60 are allowed).
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------

void Router ::dataIn_handler(NATIVE_INT_TYPE portNum, Fw::Buffer& packetBuffer, Fw::Buffer& contextBuffer) {

Check notice

Code scanning / CodeQL

Long function without assertion

All functions of more than 10 lines should have at least one assertion.
{
(void) memset(m_pollBuffer, 0, sizeof m_pollBuffer);
}
Deframer ::Deframer(const char* const compName) : DeframerComponentBase(compName) {}

Check notice

Code scanning / CodeQL

Use of basic integral type

compName uses the basic integral type char rather than a typedef with size and signedness.
DeframingProtocol::DEFRAMING_STATUS_SUCCESS;
// The ring buffer capacity
const NATIVE_UINT_TYPE ringCapacity = m_inRing.get_capacity();
void Deframer ::framedIn_handler(FwIndexType portNum, Fw::Buffer& data, Fw::Buffer& context) {

Check notice

Code scanning / CodeQL

Use of basic integral type

portNum uses the basic integral type int rather than a typedef with size and signedness.
// Component construction and destruction
// ----------------------------------------------------------------------

FrameAccumulator ::FrameAccumulator(const char* const compName) : FrameAccumulatorComponentBase(compName),

Check notice

Code scanning / CodeQL

Use of basic integral type

compName uses the basic integral type char rather than a typedef with size and signedness.
}
}

void FrameAccumulator ::configure(const FrameDetector& detector, NATIVE_UINT_TYPE allocationId,

Check notice

Code scanning / CodeQL

Use of basic integral type

allocationId uses the basic integral type unsigned int rather than a typedef with size and signedness.

void FrameAccumulator ::configure(const FrameDetector& detector, NATIVE_UINT_TYPE allocationId,
Fw::MemAllocator& allocator,
FwSizeType store_size

Check notice

Code scanning / CodeQL

Use of basic integral type

store_size uses the basic integral type unsigned long rather than a typedef with size and signedness.
U8* m_memory;

//! Identification used with the memory allocator
NATIVE_UINT_TYPE m_allocatorId;

Check notice

Code scanning / CodeQL

Use of basic integral type

m_allocatorId uses the basic integral type unsigned int rather than a typedef with size and signedness.
Svc/Router/Router.cpp Dismissed Show dismissed Hide dismissed
Svc/Router/Router.cpp Dismissed Show dismissed Hide dismissed
}

// Whether to deallocate the packet buffer
bool deallocate = true;

Check notice

Code scanning / CodeQL

Use of basic integral type

deallocate uses the basic integral type bool rather than a typedef with size and signedness.
}
}

void Router ::cmdResponseIn_handler(NATIVE_INT_TYPE portNum,

Check notice

Code scanning / CodeQL

Use of basic integral type

portNum uses the basic integral type int rather than a typedef with size and signedness.
@thomas-bc
Copy link
Collaborator Author

After further review, we're going to try to move away from the templated FrameDetectors and instead leverage FPP to specify the header format, and leverage the struct's serializing functions to do the grunt work.

Closing for now so as not to spam CI and yall with notifications - will reopen when we're closer to done

@thomas-bc thomas-bc closed this Feb 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Update Instructions Needed Need to add instructions in the release notes for updates.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Build uplink router Build A Frame Reassembler Break Apart Deframer
4 participants