001package jmri.jmrix.loconet.downloader; 002 003import java.awt.Color; 004import java.awt.event.ActionEvent; 005import java.awt.event.FocusEvent; 006import java.awt.event.FocusListener; 007 008import javax.swing.BoxLayout; 009import javax.swing.ButtonGroup; 010import javax.swing.JFileChooser; 011import javax.swing.JLabel; 012import javax.swing.JPanel; 013import javax.swing.JRadioButton; 014import javax.swing.JSeparator; 015import javax.swing.JTextField; 016 017import jmri.jmrit.MemoryContents; 018import jmri.jmrix.loconet.LnConstants; 019import jmri.jmrix.loconet.LocoNetMessage; 020import jmri.jmrix.loconet.LocoNetSystemConnectionMemo; 021import jmri.util.swing.JmriJOptionPane; 022 023/** 024 * Pane for downloading .hex files and .dmf files to those LocoNet devices which 025 * support firmware updates via LocoNet IPL messages. 026 * <p> 027 * This version relies on the file contents interpretation mechanisms built into 028 * the readHex() methods found in class jmri.jmrit.MemoryContents to 029 * automatically interpret the file's addressing type - either 16-bit or 24-bit 030 * addressing. The interpreted addressing type is reported in the pane after a 031 * file is read. The user cannot select the addressing type. 032 * <p> 033 * This version relies on the file contents checking mechanisms built into the 034 * readHex() methods found in class jmri.jmrit.MemoryContents to check for a 035 * wide variety of possible issues in the contents of the firmware update file. 036 * Any exception thrown by at method is used to select an error message to 037 * display in the status line of the pane. 038 * 039 * @author Bob Jacobsen Copyright (C) 2005, 2015 040 * @author B. Milhaupt Copyright (C) 2013, 2014, 2017 041 */ 042public class LoaderPane extends jmri.jmrix.AbstractLoaderPane 043 implements jmri.jmrix.loconet.swing.LnPanelInterface { 044 045 /** 046 * LnPanelInterface implementation makes "memo" object available as convenience 047 */ 048 protected LocoNetSystemConnectionMemo memo; 049 050 /** 051 * {@inheritDoc} 052 */ 053 @Override 054 public void initContext(Object context) { 055 if (context instanceof LocoNetSystemConnectionMemo) { 056 initComponents((LocoNetSystemConnectionMemo) context); 057 } 058 } 059 060 /** 061 * This gets invoked early. We don't want it to do anything, so 062 * we just fail to pass it up. Instead, we wait for the later call of 063 * initComponents(LocoNetSystemConnectionMemo memo) 064 */ 065 @Override 066 public void initComponents(){ 067 } 068 069 /** 070 * {@inheritDoc} 071 */ 072 @Override 073 public void initComponents(LocoNetSystemConnectionMemo memo) { 074 this.memo = memo; 075 super.initComponents(); 076 } 077 078 /** 079 * LnPanelInterface implementation creates standard form of title. 080 * 081 * @param menuTitle is a string containing the name of the tool 082 * @return a new string containing the connection's UserName plus the name 083 * of the tool 084 */ 085 public String getTitle(String menuTitle) { return jmri.jmrix.loconet.swing.LnPanel.getTitleHelper(memo, menuTitle); } 086 087 088 089 // Local GUI member declarations 090 JTextField bootload = new JTextField(); 091 JTextField mfg = new JTextField(); 092 093 JTextField developer = new JTextField(); 094 JTextField product = new JTextField(); 095 JTextField hardware = new JTextField(); 096 JTextField software = new JTextField(); 097 JTextField delay = new JTextField(); 098 JTextField eestart = new JTextField(); 099 JTextField eraseBlockSize = new JTextField(); 100 101 JRadioButton checkhardwareno = new JRadioButton(Bundle.getMessage("ButtonCheckHardwareNo")); 102 JRadioButton checkhardwareexact = new JRadioButton(Bundle.getMessage("ButtonCheckHardwareExact")); 103 JRadioButton checkhardwaregreater = new JRadioButton(Bundle.getMessage("ButtonCheckHardwareGreater")); 104 ButtonGroup hardgroup = new ButtonGroup(); 105 106 JRadioButton checksoftwareno = new JRadioButton(Bundle.getMessage("ButtonCheckSoftwareNo")); 107 JRadioButton checksoftwareless = new JRadioButton(Bundle.getMessage("ButtonCheckSoftwareLess")); 108 ButtonGroup softgroup = new ButtonGroup(); 109 110 111 private static final int PXCT1DOWNLOAD = 0x40; 112 static int PXCT2SETUP = 0x00; 113 static int PXCT2SENDADDRESS = 0x10; 114 static int PXCT2SENDDATA = 0x20; 115 static int PXCT2VERIFYDATA = 0x30; 116 static int PXCT2ENDOPERATION = 0x40; 117 118 /* 119 * Flags for "Options". 120 * See {@link http://embeddedloconet.cvs.sourceforge.net/viewvc/embeddedloconet/apps/BootLoader/BootloaderUser.c} 121 */ 122 private static final int DO_NOT_CHECK_SOFTWARE_VERSION = 0x00; 123 private static final int CHECK_SOFTWARE_VERSION_LESS = 0x04; 124 125 private static final int DO_NOT_CHECK_HARDWARE_VERSION = 0x00; 126 private static final int REQUIRE_HARDWARE_VERSION_EXACT_MATCH = 0x01; 127 private static final int ACCEPT_LATER_HARDWARE_VERSIONS = 0x03; 128 129 private static final int SW_FLAGS_MSK = 0x04; 130 private static final int HW_FLAGS_MSK = 0x03; 131 132 // some constant string declarations 133 private static final String MIN_VALUE_ZERO = "0"; // NOI18N 134 private static final String MIN_EESTART_VALUE = "8"; // NOI18N 135 private static final String MAX_VALUE_255 = "255"; // NOI18N 136 private static final String MAX_VALUE_65535 = "65535"; // NOI18N 137 private static final String MAX_EESTART_VALUE = "FFFFF8"; // NOI18N 138 private static final String MIN_DELAY_VALUE = "5"; // NOI18N 139 private static final String MAX_DELAY_VALUE = "500"; // NOI18N 140 private static final String MIN_VALUE_64 = "64"; // NOI18N 141 private static final String MAX_VALUE_128 = "128"; // NOI18N 142 143 public LoaderPane() { 144 } 145 146 @Override 147 public String getHelpTarget() { 148 return "package.jmri.jmrix.loconet.downloader.LoaderFrame"; // NOI18N 149 } 150 151 @Override 152 public String getTitle() { 153 return getTitle(Bundle.getMessage("TitleLoader")); 154 } 155 156 @Override 157 protected void addOptionsPanel() { 158 { 159 // create a panel for displaying/modifying the bootloader version 160 JPanel p = new JPanel(); 161 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); 162 p.add(new JLabel(Bundle.getMessage("LabelBootload") + " ")); 163 p.add(bootload); 164 bootload.setToolTipText(Bundle.getMessage("TipValueRange", 165 MIN_VALUE_ZERO, MAX_VALUE_255)); 166 bootload.addFocusListener(new FocusListener() { 167 @Override 168 public void focusGained(FocusEvent e) { 169 } 170 171 @Override 172 public void focusLost(FocusEvent e) { 173 intParameterIsValid(bootload, 0, 255); 174 updateDownloadVerifyButtons(); 175 } 176 }); 177 add(p); 178 } 179 180 { 181 // create a panel for displaying/modifying the manufacturer number 182 JPanel p = new JPanel(); 183 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); 184 p.add(new JLabel(Bundle.getMessage("LabelMfg") + " ")); 185 mfg.setToolTipText(Bundle.getMessage("TipValueRange", 186 MIN_VALUE_ZERO, MAX_VALUE_255)); 187 p.add(mfg); 188 mfg.addFocusListener(new FocusListener() { 189 @Override 190 public void focusGained(FocusEvent e) { 191 } 192 193 @Override 194 public void focusLost(FocusEvent e) { 195 intParameterIsValid(mfg, 0, 255); 196 updateDownloadVerifyButtons(); 197 } 198 }); 199 add(p); 200 } 201 202 { 203 // create a panel for displaying/modifying the developer number 204 JPanel p = new JPanel(); 205 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); 206 p.add(new JLabel(Bundle.getMessage("LabelDev") 207 + " ")); // NOI18N 208 developer.setToolTipText(Bundle.getMessage("TipValueRange", 209 MIN_VALUE_ZERO, MAX_VALUE_255)); 210 p.add(developer); 211 developer.addFocusListener(new FocusListener() { 212 @Override 213 public void focusGained(FocusEvent e) { 214 } 215 216 @Override 217 public void focusLost(FocusEvent e) { 218 intParameterIsValid(developer, 0, 255); 219 updateDownloadVerifyButtons(); 220 } 221 }); 222 add(p); 223 } 224 225 { 226 // create a panel for displaying/modifying the product number 227 JPanel p = new JPanel(); 228 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); 229 p.add(new JLabel(Bundle.getMessage("LabelProduct") + " ")); 230 product.setToolTipText(Bundle.getMessage("TipValueRange", 231 MIN_VALUE_ZERO, MAX_VALUE_65535)); 232 p.add(product); 233 product.addFocusListener(new FocusListener() { 234 @Override 235 public void focusGained(FocusEvent e) { 236 } 237 238 @Override 239 public void focusLost(FocusEvent e) { 240 intParameterIsValid(product, 0, 65535); 241 updateDownloadVerifyButtons(); 242 } 243 }); 244 245 add(p); 246 } 247 248 { 249 // create a panel for displaying/modifying the hardware version 250 JPanel p = new JPanel(); 251 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); 252 hardware.setToolTipText(Bundle.getMessage("TipValueRange", 253 MIN_VALUE_ZERO, MAX_VALUE_255)); 254 p.add(new JLabel(Bundle.getMessage("LabelHardware") + " ")); 255 p.add(hardware); 256 hardware.addFocusListener(new FocusListener() { 257 @Override 258 public void focusGained(FocusEvent e) { 259 } 260 261 @Override 262 public void focusLost(FocusEvent e) { 263 intParameterIsValid(hardware, 0, 255); 264 updateDownloadVerifyButtons(); 265 } 266 }); 267 268 add(p); 269 } 270 271 { 272 // create a panel for displaying/modifying the hardware options 273 JPanel p = new JPanel(); 274 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 275 p.add(checkhardwareno); 276 p.add(checkhardwareexact); 277 p.add(checkhardwaregreater); 278 279 hardgroup.add(checkhardwareno); 280 hardgroup.add(checkhardwareexact); 281 hardgroup.add(checkhardwaregreater); 282 283// checkhardwareno.addFocusListener(new FocusListener() { 284// @Override public void focusGained(FocusEvent e) { 285// } 286// @Override public void focusLost(FocusEvent e) { 287// updateDownloadVerifyButtons(); 288// } 289// }); 290// checkhardwareexact.addFocusListener(new FocusListener() { 291// @Override public void focusGained(FocusEvent e) { 292// } 293// @Override public void focusLost(FocusEvent e) { 294// updateDownloadVerifyButtons(); 295// } 296// }); 297// checkhardwaregreater.addFocusListener(new FocusListener() { 298// @Override public void focusGained(FocusEvent e) { 299// } 300// @Override public void focusLost(FocusEvent e) { 301// updateDownloadVerifyButtons(); 302// } 303// }); 304 checkhardwareno.addActionListener(this); 305 checkhardwareexact.addActionListener(this); 306 checkhardwaregreater.addActionListener(this); 307 add(p); 308 } 309 310 { 311 // create a panel for displaying/modifying the software version 312 JPanel p = new JPanel(); 313 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); 314 p.add(new JLabel(Bundle.getMessage("LabelSoftware") + " ")); 315 software.setToolTipText(Bundle.getMessage("TipValueRange", 316 MIN_VALUE_ZERO, MAX_VALUE_255)); 317 p.add(software); 318 software.addFocusListener(new FocusListener() { 319 @Override 320 public void focusGained(FocusEvent e) { 321 } 322 323 @Override 324 public void focusLost(FocusEvent e) { 325 intParameterIsValid(software, 0, 255); 326 updateDownloadVerifyButtons(); 327 } 328 }); 329 330 add(p); 331 } 332 333 { 334 // create a panel for displaying/modifying the software options 335 JPanel p = new JPanel(); 336 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 337 p.add(checksoftwareno); 338 p.add(checksoftwareless); 339 340 softgroup.add(checksoftwareno); 341 softgroup.add(checksoftwareless); 342 343 checksoftwareno.addActionListener(this); 344 checksoftwareless.addActionListener(this); 345 346 add(p); 347 } 348 349 { 350 // create a panel for displaying/modifying the delay value 351 JPanel p = new JPanel(); 352 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); 353 p.add(new JLabel(Bundle.getMessage("LabelDelay") + " ")); 354 delay.setToolTipText(Bundle.getMessage("TipValueRange", 355 MIN_DELAY_VALUE, MAX_DELAY_VALUE)); 356 357 p.add(delay); 358 delay.addFocusListener(new FocusListener() { 359 @Override 360 public void focusGained(FocusEvent e) { 361 } 362 363 @Override 364 public void focusLost(FocusEvent e) { 365 intParameterIsValid(hardware, 366 Integer.parseInt(MIN_DELAY_VALUE), 367 Integer.parseInt(MAX_DELAY_VALUE)); 368 updateDownloadVerifyButtons(); 369 } 370 }); 371 372 add(p); 373 } 374 375 { 376 // create a panel for displaying/modifying the EEPROM start address 377 JPanel p = new JPanel(); 378 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); 379 p.add(new JLabel(Bundle.getMessage("LabelEEStart") + " ")); 380 eestart.setToolTipText(Bundle.getMessage("TipValueRange", 381 MIN_EESTART_VALUE, MAX_EESTART_VALUE)); 382 p.add(eestart); 383 eestart.addFocusListener(new FocusListener() { 384 @Override 385 public void focusGained(FocusEvent e) { 386 } 387 388 @Override 389 public void focusLost(FocusEvent e) { 390 updateDownloadVerifyButtons(); 391 } 392 }); 393 394 add(p); 395 } 396 397 { 398 // create a panel for displaying/modifying the Erase Block Size 399 JPanel p = new JPanel(); 400 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); 401 p.add(new JLabel(Bundle.getMessage("LabelEraseBlockSize") 402 + " ")); // NOI18N 403 eraseBlockSize.setToolTipText(Bundle.getMessage("TipValueRange", 404 MIN_VALUE_64, MAX_VALUE_128)); 405 p.add(eraseBlockSize); 406 eraseBlockSize.addFocusListener(new FocusListener() { 407 @Override 408 public void focusGained(FocusEvent e) { 409 } 410 411 @Override 412 public void focusLost(FocusEvent e) { 413 if (!intParameterIsValid(eraseBlockSize, 64, 128)) { 414 status.setText(Bundle.getMessage("ErrorInvalidParameter")); 415 } 416 updateDownloadVerifyButtons(); 417 } 418 }); 419 add(p); 420 } 421 add(new JSeparator()); 422 423 } 424 425 @Override 426 protected void handleOptionsInFileContent(MemoryContents inputContent){ 427 // get some key/value pairs from the input file (if available) 428 String text = inputContent.extractValueOfKey("Bootloader Version"); // NOI18N 429 if (text != null) { 430 bootload.setText(text); 431 } 432 433 text = inputContent.extractValueOfKey("Manufacturer Code"); // NOI18N 434 if (text != null) { 435 mfg.setText(text); 436 } 437 438 text = inputContent.extractValueOfKey("Developer Code"); // NOI18N 439 if (text != null) { 440 developer.setText(text); 441 } 442 443 text = inputContent.extractValueOfKey("Product Code"); // NOI18N 444 if (text != null) { 445 product.setText(text); 446 } 447 448 text = inputContent.extractValueOfKey("Hardware Version"); // NOI18N 449 if (text != null) { 450 hardware.setText(text); 451 } 452 453 text = inputContent.extractValueOfKey("Software Version"); // NOI18N 454 if (text != null) { 455 software.setText(text); 456 } 457 458 text = inputContent.extractValueOfKey("Options"); // NOI18N 459 if (text != null) { 460 try { 461 this.setOptionsRadiobuttons(text); 462 } catch (NumberFormatException ex) { 463 JmriJOptionPane.showMessageDialog(this, 464 Bundle.getMessage("ErrorInvalidOptionInFile", text, "Options"), // NOI18N 465 Bundle.getMessage("ErrorTitle"), 466 JmriJOptionPane.ERROR_MESSAGE); 467 this.disableDownloadVerifyButtons(); 468 log.warn("Invalid dmf file 'Options' value {}",text); 469 return; 470 } 471 } 472 473 text = inputContent.extractValueOfKey("Delay"); // NOI18N 474 if (text != null) { 475 delay.setText(text); 476 } 477 478 text = inputContent.extractValueOfKey("EEPROM Start Address"); // NOI18N 479 if (text != null) { 480 eestart.setText(text); 481 } 482 483 text = inputContent.extractValueOfKey("Erase Blk Size"); // NOI18N 484 if (text != null) { 485 eraseBlockSize.setText(text); 486 boolean interpretationProblem = false; 487 try { 488 int i = Integer.parseInt(text); 489 if ((i > 128) || (i < 64)) { 490 interpretationProblem = true; 491 } 492 } catch (java.lang.NumberFormatException e) { 493 interpretationProblem = true; 494 } 495 496 if (interpretationProblem == true) { 497 log.warn("Invalid dmf file 'Erase Blk Size' value {}",text); 498 JmriJOptionPane.showMessageDialog(this, 499 Bundle.getMessage("ErrorInvalidEraseBlkSize", text, "Erase Blk Size"), // NOI18N 500 Bundle.getMessage("ErrorTitle"), // NOI18N 501 JmriJOptionPane.ERROR_MESSAGE); 502 this.disableDownloadVerifyButtons(); 503 // clear out the firmware image to ensure that the user won't 504 // write it to the device 505 inputContent.clear(); 506 setDefaultFieldValues(); 507 clearInputFileName(); 508 } 509 } 510 } 511 512 /** 513 * Add filter(s) for possible types to the input file chooser. 514 * @param chooser a JFileChooser to which the filter is to be added 515 */ 516 @Override 517 protected void addChooserFilters(JFileChooser chooser) { 518 javax.swing.filechooser.FileNameExtensionFilter filter 519 = new javax.swing.filechooser.FileNameExtensionFilter( 520 Bundle.getMessage("FileFilterLabel", // NOI18N 521 "*.dfm, *.hex"), // NOI18N 522 "dmf", "hex"); // NOI18N 523 524 chooser.addChoosableFileFilter( 525 new javax.swing.filechooser.FileNameExtensionFilter( 526 "Digitrax Mangled Firmware (*.dmf)", "dmf")); // NOI18N 527 chooser.addChoosableFileFilter( 528 new javax.swing.filechooser.FileNameExtensionFilter( 529 "Intel Hex Format Firmware (*.hex)", "hex")); // NOI18N 530 chooser.addChoosableFileFilter(filter); 531 532 // make the downloadable file filter the default active filter 533 chooser.setFileFilter(filter); 534 } 535 536 private void setOptionsRadiobuttons(String text) throws NumberFormatException { 537 try { 538 int control = Integer.parseInt(text); 539 switch (control & SW_FLAGS_MSK) { 540 case CHECK_SOFTWARE_VERSION_LESS: 541 checksoftwareless.setSelected(true); 542 checksoftwareno.setSelected(false); 543 break; 544 case DO_NOT_CHECK_SOFTWARE_VERSION: 545 checksoftwareless.setSelected(false); 546 checksoftwareno.setSelected(true); 547 break; 548 default: 549 throw new NumberFormatException("Invalid Software Options: " // NOI18N 550 + (control & SW_FLAGS_MSK)); 551 } 552 switch (control & HW_FLAGS_MSK) { 553 case DO_NOT_CHECK_HARDWARE_VERSION: 554 checkhardwareno.setSelected(true); 555 checkhardwareexact.setSelected(false); 556 checkhardwaregreater.setSelected(false); 557 break; 558 case REQUIRE_HARDWARE_VERSION_EXACT_MATCH: 559 checkhardwareno.setSelected(false); 560 checkhardwareexact.setSelected(true); 561 checkhardwaregreater.setSelected(false); 562 break; 563 case ACCEPT_LATER_HARDWARE_VERSIONS: 564 checkhardwareno.setSelected(false); 565 checkhardwareexact.setSelected(false); 566 checkhardwaregreater.setSelected(true); 567 break; 568 default: 569 throw new NumberFormatException("Invalid Hardware Options: " // NOI18N 570 + (control & HW_FLAGS_MSK)); 571 } 572 } catch (NumberFormatException ex) { 573 log.error("Invalid Option value: {}", text); // NOI18N 574 throw new NumberFormatException(ex.getLocalizedMessage()); 575 } 576 } 577 578 579 @Override 580 protected void doLoad() { 581 super.doLoad(); 582 583 // start the download itself 584 operation = PXCT2SENDDATA; 585 sendSequence(); 586 } 587 588 @Override 589 protected void doVerify() { 590 super.doVerify(); 591 592 // start the download itself 593 operation = PXCT2VERIFYDATA; 594 sendSequence(); 595 } 596 597 private int operation; 598 599 private void sendSequence() { 600 int mfgval; 601 int developerval; 602 int prodval; 603 int hardval; 604 int softval; 605 int control; 606 607 // before starting the send sequence, check for bad values in the 608 // GUI text fields containing the parameters. 609 if (!parametersAreValid()) { 610 disableDownloadVerifyButtons(); 611 status.setText(Bundle.getMessage("ErrorInvalidParameter")); 612 return; 613 } 614 if (inputContent.isEmpty()) { 615 disableDownloadVerifyButtons(); 616 status.setText(Bundle.getMessage("ErrorEmptyFirmwareFile")); 617 return; 618 } 619 620 // now know that the GUI text fields are valid and have some data to move. 621 try { 622 mfgval = Integer.parseInt(mfg.getText()); 623 if (mfgval < 0 || mfgval > 0xff) { 624 throw new NumberFormatException("out of range"); // NOI18N 625 } 626 } catch (NumberFormatException ex) { 627 log.error("sendSequence() failed due to bad Manufacturer Number value {}", mfg.getText()); // NOI18N 628 mfg.setForeground(Color.red); 629 mfg.requestFocusInWindow(); 630 enableDownloadVerifyButtons(); 631 status.setText(Bundle.getMessage("ErrorInvalidValueInGUI", 632 Bundle.getMessage("LabelMfg"), 633 mfg.getText())); 634 return; 635 } 636 637 try { 638 developerval = Integer.parseInt(developer.getText()); 639 if (developerval < 0 || developerval > 0xff) { 640 throw new NumberFormatException("out of range"); // NOI18N 641 } 642 } catch (NumberFormatException ex) { 643 log.error("sendSequence() failed due to bad Developer Number value {}", developer.getText()); // NOI18N 644 developer.setForeground(Color.red); 645 developer.requestFocusInWindow(); 646 enableDownloadVerifyButtons(); 647 status.setText(Bundle.getMessage("ErrorInvalidValueInGUI", 648 Bundle.getMessage("LabelDev"), 649 developer.getText())); 650 return; 651 } 652 653 try { 654 prodval = Integer.parseInt(product.getText()); 655 if (prodval < 0 || prodval > 0xffff) { 656 throw new NumberFormatException("out of range"); // NOI18N 657 } 658 } catch (NumberFormatException ex) { 659 log.error("sendSequence() failed due to bad Product Code value {}", product.getText()); // NOI18N 660 product.setForeground(Color.red); 661 product.requestFocusInWindow(); 662 this.enableDownloadVerifyButtons(); 663 enableDownloadVerifyButtons(); 664 status.setText(Bundle.getMessage("ErrorInvalidValueInGUI", 665 Bundle.getMessage("LabelProduct"), 666 product.getText())); 667 return; 668 } 669 670 try { 671 hardval = Integer.parseInt(hardware.getText()); 672 if (hardval < 0 || hardval > 0xff) { 673 throw new NumberFormatException("out of range"); // NOI18N 674 } 675 } catch (NumberFormatException ex) { 676 log.error("sendSequence() failed due to bad Hardware Version value {}", hardware.getText()); // NOI18N 677 hardware.setForeground(Color.red); 678 hardware.requestFocusInWindow(); 679 enableDownloadVerifyButtons(); 680 status.setText(Bundle.getMessage("ErrorInvalidValueInGUI", 681 Bundle.getMessage("LabelHardware"), 682 hardware.getText())); 683 return; 684 } 685 686 try { 687 softval = Integer.parseInt(software.getText()); 688 if (softval < 0 || softval > 0xff) { 689 throw new NumberFormatException("out of range"); // NOI18N 690 } 691 } catch (NumberFormatException ex) { 692 log.error("sendSequence() failed due to bad Software Version value {}", software.getText()); // NOI18N 693 software.setForeground(Color.red); 694 software.requestFocusInWindow(); 695 enableDownloadVerifyButtons(); 696 status.setText(Bundle.getMessage("ErrorInvalidValueInGUI", 697 Bundle.getMessage("LabelSoftware"), 698 software.getText())); 699 return; 700 } 701 702 control = computeOptionsValFromRadiobuttons(); 703 704 try { 705 delayval = Integer.parseInt(delay.getText()); 706 if ((delayval < Integer.parseInt(MIN_DELAY_VALUE)) 707 || (delayval > Integer.parseInt(MAX_DELAY_VALUE))) { 708 throw new NumberFormatException("out of range"); // NOI18N 709 } 710 } catch (NumberFormatException ex) { 711 log.error("sendSequence() failed due to bad Delay value {}", delay.getText()); // NOI18N 712 delay.setForeground(Color.red); 713 delay.requestFocusInWindow(); 714 enableDownloadVerifyButtons(); 715 status.setText(Bundle.getMessage("ErrorInvalidValueInGUI", 716 Bundle.getMessage("LabelDelay"), 717 delay.getText())); 718 return; 719 } 720 721 try { 722 eestartval = Integer.parseInt(eestart.getText(), 16); 723 if ((eestartval < Integer.parseInt(MIN_EESTART_VALUE, 16)) 724 || (eestartval > Integer.parseInt(MAX_EESTART_VALUE, 16))) { 725 throw new NumberFormatException("out of range"); // NOI18N 726 } 727 } catch (NumberFormatException ex) { 728 log.error("sendSequence() failed due to bad EESTART value {}", eestart.getText()); // NOI18N 729 eestart.setForeground(Color.red); 730 eestart.requestFocusInWindow(); 731 enableDownloadVerifyButtons(); 732 status.setText(Bundle.getMessage("ErrorInvalidValueInGUI", 733 Bundle.getMessage("LabelEEStart"), 734 eestart.getText())); 735 return; 736 } 737 738 // send start 739 sendOne(PXCT2SETUP, mfgval, prodval & 0xff, hardval, softval, 740 control, 0, developerval, prodval / 256); 741 742 // start transmission loop 743 new Thread(new Sender()).start(); 744 } 745 746 void sendOne(int pxct2, int d1, int d2, int d3, int d4, 747 int d5, int d6, int d7, int d8) { 748 LocoNetMessage m = new LocoNetMessage(16); 749 m.setOpCode(LnConstants.OPC_PEER_XFER); 750 m.setElement(1, 0x10); 751 m.setElement(2, 0x7F); 752 m.setElement(3, 0x7F); 753 m.setElement(4, 0x7F); 754 755 int d1u = (d1 & 0x80) / 0x80; 756 int d2u = (d2 & 0x80) / 0x40; 757 int d3u = (d3 & 0x80) / 0x20; 758 int d4u = (d4 & 0x80) / 0x10; 759 int lowbits = d1u | d2u | d3u | d4u; 760 761 m.setElement(5, (lowbits | PXCT1DOWNLOAD) & 0x7F); // PXCT1 762 m.setElement(6, d1 & 0x7F); // D1 763 m.setElement(7, d2 & 0x7F); // D2 764 m.setElement(8, d3 & 0x7F); // D3 765 m.setElement(9, d4 & 0x7F); // D4 766 767 int d5u = (d5 & 0x80) / 0x80; 768 int d6u = (d6 & 0x80) / 0x40; 769 int d7u = (d7 & 0x80) / 0x20; 770 int d8u = (d8 & 0x80) / 0x10; 771 lowbits = d5u | d6u | d7u | d8u; 772 773 m.setElement(10, (lowbits | pxct2) & 0x7F); // PXCT2 774 m.setElement(11, d5 & 0x7F); // D5 775 m.setElement(12, d6 & 0x7F); // D6 776 m.setElement(13, d7 & 0x7F); // D7 777 m.setElement(14, d8 & 0x7F); // D8 778 779 memo.getLnTrafficController().sendLocoNetMessage(m); 780 781 } 782 783 int startaddr; 784 int endaddr; 785 int delayval; 786 int eestartval; 787 788 private class Sender implements Runnable { 789 790 int totalmsgs; 791 int sentmsgs; 792 793 // send the next data, and a termination record when done 794 @Override 795 public void run() { 796 // define range to be checked for download 797 startaddr = 0x000000; 798 endaddr = 0xFFFFFF; 799 800 if ((startaddr & 0x7) != 0) { 801 log.error("Can only start on an 8-byte boundary: {}", startaddr); 802 } 803 804 // fast scan to count bytes to send 805 int location = inputContent.nextContent(startaddr); 806 totalmsgs = 0; 807 sentmsgs = 0; 808 location = location & 0x00FFFFF8; // mask off bits to be multiple of 8 809 do { 810 location = location + 8; 811 totalmsgs++; 812 // update to the next location for data 813 int next = inputContent.nextContent(location); 814 if (next < 0) { 815 break; // no data left 816 } 817 location = next & 0x00FFFFF8; // mask off bits to be multiple of 8 818 819 } while (location <= endaddr); 820 821 // find the initial location with data 822 location = inputContent.nextContent(startaddr); 823 if (location < 0) { 824 log.info("No data, which seems odd"); 825 return; // ends load process 826 } 827 location = location & 0x00FFFFF8; // mask off bits to be multiple of 8 828 829 doLongWait(location, 5); 830 setAddr(location); 831 doLongWait(location, 2); 832 833 do { 834 835 // send this data 836 sentmsgs++; 837 sendOne(operation, // either send or verify 838 inputContent.getLocation(location++), 839 inputContent.getLocation(location++), 840 inputContent.getLocation(location++), 841 inputContent.getLocation(location++), 842 inputContent.getLocation(location++), 843 inputContent.getLocation(location++), 844 inputContent.getLocation(location++), 845 inputContent.getLocation(location++)); 846 847 // update GUI intermittently 848 if ((sentmsgs % 5) == 0) { 849 // update progress bar via the queue to ensure synchronization 850 updateGUI(100 * sentmsgs / totalmsgs); 851 } 852 853 // wait for completion of last operation 854 doWait(location); 855 856 // update to the next location for data 857 int next = inputContent.nextContent(location); 858 if (next < 0) { 859 break; // no data left 860 } 861 next = next & 0x00FFFFF8; // mask off bits to be multiple of 8 862 if ((next != location) || ((location & 0x3f) == 0x00)) { 863 // wait for completion 864 doLongWait(next, 4); // extra wait while device writes memory 865 // change to next location 866 setAddr(next); 867 doLongWait(next, 2); // double wait after sending new address 868 869 } 870 location = next; 871 872 } while (!isOperationAborted() && (location <= endaddr)); 873 874 // send end (after wait) 875 doLongWait(location,4); 876 sendOne(PXCT2ENDOPERATION, 0, 0, 0, 0, 0, 0, 0, 0); 877 878 this.updateGUI(100); //draw bar to 100% 879 880 // signal end to GUI via the queue to ensure synchronization 881 Runnable r = new Runnable() { 882 @Override 883 public void run() { 884 enableGUI(); 885 } 886 }; 887 javax.swing.SwingUtilities.invokeLater(r); 888 889 } 890 891 /** 892 * Send a command to resume at another address 893 * @param location to be written next 894 */ 895 void setAddr(int location) { 896 sendOne(PXCT2SENDADDRESS, 897 (location / 256 / 256) & 0xFF, 898 (location / 256) & 0xFF, 899 location & 0xFF, 900 0, 0, 0, 0, 0); 901 } 902 903 /** 904 * Wait the specified time. 905 * 906 * 16*10/16.44 = 14 msec is the time it takes to send the message. 907 * @param address to be sent next, for computing the delay before 908 * sending the next message 909 */ 910 void doWait(int address) { 911 try { 912 synchronized (this) { 913 // make sure enough time in EEPROM address space 914 int tdelay; 915 if (address >= eestartval) { 916 tdelay = delayval + 50 + 14; 917 } else { 918 tdelay = delayval + 4 + 14; 919 } 920 // do the actual wait 921 wait(tdelay); 922 } 923 } catch (InterruptedException e) { 924 Thread.currentThread().interrupt(); // retain if needed later 925 } 926 } 927 928 /** 929 * Wait the time appropriate for the address. 930 * 931 * @param address to be sent next, for computing the delay before 932 * sending the next message 933 */ 934 void doLongWait(int address, int multiplier) { 935 try { 936 synchronized (this) { 937 // make sure enough time in EEPROM address space 938 int tdelay; 939 if (address >= eestartval) { 940 tdelay = (delayval + 50 + 14) * multiplier; 941 } else { 942 tdelay = (delayval) * multiplier; 943 } 944 // do the actual wait 945 wait(tdelay); 946 } 947 } catch (InterruptedException e) { 948 Thread.currentThread().interrupt(); // retain if needed later 949 } 950 } 951 952 /** 953 * Signal GUI that it's the end of the download 954 * <p> 955 * Should be invoked on the Swing thread 956 */ 957 void enableGUI() { 958 LoaderPane.this.enableDownloadVerifyButtons(); 959 } 960 961 /** 962 * Update the GUI for progress. 963 * 964 * @param value is the percentage of "doneness" to be displayed 965 */ 966 void updateGUI(final int value) { 967 javax.swing.SwingUtilities.invokeLater(new Runnable() { 968 @Override 969 public void run() { 970 log.debug("updateGUI with {}", value); 971 // update progress bar 972 bar.setValue(value); 973 } 974 }); 975 } 976 977 } 978 979 @Override 980 protected void setDefaultFieldValues() { 981 addressSizeButtonGroup.clearSelection(); 982 bootload.setText("1"); // NOI18N 983 mfg.setText("1"); // NOI18N 984 developer.setText("1"); // NOI18N 985 product.setText("1"); // NOI18N 986 hardware.setText("1"); // NOI18N 987 software.setText("1"); // NOI18N 988 delay.setText("200"); // NOI18N 989 eestart.setText("C00000"); // NOI18N 990 eraseBlockSize.setText("64"); // NOI18N 991 992 try { 993 setOptionsRadiobuttons(Integer.toString(DO_NOT_CHECK_SOFTWARE_VERSION + REQUIRE_HARDWARE_VERSION_EXACT_MATCH)); 994 } catch (NumberFormatException ex) { 995 throw (new java.lang.Error("SetCheckboxes Failed to update the GUI for known-good parameters")); // NOI18N 996 } 997 parametersAreValid(); 998 } 999 1000 /** 1001 * Checks the values in the GUI text boxes to determine if any are invalid. 1002 * Intended for use immediately after reading a firmware file for the 1003 * purpose of validating any key/value pairs found in the file. Also 1004 * intended for use immediately before a "verify" or "download" operation to 1005 * check that the user has not changed any of the GUI text values to ones 1006 * that are unsupported. 1007 * <p> 1008 * Note that this method cannot guarantee that the values are suitable for 1009 * the hardware being updated and/or for the particular firmware information 1010 * which was read from the firmware file. 1011 * 1012 * @return false if one or more GUI text box contains an invalid value 1013 */ 1014 @Override 1015 protected boolean parametersAreValid() { 1016 boolean allIsOk; 1017 allIsOk = true; // assume that all GUI values are ok. 1018 String text; // temporary variable to hold text from GUI element 1019 int junk; // temporary variable to hold interpreted GUI value 1020 1021 boolean temp; 1022 temp = intParameterIsValid(bootload, 0, 255); 1023 allIsOk &= temp; 1024 if (!temp) { 1025 log.info("Bootloader Version Number is not valid: {}", bootload.getText()); 1026 } 1027 temp = intParameterIsValid(mfg, 0, 255); 1028 allIsOk &= temp; 1029 if (!temp) { 1030 log.info("Manufacturer Number is not valid: {}", mfg.getText()); 1031 } 1032 temp = intParameterIsValid(developer, 0, 255); 1033 allIsOk &= temp; 1034 if (!temp) { 1035 log.info("Developer Number is not valid: {}", bootload.getText()); 1036 } 1037 temp = intParameterIsValid(product, 0, 65535); 1038 allIsOk &= temp; 1039 if (!temp) { 1040 log.info("Product Code is not valid: {}", product.getText()); 1041 } 1042 temp = intParameterIsValid(hardware, 0, 255); 1043 allIsOk &= temp; 1044 if (!temp) { 1045 log.info("Hardware Version Number is not valid: {}", hardware.getText()); 1046 } 1047 temp = intParameterIsValid(software, 0, 255); 1048 allIsOk &= temp; 1049 if (!temp) { 1050 log.info("Software Version Number is not valid: {}", software.getText()); 1051 } 1052 temp = intParameterIsValid(delay, 1053 Integer.parseInt(MIN_DELAY_VALUE), 1054 Integer.parseInt(MAX_DELAY_VALUE)); 1055 allIsOk &= temp; 1056 if (!temp) { 1057 log.info("Delay is not valid: {}", delay.getText()); 1058 } 1059 temp = (hardgroup.getSelection() != null); 1060 allIsOk &= temp; 1061 if (!temp) { 1062 log.info("No harware version check radio button is selected."); 1063 } 1064 temp = (softgroup.getSelection() != null); 1065 allIsOk &= temp; 1066 if (!temp) { 1067 log.info("No software version check radio button is selected."); 1068 } 1069 temp = true; 1070 eestart.setForeground(Color.black); 1071 text = eestart.getText(); 1072 if (text.equals("")) { 1073 eestart.setText("0"); 1074 eestart.setForeground(Color.red); 1075 temp = false; 1076 } else { 1077 try { 1078 junk = Integer.parseInt(text, 16); 1079 } catch (NumberFormatException ex) { 1080 junk = -1; 1081 } 1082 if ((junk < Integer.parseInt(MIN_EESTART_VALUE, 16)) 1083 || ((junk % 8) != 0) 1084 || (junk > Integer.parseInt(MAX_EESTART_VALUE, 16))) { 1085 eestart.setForeground(Color.red); 1086 temp = false; 1087 } else { 1088 eestart.setForeground(Color.black); 1089 temp = true; 1090 } 1091 } 1092 eestart.updateUI(); 1093 1094 allIsOk &= temp; 1095 1096 temp = intParameterIsValid(eraseBlockSize, 64, 128); 1097 allIsOk &= temp; 1098 if (!temp) { 1099 log.info("Erase Block Sizez is not valid: {}", eraseBlockSize.getText()); 1100 } 1101 1102 if (allIsOk == true) { 1103 log.debug("No problems found when checking parameter values."); 1104 } 1105 1106 return allIsOk; 1107 } 1108 1109 private int computeOptionsValFromRadiobuttons() { 1110 int control = 0; 1111 if (checksoftwareless.isSelected()) { 1112 control |= CHECK_SOFTWARE_VERSION_LESS; 1113 } 1114 1115 if (checkhardwareexact.isSelected()) { 1116 control |= REQUIRE_HARDWARE_VERSION_EXACT_MATCH; 1117 } else if (checkhardwaregreater.isSelected()) { 1118 control |= ACCEPT_LATER_HARDWARE_VERSIONS; 1119 } 1120 return control; 1121 } 1122 1123 /** 1124 * Conditionally enables or disables the Download and Verify GUI buttons 1125 * based on the validity of the parameter values in the GUI and the state of 1126 * the memory contents object. 1127 */ 1128 @Override 1129 protected void updateDownloadVerifyButtons() { 1130 if (parametersAreValid() && !inputContent.isEmpty()) { 1131 enableDownloadVerifyButtons(); 1132 } else { 1133 disableDownloadVerifyButtons(); 1134 } 1135 } 1136 1137 @Override 1138 public void actionPerformed(ActionEvent e) { 1139 updateDownloadVerifyButtons(); 1140 log.info("ActionListener"); 1141 } 1142 1143 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LoaderPane.class); 1144 1145}