001package jmri.jmrix.nce.consist; 002 003import java.awt.Dimension; 004import java.awt.GridBagConstraints; 005import java.awt.GridBagLayout; 006import java.awt.event.ActionListener; 007import java.text.MessageFormat; 008import java.util.ArrayList; 009import java.util.List; 010import java.util.Objects; 011 012import javax.annotation.Nonnull; 013import javax.swing.*; 014 015import org.slf4j.Logger; 016import org.slf4j.LoggerFactory; 017 018import jmri.DccLocoAddress; 019import jmri.InstanceManager; 020import jmri.jmrit.roster.RosterEntry; 021import jmri.jmrit.roster.swing.RosterEntryComboBox; 022import jmri.jmrit.throttle.ThrottleFrameManager; 023import jmri.jmrix.nce.*; 024 025/** 026 * Pane for user edit of NCE Consists 027 * 028 * NCE Consists are stored in Command Station (CS) memory starting at address 029 * xF500 and ending xFAFF. NCE supports up to 127 consists, numbered 1 to 127. 030 * They track the lead loco, rear loco, and four mid locos in the consist file. 031 * NCE cabs start at consist 127 when building and reviewing consists, so we 032 * also start with 127. Consist lead locos are stored in memory locations xF500 033 * through xF5FF. Consist rear locos are stored in memory locations xF600 034 * through xF6FF. Mid consist locos (four max) are stored in memory locations 035 * xF700 through xFAFF. If a long address is in use, bits 6 and 7 of the high 036 * byte are set. Example: Long address 3 = 0xc0 0x03 Short address 3 = 0x00 0x03 037 * 038 * NCE file format: 039 * 040 * :F500 (con 0 lead loco) (con 1 lead loco) ....... (con 7 lead loco) :F510 041 * (con 8 lead loco) ........ (con 15 lead loco) . . :F5F0 (con 120 lead loco) 042 * ..... (con 127 lead loco) 043 * 044 * :F600 (con 0 rear loco) (con 1 rear loco) ....... (con 7 rear loco) . . :F6F0 045 * (con 120 rear loco) ..... (con 127 rear loco) 046 * 047 * :F700 (con 0 mid loco1) (con 0 mid loco2) (con 0 mid loco3) (con 0 mid loco4) 048 * . . :FAF0 (con 126 mid loco1) .. (con 126 mid loco4)(con 127 mid loco1) .. 049 * (con 127 mid loco4) :0000 050 * 051 * @author Dan Boudreau Copyright (C) 2007 2008 Cloned from NceConsistEditFrame 052 * by 053 * @author kcameron Copyright (C) 2010 054 */ 055public class NceConsistEditPanel extends jmri.jmrix.nce.swing.NcePanel implements 056 jmri.jmrix.nce.NceListener { 057 058 NceConsistRoster nceConsistRoster = InstanceManager.getDefault(NceConsistRoster.class); 059 060 private static final int CONSIST_MIN = 1; // NCE doesn't use consist 0 061 private static final int CONSIST_MAX = 127; 062 private static final int LOC_ADR_MIN = 0; // loco address range 063 private static final int LOC_ADR_MAX = 9999; // max range for NCE 064 private static final int LOC_ADR_REPLACE = 0x3FFF; // dummy loco address 065 066 private int consistNum = 0; // consist being worked 067 private boolean newConsist = true; // new consist is displayed 068 069 private int locoPosition = LEAD; // which loco memory bank, 0 = lead, 1 = rear, 2 = mid 070 private static final int LEAD = 0; 071 private static final int REAR = 1; 072 private static final int MID = 2; 073 074 // Verify that loco isn't already a lead or rear loco 075 private int consistNumVerify; // which consist number we're checking 076 private final int[] locoVerifyList = new int[6]; // list of locos to verify 077 private int verifyType; // type of verification 078 private static final int VERIFY_DONE = 0; 079 private static final int VERIFY_LEAD_REAR = 1; // lead or rear loco 080 private static final int VERIFY_MID_FWD = 2; // mid loco foward 081 private static final int VERIFY_MID_REV = 4; // mid loco reverse 082 private static final int VERIFY_ALL = 8; // verify all locos 083 084 private int replyLen = 0; // expected byte length 085 private int waiting = 0; // to catch responses not intended for this module 086 087 // the 16 byte reply states 088 private boolean consistSearchNext = false; // next search 089 private boolean consistSearchPrevious = false; // previous search 090 private boolean locoSearch = false; // when true searching for lead or rear loco in consist 091 092 private boolean emptyConsistSearch = false; // when true searching for an empty consist 093 private boolean verifyRosterMatch = false; // when true verify that roster matches consist in NCE CS 094 095 private static final int CONSIST_ERROR = -1; 096 private static final int ADDRESS_ERROR = -1; 097 098 private int consistCount = 0; // search count not to exceed CONSIST_MAX 099 100 private boolean refresh = false; // when true, refresh loco info from CS 101 102 // member declarations 103 JLabel textConsist = new JLabel(); 104 JLabel textStatus = new JLabel(); 105 JLabel consistStatus = new JLabel(); 106 107 // major buttons 108 JButton previousButton = new JButton(); 109 JButton nextButton = new JButton(); 110 JButton getButton = new JButton(); 111 JButton throttleButton = new JButton(); 112 JButton clearCancelButton = new JButton(); 113 JButton saveLoadButton = new JButton(); 114 JButton deleteButton = new JButton(); 115 JButton backUpButton = new JButton(); 116 JButton restoreButton = new JButton(); 117 118 // check boxes 119 JCheckBox checkBoxEmpty = new JCheckBox(); 120 JCheckBox checkBoxVerify = new JCheckBox(); 121 JCheckBox checkBoxConsist = new JCheckBox(); 122 123 // consist text field 124 JTextField consistTextField = new JTextField(4); 125 126 // labels 127 JLabel textLocomotive = new JLabel(); 128 JLabel textRoster = new JLabel(); 129 JLabel textAddress = new JLabel(); 130 JLabel textAddrType = new JLabel(); 131 JLabel textDirection = new JLabel(); 132 133 JLabel textConRoster = new JLabel(); 134 JLabel textConRoadName = new JLabel(); 135 JLabel textConRoadNumber = new JLabel(); 136 JLabel textConModel = new JLabel(); 137 138 JComboBox<String> conRosterBox = nceConsistRoster.fullRosterComboBox(); 139 140 // for padding out panel 141 JLabel space1 = new JLabel(" "); 142 JLabel space2 = new JLabel(" "); 143 JLabel space3a = new JLabel(" "); 144 JLabel space3b = new JLabel(" "); 145 JLabel space3c = new JLabel(" "); 146 JLabel space3d = new JLabel(" "); 147 148 JLabel space15 = new JLabel(" "); 149 150 // lead loco 151 JLabel textLoco1 = new JLabel(); 152 JTextField locoTextField1 = new JTextField(4); 153 JComboBox<Object> locoRosterBox1 = new RosterEntryComboBox(); 154 JButton adrButton1 = new JButton(); 155 JButton cmdButton1 = new JButton(); 156 JButton dirButton1 = new JButton(); 157 158 // rear loco 159 JLabel textLoco2 = new JLabel(); 160 JTextField locoTextField2 = new JTextField(4); 161 JComboBox<Object> locoRosterBox2 = new RosterEntryComboBox(); 162 JButton adrButton2 = new JButton(); 163 JButton cmdButton2 = new JButton(); 164 JButton dirButton2 = new JButton(); 165 166 // mid loco 167 JLabel textLoco3 = new JLabel(); 168 JTextField locoTextField3 = new JTextField(4); 169 JComboBox<Object> locoRosterBox3 = new RosterEntryComboBox(); 170 JButton adrButton3 = new JButton(); 171 JButton cmdButton3 = new JButton(); 172 JButton dirButton3 = new JButton(); 173 174 // mid loco 175 JLabel textLoco4 = new JLabel(); 176 JTextField locoTextField4 = new JTextField(4); 177 JComboBox<Object> locoRosterBox4 = new RosterEntryComboBox(); 178 JButton adrButton4 = new JButton(); 179 JButton cmdButton4 = new JButton(); 180 JButton dirButton4 = new JButton(); 181 182 // mid loco 183 JLabel textLoco5 = new JLabel(); 184 JTextField locoTextField5 = new JTextField(4); 185 JComboBox<Object> locoRosterBox5 = new RosterEntryComboBox(); 186 JButton adrButton5 = new JButton(); 187 JButton cmdButton5 = new JButton(); 188 JButton dirButton5 = new JButton(); 189 190 // mid loco 191 JLabel textLoco6 = new JLabel(); 192 JTextField locoTextField6 = new JTextField(4); 193 JComboBox<Object> locoRosterBox6 = new RosterEntryComboBox(); 194 JButton adrButton6 = new JButton(); 195 JButton cmdButton6 = new JButton(); 196 JButton dirButton6 = new JButton(); 197 198 private NceTrafficController tc = null; 199 200 public NceConsistEditPanel() { 201 super(); 202 } 203 204 /** 205 * {@inheritDoc} 206 */ 207 @Override 208 public void initContext(Object context) { 209 if (context instanceof NceSystemConnectionMemo) { 210 try { 211 initComponents((NceSystemConnectionMemo) context); 212 } catch (Exception e) { 213 log.error("NceConsistEdit initContext failed"); // NOI18N 214 } 215 } 216 } 217 218 /** 219 * {@inheritDoc} 220 */ 221 @Override 222 public String getHelpTarget() { 223 return "package.jmri.jmrix.nce.consist.NceConsistEditFrame"; 224 } 225 226 /** 227 * {@inheritDoc} 228 */ 229 @Override 230 public String getTitle() { 231 StringBuilder x = new StringBuilder(); 232 if (memo != null) { 233 x.append(memo.getUserName()); 234 } else { 235 x.append("NCE_"); // NOI18N 236 } 237 x.append(": "); 238 x.append(Bundle.getMessage("NceConsistEditTitle")); 239 return x.toString(); 240 } 241 242 /** 243 * {@inheritDoc} 244 */ 245 @Override 246 @Nonnull 247 public List<JMenu> getMenus() { 248 // build menu 249 JMenu toolMenu = new JMenu(Bundle.getMessage("MenuTools")); 250 toolMenu.add(new NceConsistRosterMenu(Bundle.getMessage("RosterTitle"), 251 jmri.jmrit.roster.swing.RosterMenu.MAINMENU, this)); 252 List<JMenu> l = new ArrayList<>(); 253 l.add(toolMenu); 254 return l; 255 } 256 257 /** 258 * {@inheritDoc} 259 */ 260 @Override 261 public void initComponents(NceSystemConnectionMemo m) { 262 this.memo = m; 263 this.tc = m.getNceTrafficController(); 264 // the following code sets the frame's initial state 265 266 textConsist.setText(Bundle.getMessage("L_Consist")); 267 268 textStatus.setText(Bundle.getMessage("L_Status")); 269 270 consistStatus.setText(Bundle.getMessage("EditStateUNKNOWN")); 271 272 previousButton.setText(Bundle.getMessage("KeyPREVIOUS")); 273 previousButton.setToolTipText(Bundle.getMessage("ToolTipPrevious")); 274 275 nextButton.setText(Bundle.getMessage("KeyNEXT")); 276 nextButton.setToolTipText(Bundle.getMessage("ToolTipNext")); 277 278 getButton.setText(Bundle.getMessage("KeyGET")); 279 getButton.setToolTipText(Bundle.getMessage("ToolTipGet")); 280 281 consistTextField.setText(Integer.toString(CONSIST_MAX)); 282 consistTextField.setToolTipText(MessageFormat.format(Bundle.getMessage("ToolTipConsist"), CONSIST_MIN, CONSIST_MAX)); 283 consistTextField.setMaximumSize(new Dimension(consistTextField 284 .getMaximumSize().width, 285 consistTextField.getPreferredSize().height)); 286 287 textLocomotive.setText(Bundle.getMessage("L_Loco")); 288 textRoster.setText(Bundle.getMessage("L_Roster")); 289 textAddress.setText(Bundle.getMessage("L_Address")); 290 textAddrType.setText(Bundle.getMessage("L_Type")); 291 textDirection.setText(Bundle.getMessage("L_Direction")); 292 293 textConRoster.setText(Bundle.getMessage("L_Consist")); 294 textConRoadName.setText(""); 295 textConRoadNumber.setText(""); 296 textConModel.setText(""); 297 298 throttleButton.setText(Bundle.getMessage("L_Throttle")); 299 throttleButton.setEnabled(true); 300 throttleButton.setToolTipText(Bundle.getMessage("ToolTipThrottle")); 301 302 clearCancelButton.setText(Bundle.getMessage("KeyCLEAR")); 303 clearCancelButton.setEnabled(false); 304 clearCancelButton.setToolTipText(Bundle.getMessage("ToolTipClear")); 305 306 saveLoadButton.setText(Bundle.getMessage("KeySAVE")); 307 saveLoadButton.setVisible(false); 308 saveLoadButton.setEnabled(false); 309 saveLoadButton.setToolTipText(Bundle.getMessage("ToolTipSave")); 310 311 deleteButton.setText(Bundle.getMessage("KeyDELETE")); 312 deleteButton.setVisible(false); 313 deleteButton.setEnabled(false); 314 deleteButton.setToolTipText(Bundle.getMessage("ToolTipDelete")); 315 316 backUpButton.setText(Bundle.getMessage("KeyBACKUP")); 317 backUpButton.setToolTipText(Bundle.getMessage("ToolTipBackup")); 318 319 restoreButton.setText(Bundle.getMessage("KeyRESTORE")); 320 restoreButton.setToolTipText(Bundle.getMessage("ToolTipRestore")); 321 322 checkBoxEmpty.setText(Bundle.getMessage("KeyEMPTY")); 323 checkBoxEmpty.setToolTipText(Bundle.getMessage("ToolTipEmpty")); 324 325 checkBoxVerify.setText(Bundle.getMessage("KeyVERIFY")); 326 checkBoxVerify.setSelected(true); 327 checkBoxVerify.setToolTipText(Bundle.getMessage("ToolTipVerify")); 328 329 checkBoxConsist.setText(Bundle.getMessage("KeyCONSIST")); 330 checkBoxConsist.setSelected(true); 331 checkBoxConsist.setToolTipText(Bundle.getMessage("ToolTipConsistCkBox")); 332 333 initLocoFields(); 334 335 setLayout(new GridBagLayout()); 336 337 // Layout the panel by rows 338 // row 0 339 addItem(textConsist, 2, 0); 340 // row 1 341 addItem(previousButton, 1, 1); 342 addItem(consistTextField, 2, 1); 343 addItem(nextButton, 3, 1); 344 addItem(checkBoxEmpty, 5, 1); 345 // row 2 346 addItem(textStatus, 0, 2); 347 addItem(consistStatus, 1, 2); 348 addItem(getButton, 2, 2); 349 addItem(checkBoxVerify, 5, 2); 350 // row 3 351 addItem(space3a, 1, 3); 352 addItem(space3b, 2, 3); 353 addItem(space3c, 3, 3); 354 addItem(space3d, 4, 3); 355 // row 4 356 addItem(textConRoster, 1, 4); 357 // row 5 358 addItem(conRosterBox, 1, 5); 359 addItem(textConRoadName, 2, 5); 360 addItem(textConRoadNumber, 3, 5); 361 addItem(textConModel, 4, 5); 362 addItem(checkBoxConsist, 5, 5); 363 initConsistRoster(conRosterBox); 364 365 // row 6 padding for looks 366 addItem(space1, 1, 6); 367 // row 7 labels 368 addItem(textLocomotive, 0, 7); 369 addItem(textRoster, 1, 7); 370 addItem(textAddress, 2, 7); 371 addItem(textAddrType, 3, 7); 372 addItem(textDirection, 4, 7); 373 374 // row 8 Lead Locomotive 375 addLocoRow(textLoco1, locoRosterBox1, locoTextField1, adrButton1, 376 dirButton1, cmdButton1, 8); 377 // row 9 Rear Locomotive 378 addLocoRow(textLoco2, locoRosterBox2, locoTextField2, adrButton2, 379 dirButton2, cmdButton2, 9); 380 // row 10 Mid Locomotive 381 addLocoRow(textLoco3, locoRosterBox3, locoTextField3, adrButton3, 382 dirButton3, cmdButton3, 10); 383 // row 11 Mid Locomotive 384 addLocoRow(textLoco4, locoRosterBox4, locoTextField4, adrButton4, 385 dirButton4, cmdButton4, 11); 386 // row 12 Mid Locomotive 387 addLocoRow(textLoco5, locoRosterBox5, locoTextField5, adrButton5, 388 dirButton5, cmdButton5, 12); 389 // row 13 Mid Locomotive 390 addLocoRow(textLoco6, locoRosterBox6, locoTextField6, adrButton6, 391 dirButton6, cmdButton6, 13); 392 393 // row 15 padding for looks 394 addItem(space15, 2, 15); 395 // row 16 396 addItem(throttleButton, 0, 16); 397 addItem(clearCancelButton, 1, 16); 398 addItem(saveLoadButton, 2, 16); 399 addItem(deleteButton, 3, 16); 400 addItem(backUpButton, 4, 16); 401 addItem(restoreButton, 5, 16); 402 403 // setup buttons 404 addButtonAction(previousButton); 405 addButtonAction(nextButton); 406 addButtonAction(getButton); 407 addButtonAction(throttleButton); 408 addButtonAction(clearCancelButton); 409 addButtonAction(saveLoadButton); 410 addButtonAction(deleteButton); 411 addButtonAction(backUpButton); 412 addButtonAction(restoreButton); 413 414 // setup checkboxes 415 addCheckBoxAction(checkBoxConsist); 416 checkBoxConsist(); 417 } 418 419 // Previous, Next, Get, Throttle, Clear/Cancel, Save/Load, Delete, Restore & Backup buttons 420 public void buttonActionPerformed(java.awt.event.ActionEvent ae) { 421 // if we're searching ignore user 422 if (consistSearchNext || consistSearchPrevious || locoSearch) { 423 return; 424 } 425 // throttle button 426 if (ae.getSource() == throttleButton) { 427 if (!validConsist()) { 428 return; 429 } 430 int locoAddr = validLocoAdr(locoTextField1.getText()); 431 boolean isLong = (adrButton1.getText().equals(Bundle.getMessage("KeyLONG"))); 432 if (locoAddr < 0) { 433 return; 434 } 435 consistNum = validConsist(consistTextField.getText()); 436 jmri.jmrit.throttle.ThrottleFrame tf 437 = InstanceManager.getDefault(ThrottleFrameManager.class).createThrottleFrame(); 438 tf.getAddressPanel().setAddress(consistNum, false); // use consist address 439 if (JOptionPane.showConfirmDialog(null, 440 Bundle.getMessage("DIALOG_Funct2Lead"), Bundle.getMessage("DIALOG_NceThrottle"), 441 JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { 442 tf.getAddressPanel().setAddress(locoAddr, isLong); // use lead loco address 443 } 444 tf.toFront(); 445 return; 446 } 447 // clear or cancel button 448 if (ae.getSource() == clearCancelButton) { 449 // button can be Clear or Cancel 450 if (clearCancelButton.getText().equals(Bundle.getMessage("KeyCLEAR"))) { 451 updateRoster(Bundle.getMessage("CLEARED")); 452 // set refresh flag to update panel 453 refresh = true; 454 killConsist(); 455 456 // must be cancel button 457 } else { 458 changeButtons(false); 459 consistNum = getConsist(); // reload panel 460 } 461 } 462 463 // save or load button 464 if (ae.getSource() == saveLoadButton) { 465 if (!validConsist()) { 466 return; 467 } 468 // check to see if user modified the roster 469 if (canLoad()) { 470 consistStatus.setText(Bundle.getMessage("EditStateOKAY")); 471 } else { 472 consistStatus.setText(Bundle.getMessage("EditStateERROR")); 473 saveLoadButton.setEnabled(false); 474 return; 475 } 476 enableAllLocoRows(false); 477 if (saveLoadButton.getText().equals(Bundle.getMessage("KeyLOAD"))) { 478 loadShift(); // get rid of empty mids! 479 updateRoster(consistTextField.getText()); 480 consistNum = validConsist(consistTextField.getText()); 481 // load right away or verify? 482 if (!verifyAllLocoAddr()) { 483 fullLoad(); 484 } 485 } else if (updateRoster(consistTextField.getText())) { 486 saveLoadButton.setEnabled(false); 487 consistNum = getConsist(); // reload panel 488 } 489 return; 490 } 491 492 // delete button 493 if (ae.getSource() == deleteButton) { 494 if (JOptionPane.showConfirmDialog(null, 495 Bundle.getMessage("DIALOG_ConfirmDelete", Objects.requireNonNull(conRosterBox.getSelectedItem())), 496 Bundle.getMessage("DIALOG_NceDelete"), 497 JOptionPane.OK_CANCEL_OPTION) != JOptionPane.OK_OPTION) { 498 return; 499 } 500 deleteRoster(); 501 changeButtons(false); // yes, clear delete button 502 return; 503 } 504 if (ae.getSource() == previousButton) { 505 consistSearchPrevious = true; 506 consistNum = getConsist(); // check for valid and kick off read 507 } 508 if (ae.getSource() == nextButton) { 509 consistSearchNext = true; 510 consistNum = getConsist(); // check for valid and kick off read 511 } 512 if (ae.getSource() == getButton) { 513 // Get Consist 514 consistNum = getConsist(); 515 } 516 if (ae.getSource() == backUpButton) { 517 Thread mb = new NceConsistBackup(tc); 518 mb.setName("Consist Backup"); 519 mb.start(); 520 } 521 if (ae.getSource() == restoreButton) { 522 Thread mr = new NceConsistRestore(tc); 523 mr.setName("Consist Restore"); 524 mr.start(); 525 } 526 } 527 528 // One of six loco command buttons, add, replace or delete 529 public void buttonActionCmdPerformed(java.awt.event.ActionEvent ae) { 530 // if we're searching ignore user 531 if (consistSearchNext || consistSearchPrevious || locoSearch) { 532 return; 533 } 534 if (consistChanged()) { 535 return; 536 } 537 if (ae.getSource() == cmdButton1) { 538 modifyLocoFields(locoRosterBox1, locoTextField1, adrButton1, 539 dirButton1, cmdButton1); 540 } 541 if (ae.getSource() == cmdButton2) { 542 modifyLocoFields(locoRosterBox2, locoTextField2, adrButton2, 543 dirButton2, cmdButton2); 544 } 545 if (ae.getSource() == cmdButton3) { 546 modifyLocoFields(locoRosterBox3, locoTextField3, adrButton3, 547 dirButton3, cmdButton3); 548 } 549 if (ae.getSource() == cmdButton4) { 550 modifyLocoFields(locoRosterBox4, locoTextField4, adrButton4, 551 dirButton4, cmdButton4); 552 } 553 if (ae.getSource() == cmdButton5) { 554 modifyLocoFields(locoRosterBox5, locoTextField5, adrButton5, 555 dirButton5, cmdButton5); 556 } 557 if (ae.getSource() == cmdButton6) { 558 modifyLocoFields(locoRosterBox6, locoTextField6, adrButton6, 559 dirButton6, cmdButton6); 560 } 561 if (updateRoster(consistTextField.getText())) { 562 saveLoadButton.setEnabled(false); 563 } 564 } 565 566 // one of six loco address type buttons 567 public void buttonActionAdrPerformed(java.awt.event.ActionEvent ae) { 568 // if we're searching ignore user 569 if (consistSearchNext || consistSearchPrevious || locoSearch) { 570 return; 571 } 572 if (consistChanged()) { 573 return; 574 } 575 if (ae.getSource() == adrButton1) { 576 toggleAdrButton(locoTextField1, adrButton1); 577 } 578 if (ae.getSource() == adrButton2) { 579 toggleAdrButton(locoTextField2, adrButton2); 580 } 581 if (ae.getSource() == adrButton3) { 582 toggleAdrButton(locoTextField3, adrButton3); 583 } 584 if (ae.getSource() == adrButton4) { 585 toggleAdrButton(locoTextField4, adrButton4); 586 } 587 if (ae.getSource() == adrButton5) { 588 toggleAdrButton(locoTextField5, adrButton5); 589 } 590 if (ae.getSource() == adrButton6) { 591 toggleAdrButton(locoTextField6, adrButton6); 592 } 593 } 594 595 private void toggleAdrButton(JTextField locoTextField, JButton adrButton) { 596 if (validLocoAdr(locoTextField.getText()) < 0) { 597 return; 598 } 599 if (locoTextField.getText().equals("")) { 600 JOptionPane.showMessageDialog(this, 601 Bundle.getMessage("DIALOG_EnterLocoB4AddrChg"), 602 Bundle.getMessage("DIALOG_NceConsist"), 603 JOptionPane.ERROR_MESSAGE); 604 return; 605 } else { 606 if (adrButton.getText().equals(Bundle.getMessage("KeyLONG"))) { 607 if ((Integer.parseInt(locoTextField.getText()) < 128) 608 && (Integer.parseInt(locoTextField.getText()) > 0)) { 609 adrButton.setText(Bundle.getMessage("KeySHORT")); 610 } 611 } else { 612 adrButton.setText(Bundle.getMessage("KeyLONG")); 613 } 614 } 615 } 616 617 // one of six loco direction buttons 618 public void buttonActionDirPerformed(java.awt.event.ActionEvent ae) { 619 // if we're searching ignore user 620 if (consistSearchNext || consistSearchPrevious || locoSearch) { 621 return; 622 } 623 if (consistChanged()) { 624 return; 625 } 626 if (ae.getSource() == dirButton1) { 627 toggleDirButton(locoTextField1, dirButton1, cmdButton1); 628 } 629 if (ae.getSource() == dirButton2) { 630 toggleDirButton(locoTextField2, dirButton2, cmdButton2); 631 } 632 if (ae.getSource() == dirButton3) { 633 toggleDirButton(locoTextField3, dirButton3, cmdButton3); 634 } 635 if (ae.getSource() == dirButton4) { 636 toggleDirButton(locoTextField4, dirButton4, cmdButton4); 637 } 638 if (ae.getSource() == dirButton5) { 639 toggleDirButton(locoTextField5, dirButton5, cmdButton5); 640 } 641 if (ae.getSource() == dirButton6) { 642 toggleDirButton(locoTextField6, dirButton6, cmdButton6); 643 } 644 saveLoadButton.setEnabled(canLoad()); 645 } 646 647 private void toggleDirButton(JTextField locoTextField, JButton dirButton, 648 JButton cmdButton) { 649 if (validLocoAdr(locoTextField.getText()) < 0) { 650 return; 651 } 652 if (locoTextField.getText().equals("")) { 653 JOptionPane.showMessageDialog(this, 654 Bundle.getMessage("DIALOG_EnterLocoB4DirChg"), 655 Bundle.getMessage("DIALOG_NceConsist"), JOptionPane.ERROR_MESSAGE); 656 return; 657 } 658 cmdButton.setEnabled(true); 659 if (dirButton.getText().equals(Bundle.getMessage("KeyFWD"))) { 660 dirButton.setText(Bundle.getMessage("KeyREV")); 661 } else { 662 dirButton.setText(Bundle.getMessage("KeyFWD")); 663 } 664 } 665 666 // one of six roster select, load loco number and address length 667 public void locoSelected(java.awt.event.ActionEvent ae) { 668 if (ae.getSource() == locoRosterBox1) { 669 rosterBoxSelect(locoRosterBox1, locoTextField1, adrButton1); 670 } 671 if (ae.getSource() == locoRosterBox2) { 672 rosterBoxSelect(locoRosterBox2, locoTextField2, adrButton2); 673 } 674 if (ae.getSource() == locoRosterBox3) { 675 rosterBoxSelect(locoRosterBox3, locoTextField3, adrButton3); 676 } 677 if (ae.getSource() == locoRosterBox4) { 678 rosterBoxSelect(locoRosterBox4, locoTextField4, adrButton4); 679 } 680 if (ae.getSource() == locoRosterBox5) { 681 rosterBoxSelect(locoRosterBox5, locoTextField5, adrButton5); 682 } 683 if (ae.getSource() == locoRosterBox6) { 684 rosterBoxSelect(locoRosterBox6, locoTextField6, adrButton6); 685 } 686 } 687 688 // load a loco from roster 689 private void rosterBoxSelect(JComboBox<Object> locoRosterBox, 690 JTextField locoTextField, JButton adrButton) { 691 RosterEntry entry = null; 692 Object o = locoRosterBox.getSelectedItem(); 693 if (o.getClass().equals(RosterEntry.class)) { 694 entry = (RosterEntry) o; 695 } 696 if (entry != null) { 697 DccLocoAddress a = entry.getDccLocoAddress(); 698 699 locoTextField.setText("" + a.getNumber()); 700 if (a.isLongAddress()) { 701 adrButton.setText(Bundle.getMessage("KeyLONG")); 702 } else { 703 adrButton.setText(Bundle.getMessage("KeySHORT")); 704 } 705 // if lead loco get road number and name 706 if (locoRosterBox == locoRosterBox1) { 707 textConRoadName.setText(entry.getRoadName()); 708 textConRoadNumber.setText(entry.getRoadNumber()); 709 textConModel.setText(entry.getModel()); 710 } 711 } 712 } 713 714 // load a consist from roster 715 public void consistRosterSelected(java.awt.event.ActionEvent ae) { 716 if (consistSearchNext || consistSearchPrevious || locoSearch) { 717 return; 718 } 719 String entry = ""; 720 entry = conRosterBox.getSelectedItem().toString(); 721 log.debug("load consist {} from roster ", entry); 722 if (entry.equals("")) { 723 changeButtons(false); 724 consistNum = getConsist(); // reload panel 725 return; 726 } 727 changeButtons(true); 728 loadRosterEntry(entry); 729 } 730 731 // checkbox modified 732 public void checkBoxActionPerformed(java.awt.event.ActionEvent ae) { 733 if (ae.getSource() == checkBoxConsist) { 734 checkBoxConsist(); 735 } 736 } 737 738 private void checkBoxConsist() { 739 if (checkBoxConsist.isSelected()) { 740 conRosterBox.setEnabled(true); 741 saveLoadButton.setVisible(true); 742 saveLoadButton.setEnabled(canLoad()); 743 deleteButton.setVisible(true); 744 } else { 745 conRosterBox.setEnabled(false); 746 conRosterBox.removeActionListener(consistRosterListener); 747 conRosterBox.setSelectedIndex(0); 748 conRosterBox.addActionListener(consistRosterListener); 749 saveLoadButton.setVisible(false); 750 saveLoadButton.setEnabled(false); 751 deleteButton.setVisible(false); 752 deleteButton.setEnabled(false); 753 } 754 } 755 756 // gets the user supplied consist number and then reads NCE CS memory 757 private int getConsist() { 758 newConsist = true; 759 int consistNumber = validConsist(consistTextField.getText()); 760 if (consistNumber == CONSIST_ERROR) { 761 consistSearchPrevious = false; 762 consistSearchNext = false; 763 return consistNumber; 764 } 765 if (consistSearchNext || consistSearchPrevious) { 766 consistCount = 0; // used to determine if all 127 consist have been read 767 consistStatus.setText(Bundle.getMessage("EditStateSEARCH")); 768 } else { 769 consistStatus.setText(Bundle.getMessage("EditStateWAIT")); 770 if (consistNumber == consistNum) { 771 newConsist = false; 772 } 773 } 774 775 // if busy don't request 776 if (waiting > 0) { 777 return consistNumber; 778 } 779 780 if (consistSearchNext) { 781 readConsistMemory(consistNumber - 7, LEAD); 782 } else { 783 readConsistMemory(consistNumber, LEAD); // Get or Previous button 784 } 785 return consistNumber; 786 } 787 788 /** 789 * Check for valid consist, popup error message if not 790 * 791 * @return true if valid 792 */ 793 private boolean validConsist() { 794 int consistNumber = validConsist(consistTextField.getText()); 795 if (consistNumber == CONSIST_ERROR) { 796 consistStatus.setText(Bundle.getMessage("EditStateERROR")); 797 JOptionPane.showMessageDialog(this, 798 MessageFormat.format(Bundle.getMessage("ToolTipConsist"), new Object[] {CONSIST_MIN, CONSIST_MAX}), Bundle.getMessage("DIALOG_NceConsist"), 799 JOptionPane.ERROR_MESSAGE); 800 return false; 801 } 802 return true; 803 } 804 805 // Check for valid consist number, return number if valid, -1 or CONSIST_ERROR if not. 806 private int validConsist(String s) { 807 int consistNumber; 808 try { 809 consistNumber = Integer.parseInt(s); 810 } catch (NumberFormatException e) { 811 return CONSIST_ERROR; 812 } 813 if (consistNumber < CONSIST_MIN || consistNumber > CONSIST_MAX) { 814 return CONSIST_ERROR; 815 } else { 816 return consistNumber; 817 } 818 } 819 820 /** 821 * @param s loco address 822 * @return number if valid, -1 or ADDRESS_ERROR if not 823 */ 824 private int validLocoAdr(String s) { 825 int locoAddress; 826 try { 827 locoAddress = Integer.parseInt(s); 828 } catch (NumberFormatException e) { 829 locoAddress = ADDRESS_ERROR; 830 } 831 if (locoAddress < LOC_ADR_MIN || locoAddress > LOC_ADR_MAX) { 832 JOptionPane.showMessageDialog(this, 833 Bundle.getMessage("DIALOG_AddrRange"), Bundle.getMessage("DIALOG_NceConsist"), 834 JOptionPane.ERROR_MESSAGE); 835 return ADDRESS_ERROR; 836 } else { 837 return locoAddress; 838 } 839 } 840 841 // check to see if user modified consist number 842 private boolean consistChanged() { 843 if (consistNum != validConsist(consistTextField.getText())) { 844 JOptionPane.showMessageDialog(this, 845 Bundle.getMessage("DIALOG_PressRead", Bundle.getMessage("KeyGET")), 846 Bundle.getMessage("DIALOG_NceConsist"), 847 JOptionPane.ERROR_MESSAGE); 848 return true; 849 } else { 850 newConsist = false; 851 return false; 852 } 853 } 854 855 /** 856 * Reads 16 bytes of NCE consist memory based on consist number and loco 857 * position in the consist 0=lead 1=rear 2=mid 858 */ 859 private void readConsistMemory(int consistNum, int engPosition) { 860 locoPosition = engPosition; 861 int nceMemAddr = (consistNum * 2) + NceCmdStationMemory.CabMemorySerial.CS_CONSIST_MEM; 862 if (locoPosition == REAR) { 863 nceMemAddr = (consistNum * 2) + NceCmdStationMemory.CabMemorySerial.CS_CON_MEM_REAR; 864 } 865 if (locoPosition == MID) { 866 nceMemAddr = (consistNum * 8) + NceCmdStationMemory.CabMemorySerial.CS_CON_MEM_MID; 867 } 868 log.debug("Read consist ({}) position ({}) NCE memory address ({})", consistNum, engPosition, Integer.toHexString(nceMemAddr)); 869 byte[] bl = NceBinaryCommand.accMemoryRead(nceMemAddr); 870 sendNceMessage(bl, NceMessage.REPLY_16); 871 } 872 873 NceConsistRosterEntry nceConsistRosterEntry; 874 875 private void loadRosterEntry(String entry) { 876 nceConsistRosterEntry = nceConsistRoster.entryFromTitle(entry); 877 consistTextField.setText(nceConsistRosterEntry.getConsistNumber()); 878 int cNum = validConsist(nceConsistRosterEntry.getConsistNumber()); 879 880 if (0 < cNum) { 881 log.debug("verify consist matches roster selection"); 882 verifyRosterMatch = true; 883 consistNum = getConsist(); 884 } else { 885 if (nceConsistRosterEntry.getConsistNumber().equals(Bundle.getMessage("CLEARED")) || nceConsistRosterEntry.getConsistNumber().equals("0")) { 886 log.debug("search for empty consist"); 887 consistTextField.setText(Integer.toString(CONSIST_MAX)); 888 emptyConsistSearch = true; 889 consistSearchNext = true; 890 consistNum = getConsist(); 891 loadFullRoster(nceConsistRosterEntry); 892 saveLoadButton.setEnabled(false); 893 } else { 894 log.error("roster consist number is out of range: {}", consistNum); 895 consistStatus.setText(Bundle.getMessage("EditStateERROR")); 896 } 897 } 898 } 899 900 private void loadFullRoster(NceConsistRosterEntry nceConsistRosterEntry) { 901 // get road name, number and model 902 textConRoadName.setText(nceConsistRosterEntry.getRoadName()); 903 textConRoadNumber.setText(nceConsistRosterEntry.getRoadNumber()); 904 textConModel.setText(nceConsistRosterEntry.getModel()); 905 906 // load lead loco 907 locoTextField1.setText(nceConsistRosterEntry.getLoco1DccAddress()); 908 adrButton1.setText(nceConsistRosterEntry.isLoco1LongAddress() ? Bundle.getMessage("KeyLONG") : Bundle.getMessage("KeySHORT")); 909 dirButton1.setText(convertDTD(nceConsistRosterEntry.getLoco1Direction())); 910 locoRosterBox1.setEnabled(true); 911 locoTextField1.setEnabled(true); 912 adrButton1.setEnabled(true); 913 dirButton1.setEnabled(true); 914 915 // load rear loco 916 locoTextField2.setText(nceConsistRosterEntry.getLoco2DccAddress()); 917 adrButton2.setText(nceConsistRosterEntry.isLoco2LongAddress() ? Bundle.getMessage("KeyLONG") : Bundle.getMessage("KeySHORT")); 918 dirButton2.setText(convertDTD(nceConsistRosterEntry.getLoco2Direction())); 919 locoRosterBox2.setEnabled(true); 920 locoTextField2.setEnabled(true); 921 adrButton2.setEnabled(true); 922 dirButton2.setEnabled(true); 923 924 // load Mid1 loco 925 locoTextField3.setText(nceConsistRosterEntry.getLoco3DccAddress()); 926 adrButton3.setText(nceConsistRosterEntry.isLoco3LongAddress() ? Bundle.getMessage("KeyLONG") : Bundle.getMessage("KeySHORT")); 927 dirButton3.setText(convertDTD(nceConsistRosterEntry.getLoco3Direction())); 928 locoRosterBox3.setEnabled(true); 929 locoTextField3.setEnabled(true); 930 adrButton3.setEnabled(true); 931 dirButton3.setEnabled(true); 932 933 // load Mid2 loco 934 locoTextField4.setText(nceConsistRosterEntry.getLoco4DccAddress()); 935 adrButton4.setText(nceConsistRosterEntry.isLoco4LongAddress() ? Bundle.getMessage("KeyLONG") : Bundle.getMessage("KeySHORT")); 936 dirButton4.setText(convertDTD(nceConsistRosterEntry.getLoco4Direction())); 937 locoRosterBox4.setEnabled(true); 938 locoTextField4.setEnabled(true); 939 adrButton4.setEnabled(true); 940 dirButton4.setEnabled(true); 941 942 // load Mid3 loco 943 locoTextField5.setText(nceConsistRosterEntry.getLoco5DccAddress()); 944 adrButton5.setText(nceConsistRosterEntry.isLoco5LongAddress() ? Bundle.getMessage("KeyLONG") : Bundle.getMessage("KeySHORT")); 945 dirButton5.setText(convertDTD(nceConsistRosterEntry.getLoco5Direction())); 946 locoRosterBox5.setEnabled(true); 947 locoTextField5.setEnabled(true); 948 adrButton5.setEnabled(true); 949 dirButton5.setEnabled(true); 950 951 // load Mid4 loco 952 locoTextField6.setText(nceConsistRosterEntry.getLoco6DccAddress()); 953 adrButton6.setText(nceConsistRosterEntry.isLoco6LongAddress() ? Bundle.getMessage("KeyLONG") : Bundle.getMessage("KeySHORT")); 954 dirButton6.setText(convertDTD(nceConsistRosterEntry.getLoco6Direction())); 955 locoRosterBox6.setEnabled(true); 956 locoTextField6.setEnabled(true); 957 adrButton6.setEnabled(true); 958 dirButton6.setEnabled(true); 959 } 960 961 /** 962 * checks to see if all loco addresses in NCE consist match roster updates 963 * road name, road number, and loco direction fields 964 * 965 * @return true if match 966 */ 967 private boolean consistRosterMatch(NceConsistRosterEntry nceConsistRosterEntry) { 968 if (consistTextField.getText().equals(nceConsistRosterEntry.getConsistNumber()) 969 && locoTextField1.getText().equals(nceConsistRosterEntry.getLoco1DccAddress()) 970 && locoTextField2.getText().equals(nceConsistRosterEntry.getLoco2DccAddress()) 971 && locoTextField3.getText().equals(nceConsistRosterEntry.getLoco3DccAddress()) 972 && locoTextField4.getText().equals(nceConsistRosterEntry.getLoco4DccAddress()) 973 && locoTextField5.getText().equals(nceConsistRosterEntry.getLoco5DccAddress()) 974 && locoTextField6.getText().equals(nceConsistRosterEntry.getLoco6DccAddress())) { 975 // match! Only load the elements needed 976 if (newConsist) { 977 textConRoadName.setText(nceConsistRosterEntry.getRoadName()); 978 textConRoadNumber.setText(nceConsistRosterEntry.getRoadNumber()); 979 textConModel.setText(nceConsistRosterEntry.getModel()); 980 dirButton1.setText(convertDTD(nceConsistRosterEntry.getLoco1Direction())); 981 dirButton2.setText(convertDTD(nceConsistRosterEntry.getLoco2Direction())); 982 dirButton3.setText(convertDTD(nceConsistRosterEntry.getLoco3Direction())); 983 dirButton4.setText(convertDTD(nceConsistRosterEntry.getLoco4Direction())); 984 dirButton5.setText(convertDTD(nceConsistRosterEntry.getLoco5Direction())); 985 dirButton6.setText(convertDTD(nceConsistRosterEntry.getLoco6Direction())); 986 } 987 return true; 988 } else { 989 return false; 990 } 991 } 992 993 private final boolean enablePartialMatch = true; 994 995 /** 996 * checks to see if some loco addresses in NCE consist match roster updates 997 * road name, road number, and loco direction fields 998 * 999 * @return true if there was at least one match 1000 */ 1001 private boolean consistRosterPartialMatch(NceConsistRosterEntry cre) { 1002 if (!enablePartialMatch) { 1003 return false; 1004 } 1005 // does loco1 match? 1006 if (consistTextField.getText().equals(cre.getConsistNumber()) 1007 && locoTextField1.getText().equals(cre.getLoco1DccAddress())) { 1008 dirButton1.setText(convertDTD(cre.getLoco1Direction())); 1009 textConRoadName.setText(cre.getRoadName()); 1010 textConRoadNumber.setText(cre.getRoadNumber()); 1011 textConModel.setText(cre.getModel()); 1012 } else { 1013 consistStatus.setText(Bundle.getMessage("EditStateUNKNOWN")); 1014 return false; 1015 } 1016 if (locoTextField2.getText().equals(cre.getLoco2DccAddress())) { 1017 dirButton2.setText(convertDTD(cre.getLoco2Direction())); 1018 } 1019 if (locoTextField3.getText().equals(cre.getLoco3DccAddress())) { 1020 dirButton3.setText(convertDTD(cre.getLoco3Direction())); 1021 } 1022 if (locoTextField4.getText().equals(cre.getLoco4DccAddress())) { 1023 dirButton4.setText(convertDTD(cre.getLoco4Direction())); 1024 } 1025 if (locoTextField5.getText().equals(cre.getLoco5DccAddress())) { 1026 dirButton5.setText(convertDTD(cre.getLoco5Direction())); 1027 } 1028 if (locoTextField6.getText().equals(cre.getLoco6DccAddress())) { 1029 dirButton6.setText(convertDTD(cre.getLoco6Direction())); 1030 } 1031 consistStatus.setText(Bundle.getMessage("EditStateMODIFIED")); 1032 return true; 1033 } 1034 1035 protected List<NceConsistRosterEntry> consistList = new ArrayList<>(); 1036 1037 /** 1038 * returns true if update successful 1039 */ 1040 private boolean updateRoster(String consistNumber) { 1041 if (!checkBoxConsist.isSelected()) { 1042 return false; 1043 } 1044 String id = locoTextField1.getText(); // lead loco is the consist id 1045 if (id.equals("")) { 1046 log.debug("Attempt to modify consist without valid id"); 1047 return false; 1048 } 1049 // need rear loco to form a consist 1050 if (locoTextField2.getText().equals("")) { 1051 return false; 1052 } 1053 NceConsistRosterEntry nceConsistRosterEntry; 1054 consistList = nceConsistRoster.matchingList(null, null, 1055 null, null, null, null, null, null, null, id); 1056 // if consist doesn't exist in roster ask user if they want to create one 1057 if (consistList.isEmpty()) { 1058 if (JOptionPane.showConfirmDialog(null, Bundle.getMessage("DIALOG_ConfirmAdd", id), 1059 Bundle.getMessage("DIALOG_NceSave"), 1060 JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) { 1061 return false; 1062 } 1063 nceConsistRosterEntry = new NceConsistRosterEntry(); 1064 nceConsistRoster.addEntry(nceConsistRosterEntry); 1065 // roster entry exists, does it match? 1066 } else { 1067 nceConsistRosterEntry = nceConsistRoster.entryFromTitle(id); 1068 // if all of the loco addresses match, just update without telling user 1069 consistList = nceConsistRoster 1070 .matchingList(null, null, null, locoTextField1.getText(), 1071 locoTextField2.getText(), locoTextField3.getText(), 1072 locoTextField4.getText(), locoTextField5.getText(), 1073 locoTextField6.getText(), id); 1074 // if it doesn't match, do we want to modify it? 1075 if (consistList.isEmpty()) { 1076 if (JOptionPane.showConfirmDialog(null, 1077 Bundle.getMessage("DIALOG_ConfirmUpdate", id, getRosterText(nceConsistRosterEntry)), 1078 Bundle.getMessage("DIALOG_NceUpdate"), 1079 JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION) { 1080 // update consist if command was to clear 1081 if (consistNumber.equals(Bundle.getMessage("CLEARED"))) { 1082 nceConsistRosterEntry.setConsistNumber(consistNumber); 1083 writeRosterFile(); 1084 } 1085 return false; 1086 } 1087 } 1088 log.debug("Modify consist {}", id); 1089 } 1090 // save all elements of a consist roster 1091 nceConsistRosterEntry.setId(id); 1092 nceConsistRosterEntry.setConsistNumber(consistNumber); 1093 nceConsistRosterEntry.setRoadName(textConRoadName.getText()); 1094 nceConsistRosterEntry.setRoadNumber(textConRoadNumber.getText()); 1095 nceConsistRosterEntry.setModel(textConModel.getText()); 1096 // save lead loco 1097 nceConsistRosterEntry.setLoco1DccAddress(locoTextField1.getText()); 1098 nceConsistRosterEntry.setLoco1LongAddress(adrButton1.getText().equals(Bundle.getMessage("KeyLONG"))); 1099 nceConsistRosterEntry.setLoco1Direction(directionDTD(dirButton1)); 1100 // save rear loco 1101 nceConsistRosterEntry.setLoco2DccAddress(locoTextField2.getText()); 1102 nceConsistRosterEntry.setLoco2LongAddress(adrButton2.getText().equals(Bundle.getMessage("KeyLONG"))); 1103 nceConsistRosterEntry.setLoco2Direction(directionDTD(dirButton2)); 1104 // save Mid1 loco 1105 nceConsistRosterEntry.setLoco3DccAddress(locoTextField3.getText()); 1106 nceConsistRosterEntry.setLoco3LongAddress(adrButton3.getText().equals(Bundle.getMessage("KeyLONG"))); 1107 nceConsistRosterEntry.setLoco3Direction(directionDTD(dirButton3)); 1108 // save Mid2 loco 1109 nceConsistRosterEntry.setLoco4DccAddress(locoTextField4.getText()); 1110 nceConsistRosterEntry.setLoco4LongAddress(adrButton4.getText().equals(Bundle.getMessage("KeyLONG"))); 1111 nceConsistRosterEntry.setLoco4Direction(directionDTD(dirButton4)); 1112 // save Mid3 loco 1113 nceConsistRosterEntry.setLoco5DccAddress(locoTextField5.getText()); 1114 nceConsistRosterEntry.setLoco5LongAddress(adrButton5.getText().equals(Bundle.getMessage("KeyLONG"))); 1115 nceConsistRosterEntry.setLoco5Direction(directionDTD(dirButton5)); 1116 // save Mid4 loco 1117 nceConsistRosterEntry.setLoco6DccAddress(locoTextField6.getText()); 1118 nceConsistRosterEntry.setLoco6LongAddress(adrButton6.getText().equals(Bundle.getMessage("KeyLONG"))); 1119 nceConsistRosterEntry.setLoco6Direction(directionDTD(dirButton6)); 1120 1121 writeRosterFile(); 1122 return true; 1123 } 1124 1125 /** 1126 * @return DTD direction format based on the loco direction button 1127 */ 1128 private String directionDTD(JButton dirButton) { 1129 String formatDTD = Bundle.getMessage("DTD_UNKNOWN"); 1130 if (dirButton.getText().equals(Bundle.getMessage("KeyFWD"))) { 1131 formatDTD = Bundle.getMessage("DTD_FORWARD"); 1132 } 1133 if (dirButton.getText().equals(Bundle.getMessage("KeyREV"))) { 1134 formatDTD = Bundle.getMessage("DTD_REVERSE"); 1135 } 1136 return formatDTD; 1137 } 1138 1139 /** 1140 * @return converts DTD direction to FWD, REV, and ?? 1141 */ 1142 private String convertDTD(String formatDTD) { 1143 String word = Bundle.getMessage("KeyQUESTION"); 1144 if (formatDTD.equals(Bundle.getMessage("DTD_FORWARD"))) { 1145 word = Bundle.getMessage("KeyFWD"); 1146 } 1147 if (formatDTD.equals(Bundle.getMessage("DTD_REVERSE"))) { 1148 word = Bundle.getMessage("KeyREV"); 1149 } 1150 return word; 1151 } 1152 1153 /** 1154 * @return converts DTD direction to FWD, REV, and "" 1155 */ 1156 private String shortHandConvertDTD(String formatDTD) { 1157 String word = ""; 1158 if (formatDTD.equals(Bundle.getMessage("DTD_FORWARD"))) { 1159 word = Bundle.getMessage("KeyFWD"); 1160 } 1161 if (formatDTD.equals(Bundle.getMessage("DTD_REVERSE"))) { 1162 word = Bundle.getMessage("KeyREV"); 1163 } 1164 return word; 1165 } 1166 1167 // remove selected consist from roster 1168 private void deleteRoster() { 1169 String entry = conRosterBox.getSelectedItem().toString(); 1170 log.debug("remove consist {} from roster ", entry); 1171 // delete it from roster 1172 nceConsistRoster.removeEntry(nceConsistRoster.entryFromTitle(entry)); 1173 writeRosterFile(); 1174 } 1175 1176 private void writeRosterFile() { 1177 conRosterBox.removeActionListener(consistRosterListener); 1178 nceConsistRoster.writeRosterFile(); 1179 nceConsistRoster.updateComboBox(conRosterBox); 1180 conRosterBox.insertItemAt("", 0); 1181 conRosterBox.setSelectedIndex(0); 1182 conRosterBox.addActionListener(consistRosterListener); 1183 } 1184 1185 // can the consist be loaded into NCE memory? 1186 private boolean canLoad() { 1187 if (locoTextField1.getText().equals("")) { 1188 return false; 1189 } 1190 if (dirButton1.getText().equals(Bundle.getMessage("KeyQUESTION"))) { 1191 return false; 1192 } 1193 if (locoTextField2.getText().equals("")) { 1194 return false; 1195 } 1196 if (dirButton2.getText().equals(Bundle.getMessage("KeyQUESTION"))) { 1197 return false; 1198 } 1199 if (!locoTextField3.getText().equals("") 1200 && dirButton3.getText().equals(Bundle.getMessage("KeyQUESTION"))) { 1201 return false; 1202 } 1203 if (!locoTextField4.getText().equals("") 1204 && dirButton4.getText().equals(Bundle.getMessage("KeyQUESTION"))) { 1205 return false; 1206 } 1207 if (!locoTextField5.getText().equals("") 1208 && dirButton5.getText().equals(Bundle.getMessage("KeyQUESTION"))) { 1209 return false; 1210 } 1211 if (!locoTextField6.getText().equals("") 1212 && dirButton6.getText().equals(Bundle.getMessage("KeyQUESTION"))) { 1213 return false; 1214 } 1215 // okay to load, clean up empty loco fields 1216 if (locoTextField3.getText().equals("")) { 1217 dirButton3.setText(Bundle.getMessage("KeyQUESTION")); 1218 } 1219 if (locoTextField4.getText().equals("")) { 1220 dirButton4.setText(Bundle.getMessage("KeyQUESTION")); 1221 } 1222 if (locoTextField5.getText().equals("")) { 1223 dirButton5.setText(Bundle.getMessage("KeyQUESTION")); 1224 } 1225 if (locoTextField6.getText().equals("")) { 1226 dirButton6.setText(Bundle.getMessage("KeyQUESTION")); 1227 } 1228 if (saveLoadButton.getText().equals(Bundle.getMessage("KeyLOAD"))) { 1229 return true; 1230 } else if (exactMatch) { 1231 return false; // no need to save, exact match! 1232 } else { 1233 return true; 1234 } 1235 } 1236 1237 // mimic NCE mid loco shift when there's empties 1238 private void loadShift() { 1239 for (int i = 0; i < 3; i++) { 1240 shiftOneLine(locoTextField5, adrButton5, dirButton5, locoTextField6, 1241 adrButton6, dirButton6); 1242 shiftOneLine(locoTextField4, adrButton4, dirButton4, locoTextField5, 1243 adrButton5, dirButton5); 1244 shiftOneLine(locoTextField3, adrButton3, dirButton3, locoTextField4, 1245 adrButton4, dirButton4); 1246 shiftOneLine(locoTextField2, adrButton2, dirButton2, locoTextField3, 1247 adrButton3, dirButton3); 1248 } 1249 } 1250 1251 private void shiftOneLine(JTextField locoTextFieldLow, JButton adrButtonLow, 1252 JButton dirButtonLow, JTextField locoTextFieldHigh, 1253 JButton adrButtonHigh, JButton dirButtonHigh) { 1254 if (locoTextFieldLow.getText().equals("") && !locoTextFieldHigh.getText().equals((""))) { 1255 locoTextFieldLow.setText(locoTextFieldHigh.getText()); 1256 adrButtonLow.setText(adrButtonHigh.getText()); 1257 dirButtonLow.setText(dirButtonHigh.getText()); 1258 dirButtonHigh.setText(Bundle.getMessage("KeyQUESTION")); 1259 locoTextFieldHigh.setText(""); 1260 } else { 1261 return; 1262 } 1263 } 1264 1265 // change button operation during load consist from roster 1266 private void changeButtons(boolean rosterDisplay) { 1267 if (rosterDisplay) { 1268 clearCancelButton.setText(Bundle.getMessage("KeyCANCEL")); 1269 clearCancelButton.setToolTipText(Bundle.getMessage("ToolTipCancel")); 1270 clearCancelButton.setEnabled(true); 1271 saveLoadButton.setText(Bundle.getMessage("KeyLOAD")); 1272 saveLoadButton.setToolTipText(Bundle.getMessage("ToolTipLoad")); 1273 } else { 1274 clearCancelButton.setText(Bundle.getMessage("KeyCLEAR")); 1275 clearCancelButton.setToolTipText(Bundle.getMessage("ToolTipClear")); 1276 saveLoadButton.setText(Bundle.getMessage("KeySAVE")); 1277 saveLoadButton.setToolTipText(Bundle.getMessage("ToolTipSave")); 1278 clearCancelButton.setEnabled(!locoTextField1.getText().equals("")); 1279 } 1280 1281 // toggle (on if we're loading a consist from roster) 1282 deleteButton.setEnabled(rosterDisplay); 1283 1284 // toggle (off if we're loading a consist from roster) 1285 previousButton.setEnabled(!rosterDisplay); 1286 nextButton.setEnabled(!rosterDisplay); 1287 getButton.setEnabled(!rosterDisplay); 1288 backUpButton.setEnabled(!rosterDisplay); 1289 restoreButton.setEnabled(!rosterDisplay); 1290 saveLoadButton.setEnabled(!rosterDisplay); 1291 1292 cmdButton1.setVisible(!rosterDisplay); 1293 cmdButton2.setVisible(!rosterDisplay); 1294 cmdButton3.setVisible(!rosterDisplay); 1295 cmdButton4.setVisible(!rosterDisplay); 1296 cmdButton5.setVisible(!rosterDisplay); 1297 cmdButton6.setVisible(!rosterDisplay); 1298 } 1299 1300 /** 1301 * Kills consist using lead loco address 1302 */ 1303 private void killConsist() { 1304 if (validLocoAdr(locoTextField1.getText()) < 0) // special case where lead or rear loco was being replaced 1305 { 1306 return; 1307 } 1308 int locoAddr = getLocoAddr(locoTextField1, adrButton1); 1309 sendNceBinaryCommand(locoAddr, NceMessage.LOCO_CMD_KILL_CONSIST, 1310 (byte) 0); 1311 } 1312 1313 private void sendNceBinaryCommand(int locoAddr, byte nceLocoCmd, 1314 byte consistNumber) { 1315 byte[] bl = NceBinaryCommand.nceLocoCmd(locoAddr, nceLocoCmd, 1316 consistNumber); 1317 sendNceMessage(bl, NceMessage.REPLY_1); 1318 } 1319 1320 @Override 1321 public void message(NceMessage m) { 1322 } // ignore replies 1323 1324 // NCE CS response from add, delete, save, get, next, previous, etc 1325 // A single byte response is expected from commands 1326 // A 16 byte response is expected when loading a consist or searching 1327 @Override 1328 public void reply(NceReply nceReply) { 1329 if (waiting <= 0) { 1330 log.error("unexpected response"); 1331 return; 1332 } 1333 waiting--; 1334 1335 if (nceReply.getNumDataElements() != replyLen) { 1336 consistStatus.setText(Bundle.getMessage("EditStateERROR")); 1337 log.error("reply length error, expecting: {} got: {}", replyLen, nceReply.getNumDataElements()); 1338 return; 1339 } 1340 1341 // response to commands 1342 if (replyLen == NceMessage.REPLY_1) { 1343 // Looking for proper response 1344 int recChar = nceReply.getElement(0); 1345 log.debug("command reply: {}", recChar); 1346 if (recChar == NceMessage.NCE_OKAY) { 1347 if (locoSearch && waiting == 0) { 1348 readConsistMemory(consistNumVerify, LEAD); 1349 consistStatus.setText(Bundle.getMessage("EditStateVERIFY")); 1350 return; 1351 } 1352 if (refresh && waiting == 0) { 1353 refresh = false; 1354 // update panel 1355 readConsistMemory(consistNum, LEAD); 1356 return; 1357 } 1358 consistStatus.setText(Bundle.getMessage("EditStateOKAY")); 1359 } else { 1360 consistStatus.setText(Bundle.getMessage("EditStateERROR")); 1361 } 1362 return; 1363 } 1364 1365 // Consist memory read 1366 if (replyLen == NceMessage.REPLY_16) { 1367 // are we verifying that loco isn't already part of a consist? 1368 if (locoSearch) { 1369 // search the 16 bytes for a loco match 1370 for (int i = 0; i < NceMessage.REPLY_16;) { 1371 int recChar_High = nceReply.getElement(i++); 1372 recChar_High = (recChar_High << 8) & 0xFF00; 1373 int recChar_Low = nceReply.getElement(i++); 1374 recChar_Low = recChar_Low & 0xFF; 1375 int locoAddress = recChar_High + recChar_Low; 1376 // does it match any of the locos? 1377 for (int j = 0; j < locoVerifyList.length; j++) { 1378 if (locoVerifyList[j] == 0) { 1379 break; // done searching 1380 } 1381 if (locoAddress == locoVerifyList[j]) { 1382 // ignore matching the consist that we're adding the 1383 // loco 1384 if (consistNumVerify != consistNum) { 1385 locoSearch = false; // quit the search 1386 consistStatus.setText(Bundle.getMessage("EditStateERROR")); 1387 locoNumInUse = locoAddress & 0x3FFF; 1388 queueError(ERROR_LOCO_IN_USE); 1389 return; 1390 } 1391 } 1392 } 1393 consistNumVerify++; 1394 } 1395 if (consistNumVerify > CONSIST_MAX) { 1396 if (locoPosition == LEAD) { 1397 // now verify the rear loco consist 1398 locoPosition = REAR; 1399 consistNumVerify = 0; 1400 } else { 1401 // verify complete, loco address is unique 1402 locoSearch = false; 1403 consistStatus.setText(Bundle.getMessage("EditStateOKAY")); 1404 // determine the type of verification 1405 if (verifyType == VERIFY_LEAD_REAR) { 1406 if (refresh && waiting == 0) { 1407 refresh = false; 1408 // update panel 1409 readConsistMemory(consistNum, LEAD); 1410 } 1411 } else if (verifyType == VERIFY_MID_FWD) { 1412 sendNceBinaryCommand(locoVerifyList[0], 1413 NceMessage.LOCO_CMD_FWD_CONSIST_MID, 1414 (byte) consistNum); 1415 } else if (verifyType == VERIFY_MID_REV) { 1416 sendNceBinaryCommand(locoVerifyList[0], 1417 NceMessage.LOCO_CMD_REV_CONSIST_MID, 1418 (byte) consistNum); 1419 } else if (verifyType == VERIFY_ALL) { 1420 fullLoad(); 1421 } else { 1422 log.debug("verifyType out of range"); 1423 } 1424 verifyType = VERIFY_DONE; 1425 return; 1426 } 1427 } 1428 // continue verify 1429 readConsistMemory(consistNumVerify, locoPosition); 1430 return; 1431 } 1432 // are we searching for a consist? 1433 if (consistSearchNext) { 1434 for (int i = NceMessage.REPLY_16 - 1; i > 0;) { 1435 int recChar_Low = nceReply.getElement(i--); 1436 recChar_Low = recChar_Low & 0xFF; 1437 int recChar_High = nceReply.getElement(i--); 1438 recChar_High = (recChar_High << 8) & 0xFF00; 1439 int locoAddress = recChar_High + recChar_Low; 1440 1441 if (emptyConsistSearch) { 1442 if (locoAddress == 0) { 1443 // found an empty consist! 1444 consistSearchNext = false; 1445 emptyConsistSearch = false; 1446 consistStatus.setText(Bundle.getMessage("EditStateOKAY")); 1447 saveLoadButton.setEnabled(canLoad()); 1448 return; 1449 } 1450 } else if (checkBoxEmpty.isSelected()) { 1451 if (locoAddress == 0 && consistCount > 0) { 1452 // found an empty consist! 1453 log.debug("Empty consist ({})", consistNum); 1454 consistSearchNext = false; 1455 // update the panel 1456 readConsistMemory(consistNum, LEAD); 1457 return; 1458 } 1459 } else if (locoAddress != 0 && consistCount > 0) { 1460 // found a consist! 1461 consistSearchNext = false; 1462 readConsistMemory(consistNum, LEAD); 1463 return; 1464 } 1465 if (++consistCount > CONSIST_MAX) { 1466 // could not find a consist 1467 consistSearchNext = false; 1468 consistStatus.setText(Bundle.getMessage("EditStateNONE")); 1469 if (emptyConsistSearch) { 1470 emptyConsistSearch = false; 1471 queueError(ERROR_NO_EMPTY_CONSIST); 1472 } 1473 return; // don't update panel 1474 } 1475 // look for next consist 1476 consistNum--; 1477 if (consistNum < CONSIST_MIN) { 1478 consistNum = CONSIST_MAX; 1479 } 1480 consistTextField.setText(Integer.toString(consistNum)); 1481 if (consistNum == CONSIST_MAX) { 1482 // we need to read NCE memory to continue 1483 break; 1484 } 1485 } 1486 // continue searching 1487 readConsistMemory(consistNum - 7, LEAD); 1488 return; 1489 } 1490 // are we searching? 1491 if (consistSearchPrevious) { 1492 for (int i = 0; i < NceMessage.REPLY_16;) { 1493 int recChar_High = nceReply.getElement(i++); 1494 recChar_High = (recChar_High << 8) & 0xFF00; 1495 int recChar_Low = nceReply.getElement(i++); 1496 recChar_Low = recChar_Low & 0xFF; 1497 int locoAddress = recChar_High + recChar_Low; 1498 1499 if (checkBoxEmpty.isSelected()) { 1500 if (locoAddress == 0 && consistCount > 0) { 1501 consistSearchPrevious = false; 1502 break; 1503 } 1504 } else if (locoAddress != 0 && consistCount > 0) { 1505 consistSearchPrevious = false; 1506 break; 1507 } 1508 if (++consistCount > CONSIST_MAX) { 1509 consistStatus.setText(Bundle.getMessage("EditStateNONE")); 1510 consistSearchPrevious = false; 1511 return; // don't update the panel 1512 } 1513 consistNum++; 1514 if (consistNum > CONSIST_MAX) { 1515 consistNum = CONSIST_MIN; 1516 } 1517 consistTextField.setText(Integer.toString(consistNum)); 1518 // have we wrapped? if yes, need to read NCE memory 1519 if (consistNum == CONSIST_MIN) { 1520 break; 1521 } 1522 } 1523 readConsistMemory(consistNum, LEAD); 1524 return; 1525 } 1526 1527 // Panel update, load lead loco 1528 if (locoPosition == LEAD) { 1529 boolean loco1exists = updateLocoFields(nceReply, 0, locoRosterBox1, 1530 locoTextField1, adrButton1, dirButton1, cmdButton1); 1531 if (clearCancelButton.getText().equals(Bundle.getMessage("KeyCLEAR"))) { 1532 clearCancelButton.setEnabled(loco1exists); 1533 } 1534 1535 // load rear loco 1536 } else if (locoPosition == REAR) { 1537 updateLocoFields(nceReply, 0, locoRosterBox2, locoTextField2, 1538 adrButton2, dirButton2, cmdButton2); 1539 1540 // load mid locos 1541 } else { 1542 updateLocoFields(nceReply, 0, locoRosterBox3, locoTextField3, 1543 adrButton3, dirButton3, cmdButton3); 1544 updateLocoFields(nceReply, 2, locoRosterBox4, locoTextField4, 1545 adrButton4, dirButton4, cmdButton4); 1546 updateLocoFields(nceReply, 4, locoRosterBox5, locoTextField5, 1547 adrButton5, dirButton5, cmdButton5); 1548 updateLocoFields(nceReply, 6, locoRosterBox6, locoTextField6, 1549 adrButton6, dirButton6, cmdButton6); 1550 consistStatus.setText(Bundle.getMessage("EditStateOKAY")); 1551 checkForRosterMatch(); 1552 saveLoadButton.setEnabled(canLoad()); 1553 } 1554 // read the next loco number in the consist 1555 if (locoPosition == LEAD || locoPosition == REAR) { 1556 locoPosition++; 1557 readConsistMemory(consistNum, locoPosition); 1558 } 1559 } 1560 } 1561 1562 private boolean exactMatch = false; 1563 1564 private void checkForRosterMatch() { 1565 exactMatch = false; 1566 if (!verifyRosterMatch) { 1567 nceConsistRosterEntry = nceConsistRoster.entryFromTitle(locoTextField1.getText()); 1568 } 1569 if (nceConsistRosterEntry == null) { 1570 if (checkBoxConsist.isSelected() && !locoTextField1.getText().equals("")) { 1571 consistStatus.setText(Bundle.getMessage("EditStateUNKNOWN")); 1572 } else { 1573 textConRoadName.setText(""); 1574 } 1575 textConRoadNumber.setText(""); 1576 textConModel.setText(""); 1577 return; 1578 } 1579 if (consistRosterMatch(nceConsistRosterEntry)) { 1580 exactMatch = true; 1581 // exact match! 1582 if (verifyRosterMatch) { 1583 queueError(WARN_CONSIST_ALREADY_LOADED); 1584 } 1585 verifyRosterMatch = false; 1586 } else { 1587 // not an exact match! 1588 if (verifyRosterMatch) { 1589 queueError(ERROR_CONSIST_DOESNT_MATCH); 1590 } 1591 verifyRosterMatch = false; 1592 if (!consistRosterPartialMatch(nceConsistRosterEntry)) { 1593 textConRoadName.setText(""); 1594 textConRoadNumber.setText(""); 1595 textConModel.setText(""); 1596 } 1597 } 1598 } 1599 1600 // update loco fields, returns false if loco address is null 1601 private boolean updateLocoFields(NceReply r, int index, 1602 JComboBox<Object> locoRosterBox, JTextField locoTextField, 1603 JButton adrButton, JButton dirButton, JButton cmdButton) { 1604 // index = 0 for lead and rear locos, 0,2,4,6 for mid 1605 String locoAddrText = getLocoAddrText(r, index); 1606 boolean locoType = getLocoAddressType(r, index); // Long or short address? 1607 String locoDirection = getLocoDirection(dirButton); 1608 1609 locoTextField.setText(locoAddrText); 1610 locoRosterBox.setSelectedIndex(0); 1611 1612 if (locoAddrText.equals("") || locoAddrText.equals(Bundle.getMessage("REPLACE_LOCO"))) { 1613 locoRosterBox.setEnabled(true); 1614 locoTextField.setEnabled(true); 1615 cmdButton.setText(Bundle.getMessage("KeyADD")); 1616 cmdButton.setVisible(true); 1617 cmdButton.setEnabled(false); 1618 cmdButton.setToolTipText(Bundle.getMessage("ToolTipAdd")); 1619 dirButton.setText(Bundle.getMessage("KeyQUESTION")); 1620 dirButton.setEnabled(true); 1621 adrButton.setText(Bundle.getMessage("KeyLONG")); 1622 adrButton.setEnabled(true); 1623 return false; 1624 } else { 1625 locoTextField.setText(locoAddrText); 1626 locoRosterBox.setEnabled(false); 1627 locoTextField.setEnabled(false); 1628 cmdButton.setEnabled(true); 1629 dirButton.setText(locoDirection); 1630 dirButton.setEnabled(false); 1631 adrButton.setText((locoType) ? Bundle.getMessage("KeyLONG") : Bundle.getMessage("KeySHORT")); 1632 adrButton.setEnabled(false); 1633 1634 // can not delete lead or rear locos, but can replace 1635 if (locoTextField == locoTextField1 || locoTextField == locoTextField2) { 1636 cmdButton.setText(Bundle.getMessage("KeyREPLACE")); 1637 cmdButton.setToolTipText("Press to delete and replace this loco"); 1638 } else { 1639 cmdButton.setText(Bundle.getMessage("KeyDELETE")); 1640 cmdButton.setToolTipText("Press to delete this loco from consist"); 1641 } 1642 return true; 1643 } 1644 } 1645 1646 // modify loco fields because an Add, Replace, Delete button has been pressed 1647 private void modifyLocoFields(JComboBox<Object> locoRosterBox, 1648 JTextField locoTextField, JButton adrButton, JButton dirButton, 1649 JButton cmdButton) { 1650 if (validLocoAdr(locoTextField.getText()) < 0) { 1651 return; 1652 } 1653 byte consistNumber = (byte) validConsist(consistTextField.getText()); 1654 if (consistNumber < 0) { 1655 return; 1656 } 1657 if (locoTextField.getText().equals("")) { 1658 JOptionPane.showMessageDialog(this, 1659 Bundle.getMessage("DIALOG_EnterLocoB4Add"), 1660 Bundle.getMessage("DIALOG_NceConsist"), 1661 JOptionPane.ERROR_MESSAGE); 1662 return; 1663 } 1664 // set reflesh flag to update panel 1665 refresh = true; 1666 int locoAddr = getLocoAddr(locoTextField, adrButton); 1667 1668 if (cmdButton.getText().equals(Bundle.getMessage("KeyDELETE"))) { 1669 sendNceBinaryCommand(locoAddr, 1670 NceMessage.LOCO_CMD_DELETE_LOCO_CONSIST, (byte) 0); 1671 1672 } else if (cmdButton.getText().equals(Bundle.getMessage("KeyREPLACE"))) { 1673 1674 // Kill refresh flag, no update when replacing loco 1675 refresh = false; 1676 1677 // allow user to add loco to lead or rear consist 1678 locoRosterBox.setEnabled(true); 1679 locoTextField.setText(""); 1680 locoTextField.setEnabled(true); 1681 adrButton.setText(Bundle.getMessage("KeyLONG")); 1682 adrButton.setEnabled(true); 1683 dirButton.setText(Bundle.getMessage("KeyQUESTION")); 1684 dirButton.setEnabled(true); 1685 cmdButton.setText(Bundle.getMessage("KeyADD")); 1686 cmdButton.setToolTipText(Bundle.getMessage("ToolTipAdd")); 1687 1688 // now update CS memory in case user doesn't use the Add button 1689 // this will also allow us to delete the loco from the layout 1690 if (locoTextField == locoTextField1) { 1691 // replace lead loco 1692 sendNceBinaryCommand(LOC_ADR_REPLACE, 1693 NceMessage.LOCO_CMD_FWD_CONSIST_LEAD, consistNumber); 1694 // no lead loco so we can't kill the consist 1695 clearCancelButton.setEnabled(false); 1696 } else { 1697 // replace rear loco 1698 sendNceBinaryCommand(LOC_ADR_REPLACE, 1699 NceMessage.LOCO_CMD_FWD_CONSIST_REAR, consistNumber); 1700 } 1701 // now delete lead or rear loco from layout 1702 sendNceBinaryCommand(locoAddr, 1703 NceMessage.LOCO_CMD_DELETE_LOCO_CONSIST, (byte) 0); 1704 } else { 1705 // ADD button has been pressed 1706 if (dirButton.getText().equals(Bundle.getMessage("KeyQUESTION"))) { 1707 JOptionPane.showMessageDialog(this, 1708 Bundle.getMessage("DIALOG_SetDirB4Consist"), 1709 Bundle.getMessage("DIALOG_NceConsist"), JOptionPane.ERROR_MESSAGE); 1710 1711 // kill refresh flag, no update if Add button is enabled 1712 // and loco direction isn't known (lead, rear, replacement) 1713 refresh = false; 1714 return; 1715 } 1716 // delete loco from any existing consists 1717 sendNceBinaryCommand(locoAddr, 1718 NceMessage.LOCO_CMD_DELETE_LOCO_CONSIST, (byte) 0); 1719 1720 // check to see if loco is already a lead or rear in another consist 1721 verifyLocoAddr(locoAddr); 1722 1723 // now we need to determine if lead, rear, or mid loco 1724 // lead loco? 1725 if (locoTextField == locoTextField1) { 1726 if (dirButton.getText().equals(Bundle.getMessage("KeyFWD"))) { 1727 sendNceBinaryCommand(locoAddr, 1728 NceMessage.LOCO_CMD_FWD_CONSIST_LEAD, consistNumber); 1729 } 1730 if (dirButton.getText().equals(Bundle.getMessage("KeyREV"))) { 1731 sendNceBinaryCommand(locoAddr, 1732 NceMessage.LOCO_CMD_REV_CONSIST_LEAD, consistNumber); 1733 } 1734 // rear loco? 1735 } else if (locoTextField == locoTextField2) { 1736 if (dirButton.getText().equals(Bundle.getMessage("KeyFWD"))) { 1737 sendNceBinaryCommand(locoAddr, 1738 NceMessage.LOCO_CMD_FWD_CONSIST_REAR, consistNumber); 1739 } 1740 if (dirButton.getText().equals(Bundle.getMessage("KeyREV"))) { 1741 sendNceBinaryCommand(locoAddr, 1742 NceMessage.LOCO_CMD_REV_CONSIST_REAR, consistNumber); 1743 } 1744 // must be mid loco 1745 } else { 1746 // wait for verify to complete before updating mid loco 1747 if (locoSearch) { 1748 if (dirButton.getText().equals(Bundle.getMessage("KeyFWD"))) { 1749 verifyType = VERIFY_MID_FWD; 1750 } else { 1751 verifyType = VERIFY_MID_REV; 1752 } 1753 // no verify, just load and go! 1754 } else { 1755 if (dirButton.getText().equals(Bundle.getMessage("KeyFWD"))) { 1756 sendNceBinaryCommand(locoAddr, 1757 NceMessage.LOCO_CMD_FWD_CONSIST_MID, consistNumber); 1758 } 1759 if (dirButton.getText().equals(Bundle.getMessage("KeyREV"))) { 1760 sendNceBinaryCommand(locoAddr, 1761 NceMessage.LOCO_CMD_REV_CONSIST_MID, consistNumber); 1762 } 1763 } 1764 } 1765 } 1766 } 1767 1768 private void fullLoad() { 1769 refresh = true; 1770 loadOneLine(locoRosterBox1, locoTextField1, adrButton1, 1771 dirButton1, cmdButton1); 1772 loadOneLine(locoRosterBox2, locoTextField2, adrButton2, 1773 dirButton2, cmdButton2); 1774 loadOneLine(locoRosterBox3, locoTextField3, adrButton3, 1775 dirButton3, cmdButton3); 1776 loadOneLine(locoRosterBox4, locoTextField4, adrButton4, 1777 dirButton4, cmdButton4); 1778 loadOneLine(locoRosterBox5, locoTextField5, adrButton5, 1779 dirButton5, cmdButton5); 1780 loadOneLine(locoRosterBox6, locoTextField6, adrButton6, 1781 dirButton6, cmdButton6); 1782 changeButtons(false); 1783 } 1784 1785 /** 1786 * updates NCE CS based on the loco line supplied called by load button 1787 * 1788 */ 1789 private void loadOneLine(JComboBox<Object> locoRosterBox, JTextField locoTextField, 1790 JButton adrButton, JButton dirButton, JButton cmdButton) { 1791 if (locoTextField.getText().equals("")) { 1792 return; 1793 } 1794 if (validLocoAdr(locoTextField.getText()) < 0) { 1795 return; 1796 } 1797 byte cN = (byte) validConsist(consistTextField.getText()); 1798 if (cN < 0) { 1799 return; 1800 } 1801 1802 int locoAddr = getLocoAddr(locoTextField, adrButton); 1803 1804 // ADD loco to consist 1805 if (dirButton.getText().equals(Bundle.getMessage("KeyQUESTION"))) { 1806 JOptionPane.showMessageDialog(this, 1807 Bundle.getMessage("DIALOG_SetDirB4Consist"), Bundle.getMessage("DIALOG_NceConsist"), 1808 JOptionPane.ERROR_MESSAGE); 1809 return; 1810 } 1811 1812 // delete loco from any existing consists 1813 sendNceBinaryCommand(locoAddr, 1814 NceMessage.LOCO_CMD_DELETE_LOCO_CONSIST, (byte) 0); 1815 // now we need to determine if lead, rear, or mid loco 1816 // lead loco? 1817 if (locoTextField == locoTextField1) { 1818 // kill the consist first to clear NCE CS memory 1819 sendNceBinaryCommand(locoAddr, 1820 NceMessage.LOCO_CMD_FWD_CONSIST_LEAD, cN); 1821 sendNceBinaryCommand(locoAddr, NceMessage.LOCO_CMD_KILL_CONSIST, 1822 (byte) 0); 1823 // now load 1824 if (dirButton.getText().equals(Bundle.getMessage("KeyFWD"))) { 1825 sendNceBinaryCommand(locoAddr, 1826 NceMessage.LOCO_CMD_FWD_CONSIST_LEAD, cN); 1827 } 1828 if (dirButton.getText().equals(Bundle.getMessage("KeyREV"))) { 1829 sendNceBinaryCommand(locoAddr, 1830 NceMessage.LOCO_CMD_REV_CONSIST_LEAD, cN); 1831 } 1832 // rear loco? 1833 } else if (locoTextField == locoTextField2) { 1834 if (dirButton.getText().equals(Bundle.getMessage("KeyFWD"))) { 1835 sendNceBinaryCommand(locoAddr, 1836 NceMessage.LOCO_CMD_FWD_CONSIST_REAR, cN); 1837 } 1838 if (dirButton.getText().equals(Bundle.getMessage("KeyREV"))) { 1839 sendNceBinaryCommand(locoAddr, 1840 NceMessage.LOCO_CMD_REV_CONSIST_REAR, cN); 1841 } 1842 // must be mid loco 1843 } else { 1844 if (dirButton.getText().equals(Bundle.getMessage("KeyFWD"))) { 1845 sendNceBinaryCommand(locoAddr, 1846 NceMessage.LOCO_CMD_FWD_CONSIST_MID, cN); 1847 } 1848 if (dirButton.getText().equals(Bundle.getMessage("KeyREV"))) { 1849 sendNceBinaryCommand(locoAddr, 1850 NceMessage.LOCO_CMD_REV_CONSIST_MID, cN); 1851 } 1852 } 1853 } 1854 1855 private int getLocoAddr(JTextField locoTextField, JButton adrButton) { 1856 int locoAddr = Integer.parseInt(locoTextField.getText()); 1857 if (locoAddr >= 128) { 1858 locoAddr += 0xC000; 1859 } else if (adrButton.getText().equals(Bundle.getMessage("KeyLONG"))) { 1860 locoAddr += 0xC000; 1861 } 1862 return locoAddr; 1863 } 1864 1865 private void sendNceMessage(byte[] b, int replyLength) { 1866 NceMessage m = NceMessage.createBinaryMessage(tc, b, replyLength); 1867 waiting++; 1868 replyLen = replyLength; // Expect n byte response 1869 tc.sendNceMessage(m, this); 1870 } 1871 1872 // get loco address type, returns true if long 1873 private boolean getLocoAddressType(NceReply r, int i) { 1874 int rC = r.getElement(i); 1875 rC = rC & 0xC0; // long address if 2 msb are set 1876 if (rC == 0xC0) { 1877 return true; 1878 } else { 1879 return false; 1880 } 1881 } 1882 1883 private String getLocoAddrText(NceReply r, int i) { 1884 int rC_u = r.getElement(i++); 1885 int rC = (rC_u << 8) & 0x3F00; 1886 int rC_l = r.getElement(i); 1887 rC = rC + (rC_l & 0xFF); 1888 String locoAddrText = ""; 1889 if ((rC_u != 0) || (rC_l != 0)) { 1890 locoAddrText = Integer.toString(rC); 1891 } 1892 if (rC == LOC_ADR_REPLACE) { 1893 locoAddrText = Bundle.getMessage("REPLACE_LOCO"); 1894 } 1895 return locoAddrText; 1896 } 1897 1898 private String getLocoDirection(JButton dirButton) { 1899 if (newConsist) { 1900 return Bundle.getMessage("KeyQUESTION"); 1901 } else { 1902 return dirButton.getText(); 1903 } 1904 } 1905 1906 // check command station memory for lead or rear loco match 1907 private void verifyLocoAddr(int locoAddr) { 1908 verifyType = VERIFY_LEAD_REAR; 1909 if (checkBoxVerify.isSelected()) { 1910 locoVerifyList[0] = locoAddr; 1911 locoVerifyList[1] = 0; // end of list 1912 locoSearch = true; 1913 consistNumVerify = 0; 1914 } 1915 } 1916 1917 // check command station memory for lead or rear loco match 1918 private boolean verifyAllLocoAddr() { 1919 verifyType = VERIFY_ALL; 1920 if (checkBoxVerify.isSelected()) { 1921 int i = 0; 1922 if (!locoTextField1.getText().equals("") && validLocoAdr(locoTextField1.getText()) > 0) { 1923 locoVerifyList[i++] = getLocoAddr(locoTextField1, adrButton1); 1924 } 1925 if (!locoTextField2.getText().equals("") && validLocoAdr(locoTextField2.getText()) > 0) { 1926 locoVerifyList[i++] = getLocoAddr(locoTextField2, adrButton2); 1927 } 1928 if (!locoTextField3.getText().equals("") && validLocoAdr(locoTextField3.getText()) > 0) { 1929 locoVerifyList[i++] = getLocoAddr(locoTextField3, adrButton3); 1930 } 1931 if (!locoTextField4.getText().equals("") && validLocoAdr(locoTextField4.getText()) > 0) { 1932 locoVerifyList[i++] = getLocoAddr(locoTextField4, adrButton4); 1933 } 1934 if (!locoTextField5.getText().equals("") && validLocoAdr(locoTextField5.getText()) > 0) { 1935 locoVerifyList[i++] = getLocoAddr(locoTextField5, adrButton5); 1936 } 1937 if (!locoTextField6.getText().equals("") && validLocoAdr(locoTextField6.getText()) > 0) { 1938 locoVerifyList[i++] = getLocoAddr(locoTextField6, adrButton6); 1939 } else { 1940 locoVerifyList[i] = 0; 1941 } 1942 locoSearch = true; 1943 consistNumVerify = 0; 1944 consistStatus.setText(Bundle.getMessage("EditStateVERIFY")); 1945 readConsistMemory(consistNumVerify, LEAD); 1946 return true; 1947 } 1948 return false; 1949 } 1950 1951 private void addLocoRow(JComponent col1, JComponent col2, JComponent col3, 1952 JComponent col4, JComponent col5, JComponent col6, int row) { 1953 addItem(col1, 0, row); 1954 addItem(col2, 1, row); 1955 addItem(col3, 2, row); 1956 addItem(col4, 3, row); 1957 addItem(col5, 4, row); 1958 addItem(col6, 5, row); 1959 } 1960 1961 private void addItem(JComponent c, int x, int y) { 1962 GridBagConstraints gc = new GridBagConstraints(); 1963 gc.gridx = x; 1964 gc.gridy = y; 1965 gc.weightx = 100.0; 1966 gc.weighty = 100.0; 1967 add(c, gc); 1968 } 1969 1970 private void addButtonAction(JButton b) { 1971 b.addActionListener(new java.awt.event.ActionListener() { 1972 @Override 1973 public void actionPerformed(java.awt.event.ActionEvent e) { 1974 buttonActionPerformed(e); 1975 } 1976 }); 1977 } 1978 1979 private void addCheckBoxAction(JCheckBox cb) { 1980 cb.addActionListener(new java.awt.event.ActionListener() { 1981 @Override 1982 public void actionPerformed(java.awt.event.ActionEvent e) { 1983 checkBoxActionPerformed(e); 1984 } 1985 }); 1986 } 1987 1988 private void enableAllLocoRows(boolean flag) { 1989 enableLocoRow(flag, locoTextField1, locoRosterBox1, 1990 adrButton1, dirButton1, cmdButton1); 1991 enableLocoRow(flag, locoTextField2, locoRosterBox2, 1992 adrButton2, dirButton2, cmdButton2); 1993 enableLocoRow(flag, locoTextField3, locoRosterBox3, 1994 adrButton3, dirButton3, cmdButton3); 1995 enableLocoRow(flag, locoTextField4, locoRosterBox4, 1996 adrButton4, dirButton4, cmdButton4); 1997 enableLocoRow(flag, locoTextField5, locoRosterBox5, 1998 adrButton5, dirButton5, cmdButton5); 1999 enableLocoRow(flag, locoTextField6, locoRosterBox6, 2000 adrButton6, dirButton6, cmdButton6); 2001 } 2002 2003 private void enableLocoRow(boolean flag, JTextField locoTextField, 2004 JComboBox<Object> locoRosterBox, JButton adrButton, JButton dirButton, 2005 JButton cmdButton) { 2006 locoTextField.setEnabled(flag); 2007 locoRosterBox.setEnabled(flag); 2008 adrButton.setEnabled(flag); 2009 dirButton.setEnabled(flag); 2010 cmdButton.setEnabled(flag); 2011 } 2012 2013 // initialize loco fields 2014 private void initLocoFields() { 2015 initLocoRow(1, Bundle.getMessage("LeadLabel"), textLoco1, locoTextField1, locoRosterBox1, 2016 adrButton1, dirButton1, cmdButton1); 2017 initLocoRow(2, Bundle.getMessage("RearLabel"), textLoco2, locoTextField2, locoRosterBox2, 2018 adrButton2, dirButton2, cmdButton2); 2019 initLocoRow(3, Bundle.getMessage("MidLabel", "1"), textLoco3, locoTextField3, locoRosterBox3, 2020 adrButton3, dirButton3, cmdButton3); 2021 initLocoRow(4, Bundle.getMessage("MidLabel", "2"), textLoco4, locoTextField4, locoRosterBox4, 2022 adrButton4, dirButton4, cmdButton4); 2023 initLocoRow(5, Bundle.getMessage("MidLabel", "3"), textLoco5, locoTextField5, locoRosterBox5, 2024 adrButton5, dirButton5, cmdButton5); 2025 initLocoRow(6, Bundle.getMessage("MidLabel", "4"), textLoco6, locoTextField6, locoRosterBox6, 2026 adrButton6, dirButton6, cmdButton6); 2027 } 2028 2029 private void initLocoRow(int row, String s, JLabel textLoco, 2030 JTextField locoTextField, JComboBox<Object> locoRosterBox, 2031 JButton adrButton, JButton dirButton, JButton cmdButton) { 2032 2033 textLoco.setText(s); 2034 textLoco.setVisible(true); 2035 2036 adrButton.setText(Bundle.getMessage("KeyLONG")); 2037 adrButton.setVisible(true); 2038 adrButton.setEnabled(false); 2039 adrButton.setToolTipText(Bundle.getMessage("ToolTipAddressType")); 2040 adrButton.addActionListener(new java.awt.event.ActionListener() { 2041 @Override 2042 public void actionPerformed(java.awt.event.ActionEvent e) { 2043 buttonActionAdrPerformed(e); 2044 } 2045 }); 2046 2047 locoRosterBox.setVisible(true); 2048 locoRosterBox.setEnabled(false); 2049 locoRosterBox.setToolTipText(Bundle.getMessage("ToolTipSelectLoco")); 2050 locoRosterBox.addActionListener(new java.awt.event.ActionListener() { 2051 @Override 2052 public void actionPerformed(java.awt.event.ActionEvent e) { 2053 locoSelected(e); 2054 } 2055 }); 2056 2057 dirButton.setText(Bundle.getMessage("KeyQUESTION")); 2058 dirButton.setVisible(true); 2059 dirButton.setEnabled(false); 2060 dirButton.setToolTipText(Bundle.getMessage("ToolTipDirection")); 2061 dirButton.addActionListener(new java.awt.event.ActionListener() { 2062 @Override 2063 public void actionPerformed(java.awt.event.ActionEvent e) { 2064 buttonActionDirPerformed(e); 2065 } 2066 }); 2067 2068 cmdButton.setText(Bundle.getMessage("KeyADD")); 2069 cmdButton.setVisible(true); 2070 cmdButton.setEnabled(false); 2071 cmdButton.setToolTipText(Bundle.getMessage("ToolTipAdd")); 2072 cmdButton.addActionListener(this::buttonActionCmdPerformed); 2073 2074 locoTextField.setText(""); 2075 locoTextField.setEnabled(false); 2076 locoTextField.setToolTipText(Bundle.getMessage("ToolTipEnterLoco")); 2077 locoTextField.setMaximumSize(new Dimension( 2078 locoTextField.getMaximumSize().width, locoTextField 2079 .getPreferredSize().height)); 2080 } 2081 2082 ActionListener consistRosterListener; 2083 2084 private void initConsistRoster(JComboBox<String> conRosterBox) { 2085 conRosterBox.insertItemAt("", 0); 2086 conRosterBox.setSelectedIndex(0); 2087 conRosterBox.setVisible(true); 2088 conRosterBox.setEnabled(false); 2089 conRosterBox.setToolTipText(Bundle.getMessage("ToolTipSelectConsist")); 2090 conRosterBox.addActionListener(consistRosterListener = this::consistRosterSelected); 2091 } 2092 2093 private static final int ERROR_LOCO_IN_USE = 1; 2094 private static final int ERROR_NO_EMPTY_CONSIST = 2; 2095 private static final int ERROR_CONSIST_DOESNT_MATCH = 3; 2096 private static final int WARN_CONSIST_ALREADY_LOADED = 4; 2097 private int locoNumInUse; // report loco alreay in use 2098 private int errorCode = 0; 2099 2100 private void queueError(int errorCode) { 2101 log.debug("queue warning/error message: {}", errorCode); 2102 if (this.errorCode != 0) { 2103 log.debug("multiple errors reported {}", this.errorCode); 2104 return; 2105 } 2106 this.errorCode = errorCode; 2107 // Bad to stop receive thread with JOptionPane error message 2108 // so start up a new thread to report error 2109 Thread errorThread = new Thread(new Runnable() { 2110 @Override 2111 public void run() { 2112 reportError(); 2113 } 2114 }); 2115 errorThread.setName("Report Error"); // NOI18N 2116 errorThread.start(); 2117 } 2118 2119 public void reportError() { 2120 switch (errorCode) { 2121 2122 case ERROR_LOCO_IN_USE: 2123 JOptionPane.showMessageDialog(this, 2124 Bundle.getMessage("DIALOG_LocoInUse", locoNumInUse, consistNumVerify), 2125 Bundle.getMessage("DIALOG_NceConsist"), 2126 JOptionPane.ERROR_MESSAGE); 2127 break; 2128 2129 case ERROR_NO_EMPTY_CONSIST: 2130 JOptionPane.showMessageDialog(this, 2131 Bundle.getMessage("DIALOG_NoEmptyConsist"), 2132 Bundle.getMessage("DIALOG_NceConsist"), 2133 JOptionPane.ERROR_MESSAGE); 2134 break; 2135 2136 case ERROR_CONSIST_DOESNT_MATCH: 2137 if (JOptionPane.showConfirmDialog(null, 2138 Bundle.getMessage("DIALOG_RosterNotMatch") + " " 2139 + getRosterText(nceConsistRosterEntry), 2140 Bundle.getMessage("DIALOG_NceContinue"), 2141 JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) { 2142 if (JOptionPane.showConfirmDialog(null, 2143 Bundle.getMessage("DIALOG_RosterNotMatch1", 2144 nceConsistRosterEntry.getId(), nceConsistRosterEntry.getConsistNumber()) 2145 + "\n " + Bundle.getMessage("DIALOG_RosterNotMatch2"), 2146 Bundle.getMessage("DIALOG_NceReset"), 2147 JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { 2148 nceConsistRosterEntry.setConsistNumber(Bundle.getMessage("CLEARED")); 2149 } 2150 changeButtons(false); 2151 saveLoadButton.setEnabled(canLoad()); 2152 break; 2153 } 2154 changeButtons(true); 2155 loadFullRoster(nceConsistRosterEntry); 2156 saveLoadButton.setEnabled(canLoad()); 2157 break; 2158 case WARN_CONSIST_ALREADY_LOADED: 2159 JOptionPane.showMessageDialog(this, 2160 Bundle.getMessage("DIALOG_ConsistWasLoaded"), 2161 Bundle.getMessage("DIALOG_NceConsist"), JOptionPane.WARNING_MESSAGE); 2162 break; 2163 default: 2164 log.error("Error code out of range"); 2165 } 2166 errorCode = 0; 2167 } 2168 2169 private String getRosterText(NceConsistRosterEntry nceConsistRosterEntry) { 2170 return "\n" 2171 + "\n" 2172 + Bundle.getMessage("ROSTER_ConsistNum") 2173 + " " + nceConsistRosterEntry.getConsistNumber() 2174 + "\n" 2175 + Bundle.getMessage("ROSTER_LeadLoco") 2176 + " " + nceConsistRosterEntry.getLoco1DccAddress() 2177 + " " + shortHandConvertDTD(nceConsistRosterEntry.getLoco1Direction()) 2178 + "\n" 2179 + Bundle.getMessage("ROSTER_RearLoco") 2180 + " " + nceConsistRosterEntry.getLoco2DccAddress() 2181 + " " + shortHandConvertDTD(nceConsistRosterEntry.getLoco2Direction()) 2182 + "\n" 2183 + Bundle.getMessage("ROSTER_Mid1Loco") 2184 + " " + nceConsistRosterEntry.getLoco3DccAddress() 2185 + " " + shortHandConvertDTD(nceConsistRosterEntry.getLoco3Direction()) 2186 + "\n" 2187 + Bundle.getMessage("ROSTER_Mid2Loco") 2188 + " " + nceConsistRosterEntry.getLoco4DccAddress() 2189 + " " + shortHandConvertDTD(nceConsistRosterEntry.getLoco4Direction()) 2190 + "\n" 2191 + Bundle.getMessage("ROSTER_Mid3Loco") 2192 + " " + nceConsistRosterEntry.getLoco5DccAddress() 2193 + " " + shortHandConvertDTD(nceConsistRosterEntry.getLoco5Direction()) 2194 + "\n" 2195 + Bundle.getMessage("ROSTER_Mid4Loco") 2196 + " " + nceConsistRosterEntry.getLoco6DccAddress() 2197 + " " + shortHandConvertDTD(nceConsistRosterEntry.getLoco6Direction()); 2198 } 2199 2200 /** 2201 * Nested class to create one of these using old-style defaults 2202 */ 2203 static public class Default extends jmri.jmrix.nce.swing.NceNamedPaneAction { 2204 2205 public Default() { 2206 super("Open NCE Consist Editor", 2207 new jmri.util.swing.sdi.JmriJFrameInterface(), 2208 NceConsistEditPanel.class.getName(), 2209 jmri.InstanceManager.getDefault(NceSystemConnectionMemo.class)); 2210 } 2211 } 2212 2213 private final static Logger log = LoggerFactory 2214 .getLogger(NceConsistEditPanel.class); 2215}