001package jmri.jmrix.powerline.cm11;
002
003import jmri.jmrix.AbstractMRListener;
004import jmri.jmrix.AbstractMRMessage;
005import jmri.jmrix.AbstractMRReply;
006import jmri.jmrix.powerline.SerialListener;
007import jmri.jmrix.powerline.SerialMessage;
008import jmri.jmrix.powerline.SerialSystemConnectionMemo;
009import jmri.jmrix.powerline.SerialTrafficController;
010import jmri.jmrix.powerline.X10Sequence;
011import org.slf4j.Logger;
012import org.slf4j.LoggerFactory;
013
014/**
015 * Converts Stream-based I/O to/from messages. The "SerialInterface" side
016 * sends/receives message objects.
017 * <p>
018 * The connection to a SerialPortController is via a pair of *Streams, which
019 * then carry sequences of characters for transmission. Note that this
020 * processing is handled in an independent thread.
021 * <p>
022 * This maintains a list of nodes, but doesn't currently do anything with it.
023 *
024 * @author Bob Jacobsen Copyright (C) 2001, 2003, 2005, 2006, 2008 Converted to
025 * multiple connection
026 * @author kcameron Copyright (C) 2011
027 */
028public class SpecificTrafficController extends SerialTrafficController {
029
030    public SpecificTrafficController(SerialSystemConnectionMemo memo) {
031        super();
032        this.memo = memo;
033        logDebug = log.isDebugEnabled();
034
035        // not polled at all, so allow unexpected messages, and
036        // use poll delay just to spread out startup
037        setAllowUnexpectedReply(true);
038        mWaitBeforePoll = 1000;  // can take a long time to send
039
040    }
041
042    /**
043     * Send a sequence of X10 messages
044     * <p>
045     * Makes them into the local messages and then queues in order
046     */
047    @Override
048    synchronized public void sendX10Sequence(X10Sequence s, SerialListener l) {
049        s.reset();
050        X10Sequence.Command c;
051        while ((c = s.getCommand()) != null) {
052            SerialMessage m;
053            if (c.isAddress()) {
054                m = SpecificMessage.getAddress(c.getHouseCode(), ((X10Sequence.Address) c).getAddress());
055            } else if (c.isFunction()) {
056                X10Sequence.Function f = (X10Sequence.Function) c;
057                if (f.getDimCount() > 0) {
058                    m = SpecificMessage.getFunctionDim(f.getHouseCode(), f.getFunction(), f.getDimCount());
059                } else {
060                    m = SpecificMessage.getFunction(f.getHouseCode(), f.getFunction());
061                }
062            } else {
063                // isn't address or function
064                X10Sequence.ExtData e = (X10Sequence.ExtData) c;
065                m = SpecificMessage.getExtCmd(c.getHouseCode(), e.getAddress(), e.getExtCmd(), e.getExtData());
066            }
067            sendSerialMessage(m, l);
068        }
069    }
070
071    /**
072     * This system provides 22 dim steps
073     */
074    @Override
075    public int getNumberOfIntensitySteps() {
076        return 63;
077    }
078
079    /**
080     * Get a message of a specific length for filling in.
081     */
082    @Override
083    public SerialMessage getSerialMessage(int length) {
084        return new SpecificMessage(length);
085    }
086
087    @Override
088    protected void forwardToPort(AbstractMRMessage m, AbstractMRListener reply) {
089        if (logDebug) {
090            log.debug("forward {}", m);
091        }
092        sendInterlock = ((SerialMessage) m).getInterlocked();
093        super.forwardToPort(m, reply);
094    }
095
096    /**
097     * Specific class override of the Serial class
098     */
099    @Override
100    protected AbstractMRReply newReply() {
101        SpecificReply reply = new SpecificReply(memo.getTrafficController());
102        return reply;
103    }
104
105    boolean sendInterlock = false; // send the 00 interlock when CRC received
106    boolean expectLength = false;  // next byte is length of read
107    boolean countingBytes = false; // counting remainingBytes into reply buffer
108    int remainingBytes = 0;        // count of bytes _left_
109
110    /**
111     * Specific class override of the Serial class
112     */
113    @Override
114    public boolean endOfMessage(AbstractMRReply msg) {
115        // check if this byte is length
116        if (expectLength) {
117            expectLength = false;
118            countingBytes = true;
119            remainingBytes = msg.getElement(1) & 0xF; // 0 was the read command; max 9, really
120            if (logDebug) {
121                log.debug("Receive count set to {}", remainingBytes);
122            }
123            return false;
124        }
125        if (remainingBytes > 0) {
126            if (remainingBytes > 8) {
127                log.error("Invalid remainingBytes: {}", remainingBytes);
128                remainingBytes = 0;
129                return true;
130            }
131            remainingBytes--;
132            if (remainingBytes == 0) {
133                countingBytes = false;
134                return true;  // done
135            }
136            return false; // wait for one more
137        }
138        // check for data available
139        if ((msg.getElement(0) & 0xFF) == Constants.POLL_REQ) {
140            // get message
141            SerialMessage m = new SpecificMessage(1);
142            m.setElement(0, Constants.POLL_ACK);
143            expectLength = true;  // next byte is length
144            forwardToPort(m, null);
145            return false;  // reply message will get data appended            
146        }
147        // check for request time
148        if (((msg.getElement(0) & 0xFF) == Constants.TIME_REQ_CP10) || ((msg.getElement(0) & 0xFF) == Constants.TIME_REQ_CP11)) {
149            SerialMessage m = SpecificMessage.setCM11Time(X10Sequence.encode(1));
150            forwardToPort(m, null);
151            return true;  // message done
152        }
153        // check for reporting macro trigger
154        if ((msg.getElement(0) & 0xFF) == 0x5B) {
155            if (msg.getNumDataElements() >= 3) {
156                return true;
157            } else {
158                return false; // waiting for high-low addr
159            }
160        }
161        // if the interlock is present, send it
162        if (sendInterlock) {
163            if (logDebug) {
164                log.debug("Send interlock");
165            }
166            sendInterlock = false;
167            SerialMessage m = new SpecificMessage(1);
168            m.setElement(0, 0); // not really needed, but this is a slow protocol anyway
169            forwardToPort(m, null);
170            return false; // just leave in buffer
171        }
172        if (logDebug) {
173            log.debug("end of message: {}", msg);
174        }
175        return true;
176    }
177
178    private final static Logger log = LoggerFactory.getLogger(SpecificTrafficController.class);
179
180}
181