001package jmri.jmrix.powerline.insteon2412s;
002
003import java.io.DataInputStream;
004import jmri.jmrix.AbstractMRListener;
005import jmri.jmrix.AbstractMRMessage;
006import jmri.jmrix.AbstractMRReply;
007import jmri.jmrix.powerline.InsteonSequence;
008import jmri.jmrix.powerline.SerialListener;
009import jmri.jmrix.powerline.SerialMessage;
010import jmri.jmrix.powerline.SerialSystemConnectionMemo;
011import jmri.jmrix.powerline.SerialTrafficController;
012import jmri.jmrix.powerline.X10Sequence;
013import org.slf4j.Logger;
014import org.slf4j.LoggerFactory;
015
016/**
017 * Converts Stream-based I/O to/from messages. The "SerialInterface" side
018 * sends/receives message objects.
019 * <p>
020 * The connection to a SerialPortController is via a pair of *Streams, which
021 * then carry sequences of characters for transmission. Note that this
022 * processing is handled in an independent thread.
023 * <p>
024 * This maintains a list of nodes, but doesn't currently do anything with it.
025 *
026 * @author Bob Jacobsen Copyright (C) 2001, 2003, 2005, 2006, 2008, 2009
027 * @author Ken Cameron Copyright (C) 2010 Converted to multiple connection
028 * @author kcameron Copyright (C) 2011
029 */
030public class SpecificTrafficController extends SerialTrafficController {
031
032    public SpecificTrafficController(SerialSystemConnectionMemo memo) {
033        super();
034        this.memo = memo;
035        logDebug = log.isDebugEnabled();
036
037        // not polled at all, so allow unexpected messages, and
038        // use poll delay just to spread out startup
039        setAllowUnexpectedReply(true);
040        mWaitBeforePoll = 1000;  // can take a long time to send
041
042    }
043
044    /**
045     * Send a sequence of X10 messages
046     * <p>
047     * Makes them into the local messages and then queues in order
048     */
049    @Override
050    synchronized public void sendX10Sequence(X10Sequence s, SerialListener l) {
051        s.reset();
052        X10Sequence.Command c;
053        while ((c = s.getCommand()) != null) {
054            SpecificMessage m;
055            if (c.isAddress()) {
056                m = SpecificMessage.getX10Address(c.getHouseCode(), ((X10Sequence.Address) c).getAddress());
057            } else {
058                X10Sequence.Function f = (X10Sequence.Function) c;
059                if (f.getDimCount() > 0) {
060                    m = SpecificMessage.getX10FunctionDim(f.getHouseCode(), f.getFunction(), f.getDimCount());
061                } else {
062                    m = SpecificMessage.getX10Function(f.getHouseCode(), f.getFunction());
063                }
064            }
065            sendSerialMessage(m, l);
066            // Someone help me improve this
067            // Without this wait, the commands are too close together and will return
068            // an 0x15 which means they failed.
069            // But there must be a better way to delay the sending of the next command.
070            try {
071                wait(250);
072            } catch (InterruptedException ex) {
073                log.error("Interrupted Exception", ex);
074            }
075        }
076    }
077
078    /**
079     * Send a sequence of Insteon messages
080     * <p>
081     * Makes them into the local messages and then queues in order
082     */
083    @Override
084    synchronized public void sendInsteonSequence(InsteonSequence s, SerialListener l) {
085        s.reset();
086        InsteonSequence.Command c;
087        while ((c = s.getCommand()) != null) {
088            SpecificMessage m;
089            if (c.isAddress()) {
090                // We should not get here
091                // Clean this up later
092                m = SpecificMessage.getInsteonAddress(-1, -1, -1);
093            } else {
094                InsteonSequence.Function f = (InsteonSequence.Function) c;
095                m = SpecificMessage.getInsteonFunction(f.getAddressHigh(), f.getAddressMiddle(), f.getAddressLow(), f.getFunction(), f.getFlag(), f.getCommand1(), f.getCommand2());
096            }
097            sendSerialMessage(m, l);
098            // Someone help me improve this
099            // Without this wait, the commands are too close together and will return
100            // an 0x15 which means they failed.
101            // But there must be a better way to delay the sending of the next command.
102 /*
103             try {
104             wait(250);
105             } catch (InterruptedException ex) {
106             log.error("", ex);
107             }
108             */
109        }
110    }
111
112    /**
113     * Get a message of a specific length for filling in.
114     */
115    @Override
116    public SerialMessage getSerialMessage(int length) {
117        return new SpecificMessage(length);
118    }
119
120    @Override
121    protected void forwardToPort(AbstractMRMessage m, AbstractMRListener reply) {
122        if (logDebug) {
123            log.debug("forward {}", m);
124        }
125        super.forwardToPort(m, reply);
126    }
127
128    @Override
129    protected AbstractMRReply newReply() {
130        SpecificReply reply = new SpecificReply(memo.getTrafficController());
131        return reply;
132    }
133
134    @Override
135    protected boolean endOfMessage(AbstractMRReply msg) {
136        if (msg.getNumDataElements() >= 2) {
137            if (msg.getElement(0) != Constants.HEAD_STX) {
138                return false;
139            }
140            int cmd = msg.getElement(1);
141            switch (msg.getNumDataElements()) {
142                case 2:
143                    if (cmd == Constants.POLL_REQ_BUTTON_RESET) {
144                        return true;
145                    }
146                    break;
147                case 3:
148                    if (cmd == Constants.POLL_REQ_BUTTON) {
149                        return true;
150                    }
151                    break;
152                case 4:
153                    if (cmd == Constants.POLL_REQ_X10) {
154                        return true;
155                    }
156                    break;
157                case 5: // reply from send X10 command
158                    if (cmd == Constants.FUNCTION_REQ_X10) {
159                        return true;
160                    }
161                    break;
162                case 11:
163                    if (cmd == Constants.POLL_REQ_STD) {
164                        return true;
165                    }
166                    break;
167                case 12: // reply from send standard Insteon command
168                    if ((cmd == Constants.FUNCTION_REQ_STD) && ((msg.getElement(5) & Constants.FLAG_BIT_STDEXT) == Constants.FLAG_STD)) {
169                        return true;
170                    }
171                    break;
172                case 25:
173                    if (cmd == Constants.POLL_REQ_EXT) {
174                        return true;
175                    }
176                    break;
177                case 26: // reply from send extended Insteon command
178                    if ((cmd == Constants.FUNCTION_REQ_STD) && ((msg.getElement(5) & Constants.FLAG_BIT_STDEXT) == Constants.FLAG_EXT)) {
179                        return true;
180                    }
181                    break;
182                default:
183                    break;
184            }
185        }
186        if (logDebug) {
187            log.debug("end of message: {}", msg);
188        }
189        return false;
190    }
191
192    /**
193     * read a stream and pick packets out of it. knows the size of the packets
194     * from the contents.
195     */
196    @Override
197    protected void loadChars(AbstractMRReply msg, DataInputStream istream) throws java.io.IOException {
198        byte char1 = readByteProtected(istream);
199        if (logDebug) {
200            log.debug("loadChars: {}", char1);
201        }
202        if ((char1 & 0xFF) == Constants.HEAD_STX) {  // 0x02 means start of command.
203            msg.setElement(0, char1);
204            byte char2 = readByteProtected(istream);
205            if ((char2 & 0xFF) == Constants.FUNCTION_REQ_STD) {  // 0x62 means normal send command reply.
206                msg.setElement(1, char2);
207                byte addr1 = readByteProtected(istream);
208                msg.setElement(2, addr1);
209                byte addr2 = readByteProtected(istream);
210                msg.setElement(3, addr2);
211                byte addr3 = readByteProtected(istream);
212                msg.setElement(4, addr3);
213                byte flag1 = readByteProtected(istream);
214                msg.setElement(5, flag1);
215                int bufsize = 2 + 1;
216                if ((flag1 & Constants.FLAG_BIT_STDEXT) != 0x00) {
217                    bufsize = 14 + 1;
218                }
219                for (int i = 6; i < (5 + bufsize); i++) {
220                    byte byt = readByteProtected(istream);
221                    msg.setElement(i, byt);
222                }
223            } else if ((char2 & 0xFF) == Constants.FUNCTION_REQ_X10) {  // 0x63 means normal send X10 command reply.
224                msg.setElement(1, char2);
225                byte addrx1 = readByteProtected(istream);
226                msg.setElement(2, addrx1);
227                byte cmd1 = readByteProtected(istream);
228                msg.setElement(3, cmd1);
229                byte ack1 = readByteProtected(istream);
230                msg.setElement(4, ack1);
231            } else if ((char2 & 0xFF) == Constants.POLL_REQ_STD) {  // 0x50 means normal command received.
232                msg.setElement(1, char2);
233                for (int i = 2; i < (2 + 9); i++) {
234                    byte byt = readByteProtected(istream);
235                    msg.setElement(2, byt);
236                }
237            } else if ((char2 & 0xFF) == Constants.POLL_REQ_EXT) {  // 0x51 means extended command received.
238                msg.setElement(1, char2);
239                for (int i = 2; i < (2 + 23); i++) {
240                    byte byt = readByteProtected(istream);
241                    msg.setElement(2, byt);
242                }
243            } else if ((char2 & 0xFF) == Constants.POLL_REQ_X10) {  // 0x52 means standard X10 received command.
244                msg.setElement(1, char2);
245                byte rawX10data = readByteProtected(istream);
246                msg.setElement(2, rawX10data);
247                int x10Flag = readByteProtected(istream);
248                msg.setElement(3, x10Flag);
249                if ((x10Flag&0xFF) == Constants.FLAG_X10_RECV_CMD) {
250                    if (logDebug) {
251                        log.debug("loadChars: X10 Command Poll Received {} {}", X10Sequence.houseValueToText((rawX10data & 0xF0) >> 4), X10Sequence.functionName((rawX10data & 0x0F)));
252                    }
253                } else {
254                    if (logDebug) {
255                        log.debug("loadChars: X10 Unit Poll Received {} {}", X10Sequence.houseValueToText((rawX10data & 0xF0) >> 4), X10Sequence.formatCommandByte(rawX10data));
256                    }
257                }
258            } else if ((char2 & 0xFF) == Constants.POLL_REQ_BUTTON) {  // 0x54 means interface button received command.
259                msg.setElement(1, char2);
260                byte dat = readByteProtected(istream);
261                msg.setElement(2, dat);
262            } else if ((char2 & 0xFF) == Constants.POLL_REQ_BUTTON_RESET) {  // 0x55 means interface button received command.
263                msg.setElement(1, char2);
264            } else {
265                msg.setElement(1, char2);
266                if (logDebug) {
267                    log.debug("loadChars: Unknown cmd byte {}", char2);
268                }
269            }
270        }
271    }
272    private final static Logger log = LoggerFactory.getLogger(SpecificTrafficController.class);
273}