001package jmri.jmrix.maple; 002 003import jmri.Turnout; 004import jmri.implementation.AbstractTurnout; 005import org.slf4j.Logger; 006import org.slf4j.LoggerFactory; 007 008/** 009 * Turnout implementation for Maple systems. 010 * <p> 011 * This object doesn't listen to the interface communications. This is because 012 * it should be the only object that is sending messages for this turnout; more 013 * than one Turnout object pointing to a single device is not allowed. 014 * <p> 015 * Turnouts on the layout may be controlled by one or two output bits. To 016 * control a turnout from one Turnout object via two output bits, the output 017 * bits must be on the same node, the Turnout address must point to the first 018 * output bit, and the second output bit must follow the output bit at the next 019 * address. Valid states for the two bits controlling the two-bit turnout are: 020 * ON OFF, and OFF ON for the two bits. 021 * <p> 022 * This class can also drive pulsed outputs, which can be combined with the 023 * two-bit option in the expected ways. 024 * <p> 025 * When a Turnout is configured for pulsed and two-output, a request to go to a 026 * new CommandedState sets the desired configuration for the pulse interval, 027 * then sets both leads to their off condition. 028 * <p> 029 * When a Turnout is configured for pulsed and one output, a request to go to a 030 * new CommandedState just sets the output on for the interval; it's assumed 031 * that there's something out on the layout that converts that pulse into a 032 * "flip to other state" operation. 033 * <p> 034 * Finally, this implementation supports the "inverted" option. Inverted applies 035 * to the status of the lead on the output itself. 036 * <p> 037 * For example, a pulsed, two-output, inverted turnout will have both pins set 038 * to 1 in the resting state. When THROWN, one lead will be set to 0 for the 039 * configured interval, then set back to 1. 040 * <p> 041 * For more discussion of this, please see the 042 * <a href="http://jmri.org/help/en/html/hardware/cmri/CMRI.shtml#options">documentation 043 * page</a> 044 * (for C/MRI, but this is similar). 045 * 046 * NOTE: In the current version Maple support, code for implementing pulsed 047 * turnouts has been commented out. 048 * 049 * @author Bob Jacobsen Copyright (C) 2003, 2007, 2008 050 * @author David Duchamp Copyright (C) 2004, 2007 051 * @author Dan Boudreau Copyright (C) 2007 052 */ 053public class SerialTurnout extends AbstractTurnout { 054 055 private MapleSystemConnectionMemo _memo = null; 056 057 /** 058 * Create a Turnout object, with both system and user names. 059 * <p> 060 * 'systemName' has already been validated in SerialTurnoutManager 061 * 062 * @param systemName the system name for this Turnout 063 * @param userName the user name for this Turnout 064 * @param memo the memo for the system connection 065 */ 066 public SerialTurnout(String systemName, String userName, MapleSystemConnectionMemo memo) { 067 super(systemName, userName); 068 // Save system Name 069 tSystemName = systemName; 070 _memo = memo; 071 // Extract the Bit from the name 072 tBit = SerialAddress.getBitFromSystemName(systemName, _memo.getSystemPrefix()); 073 } 074 075 /** 076 * Create a Turnout object, with only a system name. 077 * <p> 078 * 'systemName' has already been validated in SerialTurnoutManager 079 * 080 * @param systemName the system name for this Turnout 081 * @param memo the memo for the system connection 082 */ 083 public SerialTurnout(String systemName, MapleSystemConnectionMemo memo) { 084 super(systemName); 085 // Save system Name 086 tSystemName = systemName; 087 _memo = memo; 088 // Extract the Bit from the name 089 tBit = SerialAddress.getBitFromSystemName(systemName, _memo.getSystemPrefix()); 090 } 091 092 /** 093 * {@inheritDoc} 094 */ 095 @Override 096 protected void forwardCommandChangeToLayout(int newState) { 097 try { 098 sendMessage(stateChangeCheck(newState)); 099 } catch (IllegalArgumentException ex) { 100 log.error("new state invalid, Turnout not set"); 101 } 102 } 103 104 /** 105 * Turnouts do support inversion. 106 */ 107 @Override 108 public boolean canInvert() { 109 return true; 110 } 111 112 @Override 113 protected void turnoutPushbuttonLockout(boolean _pushButtonLockout) { 114 if (log.isDebugEnabled()) { 115 log.debug("Send command to {} Pushbutton", (_pushButtonLockout ? "Lock" : "Unlock")); 116 } 117 } 118 119 // data members 120 String tSystemName; // System Name of this turnout 121 protected int tBit; // bit number of turnout control in Serial node 122// protected SerialNode tNode = null; 123 protected javax.swing.Timer mPulseClosedTimer = null; 124 protected javax.swing.Timer mPulseThrownTimer = null; 125 protected boolean mPulseTimerOn = false; 126 127 /** 128 * Control the actual layout hardware. The request is for a particular 129 * functional setting, e.g. CLOSED or THROWN. The "inverted" status of the 130 * output leads is handled here. 131 * @param closed true sets the Turnout to CLOSED 132 */ 133 protected void sendMessage(boolean closed) { 134 // if a Pulse Timer is running, ignore the call 135 if (!mPulseTimerOn) { 136 if (getNumberControlBits() == 1) { 137 // check for pulsed control 138 if (getControlType() == 0) { 139 // steady state control, get current status of the output bit 140 if ((_memo.getTrafficController().outputBits().getOutputBit(tBit) ^ getInverted()) != closed) { 141 // bit state is different from the requested state, set it 142 _memo.getTrafficController().outputBits().setOutputBit(tBit, closed ^ getInverted()); 143 } else { 144 // Bit state is the same as requested state, so nothing 145 // will happen if requested state is set. 146 // Check if turnout known state is different from requested state 147 int kState = getKnownState(); 148 if (closed) { 149 // CLOSED is being requested 150 if ((kState & Turnout.THROWN) != 0) { 151 // known state is different from output bit, set output bit to be correct 152 // for known state, then start a timer to set it to requested state 153 _memo.getTrafficController().outputBits().setOutputBit(tBit, false ^ getInverted()); 154// // start a timer to finish setting this turnout 155// if (mPulseClosedTimer==null) { 156// mPulseClosedTimer = new javax.swing.Timer(OutputBits.instance().getPulseWidth(), 157// new java.awt.event.ActionListener() { 158// public void actionPerformed(java.awt.event.ActionEvent e) { 159// OutputBits.instance().setOutputBit(tBit, true^getInverted()); 160// mPulseClosedTimer.stop(); 161// mPulseTimerOn = false; 162// } 163// }); 164// } 165// mPulseTimerOn = true; 166// mPulseClosedTimer.start(); 167 } 168 } else { 169 // THROWN is being requested 170 if ((kState & Turnout.CLOSED) != 0) { 171 // known state is different from output bit, set output bit to be correct 172 // for known state, then start a timer to set it to requested state 173 _memo.getTrafficController().outputBits().setOutputBit(tBit, true ^ getInverted()); 174// // start a timer to finish setting this turnout 175// if (mPulseThrownTimer==null) { 176// mPulseThrownTimer = new javax.swing.Timer(OutputBits.instance().getPulseWidth(), 177// new java.awt.event.ActionListener() { 178// public void actionPerformed(java.awt.event.ActionEvent e) { 179// OutputBits.instance().setOutputBit(tBit, false^getInverted()); 180// mPulseThrownTimer.stop(); 181// mPulseTimerOn = false; 182// } 183// }); 184// } 185// mPulseTimerOn = true; 186// mPulseThrownTimer.start(); 187 } 188 } 189 } 190 } else { 191 // Pulse control 192// int iTime = OutputBits.instance().getPulseWidth(); 193// // Get current known state of turnout 194// int kState = getKnownState(); 195// if ( (closed && ((kState & Turnout.THROWN) != 0)) || 196// (!closed && ((kState & Turnout.CLOSED) != 0)) ) { 197// // known and requested are different, a change is requested 198// // Pulse the line, first turn bit on 199// OutputBits.instance().setOutputBit(tBit,false^getInverted()); 200// // Start a timer to return bit to off state 201// if (mPulseClosedTimer==null) { 202// mPulseClosedTimer = new javax.swing.Timer(iTime, new 203// java.awt.event.ActionListener() { 204// public void actionPerformed(java.awt.event.ActionEvent e) { 205// OutputBits.instance().setOutputBit(tBit, true^getInverted()); 206// mPulseClosedTimer.stop(); 207// mPulseTimerOn = false; 208// } 209// }); 210// } 211// mPulseTimerOn = true; 212// mPulseClosedTimer.start(); 213// } 214 } 215 } 216 } 217 } 218 219 private final static Logger log = LoggerFactory.getLogger(SerialTurnout.class); 220 221}