001package jmri.jmrix.mrc.simulator;
002
003import java.io.DataInputStream;
004import java.io.DataOutputStream;
005import java.io.IOException;
006import java.io.PipedInputStream;
007import java.io.PipedOutputStream;
008import jmri.jmrix.mrc.MrcMessage;
009import jmri.jmrix.mrc.MrcPacketizer;
010import jmri.jmrix.mrc.MrcPackets;
011import jmri.jmrix.mrc.MrcPortController;
012import jmri.jmrix.mrc.MrcSystemConnectionMemo;
013import jmri.util.ImmediatePipedOutputStream;
014import org.slf4j.Logger;
015import org.slf4j.LoggerFactory;
016
017/**
018 * MRC simulator.
019 *
020 * @author Bob Jacobsen Copyright (C) 2001, 2002
021 * @author Paul Bender, Copyright (C) 2009
022 * @author Daniel Boudreau Copyright (C) 2010
023 */
024public class SimulatorAdapter extends MrcPortController implements Runnable {
025
026    // private control members
027    private Thread sourceThread;
028
029    // streams to share with user class
030    private DataOutputStream pout = null; // this is provided to classes who want to write to us
031    private DataInputStream pin = null; // this is provided to classes who want data from us
032
033    // internal ends of the pipes
034    private DataOutputStream outpipe = null; // feed pin
035    private DataInputStream inpipe = null; // feed pout
036
037    public SimulatorAdapter() {
038        super(new MrcSystemConnectionMemo());
039    }
040
041    @Override
042    public String openPort(String portName, String appName) {
043        try {
044            PipedOutputStream tempPipeI = new ImmediatePipedOutputStream();
045            pout = new DataOutputStream(tempPipeI);
046            inpipe = new DataInputStream(new PipedInputStream(tempPipeI));
047            PipedOutputStream tempPipeO = new ImmediatePipedOutputStream();
048            outpipe = new DataOutputStream(tempPipeO);
049            pin = new DataInputStream(new PipedInputStream(tempPipeO));
050        } catch (java.io.IOException e) {
051            log.error("init (pipe): Exception: ", e); // NOI18N
052        }
053        opened = true;
054        return null; // indicates OK return
055    }
056
057    /**
058     * Set up all of the other objects to simulate operation with an MRC command
059     * station.
060     */
061    @Override
062    public void configure() {
063        MrcPacketizer tc = new MrcPacketizer();
064        tc.connectPort(this);
065        this.getSystemConnectionMemo().setMrcTrafficController(tc);
066        tc.setAdapterMemo(this.getSystemConnectionMemo());
067        //tc.connectPort(this);     
068
069        this.getSystemConnectionMemo().configureManagers();
070        tc.setCabNumber(2);
071
072        // start the simulator
073        sourceThread = new Thread(this);
074        sourceThread.setName("Mrc Simulator"); // NOI18N
075        sourceThread.setPriority(Thread.MIN_PRIORITY);
076        sourceThread.start();
077        tc.startThreads();
078    }
079
080    // base class methods for the MrcPortController interface
081    @Override
082    public DataInputStream getInputStream() {
083        if (!opened || pin == null) {
084            log.error("getInputStream called before load(), stream not available"); // NOI18N
085        }
086        return pin;
087    }
088
089    @Override
090    public DataOutputStream getOutputStream() {
091        if (!opened || pout == null) {
092            log.error("getOutputStream called before load(), stream not available"); // NOI18N
093        }
094        return pout;
095    }
096
097    @Override
098    public boolean status() {
099        return opened;
100    }
101
102    /**
103     * {@inheritDoc}
104     */
105    @Override
106    public String[] validBaudRates() {
107        log.debug("validBaudRates should not have been invoked"); // NOI18N
108        return new String[]{};
109    }
110
111    /**
112     * {@inheritDoc}
113     */
114    @Override
115    public int[] validBaudNumbers() {
116        return new int[]{};
117    }
118
119    @Override
120    public String getCurrentBaudRate() {
121        return "";
122    }
123
124    @Override
125    public String getCurrentPortName(){
126        return "";
127    }
128
129    @Override
130    public void run() { // start a new thread
131        // This thread has one task. It repeatedly reads from the input pipe
132        // and writes an appropriate response to the output pipe. This is the heart
133        // of the MRC command station simulation.
134        // report status?
135        log.info("MRC Simulator Started"); // NOI18N
136        int cab = 1;
137        while (true) {
138            try {
139                synchronized (this) {
140                    wait(100);
141                }
142            } catch (InterruptedException e) {
143                log.debug("Interrupted, ending");
144                return;
145            }
146            MrcMessage m = readMessage();
147            if (log.isDebugEnabled()) {
148                StringBuffer buf = new StringBuffer();
149                if (m != null) {
150                    for (int i = 0; i < m.getNumDataElements(); i++) {
151                        buf.append(Integer.toHexString(0xFF & m.getElement(i))).append(" ");
152                    }
153                } else {
154                    buf.append("null message buffer"); // NOI18N
155                }
156                log.debug("Mrc Simulator Thread received message: {}", buf);
157            }
158            if (m != null && m.getNumDataElements() > 4) {
159                //Send a default good reply message
160                MrcMessage r = new MrcMessage(4);
161                r.setElement(0, MrcPackets.GOODCMDRECEIVEDCODE);
162                r.setElement(1, 0x0);
163                r.setElement(2, MrcPackets.GOODCMDRECEIVEDCODE);
164                r.setElement(3, 0x0);
165                writeReply(r);
166                if (m.isReplyExpected()) {
167                    r = generateReply(m);
168                    writeReply(r);
169                }
170                if (log.isDebugEnabled()) {
171                    StringBuffer buf = new StringBuffer();
172                    for (int i = 0; i < r.getNumDataElements(); i++) {
173                        buf.append(Integer.toHexString(0xFF & r.getElement(i))).append(" ");
174                    }
175                    log.debug("Mrc Simulator Thread sent reply: {}", buf );
176                }
177            } else {
178                if (cab > 8) {
179                    cab = 1;
180                }
181                int[] poll = new int[]{cab, 1, cab, 0, cab, 0};
182                cab++;
183                MrcMessage r = new MrcMessage(poll);
184                writeReply(r);
185            }
186        }
187    }
188
189    // readMessage reads one incoming message from the buffer
190    private MrcMessage readMessage() {
191        MrcMessage msg = null;
192        try {
193            if (inpipe.available() > 0) {
194                msg = loadChars();
195            }
196        } catch (java.io.IOException e) {
197
198        }
199        return (msg);
200    }
201
202    /**
203     * Get characters from the input source.
204     *
205     * @return filled message
206     * @throws IOException when presented by the input source.
207     */
208    private MrcMessage loadChars() throws java.io.IOException {
209        int nchars;
210        byte[] rcvBuffer = new byte[32];
211
212        nchars = inpipe.read(rcvBuffer, 0, 32);
213        //log.debug("new message received");
214        MrcMessage msg = new MrcMessage(nchars);
215
216        for (int i = 0; i < nchars; i++) {
217            msg.setElement(i, rcvBuffer[i] & 0xFF);
218        }
219        return msg;
220    }
221
222    // generateReply is the heart of the simulation.  It translates an 
223    // incoming MrcMessage into an outgoing MrcReply.
224    private MrcMessage generateReply(MrcMessage m) {
225        MrcMessage reply = new MrcMessage(4);
226        if (m.getNumDataElements() < 4) {
227            reply.setElement(0, MrcPackets.BADCMDRECEIVEDCODE);
228            reply.setElement(1, 0x0);
229            reply.setElement(2, MrcPackets.BADCMDRECEIVEDCODE);
230            reply.setElement(3, 0x0);
231            return reply;
232        }
233        int command = m.getElement(0);
234        if (command != m.getElement(2) && m.getElement(1) != 1) {
235            reply.setElement(0, MrcPackets.BADCMDRECEIVEDCODE);
236            reply.setElement(1, 0x0);
237            reply.setElement(2, MrcPackets.BADCMDRECEIVEDCODE);
238            reply.setElement(3, 0x0);
239            return reply;
240        }
241        switch (command) {
242            case MrcPackets.SETCLOCKRATIOCMD:  // set fast clock ratio
243//   reply.setElement(0, 0x06);
244//   reply.setElement(1, 0x02);
245//   reply.setElement(2, 0x01);
246                break;
247            case MrcPackets.SETCLOCKTIMECMD: // Set clock
248                break;
249            case MrcPackets.SETCLOCKAMPMCMD: // Set clock mode
250                break;
251            case MrcPackets.THROTTLEPACKETCMD: // Set clock mode
252                reply.setElement(0, MrcPackets.LOCOSOLECONTROLCODE);
253                reply.setElement(1, 0x00);
254                reply.setElement(2, MrcPackets.LOCOSOLECONTROLCODE);
255                reply.setElement(3, 0x00);
256                break;
257//  case MrcMessage.READ_REG_CMD:
258//   reply.setElement(0, 0xff);   // dummy data
259//   //reply.setElement(1,MRC_DATA_OUT_OF_RANGE);  // forces fail
260//   reply.setElement(1,MRC_OKAY);  // forces succeed
261//   break;
262            case MrcPackets.FUNCTIONGROUP2PACKETCMD:
263                // Use this to simulate a missed poll
264                reply = new MrcMessage(6);
265                reply.setElement(0, 0x03);
266                reply.setElement(1, 0x01);
267                reply.setElement(2, 0x03);
268                reply.setElement(3, 0x0);
269                reply.setElement(4, 0x03);
270                reply.setElement(5, 0x0);
271                break;
272            default:
273                // we don't know what it is but presume ok
274                reply.setElement(0, MrcPackets.GOODCMDRECEIVEDCODE);
275                reply.setElement(1, 0x0);
276                reply.setElement(2, MrcPackets.GOODCMDRECEIVEDCODE);
277                reply.setElement(3, 0x0);
278                break;
279        }
280        return reply;
281    }
282
283    private void writeReply(MrcMessage r) {
284        if (r == null) {
285            return;
286        }
287        for (int i = 0; i < r.getNumDataElements(); i++) {
288            try {
289                outpipe.writeByte((byte) r.getElement(i));
290            } catch (java.io.IOException ex) {
291            }
292        }
293        try {
294            outpipe.flush();
295        } catch (java.io.IOException ex) {
296        }
297    }
298
299// private byte[] turnoutMemory = new byte[256];
300// private byte[] macroMemory = new byte[256*20+16]; // and a little padding
301// private byte[] consistMemory = new byte[256*6+16]; // and a little padding
302// /* Read MRC memory.  This implementation simulates reading the MRC
303//  * command station memory.  There are three memory blocks that are
304//  * supported, turnout status, macros, and consists.  The turnout status
305//  * memory is 256 bytes and starts at memory address 0xEC00. The macro memory
306//  * is 256*20 or 5120 bytes and starts at memory address 0xC800. The consist
307//  * memory is 256*6 or 1536 bytes and starts at memory address 0xF500.
308//  * 
309//  */
310// private MrcReply readMemory (MrcMessage m, MrcReply reply, int num){
311//  if (num>16){
312//   log.error("Mrc read memory command was greater than 16");
313//   return null;
314//  }
315//  int mrcMemoryAddress = getMrcAddress(m);
316//  if (mrcMemoryAddress >= MrcTurnoutMonitor.CS_ACCY_MEMORY && mrcMemoryAddress < MrcTurnoutMonitor.CS_ACCY_MEMORY+256){
317//   log.debug("Reading turnout memory: "+Integer.toHexString(mrcMemoryAddress));
318//   int offset = m.getElement(2);
319//   for (int i=0; i<num; i++)
320//    reply.setElement(i, turnoutMemory[offset+i]);
321//   return reply;
322//  }
323//  if (mrcMemoryAddress >= MrcCmdStationMemory.CabMemorySerial.CS_CONSIST_MEM && mrcMemoryAddress < MrcCmdStationMemory.CabMemorySerial.CS_CONSIST_MEM+256*6){
324//   log.debug("Reading consist memory: "+Integer.toHexString(mrcMemoryAddress));
325//   int offset = mrcMemoryAddress - MrcCmdStationMemory.CabMemorySerial.CS_CONSIST_MEM;
326//   for (int i=0; i<num; i++)
327//    reply.setElement(i, consistMemory[offset+i]);
328//   return reply;
329//  }
330//  if (mrcMemoryAddress >= MrcCmdStationMemory.CabMemorySerial.CS_MACRO_MEM && mrcMemoryAddress < MrcCmdStationMemory.CabMemorySerial.CS_MACRO_MEM+256*20){
331//   log.debug("Reading macro memory: "+Integer.toHexString(mrcMemoryAddress));
332//   int offset = mrcMemoryAddress-MrcCmdStationMemory.CabMemorySerial.CS_MACRO_MEM;
333//   log.debug("offset:"+offset);
334//   for (int i=0; i<num; i++)
335//    reply.setElement(i, macroMemory[offset+i]);
336//   return reply;
337//  }
338//  for (int i=0; i<num; i++)
339//   reply.setElement(i, 0x00);   // default fixed data
340//  return reply;
341// }
342// private MrcReply writeMemory (MrcMessage m, MrcReply reply, int num, boolean skipbyte){
343//  if (num>16){
344//   log.error("Mrc write memory command was greater than 16");
345//   return null;
346//  }
347//  int mrcMemoryAddress = getMrcAddress(m);
348//  int byteDataBegins = 3;
349//  if (skipbyte)
350//   byteDataBegins++;
351//  if (mrcMemoryAddress >= MrcTurnoutMonitor.CS_ACCY_MEMORY && mrcMemoryAddress < MrcTurnoutMonitor.CS_ACCY_MEMORY+256){
352//   log.debug("Writing turnout memory: "+Integer.toHexString(mrcMemoryAddress));
353//   int offset = m.getElement(2);
354//   for (int i=0; i<num; i++)
355//    turnoutMemory[offset+i] = (byte)m.getElement(i+byteDataBegins);
356//  }
357//  if (mrcMemoryAddress >= MrcCmdStationMemory.CabMemorySerial.CS_CONSIST_MEM && mrcMemoryAddress < MrcCmdStationMemory.CabMemorySerial.CS_CONSIST_MEM+256*6){
358//   log.debug("Writing consist memory: "+Integer.toHexString(mrcMemoryAddress));
359//   int offset = mrcMemoryAddress-MrcCmdStationMemory.CabMemorySerial.CS_CONSIST_MEM;
360//   for (int i=0; i<num; i++)
361//    consistMemory[offset+i] = (byte)m.getElement(i+byteDataBegins);
362//  }
363//  if (mrcMemoryAddress >= MrcCmdStationMemory.CabMemorySerial.CS_MACRO_MEM && mrcMemoryAddress < MrcCmdStationMemory.CabMemorySerial.CS_MACRO_MEM+256*20){
364//   log.debug("Writing macro memory: "+Integer.toHexString(mrcMemoryAddress));
365//   int offset = mrcMemoryAddress-MrcCmdStationMemory.CabMemorySerial.CS_MACRO_MEM;
366//   log.debug("offset:"+offset);
367//   for (int i=0; i<num; i++)
368//    macroMemory[offset+i] = (byte)m.getElement(i+byteDataBegins);
369//  }
370//  reply.setElement(0, MRC_OKAY);   // Mrc okay reply!
371//  return reply;
372// }
373// private int getMrcAddress(MrcMessage m){
374//  int addr = m.getElement(1);
375//  addr = addr * 256;
376//  addr = addr + m.getElement(2);
377//  return addr;
378// }
379// 
380// private MrcReply accessoryCommand(MrcMessage m, MrcReply reply){
381//  if (m.getElement(3) == 0x03 || m.getElement(3) == 0x04){  // 0x03 = close, 0x04 = throw
382//   String operation = "close";
383//   if (m.getElement(3) == 0x04)
384//    operation = "throw";
385//   int mrcAccessoryAddress = getMrcAddress(m);
386//   log.debug("Accessory command "+operation+" NT"+mrcAccessoryAddress);
387//   if (mrcAccessoryAddress > 2044){
388//    log.error("Turnout address greater than 2044, address: "+mrcAccessoryAddress );
389//    return null;
390//   }
391//   int bit = (mrcAccessoryAddress-1) & 0x07;
392//   int setMask = 0x01;
393//   for (int i=0; i<bit; i++){
394//    setMask = setMask<<1;
395//   }
396//   int clearMask = 0x0FFF - setMask;
397//   //log.debug("setMask:"+Integer.toHexString(setMask)+" clearMask:"+Integer.toHexString(clearMask));
398//   int offset = (mrcAccessoryAddress-1)>>3;
399//   int read = turnoutMemory[offset];
400//   byte write = (byte)(read & clearMask & 0xFF);
401//
402//   if (operation.equals("close"))
403//    write = (byte)(write + setMask); // set bit if closed
404//   turnoutMemory[offset] = write;
405//   //log.debug("wrote:"+Integer.toHexString(write)); 
406//  }
407//  reply.setElement(0, MRC_OKAY);   // Mrc okay reply!
408//  return reply;
409// }
410    private final static Logger log = LoggerFactory
411            .getLogger(SimulatorAdapter.class);
412
413}