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