001package jmri.jmrix.secsi;
002
003import jmri.Turnout;
004import jmri.implementation.AbstractTurnout;
005import org.slf4j.Logger;
006import org.slf4j.LoggerFactory;
007
008/**
009 * Extend jmri.AbstractTurnout for SECSI serial layouts.
010 *
011 * This object doesn't listen to the SECSI serial communications. This is
012 * because it should be the only object that is sending messages for this
013 * turnout; more than one Turnout object pointing to a single device is not
014 * allowed.
015 *
016 * @author Bob Jacobsen Copyright (C) 2003, 2006, 2007
017 */
018public class SerialTurnout extends AbstractTurnout {
019
020    private SecsiSystemConnectionMemo memo = null;
021
022    /**
023     * Create a Turnout object, with both system and user names.
024     * <p>
025     * 'systemName' was previously validated in SerialTurnoutManager.
026     * @param systemName turnout system name
027     * @param userName turnout user name
028     * @param _memo system connection
029     */
030    public SerialTurnout(String systemName, String userName, SecsiSystemConnectionMemo _memo) {
031        super(systemName, userName);
032        memo = _memo;
033        // Save system Name
034        tSystemName = systemName;
035        // Extract the Bit from the name
036        tBit = SerialAddress.getBitFromSystemName(systemName, memo.getSystemPrefix());
037    }
038
039    /**
040     * {@inheritDoc}
041     * Sends a Secsi command
042     */
043    @Override
044    protected void forwardCommandChangeToLayout(int newState) {
045        // implementing classes will typically have a function/listener to get
046        // updates from the layout, which will then call
047        //  public void firePropertyChange(String propertyName,
048        //          Object oldValue, Object newValue)
049        // _once_ if anything has changed state (or set the commanded state directly)
050
051        // sort out states
052        if ((newState & Turnout.CLOSED) != 0) {
053            // first look for the double case, which we can't handle
054            if ((newState & Turnout.THROWN) != 0) {
055                // this is the disaster case!
056                log.error("Cannot command both CLOSED and THROWN {}", newState);
057                return;
058            } else {
059                // send a CLOSED command
060                sendMessage(getInverted());
061            }
062        } else {
063            // send a THROWN command
064            sendMessage(!getInverted());
065        }
066    }
067
068    @Override
069    protected void turnoutPushbuttonLockout(boolean _pushButtonLockout) {
070        log.debug("Send command to {} Pushbutton", (_pushButtonLockout ? "Lock" : "Unlock"));
071    }
072
073    @Override
074    public void dispose() {
075        // no connections need to be broken
076        super.dispose();
077    }
078
079    // data members
080    String tSystemName; // System Name of this turnout
081    int tBit;           // bit number of turnout control in Serial node
082
083    protected void sendMessage(boolean closed) {
084        SerialNode tNode = SerialAddress.getNodeFromSystemName(tSystemName, memo.getTrafficController());
085        if (tNode == null) {
086            // node does not exist, ignore call
087            return;
088        }
089        tNode.setOutputBit(tBit, closed);
090    }
091
092    private final static Logger log = LoggerFactory.getLogger(SerialTurnout.class);
093
094}