001package jmri.jmrix.powerline.insteon2412s; 002 003import jmri.jmrix.powerline.SerialMessage; 004import jmri.jmrix.powerline.X10Sequence; 005import jmri.util.StringUtil; 006 007/** 008 * Contains the data payload of a serial packet. 009 * <p> 010 * The transmission protocol can come in one of several forms: 011 * <ul> 012 * <li>If the interlocked parameter is false (default), the packet is just sent. 013 * If the response length is not zero, a reply of that length is expected. 014 * <li>If the interlocked parameter is true, the transmission will require a CRC 015 * interlock, which will be automatically added. (Design note: this is done to 016 * make sure that the messages remain atomic) 017 * </ul> 018 * 019 * @author Bob Jacobsen Copyright (C) 2001,2003, 2006, 2007, 2008, 2009 020 * @author Ken Cameron Copyright (C) 2010 021 */ 022public class SpecificMessage extends SerialMessage { 023 // is this logically an abstract class? 024 025 public SpecificMessage(int l) { 026 super(l); 027 setResponseLength(0); // only polls require a response 028 setBinary(true); 029 setTimeout(5000); 030 } 031 032 /** 033 * This ctor interprets the String as the exact sequence to send, 034 * byte-for-byte. 035 * 036 * @param m message 037 * @param l response length in bytes 038 */ 039 public SpecificMessage(String m, int l) { 040 super(m, l); 041 } 042 043 boolean interlocked = false; 044 045 @Override 046 public void setInterlocked(boolean v) { 047 interlocked = v; 048 } 049 050 @Override 051 public boolean getInterlocked() { 052 return interlocked; 053 } 054 055 @Override 056 public String toMonitorString() { 057 // check for valid length 058 int len = getNumDataElements(); 059 StringBuilder text = new StringBuilder(); 060 if ((getElement(0) & 0xFF) != Constants.HEAD_STX) { 061 text.append("INVALID HEADER: ").append(String.format("0x%1X", getElement(0) & 0xFF)); 062 text.append(" len: ").append(len); 063 } else { 064 switch (getElement(1) & 0xFF) { 065 case Constants.FUNCTION_REQ_STD: 066 text.append("Send Cmd "); 067 if (len == 8 || len == 22) { 068 if ((getElement(5) & Constants.FLAG_BIT_STDEXT) == Constants.FLAG_STD) { 069 text.append(" Std"); 070 } else if (len == 22) { 071 text.append(" Ext"); 072 } 073 switch (getElement(5) & Constants.FLAG_MASK_MSGTYPE) { 074 case Constants.FLAG_TYPE_P2P: 075 text.append(" Direct"); 076 break; 077 case Constants.FLAG_TYPE_ACK: 078 text.append(" ACK"); 079 break; 080 case Constants.FLAG_TYPE_NAK: 081 text.append(" NAK"); 082 break; 083 case Constants.FLAG_TYPE_GBCAST: 084 text.append(" Group Broadcast"); 085 break; 086 case Constants.FLAG_TYPE_GBCLEANUP: 087 text.append(" Group Broadcast Cleanup"); 088 break; 089 case Constants.FLAG_TYPE_GBCLEANACK: 090 text.append(" Group Broadcast Cleanup ACK"); 091 break; 092 case Constants.FLAG_TYPE_GBCLEANNAK: 093 text.append(" Group Broadcast Cleanup NAK"); 094 break; 095 default: 096 log.warn("Unhandled flag type: {}", getElement(5) & Constants.FLAG_MASK_MSGTYPE); 097 break; 098 } 099 text.append(" message,"); 100 text.append(String.format(" %d hops left", 101 (getElement(5) & Constants.FLAG_MASK_HOPSLEFT >> Constants.FLAG_SHIFT_HOPSLEFT))); 102 text.append(String.format(" , %d max hops", (getElement(5) & Constants.FLAG_MASK_MAXHOPS))); 103 text.append(" addr ").append(String.format("%1$X.%2$X.%3$X", 104 (getElement(2) & 0xFF), (getElement(3) & 0xFF), (getElement(4) & 0xFF))); 105 switch (getElement(6) & 0xFF) { 106 case Constants.CMD_LIGHT_ON_RAMP: 107 text.append(" ON RAMP "); 108 text.append((getElement(7) & 0xFF) / 256.0); 109 break; 110 case Constants.CMD_LIGHT_ON_FAST: 111 text.append(" ON FAST "); 112 text.append((getElement(7) & 0xFF) / 256.0); 113 break; 114 case Constants.CMD_LIGHT_OFF_FAST: 115 text.append(" OFF FAST "); 116 text.append((getElement(7) & 0xFF) / 256.0); 117 break; 118 case Constants.CMD_LIGHT_OFF_RAMP: 119 text.append(" OFF "); 120 text.append((getElement(7) & 0xFF) / 256.0); 121 break; 122 case Constants.CMD_LIGHT_CHG: 123 text.append(" CHG "); 124 text.append((getElement(7) & 0xFF) / 256.0); 125 break; 126 default: 127 text.append(" Unknown cmd: ").append(StringUtil.twoHexFromInt(getElement(6) & 0xFF)); 128 break; 129 } 130 } else { 131 text.append(" !! Length wrong: ").append(len); 132 } 133 break; 134 // i wrote this then figured the POLL are replies 135// case Constants.POLL_REQ_BUTTON : 136// text.append("Poll Button "); 137// int button = ((getElement(2) & Constants.BUTTON_BITS_ID) >> 4) + 1; 138// text.append(button); 139// int op = getElement(2) & Constants.BUTTON_BITS_OP; 140// if (op == Constants.BUTTON_HELD) { 141// text.append(" HELD"); 142// } else if (op == Constants.BUTTON_REL) { 143// text.append(" RELEASED"); 144// } else if (op == Constants.BUTTON_TAP) { 145// text.append(" TAP"); 146// } 147// break; 148// case Constants.POLL_REQ_BUTTON_RESET : 149// text.append("Reset by Button at Power Cycle"); 150// break; 151 case Constants.FUNCTION_REQ_X10: 152 text.append("Send Cmd X10 "); 153 if ((getElement(3) & Constants.FLAG_BIT_X10_CMDUNIT) == Constants.FLAG_X10_RECV_CMD) { 154 text.append(X10Sequence.formatCommandByte(getElement(2) & 0xFF)); 155 } else { 156 text.append(X10Sequence.formatAddressByte(getElement(2) & 0xFF)); 157 } 158 break; 159// case Constants.POLL_REQ_X10 : 160// text.append("Poll Cmd X10 "); 161// if ((getElement(3)& Constants.FLAG_BIT_X10_CMDUNIT) == Constants.FLAG_X10_RECV_CMD) { 162// text.append(X10Sequence.formatCommandByte(getElement(2) & 0xFF)); 163// } else { 164// text.append(X10Sequence.formatAddressByte(getElement(2)& 0xFF)); 165// } 166// break; 167 default: { 168 text.append(" Unknown command: ").append(StringUtil.twoHexFromInt(getElement(1) & 0xFF)); 169 text.append(" len: ").append(len); 170 } 171 } 172 } 173 return text + "\n"; 174 } 175 176 /** 177 * This ctor interprets the byte array as a sequence of characters to send. 178 * 179 * @param a Array of bytes to send 180 * @param l length of expected reply 181 */ 182 public SpecificMessage(byte[] a, int l) { 183 super(a, l); 184 } 185 186 int responseLength = -1; // -1 is an invalid value, indicating it hasn't been set 187 188 @Override 189 public void setResponseLength(int l) { 190 responseLength = l; 191 } 192 193 @Override 194 public int getResponseLength() { 195 return responseLength; 196 } 197 198 // static methods to recognize a message 199// public boolean isPoll() { return getElement(1)==48;} 200// public boolean isXmt() { return getElement(1)==17;} 201// public int getAddr() { return getElement(0); } 202 // static methods to return a formatted message 203 public static SerialMessage getPoll(int addr) { 204 // Powerline implementation does not currently poll 205 return null; 206 } 207 208 /** 209 * create an Insteon message with the X10 address 210 * @param housecode X10 housecode 211 * @param devicecode X10 devicecode 212 * 213 * @return message formated message 214 */ 215 public static SpecificMessage getX10Address(int housecode, int devicecode) { 216 SpecificMessage m = new SpecificMessage(4); 217 m.setInterlocked(false); 218 m.setElement(0, Constants.HEAD_STX); 219 m.setElement(1, Constants.FUNCTION_REQ_X10); 220 m.setElement(2, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode)); 221 m.setElement(3, 0x00); // 0x00 Means address 222 return m; 223 } 224 225 /** 226 * create an Insteon message with the X10 address and dim steps 227 * 228 * @param housecode X10 housecode 229 * @param devicecode X10 devicecode 230 * @param dimcode value for dimming 231 * 232 * @return message formated message 233 */ 234 public static SpecificMessage getX10AddressDim(int housecode, int devicecode, int dimcode) { 235 SpecificMessage m = new SpecificMessage(4); 236 m.setInterlocked(false); 237 m.setElement(0, Constants.HEAD_STX); 238 m.setElement(1, Constants.FUNCTION_REQ_X10); 239 if (dimcode > 0) { 240 m.setElement(2, 0x04 | ((dimcode & 0x1f) << 3)); 241 } else { 242 m.setElement(2, 0x04); 243 } 244 m.setElement(3, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode)); 245 m.setElement(3, 0x80); // 0x00 Means address 246 return m; 247 } 248 249 public static SpecificMessage getX10FunctionDim(int housecode, int function, int dimcode) { 250 SpecificMessage m = new SpecificMessage(2); 251 m.setInterlocked(true); 252 if (dimcode > 0) { 253 m.setElement(0, 0x06 | ((dimcode & 0x1f) << 3)); 254 } else { 255 m.setElement(0, 0x06); 256 } 257 m.setElement(1, (X10Sequence.encode(housecode) << 4) + function); 258 return m; 259 } 260 261 public static SpecificMessage getX10Function(int housecode, int function) { 262 SpecificMessage m = new SpecificMessage(4); 263// m.setInterlocked(true); 264 m.setInterlocked(false); 265 m.setElement(0, Constants.HEAD_STX); 266 m.setElement(1, Constants.FUNCTION_REQ_X10); 267 m.setElement(2, (X10Sequence.encode(housecode) << 4) + function); 268 m.setElement(3, 0x80); // 0x80 means function 269 return m; 270 } 271 272 public static SpecificMessage getInsteonAddress(int idhighbyte, int idmiddlebyte, int idlowbyte) { 273 SpecificMessage m = new SpecificMessage(8); 274// m.setInterlocked(true); 275 m.setInterlocked(false); 276 m.setElement(0, Constants.HEAD_STX); 277 m.setElement(1, Constants.FUNCTION_REQ_STD); 278 m.setElement(2, idhighbyte); 279 m.setElement(3, idmiddlebyte); 280 m.setElement(4, idlowbyte); 281 m.setElement(5, 0x0F); 282 m.setElement(6, 0x11); 283 m.setElement(7, 0xFF); 284 return m; 285 } 286 287 public static SpecificMessage getInsteonFunction(int idhighbyte, int idmiddlebyte, int idlowbyte, 288 int function, int flag, int cmd1, int cmd2) { 289 SpecificMessage m = new SpecificMessage(8); 290// m.setInterlocked(true); 291 m.setInterlocked(false); 292 m.setElement(0, Constants.HEAD_STX); 293 m.setElement(1, Constants.FUNCTION_REQ_STD); 294 m.setElement(2, idhighbyte); 295 m.setElement(3, idmiddlebyte); 296 m.setElement(4, idlowbyte); 297 m.setElement(5, flag); 298 m.setElement(6, cmd1); 299 m.setElement(7, cmd2); 300 return m; 301 } 302 303 // initialize logging 304 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SpecificMessage.class); 305 306}