001package jmri.jmrix.loconet;
002
003import org.slf4j.Logger;
004import org.slf4j.LoggerFactory;
005
006/**
007 * Base for classes representing a LocoNet communications port.
008 *
009 * @author Bob Jacobsen Copyright (C) 2001, 2002
010 */
011public abstract class LnPortController extends jmri.jmrix.AbstractSerialPortController {
012    // base class. Implementations will provide InputStream and OutputStream
013    // objects to LnTrafficController classes, who in turn will deal in messages.
014
015    protected LnPortController(LocoNetSystemConnectionMemo connectionMemo) {
016        super(connectionMemo);
017        setManufacturer(LnConnectionTypeList.DIGITRAX);
018
019        allowConnectionRecovery = true;
020        reconnectMaxAttempts = -1; // retry indefinitely
021    }
022
023    /**
024     * Check that this object is ready to operate. This is a question of
025     * configuration, not transient hardware status.
026     */
027    @Override
028    public abstract boolean status();
029
030    /**
031     * Can the port accept additional characters? This might go false for short
032     * intervals, but it might also stick off if something goes wrong.
033     * <p>
034     * Provide a default implementation for the MS100, etc.
035     *
036     * @return _always_ true, as we rely on the queueing in the port itself
037     */
038    public boolean okToSend() {
039        return true;
040    }
041
042    private final java.util.concurrent.locks.Lock lockRecover =
043            new java.util.concurrent.locks.ReentrantLock();
044
045    protected LnCommandStationType commandStationType = null;
046
047    protected boolean mTurnoutNoRetry = false;
048    protected boolean mTurnoutExtraSpace = false;
049    protected boolean mInterrogateAtStart = true;
050    protected boolean mTranspondingAvailable = false;
051
052    protected boolean mLoconetProtocolAutoDetect = true;
053
054    protected LnCommandStationType[] commandStationTypes = {
055        LnCommandStationType.COMMAND_STATION_DCS100,
056        LnCommandStationType.COMMAND_STATION_DCS240,
057        LnCommandStationType.COMMAND_STATION_DCS240PLUS,
058        LnCommandStationType.COMMAND_STATION_DCS210,
059        LnCommandStationType.COMMAND_STATION_DCS210PLUS,
060        LnCommandStationType.COMMAND_STATION_DCS200,
061        LnCommandStationType.COMMAND_STATION_DCS050,
062        LnCommandStationType.COMMAND_STATION_DCS051,
063        LnCommandStationType.COMMAND_STATION_DCS052,
064        LnCommandStationType.COMMAND_STATION_DB150,
065        LnCommandStationType.COMMAND_STATION_IBX_TYPE_1,
066        LnCommandStationType.COMMAND_STATION_IBX_TYPE_2,
067        LnCommandStationType.COMMAND_STATION_LBPS,
068        LnCommandStationType.COMMAND_STATION_MM};
069
070    protected String[] commandStationNames;
071
072    {
073        commandStationNames = new String[commandStationTypes.length];
074        int i = 0;
075        for (LnCommandStationType type : commandStationTypes) {
076            commandStationNames[i++] = type.getName();
077        }
078    }
079
080    // There are also "PR3 standalone programmer" and "Stand-alone LocoNet" in pr3/PR3Adapter
081    // and "PR2 standalone programmer" in pr2/Pr2Adapter
082    /**
083     * Set config info from a name, which needs to be one of the valid ones.
084     * @param name the name of the command station type
085     */
086    public void setCommandStationType(String name) {
087        setCommandStationType(LnCommandStationType.getByName(name));
088    }
089
090    /**
091     * Set config info from the command station type enum.
092     * @param value the LnCommandStationType
093     */
094    public void setCommandStationType(LnCommandStationType value) {
095        if (value == null) {
096            return;  // can happen while switching protocols
097        }
098        log.debug("setCommandStationType: {}", value); // NOI18N
099        commandStationType = value;
100    }
101
102    public void setTurnoutHandling(String value) {
103        if (value.equals("One Only") || value.equals(Bundle.getMessage("HandleOneOnly"))
104                || value.equals("Both") || value.equals(Bundle.getMessage("HandleBoth"))) {
105            mTurnoutNoRetry = true;
106        }
107        log.debug("turnout no retry: {}", mTurnoutNoRetry); // NOI18N
108        if (value.equals("Spread") || value.equals(Bundle.getMessage("HandleSpread"))
109                || value.equals("Both") || value.equals(Bundle.getMessage("HandleBoth"))) {
110            mTurnoutExtraSpace = true;
111        }
112        log.debug("turnout extra space: {}", mTurnoutExtraSpace); // NOI18N
113    }
114
115    public void setTranspondingAvailable(String value) {
116        // default (most common state) is off, so just check for Yes
117        mTranspondingAvailable = (value.equals("Yes") || value.equals(Bundle.getMessage("ButtonYes")));
118        log.debug("transponding available: {}", mTranspondingAvailable); // NOI18N
119    }
120
121    public void setLoconetProtocolAutoDetect(String value) {
122        // default (most common state) is off, so just check for Yes
123        mLoconetProtocolAutoDetect = (value.equals("Yes") || value.equals(Bundle.getMessage("LoconetProtocolAutoDetect")));
124        log.debug("Loconet XPSlots: {}", mLoconetProtocolAutoDetect); // NOI18N
125    }
126
127    public void setInterrogateOnStart(String value) {
128        // default (most common state) is on, so just check for No
129        mInterrogateAtStart = !(value.equals("No") || value.equals(Bundle.getMessage("ButtonNo")));
130        log.debug("tInterrogate on Start: {}", mInterrogateAtStart); // NOI18N
131    }
132
133    @Override
134    public LocoNetSystemConnectionMemo getSystemConnectionMemo() {
135        return (LocoNetSystemConnectionMemo) super.getSystemConnectionMemo();
136    }
137
138    @Override
139    public final void recover() {
140        if (allowConnectionRecovery && opened) {
141            log.info("Connection lost. Attempting to recover...");
142        }
143
144        // This method is sometimes called reentrant so to protect from that,
145        // we have a lock.
146        if (lockRecover.tryLock()) {
147            try {
148                super.recover();
149            } finally {
150                lockRecover.unlock();
151            }
152        } else {
153            log.warn("Reconnect already running");
154        }
155    }
156
157    // after reconnect, reattach the packetizer's streams and restart the receive thread
158    @Override
159    protected final void resetupConnection() {
160        LnTrafficController tc = getSystemConnectionMemo().getLnTrafficController();
161        if (tc instanceof LnPacketizer) {
162            LnPacketizer packets = (LnPacketizer) tc;
163            packets.connectPort(this);
164            packets.restartRcvThread();
165        }
166    }
167
168    private static final Logger log = LoggerFactory.getLogger(LnPortController.class);
169
170}