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}