001package jmri.jmrix.powerline;
002
003import jmri.Turnout;
004import jmri.implementation.AbstractTurnout;
005import org.slf4j.Logger;
006import org.slf4j.LoggerFactory;
007
008/**
009 * Turnout implementation for X10.
010 * <p>
011 * This object doesn't listen to the serial communications. It should
012 * eventually, so it can track changes outside the program.
013 * <p>
014 * Within JMRI, only one Turnout object should besending messages to a turnout
015 * address; more than one Turnout object pointing to a single device is not
016 * allowed.
017 *
018 * Extend jmri.AbstractTurnout for powerline serial layouts
019 *
020 * @author Bob Jacobsen Copyright (C) 2003, 2006, 2007, 2008 Converted to
021 * multiple connection
022 * @author kcameron Copyright (C) 2011
023 */
024public class SerialTurnout extends AbstractTurnout {
025
026    /**
027     * Create a Turnout object, with both system and user names.
028     * <p>
029     * 'systemName' was previously validated in SerialTurnoutManager
030     * @param systemName system name
031     * @param tc traffic controller
032     * @param userName user name
033     */
034    public SerialTurnout(String systemName, SerialTrafficController tc, String userName) {
035        super(systemName, userName);
036        this.tc = tc;
037        // Convert to the two-part X10 address
038        housecode = tc.getAdapterMemo().getSerialAddress().x10HouseCodeAsValueFromSystemName(getSystemName());
039        devicecode = tc.getAdapterMemo().getSerialAddress().x10DeviceCodeAsValueFromSystemName(getSystemName());
040    }
041
042    private SerialTrafficController tc = null;
043
044    /**
045     * {@inheritDoc}
046     */
047    @Override
048    protected void forwardCommandChangeToLayout(int newState) {
049        // implementing classes will typically have a function/listener to get
050        // updates from the layout, which will then call
051        //  public void firePropertyChange(String propertyName,
052        //                    Object oldValue,
053        //      Object newValue)
054        // _once_ if anything has changed state (or set the commanded state directly)
055
056        // sort out states
057        if ((newState & Turnout.CLOSED) != 0) {
058            // first look for the double case, which we can't handle
059            if ((newState & Turnout.THROWN) != 0) {
060                // this is the disaster case!
061                log.error("Cannot command both CLOSED and THROWN {}", newState);
062                return;
063            } else {
064                // send a CLOSED command
065                sendMessage(!getInverted());
066            }
067        } else {
068            // send a THROWN command
069            sendMessage(getInverted());
070        }
071    }
072
073    @Override
074    protected void turnoutPushbuttonLockout(boolean _pushButtonLockout) {
075        log.debug("Send command to {} Pushbutton", (_pushButtonLockout ? "Lock" : "Unlock"));
076    }
077
078    // data members holding the X10 address
079    int housecode = -1;
080    int devicecode = -1;
081
082    protected void sendMessage(boolean closed) {
083        if (log.isDebugEnabled()) {
084            log.debug("set closed {} house {} device {}", closed, X10Sequence.houseCodeToText(housecode), devicecode);
085        }
086        // create output sequence of address, then function
087        X10Sequence out = new X10Sequence();
088        out.addAddress(housecode, devicecode);
089        out.addFunction(housecode, (closed ? X10Sequence.FUNCTION_OFF : X10Sequence.FUNCTION_ON), 0);
090        // send
091        tc.sendX10Sequence(out, null);
092    }
093
094    private final static Logger log = LoggerFactory.getLogger(SerialTurnout.class);
095
096}