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