Skip to content
This repository was archived by the owner on Nov 1, 2023. It is now read-only.

Commit 8d20441

Browse files
committed
Improve datalog reader API to be more pythonic
- Add bulk of wpilib printlog example to examples
1 parent 9e5092f commit 8d20441

File tree

2 files changed

+279
-22
lines changed

2 files changed

+279
-22
lines changed

examples/printlog.py

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
#!/usr/bin/env python3
2+
#
3+
# Copyright (c) FIRST and other WPILib contributors.
4+
# Open Source Software; you can modify and/or share it under the terms of
5+
# the WPILib BSD license file in the root directory of this project.
26

37
import argparse
4-
import pathlib
8+
import datetime
59

610
from wpiutil.log import DataLogReader
711

@@ -11,8 +15,76 @@
1115
args = parser.parse_args()
1216

1317
reader = DataLogReader(args.infile)
18+
19+
entries = {}
1420
for record in reader:
21+
timestamp = record.getTimestamp() / 1000000
1522
if record.isStart():
16-
print(record.getStartData())
23+
try:
24+
data = record.getStartData()
25+
print(f"{data} [{timestamp}]")
26+
if data.entry in entries:
27+
print("...DUPLICATE entry ID, overriding")
28+
entries[data.entry] = data
29+
except TypeError as e:
30+
print("Start(INVALID)")
31+
elif record.isFinish():
32+
try:
33+
entry = record.getFinishEntry()
34+
print(f"Finish({entry}) [{timestamp}]")
35+
if entry not in entries:
36+
print("...ID not found")
37+
else:
38+
del entries[entry]
39+
except TypeError as e:
40+
print("Finish(INVALID)")
41+
elif record.isSetMetadata():
42+
try:
43+
data = record.getSetMetadataData()
44+
print(f"{data} [{timestamp}]")
45+
if data.entry not in entries:
46+
print("...ID not found")
47+
except TypeError as e:
48+
print("SetMetadata(INVALID)")
49+
elif record.isControl():
50+
print("Unrecognized control record")
1751
else:
18-
print(record.getBoolean())
52+
print(f"Data({record.getEntry()}, size={record.getSize()}) ", end="")
53+
entry = entries.get(record.getEntry(), None)
54+
if entry is None:
55+
print("<ID not found>")
56+
continue
57+
print(f"<name='{entry.name}', type='{entry.type}'> [{timestamp}]")
58+
59+
try:
60+
# handle systemTime specially
61+
if entry.name == "systemTime" and entry.type == "int64":
62+
dt = datetime.fromtimestamp(record.getInteger() / 1000000)
63+
print(" {:%Y-%m-%d %H:%M:%S.%f}".format(dt))
64+
continue
65+
66+
if entry.type == "double":
67+
print(f" {record.getDouble()}")
68+
elif entry.type == "int64":
69+
print(f" {record.getInteger()}")
70+
elif entry.type == "string" or entry.type == "json":
71+
print(f" '{record.getString()}'")
72+
elif entry.type == "boolean":
73+
print(f" {record.getBoolean()}")
74+
elif entry.type == "boolean[]":
75+
arr = record.getBooleanArray()
76+
print(f" {arr}")
77+
elif entry.type == "double[]":
78+
arr = record.getDoubleArray()
79+
print(f" {arr}")
80+
elif entry.type == "float[]":
81+
arr = record.getFloatArray()
82+
print(f" {arr}")
83+
elif entry.type == "int64[]":
84+
arr = record.getIntegerArray()
85+
print(f" {arr}")
86+
elif entry.type == "string[]":
87+
arr = record.getStringArray()
88+
print(f" {arr}")
89+
except TypeError as e:
90+
print(" invalid", e)

gen/DataLogReader.yml

Lines changed: 204 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,20 @@ classes:
55
subpackage: log
66
attributes:
77
entry:
8+
access: readonly
89
name:
10+
access: readonly
911
type:
12+
access: readonly
1013
metadata:
14+
access: readonly
1115
MetadataRecordData:
1216
subpackage: log
1317
attributes:
1418
entry:
19+
access: readonly
1520
metadata:
21+
access: readonly
1622
DataLogRecord:
1723
subpackage: log
1824
methods:
@@ -21,63 +27,243 @@ classes:
2127
"":
2228
ignore: true
2329
int, int64_t, wpi::span<const uint8_t>:
30+
ignore: true
2431
GetEntry:
2532
GetTimestamp:
2633
GetSize:
2734
GetRaw:
35+
no_release_gil: true
36+
cpp_code: |
37+
[](const DataLogRecord *self) {
38+
auto data = self->GetRaw();
39+
return py::bytes((char*)data.data(), data.size());
40+
}
2841
IsControl:
2942
IsStart:
3043
IsFinish:
3144
IsSetMetadata:
3245
GetStartData:
46+
no_release_gil: true
3347
param_override:
3448
out:
35-
force_out: true
36-
default:
49+
ignore: true
50+
doc: |
51+
Decodes a start control record. Raises TypeError on error.
52+
cpp_code: |
53+
[](const DataLogRecord *self) {
54+
auto ptr = std::make_unique<wpi::log::StartRecordData>();
55+
if (!self->GetStartData(ptr.get())) {
56+
throw py::type_error("not a start record");
57+
}
58+
return std::move(ptr);
59+
}
60+
return_value_policy: reference_internal
3761
GetFinishEntry:
62+
no_release_gil: true
3863
param_override:
3964
out:
40-
force_out: true
41-
default:
65+
ignore: true
66+
doc: |
67+
Decodes a finish control record. Raises TypeError on error.
68+
cpp_code: |
69+
[](const DataLogRecord *self) {
70+
int value;
71+
if (!self->GetFinishEntry(&value)) {
72+
throw py::type_error("not a finish entry");
73+
}
74+
return value;
75+
}
4276
GetSetMetadataData:
77+
no_release_gil: true
4378
param_override:
4479
out:
45-
force_out: true
46-
default:
80+
ignore: true
81+
doc: |
82+
Decodes a set metadata control record. Raises TypeError on error.
83+
cpp_code: |
84+
[](const DataLogRecord *self) {
85+
auto ptr = std::make_unique<wpi::log::MetadataRecordData>();
86+
if (!self->GetSetMetadataData(ptr.get())) {
87+
throw py::type_error("not a metadata control record");
88+
}
89+
return std::move(ptr);
90+
}
91+
return_value_policy: reference_internal
4792
GetBoolean:
93+
no_release_gil: true
94+
param_override:
95+
value:
96+
ignore: true
97+
doc: |
98+
Decodes a data record as a boolean. Note if the data type (as indicated in
99+
the corresponding start control record for this entry) is not "boolean",
100+
invalid results may be returned or TypeError will be raised.
101+
cpp_code: |
102+
[](const DataLogRecord *self) {
103+
bool value;
104+
if (!self->GetBoolean(&value)) {
105+
throw py::type_error("not a boolean");
106+
}
107+
return value;
108+
}
48109
GetInteger:
110+
no_release_gil: true
111+
param_override:
112+
value:
113+
ignore: true
114+
doc: |
115+
Decodes a data record as an integer. Note if the data type (as indicated in
116+
the corresponding start control record for this entry) is not "int64",
117+
invalid results may be returned or TypeError will be raised.
118+
cpp_code: |
119+
[](const DataLogRecord *self) {
120+
int64_t value;
121+
if (!self->GetInteger(&value)) {
122+
throw py::type_error("not an integer");
123+
}
124+
return value;
125+
}
49126
GetFloat:
127+
no_release_gil: true
128+
param_override:
129+
value:
130+
ignore: true
131+
doc: |
132+
Decodes a data record as a float. Note if the data type (as indicated in
133+
the corresponding start control record for this entry) is not "float",
134+
invalid results may be returned or TypeError will be raised.
135+
cpp_code: |
136+
[](const DataLogRecord *self) {
137+
float value;
138+
if (!self->GetFloat(&value)) {
139+
throw py::type_error("not a float");
140+
}
141+
return value;
142+
}
50143
GetDouble:
144+
no_release_gil: true
145+
param_override:
146+
value:
147+
ignore: true
148+
doc: |
149+
Decodes a data record as a double. Note if the data type (as indicated in
150+
the corresponding start control record for this entry) is not "double",
151+
invalid results may be returned or TypeError will be raised.
152+
cpp_code: |
153+
[](const DataLogRecord *self) {
154+
double value;
155+
if (!self->GetDouble(&value)) {
156+
throw py::type_error("not a double");
157+
}
158+
return value;
159+
}
51160
GetString:
161+
no_release_gil: true
52162
param_override:
53163
value:
54-
force_out: true
55-
default:
164+
ignore: true
165+
doc: |
166+
Decodes a data record as a string. Note if the data type (as indicated in
167+
the corresponding start control record for this entry) is not "string",
168+
invalid results may be returned or TypeError will be raised.
169+
cpp_code: |
170+
[](const DataLogRecord *self) {
171+
std::string_view value;
172+
if (!self->GetString(&value)) {
173+
throw py::type_error("not a string");
174+
}
175+
return value;
176+
}
56177
GetBooleanArray:
178+
no_release_gil: true
57179
param_override:
58180
arr:
59-
force_out: true
60-
default:
181+
ignore: true
182+
doc: |
183+
Decodes a data record as a boolean array. Note if the data type (as
184+
indicated in the corresponding start control record for this entry) is not
185+
"boolean[]", invalid results may be returned or a TypeError may be raised.
186+
cpp_code: |
187+
[](const DataLogRecord *self) {
188+
std::vector<int> arr;
189+
if (!self->GetBooleanArray(&arr)) {
190+
throw py::type_error("not a boolean array");
191+
}
192+
py::list l(arr.size());
193+
for (size_t i = 0; i < arr.size(); i++) {
194+
auto b = py::bool_(arr[i]);
195+
PyList_SET_ITEM(l.ptr(), i, b.release().ptr());
196+
}
197+
return std::move(l);
198+
}
61199
GetIntegerArray:
200+
no_release_gil: true
62201
param_override:
63202
arr:
64-
force_out: true
65-
default:
203+
ignore: true
204+
doc: |
205+
Decodes a data record as an integer array. Note if the data type (as
206+
indicated in the corresponding start control record for this entry) is not
207+
"int64[]", invalid results may be returned or a TypeError may be raised.
208+
cpp_code: |
209+
[](const DataLogRecord *self) {
210+
std::vector<int64_t> arr;
211+
if (!self->GetIntegerArray(&arr)) {
212+
throw py::type_error("not an integer array");
213+
}
214+
return std::move(arr);
215+
}
66216
GetFloatArray:
217+
no_release_gil: true
67218
param_override:
68219
arr:
69-
force_out: true
70-
default:
220+
ignore: true
221+
doc: |
222+
Decodes a data record as a float array. Note if the data type (as
223+
indicated in the corresponding start control record for this entry) is not
224+
"float[]", invalid results may be returned or a TypeError may be raised.
225+
cpp_code: |
226+
[](const DataLogRecord *self) {
227+
std::vector<float> arr;
228+
if (!self->GetFloatArray(&arr)) {
229+
throw py::type_error("not a float array");
230+
}
231+
return std::move(arr);
232+
}
71233
GetDoubleArray:
234+
no_release_gil: true
72235
param_override:
73236
arr:
74-
force_out: true
75-
default:
237+
ignore: true
238+
doc: |
239+
Decodes a data record as a double array. Note if the data type (as
240+
indicated in the corresponding start control record for this entry) is not
241+
"double[]", invalid results may be returned or a TypeError may be raised.
242+
cpp_code: |
243+
[](const DataLogRecord *self) {
244+
std::vector<double> arr;
245+
if (!self->GetDoubleArray(&arr)) {
246+
throw py::type_error("not a double array");
247+
}
248+
return std::move(arr);
249+
}
76250
GetStringArray:
251+
no_release_gil: true
77252
param_override:
78253
arr:
79-
force_out: true
80-
default:
254+
ignore: true
255+
doc: |
256+
Decodes a data record as a string array. Note if the data type (as
257+
indicated in the corresponding start control record for this entry) is not
258+
"string[]", invalid results may be returned or a TypeError may be raised.
259+
cpp_code: |
260+
[](const DataLogRecord *self) {
261+
std::vector<std::string_view> arr;
262+
if (!self->GetStringArray(&arr)) {
263+
throw py::type_error("not a string array");
264+
}
265+
return std::move(arr);
266+
}
81267
DataLogIterator:
82268
ignore: true
83269
DataLogReader:
@@ -151,4 +337,3 @@ inline_code: |
151337
.def("__iter__", [](wpi::log::DataLogReader * that) {
152338
return py::make_iterator(that->begin(), that->end());
153339
}, py::keep_alive<0,1>());
154-

0 commit comments

Comments
 (0)