mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-29 02:21:44 +00:00
[hal, wpilib] Add REV PneumaticsHub (#3600)
This commit is contained in:
@@ -45,4 +45,8 @@ public class PortsJNI extends JNIWrapper {
|
||||
public static native int getNumREVPDHModules();
|
||||
|
||||
public static native int getNumREVPDHChannels();
|
||||
|
||||
public static native int getNumREVPHModules();
|
||||
|
||||
public static native int getNumREVPHChannels();
|
||||
}
|
||||
|
||||
32
hal/src/main/java/edu/wpi/first/hal/REVPHJNI.java
Normal file
32
hal/src/main/java/edu/wpi/first/hal/REVPHJNI.java
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.hal;
|
||||
|
||||
@SuppressWarnings("AbbreviationAsWordInName")
|
||||
public class REVPHJNI extends JNIWrapper {
|
||||
public static native int initialize(int module);
|
||||
|
||||
public static native void free(int handle);
|
||||
|
||||
public static native boolean checkSolenoidChannel(int channel);
|
||||
|
||||
public static native boolean getCompressor(int handle);
|
||||
|
||||
public static native void setClosedLoopControl(int handle, boolean enabled);
|
||||
|
||||
public static native boolean getClosedLoopControl(int handle);
|
||||
|
||||
public static native boolean getPressureSwitch(int handle);
|
||||
|
||||
public static native double getAnalogPressure(int handle, int channel);
|
||||
|
||||
public static native double getCompressorCurrent(int handle);
|
||||
|
||||
public static native int getSolenoids(int handle);
|
||||
|
||||
public static native void setSolenoids(int handle, int mask, int values);
|
||||
|
||||
public static native void fireOneShot(int handle, int index, int durMs);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.hal.simulation;
|
||||
|
||||
import edu.wpi.first.hal.JNIWrapper;
|
||||
|
||||
@SuppressWarnings("AbbreviationAsWordInName")
|
||||
public class REVPHDataJNI extends JNIWrapper {
|
||||
public static native int registerInitializedCallback(
|
||||
int index, NotifyCallback callback, boolean initialNotify);
|
||||
|
||||
public static native void cancelInitializedCallback(int index, int uid);
|
||||
|
||||
public static native boolean getInitialized(int index);
|
||||
|
||||
public static native void setInitialized(int index, boolean initialized);
|
||||
|
||||
public static native int registerSolenoidOutputCallback(
|
||||
int index, int channel, NotifyCallback callback, boolean initialNotify);
|
||||
|
||||
public static native void cancelSolenoidOutputCallback(int index, int channel, int uid);
|
||||
|
||||
public static native boolean getSolenoidOutput(int index, int channel);
|
||||
|
||||
public static native void setSolenoidOutput(int index, int channel, boolean solenoidOutput);
|
||||
|
||||
public static native int registerCompressorOnCallback(
|
||||
int index, NotifyCallback callback, boolean initialNotify);
|
||||
|
||||
public static native void cancelCompressorOnCallback(int index, int uid);
|
||||
|
||||
public static native boolean getCompressorOn(int index);
|
||||
|
||||
public static native void setCompressorOn(int index, boolean compressorOn);
|
||||
|
||||
public static native int registerClosedLoopEnabledCallback(
|
||||
int index, NotifyCallback callback, boolean initialNotify);
|
||||
|
||||
public static native void cancelClosedLoopEnabledCallback(int index, int uid);
|
||||
|
||||
public static native boolean getClosedLoopEnabled(int index);
|
||||
|
||||
public static native void setClosedLoopEnabled(int index, boolean closeLoopEnabled);
|
||||
|
||||
public static native int registerPressureSwitchCallback(
|
||||
int index, NotifyCallback callback, boolean initialNotify);
|
||||
|
||||
public static native void cancelPressureSwitchCallback(int index, int uid);
|
||||
|
||||
public static native boolean getPressureSwitch(int index);
|
||||
|
||||
public static native void setPressureSwitch(int index, boolean pressureSwitch);
|
||||
|
||||
public static native int registerCompressorCurrentCallback(
|
||||
int index, NotifyCallback callback, boolean initialNotify);
|
||||
|
||||
public static native void cancelCompressorCurrentCallback(int index, int uid);
|
||||
|
||||
public static native double getCompressorCurrent(int index);
|
||||
|
||||
public static native void setCompressorCurrent(int index, double compressorCurrent);
|
||||
|
||||
public static native void registerAllNonSolenoidCallbacks(
|
||||
int index, NotifyCallback callback, boolean initialNotify);
|
||||
|
||||
public static native void registerAllSolenoidCallbacks(
|
||||
int index, int channel, NotifyCallback callback, boolean initialNotify);
|
||||
|
||||
public static native void resetData(int index);
|
||||
}
|
||||
@@ -41,6 +41,7 @@ namespace hal {
|
||||
namespace init {
|
||||
void InitializeHAL() {
|
||||
InitializeCTREPCM();
|
||||
InitializeREVPH();
|
||||
InitializeAddressableLED();
|
||||
InitializeAccelerometer();
|
||||
InitializeAnalogAccumulator();
|
||||
|
||||
@@ -17,6 +17,7 @@ inline void CheckInit() {
|
||||
}
|
||||
|
||||
extern void InitializeCTREPCM();
|
||||
extern void InitializeREVPH();
|
||||
extern void InitializeAccelerometer();
|
||||
extern void InitializeAddressableLED();
|
||||
extern void InitializeAnalogAccumulator();
|
||||
|
||||
@@ -74,6 +74,12 @@ int32_t HAL_GetNumREVPDHModules(void) {
|
||||
int32_t HAL_GetNumREVPDHChannels(void) {
|
||||
return kNumREVPDHChannels;
|
||||
}
|
||||
int32_t HAL_GetNumREVPHModules(void) {
|
||||
return kNumREVPHModules;
|
||||
}
|
||||
int32_t HAL_GetNumREVPHChannels(void) {
|
||||
return kNumREVPHChannels;
|
||||
}
|
||||
int32_t HAL_GetNumDutyCycles(void) {
|
||||
return kNumDutyCycles;
|
||||
}
|
||||
|
||||
@@ -36,5 +36,7 @@ constexpr int32_t kNumREVPDHModules = 63;
|
||||
constexpr int32_t kNumREVPDHChannels = 24;
|
||||
constexpr int32_t kNumDutyCycles = tDutyCycle::kNumSystems;
|
||||
constexpr int32_t kNumAddressableLEDs = tLED::kNumSystems;
|
||||
constexpr int32_t kNumREVPHModules = 63;
|
||||
constexpr int32_t kNumREVPHChannels = 16;
|
||||
|
||||
} // namespace hal
|
||||
|
||||
479
hal/src/main/native/athena/REVPH.cpp
Normal file
479
hal/src/main/native/athena/REVPH.cpp
Normal file
@@ -0,0 +1,479 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "hal/REVPH.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "HALInitializer.h"
|
||||
#include "HALInternal.h"
|
||||
#include "PortsInternal.h"
|
||||
#include "hal/CANAPI.h"
|
||||
#include "hal/Errors.h"
|
||||
#include "hal/handles/IndexedHandleResource.h"
|
||||
#include "rev/PHFrames.h"
|
||||
|
||||
using namespace hal;
|
||||
|
||||
static constexpr HAL_CANManufacturer manufacturer =
|
||||
HAL_CANManufacturer::HAL_CAN_Man_kREV;
|
||||
|
||||
static constexpr HAL_CANDeviceType deviceType =
|
||||
HAL_CANDeviceType::HAL_CAN_Dev_kPneumatics;
|
||||
|
||||
static constexpr int32_t kDefaultControlPeriod = 20;
|
||||
// static constexpr uint8_t kDefaultSensorMask = (1 <<
|
||||
// HAL_REV_PHSENSOR_DIGITAL);
|
||||
static constexpr uint8_t kDefaultCompressorDuty = 255;
|
||||
static constexpr uint8_t kDefaultPressureTarget = 120;
|
||||
static constexpr uint8_t kDefaultPressureHysteresis = 60;
|
||||
|
||||
#define HAL_REV_MAX_PULSE_TIME 65534
|
||||
#define HAL_REV_MAX_PRESSURE_TARGET 120
|
||||
#define HAL_REV_MAX_PRESSURE_HYSTERESIS HAL_REV_MAX_PRESSURE_TARGET
|
||||
|
||||
static constexpr uint32_t APIFromExtId(uint32_t extId) {
|
||||
return (extId >> 6) & 0x3FF;
|
||||
}
|
||||
|
||||
static constexpr uint32_t PH_SET_ALL_FRAME_API =
|
||||
APIFromExtId(PH_SET_ALL_FRAME_ID);
|
||||
static constexpr uint32_t PH_PULSE_ONCE_FRAME_API =
|
||||
APIFromExtId(PH_PULSE_ONCE_FRAME_ID);
|
||||
static constexpr uint32_t PH_STATUS0_FRAME_API =
|
||||
APIFromExtId(PH_STATUS0_FRAME_ID);
|
||||
static constexpr uint32_t PH_STATUS1_FRAME_API =
|
||||
APIFromExtId(PH_STATUS1_FRAME_ID);
|
||||
|
||||
static constexpr int32_t kPHFrameStatus0Timeout = 50;
|
||||
static constexpr int32_t kPHFrameStatus1Timeout = 50;
|
||||
|
||||
namespace {
|
||||
|
||||
struct REV_PHObj {
|
||||
int32_t controlPeriod;
|
||||
PH_set_all_t desiredSolenoidsState;
|
||||
wpi::mutex solenoidLock;
|
||||
HAL_CANHandle hcan;
|
||||
std::string previousAllocation;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
static IndexedHandleResource<HAL_REVPHHandle, REV_PHObj, 63,
|
||||
HAL_HandleEnum::REVPH>* REVPHHandles;
|
||||
|
||||
namespace hal::init {
|
||||
void InitializeREVPH() {
|
||||
static IndexedHandleResource<HAL_REVPHHandle, REV_PHObj, kNumREVPHModules,
|
||||
HAL_HandleEnum::REVPH>
|
||||
rH;
|
||||
REVPHHandles = &rH;
|
||||
}
|
||||
} // namespace hal::init
|
||||
|
||||
static PH_status0_t HAL_REV_ReadPHStatus0(HAL_CANHandle hcan, int32_t* status) {
|
||||
uint8_t packedData[8] = {0};
|
||||
int32_t length = 0;
|
||||
uint64_t timestamp = 0;
|
||||
PH_status0_t result = {};
|
||||
|
||||
HAL_ReadCANPacketTimeout(hcan, PH_STATUS0_FRAME_API, packedData, &length,
|
||||
×tamp, kPHFrameStatus0Timeout * 2, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
PH_status0_unpack(&result, packedData, PH_STATUS0_LENGTH);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static PH_status1_t HAL_REV_ReadPHStatus1(HAL_CANHandle hcan, int32_t* status) {
|
||||
uint8_t packedData[8] = {0};
|
||||
int32_t length = 0;
|
||||
uint64_t timestamp = 0;
|
||||
PH_status1_t result = {};
|
||||
|
||||
HAL_ReadCANPacketTimeout(hcan, PH_STATUS1_FRAME_API, packedData, &length,
|
||||
×tamp, kPHFrameStatus1Timeout * 2, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
PH_status1_unpack(&result, packedData, PH_STATUS1_LENGTH);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
enum REV_SolenoidState {
|
||||
kSolenoidDisabled = 0,
|
||||
kSolenoidEnabled,
|
||||
kSolenoidControlledViaPulse
|
||||
};
|
||||
|
||||
static void HAL_REV_UpdateDesiredPHSolenoidState(REV_PHObj* hph,
|
||||
int32_t solenoid,
|
||||
REV_SolenoidState state) {
|
||||
switch (solenoid) {
|
||||
case 0:
|
||||
hph->desiredSolenoidsState.channel_0 = state;
|
||||
break;
|
||||
case 1:
|
||||
hph->desiredSolenoidsState.channel_1 = state;
|
||||
break;
|
||||
case 2:
|
||||
hph->desiredSolenoidsState.channel_2 = state;
|
||||
break;
|
||||
case 3:
|
||||
hph->desiredSolenoidsState.channel_3 = state;
|
||||
break;
|
||||
case 4:
|
||||
hph->desiredSolenoidsState.channel_4 = state;
|
||||
break;
|
||||
case 5:
|
||||
hph->desiredSolenoidsState.channel_5 = state;
|
||||
break;
|
||||
case 6:
|
||||
hph->desiredSolenoidsState.channel_6 = state;
|
||||
break;
|
||||
case 7:
|
||||
hph->desiredSolenoidsState.channel_7 = state;
|
||||
break;
|
||||
case 8:
|
||||
hph->desiredSolenoidsState.channel_8 = state;
|
||||
break;
|
||||
case 9:
|
||||
hph->desiredSolenoidsState.channel_9 = state;
|
||||
break;
|
||||
case 10:
|
||||
hph->desiredSolenoidsState.channel_10 = state;
|
||||
break;
|
||||
case 11:
|
||||
hph->desiredSolenoidsState.channel_11 = state;
|
||||
break;
|
||||
case 12:
|
||||
hph->desiredSolenoidsState.channel_12 = state;
|
||||
break;
|
||||
case 13:
|
||||
hph->desiredSolenoidsState.channel_13 = state;
|
||||
break;
|
||||
case 14:
|
||||
hph->desiredSolenoidsState.channel_14 = state;
|
||||
break;
|
||||
case 15:
|
||||
hph->desiredSolenoidsState.channel_15 = state;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void HAL_REV_SendSolenoidsState(REV_PHObj* hph, int32_t* status) {
|
||||
uint8_t packedData[PH_SET_ALL_LENGTH] = {0};
|
||||
PH_set_all_pack(packedData, &(hph->desiredSolenoidsState), PH_SET_ALL_LENGTH);
|
||||
HAL_WriteCANPacketRepeating(hph->hcan, packedData, PH_SET_ALL_LENGTH,
|
||||
PH_SET_ALL_FRAME_API, hph->controlPeriod, status);
|
||||
}
|
||||
|
||||
static HAL_Bool HAL_REV_CheckPHPulseTime(int32_t time) {
|
||||
return ((time > 0) && (time <= HAL_REV_MAX_PULSE_TIME)) ? 1 : 0;
|
||||
}
|
||||
|
||||
HAL_REVPHHandle HAL_InitializeREVPH(int32_t module,
|
||||
const char* allocationLocation,
|
||||
int32_t* status) {
|
||||
hal::init::CheckInit();
|
||||
if (!HAL_CheckREVPHModuleNumber(module)) {
|
||||
hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for REV PH", 1,
|
||||
kNumREVPHModules, module);
|
||||
return HAL_kInvalidHandle;
|
||||
}
|
||||
|
||||
HAL_REVPHHandle handle;
|
||||
auto hph = REVPHHandles->Allocate(module, &handle, status);
|
||||
if (*status != 0) {
|
||||
if (hph) {
|
||||
hal::SetLastErrorPreviouslyAllocated(status, "REV PH", module,
|
||||
hph->previousAllocation);
|
||||
} else {
|
||||
hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for REV PH", 1,
|
||||
kNumREVPHModules, module);
|
||||
}
|
||||
return HAL_kInvalidHandle; // failed to allocate. Pass error back.
|
||||
}
|
||||
|
||||
HAL_CANHandle hcan =
|
||||
HAL_InitializeCAN(manufacturer, module, deviceType, status);
|
||||
|
||||
if (*status != 0) {
|
||||
REVPHHandles->Free(handle);
|
||||
return HAL_kInvalidHandle;
|
||||
}
|
||||
|
||||
hph->previousAllocation = allocationLocation ? allocationLocation : "";
|
||||
hph->hcan = hcan;
|
||||
hph->controlPeriod = kDefaultControlPeriod;
|
||||
|
||||
// TODO any other things
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void HAL_FreeREVPH(HAL_REVPHHandle handle) {
|
||||
auto hph = REVPHHandles->Get(handle);
|
||||
if (hph == nullptr)
|
||||
return;
|
||||
|
||||
HAL_CleanCAN(hph->hcan);
|
||||
|
||||
REVPHHandles->Free(handle);
|
||||
}
|
||||
|
||||
HAL_Bool HAL_CheckREVPHModuleNumber(int32_t module) {
|
||||
return module >= 1 && module < kNumREVPDHModules;
|
||||
}
|
||||
|
||||
HAL_Bool HAL_CheckREVPHSolenoidChannel(int32_t channel) {
|
||||
return channel >= 0 && channel < kNumREVPHChannels;
|
||||
}
|
||||
|
||||
HAL_Bool HAL_GetREVPHCompressor(HAL_REVPHHandle handle, int32_t* status) {
|
||||
auto ph = REVPHHandles->Get(handle);
|
||||
if (ph == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
PH_status0_t status0 = HAL_REV_ReadPHStatus0(ph->hcan, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return status0.compressor_on;
|
||||
}
|
||||
|
||||
void HAL_SetREVPHClosedLoopControl(HAL_REVPHHandle handle, HAL_Bool enabled,
|
||||
int32_t* status) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
HAL_Bool HAL_GetREVPHClosedLoopControl(HAL_REVPHHandle handle,
|
||||
int32_t* status) {
|
||||
return false; // TODO
|
||||
}
|
||||
|
||||
HAL_Bool HAL_GetREVPHPressureSwitch(HAL_REVPHHandle handle, int32_t* status) {
|
||||
auto ph = REVPHHandles->Get(handle);
|
||||
if (ph == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
PH_status0_t status0 = HAL_REV_ReadPHStatus0(ph->hcan, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return status0.digital_sensor;
|
||||
}
|
||||
|
||||
double HAL_GetREVPHCompressorCurrent(HAL_REVPHHandle handle, int32_t* status) {
|
||||
auto ph = REVPHHandles->Get(handle);
|
||||
if (ph == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
PH_status1_t status1 = HAL_REV_ReadPHStatus1(ph->hcan, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return PH_status1_compressor_current_decode(status1.compressor_current);
|
||||
}
|
||||
|
||||
double HAL_GetREVPHAnalogPressure(HAL_REVPHHandle handle, int32_t channel,
|
||||
int32_t* status) {
|
||||
auto ph = REVPHHandles->Get(handle);
|
||||
if (ph == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (channel < 0 || channel > 1) {
|
||||
*status = PARAMETER_OUT_OF_RANGE;
|
||||
hal::SetLastErrorIndexOutOfRange(status, "Invalid REV Analog Index", 0, 2,
|
||||
channel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PH_status0_t status0 = HAL_REV_ReadPHStatus0(ph->hcan, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (channel == 1) {
|
||||
return PH_status0_analog_0_decode(status0.analog_0);
|
||||
}
|
||||
return PH_status0_analog_1_decode(status0.analog_1);
|
||||
}
|
||||
|
||||
int32_t HAL_GetREVPHSolenoids(HAL_REVPHHandle handle, int32_t* status) {
|
||||
auto ph = REVPHHandles->Get(handle);
|
||||
if (ph == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
PH_status0_t status0 = HAL_REV_ReadPHStatus0(ph->hcan, status);
|
||||
|
||||
if (*status != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t result = status0.channel_0;
|
||||
result |= status0.channel_1 << 1;
|
||||
result |= status0.channel_2 << 2;
|
||||
result |= status0.channel_3 << 3;
|
||||
result |= status0.channel_4 << 4;
|
||||
result |= status0.channel_5 << 5;
|
||||
result |= status0.channel_6 << 6;
|
||||
result |= status0.channel_7 << 7;
|
||||
result |= status0.channel_8 << 8;
|
||||
result |= status0.channel_9 << 9;
|
||||
result |= status0.channel_10 << 10;
|
||||
result |= status0.channel_11 << 11;
|
||||
result |= status0.channel_12 << 12;
|
||||
result |= status0.channel_13 << 13;
|
||||
result |= status0.channel_14 << 14;
|
||||
result |= status0.channel_15 << 15;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void HAL_SetREVPHSolenoids(HAL_REVPHHandle handle, int32_t mask, int32_t values,
|
||||
int32_t* status) {
|
||||
auto ph = REVPHHandles->Get(handle);
|
||||
if (ph == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
std::scoped_lock lock{ph->solenoidLock};
|
||||
for (int solenoid = 0; solenoid < kNumREVPHChannels; solenoid++) {
|
||||
if (mask & (1 << solenoid)) {
|
||||
// The mask bit for the solenoid is set, so we update the solenoid state
|
||||
REV_SolenoidState desiredSolenoidState =
|
||||
values & (1 << solenoid) ? kSolenoidEnabled : kSolenoidDisabled;
|
||||
HAL_REV_UpdateDesiredPHSolenoidState(ph.get(), solenoid,
|
||||
desiredSolenoidState);
|
||||
}
|
||||
}
|
||||
HAL_REV_SendSolenoidsState(ph.get(), status);
|
||||
}
|
||||
|
||||
void HAL_FireREVPHOneShot(HAL_REVPHHandle handle, int32_t index, int32_t durMs,
|
||||
int32_t* status) {
|
||||
auto ph = REVPHHandles->Get(handle);
|
||||
if (ph == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
if (index >= kNumREVPHChannels || index < 0) {
|
||||
*status = PARAMETER_OUT_OF_RANGE;
|
||||
hal::SetLastError(
|
||||
status,
|
||||
fmt::format("Only [0-15] are valid index values. Requested {}", index));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HAL_REV_CheckPHPulseTime(durMs)) {
|
||||
*status = PARAMETER_OUT_OF_RANGE;
|
||||
hal::SetLastError(
|
||||
status,
|
||||
fmt::format("Time not within expected range [0-65534]. Requested {}",
|
||||
durMs));
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::scoped_lock lock{ph->solenoidLock};
|
||||
HAL_REV_UpdateDesiredPHSolenoidState(ph.get(), index,
|
||||
kSolenoidControlledViaPulse);
|
||||
HAL_REV_SendSolenoidsState(ph.get(), status);
|
||||
}
|
||||
|
||||
if (*status != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
PH_pulse_once_t pulse = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
pulse.pulse_length_ms = durMs;
|
||||
|
||||
// Specify which solenoid should be pulsed
|
||||
// The protocol supports specifying any number of solenoids to be pulsed at
|
||||
// the same time, should that functionality be exposed to users in the future.
|
||||
switch (index) {
|
||||
case 0:
|
||||
pulse.channel_0 = true;
|
||||
break;
|
||||
case 1:
|
||||
pulse.channel_1 = true;
|
||||
break;
|
||||
case 2:
|
||||
pulse.channel_2 = true;
|
||||
break;
|
||||
case 3:
|
||||
pulse.channel_3 = true;
|
||||
break;
|
||||
case 4:
|
||||
pulse.channel_4 = true;
|
||||
break;
|
||||
case 5:
|
||||
pulse.channel_5 = true;
|
||||
break;
|
||||
case 6:
|
||||
pulse.channel_6 = true;
|
||||
break;
|
||||
case 7:
|
||||
pulse.channel_7 = true;
|
||||
break;
|
||||
case 8:
|
||||
pulse.channel_8 = true;
|
||||
break;
|
||||
case 9:
|
||||
pulse.channel_9 = true;
|
||||
break;
|
||||
case 10:
|
||||
pulse.channel_10 = true;
|
||||
break;
|
||||
case 11:
|
||||
pulse.channel_11 = true;
|
||||
break;
|
||||
case 12:
|
||||
pulse.channel_12 = true;
|
||||
break;
|
||||
case 13:
|
||||
pulse.channel_13 = true;
|
||||
break;
|
||||
case 14:
|
||||
pulse.channel_14 = true;
|
||||
break;
|
||||
case 15:
|
||||
pulse.channel_15 = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Send pulse command
|
||||
uint8_t packedData[PH_PULSE_ONCE_LENGTH] = {0};
|
||||
PH_pulse_once_pack(packedData, &pulse, PH_PULSE_ONCE_LENGTH);
|
||||
HAL_WriteCANPacket(ph->hcan, packedData, PH_PULSE_ONCE_LENGTH,
|
||||
PH_PULSE_ONCE_FRAME_API, status);
|
||||
}
|
||||
37
hal/src/main/native/athena/mockdata/REVPHData.cpp
Normal file
37
hal/src/main/native/athena/mockdata/REVPHData.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "hal/simulation/REVPHData.h"
|
||||
|
||||
#include "hal/simulation/SimDataValue.h"
|
||||
|
||||
extern "C" {
|
||||
void HALSIM_ResetREVPHData(int32_t index) {}
|
||||
|
||||
#define DEFINE_CAPI(TYPE, CAPINAME, RETURN) \
|
||||
HAL_SIMDATAVALUE_STUB_CAPI(TYPE, HALSIM, REVPH##CAPINAME, RETURN)
|
||||
|
||||
HAL_SIMDATAVALUE_STUB_CAPI_CHANNEL(HAL_Bool, HALSIM, REVPHSolenoidOutput, false)
|
||||
DEFINE_CAPI(HAL_Bool, Initialized, false)
|
||||
DEFINE_CAPI(HAL_Bool, CompressorOn, false)
|
||||
DEFINE_CAPI(HAL_Bool, ClosedLoopEnabled, false)
|
||||
DEFINE_CAPI(HAL_Bool, PressureSwitch, false)
|
||||
DEFINE_CAPI(double, CompressorCurrent, 0)
|
||||
|
||||
void HALSIM_GetREVPHAllSolenoids(int32_t index, uint8_t* values) {
|
||||
*values = 0;
|
||||
}
|
||||
|
||||
void HALSIM_SetREVPHAllSolenoids(int32_t index, uint8_t values) {}
|
||||
|
||||
void HALSIM_RegisterREVPHAllNonSolenoidCallbacks(int32_t index,
|
||||
HAL_NotifyCallback callback,
|
||||
void* param,
|
||||
HAL_Bool initialNotify) {}
|
||||
|
||||
void HALSIM_RegisterREVPHAllSolenoidCallbacks(int32_t index, int32_t channel,
|
||||
HAL_NotifyCallback callback,
|
||||
void* param,
|
||||
HAL_Bool initialNotify) {}
|
||||
} // extern "C"
|
||||
1720
hal/src/main/native/athena/rev/PHFrames.cpp
Normal file
1720
hal/src/main/native/athena/rev/PHFrames.cpp
Normal file
File diff suppressed because it is too large
Load Diff
3249
hal/src/main/native/athena/rev/PHFrames.h
Normal file
3249
hal/src/main/native/athena/rev/PHFrames.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -272,4 +272,30 @@ Java_edu_wpi_first_hal_PortsJNI_getNumREVPDHChannels
|
||||
jint value = HAL_GetNumREVPDHChannels();
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_PortsJNI
|
||||
* Method: getNumREVPHModules
|
||||
* Signature: ()I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_hal_PortsJNI_getNumREVPHModules
|
||||
(JNIEnv* env, jclass)
|
||||
{
|
||||
jint value = HAL_GetNumREVPHModules();
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_PortsJNI
|
||||
* Method: getNumREVPHChannels
|
||||
* Signature: ()I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_hal_PortsJNI_getNumREVPHChannels
|
||||
(JNIEnv* env, jclass)
|
||||
{
|
||||
jint value = HAL_GetNumREVPHChannels();
|
||||
return value;
|
||||
}
|
||||
} // extern "C"
|
||||
|
||||
191
hal/src/main/native/cpp/jni/REVPHJNI.cpp
Normal file
191
hal/src/main/native/cpp/jni/REVPHJNI.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <wpi/jni_util.h>
|
||||
|
||||
#include "HALUtil.h"
|
||||
#include "edu_wpi_first_hal_REVPHJNI.h"
|
||||
#include "hal/Ports.h"
|
||||
#include "hal/REVPH.h"
|
||||
#include "hal/handles/HandlesInternal.h"
|
||||
|
||||
using namespace hal;
|
||||
|
||||
extern "C" {
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_REVPHJNI
|
||||
* Method: initialize
|
||||
* Signature: (I)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_hal_REVPHJNI_initialize
|
||||
(JNIEnv* env, jclass, jint module)
|
||||
{
|
||||
int32_t status = 0;
|
||||
auto stack = wpi::java::GetJavaStackTrace(env, "edu.wpi.first");
|
||||
auto handle = HAL_InitializeREVPH(module, stack.c_str(), &status);
|
||||
CheckStatusForceThrow(env, status);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_REVPHJNI
|
||||
* Method: free
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_REVPHJNI_free
|
||||
(JNIEnv* env, jclass, jint handle)
|
||||
{
|
||||
HAL_FreeREVPH(handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_REVPHJNI
|
||||
* Method: checkSolenoidChannel
|
||||
* Signature: (I)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_edu_wpi_first_hal_REVPHJNI_checkSolenoidChannel
|
||||
(JNIEnv*, jclass, jint channel)
|
||||
{
|
||||
return HAL_CheckREVPHSolenoidChannel(channel);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_REVPHJNI
|
||||
* Method: getCompressor
|
||||
* Signature: (I)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_edu_wpi_first_hal_REVPHJNI_getCompressor
|
||||
(JNIEnv* env, jclass, jint handle)
|
||||
{
|
||||
int32_t status = 0;
|
||||
auto result = HAL_GetREVPHCompressor(handle, &status);
|
||||
CheckStatus(env, status, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_REVPHJNI
|
||||
* Method: setClosedLoopControl
|
||||
* Signature: (IZ)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_REVPHJNI_setClosedLoopControl
|
||||
(JNIEnv* env, jclass, jint handle, jboolean enabled)
|
||||
{
|
||||
int32_t status = 0;
|
||||
HAL_SetREVPHClosedLoopControl(handle, enabled, &status);
|
||||
CheckStatus(env, status, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_REVPHJNI
|
||||
* Method: getClosedLoopControl
|
||||
* Signature: (I)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_edu_wpi_first_hal_REVPHJNI_getClosedLoopControl
|
||||
(JNIEnv* env, jclass, jint handle)
|
||||
{
|
||||
int32_t status = 0;
|
||||
auto result = HAL_GetREVPHClosedLoopControl(handle, &status);
|
||||
CheckStatus(env, status, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_REVPHJNI
|
||||
* Method: getPressureSwitch
|
||||
* Signature: (I)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_edu_wpi_first_hal_REVPHJNI_getPressureSwitch
|
||||
(JNIEnv* env, jclass, jint handle)
|
||||
{
|
||||
int32_t status = 0;
|
||||
auto result = HAL_GetREVPHPressureSwitch(handle, &status);
|
||||
CheckStatus(env, status, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_REVPHJNI
|
||||
* Method: getAnalogPressure
|
||||
* Signature: (II)D
|
||||
*/
|
||||
JNIEXPORT jdouble JNICALL
|
||||
Java_edu_wpi_first_hal_REVPHJNI_getAnalogPressure
|
||||
(JNIEnv* env, jclass, jint handle, jint channel)
|
||||
{
|
||||
int32_t status = 0;
|
||||
auto result = HAL_GetREVPHAnalogPressure(handle, channel, &status);
|
||||
CheckStatus(env, status, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_REVPHJNI
|
||||
* Method: getCompressorCurrent
|
||||
* Signature: (I)D
|
||||
*/
|
||||
JNIEXPORT jdouble JNICALL
|
||||
Java_edu_wpi_first_hal_REVPHJNI_getCompressorCurrent
|
||||
(JNIEnv* env, jclass, jint handle)
|
||||
{
|
||||
int32_t status = 0;
|
||||
auto result = HAL_GetREVPHCompressorCurrent(handle, &status);
|
||||
CheckStatus(env, status, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_REVPHJNI
|
||||
* Method: getSolenoids
|
||||
* Signature: (I)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_hal_REVPHJNI_getSolenoids
|
||||
(JNIEnv* env, jclass, jint handle)
|
||||
{
|
||||
int32_t status = 0;
|
||||
auto result = HAL_GetREVPHSolenoids(handle, &status);
|
||||
CheckStatus(env, status, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_REVPHJNI
|
||||
* Method: setSolenoids
|
||||
* Signature: (III)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_REVPHJNI_setSolenoids
|
||||
(JNIEnv* env, jclass, jint handle, jint mask, jint value)
|
||||
{
|
||||
int32_t status = 0;
|
||||
HAL_SetREVPHSolenoids(handle, mask, value, &status);
|
||||
CheckStatus(env, status, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_REVPHJNI
|
||||
* Method: fireOneShot
|
||||
* Signature: (III)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_REVPHJNI_fireOneShot
|
||||
(JNIEnv* env, jclass, jint handle, jint index, jint durMs)
|
||||
{
|
||||
int32_t status = 0;
|
||||
HAL_FireREVPHOneShot(handle, index, durMs, &status);
|
||||
CheckStatus(env, status, false);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
365
hal/src/main/native/cpp/jni/simulation/REVPHDataJNI.cpp
Normal file
365
hal/src/main/native/cpp/jni/simulation/REVPHDataJNI.cpp
Normal file
@@ -0,0 +1,365 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "CallbackStore.h"
|
||||
#include "edu_wpi_first_hal_simulation_REVPHDataJNI.h"
|
||||
#include "hal/simulation/REVPHData.h"
|
||||
|
||||
using namespace hal;
|
||||
|
||||
extern "C" {
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: registerInitializedCallback
|
||||
* Signature: (ILjava/lang/Object;Z)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_registerInitializedCallback
|
||||
(JNIEnv* env, jclass, jint index, jobject callback, jboolean initialNotify)
|
||||
{
|
||||
return sim::AllocateCallback(env, index, callback, initialNotify,
|
||||
&HALSIM_RegisterREVPHInitializedCallback);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: cancelInitializedCallback
|
||||
* Signature: (II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_cancelInitializedCallback
|
||||
(JNIEnv* env, jclass, jint index, jint handle)
|
||||
{
|
||||
return sim::FreeCallback(env, handle, index,
|
||||
&HALSIM_CancelREVPHInitializedCallback);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: getInitialized
|
||||
* Signature: (I)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_getInitialized
|
||||
(JNIEnv*, jclass, jint index)
|
||||
{
|
||||
return HALSIM_GetREVPHInitialized(index);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: setInitialized
|
||||
* Signature: (IZ)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_setInitialized
|
||||
(JNIEnv*, jclass, jint index, jboolean value)
|
||||
{
|
||||
HALSIM_SetREVPHInitialized(index, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: registerSolenoidOutputCallback
|
||||
* Signature: (IILjava/lang/Object;Z)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_registerSolenoidOutputCallback
|
||||
(JNIEnv* env, jclass, jint index, jint channel, jobject callback,
|
||||
jboolean initialNotify)
|
||||
{
|
||||
return sim::AllocateChannelCallback(
|
||||
env, index, channel, callback, initialNotify,
|
||||
&HALSIM_RegisterREVPHSolenoidOutputCallback);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: cancelSolenoidOutputCallback
|
||||
* Signature: (III)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_cancelSolenoidOutputCallback
|
||||
(JNIEnv* env, jclass, jint index, jint channel, jint handle)
|
||||
{
|
||||
return sim::FreeChannelCallback(env, handle, index, channel,
|
||||
&HALSIM_CancelREVPHSolenoidOutputCallback);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: getSolenoidOutput
|
||||
* Signature: (II)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_getSolenoidOutput
|
||||
(JNIEnv*, jclass, jint index, jint channel)
|
||||
{
|
||||
return HALSIM_GetREVPHSolenoidOutput(index, channel);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: setSolenoidOutput
|
||||
* Signature: (IIZ)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_setSolenoidOutput
|
||||
(JNIEnv*, jclass, jint index, jint channel, jboolean value)
|
||||
{
|
||||
HALSIM_SetREVPHSolenoidOutput(index, channel, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: registerCompressorOnCallback
|
||||
* Signature: (ILjava/lang/Object;Z)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_registerCompressorOnCallback
|
||||
(JNIEnv* env, jclass, jint index, jobject callback, jboolean initialNotify)
|
||||
{
|
||||
return sim::AllocateCallback(env, index, callback, initialNotify,
|
||||
&HALSIM_RegisterREVPHCompressorOnCallback);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: cancelCompressorOnCallback
|
||||
* Signature: (II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_cancelCompressorOnCallback
|
||||
(JNIEnv* env, jclass, jint index, jint handle)
|
||||
{
|
||||
return sim::FreeCallback(env, handle, index,
|
||||
&HALSIM_CancelREVPHCompressorOnCallback);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: getCompressorOn
|
||||
* Signature: (I)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_getCompressorOn
|
||||
(JNIEnv*, jclass, jint index)
|
||||
{
|
||||
return HALSIM_GetREVPHCompressorOn(index);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: setCompressorOn
|
||||
* Signature: (IZ)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_setCompressorOn
|
||||
(JNIEnv*, jclass, jint index, jboolean value)
|
||||
{
|
||||
HALSIM_SetREVPHCompressorOn(index, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: registerClosedLoopEnabledCallback
|
||||
* Signature: (ILjava/lang/Object;Z)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_registerClosedLoopEnabledCallback
|
||||
(JNIEnv* env, jclass, jint index, jobject callback, jboolean initialNotify)
|
||||
{
|
||||
return sim::AllocateCallback(env, index, callback, initialNotify,
|
||||
&HALSIM_RegisterREVPHClosedLoopEnabledCallback);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: cancelClosedLoopEnabledCallback
|
||||
* Signature: (II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_cancelClosedLoopEnabledCallback
|
||||
(JNIEnv* env, jclass, jint index, jint handle)
|
||||
{
|
||||
return sim::FreeCallback(env, handle, index,
|
||||
&HALSIM_CancelREVPHClosedLoopEnabledCallback);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: getClosedLoopEnabled
|
||||
* Signature: (I)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_getClosedLoopEnabled
|
||||
(JNIEnv*, jclass, jint index)
|
||||
{
|
||||
return HALSIM_GetREVPHClosedLoopEnabled(index);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: setClosedLoopEnabled
|
||||
* Signature: (IZ)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_setClosedLoopEnabled
|
||||
(JNIEnv*, jclass, jint index, jboolean value)
|
||||
{
|
||||
HALSIM_SetREVPHClosedLoopEnabled(index, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: registerPressureSwitchCallback
|
||||
* Signature: (ILjava/lang/Object;Z)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_registerPressureSwitchCallback
|
||||
(JNIEnv* env, jclass, jint index, jobject callback, jboolean initialNotify)
|
||||
{
|
||||
return sim::AllocateCallback(env, index, callback, initialNotify,
|
||||
&HALSIM_RegisterREVPHPressureSwitchCallback);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: cancelPressureSwitchCallback
|
||||
* Signature: (II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_cancelPressureSwitchCallback
|
||||
(JNIEnv* env, jclass, jint index, jint handle)
|
||||
{
|
||||
return sim::FreeCallback(env, handle, index,
|
||||
&HALSIM_CancelREVPHPressureSwitchCallback);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: getPressureSwitch
|
||||
* Signature: (I)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_getPressureSwitch
|
||||
(JNIEnv*, jclass, jint index)
|
||||
{
|
||||
return HALSIM_GetREVPHPressureSwitch(index);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: setPressureSwitch
|
||||
* Signature: (IZ)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_setPressureSwitch
|
||||
(JNIEnv*, jclass, jint index, jboolean value)
|
||||
{
|
||||
HALSIM_SetREVPHPressureSwitch(index, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: registerCompressorCurrentCallback
|
||||
* Signature: (ILjava/lang/Object;Z)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_registerCompressorCurrentCallback
|
||||
(JNIEnv* env, jclass, jint index, jobject callback, jboolean initialNotify)
|
||||
{
|
||||
return sim::AllocateCallback(env, index, callback, initialNotify,
|
||||
&HALSIM_RegisterREVPHCompressorCurrentCallback);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: cancelCompressorCurrentCallback
|
||||
* Signature: (II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_cancelCompressorCurrentCallback
|
||||
(JNIEnv* env, jclass, jint index, jint handle)
|
||||
{
|
||||
return sim::FreeCallback(env, handle, index,
|
||||
&HALSIM_CancelREVPHCompressorCurrentCallback);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: getCompressorCurrent
|
||||
* Signature: (I)D
|
||||
*/
|
||||
JNIEXPORT jdouble JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_getCompressorCurrent
|
||||
(JNIEnv*, jclass, jint index)
|
||||
{
|
||||
return HALSIM_GetREVPHCompressorCurrent(index);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: setCompressorCurrent
|
||||
* Signature: (ID)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_setCompressorCurrent
|
||||
(JNIEnv*, jclass, jint index, jdouble value)
|
||||
{
|
||||
HALSIM_SetREVPHCompressorCurrent(index, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: registerAllNonSolenoidCallbacks
|
||||
* Signature: (ILjava/lang/Object;Z)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_registerAllNonSolenoidCallbacks
|
||||
(JNIEnv* env, jclass, jint index, jobject callback, jboolean initialNotify)
|
||||
{
|
||||
sim::AllocateCallback(
|
||||
env, index, callback, initialNotify,
|
||||
[](int32_t index, HAL_NotifyCallback cb, void* param, HAL_Bool in) {
|
||||
HALSIM_RegisterREVPHAllNonSolenoidCallbacks(index, cb, param, in);
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: registerAllSolenoidCallbacks
|
||||
* Signature: (IILjava/lang/Object;Z)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_registerAllSolenoidCallbacks
|
||||
(JNIEnv* env, jclass, jint index, jint channel, jobject callback,
|
||||
jboolean initialNotify)
|
||||
{
|
||||
sim::AllocateChannelCallback(
|
||||
env, index, channel, callback, initialNotify,
|
||||
[](int32_t index, int32_t channel, HAL_NotifyCallback cb, void* param,
|
||||
HAL_Bool in) {
|
||||
HALSIM_RegisterREVPHAllSolenoidCallbacks(index, channel, cb, param, in);
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_REVPHDataJNI
|
||||
* Method: resetData
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_REVPHDataJNI_resetData
|
||||
(JNIEnv*, jclass, jint index)
|
||||
{
|
||||
HALSIM_ResetREVPHData(index);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -156,6 +156,20 @@ int32_t HAL_GetNumREVPDHModules(void);
|
||||
*/
|
||||
int32_t HAL_GetNumREVPDHChannels(void);
|
||||
|
||||
/**
|
||||
* Gets the number of PH modules in the current system.
|
||||
*
|
||||
* @return the number of PH modules
|
||||
*/
|
||||
int32_t HAL_GetNumREVPHModules(void);
|
||||
|
||||
/**
|
||||
* Gets the number of PH channels in the current system.
|
||||
*
|
||||
* @return the number of PH channels
|
||||
*/
|
||||
int32_t HAL_GetNumREVPHChannels(void);
|
||||
|
||||
/**
|
||||
* Gets the number of duty cycle inputs in the current system.
|
||||
*
|
||||
|
||||
49
hal/src/main/native/include/hal/REVPH.h
Normal file
49
hal/src/main/native/include/hal/REVPH.h
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hal/Types.h"
|
||||
|
||||
/**
|
||||
* @defgroup hal_rev_ph REV PH Functions
|
||||
* @ingroup hal_capi
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
HAL_REVPHHandle HAL_InitializeREVPH(int32_t module,
|
||||
const char* allocationLocation,
|
||||
int32_t* status);
|
||||
|
||||
void HAL_FreeREVPH(HAL_REVPHHandle handle);
|
||||
|
||||
HAL_Bool HAL_CheckREVPHSolenoidChannel(int32_t channel);
|
||||
HAL_Bool HAL_CheckREVPHModuleNumber(int32_t module);
|
||||
|
||||
HAL_Bool HAL_GetREVPHCompressor(HAL_REVPHHandle handle, int32_t* status);
|
||||
void HAL_SetREVPHClosedLoopControl(HAL_REVPHHandle handle, HAL_Bool enabled,
|
||||
int32_t* status);
|
||||
HAL_Bool HAL_GetREVPHClosedLoopControl(HAL_REVPHHandle handle, int32_t* status);
|
||||
HAL_Bool HAL_GetREVPHPressureSwitch(HAL_REVPHHandle handle, int32_t* status);
|
||||
double HAL_GetREVPHCompressorCurrent(HAL_REVPHHandle handle, int32_t* status);
|
||||
double HAL_GetREVPHAnalogPressure(HAL_REVPHHandle handle, int32_t channel,
|
||||
int32_t* status);
|
||||
|
||||
int32_t HAL_GetREVPHSolenoids(HAL_REVPHHandle handle, int32_t* status);
|
||||
void HAL_SetREVPHSolenoids(HAL_REVPHHandle handle, int32_t mask, int32_t values,
|
||||
int32_t* status);
|
||||
|
||||
void HAL_FireREVPHOneShot(HAL_REVPHHandle handle, int32_t index, int32_t durMs,
|
||||
int32_t* status);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
/** @} */
|
||||
@@ -68,6 +68,8 @@ typedef HAL_Handle HAL_CTREPCMHandle;
|
||||
|
||||
typedef HAL_Handle HAL_REVPDHHandle;
|
||||
|
||||
typedef HAL_Handle HAL_REVPHHandle;
|
||||
|
||||
typedef int32_t HAL_Bool;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -68,7 +68,8 @@ enum class HAL_HandleEnum {
|
||||
AddressableLED = 23,
|
||||
CTREPCM = 24,
|
||||
CTREPDP = 25,
|
||||
REVPDH = 26
|
||||
REVPDH = 26,
|
||||
REVPH = 27,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
80
hal/src/main/native/include/hal/simulation/REVPHData.h
Normal file
80
hal/src/main/native/include/hal/simulation/REVPHData.h
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hal/Types.h"
|
||||
#include "hal/simulation/NotifyListener.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void HALSIM_ResetREVPHData(int32_t index);
|
||||
int32_t HALSIM_RegisterREVPHInitializedCallback(int32_t index,
|
||||
HAL_NotifyCallback callback,
|
||||
void* param,
|
||||
HAL_Bool initialNotify);
|
||||
void HALSIM_CancelREVPHInitializedCallback(int32_t index, int32_t uid);
|
||||
HAL_Bool HALSIM_GetREVPHInitialized(int32_t index);
|
||||
void HALSIM_SetREVPHInitialized(int32_t index, HAL_Bool solenoidInitialized);
|
||||
|
||||
int32_t HALSIM_RegisterREVPHSolenoidOutputCallback(int32_t index,
|
||||
int32_t channel,
|
||||
HAL_NotifyCallback callback,
|
||||
void* param,
|
||||
HAL_Bool initialNotify);
|
||||
void HALSIM_CancelREVPHSolenoidOutputCallback(int32_t index, int32_t channel,
|
||||
int32_t uid);
|
||||
HAL_Bool HALSIM_GetREVPHSolenoidOutput(int32_t index, int32_t channel);
|
||||
void HALSIM_SetREVPHSolenoidOutput(int32_t index, int32_t channel,
|
||||
HAL_Bool solenoidOutput);
|
||||
|
||||
int32_t HALSIM_RegisterREVPHCompressorOnCallback(int32_t index,
|
||||
HAL_NotifyCallback callback,
|
||||
void* param,
|
||||
HAL_Bool initialNotify);
|
||||
void HALSIM_CancelREVPHCompressorOnCallback(int32_t index, int32_t uid);
|
||||
HAL_Bool HALSIM_GetREVPHCompressorOn(int32_t index);
|
||||
void HALSIM_SetREVPHCompressorOn(int32_t index, HAL_Bool compressorOn);
|
||||
|
||||
int32_t HALSIM_RegisterREVPHClosedLoopEnabledCallback(
|
||||
int32_t index, HAL_NotifyCallback callback, void* param,
|
||||
HAL_Bool initialNotify);
|
||||
void HALSIM_CancelREVPHClosedLoopEnabledCallback(int32_t index, int32_t uid);
|
||||
HAL_Bool HALSIM_GetREVPHClosedLoopEnabled(int32_t index);
|
||||
void HALSIM_SetREVPHClosedLoopEnabled(int32_t index,
|
||||
HAL_Bool closedLoopEnabled);
|
||||
|
||||
int32_t HALSIM_RegisterREVPHPressureSwitchCallback(int32_t index,
|
||||
HAL_NotifyCallback callback,
|
||||
void* param,
|
||||
HAL_Bool initialNotify);
|
||||
void HALSIM_CancelREVPHPressureSwitchCallback(int32_t index, int32_t uid);
|
||||
HAL_Bool HALSIM_GetREVPHPressureSwitch(int32_t index);
|
||||
void HALSIM_SetREVPHPressureSwitch(int32_t index, HAL_Bool pressureSwitch);
|
||||
|
||||
int32_t HALSIM_RegisterREVPHCompressorCurrentCallback(
|
||||
int32_t index, HAL_NotifyCallback callback, void* param,
|
||||
HAL_Bool initialNotify);
|
||||
void HALSIM_CancelREVPHCompressorCurrentCallback(int32_t index, int32_t uid);
|
||||
double HALSIM_GetREVPHCompressorCurrent(int32_t index);
|
||||
void HALSIM_SetREVPHCompressorCurrent(int32_t index, double compressorCurrent);
|
||||
|
||||
void HALSIM_GetREVPHAllSolenoids(int32_t index, uint8_t* values);
|
||||
void HALSIM_SetREVPHAllSolenoids(int32_t index, uint8_t values);
|
||||
|
||||
void HALSIM_RegisterREVPHAllNonSolenoidCallbacks(int32_t index,
|
||||
HAL_NotifyCallback callback,
|
||||
void* param,
|
||||
HAL_Bool initialNotify);
|
||||
|
||||
void HALSIM_RegisterREVPHAllSolenoidCallbacks(int32_t index, int32_t channel,
|
||||
HAL_NotifyCallback callback,
|
||||
void* param,
|
||||
HAL_Bool initialNotify);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
@@ -48,7 +48,7 @@ HAL_CTREPCMHandle HAL_InitializeCTREPCM(int32_t module,
|
||||
pcm->previousAllocation);
|
||||
} else {
|
||||
hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for CTRE PCM", 0,
|
||||
kNumAccumulators, module);
|
||||
kNumCTREPCMModules, module);
|
||||
}
|
||||
return HAL_kInvalidHandle; // failed to allocate. Pass error back.
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ void InitializeHAL() {
|
||||
InitializeEncoderData();
|
||||
InitializeI2CData();
|
||||
InitializeCTREPCMData();
|
||||
InitializeREVPHData();
|
||||
InitializePowerDistributionData();
|
||||
InitializePWMData();
|
||||
InitializeRelayData();
|
||||
@@ -107,6 +108,7 @@ void InitializeHAL() {
|
||||
InitializePorts();
|
||||
InitializePower();
|
||||
InitializeCTREPCM();
|
||||
InitializeREVPH();
|
||||
InitializePWM();
|
||||
InitializeRelay();
|
||||
InitializeSerialPort();
|
||||
|
||||
@@ -32,6 +32,7 @@ extern void InitializeDriverStationData();
|
||||
extern void InitializeEncoderData();
|
||||
extern void InitializeI2CData();
|
||||
extern void InitializeCTREPCMData();
|
||||
extern void InitializeREVPHData();
|
||||
extern void InitializePowerDistributionData();
|
||||
extern void InitializePWMData();
|
||||
extern void InitializeRelayData();
|
||||
@@ -65,6 +66,7 @@ extern void InitializePowerDistribution();
|
||||
extern void InitializePorts();
|
||||
extern void InitializePower();
|
||||
extern void InitializeCTREPCM();
|
||||
extern void InitializeREVPH();
|
||||
extern void InitializePWM();
|
||||
extern void InitializeRelay();
|
||||
extern void InitializeSerialPort();
|
||||
|
||||
@@ -73,6 +73,12 @@ int32_t HAL_GetNumREVPDHModules(void) {
|
||||
int32_t HAL_GetNumREVPDHChannels(void) {
|
||||
return kNumREVPDHChannels;
|
||||
}
|
||||
int32_t HAL_GetNumREVPHModules(void) {
|
||||
return kNumREVPHModules;
|
||||
}
|
||||
int32_t HAL_GetNumREVPHChannels(void) {
|
||||
return kNumREVPHChannels;
|
||||
}
|
||||
int32_t HAL_GetNumDutyCycles(void) {
|
||||
return kNumDutyCycles;
|
||||
}
|
||||
|
||||
@@ -29,4 +29,6 @@ constexpr int32_t kNumREVPDHModules = 63;
|
||||
constexpr int32_t kNumREVPDHChannels = 24;
|
||||
constexpr int32_t kNumDutyCycles = 8;
|
||||
constexpr int32_t kNumAddressableLEDs = 1;
|
||||
constexpr int32_t kNumREVPHModules = 63;
|
||||
constexpr int32_t kNumREVPHChannels = 16;
|
||||
} // namespace hal
|
||||
|
||||
181
hal/src/main/native/sim/REVPH.cpp
Normal file
181
hal/src/main/native/sim/REVPH.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "hal/REVPH.h"
|
||||
|
||||
#include "HALInitializer.h"
|
||||
#include "HALInternal.h"
|
||||
#include "PortsInternal.h"
|
||||
#include "hal/CANAPI.h"
|
||||
#include "hal/Errors.h"
|
||||
#include "hal/handles/IndexedHandleResource.h"
|
||||
#include "mockdata/REVPHDataInternal.h"
|
||||
|
||||
using namespace hal;
|
||||
|
||||
namespace {
|
||||
struct PCM {
|
||||
int32_t module;
|
||||
wpi::mutex lock;
|
||||
std::string previousAllocation;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static IndexedHandleResource<HAL_REVPHHandle, PCM, kNumREVPHModules,
|
||||
HAL_HandleEnum::REVPH>* pcmHandles;
|
||||
|
||||
namespace hal::init {
|
||||
void InitializeREVPH() {
|
||||
static IndexedHandleResource<HAL_REVPHHandle, PCM, kNumREVPHModules,
|
||||
HAL_HandleEnum::REVPH>
|
||||
pH;
|
||||
pcmHandles = &pH;
|
||||
}
|
||||
} // namespace hal::init
|
||||
|
||||
HAL_REVPHHandle HAL_InitializeREVPH(int32_t module,
|
||||
const char* allocationLocation,
|
||||
int32_t* status) {
|
||||
hal::init::CheckInit();
|
||||
|
||||
if (module == 0) {
|
||||
hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for REV PH", 1,
|
||||
kNumREVPHModules, module);
|
||||
return HAL_kInvalidHandle;
|
||||
}
|
||||
|
||||
HAL_REVPHHandle handle;
|
||||
auto pcm = pcmHandles->Allocate(module, &handle, status);
|
||||
|
||||
if (*status != 0) {
|
||||
if (pcm) {
|
||||
hal::SetLastErrorPreviouslyAllocated(status, "REV PH", module,
|
||||
pcm->previousAllocation);
|
||||
} else {
|
||||
hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for REV PH", 1,
|
||||
kNumREVPHModules, module);
|
||||
}
|
||||
return HAL_kInvalidHandle; // failed to allocate. Pass error back.
|
||||
}
|
||||
|
||||
pcm->previousAllocation = allocationLocation ? allocationLocation : "";
|
||||
pcm->module = module;
|
||||
|
||||
SimREVPHData[module].initialized = true;
|
||||
// Enable closed loop
|
||||
SimREVPHData[module].closedLoopEnabled = true;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void HAL_FreeREVPH(HAL_REVPHHandle handle) {
|
||||
auto pcm = pcmHandles->Get(handle);
|
||||
if (pcm == nullptr) {
|
||||
pcmHandles->Free(handle);
|
||||
return;
|
||||
}
|
||||
SimREVPHData[pcm->module].initialized = false;
|
||||
pcmHandles->Free(handle);
|
||||
}
|
||||
|
||||
HAL_Bool HAL_CheckREVPHModuleNumber(int32_t module) {
|
||||
return module >= 1 && module < kNumREVPDHModules;
|
||||
}
|
||||
|
||||
HAL_Bool HAL_CheckREVPHSolenoidChannel(int32_t channel) {
|
||||
return channel < kNumREVPHChannels && channel >= 0;
|
||||
}
|
||||
|
||||
HAL_Bool HAL_GetREVPHCompressor(HAL_REVPHHandle handle, int32_t* status) {
|
||||
auto pcm = pcmHandles->Get(handle);
|
||||
if (pcm == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
return SimREVPHData[pcm->module].compressorOn;
|
||||
}
|
||||
|
||||
void HAL_SetREVPHClosedLoopControl(HAL_REVPHHandle handle, HAL_Bool enabled,
|
||||
int32_t* status) {
|
||||
auto pcm = pcmHandles->Get(handle);
|
||||
if (pcm == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
SimREVPHData[pcm->module].closedLoopEnabled = enabled;
|
||||
}
|
||||
|
||||
HAL_Bool HAL_GetREVPHClosedLoopControl(HAL_REVPHHandle handle,
|
||||
int32_t* status) {
|
||||
auto pcm = pcmHandles->Get(handle);
|
||||
if (pcm == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
return SimREVPHData[pcm->module].closedLoopEnabled;
|
||||
}
|
||||
|
||||
HAL_Bool HAL_GetREVPHPressureSwitch(HAL_REVPHHandle handle, int32_t* status) {
|
||||
auto pcm = pcmHandles->Get(handle);
|
||||
if (pcm == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
return SimREVPHData[pcm->module].pressureSwitch;
|
||||
}
|
||||
|
||||
double HAL_GetREVPHAnalogPressure(HAL_REVPHHandle handle, int32_t channel,
|
||||
int32_t* status) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
double HAL_GetREVPHCompressorCurrent(HAL_REVPHHandle handle, int32_t* status) {
|
||||
auto pcm = pcmHandles->Get(handle);
|
||||
if (pcm == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return SimREVPHData[pcm->module].compressorCurrent;
|
||||
}
|
||||
|
||||
int32_t HAL_GetREVPHSolenoids(HAL_REVPHHandle handle, int32_t* status) {
|
||||
auto pcm = pcmHandles->Get(handle);
|
||||
if (pcm == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::scoped_lock lock{pcm->lock};
|
||||
auto& data = SimREVPHData[pcm->module].solenoidOutput;
|
||||
uint8_t ret = 0;
|
||||
for (int i = 0; i < kNumREVPHChannels; i++) {
|
||||
ret |= (data[i] << i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void HAL_SetREVPHSolenoids(HAL_REVPHHandle handle, int32_t mask, int32_t values,
|
||||
int32_t* status) {
|
||||
auto pcm = pcmHandles->Get(handle);
|
||||
if (pcm == nullptr) {
|
||||
*status = HAL_HANDLE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
auto& data = SimREVPHData[pcm->module].solenoidOutput;
|
||||
std::scoped_lock lock{pcm->lock};
|
||||
for (int i = 0; i < kNumREVPHChannels; i++) {
|
||||
auto indexMask = (1 << i);
|
||||
if ((mask & indexMask) != 0) {
|
||||
data[i] = (values & indexMask) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_FireREVPHOneShot(HAL_REVPHHandle handle, int32_t index, int32_t durMs,
|
||||
int32_t* status) {}
|
||||
83
hal/src/main/native/sim/mockdata/REVPHData.cpp
Normal file
83
hal/src/main/native/sim/mockdata/REVPHData.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "../PortsInternal.h"
|
||||
#include "REVPHDataInternal.h"
|
||||
|
||||
using namespace hal;
|
||||
|
||||
namespace hal::init {
|
||||
void InitializeREVPHData() {
|
||||
static REVPHData spd[kNumREVPHModules];
|
||||
::hal::SimREVPHData = spd;
|
||||
}
|
||||
} // namespace hal::init
|
||||
|
||||
REVPHData* hal::SimREVPHData;
|
||||
void REVPHData::ResetData() {
|
||||
for (int i = 0; i < kNumREVPHChannels; i++) {
|
||||
solenoidOutput[i].Reset(false);
|
||||
}
|
||||
initialized.Reset(false);
|
||||
compressorOn.Reset(false);
|
||||
closedLoopEnabled.Reset(true);
|
||||
pressureSwitch.Reset(false);
|
||||
compressorCurrent.Reset(0.0);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void HALSIM_ResetREVPHData(int32_t index) {
|
||||
SimREVPHData[index].ResetData();
|
||||
}
|
||||
|
||||
#define DEFINE_CAPI(TYPE, CAPINAME, LOWERNAME) \
|
||||
HAL_SIMDATAVALUE_DEFINE_CAPI(TYPE, HALSIM, REVPH##CAPINAME, SimREVPHData, \
|
||||
LOWERNAME)
|
||||
|
||||
HAL_SIMDATAVALUE_DEFINE_CAPI_CHANNEL(HAL_Bool, HALSIM, REVPHSolenoidOutput,
|
||||
SimREVPHData, solenoidOutput)
|
||||
DEFINE_CAPI(HAL_Bool, Initialized, initialized)
|
||||
DEFINE_CAPI(HAL_Bool, CompressorOn, compressorOn)
|
||||
DEFINE_CAPI(HAL_Bool, ClosedLoopEnabled, closedLoopEnabled)
|
||||
DEFINE_CAPI(HAL_Bool, PressureSwitch, pressureSwitch)
|
||||
DEFINE_CAPI(double, CompressorCurrent, compressorCurrent)
|
||||
|
||||
void HALSIM_GetREVPHAllSolenoids(int32_t index, uint8_t* values) {
|
||||
auto& data = SimREVPHData[index].solenoidOutput;
|
||||
uint8_t ret = 0;
|
||||
for (int i = 0; i < kNumCTRESolenoidChannels; i++) {
|
||||
ret |= (data[i] << i);
|
||||
}
|
||||
*values = ret;
|
||||
}
|
||||
|
||||
void HALSIM_SetREVPHAllSolenoids(int32_t index, uint8_t values) {
|
||||
auto& data = SimREVPHData[index].solenoidOutput;
|
||||
for (int i = 0; i < kNumCTRESolenoidChannels; i++) {
|
||||
data[i] = (values & 0x1) != 0;
|
||||
values >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
#define REGISTER(NAME) \
|
||||
SimREVPHData[index].NAME.RegisterCallback(callback, param, initialNotify)
|
||||
|
||||
void HALSIM_RegisterREVPHAllNonSolenoidCallbacks(int32_t index,
|
||||
HAL_NotifyCallback callback,
|
||||
void* param,
|
||||
HAL_Bool initialNotify) {
|
||||
REGISTER(initialized);
|
||||
REGISTER(compressorOn);
|
||||
REGISTER(closedLoopEnabled);
|
||||
REGISTER(pressureSwitch);
|
||||
REGISTER(compressorCurrent);
|
||||
}
|
||||
|
||||
void HALSIM_RegisterREVPHAllSolenoidCallbacks(int32_t index, int32_t channel,
|
||||
HAL_NotifyCallback callback,
|
||||
void* param,
|
||||
HAL_Bool initialNotify) {
|
||||
REGISTER(solenoidOutput[channel]);
|
||||
}
|
||||
} // extern "C"
|
||||
43
hal/src/main/native/sim/mockdata/REVPHDataInternal.h
Normal file
43
hal/src/main/native/sim/mockdata/REVPHDataInternal.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../PortsInternal.h"
|
||||
#include "hal/simulation/REVPHData.h"
|
||||
#include "hal/simulation/SimDataValue.h"
|
||||
|
||||
namespace hal {
|
||||
class REVPHData {
|
||||
HAL_SIMDATAVALUE_DEFINE_NAME(Initialized)
|
||||
HAL_SIMDATAVALUE_DEFINE_NAME(SolenoidOutput)
|
||||
HAL_SIMDATAVALUE_DEFINE_NAME(CompressorOn)
|
||||
HAL_SIMDATAVALUE_DEFINE_NAME(ClosedLoopEnabled)
|
||||
HAL_SIMDATAVALUE_DEFINE_NAME(PressureSwitch)
|
||||
HAL_SIMDATAVALUE_DEFINE_NAME(CompressorCurrent)
|
||||
|
||||
static LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr HAL_Bool
|
||||
GetSolenoidOutputDefault() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
SimDataValue<HAL_Bool, HAL_MakeBoolean, GetInitializedName> initialized{
|
||||
false};
|
||||
SimDataValue<HAL_Bool, HAL_MakeBoolean, GetSolenoidOutputName,
|
||||
GetSolenoidOutputDefault>
|
||||
solenoidOutput[kNumREVPHChannels];
|
||||
SimDataValue<HAL_Bool, HAL_MakeBoolean, GetCompressorOnName> compressorOn{
|
||||
false};
|
||||
SimDataValue<HAL_Bool, HAL_MakeBoolean, GetClosedLoopEnabledName>
|
||||
closedLoopEnabled{true};
|
||||
SimDataValue<HAL_Bool, HAL_MakeBoolean, GetPressureSwitchName> pressureSwitch{
|
||||
false};
|
||||
SimDataValue<double, HAL_MakeDouble, GetCompressorCurrentName>
|
||||
compressorCurrent{0.0};
|
||||
|
||||
virtual void ResetData();
|
||||
};
|
||||
extern REVPHData* SimREVPHData;
|
||||
} // namespace hal
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "frc/Errors.h"
|
||||
#include "frc/PneumaticsControlModule.h"
|
||||
#include "frc/PneumaticsHub.h"
|
||||
#include "frc/SensorUtil.h"
|
||||
|
||||
using namespace frc;
|
||||
@@ -14,6 +15,8 @@ std::shared_ptr<PneumaticsBase> PneumaticsBase::GetForType(
|
||||
int module, PneumaticsModuleType moduleType) {
|
||||
if (moduleType == PneumaticsModuleType::CTREPCM) {
|
||||
return PneumaticsControlModule::GetForModule(module);
|
||||
} else if (moduleType == PneumaticsModuleType::REVPH) {
|
||||
return PneumaticsHub::GetForModule(module);
|
||||
}
|
||||
throw FRC_MakeError(err::InvalidParameter, "{}", moduleType);
|
||||
}
|
||||
@@ -21,6 +24,8 @@ std::shared_ptr<PneumaticsBase> PneumaticsBase::GetForType(
|
||||
int PneumaticsBase::GetDefaultForType(PneumaticsModuleType moduleType) {
|
||||
if (moduleType == PneumaticsModuleType::CTREPCM) {
|
||||
return SensorUtil::GetDefaultCTREPCMModule();
|
||||
} else if (moduleType == PneumaticsModuleType::REVPH) {
|
||||
return SensorUtil::GetDefaultREVPHModule();
|
||||
}
|
||||
throw FRC_MakeError(err::InvalidParameter, "{}", moduleType);
|
||||
}
|
||||
|
||||
210
wpilibc/src/main/native/cpp/PneumaticsHub.cpp
Normal file
210
wpilibc/src/main/native/cpp/PneumaticsHub.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "frc/PneumaticsHub.h"
|
||||
|
||||
#include <hal/REVPH.h>
|
||||
#include <wpi/NullDeleter.h>
|
||||
#include <wpi/StackTrace.h>
|
||||
|
||||
#include "frc/Compressor.h"
|
||||
#include "frc/DoubleSolenoid.h"
|
||||
#include "frc/Errors.h"
|
||||
#include "frc/SensorUtil.h"
|
||||
#include "frc/Solenoid.h"
|
||||
|
||||
using namespace frc;
|
||||
|
||||
wpi::mutex PneumaticsHub::m_handleLock;
|
||||
std::unique_ptr<wpi::DenseMap<int, std::weak_ptr<PneumaticsHub::DataStore>>>
|
||||
PneumaticsHub::m_handleMap = nullptr;
|
||||
|
||||
// Always called under lock, so we can avoid the double lock from the magic
|
||||
// static
|
||||
std::weak_ptr<PneumaticsHub::DataStore>& PneumaticsHub::GetDataStore(
|
||||
int module) {
|
||||
if (!m_handleMap) {
|
||||
m_handleMap = std::make_unique<
|
||||
wpi::DenseMap<int, std::weak_ptr<PneumaticsHub::DataStore>>>();
|
||||
}
|
||||
return (*m_handleMap)[module];
|
||||
}
|
||||
|
||||
class PneumaticsHub::DataStore {
|
||||
public:
|
||||
explicit DataStore(int module, const char* stackTrace) {
|
||||
int32_t status = 0;
|
||||
HAL_REVPHHandle handle = HAL_InitializeREVPH(module, stackTrace, &status);
|
||||
FRC_CheckErrorStatus(status, "Module {}", module);
|
||||
m_moduleObject = PneumaticsHub{handle, module};
|
||||
m_moduleObject.m_dataStore =
|
||||
std::shared_ptr<DataStore>{this, wpi::NullDeleter<DataStore>()};
|
||||
}
|
||||
|
||||
~DataStore() noexcept { HAL_FreeREVPH(m_moduleObject.m_handle); }
|
||||
|
||||
DataStore(DataStore&&) = delete;
|
||||
DataStore& operator=(DataStore&&) = delete;
|
||||
|
||||
private:
|
||||
friend class PneumaticsHub;
|
||||
uint32_t m_reservedMask{0};
|
||||
bool m_compressorReserved{false};
|
||||
wpi::mutex m_reservedLock;
|
||||
PneumaticsHub m_moduleObject{HAL_kInvalidHandle, 0};
|
||||
};
|
||||
|
||||
PneumaticsHub::PneumaticsHub()
|
||||
: PneumaticsHub{SensorUtil::GetDefaultREVPHModule()} {}
|
||||
|
||||
PneumaticsHub::PneumaticsHub(int module) {
|
||||
std::string stackTrace = wpi::GetStackTrace(1);
|
||||
std::scoped_lock lock(m_handleLock);
|
||||
auto& res = GetDataStore(module);
|
||||
m_dataStore = res.lock();
|
||||
if (!m_dataStore) {
|
||||
m_dataStore = std::make_shared<DataStore>(module, stackTrace.c_str());
|
||||
res = m_dataStore;
|
||||
}
|
||||
m_handle = m_dataStore->m_moduleObject.m_handle;
|
||||
m_module = module;
|
||||
}
|
||||
|
||||
PneumaticsHub::PneumaticsHub(HAL_REVPHHandle handle, int module)
|
||||
: m_handle{handle}, m_module{module} {}
|
||||
|
||||
bool PneumaticsHub::GetCompressor() const {
|
||||
int32_t status = 0;
|
||||
auto result = HAL_GetREVPHCompressor(m_handle, &status);
|
||||
FRC_CheckErrorStatus(status, "Module {}", m_module);
|
||||
return result;
|
||||
}
|
||||
|
||||
void PneumaticsHub::SetClosedLoopControl(bool enabled) {
|
||||
int32_t status = 0;
|
||||
HAL_SetREVPHClosedLoopControl(m_handle, enabled, &status);
|
||||
FRC_CheckErrorStatus(status, "Module {}", m_module);
|
||||
}
|
||||
|
||||
bool PneumaticsHub::GetClosedLoopControl() const {
|
||||
int32_t status = 0;
|
||||
auto result = HAL_GetREVPHClosedLoopControl(m_handle, &status);
|
||||
FRC_CheckErrorStatus(status, "Module {}", m_module);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool PneumaticsHub::GetPressureSwitch() const {
|
||||
int32_t status = 0;
|
||||
auto result = HAL_GetREVPHPressureSwitch(m_handle, &status);
|
||||
FRC_CheckErrorStatus(status, "Module {}", m_module);
|
||||
return result;
|
||||
}
|
||||
|
||||
double PneumaticsHub::GetCompressorCurrent() const {
|
||||
int32_t status = 0;
|
||||
auto result = HAL_GetREVPHCompressorCurrent(m_handle, &status);
|
||||
FRC_CheckErrorStatus(status, "Module {}", m_module);
|
||||
return result;
|
||||
}
|
||||
|
||||
void PneumaticsHub::SetSolenoids(int mask, int values) {
|
||||
int32_t status = 0;
|
||||
HAL_SetREVPHSolenoids(m_handle, mask, values, &status);
|
||||
FRC_CheckErrorStatus(status, "Module {}", m_module);
|
||||
}
|
||||
|
||||
int PneumaticsHub::GetSolenoids() const {
|
||||
int32_t status = 0;
|
||||
auto result = HAL_GetREVPHSolenoids(m_handle, &status);
|
||||
FRC_CheckErrorStatus(status, "Module {}", m_module);
|
||||
return result;
|
||||
}
|
||||
|
||||
int PneumaticsHub::GetModuleNumber() const {
|
||||
return m_module;
|
||||
}
|
||||
|
||||
int PneumaticsHub::GetSolenoidDisabledList() const {
|
||||
return 0;
|
||||
// TODO Fix me
|
||||
// int32_t status = 0;
|
||||
// auto result = HAL_GetREVPHSolenoidDisabledList(m_handle, &status);
|
||||
// FRC_CheckErrorStatus(status, "Module {}", m_module);
|
||||
// return result;
|
||||
}
|
||||
|
||||
void PneumaticsHub::FireOneShot(int index) {
|
||||
// TODO Fix me
|
||||
// int32_t status = 0;
|
||||
// HAL_FireREVPHOneShot(m_handle, index, &status);
|
||||
// FRC_CheckErrorStatus(status, "Module {}", m_module);
|
||||
}
|
||||
|
||||
void PneumaticsHub::SetOneShotDuration(int index, units::second_t duration) {
|
||||
// TODO Fix me
|
||||
// int32_t status = 0;
|
||||
// units::millisecond_t millis = duration;
|
||||
// HAL_SetREVPHOneShotDuration(m_handle, index, millis.to<int32_t>(),
|
||||
// &status); FRC_CheckErrorStatus(status, "Module {}", m_module);
|
||||
}
|
||||
|
||||
bool PneumaticsHub::CheckSolenoidChannel(int channel) const {
|
||||
return HAL_CheckREVPHSolenoidChannel(channel);
|
||||
}
|
||||
|
||||
int PneumaticsHub::CheckAndReserveSolenoids(int mask) {
|
||||
std::scoped_lock lock{m_dataStore->m_reservedLock};
|
||||
uint32_t uMask = static_cast<uint32_t>(mask);
|
||||
if ((m_dataStore->m_reservedMask & uMask) != 0) {
|
||||
return m_dataStore->m_reservedMask & uMask;
|
||||
}
|
||||
m_dataStore->m_reservedMask |= uMask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PneumaticsHub::UnreserveSolenoids(int mask) {
|
||||
std::scoped_lock lock{m_dataStore->m_reservedLock};
|
||||
m_dataStore->m_reservedMask &= ~(static_cast<uint32_t>(mask));
|
||||
}
|
||||
|
||||
bool PneumaticsHub::ReserveCompressor() {
|
||||
std::scoped_lock lock{m_dataStore->m_reservedLock};
|
||||
if (m_dataStore->m_compressorReserved) {
|
||||
return false;
|
||||
}
|
||||
m_dataStore->m_compressorReserved = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PneumaticsHub::UnreserveCompressor() {
|
||||
std::scoped_lock lock{m_dataStore->m_reservedLock};
|
||||
m_dataStore->m_compressorReserved = false;
|
||||
}
|
||||
|
||||
Solenoid PneumaticsHub::MakeSolenoid(int channel) {
|
||||
return Solenoid{m_module, PneumaticsModuleType::REVPH, channel};
|
||||
}
|
||||
|
||||
DoubleSolenoid PneumaticsHub::MakeDoubleSolenoid(int forwardChannel,
|
||||
int reverseChannel) {
|
||||
return DoubleSolenoid{m_module, PneumaticsModuleType::REVPH, forwardChannel,
|
||||
reverseChannel};
|
||||
}
|
||||
|
||||
Compressor PneumaticsHub::MakeCompressor() {
|
||||
return Compressor{m_module, PneumaticsModuleType::REVPH};
|
||||
}
|
||||
|
||||
std::shared_ptr<PneumaticsBase> PneumaticsHub::GetForModule(int module) {
|
||||
std::string stackTrace = wpi::GetStackTrace(1);
|
||||
std::scoped_lock lock(m_handleLock);
|
||||
auto& res = GetDataStore(module);
|
||||
std::shared_ptr<DataStore> dataStore = res.lock();
|
||||
if (!dataStore) {
|
||||
dataStore = std::make_shared<DataStore>(module, stackTrace.c_str());
|
||||
res = dataStore;
|
||||
}
|
||||
|
||||
return std::shared_ptr<PneumaticsBase>{dataStore, &dataStore->m_moduleObject};
|
||||
}
|
||||
@@ -23,6 +23,10 @@ int SensorUtil::GetDefaultCTREPCMModule() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SensorUtil::GetDefaultREVPHModule() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool SensorUtil::CheckDigitalChannel(int channel) {
|
||||
return HAL_CheckDIOChannel(channel);
|
||||
}
|
||||
|
||||
139
wpilibc/src/main/native/cpp/simulation/REVPHSim.cpp
Normal file
139
wpilibc/src/main/native/cpp/simulation/REVPHSim.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "frc/simulation/REVPHSim.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <hal/simulation/REVPHData.h>
|
||||
|
||||
#include "frc/SensorUtil.h"
|
||||
|
||||
using namespace frc;
|
||||
using namespace frc::sim;
|
||||
|
||||
REVPHSim::REVPHSim() : m_index{SensorUtil::GetDefaultREVPHModule()} {}
|
||||
|
||||
REVPHSim::REVPHSim(int module) : m_index{module} {}
|
||||
|
||||
REVPHSim::REVPHSim(const PneumaticsBase& pneumatics)
|
||||
: m_index{pneumatics.GetModuleNumber()} {}
|
||||
|
||||
std::unique_ptr<CallbackStore> REVPHSim::RegisterInitializedCallback(
|
||||
NotifyCallback callback, bool initialNotify) {
|
||||
auto store = std::make_unique<CallbackStore>(
|
||||
m_index, -1, callback, &HALSIM_CancelREVPHInitializedCallback);
|
||||
store->SetUid(HALSIM_RegisterREVPHInitializedCallback(
|
||||
m_index, &CallbackStoreThunk, store.get(), initialNotify));
|
||||
return store;
|
||||
}
|
||||
|
||||
bool REVPHSim::GetInitialized() const {
|
||||
return HALSIM_GetREVPHInitialized(m_index);
|
||||
}
|
||||
|
||||
void REVPHSim::SetInitialized(bool solenoidInitialized) {
|
||||
HALSIM_SetREVPHInitialized(m_index, solenoidInitialized);
|
||||
}
|
||||
|
||||
std::unique_ptr<CallbackStore> REVPHSim::RegisterSolenoidOutputCallback(
|
||||
int channel, NotifyCallback callback, bool initialNotify) {
|
||||
auto store = std::make_unique<CallbackStore>(
|
||||
m_index, channel, -1, callback,
|
||||
&HALSIM_CancelREVPHSolenoidOutputCallback);
|
||||
store->SetUid(HALSIM_RegisterREVPHSolenoidOutputCallback(
|
||||
m_index, channel, &CallbackStoreThunk, store.get(), initialNotify));
|
||||
return store;
|
||||
}
|
||||
|
||||
bool REVPHSim::GetSolenoidOutput(int channel) const {
|
||||
return HALSIM_GetREVPHSolenoidOutput(m_index, channel);
|
||||
}
|
||||
|
||||
void REVPHSim::SetSolenoidOutput(int channel, bool solenoidOutput) {
|
||||
HALSIM_SetREVPHSolenoidOutput(m_index, channel, solenoidOutput);
|
||||
}
|
||||
|
||||
std::unique_ptr<CallbackStore> REVPHSim::RegisterCompressorOnCallback(
|
||||
NotifyCallback callback, bool initialNotify) {
|
||||
auto store = std::make_unique<CallbackStore>(
|
||||
m_index, -1, callback, &HALSIM_CancelREVPHCompressorOnCallback);
|
||||
store->SetUid(HALSIM_RegisterREVPHCompressorOnCallback(
|
||||
m_index, &CallbackStoreThunk, store.get(), initialNotify));
|
||||
return store;
|
||||
}
|
||||
|
||||
bool REVPHSim::GetCompressorOn() const {
|
||||
return HALSIM_GetREVPHCompressorOn(m_index);
|
||||
}
|
||||
|
||||
void REVPHSim::SetCompressorOn(bool compressorOn) {
|
||||
HALSIM_SetREVPHCompressorOn(m_index, compressorOn);
|
||||
}
|
||||
|
||||
std::unique_ptr<CallbackStore> REVPHSim::RegisterClosedLoopEnabledCallback(
|
||||
NotifyCallback callback, bool initialNotify) {
|
||||
auto store = std::make_unique<CallbackStore>(
|
||||
m_index, -1, callback, &HALSIM_CancelREVPHClosedLoopEnabledCallback);
|
||||
store->SetUid(HALSIM_RegisterREVPHClosedLoopEnabledCallback(
|
||||
m_index, &CallbackStoreThunk, store.get(), initialNotify));
|
||||
return store;
|
||||
}
|
||||
|
||||
bool REVPHSim::GetClosedLoopEnabled() const {
|
||||
return HALSIM_GetREVPHClosedLoopEnabled(m_index);
|
||||
}
|
||||
|
||||
void REVPHSim::SetClosedLoopEnabled(bool closedLoopEnabled) {
|
||||
HALSIM_SetREVPHClosedLoopEnabled(m_index, closedLoopEnabled);
|
||||
}
|
||||
|
||||
std::unique_ptr<CallbackStore> REVPHSim::RegisterPressureSwitchCallback(
|
||||
NotifyCallback callback, bool initialNotify) {
|
||||
auto store = std::make_unique<CallbackStore>(
|
||||
m_index, -1, callback, &HALSIM_CancelREVPHPressureSwitchCallback);
|
||||
store->SetUid(HALSIM_RegisterREVPHPressureSwitchCallback(
|
||||
m_index, &CallbackStoreThunk, store.get(), initialNotify));
|
||||
return store;
|
||||
}
|
||||
|
||||
bool REVPHSim::GetPressureSwitch() const {
|
||||
return HALSIM_GetREVPHPressureSwitch(m_index);
|
||||
}
|
||||
|
||||
void REVPHSim::SetPressureSwitch(bool pressureSwitch) {
|
||||
HALSIM_SetREVPHPressureSwitch(m_index, pressureSwitch);
|
||||
}
|
||||
|
||||
std::unique_ptr<CallbackStore> REVPHSim::RegisterCompressorCurrentCallback(
|
||||
NotifyCallback callback, bool initialNotify) {
|
||||
auto store = std::make_unique<CallbackStore>(
|
||||
m_index, -1, callback, &HALSIM_CancelREVPHCompressorCurrentCallback);
|
||||
store->SetUid(HALSIM_RegisterREVPHCompressorCurrentCallback(
|
||||
m_index, &CallbackStoreThunk, store.get(), initialNotify));
|
||||
return store;
|
||||
}
|
||||
|
||||
double REVPHSim::GetCompressorCurrent() const {
|
||||
return HALSIM_GetREVPHCompressorCurrent(m_index);
|
||||
}
|
||||
|
||||
void REVPHSim::SetCompressorCurrent(double compressorCurrent) {
|
||||
HALSIM_SetREVPHCompressorCurrent(m_index, compressorCurrent);
|
||||
}
|
||||
|
||||
uint8_t REVPHSim::GetAllSolenoidOutputs() const {
|
||||
uint8_t ret = 0;
|
||||
HALSIM_GetREVPHAllSolenoids(m_index, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void REVPHSim::SetAllSolenoidOutputs(uint8_t outputs) {
|
||||
HALSIM_SetREVPHAllSolenoids(m_index, outputs);
|
||||
}
|
||||
|
||||
void REVPHSim::ResetData() {
|
||||
HALSIM_ResetREVPHData(m_index);
|
||||
}
|
||||
77
wpilibc/src/main/native/include/frc/PneumaticsHub.h
Normal file
77
wpilibc/src/main/native/include/frc/PneumaticsHub.h
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <hal/Types.h>
|
||||
#include <wpi/DenseMap.h>
|
||||
#include <wpi/mutex.h>
|
||||
|
||||
#include "PneumaticsBase.h"
|
||||
|
||||
namespace frc {
|
||||
class PneumaticsHub : public PneumaticsBase {
|
||||
public:
|
||||
PneumaticsHub();
|
||||
explicit PneumaticsHub(int module);
|
||||
|
||||
~PneumaticsHub() override = default;
|
||||
|
||||
bool GetCompressor() const override;
|
||||
|
||||
void SetClosedLoopControl(bool enabled) override;
|
||||
|
||||
bool GetClosedLoopControl() const override;
|
||||
|
||||
bool GetPressureSwitch() const override;
|
||||
|
||||
double GetCompressorCurrent() const override;
|
||||
|
||||
void SetSolenoids(int mask, int values) override;
|
||||
|
||||
int GetSolenoids() const override;
|
||||
|
||||
int GetModuleNumber() const override;
|
||||
|
||||
int GetSolenoidDisabledList() const override;
|
||||
|
||||
void FireOneShot(int index) override;
|
||||
|
||||
void SetOneShotDuration(int index, units::second_t duration) override;
|
||||
|
||||
bool CheckSolenoidChannel(int channel) const override;
|
||||
|
||||
int CheckAndReserveSolenoids(int mask) override;
|
||||
|
||||
void UnreserveSolenoids(int mask) override;
|
||||
|
||||
bool ReserveCompressor() override;
|
||||
|
||||
void UnreserveCompressor() override;
|
||||
|
||||
Solenoid MakeSolenoid(int channel) override;
|
||||
DoubleSolenoid MakeDoubleSolenoid(int forwardChannel,
|
||||
int reverseChannel) override;
|
||||
Compressor MakeCompressor() override;
|
||||
|
||||
private:
|
||||
class DataStore;
|
||||
friend class DataStore;
|
||||
friend class PneumaticsBase;
|
||||
PneumaticsHub(HAL_REVPHHandle handle, int module);
|
||||
|
||||
static std::shared_ptr<PneumaticsBase> GetForModule(int module);
|
||||
|
||||
std::shared_ptr<DataStore> m_dataStore;
|
||||
HAL_REVPHHandle m_handle;
|
||||
int m_module;
|
||||
|
||||
static wpi::mutex m_handleLock;
|
||||
static std::unique_ptr<wpi::DenseMap<int, std::weak_ptr<DataStore>>>
|
||||
m_handleMap;
|
||||
static std::weak_ptr<DataStore>& GetDataStore(int module);
|
||||
};
|
||||
} // namespace frc
|
||||
@@ -21,6 +21,13 @@ class SensorUtil final {
|
||||
*/
|
||||
static int GetDefaultCTREPCMModule();
|
||||
|
||||
/**
|
||||
* Get the number of the default solenoid module.
|
||||
*
|
||||
* @return The number of the default solenoid module.
|
||||
*/
|
||||
static int GetDefaultREVPHModule();
|
||||
|
||||
/**
|
||||
* Check that the digital channel number is valid.
|
||||
*
|
||||
|
||||
214
wpilibc/src/main/native/include/frc/simulation/REVPHSim.h
Normal file
214
wpilibc/src/main/native/include/frc/simulation/REVPHSim.h
Normal file
@@ -0,0 +1,214 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "frc/PneumaticsBase.h"
|
||||
#include "frc/simulation/CallbackStore.h"
|
||||
|
||||
namespace frc {
|
||||
|
||||
class Compressor;
|
||||
|
||||
namespace sim {
|
||||
|
||||
/**
|
||||
* Class to control a simulated Pneumatic Control Module (PCM).
|
||||
*/
|
||||
class REVPHSim {
|
||||
public:
|
||||
/**
|
||||
* Constructs with the default PCM module number (CAN ID).
|
||||
*/
|
||||
REVPHSim();
|
||||
|
||||
/**
|
||||
* Constructs from a PCM module number (CAN ID).
|
||||
*
|
||||
* @param module module number
|
||||
*/
|
||||
explicit REVPHSim(int module);
|
||||
|
||||
explicit REVPHSim(const PneumaticsBase& pneumatics);
|
||||
|
||||
/**
|
||||
* Register a callback to be run when a solenoid is initialized on a channel.
|
||||
*
|
||||
* @param channel the channel to monitor
|
||||
* @param callback the callback
|
||||
* @param initialNotify should the callback be run with the initial state
|
||||
* @return the CallbackStore object associated with this callback
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<CallbackStore> RegisterInitializedCallback(
|
||||
NotifyCallback callback, bool initialNotify);
|
||||
|
||||
/**
|
||||
* Check if a solenoid has been initialized on a specific channel.
|
||||
*
|
||||
* @return true if initialized
|
||||
*/
|
||||
bool GetInitialized() const;
|
||||
|
||||
/**
|
||||
* Define whether a solenoid has been initialized on a specific channel.
|
||||
*
|
||||
* @param channel the channel
|
||||
* @param solenoidInitialized is there a solenoid initialized on that channel
|
||||
*/
|
||||
void SetInitialized(bool solenoidInitialized);
|
||||
|
||||
/**
|
||||
* Register a callback to be run when the solenoid output on a channel
|
||||
* changes.
|
||||
*
|
||||
* @param channel the channel to monitor
|
||||
* @param callback the callback
|
||||
* @param initialNotify should the callback be run with the initial value
|
||||
* @return the CallbackStore object associated with this callback
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<CallbackStore> RegisterSolenoidOutputCallback(
|
||||
int channel, NotifyCallback callback, bool initialNotify);
|
||||
|
||||
/**
|
||||
* Check the solenoid output on a specific channel.
|
||||
*
|
||||
* @param channel the channel to check
|
||||
* @return the solenoid output
|
||||
*/
|
||||
bool GetSolenoidOutput(int channel) const;
|
||||
|
||||
/**
|
||||
* Change the solenoid output on a specific channel.
|
||||
*
|
||||
* @param channel the channel to check
|
||||
* @param solenoidOutput the new solenoid output
|
||||
*/
|
||||
void SetSolenoidOutput(int channel, bool solenoidOutput);
|
||||
|
||||
/**
|
||||
* Register a callback to be run when the compressor activates.
|
||||
*
|
||||
* @param callback the callback
|
||||
* @param initialNotify whether to run the callback with the initial state
|
||||
* @return the CallbackStore object associated with this callback
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<CallbackStore> RegisterCompressorOnCallback(
|
||||
NotifyCallback callback, bool initialNotify);
|
||||
|
||||
/**
|
||||
* Check if the compressor is on.
|
||||
*
|
||||
* @return true if the compressor is active
|
||||
*/
|
||||
bool GetCompressorOn() const;
|
||||
|
||||
/**
|
||||
* Set whether the compressor is active.
|
||||
*
|
||||
* @param compressorOn the new value
|
||||
*/
|
||||
void SetCompressorOn(bool compressorOn);
|
||||
|
||||
/**
|
||||
* Register a callback to be run whenever the closed loop state changes.
|
||||
*
|
||||
* @param callback the callback
|
||||
* @param initialNotify whether the callback should be called with the
|
||||
* initial value
|
||||
* @return the CallbackStore object associated with this callback
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<CallbackStore>
|
||||
RegisterClosedLoopEnabledCallback(NotifyCallback callback,
|
||||
bool initialNotify);
|
||||
|
||||
/**
|
||||
* Check whether the closed loop compressor control is active.
|
||||
*
|
||||
* @return true if active
|
||||
*/
|
||||
bool GetClosedLoopEnabled() const;
|
||||
|
||||
/**
|
||||
* Turn on/off the closed loop control of the compressor.
|
||||
*
|
||||
* @param closedLoopEnabled whether the control loop is active
|
||||
*/
|
||||
void SetClosedLoopEnabled(bool closedLoopEnabled);
|
||||
|
||||
/**
|
||||
* Register a callback to be run whenever the pressure switch value changes.
|
||||
*
|
||||
* @param callback the callback
|
||||
* @param initialNotify whether the callback should be called with the
|
||||
* initial value
|
||||
* @return the CallbackStore object associated with this callback
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<CallbackStore> RegisterPressureSwitchCallback(
|
||||
NotifyCallback callback, bool initialNotify);
|
||||
|
||||
/**
|
||||
* Check the value of the pressure switch.
|
||||
*
|
||||
* @return the pressure switch value
|
||||
*/
|
||||
bool GetPressureSwitch() const;
|
||||
|
||||
/**
|
||||
* Set the value of the pressure switch.
|
||||
*
|
||||
* @param pressureSwitch the new value
|
||||
*/
|
||||
void SetPressureSwitch(bool pressureSwitch);
|
||||
|
||||
/**
|
||||
* Register a callback to be run whenever the compressor current changes.
|
||||
*
|
||||
* @param callback the callback
|
||||
* @param initialNotify whether to call the callback with the initial state
|
||||
* @return the CallbackStore object associated with this callback
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<CallbackStore>
|
||||
RegisterCompressorCurrentCallback(NotifyCallback callback,
|
||||
bool initialNotify);
|
||||
|
||||
/**
|
||||
* Read the compressor current.
|
||||
*
|
||||
* @return the current of the compressor connected to this module
|
||||
*/
|
||||
double GetCompressorCurrent() const;
|
||||
|
||||
/**
|
||||
* Set the compressor current.
|
||||
*
|
||||
* @param compressorCurrent the new compressor current
|
||||
*/
|
||||
void SetCompressorCurrent(double compressorCurrent);
|
||||
|
||||
/**
|
||||
* Get the current value of all solenoid outputs.
|
||||
*
|
||||
* @return the solenoid outputs (1 bit per output)
|
||||
*/
|
||||
uint8_t GetAllSolenoidOutputs() const;
|
||||
|
||||
/**
|
||||
* Change all of the solenoid outputs.
|
||||
*
|
||||
* @param outputs the new solenoid outputs (1 bit per output)
|
||||
*/
|
||||
void SetAllSolenoidOutputs(uint8_t outputs);
|
||||
|
||||
/**
|
||||
* Reset all simulation data for this object.
|
||||
*/
|
||||
void ResetData();
|
||||
|
||||
private:
|
||||
int m_index;
|
||||
};
|
||||
} // namespace sim
|
||||
} // namespace frc
|
||||
75
wpilibc/src/test/native/cpp/DoubleSolenoidTestCTRE.cpp
Normal file
75
wpilibc/src/test/native/cpp/DoubleSolenoidTestCTRE.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include <hal/HAL.h>
|
||||
|
||||
#include "frc/DoubleSolenoid.h"
|
||||
#include "frc/PneumaticsControlModule.h"
|
||||
#include "frc/Solenoid.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace frc {
|
||||
|
||||
TEST(DoubleSolenoidCTRETest, ValidInitialization) {
|
||||
DoubleSolenoid solenoid{3, frc::PneumaticsModuleType::CTREPCM, 2, 3};
|
||||
solenoid.Set(DoubleSolenoid::kReverse);
|
||||
EXPECT_EQ(DoubleSolenoid::kReverse, solenoid.Get());
|
||||
|
||||
solenoid.Set(DoubleSolenoid::kForward);
|
||||
EXPECT_EQ(DoubleSolenoid::kForward, solenoid.Get());
|
||||
|
||||
solenoid.Set(DoubleSolenoid::kOff);
|
||||
EXPECT_EQ(DoubleSolenoid::kOff, solenoid.Get());
|
||||
}
|
||||
|
||||
TEST(DoubleSolenoidCTRETest, ThrowForwardPortAlreadyInitialized) {
|
||||
// Single solenoid that is reused for forward port
|
||||
Solenoid solenoid{5, frc::PneumaticsModuleType::CTREPCM, 2};
|
||||
EXPECT_THROW(DoubleSolenoid(5, frc::PneumaticsModuleType::CTREPCM, 2, 3),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(DoubleSolenoidCTRETest, ThrowReversePortAlreadyInitialized) {
|
||||
// Single solenoid that is reused for forward port
|
||||
Solenoid solenoid{6, frc::PneumaticsModuleType::CTREPCM, 3};
|
||||
EXPECT_THROW(DoubleSolenoid(6, frc::PneumaticsModuleType::CTREPCM, 2, 3),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(DoubleSolenoidCTRETest, ThrowBothPortsAlreadyInitialized) {
|
||||
PneumaticsControlModule pcm{6};
|
||||
// Single solenoid that is reused for forward port
|
||||
Solenoid solenoid0(6, frc::PneumaticsModuleType::CTREPCM, 2);
|
||||
Solenoid solenoid1(6, frc::PneumaticsModuleType::CTREPCM, 3);
|
||||
EXPECT_THROW(DoubleSolenoid(6, frc::PneumaticsModuleType::CTREPCM, 2, 3),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(DoubleSolenoidCTRETest, Toggle) {
|
||||
DoubleSolenoid solenoid{4, frc::PneumaticsModuleType::CTREPCM, 2, 3};
|
||||
// Bootstrap it into reverse
|
||||
solenoid.Set(DoubleSolenoid::kReverse);
|
||||
|
||||
solenoid.Toggle();
|
||||
EXPECT_EQ(DoubleSolenoid::kForward, solenoid.Get());
|
||||
|
||||
solenoid.Toggle();
|
||||
EXPECT_EQ(DoubleSolenoid::kReverse, solenoid.Get());
|
||||
|
||||
// Of shouldn't do anything on toggle
|
||||
solenoid.Set(DoubleSolenoid::kOff);
|
||||
solenoid.Toggle();
|
||||
EXPECT_EQ(DoubleSolenoid::kOff, solenoid.Get());
|
||||
}
|
||||
|
||||
TEST(DoubleSolenoidCTRETest, InvalidForwardPort) {
|
||||
EXPECT_THROW(DoubleSolenoid(0, frc::PneumaticsModuleType::CTREPCM, 100, 1),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(DoubleSolenoidCTRETest, InvalidReversePort) {
|
||||
EXPECT_THROW(DoubleSolenoid(0, frc::PneumaticsModuleType::CTREPCM, 0, 100),
|
||||
std::runtime_error);
|
||||
}
|
||||
} // namespace frc
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
namespace frc {
|
||||
|
||||
TEST(DoubleSolenoidTest, ValidInitialization) {
|
||||
TEST(DoubleSolenoidREVTest, ValidInitialization) {
|
||||
DoubleSolenoid solenoid{3, frc::PneumaticsModuleType::CTREPCM, 2, 3};
|
||||
solenoid.Set(DoubleSolenoid::kReverse);
|
||||
EXPECT_EQ(DoubleSolenoid::kReverse, solenoid.Get());
|
||||
@@ -23,21 +23,21 @@ TEST(DoubleSolenoidTest, ValidInitialization) {
|
||||
EXPECT_EQ(DoubleSolenoid::kOff, solenoid.Get());
|
||||
}
|
||||
|
||||
TEST(DoubleSolenoidTest, ThrowForwardPortAlreadyInitialized) {
|
||||
TEST(DoubleSolenoidREVTest, ThrowForwardPortAlreadyInitialized) {
|
||||
// Single solenoid that is reused for forward port
|
||||
Solenoid solenoid{5, frc::PneumaticsModuleType::CTREPCM, 2};
|
||||
EXPECT_THROW(DoubleSolenoid(5, frc::PneumaticsModuleType::CTREPCM, 2, 3),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(DoubleSolenoidTest, ThrowReversePortAlreadyInitialized) {
|
||||
TEST(DoubleSolenoidREVTest, ThrowReversePortAlreadyInitialized) {
|
||||
// Single solenoid that is reused for forward port
|
||||
Solenoid solenoid{6, frc::PneumaticsModuleType::CTREPCM, 3};
|
||||
EXPECT_THROW(DoubleSolenoid(6, frc::PneumaticsModuleType::CTREPCM, 2, 3),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(DoubleSolenoidTest, ThrowBothPortsAlreadyInitialized) {
|
||||
TEST(DoubleSolenoidREVTest, ThrowBothPortsAlreadyInitialized) {
|
||||
PneumaticsControlModule pcm{6};
|
||||
// Single solenoid that is reused for forward port
|
||||
Solenoid solenoid0(6, frc::PneumaticsModuleType::CTREPCM, 2);
|
||||
@@ -46,7 +46,7 @@ TEST(DoubleSolenoidTest, ThrowBothPortsAlreadyInitialized) {
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(DoubleSolenoidTest, Toggle) {
|
||||
TEST(DoubleSolenoidREVTest, Toggle) {
|
||||
DoubleSolenoid solenoid{4, frc::PneumaticsModuleType::CTREPCM, 2, 3};
|
||||
// Bootstrap it into reverse
|
||||
solenoid.Set(DoubleSolenoid::kReverse);
|
||||
@@ -63,12 +63,12 @@ TEST(DoubleSolenoidTest, Toggle) {
|
||||
EXPECT_EQ(DoubleSolenoid::kOff, solenoid.Get());
|
||||
}
|
||||
|
||||
TEST(DoubleSolenoidTest, InvalidForwardPort) {
|
||||
TEST(DoubleSolenoidREVTest, InvalidForwardPort) {
|
||||
EXPECT_THROW(DoubleSolenoid(0, frc::PneumaticsModuleType::CTREPCM, 100, 1),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(DoubleSolenoidTest, InvalidReversePort) {
|
||||
TEST(DoubleSolenoidREVTest, InvalidReversePort) {
|
||||
EXPECT_THROW(DoubleSolenoid(0, frc::PneumaticsModuleType::CTREPCM, 0, 100),
|
||||
std::runtime_error);
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace frc {
|
||||
TEST(SolenoidTest, ValidInitialization) {
|
||||
TEST(SolenoidCTRETest, ValidInitialization) {
|
||||
Solenoid solenoid{3, frc::PneumaticsModuleType::CTREPCM, 2};
|
||||
EXPECT_EQ(2, solenoid.GetChannel());
|
||||
|
||||
@@ -21,24 +21,24 @@ TEST(SolenoidTest, ValidInitialization) {
|
||||
EXPECT_FALSE(solenoid.Get());
|
||||
}
|
||||
|
||||
TEST(SolenoidTest, DoubleInitialization) {
|
||||
TEST(SolenoidCTRETest, DoubleInitialization) {
|
||||
Solenoid solenoid{3, frc::PneumaticsModuleType::CTREPCM, 2};
|
||||
EXPECT_THROW(Solenoid(3, frc::PneumaticsModuleType::CTREPCM, 2),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(SolenoidTest, DoubleInitializationFromDoubleSolenoid) {
|
||||
TEST(SolenoidCTRETest, DoubleInitializationFromDoubleSolenoid) {
|
||||
DoubleSolenoid solenoid{3, frc::PneumaticsModuleType::CTREPCM, 2, 3};
|
||||
EXPECT_THROW(Solenoid(3, frc::PneumaticsModuleType::CTREPCM, 2),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(SolenoidTest, InvalidChannel) {
|
||||
TEST(SolenoidCTRETest, InvalidChannel) {
|
||||
EXPECT_THROW(Solenoid(3, frc::PneumaticsModuleType::CTREPCM, 100),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(SolenoidTest, Toggle) {
|
||||
TEST(SolenoidCTRETest, Toggle) {
|
||||
Solenoid solenoid{3, frc::PneumaticsModuleType::CTREPCM, 2};
|
||||
solenoid.Set(true);
|
||||
EXPECT_TRUE(solenoid.Get());
|
||||
52
wpilibc/src/test/native/cpp/SolenoidTestREV.cpp
Normal file
52
wpilibc/src/test/native/cpp/SolenoidTestREV.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include <hal/HAL.h>
|
||||
|
||||
#include "frc/DoubleSolenoid.h"
|
||||
#include "frc/PneumaticsControlModule.h"
|
||||
#include "frc/Solenoid.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace frc {
|
||||
TEST(SolenoidREVTest, ValidInitialization) {
|
||||
Solenoid solenoid{3, frc::PneumaticsModuleType::REVPH, 2};
|
||||
EXPECT_EQ(2, solenoid.GetChannel());
|
||||
|
||||
solenoid.Set(true);
|
||||
EXPECT_TRUE(solenoid.Get());
|
||||
|
||||
solenoid.Set(false);
|
||||
EXPECT_FALSE(solenoid.Get());
|
||||
}
|
||||
|
||||
TEST(SolenoidREVTest, DoubleInitialization) {
|
||||
Solenoid solenoid{3, frc::PneumaticsModuleType::REVPH, 2};
|
||||
EXPECT_THROW(Solenoid(3, frc::PneumaticsModuleType::REVPH, 2),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(SolenoidREVTest, DoubleInitializationFromDoubleSolenoid) {
|
||||
DoubleSolenoid solenoid{3, frc::PneumaticsModuleType::REVPH, 2, 3};
|
||||
EXPECT_THROW(Solenoid(3, frc::PneumaticsModuleType::REVPH, 2),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(SolenoidREVTest, InvalidChannel) {
|
||||
EXPECT_THROW(Solenoid(3, frc::PneumaticsModuleType::REVPH, 100),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(SolenoidREVTest, Toggle) {
|
||||
Solenoid solenoid{3, frc::PneumaticsModuleType::REVPH, 2};
|
||||
solenoid.Set(true);
|
||||
EXPECT_TRUE(solenoid.Get());
|
||||
|
||||
solenoid.Toggle();
|
||||
EXPECT_FALSE(solenoid.Get());
|
||||
|
||||
solenoid.Toggle();
|
||||
EXPECT_TRUE(solenoid.Get());
|
||||
}
|
||||
} // namespace frc
|
||||
150
wpilibc/src/test/native/cpp/simulation/REVPHSimTest.cpp
Normal file
150
wpilibc/src/test/native/cpp/simulation/REVPHSimTest.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "frc/simulation/REVPHSim.h" // NOLINT(build/include_order)
|
||||
|
||||
#include <hal/HAL.h>
|
||||
|
||||
#include "callback_helpers/TestCallbackHelpers.h"
|
||||
#include "frc/DoubleSolenoid.h"
|
||||
#include "frc/PneumaticsHub.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace frc::sim {
|
||||
|
||||
TEST(REVPHSimTest, InitializedCallback) {
|
||||
REVPHSim sim;
|
||||
|
||||
sim.ResetData();
|
||||
EXPECT_FALSE(sim.GetInitialized());
|
||||
|
||||
BooleanCallback callback;
|
||||
auto cb = sim.RegisterInitializedCallback(callback.GetCallback(), false);
|
||||
|
||||
PneumaticsHub ph;
|
||||
EXPECT_TRUE(sim.GetInitialized());
|
||||
EXPECT_TRUE(callback.WasTriggered());
|
||||
EXPECT_TRUE(callback.GetLastValue());
|
||||
}
|
||||
|
||||
TEST(REVPHSimTest, SolenoidOutput) {
|
||||
PneumaticsHub ph;
|
||||
REVPHSim sim(ph);
|
||||
sim.ResetData();
|
||||
|
||||
DoubleSolenoid doubleSolenoid{1, frc::PneumaticsModuleType::REVPH, 3, 4};
|
||||
|
||||
BooleanCallback callback3;
|
||||
BooleanCallback callback4;
|
||||
auto cb3 =
|
||||
sim.RegisterSolenoidOutputCallback(3, callback3.GetCallback(), false);
|
||||
auto cb4 =
|
||||
sim.RegisterSolenoidOutputCallback(4, callback4.GetCallback(), false);
|
||||
|
||||
callback3.Reset();
|
||||
callback4.Reset();
|
||||
doubleSolenoid.Set(DoubleSolenoid::kReverse);
|
||||
EXPECT_FALSE(callback3.WasTriggered());
|
||||
EXPECT_FALSE(callback3.GetLastValue());
|
||||
EXPECT_TRUE(callback4.WasTriggered());
|
||||
EXPECT_TRUE(callback4.GetLastValue());
|
||||
EXPECT_FALSE(sim.GetSolenoidOutput(3));
|
||||
EXPECT_TRUE(sim.GetSolenoidOutput(4));
|
||||
EXPECT_EQ(0b00010000, ph.GetSolenoids());
|
||||
EXPECT_EQ(0b00010000, sim.GetAllSolenoidOutputs());
|
||||
|
||||
callback3.Reset();
|
||||
callback4.Reset();
|
||||
doubleSolenoid.Set(DoubleSolenoid::kForward);
|
||||
EXPECT_TRUE(callback3.WasTriggered());
|
||||
EXPECT_TRUE(callback3.GetLastValue());
|
||||
EXPECT_TRUE(callback4.WasTriggered());
|
||||
EXPECT_FALSE(callback4.GetLastValue());
|
||||
EXPECT_TRUE(sim.GetSolenoidOutput(3));
|
||||
EXPECT_FALSE(sim.GetSolenoidOutput(4));
|
||||
EXPECT_EQ(0b00001000, ph.GetSolenoids());
|
||||
EXPECT_EQ(0b00001000, sim.GetAllSolenoidOutputs());
|
||||
|
||||
callback3.Reset();
|
||||
callback4.Reset();
|
||||
doubleSolenoid.Set(DoubleSolenoid::kOff);
|
||||
EXPECT_TRUE(callback3.WasTriggered());
|
||||
EXPECT_FALSE(callback3.GetLastValue());
|
||||
EXPECT_FALSE(callback4.WasTriggered());
|
||||
EXPECT_FALSE(callback4.GetLastValue());
|
||||
EXPECT_FALSE(sim.GetSolenoidOutput(3));
|
||||
EXPECT_FALSE(sim.GetSolenoidOutput(4));
|
||||
EXPECT_EQ(0b00000000, ph.GetSolenoids());
|
||||
EXPECT_EQ(0b00000000, sim.GetAllSolenoidOutputs());
|
||||
}
|
||||
|
||||
TEST(REVPHSimTest, SetCompressorOn) {
|
||||
PneumaticsHub ph;
|
||||
REVPHSim sim(ph);
|
||||
sim.ResetData();
|
||||
|
||||
BooleanCallback callback;
|
||||
auto cb = sim.RegisterCompressorOnCallback(callback.GetCallback(), false);
|
||||
|
||||
EXPECT_FALSE(ph.GetCompressor());
|
||||
EXPECT_FALSE(ph.GetCompressor());
|
||||
sim.SetCompressorOn(true);
|
||||
EXPECT_TRUE(sim.GetCompressorOn());
|
||||
EXPECT_TRUE(ph.GetCompressor());
|
||||
EXPECT_TRUE(callback.WasTriggered());
|
||||
EXPECT_TRUE(callback.GetLastValue());
|
||||
}
|
||||
|
||||
TEST(REVPHSimTest, SetClosedLoopEnabled) {
|
||||
PneumaticsHub ph;
|
||||
REVPHSim sim(ph);
|
||||
sim.ResetData();
|
||||
|
||||
BooleanCallback callback;
|
||||
auto cb =
|
||||
sim.RegisterClosedLoopEnabledCallback(callback.GetCallback(), false);
|
||||
|
||||
ph.SetClosedLoopControl(false);
|
||||
EXPECT_FALSE(ph.GetClosedLoopControl());
|
||||
|
||||
ph.SetClosedLoopControl(true);
|
||||
EXPECT_TRUE(sim.GetClosedLoopEnabled());
|
||||
EXPECT_TRUE(ph.GetClosedLoopControl());
|
||||
EXPECT_TRUE(callback.WasTriggered());
|
||||
EXPECT_TRUE(callback.GetLastValue());
|
||||
}
|
||||
|
||||
TEST(REVPHSimTest, SetPressureSwitchEnabled) {
|
||||
PneumaticsHub ph;
|
||||
REVPHSim sim(ph);
|
||||
sim.ResetData();
|
||||
|
||||
BooleanCallback callback;
|
||||
auto cb = sim.RegisterPressureSwitchCallback(callback.GetCallback(), false);
|
||||
|
||||
EXPECT_FALSE(ph.GetPressureSwitch());
|
||||
|
||||
sim.SetPressureSwitch(true);
|
||||
EXPECT_TRUE(sim.GetPressureSwitch());
|
||||
EXPECT_TRUE(ph.GetPressureSwitch());
|
||||
EXPECT_TRUE(callback.WasTriggered());
|
||||
EXPECT_TRUE(callback.GetLastValue());
|
||||
}
|
||||
|
||||
TEST(REVPHSimTest, SetCompressorCurrent) {
|
||||
PneumaticsHub ph;
|
||||
REVPHSim sim(ph);
|
||||
sim.ResetData();
|
||||
|
||||
DoubleCallback callback;
|
||||
auto cb =
|
||||
sim.RegisterCompressorCurrentCallback(callback.GetCallback(), false);
|
||||
|
||||
sim.SetCompressorCurrent(35.04);
|
||||
EXPECT_EQ(35.04, sim.GetCompressorCurrent());
|
||||
EXPECT_EQ(35.04, ph.GetCompressorCurrent());
|
||||
EXPECT_TRUE(callback.WasTriggered());
|
||||
EXPECT_EQ(35.04, callback.GetLastValue());
|
||||
}
|
||||
} // namespace frc::sim
|
||||
@@ -12,9 +12,11 @@ public interface PneumaticsBase extends AutoCloseable {
|
||||
* @param type module type
|
||||
* @return module
|
||||
*/
|
||||
static PneumaticsControlModule getForType(int module, PneumaticsModuleType type) {
|
||||
static PneumaticsBase getForType(int module, PneumaticsModuleType type) {
|
||||
if (type == PneumaticsModuleType.CTREPCM) {
|
||||
return new PneumaticsControlModule();
|
||||
return new PneumaticsControlModule(module);
|
||||
} else if (type == PneumaticsModuleType.REVPH) {
|
||||
return new PneumaticsHub(module);
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown module type");
|
||||
}
|
||||
@@ -28,6 +30,8 @@ public interface PneumaticsBase extends AutoCloseable {
|
||||
static int getDefaultForType(PneumaticsModuleType type) {
|
||||
if (type == PneumaticsModuleType.CTREPCM) {
|
||||
return SensorUtil.getDefaultCTREPCMModule();
|
||||
} else if (type == PneumaticsModuleType.REVPH) {
|
||||
return SensorUtil.getDefaultREVPHModule();
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown module type");
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ public class PneumaticsControlModule implements PneumaticsBase {
|
||||
|
||||
@Override
|
||||
public int getModuleNumber() {
|
||||
return m_dataStore.m_handle;
|
||||
return m_dataStore.m_module;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
203
wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticsHub.java
Normal file
203
wpilibj/src/main/java/edu/wpi/first/wpilibj/PneumaticsHub.java
Normal file
@@ -0,0 +1,203 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj;
|
||||
|
||||
import edu.wpi.first.hal.REVPHJNI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/** Module class for controlling a REV Robotics Pneumatics Hub. */
|
||||
public class PneumaticsHub implements PneumaticsBase {
|
||||
private static class DataStore implements AutoCloseable {
|
||||
public final int m_module;
|
||||
public final int m_handle;
|
||||
private int m_refCount;
|
||||
private int m_reservedMask;
|
||||
private boolean m_compressorReserved;
|
||||
private final Object m_reserveLock = new Object();
|
||||
|
||||
DataStore(int module) {
|
||||
m_handle = REVPHJNI.initialize(module);
|
||||
m_module = module;
|
||||
m_handleMap.put(module, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
REVPHJNI.free(m_handle);
|
||||
m_handleMap.remove(m_module);
|
||||
}
|
||||
|
||||
public void addRef() {
|
||||
m_refCount++;
|
||||
}
|
||||
|
||||
public void removeRef() {
|
||||
m_refCount--;
|
||||
if (m_refCount == 0) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map<Integer, DataStore> m_handleMap = new HashMap<>();
|
||||
private static final Object m_handleLock = new Object();
|
||||
|
||||
private static DataStore getForModule(int module) {
|
||||
synchronized (m_handleLock) {
|
||||
Integer moduleBoxed = module;
|
||||
DataStore pcm = m_handleMap.get(moduleBoxed);
|
||||
if (pcm == null) {
|
||||
pcm = new DataStore(module);
|
||||
}
|
||||
pcm.addRef();
|
||||
return pcm;
|
||||
}
|
||||
}
|
||||
|
||||
private static void freeModule(DataStore store) {
|
||||
synchronized (m_handleLock) {
|
||||
store.removeRef();
|
||||
}
|
||||
}
|
||||
|
||||
private final DataStore m_dataStore;
|
||||
private final int m_handle;
|
||||
|
||||
/** Constructs a PneumaticsHub with the default id (1). */
|
||||
public PneumaticsHub() {
|
||||
this(SensorUtil.getDefaultREVPHModule());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a PneumaticsHub.
|
||||
*
|
||||
* @param module module number to construct
|
||||
*/
|
||||
public PneumaticsHub(int module) {
|
||||
m_dataStore = getForModule(module);
|
||||
m_handle = m_dataStore.m_handle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
freeModule(m_dataStore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getCompressor() {
|
||||
return REVPHJNI.getCompressor(m_handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClosedLoopControl(boolean enabled) {
|
||||
REVPHJNI.setClosedLoopControl(m_handle, enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getClosedLoopControl() {
|
||||
return REVPHJNI.getClosedLoopControl(m_handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPressureSwitch() {
|
||||
return REVPHJNI.getPressureSwitch(m_handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getCompressorCurrent() {
|
||||
return REVPHJNI.getCompressorCurrent(m_handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSolenoids(int mask, int values) {
|
||||
REVPHJNI.setSolenoids(m_handle, mask, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSolenoids() {
|
||||
return REVPHJNI.getSolenoids(m_handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModuleNumber() {
|
||||
return m_dataStore.m_module;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireOneShot(int index) {
|
||||
// TODO Combine APIs
|
||||
// REVPHJNI.fireOneShot(m_handle, index, durMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOneShotDuration(int index, int durMs) {
|
||||
// TODO Combine APIs
|
||||
// REVPHJNI.setOneShotDuration(m_handle, index, durMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkSolenoidChannel(int channel) {
|
||||
return REVPHJNI.checkSolenoidChannel(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkAndReserveSolenoids(int mask) {
|
||||
synchronized (m_dataStore.m_reserveLock) {
|
||||
if ((m_dataStore.m_reservedMask & mask) != 0) {
|
||||
return m_dataStore.m_reservedMask & mask;
|
||||
}
|
||||
m_dataStore.m_reservedMask |= mask;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unreserveSolenoids(int mask) {
|
||||
synchronized (m_dataStore.m_reserveLock) {
|
||||
m_dataStore.m_reservedMask &= ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Solenoid makeSolenoid(int channel) {
|
||||
return new Solenoid(m_dataStore.m_module, PneumaticsModuleType.REVPH, channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleSolenoid makeDoubleSolenoid(int forwardChannel, int reverseChannel) {
|
||||
return new DoubleSolenoid(
|
||||
m_dataStore.m_module, PneumaticsModuleType.REVPH, forwardChannel, reverseChannel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Compressor makeCompressor() {
|
||||
return new Compressor(m_dataStore.m_module, PneumaticsModuleType.REVPH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean reserveCompressor() {
|
||||
synchronized (m_dataStore.m_reserveLock) {
|
||||
if (m_dataStore.m_compressorReserved) {
|
||||
return false;
|
||||
}
|
||||
m_dataStore.m_compressorReserved = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unreserveCompressor() {
|
||||
synchronized (m_dataStore.m_reserveLock) {
|
||||
m_dataStore.m_compressorReserved = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSolenoidDisabledList() {
|
||||
// TODO Get this working
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,10 @@ public final class SensorUtil {
|
||||
/** Number of PCM Modules. */
|
||||
public static final int kCTREPCMModules = PortsJNI.getNumCTREPCMModules();
|
||||
|
||||
public static final int kREVPHChannels = PortsJNI.getNumREVPHChannels();
|
||||
|
||||
public static final int kREVPHModules = PortsJNI.getNumREVPHModules();
|
||||
|
||||
/**
|
||||
* Check that the digital channel number is valid. Verify that the channel number is one of the
|
||||
* legal channel numbers. Channel numbers are 0-based.
|
||||
@@ -142,5 +146,15 @@ public final class SensorUtil {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of the default solenoid module.
|
||||
*
|
||||
* @return The number of the default solenoid module.
|
||||
*/
|
||||
@SuppressWarnings("AbbreviationAsWordInName")
|
||||
public static int getDefaultREVPHModule() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
private SensorUtil() {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.simulation;
|
||||
|
||||
import edu.wpi.first.hal.simulation.NotifyCallback;
|
||||
import edu.wpi.first.hal.simulation.REVPHDataJNI;
|
||||
import edu.wpi.first.wpilibj.PneumaticsBase;
|
||||
import edu.wpi.first.wpilibj.SensorUtil;
|
||||
|
||||
/** Class to control a simulated PneumaticsHub (PH). */
|
||||
@SuppressWarnings("AbbreviationAsWordInName")
|
||||
public class REVPHSim {
|
||||
private final int m_index;
|
||||
|
||||
/** Constructs for the default PH. */
|
||||
public REVPHSim() {
|
||||
m_index = SensorUtil.getDefaultREVPHModule();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs from a PH module number (CAN ID).
|
||||
*
|
||||
* @param module module number
|
||||
*/
|
||||
public REVPHSim(int module) {
|
||||
m_index = module;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs from a Compressor object.
|
||||
*
|
||||
* @param module PCM module to simulate
|
||||
*/
|
||||
public REVPHSim(PneumaticsBase module) {
|
||||
m_index = module.getModuleNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be run when the solenoid output on a channel changes.
|
||||
*
|
||||
* @param channel the channel to monitor
|
||||
* @param callback the callback
|
||||
* @param initialNotify should the callback be run with the initial value
|
||||
* @return the {@link CallbackStore} object associated with this callback. Save a reference to
|
||||
* this object so GC doesn't cancel the callback.
|
||||
*/
|
||||
public CallbackStore registerSolenoidOutputCallback(
|
||||
int channel, NotifyCallback callback, boolean initialNotify) {
|
||||
int uid =
|
||||
REVPHDataJNI.registerSolenoidOutputCallback(m_index, channel, callback, initialNotify);
|
||||
return new CallbackStore(m_index, channel, uid, REVPHDataJNI::cancelSolenoidOutputCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the solenoid output on a specific channel.
|
||||
*
|
||||
* @param channel the channel to check
|
||||
* @return the solenoid output
|
||||
*/
|
||||
public boolean getSolenoidOutput(int channel) {
|
||||
return REVPHDataJNI.getSolenoidOutput(m_index, channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the solenoid output on a specific channel.
|
||||
*
|
||||
* @param channel the channel to check
|
||||
* @param solenoidOutput the new solenoid output
|
||||
*/
|
||||
public void setSolenoidOutput(int channel, boolean solenoidOutput) {
|
||||
REVPHDataJNI.setSolenoidOutput(m_index, channel, solenoidOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be run when the compressor is initialized.
|
||||
*
|
||||
* @param callback the callback
|
||||
* @param initialNotify whether to run the callback with the initial state
|
||||
* @return the {@link CallbackStore} object associated with this callback. Save a reference to
|
||||
* this object so GC doesn't cancel the callback.
|
||||
*/
|
||||
public CallbackStore registerInitializedCallback(NotifyCallback callback, boolean initialNotify) {
|
||||
int uid = REVPHDataJNI.registerInitializedCallback(m_index, callback, initialNotify);
|
||||
return new CallbackStore(m_index, uid, REVPHDataJNI::cancelInitializedCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the compressor has been initialized.
|
||||
*
|
||||
* @return true if initialized
|
||||
*/
|
||||
public boolean getInitialized() {
|
||||
return REVPHDataJNI.getInitialized(m_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define whether the compressor has been initialized.
|
||||
*
|
||||
* @param initialized whether the compressor is initialized
|
||||
*/
|
||||
public void setInitialized(boolean initialized) {
|
||||
REVPHDataJNI.setInitialized(m_index, initialized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be run when the compressor activates.
|
||||
*
|
||||
* @param callback the callback
|
||||
* @param initialNotify whether to run the callback with the initial state
|
||||
* @return the {@link CallbackStore} object associated with this callback. Save a reference to
|
||||
* this object so GC doesn't cancel the callback.
|
||||
*/
|
||||
public CallbackStore registerCompressorOnCallback(
|
||||
NotifyCallback callback, boolean initialNotify) {
|
||||
int uid = REVPHDataJNI.registerCompressorOnCallback(m_index, callback, initialNotify);
|
||||
return new CallbackStore(m_index, uid, REVPHDataJNI::cancelCompressorOnCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the compressor is on.
|
||||
*
|
||||
* @return true if the compressor is active
|
||||
*/
|
||||
public boolean getCompressorOn() {
|
||||
return REVPHDataJNI.getCompressorOn(m_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the compressor is active.
|
||||
*
|
||||
* @param compressorOn the new value
|
||||
*/
|
||||
public void setCompressorOn(boolean compressorOn) {
|
||||
REVPHDataJNI.setCompressorOn(m_index, compressorOn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be run whenever the closed loop state changes.
|
||||
*
|
||||
* @param callback the callback
|
||||
* @param initialNotify whether the callback should be called with the initial value
|
||||
* @return the {@link CallbackStore} object associated with this callback. Save a reference to
|
||||
* this object so GC doesn't cancel the callback.
|
||||
*/
|
||||
public CallbackStore registerClosedLoopEnabledCallback(
|
||||
NotifyCallback callback, boolean initialNotify) {
|
||||
int uid = REVPHDataJNI.registerClosedLoopEnabledCallback(m_index, callback, initialNotify);
|
||||
return new CallbackStore(m_index, uid, REVPHDataJNI::cancelClosedLoopEnabledCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the closed loop compressor control is active.
|
||||
*
|
||||
* @return true if active
|
||||
*/
|
||||
public boolean getClosedLoopEnabled() {
|
||||
return REVPHDataJNI.getClosedLoopEnabled(m_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn on/off the closed loop control of the compressor.
|
||||
*
|
||||
* @param closedLoopEnabled whether the control loop is active
|
||||
*/
|
||||
public void setClosedLoopEnabled(boolean closedLoopEnabled) {
|
||||
REVPHDataJNI.setClosedLoopEnabled(m_index, closedLoopEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be run whenever the pressure switch value changes.
|
||||
*
|
||||
* @param callback the callback
|
||||
* @param initialNotify whether the callback should be called with the initial value
|
||||
* @return the {@link CallbackStore} object associated with this callback. Save a reference to
|
||||
* this object so GC doesn't cancel the callback.
|
||||
*/
|
||||
public CallbackStore registerPressureSwitchCallback(
|
||||
NotifyCallback callback, boolean initialNotify) {
|
||||
int uid = REVPHDataJNI.registerPressureSwitchCallback(m_index, callback, initialNotify);
|
||||
return new CallbackStore(m_index, uid, REVPHDataJNI::cancelPressureSwitchCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the value of the pressure switch.
|
||||
*
|
||||
* @return the pressure switch value
|
||||
*/
|
||||
public boolean getPressureSwitch() {
|
||||
return REVPHDataJNI.getPressureSwitch(m_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the pressure switch.
|
||||
*
|
||||
* @param pressureSwitch the new value
|
||||
*/
|
||||
public void setPressureSwitch(boolean pressureSwitch) {
|
||||
REVPHDataJNI.setPressureSwitch(m_index, pressureSwitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be run whenever the compressor current changes.
|
||||
*
|
||||
* @param callback the callback
|
||||
* @param initialNotify whether to call the callback with the initial state
|
||||
* @return the {@link CallbackStore} object associated with this callback. Save a reference to
|
||||
* this object so GC doesn't cancel the callback.
|
||||
*/
|
||||
public CallbackStore registerCompressorCurrentCallback(
|
||||
NotifyCallback callback, boolean initialNotify) {
|
||||
int uid = REVPHDataJNI.registerCompressorCurrentCallback(m_index, callback, initialNotify);
|
||||
return new CallbackStore(m_index, uid, REVPHDataJNI::cancelCompressorCurrentCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the compressor current.
|
||||
*
|
||||
* @return the current of the compressor connected to this module
|
||||
*/
|
||||
public double getCompressorCurrent() {
|
||||
return REVPHDataJNI.getCompressorCurrent(m_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the compressor current.
|
||||
*
|
||||
* @param compressorCurrent the new compressor current
|
||||
*/
|
||||
public void setCompressorCurrent(double compressorCurrent) {
|
||||
REVPHDataJNI.setCompressorCurrent(m_index, compressorCurrent);
|
||||
}
|
||||
|
||||
/** Reset all simulation data for this object. */
|
||||
public void resetData() {
|
||||
REVPHDataJNI.resetData(m_index);
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import edu.wpi.first.hal.util.AllocationException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class DoubleSolenoidTest {
|
||||
public class DoubleSolenoidTestCTRE {
|
||||
@Test
|
||||
void testValidInitialization() {
|
||||
try (DoubleSolenoid solenoid = new DoubleSolenoid(3, PneumaticsModuleType.CTREPCM, 2, 3)) {
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import edu.wpi.first.hal.util.AllocationException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class DoubleSolenoidTestREV {
|
||||
@Test
|
||||
void testValidInitialization() {
|
||||
try (DoubleSolenoid solenoid = new DoubleSolenoid(3, PneumaticsModuleType.REVPH, 2, 3)) {
|
||||
solenoid.set(DoubleSolenoid.Value.kReverse);
|
||||
assertEquals(DoubleSolenoid.Value.kReverse, solenoid.get());
|
||||
|
||||
solenoid.set(DoubleSolenoid.Value.kForward);
|
||||
assertEquals(DoubleSolenoid.Value.kForward, solenoid.get());
|
||||
|
||||
solenoid.set(DoubleSolenoid.Value.kOff);
|
||||
assertEquals(DoubleSolenoid.Value.kOff, solenoid.get());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testThrowForwardPortAlreadyInitialized() {
|
||||
try (
|
||||
// Single solenoid that is reused for forward port
|
||||
Solenoid solenoid = new Solenoid(5, PneumaticsModuleType.REVPH, 2)) {
|
||||
assertThrows(
|
||||
AllocationException.class, () -> new DoubleSolenoid(5, PneumaticsModuleType.REVPH, 2, 3));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testThrowReversePortAlreadyInitialized() {
|
||||
try (
|
||||
// Single solenoid that is reused for forward port
|
||||
Solenoid solenoid = new Solenoid(6, PneumaticsModuleType.REVPH, 3)) {
|
||||
assertThrows(
|
||||
AllocationException.class, () -> new DoubleSolenoid(6, PneumaticsModuleType.REVPH, 2, 3));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testThrowBothPortsAlreadyInitialized() {
|
||||
try (
|
||||
// Single solenoid that is reused for forward port
|
||||
Solenoid solenoid0 = new Solenoid(6, PneumaticsModuleType.REVPH, 2);
|
||||
Solenoid solenoid1 = new Solenoid(6, PneumaticsModuleType.REVPH, 3)) {
|
||||
assertThrows(
|
||||
AllocationException.class, () -> new DoubleSolenoid(6, PneumaticsModuleType.REVPH, 2, 3));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToggle() {
|
||||
try (DoubleSolenoid solenoid = new DoubleSolenoid(4, PneumaticsModuleType.REVPH, 2, 3)) {
|
||||
// Bootstrap it into reverse
|
||||
solenoid.set(DoubleSolenoid.Value.kReverse);
|
||||
|
||||
solenoid.toggle();
|
||||
assertEquals(DoubleSolenoid.Value.kForward, solenoid.get());
|
||||
|
||||
solenoid.toggle();
|
||||
assertEquals(DoubleSolenoid.Value.kReverse, solenoid.get());
|
||||
|
||||
// Of shouldn't do anything on toggle
|
||||
solenoid.set(DoubleSolenoid.Value.kOff);
|
||||
solenoid.toggle();
|
||||
assertEquals(DoubleSolenoid.Value.kOff, solenoid.get());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvalidForwardPort() {
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> new DoubleSolenoid(1, PneumaticsModuleType.REVPH, 100, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvalidReversePort() {
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> new DoubleSolenoid(1, PneumaticsModuleType.REVPH, 0, 100));
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import edu.wpi.first.hal.util.AllocationException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class SolenoidTest {
|
||||
public class SolenoidTestCTRE {
|
||||
@Test
|
||||
void testValidInitialization() {
|
||||
try (Solenoid solenoid = new Solenoid(3, PneumaticsModuleType.CTREPCM, 2)) {
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import edu.wpi.first.hal.util.AllocationException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class SolenoidTestREV {
|
||||
@Test
|
||||
void testValidInitialization() {
|
||||
try (Solenoid solenoid = new Solenoid(3, PneumaticsModuleType.REVPH, 2)) {
|
||||
assertEquals(2, solenoid.getChannel());
|
||||
|
||||
solenoid.set(true);
|
||||
assertTrue(solenoid.get());
|
||||
|
||||
solenoid.set(false);
|
||||
assertFalse(solenoid.get());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDoubleInitialization() {
|
||||
try (Solenoid solenoid = new Solenoid(3, PneumaticsModuleType.REVPH, 2)) {
|
||||
assertThrows(AllocationException.class, () -> new Solenoid(3, PneumaticsModuleType.REVPH, 2));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDoubleInitializationFromDoubleSolenoid() {
|
||||
try (DoubleSolenoid solenoid = new DoubleSolenoid(3, PneumaticsModuleType.REVPH, 2, 3)) {
|
||||
assertThrows(AllocationException.class, () -> new Solenoid(3, PneumaticsModuleType.REVPH, 2));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvalidChannel() {
|
||||
assertThrows(
|
||||
IllegalArgumentException.class, () -> new Solenoid(3, PneumaticsModuleType.REVPH, 100));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToggle() {
|
||||
try (Solenoid solenoid = new Solenoid(3, PneumaticsModuleType.REVPH, 2)) {
|
||||
solenoid.set(true);
|
||||
assertTrue(solenoid.get());
|
||||
|
||||
solenoid.toggle();
|
||||
assertFalse(solenoid.get());
|
||||
|
||||
solenoid.toggle();
|
||||
assertTrue(solenoid.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.simulation;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import edu.wpi.first.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.DoubleSolenoid;
|
||||
import edu.wpi.first.wpilibj.PneumaticsHub;
|
||||
import edu.wpi.first.wpilibj.PneumaticsModuleType;
|
||||
import edu.wpi.first.wpilibj.simulation.testutils.BooleanCallback;
|
||||
import edu.wpi.first.wpilibj.simulation.testutils.DoubleCallback;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@SuppressWarnings("AbbreviationAsWordInName")
|
||||
class REVPHSimTest {
|
||||
@Test
|
||||
void testInitialization() {
|
||||
HAL.initialize(500, 0);
|
||||
|
||||
REVPHSim sim = new REVPHSim(1);
|
||||
sim.resetData();
|
||||
assertFalse(sim.getInitialized());
|
||||
|
||||
BooleanCallback callback = new BooleanCallback();
|
||||
|
||||
try (CallbackStore cb = sim.registerInitializedCallback(callback, false);
|
||||
PneumaticsHub ph = new PneumaticsHub(1)) {
|
||||
assertTrue(sim.getInitialized());
|
||||
}
|
||||
assertFalse(sim.getInitialized());
|
||||
}
|
||||
|
||||
@Test
|
||||
void solenoidOutputTest() {
|
||||
HAL.initialize(500, 0);
|
||||
|
||||
try (PneumaticsHub ph = new PneumaticsHub(1);
|
||||
DoubleSolenoid doubleSolenoid = new DoubleSolenoid(1, PneumaticsModuleType.REVPH, 3, 4)) {
|
||||
REVPHSim sim = new REVPHSim(ph);
|
||||
sim.resetData();
|
||||
|
||||
BooleanCallback callback3 = new BooleanCallback();
|
||||
BooleanCallback callback4 = new BooleanCallback();
|
||||
|
||||
try (CallbackStore cb3 = sim.registerSolenoidOutputCallback(3, callback3, false);
|
||||
CallbackStore cb4 = sim.registerSolenoidOutputCallback(4, callback4, false)) {
|
||||
// Reverse
|
||||
callback3.reset();
|
||||
callback4.reset();
|
||||
doubleSolenoid.set(DoubleSolenoid.Value.kReverse);
|
||||
assertFalse(callback3.wasTriggered());
|
||||
assertNull(callback3.getSetValue());
|
||||
assertTrue(callback4.wasTriggered());
|
||||
assertTrue(callback4.getSetValue());
|
||||
assertFalse(sim.getSolenoidOutput(3));
|
||||
assertTrue(sim.getSolenoidOutput(4));
|
||||
assertEquals(0x10, ph.getSolenoids());
|
||||
|
||||
// Forward
|
||||
callback3.reset();
|
||||
callback4.reset();
|
||||
doubleSolenoid.set(DoubleSolenoid.Value.kForward);
|
||||
assertTrue(callback3.wasTriggered());
|
||||
assertTrue(callback3.getSetValue());
|
||||
assertTrue(callback4.wasTriggered());
|
||||
assertFalse(callback4.getSetValue());
|
||||
assertTrue(sim.getSolenoidOutput(3));
|
||||
assertFalse(sim.getSolenoidOutput(4));
|
||||
assertEquals(0x8, ph.getSolenoids());
|
||||
|
||||
// Off
|
||||
callback3.reset();
|
||||
callback4.reset();
|
||||
doubleSolenoid.set(DoubleSolenoid.Value.kOff);
|
||||
assertTrue(callback3.wasTriggered());
|
||||
assertFalse(callback3.getSetValue());
|
||||
assertFalse(callback4.wasTriggered());
|
||||
assertNull(callback4.getSetValue());
|
||||
assertFalse(sim.getSolenoidOutput(3));
|
||||
assertFalse(sim.getSolenoidOutput(4));
|
||||
assertEquals(0x0, ph.getSolenoids());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void setCompressorOnTest() {
|
||||
HAL.initialize(500, 0);
|
||||
|
||||
REVPHSim sim = new REVPHSim(1);
|
||||
BooleanCallback callback = new BooleanCallback();
|
||||
|
||||
try (PneumaticsHub ph = new PneumaticsHub(1);
|
||||
CallbackStore cb = sim.registerCompressorOnCallback(callback, false)) {
|
||||
assertFalse(ph.getCompressor());
|
||||
assertFalse(sim.getCompressorOn());
|
||||
sim.setCompressorOn(true);
|
||||
assertTrue(ph.getCompressor());
|
||||
assertTrue(sim.getCompressorOn());
|
||||
assertTrue(callback.wasTriggered());
|
||||
assertTrue(callback.getSetValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void setClosedLoopEnabled() {
|
||||
HAL.initialize(500, 0);
|
||||
|
||||
REVPHSim sim = new REVPHSim(1);
|
||||
BooleanCallback callback = new BooleanCallback();
|
||||
|
||||
try (PneumaticsHub ph = new PneumaticsHub(1);
|
||||
CallbackStore cb = sim.registerClosedLoopEnabledCallback(callback, false)) {
|
||||
ph.setClosedLoopControl(false);
|
||||
assertFalse(ph.getClosedLoopControl());
|
||||
|
||||
ph.setClosedLoopControl(true);
|
||||
assertTrue(sim.getClosedLoopEnabled());
|
||||
assertTrue(ph.getClosedLoopControl());
|
||||
assertTrue(callback.wasTriggered());
|
||||
assertTrue(callback.getSetValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void setPressureSwitchEnabledTest() {
|
||||
HAL.initialize(500, 0);
|
||||
|
||||
REVPHSim sim = new REVPHSim(1);
|
||||
BooleanCallback callback = new BooleanCallback();
|
||||
|
||||
try (PneumaticsHub ph = new PneumaticsHub(1);
|
||||
CallbackStore cb = sim.registerPressureSwitchCallback(callback, false)) {
|
||||
assertFalse(ph.getPressureSwitch());
|
||||
|
||||
sim.setPressureSwitch(true);
|
||||
assertTrue(sim.getPressureSwitch());
|
||||
assertTrue(ph.getPressureSwitch());
|
||||
assertTrue(callback.wasTriggered());
|
||||
assertTrue(callback.getSetValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void setCompressorCurrentTest() {
|
||||
HAL.initialize(500, 0);
|
||||
|
||||
REVPHSim sim = new REVPHSim(1);
|
||||
DoubleCallback callback = new DoubleCallback();
|
||||
|
||||
try (PneumaticsHub ph = new PneumaticsHub(1);
|
||||
CallbackStore cb = sim.registerCompressorCurrentCallback(callback, false)) {
|
||||
assertFalse(ph.getPressureSwitch());
|
||||
|
||||
sim.setCompressorCurrent(35.04);
|
||||
assertEquals(35.04, sim.getCompressorCurrent());
|
||||
assertEquals(35.04, ph.getCompressorCurrent());
|
||||
assertTrue(callback.wasTriggered());
|
||||
assertEquals(35.04, callback.getSetValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user