2019-09-28 11:34:46 -07:00
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
/* Copyright (c) 2019 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 "mockdata/SimDeviceData.h" // NOLINT(build/include_order)
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
|
|
#include "SimDeviceDataInternal.h"
|
|
|
|
|
|
|
|
|
|
using namespace hal;
|
|
|
|
|
|
|
|
|
|
namespace hal {
|
|
|
|
|
namespace init {
|
|
|
|
|
void InitializeSimDeviceData() {
|
|
|
|
|
static SimDeviceData sdd;
|
|
|
|
|
::hal::SimSimDeviceData = &sdd;
|
|
|
|
|
}
|
|
|
|
|
} // namespace init
|
|
|
|
|
} // namespace hal
|
|
|
|
|
|
|
|
|
|
SimDeviceData* hal::SimSimDeviceData;
|
|
|
|
|
|
|
|
|
|
SimDeviceData::Device* SimDeviceData::LookupDevice(HAL_SimDeviceHandle handle) {
|
|
|
|
|
if (handle <= 0) return nullptr;
|
|
|
|
|
--handle;
|
|
|
|
|
if (static_cast<uint32_t>(handle) >= m_devices.size() || !m_devices[handle])
|
|
|
|
|
return nullptr;
|
|
|
|
|
return m_devices[handle].get();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SimDeviceData::Value* SimDeviceData::LookupValue(HAL_SimValueHandle handle) {
|
|
|
|
|
if (handle <= 0) return nullptr;
|
|
|
|
|
|
|
|
|
|
// look up device
|
|
|
|
|
Device* deviceImpl = LookupDevice(handle >> 16);
|
2019-10-13 22:25:59 -07:00
|
|
|
if (!deviceImpl) return nullptr;
|
2019-09-28 11:34:46 -07:00
|
|
|
|
|
|
|
|
// look up value
|
|
|
|
|
handle &= 0xffff;
|
|
|
|
|
--handle;
|
|
|
|
|
if (static_cast<uint32_t>(handle) >= deviceImpl->values.size() ||
|
|
|
|
|
!deviceImpl->values[handle])
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
return deviceImpl->values[handle].get();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HAL_SimDeviceHandle SimDeviceData::CreateDevice(const char* name) {
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
|
|
|
|
|
// check for duplicates and don't overwrite them
|
|
|
|
|
auto it = m_deviceMap.find(name);
|
|
|
|
|
if (it != m_deviceMap.end()) return 0;
|
|
|
|
|
|
|
|
|
|
// don't allow more than 4096 devices (limit driven by 12-bit allocation in
|
|
|
|
|
// value changed callback uid)
|
|
|
|
|
if (m_devices.size() >= 4095) return 0;
|
|
|
|
|
|
|
|
|
|
// create and save
|
|
|
|
|
auto deviceImpl = std::make_shared<Device>(name);
|
|
|
|
|
HAL_SimDeviceHandle deviceHandle = m_devices.emplace_back(deviceImpl) + 1;
|
|
|
|
|
deviceImpl->handle = deviceHandle;
|
|
|
|
|
m_deviceMap[name] = deviceImpl;
|
|
|
|
|
|
|
|
|
|
// notify callbacks
|
|
|
|
|
m_deviceCreated(name, deviceHandle);
|
|
|
|
|
|
|
|
|
|
return deviceHandle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SimDeviceData::FreeDevice(HAL_SimDeviceHandle handle) {
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
--handle;
|
|
|
|
|
|
|
|
|
|
// see if it exists
|
|
|
|
|
if (handle < 0 || static_cast<uint32_t>(handle) >= m_devices.size()) return;
|
|
|
|
|
auto deviceImpl = std::move(m_devices[handle]);
|
|
|
|
|
if (!deviceImpl) return;
|
|
|
|
|
|
|
|
|
|
// remove from map
|
|
|
|
|
m_deviceMap.erase(deviceImpl->name);
|
|
|
|
|
|
|
|
|
|
// remove from vector
|
|
|
|
|
m_devices.erase(handle);
|
|
|
|
|
|
|
|
|
|
// notify callbacks
|
|
|
|
|
m_deviceFreed(deviceImpl->name.c_str(), handle + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HAL_SimValueHandle SimDeviceData::CreateValue(HAL_SimDeviceHandle device,
|
|
|
|
|
const char* name, bool readonly,
|
|
|
|
|
int32_t numOptions,
|
|
|
|
|
const char** options,
|
|
|
|
|
const HAL_Value& initialValue) {
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
|
|
|
|
|
// look up device
|
|
|
|
|
Device* deviceImpl = LookupDevice(device);
|
|
|
|
|
if (!deviceImpl) return 0;
|
|
|
|
|
|
|
|
|
|
// check for duplicates and don't overwrite them
|
|
|
|
|
auto it = deviceImpl->valueMap.find(name);
|
|
|
|
|
if (it != deviceImpl->valueMap.end()) return 0;
|
|
|
|
|
|
|
|
|
|
// don't allow more than 4096 values per device (limit driven by 12-bit
|
|
|
|
|
// allocation in value changed callback uid)
|
|
|
|
|
if (deviceImpl->values.size() >= 4095) return 0;
|
|
|
|
|
|
|
|
|
|
// create and save; encode device into handle
|
|
|
|
|
auto valueImplPtr = std::make_unique<Value>(name, readonly, initialValue);
|
|
|
|
|
Value* valueImpl = valueImplPtr.get();
|
|
|
|
|
HAL_SimValueHandle valueHandle =
|
|
|
|
|
(device << 16) |
|
|
|
|
|
(deviceImpl->values.emplace_back(std::move(valueImplPtr)) + 1);
|
|
|
|
|
valueImpl->handle = valueHandle;
|
|
|
|
|
// copy options (if any provided)
|
|
|
|
|
if (numOptions > 0 && options) {
|
|
|
|
|
valueImpl->enumOptions.reserve(numOptions);
|
|
|
|
|
valueImpl->cstrEnumOptions.reserve(numOptions);
|
|
|
|
|
for (int32_t i = 0; i < numOptions; ++i) {
|
|
|
|
|
valueImpl->enumOptions.emplace_back(options[i]);
|
|
|
|
|
// point to our copy of the string, not the passed-in one
|
|
|
|
|
valueImpl->cstrEnumOptions.emplace_back(
|
|
|
|
|
valueImpl->enumOptions.back().c_str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
deviceImpl->valueMap[name] = valueImpl;
|
|
|
|
|
|
|
|
|
|
// notify callbacks
|
|
|
|
|
deviceImpl->valueCreated(name, valueHandle, readonly, &initialValue);
|
|
|
|
|
|
|
|
|
|
return valueHandle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HAL_Value SimDeviceData::GetValue(HAL_SimValueHandle handle) {
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
Value* valueImpl = LookupValue(handle);
|
|
|
|
|
|
|
|
|
|
if (!valueImpl) {
|
|
|
|
|
HAL_Value value;
|
|
|
|
|
value.data.v_int = 0;
|
|
|
|
|
value.type = HAL_UNASSIGNED;
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return valueImpl->value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SimDeviceData::SetValue(HAL_SimValueHandle handle,
|
|
|
|
|
const HAL_Value& value) {
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
Value* valueImpl = LookupValue(handle);
|
|
|
|
|
if (!valueImpl) return;
|
|
|
|
|
|
|
|
|
|
valueImpl->value = value;
|
|
|
|
|
|
|
|
|
|
// notify callbacks
|
|
|
|
|
valueImpl->changed(valueImpl->name.c_str(), valueImpl->handle,
|
|
|
|
|
valueImpl->readonly, &value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t SimDeviceData::RegisterDeviceCreatedCallback(
|
|
|
|
|
const char* prefix, void* param, HALSIM_SimDeviceCallback callback,
|
|
|
|
|
bool initialNotify) {
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
|
|
|
|
|
// register callback
|
|
|
|
|
int32_t index = m_deviceCreated.Register(prefix, param, callback);
|
|
|
|
|
|
|
|
|
|
// initial notifications
|
|
|
|
|
if (initialNotify) {
|
|
|
|
|
for (auto&& device : m_devices)
|
|
|
|
|
callback(device->name.c_str(), param, device->handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SimDeviceData::CancelDeviceCreatedCallback(int32_t uid) {
|
|
|
|
|
if (uid <= 0) return;
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
m_deviceCreated.Cancel(uid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t SimDeviceData::RegisterDeviceFreedCallback(
|
|
|
|
|
const char* prefix, void* param, HALSIM_SimDeviceCallback callback) {
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
return m_deviceFreed.Register(prefix, param, callback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SimDeviceData::CancelDeviceFreedCallback(int32_t uid) {
|
|
|
|
|
if (uid <= 0) return;
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
m_deviceFreed.Cancel(uid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HAL_SimDeviceHandle SimDeviceData::GetDeviceHandle(const char* name) {
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
auto it = m_deviceMap.find(name);
|
|
|
|
|
if (it == m_deviceMap.end()) return 0;
|
|
|
|
|
if (auto deviceImpl = it->getValue().lock())
|
|
|
|
|
return deviceImpl->handle;
|
|
|
|
|
else
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char* SimDeviceData::GetDeviceName(HAL_SimDeviceHandle handle) {
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
|
|
|
|
|
// look up device
|
|
|
|
|
Device* deviceImpl = LookupDevice(handle);
|
|
|
|
|
if (!deviceImpl) return nullptr;
|
|
|
|
|
|
|
|
|
|
return deviceImpl->name.c_str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SimDeviceData::EnumerateDevices(const char* prefix, void* param,
|
|
|
|
|
HALSIM_SimDeviceCallback callback) {
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
for (auto&& device : m_devices) {
|
|
|
|
|
if (wpi::StringRef{device->name}.startswith(prefix))
|
|
|
|
|
callback(device->name.c_str(), param, device->handle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t SimDeviceData::RegisterValueCreatedCallback(
|
|
|
|
|
HAL_SimDeviceHandle device, void* param, HALSIM_SimValueCallback callback,
|
|
|
|
|
bool initialNotify) {
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
Device* deviceImpl = LookupDevice(device);
|
|
|
|
|
if (!deviceImpl) return -1;
|
|
|
|
|
|
|
|
|
|
// register callback
|
|
|
|
|
int32_t index = deviceImpl->valueCreated.Register(callback, param);
|
|
|
|
|
|
|
|
|
|
// initial notifications
|
|
|
|
|
if (initialNotify) {
|
|
|
|
|
for (auto&& value : deviceImpl->values)
|
|
|
|
|
callback(value->name.c_str(), param, value->handle, value->readonly,
|
|
|
|
|
&value->value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// encode device into uid
|
|
|
|
|
return (device << 16) | (index & 0xffff);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SimDeviceData::CancelValueCreatedCallback(int32_t uid) {
|
|
|
|
|
if (uid <= 0) return;
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
Device* deviceImpl = LookupDevice(uid >> 16);
|
|
|
|
|
if (!deviceImpl) return;
|
|
|
|
|
deviceImpl->valueCreated.Cancel(uid & 0xffff);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t SimDeviceData::RegisterValueChangedCallback(
|
|
|
|
|
HAL_SimValueHandle handle, void* param, HALSIM_SimValueCallback callback,
|
|
|
|
|
bool initialNotify) {
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
Value* valueImpl = LookupValue(handle);
|
|
|
|
|
if (!valueImpl) return -1;
|
|
|
|
|
|
|
|
|
|
// register callback
|
|
|
|
|
int32_t index = valueImpl->changed.Register(callback, param);
|
|
|
|
|
|
|
|
|
|
// initial notification
|
|
|
|
|
if (initialNotify)
|
|
|
|
|
callback(valueImpl->name.c_str(), param, valueImpl->handle,
|
|
|
|
|
valueImpl->readonly, &valueImpl->value);
|
|
|
|
|
|
|
|
|
|
// encode device and value into uid
|
|
|
|
|
return (((handle >> 16) & 0xfff) << 19) | ((handle & 0xfff) << 7) |
|
|
|
|
|
(index & 0x7f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SimDeviceData::CancelValueChangedCallback(int32_t uid) {
|
|
|
|
|
if (uid <= 0) return;
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
Value* valueImpl = LookupValue(((uid >> 19) << 16) | ((uid >> 7) & 0xfff));
|
|
|
|
|
if (!valueImpl) return;
|
|
|
|
|
valueImpl->changed.Cancel(uid & 0x7f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HAL_SimValueHandle SimDeviceData::GetValueHandle(HAL_SimDeviceHandle device,
|
|
|
|
|
const char* name) {
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
Device* deviceImpl = LookupDevice(device);
|
|
|
|
|
if (!deviceImpl) return 0;
|
|
|
|
|
|
|
|
|
|
// lookup value
|
|
|
|
|
auto it = deviceImpl->valueMap.find(name);
|
|
|
|
|
if (it == deviceImpl->valueMap.end()) return 0;
|
|
|
|
|
if (!it->getValue()) return 0;
|
|
|
|
|
return it->getValue()->handle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SimDeviceData::EnumerateValues(HAL_SimDeviceHandle device, void* param,
|
|
|
|
|
HALSIM_SimValueCallback callback) {
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
Device* deviceImpl = LookupDevice(device);
|
|
|
|
|
if (!deviceImpl) return;
|
|
|
|
|
|
|
|
|
|
for (auto&& value : deviceImpl->values)
|
|
|
|
|
callback(value->name.c_str(), param, value->handle, value->readonly,
|
|
|
|
|
&value->value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char** SimDeviceData::GetValueEnumOptions(HAL_SimValueHandle handle,
|
|
|
|
|
int32_t* numOptions) {
|
|
|
|
|
*numOptions = 0;
|
|
|
|
|
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
Value* valueImpl = LookupValue(handle);
|
|
|
|
|
if (!valueImpl) return nullptr;
|
|
|
|
|
|
|
|
|
|
// get list of options (safe to return as they never change)
|
|
|
|
|
auto& options = valueImpl->cstrEnumOptions;
|
|
|
|
|
*numOptions = options.size();
|
|
|
|
|
return options.data();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SimDeviceData::ResetData() {
|
|
|
|
|
std::scoped_lock lock(m_mutex);
|
|
|
|
|
m_devices.clear();
|
|
|
|
|
m_deviceMap.clear();
|
|
|
|
|
m_deviceCreated.Reset();
|
|
|
|
|
m_deviceFreed.Reset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
|
|
|
|
|
int32_t HALSIM_RegisterSimDeviceCreatedCallback(
|
|
|
|
|
const char* prefix, void* param, HALSIM_SimDeviceCallback callback,
|
|
|
|
|
HAL_Bool initialNotify) {
|
|
|
|
|
return SimSimDeviceData->RegisterDeviceCreatedCallback(
|
|
|
|
|
prefix, param, callback, initialNotify);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HALSIM_CancelSimDeviceCreatedCallback(int32_t uid) {
|
|
|
|
|
SimSimDeviceData->CancelDeviceCreatedCallback(uid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t HALSIM_RegisterSimDeviceFreedCallback(
|
|
|
|
|
const char* prefix, void* param, HALSIM_SimDeviceCallback callback) {
|
|
|
|
|
return SimSimDeviceData->RegisterDeviceFreedCallback(prefix, param, callback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HALSIM_CancelSimDeviceFreedCallback(int32_t uid) {
|
|
|
|
|
SimSimDeviceData->CancelDeviceFreedCallback(uid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HAL_SimDeviceHandle HALSIM_GetSimDeviceHandle(const char* name) {
|
|
|
|
|
return SimSimDeviceData->GetDeviceHandle(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char* HALSIM_GetSimDeviceName(HAL_SimDeviceHandle handle) {
|
|
|
|
|
return SimSimDeviceData->GetDeviceName(handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HAL_SimDeviceHandle HALSIM_GetSimValueDeviceHandle(HAL_SimValueHandle handle) {
|
|
|
|
|
if (handle <= 0) return 0;
|
|
|
|
|
return handle >> 16;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HALSIM_EnumerateSimDevices(const char* prefix, void* param,
|
|
|
|
|
HALSIM_SimDeviceCallback callback) {
|
|
|
|
|
SimSimDeviceData->EnumerateDevices(prefix, param, callback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t HALSIM_RegisterSimValueCreatedCallback(HAL_SimDeviceHandle device,
|
|
|
|
|
void* param,
|
|
|
|
|
HALSIM_SimValueCallback callback,
|
|
|
|
|
HAL_Bool initialNotify) {
|
|
|
|
|
return SimSimDeviceData->RegisterValueCreatedCallback(device, param, callback,
|
|
|
|
|
initialNotify);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HALSIM_CancelSimValueCreatedCallback(int32_t uid) {
|
|
|
|
|
SimSimDeviceData->CancelValueCreatedCallback(uid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t HALSIM_RegisterSimValueChangedCallback(HAL_SimValueHandle handle,
|
|
|
|
|
void* param,
|
|
|
|
|
HALSIM_SimValueCallback callback,
|
|
|
|
|
HAL_Bool initialNotify) {
|
|
|
|
|
return SimSimDeviceData->RegisterValueChangedCallback(handle, param, callback,
|
|
|
|
|
initialNotify);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HALSIM_CancelSimValueChangedCallback(int32_t uid) {
|
|
|
|
|
SimSimDeviceData->CancelValueChangedCallback(uid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HAL_SimValueHandle HALSIM_GetSimValueHandle(HAL_SimDeviceHandle device,
|
|
|
|
|
const char* name) {
|
|
|
|
|
return SimSimDeviceData->GetValueHandle(device, name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HALSIM_EnumerateSimValues(HAL_SimDeviceHandle device, void* param,
|
|
|
|
|
HALSIM_SimValueCallback callback) {
|
|
|
|
|
SimSimDeviceData->EnumerateValues(device, param, callback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char** HALSIM_GetSimValueEnumOptions(HAL_SimValueHandle handle,
|
|
|
|
|
int32_t* numOptions) {
|
|
|
|
|
return SimSimDeviceData->GetValueEnumOptions(handle, numOptions);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HALSIM_ResetSimDeviceData(void) { SimSimDeviceData->ResetData(); }
|
|
|
|
|
|
|
|
|
|
} // extern "C"
|