001package jmri.jmrix.tams.simulator;
002
003import java.io.DataInputStream;
004import java.io.DataOutputStream;
005import java.io.IOException;
006import java.io.PipedInputStream;
007import java.io.PipedOutputStream;
008
009import jmri.jmrix.tams.TamsMessage;
010import jmri.jmrix.tams.TamsPortController;
011import jmri.jmrix.tams.TamsReply;
012import jmri.jmrix.tams.TamsSystemConnectionMemo;
013import jmri.jmrix.tams.TamsTrafficController;
014import jmri.util.ImmediatePipedOutputStream;
015import org.slf4j.Logger;
016import org.slf4j.LoggerFactory;
017
018/**
019 * Tams simulator.
020 * Derived from MRC Simulator
021 *
022 * @author Bob Jacobsen Copyright (C) 2001, 2002
023 * @author Paul Bender, Copyright (C) 2009
024 * @author Daniel Boudreau Copyright (C) 2010
025 * 
026 */
027public class SimulatorAdapter extends TamsPortController implements Runnable {
028
029    // private control members
030    private Thread sourceThread;
031
032    // streams to share with user class
033    private DataOutputStream pout = null; // this is provided to classes who want to write to us
034    private DataInputStream pin = null; // this is provided to classes who want data from us
035
036    // internal ends of the pipes
037    private DataOutputStream outpipe = null; // feed pin
038    private DataInputStream inpipe = null; // feed pout
039
040    public SimulatorAdapter() {
041        super(new TamsSystemConnectionMemo());
042    }
043
044    @Override
045    public String openPort(String portName, String appName) {
046        try {
047            PipedOutputStream tempPipeI = new ImmediatePipedOutputStream();
048            pout = new DataOutputStream(tempPipeI);
049            inpipe = new DataInputStream(new PipedInputStream(tempPipeI));
050            PipedOutputStream tempPipeO = new ImmediatePipedOutputStream();
051            outpipe = new DataOutputStream(tempPipeO);
052            pin = new DataInputStream(new PipedInputStream(tempPipeO));
053        } catch (java.io.IOException e) {
054            log.error("init (pipe): Exception: {}", e.toString());
055        }
056        opened = true;
057        return null; // indicates OK return
058    }
059
060    /**
061     * Set up all of the other objects to simulate operation with a Tams command
062     * station.
063     */
064    @Override
065    public void configure() {
066        TamsTrafficController tc = new TamsTrafficController();
067        tc.connectPort(this);
068        this.getSystemConnectionMemo().setTamsTrafficController(tc);
069        tc.setAdapterMemo(this.getSystemConnectionMemo());
070
071        this.getSystemConnectionMemo().configureManagers();
072        //tc.setCabNumber(2);
073
074        // start the simulator
075        sourceThread = new Thread(this);
076        sourceThread.setName("Tams Simulator");
077        sourceThread.setPriority(Thread.MIN_PRIORITY);
078        sourceThread.start();
079        //tc.startThreads();
080    }
081
082    // base class methods for the TamsPortController interface
083
084    @Override
085    public DataInputStream getInputStream() {
086        if (!opened || pin == null) {
087            log.error("getInputStream called before load(), stream not available");
088        }
089        return pin;
090    }
091
092    @Override
093    public DataOutputStream getOutputStream() {
094        if (!opened || pout == null) {
095            log.error("getOutputStream called before load(), stream not available");
096        }
097        return pout;
098    }
099
100    @Override
101    public boolean status() {
102        return opened;
103    }
104
105    /**
106     * {@inheritDoc}
107     */
108    @Override
109    public String[] validBaudRates() {
110        log.debug("validBaudRates should not have been invoked");
111        return new String[]{};
112    }
113
114    /**
115     * {@inheritDoc}
116     */
117    @Override
118    public int[] validBaudNumbers() {
119        return new int[]{};
120    }
121
122    @Override
123    public String getCurrentBaudRate() {
124        return "";
125    }
126
127    @Override
128    public String getCurrentPortName(){
129        return "";
130    }
131
132    @Override
133    public void run() { // start a new thread
134        // This thread has one task. It repeatedly reads from the input pipe
135        // and writes an appropriate response to the output pipe. This is the heart
136        // of the TAMS command station simulation.
137        // report status?
138        if (log.isInfoEnabled()) {
139            log.info("TAMS Simulator Started");
140        }
141        while (true) {
142            try {
143                synchronized (this) {
144                    wait(50);
145                }
146            } catch (InterruptedException e) {
147                log.debug("interrupted, ending");
148                return;
149            }
150            TamsMessage m = readMessage();
151            TamsReply r;
152            if (log.isDebugEnabled()) {
153                StringBuilder buf = new StringBuilder();
154                if (m != null) {
155                    for (int i = 0; i < m.getNumDataElements(); i++) {
156                        buf.append(Integer.toHexString(0xFF & m.getElement(i))).append(" ");
157                    }
158                } else {
159                    buf.append("null message buffer");
160                }
161                log.debug("Tams Simulator Thread received message: {}", buf );
162            }
163            if (m != null) {
164                //if(m.isReplyExpected()){
165                r = generateReply(m);
166                writeReply(r);
167                //}
168                if (log.isDebugEnabled()) {
169                    StringBuilder buf = new StringBuilder();
170                    for (int i = 0; i < r.getNumDataElements(); i++) {
171                        buf.append(Integer.toHexString(0xFF & r.getElement(i))).append(" ");
172                    }
173                    log.debug("Tams Simulator Thread sent reply: {}", buf );
174                }
175            }
176        }
177    }
178
179    // readMessage reads one incoming message from the buffer
180    private TamsMessage readMessage() {
181        TamsMessage msg = null;
182        try {
183            if (inpipe.available() > 0) {
184                msg = loadChars();
185            }
186        } catch (java.io.IOException e) {
187
188        }
189        return (msg);
190    }
191
192    /**
193     * Get characters from the input source.
194     *
195     * @return filled message
196     * @throws IOException when presented by the input source.
197     */
198    private TamsMessage loadChars() throws java.io.IOException {
199        int nchars;
200        byte[] rcvBuffer = new byte[32];
201
202        nchars = inpipe.read(rcvBuffer, 0, 32);
203        //log.debug("new message received");
204        TamsMessage msg = new TamsMessage(nchars);
205
206        for (int i = 0; i < nchars; i++) {
207            msg.setElement(i, rcvBuffer[i] & 0xFF);
208        }
209        return msg;
210    }
211
212    // generateReply is the heart of the simulation.  It translates an 
213    // incoming TamsMessage into an outgoing TamsReply.
214    private TamsReply generateReply(TamsMessage m) {
215        TamsReply reply = new TamsReply();
216        int i = 0;
217        log.debug("Rec {}", m.toString());
218        if (m.toString().startsWith("xY")) {
219            reply.setElement(i++, 0x00);
220        } else if (m.toString().startsWith("xSR")) {
221            reply.setElement(i++, 0x53);
222            reply.setElement(i++, 0x52);
223            reply.setElement(i++, 0x20);
224            reply.setElement(i++, m.getElement(3));
225        } else if (m.getElement(0) == 0x99) {// && m.getElement(1)==0x53 && m.getElement(2)=0x52 && m.getElement(3)==0x30){
226            reply.setElement(i++, 0x55);
227            reply.setElement(i++, 0x55);
228            reply.setElement(i++, 0xAA);
229            reply.setElement(i++, 0xAA);
230            reply.setElement(i++, 0x00);
231            reply.setElement(i++, 0x00);
232            reply.setElement(i++, 0x00);
233            reply.setElement(i++, 0x00);
234            reply.setElement(i++, 0x00);
235            reply.setElement(i++, 0x00);
236            reply.setElement(i++, 0x00);
237            reply.setElement(i++, 0x00);
238            reply.setElement(i++, 0x00);
239            reply.setElement(i++, 0x00);
240            reply.setElement(i++, 0x00);
241            reply.setElement(i++, 0x00);
242            reply.setElement(i++, 0x00);
243            reply.setElement(i++, 0x00);
244            reply.setElement(i++, 0x00);
245            reply.setElement(i++, 0x00);
246            reply.setElement(i++, 0x00);
247            reply.setElement(i++, 0x00);
248            reply.setElement(i++, 0x00);
249            reply.setElement(i++, 0x00);
250            reply.setElement(i++, 0x00);
251            reply.setElement(i++, 0x00);
252            reply.setElement(i++, 0x00);
253            reply.setElement(i++, 0x00);
254            reply.setElement(i++, 0x00);
255            reply.setElement(i++, 0x00);
256            reply.setElement(i++, 0x00);
257            reply.setElement(i++, 0x00);
258            reply.setElement(i++, 0x00);
259            reply.setElement(i++, 0x00);
260            reply.setElement(i++, 0x00);
261            reply.setElement(i++, 0x00);
262            reply.setElement(i++, 0x00);
263            reply.setElement(i++, 0x00);
264            reply.setElement(i++, 0x00);
265            reply.setElement(i++, 0x00);
266            reply.setElement(i++, 0x00);
267            reply.setElement(i++, 0x00);
268            reply.setElement(i++, 0x00);
269            reply.setElement(i++, 0x00);
270            reply.setElement(i++, 0x00);
271            reply.setElement(i++, 0x00);
272            reply.setElement(i++, 0x00);
273            reply.setElement(i++, 0x00);
274            reply.setElement(i++, 0x00);
275            reply.setElement(i++, 0x00);
276            reply.setElement(i++, 0x00);
277            reply.setElement(i++, 0x00);
278            reply.setElement(i++, 0x00);
279            reply.setElement(i++, 0x00);
280            reply.setElement(i++, 0x00);
281            reply.setElement(i++, 0x00);
282            reply.setElement(i++, 0x00);
283            reply.setElement(i++, 0x00);
284            reply.setElement(i++, 0x00);
285            reply.setElement(i++, 0x00);
286            reply.setElement(i++, 0x00);
287            reply.setElement(i++, 0x00);
288        }
289        reply.setElement(i++, 0x0d);
290        reply.setElement(i++, 0x5d);
291        return reply;
292    }
293
294    private void writeReply(TamsReply r) {
295        if (r == null) {
296            return;
297        }
298        for (int i = 0; i < r.getNumDataElements(); i++) {
299            try {
300                outpipe.writeByte((byte) r.getElement(i));
301            } catch (java.io.IOException ex) {
302            }
303        }
304        try {
305            outpipe.flush();
306        } catch (java.io.IOException ex) {
307        }
308    }
309
310    private final static Logger log = LoggerFactory
311            .getLogger(SimulatorAdapter.class);
312
313}