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