GCC Code Coverage Report


Directory: lib/fprime-samd/fprime-samd/
File: Svc/Framer/Framer.cpp
Date: 2026-09-26 00:05:09
Exec Total Coverage
Lines: 0 72 0.0%
Functions: 0 7 0.0%
Branches: 0 118 0.0%

Line Branch Exec Source
1 // ======================================================================
2 // \title Framer.cpp
3 // \author tumbar
4 // \brief cpp file for Framer component implementation class
5 // ======================================================================
6
7 #include "fprime-samd/Svc/Framer/Framer.hpp"
8 #include "Drv/ByteStreamDriverModel/ByteStreamStatusEnumAc.hpp"
9 #include "Fw/Com/ComBuffer.hpp"
10 #include "Fw/Types/Assert.hpp"
11 #include "Svc/FprimeProtocol/FrameHeaderSerializableAc.hpp"
12 #include "Svc/FprimeProtocol/FrameTrailerSerializableAc.hpp"
13 #include "Utils/Hash/Hash.hpp"
14 #include "samd-config/FramerConfig.hpp"
15
16 namespace Samd21 {
17
18 // ----------------------------------------------------------------------
19 // Component construction and destruction
20 // ----------------------------------------------------------------------
21
22 ✗ Framer ::Framer(const char* const compName)
23 ✗ : FramerComponentBase(compName), m_driverConnected(false), m_activeBufferIdx(0), m_droppedPackets(0) {
24 // Initialize both buffers to IDLE state with space reserved for header
25 ✗ for (FwIndexType i = 0; i < 2; i++) {
26 ✗ m_buffers[i].size = 0;
27 ✗ m_buffers[i].state = IDLE;
28 }
29 ✗ }
30
31 ✗ Framer ::~Framer() {}
32
33 // ----------------------------------------------------------------------
34 // Handler implementations for typed input ports
35 // ----------------------------------------------------------------------
36
37 ✗ void Framer ::comPacketQueueIn_handler(FwIndexType portNum, Fw::ComBuffer& data, U32 context) {
38 ✗ TxBuffer* activeBuf = &m_buffers[m_activeBufferIdx];
39
40 // Calculate space needed: frame header + ComBuffer data + trailer
41 ✗ FwSizeType spaceNeeded = Svc::FprimeProtocol::FrameHeader::SERIALIZED_SIZE + data.getSize() +
42 ✗ Svc::FprimeProtocol::FrameTrailer::SERIALIZED_SIZE;
43
44 // If both buffers are in flight (backpressure), drop this packet
45 ✗ if (activeBuf->state == TRANSMITTING) {
46 ✗ this->m_droppedPackets++;
47 ✗ return;
48 }
49
50 // If adding this ComBuffer would overflow, flush first
51 ✗ if (activeBuf->size + spaceNeeded > FramerConfig::FRAMER_TX_BUFFER_SIZE) {
52 ✗ flushActiveBuffer();
53
54 // flushActiveBuffer() swapped the active buffer -- re-acquire it
55 ✗ activeBuf = &m_buffers[m_activeBufferIdx];
56
57 // If both buffers are in flight (backpressure), drop this packet
58 ✗ if (activeBuf->state == TRANSMITTING) {
59 ✗ this->m_droppedPackets++;
60 ✗ return;
61 }
62
63 // After flush, the swapped-in buffer should be idle and empty
64 ✗ FW_ASSERT(activeBuf->state == IDLE);
65 ✗ FW_ASSERT(activeBuf->size == 0);
66 }
67
68 // Mark buffer as active if it's idle
69 ✗ if (activeBuf->state == IDLE) {
70 ✗ activeBuf->state = ACTIVE;
71 }
72
73 // Append ComBuffer data directly to the buffer (after existing data)
74 ✗ Fw::Buffer bufWrapper(activeBuf->data + activeBuf->size, FramerConfig::FRAMER_TX_BUFFER_SIZE - activeBuf->size);
75 ✗ Fw::ExternalSerializeBuffer serializer(bufWrapper.getData(), bufWrapper.getSize());
76
77 // Write the packet header
78 ✗ FW_ASSERT(data.getSize() <= std::numeric_limits<Svc::FprimeProtocol::TokenType>::max(),
79 static_cast<FwAssertArgType>(data.getSize()));
80
81 ✗ Svc::FprimeProtocol::FrameHeader header;
82 ✗ header.set_lengthField(static_cast<Svc::FprimeProtocol::TokenType>(data.getSize()));
83
84 ✗ Fw::SerializeStatus status = serializer.serializeFrom(header);
85 ✗ FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
86
87 // Write the packet data
88 ✗ status = serializer.serializeFrom(data.getBuffAddr(), data.getSize(), Fw::Serialization::OMIT_LENGTH);
89 ✗ FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status, data.getSize(), portNum);
90
91 // Compute the CRC of the packet (header + data)
92 ✗ Utils::HashBuffer hashBuffer;
93 ✗ Utils::Hash::hash(activeBuf->data + activeBuf->size,
94 ✗ data.getSize() + Svc::FprimeProtocol::FrameHeader::SERIALIZED_SIZE, hashBuffer);
95
96 // Write trailer with CRC
97 ✗ Svc::FprimeProtocol::FrameTrailer trailer;
98 ✗ trailer.set_crcField(hashBuffer.asBigEndianU32());
99
100 ✗ status = serializer.serializeFrom(trailer);
101 ✗ FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
102
103 // Update buffer size to include the packet frame
104 ✗ activeBuf->size += serializer.getSize();
105 ✗ FW_ASSERT(activeBuf->size <= FramerConfig::FRAMER_TX_BUFFER_SIZE);
106 ✗ }
107
108 ✗ void Framer ::drvConnected_handler(FwIndexType portNum) {
109 ✗ this->m_driverConnected = true;
110 ✗ }
111
112 ✗ void Framer ::drvReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer, const Drv::ByteStreamStatus& status) {
113 // Find which buffer was returned
114 ✗ for (FwIndexType i = 0; i < 2; i++) {
115 ✗ if (m_buffers[i].state == TRANSMITTING && fwBuffer.getData() == m_buffers[i].data) {
116 // Mark buffer as idle and ready for reuse, reset the size
117 ✗ m_buffers[i].state = IDLE;
118 ✗ m_buffers[i].size = 0;
119 ✗ return;
120 }
121 }
122 ✗ FW_ASSERT(false); // Buffer returned that we didn't send
123 }
124
125 ✗ void Framer ::flushActiveBuffer() {
126 ✗ FW_ASSERT(this->m_driverConnected);
127
128 ✗ TxBuffer& activeBuf = m_buffers[m_activeBufferIdx];
129
130 // Nothing to flush if buffer is idle or contains no data
131 ✗ if (activeBuf.state == IDLE || activeBuf.size == 0) {
132 ✗ return;
133 }
134
135 // Buffer must not already be in flight
136 ✗ FW_ASSERT(activeBuf.state == ACTIVE);
137
138 // Mark buffer as in-flight
139 ✗ activeBuf.state = TRANSMITTING;
140
141 // Switch to the next buffer
142 ✗ m_activeBufferIdx = (m_activeBufferIdx + 1) % 2;
143
144 // Send the complete frame to the driver
145 ✗ Fw::Buffer txBuffer(activeBuf.data, activeBuf.size);
146 ✗ drvSendOut_out(0, txBuffer);
147 ✗ }
148
149 ✗ void Framer ::schedIn_handler(FwIndexType portNum, U32 context) {
150 ✗ if (this->m_driverConnected) {
151 // Only flush if the other buffer is not transmitting
152 ✗ FwIndexType nextBufferIdx = (m_activeBufferIdx + 1) % 2;
153 ✗ TxBuffer& nextBuf = m_buffers[nextBufferIdx];
154
155 ✗ this->tlmWrite_DroppedPackets(this->m_droppedPackets);
156
157 ✗ if (nextBuf.state == TRANSMITTING) {
158 // Can't flush because both buffers would be in flight
159 // Wait for next schedule cycle
160 ✗ return;
161 }
162
163 ✗ flushActiveBuffer();
164 }
165 }
166
167 } // namespace Samd21
168