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