REVLib - C++
CANDeviceScanner.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2018-2026 REV Robotics
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of REV Robotics nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#pragma once
30
31#include <rev/driver/FRCCanSpec.h>
32#include <stdint.h>
33
34#include <atomic>
35#include <map>
36#include <memory>
37#include <set>
38#include <string>
39#include <thread>
40#include <unordered_map>
41#include <utility>
42#include <vector>
43
44namespace rev {
45namespace detail {
46
47static const char* frc_deviceType_text[16] = {
48 "Broadcast", "Robot Controller",
49 "Motor Controller", "Relay Controller",
50 "Gyro Sensor", "Accelerometer",
51 "Ultrasonic Sensor", "Gear Tooth Sensor",
52 "Power Distribution", "Pneumatics Controller",
53 "Misc CAN Device", "IO Breakout",
54 "Servo Controller", "Reserved",
55 "Firmware Update", "Unknown",
56};
57
58static inline const char* GetFRCDeviceTypeText(uint32_t index) {
59 if (index > 12 && index < 31) {
60 index = 13;
61 } else if (index > 31) {
62 index = 15;
63 } else if (index == 31) {
64 index = 14;
65 } else {
66 index = 0;
67 }
68 return frc_deviceType_text[index];
69}
70
71static const char* frc_manufacturer_text[18] = {"Broadcast",
72 "NI",
73 "LM",
74 "DEKA",
75 "CTRE",
76 "REV",
77 "Grapple",
78 "MindSensors",
79 "Team Use",
80 "Kauai Labs",
81 "Copperforge",
82 "Playing with Fusion",
83 "Studica",
84 "TheThriftyBot",
85 "Redux Robotics",
86 "AndyMark",
87 "Vivid-Hosting",
88 "Unknown"};
89
90static inline const char* GetFRCManufacturerText(uint32_t index) {
91 index = (index > 17) ? 17 : index;
92 return frc_manufacturer_text[index];
93}
94
95// Note: This is dependent on __attribute__((PACKED)) and processor is
96// little-endian
97
98#ifdef _WIN32
99#define PACKED
100#pragma pack(push, 1)
101#else
102#define PACKED __attribute__((__packed__))
103#endif
104
106 uint16_t deviceNumber : 6;
107 uint16_t api : 10;
108 rev::FRCManufacturer manufacturer : 8;
109 rev::FRCDeviceType deviceType : 5;
110 uint8_t rsvd : 3; // these are DNC
111};
112
113#ifdef _WIN32
114#pragma pack(pop)
115#endif
116
119 uint32_t raw;
120};
121
123 rev::FRCDeviceType deviceTypeId;
124 rev::FRCManufacturer manufacturerId;
125 uint16_t canId;
126 uint32_t uniqueId;
127 std::string name;
128 CANScanIdentifier(uint32_t arbid, std::string name, uint32_t uniqueId = 0) {
129 frc_frameID_t frame;
130 frame.raw = arbid;
131 this->name = name;
132 this->uniqueId = uniqueId;
133 this->deviceTypeId = frame.fields.deviceType;
134 this->manufacturerId = frame.fields.manufacturer;
135 this->canId = frame.fields.deviceNumber;
136 }
137
138 std::string Name() const {
139 if (name.empty()) {
140 return std::string(GetFRCManufacturerText(manufacturerId)) + " " +
141 std::string(GetFRCDeviceTypeText(deviceTypeId));
142 } else {
143 return name;
144 }
145 }
146};
147
148inline bool operator<(const CANScanIdentifier& lhs,
149 const CANScanIdentifier& rhs) {
150 return std::tie(lhs.deviceTypeId, lhs.manufacturerId, lhs.canId,
151 lhs.uniqueId) < std::tie(rhs.deviceTypeId,
152 rhs.manufacturerId, rhs.canId,
153 rhs.uniqueId);
154}
155
156inline bool operator>(const CANScanIdentifier& lhs,
157 const CANScanIdentifier& rhs) {
158 return std::tie(lhs.deviceTypeId, lhs.manufacturerId, lhs.canId,
159 lhs.uniqueId) > std::tie(rhs.deviceTypeId,
160 rhs.manufacturerId, rhs.canId,
161 rhs.uniqueId);
162}
163
164inline bool operator==(const CANScanIdentifier& lhs,
165 const CANScanIdentifier& rhs) {
166 return std::tie(lhs.deviceTypeId, lhs.manufacturerId, lhs.canId,
167 lhs.uniqueId) == std::tie(rhs.deviceTypeId,
168 rhs.manufacturerId, rhs.canId,
169 rhs.uniqueId);
170}
171
173public:
174 // Make sure the buffer size is large enough to capture all needed messages
175 // in the thread interval time
176 explicit CANBusScanner(int buffersize = 256, int threadIntervalMs = 10);
178
179 bool Start();
180 void Stop();
181 bool Running();
182 std::string LastError();
183 std::vector<CANScanIdentifier> CANBusScan();
184 void RegisterDevice(std::string name, std::vector<uint32_t> validIds,
185 int32_t maxFramePeriodMs = 100);
186
187private:
188 class CANScanElement {
189 uint64_t lastSeen;
190 uint64_t timeout;
191
192 public:
193 explicit CANScanElement(uint64_t timeoutMs = 1000);
194 void UpdateLastSeen();
195 bool IsActive() const;
196 };
197
198 class CANScanCollection {
199 public:
200 CANScanCollection(std::string name, uint32_t arbId, uint64_t timeoutMs)
201 : name(name), timeout(timeoutMs), arbId(arbId) {}
202
203 std::vector<int> ActiveDevices() const {
204 std::vector<int> result;
205 for (const auto& [devKey, devValue] : devices) {
206 if (devValue.IsActive()) {
207 result.push_back(devKey);
208 }
209 }
210 return result;
211 }
212
213 void AddOrUpdateDevice(int id) {
214 if (devices.find(id) != devices.end()) {
215 devices[id] = CANScanElement(timeout);
216 }
217 devices[id].UpdateLastSeen();
218 }
219
220 std::string Name() const { return name; }
221 uint32_t ArbId() const { return arbId; }
222
223 private:
224 std::string name;
225 uint64_t timeout;
226 uint32_t arbId;
227 std::unordered_map<int, CANScanElement> devices;
228 };
229
230 // Data structure is a map of all arbIds that point to a single shared
231 // collection and a vector of all collections (same pointer). This allows a
232 // fast lookup per arbID received and a way to iterate through by registered
233 // device
234 std::unordered_map<uint32_t, std::shared_ptr<CANScanCollection> >
235 m_registeredDevices;
236 std::vector<std::shared_ptr<CANScanCollection> > m_registeredList;
237
238 int m_streamBufferSize;
239 uint32_t m_streamHandle;
240 int m_threadInterval;
241
242 std::thread m_thread;
243 std::atomic_bool m_stopThread;
244 std::atomic_bool m_running;
245 std::string m_lastError;
246
247 void run();
248};
249
250} // namespace detail
251} // namespace rev
#define PACKED
Definition: CANDeviceScanner.h:102
Definition: CANDeviceScanner.h:172
~CANBusScanner()
Definition: CANDeviceScanner.cpp:55
bool Start()
Definition: CANDeviceScanner.cpp:64
std::vector< CANScanIdentifier > CANBusScan()
Definition: CANDeviceScanner.cpp:149
bool Running()
Definition: CANDeviceScanner.cpp:92
void Stop()
Definition: CANDeviceScanner.cpp:84
void RegisterDevice(std::string name, std::vector< uint32_t > validIds, int32_t maxFramePeriodMs=100)
Definition: CANDeviceScanner.cpp:133
std::string LastError()
Definition: CANDeviceScanner.cpp:94
CANBusScanner(int buffersize=256, int threadIntervalMs=10)
Definition: CANDeviceScanner.cpp:48
bool operator<(const CANScanIdentifier &lhs, const CANScanIdentifier &rhs)
Definition: CANDeviceScanner.h:148
bool operator==(const CANScanIdentifier &lhs, const CANScanIdentifier &rhs)
Definition: CANDeviceScanner.h:164
bool operator>(const CANScanIdentifier &lhs, const CANScanIdentifier &rhs)
Definition: CANDeviceScanner.h:156
Definition: AbsoluteEncoder.h:33
Definition: CANDeviceScanner.h:122
rev::FRCManufacturer manufacturerId
Definition: CANDeviceScanner.h:124
uint16_t canId
Definition: CANDeviceScanner.h:125
std::string Name() const
Definition: CANDeviceScanner.h:138
uint32_t uniqueId
Definition: CANDeviceScanner.h:126
rev::FRCDeviceType deviceTypeId
Definition: CANDeviceScanner.h:123
CANScanIdentifier(uint32_t arbid, std::string name, uint32_t uniqueId=0)
Definition: CANDeviceScanner.h:128
std::string name
Definition: CANDeviceScanner.h:127
Definition: CANDeviceScanner.h:105
uint16_t api
Definition: CANDeviceScanner.h:107
uint16_t deviceNumber
Definition: CANDeviceScanner.h:106
rev::FRCManufacturer manufacturer
Definition: CANDeviceScanner.h:108
uint8_t rsvd
Definition: CANDeviceScanner.h:110
rev::FRCDeviceType deviceType
Definition: CANDeviceScanner.h:109
Definition: CANDeviceScanner.h:117
frc_frameIDFields_t fields
Definition: CANDeviceScanner.h:118
uint32_t raw
Definition: CANDeviceScanner.h:119