001package jmri.jmrix.loconet.demoport; 002 003import java.io.*; 004 005import jmri.SystemConnectionMemo; 006import jmri.jmrix.*; 007import jmri.jmrix.loconet.LnConstants; 008import jmri.jmrix.loconet.LocoNetMessage; 009import jmri.util.ThreadingUtil; 010 011/** 012 * Demonstration of replacing the serial port with a fake port. 013 * This class requires a working LocoNet serial port connection. 014 * 015 * @author Daniel Bergqvist (C) 2024 016 */ 017public class DemoSerialPort extends AbstractSerialPortController { 018 019 private final DemoPanel _panel; 020 private BufferedOutputStream _outputStream; 021 022 DemoSerialPort(DemoPanel panel, SystemConnectionMemo memo) { 023 super(memo); 024 this._panel = panel; 025 } 026 027 @Override 028 public void configure() { 029 // Do nothing 030 } 031 032 @Override 033 public String openPort(String portName, String appName) { 034 // get and open the primary port 035 currentSerialPort = activatePort(portName, log); 036 if (currentSerialPort == null) { 037 log.error("failed to connect to {}", portName); 038 return Bundle.getMessage("SerialPortNotFound", portName); 039 } 040 log.info("Connecting via {} {}", portName, currentSerialPort); 041 042 setBaudRate(currentSerialPort, 57600); 043 configureLeads(currentSerialPort, true, true); 044 setFlowControl(currentSerialPort, FlowControl.RTSCTS); 045 046 setComPortTimeouts(currentSerialPort, Blocking.READ_SEMI_BLOCKING, 100); 047 048 // report status 049 reportPortStatus(log, portName); 050 051 opened = true; 052 053 return null; // indicates OK return 054 } 055 056 public void startDemo() { 057 log.error("startDemo()"); 058 AbstractSerialPortController pc = _panel.getPortController(); 059 if (pc == null) { 060 log.error("startDemo(). PortController is null"); 061 return; 062 } 063 String portName = pc.getCurrentPortName(); 064 log.error("Serial port: {}", pc.getPortSettingsString()); 065 pc.replacePortWithFakePort(); 066 String result = openPort(portName, "JMRI app"); 067 if (result == null) { 068 _panel.addMessage("Connection successful\n"); 069 ThreadingUtil.newThread(new LocoNetListener(getInputStream()), 070 "Demo serial port") 071 .start(); 072 _outputStream = new BufferedOutputStream(getOutputStream()); 073 } else { 074 _panel.addMessage(result); 075 } 076 } 077 078 public void throwTurnout(int turnout, boolean throwTurnout) { 079 try { 080 LocoNetMessage msg = new LocoNetMessage(4); 081 msg.setOpCode(LnConstants.OPC_SW_REQ); 082 msg.setElement(1, turnout-1); 083 msg.setElement(2, throwTurnout ? 0x10 : 0x30); 084 msg.setParity(); 085 for (int i=0; i < msg.getNumDataElements(); i++) { 086 _outputStream.write(msg.getElement(i)); 087 } 088 _outputStream.flush(); 089 } catch (IOException ex) { 090 log.error("Exception: {}", ex.getMessage()); 091 } 092 } 093 094 095 private final class LocoNetListener implements Runnable { 096 097 private final InputStream _stream; 098 private final int[] data = new int[256]; 099 private int pos = 0; 100 101 private LocoNetListener(InputStream stream) { 102 this._stream = stream; 103 } 104 105 private int numBytesInMessage() { 106 switch (data[0] & 0xE0) { 107 case 0x80: return 2; 108 case 0xA0: return 4; 109 case 0xC0: return 6; 110 case 0xE0: 111 if (pos >= 2) return data[1]; 112 else return 255; // We have only first byte so we don't know length yet. 113 default: 114 throw new IllegalArgumentException("Unknown length of package"); 115 } 116 } 117 118 @Override 119 public void run() { 120 while (! Thread.interrupted() ) { // loop until asked to stop 121 try { 122 int b = _stream.read(); 123 _panel.addMessage(String.format("%02X ", b)); 124 if (b < 128 && pos == 0) { 125 // We are in the middle of a message and have missed 126 // the beginning of the message. Ignore it. 127 continue; 128 } 129 data[pos++] = b; 130 if (pos >= numBytesInMessage()) { 131 LocoNetMessage msg = new LocoNetMessage(data); 132 _panel.addMessage(msg.toMonitorString()); 133 pos = 0; 134 } 135 } catch (InterruptedIOException ex) { 136 // Do nothing, just ignore the error 137 } catch (IOException ex) { 138 log.error("Exception: {}", ex.getMessage()); 139 return; 140 } 141 } 142 } 143 } 144 145 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DemoSerialPort.class); 146}