blob: cb89934524688291de7766058e9934631fb76816 [file] [log] [blame]
James Kuszmaulcf324122023-01-14 14:07:17 -08001// 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#include <ctime>
6
James Kuszmaulb13e13f2023-11-22 20:44:04 -08007#include <fmt/chrono.h>
8#include <fmt/format.h>
9
James Kuszmaulcf324122023-01-14 14:07:17 -080010#include "wpi/DataLogReader.h"
11#include "wpi/DenseMap.h"
12#include "wpi/MemoryBuffer.h"
13
14int main(int argc, const char** argv) {
15 if (argc != 2) {
16 fmt::print(stderr, "Usage: printlog <file>\n");
17 return EXIT_FAILURE;
18 }
19 std::error_code ec;
20 wpi::log::DataLogReader reader{wpi::MemoryBuffer::GetFile(argv[1], ec)};
21 if (ec) {
22 fmt::print(stderr, "could not open file: {}\n", ec.message());
23 return EXIT_FAILURE;
24 }
25 if (!reader) {
26 fmt::print(stderr, "not a log file\n");
27 return EXIT_FAILURE;
28 }
29
30 wpi::DenseMap<int, wpi::log::StartRecordData> entries;
31 for (auto&& record : reader) {
32 if (record.IsStart()) {
33 wpi::log::StartRecordData data;
34 if (record.GetStartData(&data)) {
35 fmt::print("Start({}, name='{}', type='{}', metadata='{}') [{}]\n",
36 data.entry, data.name, data.type, data.metadata,
37 record.GetTimestamp() / 1000000.0);
38 if (entries.find(data.entry) != entries.end()) {
39 fmt::print("...DUPLICATE entry ID, overriding\n");
40 }
41 entries[data.entry] = data;
42 } else {
43 fmt::print("Start(INVALID)\n");
44 }
45 } else if (record.IsFinish()) {
46 int entry;
47 if (record.GetFinishEntry(&entry)) {
48 fmt::print("Finish({}) [{}]\n", entry,
49 record.GetTimestamp() / 1000000.0);
50 auto it = entries.find(entry);
51 if (it == entries.end()) {
52 fmt::print("...ID not found\n");
53 } else {
54 entries.erase(it);
55 }
56 } else {
57 fmt::print("Finish(INVALID)\n");
58 }
59 } else if (record.IsSetMetadata()) {
60 wpi::log::MetadataRecordData data;
61 if (record.GetSetMetadataData(&data)) {
62 fmt::print("SetMetadata({}, '{}') [{}]\n", data.entry, data.metadata,
63 record.GetTimestamp() / 1000000.0);
64 auto it = entries.find(data.entry);
65 if (it == entries.end()) {
66 fmt::print("...ID not found\n");
67 } else {
68 it->second.metadata = data.metadata;
69 }
70 } else {
71 fmt::print("SetMetadata(INVALID)\n");
72 }
73 } else if (record.IsControl()) {
74 fmt::print("Unrecognized control record\n");
75 } else {
76 fmt::print("Data({}, size={}) ", record.GetEntry(), record.GetSize());
77 auto entry = entries.find(record.GetEntry());
78 if (entry == entries.end()) {
79 fmt::print("<ID not found>\n");
80 continue;
81 }
82 fmt::print("<name='{}', type='{}'> [{}]\n", entry->second.name,
83 entry->second.type, record.GetTimestamp() / 1000000.0);
84
85 // handle systemTime specially
86 if (entry->second.name == "systemTime" && entry->second.type == "int64") {
87 int64_t val;
88 if (record.GetInteger(&val)) {
89 std::time_t timeval = val / 1000000;
90 fmt::print(" {:%Y-%m-%d %H:%M:%S}.{:06}\n",
91 *std::localtime(&timeval), val % 1000000);
92 } else {
93 fmt::print(" invalid\n");
94 }
95 continue;
96 }
97
98 if (entry->second.type == "double") {
99 double val;
100 if (record.GetDouble(&val)) {
101 fmt::print(" {}\n", val);
102 } else {
103 fmt::print(" invalid\n");
104 }
105 } else if (entry->second.type == "int64") {
106 int64_t val;
107 if (record.GetInteger(&val)) {
108 fmt::print(" {}\n", val);
109 } else {
110 fmt::print(" invalid\n");
111 }
112 } else if (entry->second.type == "string" ||
113 entry->second.type == "json") {
114 std::string_view val;
115 record.GetString(&val);
116 fmt::print(" '{}'\n", val);
117 } else if (entry->second.type == "boolean") {
118 bool val;
119 if (record.GetBoolean(&val)) {
120 fmt::print(" {}\n", val);
121 } else {
122 fmt::print(" invalid\n");
123 }
124 } else if (entry->second.type == "boolean[]") {
125 std::vector<int> val;
126 if (record.GetBooleanArray(&val)) {
127 fmt::print(" {}\n", fmt::join(val, ", "));
128 } else {
129 fmt::print(" invalid\n");
130 }
131 } else if (entry->second.type == "double[]") {
132 std::vector<double> val;
133 if (record.GetDoubleArray(&val)) {
134 fmt::print(" {}\n", fmt::join(val, ", "));
135 } else {
136 fmt::print(" invalid\n");
137 }
138 } else if (entry->second.type == "float[]") {
139 std::vector<float> val;
140 if (record.GetFloatArray(&val)) {
141 fmt::print(" {}\n", fmt::join(val, ", "));
142 } else {
143 fmt::print(" invalid\n");
144 }
145 } else if (entry->second.type == "int64[]") {
146 std::vector<int64_t> val;
147 if (record.GetIntegerArray(&val)) {
148 fmt::print(" {}\n", fmt::join(val, ", "));
149 } else {
150 fmt::print(" invalid\n");
151 }
152 } else if (entry->second.type == "string[]") {
153 std::vector<std::string_view> val;
154 if (record.GetStringArray(&val)) {
155 fmt::print(" {}\n", fmt::join(val, ", "));
156 } else {
157 fmt::print(" invalid\n");
158 }
159 }
160 }
161 }
162}