blob: 4d733bb5e0958103383221217fd1ad845ce5ae19 [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
Brian Silverman8fce7482020-01-05 13:18:21 -08004
5#include "hal/CANAPI.h"
6
Brian Silverman8fce7482020-01-05 13:18:21 -08007#include <wpi/DenseMap.h>
8
9#include "CANAPIInternal.h"
10#include "HALInitializer.h"
11#include "hal/CAN.h"
12#include "hal/Errors.h"
Austin Schuh1e69f942020-11-14 15:06:14 -080013#include "hal/HALBase.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080014#include "hal/handles/UnlimitedHandleResource.h"
15
16using namespace hal;
17
18namespace {
19struct Receives {
20 uint64_t lastTimeStamp;
21 uint8_t data[8];
22 uint8_t length;
23};
24
25struct CANStorage {
26 HAL_CANManufacturer manufacturer;
27 HAL_CANDeviceType deviceType;
28 uint8_t deviceId;
29 wpi::mutex mapMutex;
30 wpi::SmallDenseMap<int32_t, int32_t> periodicSends;
31 wpi::SmallDenseMap<int32_t, Receives> receives;
32};
33} // namespace
34
35static UnlimitedHandleResource<HAL_CANHandle, CANStorage, HAL_HandleEnum::CAN>*
36 canHandles;
37
38static uint32_t GetPacketBaseTime() {
39 int status = 0;
40 auto basetime = HAL_GetFPGATime(&status);
41 // us to ms
42 return (basetime / 1000ull) & 0xFFFFFFFF;
43}
44
45namespace hal {
46namespace init {
47void InitializeCANAPI() {
48 static UnlimitedHandleResource<HAL_CANHandle, CANStorage, HAL_HandleEnum::CAN>
49 cH;
50 canHandles = &cH;
51}
52} // namespace init
53namespace can {
54int32_t GetCANModuleFromHandle(HAL_CANHandle handle, int32_t* status) {
55 auto can = canHandles->Get(handle);
56 if (!can) {
57 *status = HAL_HANDLE_ERROR;
58 return -1;
59 }
60 return can->deviceId;
61}
62} // namespace can
63} // namespace hal
64
65static int32_t CreateCANId(CANStorage* storage, int32_t apiId) {
66 int32_t createdId = 0;
67 createdId |= (static_cast<int32_t>(storage->deviceType) & 0x1F) << 24;
68 createdId |= (static_cast<int32_t>(storage->manufacturer) & 0xFF) << 16;
69 createdId |= (apiId & 0x3FF) << 6;
70 createdId |= (storage->deviceId & 0x3F);
71 return createdId;
72}
73
74HAL_CANHandle HAL_InitializeCAN(HAL_CANManufacturer manufacturer,
75 int32_t deviceId, HAL_CANDeviceType deviceType,
76 int32_t* status) {
77 hal::init::CheckInit();
78 auto can = std::make_shared<CANStorage>();
79
80 auto handle = canHandles->Allocate(can);
81
82 if (handle == HAL_kInvalidHandle) {
83 *status = NO_AVAILABLE_RESOURCES;
84 return HAL_kInvalidHandle;
85 }
86
87 can->deviceId = deviceId;
88 can->deviceType = deviceType;
89 can->manufacturer = manufacturer;
90
91 return handle;
92}
93
94void HAL_CleanCAN(HAL_CANHandle handle) {
95 auto data = canHandles->Free(handle);
96
97 std::scoped_lock lock(data->mapMutex);
98
99 for (auto&& i : data->periodicSends) {
100 int32_t s = 0;
101 auto id = CreateCANId(data.get(), i.first);
102 HAL_CAN_SendMessage(id, nullptr, 0, HAL_CAN_SEND_PERIOD_STOP_REPEATING, &s);
103 i.second = -1;
104 }
105}
106
107void HAL_WriteCANPacket(HAL_CANHandle handle, const uint8_t* data,
108 int32_t length, int32_t apiId, int32_t* status) {
109 auto can = canHandles->Get(handle);
110 if (!can) {
111 *status = HAL_HANDLE_ERROR;
112 return;
113 }
114 auto id = CreateCANId(can.get(), apiId);
115
116 HAL_CAN_SendMessage(id, data, length, HAL_CAN_SEND_PERIOD_NO_REPEAT, status);
117
118 if (*status != 0) {
119 return;
120 }
121 std::scoped_lock lock(can->mapMutex);
122 can->periodicSends[apiId] = -1;
123}
124
125void HAL_WriteCANPacketRepeating(HAL_CANHandle handle, const uint8_t* data,
126 int32_t length, int32_t apiId,
127 int32_t repeatMs, int32_t* status) {
128 auto can = canHandles->Get(handle);
129 if (!can) {
130 *status = HAL_HANDLE_ERROR;
131 return;
132 }
133 auto id = CreateCANId(can.get(), apiId);
134
135 HAL_CAN_SendMessage(id, data, length, repeatMs, status);
136
137 if (*status != 0) {
138 return;
139 }
140 std::scoped_lock lock(can->mapMutex);
141 can->periodicSends[apiId] = repeatMs;
142}
143
144void HAL_WriteCANRTRFrame(HAL_CANHandle handle, int32_t length, int32_t apiId,
145 int32_t* status) {
146 auto can = canHandles->Get(handle);
147 if (!can) {
148 *status = HAL_HANDLE_ERROR;
149 return;
150 }
151 auto id = CreateCANId(can.get(), apiId);
152 id |= HAL_CAN_IS_FRAME_REMOTE;
153 uint8_t data[8];
154 std::memset(data, 0, sizeof(data));
155
156 HAL_CAN_SendMessage(id, data, length, HAL_CAN_SEND_PERIOD_NO_REPEAT, status);
157
158 if (*status != 0) {
159 return;
160 }
161 std::scoped_lock lock(can->mapMutex);
162 can->periodicSends[apiId] = -1;
163}
164
165void HAL_StopCANPacketRepeating(HAL_CANHandle handle, int32_t apiId,
166 int32_t* status) {
167 auto can = canHandles->Get(handle);
168 if (!can) {
169 *status = HAL_HANDLE_ERROR;
170 return;
171 }
172 auto id = CreateCANId(can.get(), apiId);
173
174 HAL_CAN_SendMessage(id, nullptr, 0, HAL_CAN_SEND_PERIOD_STOP_REPEATING,
175 status);
176
177 if (*status != 0) {
178 return;
179 }
180 std::scoped_lock lock(can->mapMutex);
181 can->periodicSends[apiId] = -1;
182}
183
184void HAL_ReadCANPacketNew(HAL_CANHandle handle, int32_t apiId, uint8_t* data,
185 int32_t* length, uint64_t* receivedTimestamp,
186 int32_t* status) {
187 auto can = canHandles->Get(handle);
188 if (!can) {
189 *status = HAL_HANDLE_ERROR;
190 return;
191 }
192
193 uint32_t messageId = CreateCANId(can.get(), apiId);
194 uint8_t dataSize = 0;
195 uint32_t ts = 0;
196 HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
197
198 if (*status == 0) {
199 std::scoped_lock lock(can->mapMutex);
200 auto& msg = can->receives[messageId];
201 msg.length = dataSize;
202 msg.lastTimeStamp = ts;
203 // The NetComm call placed in data, copy into the msg
204 std::memcpy(msg.data, data, dataSize);
205 }
206 *length = dataSize;
207 *receivedTimestamp = ts;
208}
209
210void HAL_ReadCANPacketLatest(HAL_CANHandle handle, int32_t apiId, uint8_t* data,
211 int32_t* length, uint64_t* receivedTimestamp,
212 int32_t* status) {
213 auto can = canHandles->Get(handle);
214 if (!can) {
215 *status = HAL_HANDLE_ERROR;
216 return;
217 }
218
219 uint32_t messageId = CreateCANId(can.get(), apiId);
220 uint8_t dataSize = 0;
221 uint32_t ts = 0;
222 HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
223
224 std::scoped_lock lock(can->mapMutex);
225 if (*status == 0) {
226 // fresh update
227 auto& msg = can->receives[messageId];
228 msg.length = dataSize;
229 *length = dataSize;
230 msg.lastTimeStamp = ts;
231 *receivedTimestamp = ts;
232 // The NetComm call placed in data, copy into the msg
233 std::memcpy(msg.data, data, dataSize);
234 } else {
235 auto i = can->receives.find(messageId);
236 if (i != can->receives.end()) {
237 // Read the data from the stored message into the output
238 std::memcpy(data, i->second.data, i->second.length);
239 *length = i->second.length;
240 *receivedTimestamp = i->second.lastTimeStamp;
241 *status = 0;
242 }
243 }
244}
245
246void HAL_ReadCANPacketTimeout(HAL_CANHandle handle, int32_t apiId,
247 uint8_t* data, int32_t* length,
248 uint64_t* receivedTimestamp, int32_t timeoutMs,
249 int32_t* status) {
250 auto can = canHandles->Get(handle);
251 if (!can) {
252 *status = HAL_HANDLE_ERROR;
253 return;
254 }
255
256 uint32_t messageId = CreateCANId(can.get(), apiId);
257 uint8_t dataSize = 0;
258 uint32_t ts = 0;
259 HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
260
261 std::scoped_lock lock(can->mapMutex);
262 if (*status == 0) {
263 // fresh update
264 auto& msg = can->receives[messageId];
265 msg.length = dataSize;
266 *length = dataSize;
267 msg.lastTimeStamp = ts;
268 *receivedTimestamp = ts;
269 // The NetComm call placed in data, copy into the msg
270 std::memcpy(msg.data, data, dataSize);
271 } else {
272 auto i = can->receives.find(messageId);
273 if (i != can->receives.end()) {
274 // Found, check if new enough
275 uint32_t now = GetPacketBaseTime();
276 if (now - i->second.lastTimeStamp > static_cast<uint32_t>(timeoutMs)) {
277 // Timeout, return bad status
278 *status = HAL_CAN_TIMEOUT;
279 return;
280 }
281 // Read the data from the stored message into the output
282 std::memcpy(data, i->second.data, i->second.length);
283 *length = i->second.length;
284 *receivedTimestamp = i->second.lastTimeStamp;
285 *status = 0;
286 }
287 }
288}
289
290void HAL_ReadCANPeriodicPacket(HAL_CANHandle handle, int32_t apiId,
291 uint8_t* data, int32_t* length,
292 uint64_t* receivedTimestamp, int32_t timeoutMs,
293 int32_t periodMs, int32_t* status) {
294 auto can = canHandles->Get(handle);
295 if (!can) {
296 *status = HAL_HANDLE_ERROR;
297 return;
298 }
299
300 uint32_t messageId = CreateCANId(can.get(), apiId);
301
302 {
303 std::scoped_lock lock(can->mapMutex);
304 auto i = can->receives.find(messageId);
305 if (i != can->receives.end()) {
306 // Found, check if new enough
307 uint32_t now = GetPacketBaseTime();
308 if (now - i->second.lastTimeStamp < static_cast<uint32_t>(periodMs)) {
309 *status = 0;
310 // Read the data from the stored message into the output
311 std::memcpy(data, i->second.data, i->second.length);
312 *length = i->second.length;
313 *receivedTimestamp = i->second.lastTimeStamp;
314 return;
315 }
316 }
317 }
318
319 uint8_t dataSize = 0;
320 uint32_t ts = 0;
321 HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
322
323 std::scoped_lock lock(can->mapMutex);
324 if (*status == 0) {
325 // fresh update
326 auto& msg = can->receives[messageId];
327 msg.length = dataSize;
328 *length = dataSize;
329 msg.lastTimeStamp = ts;
330 *receivedTimestamp = ts;
331 // The NetComm call placed in data, copy into the msg
332 std::memcpy(msg.data, data, dataSize);
333 } else {
334 auto i = can->receives.find(messageId);
335 if (i != can->receives.end()) {
336 // Found, check if new enough
337 uint32_t now = GetPacketBaseTime();
338 if (now - i->second.lastTimeStamp > static_cast<uint32_t>(timeoutMs)) {
339 // Timeout, return bad status
340 *status = HAL_CAN_TIMEOUT;
341 return;
342 }
343 // Read the data from the stored message into the output
344 std::memcpy(data, i->second.data, i->second.length);
345 *length = i->second.length;
346 *receivedTimestamp = i->second.lastTimeStamp;
347 *status = 0;
348 }
349 }
350}