001package jmri.jmrix.cmri; 002 003import java.util.Comparator; 004import java.util.Locale; 005import java.util.ResourceBundle; 006import javax.annotation.Nonnull; 007import javax.annotation.CheckReturnValue; 008 009import jmri.*; 010import jmri.Manager.NameValidity; 011import jmri.jmrix.AbstractNode; 012import jmri.jmrix.ConfiguringSystemConnectionMemo; 013import jmri.jmrix.DefaultSystemConnectionMemo; 014import jmri.jmrix.cmri.serial.*; 015import jmri.jmrix.cmri.swing.CMRIComponentFactory; 016import jmri.jmrix.swing.ComponentFactory; 017import jmri.util.NamedBeanComparator; 018 019/** 020 * Minimal SystemConnectionMemo for C/MRI systems. 021 * 022 * @author Randall Wood 023 */ 024public class CMRISystemConnectionMemo extends DefaultSystemConnectionMemo implements ConfiguringSystemConnectionMemo { 025 026 public CMRISystemConnectionMemo() { 027 this("C", CMRIConnectionTypeList.CMRI); // default to "C" prefix 028 } 029 030 public CMRISystemConnectionMemo(@Nonnull String prefix, @Nonnull String userName) { 031 super(prefix, userName); 032 033 InstanceManager.store(this, CMRISystemConnectionMemo.class); // also register as specific type 034 035 // create and register the ComponentFactory for the GUI 036 cf = new CMRIComponentFactory(this); 037 InstanceManager.store(cf, ComponentFactory.class); 038 039 log.debug("Created CMRISystemConnectionMemo"); 040 } 041 042 private SerialTrafficController tc = null; 043 ComponentFactory cf = null; 044 045 /** 046 * Set the traffic controller instance associated with this connection memo. 047 * 048 * @param s jmri.jmrix.cmri.serial.SerialTrafficController object to use. 049 */ 050 public void setTrafficController(SerialTrafficController s) { 051 tc = s; 052 } 053 054 /** 055 * Get the traffic controller instance associated with this connection memo. 056 * 057 * @return the traffic controller, created if needed 058 */ 059 public SerialTrafficController getTrafficController() { 060 if (tc == null) { 061 setTrafficController(new SerialTrafficController()); 062 log.debug("Auto create of SerialTrafficController for initial configuration"); 063 } 064 return tc; 065 } 066 067 /** 068 * Get the user name for a valid system name. 069 * 070 * @param systemName the system name 071 * @return "" (null string) if the system name is not valid or does not 072 * exist 073 */ 074 public String getUserNameFromSystemName(String systemName) { 075 int offset = checkSystemPrefix(systemName); 076 if (offset < 1) { 077 return ""; 078 } 079 if (systemName.length() < offset + 1) { 080 // not a valid system name for C/MRI 081 return ""; 082 } 083 switch (systemName.charAt(offset)) { 084 case 'S': 085 Sensor s = InstanceManager.sensorManagerInstance().getBySystemName(systemName); 086 if (s != null) { 087 return s.getUserName(); 088 } else { 089 return ""; 090 } 091 case 'T': 092 Turnout t = InstanceManager.turnoutManagerInstance().getBySystemName(systemName); 093 if (t != null) { 094 return t.getUserName(); 095 } else { 096 return ""; 097 } 098 case 'L': 099 Light lgt = InstanceManager.lightManagerInstance().getBySystemName(systemName); 100 if (lgt != null) { 101 return lgt.getUserName(); 102 } else { 103 return ""; 104 } 105 default: 106 break; 107 } 108 // not any known sensor, light, or turnout 109 return ""; 110 } 111 112 /** 113 * Get the bit number from a C/MRI system name. Bits are numbered from 1. 114 * Does not check whether that node is defined on current system. 115 * 116 * @param systemName the system name 117 * @return 0 if an error is found 118 */ 119 public int getBitFromSystemName(String systemName) { 120 int offset = checkSystemPrefix(systemName); 121 if (offset < 1) { 122// log.error("invalid system prefix in CMRI system name: {}", systemName); // fix test first 123 return 0; 124 } 125 if (validSystemNameFormat(systemName, systemName.charAt(offset)) != NameValidity.VALID) { 126 // No point in normalizing if a valid system name format is not present 127 return 0; 128 } 129 // Find the beginning of the bit number field 130 int k = 0; 131 for (int i = offset + 1; (i < systemName.length()) && (k == 0); i++) { 132 if (systemName.charAt(i) == 'B') { 133 k = i + 1; 134 } 135 } 136 int n; // bit number 137 if (k == 0) { 138 // here if 'B' not found, name must be CLnnxxx format 139 int num; 140 try { 141 num = Integer.parseInt(systemName.substring(offset + 1)); 142 } catch (NumberFormatException e) { 143 log.warn("invalid character in number field of system name: {}", systemName); 144 return 0; 145 } 146 if (num > 0) { 147 n = num - ((num / 1000) * 1000); 148 } else { 149 log.warn("invalid CMRI system name: {}", systemName); 150 return 0; 151 } 152 } else { // k = position of "B" char in name 153 try { 154 n = Integer.parseInt(systemName.substring(k)); 155 } catch (NumberFormatException e) { 156 log.warn("invalid character in bit number field of CMRI system name: {}", systemName); 157 return 0; 158 } 159 } 160 return n; 161 } 162 163 /** 164 * Check and skip the System Prefix string on a system name. 165 * 166 * @param systemName the system name 167 * @return offset of the 1st character past the prefix, or -1 if not valid 168 * for this connection 169 */ 170 public int checkSystemPrefix(String systemName) { 171 if (!systemName.startsWith(getSystemPrefix())) { 172 return -1; 173 } 174 return getSystemPrefix().length(); 175 } 176 177 /** 178 * Test if a C/MRI output bit is free for assignment. Test is not performed 179 * if the node address or bit number is invalid. 180 * 181 * @param nAddress the node address 182 * @param bitNum the output bit number 183 * @return "" (empty string) if the specified output bit is free for 184 * assignment, else returns the system name of the conflicting 185 * assignment. 186 */ 187 public String isOutputBitFree(int nAddress, int bitNum) { 188 if ((nAddress < 0) || (nAddress > 127)) { 189 log.warn("invalid node address in free bit test"); 190 return ""; 191 } 192 if ((bitNum < 1) || (bitNum > 2048)) { 193 log.warn("invalid bit number in free bit test"); 194 return ""; 195 } 196 String sysName = makeSystemName("T", nAddress, bitNum); 197 Turnout t = InstanceManager.turnoutManagerInstance().getBySystemName(sysName); 198 if (t != null) { 199 return sysName; 200 } 201 String altName = convertSystemNameToAlternate(sysName); 202 t = InstanceManager.turnoutManagerInstance().getBySystemName(altName); 203 if (t != null) { 204 return altName; 205 } 206 if (bitNum > 1) { 207 sysName = makeSystemName("T", nAddress, bitNum - 1); 208 t = InstanceManager.turnoutManagerInstance().getBySystemName(sysName); 209 if (t != null) { 210 if (t.getNumberControlBits() == 2) { 211 return sysName; 212 } 213 } else { 214 altName = convertSystemNameToAlternate(sysName); 215 t = InstanceManager.turnoutManagerInstance().getBySystemName(altName); 216 if (t != null) { 217 if (t.getNumberControlBits() == 2) { 218 return altName; 219 } 220 } 221 } 222 } 223 sysName = makeSystemName("L", nAddress, bitNum); 224 Light lgt = InstanceManager.lightManagerInstance().getBySystemName(sysName); 225 if (lgt != null) { 226 return sysName; 227 } 228 altName = convertSystemNameToAlternate(sysName); 229 lgt = InstanceManager.lightManagerInstance().getBySystemName(altName); 230 if (lgt != null) { 231 return altName; 232 } 233 // not assigned to a turnout or a light 234 return ""; 235 } 236 237 /** 238 * Normalize a C/MRI system name. 239 * <p> 240 * This routine is used to ensure that each system name is uniquely linked 241 * to one C/MRI bit, by removing extra zeros inserted by the user. 242 * 243 * @param systemName the system name 244 * @return "" (empty string) if the supplied system name does not have a 245 * valid format. Otherwise a normalized name is returned in the same 246 * format as the input name. 247 */ 248 public String normalizeSystemName(String systemName) { 249 int offset = checkSystemPrefix(systemName); 250 if (offset < 1) { 251// log.error("invalid system prefix in CMRI system name: {}", systemName); // fix test first 252 return ""; 253 } 254 if (validSystemNameFormat(systemName, systemName.charAt(offset)) != NameValidity.VALID) { 255 // No point in normalizing if a valid system name format is not present 256 return ""; 257 } 258 String nName; 259 String s = ""; 260 int k = 0; 261 boolean noB = true; 262 for (int i = offset + 1; (i < systemName.length()) && noB; i++) { 263 if (systemName.charAt(i) == 'B') { 264 s = systemName.substring(offset + 1, i); 265 k = i + 1; 266 noB = false; 267 } 268 } 269 if (noB) { 270 int num = Integer.parseInt(systemName.substring(offset + 1)); 271 int nAddress = num / 1000; 272 int bitNum = num - (nAddress * 1000); 273 nName = systemName.substring(0, offset + 1) + Integer.toString((nAddress * 1000) + bitNum); 274 } else { 275 int nAddress = Integer.parseInt(s); 276 int bitNum = Integer.parseInt(systemName.substring(k, systemName.length())); 277 nName = systemName.substring(0, offset + 1) + Integer.toString(nAddress) + "B" + Integer.toString(bitNum); 278 } 279 return nName; 280 } 281 282 /** 283 * Convert one format C/MRI system name to the alternate format. 284 * 285 * @param systemName the system name 286 * @return "" (empty string) if the supplied system name does not have a 287 * valid format, or if there is no representation in the alternate 288 * naming scheme 289 */ 290 public String convertSystemNameToAlternate(String systemName) { 291 int offset = checkSystemPrefix(systemName); 292 if (offset < 1) { 293 log.error("invalid system prefix in CMRI system name: {}", systemName); 294 return ""; 295 } 296 if (validSystemNameFormat(systemName, systemName.charAt(offset)) != NameValidity.VALID) { 297 // No point in trying if a valid system name format is not present 298 return ""; 299 } 300 String altName; 301 String s = ""; 302 int k = 0; 303 boolean noB = true; 304 for (int i = offset + 1; (i < systemName.length()) && noB; i++) { 305 if (systemName.charAt(i) == 'B') { 306 s = systemName.substring(offset + 1, i); 307 k = i + 1; 308 noB = false; 309 } 310 } 311 if (noB) { 312 int num = Integer.parseInt(systemName.substring(offset + 1)); 313 int nAddress = num / 1000; 314 int bitNum = num - (nAddress * 1000); 315 altName = systemName.substring(0, offset + 1) + Integer.toString(nAddress) + "B" + Integer.toString(bitNum); 316 } else { 317 int nAddress = Integer.parseInt(s); 318 int bitNum = Integer.parseInt(systemName.substring(k, systemName.length())); 319 if (bitNum > 999) { 320 // bit number is out-of-range for a CLnnnxxx address 321 return ""; 322 } 323 altName = systemName.substring(0, offset + 1) + Integer.toString((nAddress * 1000) + bitNum); 324 } 325 return altName; 326 } 327 328 /** 329 * Validate system name format. Does not check whether that node is defined 330 * on current system. 331 * 332 * @param systemName the system name 333 * @param type the device type 334 * @return enum indicating current validity, which might be just as a prefix 335 */ 336 public NameValidity validSystemNameFormat(@Nonnull String systemName, char type) { 337 int offset = checkSystemPrefix(systemName); 338 if (offset < 1) { 339 log.debug("invalid system prefix in CMRI system name: {}", systemName); 340 return NameValidity.INVALID; 341 } 342 if (systemName.charAt(offset) != type) { 343 log.debug("invalid type character in CMRI system name: {}", systemName); 344 return NameValidity.INVALID; 345 } 346 String s = ""; 347 int k = 0; 348 boolean noB = true; 349 for (int i = offset + 1; (i < systemName.length()) && noB; i++) { 350 if (systemName.charAt(i) == 'B') { 351 s = systemName.substring(offset + 1, i); 352 k = i + 1; 353 noB = false; 354 } 355 } 356 if (noB) { 357 // This is a CLnnnxxx pattern address 358 int num; 359 try { 360 num = Integer.parseInt(systemName.substring(offset + 1)); 361 } catch (NumberFormatException e) { 362 log.debug("invalid character in number field of CMRI system name: {}", systemName); 363 return NameValidity.INVALID; 364 } 365 if ((num < 1) || (num >= 128000)) { 366 log.debug("number field out of range in CMRI system name: {}", systemName); 367 return NameValidity.INVALID; 368 } 369 if ((num - ((num / 1000) * 1000)) == 0) { 370 log.debug("bit number not in range 1 - 999 in CMRI system name: {}", systemName); 371 if (systemName.length() <= offset + 6) { 372 return NameValidity.VALID_AS_PREFIX_ONLY; 373 // may become valid by adding 1 or more digits > 0 374 } else { // unless systemName.length() > offset + 6 375 return NameValidity.INVALID; 376 } 377 } 378 } else { 379 // This is a CLnBxxx pattern address 380 if (s.length() == 0) { 381 log.debug("no node address before 'B' in CMRI system name: {}", systemName); 382 return NameValidity.INVALID; 383 } 384 int num; 385 try { 386 num = Integer.parseInt(s); 387 } catch (NumberFormatException e) { 388 log.debug("invalid character in node address field of CMRI system name: {}", systemName); 389 return NameValidity.INVALID; 390 } 391 if ((num < 0) || (num >= 128)) { 392 log.debug("node address field out of range in CMRI system name: {}", systemName); 393 return NameValidity.INVALID; 394 } 395 try { 396 num = Integer.parseInt(systemName.substring(k)); 397 } catch (NumberFormatException e) { 398 log.debug("invalid character in bit number field of CMRI system name: {}", systemName); 399 return NameValidity.INVALID; 400 } 401 if (num == 0) { 402 return NameValidity.VALID_AS_PREFIX_ONLY; 403 // may become valid by adding 1 or more digits > 0, all zeros will be removed later so total length irrelevant 404 } 405 if ((num < 1) || (num > 2048)) { 406 log.debug("bit number field out of range in CMRI system name: {}", systemName); 407 return NameValidity.INVALID; 408 } 409 } // TODO add format check for CLnn:xxx format 410 return NameValidity.VALID; 411 } 412 413 /** 414 * Validate system name format. Does not check whether that node is defined 415 * on current system. 416 * 417 * @param systemName the system name 418 * @param type the device type 419 * @param locale the Locale for user messages 420 * @return systemName unmodified 421 * @throws IllegalArgumentException if unable to validate systemName 422 */ 423 public String validateSystemNameFormat(String systemName, char type, Locale locale) throws IllegalArgumentException { 424 String prefix = getSystemPrefix() + type; 425 if (!systemName.startsWith(prefix)) { 426 throw new NamedBean.BadSystemNameException( 427 Bundle.getMessage(Locale.ENGLISH, "InvalidSystemNameInvalidPrefix", systemName), 428 Bundle.getMessage(locale, "InvalidSystemNameInvalidPrefix", systemName)); 429 } 430 String address = systemName.substring(prefix.length()); 431 int node = 0; 432 int bit = 0; 433 if (!address.contains("B") && !address.contains(":")) { 434 // This is a CLnnnxxx pattern address 435 int num; 436 try { 437 num = Integer.parseInt(address); 438 node = num / 1000; 439 bit = num - ((num / 1000) * 1000); 440 } catch (NumberFormatException ex) { 441 throw new NamedBean.BadSystemNameException( 442 Bundle.getMessage(Locale.ENGLISH, "InvalidSystemNameNotInteger", systemName, prefix), 443 Bundle.getMessage(locale, "InvalidSystemNameNotInteger", systemName, prefix)); 444 } 445 } else { 446 // This is a CLnBxxx or CLn:xxx pattern address 447 String[] parts = address.split("B"); 448 if (parts.length != 2) { 449 parts = address.split(":"); 450 if (parts.length != 2) { 451 if (address.indexOf(":") == 0 && address.indexOf("B") == 0) { 452 // no node 453 throw new NamedBean.BadSystemNameException( 454 Bundle.getMessage(Locale.ENGLISH, "InvalidSystemNameNodeInvalid", systemName, ""), 455 Bundle.getMessage(locale, "InvalidSystemNameNodeInvalid", systemName, "")); 456 } else { 457 // no bit 458 throw new NamedBean.BadSystemNameException( 459 Bundle.getMessage(Locale.ENGLISH, "InvalidSystemNameBitInvalid", systemName, ""), 460 Bundle.getMessage(locale, "InvalidSystemNameBitInvalid", systemName, "")); 461 } 462 } 463 } 464 try { 465 node = Integer.parseInt(parts[0]); 466 } catch (NumberFormatException ex) { 467 throw new NamedBean.BadSystemNameException( 468 Bundle.getMessage(Locale.ENGLISH, "InvalidSystemNameNodeInvalid", systemName, parts[0]), 469 Bundle.getMessage(locale, "InvalidSystemNameNodeInvalid", systemName, parts[0])); 470 } 471 try { 472 bit = Integer.parseInt(parts[1]); 473 } catch (NumberFormatException ex) { 474 throw new NamedBean.BadSystemNameException( 475 Bundle.getMessage(Locale.ENGLISH, "InvalidSystemNameBitInvalid", systemName, parts[1]), 476 Bundle.getMessage(locale, "InvalidSystemNameBitInvalid", systemName, parts[1])); 477 } 478 } 479 if (node < 0 || node >= 128) { 480 throw new NamedBean.BadSystemNameException( 481 Bundle.getMessage(Locale.ENGLISH, "InvalidSystemNameNodeInvalid", systemName, node), 482 Bundle.getMessage(locale, "InvalidSystemNameNodeInvalid", systemName, node)); 483 } 484 if (bit < 1 || bit > 2048) { 485 throw new NamedBean.BadSystemNameException( 486 Bundle.getMessage(Locale.ENGLISH, "InvalidSystemNameBitInvalid", systemName, bit), 487 Bundle.getMessage(locale, "InvalidSystemNameBitInvalid", systemName, bit)); 488 } 489 return systemName; 490 } 491 492 /** 493 * Test if a C/MRI input bit is free for assignment. Test is not performed 494 * if the node address is invalid or bit number is greater than 2048. 495 * 496 * @param nAddress the address to test 497 * @param bitNum the bit number to tests 498 * @return "" (empty string) if the specified input bit is free for 499 * assignment, else returns the system name of the conflicting 500 * assignment. 501 */ 502 public String isInputBitFree(int nAddress, int bitNum) { 503 if ((nAddress < 0) || (nAddress > 127)) { 504 log.warn("invalid node address in free bit test"); 505 return ""; 506 } 507 if ((bitNum < 1) || (bitNum > 2048)) { 508 log.warn("invalid bit number in free bit test"); 509 return ""; 510 } 511 String sysName = makeSystemName("S", nAddress, bitNum); 512 Sensor s = InstanceManager.sensorManagerInstance().getBySystemName(sysName); 513 if (s != null) { 514 return sysName; 515 } 516 String altName = convertSystemNameToAlternate(sysName); 517 s = InstanceManager.sensorManagerInstance().getBySystemName(altName); 518 if (s != null) { 519 return altName; 520 } 521 // not assigned to a sensor 522 return ""; 523 } 524 525 /** 526 * Construct a C/MRI system name from type character, node address, and bit 527 * number. 528 * <p> 529 * If the supplied character is not valid, or the node address is out of the 530 * 0 - 127 range, or the bit number is out of the 1 - 2048 range, an error 531 * message is logged and the null string "" is returned. 532 * 533 * @param type the device type 534 * @param nAddress the address to use 535 * @param bitNum the bit number to assign 536 * @return a system name in the CLnnnxxx, CTnnnxxx, or CSnnnxxx format if 537 * the bit number is 1 - 999. If the bit number is 1000 - 2048, the 538 * system name is returned in the CLnnnBxxxx, CTnnnBxxxx, or 539 * CSnnnBxxxx format. The returned name is normalized. 540 */ 541 public String makeSystemName(String type, int nAddress, int bitNum) { 542 String nName = ""; 543 if ((!type.equals("S")) && (!type.equals("L")) && (!type.equals("T"))) { 544 log.error("invalid type character proposed for system name"); 545 return nName; 546 } 547 if ((nAddress < 0) || (nAddress > 127)) { 548 log.warn("invalid node address proposed for system name"); 549 return nName; 550 } 551 if ((bitNum < 1) || (bitNum > 2048)) { 552 log.warn("invalid bit number proposed for system name"); 553 return nName; 554 } 555 if (bitNum < 1000) { 556 nName = getSystemPrefix() + type + Integer.toString((nAddress * 1000) + bitNum); 557 } else { 558 nName = getSystemPrefix() + type + Integer.toString(nAddress) + "B" + Integer.toString(bitNum); 559 } 560 return nName; 561 } 562 563 /** 564 * Get the serial node from a C/MRI system name. 565 * 566 * @param systemName the system name 567 * @param tc the controller for the node 568 * @return the node or null if invalid systemName format or if the node is 569 * not found 570 */ 571 public AbstractNode getNodeFromSystemName(String systemName, SerialTrafficController tc) { 572 // get the node address 573 int ua; 574 ua = getNodeAddressFromSystemName(systemName); 575 if (ua == -1) { 576 return null; 577 } 578 return tc.getNodeFromAddress(ua); 579 } 580 581 /** 582 * Validate C/MRI system name for configuration. Validates node number and 583 * system prefix. 584 * 585 * @param systemName the system name to check 586 * @param type the device type 587 * @param tc the controller for the device 588 * @return true if system name has a valid meaning in current configuration; 589 * otherwise false 590 */ 591 public boolean validSystemNameConfig(String systemName, char type, SerialTrafficController tc) { 592 if (validSystemNameFormat(systemName, type) != NameValidity.VALID) { 593 // No point in trying if a valid system name format is not present 594 return false; 595 } 596 SerialNode node = (SerialNode) getNodeFromSystemName(systemName, tc); 597 if (node == null) { 598 // The node indicated by this system address is not present 599 return false; 600 } 601 int bit = getBitFromSystemName(systemName); 602 if ((type == 'T') || (type == 'L')) { 603 if ((bit <= 0) || (bit > (node.numOutputCards() * node.getNumBitsPerCard()))) { 604 // The bit is not valid for this defined Serial node 605 return false; 606 } 607 } else if (type == 'S') { 608 if ((bit <= 0) || (bit > (node.numInputCards() * node.getNumBitsPerCard()))) { 609 // The bit is not valid for this defined Serial node 610 return false; 611 } 612 } else { 613 log.error("Invalid type specification in validSystemNameConfig call"); 614 return false; 615 } 616 // System name has passed all tests 617 return true; 618 } 619 620 /** 621 * Get the serial node address from a C/MRI system name. 622 * <p> 623 * Nodes are numbered from 0 - 127. Does not check whether that node is 624 * defined on current system. 625 * 626 * @param systemName the name containing the node 627 * @return '-1' if invalid systemName format or if the node is not found. 628 */ 629 public int getNodeAddressFromSystemName(String systemName) { 630 int offset = checkSystemPrefix(systemName); 631 if (offset < 1) { 632 return -1; 633 } 634 if ((systemName.charAt(offset) != 'L') && (systemName.charAt(offset) != 'S') && (systemName.charAt(offset) != 'T')) { 635 log.error("invalid character in header field of system name: {}", systemName); 636 return -1; 637 } 638 String s = ""; 639 boolean noB = true; 640 for (int i = offset + 1; (i < systemName.length()) && noB; i++) { 641 if (systemName.charAt(i) == 'B') { 642 s = systemName.substring(offset + 1, i); 643 noB = false; 644 } 645 } 646 int ua; 647 if (noB) { 648 int num = Integer.parseInt(systemName.substring(offset + 1)); 649 if (num > 0) { 650 ua = num / 1000; 651 } else { 652 log.warn("invalid CMRI system name: {}", systemName); 653 return -1; 654 } 655 } else { 656 if (s.length() == 0) { 657 log.warn("no node address before 'B' in CMRI system name: {}", systemName); 658 return -1; 659 } else { 660 try { 661 ua = Integer.parseInt(s); 662 } catch (NumberFormatException e) { 663 log.warn("invalid character in CMRI system name: {}", systemName); 664 return -1; 665 } 666 } 667 } 668 return ua; 669 } 670 671 /** 672 * See {@link jmri.NamedBean#compareSystemNameSuffix} for background. 673 * 674 * This is a common implementation for C/MRI Lights, Sensors and Turnouts 675 * of the comparison method. 676 * @param suffix1 suffix to compare. 677 * @param suffix2 suffix to compare. 678 * @return CMRI comparison of suffixes. 679 */ 680 @CheckReturnValue 681 public static int compareSystemNameSuffix(@Nonnull String suffix1, @Nonnull String suffix2) { 682 683 // extract node numbers and bit numbers 684 int node1 = 0, node2 = 0, bit1, bit2; 685 int t; // a temporary 686 687 t = suffix1.indexOf("B"); 688 if (t < 0) t = suffix1.indexOf(":"); 689 if (t >= 0) { 690 // alt format 691 bit1 = Integer.parseInt(suffix1.substring(t+1)); 692 if (t>0) node1 = Integer.parseInt(suffix1.substring(0, t)); 693 } else { 694 // std format 695 int len = suffix1.length(); 696 bit1 = Integer.parseInt(suffix1.substring(Math.max(0, len-3))); 697 if (len>3) node1 = Integer.parseInt(suffix1.substring(0, len-3)); 698 } 699 700 t = suffix2.indexOf("B"); 701 if (t < 0) t = suffix2.indexOf(":"); 702 if (t >= 0) { 703 // alt format 704 bit2 = Integer.parseInt(suffix2.substring(t+1)); 705 if (t>0) node2 = Integer.parseInt(suffix2.substring(0, t)); 706 } else { 707 // std format 708 int len = suffix2.length(); 709 bit2 = Integer.parseInt(suffix2.substring(Math.max(0, len-3))); 710 if (len>3) node2 = Integer.parseInt(suffix2.substring(0, len-3)); 711 } 712 713 if (node1 != node2 ) return Integer.signum(node1-node2); 714 return Integer.signum(bit1-bit2); 715 } 716 717 /** 718 * Configure the common managers for CMRI connections. This puts the common 719 * manager config in one place. 720 */ 721 @Override 722 public void configureManagers() { 723 InstanceManager.setSensorManager(getSensorManager()); 724 getTrafficController().setSensorManager(getSensorManager()); 725 726 InstanceManager.setTurnoutManager(getTurnoutManager()); 727 728 InstanceManager.setLightManager(getLightManager()); 729 register(); 730 } 731 732 public SerialTurnoutManager getTurnoutManager() { 733 if (getDisabled()) { 734 return null; 735 } 736 return (SerialTurnoutManager) classObjectMap.computeIfAbsent(TurnoutManager.class, (Class<?> c) -> new SerialTurnoutManager(this)); 737 } 738 739 public SerialSensorManager getSensorManager() { 740 if (getDisabled()) { 741 return null; 742 } 743 return (SerialSensorManager) classObjectMap.computeIfAbsent(SensorManager.class, (Class<?> c) -> new SerialSensorManager(this)); 744 } 745 746 public SerialLightManager getLightManager() { 747 if (getDisabled()) { 748 return null; 749 } 750 return (SerialLightManager) classObjectMap.computeIfAbsent(LightManager.class, (Class<?> c) -> new SerialLightManager(this)); 751 } 752 753 @Override 754 protected ResourceBundle getActionModelResourceBundle() { 755 return ResourceBundle.getBundle("jmri.jmrix.cmri.CmriActionListBundle"); 756 } 757 758 @Override 759 public <B extends NamedBean> Comparator<B> getNamedBeanComparator(Class<B> type) { 760 return new NamedBeanComparator<>(); 761 } 762 763 @Override 764 public void dispose() { 765 InstanceManager.deregister(this, CMRISystemConnectionMemo.class); 766 if (cf != null) { 767 InstanceManager.deregister(cf, ComponentFactory.class); 768 } 769 super.dispose(); 770 } 771 772 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CMRISystemConnectionMemo.class); 773 774}