001package jmri.jmrix.loconet; 002 003import org.slf4j.Logger; 004import org.slf4j.LoggerFactory; 005 006/** 007 * Utilities for handling LocoNet sensor addresses. 008 * <p> 009 * There are three addressing spaces for LocoNet sensors: 010 * <ul> 011 * <li>The space used for DS54 inputs, where the least-significant-bit in the 012 * address refers to the "Aux" and "Switch" inputs. These are represented by 013 * system names of the form LSnnnA and LSnnnS respectively. nnn is then the 014 * turnout number of the DS54 channel. 015 * <li>The space used for BDL16 inputs, where the card and section numbers are 016 * part of the address. These are represented by names of the form LScccA1 017 * through LScccA4, LScccB1 through LScccB4, and on through LScccD4. ccc is the 018 * BDL16 card number. 019 * <li>A straight-forward numeric space, represented by LSmmm. Note that this is 020 * a 1-4096 scheme, not a 0-4095. 021 * </ul> 022 * Some of the message formats used in this class are Copyright Digitrax, Inc. 023 * and used with permission as part of the JMRI project. That permission does 024 * not extend to uses in other software products. If you wish to use this code, 025 * algorithm or these message formats outside of JMRI, please contact Digitrax 026 * Inc for separate permission. 027 * 028 * @author Bob Jacobsen Copyright (C) 2001, 2002 029 */ 030public class LnSensorAddress { 031 032 int _low; 033 int _high; 034 int _as; 035 String prefix; 036 037 boolean _valid; 038 039 public LnSensorAddress(int sw1, int sw2, String prefix) { 040 _as = sw2 & 0x20; // should be a LocoNet constant? 041 _high = sw2 & 0x0F; 042 _low = sw1 & 0x7F; 043 _valid = true; 044 this.prefix = prefix; 045 } 046 047 public LnSensorAddress(String s, String prefix) { 048 _valid = false; 049 this.prefix = prefix; 050 051 // check valid 052 if (s.startsWith(prefix + "S")) { 053 // parse out and decode the name 054 if (s.charAt(s.length() - 1) == 'A') { 055 // DS54 addressing, Aux input 056 _as = 0x20; 057 int n = Integer.parseInt(s.substring(prefix.length() + 1, s.length() - 1)); 058 _high = n / 128; 059 _low = n & 0x7F; 060 _valid = true; 061 } else if (s.charAt(s.length() - 1) == 'S') { 062 // DS54 addressing, Switch input 063 _as = 0x00; 064 int n = Integer.parseInt(s.substring(prefix.length() + 1, s.length() - 1)); 065 _high = n / 128; 066 _low = n & 0x7F; 067 _valid = true; 068 } else { 069 // BDL16? 070 char c = s.charAt(s.length() - 2); 071 if (c >= 'A' && c <= 'D') { 072 // BDL16 addressing 073 int d = 0; 074 switch (c) { 075 case 'A': 076 d = 0; 077 break; 078 case 'B': 079 d = 1; 080 break; 081 case 'C': 082 d = 2; 083 break; 084 case 'D': 085 d = 3; 086 break; 087 default: 088 log.warn("Unhandled addr code: {}", c); 089 break; 090 } 091 int n = Integer.parseInt(s.substring(prefix.length() + 1, s.length() - 2)) * 16 + d * 4 092 + Integer.parseInt(s.substring(s.length() - 1, s.length())); 093 _high = n / 128; 094 _low = (n & 0x7F) / 2; 095 _as = (n & 0x01) * 0x20; 096 _valid = true; 097 } else { 098 // assume that its LSnnn style 099 int n = Integer.parseInt(s.substring(prefix.length() + 1, s.length())) - 1; 100 _high = n / 256; 101 _low = (n & 0xFE) / 2; 102 _as = (n & 0x01) * 0x20; 103 _valid = true; 104 } 105 } 106 } else { 107 // didn't find a leading LS, complain 108 reportParseError(s); 109 } 110 } 111 112 void reportParseError(String s) { 113 log.error("Can't parse sensor address string: {}", s); 114 } 115 116 /** 117 * Update a LocoNet message to have this address. 118 * 119 * It is assumed that the sensor address may be encoded into bytes 1 and 2 of the 120 * message. 121 * 122 * @param m a LocoNetmessage to be updated to contain this object's sensor address 123 */ 124 public void insertAddress(LocoNetMessage m) { 125 m.setElement(1, getLowBits()); 126 m.setElement(2, getHighBits() | getASBit()); 127 } 128 129 // convenient calculations 130 public boolean matchAddress(int a1, int a2) { // a1 is byte 1 of ln msg, a2 is byte 2 131 if (getHighBits() != (a2 & 0x0f)) { 132 return false; 133 } 134 if (getLowBits() != (a1 & 0x7f)) { 135 return false; 136 } 137 if (getASBit() != (a2 & 0x20)) { 138 return false; 139 } 140 return true; 141 } 142 143 /** 144 * @return integer value of this address in 0-4095 space 145 */ 146 protected int asInt() { 147 return _high * 256 + _low * 2 + (_as != 0 ? 1 : 0); 148 } 149 150 // accessors for parsed data 151 public int getLowBits() { 152 return _low; 153 } 154 155 public int getHighBits() { 156 return _high; 157 } 158 159 /** 160 * The bit representing the Aux or Sensor input 161 * 162 * @return 0x20 for aux input, 0x00 for switch input 163 */ 164 public int getASBit() { 165 return _as; 166 } 167 168 public boolean isValid() { 169 return _valid; 170 } 171 172 @Override 173 public String toString() { 174 return getNumericAddress() + ":" 175 + getDS54Address() + ":" 176 + getBDL16Address(); 177 } 178 179 /** 180 * Name in the 1-4096 space 181 * 182 * @return LSnnn 183 */ 184 public String getNumericAddress() { 185 return prefix + "S" + (asInt() + 1); 186 } 187 188 /** 189 * Name in the DS54 space 190 * 191 * @return LSnnnA or LSnnnS, depending on Aux or Switch input 192 */ 193 public String getDS54Address() { 194 if (_as != 0) { 195 return prefix + "S" + (_high * 128 + _low) + "A"; 196 } else { 197 return prefix + "S" + (_high * 128 + _low) + "S"; 198 } 199 } 200 201 /** 202 * Name in the BDL16 space 203 * 204 * @return e.g. LSnnnA3, with nnn the BDL16 number, A the section number, 205 * and 3 the channel number 206 */ 207 public String getBDL16Address() { 208 String letter = null; 209 String digit = null; 210 211 switch (asInt() & 0x03) { 212 case 0: 213 digit = "0"; 214 break; 215 case 1: 216 digit = "1"; 217 break; 218 case 2: 219 digit = "2"; 220 break; 221 case 3: 222 digit = "3"; 223 break; 224 default: 225 digit = "X"; 226 log.error("Unexpected digit value: {}", asInt()); 227 } 228 switch ((asInt() & 0x0C) / 4) { 229 case 0: 230 letter = "A"; 231 break; 232 case 1: 233 letter = "B"; 234 break; 235 case 2: 236 letter = "C"; 237 break; 238 case 3: 239 letter = "D"; 240 break; 241 default: 242 letter = "X"; 243 log.error("Unexpected letter value: {}", asInt()); 244 } 245 return prefix + "S" + (asInt() / 16) + letter + digit; 246 } 247 248 private final static Logger log = LoggerFactory.getLogger(LnSensorAddress.class); 249 250}