ducky's telemetry library

Committer:
dnleek
Date:
Wed Mar 18 07:59:36 2015 +0000
Revision:
0:80dd1516ad46
Telemetry library from ducky

Who changed what in which revision?

UserRevisionLine numberNew 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