001package jmri.jmrix.acela;
002
003import jmri.implementation.AbstractTurnout;
004import org.slf4j.Logger;
005import org.slf4j.LoggerFactory;
006
007/**
008 * Implementation of the Turnout Object for Acela
009 * <p>
010 * Based in part on SerialTurnout.java
011 *
012 * @author Dave Duchamp Copyright (C) 2004
013 *
014 * @author Bob Coleman Copyright (C) 2007, 2008 Based on CMRI serial example,
015 * modified to establish Acela support.
016 */
017public class AcelaTurnout extends AbstractTurnout {
018
019    private AcelaSystemConnectionMemo _memo = null;
020
021    /**
022     * Create a Turnout object, with only system name.
023     * <p>
024     * 'SystemName' was previously validated in AcelaTurnoutManager
025     *
026     * @param systemName the system name for this Turnout
027     * @param memo       the memo for the system connection
028     */
029    public AcelaTurnout(String systemName, AcelaSystemConnectionMemo memo) {
030        super(systemName);
031        _memo = memo;
032        initializeTurnout(systemName);
033    }
034
035    /**
036     * Create a Turnout object, with both system and user names.
037     * <p>
038     * 'systemName' was previously validated in AcelaTurnoutManager
039     *
040     * @param systemName the system name for this Turnout
041     * @param userName   the user name for this Turnout
042     * @param memo       the memo for the system connection
043     */
044    public AcelaTurnout(String systemName, String userName, AcelaSystemConnectionMemo memo) {
045        super(systemName, userName);
046        _memo = memo;
047        initializeTurnout(systemName);
048    }
049
050    /**
051     * Set up system dependent instance variables and set system independent
052     * instance variables to default values.
053     * Note: most instance variables are in AbstractTurnout.java
054     */
055    private void initializeTurnout(String systemName) {
056        // Extract the Bit from the name
057        mBit = AcelaAddress.getBitFromSystemName(systemName, _memo.getSystemPrefix());
058
059        // Set initial state
060        setState(UNKNOWN);
061        // Set defaults for all other instance variables
062    }
063
064    /**
065     * System dependent instance variables
066     */
067    protected int mState = UNKNOWN;  // current state of this turnout
068    int mBit = -1;                // global address from 0
069
070    /**
071     * {@inheritDoc}
072     */
073    @Override
074    protected void forwardCommandChangeToLayout(int newState) {
075        try {
076            sendMessage(stateChangeCheck(newState));
077        } catch (IllegalArgumentException ex) {
078            log.error("new state invalid, Turnout not set");
079        }
080    }
081
082    /**
083     * Send a message to the layout to lock or unlock the turnout push buttons.
084     * <p>
085     * This implementation does nothing, as Acela turnouts do not support
086     * lockout.
087     *
088     * @param pushButtonLockout true to lockout turnout push buttons; false
089     *                          otherwise
090     */
091    @Override
092    protected void turnoutPushbuttonLockout(boolean pushButtonLockout) {
093        // Acela turnouts do not currently support lockout
094    }
095
096    // Acela turnouts do support inversion
097    @Override
098    public boolean canInvert() {
099        return true;
100    }
101
102    // method which takes a turnout state as a parameter and adjusts it as necessary
103    // to reflect the turnout invert property
104    private int adjustStateForInversion(int rawState) {
105        if (getInverted() && (rawState == CLOSED || rawState == THROWN)) {
106            if (rawState == CLOSED) {
107                return THROWN;
108            } else {
109                return CLOSED;
110            }
111        } else {
112            return rawState;
113        }
114    }
115
116    protected void sendMessage(boolean closed) {
117        int newState;
118        if (closed) {
119            newState = adjustStateForInversion(CLOSED);
120        } else {
121            newState = adjustStateForInversion(THROWN);
122        }
123
124        AcelaNode mNode = AcelaAddress.getNodeFromSystemName(mSystemName, _memo);
125
126        if (mNode != null) {
127            switch (newState) {
128                case THROWN:
129                    mNode.setOutputBit(mBit, true);
130                    break;
131                case CLOSED:
132                    mNode.setOutputBit(mBit, false);
133                    break;
134                default:
135                    log.warn("illegal state requested for Turnout: {}", getSystemName());
136                    break;
137            }
138        }
139
140        if (newState != mState) {
141            int oldState = mState;
142            mState = newState;
143
144            // notify listeners, if any
145            firePropertyChange("KnownState", oldState, newState);
146        }
147    }
148
149    private final static Logger log = LoggerFactory.getLogger(AcelaTurnout.class);
150
151}