001package jmri.jmrix.lenz; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004import jmri.*; 005import jmri.jmrix.roco.RocoXNetThrottleManager; 006import org.slf4j.Logger; 007import org.slf4j.LoggerFactory; 008 009import java.awt.event.ActionEvent; 010import java.lang.reflect.Constructor; 011import java.lang.reflect.InvocationTargetException; 012 013/** 014 * This class performs Command Station dependent initialization for XpressNet. 015 * It adds the appropriate Managers via the Initialization Manager based on the 016 * Command Station Type. 017 * 018 * @author Paul Bender Copyright (C) 2003-2010,2020 019 * @author Giorgio Terdina Copyright (C) 2007 020 */ 021public class XNetInitializationManager { 022 023 public XNetInitializationManager() { 024 } 025 026 private XNetSystemConnectionMemo systemMemo; 027 private Class<? extends XNetPowerManager> powerManagerClass; 028 private Class<? extends XNetThrottleManager> throttleManagerClass; 029 private Class<? extends RocoXNetThrottleManager> rocoThrottleManagerClass; 030 private Class<? extends XNetProgrammerManager> programmerManagerClass; 031 private Class<? extends XNetProgrammer> programmerClass; 032 private Class<? extends XNetConsistManager> consistManagerClass; 033 private Class<? extends XNetTurnoutManager> turnoutManagerClass; 034 private Class<? extends XNetLightManager> lightManagerClass; 035 private Class<? extends XNetSensorManager> sensorManagerClass; 036 private boolean versionCheck = false; 037 private boolean noCommandStation = false; 038 private int initTimeout = 30000; 039 040 /** 041 * Set the version check flag to true. 042 * @return this initializer 043 */ 044 public XNetInitializationManager versionCheck(){ 045 versionCheck = true; 046 return this; 047 } 048 049 /** 050 * Set the initialization timeout 051 * @param timeout value in ms. 052 * @return this initializer. 053 */ 054 public XNetInitializationManager setTimeout(int timeout){ 055 initTimeout = timeout; 056 return this; 057 } 058 059 /** 060 * Set the memo to initialize 061 * @param systemMemo the memo 062 * @return this initializer 063 */ 064 public XNetInitializationManager memo(XNetSystemConnectionMemo systemMemo){ 065 this.systemMemo = systemMemo; 066 return this; 067 } 068 069 /** 070 * Set the defaults to the default classes in jmri.jmrix.lenz. 071 * <p> 072 * This methods sets the default values for Lenz command stations 073 * and the Roco MultiMaus and LokMaus. Use with {@link #versionCheck} 074 * and {@link #setTimeout} to automatically configure these systems. 075 * </p> 076 * @return this initializer 077 */ 078 public XNetInitializationManager setDefaults(){ 079 powerManagerClass = XNetPowerManager.class; 080 throttleManagerClass = XNetThrottleManager.class; 081 rocoThrottleManagerClass = RocoXNetThrottleManager.class; 082 programmerManagerClass = XNetProgrammerManager.class; 083 programmerClass = XNetProgrammer.class; 084 consistManagerClass = XNetConsistManager.class; 085 turnoutManagerClass = XNetTurnoutManager.class; 086 lightManagerClass = XNetLightManager.class; 087 sensorManagerClass = XNetSensorManager.class; 088 return this; 089 } 090 091 /** 092 * Set the power Manager class 093 * @param powerManagerClass the power manager class to use 094 * @return this initializer 095 */ 096 public XNetInitializationManager powerManager(Class<? extends XNetPowerManager> powerManagerClass){ 097 this.powerManagerClass = powerManagerClass; 098 return this; 099 } 100 101 private void initPowerManager(){ 102 if(powerManagerClass != null){ 103 try { 104 Constructor<? extends XNetPowerManager> ctor = powerManagerClass.getConstructor(XNetSystemConnectionMemo.class); 105 XNetPowerManager pm = ctor.newInstance(systemMemo); 106 systemMemo.setPowerManager(pm); 107 InstanceManager.store(pm,PowerManager.class); 108 } catch (NoSuchMethodException | InstantiationException | 109 IllegalAccessException | InvocationTargetException e){ 110 log.warn("Unable to construct power manager for XPressNet connection {}", systemMemo.getSystemPrefix(),e); 111 } 112 } 113 } 114 115 /** 116 * Set the Programmer class to use with the XNetProgrammerManager. 117 * @param programmerClass the programmer class to use 118 * @return this initializer. 119 */ 120 public XNetInitializationManager programmer(Class<? extends XNetProgrammer> programmerClass){ 121 this.programmerClass = programmerClass; 122 return this; 123 } 124 125 private XNetProgrammer initProgrammer(){ 126 XNetProgrammer prog = null; 127 if(programmerClass != null){ 128 try { 129 Constructor<? extends XNetProgrammer> ctor = programmerClass.getConstructor(XNetTrafficController.class); 130 prog = ctor.newInstance(systemMemo.getXNetTrafficController()); 131 } catch (NoSuchMethodException | InstantiationException | 132 IllegalAccessException | InvocationTargetException e){ 133 log.warn("Unable to construct programmer for XPressNet connection {}", systemMemo.getSystemPrefix(),e); 134 } 135 } 136 return prog; 137 } 138 139 public XNetInitializationManager noCommandStation(){ 140 this.noCommandStation = true; 141 return this; 142 } 143 144 private void initCommandStation(){ 145 if(!noCommandStation) { 146 /* The "raw" Command Station only works on systems that support Ops Mode Programming */ 147 systemMemo.setCommandStation(systemMemo.getXNetTrafficController().getCommandStation()); 148 if(systemMemo.getCommandStation()!= null) { 149 InstanceManager.store(systemMemo.getCommandStation(), jmri.CommandStation.class); 150 } 151 } 152 } 153 154 /** 155 * Set the programmer manager to initialize 156 * @param programmerManagerClass the programmer class to use. 157 * @return this initializer. 158 */ 159 public XNetInitializationManager programmerManager(Class<? extends XNetProgrammerManager> programmerManagerClass){ 160 this.programmerManagerClass = programmerManagerClass; 161 return this; 162 } 163 164 private void initProgrammerManager() { 165 XNetProgrammer programmer = initProgrammer(); 166 if (programmerManagerClass != null && programmer != null) { 167 try { 168 Constructor<? extends XNetProgrammerManager> ctor = programmerManagerClass.getConstructor(Programmer.class, XNetSystemConnectionMemo.class); 169 XNetProgrammerManager pm = ctor.newInstance(programmer, systemMemo); 170 systemMemo.setProgrammerManager(pm); 171 if (pm.isAddressedModePossible()) { 172 InstanceManager.store(pm, jmri.AddressedProgrammerManager.class); 173 initCommandStation(); 174 } 175 if (pm.isGlobalProgrammerAvailable()) { 176 InstanceManager.store(pm, GlobalProgrammerManager.class); 177 } 178 } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { 179 log.warn("Unable to construct programmer manager for XPressNet connection {}", systemMemo.getSystemPrefix(),e); 180 } 181 } 182 } 183 184 /** 185 * Set the Throttle Manager Class 186 * @param throttleManagerClass the Throttle Manager Class to use. 187 * @return this initializer 188 */ 189 public XNetInitializationManager throttleManager(Class<? extends XNetThrottleManager> throttleManagerClass){ 190 this.throttleManagerClass = throttleManagerClass; 191 return this; 192 } 193 194 private void initThrottleManager(){ 195 if(throttleManagerClass != null){ 196 try { 197 Constructor<? extends XNetThrottleManager> ctor = throttleManagerClass.getConstructor(XNetSystemConnectionMemo.class); 198 XNetThrottleManager tm = ctor.newInstance(systemMemo); 199 systemMemo.setThrottleManager(tm); 200 InstanceManager.store(tm, ThrottleManager.class); 201 } catch (NoSuchMethodException | InstantiationException | 202 IllegalAccessException | InvocationTargetException e){ 203 log.warn("Unable to construct throttle manager for XPressNet connection {}", systemMemo.getSystemPrefix()); 204 } 205 } 206 } 207 208 /** 209 * Set the Roco Throttle Manager Class 210 * @param rocoThrottleManagerClass the Roco Throttle Manager Class to use. 211 * @return this initializer 212 */ 213 public XNetInitializationManager rocoThrottleManager(Class<? extends RocoXNetThrottleManager> rocoThrottleManagerClass){ 214 this.rocoThrottleManagerClass = rocoThrottleManagerClass; 215 return this; 216 } 217 218 private void initRocoThrottleManager(){ 219 if(rocoThrottleManagerClass != null){ 220 try { 221 Constructor<? extends RocoXNetThrottleManager> ctor = rocoThrottleManagerClass.getConstructor(XNetSystemConnectionMemo.class); 222 RocoXNetThrottleManager tm = ctor.newInstance(systemMemo); 223 systemMemo.setThrottleManager(tm); 224 InstanceManager.store(tm, ThrottleManager.class); 225 } catch (NoSuchMethodException | InstantiationException | 226 IllegalAccessException | InvocationTargetException e){ 227 log.warn("Unable to construct throttle manager for XPressNet connection {}", systemMemo.getSystemPrefix()); 228 } 229 } 230 } 231 232 /** 233 * Set the Turnout Manager Class 234 * @param turnoutManagerClass the Turnout Manager Class to use. 235 * @return this initializer 236 */ 237 public XNetInitializationManager turnoutManager(Class<? extends XNetTurnoutManager> turnoutManagerClass){ 238 this.turnoutManagerClass = turnoutManagerClass; 239 return this; 240 } 241 242 private void initTurnoutManager(){ 243 if(turnoutManagerClass != null){ 244 try { 245 Constructor<? extends XNetTurnoutManager> ctor = turnoutManagerClass.getConstructor(XNetSystemConnectionMemo.class); 246 XNetTurnoutManager tm = ctor.newInstance(systemMemo); 247 systemMemo.setTurnoutManager(tm); 248 InstanceManager.setTurnoutManager(tm); 249 } catch (NoSuchMethodException | InstantiationException | 250 IllegalAccessException | InvocationTargetException e){ 251 log.warn("Unable to construct turnout manager for XPressNet connection {}", systemMemo.getSystemPrefix()); 252 } 253 } 254 } 255 256 /** 257 * Set the Sensor Manager Class 258 * @param sensorManagerClass the Sensor Manager Class to use. 259 * @return this initializer 260 */ 261 public XNetInitializationManager sensorManager(Class<? extends XNetSensorManager> sensorManagerClass){ 262 this.sensorManagerClass = sensorManagerClass; 263 return this; 264 } 265 266 private void initSensorManager(){ 267 if(sensorManagerClass != null){ 268 try { 269 Constructor<? extends XNetSensorManager> ctor = sensorManagerClass.getConstructor(XNetSystemConnectionMemo.class); 270 XNetSensorManager sm = ctor.newInstance(systemMemo); 271 systemMemo.setSensorManager(sm); 272 InstanceManager.setSensorManager(sm); 273 } catch (NoSuchMethodException | InstantiationException | 274 IllegalAccessException | InvocationTargetException e){ 275 log.warn("Unable to construct sensor manager for XPressNet connection {}", systemMemo.getSystemPrefix()); 276 } 277 } 278 } 279 280 /** 281 * Set the Light Manager Class 282 * @param lightManagerClass the Light Manager Class to use. 283 * @return this initializer 284 */ 285 public XNetInitializationManager lightManager(Class<? extends XNetLightManager> lightManagerClass){ 286 this.lightManagerClass = lightManagerClass; 287 return this; 288 } 289 290 private void initLightManager(){ 291 if(lightManagerClass != null){ 292 try { 293 Constructor<? extends XNetLightManager> ctor = lightManagerClass.getConstructor(XNetSystemConnectionMemo.class); 294 XNetLightManager lm = ctor.newInstance(systemMemo); 295 systemMemo.setLightManager(lm); 296 InstanceManager.setLightManager(lm); 297 } catch (NoSuchMethodException | InstantiationException | 298 IllegalAccessException | InvocationTargetException e){ 299 log.warn("Unable to construct light manager for XPressNet connection {}", systemMemo.getSystemPrefix()); 300 } 301 } 302 } 303 304 /** 305 * Set the Consist Manager Class 306 * @param consistManagerClass the Consist Manager Class to use. 307 * @return this initializer 308 */ 309 public XNetInitializationManager consistManager(Class<? extends XNetConsistManager> consistManagerClass){ 310 this.consistManagerClass = consistManagerClass; 311 return this; 312 } 313 314 private void initConsistManager(){ 315 if(consistManagerClass != null){ 316 try { 317 Constructor<? extends XNetConsistManager> ctor = consistManagerClass.getConstructor(XNetSystemConnectionMemo.class); 318 XNetConsistManager tm = ctor.newInstance(systemMemo); 319 systemMemo.setConsistManager(tm); 320 InstanceManager.store(tm, ConsistManager.class); 321 } catch (NoSuchMethodException | InstantiationException | 322 IllegalAccessException | InvocationTargetException e){ 323 log.warn("Unable to construct consist manager for XPressNet connection {}", systemMemo.getSystemPrefix()); 324 } 325 } 326 } 327 328 public void init() { 329 if (log.isDebugEnabled()) { 330 log.debug("Init called"); 331 } 332 /* Load managers that should work on all systems */ 333 initPowerManager(); 334 if (versionCheck) { 335 checkVersionAndInit(); 336 } else { 337 initServices(); 338 } 339 } 340 341 private void checkVersionAndInit() { 342 /* spawn a thread to request version information and wait for the 343 command station to respond */ 344 log.debug("Starting XpressNet Initialization Process"); 345 new XNetInitializer(this); 346 347 // Since we can't currently reconfigure the user interface after 348 // initialization, We need to wait for the initialization thread 349 // to finish before we can continue. The wait can be removed IF 350 // we revisit the GUI initialization process. 351 synchronized (this) { 352 log.debug("start wait"); 353 new jmri.util.WaitHandler(this); 354 log.debug("end wait"); 355 } 356 float CSSoftwareVersion = systemMemo.getXNetTrafficController() 357 .getCommandStation() 358 .getCommandStationSoftwareVersion(); 359 int CSType = systemMemo.getXNetTrafficController() 360 .getCommandStation() 361 .getCommandStationType(); 362 363 if (CSSoftwareVersion < 0) { 364 log.warn("Command Station disconnected, or powered down assuming LZ100/LZV100 V3.x"); 365 initServices(); 366 } else if (CSSoftwareVersion < 3.0) { 367 log.error("Command Station does not support XpressNet Version 3 Command Set"); 368 initThrottleManager(); 369 } else { 370 /* Next we check the command station type, and add the 371 appropriate managers */ 372 if (CSType == 0x02) { 373 log.debug("Command Station is Compact/Commander/Other"); 374 initThrottleManager(); 375 initTurnoutManager(); 376 initLightManager(); 377 initConsistManager(); 378 } else if (CSType == 0x01) { 379 log.debug("Command Station is LH200"); 380 initThrottleManager(); 381 } else if (CSType == 0x00) { 382 log.debug("Command Station is LZ100/LZV100"); 383 initServices(); 384 } else if (CSType == 0x04) { 385 log.debug("Command Station is LokMaus II"); 386 initRocoThrottleManager(); 387 initTurnoutManager(); 388 initLightManager(); 389 initSensorManager(); 390 initProgrammerManager(); 391 // LokMaus does not support XpressNET consist commands. Let's the default consist manager be loaded. 392 } else if (CSType == 0x10 ) { 393 log.debug("Command Station is multiMaus"); 394 initRocoThrottleManager(); 395 initTurnoutManager(); 396 initLightManager(); 397 initSensorManager(); 398 initProgrammerManager(); 399 // multMaus does not support XpressNET consist commands. Let's the default consist manager be loaded. 400 } else { 401 /* If we still don't know what we have, load everything */ 402 log.debug("Command Station is Unknown type"); 403 initServices(); 404 } 405 } 406 log.debug("XpressNet Initialization Complete"); 407 } 408 409 private void initServices(){ 410 initThrottleManager(); 411 initProgrammerManager(); 412 initConsistManager(); 413 initTurnoutManager(); 414 initLightManager(); 415 initSensorManager(); 416 } 417 418 /* Internal class to retrieve version Information */ 419 protected class XNetInitializer implements XNetListener { 420 421 private final javax.swing.Timer initTimer; // Timer used to let he 422 // command station response time 423 // out, and configure the defaults. 424 425 private final Object parent; 426 427 public XNetInitializer(Object Parent) { 428 429 parent = Parent; 430 431 initTimer = setupInitTimer(); 432 433 // Register as an XpressNet Listener 434 systemMemo.getXNetTrafficController().addXNetListener(XNetInterface.CS_INFO, this); 435 436 //Send Information request to LI100/LI100 437 /* First, we need to send a request for the Command Station 438 hardware and software version */ 439 XNetMessage msg = XNetMessage.getCSVersionRequestMessage(); 440 //Then Send the version request to the controller 441 systemMemo.getXNetTrafficController().sendXNetMessage(msg, this); 442 } 443 444 protected javax.swing.Timer setupInitTimer() { 445 // Initialize and start initialization timeout timer. 446 javax.swing.Timer retVal = new javax.swing.Timer(initTimeout, 447 (ActionEvent e) -> { 448 /* If the timer times out, notify any 449 waiting objects, and dispose of 450 this thread */ 451 if (log.isDebugEnabled()) { 452 log.debug("Timeout waiting for Command Station Response"); 453 } 454 finish(); 455 }); 456 retVal.setInitialDelay(initTimeout); 457 retVal.start(); 458 return retVal; 459 } 460 461 @SuppressFBWarnings(value = "NO_NOTIFY_NOT_NOTIFYALL", justification = "There should only ever be one thread waiting for this method (the designated parent, which started the thread).") 462 private void finish() { 463 initTimer.stop(); 464 // Notify the parent 465 try { 466 synchronized (parent) { 467 parent.notify(); 468 } 469 } catch (Exception e) { 470 log.error("Exception {] while notifying initialization thread.",e); 471 } 472 if (log.isDebugEnabled()) { 473 log.debug("Notification Sent"); 474 } 475 // Then dispose of this object 476 dispose(); 477 } 478 479 // listen for the responses from the LI100/LI101 480 @Override 481 public void message(XNetReply l) { 482 // Check to see if this is a response with the Command Station 483 // Version Info 484 if (l.getElement(0) == XNetConstants.CS_SERVICE_MODE_RESPONSE && 485 l.getElement(1) == XNetConstants.CS_SOFTWARE_VERSION) { 486 // This is the Command Station Software Version Response 487 systemMemo.getXNetTrafficController() 488 .getCommandStation() 489 .setCommandStationSoftwareVersion(l); 490 systemMemo.getXNetTrafficController() 491 .getCommandStation() 492 .setCommandStationType(l); 493 finish(); 494 } 495 } 496 497 // listen for the messages to the LI100/LI101 498 @Override 499 public void message(XNetMessage l) { 500 // we aren't concerned with incoming messages in this class. 501 } 502 503 // Handle a timeout notification 504 @Override 505 public void notifyTimeout(XNetMessage msg) { 506 if (log.isDebugEnabled()) { 507 log.debug("Notified of timeout on message {}",msg); 508 } 509 } 510 511 public void dispose() { 512 systemMemo.getXNetTrafficController().removeXNetListener(XNetInterface.CS_INFO, this); 513 } 514 } 515 516 private static final Logger log = LoggerFactory.getLogger(XNetInitializationManager.class); 517}