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