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