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}