CTRE Phoenix 6 C++ 25.0.0-beta-4
Loading...
Searching...
No Matches
StatusSignal.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
15#include <array>
16#include <functional>
17#include <initializer_list>
18#include <map>
19#include <ostream>
20#include <sstream>
21#include <string>
22#include <units/base.h>
23#include <units/frequency.h>
24#include <units/time.h>
25
26namespace ctre {
27namespace phoenix6 {
28
29 namespace hardware {
30 class ParentDevice;
31 }
32
33 template <typename T>
34 class StatusSignal;
35
36 /**
37 * \brief Class that provides operations to
38 * retrieve information about a status signal.
39 */
41 {
42 friend hardware::ParentDevice; // Allow ParentDevice to set error
43
44 protected:
46 uint16_t spn;
47 std::string units;
49 double baseValue = 0;
51 std::string signalName;
52
53 std::function<void()> _checkFirmVersFunction;
54
55 units::time::second_t _lastTimestamp{0_s};
56
57 template <typename T>
58 friend class StatusSignal;
59
62 uint16_t spn,
63 std::string signalName,
64 std::function<void()> checkFirmVersFunction
65 ) :
67 spn{spn},
70 _checkFirmVersFunction{std::move(checkFirmVersFunction)}
71 {
72 }
73
74 /* Constructor for an invalid BaseStatusSignal */
76 deviceIdentifier{hardware::DeviceIdentifier{}},
77 spn{0},
78 error{error},
79 signalName{"Invalid"},
81 {
82 }
83
84 virtual void UpdateUnits(uint16_t spn) = 0;
85
86 /**
87 * \brief Wait for multiple signals to arrive
88 *
89 * \param signals Signals to wait for
90 * \param count Number of signals
91 * \param network Network to wait for the signals on
92 * \param timeoutSeconds Maximum time to wait for all these signals
93 * \returns Status of the wait
94 */
96 BaseStatusSignal* const *signals,
97 size_t count,
98 const char *network,
99 double timeoutSeconds);
100
101 static ctre::phoenix::StatusCode Status_SetUpdateFrequency(const char *canbus, uint32_t deviceHash, uint16_t spn, double frequencyHz, double timeoutSeconds);
102 static ctre::phoenix::StatusCode Status_SetUpdateFrequencyForAll(BaseStatusSignal* const *signals, size_t count, double frequencyHz, double timeoutSeconds);
103 static double Status_GetAppliedUpdateFrequency(const char *canbus, uint32_t deviceHash, uint16_t spn);
104
105 /**
106 * \brief Gets signal units.
107 *
108 * \param signal Signal to get
109 * \returns Units of the signal
110 */
111 static std::string Status_GetUnits(uint32_t signal);
112
113 /**
114 * \brief Get signal update. Caller either get last received, or wait for an update.
115 *
116 * \param signal Signal to update
117 * \param network Name of bus (can, canfd, canivore-name, future protocols)
118 * \param bWaitForUpdate If true, API will wait up to timeoutSeconds for an update. If false, routine will poll last received and use timeoutSeconds to return error code if too old.
119 * \param timeoutSeconds How long to wait or how old the signal can be before error'ing
120 * \returns Status of the get
121 */
123 BaseStatusSignal &signal,
124 const char *network,
125 bool bWaitForUpdate,
126 double timeoutSeconds);
127
128 /**
129 * \brief Implementation of the WaitForAll API.
130 *
131 * \tparam Arr \c std::array or \c std::vector of BaseStatusSignal*
132 * \param location Location of the calling function
133 * \param timeoutSeconds Maximum time to wait for all the signals
134 * \param signals Signals to wait on
135 * \return Status of the wait
136 */
137 template <typename Arr>
138 static ctre::phoenix::StatusCode WaitForAllImpl(const char *location, units::time::second_t timeoutSeconds, const Arr &signals)
139 {
140 if (signals.size() < 1)
141 {
142 /* We don't have any signals to wait for, so return early */
144 c_ctre_phoenix_report_error(true, retval, 0,
145 retval.GetDescription(),
146 location,
148 return retval;
149 }
150 const std::string &network = signals[0]->deviceIdentifier.network;
151
152 for (auto signal : signals)
153 {
154 /* Check that they all have the same network */
155 if (network != signal->deviceIdentifier.network)
156 {
157 ctre::phoenix::StatusCode retval = ctre::phoenix::StatusCode::InvalidNetwork; // Networks don't match, return early
158
159 c_ctre_phoenix_report_error(true, retval, 0,
160 retval.GetDescription(),
161 location,
163 return retval;
164 }
165 }
166
167 /* Report if any device firmware versions are too old */
168 for (auto signal : signals)
169 {
170 signal->_checkFirmVersFunction();
171 }
172
173 /* Now wait for all the signals */
174 ctre::phoenix::StatusCode retval = Status_WaitForAll(signals.data(), signals.size(), network.c_str(), timeoutSeconds.to<double>());
175
176 /* error reporting */
177 if (false == retval.IsOK())
178 {
179 c_ctre_phoenix_report_error(retval.IsError(), retval, 0,
180 retval.GetDescription(),
181 location,
183 }
184 return retval;
185 }
186
187 /**
188 * \brief Type trait to verify that all types passed in are subclasses of BaseStatusSignal.
189 */
190 template <typename... Signals>
192 std::conjunction<std::is_base_of<BaseStatusSignal, std::remove_reference_t<Signals>>...>
193 {};
194
195 /**
196 * \brief Whether all types passed in are subclasses of BaseStatusSignal.
197 */
198 template <typename... Signals>
199 static constexpr bool is_all_status_signal_v = is_all_status_signal<Signals...>::value;
200
201 public:
202 virtual ~BaseStatusSignal() = 0; // Declare virtual destructor to make this class abstract
203
204 /**
205 * \brief Waits for new data on all provided signals up to timeout.
206 * This API is typically used with CANivore Bus signals as they will be synced using the
207 * CANivore Timesync feature and arrive simultaneously. Signals on a roboRIO bus cannot
208 * be synced and may require a significantly longer blocking call to receive all signals.
209 *
210 * Note that CANivore Timesync requires Phoenix Pro.
211 *
212 * This can also be used with a timeout of zero to refresh many signals at once, which
213 * is faster than calling Refresh() on every signal. This is equivalent to calling #RefreshAll.
214 *
215 * If a signal arrives multiple times while waiting, such as when *not* using CANivore
216 * Timesync, the newest signal data is fetched. Additionally, if this function times out,
217 * the newest signal data is fetched for all signals (when possible). We recommend checking
218 * the individual status codes using GetStatus() when this happens.
219 *
220 * \param timeoutSeconds Maximum time to wait for all the signals to arrive.
221 * Pass zero to refresh all signals without blocking.
222 * \param signals Signals to wait on, passed as a comma-separated list of signal references.
223 * \return An InvalidParamValue if signals array is empty,
224 * InvalidNetwork if signals are on different CAN bus networks,
225 * RxTimeout if it took longer than timeoutSeconds to receive all the signals,
226 * MultiSignalNotSupported if using the roboRIO bus with more than one signal and a non-zero timeout.
227 * An OK status code means that all signals arrived within timeoutSeconds and they are all OK.
228 *
229 * Any other value represents the StatusCode of the first failed signal.
230 * Call GetStatus() on each signal to determine which ones failed.
231 */
232 template <typename... Signals, typename = std::enable_if_t<is_all_status_signal_v<Signals...>>>
233 static ctre::phoenix::StatusCode WaitForAll(units::time::second_t timeoutSeconds, Signals &... signals)
234 {
235 return WaitForAll(timeoutSeconds,
236 std::array<BaseStatusSignal *, sizeof...(Signals)>{(&signals)...});
237 }
238 /**
239 * \brief Waits for new data on all provided signals up to timeout.
240 * This API is typically used with CANivore Bus signals as they will be synced using the
241 * CANivore Timesync feature and arrive simultaneously. Signals on a roboRIO bus cannot
242 * be synced and may require a significantly longer blocking call to receive all signals.
243 *
244 * Note that CANivore Timesync requires Phoenix Pro.
245 *
246 * This can also be used with a timeout of zero to refresh many signals at once, which
247 * is faster than calling Refresh() on every signal. This is equivalent to calling #RefreshAll.
248 *
249 * If a signal arrives multiple times while waiting, such as when *not* using CANivore
250 * Timesync, the newest signal data is fetched. Additionally, if this function times out,
251 * the newest signal data is fetched for all signals (when possible). We recommend checking
252 * the individual status codes using GetStatus() when this happens.
253 *
254 * \param timeoutSeconds Maximum time to wait for all the signals to arrive.
255 * Pass zero to refresh all signals without blocking.
256 * \param signals Signals to wait on, passed as a vector or initializer list of signal addresses.
257 * \return An InvalidParamValue if signals array is empty,
258 * InvalidNetwork if signals are on different CAN bus networks,
259 * RxTimeout if it took longer than timeoutSeconds to receive all the signals,
260 * MultiSignalNotSupported if using the roboRIO bus with more than one signal and a non-zero timeout.
261 * An OK status code means that all signals arrived within timeoutSeconds and they are all OK.
262 *
263 * Any other value represents the StatusCode of the first failed signal.
264 * Call GetStatus() on each signal to determine which ones failed.
265 */
266 static ctre::phoenix::StatusCode WaitForAll(units::time::second_t timeoutSeconds, const std::vector<BaseStatusSignal *> &signals)
267 {
268 /* static string for location */
269 constexpr char kLocation[] = "ctre::phoenix6::BaseStatusSignal::WaitForAll";
270 return WaitForAllImpl(kLocation, timeoutSeconds, signals);
271 }
272 /**
273 * \brief Waits for new data on all provided signals up to timeout.
274 * This API is typically used with CANivore Bus signals as they will be synced using the
275 * CANivore Timesync feature and arrive simultaneously. Signals on a roboRIO bus cannot
276 * be synced and may require a significantly longer blocking call to receive all signals.
277 *
278 * Note that CANivore Timesync requires Phoenix Pro.
279 *
280 * This can also be used with a timeout of zero to refresh many signals at once, which
281 * is faster than calling Refresh() on every signal. This is equivalent to calling #RefreshAll.
282 *
283 * If a signal arrives multiple times while waiting, such as when *not* using CANivore
284 * Timesync, the newest signal data is fetched. Additionally, if this function times out,
285 * the newest signal data is fetched for all signals (when possible). We recommend checking
286 * the individual status codes using GetStatus() when this happens.
287 *
288 * \param timeoutSeconds Maximum time to wait for all the signals to arrive.
289 * Pass zero to refresh all signals without blocking.
290 * \param signals Signals to wait on, passed as an array of signal addresses.
291 * \return An InvalidParamValue if signals array is empty,
292 * InvalidNetwork if signals are on different CAN bus networks,
293 * RxTimeout if it took longer than timeoutSeconds to receive all the signals,
294 * MultiSignalNotSupported if using the roboRIO bus with more than one signal and a non-zero timeout.
295 * An OK status code means that all signals arrived within timeoutSeconds and they are all OK.
296 *
297 * Any other value represents the StatusCode of the first failed signal.
298 * Call GetStatus() on each signal to determine which ones failed.
299 */
300 template <size_t N>
301 static ctre::phoenix::StatusCode WaitForAll(units::time::second_t timeoutSeconds, const std::array<BaseStatusSignal *, N> &signals)
302 {
303 /* static string for location */
304 constexpr char kLocation[] = "ctre::phoenix6::BaseStatusSignal::WaitForAll";
305 return WaitForAllImpl(kLocation, timeoutSeconds, signals);
306 }
307
308 /**
309 * \brief Performs a non-blocking refresh on all provided signals.
310 *
311 * This provides a performance improvement over separately calling Refresh() on each signal.
312 *
313 * \param signals Signals to refresh, passed as a comma-separated list of signal references.
314 * \return An InvalidParamValue if signals array is empty,
315 * InvalidNetwork if signals are on different CAN bus networks.
316 * An OK status code means that all signals are OK.
317 *
318 * Any other value represents the StatusCode of the first failed signal.
319 * Call GetStatus() on each signal to determine which ones failed.
320 */
321 template <typename... Signals, typename = std::enable_if_t<is_all_status_signal_v<Signals...>>>
322 static ctre::phoenix::StatusCode RefreshAll(Signals &... signals)
323 {
324 return RefreshAll(std::array<BaseStatusSignal *, sizeof...(Signals)>{(&signals)...});
325 }
326 /**
327 * \brief Performs a non-blocking refresh on all provided signals.
328 *
329 * This provides a performance improvement over separately calling Refresh() on each signal.
330 *
331 * \param signals Signals to refresh, passed as a vector or initializer list of signal addresses.
332 * \return An InvalidParamValue if signals array is empty,
333 * InvalidNetwork if signals are on different CAN bus networks.
334 * An OK status code means that all signals are OK.
335 *
336 * Any other value represents the StatusCode of the first failed signal.
337 * Call GetStatus() on each signal to determine which ones failed.
338 */
339 static ctre::phoenix::StatusCode RefreshAll(const std::vector<BaseStatusSignal *> &signals)
340 {
341 /* static string for location */
342 constexpr char kLocation[] = "ctre::phoenix6::BaseStatusSignal::RefreshAll";
343 return WaitForAllImpl(kLocation, 0_s, signals);
344 }
345 /**
346 * \brief Performs a non-blocking refresh on all provided signals.
347 *
348 * This provides a performance improvement over separately calling Refresh() on each signal.
349 *
350 * \param signals Signals to refresh, passed as an array of signal addresses.
351 * \return An InvalidParamValue if signals array is empty,
352 * InvalidNetwork if signals are on different CAN bus networks.
353 * An OK status code means that all signals are OK.
354 *
355 * Any other value represents the StatusCode of the first failed signal.
356 * Call GetStatus() on each signal to determine which ones failed.
357 */
358 template <size_t N>
359 static ctre::phoenix::StatusCode RefreshAll(const std::array<BaseStatusSignal *, N> &signals)
360 {
361 /* static string for location */
362 constexpr char kLocation[] = "ctre::phoenix6::BaseStatusSignal::RefreshAll";
363 return WaitForAllImpl(kLocation, 0_s, signals);
364 }
365
366 /**
367 * \brief Performs latency compensation on signal using the signalSlope and signal's latency to determine
368 * the magnitude of compensation. The caller must refresh these StatusSignals beforehand;
369 * this function only does the math required for latency compensation.
370 *
371 * \details Example usage:
372 * \code
373 * units::turn_t compensatedTurns = BaseStatusSignal::GetLatencyCompensatedValue(fx.GetPosition(), fx.GetVelocity());
374 * \endcode
375 *
376 * \tparam U Type of signal's underlying type. This is the type that the function will return.
377 * \tparam U_PER_SEC Type of signalSlope's underlying type. This must be the derivative of U.
378 * \param signal Signal to be latency compensated. Caller must make sure this signal is up to date
379 * either by calling \c Refresh() or \c WaitForUpdate().
380 * \param signalSlope Derivative of signal that informs compensation magnitude. Caller must make sure this
381 * signal is up to date either by calling \c Refresh() or \c WaitForUpdate().
382 * \param maxLatencySeconds The maximum amount of latency to compensate for in seconds. A negative or zero
383 * value disables the max latency cap. This is used to cap the contribution of
384 * latency compensation for stale signals, such as after the device has been
385 * disconnected from the CAN bus.
386 * \returns Latency compensated value from the signal StatusSignal.
387 */
388 template <typename U, typename U_PER_SEC>
389 static U GetLatencyCompensatedValue(StatusSignal<U> &signal, StatusSignal<U_PER_SEC> &signalSlope, units::time::second_t maxLatencySeconds = 0.300_s)
390 {
391 static_assert(units::traits::is_unit_t_v<U>, "signal must be a unit signal");
392 static_assert(units::traits::is_unit_t_v<U_PER_SEC>, "signalSlope must be a unit signal");
393 using lhs = typename units::traits::unit_t_traits<U>::unit_type;
394 using rhs = typename units::traits::unit_t_traits<U_PER_SEC>::unit_type;
395 static_assert(units::traits::is_convertible_unit_v<lhs, units::compound_unit<rhs, units::seconds>>,
396 "Compensation can only be performed on a signal with its derivative");
397
398 const U nonCompensatedSignal = signal.GetValue();
399 const U_PER_SEC changeInSignal = signalSlope.GetValue();
400 units::second_t latency = signal.GetTimestamp().GetLatency();
401 if (maxLatencySeconds > 0_s && latency > maxLatencySeconds) {
402 latency = maxLatencySeconds;
403 }
404 return nonCompensatedSignal + (changeInSignal * latency);
405 }
406
407 /**
408 * \brief Checks if all signals have an OK error code.
409 *
410 * \param signals Signals to check error code of, passed as a comma-separated list of signal references.
411 * \returns True if all signals are OK, false otherwise
412 */
413 template <typename... Signals, typename = std::enable_if_t<is_all_status_signal_v<Signals...>>>
414 static bool IsAllGood(Signals &... signals)
415 {
416 return IsAllGood(std::array<BaseStatusSignal *, sizeof...(Signals)>{(&signals)...});
417 }
418 /**
419 * \brief Checks if all signals have an OK error code.
420 *
421 * \param signals Signals to check error code of, passed as a vector or initializer list of signal addresses.
422 * \returns True if all signals are OK, false otherwise
423 */
424 static bool IsAllGood(const std::vector<BaseStatusSignal *> &signals)
425 {
426 for (auto signal : signals)
427 {
428 if (!signal->GetStatus().IsOK()) {
429 return false;
430 }
431 }
432 return true;
433 }
434 /**
435 * \brief Checks if all signals have an OK error code.
436 *
437 * \param signals Signals to check error code of, passed as an array of signal addresses.
438 * \returns True if all signals are OK, false otherwise
439 */
440 template <size_t N>
441 static bool IsAllGood(const std::array<BaseStatusSignal *, N> &signals)
442 {
443 for (auto signal : signals)
444 {
445 if (!signal->GetStatus().IsOK()) {
446 return false;
447 }
448 }
449 return true;
450 }
451
452 /**
453 * \brief Sets the update frequency of all specified status signals to the provided common frequency.
454 *
455 * A frequency of 0 Hz will turn off the signal. Otherwise, the minimum supported signal frequency
456 * is 4 Hz, and the maximum is 1000 Hz.
457 *
458 * If other StatusSignals in the same status frame have been set to an update frequency,
459 * the fastest requested update frequency will be applied to the frame.
460 *
461 * This will wait up to 0.100 seconds (100ms) for each signal.
462 *
463 * \param frequencyHz Rate to publish the signal in Hz.
464 * \param signals Signals to apply the update frequency to, passed as a comma-separated list of signal references.
465 * \returns Status code of the first failed update frequency set call, or OK if all succeeded
466 */
467 template <typename... Signals, typename = std::enable_if_t<is_all_status_signal_v<Signals...>>>
468 static ctre::phoenix::StatusCode SetUpdateFrequencyForAll(units::frequency::hertz_t frequencyHz, Signals &... signals)
469 {
470 return SetUpdateFrequencyForAll(frequencyHz, std::array<BaseStatusSignal *, sizeof...(Signals)>{(&signals)...});
471 }
472 /**
473 * \brief Sets the update frequency of all specified status signals to the provided common frequency.
474 *
475 * A frequency of 0 Hz will turn off the signal. Otherwise, the minimum supported signal frequency
476 * is 4 Hz, and the maximum is 1000 Hz.
477 *
478 * If other StatusSignals in the same status frame have been set to an update frequency,
479 * the fastest requested update frequency will be applied to the frame.
480 *
481 * This will wait up to 0.100 seconds (100ms) for each signal.
482 *
483 * \param frequencyHz Rate to publish the signal in Hz.
484 * \param signals Signals to apply the update frequency to, passed as a vector or initializer list of signal addresses.
485 * \returns Status code of the first failed update frequency set call, or OK if all succeeded
486 */
487 static ctre::phoenix::StatusCode SetUpdateFrequencyForAll(units::frequency::hertz_t frequencyHz, const std::vector<BaseStatusSignal *> &signals)
488 {
489 return Status_SetUpdateFrequencyForAll(signals.data(), signals.size(), frequencyHz.to<double>(), 0.100);
490 }
491 /**
492 * \brief Sets the update frequency of all specified status signals to the provided common frequency.
493 *
494 * A frequency of 0 Hz will turn off the signal. Otherwise, the minimum supported signal frequency
495 * is 4 Hz, and the maximum is 1000 Hz.
496 *
497 * If other StatusSignals in the same status frame have been set to an update frequency,
498 * the fastest requested update frequency will be applied to the frame.
499 *
500 * This will wait up to 0.100 seconds (100ms) for each signal.
501 *
502 * \param frequencyHz Rate to publish the signal in Hz.
503 * \param signals Signals to apply the update frequency to, passed as an array of signal addresses.
504 * \returns Status code of the first failed update frequency set call, or OK if all succeeded
505 */
506 template <size_t N>
507 static ctre::phoenix::StatusCode SetUpdateFrequencyForAll(units::frequency::hertz_t frequencyHz, const std::array<BaseStatusSignal *, N> &signals)
508 {
509 return Status_SetUpdateFrequencyForAll(signals.data(), signals.size(), frequencyHz.to<double>(), 0.100);
510 }
511
512 /**
513 * \brief Sets the rate at which the device will publish this signal.
514 *
515 * A frequency of 0 Hz will turn off the signal. Otherwise, the minimum supported signal
516 * frequency is 4 Hz, and the maximum is 1000 Hz.
517 *
518 * If other StatusSignals in the same status frame have been set to an update frequency,
519 * the fastest requested update frequency will be applied to the frame.
520 *
521 * \param frequencyHz Rate to publish the signal in Hz.
522 * \param timeoutSeconds Maximum amount of time to wait when performing the action
523 * \returns Status code of setting the update frequency
524 */
525 ctre::phoenix::StatusCode SetUpdateFrequency(units::frequency::hertz_t frequencyHz, units::time::second_t timeoutSeconds = 100_ms)
526 {
527 return Status_SetUpdateFrequency(
528 this->deviceIdentifier.network.c_str(),
529 this->deviceIdentifier.deviceHash,
530 this->spn,
531 frequencyHz.to<double>(),
532 timeoutSeconds.to<double>()
533 );
534 }
535
536 /**
537 * \brief Gets the rate at which the device will publish this signal.
538 *
539 * This is typically the last value passed into #SetUpdateFrequency. The returned value
540 * may be higher if another StatusSignal in the same status frame has been set to a higher
541 * update frequency.
542 *
543 * \returns Applied update frequency of the signal in Hz
544 */
545 units::frequency::hertz_t GetAppliedUpdateFrequency() const
546 {
547 return units::frequency::hertz_t{
548 Status_GetAppliedUpdateFrequency(
549 this->deviceIdentifier.network.c_str(),
550 this->deviceIdentifier.deviceHash,
551 this->spn
552 )
553 };
554 }
555
556 /**
557 * \brief Gets the name of this signal.
558 *
559 * \returns Name of this signal
560 */
561 const std::string &GetName() const { return signalName; }
562 /**
563 * \brief Gets the units for this signal.
564 *
565 * \returns String representation of units for this signal
566 */
567 const std::string &GetUnits() const { return units; }
568 /**
569 * \brief Gets the value of this signal as a double.
570 *
571 * \return Value of this signal as a double instead of the generic type
572 */
573 double GetValueAsDouble() const { return baseValue; }
574 /**
575 * \brief Get the timestamps of this signals.
576 *
577 * \returns All timestamps for this signal
578 */
579 const AllTimestamps &GetAllTimestamps() const { return timestamps; }
580 /**
581 * \brief Get the most accurate timestamp available for this signal.
582 *
583 * \details The timestamp sources from most to least accurate are:
584 *
585 * - Timestamp#TimestampSource#Device
586 * - Timestamp#TimestampSource#CANivore
587 * - Timestamp#TimestampSource#System
588 *
589 * Note that some of these sources may not be available.
590 *
591 * \returns The most accurate timestamp available for this signal
592 */
593 const Timestamp &GetTimestamp() const { return timestamps.GetBestTimestamp(); }
594 /**
595 * \brief Get the error code from when we last received this signal.
596 *
597 * \returns Last cached Error Code
598 */
599 ctre::phoenix::StatusCode GetStatus() const { return error; }
600
601 /**
602 * \brief Check whether the signal has been updated since the last check.
603 *
604 * Note that the signal must be refreshed before calling this routine.
605 *
606 * \returns true if the signal has updated since the previous call of this routine
607 */
609 {
610 bool retval = false;
611 /* did we receive an update */
612 auto const &timestamp = GetAllTimestamps().GetSystemTimestamp();
613 if (timestamp.IsValid())
614 {
615 /* if the update timestamp is new, then a new frame was sent */
616 if (_lastTimestamp != timestamp.GetTime())
617 {
618 _lastTimestamp = timestamp.GetTime();
619 retval = true;
620 }
621 }
622 return retval;
623 }
624 };
625
626 /**
627 * \brief Information from a single measurement of a status signal.
628 */
629 template <typename L>
631 {
632 /**
633 * \brief The value of the signal
634 */
636 /**
637 * \brief Timestamp of when the data point was taken
638 */
639 units::time::second_t timestamp;
640 /**
641 * \brief Status code response of getting the data
642 */
644 /**
645 * \brief Units that correspond to this point
646 */
647 std::string units;
648 };
649
650 /**
651 * \brief Represents a status signal with data of type T,
652 * and operations available to retrieve information about
653 * the signal.
654 */
655 template <typename T>
657 {
658 std::map<uint16_t, std::string> _unitStrings;
659 uint16_t _unitsKey;
660
661 void RefreshValue(bool block, units::time::second_t timeout, bool ReportOnError)
662 {
663 _checkFirmVersFunction();
664
665 this->error = Status_Get(
666 *this, this->deviceIdentifier.network.c_str(),
667 block, timeout.to<double>()
668 );
669
670 if (ReportOnError && !(this->error.IsOK()))
671 {
672 std::stringstream location;
673 location << this->deviceIdentifier.ToString() << " Status Signal " << this->signalName;
674 c_ctre_phoenix_report_error(this->error.IsError(), this->error, 0, this->error.GetDescription(), location.str().c_str(), ctre::phoenix::platform::GetStackTrace(1).c_str());
675 }
676 }
677
679
681 hardware::DeviceIdentifier deviceIdentifier,
682 uint16_t spn,
683 std::function<void()> checkFirmVersFunction,
684 std::string signalName
685 ) :
686 BaseStatusSignal{std::move(deviceIdentifier), spn, std::move(signalName), std::move(checkFirmVersFunction)},
687 _unitStrings{},
688 _unitsKey{spn}
689 {
690 }
691
693 hardware::DeviceIdentifier deviceIdentifier,
694 uint16_t spn,
695 std::function<void()> checkFirmVersFunction,
696 std::function<std::map<uint16_t, std::string>()> mapFiller,
697 std::string signalName
698 ) :
699 BaseStatusSignal{std::move(deviceIdentifier), spn, std::move(signalName), std::move(checkFirmVersFunction)},
700 _unitStrings{mapFiller()},
701 _unitsKey{spn}
702 {
703 for (auto &unitString : _unitStrings) {
704 unitString.second = Status_GetUnits(unitString.first);
705 }
706 }
707
708 /* Constructor for an invalid StatusSignal */
709 StatusSignal(ctre::phoenix::StatusCode error) :
710 BaseStatusSignal{error},
711 _unitStrings{},
712 _unitsKey{spn}
713 {
714 }
715
716 protected:
717 void UpdateUnits(uint16_t unitsKey) override
718 {
719 if (_unitsKey != unitsKey) {
720 auto const it = _unitStrings.find(unitsKey);
721 if (it != _unitStrings.end()) {
722 units = it->second;
723 _unitsKey = unitsKey;
724 }
725 }
726 }
727
728 public:
729 friend std::ostream &operator<<(std::ostream &os, const StatusSignal<T> &data)
730 {
731 /* Units may contain UTF-8 characters */
733
734 if constexpr(units::traits::is_unit_t_v<T>)
735 {
736 os << data.GetValue().value() << " " << data.GetUnits();
737 }
738 else
739 {
740 os << data.GetValue() << " " << data.GetUnits();
741 }
742 return os;
743 }
744 std::string ToString() const
745 {
746 std::stringstream ss;
747 ss << *this;
748 return ss.str();
749 }
750
751 /**
752 * \brief Gets the cached value from this status signal.
753 *
754 * \details Gets the cached value. To make sure the value is up-to-date
755 * call \c Refresh() or \c WaitForUpdate()
756 *
757 * \returns Cached value
758 */
759 T GetValue() const
760 {
761 if constexpr(units::traits::is_unit_t_v<T>)
762 {
763 return units::make_unit<T>(this->baseValue);
764 }
765 else
766 {
767 return static_cast<T>(this->baseValue);
768 }
769 }
770
771 /**
772 * \brief Refreshes the value of this status signal.
773 *
774 * If the user application caches this StatusSignal object
775 * instead of periodically fetching it from the hardware device,
776 * this function must be called to fetch fresh data.
777 *
778 * \details This performs a non-blocking refresh operation. If
779 * you want to wait until you receive the signal, call
780 * \c WaitForUpdate() instead.
781 *
782 * \param ReportOnError Whether to report any errors to the Driver Station/stderr.
783 *
784 * \returns Reference to itself
785 */
786 StatusSignal<T> &Refresh(bool ReportOnError = true)
787 {
788 RefreshValue(false, 0_s, ReportOnError); // Don't block and error if signal is older than a default timeout
789 return *this;
790 }
791 /**
792 * \brief Waits up to timeoutSec to get the up-to-date status signal value.
793 *
794 * \details This performs a blocking refresh operation. If
795 * you want to non-blocking refresh the signal, call
796 * \c Refresh() instead.
797 *
798 * \param timeoutSec Maximum time to wait for the signal to update
799 * \param ReportOnError Whether to report any errors to the Driver Station/stderr.
800 *
801 * \returns Reference to itself
802 */
803 StatusSignal<T> &WaitForUpdate(units::time::second_t timeoutSec, bool ReportOnError = true)
804 {
805 RefreshValue(true, timeoutSec, ReportOnError);
806 return *this;
807 }
808
809 /**
810 * \brief Get a basic data-only container with this information, to be used
811 * for things such as data logging.
812 *
813 * Note if looking for Phoenix 6 logging features, see the SignalLogger class instead.
814 *
815 * \returns Basic structure with all relevant information
816 */
818 {
820 toRet.value = GetValue();
821 toRet.status = GetStatus();
822 toRet.units = GetUnits();
823 toRet.timestamp = GetTimestamp().GetTime();
824 return toRet;
825 }
826
827 /**
828 * \brief Returns a lambda that calls #Refresh and #GetValue on this object. This is useful for command-based programming.
829 *
830 * \returns std::function<T()> that calls #Refresh and returns this signal's value.
831 */
832 std::function<T()> AsSupplier()
833 {
834 return [this]() { return Refresh().GetValue(); };
835 }
836 };
837
838}
839}
CTREXPORT void c_ctre_phoenix_report_error(int isError, int32_t errorCode, int isLVCode, const char *details, const char *location, const char *callStack)
A collection of timestamps for a received signal.
Definition Timestamp.hpp:133
Class that provides operations to retrieve information about a status signal.
Definition StatusSignal.hpp:41
std::string signalName
Definition StatusSignal.hpp:51
units::time::second_t _lastTimestamp
Definition StatusSignal.hpp:55
const AllTimestamps & GetAllTimestamps() const
Get the timestamps of this signals.
Definition StatusSignal.hpp:579
AllTimestamps timestamps
Definition StatusSignal.hpp:48
ctre::phoenix::StatusCode error
Definition StatusSignal.hpp:50
static ctre::phoenix::StatusCode WaitForAll(units::time::second_t timeoutSeconds, const std::vector< BaseStatusSignal * > &signals)
Waits for new data on all provided signals up to timeout.
Definition StatusSignal.hpp:266
static ctre::phoenix::StatusCode Status_WaitForAll(BaseStatusSignal *const *signals, size_t count, const char *network, double timeoutSeconds)
Wait for multiple signals to arrive.
uint16_t spn
Definition StatusSignal.hpp:46
static ctre::phoenix::StatusCode SetUpdateFrequencyForAll(units::frequency::hertz_t frequencyHz, Signals &... signals)
Sets the update frequency of all specified status signals to the provided common frequency.
Definition StatusSignal.hpp:468
static std::string Status_GetUnits(uint32_t signal)
Gets signal units.
static bool IsAllGood(Signals &... signals)
Checks if all signals have an OK error code.
Definition StatusSignal.hpp:414
static ctre::phoenix::StatusCode RefreshAll(const std::array< BaseStatusSignal *, N > &signals)
Performs a non-blocking refresh on all provided signals.
Definition StatusSignal.hpp:359
static ctre::phoenix::StatusCode RefreshAll(Signals &... signals)
Performs a non-blocking refresh on all provided signals.
Definition StatusSignal.hpp:322
std::string units
Definition StatusSignal.hpp:47
static bool IsAllGood(const std::vector< BaseStatusSignal * > &signals)
Checks if all signals have an OK error code.
Definition StatusSignal.hpp:424
static double Status_GetAppliedUpdateFrequency(const char *canbus, uint32_t deviceHash, uint16_t spn)
ctre::phoenix::StatusCode SetUpdateFrequency(units::frequency::hertz_t frequencyHz, units::time::second_t timeoutSeconds=100_ms)
Sets the rate at which the device will publish this signal.
Definition StatusSignal.hpp:525
ctre::phoenix::StatusCode GetStatus() const
Get the error code from when we last received this signal.
Definition StatusSignal.hpp:599
static ctre::phoenix::StatusCode WaitForAllImpl(const char *location, units::time::second_t timeoutSeconds, const Arr &signals)
Implementation of the WaitForAll API.
Definition StatusSignal.hpp:138
static ctre::phoenix::StatusCode SetUpdateFrequencyForAll(units::frequency::hertz_t frequencyHz, const std::array< BaseStatusSignal *, N > &signals)
Sets the update frequency of all specified status signals to the provided common frequency.
Definition StatusSignal.hpp:507
static U GetLatencyCompensatedValue(StatusSignal< U > &signal, StatusSignal< U_PER_SEC > &signalSlope, units::time::second_t maxLatencySeconds=0.300_s)
Performs latency compensation on signal using the signalSlope and signal's latency to determine the m...
Definition StatusSignal.hpp:389
virtual void UpdateUnits(uint16_t spn)=0
std::function< void()> _checkFirmVersFunction
Definition StatusSignal.hpp:53
bool HasUpdated()
Check whether the signal has been updated since the last check.
Definition StatusSignal.hpp:608
static ctre::phoenix::StatusCode SetUpdateFrequencyForAll(units::frequency::hertz_t frequencyHz, const std::vector< BaseStatusSignal * > &signals)
Sets the update frequency of all specified status signals to the provided common frequency.
Definition StatusSignal.hpp:487
BaseStatusSignal(ctre::phoenix::StatusCode error)
Definition StatusSignal.hpp:75
const std::string & GetUnits() const
Gets the units for this signal.
Definition StatusSignal.hpp:567
static ctre::phoenix::StatusCode WaitForAll(units::time::second_t timeoutSeconds, Signals &... signals)
Waits for new data on all provided signals up to timeout.
Definition StatusSignal.hpp:233
static ctre::phoenix::StatusCode Status_SetUpdateFrequency(const char *canbus, uint32_t deviceHash, uint16_t spn, double frequencyHz, double timeoutSeconds)
units::frequency::hertz_t GetAppliedUpdateFrequency() const
Gets the rate at which the device will publish this signal.
Definition StatusSignal.hpp:545
static bool IsAllGood(const std::array< BaseStatusSignal *, N > &signals)
Checks if all signals have an OK error code.
Definition StatusSignal.hpp:441
double baseValue
Definition StatusSignal.hpp:49
const std::string & GetName() const
Gets the name of this signal.
Definition StatusSignal.hpp:561
double GetValueAsDouble() const
Gets the value of this signal as a double.
Definition StatusSignal.hpp:573
hardware::DeviceIdentifier deviceIdentifier
Definition StatusSignal.hpp:45
static ctre::phoenix::StatusCode Status_SetUpdateFrequencyForAll(BaseStatusSignal *const *signals, size_t count, double frequencyHz, double timeoutSeconds)
static ctre::phoenix::StatusCode WaitForAll(units::time::second_t timeoutSeconds, const std::array< BaseStatusSignal *, N > &signals)
Waits for new data on all provided signals up to timeout.
Definition StatusSignal.hpp:301
const Timestamp & GetTimestamp() const
Get the most accurate timestamp available for this signal.
Definition StatusSignal.hpp:593
BaseStatusSignal(hardware::DeviceIdentifier deviceIdentifier, uint16_t spn, std::string signalName, std::function< void()> checkFirmVersFunction)
Definition StatusSignal.hpp:60
static ctre::phoenix::StatusCode RefreshAll(const std::vector< BaseStatusSignal * > &signals)
Performs a non-blocking refresh on all provided signals.
Definition StatusSignal.hpp:339
static ctre::phoenix::StatusCode Status_Get(BaseStatusSignal &signal, const char *network, bool bWaitForUpdate, double timeoutSeconds)
Get signal update.
Represents a status signal with data of type T, and operations available to retrieve information abou...
Definition StatusSignal.hpp:657
friend std::ostream & operator<<(std::ostream &os, const StatusSignal< T > &data)
Definition StatusSignal.hpp:729
std::string ToString() const
Definition StatusSignal.hpp:744
T GetValue() const
Gets the cached value from this status signal.
Definition StatusSignal.hpp:759
void UpdateUnits(uint16_t unitsKey) override
Definition StatusSignal.hpp:717
StatusSignal< T > & Refresh(bool ReportOnError=true)
Refreshes the value of this status signal.
Definition StatusSignal.hpp:786
SignalMeasurement< T > GetDataCopy() const
Get a basic data-only container with this information, to be used for things such as data logging.
Definition StatusSignal.hpp:817
std::function< T()> AsSupplier()
Returns a lambda that calls Refresh and GetValue on this object.
Definition StatusSignal.hpp:832
StatusSignal< T > & WaitForUpdate(units::time::second_t timeoutSec, bool ReportOnError=true)
Waits up to timeoutSec to get the up-to-date status signal value.
Definition StatusSignal.hpp:803
Information about the timestamp of a signal.
Definition Timestamp.hpp:20
units::time::second_t GetLatency() const
Get the latency of this timestamp compared to now.
Definition Timestamp.hpp:115
Definition DeviceIdentifier.hpp:19
std::string network
Definition DeviceIdentifier.hpp:21
Parent class for all devices.
Definition ParentDevice.hpp:29
Status codes reported by APIs, including OK, warnings, and errors.
Definition StatusCodes.h:27
constexpr const char * GetDescription() const
Gets the description of this StatusCode.
Definition StatusCodes.h:1066
static constexpr int InvalidParamValue
An invalid argument was passed into the function/VI, such as a null pointer.
Definition StatusCodes.h:368
constexpr bool IsOK() const
Definition StatusCodes.h:859
constexpr bool IsError() const
Definition StatusCodes.h:851
static constexpr int InvalidNetwork
InvalidNetwork.
Definition StatusCodes.h:636
static constexpr int SigNotUpdated
No new response to update signal.
Definition StatusCodes.h:418
CTREXPORT void EnableConsoleUTF8Output()
Enables UTF-8 console output.
CTREXPORT std::string GetStackTrace(int offset)
Get a stack trace, ignoring the first "offset" symbols.
Definition StatusCodes.h:18
Definition span.hpp:401
Type trait to verify that all types passed in are subclasses of BaseStatusSignal.
Definition StatusSignal.hpp:193
Information from a single measurement of a status signal.
Definition StatusSignal.hpp:631
std::string units
Units that correspond to this point.
Definition StatusSignal.hpp:647
units::time::second_t timestamp
Timestamp of when the data point was taken.
Definition StatusSignal.hpp:639
L value
The value of the signal.
Definition StatusSignal.hpp:635
ctre::phoenix::StatusCode status
Status code response of getting the data.
Definition StatusSignal.hpp:643