| /*----------------------------------------------------------------------------*/ |
| /* Copyright (c) 2017-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 "hal/AnalogTrigger.h" |
| |
| #include "AnalogInternal.h" |
| #include "HALInitializer.h" |
| #include "PortsInternal.h" |
| #include "hal/AnalogInput.h" |
| #include "hal/Errors.h" |
| #include "hal/handles/HandlesInternal.h" |
| #include "hal/handles/LimitedHandleResource.h" |
| #include "mockdata/AnalogInDataInternal.h" |
| #include "mockdata/AnalogTriggerDataInternal.h" |
| |
| namespace { |
| struct AnalogTrigger { |
| HAL_AnalogInputHandle analogHandle; |
| uint8_t index; |
| HAL_Bool trigState; |
| }; |
| } // namespace |
| |
| using namespace hal; |
| |
| static LimitedHandleResource<HAL_AnalogTriggerHandle, AnalogTrigger, |
| kNumAnalogTriggers, HAL_HandleEnum::AnalogTrigger>* |
| analogTriggerHandles; |
| |
| namespace hal { |
| namespace init { |
| void InitializeAnalogTrigger() { |
| static LimitedHandleResource<HAL_AnalogTriggerHandle, AnalogTrigger, |
| kNumAnalogTriggers, |
| HAL_HandleEnum::AnalogTrigger> |
| atH; |
| analogTriggerHandles = &atH; |
| } |
| } // namespace init |
| } // namespace hal |
| |
| int32_t hal::GetAnalogTriggerInputIndex(HAL_AnalogTriggerHandle handle, |
| int32_t* status) { |
| auto trigger = analogTriggerHandles->Get(handle); |
| if (trigger == nullptr) { |
| *status = HAL_HANDLE_ERROR; |
| return -1; |
| } |
| |
| auto analog_port = analogInputHandles->Get(trigger->analogHandle); |
| if (analog_port == nullptr) { |
| *status = HAL_HANDLE_ERROR; |
| return -1; |
| } |
| |
| return analog_port->channel; |
| } |
| |
| extern "C" { |
| |
| HAL_AnalogTriggerHandle HAL_InitializeAnalogTrigger( |
| HAL_AnalogInputHandle portHandle, int32_t* status) { |
| hal::init::CheckInit(); |
| // ensure we are given a valid and active AnalogInput handle |
| auto analog_port = analogInputHandles->Get(portHandle); |
| if (analog_port == nullptr) { |
| *status = HAL_HANDLE_ERROR; |
| return HAL_kInvalidHandle; |
| } |
| HAL_AnalogTriggerHandle handle = analogTriggerHandles->Allocate(); |
| if (handle == HAL_kInvalidHandle) { |
| *status = NO_AVAILABLE_RESOURCES; |
| return HAL_kInvalidHandle; |
| } |
| auto trigger = analogTriggerHandles->Get(handle); |
| if (trigger == nullptr) { // would only occur on thread issue |
| *status = HAL_HANDLE_ERROR; |
| return HAL_kInvalidHandle; |
| } |
| trigger->analogHandle = portHandle; |
| trigger->index = static_cast<uint8_t>(getHandleIndex(handle)); |
| |
| SimAnalogTriggerData[trigger->index].initialized = true; |
| |
| trigger->trigState = false; |
| |
| return handle; |
| } |
| |
| HAL_AnalogTriggerHandle HAL_InitializeAnalogTriggerDutyCycle( |
| HAL_DutyCycleHandle dutyCycleHandle, int32_t* status) { |
| *status = HAL_SIM_NOT_SUPPORTED; |
| return HAL_kInvalidHandle; |
| } |
| |
| void HAL_CleanAnalogTrigger(HAL_AnalogTriggerHandle analogTriggerHandle, |
| int32_t* status) { |
| auto trigger = analogTriggerHandles->Get(analogTriggerHandle); |
| analogTriggerHandles->Free(analogTriggerHandle); |
| if (trigger == nullptr) return; |
| SimAnalogTriggerData[trigger->index].initialized = false; |
| // caller owns the analog input handle. |
| } |
| |
| static double GetAnalogValueToVoltage( |
| HAL_AnalogTriggerHandle analogTriggerHandle, int32_t value, |
| int32_t* status) { |
| int32_t LSBWeight = HAL_GetAnalogLSBWeight(analogTriggerHandle, status); |
| int32_t offset = HAL_GetAnalogOffset(analogTriggerHandle, status); |
| |
| double voltage = LSBWeight * 1.0e-9 * value - offset * 1.0e-9; |
| return voltage; |
| } |
| |
| void HAL_SetAnalogTriggerLimitsRaw(HAL_AnalogTriggerHandle analogTriggerHandle, |
| int32_t lower, int32_t upper, |
| int32_t* status) { |
| auto trigger = analogTriggerHandles->Get(analogTriggerHandle); |
| if (trigger == nullptr) { |
| *status = HAL_HANDLE_ERROR; |
| return; |
| } |
| if (lower > upper) { |
| *status = ANALOG_TRIGGER_LIMIT_ORDER_ERROR; |
| } |
| |
| double trigLower = |
| GetAnalogValueToVoltage(trigger->analogHandle, lower, status); |
| if (status != 0) return; |
| double trigUpper = |
| GetAnalogValueToVoltage(trigger->analogHandle, upper, status); |
| if (status != 0) return; |
| |
| SimAnalogTriggerData[trigger->index].triggerUpperBound = trigUpper; |
| SimAnalogTriggerData[trigger->index].triggerLowerBound = trigLower; |
| } |
| |
| void HAL_SetAnalogTriggerLimitsDutyCycle( |
| HAL_AnalogTriggerHandle analogTriggerHandle, double lower, double upper, |
| int32_t* status) { |
| *status = HAL_SIM_NOT_SUPPORTED; |
| } |
| |
| void HAL_SetAnalogTriggerLimitsVoltage( |
| HAL_AnalogTriggerHandle analogTriggerHandle, double lower, double upper, |
| int32_t* status) { |
| auto trigger = analogTriggerHandles->Get(analogTriggerHandle); |
| if (trigger == nullptr) { |
| *status = HAL_HANDLE_ERROR; |
| return; |
| } |
| if (lower > upper) { |
| *status = ANALOG_TRIGGER_LIMIT_ORDER_ERROR; |
| } |
| |
| SimAnalogTriggerData[trigger->index].triggerUpperBound = upper; |
| SimAnalogTriggerData[trigger->index].triggerLowerBound = lower; |
| } |
| void HAL_SetAnalogTriggerAveraged(HAL_AnalogTriggerHandle analogTriggerHandle, |
| HAL_Bool useAveragedValue, int32_t* status) { |
| auto trigger = analogTriggerHandles->Get(analogTriggerHandle); |
| if (trigger == nullptr) { |
| *status = HAL_HANDLE_ERROR; |
| return; |
| } |
| |
| AnalogTriggerData* triggerData = &SimAnalogTriggerData[trigger->index]; |
| |
| if (triggerData->triggerMode.Get() != HALSIM_AnalogTriggerUnassigned) { |
| *status = INCOMPATIBLE_STATE; |
| return; |
| } |
| |
| auto setVal = useAveragedValue ? HALSIM_AnalogTriggerAveraged |
| : HALSIM_AnalogTriggerUnassigned; |
| triggerData->triggerMode = setVal; |
| } |
| |
| void HAL_SetAnalogTriggerFiltered(HAL_AnalogTriggerHandle analogTriggerHandle, |
| HAL_Bool useFilteredValue, int32_t* status) { |
| auto trigger = analogTriggerHandles->Get(analogTriggerHandle); |
| if (trigger == nullptr) { |
| *status = HAL_HANDLE_ERROR; |
| return; |
| } |
| |
| AnalogTriggerData* triggerData = &SimAnalogTriggerData[trigger->index]; |
| |
| if (triggerData->triggerMode.Get() != HALSIM_AnalogTriggerUnassigned) { |
| *status = INCOMPATIBLE_STATE; |
| return; |
| } |
| |
| auto setVal = useFilteredValue ? HALSIM_AnalogTriggerFiltered |
| : HALSIM_AnalogTriggerUnassigned; |
| triggerData->triggerMode = setVal; |
| } |
| |
| static double GetTriggerValue(AnalogTrigger* trigger, int32_t* status) { |
| auto analogIn = analogInputHandles->Get(trigger->analogHandle); |
| if (analogIn == nullptr) { |
| // Returning HAL Handle Error, but going to ignore lower down |
| *status = HAL_HANDLE_ERROR; |
| return 0.0; |
| } |
| |
| return SimAnalogInData[analogIn->channel].voltage; |
| } |
| |
| HAL_Bool HAL_GetAnalogTriggerInWindow( |
| HAL_AnalogTriggerHandle analogTriggerHandle, int32_t* status) { |
| auto trigger = analogTriggerHandles->Get(analogTriggerHandle); |
| if (trigger == nullptr) { |
| *status = HAL_HANDLE_ERROR; |
| return false; |
| } |
| |
| double voltage = GetTriggerValue(trigger.get(), status); |
| if (*status == HAL_HANDLE_ERROR) { |
| // Don't error if analog has been destroyed |
| *status = 0; |
| return false; |
| } |
| |
| double trigUpper = SimAnalogTriggerData[trigger->index].triggerUpperBound; |
| double trigLower = SimAnalogTriggerData[trigger->index].triggerLowerBound; |
| |
| return voltage >= trigLower && voltage <= trigUpper; |
| } |
| HAL_Bool HAL_GetAnalogTriggerTriggerState( |
| HAL_AnalogTriggerHandle analogTriggerHandle, int32_t* status) { |
| auto trigger = analogTriggerHandles->Get(analogTriggerHandle); |
| if (trigger == nullptr) { |
| *status = HAL_HANDLE_ERROR; |
| return false; |
| } |
| |
| double voltage = GetTriggerValue(trigger.get(), status); |
| if (*status == HAL_HANDLE_ERROR) { |
| // Don't error if analog has been destroyed |
| *status = 0; |
| return false; |
| } |
| |
| double trigUpper = SimAnalogTriggerData[trigger->index].triggerUpperBound; |
| double trigLower = SimAnalogTriggerData[trigger->index].triggerLowerBound; |
| |
| if (voltage < trigLower) { |
| trigger->trigState = false; |
| return false; |
| } |
| if (voltage > trigUpper) { |
| trigger->trigState = true; |
| return true; |
| } |
| return trigger->trigState; |
| } |
| HAL_Bool HAL_GetAnalogTriggerOutput(HAL_AnalogTriggerHandle analogTriggerHandle, |
| HAL_AnalogTriggerType type, |
| int32_t* status) { |
| if (type == HAL_Trigger_kInWindow) { |
| return HAL_GetAnalogTriggerInWindow(analogTriggerHandle, status); |
| } else if (type == HAL_Trigger_kState) { |
| return HAL_GetAnalogTriggerTriggerState(analogTriggerHandle, status); |
| } else { |
| *status = ANALOG_TRIGGER_PULSE_OUTPUT_ERROR; |
| return false; |
| } |
| } |
| |
| int32_t HAL_GetAnalogTriggerFPGAIndex( |
| HAL_AnalogTriggerHandle analogTriggerHandle, int32_t* status) { |
| auto trigger = analogTriggerHandles->Get(analogTriggerHandle); |
| if (trigger == nullptr) { |
| *status = HAL_HANDLE_ERROR; |
| return -1; |
| } |
| return trigger->index; |
| } |
| |
| } // extern "C" |