001package jmri.jmrix.pi;
002
003import com.pi4j.io.gpio.GpioController;
004import com.pi4j.io.gpio.GpioFactory;
005import com.pi4j.io.gpio.GpioPinDigitalOutput;
006import com.pi4j.io.gpio.Pin;
007import com.pi4j.io.gpio.PinPullResistance;
008import com.pi4j.io.gpio.PinState;
009import com.pi4j.io.gpio.RaspiPin;
010
011import jmri.implementation.AbstractTurnout;
012import jmri.jmrix.pi.simulator.GpioSimulator;
013
014import org.slf4j.Logger;
015import org.slf4j.LoggerFactory;
016
017/**
018 * Turnout interface to RaspberryPi GPIO pins.
019 *
020 * @author Paul Bender Copyright (C) 2015
021 */
022public class RaspberryPiTurnout extends AbstractTurnout implements java.io.Serializable {
023
024    // in theory gpio can be static (as in PiSensor) because there will only ever
025    // be one, but the library handles the details that make it a
026    // singleton.
027   private GpioController gpio = null;
028   private GpioPinDigitalOutput pin = null;
029
030   public RaspberryPiTurnout(String systemName) {
031        super(systemName);
032        log.trace("Provisioning turnout '{}'", systemName);
033        init(systemName);
034   }
035
036   public RaspberryPiTurnout(String systemName, String userName) {
037        super(systemName, userName);
038        log.trace("Provisioning turnout '{}' with username '{}'", systemName, userName);
039        init(systemName);
040   }
041
042   /**
043    * Common initialization for all constructors.
044    * <p>
045    * Compare {@link RaspberryPiSensor}
046    */
047   private void init(String systemName) {
048       log.debug("Provisioning turnout {}", systemName);
049       if (gpio == null) {
050            if (!RaspberryPiAdapter.isSimulator()) {
051               gpio = GpioFactory.getInstance();
052           } else {
053               gpio = GpioSimulator.getInstance();
054           }
055       }
056       int address = Integer.parseInt(getSystemName().substring(getSystemName().lastIndexOf("T") + 1));
057       String pinName = "GPIO " + address;
058       Pin p = RaspiPin.getPinByName(pinName);
059       if (p != null) {
060           try {
061            pin = gpio.provisionDigitalOutputPin(p, getSystemName());
062           } catch (java.lang.RuntimeException re) {
063               log.error("Provisioning turnout {} failed with: {}", systemName, re.getMessage());
064               throw new IllegalArgumentException(re.getMessage());
065           }
066           if (pin != null) {
067               pin.setShutdownOptions(true, PinState.LOW, PinPullResistance.OFF);
068           } else {
069               String msg = Bundle.getMessage("ProvisioningFailed", pinName, getSystemName());
070               log.error(msg);
071               throw new IllegalArgumentException(msg);
072           }
073       } else {
074           String msg = Bundle.getMessage("PinNameNotValid", pinName, systemName);
075           log.error(msg);
076           throw new IllegalArgumentException(msg);
077       }
078   }
079
080   //support inversion for RPi turnouts
081   @Override
082   public boolean canInvert() {
083       return true;
084   }
085
086   /**
087    * {@inheritDoc}
088    * Sets the GPIO pin.
089    */
090   @Override
091   protected void forwardCommandChangeToLayout(int newState) {
092      if (newState == CLOSED) {
093         log.debug("Setting turnout '{}' to CLOSED", getSystemName());
094         if (!getInverted()) {
095             pin.high();
096         } else {
097             pin.low();
098         }
099      } else if (newState == THROWN) {
100         log.debug("Setting turnout '{}' to THROWN", getSystemName());
101         if (!getInverted()) {
102             pin.low();
103         } else {
104             pin.high();
105         }
106      }
107   }
108
109   @Override
110   public void dispose() {
111       try {
112           gpio.unprovisionPin(pin);
113           // will remove it from the <GpioPin> pins list in _gpio
114       } catch (com.pi4j.io.gpio.exception.GpioPinNotProvisionedException npe){
115           log.trace("Pin not provisioned, was this turnout already disposed?");
116       }
117       super.dispose();
118   }
119
120   @Override
121   protected void turnoutPushbuttonLockout(boolean locked){
122   }
123
124    private final static Logger log = LoggerFactory.getLogger(RaspberryPiTurnout.class);
125
126}