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