CTRE Phoenix 6 C++ 24.3.0
ParentDevice.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
17#include <map>
18#include <memory>
19#include <units/dimensionless.h>
20
21namespace ctre {
22namespace phoenix6 {
23namespace hardware {
24
25 /**
26 * Parent class for all devices
27 */
29 {
30 protected:
32
34
35 /**
36 * \brief Type trait to verify that all types passed in are subclasses of ParentDevice.
37 */
38 template <typename... Devices>
40 std::conjunction<std::is_base_of<ParentDevice, std::remove_reference_t<Devices>>...>
41 {};
42
43 /**
44 * \brief Whether all types passed in are subclasses of ParentDevice.
45 */
46 template <typename... Devices>
47 static constexpr bool is_all_device_v = is_all_device<Devices...>::value;
48
49 private:
50 std::map<uint32_t, std::unique_ptr<BaseStatusSignal>> _signalValues;
52 /**
53 * Use a shared pointer so users that access the control request via #GetAppliedControl has a copy
54 * of the pointer without risk of it becoming a dangling pointer due to parallel operations
55 */
56 std::shared_ptr<controls::ControlRequest> _controlReq = std::make_shared<controls::EmptyControl>();
58
59 double _creationTime = GetCurrentTimeSeconds();
60
61 bool _isInitialized = false;
62 ctre::phoenix::StatusCode _versionStatus{ctre::phoenix::StatusCode::CouldNotRetrieveV6Firmware};
63 double _timeToRefreshVersion = GetCurrentTimeSeconds();
64
65 StatusSignal<int> _compliancy{LookupStatusSignal<int>(ctre::phoenix6::spns::SpnValue::Compliancy_Version, "Compliancy", false)};
66 StatusSignal<int> _resetSignal{LookupStatusSignal<int>(ctre::phoenix6::spns::SpnValue::Startup_ResetFlags, "ResetFlags", false)};
67
68 void ReportIfTooOld();
69
70 template <typename T>
71 StatusSignal<T> &LookupCommon(uint16_t spn, uint16_t mapper_iter, std::function<std::map<int, StatusSignal<T>>()> map_filler, std::string signalName, bool reportOnConstruction)
72 {
73 BaseStatusSignal *toFind;
74 {
75 /* lock access to the map */
76 std::lock_guard<std::recursive_mutex> lock{_signalValuesLck};
77
78 const uint32_t totalHash = spn | ((uint32_t)mapper_iter << 16);
79 /* lookup and return if found */
80 auto iter = _signalValues.find(totalHash);
81 if (iter != _signalValues.end())
82 {
83 /* Found it, toFind is now the found StatusSignal */
84 toFind = iter->second.get();
85 /* since we didn't construct, report errors */
86 reportOnConstruction = true;
87 }
88 else
89 {
90 /* insert into map */
91 /* Mapper_iter is 0 when using straight SPNs, otherwise it's nonzero for switchable SPNs */
92 if (mapper_iter != 0)
93 {
94 /* Switchable spn, so generate the map to switch with */
95 _signalValues.emplace(totalHash, std::unique_ptr<StatusSignal<T>>{new StatusSignal<T>{
96 deviceIdentifier, spn, [this]()
97 { ReportIfTooOld(); },
98 map_filler, std::move(signalName)}});
99 }
100 else
101 {
102 /* Non-switchable spn, so just add the SPN plain */
103 _signalValues.emplace(totalHash, std::unique_ptr<StatusSignal<T>>{new StatusSignal<T>{
104 deviceIdentifier, spn, [this]()
105 { ReportIfTooOld(); },
106 std::move(signalName)}});
107 }
108
109 /* look up and return */
110 iter = _signalValues.find(totalHash);
111 toFind = iter->second.get();
112 }
113 }
114
115 /* Now cast it up to the StatusSignal */
116 StatusSignal<T> *ret = dynamic_cast<StatusSignal<T> *>(toFind);
117 /* If ret is null, that means the cast failed. Otherwise we can return it */
118 if (ret == nullptr)
119 {
120 /* Cast failed, let user know this doesn't exist */
121 static StatusSignal<T> failure{ctre::phoenix::StatusCode::InvalidParamValue};
122 return failure;
123 }
124 else
125 {
126 /* Good cast, refresh it and return this now */
127 ret->Refresh(reportOnConstruction);
128 return *ret;
129 }
130 }
131
132 public:
133 ParentDevice(int deviceID, std::string model, std::string canbus) :
134 deviceIdentifier{DeviceIdentifier{deviceID, std::move(model), std::move(canbus)}}
135 {
136 /* This needs to be set to true after everything else has already been initialized */
137 _isInitialized = true;
138 }
139
140 virtual ~ParentDevice() = default;
141
144
145 /**
146 * \returns The device ID of this device [0,62].
147 */
148 int GetDeviceID() const
149 {
151 }
152
153 /**
154 * \returns Name of the network this device is on.
155 */
156 const std::string &GetNetwork() const
157 {
159 }
160
161 /**
162 * \brief Gets a number unique for this device's hardware type and ID.
163 * This number is not unique across networks.
164 *
165 * \details This can be used to easily reference hardware devices on
166 * the same network in collections such as maps.
167 *
168 * \returns Hash of this device.
169 */
170 uint64_t GetDeviceHash() const
171 {
173 }
174
175 /**
176 * \brief Get the latest applied control.
177 * Caller can cast this to the derived class if they know its type. Otherwise,
178 * use controls#ControlRequest#GetControlInfo to get info out of it.
179 *
180 * \details This returns a shared pointer to avoid becoming a dangling pointer
181 * due to parallel operations changing the underlying data. Make sure
182 * to save the shared_ptr to a variable before chaining function calls,
183 * otherwise the data may be freed early.
184 *
185 * \returns Latest applied control
186 */
187 std::shared_ptr<const controls::ControlRequest> GetAppliedControl() const
188 {
189 return _controlReq;
190 }
191
192 /**
193 * \brief Get the latest applied control.
194 * Caller can cast this to the derived class if they know its type. Otherwise,
195 * use controls#ControlRequest#GetControlInfo to get info out of it.
196 *
197 * \details This returns a shared pointer to avoid becoming a dangling pointer
198 * due to parallel operations changing the underlying data. Make sure
199 * to save the shared_ptr to a variable before chaining function calls,
200 * otherwise the data may be freed early.
201 *
202 * \returns Latest applied control
203 */
204 std::shared_ptr<controls::ControlRequest> GetAppliedControl()
205 {
206 return _controlReq;
207 }
208
209 /**
210 * \returns true if device has reset since the previous call of this routine.
211 */
213 {
214 return _resetSignal.Refresh(false).HasUpdated();
215 }
216
217 /**
218 * \returns a function that checks for device resets.
219 */
220 std::function<bool()> GetResetOccurredChecker() const
221 {
222 return [resetSignal=_resetSignal]() mutable {
223 return resetSignal.Refresh(false).HasUpdated();
224 };
225 }
226
227 /**
228 * \brief This is a reserved routine for internal testing. Use the other get routines to retrieve signal values.
229 *
230 * \param signal Signal to get.
231 * \return StatusSignalValue holding value
232 */
234 {
235 return LookupStatusSignal<double>((uint16_t)signal, "Generic", true);
236 }
237
238 /**
239 * \brief Optimizes the device's bus utilization by reducing the update frequencies of its status signals.
240 *
241 * All status signals that have not been explicitly given an update frequency using
242 * BaseStatusSignal#SetUpdateFrequency will be disabled. Note that if other status
243 * signals in the same status frame have been given an update frequency, the update
244 * frequency will be honored for the entire frame.
245 *
246 * This function only needs to be called once on this device in the robot program. Additionally, this
247 * method does not necessarily need to be called after setting the update frequencies of other signals.
248 *
249 * To restore the default status update frequencies, remove this method call, redeploy the robot
250 * application, and power-cycle the devices on the bus. Alternatively, the user can override
251 * individual status update frequencies using BaseStatusSignal#SetUpdateFrequency.
252 *
253 * \param optimizedFreqHz The update frequency to apply to the optimized status signals. A frequency of
254 * 0 Hz (default) will turn off the signals. Otherwise, the minimum supported signal
255 * frequency is 4 Hz.
256 * \param timeoutSeconds Maximum amount of time to wait for each status frame when performing the action
257 * \return Status code of the first failed update frequency set call, or OK if all succeeded
258 */
259 ctre::phoenix::StatusCode OptimizeBusUtilization(units::frequency::hertz_t optimizedFreqHz = 0_Hz, units::time::second_t timeoutSeconds = 50_ms);
260
261 /**
262 * \brief Optimizes the bus utilization of the provided devices by reducing the update
263 * frequencies of their status signals.
264 *
265 * All status signals that have not been explicitly given an update frequency using
266 * BaseStatusSignal#SetUpdateFrequency will be disabled. Note that if other status
267 * signals in the same status frame have been given an update frequency, the update
268 * frequency will be honored for the entire frame.
269 *
270 * This function only needs to be called once in the robot program for the provided devices.
271 * Additionally, this method does not necessarily need to be called after setting the update
272 * frequencies of other signals.
273 *
274 * To restore the default status update frequencies, remove this method call, redeploy the robot
275 * application, and power-cycle the devices on the bus. Alternatively, the user can override
276 * individual status update frequencies using BaseStatusSignal#SetUpdateFrequency.
277 *
278 * This will wait up to 0.050 seconds (50ms) for each status frame.
279 *
280 * \param devices Devices for which to optimize bus utilization, passed as a comma-separated list of device references.
281 * \return Status code of the first failed optimize call, or OK if all succeeded
282 */
283 template <typename... Devices, typename = std::enable_if_t<is_all_device_v<Devices...>>>
284 static ctre::phoenix::StatusCode OptimizeBusUtilizationForAll(Devices &... devices)
285 {
286 return OptimizeBusUtilizationForAll(0_Hz, devices...);
287 }
288
289 /**
290 * \brief Optimizes the bus utilization of the provided devices by reducing the update
291 * frequencies of their status signals.
292 *
293 * All status signals that have not been explicitly given an update frequency using
294 * BaseStatusSignal#SetUpdateFrequency will be disabled. Note that if other status
295 * signals in the same status frame have been given an update frequency, the update
296 * frequency will be honored for the entire frame.
297 *
298 * This function only needs to be called once in the robot program for the provided devices.
299 * Additionally, this method does not necessarily need to be called after setting the update
300 * frequencies of other signals.
301 *
302 * To restore the default status update frequencies, remove this method call, redeploy the robot
303 * application, and power-cycle the devices on the bus. Alternatively, the user can override
304 * individual status update frequencies using BaseStatusSignal#SetUpdateFrequency.
305 *
306 * This will wait up to 0.050 seconds (50ms) for each status frame.
307 *
308 * \param devices Devices for which to optimize bus utilization, passed as a vector or initializer list of device addresses.
309 * \return Status code of the first failed optimize call, or OK if all succeeded
310 */
311 static ctre::phoenix::StatusCode OptimizeBusUtilizationForAll(const std::vector<ParentDevice *> &devices)
312 {
313 return OptimizeBusUtilizationForAll(0_Hz, devices);
314 }
315
316 /**
317 * \brief Optimizes the bus utilization of the provided devices by reducing the update
318 * frequencies of their status signals.
319 *
320 * All status signals that have not been explicitly given an update frequency using
321 * BaseStatusSignal#SetUpdateFrequency will be disabled. Note that if other status
322 * signals in the same status frame have been given an update frequency, the update
323 * frequency will be honored for the entire frame.
324 *
325 * This function only needs to be called once in the robot program for the provided devices.
326 * Additionally, this method does not necessarily need to be called after setting the update
327 * frequencies of other signals.
328 *
329 * To restore the default status update frequencies, remove this method call, redeploy the robot
330 * application, and power-cycle the devices on the bus. Alternatively, the user can override
331 * individual status update frequencies using BaseStatusSignal#SetUpdateFrequency.
332 *
333 * This will wait up to 0.050 seconds (50ms) for each status frame.
334 *
335 * \param devices Devices for which to optimize bus utilization, passed as an array of device addresses.
336 * \return Status code of the first failed optimize call, or OK if all succeeded
337 */
338 template <size_t N>
339 static ctre::phoenix::StatusCode OptimizeBusUtilizationForAll(const std::array<ParentDevice *, N> &devices)
340 {
341 return OptimizeBusUtilizationForAll(0_Hz, devices);
342 }
343
344 /**
345 * \brief Optimizes the bus utilization of the provided devices by reducing the update
346 * frequencies of their status signals.
347 *
348 * All status signals that have not been explicitly given an update frequency using
349 * BaseStatusSignal#SetUpdateFrequency will be disabled. Note that if other status
350 * signals in the same status frame have been given an update frequency, the update
351 * frequency will be honored for the entire frame.
352 *
353 * This function only needs to be called once in the robot program for the provided devices.
354 * Additionally, this method does not necessarily need to be called after setting the update
355 * frequencies of other signals.
356 *
357 * To restore the default status update frequencies, remove this method call, redeploy the robot
358 * application, and power-cycle the devices on the bus. Alternatively, the user can override
359 * individual status update frequencies using BaseStatusSignal#SetUpdateFrequency.
360 *
361 * This will wait up to 0.050 seconds (50ms) for each status frame.
362 *
363 * \param optimizedFreqHz The update frequency to apply to the optimized status signals. A frequency of
364 * 0 Hz (default) will turn off the signals. Otherwise, the minimum supported signal
365 * frequency is 4 Hz.
366 * \param devices Devices for which to optimize bus utilization, passed as a comma-separated list of device references.
367 * \return Status code of the first failed optimize call, or OK if all succeeded
368 */
369 template <typename... Devices, typename = std::enable_if_t<is_all_device_v<Devices...>>>
370 static ctre::phoenix::StatusCode OptimizeBusUtilizationForAll(units::frequency::hertz_t optimizedFreqHz, Devices &... devices)
371 {
372 return OptimizeBusUtilizationForAll(optimizedFreqHz, std::array<ParentDevice *, sizeof...(Devices)>{(&devices)...});
373 }
374
375 /**
376 * \brief Optimizes the bus utilization of the provided devices by reducing the update
377 * frequencies of their status signals.
378 *
379 * All status signals that have not been explicitly given an update frequency using
380 * BaseStatusSignal#SetUpdateFrequency will be disabled. Note that if other status
381 * signals in the same status frame have been given an update frequency, the update
382 * frequency will be honored for the entire frame.
383 *
384 * This function only needs to be called once in the robot program for the provided devices.
385 * Additionally, this method does not necessarily need to be called after setting the update
386 * frequencies of other signals.
387 *
388 * To restore the default status update frequencies, remove this method call, redeploy the robot
389 * application, and power-cycle the devices on the bus. Alternatively, the user can override
390 * individual status update frequencies using BaseStatusSignal#SetUpdateFrequency.
391 *
392 * This will wait up to 0.050 seconds (50ms) for each status frame.
393 *
394 * \param optimizedFreqHz The update frequency to apply to the optimized status signals. A frequency of
395 * 0 Hz (default) will turn off the signals. Otherwise, the minimum supported signal
396 * frequency is 4 Hz.
397 * \param devices Devices for which to optimize bus utilization, passed as a vector or initializer list of device addresses.
398 * \return Status code of the first failed optimize call, or OK if all succeeded
399 */
400 static ctre::phoenix::StatusCode OptimizeBusUtilizationForAll(units::frequency::hertz_t optimizedFreqHz, const std::vector<ParentDevice *> &devices)
401 {
402 ctre::phoenix::StatusCode retval = ctre::phoenix::StatusCode::OK;
403 for (auto device : devices) {
404 const auto err = device->OptimizeBusUtilization(optimizedFreqHz);
405 if (retval.IsOK()) {
406 retval = err;
407 }
408 }
409 return retval;
410 }
411
412 /**
413 * \brief Optimizes the bus utilization of the provided devices by reducing the update
414 * frequencies of their status signals.
415 *
416 * All status signals that have not been explicitly given an update frequency using
417 * BaseStatusSignal#SetUpdateFrequency will be disabled. Note that if other status
418 * signals in the same status frame have been given an update frequency, the update
419 * frequency will be honored for the entire frame.
420 *
421 * This function only needs to be called once in the robot program for the provided devices.
422 * Additionally, this method does not necessarily need to be called after setting the update
423 * frequencies of other signals.
424 *
425 * To restore the default status update frequencies, remove this method call, redeploy the robot
426 * application, and power-cycle the devices on the bus. Alternatively, the user can override
427 * individual status update frequencies using BaseStatusSignal#SetUpdateFrequency.
428 *
429 * This will wait up to 0.050 seconds (50ms) for each status frame.
430 *
431 * \param optimizedFreqHz The update frequency to apply to the optimized status signals. A frequency of
432 * 0 Hz (default) will turn off the signals. Otherwise, the minimum supported signal
433 * frequency is 4 Hz.
434 * \param devices Devices for which to optimize bus utilization, passed as an array of device addresses.
435 * \return Status code of the first failed optimize call, or OK if all succeeded
436 */
437 template <size_t N>
438 static ctre::phoenix::StatusCode OptimizeBusUtilizationForAll(units::frequency::hertz_t optimizedFreqHz, const std::array<ParentDevice *, N> &devices)
439 {
440 ctre::phoenix::StatusCode retval = ctre::phoenix::StatusCode::OK;
441 for (auto device : devices) {
442 const auto err = device->OptimizeBusUtilization(optimizedFreqHz);
443 if (retval.IsOK()) {
444 retval = err;
445 }
446 }
447 return retval;
448 }
449
450 protected:
451 virtual ctre::phoenix::StatusCode SetControlPrivate(controls::ControlRequest &request);
452
453 template <typename T>
454 StatusSignal<T> &LookupStatusSignal(uint16_t spn, std::string signalName, bool reportOnConstruction)
455 {
456 std::function<std::map<int, StatusSignal<T>>()> emptyMapFiller = []
457 {
458 return std::map<int, StatusSignal<T>>{};
459 };
460 return LookupCommon<T>(spn, 0, emptyMapFiller, std::move(signalName), reportOnConstruction);
461 }
462
463 template <typename T>
464 StatusSignal<T> &LookupStatusSignal(uint16_t spn, uint16_t mapper_iter, std::function<std::map<int, StatusSignal<T>>()> map_filler, std::string signalName, bool reportOnConstruction)
465 {
466 return LookupCommon<T>(spn, mapper_iter, map_filler, std::move(signalName), reportOnConstruction);
467 }
468
469 /** Returns a unitless version of the StatusSignal by value. Do not store the result in a reference. */
470 template <typename T, typename U>
471 StatusSignal<T> LookupDimensionlessStatusSignal(uint16_t spn, std::string signalName)
472 {
473 return StatusSignal<T>{LookupStatusSignal<U>(spn, std::move(signalName), true)};
474 }
475 };
476
477}
478}
479}
@ OK
No Error.
Definition: StatusCodes.h:1196
@ CouldNotRetrieveV6Firmware
Device firmware could not be retrieved.
Definition: StatusCodes.h:1878
@ InvalidParamValue
An invalid argument was passed into the function/VI, such as a null pointer.
Definition: StatusCodes.h:1529
bool HasUpdated()
Check whether the signal has been updated since the last check.
Definition: StatusSignal.hpp:579
StatusSignal< T > & Refresh(bool ReportOnError=true)
Refreshes the value of this status signal.
Definition: StatusSignal.hpp:810
Abstract Control Request class that other control requests extend for use.
Definition: ControlRequest.hpp:28
Generic Empty Control class used to do nothing.
Definition: ControlRequest.hpp:71
Definition: DeviceIdentifier.hpp:19
int deviceID
Definition: DeviceIdentifier.hpp:23
uint32_t deviceHash
Definition: DeviceIdentifier.hpp:24
std::string network
Definition: DeviceIdentifier.hpp:21
Parent class for all devices.
Definition: ParentDevice.hpp:29
static controls::EmptyControl _emptyControl
Definition: ParentDevice.hpp:31
const std::string & GetNetwork() const
Definition: ParentDevice.hpp:156
static constexpr bool is_all_device_v
Whether all types passed in are subclasses of ParentDevice.
Definition: ParentDevice.hpp:47
std::shared_ptr< controls::ControlRequest > GetAppliedControl()
Get the latest applied control.
Definition: ParentDevice.hpp:204
virtual ctre::phoenix::StatusCode SetControlPrivate(controls::ControlRequest &request)
DeviceIdentifier deviceIdentifier
Definition: ParentDevice.hpp:33
StatusSignal< T > & LookupStatusSignal(uint16_t spn, std::string signalName, bool reportOnConstruction)
Definition: ParentDevice.hpp:454
std::function< bool()> GetResetOccurredChecker() const
Definition: ParentDevice.hpp:220
static ctre::phoenix::StatusCode OptimizeBusUtilizationForAll(units::frequency::hertz_t optimizedFreqHz, const std::array< ParentDevice *, N > &devices)
Optimizes the bus utilization of the provided devices by reducing the update frequencies of their sta...
Definition: ParentDevice.hpp:438
ParentDevice & operator=(ParentDevice &&)=default
ctre::phoenix::StatusCode OptimizeBusUtilization(units::frequency::hertz_t optimizedFreqHz=0_Hz, units::time::second_t timeoutSeconds=50_ms)
Optimizes the device's bus utilization by reducing the update frequencies of its status signals.
std::shared_ptr< const controls::ControlRequest > GetAppliedControl() const
Get the latest applied control.
Definition: ParentDevice.hpp:187
static ctre::phoenix::StatusCode OptimizeBusUtilizationForAll(const std::array< ParentDevice *, N > &devices)
Optimizes the bus utilization of the provided devices by reducing the update frequencies of their sta...
Definition: ParentDevice.hpp:339
static ctre::phoenix::StatusCode OptimizeBusUtilizationForAll(Devices &... devices)
Optimizes the bus utilization of the provided devices by reducing the update frequencies of their sta...
Definition: ParentDevice.hpp:284
static ctre::phoenix::StatusCode OptimizeBusUtilizationForAll(units::frequency::hertz_t optimizedFreqHz, Devices &... devices)
Optimizes the bus utilization of the provided devices by reducing the update frequencies of their sta...
Definition: ParentDevice.hpp:370
StatusSignal< double > & GetGenericSignal(uint32_t signal)
This is a reserved routine for internal testing.
Definition: ParentDevice.hpp:233
StatusSignal< T > LookupDimensionlessStatusSignal(uint16_t spn, std::string signalName)
Returns a unitless version of the StatusSignal by value.
Definition: ParentDevice.hpp:471
int GetDeviceID() const
Definition: ParentDevice.hpp:148
ParentDevice(int deviceID, std::string model, std::string canbus)
Definition: ParentDevice.hpp:133
ParentDevice(ParentDevice &&)=default
uint64_t GetDeviceHash() const
Gets a number unique for this device's hardware type and ID.
Definition: ParentDevice.hpp:170
bool HasResetOccurred()
Definition: ParentDevice.hpp:212
StatusSignal< T > & LookupStatusSignal(uint16_t spn, uint16_t mapper_iter, std::function< std::map< int, StatusSignal< T > >()> map_filler, std::string signalName, bool reportOnConstruction)
Definition: ParentDevice.hpp:464
static ctre::phoenix::StatusCode OptimizeBusUtilizationForAll(const std::vector< ParentDevice * > &devices)
Optimizes the bus utilization of the provided devices by reducing the update frequencies of their sta...
Definition: ParentDevice.hpp:311
static ctre::phoenix::StatusCode OptimizeBusUtilizationForAll(units::frequency::hertz_t optimizedFreqHz, const std::vector< ParentDevice * > &devices)
Optimizes the bus utilization of the provided devices by reducing the update frequencies of their sta...
Definition: ParentDevice.hpp:400
CTREXPORT double GetCurrentTimeSeconds()
Get the current timestamp in seconds.
Definition: string_util.hpp:15
Type trait to verify that all types passed in are subclasses of ParentDevice.
Definition: ParentDevice.hpp:41