F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
FprimeFrameDetector.cpp
Go to the documentation of this file.
1 // ======================================================================
2 // \title FprimeFrameDetector.hpp
3 // \author thomas-bc
4 // \brief hpp file for fprime frame detector definitions
5 // ======================================================================
6 
8 
9 namespace Svc {
10 namespace FrameDetectors {
11 
13  // If not enough data for header + trailer, report MORE_DATA_NEEDED
14  if (data.get_allocated_size() <
17  return Status::MORE_DATA_NEEDED;
18  }
19 
20  // NOTE: it is understood and accepted that the following code is not as efficient as it could technically be
21  // We are leveraging the FPP autocoded types to do the deserialization for us.
22  // In its current implementation, CircularBuffer is not a LinearBufferBase, which prevents us from deserializing
23  // directly from the CircularBuffer into FrameHeader/FrameTrailer. Instead, we have to copy the data into
24  // a temporary SerializeBuffer, and then deserialize from that buffer into the FrameHeader/FrameTrailer objects.
25  // A better implementation would be to have CircularBuffer implement a shared interface with LinearBufferBase,
26  // and then we could pass the CircularBuffer directly into the FrameHeader/FrameTrailer deserializers. This is left
27  // as a TODO for future improvement as it is a significant refactor
28 
31 
32  // ---------------- Frame Header ----------------
33  // Copy CircularBuffer data into linear buffer, for serialization into FrameHeader object
36  if (status != Fw::FW_SERIALIZE_OK) {
37  return Status::NO_FRAME_DETECTED;
38  }
40  status = header_ser_buffer.setBuffLen(FprimeProtocol::FrameHeader::SERIALIZED_SIZE);
41  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status));
42  // Attempt to deserialize data into the FrameHeader object
43  status = header.deserializeFrom(header_ser_buffer);
44  if (status != Fw::FW_SERIALIZE_OK) {
45  return Status::NO_FRAME_DETECTED;
46  }
47  // Check that deserialized start_word token matches expected value (default start_word value in the FPP object)
48  FprimeProtocol::FrameHeader default_value;
49  if (header.get_startWord() != default_value.get_startWord()) {
50  return Status::NO_FRAME_DETECTED;
51  }
52  // Validate size before proceeding.
53  // Use a static_assert to guarantee the two fixed overhead constants don't themselves overflow
54  // when added together. This is a compile-time check so it carries zero runtime cost, but it
55  // protects the runtime guard below if SERIALIZED_SIZE values are ever changed.
57  std::numeric_limits<FwSizeType>::max() - FprimeProtocol::FrameTrailer::SERIALIZED_SIZE,
58  "FrameHeader::SERIALIZED_SIZE + FrameTrailer::SERIALIZED_SIZE overflows FwSizeType");
59  constexpr FwSizeType header_trailer_overhead =
61 
62  // Guard: reject frames whose declared length would overflow FwSizeType when added to the
63  // fixed overhead. Using subtraction on unsigned types (as in the prior implementation) is
64  // fragile — if the constants change sign or width the subtraction itself can wrap silently.
65  // An explicit addition-based check is clearer and easier to audit.
66  if (header.get_lengthField() > std::numeric_limits<FwSizeType>::max() - header_trailer_overhead) {
67  // lengthField + overhead would overflow — frame is invalid
68  return Status::NO_FRAME_DETECTED;
69  }
70 
71  // We expect the frame size to be size of header + body (of size specified in header) + trailer.
72  // Overflow is impossible here: the guard above ensures
73  // lengthField <= MAX - header_trailer_overhead
74  const FwSizeType expected_frame_size = header.get_lengthField() + header_trailer_overhead;
75  // If the frame will never fit, then report NO_FRAME_DETECTED to drop the erroneous frame
76  if (data.get_capacity() < expected_frame_size) {
77  return Status::NO_FRAME_DETECTED;
78  }
79  // If the frame could fit but we haven't received enough data yet, report MORE_DATA_NEEDED
80  else if (data.get_allocated_size() < expected_frame_size) {
81  size_out = expected_frame_size;
82  return Status::MORE_DATA_NEEDED;
83  }
84 
85  // ---------------- Frame Trailer ----------------
88  status = data.peek(trailer_data, FprimeProtocol::FrameTrailer::SERIALIZED_SIZE,
90  if (status != Fw::FW_SERIALIZE_OK) {
91  return Status::NO_FRAME_DETECTED;
92  }
93  status = trailer_ser_buffer.setBuffLen(FprimeProtocol::FrameTrailer::SERIALIZED_SIZE);
94  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status));
95  // Deserialize trailer from circular buffer (peeked data) into trailer object
96  status = trailer.deserializeFrom(trailer_ser_buffer);
97  if (status != Fw::FW_SERIALIZE_OK) {
98  return Status::NO_FRAME_DETECTED;
99  }
100 
101  Utils::Hash hash;
102  Utils::HashBuffer hashBuffer;
103  // Compute CRC over the transmitted data (header + body).
104  // Safety invariant: the guard above ensures
105  // lengthField <= MAX - header_trailer_overhead
106  // <= MAX - HEADER_SIZE - TRAILER_SIZE
107  // < MAX - HEADER_SIZE
108  // so this addition cannot overflow. The assert makes that contract explicit at the
109  // point of use so it remains correct if this code is ever moved or refactored.
110  FW_ASSERT(header.get_lengthField() <=
111  std::numeric_limits<FwSizeType>::max() - FprimeProtocol::FrameHeader::SERIALIZED_SIZE,
112  static_cast<FwAssertArgType>(header.get_lengthField()));
114  hash.init();
115  for (FwSizeType i = 0; i < hash_field_size; i++) {
116  U8 byte = 0;
117  status = data.peek(byte, i);
118  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
119  hash.update(&byte, 1);
120  }
121  hash.finalize(hashBuffer);
122 
123  // Compare the transmitted CRC with the computed one
124  if (trailer.get_crcField() != hashBuffer.asBigEndianU32()) {
125  // CRC mismatch - there likely was data corruption. The F Prime protocol
126  // being very simple, we don't have a way to recover from this.
127  // So we report NO_FRAME_DETECTED and drop the frame
128  return Status::NO_FRAME_DETECTED;
129  }
130  // All checks passed - we have detected a frame of size expected_frame_size
131  size_out = expected_frame_size;
132  return Status::FRAME_DETECTED;
133 }
134 
135 } // namespace FrameDetectors
136 } // namespace Svc
void update(const void *const data, const FwSizeType len)
Definition: HashImpl.cpp:34
Serialization/Deserialization operation was successful.
PlatformSizeType FwSizeType
Fw::SerializeStatus deserializeFrom(Fw::SerialBufferBase &buffer, Fw::Endianness mode=Fw::Endianness::BIG)
Deserialization.
U32 get_crcField() const
Get member crcField.
Svc::FprimeProtocol::TokenType get_startWord() const
Get member startWord.
Status detect(const Types::CircularBuffer &data, FwSizeType &size_out) const override
detect if a frame is available within the circular buffer
void init()
Definition: HashImpl.cpp:30
Describes the frame header format for the F Prime communications protocol.
SerializeStatus
forward declaration for string
Status
status returned from the detection step
External serialize buffer with no copy semantics.
A generic interface for creating and comparing hash values.
Definition: Hash.hpp:24
FwSizeType get_allocated_size() const
Fw::SerializeStatus deserializeFrom(Fw::SerialBufferBase &buffer, Fw::Endianness mode=Fw::Endianness::BIG)
Deserialization.
Describes the frame trailer format for the F Prime communications protocol.
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:53
FwSizeType get_capacity() const
Fw::SerializeStatus peek(char &value, FwSizeType offset=0) const
U32 asBigEndianU32() const
Convert bytes 0 through 3 of the hash data to a big-Endian U32 value.
A container class for holding a hash buffer.
Definition: HashBuffer.hpp:26
RateGroupDivider component implementation.
SerializeStatus setBuffLen(Serializable::SizeType length) override
Set buffer length manually.
void finalize(HashBuffer &buffer) const
Definition: HashImpl.cpp:40
#define FW_ASSERT(...)
Definition: Assert.hpp:14
PlatformAssertArgType FwAssertArgType
The type of arguments to assert functions.
Svc::FprimeProtocol::TokenType get_lengthField() const
Get member lengthField.