CTRE Phoenix 6 C++ 26.0.0-beta-1
Loading...
Searching...
No Matches
HootReplay.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) Cross The Road Electronics.  All rights reserved.
3 * License information can be found in CTRE_LICENSE.txt
4 * For support and suggestions contact support@ctr-electronics.com or file
5 * an issue tracker at https://github.com/CrossTheRoadElec/Phoenix-Releases
6 */
7#pragma once
8
12#include <units/time.h>
13#include <string>
14#include <vector>
15
16#if __has_include("wpi/struct/Struct.h")
17#include "wpi/struct/Struct.h"
18#endif
19#if __has_include("wpi/protobuf/Protobuf.h")
20#include "wpi/protobuf/Protobuf.h"
21#endif
22
23namespace ctre {
24namespace phoenix6 {
25
26/**
27 * \brief Static class for controlling Phoenix 6 hoot log replay.
28 *
29 * This replays all signals in the given hoot log in simulation. Hoot logs can
30 * be created by a robot program using SignalLogger. Only one hoot log,
31 * corresponding to one CAN bus, may be replayed at a time.
32 *
33 * The signal logger always runs while replay is running. All custom signals written
34 * during replay will be automatically placed under `hoot_replay/`. Additionally, the
35 * log will contain all status signals and custom signals from the original log.
36 *
37 * During replay, all transmits from the robot program are ignored. This includes
38 * features such as control requests, configs, and setting signal update frequency.
39 * Additionally, Tuner X is not functional during log replay.
40 *
41 * To use Hoot Replay, call LoadFile(char const *) before any devices are constructed
42 * to load a hoot file and start replay. Alternatively, the CANBus(std::string_view, char const *)
43 * constructor can be used when constructing devices.
44 *
45 * After devices are constructed, Hoot Replay can be controlled using Play(), Pause(), Stop(),
46 * and Restart(). Additionally, Hoot Replay supports StepTiming(units::second_t) while paused.
47 * The current file can be closed using CloseFile(), after which a new file may be loaded.
48 */
50public:
51 /**
52 * \brief Loads the given file and starts signal log replay. Only one
53 * hoot log, corresponding to one CAN bus, may be replayed at a time.
54 *
55 * This must be called before constructing any devices or checking
56 * CAN bus status. The CANBus(std::string_view, char const *)
57 * constructor can be used when constructing devices to guarantee
58 * that this API is called first.
59 *
60 * When using relative paths, the file path is typically relative
61 * to the top-level folder of the robot project.
62 *
63 * This API is blocking on the file read.
64 *
65 * \param filepath Path and name of the hoot file to load
66 * \returns Status of opening and reading the file for replay
67 * \throws std::invalid_argument - The file is invalid, unlicensed, or
68 * targets a different version of Phoenix 6
69 */
70 static ctre::phoenix::StatusCode LoadFile(char const *filepath);
71 /**
72 * \brief Ends the hoot log replay. This stops the replay if it is running,
73 * closes the hoot log, and clears all signals read from the file.
74 */
75 static void CloseFile();
76 /**
77 * \brief Gets whether a valid hoot log file is currently loaded.
78 *
79 * \returns true if a valid hoot log file is loaded
80 */
81 static bool IsFileLoaded();
82
83 /**
84 * \brief Starts or resumes the hoot log replay.
85 *
86 * \returns Status of starting or resuming replay
87 */
89 /**
90 * \brief Pauses the hoot log replay. This maintains the current position
91 * in the log replay so it can be resumed later.
92 *
93 * \returns Status of pausing replay
94 */
96 /**
97 * \brief Stops the hoot log replay. This resets the current position in
98 * the log replay to the start.
99 *
100 * \returns Status of stopping replay
101 */
103 /**
104 * \brief Restarts the hoot log replay from the start of the log.
105 * This is equivalent to calling #Stop followed by #Play.
106 *
107 * \returns Status of restarting replay
108 */
110 {
111 auto retval = Stop();
112 if (retval.IsOK()) {
113 retval = Play();
114 }
115 return retval;
116 }
117
118 /**
119 * \brief Gets whether hoot log replay is actively playing.
120 *
121 * This API will return true in programs that do not support
122 * replay, making it safe to call without first checking if
123 * the program supports replay.
124 *
125 * \returns true if replay is playing back signals
126 */
127 static bool IsPlaying()
128 {
129 return WaitForPlaying(0_s);
130 }
131
132 /**
133 * \brief Waits until hoot log replay is actively playing.
134 *
135 * This API will immediately return true in programs that do
136 * not support replay, making it safe to call without first
137 * checking if the program supports replay
138 *
139 * Since this can block the calling thread, this should not
140 * be called with a non-zero timeout on the main thread.
141 *
142 * This can also be used with a timeout of 0 to perform
143 * a non-blocking check, which is equivalent to #IsPlaying.
144 *
145 * \param timeout Max time to wait for replay to start playing
146 * \returns true if replay is playing back signals
147 */
148 static bool WaitForPlaying(units::second_t timeout)
149 {
150 return WaitForPlayingImpl(timeout.value());
151 }
152
153 /**
154 * \brief Gets whether hoot log replay has reached the end of the log.
155 *
156 * \returns true if replay has reached the end of the log, or
157 * if no log is currently loaded
158 */
159 static bool IsFinished();
160
161 /**
162 * \brief Sets the speed of the hoot log replay. A speed of 1.0 corresponds
163 * to replaying the file in real time, and larger values increase the speed.
164 *
165 * - Minimum Value: 0.01
166 * - Maximum Value: 100.0
167 * - Default Value: 1.0
168 *
169 * \param speed Speed of the hoot log replay
170 */
171 static void SetSpeed(double speed);
172 /**
173 * \brief Advances the hoot log replay time by the given value. Replay must
174 * be paused or stopped before advancing its time.
175 *
176 * \param stepTimeSeconds The amount of time to advance
177 * \returns Status of advancing the replay time
178 */
179 static ctre::phoenix::StatusCode StepTiming(units::time::second_t stepTimeSeconds)
180 {
181 return StepTimingImpl(stepTimeSeconds.value());
182 }
183
184 /**
185 * \brief Gets a schema-serialized user signal.
186 *
187 * In an FRC robot program, users can call #GetStruct, #GetStructArray,
188 * and #GetProtobuf to directly get schema values instead.
189 *
190 * \param name Name of the signal
191 * \param type Type of the associated schema, such as struct or protobuf
192 * \returns Structure with all information about the signal
193 */
195 {
196 return GetSchemaValueImpl(name, type).ToSignalMeasurement();
197 }
198
199#if __has_include("wpi/struct/Struct.h") || defined(_CTRE_DOCS_)
200 /**
201 * \brief Gets a WPILib Struct user signal.
202 *
203 * \param name Name of the signal
204 * \param info Optional struct type info
205 * \returns Structure with all information about the signal
206 */
207 template <typename T, typename... I>
208 requires wpi::StructSerializable<T, I...>
209 static SignalMeasurement<std::optional<T>> GetStruct(std::string_view name, I const &... info)
210 {
211 auto rawSig = GetSchemaValue(name, HootSchemaType::Struct);
212
213 std::optional<T> value;
214 if (rawSig.status.IsOK()) {
215 if (rawSig.value.size() == wpi::Struct<T>::GetSize(info...)) {
216 value = wpi::Struct<T>::Unpack(rawSig.value, info...);
217 } else {
218 value = std::nullopt;
220 }
221 } else {
222 value = std::nullopt;
223 }
224
225 return {
226 rawSig.name,
227 std::move(value),
228 rawSig.timestamp,
229 std::move(rawSig.units),
230 rawSig.status
231 };
232 }
233 /**
234 * \brief Gets a WPILib Struct array user signal.
235 *
236 * \param name Name of the signal
237 * \param info Optional struct type info
238 * \returns Structure with all information about the signal
239 */
240 template <typename T, typename... I>
241 requires wpi::StructSerializable<T, I...>
242 static SignalMeasurement<std::optional<std::vector<T>>> GetStructArray(std::string_view name, I const &... info)
243 {
244 auto rawSig = GetSchemaValue(name, HootSchemaType::Struct);
245
246 std::optional<std::vector<T>> value;
247 if (rawSig.status.IsOK()) {
248 std::span<uint8_t const> data{rawSig.value};
249 auto const size = wpi::Struct<T>::GetSize(info...);
250 size_t const arr_size = data.size() / size;
251
252 if (arr_size * size == data.size()) {
253 std::vector<T> arr;
254 arr.reserve(arr_size);
255 for (size_t i = 0; i < arr_size; ++i) {
256 arr.emplace_back(wpi::UnpackStruct<T>(data, info...));
257 data = data.subspan(size);
258 }
259 value = std::move(arr);
260 } else {
261 value = std::nullopt;
263 }
264 } else {
265 value = std::nullopt;
266 }
267
268 return {
269 rawSig.name,
270 std::move(value),
271 rawSig.timestamp,
272 std::move(rawSig.units),
273 rawSig.status
274 };
275 }
276#endif
277
278#if __has_include("wpi/protobuf/Protobuf.h") || defined(_CTRE_DOCS_)
279 /**
280 * \brief Gets a Protobuf user signal.
281 *
282 * \param name Name of the signal
283 * \returns Structure with all information about the signal
284 */
285 template <wpi::ProtobufSerializable T>
286 static SignalMeasurement<std::optional<T>> GetProtobuf(std::string_view name)
287 {
288 auto rawSig = GetSchemaValue(name, HootSchemaType::Protobuf);
289 return {
290 rawSig.name,
291 rawSig.status.IsOK()
292 ? wpi::ProtobufMessage<T>{}.Unpack(rawSig.value)
293 : std::nullopt,
294 rawSig.timestamp,
295 std::move(rawSig.units),
296 rawSig.status
297 };
298 }
299#endif
300
301 /**
302 * \brief Gets a raw-bytes user signal.
303 *
304 * \param name Name of the signal
305 * \returns Structure with all information about the signal
306 */
307 static SignalMeasurement<std::vector<uint8_t>> GetRaw(std::string_view name)
308 {
309 return GetRawImpl(name).ToSignalMeasurement();
310 }
311 /**
312 * \brief Gets a boolean user signal.
313 *
314 * \param name Name of the signal
315 * \returns Structure with all information about the signal
316 */
317 static SignalMeasurement<bool> GetBoolean(std::string_view name)
318 {
319 return GetBooleanImpl(name).ToSignalMeasurement();
320 }
321 /**
322 * \brief Gets an integer user signal.
323 *
324 * \param name Name of the signal
325 * \returns Structure with all information about the signal
326 */
327 static SignalMeasurement<int64_t> GetInteger(std::string_view name)
328 {
329 return GetIntegerImpl(name).ToSignalMeasurement();
330 }
331 /**
332 * \brief Gets a float user signal.
333 *
334 * \param name Name of the signal
335 * \returns Structure with all information about the signal
336 */
337 static SignalMeasurement<float> GetFloat(std::string_view name)
338 {
339 return GetFloatImpl(name).ToSignalMeasurement();
340 }
341 /**
342 * \brief Gets a double user signal.
343 *
344 * \param name Name of the signal
345 * \returns Structure with all information about the signal
346 */
347 static SignalMeasurement<double> GetDouble(std::string_view name)
348 {
349 return GetDoubleImpl(name).ToSignalMeasurement();
350 }
351
352 /**
353 * \brief Gets a unit value user signal.
354 *
355 * \param name Name of the signal
356 * \returns Structure with all information about the signal
357 */
358 template <typename U>
359 requires units::traits::is_unit_t_v<U>
360 static SignalMeasurement<U> GetValue(std::string_view name)
361 {
362 SignalMeasurement<double> doubleSig = GetDouble(name);
363 return {
364 doubleSig.name,
365 U{doubleSig.value},
366 doubleSig.timestamp,
367 std::move(doubleSig.units),
368 doubleSig.status
369 };
370 }
371
372 /**
373 * \brief Gets a string user signal.
374 *
375 * \param name Name of the signal
376 * \returns Structure with all information about the signal
377 */
378 static SignalMeasurement<std::string> GetString(std::string_view name)
379 {
380 return GetStringImpl(name).ToSignalMeasurement();
381 }
382 /**
383 * \brief Get a boolean array user signal.
384 *
385 * \param name Name of the signal
386 * \returns Structure with all information about the signal
387 */
389 {
390 return GetBooleanArrayImpl(name).ToSignalMeasurement();
391 }
392 /**
393 * \brief Get an integer array user signal.
394 *
395 * \param name Name of the signal
396 * \returns Structure with all information about the signal
397 */
399 {
400 return GetIntegerArrayImpl(name).ToSignalMeasurement();
401 }
402 /**
403 * \brief Get a float array user signal.
404 *
405 * \param name Name of the signal
406 * \returns Structure with all information about the signal
407 */
409 {
410 return GetFloatArrayImpl(name).ToSignalMeasurement();
411 }
412 /**
413 * \brief Get a double array user signal.
414 *
415 * \param name Name of the signal
416 * \returns Structure with all information about the signal
417 */
419 {
420 return GetDoubleArrayImpl(name).ToSignalMeasurement();
421 }
422 /**
423 * \brief Get a string array user signal.
424 *
425 * \param name Name of the signal
426 * \returns Structure with all information about the signal
427 */
429 {
430 return GetStringArrayImpl(name).ToSignalMeasurement();
431 }
432
433private:
434 static bool WaitForPlayingImpl(double timeoutSeconds);
435 static ctre::phoenix::StatusCode StepTimingImpl(double stepTimeSeconds);
436
437 template <typename T>
438 struct UnitlessSignalData {
439 std::string_view name;
440 T value;
441 double timestampSec;
442 std::string units;
444
445 SignalMeasurement<T> ToSignalMeasurement() &&
446 {
447 return {
448 name,
449 std::move(value),
450 timestampSec * 1_s,
451 std::move(units),
452 status
453 };
454 }
455 };
456
457 static UnitlessSignalData<std::vector<uint8_t>> GetSchemaValueImpl(std::string_view name, HootSchemaType type);
458 static UnitlessSignalData<std::vector<uint8_t>> GetRawImpl(std::string_view name);
459 static UnitlessSignalData<bool> GetBooleanImpl(std::string_view name);
460 static UnitlessSignalData<int64_t> GetIntegerImpl(std::string_view name);
461 static UnitlessSignalData<float> GetFloatImpl(std::string_view name);
462 static UnitlessSignalData<double> GetDoubleImpl(std::string_view name);
463 static UnitlessSignalData<std::string> GetStringImpl(std::string_view name);
464 static UnitlessSignalData<std::vector<uint8_t>> GetBooleanArrayImpl(std::string_view name);
465 static UnitlessSignalData<std::vector<int64_t>> GetIntegerArrayImpl(std::string_view name);
466 static UnitlessSignalData<std::vector<float>> GetFloatArrayImpl(std::string_view name);
467 static UnitlessSignalData<std::vector<double>> GetDoubleArrayImpl(std::string_view name);
468 static UnitlessSignalData<std::vector<std::string>> GetStringArrayImpl(std::string_view name);
469};
470
471}
472}
Static class for controlling Phoenix 6 hoot log replay.
Definition HootReplay.hpp:49
static ctre::phoenix::StatusCode Play()
Starts or resumes the hoot log replay.
static SignalMeasurement< std::vector< int64_t > > GetIntegerArray(std::string_view name)
Get an integer array user signal.
Definition HootReplay.hpp:398
static SignalMeasurement< std::vector< float > > GetFloatArray(std::string_view name)
Get a float array user signal.
Definition HootReplay.hpp:408
static ctre::phoenix::StatusCode Stop()
Stops the hoot log replay.
static SignalMeasurement< std::vector< double > > GetDoubleArray(std::string_view name)
Get a double array user signal.
Definition HootReplay.hpp:418
static bool IsFileLoaded()
Gets whether a valid hoot log file is currently loaded.
static void CloseFile()
Ends the hoot log replay.
static void SetSpeed(double speed)
Sets the speed of the hoot log replay.
static bool WaitForPlaying(units::second_t timeout)
Waits until hoot log replay is actively playing.
Definition HootReplay.hpp:148
static SignalMeasurement< int64_t > GetInteger(std::string_view name)
Gets an integer user signal.
Definition HootReplay.hpp:327
static ctre::phoenix::StatusCode StepTiming(units::time::second_t stepTimeSeconds)
Advances the hoot log replay time by the given value.
Definition HootReplay.hpp:179
static SignalMeasurement< std::optional< std::vector< T > > > GetStructArray(std::string_view name, I const &... info)
Gets a WPILib Struct array user signal.
Definition HootReplay.hpp:242
static bool IsPlaying()
Gets whether hoot log replay is actively playing.
Definition HootReplay.hpp:127
static SignalMeasurement< std::optional< T > > GetStruct(std::string_view name, I const &... info)
Gets a WPILib Struct user signal.
Definition HootReplay.hpp:209
static ctre::phoenix::StatusCode LoadFile(char const *filepath)
Loads the given file and starts signal log replay.
static SignalMeasurement< std::vector< uint8_t > > GetSchemaValue(std::string_view name, HootSchemaType type)
Gets a schema-serialized user signal.
Definition HootReplay.hpp:194
static SignalMeasurement< U > GetValue(std::string_view name)
Gets a unit value user signal.
Definition HootReplay.hpp:360
static SignalMeasurement< std::vector< uint8_t > > GetRaw(std::string_view name)
Gets a raw-bytes user signal.
Definition HootReplay.hpp:307
static ctre::phoenix::StatusCode Restart()
Restarts the hoot log replay from the start of the log.
Definition HootReplay.hpp:109
static SignalMeasurement< std::string > GetString(std::string_view name)
Gets a string user signal.
Definition HootReplay.hpp:378
static SignalMeasurement< std::optional< T > > GetProtobuf(std::string_view name)
Gets a Protobuf user signal.
Definition HootReplay.hpp:286
static bool IsFinished()
Gets whether hoot log replay has reached the end of the log.
static SignalMeasurement< bool > GetBoolean(std::string_view name)
Gets a boolean user signal.
Definition HootReplay.hpp:317
static SignalMeasurement< std::vector< uint8_t > > GetBooleanArray(std::string_view name)
Get a boolean array user signal.
Definition HootReplay.hpp:388
static SignalMeasurement< double > GetDouble(std::string_view name)
Gets a double user signal.
Definition HootReplay.hpp:347
static SignalMeasurement< float > GetFloat(std::string_view name)
Gets a float user signal.
Definition HootReplay.hpp:337
static SignalMeasurement< std::vector< std::string > > GetStringArray(std::string_view name)
Get a string array user signal.
Definition HootReplay.hpp:428
static ctre::phoenix::StatusCode Pause()
Pauses the hoot log replay.
Status codes reported by APIs, including OK, warnings, and errors.
Definition StatusCodes.h:28
static constexpr int InvalidParamValue
An invalid argument was passed into the function/VI, such as a null pointer.
Definition StatusCodes.h:369
HootSchemaType
Supported schema types for a hoot user signal.
Definition HootSchemaType.hpp:15
@ Struct
Serialize using the WPILib Struct format.
@ Protobuf
Serialize using the Protobuf format.
Definition motor_constants.h:14
Information from a single measurement of a status signal.
Definition SignalMeasurement.hpp:20
T value
The value of the signal.
Definition SignalMeasurement.hpp:28
std::string units
The units of the signal measurement.
Definition SignalMeasurement.hpp:36
ctre::phoenix::StatusCode status
Status code response of getting the data.
Definition SignalMeasurement.hpp:40
units::time::second_t timestamp
Timestamp of when the data point was taken.
Definition SignalMeasurement.hpp:32
std::string_view name
The name of the signal.
Definition SignalMeasurement.hpp:24