RIT VEXU Core API
Loading...
Searching...
No Matches
types.h
1#pragma once
2#include <bit>
3#include <cstddef>
4#include <cstdint>
5#include <cstring>
6#include <span>
7#include <string>
8#include <tuple>
9#include <type_traits>
10#include <utility>
11#include <vector>
12
13namespace VDP {
14using Packet = std::vector<uint8_t>;
15using PacketSpan = std::span<const uint8_t>;
16
18enum class TypeId : uint8_t {
19 Record = 0,
20 Boolean = 1,
21 String = 2,
22
23 Double = 3,
24 Float = 4,
25
26 Uint8 = 5,
27 Uint16 = 6,
28 Uint32 = 7,
29 Uint64 = 8,
30
31 Int8 = 9,
32 Int16 = 10,
33 Int32 = 11,
34 Int64 = 12,
35
36 Q3_4 = 13,
37 Q4_4 = 14,
38 Q7_1 = 15,
39 Q1_7 = 16,
40 Q6_2 = 17,
41 Q2_6 = 18,
42 Q7_8 = 19,
43 Q8_8 = 20,
44 Q15_1 = 21,
45 Q1_15 = 22,
46 Q10_6 = 23,
47 Q9_6 = 24,
48 Q12_12 = 25,
49 Q16_8 = 26,
50 Q8_16 = 27,
51 Q15_16 = 28,
52 Q16_16 = 29,
53 Q24_8 = 30,
54 Q8_24 = 31,
55 Q31_32 = 32,
56 Q32_32 = 33,
57
58 UNKNOWN = 34,
59};
60
62template <typename... Fields>
63class Record;
64
66template <typename T>
67class Field;
68
69// TypeIdMap proto
70template <typename T>
71struct TypeIdMap;
72
74template <typename... Fields>
75struct TypeIdMap<Record<Fields...>> {
76 static constexpr TypeId value = TypeId::Record;
77};
78
79template <>
80struct TypeIdMap<bool> {
81 static constexpr TypeId value = TypeId::Boolean;
82};
83
84template <>
85struct TypeIdMap<std::string> {
86 static constexpr TypeId value = TypeId::String;
87};
88
89template <>
90struct TypeIdMap<double> {
91 static constexpr TypeId value = TypeId::Double;
92};
93
94template <>
95struct TypeIdMap<float> {
96 static constexpr TypeId value = TypeId::Float;
97};
98
99template <>
100struct TypeIdMap<uint8_t> {
101 static constexpr TypeId value = TypeId::Uint8;
102};
103
104template <>
105struct TypeIdMap<uint16_t> {
106 static constexpr TypeId value = TypeId::Uint16;
107};
108
109template <>
110struct TypeIdMap<uint32_t> {
111 static constexpr TypeId value = TypeId::Uint32;
112};
113
114template <>
115struct TypeIdMap<uint64_t> {
116 static constexpr TypeId value = TypeId::Uint64;
117};
118
119template <>
120struct TypeIdMap<int8_t> {
121 static constexpr TypeId value = TypeId::Int8;
122};
123
124template <>
125struct TypeIdMap<int16_t> {
126 static constexpr TypeId value = TypeId::Int16;
127};
128
129template <>
130struct TypeIdMap<int32_t> {
131 static constexpr TypeId value = TypeId::Int32;
132};
133
134template <>
135struct TypeIdMap<int64_t> {
136 static constexpr TypeId value = TypeId::Int64;
137};
138
140template <typename T>
141 requires std::is_same_v<T, int>
142struct TypeIdMap<T> {
143 static_assert(sizeof(T) == sizeof(int32_t), "VDP requires a 32-bit int");
144 static constexpr TypeId value = TypeId::Int32;
145};
146
148template <TypeId FixedPointType, typename Storage>
150 static constexpr TypeId type = FixedPointType;
151
152 Storage raw_value;
153};
154
156
163
170
178
181
183template <TypeId Id, typename Storage>
184struct TypeIdMap<FixedPoint<Id, Storage>> {
185 static constexpr TypeId value = Id;
186};
187
189template <typename T, typename = void>
190struct HasTypeId : std::false_type {};
191
193template <typename T>
194struct HasTypeId<T, std::void_t<decltype(TypeIdMap<T>::value)>> : std::true_type {};
195
197template <typename T>
198struct IsField : std::false_type {};
199
200template <typename T>
201struct IsField<Field<T>> : std::true_type {};
202
203template <typename... Fields>
204struct IsField<Record<Fields...>> : std::true_type {};
205
206template <typename... Fields>
207 requires(IsField<std::remove_cvref_t<Fields>>::value && ...)
208struct TypeIdMap<std::tuple<Fields...>> {
209 static constexpr TypeId value = TypeId::Record;
210};
211
213template <typename T>
214struct IsFixedPoint : std::false_type {};
215
216template <TypeId Id, typename Storage>
217struct IsFixedPoint<FixedPoint<Id, Storage>> : std::true_type {};
218
220std::string to_string(TypeId t);
221
223template <typename T>
224class Field {
225 static_assert(HasTypeId<T>::value, "Field<T>: T does not have an associated TypeId");
226
227 public:
233 Field(std::string name, T& value) : name_(std::move(name)), value_(value) {};
234
236 const std::string& get_name() const { return name_; };
237
239 const T& get_value() const { return value_; }
240
242 const TypeId get_type() const { return TypeIdMap<T>::value; }
243
248 VDP::Packet serialize_schema() const {
249 VDP::Packet out;
250 out.push_back(static_cast<uint8_t>(get_type()));
251 out.insert(out.end(), name_.begin(), name_.end());
252 out.push_back(0);
253
254 return (out);
255 }
256
261 VDP::Packet serialize_data() const {
262 // Since strings are not fixed length, we need to use a 0 byte to signal when it has ended
263 if constexpr (std::is_same_v<T, std::string>) {
264 VDP::Packet out(value_.begin(), value_.end());
265 out.push_back(0);
266 return out;
267 } else {
268 VDP::Packet out(sizeof(T));
269 std::memcpy(out.data(), &value_, sizeof(T));
270 return out;
271 }
272 }
273
278 size_t apply_update(const VDP::PacketSpan packet_in) {
279 if constexpr (std::is_same_v<T, std::string>) {
280 // if the field holds a string, find the 0 delimiter and and get the string for that
281 // length of the packet
282 auto str_end = std::find(packet_in.begin(), packet_in.end(), uint8_t(0));
283
284 auto length = str_end - packet_in.begin();
285
286 value_.assign(packet_in.begin(), str_end);
287
288 return length + 1;
289 } else {
290 /*
291 * if the field is a value that is not of variable size, just copy the data into the
292 * field's data
293 */
294 std::memcpy(&value_, packet_in.data(), sizeof(T));
295
296 return sizeof(T);
297 }
298 }
299
301 std::string data_to_string(std::size_t depth = 0) const {
302 // add the name and a specified number of indents to the string
303 std::string out = std::string(depth * 2, ' ') + name_ + " : ";
304
305 // check the held type
306 if constexpr (std::is_same_v<T, std::string>) {
307 // if it is a string just add the value directly
308 return out + value_;
309 } else if constexpr (std::is_same_v<T, bool>) {
310 // if it is a boolean translate it to a true or false string
311 return out + (value_ ? "true" : "false");
312 } else if constexpr (std::is_integral_v<T>) {
313 /*
314 * if it is an integral check if it is signed or unsigned,
315 * convert to uint64_t or int64_t accordingly, and get the string
316 * format of that
317 */
318 if (std::is_signed_v<T>) {
319 return out + std::to_string(static_cast<int64_t>(value_));
320 } else {
321 return std::to_string(static_cast<uint64_t>(value_));
322 }
323 } else if constexpr (std::is_floating_point_v<T>) {
324 // if it is a floating point we can just cast it to a string
325 return std::to_string(value_);
326 } else if constexpr (IsFixedPoint<T>::value) {
327 /*
328 * if it is a fixed point check if it is signed or unsigned,
329 * cast the raw byte value of the data to an int64_t or uint64_t,
330 * and then get the string format of that
331 */
332 if constexpr (std::is_signed_v<decltype(value_.raw_value)>) {
333 return std::to_string(static_cast<int64_t>(value_.raw_value));
334 } else {
335 return std::to_string(static_cast<uint64_t>(value_.raw_value));
336 }
337 } else {
338 // if none of those work then we do not support this type
339 static_assert(std::is_same_v<T, void>, "data_to_string does not support this type");
340 }
341 }
342
344 std::string schema_to_string(std::size_t depth = 0) const {
345 return std::string(depth * 2, ' ') + name_ + " : " + VDP::to_string(get_type());
346 }
347
348 protected:
349 std::string name_;
350 T& value_;
351};
352
354template <typename T>
355Field(std::string, T&) -> Field<T>;
356
361template <typename... Fields>
362class Record {
363 // checks that each element being input is a Field or a Record
364 static_assert((IsField<std::remove_cvref_t<Fields>>::value && ...),
365 "Record elements must all be Field or Record objects");
366
367 public:
373 explicit Record(std::string name, Fields&... fields)
374 : name_(std::move(name)), value_(fields...) {}
375
377 const std::string& get_name() const { return name_; }
378
380 TypeId get_type() const { return TypeId::Record; }
381
383 const auto& get_value() const { return value_; }
384
386 static constexpr std::size_t size() { return sizeof...(Fields); }
387
389 template <std::size_t I>
390 const auto& get() const {
391 return std::get<I>(value_);
392 }
393
395 VDP::Packet serialize_schema() const {
396 static_assert(sizeof...(Fields) <= 255, "A record cannot contain more than 255 fields");
397 Packet out;
398
399 // add the type byte
400 out.push_back(static_cast<uint8_t>(TypeId::Record));
401
402 // add the name
403 out.insert(out.end(), this->get_name().begin(), this->get_name().end());
404 out.push_back(0);
405
406 out.push_back((static_cast<uint8_t>(size())));
407
408 // use std::apply to loop through the fields in the tuple
409 std::apply(
410 [&](const auto&... fields) {
411 (
412 [&] {
413 // add the field's serialized schema to the packet
414 Packet field_schema = fields.serialize_schema();
415
416 out.insert(out.end(), field_schema.begin(), field_schema.end());
417 }(),
418 ...);
419 },
420 get_value());
421 return out;
422 }
423
428 VDP::Packet serialize_data() const {
429 VDP::Packet out;
430
431 std::apply(
432 [&](const auto&... fields) {
433 (
434 [&] {
435 // add the field's serialized data to the packet
436 Packet field_schema = fields.serialize_data();
437
438 out.insert(out.end(), field_schema.begin(), field_schema.end());
439 }(),
440 ...);
441 },
442 get_value());
443 return out;
444 }
445
447 size_t apply_update(VDP::PacketSpan pac_in) {
448 std::size_t offset = 0;
449
450 std::apply(
451 [&](auto&... fields) {
452 (
453 [&] {
454 auto read_bytes = fields.apply_update(pac_in.subspan(offset));
455 offset += read_bytes;
456 }(),
457 ...);
458 },
459 get_value());
460 return offset;
461 }
462
467 std::string data_to_string(std::size_t depth = 0) const {
468 std::string out = std::string(depth * 2, ' ') + this->get_name() + " : {\n";
469
470 /*
471 * loop through each field held within the record and add their data strings, increasing
472 * the depth
473 */
474 std::apply(
475 [&](const auto&... fields) {
476 ([&] { out += fields.data_to_string(depth + 1) += ",\n"; }(), ...);
477 },
478 this->value_);
479 out += std::string(depth * 2, ' ') + "}";
480 return out;
481 }
482
487 std::string schema_to_string(std::size_t depth = 0) const {
488 std::string out = std::string(depth * 2, ' ') + this->get_name() + " : record {\n";
489
490 /*
491 * loop through each field held within the record and add their schema strings, increasing
492 * the depth
493 */
494 std::apply(
495 [&](const auto&... fields) {
496 ([&] { out += fields.schema_to_string(depth + 1) + ",\n"; }(), ...);
497 },
498 this->value_);
499 out += std::string(depth * 2, ' ') + "}";
500 return out;
501 }
502
503 private:
504 std::string name_;
505 std::tuple<Fields&...> value_;
506};
507
509template <typename... Fields>
510Record(std::string, Fields&...) -> Record<Fields...>;
511
512} // namespace VDP
Field proto.
Definition types.h:224
std::string schema_to_string(std::size_t depth=0) const
Gets the field's schema as a string.
Definition types.h:344
VDP::Packet serialize_schema() const
Serializes the field's schema in the form of a VDP::Packet.
Definition types.h:248
std::string data_to_string(std::size_t depth=0) const
Gets the field's data as a string.
Definition types.h:301
Field(std::string name, T &value)
Constructor for a field, a form of data to be sent to the debug board.
Definition types.h:233
const std::string & get_name() const
Gets the name of the field.
Definition types.h:236
const TypeId get_type() const
Gets the type of the field in the form of a TypeId enum.
Definition types.h:242
VDP::Packet serialize_data() const
Serializes the field's data in the form of a VDP::Packet.
Definition types.h:261
size_t apply_update(const VDP::PacketSpan packet_in)
Deserializes a packet's data and applies it to the field.
Definition types.h:278
const T & get_value() const
Gets the value currently stored by the field.
Definition types.h:239
Record proto.
Definition types.h:362
VDP::Packet serialize_data() const
Serializes the record's data in the form of a VDP::Packet.
Definition types.h:428
std::string data_to_string(std::size_t depth=0) const
Converts a record's held data to a human readable string.
Definition types.h:467
std::string schema_to_string(std::size_t depth=0) const
converts a record's schema to a human readable string
Definition types.h:487
size_t apply_update(VDP::PacketSpan pac_in)
Deserializes a packet of data and applies it to the Record's fields.
Definition types.h:447
Record(std::string name, Fields &... fields)
Constructs a Record, a field that contains a tuple of other fields.
Definition types.h:373
TypeId get_type() const
Gets the TypeId of the record.
Definition types.h:380
const std::string & get_name() const
Gets the name of the record.
Definition types.h:377
static constexpr std::size_t size()
Getsthe size of the held tuple.
Definition types.h:386
VDP::Packet serialize_schema() const
Serializes the record's schema in the form of a VDP::Packet.
Definition types.h:395
const auto & get() const
Gets the field at a specified index of the held tuple.
Definition types.h:390
const auto & get_value() const
Gets the value stored by the record.
Definition types.h:383
Template for fixed point types.
Definition types.h:149
Primary template to check if a type has an associated id.
Definition types.h:190
Check for if a held field type is a Field.
Definition types.h:198
Check for if a held field type is a fixed point type.
Definition types.h:214