001package jmri.jmrix.powerline.cm11; 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 020 */ 021public class SpecificMessage extends SerialMessage { 022 // is this logically an abstract class? 023 024 public SpecificMessage(int l) { 025 super(l); 026 setResponseLength(0); // only polls require a response 027 setBinary(true); 028 setTimeout(5000); 029 } 030 031 /** 032 * This ctor interprets the String as the exact sequence to send, 033 * byte-for-byte. 034 * 035 * @param m message 036 * @param l response length in bytes 037 */ 038 public SpecificMessage(String m, int l) { 039 super(m, l); 040 } 041 042 boolean interlocked = false; 043 044 @Override 045 public void setInterlocked(boolean v) { 046 interlocked = v; 047 } 048 049 @Override 050 public boolean getInterlocked() { 051 return interlocked; 052 } 053 054 @SuppressWarnings("fallthrough") 055 @Override 056 public String toMonitorString() { 057 // check for valid length 058 int len = getNumDataElements(); 059 StringBuilder text = new StringBuilder(); 060 switch (getElement(0) & 0xFF) { 061 case Constants.MACRO_LOAD: 062 text.append("Macro load reply"); 063 break; 064 case Constants.MACRO_INITIATED: 065 text.append("Macro Poll"); 066 break; 067 case Constants.TIME_REQ_CP11: 068 text.append("Power Fail Poll"); 069 break; 070 case Constants.TIMER_DOWNLOAD: 071 text.append("Set CM11 time"); 072 break; 073 case Constants.EXT_CMD_HEADER: // extended command 074 text.append("Extended Cmd"); 075 if (len == 5) { 076 text.append(" house "); 077 text.append(X10Sequence.houseValueToText(X10Sequence.decode((getElement(1) >> 4) & 0x0F))); 078 text.append(" address device "); 079 text.append(X10Sequence.decode(getElement(2) & 0x0F)); 080 int d = getElement(3) & 0xFF; 081 switch (getElement(4) & 0xFF) { 082 case X10Sequence.EXTCMD_DIM: 083 text.append(" Direct Dim: "); 084 if ((d & 0x3F) <= 0x3E) { 085 text.append(((d & 0x3F) / 0.63) + "%"); 086 } else if (d == 0x3F) { 087 text.append("Full On"); 088 } else { 089 text.append(" data: 0x"); 090 text.append(StringUtil.twoHexFromInt(d)); 091 } 092// switch ((d >> 6) & 0x03) { 093// case 0: 094// text.append(" 3.7 Sec"); 095// break; 096// case 1: 097// text.append(" 30 Sec"); 098// break; 099// case 2: 100// text.append(" 1 Min"); 101// break; 102// case 3: 103// text.append(" 5 Min"); 104// break; 105// } 106 break; 107 default: 108 text.append(" cmd: 0x"); 109 text.append(StringUtil.twoHexFromInt(getElement(4) & 0xFF)); 110 text.append(" data: 0x"); 111 text.append(StringUtil.twoHexFromInt(getElement(3) & 0xFF)); 112 } 113 } else { 114 text.append(" wrong length: " + len); 115 } 116 break; 117 case Constants.POLL_ACK: 118 if (len == 1) { 119 text.append("Poll Ack"); 120 break; 121 } // else fall through 122 case Constants.CHECKSUM_OK: 123 if (len == 1) { 124 text.append("OK for transmission"); 125 break; 126 } // else fall through 127 default: { 128 if (len == 2) { 129 text.append(Constants.formatHeaderByte(getElement(0 & 0xFF))); 130 if ((getElement(0) & 0x02) == 0x02) { 131 text.append(" "); 132 text.append(X10Sequence.formatCommandByte(getElement(1) & 0xFF)); 133 } else { 134 text.append(" "); 135 text.append(X10Sequence.formatAddressByte(getElement(1) & 0xFF)); 136 } 137 } else { 138 text.append("Reply was not expected, len: " + len); 139 text.append(" value: " + Constants.formatHeaderByte(getElement(0 & 0xFF))); 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 lenght of 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 @Override 170 public boolean isPoll() { 171 return getElement(1) == 48; 172 } 173 174 @Override 175 public boolean isXmt() { 176 return getElement(1) == 17; 177 } 178 179 @Override 180 public int getAddr() { 181 return getElement(0); 182 } 183 184 // static methods to return a formatted message 185 static public SerialMessage getPoll(int addr) { 186 // eventually this will have to include logic for reading 187 // various bytes on the card, but our supported 188 // cards don't require that yet 189 // SerialMessage m = new SerialMessage(1); 190 // m.setResponseLength(2); 191 // m.setElement(0, addr); 192 // m.setTimeout(SHORT_TIMEOUT); // minumum reasonable timeout 193 194 // Powerline implementation does not currently poll 195 return null; 196 } 197 198 static public SpecificMessage setCM11Time(int housecode) { 199 SpecificMessage msg = new SpecificMessage(7); 200 msg.setElement(0, 0x9B); 201 msg.setElement(5, 0x01); 202 msg.setElement(6, housecode << 4); 203 return msg; 204 } 205 206 static public SpecificMessage getAddress(int housecode, int devicecode) { 207 SpecificMessage m = new SpecificMessage(2); 208 m.setInterlocked(true); 209 m.setElement(0, 0x04); 210 m.setElement(1, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode)); 211 return m; 212 } 213 214 static public SpecificMessage getAddressDim(int housecode, int devicecode, int dimcode) { 215 SpecificMessage m = new SpecificMessage(2); 216 m.setInterlocked(true); 217 if (dimcode > 0) { 218 m.setElement(0, 0x04 | ((dimcode & 0x1f) << 3)); 219 } else { 220 m.setElement(0, 0x04); 221 } 222 m.setElement(1, (X10Sequence.encode(housecode) << 4) + X10Sequence.encode(devicecode)); 223 return m; 224 } 225 226 static public SpecificMessage getFunctionDim(int housecode, int function, int dimcode) { 227 SpecificMessage m = new SpecificMessage(2); 228 m.setInterlocked(true); 229 if (dimcode > 0) { 230 m.setElement(0, 0x06 | ((dimcode & 0x1f) << 3)); 231 } else { 232 m.setElement(0, 0x06); 233 } 234 m.setElement(1, (X10Sequence.encode(housecode) << 4) + function); 235 return m; 236 } 237 238 static public SpecificMessage getFunction(int housecode, int function) { 239 SpecificMessage m = new SpecificMessage(2); 240 m.setInterlocked(true); 241 m.setElement(0, 0x06); 242 m.setElement(1, (X10Sequence.encode(housecode) << 4) + function); 243 return m; 244 } 245 246 static public SpecificMessage getExtCmd(int housecode, int devicecode, int function, int dimcode) { 247 SpecificMessage m = new SpecificMessage(5); 248 m.setInterlocked(true); 249 m.setElement(0, 0x07); 250 m.setElement(1, (X10Sequence.encode(housecode) << 4) + X10Sequence.FUNCTION_EXTENDED_CODE); 251 m.setElement(2, X10Sequence.encode(devicecode)); 252 m.setElement(3, dimcode); 253 m.setElement(4, function); 254 return m; 255 } 256} 257 258