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        // 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.connectPort(this);
137
138        // create memo
139        this.getSystemConnectionMemo().setLnTrafficController(packets);
140        // do the common manager config
141
142        this.getSystemConnectionMemo().configureCommandStation(commandStationType,
143                mTurnoutNoRetry, mTurnoutExtraSpace, mTranspondingAvailable, mInterrogateAtStart, mLoconetProtocolAutoDetect);
144        this.getSystemConnectionMemo().configureManagers();
145
146        // start operation
147        packets.startThreads();
148    }
149
150    @Override
151    public boolean status() {
152        return opened;
153    }
154
155    /**
156     * {@inheritDoc}
157     */
158    @Override
159    public String[] validBaudRates() {
160        return Arrays.copyOf(validSpeeds, validSpeeds.length);
161    }
162
163    /**
164     * {@inheritDoc}
165     */
166    @Override
167    public int[] validBaudNumbers() {
168        return Arrays.copyOf(validSpeedValues, validSpeedValues.length);
169    }
170
171    protected String[] validSpeeds = new String[]{Bundle.getMessage("Baud19200LB"), Bundle.getMessage("Baud57600LB")};
172    protected int[] validSpeedValues = new int[]{19200, 57600};
173
174    @Override
175    public int defaultBaudIndex() {
176        return 0;
177    }
178
179    // meanings are assigned to these above, so make sure the order is consistent
180    protected String[] validOption1 = new String[]{Bundle.getMessage("FlowOptionHwRecomm"), Bundle.getMessage("FlowOptionNo")};
181
182    /**
183     *  Define the readable data and internal code
184     */
185    private static String[][] packetizers = { {Bundle.getMessage("PacketizerTypelnPacketizer"),"lnPacketizer" },
186            {Bundle.getMessage("PacketizerTypelnPacketizerStrict"),"lnPacketizerStrict"} };
187
188    /**
189     *
190     * @return String array of readable choices
191     */
192    private String[] packetizerOptions() {
193        String[] retval = new String[packetizers.length];
194        for (int i=0;i < packetizers.length; i++) {
195            retval[i] = packetizers[i][0];
196        }
197        return retval;
198    }
199    /**
200     * for a given readable choice return internal value
201     * or the default
202     *
203     * @param s  string containing ?a packetizer name?
204     * @return internal value
205     */
206    protected String getPacketizerOption(String s) {
207        for (int i=0;i < packetizers.length; i++) {
208            if (packetizers[i][0].equals(s)) {
209                return packetizers[i][1];
210            }
211        }
212        return "lnPacketizer";
213    }
214    /**
215     * 
216     * @param s the packetizer to use in its readable form.
217     * @return a LnPacketizer
218     */
219    protected LnPacketizer getPacketizer(String s) {
220        LnPacketizer packets;
221        String packetSelection = getPacketizerOption(s);
222        switch (packetSelection) {
223            case "lnPacketizer":
224                packets = new LnPacketizer(this.getSystemConnectionMemo());
225                break;
226            case "lnPacketizerStrict":
227                packets = new LnPacketizerStrict(this.getSystemConnectionMemo());
228                break;
229            default:
230                packets = new LnPacketizer(this.getSystemConnectionMemo());
231                log.warn("Using Normal do not understand option [{}]", packetSelection);
232        }
233        return packets;
234    }
235
236    private final static Logger log = LoggerFactory.getLogger(LocoBufferAdapter.class);
237
238}