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