WPILibC++ 2027.0.0-alpha-4
Loading...
Searching...
No Matches
DataLog.hpp
Go to the documentation of this file.
1// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#pragma once
6
7#include <stdint.h>
8
9#include <algorithm>
10#include <concepts>
11#include <initializer_list>
12#include <optional>
13#include <ranges>
14#include <span>
15#include <string>
16#include <string_view>
17#include <tuple>
18#include <utility>
19#include <vector>
20#include <version>
21
22#include "wpi/util/DenseMap.hpp"
23#include "wpi/util/SmallVector.hpp"
25#include "wpi/util/mutex.hpp"
27#include "wpi/util/string.h"
30
31namespace wpi::util {
32class Logger;
33} // namespace wpi::util
34
35namespace wpi::log {
36
37namespace impl {
38
44
45} // namespace impl
46
47/**
48 * A data log for high-speed writing of data values.
49 *
50 * The lifetime of the data log object must be longer than any data log entry
51 * objects that refer to it.
52 *
53 * Finish() is needed only to indicate in the log that a particular entry is
54 * no longer being used (it releases the name to ID mapping). Finish() is not
55 * required to be called for data to be flushed to disk; entries in the log
56 * are written as Append() calls are being made. In fact, Finish() does not
57 * need to be called at all; this is helpful to avoid shutdown races where the
58 * DataLog object might be destroyed before other objects. It's often not a
59 * good idea to call Finish() from destructors for this reason.
60 *
61 * DataLog calls are thread safe. DataLog uses a typical multiple-supplier,
62 * single-consumer setup. Writes to the log are atomic, but there is no
63 * guaranteed order in the log when multiple threads are writing to it;
64 * whichever thread grabs the write mutex first will get written first.
65 * For this reason (as well as the fact that timestamps can be set to
66 * arbitrary values), records in the log are not guaranteed to be sorted by
67 * timestamp.
68 */
69class DataLog {
70 public:
71 virtual ~DataLog() = default;
72
73 DataLog(const DataLog&) = delete;
74 DataLog& operator=(const DataLog&) = delete;
75 DataLog(DataLog&&) = delete;
76 DataLog& operator=(const DataLog&&) = delete;
77
78 /**
79 * Explicitly flushes the log data to disk.
80 */
81 virtual void Flush() = 0;
82
83 /**
84 * Pauses appending of data records to the log. While paused, no data records
85 * are saved (e.g. AppendX is a no-op). Has no effect on entry starts /
86 * finishes / metadata changes.
87 */
88 virtual void Pause();
89
90 /**
91 * Resumes appending of data records to the log.
92 */
93 virtual void Resume();
94
95 /**
96 * Stops appending start/metadata/schema records to the log.
97 */
98 virtual void Stop();
99
100 /**
101 * Returns whether there is a data schema already registered with the given
102 * name.
103 *
104 * @param name Name (the string passed as the data type for records using this
105 * schema)
106 * @return True if schema already registered
107 */
108 bool HasSchema(std::string_view name) const;
109
110 /**
111 * Registers a data schema. Data schemas provide information for how a
112 * certain data type string can be decoded. The type string of a data schema
113 * indicates the type of the schema itself (e.g. "protobuf" for protobuf
114 * schemas, "struct" for struct schemas, etc). In the data log, schemas are
115 * saved just like normal records, with the name being generated from the
116 * provided name: "/.schema/<name>". Duplicate calls to this function with
117 * the same name are silently ignored.
118 *
119 * @param name Name (the string passed as the data type for records using this
120 * schema)
121 * @param type Type of schema (e.g. "protobuf", "struct", etc)
122 * @param schema Schema data
123 * @param timestamp Time stamp (may be 0 to indicate now)
124 */
125 void AddSchema(std::string_view name, std::string_view type,
126 std::span<const uint8_t> schema, int64_t timestamp = 0);
127
128 /**
129 * Registers a data schema. Data schemas provide information for how a
130 * certain data type string can be decoded. The type string of a data schema
131 * indicates the type of the schema itself (e.g. "protobuf" for protobuf
132 * schemas, "struct" for struct schemas, etc). In the data log, schemas are
133 * saved just like normal records, with the name being generated from the
134 * provided name: "/.schema/<name>". Duplicate calls to this function with
135 * the same name are silently ignored.
136 *
137 * @param name Name (the string passed as the data type for records using this
138 * schema)
139 * @param type Type of schema (e.g. "protobuf", "struct", etc)
140 * @param schema Schema data
141 * @param timestamp Time stamp (may be 0 to indicate now)
142 */
143 void AddSchema(std::string_view name, std::string_view type,
144 std::string_view schema, int64_t timestamp = 0) {
145 AddSchema(
146 name, type,
147 std::span<const uint8_t>{
148 reinterpret_cast<const uint8_t*>(schema.data()), schema.size()},
149 timestamp);
150 }
151
152 /**
153 * Registers a protobuf schema. Duplicate calls to this function with the same
154 * name are silently ignored.
155 *
156 * @tparam T protobuf serializable type
157 * @param msg protobuf message
158 * @param timestamp Time stamp (0 to indicate now)
159 */
160 template <wpi::util::ProtobufSerializable T>
162 int64_t timestamp = 0) {
163 if (timestamp == 0) {
164 timestamp = wpi::util::Now();
165 }
167 [this](auto typeString) { return HasSchema(typeString); },
168 [this, timestamp](auto typeString, auto schema) {
169 AddSchema(typeString, "proto:FileDescriptorProto", schema, timestamp);
170 });
171 }
172
173 /**
174 * Registers a struct schema. Duplicate calls to this function with the same
175 * name are silently ignored.
176 *
177 * @tparam T struct serializable type
178 * @param info optional struct type info
179 * @param timestamp Time stamp (0 to indicate now)
180 */
181 template <typename T, typename... I>
182 requires wpi::util::StructSerializable<T, I...>
183 void AddStructSchema(const I&... info, int64_t timestamp = 0) {
184 if (timestamp == 0) {
185 timestamp = wpi::util::Now();
186 }
188 [this, timestamp](auto typeString, auto schema) {
189 this->AddSchema(typeString, "structschema", schema, timestamp);
190 },
191 info...);
192 }
193
194 /**
195 * Start an entry. Duplicate names are allowed (with the same type), and
196 * result in the same index being returned (Start/Finish are reference
197 * counted). A duplicate name with a different type will result in an error
198 * message being printed to the console and 0 being returned (which will be
199 * ignored by the Append functions).
200 *
201 * @param name Name
202 * @param type Data type
203 * @param metadata Initial metadata (e.g. data properties)
204 * @param timestamp Time stamp (may be 0 to indicate now)
205 *
206 * @return Entry index
207 */
208 int Start(std::string_view name, std::string_view type,
209 std::string_view metadata = {}, int64_t timestamp = 0);
210
211 /**
212 * Finish an entry.
213 *
214 * @param entry Entry index
215 * @param timestamp Time stamp (may be 0 to indicate now)
216 */
217 void Finish(int entry, int64_t timestamp = 0);
218
219 /**
220 * Updates the metadata for an entry.
221 *
222 * @param entry Entry index
223 * @param metadata New metadata for the entry
224 * @param timestamp Time stamp (may be 0 to indicate now)
225 */
226 void SetMetadata(int entry, std::string_view metadata, int64_t timestamp = 0);
227
228 /**
229 * Appends a raw record to the log.
230 *
231 * @param entry Entry index, as returned by Start()
232 * @param data Byte array to record
233 * @param timestamp Time stamp (may be 0 to indicate now)
234 */
235 void AppendRaw(int entry, std::span<const uint8_t> data, int64_t timestamp);
236
237 /**
238 * Appends a raw record to the log.
239 *
240 * @param entry Entry index, as returned by Start()
241 * @param data Byte array to record
242 * @param timestamp Time stamp (may be 0 to indicate now)
243 */
244 void AppendRaw2(int entry, std::span<const std::span<const uint8_t>> data,
245 int64_t timestamp);
246
247 /**
248 * Appends a boolean record to the log.
249 *
250 * @param entry Entry index, as returned by Start()
251 * @param value Boolean value to record
252 * @param timestamp Time stamp (may be 0 to indicate now)
253 */
254 void AppendBoolean(int entry, bool value, int64_t timestamp);
255
256 /**
257 * Appends an integer record to the log.
258 *
259 * @param entry Entry index, as returned by Start()
260 * @param value Integer value to record
261 * @param timestamp Time stamp (may be 0 to indicate now)
262 */
263 void AppendInteger(int entry, int64_t value, int64_t timestamp);
264
265 /**
266 * Appends a float record to the log.
267 *
268 * @param entry Entry index, as returned by Start()
269 * @param value Float value to record
270 * @param timestamp Time stamp (may be 0 to indicate now)
271 */
272 void AppendFloat(int entry, float value, int64_t timestamp);
273
274 /**
275 * Appends a double record to the log.
276 *
277 * @param entry Entry index, as returned by Start()
278 * @param value Double value to record
279 * @param timestamp Time stamp (may be 0 to indicate now)
280 */
281 void AppendDouble(int entry, double value, int64_t timestamp);
282
283 /**
284 * Appends a string record to the log.
285 *
286 * @param entry Entry index, as returned by Start()
287 * @param value String value to record
288 * @param timestamp Time stamp (may be 0 to indicate now)
289 */
290 void AppendString(int entry, std::string_view value, int64_t timestamp);
291
292 /**
293 * Appends a boolean array record to the log.
294 *
295 * @param entry Entry index, as returned by Start()
296 * @param arr Boolean array to record
297 * @param timestamp Time stamp (may be 0 to indicate now)
298 */
299 void AppendBooleanArray(int entry, std::span<const bool> arr,
300 int64_t timestamp);
301
302 /**
303 * Appends a boolean array record to the log.
304 *
305 * @param entry Entry index, as returned by Start()
306 * @param arr Boolean array to record
307 * @param timestamp Time stamp (may be 0 to indicate now)
308 */
309 void AppendBooleanArray(int entry, std::span<const int> arr,
310 int64_t timestamp);
311
312 /**
313 * Appends a boolean array record to the log.
314 *
315 * @param entry Entry index, as returned by Start()
316 * @param arr Boolean array to record
317 * @param timestamp Time stamp (may be 0 to indicate now)
318 */
319 void AppendBooleanArray(int entry, std::span<const uint8_t> arr,
320 int64_t timestamp);
321
322 /**
323 * Appends an integer array record to the log.
324 *
325 * @param entry Entry index, as returned by Start()
326 * @param arr Integer array to record
327 * @param timestamp Time stamp (may be 0 to indicate now)
328 */
329 void AppendIntegerArray(int entry, std::span<const int64_t> arr,
330 int64_t timestamp);
331
332 /**
333 * Appends a float array record to the log.
334 *
335 * @param entry Entry index, as returned by Start()
336 * @param arr Float array to record
337 * @param timestamp Time stamp (may be 0 to indicate now)
338 */
339 void AppendFloatArray(int entry, std::span<const float> arr,
340 int64_t timestamp);
341
342 /**
343 * Appends a double array record to the log.
344 *
345 * @param entry Entry index, as returned by Start()
346 * @param arr Double array to record
347 * @param timestamp Time stamp (may be 0 to indicate now)
348 */
349 void AppendDoubleArray(int entry, std::span<const double> arr,
350 int64_t timestamp);
351
352 /**
353 * Appends a string array record to the log.
354 *
355 * @param entry Entry index, as returned by Start()
356 * @param arr String array to record
357 * @param timestamp Time stamp (may be 0 to indicate now)
358 */
359 void AppendStringArray(int entry, std::span<const std::string> arr,
360 int64_t timestamp);
361
362 /**
363 * Appends a string array record to the log.
364 *
365 * @param entry Entry index, as returned by Start()
366 * @param arr String array to record
367 * @param timestamp Time stamp (may be 0 to indicate now)
368 */
369 void AppendStringArray(int entry, std::span<const std::string_view> arr,
370 int64_t timestamp);
371
372 /**
373 * Appends a string array record to the log.
374 *
375 * @param entry Entry index, as returned by Start()
376 * @param arr String array to record
377 * @param timestamp Time stamp (may be 0 to indicate now)
378 */
379 void AppendStringArray(int entry, std::span<const struct WPI_String> arr,
380 int64_t timestamp);
381
382 protected:
383 static constexpr size_t kBlockSize = 16 * 1024;
385
386 class Buffer {
387 public:
388 explicit Buffer(size_t alloc = kBlockSize)
389 : m_buf{new uint8_t[alloc]}, m_maxLen{alloc} {}
390 ~Buffer() { delete[] m_buf; }
391
392 Buffer(const Buffer&) = delete;
393 Buffer& operator=(const Buffer&) = delete;
394
396 : m_buf{oth.m_buf}, m_len{oth.m_len}, m_maxLen{oth.m_maxLen} {
397 oth.m_buf = nullptr;
398 oth.m_len = 0;
399 oth.m_maxLen = 0;
400 }
401
403 if (m_buf) {
404 delete[] m_buf;
405 }
406 m_buf = oth.m_buf;
407 m_len = oth.m_len;
408 m_maxLen = oth.m_maxLen;
409 oth.m_buf = nullptr;
410 oth.m_len = 0;
411 oth.m_maxLen = 0;
412 return *this;
413 }
414
415 uint8_t* Reserve(size_t size) {
416 assert(size <= GetRemaining());
417 uint8_t* rv = m_buf + m_len;
418 m_len += size;
419 return rv;
420 }
421
422 void Unreserve(size_t size) { m_len -= size; }
423
424 void Clear() { m_len = 0; }
425
426 size_t GetRemaining() const { return m_maxLen - m_len; }
427
428 std::span<uint8_t> GetData() { return {m_buf, m_len}; }
429 std::span<const uint8_t> GetData() const { return {m_buf, m_len}; }
430
431 private:
432 uint8_t* m_buf;
433 size_t m_len = 0;
434 size_t m_maxLen;
435 };
436
437 /**
438 * Constructs the log. StartFile() must be called to actually start the
439 * file output.
440 *
441 * @param msglog message logger (will be called from separate thread)
442 * @param extraHeader extra header metadata
443 */
444 explicit DataLog(wpi::util::Logger& msglog, std::string_view extraHeader = "")
445 : m_msglog{msglog}, m_extraHeader{extraHeader} {}
446
447 /**
448 * Starts the log. Appends file header and Start records and schema data
449 * values for all previously started entries and schemas. No effect unless
450 * the data log is currently stopped.
451 */
452 void StartFile();
453
454 /**
455 * Provides complete set of all buffers that need to be written.
456 *
457 * Any existing contents of writeBufs will be released as if ReleaseBufs()
458 * was called prior to this call.
459 *
460 * Returned buffers should provided back via ReleaseBufs() after the write is
461 * complete.
462 *
463 * @param writeBufs buffers to be written (output)
464 */
465 void FlushBufs(std::vector<Buffer>* writeBufs);
466
467 /**
468 * Releases memory for a set of buffers back to the internal buffer pool.
469 *
470 * @param bufs buffers; empty on return
471 */
472 void ReleaseBufs(std::vector<Buffer>* bufs);
473
474 /**
475 * Called when internal buffers are half the maximum count. Called with
476 * internal mutex held; do not call any other DataLog functions from this
477 * function.
478 */
479 virtual void BufferHalfFull();
480
481 /**
482 * Called when internal buffers reach the maximum count. Called with internal
483 * mutex held; do not call any other DataLog functions from this function.
484 *
485 * @return true if log should be paused (don't call PauseLog)
486 */
487 virtual bool BufferFull() = 0;
488
489 private:
490 static constexpr size_t kMaxBufferCount = 1024 * 1024 / kBlockSize;
491 static constexpr size_t kMaxFreeCount = 256 * 1024 / kBlockSize;
492
493 // must be called with m_mutex held
494 int StartImpl(std::string_view name, std::string_view type,
495 std::string_view metadata, int64_t timestamp);
496 uint8_t* StartRecord(uint32_t entry, uint64_t timestamp, uint32_t payloadSize,
497 size_t reserveSize);
498 uint8_t* Reserve(size_t size);
499 void AppendImpl(std::span<const uint8_t> data);
500 void AppendStringImpl(std::string_view str);
501 void AppendStartRecord(int id, std::string_view name, std::string_view type,
502 std::string_view metadata, int64_t timestamp);
503 void DoReleaseBufs(std::vector<Buffer>* bufs);
504
505 protected:
507
508 private:
509 mutable wpi::util::mutex m_mutex;
510 bool m_active = false;
511 bool m_paused = false;
512 std::string m_extraHeader;
513 std::vector<Buffer> m_free;
514 std::vector<Buffer> m_outgoing;
515 struct EntryInfo {
516 std::string type;
517 int id{0};
518 };
520 struct SchemaInfo {
521 std::vector<uint8_t> data;
522 int id{0};
523 };
525 struct EntryInfo2 {
526 std::string metadata;
527 unsigned int count;
528 };
529 wpi::util::DenseMap<int, EntryInfo2> m_entryIds;
530 int m_lastId = 0;
531};
532
533/**
534 * Log entry base class.
535 */
537 protected:
538 DataLogEntry() = default;
539 DataLogEntry(DataLog& log, std::string_view name, std::string_view type,
540 std::string_view metadata = {}, int64_t timestamp = 0)
541 : m_log{&log}, m_entry{log.Start(name, type, metadata, timestamp)} {}
542
543 public:
544 DataLogEntry(const DataLogEntry&) = delete;
546
548 rhs.m_log = nullptr;
549 }
551 if (m_log) {
552 m_log->Finish(m_entry);
553 }
554 m_log = rhs.m_log;
555 rhs.m_log = nullptr;
556 m_entry = rhs.m_entry;
557 return *this;
558 }
559
560 explicit operator bool() const { return m_log != nullptr; }
561
562 /**
563 * Updates the metadata for the entry.
564 *
565 * @param metadata New metadata for the entry
566 * @param timestamp Time stamp (may be 0 to indicate now)
567 */
568 void SetMetadata(std::string_view metadata, int64_t timestamp = 0) {
569 m_log->SetMetadata(m_entry, metadata, timestamp);
570 }
571
572 /**
573 * Finishes the entry.
574 *
575 * @param timestamp Time stamp (may be 0 to indicate now)
576 */
577 void Finish(int64_t timestamp = 0) { m_log->Finish(m_entry, timestamp); }
578
579 protected:
580 DataLog* m_log = nullptr;
581 int m_entry = 0;
582};
583
584template <typename T>
586 protected:
589 std::string_view type, std::string_view metadata = {},
590 int64_t timestamp = 0)
591 : DataLogEntry{log, name, type, metadata, timestamp} {}
592
593 public:
595 : DataLogEntry{std::move(rhs)} {
596 std::scoped_lock lock{rhs.m_mutex};
597 m_lastValue = std::move(rhs.m_lastValue);
598 }
600 DataLogEntry::operator=(std::move(rhs));
601 std::scoped_lock lock{m_mutex, rhs.m_mutex};
602 m_lastValue = std::move(rhs.m_lastValue);
603 return *this;
604 }
605
606 /**
607 * Gets whether there is a last value.
608 *
609 * @note The last value is local to this class instance and updated only with
610 * Update(), not Append().
611 *
612 * @return True if last value exists, false otherwise.
613 */
614 bool HasLastValue() const { return m_lastValue.has_value(); }
615
616 /**
617 * Gets the last value.
618 *
619 * @note The last value is local to this class instance and updated only with
620 * Update(), not Append().
621 *
622 * @return Last value (empty if no last value)
623 */
624 std::optional<T> GetLastValue() const {
625 std::scoped_lock lock{m_mutex};
626 return m_lastValue;
627 }
628
629 protected:
631 std::optional<T> m_lastValue;
632};
633
634/**
635 * Log arbitrary byte data.
636 */
637class RawLogEntry : public DataLogValueEntryImpl<std::vector<uint8_t>> {
638 public:
639 static constexpr std::string_view kDataType = "raw";
640
641 RawLogEntry() = default;
642 RawLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
643 : RawLogEntry{log, name, {}, kDataType, timestamp} {}
644 RawLogEntry(DataLog& log, std::string_view name, std::string_view metadata,
645 int64_t timestamp = 0)
646 : RawLogEntry{log, name, metadata, kDataType, timestamp} {}
647 RawLogEntry(DataLog& log, std::string_view name, std::string_view metadata,
648 std::string_view type, int64_t timestamp = 0)
649 : DataLogValueEntryImpl{log, name, type, metadata, timestamp} {}
650
651 /**
652 * Appends a record to the log.
653 *
654 * @param data Data to record
655 * @param timestamp Time stamp (may be 0 to indicate now)
656 */
657 void Append(std::span<const uint8_t> data, int64_t timestamp = 0) {
658 m_log->AppendRaw(m_entry, data, timestamp);
659 }
660
661 /**
662 * Updates the last value and appends a record to the log if it has changed.
663 *
664 * @note The last value is local to this class instance; using Update() with
665 * two instances pointing to the same underlying log entry name will likely
666 * result in unexpected results.
667 *
668 * @param data Data to record
669 * @param timestamp Time stamp (may be 0 to indicate now)
670 */
671 void Update(std::span<const uint8_t> data, int64_t timestamp = 0);
672};
673
674/**
675 * Log boolean values.
676 */
678 public:
679 static constexpr std::string_view kDataType = "boolean";
680
681 BooleanLogEntry() = default;
682 BooleanLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
683 : BooleanLogEntry{log, name, {}, timestamp} {}
684 BooleanLogEntry(DataLog& log, std::string_view name,
685 std::string_view metadata, int64_t timestamp = 0)
686 : DataLogValueEntryImpl{log, name, kDataType, metadata, timestamp} {}
687
688 /**
689 * Appends a record to the log.
690 *
691 * @param value Value to record
692 * @param timestamp Time stamp (may be 0 to indicate now)
693 */
694 void Append(bool value, int64_t timestamp = 0) {
695 m_log->AppendBoolean(m_entry, value, timestamp);
696 }
697
698 /**
699 * Updates the last value and appends a record to the log if it has changed.
700 *
701 * @note The last value is local to this class instance; using Update() with
702 * two instances pointing to the same underlying log entry name will likely
703 * result in unexpected results.
704 *
705 * @param value Value to record
706 * @param timestamp Time stamp (may be 0 to indicate now)
707 */
708 void Update(bool value, int64_t timestamp = 0) {
709 std::scoped_lock lock{m_mutex};
710 if (m_lastValue != value) {
711 m_lastValue = value;
712 Append(value, timestamp);
713 }
714 }
715};
716
717/**
718 * Log integer values.
719 */
720class IntegerLogEntry : public DataLogValueEntryImpl<int64_t> {
721 public:
722 static constexpr std::string_view kDataType = "int64";
723
724 IntegerLogEntry() = default;
725 IntegerLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
726 : IntegerLogEntry{log, name, {}, timestamp} {}
727 IntegerLogEntry(DataLog& log, std::string_view name,
728 std::string_view metadata, int64_t timestamp = 0)
729 : DataLogValueEntryImpl{log, name, kDataType, metadata, timestamp} {}
730
731 /**
732 * Appends a record to the log.
733 *
734 * @param value Value to record
735 * @param timestamp Time stamp (may be 0 to indicate now)
736 */
737 void Append(int64_t value, int64_t timestamp = 0) {
738 m_log->AppendInteger(m_entry, value, timestamp);
739 }
740
741 /**
742 * Updates the last value and appends a record to the log if it has changed.
743 *
744 * @note The last value is local to this class instance; using Update() with
745 * two instances pointing to the same underlying log entry name will likely
746 * result in unexpected results.
747 *
748 * @param value Value to record
749 * @param timestamp Time stamp (may be 0 to indicate now)
750 */
751 void Update(int64_t value, int64_t timestamp = 0) {
752 std::scoped_lock lock{m_mutex};
753 if (m_lastValue != value) {
754 m_lastValue = value;
755 Append(value, timestamp);
756 }
757 }
758};
759
760/**
761 * Log float values.
762 */
764 public:
765 static constexpr std::string_view kDataType = "float";
766
767 FloatLogEntry() = default;
768 FloatLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
769 : FloatLogEntry{log, name, {}, timestamp} {}
770 FloatLogEntry(DataLog& log, std::string_view name, std::string_view metadata,
771 int64_t timestamp = 0)
772 : DataLogValueEntryImpl{log, name, kDataType, metadata, timestamp} {}
773
774 /**
775 * Appends a record to the log.
776 *
777 * @param value Value to record
778 * @param timestamp Time stamp (may be 0 to indicate now)
779 */
780 void Append(float value, int64_t timestamp = 0) {
781 m_log->AppendFloat(m_entry, value, timestamp);
782 }
783
784 /**
785 * Updates the last value and appends a record to the log if it has changed.
786 *
787 * @note The last value is local to this class instance; using Update() with
788 * two instances pointing to the same underlying log entry name will likely
789 * result in unexpected results.
790 *
791 * @param value Value to record
792 * @param timestamp Time stamp (may be 0 to indicate now)
793 */
794 void Update(float value, int64_t timestamp = 0) {
795 std::scoped_lock lock{m_mutex};
796 if (m_lastValue != value) {
797 m_lastValue = value;
798 Append(value, timestamp);
799 }
800 }
801};
802
803/**
804 * Log double values.
805 */
806class DoubleLogEntry : public DataLogValueEntryImpl<double> {
807 public:
808 static constexpr std::string_view kDataType = "double";
809
810 DoubleLogEntry() = default;
811 DoubleLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
812 : DoubleLogEntry{log, name, {}, timestamp} {}
813 DoubleLogEntry(DataLog& log, std::string_view name, std::string_view metadata,
814 int64_t timestamp = 0)
815 : DataLogValueEntryImpl{log, name, kDataType, metadata, timestamp} {}
816
817 /**
818 * Appends a record to the log.
819 *
820 * @param value Value to record
821 * @param timestamp Time stamp (may be 0 to indicate now)
822 */
823 void Append(double value, int64_t timestamp = 0) {
824 m_log->AppendDouble(m_entry, value, timestamp);
825 }
826
827 /**
828 * Updates the last value and appends a record to the log if it has changed.
829 *
830 * @note The last value is local to this class instance; using Update() with
831 * two instances pointing to the same underlying log entry name will likely
832 * result in unexpected results.
833 *
834 * @param value Value to record
835 * @param timestamp Time stamp (may be 0 to indicate now)
836 */
837 void Update(double value, int64_t timestamp = 0) {
838 std::scoped_lock lock{m_mutex};
839 if (m_lastValue != value) {
840 m_lastValue = value;
841 Append(value, timestamp);
842 }
843 }
844};
845
846/**
847 * Log string values.
848 */
849class StringLogEntry : public DataLogValueEntryImpl<std::string> {
850 public:
851 static constexpr const char* kDataType = "string";
852
853 StringLogEntry() = default;
854 StringLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
855 : StringLogEntry{log, name, {}, kDataType, timestamp} {}
856 StringLogEntry(DataLog& log, std::string_view name, std::string_view metadata,
857 int64_t timestamp = 0)
858 : StringLogEntry{log, name, metadata, kDataType, timestamp} {}
859 StringLogEntry(DataLog& log, std::string_view name, std::string_view metadata,
860 std::string_view type, int64_t timestamp = 0)
861 : DataLogValueEntryImpl{log, name, type, metadata, timestamp} {}
862
863 /**
864 * Appends a record to the log.
865 *
866 * @param value Value to record
867 * @param timestamp Time stamp (may be 0 to indicate now)
868 */
869 void Append(std::string_view value, int64_t timestamp = 0) {
870 m_log->AppendString(m_entry, value, timestamp);
871 }
872
873 /**
874 * Updates the last value and appends a record to the log if it has changed.
875 *
876 * @note The last value is local to this class instance; using Update() with
877 * two instances pointing to the same underlying log entry name will likely
878 * result in unexpected results.
879 *
880 * @param value Value to record
881 * @param timestamp Time stamp (may be 0 to indicate now)
882 */
883 void Update(std::string_view value, int64_t timestamp = 0) {
884 std::scoped_lock lock{m_mutex};
885 if (m_lastValue != value) {
886 m_lastValue = value;
887 Append(value, timestamp);
888 }
889 }
890};
891
892/**
893 * Log array of boolean values.
894 */
895class BooleanArrayLogEntry : public DataLogValueEntryImpl<std::vector<int>> {
896 public:
897 static constexpr const char* kDataType = "boolean[]";
898
901 int64_t timestamp = 0)
902 : BooleanArrayLogEntry{log, name, {}, timestamp} {}
904 std::string_view metadata, int64_t timestamp = 0)
905 : DataLogValueEntryImpl{log, name, kDataType, metadata, timestamp} {}
906
907 /**
908 * Appends a record to the log. For find functions to work, timestamp
909 * must be monotonically increasing.
910 *
911 * @param arr Values to record
912 * @param timestamp Time stamp (may be 0 to indicate now)
913 */
914 void Append(std::span<const bool> arr, int64_t timestamp = 0) {
915 m_log->AppendBooleanArray(m_entry, arr, timestamp);
916 }
917
918 /**
919 * Appends a record to the log.
920 *
921 * @param arr Values to record
922 * @param timestamp Time stamp (may be 0 to indicate now)
923 */
924 void Append(std::initializer_list<bool> arr, int64_t timestamp = 0) {
925 Append(std::span{arr.begin(), arr.end()}, timestamp);
926 }
927
928 /**
929 * Appends a record to the log.
930 *
931 * @param arr Values to record
932 * @param timestamp Time stamp (may be 0 to indicate now)
933 */
934 void Append(std::span<const int> arr, int64_t timestamp = 0) {
935 m_log->AppendBooleanArray(m_entry, arr, timestamp);
936 }
937
938 /**
939 * Appends a record to the log.
940 *
941 * @param arr Values to record
942 * @param timestamp Time stamp (may be 0 to indicate now)
943 */
944 void Append(std::initializer_list<int> arr, int64_t timestamp = 0) {
945 Append(std::span{arr.begin(), arr.end()}, timestamp);
946 }
947
948 /**
949 * Appends a record to the log.
950 *
951 * @param arr Values to record
952 * @param timestamp Time stamp (may be 0 to indicate now)
953 */
954 void Append(std::span<const uint8_t> arr, int64_t timestamp = 0) {
955 m_log->AppendBooleanArray(m_entry, arr, timestamp);
956 }
957
958 /**
959 * Updates the last value and appends a record to the log if it has changed.
960 *
961 * @note The last value is local to this class instance; using Update() with
962 * two instances pointing to the same underlying log entry name will likely
963 * result in unexpected results.
964 *
965 * @param arr Values to record
966 * @param timestamp Time stamp (may be 0 to indicate now)
967 */
968 void Update(std::span<const bool> arr, int64_t timestamp = 0);
969
970 /**
971 * Updates the last value and appends a record to the log if it has changed.
972 *
973 * @note The last value is local to this class instance; using Update() with
974 * two instances pointing to the same underlying log entry name will likely
975 * result in unexpected results.
976 *
977 * @param arr Values to record
978 * @param timestamp Time stamp (may be 0 to indicate now)
979 */
980 void Update(std::initializer_list<bool> arr, int64_t timestamp = 0) {
981 Update(std::span{arr.begin(), arr.end()}, timestamp);
982 }
983
984 /**
985 * Updates the last value and appends a record to the log if it has changed.
986 *
987 * @note The last value is local to this class instance; using Update() with
988 * two instances pointing to the same underlying log entry name will likely
989 * result in unexpected results.
990 *
991 * @param arr Values to record
992 * @param timestamp Time stamp (may be 0 to indicate now)
993 */
994 void Update(std::span<const int> arr, int64_t timestamp = 0);
995
996 /**
997 * Updates the last value and appends a record to the log if it has changed.
998 *
999 * @note The last value is local to this class instance; using Update() with
1000 * two instances pointing to the same underlying log entry name will likely
1001 * result in unexpected results.
1002 *
1003 * @param arr Values to record
1004 * @param timestamp Time stamp (may be 0 to indicate now)
1005 */
1006 void Update(std::initializer_list<int> arr, int64_t timestamp = 0) {
1007 Update(std::span{arr.begin(), arr.end()}, timestamp);
1008 }
1009
1010 /**
1011 * Updates the last value and appends a record to the log if it has changed.
1012 *
1013 * @note The last value is local to this class instance; using Update() with
1014 * two instances pointing to the same underlying log entry name will likely
1015 * result in unexpected results.
1016 *
1017 * @param arr Values to record
1018 * @param timestamp Time stamp (may be 0 to indicate now)
1019 */
1020 void Update(std::span<const uint8_t> arr, int64_t timestamp = 0);
1021};
1022
1023/**
1024 * Log array of integer values.
1025 */
1027 : public DataLogValueEntryImpl<std::vector<int64_t>> {
1028 public:
1029 static constexpr const char* kDataType = "int64[]";
1030
1033 int64_t timestamp = 0)
1034 : IntegerArrayLogEntry{log, name, {}, timestamp} {}
1036 std::string_view metadata, int64_t timestamp = 0)
1037 : DataLogValueEntryImpl{log, name, kDataType, metadata, timestamp} {}
1038
1039 /**
1040 * Appends a record to the log.
1041 *
1042 * @param arr Values to record
1043 * @param timestamp Time stamp (may be 0 to indicate now)
1044 */
1045 void Append(std::span<const int64_t> arr, int64_t timestamp = 0) {
1046 m_log->AppendIntegerArray(m_entry, arr, timestamp);
1047 }
1048
1049 /**
1050 * Appends a record to the log.
1051 *
1052 * @param arr Values to record
1053 * @param timestamp Time stamp (may be 0 to indicate now)
1054 */
1055 void Append(std::initializer_list<int64_t> arr, int64_t timestamp = 0) {
1056 Append({arr.begin(), arr.end()}, timestamp);
1057 }
1058
1059 /**
1060 * Updates the last value and appends a record to the log if it has changed.
1061 *
1062 * @note The last value is local to this class instance; using Update() with
1063 * two instances pointing to the same underlying log entry name will likely
1064 * result in unexpected results.
1065 *
1066 * @param arr Values to record
1067 * @param timestamp Time stamp (may be 0 to indicate now)
1068 */
1069 void Update(std::span<const int64_t> arr, int64_t timestamp = 0);
1070
1071 /**
1072 * Updates the last value and appends a record to the log if it has changed.
1073 *
1074 * @note The last value is local to this class instance; using Update() with
1075 * two instances pointing to the same underlying log entry name will likely
1076 * result in unexpected results.
1077 *
1078 * @param arr Values to record
1079 * @param timestamp Time stamp (may be 0 to indicate now)
1080 */
1081 void Update(std::initializer_list<int64_t> arr, int64_t timestamp = 0) {
1082 Update({arr.begin(), arr.end()}, timestamp);
1083 }
1084};
1085
1086/**
1087 * Log array of float values.
1088 */
1089class FloatArrayLogEntry : public DataLogValueEntryImpl<std::vector<float>> {
1090 public:
1091 static constexpr const char* kDataType = "float[]";
1092
1094 FloatArrayLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
1095 : FloatArrayLogEntry{log, name, {}, timestamp} {}
1097 std::string_view metadata, int64_t timestamp = 0)
1098 : DataLogValueEntryImpl{log, name, kDataType, metadata, timestamp} {}
1099
1100 /**
1101 * Appends a record to the log.
1102 *
1103 * @param arr Values to record
1104 * @param timestamp Time stamp (may be 0 to indicate now)
1105 */
1106 void Append(std::span<const float> arr, int64_t timestamp = 0) {
1107 m_log->AppendFloatArray(m_entry, arr, timestamp);
1108 }
1109
1110 /**
1111 * Appends a record to the log.
1112 *
1113 * @param arr Values to record
1114 * @param timestamp Time stamp (may be 0 to indicate now)
1115 */
1116 void Append(std::initializer_list<float> arr, int64_t timestamp = 0) {
1117 Append({arr.begin(), arr.end()}, timestamp);
1118 }
1119
1120 /**
1121 * Updates the last value and appends a record to the log if it has changed.
1122 *
1123 * @note The last value is local to this class instance; using Update() with
1124 * two instances pointing to the same underlying log entry name will likely
1125 * result in unexpected results.
1126 *
1127 * @param arr Values to record
1128 * @param timestamp Time stamp (may be 0 to indicate now)
1129 */
1130 void Update(std::span<const float> arr, int64_t timestamp = 0);
1131
1132 /**
1133 * Updates the last value and appends a record to the log if it has changed.
1134 *
1135 * @note The last value is local to this class instance; using Update() with
1136 * two instances pointing to the same underlying log entry name will likely
1137 * result in unexpected results.
1138 *
1139 * @param arr Values to record
1140 * @param timestamp Time stamp (may be 0 to indicate now)
1141 */
1142 void Update(std::initializer_list<float> arr, int64_t timestamp = 0) {
1143 Update({arr.begin(), arr.end()}, timestamp);
1144 }
1145};
1146
1147/**
1148 * Log array of double values.
1149 */
1150class DoubleArrayLogEntry : public DataLogValueEntryImpl<std::vector<double>> {
1151 public:
1152 static constexpr const char* kDataType = "double[]";
1153
1156 int64_t timestamp = 0)
1157 : DoubleArrayLogEntry{log, name, {}, timestamp} {}
1159 std::string_view metadata, int64_t timestamp = 0)
1160 : DataLogValueEntryImpl{log, name, kDataType, metadata, timestamp} {}
1161
1162 /**
1163 * Appends a record to the log.
1164 *
1165 * @param arr Values to record
1166 * @param timestamp Time stamp (may be 0 to indicate now)
1167 */
1168 void Append(std::span<const double> arr, int64_t timestamp = 0) {
1169 m_log->AppendDoubleArray(m_entry, arr, timestamp);
1170 }
1171
1172 /**
1173 * Appends a record to the log.
1174 *
1175 * @param arr Values to record
1176 * @param timestamp Time stamp (may be 0 to indicate now)
1177 */
1178 void Append(std::initializer_list<double> arr, int64_t timestamp = 0) {
1179 Append({arr.begin(), arr.end()}, timestamp);
1180 }
1181
1182 /**
1183 * Updates the last value and appends a record to the log if it has changed.
1184 *
1185 * @note The last value is local to this class instance; using Update() with
1186 * two instances pointing to the same underlying log entry name will likely
1187 * result in unexpected results.
1188 *
1189 * @param arr Values to record
1190 * @param timestamp Time stamp (may be 0 to indicate now)
1191 */
1192 void Update(std::span<const double> arr, int64_t timestamp = 0);
1193
1194 /**
1195 * Updates the last value and appends a record to the log if it has changed.
1196 *
1197 * @note The last value is local to this class instance; using Update() with
1198 * two instances pointing to the same underlying log entry name will likely
1199 * result in unexpected results.
1200 *
1201 * @param arr Values to record
1202 * @param timestamp Time stamp (may be 0 to indicate now)
1203 */
1204 void Update(std::initializer_list<double> arr, int64_t timestamp = 0) {
1205 Update({arr.begin(), arr.end()}, timestamp);
1206 }
1207};
1208
1209/**
1210 * Log array of string values.
1211 */
1213 : public DataLogValueEntryImpl<std::vector<std::string>> {
1214 public:
1215 static constexpr const char* kDataType = "string[]";
1216
1219 int64_t timestamp = 0)
1220 : StringArrayLogEntry{log, name, {}, timestamp} {}
1222 std::string_view metadata, int64_t timestamp = 0)
1223 : DataLogValueEntryImpl{log, name, kDataType, metadata, timestamp} {}
1224
1225 /**
1226 * Appends a record to the log.
1227 *
1228 * @param arr Values to record
1229 * @param timestamp Time stamp (may be 0 to indicate now)
1230 */
1231 void Append(std::span<const std::string> arr, int64_t timestamp = 0) {
1232 m_log->AppendStringArray(m_entry, arr, timestamp);
1233 }
1234
1235 /**
1236 * Appends a record to the log.
1237 *
1238 * @param arr Values to record
1239 * @param timestamp Time stamp (may be 0 to indicate now)
1240 */
1241 void Append(std::span<const std::string_view> arr, int64_t timestamp = 0) {
1242 m_log->AppendStringArray(m_entry, arr, timestamp);
1243 }
1244
1245 /**
1246 * Appends a record to the log.
1247 *
1248 * @param arr Values to record
1249 * @param timestamp Time stamp (may be 0 to indicate now)
1250 */
1251 void Append(std::initializer_list<std::string_view> arr,
1252 int64_t timestamp = 0) {
1253 Append(std::span<const std::string_view>{arr.begin(), arr.end()},
1254 timestamp);
1255 }
1256
1257 /**
1258 * Updates the last value and appends a record to the log if it has changed.
1259 *
1260 * @note The last value is local to this class instance; using Update() with
1261 * two instances pointing to the same underlying log entry name will likely
1262 * result in unexpected results.
1263 *
1264 * @param arr Values to record
1265 * @param timestamp Time stamp (may be 0 to indicate now)
1266 */
1267 void Update(std::span<const std::string> arr, int64_t timestamp = 0);
1268
1269 /**
1270 * Updates the last value and appends a record to the log if it has changed.
1271 *
1272 * @note The last value is local to this class instance; using Update() with
1273 * two instances pointing to the same underlying log entry name will likely
1274 * result in unexpected results.
1275 *
1276 * @param arr Values to record
1277 * @param timestamp Time stamp (may be 0 to indicate now)
1278 */
1279 void Update(std::span<const std::string_view> arr, int64_t timestamp = 0);
1280
1281 /**
1282 * Updates the last value and appends a record to the log if it has changed.
1283 *
1284 * @note The last value is local to this class instance; using Update() with
1285 * two instances pointing to the same underlying log entry name will likely
1286 * result in unexpected results.
1287 *
1288 * @param arr Values to record
1289 * @param timestamp Time stamp (may be 0 to indicate now)
1290 */
1291 void Update(std::initializer_list<std::string_view> arr,
1292 int64_t timestamp = 0) {
1293 Update(std::span<const std::string_view>{arr.begin(), arr.end()},
1294 timestamp);
1295 }
1296};
1297
1298/**
1299 * Log raw struct serializable objects.
1300 */
1301template <typename T, typename... I>
1302 requires wpi::util::StructSerializable<T, I...>
1304 using S = wpi::util::Struct<T, I...>;
1305
1306 public:
1307 StructLogEntry() = default;
1308 StructLogEntry(DataLog& log, std::string_view name, I... info,
1309 int64_t timestamp = 0)
1310 : StructLogEntry{log, name, {}, std::move(info)..., timestamp} {}
1311 StructLogEntry(DataLog& log, std::string_view name, std::string_view metadata,
1312 I... info, int64_t timestamp = 0)
1313 : m_info{std::move(info)...} {
1314 m_log = &log;
1315 log.AddStructSchema<T, I...>(info..., timestamp);
1317 metadata, timestamp);
1318 }
1319
1321 : DataLogEntry{std::move(rhs)}, m_info{std::move(rhs.m_info)} {
1322 std::scoped_lock lock{rhs.m_mutex};
1323 m_lastValue = std::move(rhs.m_lastValue);
1324 }
1326 DataLogEntry::operator=(std::move(rhs));
1327 m_info = std::move(rhs.m_info);
1328 std::scoped_lock lock{m_mutex, rhs.m_mutex};
1329 m_lastValue = std::move(rhs.m_lastValue);
1330 return *this;
1331 }
1332
1333 /**
1334 * Appends a record to the log.
1335 *
1336 * @param data Data to record
1337 * @param timestamp Time stamp (may be 0 to indicate now)
1338 */
1339 void Append(const T& data, int64_t timestamp = 0) {
1340 if constexpr (sizeof...(I) == 0) {
1341 if constexpr (wpi::util::is_constexpr([] { S::GetSize(); })) {
1342 uint8_t buf[S::GetSize()];
1343 S::Pack(buf, data);
1344 m_log->AppendRaw(m_entry, buf, timestamp);
1345 return;
1346 }
1347 }
1348 wpi::util::SmallVector<uint8_t, 128> buf;
1349 buf.resize_for_overwrite(std::apply(S::GetSize, m_info));
1350 std::apply([&](const I&... info) { S::Pack(buf, data, info...); }, m_info);
1351 m_log->AppendRaw(m_entry, buf, timestamp);
1352 }
1353
1354 /**
1355 * Updates the last value and appends a record to the log if it has changed.
1356 *
1357 * @note The last value is local to this class instance; using Update() with
1358 * two instances pointing to the same underlying log entry name will likely
1359 * result in unexpected results.
1360 *
1361 * @param data Data to record
1362 * @param timestamp Time stamp (may be 0 to indicate now)
1363 */
1364 void Update(const T& data, int64_t timestamp = 0) {
1365 if constexpr (sizeof...(I) == 0) {
1366 if constexpr (wpi::util::is_constexpr([] { S::GetSize(); })) {
1367 uint8_t buf[S::GetSize()];
1368 S::Pack(buf, data);
1369 std::scoped_lock lock{m_mutex};
1370 if (m_lastValue.empty() ||
1371 !std::equal(buf, buf + S::GetSize(), m_lastValue.begin(),
1372 m_lastValue.end())) {
1373 m_lastValue.assign(buf, buf + S::GetSize());
1374 m_log->AppendRaw(m_entry, buf, timestamp);
1375 }
1376 return;
1377 }
1378 }
1379 wpi::util::SmallVector<uint8_t, 128> buf;
1380 buf.resize_for_overwrite(std::apply(S::GetSize, m_info));
1381 std::apply([&](const I&... info) { S::Pack(buf, data, info...); }, m_info);
1382 std::scoped_lock lock{m_mutex};
1383 if (m_lastValue.empty() ||
1384 !std::equal(buf.begin(), buf.end(), m_lastValue.begin(),
1385 m_lastValue.end())) {
1386 m_lastValue.assign(buf.begin(), buf.end());
1387 m_log->AppendRaw(m_entry, buf, timestamp);
1388 }
1389 }
1390
1391 /**
1392 * Gets whether there is a last value.
1393 *
1394 * @note The last value is local to this class instance and updated only with
1395 * Update(), not Append().
1396 *
1397 * @return True if last value exists, false otherwise.
1398 */
1399 bool HasLastValue() const { return !m_lastValue.empty(); }
1400
1401 /**
1402 * Gets the last value.
1403 *
1404 * @note The last value is local to this class instance and updated only with
1405 * Update(), not Append().
1406 *
1407 * @return Last value (empty if no last value)
1408 */
1409 std::optional<T> GetLastValue() const {
1410 std::scoped_lock lock{m_mutex};
1411 if (m_lastValue.empty()) {
1412 return std::nullopt;
1413 }
1414 return std::apply(
1415 [&](const I&... info) { return S::Unpack(m_lastValue, info...); },
1416 m_info);
1417 }
1418
1419 private:
1420 mutable wpi::util::mutex m_mutex;
1421 std::vector<uint8_t> m_lastValue;
1422 [[no_unique_address]]
1423 std::tuple<I...> m_info;
1424};
1425
1426/**
1427 * Log raw struct serializable array of objects.
1428 */
1429template <typename T, typename... I>
1430 requires wpi::util::StructSerializable<T, I...>
1432 using S = wpi::util::Struct<T, I...>;
1433
1434 public:
1436 StructArrayLogEntry(DataLog& log, std::string_view name, I... info,
1437 int64_t timestamp = 0)
1438 : StructArrayLogEntry{log, name, {}, std::move(info)..., timestamp} {}
1440 std::string_view metadata, I... info,
1441 int64_t timestamp = 0)
1442 : m_info{std::move(info)...} {
1443 m_log = &log;
1444 log.AddStructSchema<T, I...>(info..., timestamp);
1445 m_entry = log.Start(
1446 name,
1448 metadata, timestamp);
1449 }
1450
1452 : DataLogEntry{std::move(rhs)},
1453 m_buf{std::move(rhs.m_buf)},
1454 m_info{std::move(rhs.m_info)} {
1455 std::scoped_lock lock{rhs.m_mutex};
1456 m_lastValue = std::move(rhs.m_lastValue);
1457 }
1459 DataLogEntry::operator=(std::move(rhs));
1460 m_buf = std::move(rhs.m_buf);
1461 m_info = std::move(rhs.m_info);
1462 std::scoped_lock lock{m_mutex, rhs.m_mutex};
1463 m_lastValue = std::move(rhs.m_lastValue);
1464 return *this;
1465 }
1466
1467 /**
1468 * Appends a record to the log.
1469 *
1470 * @param data Data to record
1471 * @param timestamp Time stamp (may be 0 to indicate now)
1472 */
1473 template <typename U>
1474#if __cpp_lib_ranges >= 201911L
1475 requires std::ranges::range<U> &&
1476 std::convertible_to<std::ranges::range_value_t<U>, T>
1477#endif
1478 void Append(U&& data, int64_t timestamp = 0) {
1479 std::apply(
1480 [&](const I&... info) {
1481 m_buf.Write(
1482 std::forward<U>(data),
1483 [&](auto bytes) { m_log->AppendRaw(m_entry, bytes, timestamp); },
1484 info...);
1485 },
1486 m_info);
1487 }
1488
1489 /**
1490 * Appends a record to the log.
1491 *
1492 * @param data Data to record
1493 * @param timestamp Time stamp (may be 0 to indicate now)
1494 */
1495 void Append(std::span<const T> data, int64_t timestamp = 0) {
1496 std::apply(
1497 [&](const I&... info) {
1498 m_buf.Write(
1499 data,
1500 [&](auto bytes) { m_log->AppendRaw(m_entry, bytes, timestamp); },
1501 info...);
1502 },
1503 m_info);
1504 }
1505
1506 /**
1507 * Updates the last value and appends a record to the log if it has changed.
1508 *
1509 * @note The last value is local to this class instance; using Update() with
1510 * two instances pointing to the same underlying log entry name will likely
1511 * result in unexpected results.
1512 *
1513 * @param data Data to record
1514 * @param timestamp Time stamp (may be 0 to indicate now)
1515 */
1516 void Update(std::span<const T> data, int64_t timestamp = 0) {
1517 std::apply(
1518 [&](const I&... info) {
1519 m_buf.Write(
1520 data,
1521 [&](auto bytes) {
1522 std::scoped_lock lock{m_mutex};
1523 if (!m_lastValue.has_value()) {
1524 m_lastValue = std::vector(bytes.begin(), bytes.end());
1525 m_log->AppendRaw(m_entry, bytes, timestamp);
1526 } else if (!std::equal(bytes.begin(), bytes.end(),
1527 m_lastValue->begin(),
1528 m_lastValue->end())) {
1529 m_lastValue->assign(bytes.begin(), bytes.end());
1530 m_log->AppendRaw(m_entry, bytes, timestamp);
1531 }
1532 },
1533 info...);
1534 },
1535 m_info);
1536 }
1537
1538 /**
1539 * Gets whether there is a last value.
1540 *
1541 * @note The last value is local to this class instance and updated only with
1542 * Update(), not Append().
1543 *
1544 * @return True if last value exists, false otherwise.
1545 */
1546 bool HasLastValue() const { return m_lastValue.has_value(); }
1547
1548 /**
1549 * Gets the last value.
1550 *
1551 * @note The last value is local to this class instance and updated only with
1552 * Update(), not Append().
1553 *
1554 * @return Last value (empty if no last value)
1555 */
1556 std::optional<std::vector<T>> GetLastValue() const {
1557 std::scoped_lock lock{m_mutex};
1558 if (!m_lastValue.has_value()) {
1559 return std::nullopt;
1560 }
1561 auto& lastValue = m_lastValue.value();
1562 size_t size = std::apply(S::GetSize, m_info);
1563 std::vector<T> rv;
1564 rv.reserve(lastValue.size() / size);
1565 for (auto in = lastValue.begin(), end = lastValue.end(); in < end;
1566 in += size) {
1567 std::apply(
1568 [&](const I&... info) {
1569 rv.emplace_back(S::Unpack(
1570 std::span<const uint8_t>{std::to_address(in), size}, info...));
1571 },
1572 m_info);
1573 }
1574 return rv;
1575 }
1576
1577 private:
1578 mutable wpi::util::mutex m_mutex;
1579 wpi::util::StructArrayBuffer<T, I...> m_buf;
1580 std::optional<std::vector<uint8_t>> m_lastValue;
1581 [[no_unique_address]]
1582 std::tuple<I...> m_info;
1583};
1584
1585/**
1586 * Log protobuf serializable objects.
1587 */
1588template <wpi::util::ProtobufSerializable T>
1590 using P = wpi::util::Protobuf<T>;
1591
1592 public:
1593 ProtobufLogEntry() = default;
1594 ProtobufLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
1595 : ProtobufLogEntry{log, name, {}, timestamp} {}
1596 ProtobufLogEntry(DataLog& log, std::string_view name,
1597 std::string_view metadata, int64_t timestamp = 0) {
1598 m_log = &log;
1599 log.AddProtobufSchema<T>(m_msg, timestamp);
1600 m_entry = log.Start(name, m_msg.GetTypeString(), metadata, timestamp);
1601 }
1602
1603 /**
1604 * Appends a record to the log.
1605 *
1606 * @param data Data to record
1607 * @param timestamp Time stamp (may be 0 to indicate now)
1608 */
1609 void Append(const T& data, int64_t timestamp = 0) {
1610 wpi::util::SmallVector<uint8_t, 128> buf;
1611 {
1612 std::scoped_lock lock{m_mutex};
1613 m_msg.Pack(buf, data);
1614 }
1615 m_log->AppendRaw(m_entry, buf, timestamp);
1616 }
1617
1618 /**
1619 * Updates the last value and appends a record to the log if it has changed.
1620 *
1621 * @note The last value is local to this class instance; using Update() with
1622 * two instances pointing to the same underlying log entry name will likely
1623 * result in unexpected results.
1624 *
1625 * @param data Data to record
1626 * @param timestamp Time stamp (may be 0 to indicate now)
1627 */
1628 void Update(const T& data, int64_t timestamp = 0) {
1629 std::scoped_lock lock{m_mutex};
1630 wpi::util::SmallVector<uint8_t, 128> buf;
1631 m_msg.Pack(buf, data);
1632 if (!m_lastValue.has_value()) {
1633 m_lastValue = std::vector(buf.begin(), buf.end());
1634 m_log->AppendRaw(m_entry, buf, timestamp);
1635 } else if (!std::equal(buf.begin(), buf.end(), m_lastValue->begin(),
1636 m_lastValue->end())) {
1637 m_lastValue->assign(buf.begin(), buf.end());
1638 m_log->AppendRaw(m_entry, buf, timestamp);
1639 }
1640 }
1641
1642 /**
1643 * Gets whether there is a last value.
1644 *
1645 * @note The last value is local to this class instance and updated only with
1646 * Update(), not Append().
1647 *
1648 * @return True if last value exists, false otherwise.
1649 */
1650 bool HasLastValue() const { return m_lastValue.has_value(); }
1651
1652 /**
1653 * Gets the last value.
1654 *
1655 * @note The last value is local to this class instance and updated only with
1656 * Update(), not Append().
1657 *
1658 * @return Last value (empty if no last value)
1659 */
1660 std::optional<T> GetLastValue() const {
1661 std::scoped_lock lock{m_mutex};
1662 if (!m_lastValue.has_value()) {
1663 return std::nullopt;
1664 }
1665 return m_msg.Unpack(m_lastValue);
1666 }
1667
1668 private:
1669 mutable wpi::util::mutex m_mutex;
1671 std::optional<std::vector<uint8_t>> m_lastValue;
1672};
1673
1674} // namespace wpi::log
This file defines the DenseMap class.
@ name
Definition base.h:690
void Append(std::span< const uint8_t > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:954
static constexpr const char * kDataType
Definition DataLog.hpp:897
void Append(std::initializer_list< bool > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:924
void Update(std::initializer_list< bool > arr, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
Definition DataLog.hpp:980
void Update(std::span< const bool > arr, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
BooleanArrayLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.hpp:900
void Append(std::initializer_list< int > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:944
void Append(std::span< const int > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:934
void Append(std::span< const bool > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:914
BooleanArrayLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.hpp:903
void Update(std::initializer_list< int > arr, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
Definition DataLog.hpp:1006
void Update(std::span< const uint8_t > arr, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
BooleanArrayLogEntry()=default
void Update(std::span< const int > arr, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
BooleanLogEntry()=default
BooleanLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.hpp:684
void Append(bool value, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:694
BooleanLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.hpp:682
void Update(bool value, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
Definition DataLog.hpp:708
static constexpr std::string_view kDataType
Definition DataLog.hpp:679
std::span< uint8_t > GetData()
Definition DataLog.hpp:428
Buffer(size_t alloc=kBlockSize)
Definition DataLog.hpp:388
Buffer(Buffer &&oth)
Definition DataLog.hpp:395
Buffer & operator=(Buffer &&oth)
Definition DataLog.hpp:402
~Buffer()
Definition DataLog.hpp:390
std::span< const uint8_t > GetData() const
Definition DataLog.hpp:429
void Clear()
Definition DataLog.hpp:424
Buffer(const Buffer &)=delete
size_t GetRemaining() const
Definition DataLog.hpp:426
void Unreserve(size_t size)
Definition DataLog.hpp:422
Buffer & operator=(const Buffer &)=delete
uint8_t * Reserve(size_t size)
Definition DataLog.hpp:415
Log entry base class.
Definition DataLog.hpp:536
DataLogEntry(DataLogEntry &&rhs)
Definition DataLog.hpp:547
DataLogEntry(const DataLogEntry &)=delete
void Finish(int64_t timestamp=0)
Finishes the entry.
Definition DataLog.hpp:577
DataLog * m_log
Definition DataLog.hpp:580
DataLogEntry(DataLog &log, std::string_view name, std::string_view type, std::string_view metadata={}, int64_t timestamp=0)
Definition DataLog.hpp:539
DataLogEntry()=default
DataLogEntry & operator=(const DataLogEntry &)=delete
int m_entry
Definition DataLog.hpp:581
DataLogEntry & operator=(DataLogEntry &&rhs)
Definition DataLog.hpp:550
void SetMetadata(std::string_view metadata, int64_t timestamp=0)
Updates the metadata for the entry.
Definition DataLog.hpp:568
A data log for high-speed writing of data values.
Definition DataLog.hpp:69
wpi::util::Logger & m_msglog
Definition DataLog.hpp:506
void AppendBooleanArray(int entry, std::span< const bool > arr, int64_t timestamp)
Appends a boolean array record to the log.
void AppendBooleanArray(int entry, std::span< const int > arr, int64_t timestamp)
Appends a boolean array record to the log.
void SetMetadata(int entry, std::string_view metadata, int64_t timestamp=0)
Updates the metadata for an entry.
DataLog(wpi::util::Logger &msglog, std::string_view extraHeader="")
Constructs the log.
Definition DataLog.hpp:444
void AppendStringArray(int entry, std::span< const std::string > arr, int64_t timestamp)
Appends a string array record to the log.
virtual void Stop()
Stops appending start/metadata/schema records to the log.
int Start(std::string_view name, std::string_view type, std::string_view metadata={}, int64_t timestamp=0)
Start an entry.
DataLog & operator=(const DataLog &)=delete
void AddProtobufSchema(wpi::util::ProtobufMessage< T > &msg, int64_t timestamp=0)
Registers a protobuf schema.
Definition DataLog.hpp:161
void AppendRaw(int entry, std::span< const uint8_t > data, int64_t timestamp)
Appends a raw record to the log.
void AppendIntegerArray(int entry, std::span< const int64_t > arr, int64_t timestamp)
Appends an integer array record to the log.
DataLog(const DataLog &)=delete
void AddStructSchema(const I &... info, int64_t timestamp=0)
Registers a struct schema.
Definition DataLog.hpp:183
void ReleaseBufs(std::vector< Buffer > *bufs)
Releases memory for a set of buffers back to the internal buffer pool.
void AppendStringArray(int entry, std::span< const struct WPI_String > arr, int64_t timestamp)
Appends a string array record to the log.
void AddSchema(std::string_view name, std::string_view type, std::string_view schema, int64_t timestamp=0)
Registers a data schema.
Definition DataLog.hpp:143
void AppendBooleanArray(int entry, std::span< const uint8_t > arr, int64_t timestamp)
Appends a boolean array record to the log.
void AppendDouble(int entry, double value, int64_t timestamp)
Appends a double record to the log.
void AppendFloat(int entry, float value, int64_t timestamp)
Appends a float record to the log.
DataLog & operator=(const DataLog &&)=delete
void AppendFloatArray(int entry, std::span< const float > arr, int64_t timestamp)
Appends a float array record to the log.
virtual void Resume()
Resumes appending of data records to the log.
void AppendString(int entry, std::string_view value, int64_t timestamp)
Appends a string record to the log.
void AddSchema(std::string_view name, std::string_view type, std::span< const uint8_t > schema, int64_t timestamp=0)
Registers a data schema.
static wpi::util::Logger s_defaultMessageLog
Definition DataLog.hpp:384
DataLog(DataLog &&)=delete
virtual ~DataLog()=default
static constexpr size_t kBlockSize
Definition DataLog.hpp:383
virtual void Pause()
Pauses appending of data records to the log.
void AppendStringArray(int entry, std::span< const std::string_view > arr, int64_t timestamp)
Appends a string array record to the log.
bool HasSchema(std::string_view name) const
Returns whether there is a data schema already registered with the given name.
virtual bool BufferFull()=0
Called when internal buffers reach the maximum count.
void StartFile()
Starts the log.
void AppendRaw2(int entry, std::span< const std::span< const uint8_t > > data, int64_t timestamp)
Appends a raw record to the log.
void Finish(int entry, int64_t timestamp=0)
Finish an entry.
void AppendDoubleArray(int entry, std::span< const double > arr, int64_t timestamp)
Appends a double array record to the log.
virtual void Flush()=0
Explicitly flushes the log data to disk.
void AppendInteger(int entry, int64_t value, int64_t timestamp)
Appends an integer record to the log.
void FlushBufs(std::vector< Buffer > *writeBufs)
Provides complete set of all buffers that need to be written.
virtual void BufferHalfFull()
Called when internal buffers are half the maximum count.
void AppendBoolean(int entry, bool value, int64_t timestamp)
Appends a boolean record to the log.
std::optional< T > m_lastValue
Definition DataLog.hpp:631
DataLogValueEntryImpl()=default
std::optional< T > GetLastValue() const
Gets the last value.
Definition DataLog.hpp:624
DataLogValueEntryImpl & operator=(DataLogValueEntryImpl &&rhs)
Definition DataLog.hpp:599
wpi::util::mutex m_mutex
Definition DataLog.hpp:630
DataLogValueEntryImpl(DataLog &log, std::string_view name, std::string_view type, std::string_view metadata={}, int64_t timestamp=0)
Definition DataLog.hpp:588
bool HasLastValue() const
Gets whether there is a last value.
Definition DataLog.hpp:614
DataLogValueEntryImpl(DataLogValueEntryImpl &&rhs)
Definition DataLog.hpp:594
DoubleArrayLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.hpp:1155
void Append(std::span< const double > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:1168
static constexpr const char * kDataType
Definition DataLog.hpp:1152
void Append(std::initializer_list< double > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:1178
void Update(std::span< const double > arr, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
DoubleArrayLogEntry()=default
void Update(std::initializer_list< double > arr, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
Definition DataLog.hpp:1204
DoubleArrayLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.hpp:1158
static constexpr std::string_view kDataType
Definition DataLog.hpp:808
DoubleLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.hpp:813
void Append(double value, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:823
DoubleLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.hpp:811
void Update(double value, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
Definition DataLog.hpp:837
DoubleLogEntry()=default
void Update(std::initializer_list< float > arr, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
Definition DataLog.hpp:1142
void Update(std::span< const float > arr, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
FloatArrayLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.hpp:1096
static constexpr const char * kDataType
Definition DataLog.hpp:1091
void Append(std::span< const float > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:1106
void Append(std::initializer_list< float > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:1116
FloatArrayLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.hpp:1094
FloatArrayLogEntry()=default
FloatLogEntry()=default
FloatLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.hpp:768
FloatLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.hpp:770
void Update(float value, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
Definition DataLog.hpp:794
static constexpr std::string_view kDataType
Definition DataLog.hpp:765
void Append(float value, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:780
IntegerArrayLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.hpp:1035
void Update(std::initializer_list< int64_t > arr, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
Definition DataLog.hpp:1081
void Append(std::span< const int64_t > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:1045
IntegerArrayLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.hpp:1032
void Append(std::initializer_list< int64_t > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:1055
void Update(std::span< const int64_t > arr, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
IntegerArrayLogEntry()=default
static constexpr const char * kDataType
Definition DataLog.hpp:1029
IntegerLogEntry()=default
static constexpr std::string_view kDataType
Definition DataLog.hpp:722
IntegerLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.hpp:727
void Update(int64_t value, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
Definition DataLog.hpp:751
IntegerLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.hpp:725
void Append(int64_t value, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:737
void Update(const T &data, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
Definition DataLog.hpp:1628
std::optional< T > GetLastValue() const
Gets the last value.
Definition DataLog.hpp:1660
ProtobufLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.hpp:1594
void Append(const T &data, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:1609
ProtobufLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.hpp:1596
bool HasLastValue() const
Gets whether there is a last value.
Definition DataLog.hpp:1650
ProtobufLogEntry()=default
RawLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.hpp:644
RawLogEntry(DataLog &log, std::string_view name, std::string_view metadata, std::string_view type, int64_t timestamp=0)
Definition DataLog.hpp:647
static constexpr std::string_view kDataType
Definition DataLog.hpp:639
RawLogEntry()=default
void Append(std::span< const uint8_t > data, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:657
RawLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.hpp:642
void Update(std::span< const uint8_t > data, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
void Update(std::span< const std::string_view > arr, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
void Update(std::span< const std::string > arr, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
void Append(std::initializer_list< std::string_view > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:1251
static constexpr const char * kDataType
Definition DataLog.hpp:1215
StringArrayLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.hpp:1221
void Append(std::span< const std::string > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:1231
void Append(std::span< const std::string_view > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:1241
StringArrayLogEntry()=default
void Update(std::initializer_list< std::string_view > arr, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
Definition DataLog.hpp:1291
StringArrayLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.hpp:1218
StringLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.hpp:856
void Append(std::string_view value, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:869
static constexpr const char * kDataType
Definition DataLog.hpp:851
StringLogEntry(DataLog &log, std::string_view name, std::string_view metadata, std::string_view type, int64_t timestamp=0)
Definition DataLog.hpp:859
void Update(std::string_view value, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
Definition DataLog.hpp:883
StringLogEntry()=default
StringLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.hpp:854
bool HasLastValue() const
Gets whether there is a last value.
Definition DataLog.hpp:1546
StructArrayLogEntry(DataLog &log, std::string_view name, I... info, int64_t timestamp=0)
Definition DataLog.hpp:1436
void Append(U &&data, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:1478
StructArrayLogEntry()=default
void Update(std::span< const T > data, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
Definition DataLog.hpp:1516
StructArrayLogEntry & operator=(StructArrayLogEntry &&rhs)
Definition DataLog.hpp:1458
void Append(std::span< const T > data, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:1495
StructArrayLogEntry(StructArrayLogEntry &&rhs)
Definition DataLog.hpp:1451
StructArrayLogEntry(DataLog &log, std::string_view name, std::string_view metadata, I... info, int64_t timestamp=0)
Definition DataLog.hpp:1439
std::optional< std::vector< T > > GetLastValue() const
Gets the last value.
Definition DataLog.hpp:1556
std::optional< T > GetLastValue() const
Gets the last value.
Definition DataLog.hpp:1409
StructLogEntry & operator=(StructLogEntry &&rhs)
Definition DataLog.hpp:1325
bool HasLastValue() const
Gets whether there is a last value.
Definition DataLog.hpp:1399
StructLogEntry(StructLogEntry &&rhs)
Definition DataLog.hpp:1320
StructLogEntry()=default
void Update(const T &data, int64_t timestamp=0)
Updates the last value and appends a record to the log if it has changed.
Definition DataLog.hpp:1364
StructLogEntry(DataLog &log, std::string_view name, I... info, int64_t timestamp=0)
Definition DataLog.hpp:1308
void Append(const T &data, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.hpp:1339
StructLogEntry(DataLog &log, std::string_view name, std::string_view metadata, I... info, int64_t timestamp=0)
Definition DataLog.hpp:1311
Definition Logger.hpp:26
Ease of use wrapper to make nanopb streams more opaque to the user.
Definition Protobuf.hpp:308
void ForEachProtobufDescriptor(function_ref< bool(std::string_view filename)> exists, function_ref< void(std::string_view filename, std::span< const uint8_t > descriptor)> fn)
Loops over all protobuf descriptors including nested/referenced descriptors.
Definition Protobuf.hpp:384
StringMap is a sorted associative container that contains key-value pairs with unique string keys.
Definition StringMap.hpp:26
Definition Struct.hpp:437
Specifies that a type is capable of raw struct serialization and deserialization.
Definition Struct.hpp:69
Definition StringMap.hpp:773
Definition DataLog.hpp:37
ControlRecordType
Definition DataLog.hpp:39
@ kControlFinish
Definition DataLog.hpp:41
@ kControlStart
Definition DataLog.hpp:40
@ kControlSetMetadata
Definition DataLog.hpp:42
Definition DataLogReader.hpp:17
Definition raw_os_ostream.hpp:19
void ForEachStructSchema(std::invocable< std::string_view, std::string_view > auto fn, const I &... info)
Definition Struct.hpp:424
constexpr auto MakeStructArrayTypeString(const I &... info)
Definition Struct.hpp:370
constexpr auto GetStructTypeString(const I &... info)
Get the type string for a raw struct serializable type.
Definition Struct.hpp:314
constexpr bool is_constexpr(Lambda)
Definition type_traits.hpp:81
::std::mutex mutex
Definition mutex.hpp:17
uint64_t Now()
Return a value representing the current time in microseconds.
Definition format.h:4013
Protobuf serialization template.
Definition Protobuf.hpp:36
Struct serialization template.
Definition Struct.hpp:39