|
| 1 | +# Implement a Framing Protocol |
| 2 | + |
| 3 | +This How-To Guide provides a step-by-step guide to implementing a custom framing protocol in F Prime Flight Software. |
| 4 | + |
| 5 | +The default [F Prime Protocol](../../Svc/FprimeProtocol/docs/sdd.md) is a lightweight protocol used to get development started quickly with a low-overhead communications protocol that the [F Prime GDS](https://github.com/nasa/fprime-gds) understands. However, as projects mature, there may be a need to implement a framing protocol that fits mission requirements. This document provides an overview of how to implement a custom framing protocol in F Prime Flight Software, and how to integrate it with the F Prime GDS. |
| 6 | + |
| 7 | +## Overview |
| 8 | + |
| 9 | +This guide demonstrates how to implement a custom framing protocol, referred to here as **MyCustomProtocol**. The protocol defines how data is wrapped (framed) for transmission and how frames are validated and unpacked (deframed) on reception. |
| 10 | + |
| 11 | +A reference implementation of a custom framing protocol (the "Decaf Protocol") is available in the `fprime-examples` repository: |
| 12 | +- [C++ CustomFraming Example](https://github.com/nasa/fprime-examples/tree/devel/FlightExamples/CustomFraming) |
| 13 | +- [GDS Plugin Example](https://github.com/nasa/fprime-examples/tree/devel/GdsExamples/gds-plugins/src/framing) |
| 14 | + |
| 15 | +This guide is divided into two main sections: flight software implementation and GDS integration. Note that if you are aiming to integrate with another GDS and do not wish to use the F´ GDS, you can skip the GDS section. |
| 16 | + |
| 17 | +## Flight Software Implementation |
| 18 | + |
| 19 | +To implement a custom framing protocol in F´, will need to implement the following: |
| 20 | +- **Framer**: A component that wraps payload data into frames for transmission. |
| 21 | +- **Deframer**: A component that unpacks received frames, extracts the payload data, and validate the frame. |
| 22 | +- **FrameDetector** (optional): A helper class that detects the start and end of frames in a stream of data, if your transport is stream-based (e.g., TCP, UART). |
| 23 | + |
| 24 | +The following examples will walk through the implementation of a custom framer and deframer for a hypothetical **MyCustomProtocol** protocol. Implementation details are left to the reader, but examples of such implementations can be found in the [fprime-examples repository](https://github.com/nasa/fprime-examples/tree/devel/FlightExamples/CustomFraming), or within the F´ codebase itself ([Svc.FprimeFramer](../../Svc/FprimeFramer/docs/sdd.md) and [Svc.FprimeDeframer](../../Svc/FprimeDeframer/docs/sdd.md)). |
| 25 | + |
| 26 | +1. **Define the Framer and Deframer FPP Components** |
| 27 | + |
| 28 | + Framer and Deframer components should implement the FPP interfaces by including them. |
| 29 | + |
| 30 | + In `MyCustomFramer.fpp`: |
| 31 | + ``` |
| 32 | + passive component MyCustomFramer { |
| 33 | + include "path/to/fprime/Svc/Interfaces/FramerInterface.fppi" |
| 34 | + [...] |
| 35 | + } |
| 36 | + ``` |
| 37 | + And in `MyCustomDeframer.fpp`: |
| 38 | + ``` |
| 39 | + @ Deframer implementation for MyCustomProtocol |
| 40 | + passive component MyCustomDeframer { |
| 41 | + include "path/to/fprime/Svc/Interfaces/DeframerInterface.fppi" |
| 42 | + [...] |
| 43 | + } |
| 44 | + ``` |
| 45 | + |
| 46 | +2. **Implement the Framer C++ Component** |
| 47 | + |
| 48 | + Implement the required handler functions: |
| 49 | + ```cpp |
| 50 | + // ...existing code... |
| 51 | + void MyCustomFramer ::dataIn_handler(FwIndexType portNum, Fw::Buffer& data, const ComCfg::FrameContext& context) { |
| 52 | + // TODO: Implement framing logic |
| 53 | + } |
| 54 | + |
| 55 | + void MyCustomFramer ::comStatusIn_handler(FwIndexType portNum, Fw::Success& condition) { |
| 56 | + this->comStatusOut_out(portNum, condition); // pass comStatus through (unless project requires otherwise) |
| 57 | + } |
| 58 | + |
| 59 | + void MyCustomFramer ::dataReturnIn_handler(FwIndexType portNum, Fw::Buffer& data, const ComCfg::FrameContext& context) { |
| 60 | + // TODO: handle return of data ownership |
| 61 | + // For example, if component required to allocate from a buffer manager, return the buffer to the manager |
| 62 | + } |
| 63 | + // ...existing code... |
| 64 | + ``` |
| 65 | +
|
| 66 | +3. **Implement the Deframer C++ Component** |
| 67 | +
|
| 68 | + Similarly, implement the required handler functions: |
| 69 | + ```cpp |
| 70 | + // ...existing code... |
| 71 | + void MyCustomDeframer ::dataIn_handler(FwIndexType portNum, Fw::Buffer& data, const ComCfg::FrameContext& context) { |
| 72 | + // TODO: Implement deframing logic |
| 73 | + } |
| 74 | +
|
| 75 | + void MyCustomDeframer ::dataReturnIn_handler(FwIndexType portNum, Fw::Buffer& data, const ComCfg::FrameContext& context) { |
| 76 | + // TODO: handle return of data ownership |
| 77 | + } |
| 78 | + // ...existing code... |
| 79 | + ``` |
| 80 | + |
| 81 | +4. **(Optional) Implement a Frame Detector** |
| 82 | + |
| 83 | + _When is this not needed?_ |
| 84 | + If your communications manager component always receives complete frames, you do not need to implement frame detection. This can be the case when using radios with built-in frame synchronization or when another subsystem handles frame delimiting. |
| 85 | + |
| 86 | + _When is this needed?_ |
| 87 | + If your data transport is stream-based (for example, relying on TCP or UART), or if you cannot guarantee that frames will always be received in full, you must implement a mechanism to delimit frames. F Prime provides this capability with the [Svc.FrameAccumulator](../../Svc/FrameAccumulator/docs/sdd.md) component, which uses a circular buffer and a helper `FrameDetector` to identify complete frames in the data stream. |
| 88 | + |
| 89 | + To use the `Svc.FrameAccumulator`, you need to configure it with a FrameDetector that detects when a frame is present: |
| 90 | + **MyCustomFrameDetector.hpp** |
| 91 | + ```cpp |
| 92 | + #include <Svc/FrameDetector/FrameDetector.hpp> |
| 93 | + |
| 94 | + class MyCustomFrameDetector : public Svc::FrameDetector { |
| 95 | + public: |
| 96 | + Svc::FrameDetector::Status detect(const Types::CircularBuffer& data, FwSizeType& size_out) const override; |
| 97 | + }; |
| 98 | + ``` |
| 99 | +
|
| 100 | + **MyCustomFrameDetector.cpp** |
| 101 | + ```cpp |
| 102 | + // ...existing code... |
| 103 | + Svc::FrameDetector::Status MyCustomFrameDetector::detect(const Types::CircularBuffer& data, FwSizeType& size_out) const { |
| 104 | + // TODO: Implement frame boundary detection |
| 105 | + // Refer to the Svc.FrameDetector documentation for details on how to implement this |
| 106 | + return Svc::FrameDetector::NO_FRAME_DETECTED; |
| 107 | + } |
| 108 | + // ...existing code... |
| 109 | + ``` |
| 110 | + |
| 111 | + Then configure the `Svc.FrameAccumulator` component to use your custom frame detector in your Topology CPP: |
| 112 | + **Top/Topology.cpp** |
| 113 | + ```cpp |
| 114 | + #include <path/to/MyCustomFrameDetector.hpp> |
| 115 | + // ...existing code... |
| 116 | + MyCustomFrameDetector frameDetector; |
| 117 | + // ...existing code... |
| 118 | + frameAccumulator.configure(frameDetector, 1, mallocator, 2048); |
| 119 | + ``` |
| 120 | + |
| 121 | + |
| 122 | +## F´ GDS Implementation |
| 123 | + |
| 124 | +To support your custom protocol in the F´ GDS, implement a GDS framing plugin. The GDS plugin system allows you to customize GDS behavior with user-provided code. For new framing protocols, you will need to implement a plugin that extends the `FramerDeframer`. This is further documented in the [How-To Develop a GDS Plugin Guide](./develop-gds-plugins.md) and [F Prime GDS Framing Plugin reference](../reference/gds-plugins/framing.md). |
| 125 | + |
| 126 | +For example, in Python: |
| 127 | + |
| 128 | +```python |
| 129 | +from fprime_gds.common.communication.framing import FramerDeframer |
| 130 | +from fprime_gds.plugin.definitions import gds_plugin |
| 131 | + |
| 132 | +@gds_plugin(FramerDeframer) |
| 133 | +class MyCustomFramerDeframer(FramerDeframer): |
| 134 | + """GDS plugin for MyCustomProtocol framing""" |
| 135 | + def frame(self, data): |
| 136 | + # TODO: Implement framing logic |
| 137 | + return frame |
| 138 | + |
| 139 | + def deframe(self, data, no_copy=False): |
| 140 | + # TODO: Implement deframing logic |
| 141 | + return packet, leftover_data, discarded_data |
| 142 | +``` |
| 143 | +
|
| 144 | +Make sure to [package and install the plugin in your virtual environment](./develop-gds-plugins.md#packaging-and-testing-plugins) for the GDS to be able to load it, then run it: |
| 145 | +
|
| 146 | +``` |
| 147 | +fprime-gds --framing-selection MyCustomFramerDeframer |
| 148 | +``` |
| 149 | +
|
| 150 | +## References |
| 151 | +
|
| 152 | +- [C++ CustomFraming Example](https://github.com/nasa/fprime-examples/tree/devel/FlightExamples/CustomFraming) |
| 153 | +- [GDS Plugin Example](https://github.com/nasa/fprime-examples/tree/devel/GdsExamples/gds-plugins/src/framing) |
| 154 | +- [F Prime GDS Framing Plugin](../reference/gds-plugins/framing.md) |
| 155 | +- [F Prime Communication Adapter Interface](../reference/communication-adapter-interface.md) |
| 156 | +- [F Prime GDS Plugin Development](https://fprime.jpl.nasa.gov/devel/docs/how-to/develop-gds-plugins/) |
| 157 | +
|
0 commit comments