REVLib - C++
CANDeviceScanner.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2018-2024 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 <stdint.h>
32
33#include <atomic>
34#include <map>
35#include <memory>
36#include <set>
37#include <string>
38#include <thread>
39#include <unordered_map>
40#include <utility>
41#include <vector>
42
43#include "rev/CANSparkMaxFrames.h"
44
45namespace rev {
46namespace detail {
47
48static const char* frc_deviceType_text[15] = {
49 "Broadcast",
50 "Robot Controller",
51 "Motor Controller",
52 "Relay Controller",
53 "GyroSenssor",
54 "Accelerometer Sensor",
55 "Ultrasonic Sensor",
56 "Gear Tooth Sensor",
57 "Power Distribution",
58 "Pneumatics Controller",
59 "Misc CAN Device",
60 "IO Breakout",
61 "RSVD",
62 "Firmware Update",
63 "Unknown",
64};
65
66static const char* frc_manufacturer_text[10] = {
67 "Broadcast", "NI", "LM", "DEKA", "CTRE",
68 "REV", "Grapple", "MindSensors", "TeamUse", "Unknown",
69};
70
71static inline const char* GetFRCDeviceTypeText(uint32_t index) {
72 if (index > 11 && index < 31) {
73 index = 12;
74 } else if (index > 31) {
75 index = 14;
76 } else if (index == 31) {
77 index = 13;
78 } else {
79 index = 0;
80 }
81 return frc_deviceType_text[index];
82}
83
84static inline const char* GetFRCManufacturerText(uint32_t index) {
85 index = (index > 9) ? 9 : index;
86 return frc_manufacturer_text[index];
87}
88
90 frc_deviceType_t deviceTypeId;
91 frc_manufacturer_t manufacturerId;
92 uint16_t canId;
93 uint32_t uniqueId;
94 std::string name;
95 CANScanIdentifier(uint32_t arbid, std::string name, uint32_t uniqueId = 0) {
96 frc_frameID_t frame;
97 frame.raw = arbid;
98 this->name = name;
99 this->uniqueId = uniqueId;
100 this->deviceTypeId = frame.fields.deviceType;
101 this->manufacturerId = frame.fields.manufacturer;
102 this->canId = frame.fields.deviceNumber;
103 }
104
105 std::string Name() {
106 if (name.empty()) {
107 return std::string(GetFRCManufacturerText(manufacturerId)) + " " +
108 std::string(GetFRCDeviceTypeText(deviceTypeId));
109 } else {
110 return name;
111 }
112 }
113};
114
115inline bool operator<(const CANScanIdentifier& lhs,
116 const CANScanIdentifier& rhs) {
117 return std::tie(lhs.deviceTypeId, lhs.manufacturerId, lhs.canId,
118 lhs.uniqueId) < std::tie(rhs.deviceTypeId,
119 rhs.manufacturerId, rhs.canId,
120 rhs.uniqueId);
121}
122
123inline bool operator>(const CANScanIdentifier& lhs,
124 const CANScanIdentifier& rhs) {
125 return std::tie(lhs.deviceTypeId, lhs.manufacturerId, lhs.canId,
126 lhs.uniqueId) > std::tie(rhs.deviceTypeId,
127 rhs.manufacturerId, rhs.canId,
128 rhs.uniqueId);
129}
130
131inline bool operator==(const CANScanIdentifier& lhs,
132 const CANScanIdentifier& rhs) {
133 return std::tie(lhs.deviceTypeId, lhs.manufacturerId, lhs.canId,
134 lhs.uniqueId) == std::tie(rhs.deviceTypeId,
135 rhs.manufacturerId, rhs.canId,
136 rhs.uniqueId);
137}
138
140public:
141 // Make sure the buffer size is large enough to capture all needed messages
142 // in the thread interval time
143 explicit CANBusScanner(int buffersize = 256, int threadIntervalMs = 10);
145
146 bool Start();
147 void Stop();
148 bool Running();
149 std::string LastError();
150 std::vector<CANScanIdentifier> CANBusScan();
151 void RegisterDevice(std::string name, std::vector<uint32_t> validIds,
152 int32_t maxFramePeriodMs = 100);
153
154private:
155 class CANScanElement {
156 uint64_t lastSeen;
157 uint64_t timeout;
158
159 public:
160 explicit CANScanElement(uint64_t timeoutMs = 1000);
161 void UpdateLastSeen();
162 bool IsActive() const;
163 };
164
165 class CANScanCollection {
166 public:
167 CANScanCollection(std::string name, uint32_t arbId, uint64_t timeoutMs)
168 : name(name), timeout(timeoutMs), arbId(arbId) {}
169
170 std::vector<int> ActiveDevices() const {
171 std::vector<int> result;
172 for (const auto& [devKey, devValue] : devices) {
173 if (devValue.IsActive()) {
174 result.push_back(devKey);
175 }
176 }
177 return result;
178 }
179
180 void AddOrUpdateDevice(int id) {
181 if (devices.find(id) != devices.end()) {
182 devices[id] = CANScanElement(timeout);
183 }
184 devices[id].UpdateLastSeen();
185 }
186
187 std::string Name() const { return name; }
188 uint32_t ArbId() const { return arbId; }
189
190 private:
191 std::string name;
192 uint64_t timeout;
193 uint32_t arbId;
194 std::unordered_map<int, CANScanElement> devices;
195 };
196
197 // Data structure is a map of all arbIds that point to a single shared
198 // collection and a vector of all collections (same pointer). This allows a
199 // fast lookup per arbID received and a way to iterate through by registered
200 // device
201 std::unordered_map<uint32_t, std::shared_ptr<CANScanCollection> >
202 m_registeredDevices;
203 std::vector<std::shared_ptr<CANScanCollection> > m_registeredList;
204
205 int m_streamBufferSize;
206 uint32_t m_streamHandle;
207 int m_threadInterval;
208
209 std::thread m_thread;
210 std::atomic_bool m_stopThread;
211 std::atomic_bool m_running;
212 std::string m_lastError;
213
214 void run();
215};
216
217} // namespace detail
218} // namespace rev
Definition: CANDeviceScanner.h:139
~CANBusScanner()
Definition: CANDeviceScanner.cpp:58
bool Start()
Definition: CANDeviceScanner.cpp:64
std::vector< CANScanIdentifier > CANBusScan()
Definition: CANDeviceScanner.cpp:147
bool Running()
Definition: CANDeviceScanner.cpp:90
void Stop()
Definition: CANDeviceScanner.cpp:82
void RegisterDevice(std::string name, std::vector< uint32_t > validIds, int32_t maxFramePeriodMs=100)
Definition: CANDeviceScanner.cpp:131
std::string LastError()
Definition: CANDeviceScanner.cpp:92
CANBusScanner(int buffersize=256, int threadIntervalMs=10)
Definition: CANDeviceScanner.cpp:51
bool operator<(const CANScanIdentifier &lhs, const CANScanIdentifier &rhs)
Definition: CANDeviceScanner.h:115
bool operator==(const CANScanIdentifier &lhs, const CANScanIdentifier &rhs)
Definition: CANDeviceScanner.h:131
bool operator>(const CANScanIdentifier &lhs, const CANScanIdentifier &rhs)
Definition: CANDeviceScanner.h:123
Definition: SparkLowLevel.cpp:40
Definition: CANDeviceScanner.h:89
frc_deviceType_t deviceTypeId
Definition: CANDeviceScanner.h:90
uint16_t canId
Definition: CANDeviceScanner.h:92
uint32_t uniqueId
Definition: CANDeviceScanner.h:93
CANScanIdentifier(uint32_t arbid, std::string name, uint32_t uniqueId=0)
Definition: CANDeviceScanner.h:95
std::string name
Definition: CANDeviceScanner.h:94
frc_manufacturer_t manufacturerId
Definition: CANDeviceScanner.h:91
std::string Name()
Definition: CANDeviceScanner.h:105