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 boolean opened = false;
031    private Thread sourceThread;
032
033    // streams to share with user class
034    private DataOutputStream pout = null; // this is provided to classes who want to write to us
035    private DataInputStream pin = null; // this is provided to classes who want data from us
036
037    // internal ends of the pipes
038    private DataOutputStream outpipe = null; // feed pin
039    private DataInputStream inpipe = null; // feed pout
040
041    public SimulatorAdapter() {
042        super(new TamsSystemConnectionMemo());
043    }
044
045    @Override
046    public String openPort(String portName, String appName) {
047        try {
048            PipedOutputStream tempPipeI = new ImmediatePipedOutputStream();
049            pout = new DataOutputStream(tempPipeI);
050            inpipe = new DataInputStream(new PipedInputStream(tempPipeI));
051            PipedOutputStream tempPipeO = new ImmediatePipedOutputStream();
052            outpipe = new DataOutputStream(tempPipeO);
053            pin = new DataInputStream(new PipedInputStream(tempPipeO));
054        } catch (java.io.IOException e) {
055            log.error("init (pipe): Exception: {}", e.toString());
056        }
057        opened = true;
058        return null; // indicates OK return
059    }
060
061    /**
062     * Set up all of the other objects to simulate operation with a Tams command
063     * station.
064     */
065    @Override
066    public void configure() {
067        TamsTrafficController tc = new TamsTrafficController();
068        tc.connectPort(this);
069        this.getSystemConnectionMemo().setTamsTrafficController(tc);
070        tc.setAdapterMemo(this.getSystemConnectionMemo());
071
072        this.getSystemConnectionMemo().configureManagers();
073        //tc.setCabNumber(2);
074
075        // start the simulator
076        sourceThread = new Thread(this);
077        sourceThread.setName("Tams Simulator");
078        sourceThread.setPriority(Thread.MIN_PRIORITY);
079        sourceThread.start();
080        //tc.startThreads();
081    }
082
083    // base class methods for the TamsPortController interface
084
085    @Override
086    public DataInputStream getInputStream() {
087        if (!opened || pin == null) {
088            log.error("getInputStream called before load(), stream not available");
089        }
090        return pin;
091    }
092
093    @Override
094    public DataOutputStream getOutputStream() {
095        if (!opened || pout == null) {
096            log.error("getOutputStream called before load(), stream not available");
097        }
098        return pout;
099    }
100
101    @Override
102    public boolean status() {
103        return opened;
104    }
105
106    /**
107     * {@inheritDoc}
108     */
109    @Override
110    public String[] validBaudRates() {
111        log.debug("validBaudRates should not have been invoked");
112        return new String[]{};
113    }
114
115    /**
116     * {@inheritDoc}
117     */
118    @Override
119    public int[] validBaudNumbers() {
120        return new int[]{};
121    }
122
123    @Override
124    public String getCurrentBaudRate() {
125        return "";
126    }
127
128    @Override
129    public String getCurrentPortName(){
130        return "";
131    }
132
133    @Override
134    public void run() { // start a new thread
135        // This thread has one task. It repeatedly reads from the input pipe
136        // and writes an appropriate response to the output pipe. This is the heart
137        // of the TAMS command station simulation.
138        // report status?
139        if (log.isInfoEnabled()) {
140            log.info("TAMS Simulator Started");
141        }
142        while (true) {
143            try {
144                synchronized (this) {
145                    wait(50);
146                }
147            } catch (InterruptedException e) {
148                log.debug("interrupted, ending");
149                return;
150            }
151            TamsMessage m = readMessage();
152            TamsReply r;
153            if (log.isDebugEnabled()) {
154                StringBuilder buf = new StringBuilder();
155                if (m != null) {
156                    for (int i = 0; i < m.getNumDataElements(); i++) {
157                        buf.append(Integer.toHexString(0xFF & m.getElement(i))).append(" ");
158                    }
159                } else {
160                    buf.append("null message buffer");
161                }
162                log.debug("Tams Simulator Thread received message: {}", buf );
163            }
164            if (m != null) {
165                //if(m.isReplyExpected()){
166                r = generateReply(m);
167                writeReply(r);
168                //}
169                if (log.isDebugEnabled()) {
170                    StringBuilder buf = new StringBuilder();
171                    for (int i = 0; i < r.getNumDataElements(); i++) {
172                        buf.append(Integer.toHexString(0xFF & r.getElement(i))).append(" ");
173                    }
174                    log.debug("Tams Simulator Thread sent reply: {}", buf );
175                }
176            }
177        }
178    }
179
180    // readMessage reads one incoming message from the buffer
181    private TamsMessage readMessage() {
182        TamsMessage msg = null;
183        try {
184            if (inpipe.available() > 0) {
185                msg = loadChars();
186            }
187        } catch (java.io.IOException e) {
188
189        }
190        return (msg);
191    }
192
193    /**
194     * Get characters from the input source.
195     *
196     * @return filled message
197     * @throws IOException when presented by the input source.
198     */
199    private TamsMessage loadChars() throws java.io.IOException {
200        int nchars;
201        byte[] rcvBuffer = new byte[32];
202
203        nchars = inpipe.read(rcvBuffer, 0, 32);
204        //log.debug("new message received");
205        TamsMessage msg = new TamsMessage(nchars);
206
207        for (int i = 0; i < nchars; i++) {
208            msg.setElement(i, rcvBuffer[i] & 0xFF);
209        }
210        return msg;
211    }
212
213    // generateReply is the heart of the simulation.  It translates an 
214    // incoming TamsMessage into an outgoing TamsReply.
215    private TamsReply generateReply(TamsMessage m) {
216        TamsReply reply = new TamsReply();
217        int i = 0;
218        log.debug("Rec {}", m.toString());
219        if (m.toString().startsWith("xY")) {
220            reply.setElement(i++, 0x00);
221        } else if (m.toString().startsWith("xSR")) {
222            reply.setElement(i++, 0x53);
223            reply.setElement(i++, 0x52);
224            reply.setElement(i++, 0x20);
225            reply.setElement(i++, m.getElement(3));
226        } else if (m.getElement(0) == 0x99) {// && m.getElement(1)==0x53 && m.getElement(2)=0x52 && m.getElement(3)==0x30){
227            reply.setElement(i++, 0x55);
228            reply.setElement(i++, 0x55);
229            reply.setElement(i++, 0xAA);
230            reply.setElement(i++, 0xAA);
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            reply.setElement(i++, 0x00);
289        }
290        reply.setElement(i++, 0x0d);
291        reply.setElement(i++, 0x5d);
292        return reply;
293    }
294
295    private void writeReply(TamsReply r) {
296        if (r == null) {
297            return;
298        }
299        for (int i = 0; i < r.getNumDataElements(); i++) {
300            try {
301                outpipe.writeByte((byte) r.getElement(i));
302            } catch (java.io.IOException ex) {
303            }
304        }
305        try {
306            outpipe.flush();
307        } catch (java.io.IOException ex) {
308        }
309    }
310
311    private final static Logger log = LoggerFactory
312            .getLogger(SimulatorAdapter.class);
313
314}