001package jmri.jmrix.lenz; 002 003import org.slf4j.Logger; 004import org.slf4j.LoggerFactory; 005 006/** 007 * Defines the standard/common routines used in multiple classes related to the 008 * a Lenz Command Station, on an XpressNet network. 009 * 010 * @author Bob Jacobsen Copyright (C) 2001 Portions by Paul Bender Copyright (C) 2003 011 */ 012public class LenzCommandStation implements jmri.CommandStation { 013 014 /* The First group of routines is for obtaining the Software and 015 hardware version of the Command station */ 016 017 /** 018 * We need to add a few data members for saving the version information we 019 * get from the layout. 020 */ 021 private int cmdStationType = -1; 022 private float cmdStationSoftwareVersion = -1; 023 private int cmdStationSoftwareVersionBCD = -1; 024 025 /** 026 * Return the CS Type. 027 * @return CS type. 028 */ 029 public int getCommandStationType() { 030 return cmdStationType; 031 } 032 033 /** 034 * Set the CS Type. 035 * @param t CS type. 036 */ 037 public void setCommandStationType(int t) { 038 cmdStationType = t; 039 } 040 041 /** 042 * Set the CS Type based on an XpressNet Message. 043 * @param l XNetReply containing the CS type. 044 */ 045 public void setCommandStationType(XNetReply l) { 046 if (l.getElement(0) == XNetConstants.CS_SERVICE_MODE_RESPONSE) { 047 // This is the Command Station Software Version Response 048 if (l.getElement(1) == XNetConstants.CS_SOFTWARE_VERSION) { 049 cmdStationType = l.getElement(3); 050 } 051 } 052 } 053 054 /** 055 * Get the CS Software Version. 056 * @return software version. 057 */ 058 public float getCommandStationSoftwareVersion() { 059 return cmdStationSoftwareVersion; 060 } 061 062 /** 063 * Get the CS Software Version in BCD (for use in comparisons). 064 * @return software version. 065 */ 066 public float getCommandStationSoftwareVersionBCD() { 067 return cmdStationSoftwareVersionBCD; 068 } 069 070 /** 071 * Set the CS Software Version. 072 * @param v software version. 073 */ 074 public void setCommandStationSoftwareVersion(float v) { 075 cmdStationSoftwareVersion = v; 076 } 077 078 /** 079 * Set the CS Software Version based on an XpressNet Message. 080 * @param l reply containing CS version. 081 */ 082 public void setCommandStationSoftwareVersion(XNetReply l) { 083 if (l.getElement(0) == XNetConstants.CS_SERVICE_MODE_RESPONSE) { 084 // This is the Command Station Software Version Response 085 if (l.getElement(1) == XNetConstants.CS_SOFTWARE_VERSION) { 086 try { 087 cmdStationSoftwareVersion = (l.getElementBCD(2).floatValue()) / 10; 088 } catch (java.lang.NumberFormatException nfe) { 089 // the number was not in BCD format as expected. 090 // the upper nibble is the major version and the lower 091 // nibble is the minor version. 092 cmdStationSoftwareVersion = ((l.getElement(2) & 0xf0) >> 4) + (l.getElement(2) & 0x0f) / 100.0f; 093 } 094 cmdStationSoftwareVersionBCD = l.getElement(2); 095 } 096 } 097 } 098 099 /** 100 * Provide the version string returned during the initial check. 101 * @return human readable version string. 102 */ 103 public String getVersionString() { 104 return Bundle.getMessage("CSVersionString", getCommandStationType(),getCommandStationSoftwareVersionBCD()); 105 } 106 107 /** 108 * XpressNet command station does provide Ops Mode. 109 * 110 * @return true if CS type 1 or 2, else false. 111 */ 112 public boolean isOpsModePossible() { 113 return cmdStationType != 0x01 && cmdStationType != 0x02; 114 } 115 116 // A few utility functions 117 118 /** 119 * Get the Lower byte of a locomotive address from the decimal locomotive 120 * address. 121 * @param address loco address. 122 * @return low address byte including DCC offset. 123 */ 124 public static int getDCCAddressLow(int address) { 125 /* For addresses below 100, we just return the address, otherwise, 126 we need to return the upper byte of the address after we add the 127 offset 0xC000. The first address used for addresses over 99 is 0xC064*/ 128 if (address < 100) { 129 return (address); 130 } else { 131 int temp = address + 0xC000; 132 temp = temp & 0x00FF; 133 return temp; 134 } 135 } 136 137 /** 138 * Get the Upper byte of a locomotive address from the decimal locomotive 139 * address. 140 * @param address loco address. 141 * @return upper byte after DCC offset. 142 */ 143 public static int getDCCAddressHigh(int address) { 144 /* this isn't actually the high byte, For addresses below 100, we 145 just return 0, otherwise, we need to return the upper byte of the 146 address after we add the offset 0xC000 The first address used for 147 addresses over 99 is 0xC064*/ 148 if (address < 100) { 149 return (0x00); 150 } else { 151 int temp = address + 0xC000; 152 temp = temp & 0xFF00; 153 temp = temp / 256; 154 return temp; 155 } 156 } 157 158 /** 159 * We need to calculate the locomotive address when doing the translations 160 * back to text. XpressNet Messages will have these as two elements, which 161 * need to get translated back into a single address by reversing the 162 * formulas used to calculate them in the first place. 163 * 164 * @param AH the high order byte of the address 165 * @param AL the low order byte of the address 166 * @return the address as an integer. 167 */ 168 static public int calcLocoAddress(int AH, int AL) { 169 if (AH == 0x00) { 170 /* if AH is 0, this is a short address */ 171 return (AL); 172 } 173 174 if ((AH & 0xC0) == 0x80) { 175 /* this is accessory address */ 176 if ((AL & 0x80) == 0x80) { 177 /* this is Basic accessory decoder */ 178 int address; 179 180 int part1 ; 181 part1 = (AH & 0x3F) ; 182 183 int part2 ; 184 part2 = ~AL ; 185 part2 = (part2 & 0x70) ; 186 part2 = part2 << 2 ; 187 188 address = part2 | part1 ; 189 return (address) ; 190 } else { 191 /* this is Extended accessory decoder */ 192 int address; 193 194 int part1 ; 195 part1 = (AH & 0x3F) ; 196 // part1 = part1 + 1 ; 197 part1 = part1 << 2 ; 198 199 int part2 ; 200 part2 = ~AL ; 201 part2 = (part2 & 0x70) ; 202 part2 = part2 << 4 ; 203 204 int part3 ; 205 part3 = AL ; 206 part3 = (part3 & 0x06) ; 207 part3 = part3 >> 1 ; 208 209 address = part2 | part1 | part3; 210 address = address + 1; 211 212 return (address) ; 213 } 214 } 215 216 /* This must be a long locomotive address */ 217 int address; 218 address = ((AH * 256) & 0xFF00); 219 address += (AL & 0xFF); 220 address -= 0xC000; 221 return (address); 222 } 223 224 /* To Implement the CommandStation Interface, we have to define the 225 sendPacket function */ 226 227 /** 228 * Send a specific packet to the rails. 229 * 230 * @param packet Byte array representing the packet, including the 231 * error-correction byte. Must not be null. 232 * @param repeats Number of times to repeat the transmission. 233 */ 234 @Override 235 public boolean sendPacket(byte[] packet, int repeats) { 236 237 if (_tc == null) { 238 log.error("Send Packet Called without setting traffic controller"); 239 return false; 240 } 241 242 XNetMessage msg = XNetMessage.getNMRAXNetMsg(packet); 243 for (int i = 0; i < repeats; i++) { 244 _tc.sendXNetMessage(msg, null); 245 } 246 return true; 247 } 248 249 /* 250 * For the command station interface, we need to set the traffic 251 * controller. 252 */ 253 public void setTrafficController(XNetTrafficController tc) { 254 _tc = tc; 255 } 256 257 private XNetTrafficController _tc = null; 258 259 public void setSystemConnectionMemo(XNetSystemConnectionMemo memo) { 260 adaptermemo = memo; 261 } 262 263 XNetSystemConnectionMemo adaptermemo; 264 265 @Override 266 public String getUserName() { 267 if (adaptermemo == null) { 268 return Bundle.getMessage("MenuXpressNet"); 269 } 270 return adaptermemo.getUserName(); 271 } 272 273 @Override 274 public String getSystemPrefix() { 275 if (adaptermemo == null) { 276 return "X"; 277 } 278 return adaptermemo.getSystemPrefix(); 279 } 280 281 /* 282 * Register for logging. 283 */ 284 private static final Logger log = LoggerFactory.getLogger(LenzCommandStation.class); 285 286}