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