001package jmri.jmrix.tams;
002
003import javax.annotation.Nonnull;
004import jmri.Turnout;
005import org.slf4j.Logger;
006import org.slf4j.LoggerFactory;
007
008/**
009 * Implement turnout manager for Tams systems. Reworked to support binary
010 * commands and polling of command station.
011 * <p>
012 * Based on work by Bob Jacobsen and Kevin Dickerson.
013 *
014 * @author  Jan Boen
015 */
016public class TamsTurnoutManager extends jmri.managers.AbstractTurnoutManager implements TamsListener {
017
018    public TamsTurnoutManager(TamsSystemConnectionMemo memo) {
019        super(memo);
020        //Request status of turnout changes
021        TamsMessage m = TamsMessage.getXEvtTrn();
022        memo.getTrafficController().sendTamsMessage(m, TamsTurnoutManager.this);
023        memo.getTrafficController().addPollMessage(m, TamsTurnoutManager.this);
024    }
025
026    /**
027     * {@inheritDoc}
028     */
029    @Override
030    @Nonnull
031    public TamsSystemConnectionMemo getMemo() {
032        return (TamsSystemConnectionMemo) memo;
033    }
034
035    /**
036     * {@inheritDoc}
037     */
038    @Nonnull
039    @Override
040    protected Turnout createNewTurnout(@Nonnull String systemName, String userName) throws IllegalArgumentException {
041        int addr;
042        try {
043            addr = Integer.parseInt(systemName.substring(getSystemPrefix().length() + 1));
044        } catch (NumberFormatException e) {
045            log.error("failed to convert systemName {} to a turnout address", systemName);
046            throw new IllegalArgumentException("Failed to convert systemName '"+systemName+"' to a Turnout address");
047        }
048        Turnout t = new TamsTurnout(addr, getSystemPrefix(), getMemo().getTrafficController());
049        t.setUserName(userName);
050        return t;
051    }
052    
053    /**
054     * Validates to contain at least 1 number . . .
055     * <p>
056     * TODO: check validateIntegerSystemNameFormat if min / max values are known.
057     * {@inheritDoc}
058     */
059    @Override
060    @Nonnull
061    public String validateSystemNameFormat(@Nonnull String name, @Nonnull java.util.Locale locale) throws jmri.NamedBean.BadSystemNameException {
062        return validateTrimmedMin1NumberSystemNameFormat(name,locale);
063    }
064
065    @Override
066    public boolean allowMultipleAdditions(@Nonnull String systemName) {
067        return true;
068    }
069
070    boolean noWarnDelete = false;
071
072    @Override
073    public void message(TamsMessage m) {
074        //TamsMessages are ignored
075    }
076
077    @Override
078    public void reply(TamsReply r) {//To listen for Turnout status changes
079        //TamsMessage tm = TamsMessage.getXEvtTrn();
080        if ( getMemo().getTrafficController().replyType == 'T') {//Only handle Turnout events
081            log.debug("*** Tams Turnout Reply ***");
082            if ( getMemo().getTrafficController().replyBinary ) {//Typical polling message
083                log.debug("Reply to binary command = {}", r.toString());
084                if ((r.getNumDataElements() > 1) && (r.getElement(0) > 0x00) && (r.getElement(0) != 'T')) {
085                    //Here we break up a long turnout related TamsReply into individual turnout status'
086                    for (int i = 1; i < r.getNumDataElements() - 1; i = i + 2) {
087                        //create a new TamsReply and pass it to the decoder
088                        TamsReply tr = new TamsReply();
089                        tr.setBinary(true);
090                        tr.setElement(0, r.getElement(i));
091                        tr.setElement(1, r.getElement(i + 1));
092                        log.debug("Going to pass this to the decoder = {}", tr.toString());
093                        //The decodeTurnoutState will do the actual decoding of each individual turnout
094                        decodeTurnoutState(tr, getSystemPrefix(), getMemo().getTrafficController());
095                    }
096                }
097            } else {//xSR is an ASCII message
098                //Nothing to do really
099                log.debug("Reply to ASCII command = {}", r.toString());
100            }
101        }
102    }
103
104    void decodeTurnoutState(TamsReply r, String prefix, TamsTrafficController tc) {
105        //reply to XEvtSen consists of 2 bytes per turnout
106        //1: LSB of turnout address (A0 .. A7)
107        //2: MSB of turnout address (A8 .. A10) incl. direction
108        //bit#   7     6     5     4     3     2     1     0
109        //+-----+-----+-----+-----+-----+-----+-----+-----+
110        //|Color| Sts |  0  |  0  |  0  | A10 |  A9 |  A8 |
111        //+-----+-----+-----+-----+-----+-----+-----+-----+
112        //Color 1 = straight (green), 0 = turnout (red)
113        //Sts 1 = on, 0 = off
114        //A10..A8 MSBs of turnout address
115        int lowerByte = r.getElement(0) & 0xff;
116        int upperByte = r.getElement(1) & 0xff;
117        log.debug("Decoding turnout");
118        //log.debug("Lower Byte: {}", lowerByte);
119        //log.debug("Upper Byte: {}", upperByte);
120        //Core logic to be added here
121        int turnoutAddress = (upperByte & 0x07) * 256 + lowerByte;
122        String turnoutName = prefix + "T" + Integer.toString(turnoutAddress);
123        int turnoutState = Turnout.THROWN;
124        if ((upperByte & 0x80) == 0x80) {//Only need bit #7
125            turnoutState = Turnout.CLOSED;
126        }
127        log.debug("Turnout Address: -{}-, state: {}", turnoutName, turnoutState);
128
129        //OK. Now how do we get the turnout to update in JMRI?
130        //Next line provided via JMRI dev's
131        TamsTurnout ttu = (TamsTurnout)provideTurnout(turnoutName);
132        ttu.setCommandedStateFromCS(turnoutState);
133        ttu.setKnownStateFromCS(turnoutState);
134    }
135
136    @Override
137    public void dispose() {
138        getMemo().getTrafficController().removePollMessage(TamsMessage.getXEvtTrn(), this);
139        super.dispose();
140    }
141
142    private final static Logger log = LoggerFactory.getLogger(TamsTurnoutManager.class);
143
144}