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