001package jmri.jmrix.nce; 002 003import jmri.jmrix.ConnectionStatus; 004import jmri.util.swing.JmriJOptionPane; 005 006/** 007 * Continuously checks and confirms that the communication link to the NCE 008 * Command Station is operational by reading the revision number of the EPROM. 009 * Only invokes the EPROM read when the interface experiences a timeout. 010 * <p> 011 * Checks revision of NCE CS by reading the 3 byte revision. Sends a warning 012 * message NCE EPROM found and preferences are not correct for revision selected. 013 * <p> 014 * Also checks for March 2007 EPROM and warns user about Monitoring feedback. 015 * 016 * Confirms connection to PowerCab by issuing dummy loco command. 017 * 018 * @author Daniel Boudreau (C) 2007, 2010, 2012, 2021 019 * @author Ken Cameron (C) 2023 020 * 021 */ 022public class NceConnectionStatus implements NceListener { 023 024 private static final boolean JOptPane_ERROR_MESSAGES_ENABLED = true; 025 private static final boolean JOptPane_WARNING_MESSAGES_ENABLED = false; // Disabled for headless operations! 026 027 // EPROM Checker states 028 private static final int INIT_STATE = 0; // Initial state 029 private static final int WAIT_STATE = 1; // Waiting for reply 030 private static final int CHECK_STATE = 2; // Confirm connection 031 private static final int CHECK_OK = 3; // Valid response 032 private static final int NORMAL_STATE = 4; // Normal state 033 034 private static final int WARN1_STATE = 8; // Serial interface is not functioning properly 035 private static final int WARN2_STATE = 9; // Detected 2007 March EPROM 036 037 // all of the error states below display a JmriJOptionPane error message 038 private static final int ERROR1_STATE = 16; // Wrong revision EPROM, 2004 or earlier 039 private static final int ERROR2_STATE = 17; // Wrong revision EPROM, 2006 or later 040 private static final int ERROR4_STATE = 19; // Wrong NCE System 041 private static final int ERROR5_STATE = 20; // Wrong NCE System, detected Power Cab 042 private static final int ERROR6_STATE = 21; // Wrong NCE System, detected Smart Booster SB3 043 private static final int ERROR7_STATE = 22; // Wrong NCE System, detected Power Pro 044 private static final int ERROR8_STATE = 23; // Wrong NCE System, detected SB5 045 private static final int ERROR9_STATE = 24; // Wrong NCE System, detected PH5 046 047 private int epromState = INIT_STATE; // EPROM state 048 private boolean epromChecked = false; 049 050 // Our current knowledge of NCE Command Station EPROMs. 051 // The ones we don't use (hence haven't really confirmed) are 052 // commented out to preserve the value, but indicate we don't use them. 053 private static final int VV_1999 = 4; // Revision of Apr 1999 EPROM VV.MM.mm = 4.0.1 054 // private static final int MM_1999 = 0; 055 // private static final int mm_1999 = 1; 056 057 private static final int VV_2004 = 6; // Revision of Dec 2004 EPROM VV.MM.mm = 6.0.0 058 private static final int MM_2004 = 0; 059 // private static final int mm_2004 = 0; 060 061 private static final int VV_2007 = 6; // Revision of Mar 2007 EPROM VV.MM.mm = 6.2.0 062 private static final int MM_2007 = 2; 063 private static final int mm_2007 = 0; 064 065 // private static final int mm_2007a = 1; // Revision of May 2007 EPROM VV.MM.mm = 6.2.1 066 // private static final int mm_2008 = 2; // Revision of 2008 EPROM VV.MM.mm = 6.2.2 067 private static final int mm_2021 = 3; // Revision of 2021 EPROM VV.MM.mm = 6.2.3 068 069 private static final int VV_2012 = 7; // Revision 2012 EPROM VV.MM.mm = 7.2.0 070 private static final int MM_2012 = 2; 071 072 // PH5 details, 2023 073 private static final int VV_PH5 = 8; // 1st Edition 074 private static final int MM_PH5 = 0; 075 076 // USB -> Cab bus adapter: 077 // When used with PowerCab V1.28 - 6.3.0 078 // When used with SB3 V1.28 - 6.3.1 (No program track on an SB3) 079 // When used with PH-Pro or PH-10 - 6.3.2 (limited set of features available 080 // through cab bus) 081 // 082 // Future version of PowerCab V1.61 - 6.3.4 083 // Future version of SB3 V1.61 - 6.3.5 084 // 085 // NOTE: The USB port can not read CS memory, unless greater than 7.* version 086 private static final int VV_USB_V6 = 6; // Revision of USB EPROM VV.MM.mm = 6.3.x 087 private static final int VV_USB_V7 = 7; // 2012 revision of USB EPROM VV.MM.mm = 7.3.x 088 private static final int MM_USB = 3; 089 // V6 flavors 090 private static final int mm_USB_V6_PwrCab = 0; // PowerCab 091 private static final int mm_USB_V6_SB3 = 1; // SB3 092 private static final int mm_USB_V6_PH = 2; // PH-Pro or PH-10 093 // Future releases by NCE (Not used by JMRI yet!) 094 // private static final int mm_USB_V6_ALL = 3; // All systems, not currently used 095 // private static final int mm_USB_V6_PC161 = 4; // Future use, PowerCab 1.61, not currently used 096 // private static final int mm_USB_V6_SB161 = 5; // Future use, SB3 1.61, not currently used 097 // V7 flavors 098 private static final int mm_USB_V7_PC_128_A = 0; // PowerCab with 1.28c 099 private static final int mm_USB_V7_SB5_165_A = 1; // SB5 with 1.65 100 private static final int mm_USB_V7_SB5_165_B = 2; // SB5 with 1.65 101 // private static final int mm_USB_V7_PC_165 = 3; // PowerCab with 1.65 102 private static final int mm_USB_V7_PC_128_B = 4; // PowerCab with 1.28c 103 private static final int mm_USB_V7_SB3 = 5; // SB3 with 1.28c 104 private static final int mm_USB_V7_PH = 6; // PowerPro with 3.1.2007 105 // private static final int mm_USB_V7_ALL = 7; // All systems 106 107 108 private NceTrafficController tc = null; 109 110 public NceConnectionStatus(NceTrafficController tc) { 111 super(); 112 this.tc = tc; 113 } 114 115 public NceMessage nceEpromPoll() { 116 117 if (tc.getCommandOptions() <= NceTrafficController.OPTION_1999) { 118 return null; 119 } 120 121 // normal state for this routine 122 if (epromState == NORMAL_STATE) { 123 // are there interface timeouts? 124 if (tc.hasTimeouts()) { 125 epromState = INIT_STATE; 126 } else { 127 return null; 128 } 129 } 130 131 // determine if really connected to command station by issuing dummy locomotive 132 // command, to short address 0. 133 if (epromState == CHECK_STATE) { 134 if (tc.getCommandOptions() > NceTrafficController.OPTION_2004) { 135 return NceMessage.sendLocoCmd(tc, 0x0000, NceMessage.LOCO_CMD_SELECT_LOCO, (byte) 00); 136 } 137 epromState = CHECK_OK; 138 } 139 140 if (epromState == CHECK_OK) { 141 ConnectionStatus.instance().setConnectionState(tc.getUserName(), tc.getPortName(), 142 ConnectionStatus.CONNECTION_UP); 143 epromState = NORMAL_STATE; 144 return null; 145 } 146 147 if (epromState != INIT_STATE) { 148 ConnectionStatus.instance().setConnectionState(tc.getUserName(), tc.getPortName(), 149 ConnectionStatus.CONNECTION_DOWN); 150 } 151 152 // no response from command station? 153 if (epromState == WAIT_STATE) { 154 log.warn("{}: Incorrect or no response from NCE command station port: {}", tc.getUserName(), tc.getPortName()); 155 if (JOptPane_WARNING_MESSAGES_ENABLED) { 156 JmriJOptionPane.showMessageDialog(null, 157 "JMRI could not establish communication with NCE command station. \n" + 158 "Check the \"Serial port:\" and \"Baud rate:\" in Edit -> Preferences. \n" + 159 "Confirm cabling and that the NCE system is powered up.", 160 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 161 } 162 epromState = WARN1_STATE; 163 } 164 165 // still no response from command station? 166 else if (epromState == WARN1_STATE) { 167 log.warn("{}: No response from NCE command station port: {}", tc.getUserName(), tc.getPortName()); 168 } 169 170 if (epromState == ERROR1_STATE) { 171 if (JOptPane_ERROR_MESSAGES_ENABLED) { 172 JmriJOptionPane.showMessageDialog(null, 173 "Wrong revision of Command Station EPROM selected in Preferences \n" + 174 "Change the Command Station EPROM selection to \"2004 or earlier\"", 175 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 176 } 177 epromState = NORMAL_STATE; 178 return null; 179 } 180 181 if (epromState == ERROR2_STATE) { 182 if (JOptPane_ERROR_MESSAGES_ENABLED) { 183 JmriJOptionPane.showMessageDialog(null, 184 "Wrong revision of Command Station EPROM selected in Preferences \n" + 185 "Change the Command Station EPROM selection to \"2006 or later\"", 186 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 187 } 188 epromState = NORMAL_STATE; 189 return null; 190 } 191 192 if (epromState == WARN2_STATE) { 193 log.warn("{}: Detected 2007 March EPROM which doesn't provide reliable MONITORING feedback for turnouts", tc.getUserName()); 194 // Need to add checkbox "Do not show this message again" otherwise 195 // the message can be a pain. 196 if (JOptPane_WARNING_MESSAGES_ENABLED) { 197 JmriJOptionPane.showMessageDialog(null, 198 "The 2007 March EPROM doesn't provide reliable feedback," + 199 " contact NCE if you want to use MONITORING feedback ", 200 Bundle.getMessage("WarningTitle"), JmriJOptionPane.INFORMATION_MESSAGE); 201 } 202 ConnectionStatus.instance().setConnectionState(tc.getUserName(), tc.getPortName(), 203 ConnectionStatus.CONNECTION_UP); 204 epromState = NORMAL_STATE; 205 return null; 206 } 207 208 if (epromState == ERROR4_STATE) { 209 if (JOptPane_ERROR_MESSAGES_ENABLED) { 210 JmriJOptionPane.showMessageDialog(null, 211 "Wrong NCE System Connection selected in Preferences. " + 212 "Change the System Connection to \"" + 213 jmri.jmrix.nce.serialdriver.ConnectionConfig.NAME + 214 "\" or \"" + 215 jmri.jmrix.nce.networkdriver.ConnectionConfig.NAME + 216 "\".", 217 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 218 } 219 epromState = NORMAL_STATE; 220 return null; 221 } 222 223 if (epromState == ERROR5_STATE) { 224 if (JOptPane_ERROR_MESSAGES_ENABLED) { 225 JmriJOptionPane.showMessageDialog(null, 226 "Wrong NCE System Connection selected in Preferences. " + 227 "The System Connection \"" + 228 jmri.jmrix.nce.usbdriver.ConnectionConfig.NAME + 229 "\" should change the system to \"Power Cab\".", 230 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 231 } 232 epromState = NORMAL_STATE; 233 return null; 234 } 235 236 if (epromState == ERROR6_STATE) { 237 if (JOptPane_ERROR_MESSAGES_ENABLED) { 238 JmriJOptionPane.showMessageDialog(null, 239 "Wrong NCE System Connection selected in Preferences. " + 240 "The System Connection \"" + 241 jmri.jmrix.nce.usbdriver.ConnectionConfig.NAME + 242 "\" should change the system to \"Smart Booster SB3\".", 243 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 244 } 245 epromState = NORMAL_STATE; 246 return null; 247 } 248 249 if (epromState == ERROR7_STATE) { 250 if (JOptPane_ERROR_MESSAGES_ENABLED) { 251 JmriJOptionPane.showMessageDialog(null, 252 "Wrong NCE System Connection selected in Preferences. " + 253 "The System Connection \"" + 254 jmri.jmrix.nce.usbdriver.ConnectionConfig.NAME + 255 "\" should change the system to \"Power Pro\".", 256 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 257 } 258 epromState = NORMAL_STATE; 259 return null; 260 } 261 262 if (epromState == ERROR8_STATE) { 263 if (JOptPane_ERROR_MESSAGES_ENABLED) { 264 JmriJOptionPane.showMessageDialog(null, 265 "Wrong NCE System Connection selected in Preferences. " + 266 "The System Connection \"" + 267 jmri.jmrix.nce.usbdriver.ConnectionConfig.NAME + 268 "\" should change the system to \"SB5\".", 269 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 270 } 271 epromState = NORMAL_STATE; 272 return null; 273 } 274 275 // stay in warn state until reply 276 if (epromState != WARN1_STATE) { 277 epromState = WAIT_STATE; 278 } 279 // go ahead and read the EPROM revision 280 return NceMessage.getEpromVersion(tc); 281 } 282 283 @Override 284 public void message(NceMessage m) { 285 if (log.isDebugEnabled()) { 286 log.debug("{}: unexpected message", tc.getUserName()); 287 } 288 } 289 290 @Override 291 public void reply(NceReply r) { 292 if (r.getNumDataElements() == NceMessage.REPLY_1 && epromState == CHECK_STATE) { 293 if (r.getElement(0) == NceMessage.NCE_OKAY) { 294 log.info("{}: Connected to NCE command station", tc.getUserName()); 295 epromState = CHECK_OK; 296 } else { 297 log.warn("{}: Not connected to NCE command station", tc.getUserName()); 298 epromState = INIT_STATE; 299 } 300 } else if (r.getNumDataElements() == NceMessage.REPLY_3) { 301 302 byte VV = (byte) r.getElement(0); 303 byte MM = (byte) r.getElement(1); 304 byte mm = (byte) r.getElement(2); 305 tc.setPwrProVers(VV, MM, mm); 306 307 // Is the reply valid? Check major revision, there are only three valid 308 // responses 309 // note that VV_2004 = VV_2007 = VV_USB 310 if (VV != VV_PH5 && VV != VV_2012 && VV != VV_2004 && VV != VV_1999) { 311 log.error("{}: Wrong major revision: {}", tc.getUserName(), Integer.toHexString(VV & 0xFF)); 312 // show the entire revision number 313 log.info("{}: NCE EPROM revision = {}", tc.getUserName(), tc.getPwrProVersHexText()); 314 return; 315 } 316 317 // We got a valid reply, now check to see if connected to command station 318 // or PowerCab 319 epromState = CHECK_STATE; 320 321 // Have we already done the error checking? 322 if (!epromChecked) { 323 checkEPROM(VV, MM, mm); 324 epromChecked = true; 325 } 326 } else { 327 log.warn("{}: wrong number of read bytes for revision check", tc.getUserName()); 328 } 329 } 330 331 /** 332 * EPROM version check is only done once at startup 333 * 334 * @param VV Major version number 335 * @param MM Middle version number 336 * @param mm Minor version number 337 */ 338 private void checkEPROM(byte VV, byte MM, byte mm) { 339 // Send to log file the NCE EPROM revision 340 log.info("{}: NCE EPROM revision = {}", tc.getUserName(), tc.getPwrProVersHexText()); 341 342 // Warn about the March 2007 CS EPROM 343 if (VV == VV_2007 && MM == MM_2007 && mm == mm_2007) { 344 tc.setNceEpromMarch2007(true); 345 epromState = WARN2_STATE; 346 } 347 348 // check for Power Pro 2021 or later 349 if (VV == VV_2007 && MM == MM_2007 && mm >= mm_2021) { 350 tc.setPwrProVer060203orLater(true); 351 } 352 353 // Confirm that user selected correct revision of EPROM, check for old EPROM 354 // installed, new EPROM 355 // preferences 356 if ((VV <= VV_2007 && MM < MM_2007) && (tc.getCommandOptions() >= NceTrafficController.OPTION_2006)) { 357 log.error("{}: Wrong revision ({}) of the NCE Command Station EPROM selected in Preferences", 358 tc.getUserName(), tc.getPwrProVersHexText()); 359 epromState = ERROR1_STATE; 360 } 361 362 // Confirm that user selected correct revision of EPROM, check for new EPROM 363 // installed, old EPROM 364 // preferences 365 boolean eprom2007orNewer = ((VV == VV_2007) && (MM >= MM_2007)); 366 if (((VV > VV_2007) || eprom2007orNewer) && (tc.getCommandOptions() < NceTrafficController.OPTION_2006)) { 367 log.error("{}: Wrong revision ({}) of the NCE Command Station EPROM selected in Preferences", 368 tc.getUserName(), tc.getPwrProVersHexText()); 369 epromState = ERROR2_STATE; 370 } 371 372 // Check that layout connection is correct 373 // PowerPro? 4 cases for PH, 1999, 2004, 2007, & 2012 374 if (VV == VV_1999 || 375 (VV == VV_2004 && MM == MM_2004) || 376 (VV == VV_2007 && MM == MM_2007) || 377 (VV == VV_2012 && MM == MM_2012)) { 378 // make sure system connection is not NCE USB 379 if (tc.getUsbSystem() != NceTrafficController.USB_SYSTEM_NONE) { 380 log.error("{}: System Connection is incorrect, detected Power Pro", tc.getUserName()); 381 epromState = ERROR4_STATE; 382 } 383 } 384 385 // Check for USB 6.3.x 386 if (VV == VV_USB_V6 && MM == MM_USB) { 387 // USB detected, check to see if user preferences are correct 388 if (mm == mm_USB_V6_PwrCab && tc.getUsbSystem() != NceTrafficController.USB_SYSTEM_POWERCAB) { 389 log.error("{}: System Connection is incorrect, detected USB connected to a PowerCab", tc.getUserName()); 390 epromState = ERROR5_STATE; 391 } 392 if (mm == mm_USB_V6_SB3 && tc.getUsbSystem() != NceTrafficController.USB_SYSTEM_SB3) { 393 log.error("{}: System Connection is incorrect, detected USB connected to a Smart Booster SB3", tc.getUserName()); 394 epromState = ERROR6_STATE; 395 } 396 if (mm == mm_USB_V6_PH && tc.getUsbSystem() != NceTrafficController.USB_SYSTEM_POWERPRO) { 397 log.error("{}: System Connection is incorrect, detected USB connected to a Power Pro", tc.getUserName()); 398 epromState = ERROR7_STATE; 399 } 400 } 401 // Check for USB 7.3.x 402 if (VV == VV_USB_V7 && MM == MM_USB) { 403 // USB V7 detected, check to see if user preferences are correct 404 if (((mm == mm_USB_V7_PC_128_A) || (mm == mm_USB_V7_PC_128_B)) && 405 tc.getUsbSystem() != NceTrafficController.USB_SYSTEM_POWERCAB) { 406 log.error("{}: System Connection is incorrect, detected USB connected to a PowerCab", tc.getUserName()); 407 epromState = ERROR5_STATE; 408 } 409 if (mm == mm_USB_V7_SB3 && tc.getUsbSystem() != NceTrafficController.USB_SYSTEM_SB3) { 410 log.error("{}: System Connection is incorrect, detected USB connected to a Smart Booster SB3", tc.getUserName()); 411 epromState = ERROR6_STATE; 412 } 413 if (mm == mm_USB_V7_PH && tc.getUsbSystem() != NceTrafficController.USB_SYSTEM_POWERPRO) { 414 log.error("{}: System Connection is incorrect, detected USB connected to a Power Pro", tc.getUserName()); 415 epromState = ERROR7_STATE; 416 } 417 if (((mm == mm_USB_V7_SB5_165_A) || (mm == mm_USB_V7_SB5_165_B)) && 418 tc.getUsbSystem() != NceTrafficController.USB_SYSTEM_SB5) { 419 log.error("{}: System Connection is incorrect, detected USB connected to a Smart Booster SB5", tc.getUserName()); 420 epromState = ERROR8_STATE; 421 } 422 } 423 // check for PH5 not on PH5 connection 424 if ((VV == VV_PH5) && (MM == MM_PH5)) { 425 if (tc.getCommandOptions() != NceTrafficController.OPTION_PH5) { 426 log.error("{}: System Connection is incorrect, detected PH5 not connected as a PH5", tc.getUserName()); 427 epromState = ERROR9_STATE; 428 } 429 } 430 // check for PH5 connection to a non-PH5 command station 431 if (tc.getCommandOptions() == NceTrafficController.OPTION_PH5) { 432 if ((VV != VV_PH5) || (MM != MM_PH5)) { 433 log.error("{}: System Connection is incorrect, detected something other than a PH5", tc.getUserName()); 434 epromState = ERROR4_STATE; 435 } 436 } 437 } 438 439 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NceConnectionStatus.class); 440 441}