From 43bd1957ace35c8d7775d643be08a45d71df4d34 Mon Sep 17 00:00:00 2001 From: Randall Hauch Date: Mon, 26 Oct 2015 12:45:10 -0500 Subject: [PATCH 1/2] Added component for pneumatics control module Added the `PneumaticsModule`, which represents the Pneumatics Control Module (PCM) on the 2015+ hardware and which uses the WPILib `Compressor` class for the hardware-based implementation. This commit also adds a mock implementation that behaves like the actual hardware-based implementation, except that the low-pressure switch can be manually triggered and untriggered, and the compressor faults can be manually tripped (they do reset immediately but do alter the sticky faults correctly). When the compressor runs (whether in auto or manual mode), the current sensor shows a constant 10A reading (which should be similar to what the actual Viair 090C Air Compressor consumes. When the compressor is off (for any reason), the current is set to 0. This change supports multiple PCMs on a single robot, since one of the hardware-based factory methods takes the CAN identifier of the PCM. --- .../src/org/strongback/mock/Mock.java | 20 ++ .../strongback/mock/MockPneumaticsModule.java | 175 ++++++++++++++++++ .../org/strongback/mock/MockPowerPanel.java | 66 +++++++ .../mock/MockPneumaticsModuleTest.java | 143 ++++++++++++++ .../src/org/strongback/components/Fuse.java | 63 ++++++- .../components/PneumaticsModule.java | 101 ++++++++++ .../src/org/strongback/components/Relay.java | 34 ++++ .../src/org/strongback/hardware/Hardware.java | 22 +++ .../hardware/HardwarePneumaticsModule.java | 107 +++++++++++ 9 files changed, 729 insertions(+), 2 deletions(-) create mode 100644 strongback-testing/src/org/strongback/mock/MockPneumaticsModule.java create mode 100644 strongback-testing/src/org/strongback/mock/MockPowerPanel.java create mode 100644 strongback-tests/src/org/strongback/mock/MockPneumaticsModuleTest.java create mode 100644 strongback/src/org/strongback/components/PneumaticsModule.java create mode 100644 strongback/src/org/strongback/hardware/HardwarePneumaticsModule.java diff --git a/strongback-testing/src/org/strongback/mock/Mock.java b/strongback-testing/src/org/strongback/mock/Mock.java index 130e9d4..b582e1e 100644 --- a/strongback-testing/src/org/strongback/mock/Mock.java +++ b/strongback-testing/src/org/strongback/mock/Mock.java @@ -25,6 +25,25 @@ */ public class Mock { + /** + * Create a mock power panel. + * + * @return the mock power panel; never null + */ + public static MockPowerPanel powerPanel() { + return new MockPowerPanel(16); + } + + /** + * Create a mock pneumatics module. This method can be called more than once to represent a robot with multiple pneumatics + * modules. + * + * @return the mock pneumatics module; never null + */ + public static MockPneumaticsModule pnuematicsModule() { + return new MockPneumaticsModule(); + } + /** * Create a mock clock. * @@ -175,6 +194,7 @@ public static MockSolenoid manualSolenoid() { /** * Create a relay that operates instantaneously. + * * @return the mock relay; never null */ public static MockRelay relay() { diff --git a/strongback-testing/src/org/strongback/mock/MockPneumaticsModule.java b/strongback-testing/src/org/strongback/mock/MockPneumaticsModule.java new file mode 100644 index 0000000..5455b82 --- /dev/null +++ b/strongback-testing/src/org/strongback/mock/MockPneumaticsModule.java @@ -0,0 +1,175 @@ +/* + * Strongback + * Copyright 2015, Strongback and individual contributors by the @authors tag. + * See the COPYRIGHT.txt in the distribution for a full listing of individual + * contributors. + * + * Licensed under the MIT License; you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.strongback.mock; + +import org.strongback.components.Fuse; +import org.strongback.components.PneumaticsModule; +import org.strongback.components.Switch; + +/** + * A mock implementation of the {@link PneumaticsModule} that allows the caller to control the {@link #lowPressureSwitch()}. + * When the module's {@link #automaticMode()} is enabled, then triggering the {@link #lowPressureSwitch() low pressure switch} + * will start the compressor, and {@link MockSwitch#setNotTriggered() un-triggering} the {@link #lowPressureSwitch() low + * pressure switch} will stop the compressor. + * + * @author Randall Hauch + */ +public class MockPneumaticsModule implements PneumaticsModule { + + public static double COMPRESSOR_CURRENT_WHEN_RUNNING = 10.0; + + private final StickyFaults stickyFaults = new StickyFaults(); + private final MockFaults instantaneousFaults = new MockFaults(stickyFaults); + private final MockCurrentSensor current = new MockCurrentSensor(); + private final MockSwitch running = new MockSwitch().setNotTriggered(); + private final MockSwitch lowPressure = new MockSwitch() { + @Override + public MockSwitch setTriggered(boolean triggered) { + super.setTriggered(triggered); + if (automatic.isOn()) { + // The caller is manually triggering the switch, but this affects whether compressor runs ... + runCompressor(triggered); + } + return this; + } + }; + private final MockRelay automatic = new MockRelay() { + @Override + public MockRelay off() { + super.off(); + runCompressor(false); + return this; + } + + @Override + public MockRelay on() { + super.on(); + return this; + } + }; + + public MockPneumaticsModule() { + automatic.on(); + lowPressure.setNotTriggered(); + } + + private void runCompressor( boolean isRunning ) { + if ( isRunning ) { + running.setTriggered(isRunning); + current.setCurrent(COMPRESSOR_CURRENT_WHEN_RUNNING); + } else { + running.setNotTriggered(); + current.setCurrent(0.0); + } + } + + @Override + public MockCurrentSensor compressorCurrent() { + return current; + } + + @Override + public Switch compressorRunningSwitch() { + return running; + } + + @Override + public MockSwitch lowPressureSwitch() { + return lowPressure; + } + + @Override + public MockRelay automaticMode() { + return automatic; + } + + /** + * These faults clear immediately after they are {@link Fuse#trigger() triggered}. + * @see #compressorStickyFaults() + */ + @Override + public MockFaults compressorFaults() { + return instantaneousFaults; + } + + @Override + public Faults compressorStickyFaults() { + return stickyFaults; + } + + @Override + public MockPneumaticsModule clearStickyFaults() { + stickyFaults.reset(); + return this; + } + + private void triggerFault( MockSwitch stickySwitch ) { + // Any fault should always stop the compressor ... + stickySwitch.setTriggered(); + runCompressor(false); + } + + public class MockFaults implements Faults { + private final Fuse currentTooHigh; + private final Fuse notConnected; + private final Fuse shorted; + protected MockFaults( StickyFaults sticky ) { + // These should trip the sticky faults and then immediately reset ... + currentTooHigh = Fuse.instantaneous(()->triggerFault(sticky.currentTooHigh)); + notConnected = Fuse.instantaneous(()->triggerFault(sticky.notConnected)); + shorted = Fuse.instantaneous(()->triggerFault(sticky.shorted)); + } + + @Override + public Fuse currentTooHigh() { + return currentTooHigh; + } + @Override + public Fuse notConnected() { + return notConnected; + } + @Override + public Fuse shorted() { + return shorted; + } + } + + protected static class StickyFaults implements Faults { + private final MockSwitch currentTooHigh = new MockSwitch().setNotTriggered(); + private final MockSwitch notConnected = new MockSwitch().setNotTriggered(); + private final MockSwitch shorted = new MockSwitch().setNotTriggered(); + + @Override + public Switch currentTooHigh() { + return currentTooHigh; + } + @Override + public Switch notConnected() { + return notConnected; + } + @Override + public Switch shorted() { + return shorted; + } + protected void reset() { + currentTooHigh.setNotTriggered(); + notConnected.setNotTriggered(); + shorted.setNotTriggered(); + } + } + +} diff --git a/strongback-testing/src/org/strongback/mock/MockPowerPanel.java b/strongback-testing/src/org/strongback/mock/MockPowerPanel.java new file mode 100644 index 0000000..436e84b --- /dev/null +++ b/strongback-testing/src/org/strongback/mock/MockPowerPanel.java @@ -0,0 +1,66 @@ +/* + * Strongback + * Copyright 2015, Strongback and individual contributors by the @authors tag. + * See the COPYRIGHT.txt in the distribution for a full listing of individual + * contributors. + * + * Licensed under the MIT License; you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.strongback.mock; + +import java.util.ArrayList; +import java.util.List; + +import org.strongback.components.PowerPanel; + +/** + * A mock implementation of the {@link PowerPanel}, which has 16 channels + * @author Randall Hauch + */ +public class MockPowerPanel implements PowerPanel { + + private final List channels; + private final MockTemperatureSensor temperature = new MockTemperatureSensor().setTemperature(72.0); + private final MockCurrentSensor totalCurrent = new MockCurrentSensor().setCurrent(10.0); + private final MockVoltageSensor voltage = new MockVoltageSensor().setVoltage(12.0); + + /** + * Create a mock power panel with the specified number of channels. + * @param numChannels the number of channels; must be positive + */ + public MockPowerPanel( int numChannels ) { + channels = new ArrayList<>(numChannels); + for ( int i= 0; i!= numChannels; ++i ) { + channels.add(new MockCurrentSensor()); + } + } + + @Override + public MockCurrentSensor getCurrentSensor(int channel) { + return channels.get(channel); + } + + @Override + public MockTemperatureSensor getTemperatureSensor() { + return temperature; + } + + @Override + public MockCurrentSensor getTotalCurrentSensor() { + return totalCurrent; + } + + @Override + public MockVoltageSensor getVoltageSensor() { + return voltage; + } + +} diff --git a/strongback-tests/src/org/strongback/mock/MockPneumaticsModuleTest.java b/strongback-tests/src/org/strongback/mock/MockPneumaticsModuleTest.java new file mode 100644 index 0000000..75fd07b --- /dev/null +++ b/strongback-tests/src/org/strongback/mock/MockPneumaticsModuleTest.java @@ -0,0 +1,143 @@ +/* + * Strongback + * Copyright 2015, Strongback and individual contributors by the @authors tag. + * See the COPYRIGHT.txt in the distribution for a full listing of individual + * contributors. + * + * Licensed under the MIT License; you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.strongback.mock; + +import static org.fest.assertions.Assertions.assertThat; + +import org.junit.Before; +import org.junit.Test; + +public class MockPneumaticsModuleTest { + + private MockPneumaticsModule module; + + @Before + public void beforeEach() { + this.module = new MockPneumaticsModule(); + } + + @Test + public void shouldNotBeAutomaticModeByDefault() { + assertThat(module.automaticMode().isOn()).isTrue(); + assertThat(module.compressorRunningSwitch().isTriggered()).isEqualTo(false); + } + + @Test + public void shouldNotRunCompressorAutomaticallyWhenSetToAutomaticModeAndLowPressureIsNotTriggered() { + module.lowPressureSwitch().setNotTriggered(); // start out with enough pressure + module.automaticMode().on(); + assertThat(module.compressorRunningSwitch().isTriggered()).isEqualTo(false); + assertThat(module.compressorCurrent().getCurrent()).isEqualTo(0.0); + + // drop pressure should run compressor ... + module.lowPressureSwitch().setTriggered(); + assertThat(module.compressorRunningSwitch().isTriggered()).isEqualTo(true); + assertThat(module.compressorCurrent().getCurrent()).isEqualTo(MockPneumaticsModule.COMPRESSOR_CURRENT_WHEN_RUNNING); + + // raise pressure should turn off compressor ... + module.lowPressureSwitch().setNotTriggered(); + assertThat(module.compressorRunningSwitch().isTriggered()).isEqualTo(false); + assertThat(module.compressorCurrent().getCurrent()).isEqualTo(0.0); + + // drop pressure should run compressor ... + module.lowPressureSwitch().setTriggered(); + assertThat(module.compressorRunningSwitch().isTriggered()).isEqualTo(true); + assertThat(module.compressorCurrent().getCurrent()).isEqualTo(MockPneumaticsModule.COMPRESSOR_CURRENT_WHEN_RUNNING); + + // Turn off auto mode when running ... + module.automaticMode().off(); + assertThat(module.compressorRunningSwitch().isTriggered()).isEqualTo(false); + assertThat(module.compressorCurrent().getCurrent()).isEqualTo(0.0); + } + + @Test + public void shouldRunCompressorAutomaticallyWhenSetToAutomaticModeAndLowPressureIsTriggered() { + module.lowPressureSwitch().setTriggered(); // start out with low pressure + module.automaticMode().on(); + assertThat(module.compressorRunningSwitch().isTriggered()).isEqualTo(true); + assertThat(module.compressorCurrent().getCurrent()).isEqualTo(MockPneumaticsModule.COMPRESSOR_CURRENT_WHEN_RUNNING); + + // raise pressure should turn off compressor ... + module.lowPressureSwitch().setNotTriggered(); + assertThat(module.compressorRunningSwitch().isTriggered()).isEqualTo(false); + assertThat(module.compressorCurrent().getCurrent()).isEqualTo(0.0); + + // drop pressure should run compressor ... + module.lowPressureSwitch().setTriggered(); + assertThat(module.compressorRunningSwitch().isTriggered()).isEqualTo(true); + assertThat(module.compressorCurrent().getCurrent()).isEqualTo(MockPneumaticsModule.COMPRESSOR_CURRENT_WHEN_RUNNING); + + // raise pressure should turn off compressor ... + module.lowPressureSwitch().setNotTriggered(); + assertThat(module.compressorRunningSwitch().isTriggered()).isEqualTo(false); + assertThat(module.compressorCurrent().getCurrent()).isEqualTo(0.0); + + // Turn off auto mode when not running ... + module.automaticMode().off(); + assertThat(module.compressorRunningSwitch().isTriggered()).isEqualTo(false); + assertThat(module.compressorCurrent().getCurrent()).isEqualTo(0.0); + } + + @Test + public void shouldKeepStickyFaultStates() { + assertThat(module.compressorFaults().currentTooHigh().isTriggered()).isEqualTo(false); + assertThat(module.compressorFaults().notConnected().isTriggered()).isEqualTo(false); + assertThat(module.compressorFaults().shorted().isTriggered()).isEqualTo(false); + assertThat(module.compressorStickyFaults().currentTooHigh().isTriggered()).isEqualTo(false); + assertThat(module.compressorStickyFaults().notConnected().isTriggered()).isEqualTo(false); + assertThat(module.compressorStickyFaults().shorted().isTriggered()).isEqualTo(false); + + module.compressorFaults().currentTooHigh().trigger(); + assertThat(module.compressorFaults().currentTooHigh().isTriggered()).isEqualTo(false); + assertThat(module.compressorStickyFaults().currentTooHigh().isTriggered()).isEqualTo(true); + + // Start the compressor ... + module.lowPressureSwitch().setTriggered(); // start out with low pressure + module.automaticMode().on(); + + module.compressorFaults().notConnected().trigger(); + assertThat(module.compressorFaults().notConnected().isTriggered()).isEqualTo(false); + assertThat(module.compressorStickyFaults().notConnected().isTriggered()).isEqualTo(true); + + // Compressor should stop after a fault + assertThat(module.compressorRunningSwitch().isTriggered()).isEqualTo(false); + assertThat(module.compressorCurrent().getCurrent()).isEqualTo(0.0); + + // But we can start it again ... + module.lowPressureSwitch().setTriggered(); + assertThat(module.compressorRunningSwitch().isTriggered()).isEqualTo(true); + assertThat(module.compressorCurrent().getCurrent()).isEqualTo(MockPneumaticsModule.COMPRESSOR_CURRENT_WHEN_RUNNING); + + module.compressorFaults().shorted().trigger(); + assertThat(module.compressorFaults().shorted().isTriggered()).isEqualTo(false); + assertThat(module.compressorStickyFaults().shorted().isTriggered()).isEqualTo(true); + + // Compressor should stop after a fault + assertThat(module.compressorRunningSwitch().isTriggered()).isEqualTo(false); + assertThat(module.compressorCurrent().getCurrent()).isEqualTo(0.0); + + module.clearStickyFaults(); + + assertThat(module.compressorFaults().currentTooHigh().isTriggered()).isEqualTo(false); + assertThat(module.compressorFaults().notConnected().isTriggered()).isEqualTo(false); + assertThat(module.compressorFaults().shorted().isTriggered()).isEqualTo(false); + assertThat(module.compressorStickyFaults().currentTooHigh().isTriggered()).isEqualTo(false); + assertThat(module.compressorStickyFaults().notConnected().isTriggered()).isEqualTo(false); + assertThat(module.compressorStickyFaults().shorted().isTriggered()).isEqualTo(false); + } + +} diff --git a/strongback/src/org/strongback/components/Fuse.java b/strongback/src/org/strongback/components/Fuse.java index 794d111..03ffeb9 100644 --- a/strongback/src/org/strongback/components/Fuse.java +++ b/strongback/src/org/strongback/components/Fuse.java @@ -18,6 +18,7 @@ import java.util.concurrent.TimeUnit; +import org.strongback.Strongback; import org.strongback.annotation.ThreadSafe; /** @@ -26,8 +27,8 @@ @ThreadSafe public interface Fuse extends Switch { /** - * Trigger the fuse. Once this method is called, the {@link #isTriggered()} switches but then will never change - * until it is {@link #reset()}. + * Trigger the fuse. Once this method is called, the {@link #isTriggered()} switches but then will never change until it is + * {@link #reset()}. * * @return this object to allow for chaining methods together; never null */ @@ -73,6 +74,44 @@ public String toString() { }; } + /** + * Create a simple fuse that is manually {@link #trigger() triggered} and manually {@link #reset()}. + * + * @param whenTriggered the function that is to be called when the fuse is triggered; may be null + * @return the fuse; never null + */ + public static Fuse instantaneous( Runnable whenTriggered ) { + return new Fuse() { + + @Override + public boolean isTriggered() { + return false; + } + + @Override + public Fuse trigger() { + if (whenTriggered != null) { + try { + whenTriggered.run(); + } catch (Throwable t) { + Strongback.logger(Fuse.class).error(t, "Error when calling fuse trigger function"); + } + } + return this; + } + + @Override + public Fuse reset() { + return this; + } + + @Override + public String toString() { + return "notTriggered"; + } + }; + } + /** * Create a fuse that can be manually {@link #reset()} but that will automatically reset after the given delay. * @@ -82,6 +121,19 @@ public String toString() { * @return the auto-resetting fuse; never null */ public static Fuse autoResetting(long delay, TimeUnit unit, Clock clock) { + return autoResetting(delay, unit, clock, null); + } + + /** + * Create a fuse that can be manually {@link #reset()} but that will automatically reset after the given delay. + * + * @param delay the time after the fuse is triggered that it should automatically reset; must be positive + * @param unit the time units for the delay; may not be null + * @param clock the clock that the fuse should use; if null, the system clock will be used + * @param whenTriggered the function that is to be called when the fuse is triggered; may be null + * @return the auto-resetting fuse; never null + */ + public static Fuse autoResetting(long delay, TimeUnit unit, Clock clock, Runnable whenTriggered) { Clock theClock = clock != null ? clock : Clock.system(); if (unit == null) throw new IllegalArgumentException("The time unit may not be null"); long delayTimeInMillis = unit.toMillis(delay); @@ -99,6 +151,13 @@ public synchronized boolean isTriggered() { public synchronized Fuse trigger() { triggered = true; resetTime = theClock.currentTimeInMillis() + delayTimeInMillis; + if (whenTriggered != null) { + try { + whenTriggered.run(); + } catch (Throwable t) { + Strongback.logger(Fuse.class).error(t, "Error when calling fuse trigger function"); + } + } return this; } diff --git a/strongback/src/org/strongback/components/PneumaticsModule.java b/strongback/src/org/strongback/components/PneumaticsModule.java new file mode 100644 index 0000000..85ba58c --- /dev/null +++ b/strongback/src/org/strongback/components/PneumaticsModule.java @@ -0,0 +1,101 @@ +/* + * Strongback + * Copyright 2015, Strongback and individual contributors by the @authors tag. + * See the COPYRIGHT.txt in the distribution for a full listing of individual + * contributors. + * + * Licensed under the MIT License; you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.strongback.components; + +/** + * A simple abstraction for the Pneumatics Control Module (PCM). + */ +public interface PneumaticsModule { + + /** + * Gets the sensor that reports the current draw of the compressor. + * + * @return the current sensor for the compressor; never null + */ + public CurrentSensor compressorCurrent(); + + /** + * Get the switch that reports whether the compressor is running. + * + * @return the compressor switch; never null + */ + public Switch compressorRunningSwitch(); + + /** + * Get the switch that reports whether the pressure is below the lower threshold. + * + * @return the low pressure switch; never null + */ + public Switch lowPressureSwitch(); + + /** + * Get the relay that controls whether the module automatically runs the compressor when the {@link #lowPressureSwitch()} is + * triggered and shuts if off when the switch is untriggered (i.e., the maximum pressure threshold is reached). + * + * @return the automatic mode relay; never null + */ + public Relay automaticMode(); + + /** + * Get the faults currently associated with the compressor. These state of these faults may change at any time based upon + * the state of the module, and so they cannot be manually cleared. + * + * @return the current faults; never null + * @see #compressorStickyFaults() + */ + public Faults compressorFaults(); + + /** + * Get the sticky faults associated with the compressor. Once these faults are triggered, they are only reset with + * #clearStickyFaults + * + * @return the sticky faults; never null + * @see #compressorFaults() + * @see #clearStickyFaults() + */ + public Faults compressorStickyFaults(); + + /** + * Clear all {@link #compressorStickyFaults() sticky compressor faults} that may have been triggered. + * @return this instance so that methods can be chained; never null + */ + public PneumaticsModule clearStickyFaults(); + + /** + * The set of possible faults that this module can trigger. + */ + public static interface Faults { + + /** + * The switch that is {@link Switch#isTriggered() triggered} when the compressor is not connected. + * @return the switch; never null + */ + Switch notConnected(); + + /** + * The switch that is {@link Switch#isTriggered() triggered} when the compressor current is too high. + * @return the switch; never null + */ + Switch currentTooHigh(); + + /** + * The switch that is {@link Switch#isTriggered() triggered} when the compressor is not running because it is shorted. + * @return the switch; never null + */ + Switch shorted(); + } +} \ No newline at end of file diff --git a/strongback/src/org/strongback/components/Relay.java b/strongback/src/org/strongback/components/Relay.java index d8bbc8e..4bd7879 100644 --- a/strongback/src/org/strongback/components/Relay.java +++ b/strongback/src/org/strongback/components/Relay.java @@ -16,6 +16,9 @@ package org.strongback.components; +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; + import org.strongback.annotation.ThreadSafe; /** @@ -54,6 +57,7 @@ static enum State { /** * Get the current state of this relay. + * * @return the current state; never null */ State state(); @@ -132,4 +136,34 @@ public Relay off() { } }; } + + /** + * Obtain a relay that instantaneously switches from one state to another using the given functions. + * + * @param switcher the function that switches the state, where true represents {@link State#ON} and + * false represents {@link State#OFF}; may not be null + * @param onState the function that returns true if the current state is {@link State#ON}, or + * false otherwise; may not be null + * @return the relay; never null + */ + static Relay instantaneous(Consumer switcher, BooleanSupplier onState) { + return new Relay() { + @Override + public State state() { + return onState.getAsBoolean() ? State.ON : State.OFF; + } + + @Override + public Relay on() { + switcher.accept(Boolean.TRUE); + return this; + } + + @Override + public Relay off() { + switcher.accept(Boolean.FALSE); + return this; + } + }; + } } \ No newline at end of file diff --git a/strongback/src/org/strongback/hardware/Hardware.java b/strongback/src/org/strongback/hardware/Hardware.java index 7f671fd..e1ed4bb 100644 --- a/strongback/src/org/strongback/hardware/Hardware.java +++ b/strongback/src/org/strongback/hardware/Hardware.java @@ -21,6 +21,7 @@ import org.strongback.components.DistanceSensor; import org.strongback.components.Gyroscope; import org.strongback.components.Motor; +import org.strongback.components.PneumaticsModule; import org.strongback.components.PowerPanel; import org.strongback.components.Relay; import org.strongback.components.Solenoid; @@ -42,6 +43,7 @@ import edu.wpi.first.wpilibj.AnalogTrigger; import edu.wpi.first.wpilibj.BuiltInAccelerometer; import edu.wpi.first.wpilibj.CANTalon; +import edu.wpi.first.wpilibj.Compressor; import edu.wpi.first.wpilibj.DigitalInput; import edu.wpi.first.wpilibj.DoubleSolenoid; import edu.wpi.first.wpilibj.Encoder; @@ -83,6 +85,26 @@ public static PowerPanel powerPanel() { return PowerPanel.create(pdp::getCurrent, pdp::getTotalCurrent, pdp::getVoltage, pdp::getTemperature); } + /** + * Gets the {@link PneumaticsModule} of the robot with the default CAN ID of 0. + * + * @return the {@link PneumaticsModule} of the robot; never null + */ + public static PneumaticsModule pneumaticsModule() { + return new HardwarePneumaticsModule(new Compressor()); + } + + /** + * Gets the {@link PneumaticsModule} of the robot with the supplied CAN ID. Multiple pneumatics modules can be used by + * specifying the correct CAN ID of each module. + * + * @param canID the CAN ID of the module + * @return the {@link PneumaticsModule} of the robot; never null + */ + public static PneumaticsModule pneumaticsModule(int canID) { + return new HardwarePneumaticsModule(new Compressor(canID)); + } + /** * Factory method for angle sensors. */ diff --git a/strongback/src/org/strongback/hardware/HardwarePneumaticsModule.java b/strongback/src/org/strongback/hardware/HardwarePneumaticsModule.java new file mode 100644 index 0000000..a84214c --- /dev/null +++ b/strongback/src/org/strongback/hardware/HardwarePneumaticsModule.java @@ -0,0 +1,107 @@ +/* + * Strongback + * Copyright 2015, Strongback and individual contributors by the @authors tag. + * See the COPYRIGHT.txt in the distribution for a full listing of individual + * contributors. + * + * Licensed under the MIT License; you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.strongback.hardware; + +import org.strongback.components.CurrentSensor; +import org.strongback.components.PneumaticsModule; +import org.strongback.components.Relay; +import org.strongback.components.Switch; + +import edu.wpi.first.wpilibj.Compressor; + +/** + * A {@link PneumaticsModule} implementation based upon the WPILib's {@link Compressor} class, which represents the Pneumatics + * Control Module. + * + * @author Randall Hauch + */ +public class HardwarePneumaticsModule implements PneumaticsModule { + + private final Compressor pcm; + private final Relay closedLoop; + private final Faults instantaneousFaults; + private final Faults stickyFaults; + + HardwarePneumaticsModule(Compressor pcm) { + this.pcm = pcm; + this.closedLoop = Relay.instantaneous(this.pcm::setClosedLoopControl, this.pcm::getClosedLoopControl); + this.instantaneousFaults = new Faults() { + @Override + public Switch currentTooHigh() { + return pcm::getCompressorCurrentTooHighFault; + } + @Override + public Switch notConnected() { + return pcm::getCompressorNotConnectedFault; + } + @Override + public Switch shorted() { + return pcm::getCompressorShortedFault; + } + }; + this.stickyFaults = new Faults() { + @Override + public Switch currentTooHigh() { + return pcm::getCompressorCurrentTooHighStickyFault; + } + @Override + public Switch notConnected() { + return pcm::getCompressorNotConnectedStickyFault; + } + @Override + public Switch shorted() { + return pcm::getCompressorShortedStickyFault; + } + }; + } + + @Override + public CurrentSensor compressorCurrent() { + return pcm::getCompressorCurrent; + } + + @Override + public Switch compressorRunningSwitch() { + return pcm::enabled; + } + + @Override + public Relay automaticMode() { + return closedLoop; + } + + @Override + public Switch lowPressureSwitch() { + return pcm::getPressureSwitchValue; + } + + @Override + public Faults compressorFaults() { + return instantaneousFaults; + } + + @Override + public Faults compressorStickyFaults() { + return stickyFaults; + } + + @Override + public PneumaticsModule clearStickyFaults() { + pcm.clearAllPCMStickyFaults(); + return this; + } +} From 759332b928cc09c44cdce63d342d977615ab9e97 Mon Sep 17 00:00:00 2001 From: Randall Hauch Date: Mon, 26 Oct 2015 12:45:40 -0500 Subject: [PATCH 2/2] Added toString() methods for mock objects --- .../src/org/strongback/mock/MockAccelerometer.java | 4 ++++ .../src/org/strongback/mock/MockAngleSensor.java | 4 ++++ strongback-testing/src/org/strongback/mock/MockClock.java | 4 ++++ strongback-testing/src/org/strongback/mock/MockCompass.java | 5 +++++ .../src/org/strongback/mock/MockCurrentSensor.java | 5 +++++ .../src/org/strongback/mock/MockDistanceSensor.java | 4 ++++ strongback-testing/src/org/strongback/mock/MockMotor.java | 6 ++++++ strongback-testing/src/org/strongback/mock/MockRelay.java | 5 +++++ .../src/org/strongback/mock/MockSolenoid.java | 5 +++++ .../src/org/strongback/mock/MockTemperatureSensor.java | 4 ++++ .../src/org/strongback/mock/MockThreeAxisAccelerometer.java | 5 +++++ .../src/org/strongback/mock/MockTwoAxisAccelerometer.java | 5 +++++ .../src/org/strongback/mock/MockVoltageSensor.java | 4 ++++ 13 files changed, 60 insertions(+) diff --git a/strongback-testing/src/org/strongback/mock/MockAccelerometer.java b/strongback-testing/src/org/strongback/mock/MockAccelerometer.java index 5f1df66..3552547 100644 --- a/strongback-testing/src/org/strongback/mock/MockAccelerometer.java +++ b/strongback-testing/src/org/strongback/mock/MockAccelerometer.java @@ -46,4 +46,8 @@ public MockAccelerometer setAcceleration(double accel) { return this; } + @Override + public String toString() { + return Double.toString(getAcceleration()) + " g/s\u00B2"; + } } \ No newline at end of file diff --git a/strongback-testing/src/org/strongback/mock/MockAngleSensor.java b/strongback-testing/src/org/strongback/mock/MockAngleSensor.java index ab85ba1..66c336e 100644 --- a/strongback-testing/src/org/strongback/mock/MockAngleSensor.java +++ b/strongback-testing/src/org/strongback/mock/MockAngleSensor.java @@ -50,4 +50,8 @@ public MockAngleSensor setAngle(double angle) { return this; } + @Override + public String toString() { + return Double.toString(getAngle()) + "\u00B0"; + } } diff --git a/strongback-testing/src/org/strongback/mock/MockClock.java b/strongback-testing/src/org/strongback/mock/MockClock.java index 52ad55b..b41b30c 100644 --- a/strongback-testing/src/org/strongback/mock/MockClock.java +++ b/strongback-testing/src/org/strongback/mock/MockClock.java @@ -69,4 +69,8 @@ public long currentTimeInMicros() { return ticker.get(); } + @Override + public String toString() { + return Long.toString(currentTimeInMillis()) + " ms"; + } } diff --git a/strongback-testing/src/org/strongback/mock/MockCompass.java b/strongback-testing/src/org/strongback/mock/MockCompass.java index 821dc71..82cc155 100644 --- a/strongback-testing/src/org/strongback/mock/MockCompass.java +++ b/strongback-testing/src/org/strongback/mock/MockCompass.java @@ -49,4 +49,9 @@ public MockCompass setAngle(double angle) { super.setValue(angle); return this; } + + @Override + public String toString() { + return Double.toString(getHeading()) + "\u00B0 heading (" + getAngle() + "\u00B0)"; + } } diff --git a/strongback-testing/src/org/strongback/mock/MockCurrentSensor.java b/strongback-testing/src/org/strongback/mock/MockCurrentSensor.java index 93b3967..12da1e8 100644 --- a/strongback-testing/src/org/strongback/mock/MockCurrentSensor.java +++ b/strongback-testing/src/org/strongback/mock/MockCurrentSensor.java @@ -46,4 +46,9 @@ public MockCurrentSensor setCurrent(double currentInAmps) { return this; } + @Override + public String toString() { + return Double.toString(getCurrent()) + "A"; + } + } diff --git a/strongback-testing/src/org/strongback/mock/MockDistanceSensor.java b/strongback-testing/src/org/strongback/mock/MockDistanceSensor.java index 4a7b038..b3b568b 100644 --- a/strongback-testing/src/org/strongback/mock/MockDistanceSensor.java +++ b/strongback-testing/src/org/strongback/mock/MockDistanceSensor.java @@ -62,4 +62,8 @@ public MockDistanceSensor setDistanceInFeet(double distance) { return setDistanceInInches(distance * 12.0); } + @Override + public String toString() { + return Double.toString(getDistanceInInches()) + " inches"; + } } diff --git a/strongback-testing/src/org/strongback/mock/MockMotor.java b/strongback-testing/src/org/strongback/mock/MockMotor.java index 5679a8c..c08a534 100644 --- a/strongback-testing/src/org/strongback/mock/MockMotor.java +++ b/strongback-testing/src/org/strongback/mock/MockMotor.java @@ -43,6 +43,7 @@ public MockMotor setSpeed(double speed) { return this; } + @Override public MockMotor invert() { return new MockMotor(speed) { @Override @@ -58,4 +59,9 @@ public double getSpeed() { }; } + + @Override + public String toString() { + return Double.toString(getSpeed()); + } } diff --git a/strongback-testing/src/org/strongback/mock/MockRelay.java b/strongback-testing/src/org/strongback/mock/MockRelay.java index 464bd1b..37b04f4 100644 --- a/strongback-testing/src/org/strongback/mock/MockRelay.java +++ b/strongback-testing/src/org/strongback/mock/MockRelay.java @@ -65,4 +65,9 @@ public MockRelay switchingOn() { public State state() { return state; } + + @Override + public String toString() { + return state().name(); + } } diff --git a/strongback-testing/src/org/strongback/mock/MockSolenoid.java b/strongback-testing/src/org/strongback/mock/MockSolenoid.java index 783510e..884c97e 100644 --- a/strongback-testing/src/org/strongback/mock/MockSolenoid.java +++ b/strongback-testing/src/org/strongback/mock/MockSolenoid.java @@ -63,4 +63,9 @@ public MockSolenoid stop() { direction = Direction.STOPPED; return this; } + + @Override + public String toString() { + return getDirection().name(); + } } diff --git a/strongback-testing/src/org/strongback/mock/MockTemperatureSensor.java b/strongback-testing/src/org/strongback/mock/MockTemperatureSensor.java index ee94399..e4d58ea 100644 --- a/strongback-testing/src/org/strongback/mock/MockTemperatureSensor.java +++ b/strongback-testing/src/org/strongback/mock/MockTemperatureSensor.java @@ -46,4 +46,8 @@ public MockTemperatureSensor setTemperature(double temp) { return this; } + @Override + public String toString() { + return Double.toString(getTemperatureInFahrenheit()) + "\u00B0F"; + } } diff --git a/strongback-testing/src/org/strongback/mock/MockThreeAxisAccelerometer.java b/strongback-testing/src/org/strongback/mock/MockThreeAxisAccelerometer.java index 6bdb83e..8b6bb7b 100644 --- a/strongback-testing/src/org/strongback/mock/MockThreeAxisAccelerometer.java +++ b/strongback-testing/src/org/strongback/mock/MockThreeAxisAccelerometer.java @@ -36,4 +36,9 @@ public class MockThreeAxisAccelerometer extends MockTwoAxisAccelerometer impleme public MockAccelerometer getZDirection() { return z; } + + @Override + public String toString() { + return "" + getXDirection().getAcceleration() + ", " + getYDirection().getAcceleration() + ", " + getZDirection().getAcceleration() + " g/s\u00B2"; + } } diff --git a/strongback-testing/src/org/strongback/mock/MockTwoAxisAccelerometer.java b/strongback-testing/src/org/strongback/mock/MockTwoAxisAccelerometer.java index 16a8f93..d8bc675 100644 --- a/strongback-testing/src/org/strongback/mock/MockTwoAxisAccelerometer.java +++ b/strongback-testing/src/org/strongback/mock/MockTwoAxisAccelerometer.java @@ -41,4 +41,9 @@ public MockAccelerometer getXDirection() { public MockAccelerometer getYDirection() { return y; } + + @Override + public String toString() { + return "" + getXDirection().getAcceleration() + ", " + getYDirection().getAcceleration() + " g/s\u00B2"; + } } diff --git a/strongback-testing/src/org/strongback/mock/MockVoltageSensor.java b/strongback-testing/src/org/strongback/mock/MockVoltageSensor.java index 2071a97..133695f 100644 --- a/strongback-testing/src/org/strongback/mock/MockVoltageSensor.java +++ b/strongback-testing/src/org/strongback/mock/MockVoltageSensor.java @@ -46,4 +46,8 @@ public MockVoltageSensor setVoltage(double voltage) { return this; } + @Override + public String toString() { + return Double.toString(getVoltage()) + " V"; + } }