/*----------------------------------------------------------------------------*/ /* Copyright (c) 2018-2020 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ /*----------------------------------------------------------------------------*/ #include "hal/CANAPI.h" #include #include "CANAPIInternal.h" #include "HALInitializer.h" #include "hal/CAN.h" #include "hal/Errors.h" #include "hal/HALBase.h" #include "hal/handles/UnlimitedHandleResource.h" using namespace hal; namespace { struct Receives { uint64_t lastTimeStamp; uint8_t data[8]; uint8_t length; }; struct CANStorage { HAL_CANManufacturer manufacturer; HAL_CANDeviceType deviceType; uint8_t deviceId; wpi::mutex mapMutex; wpi::SmallDenseMap periodicSends; wpi::SmallDenseMap receives; }; } // namespace static UnlimitedHandleResource* canHandles; static uint32_t GetPacketBaseTime() { int status = 0; auto basetime = HAL_GetFPGATime(&status); // us to ms return (basetime / 1000ull) & 0xFFFFFFFF; } namespace hal { namespace init { void InitializeCANAPI() { static UnlimitedHandleResource cH; canHandles = &cH; } } // namespace init namespace can { int32_t GetCANModuleFromHandle(HAL_CANHandle handle, int32_t* status) { auto can = canHandles->Get(handle); if (!can) { *status = HAL_HANDLE_ERROR; return -1; } return can->deviceId; } } // namespace can } // namespace hal static int32_t CreateCANId(CANStorage* storage, int32_t apiId) { int32_t createdId = 0; createdId |= (static_cast(storage->deviceType) & 0x1F) << 24; createdId |= (static_cast(storage->manufacturer) & 0xFF) << 16; createdId |= (apiId & 0x3FF) << 6; createdId |= (storage->deviceId & 0x3F); return createdId; } HAL_CANHandle HAL_InitializeCAN(HAL_CANManufacturer manufacturer, int32_t deviceId, HAL_CANDeviceType deviceType, int32_t* status) { hal::init::CheckInit(); auto can = std::make_shared(); auto handle = canHandles->Allocate(can); if (handle == HAL_kInvalidHandle) { *status = NO_AVAILABLE_RESOURCES; return HAL_kInvalidHandle; } can->deviceId = deviceId; can->deviceType = deviceType; can->manufacturer = manufacturer; return handle; } void HAL_CleanCAN(HAL_CANHandle handle) { auto data = canHandles->Free(handle); std::scoped_lock lock(data->mapMutex); for (auto&& i : data->periodicSends) { int32_t s = 0; auto id = CreateCANId(data.get(), i.first); HAL_CAN_SendMessage(id, nullptr, 0, HAL_CAN_SEND_PERIOD_STOP_REPEATING, &s); i.second = -1; } } void HAL_WriteCANPacket(HAL_CANHandle handle, const uint8_t* data, int32_t length, int32_t apiId, int32_t* status) { auto can = canHandles->Get(handle); if (!can) { *status = HAL_HANDLE_ERROR; return; } auto id = CreateCANId(can.get(), apiId); HAL_CAN_SendMessage(id, data, length, HAL_CAN_SEND_PERIOD_NO_REPEAT, status); if (*status != 0) { return; } std::scoped_lock lock(can->mapMutex); can->periodicSends[apiId] = -1; } void HAL_WriteCANPacketRepeating(HAL_CANHandle handle, const uint8_t* data, int32_t length, int32_t apiId, int32_t repeatMs, int32_t* status) { auto can = canHandles->Get(handle); if (!can) { *status = HAL_HANDLE_ERROR; return; } auto id = CreateCANId(can.get(), apiId); HAL_CAN_SendMessage(id, data, length, repeatMs, status); if (*status != 0) { return; } std::scoped_lock lock(can->mapMutex); can->periodicSends[apiId] = repeatMs; } void HAL_WriteCANRTRFrame(HAL_CANHandle handle, int32_t length, int32_t apiId, int32_t* status) { auto can = canHandles->Get(handle); if (!can) { *status = HAL_HANDLE_ERROR; return; } auto id = CreateCANId(can.get(), apiId); id |= HAL_CAN_IS_FRAME_REMOTE; uint8_t data[8]; std::memset(data, 0, sizeof(data)); HAL_CAN_SendMessage(id, data, length, HAL_CAN_SEND_PERIOD_NO_REPEAT, status); if (*status != 0) { return; } std::scoped_lock lock(can->mapMutex); can->periodicSends[apiId] = -1; } void HAL_StopCANPacketRepeating(HAL_CANHandle handle, int32_t apiId, int32_t* status) { auto can = canHandles->Get(handle); if (!can) { *status = HAL_HANDLE_ERROR; return; } auto id = CreateCANId(can.get(), apiId); HAL_CAN_SendMessage(id, nullptr, 0, HAL_CAN_SEND_PERIOD_STOP_REPEATING, status); if (*status != 0) { return; } std::scoped_lock lock(can->mapMutex); can->periodicSends[apiId] = -1; } void HAL_ReadCANPacketNew(HAL_CANHandle handle, int32_t apiId, uint8_t* data, int32_t* length, uint64_t* receivedTimestamp, int32_t* status) { auto can = canHandles->Get(handle); if (!can) { *status = HAL_HANDLE_ERROR; return; } uint32_t messageId = CreateCANId(can.get(), apiId); uint8_t dataSize = 0; uint32_t ts = 0; HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status); if (*status == 0) { std::scoped_lock lock(can->mapMutex); auto& msg = can->receives[messageId]; msg.length = dataSize; msg.lastTimeStamp = ts; // The NetComm call placed in data, copy into the msg std::memcpy(msg.data, data, dataSize); } *length = dataSize; *receivedTimestamp = ts; } void HAL_ReadCANPacketLatest(HAL_CANHandle handle, int32_t apiId, uint8_t* data, int32_t* length, uint64_t* receivedTimestamp, int32_t* status) { auto can = canHandles->Get(handle); if (!can) { *status = HAL_HANDLE_ERROR; return; } uint32_t messageId = CreateCANId(can.get(), apiId); uint8_t dataSize = 0; uint32_t ts = 0; HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status); std::scoped_lock lock(can->mapMutex); if (*status == 0) { // fresh update auto& msg = can->receives[messageId]; msg.length = dataSize; *length = dataSize; msg.lastTimeStamp = ts; *receivedTimestamp = ts; // The NetComm call placed in data, copy into the msg std::memcpy(msg.data, data, dataSize); } else { auto i = can->receives.find(messageId); if (i != can->receives.end()) { // Read the data from the stored message into the output std::memcpy(data, i->second.data, i->second.length); *length = i->second.length; *receivedTimestamp = i->second.lastTimeStamp; *status = 0; } } } void HAL_ReadCANPacketTimeout(HAL_CANHandle handle, int32_t apiId, uint8_t* data, int32_t* length, uint64_t* receivedTimestamp, int32_t timeoutMs, int32_t* status) { auto can = canHandles->Get(handle); if (!can) { *status = HAL_HANDLE_ERROR; return; } uint32_t messageId = CreateCANId(can.get(), apiId); uint8_t dataSize = 0; uint32_t ts = 0; HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status); std::scoped_lock lock(can->mapMutex); if (*status == 0) { // fresh update auto& msg = can->receives[messageId]; msg.length = dataSize; *length = dataSize; msg.lastTimeStamp = ts; *receivedTimestamp = ts; // The NetComm call placed in data, copy into the msg std::memcpy(msg.data, data, dataSize); } else { auto i = can->receives.find(messageId); if (i != can->receives.end()) { // Found, check if new enough uint32_t now = GetPacketBaseTime(); if (now - i->second.lastTimeStamp > static_cast(timeoutMs)) { // Timeout, return bad status *status = HAL_CAN_TIMEOUT; return; } // Read the data from the stored message into the output std::memcpy(data, i->second.data, i->second.length); *length = i->second.length; *receivedTimestamp = i->second.lastTimeStamp; *status = 0; } } } void HAL_ReadCANPeriodicPacket(HAL_CANHandle handle, int32_t apiId, uint8_t* data, int32_t* length, uint64_t* receivedTimestamp, int32_t timeoutMs, int32_t periodMs, int32_t* status) { auto can = canHandles->Get(handle); if (!can) { *status = HAL_HANDLE_ERROR; return; } uint32_t messageId = CreateCANId(can.get(), apiId); { std::scoped_lock lock(can->mapMutex); auto i = can->receives.find(messageId); if (i != can->receives.end()) { // Found, check if new enough uint32_t now = GetPacketBaseTime(); if (now - i->second.lastTimeStamp < static_cast(periodMs)) { *status = 0; // Read the data from the stored message into the output std::memcpy(data, i->second.data, i->second.length); *length = i->second.length; *receivedTimestamp = i->second.lastTimeStamp; return; } } } uint8_t dataSize = 0; uint32_t ts = 0; HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status); std::scoped_lock lock(can->mapMutex); if (*status == 0) { // fresh update auto& msg = can->receives[messageId]; msg.length = dataSize; *length = dataSize; msg.lastTimeStamp = ts; *receivedTimestamp = ts; // The NetComm call placed in data, copy into the msg std::memcpy(msg.data, data, dataSize); } else { auto i = can->receives.find(messageId); if (i != can->receives.end()) { // Found, check if new enough uint32_t now = GetPacketBaseTime(); if (now - i->second.lastTimeStamp > static_cast(timeoutMs)) { // Timeout, return bad status *status = HAL_CAN_TIMEOUT; return; } // Read the data from the stored message into the output std::memcpy(data, i->second.data, i->second.length); *length = i->second.length; *receivedTimestamp = i->second.lastTimeStamp; *status = 0; } } }