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 @Deprecated( since="5.13.5", forRemoval=true) // deprecated super 183 public SpecificMessage(byte[] a, int l) { 184 super(a, l); 185 } 186 187 int responseLength = -1; // -1 is an invalid value, indicating it hasn't been set 188 189 @Override 190 public void setResponseLength(int l) { 191 responseLength = l; 192 } 193 194 @Override 195 public int getResponseLength() { 196 return responseLength; 197 } 198 199 // static methods to recognize a message 200// public boolean isPoll() { return getElement(1)==48;} 201// public boolean isXmt() { return getElement(1)==17;} 202// public int getAddr() { return getElement(0); } 203 // static methods to return a formatted message 204 public static SerialMessage getPoll(int addr) { 205 // Powerline implementation does not currently poll 206 return null; 207 } 208 209 /** 210 * create an Insteon message with the X10 address 211 * @param housecode X10 housecode 212 * @param devicecode X10 devicecode 213 * 214 * @return message formated message 215 */ 216 public static SpecificMessage getX10Address(int housecode, int devicecode) { 217 SpecificMessage m = new SpecificMessage(4); 218 m.setInterlocked(false); 219 m.setElement(0, Constants.HEAD_STX); 220 m.setElement(1, Constants.FUNCTION_REQ_X10); 221 m.setElement(2, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode)); 222 m.setElement(3, 0x00); // 0x00 Means address 223 return m; 224 } 225 226 /** 227 * create an Insteon message with the X10 address and dim steps 228 * 229 * @param housecode X10 housecode 230 * @param devicecode X10 devicecode 231 * @param dimcode value for dimming 232 * 233 * @return message formated message 234 */ 235 public static SpecificMessage getX10AddressDim(int housecode, int devicecode, int dimcode) { 236 SpecificMessage m = new SpecificMessage(4); 237 m.setInterlocked(false); 238 m.setElement(0, Constants.HEAD_STX); 239 m.setElement(1, Constants.FUNCTION_REQ_X10); 240 if (dimcode > 0) { 241 m.setElement(2, 0x04 | ((dimcode & 0x1f) << 3)); 242 } else { 243 m.setElement(2, 0x04); 244 } 245 m.setElement(3, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode)); 246 m.setElement(3, 0x80); // 0x00 Means address 247 return m; 248 } 249 250 public static SpecificMessage getX10FunctionDim(int housecode, int function, int dimcode) { 251 SpecificMessage m = new SpecificMessage(2); 252 m.setInterlocked(true); 253 if (dimcode > 0) { 254 m.setElement(0, 0x06 | ((dimcode & 0x1f) << 3)); 255 } else { 256 m.setElement(0, 0x06); 257 } 258 m.setElement(1, (X10Sequence.encode(housecode) << 4) + function); 259 return m; 260 } 261 262 public static SpecificMessage getX10Function(int housecode, int function) { 263 SpecificMessage m = new SpecificMessage(4); 264// m.setInterlocked(true); 265 m.setInterlocked(false); 266 m.setElement(0, Constants.HEAD_STX); 267 m.setElement(1, Constants.FUNCTION_REQ_X10); 268 m.setElement(2, (X10Sequence.encode(housecode) << 4) + function); 269 m.setElement(3, 0x80); // 0x80 means function 270 return m; 271 } 272 273 public static SpecificMessage getInsteonAddress(int idhighbyte, int idmiddlebyte, int idlowbyte) { 274 SpecificMessage m = new SpecificMessage(8); 275// m.setInterlocked(true); 276 m.setInterlocked(false); 277 m.setElement(0, Constants.HEAD_STX); 278 m.setElement(1, Constants.FUNCTION_REQ_STD); 279 m.setElement(2, idhighbyte); 280 m.setElement(3, idmiddlebyte); 281 m.setElement(4, idlowbyte); 282 m.setElement(5, 0x0F); 283 m.setElement(6, 0x11); 284 m.setElement(7, 0xFF); 285 return m; 286 } 287 288 public static SpecificMessage getInsteonFunction(int idhighbyte, int idmiddlebyte, int idlowbyte, 289 int function, int flag, int cmd1, int cmd2) { 290 SpecificMessage m = new SpecificMessage(8); 291// m.setInterlocked(true); 292 m.setInterlocked(false); 293 m.setElement(0, Constants.HEAD_STX); 294 m.setElement(1, Constants.FUNCTION_REQ_STD); 295 m.setElement(2, idhighbyte); 296 m.setElement(3, idmiddlebyte); 297 m.setElement(4, idlowbyte); 298 m.setElement(5, flag); 299 m.setElement(6, cmd1); 300 m.setElement(7, cmd2); 301 return m; 302 } 303 304 // initialize logging 305 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SpecificMessage.class); 306 307}