001package jmri.jmrix.powerline.insteon2412s; 002 003import jmri.jmrix.powerline.SerialMessage; 004import jmri.jmrix.powerline.X10Sequence; 005import jmri.util.StringUtil; 006import org.slf4j.Logger; 007import org.slf4j.LoggerFactory; 008 009/** 010 * Contains the data payload of a serial packet. 011 * <p> 012 * The transmission protocol can come in one of several forms: 013 * <ul> 014 * <li>If the interlocked parameter is false (default), the packet is just sent. 015 * If the response length is not zero, a reply of that length is expected. 016 * <li>If the interlocked parameter is true, the transmission will require a CRC 017 * interlock, which will be automatically added. (Design note: this is done to 018 * make sure that the messages remain atomic) 019 * </ul> 020 * 021 * @author Bob Jacobsen Copyright (C) 2001,2003, 2006, 2007, 2008, 2009 022 * @author Ken Cameron Copyright (C) 2010 023 */ 024public class SpecificMessage extends SerialMessage { 025 // is this logically an abstract class? 026 027 public SpecificMessage(int l) { 028 super(l); 029 setResponseLength(0); // only polls require a response 030 setBinary(true); 031 setTimeout(5000); 032 } 033 034 /** 035 * This ctor interprets the String as the exact sequence to send, 036 * byte-for-byte. 037 * 038 * @param m message 039 * @param l response length in bytes 040 */ 041 public SpecificMessage(String m, int l) { 042 super(m, l); 043 } 044 045 boolean interlocked = false; 046 047 @Override 048 public void setInterlocked(boolean v) { 049 interlocked = v; 050 } 051 052 @Override 053 public boolean getInterlocked() { 054 return interlocked; 055 } 056 057 @Override 058 public String toMonitorString() { 059 // check for valid length 060 int len = getNumDataElements(); 061 StringBuilder text = new StringBuilder(); 062 if ((getElement(0) & 0xFF) != Constants.HEAD_STX) { 063 text.append("INVALID HEADER: " + String.format("0x%1X", getElement(0) & 0xFF)); 064 text.append(" len: " + len); 065 } else { 066 switch (getElement(1) & 0xFF) { 067 case Constants.FUNCTION_REQ_STD: 068 text.append("Send Cmd "); 069 if (len == 8 || len == 22) { 070 if ((getElement(5) & Constants.FLAG_BIT_STDEXT) == Constants.FLAG_STD) { 071 text.append(" Std"); 072 } else if (len == 22) { 073 text.append(" Ext"); 074 } 075 switch (getElement(5) & Constants.FLAG_MASK_MSGTYPE) { 076 case Constants.FLAG_TYPE_P2P: 077 text.append(" Direct"); 078 break; 079 case Constants.FLAG_TYPE_ACK: 080 text.append(" ACK"); 081 break; 082 case Constants.FLAG_TYPE_NAK: 083 text.append(" NAK"); 084 break; 085 case Constants.FLAG_TYPE_GBCAST: 086 text.append(" Group Broadcast"); 087 break; 088 case Constants.FLAG_TYPE_GBCLEANUP: 089 text.append(" Group Broadcast Cleanup"); 090 break; 091 case Constants.FLAG_TYPE_GBCLEANACK: 092 text.append(" Group Broadcast Cleanup ACK"); 093 break; 094 case Constants.FLAG_TYPE_GBCLEANNAK: 095 text.append(" Group Broadcast Cleanup NAK"); 096 break; 097 default: 098 log.warn("Unhandled flag type: {}", getElement(5) & Constants.FLAG_MASK_MSGTYPE); 099 break; 100 } 101 text.append(" message,"); 102 text.append(String.format(" %d hops left", (getElement(5) & Constants.FLAG_MASK_HOPSLEFT >> Constants.FLAG_SHIFT_HOPSLEFT))); 103 text.append(String.format(" , %d max hops", (getElement(5) & Constants.FLAG_MASK_MAXHOPS))); 104 text.append(" addr " + String.format("%1$X.%2$X.%3$X", (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: " + StringUtil.twoHexFromInt(getElement(6) & 0xFF)); 128 break; 129 } 130 } else { 131 text.append(" !! Length wrong: " + 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: " + StringUtil.twoHexFromInt(getElement(1) & 0xFF)); 169 text.append(" len: " + 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 static public 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 static public 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 static public 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 static public 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 static public 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 static public 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 static public SpecificMessage getInsteonFunction(int idhighbyte, int idmiddlebyte, int idlowbyte, int function, int flag, int cmd1, int cmd2) { 288 SpecificMessage m = new SpecificMessage(8); 289// m.setInterlocked(true); 290 m.setInterlocked(false); 291 m.setElement(0, Constants.HEAD_STX); 292 m.setElement(1, Constants.FUNCTION_REQ_STD); 293 m.setElement(2, idhighbyte); 294 m.setElement(3, idmiddlebyte); 295 m.setElement(4, idlowbyte); 296 m.setElement(5, flag); 297 m.setElement(6, cmd1); 298 m.setElement(7, cmd2); 299 return m; 300 } 301 302 // initialize logging 303 private final static Logger log = LoggerFactory.getLogger(SpecificMessage.class); 304 305}