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