001package jmri.jmrix.loconet.locobuffer;
002
003import java.util.Arrays;
004
005import jmri.jmrix.loconet.*;
006
007import org.slf4j.Logger;
008import org.slf4j.LoggerFactory;
009
010/**
011 * Provide access to LocoNet via a LocoBuffer attached to a serial com port.
012 * <p>
013 * Normally controlled by the LocoBufferFrame class.
014 *
015 * @author Bob Jacobsen Copyright (C) 2001, 2008, 2010
016 */
017public class LocoBufferAdapter extends LnPortController {
018
019    public LocoBufferAdapter() {
020        this(new LocoNetSystemConnectionMemo());
021    }
022
023    public LocoBufferAdapter(LocoNetSystemConnectionMemo adapterMemo) {
024        super(adapterMemo);
025        option1Name = "FlowControl"; // NOI18N
026        option2Name = "CommandStation"; // NOI18N
027        option3Name = "TurnoutHandle"; // NOI18N
028        option4Name = "PacketizerType"; // NOI18N
029        options.put(option1Name, new Option(Bundle.getMessage("XconnectionUsesLabel", Bundle.getMessage("TypeSerial")), validOption1));  // NOI18N
030        options.put(option2Name, new Option(Bundle.getMessage("CommandStationTypeLabel"), getCommandStationListWithStandaloneLN(), false));  // NOI18N
031        options.put(option3Name, new Option(Bundle.getMessage("TurnoutHandling"),
032                new String[]{Bundle.getMessage("HandleNormal"), Bundle.getMessage("HandleSpread"), Bundle.getMessage("HandleOneOnly"), Bundle.getMessage("HandleBoth")})); // I18N
033        options.put(option4Name, new Option(Bundle.getMessage("PacketizerTypeLabel"), packetizerOptions()));  // NOI18N
034        options.put("TranspondingPresent", new Option(Bundle.getMessage("TranspondingPresent"),
035                new String[]{Bundle.getMessage("ButtonNo"), Bundle.getMessage("ButtonYes")} )); // NOI18N
036        options.put("InterrogateOnStart", new Option(Bundle.getMessage("InterrogateOnStart"),
037                new String[]{Bundle.getMessage("ButtonYes"), Bundle.getMessage("ButtonNo")} )); // NOI18N
038        options.put("LoconetProtocolAutoDetect", new Option(Bundle.getMessage("LoconetProtocolAutoDetectLabel"),
039                new String[]{Bundle.getMessage("ButtonNo"),Bundle.getMessage("LoconetProtocolAutoDetect")} )); // NOI18N
040        options.put("LoconetUpdateSlotOnMessageCreation",                                       // NOI18N
041                new Option(Bundle.getMessage("LoconetUpdateSlotOnMessageCreationLabel"),        // I18N
042                new String[]{Bundle.getMessage("ButtonNo"),Bundle.getMessage("ButtonYes")} ));  // I18N
043    }
044
045    /**
046     * Create a list of possible command stations and append "Standalone LocoNet"
047     *
048     * Note: This is not suitable for use by any class which extends this class if
049     * the hardware interface is part of a command station.
050     *
051     * @return String[] containing the array of command stations, plus "Standalone
052     *          LocoNet"
053     */
054    public String[] getCommandStationListWithStandaloneLN() {
055        String[] result = new String[commandStationNames.length + 1];
056        for (int i = 0 ; i < result.length-1; ++i) {
057            result[i] = commandStationNames[i];
058        }
059        result[commandStationNames.length] = LnCommandStationType.COMMAND_STATION_STANDALONE.getName();
060        return result;
061    }
062
063    @Override
064    public String openPort(String portName, String appName) {
065        // get and open the primary port
066        currentSerialPort = activatePort(portName, log);
067        if (currentSerialPort == null) {
068            log.error("failed to connect LocoBuffer to {}", portName);
069            return Bundle.getMessage("SerialPortNotFound", portName);
070        }
071        reportOpen(portName);
072
073        // try to set it for communication via SerialDriver
074        // find the baud rate value, configure comm options
075        int baud = currentBaudNumber(mBaudRate);
076        setBaudRate(currentSerialPort, baud);
077        configureLeads(currentSerialPort, true, true);
078        setLocalFlowControl();
079
080        setComPortTimeouts(currentSerialPort, Blocking.READ_SEMI_BLOCKING, 100);
081
082        // report status
083        reportPortStatus(log, portName);
084
085        opened = true;
086
087        return null; // indicates OK return
088    }
089
090    /**
091     * Allow subtypes to change the opening message
092     * @param portName To appear in message
093     */
094    protected void reportOpen(String portName) {
095        log.info("Connecting LocoBuffer via {} {}", portName, currentSerialPort);
096    }
097
098    /**
099     * Allow subtypes to change the flow control algorithm
100     */
101    protected void setLocalFlowControl() {
102        FlowControl flow = FlowControl.RTSCTS;
103        if (getOptionState(option1Name).equals(validOption1[1])) {
104            flow = FlowControl.NONE;
105        }
106        setFlowControl(currentSerialPort, flow);
107    }
108
109    /**
110     * Can the port accept additional characters? The state of CTS determines
111     * this, as there seems to be no way to check the number of queued bytes and
112     * buffer length. This might go false for short intervals, but it might also
113     * stick off if something goes wrong.
114     *
115     * @return an indication of whether the interface is accepting transmit messages.
116     */
117    @Override
118    public boolean okToSend() {
119        return currentSerialPort.getCTS();
120    }
121
122    /**
123     * Set up all of the other objects to operate with a LocoBuffer connected to
124     * this port.
125     */
126    @Override
127    public void configure() {
128
129        setCommandStationType(getOptionState(option2Name));
130        setTurnoutHandling(getOptionState(option3Name));
131        setTranspondingAvailable(getOptionState("TranspondingPresent"));
132        setInterrogateOnStart(getOptionState("InterrogateOnStart"));
133        setLoconetProtocolAutoDetect(getOptionState("LoconetProtocolAutoDetect"));
134        // connect to a packetizing traffic controller
135        LnPacketizer packets = getPacketizer(getOptionState(option4Name));
136        packets.setLoconetUpdateSlotOnMessageCreation(Bundle.getMessage("ButtonYes").equals(getOptionState("LoconetUpdateSlotOnMessageCreation")));
137        packets.connectPort(this);
138
139        // create memo
140        this.getSystemConnectionMemo().setLnTrafficController(packets);
141        // do the common manager config
142
143        this.getSystemConnectionMemo().configureCommandStation(commandStationType,
144                mTurnoutNoRetry, mTurnoutExtraSpace, mTranspondingAvailable, mInterrogateAtStart, mLoconetProtocolAutoDetect);
145        this.getSystemConnectionMemo().configureManagers();
146
147        // start operation
148        packets.startThreads();
149    }
150
151    @Override
152    public boolean status() {
153        return opened;
154    }
155
156    /**
157     * {@inheritDoc}
158     */
159    @Override
160    public String[] validBaudRates() {
161        return Arrays.copyOf(validSpeeds, validSpeeds.length);
162    }
163
164    /**
165     * {@inheritDoc}
166     */
167    @Override
168    public int[] validBaudNumbers() {
169        return Arrays.copyOf(validSpeedValues, validSpeedValues.length);
170    }
171
172    protected String[] validSpeeds = new String[]{Bundle.getMessage("Baud19200LB"), Bundle.getMessage("Baud57600LB")};
173    protected int[] validSpeedValues = new int[]{19200, 57600};
174
175    @Override
176    public int defaultBaudIndex() {
177        return 0;
178    }
179
180    // meanings are assigned to these above, so make sure the order is consistent
181    protected String[] validOption1 = new String[]{Bundle.getMessage("FlowOptionHwRecomm"), Bundle.getMessage("FlowOptionNo")};
182
183    /**
184     *  Define the readable data and internal code
185     */
186    private static String[][] packetizers = { {Bundle.getMessage("PacketizerTypelnPacketizer"),"lnPacketizer" },
187            {Bundle.getMessage("PacketizerTypelnPacketizerStrict"),"lnPacketizerStrict"} };
188
189    /**
190     *
191     * @return String array of readable choices
192     */
193    private String[] packetizerOptions() {
194        String[] retval = new String[packetizers.length];
195        for (int i=0;i < packetizers.length; i++) {
196            retval[i] = packetizers[i][0];
197        }
198        return retval;
199    }
200    /**
201     * for a given readable choice return internal value
202     * or the default
203     *
204     * @param s  string containing ?a packetizer name?
205     * @return internal value
206     */
207    protected String getPacketizerOption(String s) {
208        for (int i=0;i < packetizers.length; i++) {
209            if (packetizers[i][0].equals(s)) {
210                return packetizers[i][1];
211            }
212        }
213        return "lnPacketizer";
214    }
215    /**
216     *
217     * @param s the packetizer to use in its readable form.
218     * @return a LnPacketizer
219     */
220    protected LnPacketizer getPacketizer(String s) {
221        LnPacketizer packets;
222        String packetSelection = getPacketizerOption(s);
223        switch (packetSelection) {
224            case "lnPacketizer":
225                packets = new LnPacketizer(this.getSystemConnectionMemo());
226                break;
227            case "lnPacketizerStrict":
228                packets = new LnPacketizerStrict(this.getSystemConnectionMemo());
229                break;
230            default:
231                packets = new LnPacketizer(this.getSystemConnectionMemo());
232                log.warn("Using Normal do not understand option [{}]", packetSelection);
233        }
234        return packets;
235    }
236
237    private static final Logger log = LoggerFactory.getLogger(LocoBufferAdapter.class);
238
239}