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}