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