Implement PCM One Shot feature. Fixes artf4731 (#539)

This commit is contained in:
sciencewhiz
2017-11-26 12:55:21 -08:00
committed by Peter Johnson
parent a338ee8be0
commit 7a250a1b93
11 changed files with 297 additions and 1 deletions

View File

@@ -154,4 +154,25 @@ void HAL_ClearAllPCMStickyFaults(int32_t module, int32_t* status) {
*status = PCM_modules[module]->ClearStickyFaults();
}
void HAL_SetOneShotDuration(HAL_SolenoidHandle solenoidPortHandle,
int32_t durMS, int32_t* status) {
auto port = solenoidHandles.Get(solenoidPortHandle);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return;
}
*status =
PCM_modules[port->module]->SetOneShotDurationMs(port->channel, durMS);
}
void HAL_FireOneShot(HAL_SolenoidHandle solenoidPortHandle, int32_t* status) {
auto port = solenoidHandles.Get(solenoidPortHandle);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return;
}
*status = PCM_modules[port->module]->FireOneShotSolenoid(port->channel);
}
} // extern "C"

View File

@@ -213,7 +213,9 @@ CTR_Code PCM::FireOneShotSolenoid(UINT8 idx)
return CTR_OKAY;
}
/* Configure the pulse width of a solenoid channel for one-shot pulse.
* Preprogrammed pulsewidth is 10ms resolute and can be between 20ms and 5.1s.
* Preprogrammed pulsewidth is 10ms resolution and can be between 10ms and
* 2.55s.
*
* @Return - CTR_Code - Error code (if any)
* @Param - idx - ID of solenoid [0,7] to configure.
* @Param - durMs - pulse width in ms.

View File

@@ -30,6 +30,9 @@ int32_t HAL_GetPCMSolenoidBlackList(int32_t module, int32_t* status);
HAL_Bool HAL_GetPCMSolenoidVoltageStickyFault(int32_t module, int32_t* status);
HAL_Bool HAL_GetPCMSolenoidVoltageFault(int32_t module, int32_t* status);
void HAL_ClearAllPCMStickyFaults(int32_t module, int32_t* status);
void HAL_SetOneShotDuration(HAL_SolenoidHandle solenoidPortHandle,
int32_t durMS, int32_t* status);
void HAL_FireOneShot(HAL_SolenoidHandle solenoidPortHandle, int32_t* status);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -117,4 +117,7 @@ HAL_Bool HAL_GetPCMSolenoidVoltageFault(int32_t module, int32_t* status) {
return 0;
}
void HAL_ClearAllPCMStickyFaults(int32_t module, int32_t* status) {}
void HAL_SetOneShotDuration(HAL_SolenoidHandle solenoidPortHandle,
int32_t durMS, int32_t* status) {}
void HAL_FireOneShot(HAL_SolenoidHandle solenoidPortHandle, int32_t* status) {}
} // extern "C"

View File

@@ -111,6 +111,36 @@ bool Solenoid::IsBlackListed() const {
return (value != 0);
}
/**
* Set the pulse duration in the PCM. This is used in conjunction with
* the startPulse method to allow the PCM to control the timing of a pulse.
* The timing can be controlled in 0.01 second increments.
*
* @param durationSeconds The duration of the pulse, from 0.01 to 2.55 seconds.
*
* @see startPulse()
*/
void Solenoid::SetPulseDuration(double durationSeconds) {
int32_t durationMS = durationSeconds * 1000;
if (StatusIsFatal()) return;
int32_t status = 0;
HAL_SetOneShotDuration(m_solenoidHandle, durationMS, &status);
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
}
/**
* Trigger the PCM to generate a pulse of the duration set in
* setPulseDuration.
*
* @see setPulseDuration()
*/
void Solenoid::StartPulse() {
if (StatusIsFatal()) return;
int32_t status = 0;
HAL_FireOneShot(m_solenoidHandle, &status);
wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
}
void Solenoid::UpdateTable() {
if (m_valueEntry) m_valueEntry.SetBoolean(Get());
}

View File

@@ -32,6 +32,8 @@ class Solenoid : public SolenoidBase, public LiveWindowSendable {
virtual void Set(bool on);
virtual bool Get() const;
bool IsBlackListed() const;
void SetPulseDuration(double durationSeconds);
void StartPulse();
void UpdateTable();
void StartLiveWindowMode();

View File

@@ -157,3 +157,85 @@ TEST_F(PCMTest, DoubleSolenoid) {
EXPECT_TRUE(solenoid.Get() == DoubleSolenoid::kReverse)
<< "Solenoid does not read reverse";
}
TEST_F(PCMTest, OneShot) {
Reset();
Solenoid solenoid1(TestBench::kSolenoidChannel1);
Solenoid solenoid2(TestBench::kSolenoidChannel2);
// Turn both solenoids off
solenoid1.Set(false);
solenoid2.Set(false);
Wait(kSolenoidDelayTime);
EXPECT_TRUE(m_fakeSolenoid1->Get()) << "Solenoid #1 did not turn off";
EXPECT_TRUE(m_fakeSolenoid2->Get()) << "Solenoid #2 did not turn off";
EXPECT_FALSE(solenoid1.Get()) << "Solenoid #1 did not read off";
EXPECT_FALSE(solenoid2.Get()) << "Solenoid #2 did not read off";
// Pulse Solenoid #1 on, and turn Solenoid #2 off
solenoid1.SetPulseDuration(2 * kSolenoidDelayTime);
solenoid2.Set(false);
solenoid1.StartPulse();
Wait(kSolenoidDelayTime);
EXPECT_FALSE(m_fakeSolenoid1->Get()) << "Solenoid #1 did not turn on";
EXPECT_TRUE(m_fakeSolenoid2->Get()) << "Solenoid #2 did not turn off";
EXPECT_TRUE(solenoid1.Get()) << "Solenoid #1 did not read on";
EXPECT_FALSE(solenoid2.Get()) << "Solenoid #2 did not read off";
Wait(2 * kSolenoidDelayTime);
EXPECT_TRUE(m_fakeSolenoid1->Get()) << "Solenoid #1 did not turn off";
EXPECT_TRUE(m_fakeSolenoid2->Get()) << "Solenoid #2 did not turn off";
EXPECT_FALSE(solenoid1.Get()) << "Solenoid #1 did not read off";
EXPECT_FALSE(solenoid2.Get()) << "Solenoid #2 did not read off";
// Turn Solenoid #1 off, and pulse Solenoid #2 on
solenoid1.Set(false);
solenoid2.SetPulseDuration(2 * kSolenoidDelayTime);
solenoid2.StartPulse();
Wait(kSolenoidDelayTime);
EXPECT_TRUE(m_fakeSolenoid1->Get()) << "Solenoid #1 did not turn off";
EXPECT_FALSE(m_fakeSolenoid2->Get()) << "Solenoid #2 did not turn on";
EXPECT_FALSE(solenoid1.Get()) << "Solenoid #1 did not read off";
EXPECT_TRUE(solenoid2.Get()) << "Solenoid #2 did not read on";
Wait(2 * kSolenoidDelayTime);
EXPECT_TRUE(m_fakeSolenoid1->Get()) << "Solenoid #1 did not turn off";
EXPECT_TRUE(m_fakeSolenoid2->Get()) << "Solenoid #2 did not turn off";
EXPECT_FALSE(solenoid1.Get()) << "Solenoid #1 did not read off";
EXPECT_FALSE(solenoid2.Get()) << "Solenoid #2 did not read off";
// Pulse both Solenoids on
solenoid1.SetPulseDuration(2 * kSolenoidDelayTime);
solenoid2.SetPulseDuration(2 * kSolenoidDelayTime);
solenoid1.StartPulse();
solenoid2.StartPulse();
Wait(kSolenoidDelayTime);
EXPECT_FALSE(m_fakeSolenoid1->Get()) << "Solenoid #1 did not turn on";
EXPECT_FALSE(m_fakeSolenoid2->Get()) << "Solenoid #2 did not turn on";
EXPECT_TRUE(solenoid1.Get()) << "Solenoid #1 did not read on";
EXPECT_TRUE(solenoid2.Get()) << "Solenoid #2 did not read on";
Wait(2 * kSolenoidDelayTime);
EXPECT_TRUE(m_fakeSolenoid1->Get()) << "Solenoid #1 did not turn off";
EXPECT_TRUE(m_fakeSolenoid2->Get()) << "Solenoid #2 did not turn off";
EXPECT_FALSE(solenoid1.Get()) << "Solenoid #1 did not read off";
EXPECT_FALSE(solenoid2.Get()) << "Solenoid #2 did not read off";
// Pulse both Solenoids on with different durations
solenoid1.SetPulseDuration(1.5 * kSolenoidDelayTime);
solenoid2.SetPulseDuration(2.5 * kSolenoidDelayTime);
solenoid1.StartPulse();
solenoid2.StartPulse();
Wait(kSolenoidDelayTime);
EXPECT_FALSE(m_fakeSolenoid1->Get()) << "Solenoid #1 did not turn on";
EXPECT_FALSE(m_fakeSolenoid2->Get()) << "Solenoid #2 did not turn on";
EXPECT_TRUE(solenoid1.Get()) << "Solenoid #1 did not read on";
EXPECT_TRUE(solenoid2.Get()) << "Solenoid #2 did not read on";
Wait(kSolenoidDelayTime);
EXPECT_TRUE(m_fakeSolenoid1->Get()) << "Solenoid #1 did not turn off";
EXPECT_FALSE(m_fakeSolenoid2->Get()) << "Solenoid #2 did not turn on";
EXPECT_FALSE(solenoid1.Get()) << "Solenoid #1 did not read off";
EXPECT_TRUE(solenoid2.Get()) << "Solenoid #2 did not read on";
Wait(2 * kSolenoidDelayTime);
EXPECT_TRUE(m_fakeSolenoid1->Get()) << "Solenoid #1 did not turn off";
EXPECT_TRUE(m_fakeSolenoid2->Get()) << "Solenoid #2 did not turn off";
EXPECT_FALSE(solenoid1.Get()) << "Solenoid #1 did not read off";
EXPECT_FALSE(solenoid2.Get()) << "Solenoid #2 did not read off";
}

View File

@@ -98,6 +98,30 @@ public class Solenoid extends SolenoidBase implements LiveWindowSendable {
return value != 0;
}
/**
* Set the pulse duration in the PCM. This is used in conjunction with
* the startPulse method to allow the PCM to control the timing of a pulse.
* The timing can be controlled in 0.01 second increments.
*
* @param durationSeconds The duration of the pulse, from 0.01 to 2.55 seconds.
*
* @see #startPulse()
*/
public void setPulseDuration(double durationSeconds) {
long durationMS = (long) (durationSeconds * 1000);
SolenoidJNI.setOneShotDuration(m_solenoidHandle, durationMS);
}
/**
* Trigger the PCM to generate a pulse of the duration set in
* setPulseDuration.
*
* @see #setPulseDuration()
*/
public void startPulse() {
SolenoidJNI.fireOneShot(m_solenoidHandle);
}
/*
* Live Window code, only does anything if live window is activated.
*/

View File

@@ -29,4 +29,8 @@ public class SolenoidJNI extends JNIWrapper {
public static native boolean getPCMSolenoidVoltageFault(int module);
public static native void clearAllPCMStickyFaults(int module);
public static native void setOneShotDuration(int portHandle, long durationMS);
public static native void fireOneShot(int portHandle);
}

View File

@@ -187,4 +187,40 @@ Java_edu_wpi_first_wpilibj_hal_SolenoidJNI_clearAllPCMStickyFaults(
CheckStatus(env, status);
}
/*
* Class: edu_wpi_first_wpilibj_hal_SolenoidJNI
* Method: setOneShotDuration
* Signature: (IJ)V
*/
JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_SolenoidJNI_setOneShotDuration
(JNIEnv *env, jclass, jint solenoid_port, jlong durationMS)
{
SOLENOIDJNI_LOG(logDEBUG) << "Calling SolenoidJNI SetOneShotDuration";
SOLENOIDJNI_LOG(logDEBUG) << "Solenoid Port Handle = "
<< (HAL_SolenoidHandle)solenoid_port;
SOLENOIDJNI_LOG(logDEBUG) << "Duration (MS) = " << durationMS;
int32_t status = 0;
HAL_SetOneShotDuration((HAL_SolenoidHandle)solenoid_port, durationMS, &status);
CheckStatus(env, status);
}
/*
* Class: edu_wpi_first_wpilibj_hal_SolenoidJNI
* Method: fireOneShot
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_SolenoidJNI_fireOneShot
(JNIEnv *env, jclass, jint solenoid_port)
{
SOLENOIDJNI_LOG(logDEBUG) << "Calling SolenoidJNI fireOneShot";
SOLENOIDJNI_LOG(logDEBUG) << "Solenoid Port Handle = "
<< (HAL_SolenoidHandle)solenoid_port;
int32_t status = 0;
HAL_FireOneShot((HAL_SolenoidHandle)solenoid_port, &status);
CheckStatus(env, status);
}
} // extern "C"

View File

@@ -180,6 +180,95 @@ public class PCMTest extends AbstractComsSetup {
solenoid.free();
}
/**
* Test if the correct solenoids turn on and off when they should.
*/
@Test
public void testOneShot() throws Exception {
reset();
Solenoid solenoid1 = new Solenoid(0);
Solenoid solenoid2 = new Solenoid(1);
solenoid1.set(false);
solenoid2.set(false);
Timer.delay(kSolenoidDelayTime);
assertTrue("Solenoid #1 did not turn off", fakeSolenoid1.get());
assertTrue("Solenoid #2 did not turn off", fakeSolenoid2.get());
assertFalse("Solenoid #1 did not report off", solenoid1.get());
assertFalse("Solenoid #2 did not report off", solenoid2.get());
// Pulse Solenoid #1 on, and turn Solenoid #2 off
solenoid1.setPulseDuration(2 * kSolenoidDelayTime);
solenoid1.startPulse();
solenoid2.set(false);
Timer.delay(kSolenoidDelayTime);
assertFalse("Solenoid #1 did not turn on", fakeSolenoid1.get());
assertTrue("Solenoid #2 did not turn off", fakeSolenoid2.get());
assertTrue("Solenoid #1 did not report on", solenoid1.get());
assertFalse("Solenoid #2 did not report off", solenoid2.get());
Timer.delay(2 * kSolenoidDelayTime);
assertTrue("Solenoid #1 did not turn off", fakeSolenoid1.get());
assertTrue("Solenoid #2 did not turn off", fakeSolenoid2.get());
assertFalse("Solenoid #1 did not report off", solenoid1.get());
assertFalse("Solenoid #2 did not report off", solenoid2.get());
// Turn Solenoid #1 off, and pulse Solenoid #2 on
solenoid1.set(false);
solenoid2.setPulseDuration(2 * kSolenoidDelayTime);
solenoid2.startPulse();
Timer.delay(kSolenoidDelayTime);
assertTrue("Solenoid #1 did not turn off", fakeSolenoid1.get());
assertFalse("Solenoid #2 did not turn on", fakeSolenoid2.get());
assertFalse("Solenoid #1 did not report off", solenoid1.get());
assertTrue("Solenoid #2 did not report on", solenoid2.get());
Timer.delay(2 * kSolenoidDelayTime);
assertTrue("Solenoid #1 did not turn off", fakeSolenoid1.get());
assertTrue("Solenoid #2 did not turn off", fakeSolenoid2.get());
assertFalse("Solenoid #1 did not report off", solenoid1.get());
assertFalse("Solenoid #2 did not report off", solenoid2.get());
// Pulse both Solenoids on
solenoid1.setPulseDuration(2 * kSolenoidDelayTime);
solenoid2.setPulseDuration(2 * kSolenoidDelayTime);
solenoid1.startPulse();
solenoid2.startPulse();
Timer.delay(kSolenoidDelayTime);
assertFalse("Solenoid #1 did not turn on", fakeSolenoid1.get());
assertFalse("Solenoid #2 did not turn on", fakeSolenoid2.get());
assertTrue("Solenoid #1 did not report on", solenoid1.get());
assertTrue("Solenoid #2 did not report on", solenoid2.get());
Timer.delay(2 * kSolenoidDelayTime);
assertTrue("Solenoid #1 did not turn off", fakeSolenoid1.get());
assertTrue("Solenoid #2 did not turn off", fakeSolenoid2.get());
assertFalse("Solenoid #1 did not report off", solenoid1.get());
assertFalse("Solenoid #2 did not report off", solenoid2.get());
// Pulse both Solenoids on with different durations
solenoid1.setPulseDuration(1.5 * kSolenoidDelayTime);
solenoid2.setPulseDuration(2.5 * kSolenoidDelayTime);
solenoid1.startPulse();
solenoid2.startPulse();
Timer.delay(kSolenoidDelayTime);
assertFalse("Solenoid #1 did not turn on", fakeSolenoid1.get());
assertFalse("Solenoid #2 did not turn on", fakeSolenoid2.get());
assertTrue("Solenoid #1 did not report on", solenoid1.get());
assertTrue("Solenoid #2 did not report on", solenoid2.get());
Timer.delay(kSolenoidDelayTime);
assertTrue("Solenoid #1 did not turn off", fakeSolenoid1.get());
assertFalse("Solenoid #2 did not turn on", fakeSolenoid2.get());
assertFalse("Solenoid #1 did not report off", solenoid1.get());
assertTrue("Solenoid #2 did not report on", solenoid2.get());
Timer.delay(kSolenoidDelayTime);
assertTrue("Solenoid #1 did not turn off", fakeSolenoid1.get());
assertTrue("Solenoid #2 did not turn off", fakeSolenoid2.get());
assertFalse("Solenoid #1 did not report off", solenoid1.get());
assertFalse("Solenoid #2 did not report off", solenoid2.get());
solenoid1.free();
solenoid2.free();
}
protected Logger getClassLogger() {
return logger;
}