ducky's telemetry library
telemetry.h@0:aaa75ea6d346, 2015-03-31 (annotated)
- Committer:
- ikrase
- Date:
- Tue Mar 31 19:56:00 2015 +0000
- Revision:
- 0:aaa75ea6d346
imported telemetry, added include guard to telemetry-mbed.h
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
ikrase | 0:aaa75ea6d346 | 1 | #ifndef _TELEMETRY_H_ |
ikrase | 0:aaa75ea6d346 | 2 | #define _TELEMETRY_H_ |
ikrase | 0:aaa75ea6d346 | 3 | |
ikrase | 0:aaa75ea6d346 | 4 | #include <stddef.h> |
ikrase | 0:aaa75ea6d346 | 5 | #include <stdint.h> |
ikrase | 0:aaa75ea6d346 | 6 | |
ikrase | 0:aaa75ea6d346 | 7 | namespace telemetry { |
ikrase | 0:aaa75ea6d346 | 8 | |
ikrase | 0:aaa75ea6d346 | 9 | #ifndef TELEMETRY_DATA_LIMIT |
ikrase | 0:aaa75ea6d346 | 10 | #define TELEMETRY_DATA_LIMIT 16 |
ikrase | 0:aaa75ea6d346 | 11 | #endif |
ikrase | 0:aaa75ea6d346 | 12 | |
ikrase | 0:aaa75ea6d346 | 13 | // Maximum number of DataInterface objects a Telemetry object can hold. |
ikrase | 0:aaa75ea6d346 | 14 | // Used for array sizing. |
ikrase | 0:aaa75ea6d346 | 15 | const size_t MAX_DATA_PER_TELEMETRY = TELEMETRY_DATA_LIMIT; |
ikrase | 0:aaa75ea6d346 | 16 | |
ikrase | 0:aaa75ea6d346 | 17 | // Maximum payload size for a received telemetry packet. |
ikrase | 0:aaa75ea6d346 | 18 | const size_t MAX_RECEIVE_PACKET_LENGTH = 255; |
ikrase | 0:aaa75ea6d346 | 19 | |
ikrase | 0:aaa75ea6d346 | 20 | // Various wire protocol constants. |
ikrase | 0:aaa75ea6d346 | 21 | const uint8_t SOF1 = 0x05; // start of frame byte 1 |
ikrase | 0:aaa75ea6d346 | 22 | const uint8_t SOF2 = 0x39; // start of frame byte 2 |
ikrase | 0:aaa75ea6d346 | 23 | const uint8_t SOF_SEQ[] = {0x05, 0x39}; |
ikrase | 0:aaa75ea6d346 | 24 | |
ikrase | 0:aaa75ea6d346 | 25 | const size_t LENGTH_SIZE = 2; |
ikrase | 0:aaa75ea6d346 | 26 | |
ikrase | 0:aaa75ea6d346 | 27 | // TODO: make these length independent |
ikrase | 0:aaa75ea6d346 | 28 | |
ikrase | 0:aaa75ea6d346 | 29 | const uint8_t OPCODE_HEADER = 0x81; |
ikrase | 0:aaa75ea6d346 | 30 | const uint8_t OPCODE_DATA = 0x01; |
ikrase | 0:aaa75ea6d346 | 31 | |
ikrase | 0:aaa75ea6d346 | 32 | const uint8_t DATAID_TERMINATOR = 0x00; |
ikrase | 0:aaa75ea6d346 | 33 | |
ikrase | 0:aaa75ea6d346 | 34 | const uint8_t DATATYPE_NUMERIC = 0x01; |
ikrase | 0:aaa75ea6d346 | 35 | const uint8_t DATATYPE_NUMERIC_ARRAY = 0x02; |
ikrase | 0:aaa75ea6d346 | 36 | |
ikrase | 0:aaa75ea6d346 | 37 | const uint8_t RECORDID_TERMINATOR = 0x00; |
ikrase | 0:aaa75ea6d346 | 38 | const uint8_t RECORDID_INTERNAL_NAME = 0x01; |
ikrase | 0:aaa75ea6d346 | 39 | const uint8_t RECORDID_DISPLAY_NAME = 0x02; |
ikrase | 0:aaa75ea6d346 | 40 | const uint8_t RECORDID_UNITS = 0x03; |
ikrase | 0:aaa75ea6d346 | 41 | |
ikrase | 0:aaa75ea6d346 | 42 | const uint8_t RECORDID_OVERRIDE_CTL = 0x08; |
ikrase | 0:aaa75ea6d346 | 43 | const uint8_t RECORDID_OVERRIDE_DATA = 0x08; |
ikrase | 0:aaa75ea6d346 | 44 | |
ikrase | 0:aaa75ea6d346 | 45 | const uint8_t RECORDID_NUMERIC_SUBTYPE = 0x40; |
ikrase | 0:aaa75ea6d346 | 46 | const uint8_t RECORDID_NUMERIC_LENGTH = 0x41; |
ikrase | 0:aaa75ea6d346 | 47 | const uint8_t RECORDID_NUMERIC_LIMITS = 0x42; |
ikrase | 0:aaa75ea6d346 | 48 | const uint8_t RECORDID_ARRAY_COUNT = 0x50; |
ikrase | 0:aaa75ea6d346 | 49 | |
ikrase | 0:aaa75ea6d346 | 50 | const uint8_t NUMERIC_SUBTYPE_UINT = 0x01; |
ikrase | 0:aaa75ea6d346 | 51 | const uint8_t NUMERIC_SUBTYPE_SINT = 0x02; |
ikrase | 0:aaa75ea6d346 | 52 | const uint8_t NUMERIC_SUBTYPE_FLOAT = 0x03; |
ikrase | 0:aaa75ea6d346 | 53 | |
ikrase | 0:aaa75ea6d346 | 54 | const uint32_t DECODER_TIMEOUT_MS = 100; |
ikrase | 0:aaa75ea6d346 | 55 | |
ikrase | 0:aaa75ea6d346 | 56 | // Hardware abstraction layer for the telemetry server. |
ikrase | 0:aaa75ea6d346 | 57 | class HalInterface { |
ikrase | 0:aaa75ea6d346 | 58 | public: |
ikrase | 0:aaa75ea6d346 | 59 | virtual ~HalInterface() {} |
ikrase | 0:aaa75ea6d346 | 60 | |
ikrase | 0:aaa75ea6d346 | 61 | // Write a byte to the transmit buffer. |
ikrase | 0:aaa75ea6d346 | 62 | virtual void transmit_byte(uint8_t data) = 0; |
ikrase | 0:aaa75ea6d346 | 63 | // Returns the number of bytes available in the receive buffer. |
ikrase | 0:aaa75ea6d346 | 64 | virtual size_t rx_available() = 0; |
ikrase | 0:aaa75ea6d346 | 65 | // Returns the next byte in the receive stream. rx_available must return > 0. |
ikrase | 0:aaa75ea6d346 | 66 | virtual uint8_t receive_byte() = 0; |
ikrase | 0:aaa75ea6d346 | 67 | |
ikrase | 0:aaa75ea6d346 | 68 | // TODO: more efficient block transmit operations? |
ikrase | 0:aaa75ea6d346 | 69 | |
ikrase | 0:aaa75ea6d346 | 70 | // Called on a telemetry error. |
ikrase | 0:aaa75ea6d346 | 71 | virtual void do_error(const char* message) = 0; |
ikrase | 0:aaa75ea6d346 | 72 | |
ikrase | 0:aaa75ea6d346 | 73 | // Return the current time in milliseconds. May overflow at any time. |
ikrase | 0:aaa75ea6d346 | 74 | virtual uint32_t get_time_ms() = 0; |
ikrase | 0:aaa75ea6d346 | 75 | }; |
ikrase | 0:aaa75ea6d346 | 76 | |
ikrase | 0:aaa75ea6d346 | 77 | // Abstract base class for building a packet to be transmitted. |
ikrase | 0:aaa75ea6d346 | 78 | // Implementation is unconstrained - writes may either be buffered or passed |
ikrase | 0:aaa75ea6d346 | 79 | // directly to the hardware transmit buffers. |
ikrase | 0:aaa75ea6d346 | 80 | class TransmitPacketInterface { |
ikrase | 0:aaa75ea6d346 | 81 | public: |
ikrase | 0:aaa75ea6d346 | 82 | virtual ~TransmitPacketInterface() {} |
ikrase | 0:aaa75ea6d346 | 83 | |
ikrase | 0:aaa75ea6d346 | 84 | // Writes a 8-bit unsigned integer to the packet stream. |
ikrase | 0:aaa75ea6d346 | 85 | virtual void write_byte(uint8_t data) = 0; |
ikrase | 0:aaa75ea6d346 | 86 | |
ikrase | 0:aaa75ea6d346 | 87 | // Writes a 8-bit unsigned integer to the packet stream. |
ikrase | 0:aaa75ea6d346 | 88 | virtual void write_uint8(uint8_t data) = 0; |
ikrase | 0:aaa75ea6d346 | 89 | // Writes a 16-bit unsigned integer to the packet stream. |
ikrase | 0:aaa75ea6d346 | 90 | virtual void write_uint16(uint16_t data) = 0; |
ikrase | 0:aaa75ea6d346 | 91 | // Writes a 32-bit unsigned integer to the packet stream. |
ikrase | 0:aaa75ea6d346 | 92 | virtual void write_uint32(uint32_t data) = 0; |
ikrase | 0:aaa75ea6d346 | 93 | // Writes a float to the packet stream. |
ikrase | 0:aaa75ea6d346 | 94 | virtual void write_float(float data) = 0; |
ikrase | 0:aaa75ea6d346 | 95 | |
ikrase | 0:aaa75ea6d346 | 96 | // Finish the packet and writes data to the transmit stream (if not already |
ikrase | 0:aaa75ea6d346 | 97 | // done). No more data may be written afterwards. |
ikrase | 0:aaa75ea6d346 | 98 | virtual void finish() = 0; |
ikrase | 0:aaa75ea6d346 | 99 | }; |
ikrase | 0:aaa75ea6d346 | 100 | |
ikrase | 0:aaa75ea6d346 | 101 | class ReceivePacketBuffer { |
ikrase | 0:aaa75ea6d346 | 102 | public: |
ikrase | 0:aaa75ea6d346 | 103 | ReceivePacketBuffer(HalInterface& hal); |
ikrase | 0:aaa75ea6d346 | 104 | |
ikrase | 0:aaa75ea6d346 | 105 | // Starts a new packet, resetting the packet length and read pointer. |
ikrase | 0:aaa75ea6d346 | 106 | void new_packet(); |
ikrase | 0:aaa75ea6d346 | 107 | |
ikrase | 0:aaa75ea6d346 | 108 | // Appends a new byte onto this packet, advancing the packet length |
ikrase | 0:aaa75ea6d346 | 109 | void add_byte(uint8_t byte); |
ikrase | 0:aaa75ea6d346 | 110 | |
ikrase | 0:aaa75ea6d346 | 111 | // Reads a 8-bit unsigned integer from the packet stream, advancing buffer. |
ikrase | 0:aaa75ea6d346 | 112 | uint8_t read_uint8(); |
ikrase | 0:aaa75ea6d346 | 113 | // Reads a 16-bit unsigned integer from the packet stream, advancing buffer. |
ikrase | 0:aaa75ea6d346 | 114 | uint16_t read_uint16(); |
ikrase | 0:aaa75ea6d346 | 115 | // Reads a 32-bit unsigned integer from the packet stream, advancing buffer. |
ikrase | 0:aaa75ea6d346 | 116 | uint32_t read_uint32(); |
ikrase | 0:aaa75ea6d346 | 117 | // Reads a float from the packet stream, advancing buffer. |
ikrase | 0:aaa75ea6d346 | 118 | float read_float(); |
ikrase | 0:aaa75ea6d346 | 119 | |
ikrase | 0:aaa75ea6d346 | 120 | protected: |
ikrase | 0:aaa75ea6d346 | 121 | HalInterface& hal; |
ikrase | 0:aaa75ea6d346 | 122 | |
ikrase | 0:aaa75ea6d346 | 123 | size_t packet_length; |
ikrase | 0:aaa75ea6d346 | 124 | size_t read_loc; |
ikrase | 0:aaa75ea6d346 | 125 | uint8_t data[MAX_RECEIVE_PACKET_LENGTH]; |
ikrase | 0:aaa75ea6d346 | 126 | }; |
ikrase | 0:aaa75ea6d346 | 127 | |
ikrase | 0:aaa75ea6d346 | 128 | // Abstract base class for telemetry data objects. |
ikrase | 0:aaa75ea6d346 | 129 | class Data { |
ikrase | 0:aaa75ea6d346 | 130 | public: |
ikrase | 0:aaa75ea6d346 | 131 | Data(const char* internal_name, const char* display_name, |
ikrase | 0:aaa75ea6d346 | 132 | const char* units): |
ikrase | 0:aaa75ea6d346 | 133 | internal_name(internal_name), |
ikrase | 0:aaa75ea6d346 | 134 | display_name(display_name), |
ikrase | 0:aaa75ea6d346 | 135 | units(units) {}; |
ikrase | 0:aaa75ea6d346 | 136 | |
ikrase | 0:aaa75ea6d346 | 137 | virtual ~Data() {} |
ikrase | 0:aaa75ea6d346 | 138 | |
ikrase | 0:aaa75ea6d346 | 139 | // Returns the data type code. |
ikrase | 0:aaa75ea6d346 | 140 | virtual uint8_t get_data_type() = 0; |
ikrase | 0:aaa75ea6d346 | 141 | |
ikrase | 0:aaa75ea6d346 | 142 | // Returns the length of the header KVRs, in bytes. Does not include the |
ikrase | 0:aaa75ea6d346 | 143 | // terminator header. |
ikrase | 0:aaa75ea6d346 | 144 | virtual size_t get_header_kvrs_length(); |
ikrase | 0:aaa75ea6d346 | 145 | // Writes the header KVRs to the transmit packet. Does not write the |
ikrase | 0:aaa75ea6d346 | 146 | // terminiator header. |
ikrase | 0:aaa75ea6d346 | 147 | virtual void write_header_kvrs(TransmitPacketInterface& packet); |
ikrase | 0:aaa75ea6d346 | 148 | |
ikrase | 0:aaa75ea6d346 | 149 | // Returns the length of the payload, in bytes. Should be "fast". |
ikrase | 0:aaa75ea6d346 | 150 | virtual size_t get_payload_length() = 0; |
ikrase | 0:aaa75ea6d346 | 151 | // Writes the payload to the transmit packet. Should be "fast". |
ikrase | 0:aaa75ea6d346 | 152 | virtual void write_payload(TransmitPacketInterface& packet) = 0; |
ikrase | 0:aaa75ea6d346 | 153 | |
ikrase | 0:aaa75ea6d346 | 154 | // Sets my value from the received packet, interpreting the current packet |
ikrase | 0:aaa75ea6d346 | 155 | // read position as my data type. |
ikrase | 0:aaa75ea6d346 | 156 | virtual void set_from_packet(ReceivePacketBuffer& packet) = 0; |
ikrase | 0:aaa75ea6d346 | 157 | |
ikrase | 0:aaa75ea6d346 | 158 | protected: |
ikrase | 0:aaa75ea6d346 | 159 | const char* internal_name; |
ikrase | 0:aaa75ea6d346 | 160 | const char* display_name; |
ikrase | 0:aaa75ea6d346 | 161 | const char* units; |
ikrase | 0:aaa75ea6d346 | 162 | }; |
ikrase | 0:aaa75ea6d346 | 163 | |
ikrase | 0:aaa75ea6d346 | 164 | // Telemetry Server object. |
ikrase | 0:aaa75ea6d346 | 165 | class Telemetry { |
ikrase | 0:aaa75ea6d346 | 166 | public: |
ikrase | 0:aaa75ea6d346 | 167 | Telemetry(HalInterface& hal) : |
ikrase | 0:aaa75ea6d346 | 168 | hal(hal), |
ikrase | 0:aaa75ea6d346 | 169 | data_count(0), |
ikrase | 0:aaa75ea6d346 | 170 | received_packet(ReceivePacketBuffer(hal)), |
ikrase | 0:aaa75ea6d346 | 171 | decoder_state(SOF), |
ikrase | 0:aaa75ea6d346 | 172 | decoder_pos(0), |
ikrase | 0:aaa75ea6d346 | 173 | packet_length(0), |
ikrase | 0:aaa75ea6d346 | 174 | decoder_last_received(false), |
ikrase | 0:aaa75ea6d346 | 175 | decoder_last_receive_ms(0), |
ikrase | 0:aaa75ea6d346 | 176 | header_transmitted(false), |
ikrase | 0:aaa75ea6d346 | 177 | packet_tx_sequence(0), |
ikrase | 0:aaa75ea6d346 | 178 | packet_rx_sequence(0) {}; |
ikrase | 0:aaa75ea6d346 | 179 | |
ikrase | 0:aaa75ea6d346 | 180 | // Associates a DataInterface with this object, returning the data ID. |
ikrase | 0:aaa75ea6d346 | 181 | size_t add_data(Data& new_data); |
ikrase | 0:aaa75ea6d346 | 182 | |
ikrase | 0:aaa75ea6d346 | 183 | // Marks a data ID as updated, to be transmitted in the next packet. |
ikrase | 0:aaa75ea6d346 | 184 | void mark_data_updated(size_t data_id); |
ikrase | 0:aaa75ea6d346 | 185 | |
ikrase | 0:aaa75ea6d346 | 186 | // Transmits header data. Must be called after all add_data calls are done |
ikrase | 0:aaa75ea6d346 | 187 | // and before and IO is done. |
ikrase | 0:aaa75ea6d346 | 188 | void transmit_header(); |
ikrase | 0:aaa75ea6d346 | 189 | |
ikrase | 0:aaa75ea6d346 | 190 | // Does IO, including transmitting telemetry packets. Should be called on |
ikrase | 0:aaa75ea6d346 | 191 | // a regular basis. Since this does IO, this may block depending on the HAL |
ikrase | 0:aaa75ea6d346 | 192 | // semantics. |
ikrase | 0:aaa75ea6d346 | 193 | void do_io(); |
ikrase | 0:aaa75ea6d346 | 194 | |
ikrase | 0:aaa75ea6d346 | 195 | // TODO: better docs defining in-band receive. |
ikrase | 0:aaa75ea6d346 | 196 | // Returns the number of bytes available in the receive stream. |
ikrase | 0:aaa75ea6d346 | 197 | size_t receive_available(); |
ikrase | 0:aaa75ea6d346 | 198 | // Returns the next byte in the receive stream. |
ikrase | 0:aaa75ea6d346 | 199 | uint8_t read_receive(); |
ikrase | 0:aaa75ea6d346 | 200 | |
ikrase | 0:aaa75ea6d346 | 201 | // Calls the HAL's error function if some condition is false. |
ikrase | 0:aaa75ea6d346 | 202 | void do_error(const char* message) { |
ikrase | 0:aaa75ea6d346 | 203 | hal.do_error(message); |
ikrase | 0:aaa75ea6d346 | 204 | } |
ikrase | 0:aaa75ea6d346 | 205 | |
ikrase | 0:aaa75ea6d346 | 206 | protected: |
ikrase | 0:aaa75ea6d346 | 207 | // Transmits any updated data. |
ikrase | 0:aaa75ea6d346 | 208 | void transmit_data(); |
ikrase | 0:aaa75ea6d346 | 209 | |
ikrase | 0:aaa75ea6d346 | 210 | // Handles received data, splitting regular UART data from in-band packet |
ikrase | 0:aaa75ea6d346 | 211 | // data and processing received telemetry packets. |
ikrase | 0:aaa75ea6d346 | 212 | void process_received_data(); |
ikrase | 0:aaa75ea6d346 | 213 | |
ikrase | 0:aaa75ea6d346 | 214 | // Handles a received packet in received_packet. |
ikrase | 0:aaa75ea6d346 | 215 | void process_received_packet(); |
ikrase | 0:aaa75ea6d346 | 216 | |
ikrase | 0:aaa75ea6d346 | 217 | HalInterface& hal; |
ikrase | 0:aaa75ea6d346 | 218 | |
ikrase | 0:aaa75ea6d346 | 219 | // Array of associated DataInterface objects. The index+1 is the |
ikrase | 0:aaa75ea6d346 | 220 | // DataInterface's data ID field. |
ikrase | 0:aaa75ea6d346 | 221 | Data* data[MAX_DATA_PER_TELEMETRY]; |
ikrase | 0:aaa75ea6d346 | 222 | // Whether each data has been updated or not. |
ikrase | 0:aaa75ea6d346 | 223 | bool data_updated[MAX_DATA_PER_TELEMETRY]; |
ikrase | 0:aaa75ea6d346 | 224 | // Count of associated DataInterface objects. |
ikrase | 0:aaa75ea6d346 | 225 | size_t data_count; |
ikrase | 0:aaa75ea6d346 | 226 | |
ikrase | 0:aaa75ea6d346 | 227 | // Buffer holding the receive packet being assembled / parsed. |
ikrase | 0:aaa75ea6d346 | 228 | ReceivePacketBuffer received_packet; |
ikrase | 0:aaa75ea6d346 | 229 | |
ikrase | 0:aaa75ea6d346 | 230 | enum DecoderState { |
ikrase | 0:aaa75ea6d346 | 231 | SOF, // reading start-of-frame sequence (or just non-telemetry data) |
ikrase | 0:aaa75ea6d346 | 232 | LENGTH, // reading packet length |
ikrase | 0:aaa75ea6d346 | 233 | DATA, // reading telemetry packet data |
ikrase | 0:aaa75ea6d346 | 234 | DATA_DESTUFF, // reading a stuffed byte |
ikrase | 0:aaa75ea6d346 | 235 | DATA_DESTUFF_END // last stuffed byte in a packet |
ikrase | 0:aaa75ea6d346 | 236 | } decoder_state; |
ikrase | 0:aaa75ea6d346 | 237 | |
ikrase | 0:aaa75ea6d346 | 238 | size_t decoder_pos; |
ikrase | 0:aaa75ea6d346 | 239 | size_t packet_length; |
ikrase | 0:aaa75ea6d346 | 240 | bool decoder_last_received; |
ikrase | 0:aaa75ea6d346 | 241 | uint32_t decoder_last_receive_ms; |
ikrase | 0:aaa75ea6d346 | 242 | |
ikrase | 0:aaa75ea6d346 | 243 | bool header_transmitted; |
ikrase | 0:aaa75ea6d346 | 244 | |
ikrase | 0:aaa75ea6d346 | 245 | // Sequence number of the next packet to be transmitted. |
ikrase | 0:aaa75ea6d346 | 246 | uint8_t packet_tx_sequence; |
ikrase | 0:aaa75ea6d346 | 247 | uint8_t packet_rx_sequence; // TODO use this somewhere |
ikrase | 0:aaa75ea6d346 | 248 | }; |
ikrase | 0:aaa75ea6d346 | 249 | |
ikrase | 0:aaa75ea6d346 | 250 | // A telemetry packet with a length known before data is written to it. |
ikrase | 0:aaa75ea6d346 | 251 | // Data is written directly to the hardware transmit buffers without packet |
ikrase | 0:aaa75ea6d346 | 252 | // buffering. Assumes transmit buffers won't fill up. |
ikrase | 0:aaa75ea6d346 | 253 | class FixedLengthTransmitPacket : public TransmitPacketInterface { |
ikrase | 0:aaa75ea6d346 | 254 | public: |
ikrase | 0:aaa75ea6d346 | 255 | FixedLengthTransmitPacket(HalInterface& hal, size_t length); |
ikrase | 0:aaa75ea6d346 | 256 | |
ikrase | 0:aaa75ea6d346 | 257 | virtual void write_byte(uint8_t data); |
ikrase | 0:aaa75ea6d346 | 258 | |
ikrase | 0:aaa75ea6d346 | 259 | virtual void write_uint8(uint8_t data); |
ikrase | 0:aaa75ea6d346 | 260 | virtual void write_uint16(uint16_t data); |
ikrase | 0:aaa75ea6d346 | 261 | virtual void write_uint32(uint32_t data); |
ikrase | 0:aaa75ea6d346 | 262 | virtual void write_float(float data); |
ikrase | 0:aaa75ea6d346 | 263 | |
ikrase | 0:aaa75ea6d346 | 264 | virtual void finish(); |
ikrase | 0:aaa75ea6d346 | 265 | |
ikrase | 0:aaa75ea6d346 | 266 | protected: |
ikrase | 0:aaa75ea6d346 | 267 | HalInterface& hal; |
ikrase | 0:aaa75ea6d346 | 268 | |
ikrase | 0:aaa75ea6d346 | 269 | // Predetermined length, in bytes, of this packet's payload, for sanity check. |
ikrase | 0:aaa75ea6d346 | 270 | size_t length; |
ikrase | 0:aaa75ea6d346 | 271 | |
ikrase | 0:aaa75ea6d346 | 272 | // Current length, in bytes, of this packet's payload. |
ikrase | 0:aaa75ea6d346 | 273 | size_t count; |
ikrase | 0:aaa75ea6d346 | 274 | |
ikrase | 0:aaa75ea6d346 | 275 | // Is the packet valid? |
ikrase | 0:aaa75ea6d346 | 276 | bool valid; |
ikrase | 0:aaa75ea6d346 | 277 | }; |
ikrase | 0:aaa75ea6d346 | 278 | |
ikrase | 0:aaa75ea6d346 | 279 | template <typename T> |
ikrase | 0:aaa75ea6d346 | 280 | class Numeric : public Data { |
ikrase | 0:aaa75ea6d346 | 281 | public: |
ikrase | 0:aaa75ea6d346 | 282 | Numeric(Telemetry& telemetry_container, |
ikrase | 0:aaa75ea6d346 | 283 | const char* internal_name, const char* display_name, |
ikrase | 0:aaa75ea6d346 | 284 | const char* units, T init_value): |
ikrase | 0:aaa75ea6d346 | 285 | Data(internal_name, display_name, units), |
ikrase | 0:aaa75ea6d346 | 286 | telemetry_container(telemetry_container), |
ikrase | 0:aaa75ea6d346 | 287 | value(init_value), min_val(init_value), max_val(init_value), |
ikrase | 0:aaa75ea6d346 | 288 | frozen(false) { |
ikrase | 0:aaa75ea6d346 | 289 | data_id = telemetry_container.add_data(*this); |
ikrase | 0:aaa75ea6d346 | 290 | } |
ikrase | 0:aaa75ea6d346 | 291 | |
ikrase | 0:aaa75ea6d346 | 292 | T operator = (T b) { |
ikrase | 0:aaa75ea6d346 | 293 | if (!frozen) { |
ikrase | 0:aaa75ea6d346 | 294 | value = b; |
ikrase | 0:aaa75ea6d346 | 295 | telemetry_container.mark_data_updated(data_id); |
ikrase | 0:aaa75ea6d346 | 296 | } |
ikrase | 0:aaa75ea6d346 | 297 | return value; |
ikrase | 0:aaa75ea6d346 | 298 | } |
ikrase | 0:aaa75ea6d346 | 299 | |
ikrase | 0:aaa75ea6d346 | 300 | operator T() { |
ikrase | 0:aaa75ea6d346 | 301 | return value; |
ikrase | 0:aaa75ea6d346 | 302 | } |
ikrase | 0:aaa75ea6d346 | 303 | |
ikrase | 0:aaa75ea6d346 | 304 | Numeric<T>& set_limits(T min, T max) { |
ikrase | 0:aaa75ea6d346 | 305 | min_val = min; |
ikrase | 0:aaa75ea6d346 | 306 | max_val = max; |
ikrase | 0:aaa75ea6d346 | 307 | return *this; |
ikrase | 0:aaa75ea6d346 | 308 | } |
ikrase | 0:aaa75ea6d346 | 309 | |
ikrase | 0:aaa75ea6d346 | 310 | virtual uint8_t get_data_type() { return DATATYPE_NUMERIC; } |
ikrase | 0:aaa75ea6d346 | 311 | |
ikrase | 0:aaa75ea6d346 | 312 | virtual size_t get_header_kvrs_length() { |
ikrase | 0:aaa75ea6d346 | 313 | return Data::get_header_kvrs_length() |
ikrase | 0:aaa75ea6d346 | 314 | + 1 + 1 // subtype |
ikrase | 0:aaa75ea6d346 | 315 | + 1 + 1 // data length |
ikrase | 0:aaa75ea6d346 | 316 | + 1 + sizeof(value) + sizeof(value); // limits |
ikrase | 0:aaa75ea6d346 | 317 | } |
ikrase | 0:aaa75ea6d346 | 318 | |
ikrase | 0:aaa75ea6d346 | 319 | virtual void write_header_kvrs(TransmitPacketInterface& packet) { |
ikrase | 0:aaa75ea6d346 | 320 | Data::write_header_kvrs(packet); |
ikrase | 0:aaa75ea6d346 | 321 | packet.write_uint8(RECORDID_NUMERIC_SUBTYPE); |
ikrase | 0:aaa75ea6d346 | 322 | packet.write_uint8(get_subtype()); |
ikrase | 0:aaa75ea6d346 | 323 | packet.write_uint8(RECORDID_NUMERIC_LENGTH); |
ikrase | 0:aaa75ea6d346 | 324 | packet.write_uint8(sizeof(value)); |
ikrase | 0:aaa75ea6d346 | 325 | packet.write_uint8(RECORDID_NUMERIC_LIMITS); |
ikrase | 0:aaa75ea6d346 | 326 | serialize_data(min_val, packet); |
ikrase | 0:aaa75ea6d346 | 327 | serialize_data(max_val, packet); |
ikrase | 0:aaa75ea6d346 | 328 | } |
ikrase | 0:aaa75ea6d346 | 329 | |
ikrase | 0:aaa75ea6d346 | 330 | uint8_t get_subtype(); |
ikrase | 0:aaa75ea6d346 | 331 | |
ikrase | 0:aaa75ea6d346 | 332 | virtual size_t get_payload_length() { return sizeof(value); } |
ikrase | 0:aaa75ea6d346 | 333 | virtual void write_payload(TransmitPacketInterface& packet) { serialize_data(value, packet); } |
ikrase | 0:aaa75ea6d346 | 334 | virtual void set_from_packet(ReceivePacketBuffer& packet) { |
ikrase | 0:aaa75ea6d346 | 335 | value = deserialize_data(packet); |
ikrase | 0:aaa75ea6d346 | 336 | telemetry_container.mark_data_updated(data_id); } |
ikrase | 0:aaa75ea6d346 | 337 | |
ikrase | 0:aaa75ea6d346 | 338 | void serialize_data(T data, TransmitPacketInterface& packet); |
ikrase | 0:aaa75ea6d346 | 339 | T deserialize_data(ReceivePacketBuffer& packet); |
ikrase | 0:aaa75ea6d346 | 340 | |
ikrase | 0:aaa75ea6d346 | 341 | protected: |
ikrase | 0:aaa75ea6d346 | 342 | Telemetry& telemetry_container; |
ikrase | 0:aaa75ea6d346 | 343 | size_t data_id; |
ikrase | 0:aaa75ea6d346 | 344 | T value; |
ikrase | 0:aaa75ea6d346 | 345 | T min_val, max_val; |
ikrase | 0:aaa75ea6d346 | 346 | bool frozen; |
ikrase | 0:aaa75ea6d346 | 347 | }; |
ikrase | 0:aaa75ea6d346 | 348 | |
ikrase | 0:aaa75ea6d346 | 349 | template <typename T, uint32_t array_count> |
ikrase | 0:aaa75ea6d346 | 350 | class NumericArrayAccessor; |
ikrase | 0:aaa75ea6d346 | 351 | |
ikrase | 0:aaa75ea6d346 | 352 | // TODO: fix this partial specialization inheritance nightmare |
ikrase | 0:aaa75ea6d346 | 353 | template <typename T, uint32_t array_count> |
ikrase | 0:aaa75ea6d346 | 354 | class NumericArrayBase : public Data { |
ikrase | 0:aaa75ea6d346 | 355 | friend class NumericArrayAccessor<T, array_count>; |
ikrase | 0:aaa75ea6d346 | 356 | public: |
ikrase | 0:aaa75ea6d346 | 357 | NumericArrayBase(Telemetry& telemetry_container, |
ikrase | 0:aaa75ea6d346 | 358 | const char* internal_name, const char* display_name, |
ikrase | 0:aaa75ea6d346 | 359 | const char* units, T elem_init_value): |
ikrase | 0:aaa75ea6d346 | 360 | Data(internal_name, display_name, units), |
ikrase | 0:aaa75ea6d346 | 361 | telemetry_container(telemetry_container), |
ikrase | 0:aaa75ea6d346 | 362 | min_val(elem_init_value), max_val(elem_init_value), |
ikrase | 0:aaa75ea6d346 | 363 | frozen(false) { |
ikrase | 0:aaa75ea6d346 | 364 | for (size_t i=0; i<array_count; i++) { |
ikrase | 0:aaa75ea6d346 | 365 | value[i] = elem_init_value; |
ikrase | 0:aaa75ea6d346 | 366 | } |
ikrase | 0:aaa75ea6d346 | 367 | data_id = telemetry_container.add_data(*this); |
ikrase | 0:aaa75ea6d346 | 368 | } |
ikrase | 0:aaa75ea6d346 | 369 | |
ikrase | 0:aaa75ea6d346 | 370 | NumericArrayAccessor<T, array_count> operator[] (const int index) { |
ikrase | 0:aaa75ea6d346 | 371 | // TODO: add bounds checking here |
ikrase | 0:aaa75ea6d346 | 372 | // TODO: add "frozen" check |
ikrase | 0:aaa75ea6d346 | 373 | return NumericArrayAccessor<T, array_count>(*this, index); |
ikrase | 0:aaa75ea6d346 | 374 | } |
ikrase | 0:aaa75ea6d346 | 375 | |
ikrase | 0:aaa75ea6d346 | 376 | NumericArrayBase<T, array_count>& set_limits(T min, T max) { |
ikrase | 0:aaa75ea6d346 | 377 | min_val = min; |
ikrase | 0:aaa75ea6d346 | 378 | max_val = max; |
ikrase | 0:aaa75ea6d346 | 379 | return *this; |
ikrase | 0:aaa75ea6d346 | 380 | } |
ikrase | 0:aaa75ea6d346 | 381 | |
ikrase | 0:aaa75ea6d346 | 382 | virtual uint8_t get_data_type() { return DATATYPE_NUMERIC_ARRAY; } |
ikrase | 0:aaa75ea6d346 | 383 | |
ikrase | 0:aaa75ea6d346 | 384 | virtual size_t get_header_kvrs_length() { |
ikrase | 0:aaa75ea6d346 | 385 | return Data::get_header_kvrs_length() |
ikrase | 0:aaa75ea6d346 | 386 | + 1 + 1 // subtype |
ikrase | 0:aaa75ea6d346 | 387 | + 1 + 1 // data length |
ikrase | 0:aaa75ea6d346 | 388 | + 1 + 4 // array length |
ikrase | 0:aaa75ea6d346 | 389 | + 1 + sizeof(value[0]) + sizeof(value[0]); // limits |
ikrase | 0:aaa75ea6d346 | 390 | } |
ikrase | 0:aaa75ea6d346 | 391 | |
ikrase | 0:aaa75ea6d346 | 392 | virtual void write_header_kvrs(TransmitPacketInterface& packet) { |
ikrase | 0:aaa75ea6d346 | 393 | Data::write_header_kvrs(packet); |
ikrase | 0:aaa75ea6d346 | 394 | packet.write_uint8(RECORDID_NUMERIC_SUBTYPE); |
ikrase | 0:aaa75ea6d346 | 395 | packet.write_uint8(get_subtype()); |
ikrase | 0:aaa75ea6d346 | 396 | packet.write_uint8(RECORDID_NUMERIC_LENGTH); |
ikrase | 0:aaa75ea6d346 | 397 | packet.write_uint8(sizeof(value[0])); |
ikrase | 0:aaa75ea6d346 | 398 | packet.write_uint8(RECORDID_ARRAY_COUNT); |
ikrase | 0:aaa75ea6d346 | 399 | packet.write_uint32(array_count); |
ikrase | 0:aaa75ea6d346 | 400 | packet.write_uint8(RECORDID_NUMERIC_LIMITS); |
ikrase | 0:aaa75ea6d346 | 401 | serialize_data(min_val, packet); |
ikrase | 0:aaa75ea6d346 | 402 | serialize_data(max_val, packet); |
ikrase | 0:aaa75ea6d346 | 403 | } |
ikrase | 0:aaa75ea6d346 | 404 | |
ikrase | 0:aaa75ea6d346 | 405 | virtual uint8_t get_subtype() = 0; |
ikrase | 0:aaa75ea6d346 | 406 | |
ikrase | 0:aaa75ea6d346 | 407 | virtual size_t get_payload_length() { return sizeof(value); } |
ikrase | 0:aaa75ea6d346 | 408 | virtual void write_payload(TransmitPacketInterface& packet) { |
ikrase | 0:aaa75ea6d346 | 409 | for (size_t i=0; i<array_count; i++) { serialize_data(this->value[i], packet); } } |
ikrase | 0:aaa75ea6d346 | 410 | virtual void set_from_packet(ReceivePacketBuffer& packet) { |
ikrase | 0:aaa75ea6d346 | 411 | for (size_t i=0; i<array_count; i++) { value[i] = deserialize_data(packet); } |
ikrase | 0:aaa75ea6d346 | 412 | telemetry_container.mark_data_updated(data_id); } |
ikrase | 0:aaa75ea6d346 | 413 | |
ikrase | 0:aaa75ea6d346 | 414 | virtual void serialize_data(T data, TransmitPacketInterface& packet) = 0; |
ikrase | 0:aaa75ea6d346 | 415 | virtual T deserialize_data(ReceivePacketBuffer& packet) = 0; |
ikrase | 0:aaa75ea6d346 | 416 | |
ikrase | 0:aaa75ea6d346 | 417 | protected: |
ikrase | 0:aaa75ea6d346 | 418 | Telemetry& telemetry_container; |
ikrase | 0:aaa75ea6d346 | 419 | size_t data_id; |
ikrase | 0:aaa75ea6d346 | 420 | T value[array_count]; |
ikrase | 0:aaa75ea6d346 | 421 | T min_val, max_val; |
ikrase | 0:aaa75ea6d346 | 422 | bool frozen; |
ikrase | 0:aaa75ea6d346 | 423 | }; |
ikrase | 0:aaa75ea6d346 | 424 | |
ikrase | 0:aaa75ea6d346 | 425 | template <typename T, uint32_t array_count> |
ikrase | 0:aaa75ea6d346 | 426 | class NumericArrayAccessor { |
ikrase | 0:aaa75ea6d346 | 427 | public: |
ikrase | 0:aaa75ea6d346 | 428 | NumericArrayAccessor(NumericArrayBase<T, array_count>& container, size_t index) : |
ikrase | 0:aaa75ea6d346 | 429 | container(container), index(index) { } |
ikrase | 0:aaa75ea6d346 | 430 | |
ikrase | 0:aaa75ea6d346 | 431 | T operator = (T b) { |
ikrase | 0:aaa75ea6d346 | 432 | if (!container.frozen) { |
ikrase | 0:aaa75ea6d346 | 433 | container.value[index] = b; |
ikrase | 0:aaa75ea6d346 | 434 | container.telemetry_container.mark_data_updated(container.data_id); |
ikrase | 0:aaa75ea6d346 | 435 | } |
ikrase | 0:aaa75ea6d346 | 436 | return container.value[index]; |
ikrase | 0:aaa75ea6d346 | 437 | } |
ikrase | 0:aaa75ea6d346 | 438 | |
ikrase | 0:aaa75ea6d346 | 439 | operator T() { |
ikrase | 0:aaa75ea6d346 | 440 | return container.value[index]; |
ikrase | 0:aaa75ea6d346 | 441 | } |
ikrase | 0:aaa75ea6d346 | 442 | |
ikrase | 0:aaa75ea6d346 | 443 | protected: |
ikrase | 0:aaa75ea6d346 | 444 | NumericArrayBase<T, array_count>& container; |
ikrase | 0:aaa75ea6d346 | 445 | size_t index; |
ikrase | 0:aaa75ea6d346 | 446 | }; |
ikrase | 0:aaa75ea6d346 | 447 | |
ikrase | 0:aaa75ea6d346 | 448 | template <typename T, uint32_t array_count> |
ikrase | 0:aaa75ea6d346 | 449 | class NumericArray : public NumericArrayBase<T, array_count> { |
ikrase | 0:aaa75ea6d346 | 450 | NumericArray(Telemetry& telemetry_container, |
ikrase | 0:aaa75ea6d346 | 451 | const char* internal_name, const char* display_name, |
ikrase | 0:aaa75ea6d346 | 452 | const char* units, T elem_init_value); |
ikrase | 0:aaa75ea6d346 | 453 | virtual uint8_t get_subtype(); |
ikrase | 0:aaa75ea6d346 | 454 | virtual void write_payload(TransmitPacketInterface& packet); |
ikrase | 0:aaa75ea6d346 | 455 | virtual T deserialize_data(ReceivePacketBuffer& packet); |
ikrase | 0:aaa75ea6d346 | 456 | }; |
ikrase | 0:aaa75ea6d346 | 457 | |
ikrase | 0:aaa75ea6d346 | 458 | template <uint32_t array_count> |
ikrase | 0:aaa75ea6d346 | 459 | class NumericArray<uint8_t, array_count> : public NumericArrayBase<uint8_t, array_count> { |
ikrase | 0:aaa75ea6d346 | 460 | public: |
ikrase | 0:aaa75ea6d346 | 461 | NumericArray(Telemetry& telemetry_container, |
ikrase | 0:aaa75ea6d346 | 462 | const char* internal_name, const char* display_name, |
ikrase | 0:aaa75ea6d346 | 463 | const char* units, uint8_t elem_init_value): |
ikrase | 0:aaa75ea6d346 | 464 | NumericArrayBase<uint8_t, array_count>( |
ikrase | 0:aaa75ea6d346 | 465 | telemetry_container, internal_name, display_name, |
ikrase | 0:aaa75ea6d346 | 466 | units, elem_init_value) {}; |
ikrase | 0:aaa75ea6d346 | 467 | virtual uint8_t get_subtype() {return NUMERIC_SUBTYPE_UINT; } |
ikrase | 0:aaa75ea6d346 | 468 | virtual void serialize_data(uint8_t data, TransmitPacketInterface& packet) { |
ikrase | 0:aaa75ea6d346 | 469 | packet.write_uint8(data); } |
ikrase | 0:aaa75ea6d346 | 470 | virtual uint8_t deserialize_data(ReceivePacketBuffer& packet) { |
ikrase | 0:aaa75ea6d346 | 471 | return packet.read_uint8(); } |
ikrase | 0:aaa75ea6d346 | 472 | }; |
ikrase | 0:aaa75ea6d346 | 473 | |
ikrase | 0:aaa75ea6d346 | 474 | template <uint32_t array_count> |
ikrase | 0:aaa75ea6d346 | 475 | class NumericArray<uint16_t, array_count> : public NumericArrayBase<uint16_t, array_count> { |
ikrase | 0:aaa75ea6d346 | 476 | public: |
ikrase | 0:aaa75ea6d346 | 477 | NumericArray(Telemetry& telemetry_container, |
ikrase | 0:aaa75ea6d346 | 478 | const char* internal_name, const char* display_name, |
ikrase | 0:aaa75ea6d346 | 479 | const char* units, uint16_t elem_init_value): |
ikrase | 0:aaa75ea6d346 | 480 | NumericArrayBase<uint16_t, array_count>( |
ikrase | 0:aaa75ea6d346 | 481 | telemetry_container, internal_name, display_name, |
ikrase | 0:aaa75ea6d346 | 482 | units, elem_init_value) {}; |
ikrase | 0:aaa75ea6d346 | 483 | virtual uint8_t get_subtype() {return NUMERIC_SUBTYPE_UINT; } |
ikrase | 0:aaa75ea6d346 | 484 | virtual void serialize_data(uint16_t data, TransmitPacketInterface& packet) { |
ikrase | 0:aaa75ea6d346 | 485 | packet.write_uint16(data); } |
ikrase | 0:aaa75ea6d346 | 486 | virtual uint16_t deserialize_data(ReceivePacketBuffer& packet) { |
ikrase | 0:aaa75ea6d346 | 487 | return packet.read_uint16(); } |
ikrase | 0:aaa75ea6d346 | 488 | }; |
ikrase | 0:aaa75ea6d346 | 489 | |
ikrase | 0:aaa75ea6d346 | 490 | template <uint32_t array_count> |
ikrase | 0:aaa75ea6d346 | 491 | class NumericArray<uint32_t, array_count> : public NumericArrayBase<uint32_t, array_count> { |
ikrase | 0:aaa75ea6d346 | 492 | public: |
ikrase | 0:aaa75ea6d346 | 493 | NumericArray(Telemetry& telemetry_container, |
ikrase | 0:aaa75ea6d346 | 494 | const char* internal_name, const char* display_name, |
ikrase | 0:aaa75ea6d346 | 495 | const char* units, uint32_t elem_init_value): |
ikrase | 0:aaa75ea6d346 | 496 | NumericArrayBase<uint32_t, array_count>( |
ikrase | 0:aaa75ea6d346 | 497 | telemetry_container, internal_name, display_name, |
ikrase | 0:aaa75ea6d346 | 498 | units, elem_init_value) {}; |
ikrase | 0:aaa75ea6d346 | 499 | virtual uint8_t get_subtype() {return NUMERIC_SUBTYPE_UINT; } |
ikrase | 0:aaa75ea6d346 | 500 | virtual void serialize_data(uint32_t data, TransmitPacketInterface& packet) { |
ikrase | 0:aaa75ea6d346 | 501 | packet.write_uint32(data); } |
ikrase | 0:aaa75ea6d346 | 502 | virtual uint32_t deserialize_data(ReceivePacketBuffer& packet) { |
ikrase | 0:aaa75ea6d346 | 503 | return packet.read_uint32(); } |
ikrase | 0:aaa75ea6d346 | 504 | }; |
ikrase | 0:aaa75ea6d346 | 505 | |
ikrase | 0:aaa75ea6d346 | 506 | template <uint32_t array_count> |
ikrase | 0:aaa75ea6d346 | 507 | class NumericArray<float, array_count> : public NumericArrayBase<float, array_count> { |
ikrase | 0:aaa75ea6d346 | 508 | public: |
ikrase | 0:aaa75ea6d346 | 509 | NumericArray(Telemetry& telemetry_container, |
ikrase | 0:aaa75ea6d346 | 510 | const char* internal_name, const char* display_name, |
ikrase | 0:aaa75ea6d346 | 511 | const char* units, float elem_init_value): |
ikrase | 0:aaa75ea6d346 | 512 | NumericArrayBase<float, array_count>( |
ikrase | 0:aaa75ea6d346 | 513 | telemetry_container, internal_name, display_name, |
ikrase | 0:aaa75ea6d346 | 514 | units, elem_init_value) {}; |
ikrase | 0:aaa75ea6d346 | 515 | virtual uint8_t get_subtype() {return NUMERIC_SUBTYPE_FLOAT; } |
ikrase | 0:aaa75ea6d346 | 516 | virtual void serialize_data(float data, TransmitPacketInterface& packet) { |
ikrase | 0:aaa75ea6d346 | 517 | packet.write_float(data); } |
ikrase | 0:aaa75ea6d346 | 518 | virtual float deserialize_data(ReceivePacketBuffer& packet) { |
ikrase | 0:aaa75ea6d346 | 519 | return packet.read_float(); } |
ikrase | 0:aaa75ea6d346 | 520 | }; |
ikrase | 0:aaa75ea6d346 | 521 | |
ikrase | 0:aaa75ea6d346 | 522 | } |
ikrase | 0:aaa75ea6d346 | 523 | |
ikrase | 0:aaa75ea6d346 | 524 | #endif |