001package jmri.jmrix.loconet.streamport; 002 003import jmri.jmrix.loconet.LnPacketizer; 004import jmri.jmrix.loconet.LocoNetSystemConnectionMemo; 005import org.slf4j.Logger; 006import org.slf4j.LoggerFactory; 007 008/** 009 * Converts Stream-based I/O to/from LocoNet messages. The "LocoNetInterface" 010 * side sends/receives LocoNetMessage objects. The connection to a 011 * LnPortController is via a pair of *Streams, which then carry sequences of 012 * characters for transmission. 013 * <p> 014 * Messages come to this via the main GUI thread, and are forwarded back to 015 * listeners in that same thread. Reception and transmission are handled in 016 * dedicated threads by RcvHandler and XmtHandler objects. Those are internal 017 * classes defined here. The thread priorities are: 018 * <ul> 019 * <li> RcvHandler - at highest available priority 020 * <li> XmtHandler - down one, which is assumed to be above the GUI 021 * <li> (everything else) 022 * </ul> 023 * Some of the message formats used in this class are Copyright Digitrax, Inc. 024 * and used with permission as part of the JMRI project. That permission does 025 * not extend to uses in other software products. If you wish to use this code, 026 * algorithm or these message formats outside of JMRI, please contact Digitrax 027 * Inc for separate permission. 028 * 029 * @author Bob Jacobsen Copyright (C) 2001 030 */ 031public class LnStreamPortPacketizer extends LnPacketizer { 032 033 public LnStreamPortPacketizer(LocoNetSystemConnectionMemo m) { 034 super(m); 035 } 036 037 public LnStreamPortController streamController = null; 038 039 @Override 040 public boolean isXmtBusy() { 041 if (streamController == null) { 042 return false; 043 } 044 return true; 045 } 046 047 /** 048 * Make connection to existing LnPortController object. 049 * 050 * @param p Port controller to connect to. Save this for a later disconnect 051 * call 052 */ 053 public void connectPort(LnStreamPortController p) { 054 istream = p.getInputStream(); 055 ostream = p.getOutputStream(); 056 if (controller != null) { 057 log.warn("connectPort: connect called while connected"); 058 } 059 streamController = p; 060 } 061 062 /** 063 * Break connection to existing LnPortController object. Once broken, 064 * attempts to send via "message" member will fail. 065 * 066 * @param p previously connected port 067 */ 068 public void disconnectPort(LnStreamPortController p) { 069 istream = null; 070 ostream = null; 071 if (streamController != p) { 072 log.warn("disconnectPort: disconnect called from non-connected LnStreamPortController"); 073 } 074 streamController = null; 075 } 076 077 /** 078 * Captive class to handle transmission 079 */ 080 class XmtHandler implements Runnable { 081 082 @Override 083 public void run() { 084 085 while (!threadStopRequest) { // loop until asked to stop 086 // any input? 087 try { 088 // get content; blocks until present 089 log.trace("check for input"); // NOI18N 090 091 byte msg[] = xmtList.take(); 092 093 // input - now send 094 try { 095 if (ostream != null) { 096 if (!streamController.okToSend()) { 097 log.debug("LocoNet port not ready to receive"); // NOI18N 098 } 099 if (log.isDebugEnabled()) { // avoid String building if not needed 100 log.debug("start write to stream: {}", jmri.util.StringUtil.hexStringFromBytes(msg)); // NOI18N 101 } 102 ostream.write(msg); 103 ostream.flush(); 104 if (log.isTraceEnabled()) { // avoid String building if not needed 105 log.trace("end write to stream: {}", jmri.util.StringUtil.hexStringFromBytes(msg)); // NOI18N 106 } 107 messageTransmitted(msg); 108 } else { 109 // no stream connected 110 log.warn("sendLocoNetMessage: no connection established"); // NOI18N 111 } 112 } catch (java.io.IOException e) { 113 log.warn("sendLocoNetMessage: IOException: {}", e.toString()); // NOI18N 114 } 115 } catch (InterruptedException ie) { 116 return; // ending the thread 117 } 118 } 119 } 120 } 121 122 /** 123 * Invoked at startup to start the threads needed here. 124 */ 125 @Override 126 public void startThreads() { 127 int priority = Thread.currentThread().getPriority(); 128 log.debug("startThreads current priority = {} max available = " + Thread.MAX_PRIORITY + " default = " + Thread.NORM_PRIORITY + " min available = " + Thread.MIN_PRIORITY, priority); // NOI18N 129 130 // make sure that the xmt priority is no lower than the current priority 131 int xmtpriority = (Thread.MAX_PRIORITY - 1 > priority ? Thread.MAX_PRIORITY - 1 : Thread.MAX_PRIORITY); 132 // start the XmtHandler in a thread of its own 133 if (xmtHandler == null) { 134 xmtHandler = new XmtHandler(); 135 } 136 xmtThread = jmri.util.ThreadingUtil.newThread(xmtHandler, "LocoNet transmit handler"); // NOI18N 137 log.debug("Xmt thread starts at priority {}", xmtpriority); // NOI18N 138 xmtThread.setDaemon(true); 139 xmtThread.setPriority(Thread.MAX_PRIORITY - 1); 140 xmtThread.start(); 141 142 // start the RcvHandler in a thread of its own 143 if (rcvHandler == null) { 144 rcvHandler = new RcvHandler(this); 145 } 146 rcvThread = jmri.util.ThreadingUtil.newThread(rcvHandler, "LocoNet receive handler"); // NOI18N 147 rcvThread.setDaemon(true); 148 rcvThread.setPriority(Thread.MAX_PRIORITY); 149 rcvThread.start(); 150 } 151 152 private final static Logger log = LoggerFactory.getLogger(LnStreamPortPacketizer.class); 153 154}