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 public SpecificMessage(byte[] a, int l) { 153 super(a, l); 154 } 155 156 int responseLength = -1; // -1 is an invalid value, indicating it hasn't been set 157 158 @Override 159 public void setResponseLength(int l) { 160 responseLength = l; 161 } 162 163 @Override 164 public int getResponseLength() { 165 return responseLength; 166 } 167 168 // static methods to recognize a message 169// public boolean isPoll() { return getElement(1)==48;} 170// public boolean isXmt() { return getElement(1)==17;} 171// public int getAddr() { return getElement(0); } 172 // static methods to return a formatted message 173 public static SerialMessage getPoll(int addr) { 174 // Powerline implementation does not currently poll 175 return null; 176 } 177 178 /** 179 * create an Insteon message with the X10 address 180 * @param housecode value of the housecode of X10 address 181 * @param devicecode value of the devicecode of X10 address 182 * 183 * @return message formated message with parameters 184 */ 185 public static SpecificMessage getX10Address(int housecode, int devicecode) { 186 SpecificMessage m = new SpecificMessage(4); 187 m.setInterlocked(false); 188 m.setElement(0, Constants.HEAD_STX); 189 m.setElement(1, Constants.FUNCTION_REQ_X10); 190 m.setElement(2, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode)); 191 m.setElement(3, 0x00); // 0x00 Means address 192 return m; 193 } 194 195 /** 196 * create an Insteon message with the X10 address and dim steps 197 * 198 * @param housecode value of the housecode of X10 address 199 * @param devicecode value of the devicecode of X10 address 200 * @param dimcode value of how dim to set the light 201 * 202 * @return message formated message with parameters 203 */ 204 public static SpecificMessage getX10AddressDim(int housecode, int devicecode, int dimcode) { 205 SpecificMessage m = new SpecificMessage(4); 206 m.setInterlocked(false); 207 m.setElement(0, Constants.HEAD_STX); 208 m.setElement(1, Constants.FUNCTION_REQ_X10); 209 if (dimcode > 0) { 210 m.setElement(2, 0x04 | ((dimcode & 0x1f) << 3)); 211 } else { 212 m.setElement(2, 0x04); 213 } 214 m.setElement(3, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode)); 215 m.setElement(3, 0x80); // 0x00 Means address 216 return m; 217 } 218 219 public static SpecificMessage getX10FunctionDim(int housecode, int function, int dimcode) { 220 SpecificMessage m = new SpecificMessage(2); 221 m.setInterlocked(true); 222 if (dimcode > 0) { 223 m.setElement(0, 0x06 | ((dimcode & 0x1f) << 3)); 224 } else { 225 m.setElement(0, 0x06); 226 } 227 m.setElement(1, (X10Sequence.encode(housecode) << 4) + function); 228 return m; 229 } 230 231 public static SpecificMessage getX10Function(int housecode, int function) { 232 SpecificMessage m = new SpecificMessage(4); 233// m.setInterlocked(true); 234 m.setInterlocked(false); 235 m.setElement(0, Constants.HEAD_STX); 236 m.setElement(1, Constants.FUNCTION_REQ_X10); 237 m.setElement(2, (X10Sequence.encode(housecode) << 4) + function); 238 m.setElement(3, 0x80); // 0x80 means function 239 return m; 240 } 241 242 public static SpecificMessage getExtCmd(int housecode, int devicecode, int function, int dimcode) { 243 SpecificMessage m = new SpecificMessage(5); 244 m.setInterlocked(true); 245 m.setElement(0, 0x07); 246 m.setElement(1, (X10Sequence.encode(housecode) << 4) + X10Sequence.FUNCTION_EXTENDED_CODE); 247 m.setElement(2, X10Sequence.encode(devicecode)); 248 m.setElement(3, dimcode); 249 m.setElement(4, function); 250 return m; 251 } 252 253 public static SpecificMessage getInsteonAddress(int idhighbyte, int idmiddlebyte, int idlowbyte) { 254 SpecificMessage m = new SpecificMessage(8); 255// m.setInterlocked(true); 256 m.setInterlocked(false); 257 m.setElement(0, Constants.HEAD_STX); 258 m.setElement(1, Constants.FUNCTION_REQ_STD); 259 m.setElement(2, idhighbyte); 260 m.setElement(3, idmiddlebyte); 261 m.setElement(4, idlowbyte); 262 m.setElement(5, 0x0F); 263 m.setElement(6, 0x11); 264 m.setElement(7, 0xFF); 265 return m; 266 } 267 268 public static SpecificMessage getInsteonFunction(int idhighbyte, int idmiddlebyte, int idlowbyte, 269 int function, int flag, int cmd1, int cmd2) { 270 SpecificMessage m = new SpecificMessage(8); 271// m.setInterlocked(true); 272 m.setInterlocked(false); 273 m.setElement(0, Constants.HEAD_STX); 274 m.setElement(1, Constants.FUNCTION_REQ_STD); 275 m.setElement(2, idhighbyte); 276 m.setElement(3, idmiddlebyte); 277 m.setElement(4, idlowbyte); 278 m.setElement(5, flag); 279 m.setElement(6, cmd1); 280 m.setElement(7, cmd2); 281 return m; 282 } 283 284}