WPILibC++ 2025.3.1
Loading...
Searching...
No Matches
DataLog.h
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/DataLog_c.h"
23#include "wpi/DenseMap.h"
24#include "wpi/SmallVector.h"
25#include "wpi/StringMap.h"
26#include "wpi/mutex.h"
28#include "wpi/string.h"
29#include "wpi/struct/Struct.h"
30#include "wpi/timestamp.h"
31
32namespace wpi {
33class Logger;
34} // namespace wpi
35
36namespace wpi::log {
37
38namespace impl {
39
45
46} // namespace impl
47
48/**
49 * A data log for high-speed writing of data values.
50 *
51 * The lifetime of the data log object must be longer than any data log entry
52 * objects that refer to it.
53 *
54 * Finish() is needed only to indicate in the log that a particular entry is
55 * no longer being used (it releases the name to ID mapping). Finish() is not
56 * required to be called for data to be flushed to disk; entries in the log
57 * are written as Append() calls are being made. In fact, Finish() does not
58 * need to be called at all; this is helpful to avoid shutdown races where the
59 * DataLog object might be destroyed before other objects. It's often not a
60 * good idea to call Finish() from destructors for this reason.
61 *
62 * DataLog calls are thread safe. DataLog uses a typical multiple-supplier,
63 * single-consumer setup. Writes to the log are atomic, but there is no
64 * guaranteed order in the log when multiple threads are writing to it;
65 * whichever thread grabs the write mutex first will get written first.
66 * For this reason (as well as the fact that timestamps can be set to
67 * arbitrary values), records in the log are not guaranteed to be sorted by
68 * timestamp.
69 */
70class DataLog {
71 public:
72 virtual ~DataLog() = default;
73
74 DataLog(const DataLog&) = delete;
75 DataLog& operator=(const DataLog&) = delete;
76 DataLog(DataLog&&) = delete;
77 DataLog& operator=(const DataLog&&) = delete;
78
79 /**
80 * Explicitly flushes the log data to disk.
81 */
82 virtual void Flush() = 0;
83
84 /**
85 * Pauses appending of data records to the log. While paused, no data records
86 * are saved (e.g. AppendX is a no-op). Has no effect on entry starts /
87 * finishes / metadata changes.
88 */
89 virtual void Pause();
90
91 /**
92 * Resumes appending of data records to the log.
93 */
94 virtual void Resume();
95
96 /**
97 * Stops appending start/metadata/schema records to the log.
98 */
99 virtual void Stop();
100
101 /**
102 * Returns whether there is a data schema already registered with the given
103 * name.
104 *
105 * @param name Name (the string passed as the data type for records using this
106 * schema)
107 * @return True if schema already registered
108 */
109 bool HasSchema(std::string_view name) const;
110
111 /**
112 * Registers a data schema. Data schemas provide information for how a
113 * certain data type string can be decoded. The type string of a data schema
114 * indicates the type of the schema itself (e.g. "protobuf" for protobuf
115 * schemas, "struct" for struct schemas, etc). In the data log, schemas are
116 * saved just like normal records, with the name being generated from the
117 * provided name: "/.schema/<name>". Duplicate calls to this function with
118 * the same name are silently ignored.
119 *
120 * @param name Name (the string passed as the data type for records using this
121 * schema)
122 * @param type Type of schema (e.g. "protobuf", "struct", etc)
123 * @param schema Schema data
124 * @param timestamp Time stamp (may be 0 to indicate now)
125 */
126 void AddSchema(std::string_view name, std::string_view type,
127 std::span<const uint8_t> schema, int64_t timestamp = 0);
128
129 /**
130 * Registers a data schema. Data schemas provide information for how a
131 * certain data type string can be decoded. The type string of a data schema
132 * indicates the type of the schema itself (e.g. "protobuf" for protobuf
133 * schemas, "struct" for struct schemas, etc). In the data log, schemas are
134 * saved just like normal records, with the name being generated from the
135 * provided name: "/.schema/<name>". Duplicate calls to this function with
136 * the same name are silently ignored.
137 *
138 * @param name Name (the string passed as the data type for records using this
139 * schema)
140 * @param type Type of schema (e.g. "protobuf", "struct", etc)
141 * @param schema Schema data
142 * @param timestamp Time stamp (may be 0 to indicate now)
143 */
144 void AddSchema(std::string_view name, std::string_view type,
145 std::string_view schema, int64_t timestamp = 0) {
146 AddSchema(
147 name, type,
148 std::span<const uint8_t>{
149 reinterpret_cast<const uint8_t*>(schema.data()), schema.size()},
150 timestamp);
151 }
152
153 /**
154 * Registers a protobuf schema. Duplicate calls to this function with the same
155 * name are silently ignored.
156 *
157 * @tparam T protobuf serializable type
158 * @param msg protobuf message
159 * @param timestamp Time stamp (0 to indicate now)
160 */
161 template <ProtobufSerializable T>
162 void AddProtobufSchema(ProtobufMessage<T>& msg, int64_t timestamp = 0) {
163 if (timestamp == 0) {
164 timestamp = 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 StructSerializable<T, I...>
183 void AddStructSchema(const I&... info, int64_t timestamp = 0) {
184 if (timestamp == 0) {
185 timestamp = 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::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::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 std::vector<uint8_t> schemaData; // only set for schema entries
518 int id{0};
519 };
521 struct EntryInfo2 {
522 std::string metadata;
523 unsigned int count;
524 };
526 int m_lastId = 0;
527};
528
529/**
530 * Log entry base class.
531 */
533 protected:
534 DataLogEntry() = default;
535 DataLogEntry(DataLog& log, std::string_view name, std::string_view type,
536 std::string_view metadata = {}, int64_t timestamp = 0)
537 : m_log{&log}, m_entry{log.Start(name, type, metadata, timestamp)} {}
538
539 public:
540 DataLogEntry(const DataLogEntry&) = delete;
542
544 rhs.m_log = nullptr;
545 }
547 if (m_log) {
549 }
550 m_log = rhs.m_log;
551 rhs.m_log = nullptr;
552 m_entry = rhs.m_entry;
553 return *this;
554 }
555
556 explicit operator bool() const { return m_log != nullptr; }
557
558 /**
559 * Updates the metadata for the entry.
560 *
561 * @param metadata New metadata for the entry
562 * @param timestamp Time stamp (may be 0 to indicate now)
563 */
564 void SetMetadata(std::string_view metadata, int64_t timestamp = 0) {
565 m_log->SetMetadata(m_entry, metadata, timestamp);
566 }
567
568 /**
569 * Finishes the entry.
570 *
571 * @param timestamp Time stamp (may be 0 to indicate now)
572 */
573 void Finish(int64_t timestamp = 0) { m_log->Finish(m_entry, timestamp); }
574
575 protected:
576 DataLog* m_log = nullptr;
577 int m_entry = 0;
578};
579
580template <typename T>
582 protected:
584 DataLogValueEntryImpl(DataLog& log, std::string_view name,
585 std::string_view type, std::string_view metadata = {},
586 int64_t timestamp = 0)
587 : DataLogEntry{log, name, type, metadata, timestamp} {}
588
589 public:
591 : DataLogEntry{std::move(rhs)} {
592 std::scoped_lock lock{rhs.m_mutex};
593 m_lastValue = std::move(rhs.m_lastValue);
594 }
596 DataLogEntry::operator=(std::move(rhs));
597 std::scoped_lock lock{m_mutex, rhs.m_mutex};
598 m_lastValue = std::move(rhs.m_lastValue);
599 return *this;
600 }
601
602 /**
603 * Gets whether there is a last value.
604 *
605 * @note The last value is local to this class instance and updated only with
606 * Update(), not Append().
607 *
608 * @return True if last value exists, false otherwise.
609 */
610 bool HasLastValue() const { return m_lastValue.has_value(); }
611
612 /**
613 * Gets the last value.
614 *
615 * @note The last value is local to this class instance and updated only with
616 * Update(), not Append().
617 *
618 * @return Last value (empty if no last value)
619 */
620 std::optional<T> GetLastValue() const {
621 std::scoped_lock lock{m_mutex};
622 return m_lastValue;
623 }
624
625 protected:
627 std::optional<T> m_lastValue;
628};
629
630/**
631 * Log arbitrary byte data.
632 */
633class RawLogEntry : public DataLogValueEntryImpl<std::vector<uint8_t>> {
634 public:
635 static constexpr std::string_view kDataType = "raw";
636
637 RawLogEntry() = default;
638 RawLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
639 : RawLogEntry{log, name, {}, kDataType, timestamp} {}
640 RawLogEntry(DataLog& log, std::string_view name, std::string_view metadata,
641 int64_t timestamp = 0)
642 : RawLogEntry{log, name, metadata, kDataType, timestamp} {}
643 RawLogEntry(DataLog& log, std::string_view name, std::string_view metadata,
644 std::string_view type, int64_t timestamp = 0)
645 : DataLogValueEntryImpl{log, name, type, metadata, timestamp} {}
646
647 /**
648 * Appends a record to the log.
649 *
650 * @param data Data to record
651 * @param timestamp Time stamp (may be 0 to indicate now)
652 */
653 void Append(std::span<const uint8_t> data, int64_t timestamp = 0) {
654 m_log->AppendRaw(m_entry, data, timestamp);
655 }
656
657 /**
658 * Updates the last value and appends a record to the log if it has changed.
659 *
660 * @note The last value is local to this class instance; using Update() with
661 * two instances pointing to the same underlying log entry name will likely
662 * result in unexpected results.
663 *
664 * @param data Data to record
665 * @param timestamp Time stamp (may be 0 to indicate now)
666 */
667 void Update(std::span<const uint8_t> data, int64_t timestamp = 0);
668};
669
670/**
671 * Log boolean values.
672 */
674 public:
675 static constexpr std::string_view kDataType = "boolean";
676
677 BooleanLogEntry() = default;
678 BooleanLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
679 : BooleanLogEntry{log, name, {}, timestamp} {}
680 BooleanLogEntry(DataLog& log, std::string_view name,
681 std::string_view metadata, int64_t timestamp = 0)
682 : DataLogValueEntryImpl{log, name, kDataType, metadata, timestamp} {}
683
684 /**
685 * Appends a record to the log.
686 *
687 * @param value Value to record
688 * @param timestamp Time stamp (may be 0 to indicate now)
689 */
690 void Append(bool value, int64_t timestamp = 0) {
691 m_log->AppendBoolean(m_entry, value, timestamp);
692 }
693
694 /**
695 * Updates the last value and appends a record to the log if it has changed.
696 *
697 * @note The last value is local to this class instance; using Update() with
698 * two instances pointing to the same underlying log entry name will likely
699 * result in unexpected results.
700 *
701 * @param value Value to record
702 * @param timestamp Time stamp (may be 0 to indicate now)
703 */
704 void Update(bool value, int64_t timestamp = 0) {
705 std::scoped_lock lock{m_mutex};
706 if (m_lastValue != value) {
707 m_lastValue = value;
708 Append(value, timestamp);
709 }
710 }
711};
712
713/**
714 * Log integer values.
715 */
716class IntegerLogEntry : public DataLogValueEntryImpl<int64_t> {
717 public:
718 static constexpr std::string_view kDataType = "int64";
719
720 IntegerLogEntry() = default;
721 IntegerLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
722 : IntegerLogEntry{log, name, {}, timestamp} {}
723 IntegerLogEntry(DataLog& log, std::string_view name,
724 std::string_view metadata, int64_t timestamp = 0)
725 : DataLogValueEntryImpl{log, name, kDataType, metadata, timestamp} {}
726
727 /**
728 * Appends a record to the log.
729 *
730 * @param value Value to record
731 * @param timestamp Time stamp (may be 0 to indicate now)
732 */
733 void Append(int64_t value, int64_t timestamp = 0) {
734 m_log->AppendInteger(m_entry, value, timestamp);
735 }
736
737 /**
738 * Updates the last value and appends a record to the log if it has changed.
739 *
740 * @note The last value is local to this class instance; using Update() with
741 * two instances pointing to the same underlying log entry name will likely
742 * result in unexpected results.
743 *
744 * @param value Value to record
745 * @param timestamp Time stamp (may be 0 to indicate now)
746 */
747 void Update(int64_t value, int64_t timestamp = 0) {
748 std::scoped_lock lock{m_mutex};
749 if (m_lastValue != value) {
750 m_lastValue = value;
751 Append(value, timestamp);
752 }
753 }
754};
755
756/**
757 * Log float values.
758 */
760 public:
761 static constexpr std::string_view kDataType = "float";
762
763 FloatLogEntry() = default;
764 FloatLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
765 : FloatLogEntry{log, name, {}, timestamp} {}
766 FloatLogEntry(DataLog& log, std::string_view name, std::string_view metadata,
767 int64_t timestamp = 0)
768 : DataLogValueEntryImpl{log, name, kDataType, metadata, timestamp} {}
769
770 /**
771 * Appends a record to the log.
772 *
773 * @param value Value to record
774 * @param timestamp Time stamp (may be 0 to indicate now)
775 */
776 void Append(float value, int64_t timestamp = 0) {
777 m_log->AppendFloat(m_entry, value, timestamp);
778 }
779
780 /**
781 * Updates the last value and appends a record to the log if it has changed.
782 *
783 * @note The last value is local to this class instance; using Update() with
784 * two instances pointing to the same underlying log entry name will likely
785 * result in unexpected results.
786 *
787 * @param value Value to record
788 * @param timestamp Time stamp (may be 0 to indicate now)
789 */
790 void Update(float value, int64_t timestamp = 0) {
791 std::scoped_lock lock{m_mutex};
792 if (m_lastValue != value) {
793 m_lastValue = value;
794 Append(value, timestamp);
795 }
796 }
797};
798
799/**
800 * Log double values.
801 */
802class DoubleLogEntry : public DataLogValueEntryImpl<double> {
803 public:
804 static constexpr std::string_view kDataType = "double";
805
806 DoubleLogEntry() = default;
807 DoubleLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
808 : DoubleLogEntry{log, name, {}, timestamp} {}
809 DoubleLogEntry(DataLog& log, std::string_view name, std::string_view metadata,
810 int64_t timestamp = 0)
811 : DataLogValueEntryImpl{log, name, kDataType, metadata, timestamp} {}
812
813 /**
814 * Appends a record to the log.
815 *
816 * @param value Value to record
817 * @param timestamp Time stamp (may be 0 to indicate now)
818 */
819 void Append(double value, int64_t timestamp = 0) {
820 m_log->AppendDouble(m_entry, value, timestamp);
821 }
822
823 /**
824 * Updates the last value and appends a record to the log if it has changed.
825 *
826 * @note The last value is local to this class instance; using Update() with
827 * two instances pointing to the same underlying log entry name will likely
828 * result in unexpected results.
829 *
830 * @param value Value to record
831 * @param timestamp Time stamp (may be 0 to indicate now)
832 */
833 void Update(double value, int64_t timestamp = 0) {
834 std::scoped_lock lock{m_mutex};
835 if (m_lastValue != value) {
836 m_lastValue = value;
837 Append(value, timestamp);
838 }
839 }
840};
841
842/**
843 * Log string values.
844 */
845class StringLogEntry : public DataLogValueEntryImpl<std::string> {
846 public:
847 static constexpr const char* kDataType = "string";
848
849 StringLogEntry() = default;
850 StringLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
851 : StringLogEntry{log, name, {}, kDataType, timestamp} {}
852 StringLogEntry(DataLog& log, std::string_view name, std::string_view metadata,
853 int64_t timestamp = 0)
854 : StringLogEntry{log, name, metadata, kDataType, timestamp} {}
855 StringLogEntry(DataLog& log, std::string_view name, std::string_view metadata,
856 std::string_view type, int64_t timestamp = 0)
857 : DataLogValueEntryImpl{log, name, type, metadata, timestamp} {}
858
859 /**
860 * Appends a record to the log.
861 *
862 * @param value Value to record
863 * @param timestamp Time stamp (may be 0 to indicate now)
864 */
865 void Append(std::string_view value, int64_t timestamp = 0) {
866 m_log->AppendString(m_entry, value, timestamp);
867 }
868
869 /**
870 * Updates the last value and appends a record to the log if it has changed.
871 *
872 * @note The last value is local to this class instance; using Update() with
873 * two instances pointing to the same underlying log entry name will likely
874 * result in unexpected results.
875 *
876 * @param value Value to record
877 * @param timestamp Time stamp (may be 0 to indicate now)
878 */
879 void Update(std::string_view value, int64_t timestamp = 0) {
880 std::scoped_lock lock{m_mutex};
881 if (m_lastValue != value) {
882 m_lastValue = value;
883 Append(value, timestamp);
884 }
885 }
886};
887
888/**
889 * Log array of boolean values.
890 */
891class BooleanArrayLogEntry : public DataLogValueEntryImpl<std::vector<int>> {
892 public:
893 static constexpr const char* kDataType = "boolean[]";
894
896 BooleanArrayLogEntry(DataLog& log, std::string_view name,
897 int64_t timestamp = 0)
898 : BooleanArrayLogEntry{log, name, {}, timestamp} {}
899 BooleanArrayLogEntry(DataLog& log, std::string_view name,
900 std::string_view metadata, int64_t timestamp = 0)
901 : DataLogValueEntryImpl{log, name, kDataType, metadata, timestamp} {}
902
903 /**
904 * Appends a record to the log. For find functions to work, timestamp
905 * must be monotonically increasing.
906 *
907 * @param arr Values to record
908 * @param timestamp Time stamp (may be 0 to indicate now)
909 */
910 void Append(std::span<const bool> arr, int64_t timestamp = 0) {
911 m_log->AppendBooleanArray(m_entry, arr, timestamp);
912 }
913
914 /**
915 * Appends a record to the log.
916 *
917 * @param arr Values to record
918 * @param timestamp Time stamp (may be 0 to indicate now)
919 */
920 void Append(std::initializer_list<bool> arr, int64_t timestamp = 0) {
921 Append(std::span{arr.begin(), arr.end()}, timestamp);
922 }
923
924 /**
925 * Appends a record to the log.
926 *
927 * @param arr Values to record
928 * @param timestamp Time stamp (may be 0 to indicate now)
929 */
930 void Append(std::span<const int> arr, int64_t timestamp = 0) {
931 m_log->AppendBooleanArray(m_entry, arr, timestamp);
932 }
933
934 /**
935 * Appends a record to the log.
936 *
937 * @param arr Values to record
938 * @param timestamp Time stamp (may be 0 to indicate now)
939 */
940 void Append(std::initializer_list<int> arr, int64_t timestamp = 0) {
941 Append(std::span{arr.begin(), arr.end()}, timestamp);
942 }
943
944 /**
945 * Appends a record to the log.
946 *
947 * @param arr Values to record
948 * @param timestamp Time stamp (may be 0 to indicate now)
949 */
950 void Append(std::span<const uint8_t> arr, int64_t timestamp = 0) {
951 m_log->AppendBooleanArray(m_entry, arr, timestamp);
952 }
953
954 /**
955 * Updates the last value and appends a record to the log if it has changed.
956 *
957 * @note The last value is local to this class instance; using Update() with
958 * two instances pointing to the same underlying log entry name will likely
959 * result in unexpected results.
960 *
961 * @param arr Values to record
962 * @param timestamp Time stamp (may be 0 to indicate now)
963 */
964 void Update(std::span<const bool> arr, int64_t timestamp = 0);
965
966 /**
967 * Updates the last value and appends a record to the log if it has changed.
968 *
969 * @note The last value is local to this class instance; using Update() with
970 * two instances pointing to the same underlying log entry name will likely
971 * result in unexpected results.
972 *
973 * @param arr Values to record
974 * @param timestamp Time stamp (may be 0 to indicate now)
975 */
976 void Update(std::initializer_list<bool> arr, int64_t timestamp = 0) {
977 Update(std::span{arr.begin(), arr.end()}, timestamp);
978 }
979
980 /**
981 * Updates the last value and appends a record to the log if it has changed.
982 *
983 * @note The last value is local to this class instance; using Update() with
984 * two instances pointing to the same underlying log entry name will likely
985 * result in unexpected results.
986 *
987 * @param arr Values to record
988 * @param timestamp Time stamp (may be 0 to indicate now)
989 */
990 void Update(std::span<const int> arr, int64_t timestamp = 0);
991
992 /**
993 * Updates the last value and appends a record to the log if it has changed.
994 *
995 * @note The last value is local to this class instance; using Update() with
996 * two instances pointing to the same underlying log entry name will likely
997 * result in unexpected results.
998 *
999 * @param arr Values to record
1000 * @param timestamp Time stamp (may be 0 to indicate now)
1001 */
1002 void Update(std::initializer_list<int> arr, int64_t timestamp = 0) {
1003 Update(std::span{arr.begin(), arr.end()}, timestamp);
1004 }
1005
1006 /**
1007 * Updates the last value and appends a record to the log if it has changed.
1008 *
1009 * @note The last value is local to this class instance; using Update() with
1010 * two instances pointing to the same underlying log entry name will likely
1011 * result in unexpected results.
1012 *
1013 * @param arr Values to record
1014 * @param timestamp Time stamp (may be 0 to indicate now)
1015 */
1016 void Update(std::span<const uint8_t> arr, int64_t timestamp = 0);
1017};
1018
1019/**
1020 * Log array of integer values.
1021 */
1023 : public DataLogValueEntryImpl<std::vector<int64_t>> {
1024 public:
1025 static constexpr const char* kDataType = "int64[]";
1026
1028 IntegerArrayLogEntry(DataLog& log, std::string_view name,
1029 int64_t timestamp = 0)
1030 : IntegerArrayLogEntry{log, name, {}, timestamp} {}
1031 IntegerArrayLogEntry(DataLog& log, std::string_view name,
1032 std::string_view metadata, int64_t timestamp = 0)
1033 : DataLogValueEntryImpl{log, name, kDataType, metadata, timestamp} {}
1034
1035 /**
1036 * Appends a record to the log.
1037 *
1038 * @param arr Values to record
1039 * @param timestamp Time stamp (may be 0 to indicate now)
1040 */
1041 void Append(std::span<const int64_t> arr, int64_t timestamp = 0) {
1042 m_log->AppendIntegerArray(m_entry, arr, timestamp);
1043 }
1044
1045 /**
1046 * Appends a record to the log.
1047 *
1048 * @param arr Values to record
1049 * @param timestamp Time stamp (may be 0 to indicate now)
1050 */
1051 void Append(std::initializer_list<int64_t> arr, int64_t timestamp = 0) {
1052 Append({arr.begin(), arr.end()}, timestamp);
1053 }
1054
1055 /**
1056 * Updates the last value and appends a record to the log if it has changed.
1057 *
1058 * @note The last value is local to this class instance; using Update() with
1059 * two instances pointing to the same underlying log entry name will likely
1060 * result in unexpected results.
1061 *
1062 * @param arr Values to record
1063 * @param timestamp Time stamp (may be 0 to indicate now)
1064 */
1065 void Update(std::span<const int64_t> arr, int64_t timestamp = 0);
1066
1067 /**
1068 * Updates the last value and appends a record to the log if it has changed.
1069 *
1070 * @note The last value is local to this class instance; using Update() with
1071 * two instances pointing to the same underlying log entry name will likely
1072 * result in unexpected results.
1073 *
1074 * @param arr Values to record
1075 * @param timestamp Time stamp (may be 0 to indicate now)
1076 */
1077 void Update(std::initializer_list<int64_t> arr, int64_t timestamp = 0) {
1078 Update({arr.begin(), arr.end()}, timestamp);
1079 }
1080};
1081
1082/**
1083 * Log array of float values.
1084 */
1085class FloatArrayLogEntry : public DataLogValueEntryImpl<std::vector<float>> {
1086 public:
1087 static constexpr const char* kDataType = "float[]";
1088
1090 FloatArrayLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
1091 : FloatArrayLogEntry{log, name, {}, timestamp} {}
1092 FloatArrayLogEntry(DataLog& log, std::string_view name,
1093 std::string_view metadata, int64_t timestamp = 0)
1094 : DataLogValueEntryImpl{log, name, kDataType, metadata, timestamp} {}
1095
1096 /**
1097 * Appends a record to the log.
1098 *
1099 * @param arr Values to record
1100 * @param timestamp Time stamp (may be 0 to indicate now)
1101 */
1102 void Append(std::span<const float> arr, int64_t timestamp = 0) {
1103 m_log->AppendFloatArray(m_entry, arr, timestamp);
1104 }
1105
1106 /**
1107 * Appends a record to the log.
1108 *
1109 * @param arr Values to record
1110 * @param timestamp Time stamp (may be 0 to indicate now)
1111 */
1112 void Append(std::initializer_list<float> arr, int64_t timestamp = 0) {
1113 Append({arr.begin(), arr.end()}, timestamp);
1114 }
1115
1116 /**
1117 * Updates the last value and appends a record to the log if it has changed.
1118 *
1119 * @note The last value is local to this class instance; using Update() with
1120 * two instances pointing to the same underlying log entry name will likely
1121 * result in unexpected results.
1122 *
1123 * @param arr Values to record
1124 * @param timestamp Time stamp (may be 0 to indicate now)
1125 */
1126 void Update(std::span<const float> arr, int64_t timestamp = 0);
1127
1128 /**
1129 * Updates the last value and appends a record to the log if it has changed.
1130 *
1131 * @note The last value is local to this class instance; using Update() with
1132 * two instances pointing to the same underlying log entry name will likely
1133 * result in unexpected results.
1134 *
1135 * @param arr Values to record
1136 * @param timestamp Time stamp (may be 0 to indicate now)
1137 */
1138 void Update(std::initializer_list<float> arr, int64_t timestamp = 0) {
1139 Update({arr.begin(), arr.end()}, timestamp);
1140 }
1141};
1142
1143/**
1144 * Log array of double values.
1145 */
1146class DoubleArrayLogEntry : public DataLogValueEntryImpl<std::vector<double>> {
1147 public:
1148 static constexpr const char* kDataType = "double[]";
1149
1151 DoubleArrayLogEntry(DataLog& log, std::string_view name,
1152 int64_t timestamp = 0)
1153 : DoubleArrayLogEntry{log, name, {}, timestamp} {}
1154 DoubleArrayLogEntry(DataLog& log, std::string_view name,
1155 std::string_view metadata, int64_t timestamp = 0)
1156 : DataLogValueEntryImpl{log, name, kDataType, metadata, timestamp} {}
1157
1158 /**
1159 * Appends a record to the log.
1160 *
1161 * @param arr Values to record
1162 * @param timestamp Time stamp (may be 0 to indicate now)
1163 */
1164 void Append(std::span<const double> arr, int64_t timestamp = 0) {
1165 m_log->AppendDoubleArray(m_entry, arr, timestamp);
1166 }
1167
1168 /**
1169 * Appends a record to the log.
1170 *
1171 * @param arr Values to record
1172 * @param timestamp Time stamp (may be 0 to indicate now)
1173 */
1174 void Append(std::initializer_list<double> arr, int64_t timestamp = 0) {
1175 Append({arr.begin(), arr.end()}, timestamp);
1176 }
1177
1178 /**
1179 * Updates the last value and appends a record to the log if it has changed.
1180 *
1181 * @note The last value is local to this class instance; using Update() with
1182 * two instances pointing to the same underlying log entry name will likely
1183 * result in unexpected results.
1184 *
1185 * @param arr Values to record
1186 * @param timestamp Time stamp (may be 0 to indicate now)
1187 */
1188 void Update(std::span<const double> arr, int64_t timestamp = 0);
1189
1190 /**
1191 * Updates the last value and appends a record to the log if it has changed.
1192 *
1193 * @note The last value is local to this class instance; using Update() with
1194 * two instances pointing to the same underlying log entry name will likely
1195 * result in unexpected results.
1196 *
1197 * @param arr Values to record
1198 * @param timestamp Time stamp (may be 0 to indicate now)
1199 */
1200 void Update(std::initializer_list<double> arr, int64_t timestamp = 0) {
1201 Update({arr.begin(), arr.end()}, timestamp);
1202 }
1203};
1204
1205/**
1206 * Log array of string values.
1207 */
1209 : public DataLogValueEntryImpl<std::vector<std::string>> {
1210 public:
1211 static constexpr const char* kDataType = "string[]";
1212
1214 StringArrayLogEntry(DataLog& log, std::string_view name,
1215 int64_t timestamp = 0)
1216 : StringArrayLogEntry{log, name, {}, timestamp} {}
1217 StringArrayLogEntry(DataLog& log, std::string_view name,
1218 std::string_view metadata, int64_t timestamp = 0)
1219 : DataLogValueEntryImpl{log, name, kDataType, metadata, timestamp} {}
1220
1221 /**
1222 * Appends a record to the log.
1223 *
1224 * @param arr Values to record
1225 * @param timestamp Time stamp (may be 0 to indicate now)
1226 */
1227 void Append(std::span<const std::string> arr, int64_t timestamp = 0) {
1228 m_log->AppendStringArray(m_entry, arr, timestamp);
1229 }
1230
1231 /**
1232 * Appends a record to the log.
1233 *
1234 * @param arr Values to record
1235 * @param timestamp Time stamp (may be 0 to indicate now)
1236 */
1237 void Append(std::span<const std::string_view> arr, int64_t timestamp = 0) {
1238 m_log->AppendStringArray(m_entry, arr, timestamp);
1239 }
1240
1241 /**
1242 * Appends a record to the log.
1243 *
1244 * @param arr Values to record
1245 * @param timestamp Time stamp (may be 0 to indicate now)
1246 */
1247 void Append(std::initializer_list<std::string_view> arr,
1248 int64_t timestamp = 0) {
1249 Append(std::span<const std::string_view>{arr.begin(), arr.end()},
1250 timestamp);
1251 }
1252
1253 /**
1254 * Updates the last value and appends a record to the log if it has changed.
1255 *
1256 * @note The last value is local to this class instance; using Update() with
1257 * two instances pointing to the same underlying log entry name will likely
1258 * result in unexpected results.
1259 *
1260 * @param arr Values to record
1261 * @param timestamp Time stamp (may be 0 to indicate now)
1262 */
1263 void Update(std::span<const std::string> arr, int64_t timestamp = 0);
1264
1265 /**
1266 * Updates the last value and appends a record to the log if it has changed.
1267 *
1268 * @note The last value is local to this class instance; using Update() with
1269 * two instances pointing to the same underlying log entry name will likely
1270 * result in unexpected results.
1271 *
1272 * @param arr Values to record
1273 * @param timestamp Time stamp (may be 0 to indicate now)
1274 */
1275 void Update(std::span<const std::string_view> arr, int64_t timestamp = 0);
1276
1277 /**
1278 * Updates the last value and appends a record to the log if it has changed.
1279 *
1280 * @note The last value is local to this class instance; using Update() with
1281 * two instances pointing to the same underlying log entry name will likely
1282 * result in unexpected results.
1283 *
1284 * @param arr Values to record
1285 * @param timestamp Time stamp (may be 0 to indicate now)
1286 */
1287 void Update(std::initializer_list<std::string_view> arr,
1288 int64_t timestamp = 0) {
1289 Update(std::span<const std::string_view>{arr.begin(), arr.end()},
1290 timestamp);
1291 }
1292};
1293
1294/**
1295 * Log raw struct serializable objects.
1296 */
1297template <typename T, typename... I>
1298 requires StructSerializable<T, I...>
1300 using S = Struct<T, I...>;
1301
1302 public:
1303 StructLogEntry() = default;
1304 StructLogEntry(DataLog& log, std::string_view name, I... info,
1305 int64_t timestamp = 0)
1306 : StructLogEntry{log, name, {}, std::move(info)..., timestamp} {}
1307 StructLogEntry(DataLog& log, std::string_view name, std::string_view metadata,
1308 I... info, int64_t timestamp = 0)
1309 : m_info{std::move(info)...} {
1310 m_log = &log;
1311 log.AddStructSchema<T, I...>(info..., timestamp);
1312 m_entry =
1313 log.Start(name, GetStructTypeString<T>(info...), metadata, timestamp);
1314 }
1315
1317 : DataLogEntry{std::move(rhs)}, m_info{std::move(rhs.m_info)} {
1318 std::scoped_lock lock{rhs.m_mutex};
1319 m_lastValue = std::move(rhs.m_lastValue);
1320 }
1322 DataLogEntry::operator=(std::move(rhs));
1323 m_info = std::move(rhs.m_info);
1324 std::scoped_lock lock{m_mutex, rhs.m_mutex};
1325 m_lastValue = std::move(rhs.m_lastValue);
1326 return *this;
1327 }
1328
1329 /**
1330 * Appends a record to the log.
1331 *
1332 * @param data Data to record
1333 * @param timestamp Time stamp (may be 0 to indicate now)
1334 */
1335 void Append(const T& data, int64_t timestamp = 0) {
1336 if constexpr (sizeof...(I) == 0) {
1337 if constexpr (wpi::is_constexpr([] { S::GetSize(); })) {
1338 uint8_t buf[S::GetSize()];
1339 S::Pack(buf, data);
1340 m_log->AppendRaw(m_entry, buf, timestamp);
1341 return;
1342 }
1343 }
1345 buf.resize_for_overwrite(std::apply(S::GetSize, m_info));
1346 std::apply([&](const I&... info) { S::Pack(buf, data, info...); }, m_info);
1347 m_log->AppendRaw(m_entry, buf, timestamp);
1348 }
1349
1350 /**
1351 * Updates the last value and appends a record to the log if it has changed.
1352 *
1353 * @note The last value is local to this class instance; using Update() with
1354 * two instances pointing to the same underlying log entry name will likely
1355 * result in unexpected results.
1356 *
1357 * @param data Data to record
1358 * @param timestamp Time stamp (may be 0 to indicate now)
1359 */
1360 void Update(const T& data, int64_t timestamp = 0) {
1361 if constexpr (sizeof...(I) == 0) {
1362 if constexpr (wpi::is_constexpr([] { S::GetSize(); })) {
1363 uint8_t buf[S::GetSize()];
1364 S::Pack(buf, data);
1365 std::scoped_lock lock{m_mutex};
1366 if (m_lastValue.empty() ||
1367 !std::equal(buf, buf + S::GetSize(), m_lastValue.begin(),
1368 m_lastValue.end())) {
1369 m_lastValue.assign(buf, buf + S::GetSize());
1370 m_log->AppendRaw(m_entry, buf, timestamp);
1371 }
1372 return;
1373 }
1374 }
1376 buf.resize_for_overwrite(std::apply(S::GetSize, m_info));
1377 std::apply([&](const I&... info) { S::Pack(buf, data, info...); }, m_info);
1378 std::scoped_lock lock{m_mutex};
1379 if (m_lastValue.empty() ||
1380 !std::equal(buf.begin(), buf.end(), m_lastValue.begin(),
1381 m_lastValue.end())) {
1382 m_lastValue.assign(buf.begin(), buf.end());
1383 m_log->AppendRaw(m_entry, buf, timestamp);
1384 }
1385 }
1386
1387 /**
1388 * Gets whether there is a last value.
1389 *
1390 * @note The last value is local to this class instance and updated only with
1391 * Update(), not Append().
1392 *
1393 * @return True if last value exists, false otherwise.
1394 */
1395 bool HasLastValue() const { return !m_lastValue.empty(); }
1396
1397 /**
1398 * Gets the last value.
1399 *
1400 * @note The last value is local to this class instance and updated only with
1401 * Update(), not Append().
1402 *
1403 * @return Last value (empty if no last value)
1404 */
1405 std::optional<T> GetLastValue() const {
1406 std::scoped_lock lock{m_mutex};
1407 if (m_lastValue.empty()) {
1408 return std::nullopt;
1409 }
1410 return std::apply(
1411 [&](const I&... info) { return S::Unpack(m_lastValue, info...); },
1412 m_info);
1413 }
1414
1415 private:
1416 mutable wpi::mutex m_mutex;
1417 std::vector<uint8_t> m_lastValue;
1418 [[no_unique_address]]
1419 std::tuple<I...> m_info;
1420};
1421
1422/**
1423 * Log raw struct serializable array of objects.
1424 */
1425template <typename T, typename... I>
1426 requires StructSerializable<T, I...>
1428 using S = Struct<T, I...>;
1429
1430 public:
1432 StructArrayLogEntry(DataLog& log, std::string_view name, I... info,
1433 int64_t timestamp = 0)
1434 : StructArrayLogEntry{log, name, {}, std::move(info)..., timestamp} {}
1435 StructArrayLogEntry(DataLog& log, std::string_view name,
1436 std::string_view metadata, I... info,
1437 int64_t timestamp = 0)
1438 : m_info{std::move(info)...} {
1439 m_log = &log;
1440 log.AddStructSchema<T, I...>(info..., timestamp);
1441 m_entry = log.Start(
1442 name, MakeStructArrayTypeString<T, std::dynamic_extent>(info...),
1443 metadata, timestamp);
1444 }
1445
1447 : DataLogEntry{std::move(rhs)},
1448 m_buf{std::move(rhs.m_buf)},
1449 m_info{std::move(rhs.m_info)} {
1450 std::scoped_lock lock{rhs.m_mutex};
1451 m_lastValue = std::move(rhs.m_lastValue);
1452 }
1454 DataLogEntry::operator=(std::move(rhs));
1455 m_buf = std::move(rhs.m_buf);
1456 m_info = std::move(rhs.m_info);
1457 std::scoped_lock lock{m_mutex, rhs.m_mutex};
1458 m_lastValue = std::move(rhs.m_lastValue);
1459 return *this;
1460 }
1461
1462 /**
1463 * Appends a record to the log.
1464 *
1465 * @param data Data to record
1466 * @param timestamp Time stamp (may be 0 to indicate now)
1467 */
1468 template <typename U>
1469#if __cpp_lib_ranges >= 201911L
1470 requires std::ranges::range<U> &&
1471 std::convertible_to<std::ranges::range_value_t<U>, T>
1472#endif
1473 void Append(U&& data, int64_t timestamp = 0) {
1474 std::apply(
1475 [&](const I&... info) {
1476 m_buf.Write(
1477 std::forward<U>(data),
1478 [&](auto bytes) { m_log->AppendRaw(m_entry, bytes, timestamp); },
1479 info...);
1480 },
1481 m_info);
1482 }
1483
1484 /**
1485 * Appends a record to the log.
1486 *
1487 * @param data Data to record
1488 * @param timestamp Time stamp (may be 0 to indicate now)
1489 */
1490 void Append(std::span<const T> data, int64_t timestamp = 0) {
1491 std::apply(
1492 [&](const I&... info) {
1493 m_buf.Write(
1494 data,
1495 [&](auto bytes) { m_log->AppendRaw(m_entry, bytes, timestamp); },
1496 info...);
1497 },
1498 m_info);
1499 }
1500
1501 /**
1502 * Updates the last value and appends a record to the log if it has changed.
1503 *
1504 * @note The last value is local to this class instance; using Update() with
1505 * two instances pointing to the same underlying log entry name will likely
1506 * result in unexpected results.
1507 *
1508 * @param data Data to record
1509 * @param timestamp Time stamp (may be 0 to indicate now)
1510 */
1511 void Update(std::span<const T> data, int64_t timestamp = 0) {
1512 std::apply(
1513 [&](const I&... info) {
1514 m_buf.Write(
1515 data,
1516 [&](auto bytes) {
1517 std::scoped_lock lock{m_mutex};
1518 if (!m_lastValue.has_value()) {
1519 m_lastValue = std::vector(bytes.begin(), bytes.end());
1520 m_log->AppendRaw(m_entry, bytes, timestamp);
1521 } else if (!std::equal(bytes.begin(), bytes.end(),
1522 m_lastValue->begin(),
1523 m_lastValue->end())) {
1524 m_lastValue->assign(bytes.begin(), bytes.end());
1525 m_log->AppendRaw(m_entry, bytes, timestamp);
1526 }
1527 },
1528 info...);
1529 },
1530 m_info);
1531 }
1532
1533 /**
1534 * Gets whether there is a last value.
1535 *
1536 * @note The last value is local to this class instance and updated only with
1537 * Update(), not Append().
1538 *
1539 * @return True if last value exists, false otherwise.
1540 */
1541 bool HasLastValue() const { return m_lastValue.has_value(); }
1542
1543 /**
1544 * Gets the last value.
1545 *
1546 * @note The last value is local to this class instance and updated only with
1547 * Update(), not Append().
1548 *
1549 * @return Last value (empty if no last value)
1550 */
1551 std::optional<std::vector<T>> GetLastValue() const {
1552 std::scoped_lock lock{m_mutex};
1553 if (!m_lastValue.has_value()) {
1554 return std::nullopt;
1555 }
1556 auto& lastValue = m_lastValue.value();
1557 size_t size = std::apply(S::GetSize, m_info);
1558 std::vector<T> rv;
1559 rv.reserve(lastValue.size() / size);
1560 for (auto in = lastValue.begin(), end = lastValue.end(); in < end;
1561 in += size) {
1562 std::apply(
1563 [&](const I&... info) {
1564 rv.emplace_back(S::Unpack(
1565 std::span<const uint8_t>{std::to_address(in), size}, info...));
1566 },
1567 m_info);
1568 }
1569 return rv;
1570 }
1571
1572 private:
1573 mutable wpi::mutex m_mutex;
1574 StructArrayBuffer<T, I...> m_buf;
1575 std::optional<std::vector<uint8_t>> m_lastValue;
1576 [[no_unique_address]]
1577 std::tuple<I...> m_info;
1578};
1579
1580/**
1581 * Log protobuf serializable objects.
1582 */
1583template <ProtobufSerializable T>
1585 using P = Protobuf<T>;
1586
1587 public:
1588 ProtobufLogEntry() = default;
1589 ProtobufLogEntry(DataLog& log, std::string_view name, int64_t timestamp = 0)
1590 : ProtobufLogEntry{log, name, {}, timestamp} {}
1591 ProtobufLogEntry(DataLog& log, std::string_view name,
1592 std::string_view metadata, int64_t timestamp = 0) {
1593 m_log = &log;
1594 log.AddProtobufSchema<T>(m_msg, timestamp);
1595 m_entry = log.Start(name, m_msg.GetTypeString(), metadata, timestamp);
1596 }
1597
1598 /**
1599 * Appends a record to the log.
1600 *
1601 * @param data Data to record
1602 * @param timestamp Time stamp (may be 0 to indicate now)
1603 */
1604 void Append(const T& data, int64_t timestamp = 0) {
1606 {
1607 std::scoped_lock lock{m_mutex};
1608 m_msg.Pack(buf, data);
1609 }
1610 m_log->AppendRaw(m_entry, buf, timestamp);
1611 }
1612
1613 /**
1614 * Updates the last value and appends a record to the log if it has changed.
1615 *
1616 * @note The last value is local to this class instance; using Update() with
1617 * two instances pointing to the same underlying log entry name will likely
1618 * result in unexpected results.
1619 *
1620 * @param data Data to record
1621 * @param timestamp Time stamp (may be 0 to indicate now)
1622 */
1623 void Update(const T& data, int64_t timestamp = 0) {
1624 std::scoped_lock lock{m_mutex};
1626 m_msg.Pack(buf, data);
1627 if (!m_lastValue.has_value()) {
1628 m_lastValue = std::vector(buf.begin(), buf.end());
1629 m_log->AppendRaw(m_entry, buf, timestamp);
1630 } else if (!std::equal(buf.begin(), buf.end(), m_lastValue->begin(),
1631 m_lastValue->end())) {
1632 m_lastValue->assign(buf.begin(), buf.end());
1633 m_log->AppendRaw(m_entry, buf, timestamp);
1634 }
1635 }
1636
1637 /**
1638 * Gets whether there is a last value.
1639 *
1640 * @note The last value is local to this class instance and updated only with
1641 * Update(), not Append().
1642 *
1643 * @return True if last value exists, false otherwise.
1644 */
1645 bool HasLastValue() const { return m_lastValue.has_value(); }
1646
1647 /**
1648 * Gets the last value.
1649 *
1650 * @note The last value is local to this class instance and updated only with
1651 * Update(), not Append().
1652 *
1653 * @return Last value (empty if no last value)
1654 */
1655 std::optional<T> GetLastValue() const {
1656 std::scoped_lock lock{m_mutex};
1657 if (!m_lastValue.has_value()) {
1658 return std::nullopt;
1659 }
1660 return m_msg.Unpack(m_lastValue);
1661 }
1662
1663 private:
1664 mutable wpi::mutex m_mutex;
1665 ProtobufMessage<T> m_msg;
1666 std::optional<std::vector<uint8_t>> m_lastValue;
1667};
1668
1669} // namespace wpi::log
This file defines the DenseMap class.
This file defines the SmallVector class.
Definition DenseMap.h:759
Definition Logger.h:27
Ease of use wrapper to make nanopb streams more opaque to the user.
Definition Protobuf.h:307
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.h:383
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition SmallVector.h:1212
void resize_for_overwrite(size_type N)
Like resize, but T is POD, the new values won't be initialized.
Definition SmallVector.h:657
iterator begin()
Definition SmallVector.h:283
iterator end()
Definition SmallVector.h:285
StringMap is a sorted associative container that contains key-value pairs with unique string keys.
Definition StringMap.h:26
Definition Struct.h:437
Log array of boolean values.
Definition DataLog.h:891
void Append(std::span< const uint8_t > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:950
void Append(std::initializer_list< bool > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:920
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.h:976
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.h:896
void Append(std::initializer_list< int > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:940
void Append(std::span< const int > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:930
void Append(std::span< const bool > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:910
BooleanArrayLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.h:899
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.h:1002
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.
Log boolean values.
Definition DataLog.h:673
BooleanLogEntry()=default
BooleanLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.h:680
void Append(bool value, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:690
BooleanLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.h:678
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.h:704
Definition DataLog.h:386
std::span< uint8_t > GetData()
Definition DataLog.h:428
Buffer(size_t alloc=kBlockSize)
Definition DataLog.h:388
Buffer(Buffer &&oth)
Definition DataLog.h:395
Buffer & operator=(Buffer &&oth)
Definition DataLog.h:402
~Buffer()
Definition DataLog.h:390
std::span< const uint8_t > GetData() const
Definition DataLog.h:429
void Clear()
Definition DataLog.h:424
Buffer(const Buffer &)=delete
size_t GetRemaining() const
Definition DataLog.h:426
void Unreserve(size_t size)
Definition DataLog.h:422
Buffer & operator=(const Buffer &)=delete
uint8_t * Reserve(size_t size)
Definition DataLog.h:415
Log entry base class.
Definition DataLog.h:532
DataLogEntry(DataLogEntry &&rhs)
Definition DataLog.h:543
DataLogEntry(const DataLogEntry &)=delete
void Finish(int64_t timestamp=0)
Finishes the entry.
Definition DataLog.h:573
DataLog * m_log
Definition DataLog.h:576
DataLogEntry(DataLog &log, std::string_view name, std::string_view type, std::string_view metadata={}, int64_t timestamp=0)
Definition DataLog.h:535
DataLogEntry()=default
DataLogEntry & operator=(const DataLogEntry &)=delete
int m_entry
Definition DataLog.h:577
DataLogEntry & operator=(DataLogEntry &&rhs)
Definition DataLog.h:546
void SetMetadata(std::string_view metadata, int64_t timestamp=0)
Updates the metadata for the entry.
Definition DataLog.h:564
A data log for high-speed writing of data values.
Definition DataLog.h:70
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.
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 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.
void AddStructSchema(const I &... info, int64_t timestamp=0)
Registers a struct schema.
Definition DataLog.h:183
DataLog(const DataLog &)=delete
void ReleaseBufs(std::vector< Buffer > *bufs)
Releases memory for a set of buffers back to the internal buffer pool.
DataLog(wpi::Logger &msglog, std::string_view extraHeader="")
Constructs the log.
Definition DataLog.h:444
void AppendStringArray(int entry, std::span< const struct WPI_String > arr, int64_t timestamp)
Appends a string array record to the log.
static wpi::Logger s_defaultMessageLog
Definition DataLog.h:384
void AddSchema(std::string_view name, std::string_view type, std::string_view schema, int64_t timestamp=0)
Registers a data schema.
Definition DataLog.h:144
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
wpi::Logger & m_msglog
Definition DataLog.h:506
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.
DataLog(DataLog &&)=delete
virtual ~DataLog()=default
static constexpr size_t kBlockSize
Definition DataLog.h: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 AddProtobufSchema(ProtobufMessage< T > &msg, int64_t timestamp=0)
Registers a protobuf schema.
Definition DataLog.h:162
void AppendBoolean(int entry, bool value, int64_t timestamp)
Appends a boolean record to the log.
Definition DataLog.h:581
wpi::mutex m_mutex
Definition DataLog.h:626
std::optional< T > m_lastValue
Definition DataLog.h:627
DataLogValueEntryImpl()=default
std::optional< T > GetLastValue() const
Gets the last value.
Definition DataLog.h:620
DataLogValueEntryImpl & operator=(DataLogValueEntryImpl &&rhs)
Definition DataLog.h:595
DataLogValueEntryImpl(DataLog &log, std::string_view name, std::string_view type, std::string_view metadata={}, int64_t timestamp=0)
Definition DataLog.h:584
bool HasLastValue() const
Gets whether there is a last value.
Definition DataLog.h:610
DataLogValueEntryImpl(DataLogValueEntryImpl &&rhs)
Definition DataLog.h:590
Log array of double values.
Definition DataLog.h:1146
DoubleArrayLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.h:1151
void Append(std::span< const double > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:1164
void Append(std::initializer_list< double > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:1174
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.h:1200
DoubleArrayLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.h:1154
Log double values.
Definition DataLog.h:802
DoubleLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.h:809
void Append(double value, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:819
DoubleLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.h:807
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.h:833
DoubleLogEntry()=default
Log array of float values.
Definition DataLog.h:1085
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.h:1138
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.h:1092
void Append(std::span< const float > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:1102
void Append(std::initializer_list< float > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:1112
FloatArrayLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.h:1090
FloatArrayLogEntry()=default
Log float values.
Definition DataLog.h:759
FloatLogEntry()=default
FloatLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.h:764
FloatLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.h:766
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.h:790
void Append(float value, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:776
Log array of integer values.
Definition DataLog.h:1023
IntegerArrayLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.h:1031
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.h:1077
void Append(std::span< const int64_t > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:1041
IntegerArrayLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.h:1028
void Append(std::initializer_list< int64_t > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:1051
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
Log integer values.
Definition DataLog.h:716
IntegerLogEntry()=default
IntegerLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.h:723
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.h:747
IntegerLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.h:721
void Append(int64_t value, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:733
Log protobuf serializable objects.
Definition DataLog.h:1584
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.h:1623
std::optional< T > GetLastValue() const
Gets the last value.
Definition DataLog.h:1655
ProtobufLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.h:1589
void Append(const T &data, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:1604
ProtobufLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.h:1591
bool HasLastValue() const
Gets whether there is a last value.
Definition DataLog.h:1645
ProtobufLogEntry()=default
Log arbitrary byte data.
Definition DataLog.h:633
RawLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.h:640
RawLogEntry(DataLog &log, std::string_view name, std::string_view metadata, std::string_view type, int64_t timestamp=0)
Definition DataLog.h:643
static constexpr std::string_view kDataType
Definition DataLog.h:635
RawLogEntry()=default
void Append(std::span< const uint8_t > data, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:653
RawLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.h:638
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.
Log array of string values.
Definition DataLog.h:1209
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.h:1247
StringArrayLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.h:1217
void Append(std::span< const std::string > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:1227
void Append(std::span< const std::string_view > arr, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:1237
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.h:1287
StringArrayLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.h:1214
Log string values.
Definition DataLog.h:845
StringLogEntry(DataLog &log, std::string_view name, std::string_view metadata, int64_t timestamp=0)
Definition DataLog.h:852
void Append(std::string_view value, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:865
StringLogEntry(DataLog &log, std::string_view name, std::string_view metadata, std::string_view type, int64_t timestamp=0)
Definition DataLog.h:855
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.h:879
StringLogEntry()=default
StringLogEntry(DataLog &log, std::string_view name, int64_t timestamp=0)
Definition DataLog.h:850
Log raw struct serializable array of objects.
Definition DataLog.h:1427
bool HasLastValue() const
Gets whether there is a last value.
Definition DataLog.h:1541
StructArrayLogEntry(DataLog &log, std::string_view name, I... info, int64_t timestamp=0)
Definition DataLog.h:1432
void Append(U &&data, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:1473
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.h:1511
StructArrayLogEntry & operator=(StructArrayLogEntry &&rhs)
Definition DataLog.h:1453
void Append(std::span< const T > data, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:1490
StructArrayLogEntry(StructArrayLogEntry &&rhs)
Definition DataLog.h:1446
StructArrayLogEntry(DataLog &log, std::string_view name, std::string_view metadata, I... info, int64_t timestamp=0)
Definition DataLog.h:1435
std::optional< std::vector< T > > GetLastValue() const
Gets the last value.
Definition DataLog.h:1551
Log raw struct serializable objects.
Definition DataLog.h:1299
std::optional< T > GetLastValue() const
Gets the last value.
Definition DataLog.h:1405
StructLogEntry & operator=(StructLogEntry &&rhs)
Definition DataLog.h:1321
bool HasLastValue() const
Gets whether there is a last value.
Definition DataLog.h:1395
StructLogEntry(StructLogEntry &&rhs)
Definition DataLog.h:1316
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.h:1360
StructLogEntry(DataLog &log, std::string_view name, I... info, int64_t timestamp=0)
Definition DataLog.h:1304
void Append(const T &data, int64_t timestamp=0)
Appends a record to the log.
Definition DataLog.h:1335
StructLogEntry(DataLog &log, std::string_view name, std::string_view metadata, I... info, int64_t timestamp=0)
Definition DataLog.h:1307
Specifies that a type is capable of raw struct serialization and deserialization.
Definition Struct.h:69
Implement std::hash so that hash_code can be used in STL containers.
Definition PointerIntPair.h:280
ControlRecordType
Definition DataLog.h:40
@ kControlFinish
Definition DataLog.h:42
@ kControlStart
Definition DataLog.h:41
@ kControlSetMetadata
Definition DataLog.h:43
Definition ntcore_cpp.h:31
Foonathan namespace.
Definition ntcore_cpp.h:26
void ForEachStructSchema(std::invocable< std::string_view, std::string_view > auto fn, const I &... info)
Definition Struct.h:424
constexpr bool is_constexpr(Lambda)
Definition type_traits.h:81
::std::mutex mutex
Definition mutex.h:17
uint64_t Now()
Return a value representing the current time in microseconds.
Definition format.h:3858
Protobuf serialization template.
Definition Protobuf.h:36
Struct serialization template.
Definition Struct.h:39