F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
AosFramer.cpp
Go to the documentation of this file.
1 // ======================================================================
2 // \title AosFramer.cpp
3 // \author Will MacCormack (Aos Modifications)
4 // \brief cpp file for AosFramer component implementation class
5 // \details modified from thomas-bc's TmFramer
6 // ======================================================================
7 
11 
12 namespace Svc {
13 
14 namespace Ccsds {
15 // ----------------------------------------------------------------------
16 // Component construction and destruction
17 // ----------------------------------------------------------------------
18 
19 AosFramer ::AosFramer(const char* const compName) : AosFramerComponentBase(compName) {
20  // Default to FECF on, Max Sized if you don't override w/ another configure call
22 }
23 
25 
26 void AosFramer::configure(const U32 fixedFrameSize, const bool frameErrorControlField, const U8 idlePvns) {
27  // fixedFrameSize must be less than or equal to the maximum defined in ComCfg.fpp
28  FW_ASSERT(fixedFrameSize <= ComCfg::AosMaxFrameFixedSize, static_cast<FwAssertArgType>(fixedFrameSize));
29 
30  // AOS Frame Fixed Size must be at least large enough to hold header, trailer and data
32  (frameErrorControlField ? AOSTrailer::SERIALIZED_SIZE : 0),
33  static_cast<FwAssertArgType>(fixedFrameSize));
34 
35  // AOS Framer must be provided with a protocol to use for Idle Packets
36  // Currently, only SPP idle packing is supported
37  // EPP is more optimal, however
38  FW_ASSERT(idlePvns & PvnBitfield::SPP_MASK, static_cast<FwAssertArgType>(idlePvns));
39 
40  // FECF is constant for a given Physical Channel during a Mission Phase (4.1.6.1.3)
41  this->m_fecf = frameErrorControlField;
42 
43  // For each vc, init the buffer objects
44  for (U8 ind = 0; ind < sizeof(this->m_vcs) / sizeof(AosVc); ind++) {
45  AosVc& currentVc = this->m_vcs[ind];
46 
47  // Write the index for mapping a vc struct onto an output port
48  currentVc.vc_struct_index = ind;
49  currentVc.frame.buffer = {currentVc.frame.backer, fixedFrameSize};
50  // Set the bitmask of PVNs to use for idle packets
51  currentVc.idle_packet_types = idlePvns;
52  }
53 }
54 
55 // ----------------------------------------------------------------------
56 // Handler implementations for typed input ports
57 // ----------------------------------------------------------------------
58 
59 void AosFramer ::dataIn_handler(FwIndexType portNum, Fw::Buffer& data, const ComCfg::FrameContext& context) {
60  // Get the VC Struct for this buffer
61  const U8 vcId = context.get_vcId();
62  AosVc& currentVc = this->get_vc_struct(context);
63  FW_ASSERT(currentVc.virtualChannelId == vcId);
64 
65  // Ensure rest of the com stack is complying with the [communications adapter
66  // interface](docs/reference/communication-adapter-interface.md)
67  FW_ASSERT(currentVc.frame.state == BufferOwnershipState::OWNED,
68  static_cast<FwAssertArgType>(currentVc.frame.state));
69 
70  // AOS Header & M_PDU Header
71  // If this is the first fresh packet, set the M_PDU firstHeaderPointer
72  if (!currentVc.past_first_fresh_packet) {
73  // setup our headers if we have a packet + context ready to go
74  setup_header(context);
75  setup_m_pdu_header(context);
76 
77  // We now will only work with fresh packets for the rest of this frame
78  currentVc.past_first_fresh_packet = true;
79  }
80 
81  // Pack this packet
82  pack_pad_send(data, context);
83 }
84 
85 void AosFramer::compute_and_inject_fecf(AosVc& currentVc) {
86  // -------------------------------------------------
87  // Trailer (CRC)
88  // -------------------------------------------------
89  AOSTrailer trailer;
90 
91  const FwSizeType fecfStart = currentVc.frame.buffer.getSize() - AOSTrailer::SERIALIZED_SIZE;
92  // Compute CRC over the entire frame buffer minus the FECF trailer (Standard 4.1.6)
93  U16 crc = Ccsds::Utils::CRC16::compute(currentVc.frame.buffer.getData(), static_cast<U32>(fecfStart));
94  // Set the Frame Error Control Field (FECF)
95  trailer.set_fecf(crc);
96  // Move the serializer pointer to the end of the location where the trailer will be
97  // serialized
98  auto frameSerializer = currentVc.frame.buffer.getSerializer();
99  Fw::SerializeStatus status = frameSerializer.moveSerToOffset(fecfStart);
100  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
101 
102  status = frameSerializer.serializeFrom(trailer);
103  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
104 }
105 
106 void AosFramer ::comStatusIn_handler(FwIndexType portNum, Fw::Success& condition) {
107  // We just ask upstream for more packets
108  // comQueue decides to which VCs to allocate comStatus success
109  if (this->isConnected_comStatusOut_OutputPort(portNum)) {
110  // Forward the comStatus upstream
111  this->comStatusOut_out(portNum, condition);
112  }
113 }
114 
115 void AosFramer ::dataReturnIn_handler(FwIndexType portNum,
116  Fw::Buffer& frameBuffer,
117  const ComCfg::FrameContext& context) {
118  // Get the VC Struct for this buffer
119  AosVc& currentVc = this->get_vc_struct(context);
120 
121  // Assert that the returned buffer is the member, and set ownership state
122  FW_ASSERT(buffer_belongs(frameBuffer, currentVc.frame.backer, sizeof(currentVc.frame.backer)));
123  currentVc.frame.state = BufferOwnershipState::OWNED;
124 
125  // If we have an outstanding packet from the prior frame, pack it
126  if (currentVc.outstanding.packet.isValid()) {
127  this->pack_pad_send(currentVc.outstanding.packet, currentVc.outstanding.context, currentVc.outstanding.offset);
128  }
129 }
130 
131 AosFramer::AosVc& AosFramer ::get_vc_struct(const ComCfg::FrameContext& context) {
132  // MultiVc support would require looking up a vc struct ind given the vcIndex
133  // (unless you force them to be continuous and start at zero)
134  const U8 ind = 0;
135  AosVc& currentVc = this->m_vcs[ind];
136 
137  // Ensure configure was called
138  FW_ASSERT(currentVc.vc_struct_index == ind);
139  FW_ASSERT(currentVc.virtualChannelId == context.get_vcId());
140  return currentVc;
141 }
142 
143 void AosFramer ::setup_header(const ComCfg::FrameContext& context) {
144  // Get the VC Struct for this vc
145  AosVc& currentVc = this->get_vc_struct(context);
146 
147  // -----------------------------------------------
148  // Header
149  // -----------------------------------------------
150  AOSHeader header;
151 
152  // GVCID (Global Virtual Channel ID) (Standard 4.1.2.2 and 4.1.2.3)
153  U16 globalVcId = static_cast<U16>(context.get_vcId() << AOSHeaderSubfields::virtualChannelIdOffset);
154  globalVcId |= static_cast<U16>((ComCfg::SpacecraftId & 0x00FF) << AOSHeaderSubfields::spacecraftIdLsbOffset);
155  globalVcId |= static_cast<U16>((Tfvn::AOS & 0x3) << AOSHeaderSubfields::frameVersionOffset);
156 
157  // Virtual Channel Frame Count (4.1.2.4)
158  U32 frameCountAndSignaling = static_cast<U32>((currentVc.virtualFrameCount & 0x00FFFFFFU)
159  << static_cast<FwSizeType>(AOSHeaderSubfields::vcFrameCountOffset));
160 
161  // Virtual Channel Frame Count Cycle Use Flag (4.1.2.5.3)
162  frameCountAndSignaling |= static_cast<U32>(1 << AOSHeaderSubfields::cycleCountFlagOffset);
163 
164  // Spacecraft ID MSB (4.1.2.5.4)
165  frameCountAndSignaling |=
166  static_cast<U32>((ComCfg::SpacecraftId & 0x0300) >> (8 - AOSHeaderSubfields::spacecraftIdMsbOffset));
167 
168  // Virtual Channel Frame Cycle Count (4.1.2.5.5)
169  frameCountAndSignaling |= static_cast<U32>((currentVc.virtualFrameCount & 0x0F000000) >> 24);
170 
171  header.set_globalVcId(globalVcId);
172  header.set_frameCountAndSignaling(frameCountAndSignaling);
173 
174  // Perform the modulo at serialization time makes vc cycle count easier to make optional
175  currentVc.virtualFrameCount++; // U24 base Frame Count
176  // Extended by 4 bit VC Cycle Count
177  // intended to wrap around (modulo 268,435,456)
178 
179  // -----------------------------------------------
180  // Write Header
181  // -----------------------------------------------
182  Fw::SerializeStatus status;
183  // Use our member Fw::Buffer
184  auto frameSerializer = currentVc.frame.buffer.getSerializer();
185 
186  status = frameSerializer.moveSerToOffset(0);
187  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
188 
189  status = frameSerializer.serializeFrom(header);
190  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
191 }
192 
193 void AosFramer ::setup_m_pdu_header(const ComCfg::FrameContext& context, bool no_fresh) {
194  // Get the VC Struct for this vc
195  AosVc& currentVc = this->get_vc_struct(context);
196 
197  M_PDUHeader muxedPdu;
198  if (no_fresh) {
199  // All Ones since no packets start in this frame (no fresh packets)
200  muxedPdu.set_firstHeaderPointer(0xFFFF);
201  } else {
202  // Called at first fresh packet (not a packet tail)
203  // So current payload offset is the pointer to first packet header
204  muxedPdu.set_firstHeaderPointer(currentVc.current_payload_offset);
205  }
206 
207  auto frameSerializer = currentVc.frame.buffer.getSerializer();
208  Fw::SerializeStatus status = frameSerializer.moveSerToOffset(AOSHeader::SERIALIZED_SIZE);
209  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
210 
211  frameSerializer.serializeFrom(muxedPdu);
212  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
213 }
214 
215 FwSizeType AosFramer::get_min_size() {
217 }
218 
219 bool AosFramer::buffer_belongs(Fw::Buffer& buffer, U8 const* start, FwSizeType size) {
220  return (buffer.getData() >= start && buffer.getData() < start + size);
221 }
222 
223 void AosFramer::check_and_send_vc(AosFramer::AosVc& currentVc) {
224  const FwSizeType maxPayload = currentVc.frame.buffer.getSize() - get_min_size();
225 
226  // Check if we've filled up the M_PDU payload
227  if (currentVc.current_payload_offset == maxPayload) {
228  // Write the headers if we haven't already (continuing packet only)
229  if (!currentVc.past_first_fresh_packet) {
230  // Setup the AOS header right before we send with the context cached in outstanding
231  setup_header(currentVc.outstanding.context);
232 
233  // Setup the M_PDU header w/ all zero (only continuing packets)
234  setup_m_pdu_header(currentVc.outstanding.context, true);
235  }
236 
237  // Compute Trailer
238  if (this->m_fecf) {
239  compute_and_inject_fecf(currentVc);
240  }
241 
242  // Ensure we aren't double sending
243  FW_ASSERT(currentVc.frame.state == BufferOwnershipState::OWNED);
244  currentVc.frame.state = BufferOwnershipState::NOT_OWNED;
245 
246  // Clean up our per frame vc values
247  currentVc.current_payload_offset = 0;
248  currentVc.past_first_fresh_packet = false;
249 
250  // Write the completed frame
251  this->dataOut_out(0, currentVc.frame.buffer, currentVc.outstanding.context);
252  } else if (this->isConnected_comStatusOut_OutputPort(currentVc.vc_struct_index)) {
253  // We got more room, so ask for more bytes
254  Fw::Success condition = Fw::Success::SUCCESS;
255  this->comStatusOut_out(currentVc.vc_struct_index, condition);
256  }
257 }
258 
259 void AosFramer ::pack_pad_send(Fw::Buffer& data, const ComCfg::FrameContext& context, FwSizeType dataOffset) {
260  // Pack this packet into the M_PDU
261  pack_packet(data, context, dataOffset);
262 
263  // Get the VC Struct for this buffer
264  AosVc& currentVc = this->get_vc_struct(context);
265 
266  // Pack with idle packets if sendNow and not full already
267  // TODO: Add configurable time elapsed & buffer remaining based idle packing
268  if (context.get_sendNow()) {
269  // Compute some sizes to check if we've got space left to pad
270  const FwSizeType min_size = get_min_size();
271  const FwSizeType maxPayload = currentVc.frame.buffer.getSize() - min_size;
272 
273  if (currentVc.current_payload_offset < maxPayload) {
274  // As per TM Standard 4.2.2.5, fill the rest of the data field with an Idle Packet
275  fill_with_idle_packet(currentVc, context);
276  }
277  }
278 
279  // Send the frame if we've filled it
280  check_and_send_vc(currentVc);
281 }
282 
283 void AosFramer ::pack_packet(Fw::Buffer& data, const ComCfg::FrameContext& context, FwSizeType dataOffset) {
284  // Ensure the packet is starting within the frame
285  const FwSizeType min_size = get_min_size();
286 
287  // Get the VC Struct for this buffer
288  AosVc& currentVc = this->get_vc_struct(context);
289 
290  const FwSizeType maxPayload = currentVc.frame.buffer.getSize() - min_size;
291  const FwSizeType bytesAvailable = maxPayload - currentVc.current_payload_offset;
292  FW_ASSERT(bytesAvailable < currentVc.frame.buffer.getSize(),
293  static_cast<FwAssertArgType>(min_size + currentVc.current_payload_offset));
294 
295  // -------------------------------------------------
296  // Payload Packet
297  // -------------------------------------------------
298  Fw::SerializeStatus status;
299  // Use our member Fw::Buffer
300  auto frameSerializer = currentVc.frame.buffer.getSerializer();
301  status = frameSerializer.moveSerToOffset(START_OF_PAYLOAD + currentVc.current_payload_offset);
302  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
303 
304  const U8* dataStart = data.getData() + dataOffset;
305  // min of (remaining bytes in buffer and available bytes in frame)
306  FwSizeType dataSize = data.getSize() - dataOffset;
307 
308  // Determine if we can write the whole packet or not
309  if (dataSize <= bytesAvailable) {
310  // Whole packet fits, no need to store to outstanding
311  currentVc.outstanding.offset = 0;
312  } else {
313  // We'll only write a subset (clamp our write to the available size)
314  dataSize = bytesAvailable;
315 
316  // We'll pick up serialization from here later
317  currentVc.outstanding.offset = dataOffset + dataSize;
318  currentVc.outstanding.packet = data;
319  }
320 
321  status = frameSerializer.serializeFrom(dataStart, dataSize, Fw::Serialization::OMIT_LENGTH);
322  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
323 
324  // Shift our offset into the M_PDU payload region by how many bytes we wrote
325  currentVc.current_payload_offset = static_cast<U16>(currentVc.current_payload_offset + dataSize);
326 
327  // Context must always be present once any packet payload bytes are written
328  currentVc.outstanding.context = context;
329 
330  // Return the buffer if no bytes are outstanding
331  if (currentVc.outstanding.offset == 0) {
332  // Return the buffer if this isn't the SPP Idle buff
333  if (!buffer_belongs(data, currentVc.spp_idle.backer, sizeof(currentVc.spp_idle.backer))) {
334  this->dataReturnOut_out(0, data,
335  context); // return ownership of the original data buffer
336  }
337 
338  currentVc.outstanding.packet = {};
339  currentVc.outstanding.offset = 0;
340  }
341 }
342 
343 void AosFramer ::serialize_idle_spp_packet(Fw::SerializeBufferBase& serializer, U16 length) {
344  // APID to use for this Idle Packet
345  constexpr U16 idleApid = static_cast<U16>(ComCfg::Apid::SPP_IDLE_PACKET);
346 
347  // Length token is defined as the number of bytes of payload data minus 1
348  const U16 lengthToken = static_cast<U16>(length - SpacePacketHeader::SERIALIZED_SIZE - 1);
349 
350  SpacePacketHeader header;
351  header.set_packetIdentification(idleApid);
352  header.set_packetSequenceControl(
353  0x3 << SpacePacketSubfields::SeqFlagsOffset); // Sequence Flags = 0b11 (unsegmented) & unused Seq count
354  header.set_packetDataLength(lengthToken);
355  // Serialize header into frame
356  Fw::SerializeStatus status = serializer.serializeFrom(header);
357  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
358 
359  // Fill with idle pattern
360  for (U16 i = static_cast<U16>(SpacePacketHeader::SERIALIZED_SIZE); i < length; i++) {
361  status = serializer.serializeFrom(SPP_IDLE_DATA_PATTERN); // Idle data
362  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
363  }
364 }
365 
366 void AosFramer ::fill_with_idle_packet(AosVc& vc, const ComCfg::FrameContext& context) {
367  // Bytes that aren't actual PDUs
368  const U16 overhead = START_OF_PAYLOAD + (m_fecf ? AOSTrailer::SERIALIZED_SIZE : 0);
369  // Bytes for all the PDUs in this VC Frame
370  FW_ASSERT(vc.frame.buffer.getSize() > 0, static_cast<FwAssertArgType>(vc.frame.buffer.getSize()));
371  FW_ASSERT(vc.frame.buffer.getSize() < std::numeric_limits<U16>::max(),
372  static_cast<FwAssertArgType>(vc.frame.buffer.getSize()));
373  const U16 pduSize = static_cast<U16>(vc.frame.buffer.getSize() - overhead);
374 
375  // How many bytes are left over
376  const U16 idlePacketSize = static_cast<U16>(pduSize - vc.current_payload_offset);
377 
378  // Grab a serializer @ the current offset
379  auto frameSerializer = vc.frame.buffer.getSerializer();
380  Fw::SerializeStatus status = frameSerializer.moveSerToOffset(START_OF_PAYLOAD + vc.current_payload_offset);
381  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
382 
383  // If there wasn't a fresh packet before this
384  // set the M_PDU firstHeaderPointer
385  if (!vc.past_first_fresh_packet) {
386  // setup our headers if we have a packet + context ready to go
387  setup_header(context);
388  setup_m_pdu_header(context);
389 
390  // This idle packet is the fresh packet
391  vc.past_first_fresh_packet = true;
392  }
393 
394  // Use EPP if we can (solves for all sizes)
395  if (vc.idle_packet_types & PvnBitfield::EPP_MASK) {
396  // TODO: Serialize an EPP of the right size
397  FW_ASSERT(1);
398  }
399  // While we are using only SPP, we have to comply w/ the min SPP packet size
400  // We'll stripe this packet onto the next frame of this VC if we have to
401  else if (idlePacketSize < 7) {
402  // Serialize the Idle packet into the spp_idle_backer
403 
404  // Make sure we aren't overwriting a packet fragment
405  FW_ASSERT(!vc.outstanding.packet.isValid());
406 
407  // Setup the PDU to point at our idle buffer
408  vc.outstanding.packet = {vc.spp_idle.backer, MIN_SPP_LENGTH};
409 
410  // Write the SPP Idle to the outstanding packet
411  auto pduSerializer = vc.outstanding.packet.getSerializer();
412  serialize_idle_spp_packet(pduSerializer, MIN_SPP_LENGTH);
413 
414  // Remove the sendNow param
415  // The idle fragment that spills onto the next doesn't need to be sentNow
416  ComCfg::FrameContext filtered_context = context;
417  filtered_context.set_sendNow(false);
418 
419  // Use the normal pack command since we will have leftovers
420  // There won't be an outstanding packet since we'd use those bytes instead
421  pack_packet(vc.outstanding.packet, filtered_context);
422  } else {
423  // Serialize an idle packet right into the frame
424  serialize_idle_spp_packet(frameSerializer, idlePacketSize);
425 
426  // Increment the offset since we serialized directly into the frame
427  vc.current_payload_offset = static_cast<U16>(vc.current_payload_offset + idlePacketSize);
428  }
429 }
430 
431 } // namespace Ccsds
432 } // namespace Svc
Serialization/Deserialization operation was successful.
static U16 compute(const U8 *buffer, U32 length)
compute CRC16 for a buffer
Definition: CRC16.hpp:43
Advanced Orbiting Systems SDL.
Definition: TfvnEnumAc.hpp:45
SerializeStatus serializeFrom(U8 val, Endianness mode=Endianness::BIG) override
Serialize an 8-bit unsigned integer value.
Representing success.
PlatformSizeType FwSizeType
~AosFramer()
Destroy AosFramer object.
Definition: AosFramer.cpp:24
U8 * getData() const
Definition: Buffer.cpp:56
Auto-generated base for AosFramer component.
void set_sendNow(bool sendNow)
Set member sendNow.
bool isConnected_comStatusOut_OutputPort(FwIndexType portNum)
void dataReturnOut_out(FwIndexType portNum, Fw::Buffer &data, const ComCfg::FrameContext &context)
Invoke output port dataReturnOut.
AosFramer(const char *const compName)
Construct AosFramer object.
Definition: AosFramer.cpp:19
SerializeStatus
forward declaration for string
bool get_sendNow() const
Get member sendNow.
Omit length from serialization.
U8 get_vcId() const
Get member vcId.
The size of the serial representation.
The size of the serial representation.
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:53
void comStatusOut_out(FwIndexType portNum, Fw::Success &condition)
Invoke output port comStatusOut.
FwSizeType getSize() const
Definition: Buffer.cpp:60
The size of the serial representation.
PlatformIndexType FwIndexType
Type used to pass context info between components during framing/deframing.
RateGroupDivider component implementation.
Per Space Packet Standard, all 1s (11bits) is reserved for Idle Packets.
Definition: ApidEnumAc.hpp:51
void configure(U32 fixedFixedSize, bool frameErrorControlField, U8 idlePvns=PvnBitfield::SPP_MASK)
Definition: AosFramer.cpp:26
#define FW_ASSERT(...)
Definition: Assert.hpp:14
Success/Failure.
PlatformAssertArgType FwAssertArgType
The type of arguments to assert functions.
void dataOut_out(FwIndexType portNum, Fw::Buffer &data, const ComCfg::FrameContext &context)
Invoke output port dataOut.