001package jmri.jmrix.powerline; 002 003/** 004 * Represent a sequence of one or more X10 commands (addresses and functions). 005 * <p> 006 * These are X10 specific, but not device/interface specific. 007 * <p> 008 * A sequence should consist of addressing (1 or more), and then one or more 009 * commands. It can address multiple devices, but not more than one house-code. 010 * <p> 011 * House codes and devices within this class are sequential numbers (1-16 for 012 * house code, 1-16 for device code). These must be translated to line coding by 013 * other code that converts the sequence to adapter-specific messages. The 014 * {@link #encode} and {@link #decode} functions are provided to make that 015 * easier by converting to and from the standard line-code sequences, but you 016 * should check the coding of your new specific adapter before using them. 017 * 018 * @author Bob Jacobsen Copyright (C) 2008 019 */ 020public class X10Sequence { 021 022 public static final int FUNCTION_ALL_UNITS_OFF = 0; 023 public static final int FUNCTION_ALL_LIGHTS_ON = 1; 024 public static final int FUNCTION_ON = 2; 025 public static final int FUNCTION_OFF = 3; 026 public static final int FUNCTION_DIM = 4; 027 public static final int FUNCTION_BRIGHT = 5; 028 public static final int FUNCTION_ALL_LIGHTS_OFF = 6; 029 public static final int FUNCTION_EXTENDED_CODE = 7; 030 public static final int FUNCTION_HAIL_REQUEST = 8; 031 public static final int FUNCTION_HAIL_ACKNOWLEDGE = 9; 032 public static final int FUNCTION_PRESET_DIM_1 = 10; 033 public static final int FUNCTION_PRESET_DIM_2 = 11; 034 public static final int FUNCTION_EXTENDED_DATA_TRANSFER = 12; 035 public static final int FUNCTION_STATUS_ON = 13; 036 public static final int FUNCTION_STATUS_OFF = 14; 037 public static final int FUNCTION_STATUS_REQUEST = 15; 038 039 public static final int EXTCMD_DIM = 0x31; 040 041 // First implementation of this class uses a fixed length 042 // array to hold the sequence; there's a practical limit to how 043 // many X10 commands anybody would want to send at once! 044 private static final int MAXINDEX = 32; 045 int index = 0; 046 Command[] cmds = new Command[MAXINDEX]; // doesn't scale, but that's for another day 047 048 /** 049 * Append a new "do function" operation to the sequence 050 * @param house house code 051 * @param function function 052 * @param dimcount dimming step count 053 */ 054 public void addFunction(int house, int function, int dimcount) { 055 if (index >= MAXINDEX) { 056 throw new IllegalArgumentException("Sequence too long"); 057 } 058 cmds[index] = new Function(house, function, dimcount); 059 index++; 060 } 061 062 /** 063 * Append a new "set address" operation to the sequence 064 * @param house house code A-P 065 * @param device device 1-16 066 */ 067 public void addAddress(int house, int device) { 068 if (index >= MAXINDEX) { 069 throw new IllegalArgumentException("Sequence too long"); 070 } 071 cmds[index] = new Address(house, device); 072 index++; 073 } 074 075 /** 076 * Append a new "do function" operation to the sequence 077 * @param house A-P 078 * @param device 1-16 079 * @param cmd command code 080 * @param data additional data 081 */ 082 public void addExtData(int house, int device, int cmd, int data) { 083 if (index >= MAXINDEX) { 084 throw new IllegalArgumentException("Sequence too long"); 085 } 086 cmds[index] = new ExtData(house, device, cmd, data); 087 index++; 088 } 089 090 /** 091 * Next getCommand will be the first in the sequence 092 */ 093 public void reset() { 094 index = 0; 095 } 096 097 /** 098 * Retrieve the next command in the sequence 099 * @return next available command 100 */ 101 public Command getCommand() { 102 return cmds[index++]; 103 } 104 105 /** 106 * Represent a single X10 command, which is either a "set address" or "do 107 * function" operation 108 */ 109 public interface Command { 110 111 public boolean isAddress(); 112 113 public boolean isFunction(); 114 115 public int getHouseCode(); 116 } 117 118 /** 119 * Represent a single "set address" X10 command 120 */ 121 public static class Address implements Command { 122 123 public Address(int house, int device) { 124 this.house = house; 125 this.device = device; 126 } 127 int house; 128 int device; 129 130 @Override 131 public int getHouseCode() { 132 return house; 133 } 134 135 public int getAddress() { 136 return device; 137 } 138 139 @Override 140 public boolean isAddress() { 141 return true; 142 } 143 144 @Override 145 public boolean isFunction() { 146 return false; 147 } 148 } 149 150 /** 151 * Represent a single "do function" X10 command 152 */ 153 public static class Function implements Command { 154 155 public Function(int house, int function, int dimcount) { 156 this.house = house; 157 this.function = function; 158 this.dimcount = dimcount; 159 } 160 int house; 161 int function; 162 int dimcount; 163 164 @Override 165 public int getHouseCode() { 166 return house; 167 } 168 169 public int getFunction() { 170 return function; 171 } 172 173 public int getDimCount() { 174 return dimcount; 175 } 176 177 @Override 178 public boolean isAddress() { 179 return false; 180 } 181 182 @Override 183 public boolean isFunction() { 184 return true; 185 } 186 } 187 188 /** 189 * Represent a single "Extended Data" X10 command 190 */ 191 public static class ExtData implements Command { 192 193 public ExtData(int house, int device, int cmd, int data) { 194 this.house = house; 195 this.device = device; 196 this.cmd = cmd; 197 this.data = data; 198 } 199 int house; 200 int device; 201 int cmd; 202 int data; 203 204 public int getExtData() { 205 return data; 206 } 207 208 public int getExtCmd() { 209 return cmd; 210 } 211 212 @Override 213 public int getHouseCode() { 214 return house; 215 } 216 217 public int getAddress() { 218 return device; 219 } 220 221 @Override 222 public boolean isAddress() { 223 return false; 224 } 225 226 @Override 227 public boolean isFunction() { 228 return false; 229 } 230 } 231 232 /** 233 * Array of human readable names for X10 commands, indexed by the command 234 * numbers that are constants in this class. 235 */ 236 static String[] functionNames = new String[]{ 237 "All Off", "All Lights On", "On", "Off", 238 "Dim", "Bright", "All Lights Off", "Extended Code", 239 "Hail Request", "Hail Ack", "Preset Dim 1", "Preset Dim 2", 240 "Ext Data Trnsfr", "Status On", "Status Off", "Status Req" 241 }; 242 243 /** 244 * Return a human-readable name for a function code 245 * @param i value of function code 246 * @return string translation 247 */ 248 public static String functionName(int i) { 249 return functionNames[i]; 250 } 251 252 /** 253 * For the house (A-P) and device (1-16) codes, get the line-coded value. 254 * Argument is from 1 to 16 only. 255 * @param i house or device code value 256 * @return line code value 257 */ 258 public static int encode(int i) { 259 if (i < 1 || i > 16) { 260 throw new IllegalArgumentException("Encode outside 1-16: " + i); 261 } 262 return encoder[i]; 263 } 264 static final int[] encoder = new int[]{-1, 265 0x6, 0xE, 0x2, 0xA, 0x1, 0x9, 0x5, 0xD, // 1-8 266 0x7, 0xF, 0x3, 0xB, 0x0, 0x8, 0x4, 0xC}; 267 268 /** 269 * Get house (A-P as 1-16) or device (1-16) from line-coded value. 270 * @param i line code value 271 * @return house or device code value 272 */ 273 public static int decode(int i) { 274 if (i < 0 || i > 15) { 275 throw new IllegalArgumentException("Decode outside 1-16: " + i); 276 } 277 return decoder[i]; 278 } 279 static final int[] decoder = new int[]{13, 280 5, 3, 11, 15, 7, 1, 9, 14, // 1-8 281 6, 4, 12, 16, 8, 2, 10}; // 9-15 282 283 /** 284 * Pretty-print an address code 285 * @param b address code value 286 * @return human string form 287 */ 288 public static String formatAddressByte(int b) { 289 return "House " + X10Sequence.houseValueToText(X10Sequence.decode((b >> 4) & 0x0F)) 290 + " address device " + X10Sequence.decode(b & 0x0f); 291 } 292 293 /** 294 * Pretty-print a function code 295 * @param b command code value 296 * @return human string form 297 */ 298 public static String formatCommandByte(int b) { 299 return "House " + X10Sequence.houseValueToText(X10Sequence.decode((b >> 4) & 0x0F)) 300 + " function: " + X10Sequence.functionName(b & 0x0f); 301 } 302 303 /** 304 * Translate House Value (1 to 16) to text 305 * @param hV device code value 306 * @return human string form 307 */ 308 public static String houseValueToText(int hV) { 309 if (hV >= 1 && hV <= 16) { 310 return houseValueDecoder[hV]; 311 } else { 312 return "??"; 313 } 314 } 315 static String[] houseValueDecoder = new String[]{"??", 316 "A", "B", "C", "D", "E", "F", "G", "H", 317 "I", "J", "K", "L", "M", "N", "O", "P"}; 318 319 /** 320 * Translate House Code to text 321 * @param hC house code 322 * @return A-P 323 */ 324 public static String houseCodeToText(int hC) { 325 String hCode = ""; 326 switch (hC) { 327 case 0x06: 328 hCode = "A"; 329 break; 330 case 0x0E: 331 hCode = "B"; 332 break; 333 case 0x02: 334 hCode = "C"; 335 break; 336 case 0x0A: 337 hCode = "D"; 338 break; 339 case 0x01: 340 hCode = "E"; 341 break; 342 case 0x09: 343 hCode = "F"; 344 break; 345 case 0x05: 346 hCode = "G"; 347 break; 348 case 0x0D: 349 hCode = "H"; 350 break; 351 case 0x07: 352 hCode = "I"; 353 break; 354 case 0x0F: 355 hCode = "J"; 356 break; 357 case 0x03: 358 hCode = "K"; 359 break; 360 case 0x0B: 361 hCode = "L"; 362 break; 363 case 0x00: 364 hCode = "M"; 365 break; 366 case 0x08: 367 hCode = "N"; 368 break; 369 case 0x04: 370 hCode = "O"; 371 break; 372 case 0x0C: 373 hCode = "P"; 374 break; 375 default: 376 hCode = "Unk hC:" + hC; 377 break; 378 } 379 return hCode; 380 } 381}