001package jmri.jmrix.powerline.simulator; 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 * Send a sequence of X10 messages. 045 * <p> 046 * Makes them into the local messages and then queues in order. 047 */ 048 @Override 049 synchronized public void sendX10Sequence(X10Sequence s, SerialListener l) { 050 s.reset(); 051 X10Sequence.Command c; 052 while ((c = s.getCommand()) != null) { 053 SpecificMessage m; 054 if (c.isAddress()) { 055 m = SpecificMessage.getX10Address(c.getHouseCode(), ((X10Sequence.Address) c).getAddress()); 056 } else if (c.isFunction()) { 057 X10Sequence.Function f = (X10Sequence.Function) c; 058 if (f.getDimCount() > 0) { 059 m = SpecificMessage.getX10FunctionDim(f.getHouseCode(), f.getFunction(), f.getDimCount()); 060 } else { 061 m = SpecificMessage.getX10Function(f.getHouseCode(), f.getFunction()); 062 } 063 } else { 064 // isn't address or function 065 X10Sequence.ExtData e = (X10Sequence.ExtData) c; 066 m = SpecificMessage.getExtCmd(c.getHouseCode(), e.getAddress(), e.getExtCmd(), e.getExtData()); 067 } 068 sendSerialMessage(m, l); 069 // Someone help me improve this 070 // Without this wait, the commands are too close together and will return 071 // an 0x15 which means they failed. 072 // But there must be a better way to delay the sending of the next command. 073 try { 074 wait(250); 075 } catch (InterruptedException ex) { 076 log.error("Interrupted Exception", ex); 077 } 078 } 079 } 080 081 /** 082 * Send a sequence of Insteon messages. 083 * <p> 084 * Makes them into the local messages and then queues in order. 085 */ 086 @Override 087 synchronized public void sendInsteonSequence(InsteonSequence s, SerialListener l) { 088 s.reset(); 089 InsteonSequence.Command c; 090 while ((c = s.getCommand()) != null) { 091 SpecificMessage m; 092 if (c.isAddress()) { 093 // We should not get here 094 // Clean this up later 095 m = SpecificMessage.getInsteonAddress(-1, -1, -1); 096 } else { 097 InsteonSequence.Function f = (InsteonSequence.Function) c; 098 m = SpecificMessage.getInsteonFunction(f.getAddressHigh(), f.getAddressMiddle(), f.getAddressLow(), f.getFunction(), f.getFlag(), f.getCommand1(), f.getCommand2()); 099 } 100 sendSerialMessage(m, l); 101 // Someone help me improve this 102 // Without this wait, the commands are too close together and will return 103 // an 0x15 which means they failed. 104 // But there must be a better way to delay the sending of the next command. 105 /* 106 try { 107 wait(250); 108 } catch (InterruptedException ex) { 109 log.error("", ex); 110 } 111 */ 112 } 113 } 114 115 /** 116 * Get a message of a specific length for filling in. 117 */ 118 @Override 119 public SerialMessage getSerialMessage(int length) { 120 return new SpecificMessage(length); 121 } 122 123 @Override 124 protected void forwardToPort(AbstractMRMessage m, AbstractMRListener reply) { 125 if (logDebug) { 126 log.debug("forward {}", m); 127 } 128 super.forwardToPort(m, reply); 129 } 130 131 @Override 132 protected AbstractMRReply newReply() { 133 SpecificReply reply = new SpecificReply(memo.getTrafficController()); 134 return reply; 135 } 136 137 @Override 138 protected boolean endOfMessage(AbstractMRReply msg) { 139 if (msg.getNumDataElements() >= 2) { 140 if (msg.getElement(0) != Constants.HEAD_STX) { 141 return false; 142 } 143 int cmd = msg.getElement(1); 144 switch (msg.getNumDataElements()) { 145 case 2: 146 if (cmd == Constants.POLL_REQ_BUTTON_RESET) { 147 return true; 148 } 149 break; 150 case 3: 151 if (cmd == Constants.POLL_REQ_BUTTON) { 152 return true; 153 } 154 break; 155 case 4: 156 if (cmd == Constants.POLL_REQ_X10) { 157 return true; 158 } 159 break; 160 case 5: // reply from send X10 command 161 if (cmd == Constants.FUNCTION_REQ_X10) { 162 return true; 163 } 164 break; 165 case 11: 166 if (cmd == Constants.POLL_REQ_STD) { 167 return true; 168 } 169 break; 170 case 12: // reply from send standard Insteon command 171 if ((cmd == Constants.FUNCTION_REQ_STD) && ((msg.getElement(5) & Constants.FLAG_BIT_STDEXT) == Constants.FLAG_STD)) { 172 return true; 173 } 174 break; 175 case 25: 176 if (cmd == Constants.POLL_REQ_EXT) { 177 return true; 178 } 179 break; 180 case 26: // reply from send extended Insteon command 181 if ((cmd == Constants.FUNCTION_REQ_STD) && ((msg.getElement(5) & Constants.FLAG_BIT_STDEXT) == Constants.FLAG_EXT)) { 182 return true; 183 } 184 break; 185 default: 186 break; 187 } 188 } 189 if (logDebug) { 190 log.debug("end of message: {}", msg); 191 } 192 return false; 193 } 194 195 /** 196 * Read a stream and pick packets out of it. Knows the size of the packets 197 * from the contents. 198 */ 199 @Override 200 protected void loadChars(AbstractMRReply msg, DataInputStream istream) throws java.io.IOException { 201 byte char1 = readByteProtected(istream); 202 if (logDebug) { 203 log.debug("loadChars: {}", char1); 204 } 205 if ((char1 & 0xFF) == Constants.HEAD_STX) { // 0x02 means start of command. 206 msg.setElement(0, char1); 207 byte char2 = readByteProtected(istream); 208 if ((char2 & 0xFF) == Constants.FUNCTION_REQ_STD) { // 0x62 means normal send command reply. 209 msg.setElement(1, char2); 210 byte addr1 = readByteProtected(istream); 211 msg.setElement(2, addr1); 212 byte addr2 = readByteProtected(istream); 213 msg.setElement(3, addr2); 214 byte addr3 = readByteProtected(istream); 215 msg.setElement(4, addr3); 216 byte flag1 = readByteProtected(istream); 217 msg.setElement(5, flag1); 218 int bufsize = 2 + 1; 219 if ((flag1 & Constants.FLAG_BIT_STDEXT) != 0x00) { 220 bufsize = 14 + 1; 221 } 222 for (int i = 6; i < (5 + bufsize); i++) { 223 byte byt = readByteProtected(istream); 224 msg.setElement(i, byt); 225 } 226 } else if ((char2 & 0xFF) == Constants.FUNCTION_REQ_X10) { // 0x63 means normal send X10 command reply. 227 msg.setElement(1, char2); 228 byte addrx1 = readByteProtected(istream); 229 msg.setElement(2, addrx1); 230 byte cmd1 = readByteProtected(istream); 231 msg.setElement(3, cmd1); 232 byte ack1 = readByteProtected(istream); 233 msg.setElement(4, ack1); 234 } else if ((char2 & 0xFF) == Constants.POLL_REQ_STD) { // 0x50 means normal command received. 235 msg.setElement(1, char2); 236 for (int i = 2; i < (2 + 9); i++) { 237 byte byt = readByteProtected(istream); 238 msg.setElement(2, byt); 239 } 240 } else if ((char2 & 0xFF) == Constants.POLL_REQ_EXT) { // 0x51 means extended command received. 241 msg.setElement(1, char2); 242 for (int i = 2; i < (2 + 23); i++) { 243 byte byt = readByteProtected(istream); 244 msg.setElement(2, byt); 245 } 246 } else if ((char2 & 0xFF) == Constants.POLL_REQ_X10) { // 0x52 means standard X10 received command. 247 msg.setElement(1, char2); 248 byte rawX10data = readByteProtected(istream); 249 msg.setElement(2, rawX10data); 250 byte x10Flag = readByteProtected(istream); 251 msg.setElement(3, x10Flag); 252 if ((x10Flag&0xFF) == Constants.FLAG_X10_RECV_CMD) { 253 if (logDebug) { 254 log.debug("loadChars: X10 Command Poll Received {} {}", X10Sequence.houseValueToText((rawX10data & 0xF0) >> 4), X10Sequence.functionName((rawX10data & 0x0F))); 255 } 256 } else { 257 if (logDebug) { 258 log.debug("loadChars: X10 Unit Poll Received {} {}", X10Sequence.houseValueToText((rawX10data & 0xF0) >> 4), X10Sequence.formatCommandByte(rawX10data)); 259 } 260 } 261 } else if ((char2 & 0xFF) == Constants.POLL_REQ_BUTTON) { // 0x54 means interface button received command. 262 msg.setElement(1, char2); 263 byte dat = readByteProtected(istream); 264 msg.setElement(2, dat); 265 } else if ((char2 & 0xFF) == Constants.POLL_REQ_BUTTON_RESET) { // 0x55 means interface button received command. 266 msg.setElement(1, char2); 267 } else { 268 msg.setElement(1, char2); 269 if (logDebug) { 270 log.debug("loadChars: Unknown cmd byte {}", char2); 271 } 272 } 273 } 274 } 275 276 private final static Logger log = LoggerFactory.getLogger(SpecificTrafficController.class); 277 278}