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}