001package jmri.jmrix.powerline.simulator; 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 text.append(" addr ").append(String.format("%1$X.%2$X.%3$X", 074 (getElement(2) & 0xFF), (getElement(3) & 0xFF), (getElement(4) & 0xFF))); 075 switch (getElement(6) & 0xFF) { 076 case Constants.CMD_LIGHT_ON_RAMP: 077 text.append(" ON RAMP "); 078 text.append((getElement(7) & 0xFF) / 256.0); 079 break; 080 case Constants.CMD_LIGHT_ON_FAST: 081 text.append(" ON FAST "); 082 text.append((getElement(7) & 0xFF) / 256.0); 083 break; 084 case Constants.CMD_LIGHT_OFF_FAST: 085 text.append(" OFF FAST "); 086 text.append((getElement(7) & 0xFF) / 256.0); 087 break; 088 case Constants.CMD_LIGHT_OFF_RAMP: 089 text.append(" OFF "); 090 text.append((getElement(7) & 0xFF) / 256.0); 091 break; 092 case Constants.CMD_LIGHT_CHG: 093 text.append(" CHG "); 094 text.append((getElement(7) & 0xFF) / 256.0); 095 break; 096 default: 097 text.append(" Unknown cmd: ").append(StringUtil.twoHexFromInt(getElement(6) & 0xFF)); 098 break; 099 } 100 } else { 101 text.append(" !! Length wrong: ").append(len); 102 } 103 break; 104 // i wrote this then figured the POLL are replies 105// case Constants.POLL_REQ_BUTTON : 106// text.append("Poll Button "); 107// int button = ((getElement(2) & Constants.BUTTON_BITS_ID) >> 4) + 1; 108// text.append(button); 109// int op = getElement(2) & Constants.BUTTON_BITS_OP; 110// if (op == Constants.BUTTON_HELD) { 111// text.append(" HELD"); 112// } else if (op == Constants.BUTTON_REL) { 113// text.append(" RELEASED"); 114// } else if (op == Constants.BUTTON_TAP) { 115// text.append(" TAP"); 116// } 117// break; 118// case Constants.POLL_REQ_BUTTON_RESET : 119// text.append("Reset by Button at Power Cycle"); 120// break; 121 case Constants.FUNCTION_REQ_X10: 122 text.append("Send Cmd X10 "); 123 if ((getElement(3) & Constants.FLAG_BIT_X10_CMDUNIT) == Constants.FLAG_X10_RECV_CMD) { 124 text.append(X10Sequence.formatCommandByte(getElement(2) & 0xFF)); 125 } else { 126 text.append(X10Sequence.formatAddressByte(getElement(2) & 0xFF)); 127 } 128 break; 129// case Constants.POLL_REQ_X10 : 130// text.append("Poll Cmd X10 "); 131// if ((getElement(3)& Constants.FLAG_BIT_X10_CMDUNIT) == Constants.FLAG_X10_RECV_CMD) { 132// text.append(X10Sequence.formatCommandByte(getElement(2) & 0xFF)); 133// } else { 134// text.append(X10Sequence.formatAddressByte(getElement(2)& 0xFF)); 135// } 136// break; 137 default: { 138 text.append(" Unknown command: ").append(StringUtil.twoHexFromInt(getElement(1) & 0xFF)); 139 text.append(" len: ").append(len); 140 } 141 } 142 } 143 return text + "\n"; 144 } 145 146 /** 147 * This ctor interprets the byte array as a sequence of characters to send. 148 * 149 * @param a Array of bytes to send 150 * @param l length expected reply 151 */ 152 @Deprecated( since="5.13.5", forRemoval=true) // deprecated super 153 public SpecificMessage(byte[] a, int l) { 154 super(a, l); 155 } 156 157 int responseLength = -1; // -1 is an invalid value, indicating it hasn't been set 158 159 @Override 160 public void setResponseLength(int l) { 161 responseLength = l; 162 } 163 164 @Override 165 public int getResponseLength() { 166 return responseLength; 167 } 168 169 // static methods to recognize a message 170// public boolean isPoll() { return getElement(1)==48;} 171// public boolean isXmt() { return getElement(1)==17;} 172// public int getAddr() { return getElement(0); } 173 // static methods to return a formatted message 174 public static SerialMessage getPoll(int addr) { 175 // Powerline implementation does not currently poll 176 return null; 177 } 178 179 /** 180 * create an Insteon message with the X10 address 181 * @param housecode value of the housecode of X10 address 182 * @param devicecode value of the devicecode of X10 address 183 * 184 * @return message formated message with parameters 185 */ 186 public static SpecificMessage getX10Address(int housecode, int devicecode) { 187 SpecificMessage m = new SpecificMessage(4); 188 m.setInterlocked(false); 189 m.setElement(0, Constants.HEAD_STX); 190 m.setElement(1, Constants.FUNCTION_REQ_X10); 191 m.setElement(2, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode)); 192 m.setElement(3, 0x00); // 0x00 Means address 193 return m; 194 } 195 196 /** 197 * create an Insteon message with the X10 address and dim steps 198 * 199 * @param housecode value of the housecode of X10 address 200 * @param devicecode value of the devicecode of X10 address 201 * @param dimcode value of how dim to set the light 202 * 203 * @return message formated message with parameters 204 */ 205 public static SpecificMessage getX10AddressDim(int housecode, int devicecode, int dimcode) { 206 SpecificMessage m = new SpecificMessage(4); 207 m.setInterlocked(false); 208 m.setElement(0, Constants.HEAD_STX); 209 m.setElement(1, Constants.FUNCTION_REQ_X10); 210 if (dimcode > 0) { 211 m.setElement(2, 0x04 | ((dimcode & 0x1f) << 3)); 212 } else { 213 m.setElement(2, 0x04); 214 } 215 m.setElement(3, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode)); 216 m.setElement(3, 0x80); // 0x00 Means address 217 return m; 218 } 219 220 public static SpecificMessage getX10FunctionDim(int housecode, int function, int dimcode) { 221 SpecificMessage m = new SpecificMessage(2); 222 m.setInterlocked(true); 223 if (dimcode > 0) { 224 m.setElement(0, 0x06 | ((dimcode & 0x1f) << 3)); 225 } else { 226 m.setElement(0, 0x06); 227 } 228 m.setElement(1, (X10Sequence.encode(housecode) << 4) + function); 229 return m; 230 } 231 232 public static SpecificMessage getX10Function(int housecode, int function) { 233 SpecificMessage m = new SpecificMessage(4); 234// m.setInterlocked(true); 235 m.setInterlocked(false); 236 m.setElement(0, Constants.HEAD_STX); 237 m.setElement(1, Constants.FUNCTION_REQ_X10); 238 m.setElement(2, (X10Sequence.encode(housecode) << 4) + function); 239 m.setElement(3, 0x80); // 0x80 means function 240 return m; 241 } 242 243 public static SpecificMessage getExtCmd(int housecode, int devicecode, int function, int dimcode) { 244 SpecificMessage m = new SpecificMessage(5); 245 m.setInterlocked(true); 246 m.setElement(0, 0x07); 247 m.setElement(1, (X10Sequence.encode(housecode) << 4) + X10Sequence.FUNCTION_EXTENDED_CODE); 248 m.setElement(2, X10Sequence.encode(devicecode)); 249 m.setElement(3, dimcode); 250 m.setElement(4, function); 251 return m; 252 } 253 254 public static SpecificMessage getInsteonAddress(int idhighbyte, int idmiddlebyte, int idlowbyte) { 255 SpecificMessage m = new SpecificMessage(8); 256// m.setInterlocked(true); 257 m.setInterlocked(false); 258 m.setElement(0, Constants.HEAD_STX); 259 m.setElement(1, Constants.FUNCTION_REQ_STD); 260 m.setElement(2, idhighbyte); 261 m.setElement(3, idmiddlebyte); 262 m.setElement(4, idlowbyte); 263 m.setElement(5, 0x0F); 264 m.setElement(6, 0x11); 265 m.setElement(7, 0xFF); 266 return m; 267 } 268 269 public static SpecificMessage getInsteonFunction(int idhighbyte, int idmiddlebyte, int idlowbyte, 270 int function, int flag, int cmd1, int cmd2) { 271 SpecificMessage m = new SpecificMessage(8); 272// m.setInterlocked(true); 273 m.setInterlocked(false); 274 m.setElement(0, Constants.HEAD_STX); 275 m.setElement(1, Constants.FUNCTION_REQ_STD); 276 m.setElement(2, idhighbyte); 277 m.setElement(3, idmiddlebyte); 278 m.setElement(4, idlowbyte); 279 m.setElement(5, flag); 280 m.setElement(6, cmd1); 281 m.setElement(7, cmd2); 282 return m; 283 } 284 285}