/*
* Copyright 2015-2016 Cel Skeggs
*
* This file is part of the CCRE, the Common Chicken Runtime Engine.
*
* The CCRE is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* The CCRE is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the CCRE. If not, see <http://www.gnu.org/licenses/>.
*/
package ccre.ctrl;
import java.util.HashMap;
import ccre.channel.BooleanInput;
import ccre.channel.DerivedBooleanInput;
import ccre.channel.DerivedFloatInput;
import ccre.channel.EventInput;
import ccre.channel.FloatInput;
import ccre.channel.FloatOutput;
import ccre.verifier.FlowPhase;
/**
* An abstract Joystick implementation, that allows one to convert a poll-based
* interface into an event-driven dataflow Joystick.
*
* @author skeggsc
*/
public abstract class AbstractJoystick implements Joystick {
private final EventInput check;
private final int axisCount, buttonCount;
/**
* Creates a new AbstractJoystick. When <code>check</code> is fired, the
* Joystick checks to see if any buttons, any axes, or the POV hat have
* changed state. If they have, their corresponding channels are updated.
*
* @param check when to poll the Joystick.
* @param axisCount the number of axes on the Joystick.
* @param buttonCount the number of buttons on the Joystick.
*/
public AbstractJoystick(EventInput check, int axisCount, int buttonCount) {
this.check = check;
this.axisCount = axisCount;
this.buttonCount = buttonCount;
}
private final HashMap<Integer, BooleanInput> buttons = new HashMap<>();
private final HashMap<Integer, FloatInput> floats = new HashMap<>();
private final HashMap<Integer, BooleanInput> povs = new HashMap<>();
private float rumbleStateLeft, rumbleStateRight;
@Override
public BooleanInput button(int btn) {
BooleanInput old = buttons.get(btn);
if (old != null) {
return old;
}
if (btn < 1 || btn > buttonCount) {
throw new IllegalArgumentException("button index must be in range of 1 ... " + buttonCount);
}
BooleanInput out = new DerivedBooleanInput(check) {
@Override
protected boolean apply() {
return getButton(btn);
}
};
buttons.put(btn, out);
return out;
}
/**
* Polls button number <code>btn</code> for its state.
*
* @param btn the button number, which is in the range 1 to
* <code>buttonCount</code>, inclusive.
* @return true if the button is currently pressed, or false if it is not.
*/
@FlowPhase
protected abstract boolean getButton(int btn);
@Override
public FloatInput axis(int axis) {
FloatInput old = floats.get(axis);
if (old != null) {
return old;
}
if (axis < 1 || axis > axisCount) {
throw new IllegalArgumentException("axis index must be in range of 1 ... " + axisCount);
}
FloatInput out = new DerivedFloatInput(check) {
@Override
protected float apply() {
return getAxis(axis);
}
};
floats.put(axis, out);
return out;
}
/**
* Polls axis number <code>axis</code> for its state.
*
* @param axis the axis number, which is in the range 1 to
* <code>axisCount</code>, inclusive.
* @return the axis's current position, from -1.0 to +1.0.
*/
@FlowPhase
protected abstract float getAxis(int axis);
@Override
public BooleanInput isPOV(int direction) {
BooleanInput old = povs.get(direction);
if (old != null) {
return old;
}
if (direction < 0 || direction >= 360) {
throw new IllegalArgumentException("isPOV index must be in range of 0 ... 359");
}
BooleanInput out = new DerivedBooleanInput(check) {
@Override
protected boolean apply() {
return getPOV(direction);
}
};
povs.put(direction, out);
return out;
}
/**
* Polls the POV hat and checks if it points in a certain direction.
*
* @param direction the direction to check, in degrees from 0 to 359,
* inclusive.
* @return true if the POV hat is currently pointing in this direction, or
* false otherwise.
*/
@FlowPhase
protected abstract boolean getPOV(int direction);
@Override
public FloatOutput rumble(boolean right) {
return (value) -> {
if (right) {
rumbleStateRight = value;
} else {
rumbleStateLeft = value;
}
setRumble(rumbleStateLeft, rumbleStateRight);
};
}
/**
* Changes the rumble level of the rumblers, if they exist.
*
* @param left a value from 0.0 to 1.0
* @param right a value from 0.0 to 1.0
*/
@FlowPhase
protected abstract void setRumble(float left, float right);
}