001package jmri.jmrit.roster.swing.speedprofile; 002 003import java.awt.BorderLayout; 004import java.awt.Color; 005import java.awt.Component; 006import java.awt.GridBagConstraints; 007import java.awt.GridBagLayout; 008import java.awt.event.ActionEvent; 009import java.beans.PropertyChangeEvent; 010import java.beans.PropertyChangeListener; 011import java.util.ArrayList; 012import java.util.List; 013import java.util.Map; 014import java.util.TreeMap; 015 016import javax.swing.BorderFactory; 017import javax.swing.Box; 018import javax.swing.BoxLayout; 019import javax.swing.ButtonGroup; 020import javax.swing.JButton; 021import javax.swing.JCheckBox; 022import javax.swing.JLabel; 023import javax.swing.JPanel; 024import javax.swing.JRadioButton; 025import javax.swing.JTextField; 026import javax.swing.event.DocumentEvent; 027import javax.swing.event.DocumentListener; 028 029import jmri.Block; 030import jmri.DccThrottle; 031import jmri.InstanceManager; 032import jmri.Sensor; 033import jmri.SensorManager; 034import jmri.SpeedStepMode; 035import jmri.Throttle; 036import jmri.ThrottleListener; 037import jmri.jmrit.logix.WarrantPreferences; 038import jmri.jmrit.roster.Roster; 039import jmri.jmrit.roster.RosterEntry; 040import jmri.jmrit.roster.RosterSpeedProfile; 041import jmri.jmrit.roster.swing.RosterEntryComboBox; 042import jmri.profile.ProfileManager; 043import jmri.profile.ProfileUtils; 044import jmri.util.jdom.JDOMUtil; 045import jmri.util.swing.BeanSelectCreatePanel; 046import jmri.util.swing.JmriJOptionPane; 047 048import org.jdom2.Element; 049import org.jdom2.JDOMException; 050 051/** 052 * Set up and run automated speed table calibration. 053 * <p> 054 * Uses three sensors in a row (see diagram in window help): 055 * <ul> 056 * <li>Start sensor: Track where locomotive starts 057 * <li>Block sensor: Middle track. This time through this is used to measure the 058 * speed. 059 * <li>Finish sensor: Track where locomotive stops before repeating. 060 * </ul> 061 * The expected sequence is: 062 * <ul> 063 * <li>Start moving with Start sensor on, others off. 064 * <li>Block (middle) sensor goes active: startListener calls startTiming 065 * <li>Finish sensor goes active: finishListener calls stopCurrentSpeedStep 066 * <li>Block (middle) sensor goes inactive: startListener calls stopLoco, which 067 * stops loco after 2.5 seconds 068 * </ul> 069 * After a forward run, the Start and Finish sensors are swapped for a run in 070 * reverse. 071 */ 072class SpeedProfilePanel extends jmri.util.swing.JmriPanel implements ThrottleListener { 073 074 public static final String XML_ROOT = "speedprofiler-config"; 075 public static final String XML_NAMESPACE = "http://jmri.org/xml/schema/speedometer-3-9-3.xsd"; 076 JButton profileButton = new JButton(Bundle.getMessage("ButtonStart")); 077 JButton cancelButton = new JButton(Bundle.getMessage("ButtonCancel")); 078 JButton testButton = new JButton(Bundle.getMessage("ButtonTest")); 079 JButton testCancelButton = new JButton(Bundle.getMessage("ButtonCancel")); 080 JButton clearNewDataButton = new JButton(Bundle.getMessage("ButtonClearNewData")); 081 JButton viewNewButton = new JButton(Bundle.getMessage("ButtonViewNew")); 082 JButton viewMergedButton = new JButton(Bundle.getMessage("ButtonViewMerged")); 083 JButton viewButton = new JButton(Bundle.getMessage("ButtonViewCurrent")); 084 JCheckBox createSensorsCheckBox = new JCheckBox(Bundle.getMessage("CreateSensorsIfNotDefined")); 085 JCheckBox useCurrentSpeedStepsCheckBox = new JCheckBox(Bundle.getMessage("UseCurrentSpeedSteps")); 086 087 JButton updateProfileButton = new JButton(Bundle.getMessage("ButtonUpdateProfile")); 088 JButton replaceProfileButton = new JButton(Bundle.getMessage("ButtonSaveProfile")); 089 JButton deleteProfileButton = new JButton(Bundle.getMessage("ButtonDeleteProfile")); 090 JButton saveDefaultsButton = new JButton(Bundle.getMessage("ButtonSaveDefaults")); 091 JTextField lengthField = new JTextField(10); 092 JRadioButton lengthUnitInches = new JRadioButton(Bundle.getMessage("in")); 093 JRadioButton lengthUnitMm = new JRadioButton(Bundle.getMessage("mm")); 094 ButtonGroup lengthUnit = new ButtonGroup(); 095 JTextField sensorDelay = new JTextField(5); 096 JTextField speedStepTest = new JTextField(5); 097 JTextField speedStepTestFwd = new JTextField(10); 098 JTextField speedStepTestRev = new JTextField(10); 099 JTextField speedStepFrom = new JTextField(5); 100 JTextField speedStepTo = new JTextField(5); 101 JTextField speedStepIncr = new JTextField(5); 102 MakeLabelPanel labelSpeedStepIncrement; 103 JLabel warrentScaleLabel = new JLabel(); 104 105 // Start or finish sensor 106 BeanSelectCreatePanel<Sensor> sensorAPanel = new BeanSelectCreatePanel<>(InstanceManager.sensorManagerInstance(), null); 107 108 // Finish or start sensor 109 BeanSelectCreatePanel<Sensor> sensorBPanel = new BeanSelectCreatePanel<>(InstanceManager.sensorManagerInstance(), null); 110 111 // Block sensor 112 BeanSelectCreatePanel<Block> blockCPanel = new BeanSelectCreatePanel<>(InstanceManager.getDefault(jmri.BlockManager.class), null); 113 BeanSelectCreatePanel<Sensor> sensorCPanel = new BeanSelectCreatePanel<>(InstanceManager.sensorManagerInstance(), null); 114 115 RosterEntryComboBox reBox = new RosterEntryComboBox(); 116 JLabel throttleStatus = new JLabel(); 117 118 SpeedProfileTable table = null; 119 boolean profile = false; 120 boolean test = false; 121 float testSpeedFwd = 0.0f; 122 float testSpeedRev = 0.0f; 123 boolean save = false; 124 boolean unmergedNewData = false; // true if new data has been gathered but not merged to profile 125 boolean unsavedUpdatedProfile = false; // true if the roster profile has been updated but not saved 126 127 private JLabel sourceLabel; 128 129 /* 130 * Capture changes in speed steps 131 */ 132 private PropertyChangeListener throttleListener = new PropertyChangeListener() { 133 @Override 134 public void propertyChange(PropertyChangeEvent evt) { 135 if (evt == null) { 136 return; 137 } 138 if (Throttle.SPEEDSTEPS.compareTo(evt.getPropertyName()) == 0) { 139 throttleSpeedSteps = ((SpeedStepMode) evt.getNewValue()).numSteps; 140 log.debug("propertyChange: {} ",Throttle.SPEEDSTEPS); 141 } 142 } 143 }; 144 145 public SpeedProfilePanel() { 146 JPanel main = new JPanel(); 147 148 GridBagLayout gb = new GridBagLayout(); 149 GridBagConstraints c = new GridBagConstraints(); 150 main.setLayout(gb); 151 int gridRow = 0; 152 c.gridx = 0; 153 c.gridy = 0; 154 c.weightx = 1.0; 155 c.anchor = GridBagConstraints.CENTER; 156 JLabel label = new JLabel(Bundle.getMessage("LabelLengthOfBlock")); 157 JPanel lengthPanel = new JPanel(); 158 lengthUnitMm.setActionCommand("MM"); 159 lengthUnitInches.setActionCommand("IN"); 160 lengthPanel.add(lengthField); 161 lengthUnit.add(lengthUnitMm); 162 lengthUnit.add(lengthUnitInches); 163 lengthPanel.add(lengthUnitMm); 164 lengthPanel.add(lengthUnitMm); 165 lengthPanel.add(lengthUnitInches); 166 addRow(main, gb, c, gridRow++, label, lengthPanel); 167 label = new JLabel(Bundle.getMessage("LabelSensorDelay")); 168 addRow(main, gb, c, gridRow++, label, sensorDelay); 169 label = new JLabel(""); 170 createSensorsCheckBox.setToolTipText(Bundle.getMessage("CreateSensorsIfNotDefinedHint")); 171 addRow(main, gb, c, gridRow++, label, createSensorsCheckBox); 172 label = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("LabelStartSensor"))); 173 addRow(main, gb, c, gridRow++, label, sensorAPanel); 174 label = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("LabelBlockSensor"))); 175 addRow(main, gb, c, gridRow++, label, sensorCPanel); 176 label = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("LabelFinishSensor"))); 177 addRow(main, gb, c, gridRow++, label, sensorBPanel); 178 label = new JLabel(Bundle.getMessage("LabelSelectRoster")); 179 JPanel left = makePadPanel(label); 180 JPanel right = makePadPanel(reBox); 181 addRow(main, gb, c, gridRow++, left, right); 182 label = new JLabel(Bundle.getMessage("LabelThrottleType")); 183 left = makePadPanel(label); 184 right = makePadPanel(throttleStatus); 185 throttleStatus.setText(""); 186 addRow(main, gb, c, gridRow++, left, right); 187 JPanel panelViews = new JPanel(); 188 panelViews.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TitleView"))); 189 panelViews.setLayout(new BoxLayout(panelViews, BoxLayout.LINE_AXIS)); 190 panelViews.add(clearNewDataButton); 191 panelViews.add(viewNewButton); 192 panelViews.add(viewMergedButton); 193 panelViews.add(viewButton); 194 left = makePadPanel(panelViews); 195 JPanel panelProfileControl = new JPanel(); 196 panelProfileControl.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("ButtonProfile"))); 197 panelProfileControl.setLayout(new BoxLayout(panelProfileControl, BoxLayout.LINE_AXIS)); 198 panelProfileControl.add(profileButton); 199 panelProfileControl.add(cancelButton); 200 right = makePadPanel(panelProfileControl); 201 addRow(main, gb, c, gridRow++, left, right); 202 203 left = new JPanel(); 204 left.add(Box.createRigidArea(new java.awt.Dimension(20, 10))); 205 left.setLayout(new BoxLayout(left, BoxLayout.PAGE_AXIS)); 206 left.add(new MakeLabelPanel("LabelStartStep", speedStepFrom)); 207 speedStepFrom.setToolTipText(Bundle.getMessage("StartStepToolTip")); 208 left.add(new MakeLabelPanel("LabelFinishStep", speedStepTo)); 209 speedStepTo.setToolTipText(Bundle.getMessage("FinishStepToolTip")); 210 // we will be updating this one, so we need to save it. 211 labelSpeedStepIncrement = new MakeLabelPanel("LabelStepIncr", speedStepIncr); 212 left.add(labelSpeedStepIncrement); 213 speedStepIncr.setToolTipText(Bundle.getMessage("StepIncrToolTip")); 214 left.add(useCurrentSpeedStepsCheckBox); 215 right = new JPanel(); 216 addRow(main, gb, c, gridRow++, left, right); 217 218 219 JPanel testDataPanel = new JPanel(); 220 testDataPanel.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TestProfileData"))); 221 testDataPanel.setLayout(new BoxLayout(testDataPanel, BoxLayout.LINE_AXIS)); 222 testDataPanel.add(new MakeLabelPanel("LabelTestStep", speedStepTest)); 223 speedStepTest.setToolTipText(Bundle.getMessage("StepTestToolTip")); 224 speedStepTestFwd.setEditable(false); 225 speedStepTestFwd.setPreferredSize(new JTextField("99999.99 WWWWW ").getPreferredSize()); 226 testDataPanel.add(new MakeLabelPanel("LabelTestStepFwd", speedStepTestFwd)); 227 speedStepTestFwd.setToolTipText(Bundle.getMessage("ForwardTestToolTip")); 228 speedStepTestRev.setPreferredSize(new JTextField("99999.99 WWWWW ").getPreferredSize()); 229 speedStepTestRev.setEditable(false); 230 testDataPanel.add(new MakeLabelPanel("LabelTestStepRev", speedStepTestRev)); 231 speedStepTestRev.setToolTipText(Bundle.getMessage("ReverseTestToolTip")); 232 left = makePadPanel(testDataPanel); 233 234 JPanel testProfileControl = new JPanel(); 235 testProfileControl.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TitleTestProfile"))); 236 testProfileControl.setLayout(new BoxLayout(testProfileControl, BoxLayout.LINE_AXIS)); 237 testProfileControl.add(testButton); 238 testProfileControl.add(testCancelButton); 239 right = makePadPanel(testProfileControl); 240 241 addRow(main, gb, c, gridRow++, left, right); 242 243 c.fill = GridBagConstraints.HORIZONTAL; 244 c.gridx = 0; 245 c.gridy = gridRow++; 246 c.gridwidth = 2; 247 sourceLabel = new JLabel(" "); 248 sourceLabel.setBackground(Color.white); 249 left = makePadPanel(sourceLabel); 250 gb.setConstraints(left, c); 251 main.add(left); 252 253 WarrantPreferences preferences = WarrantPreferences.getDefault(); 254 warrentScaleLabel.setText(Bundle.getMessage("LabelLayoutScale") + " 1:" + Float.toString(preferences.getLayoutScale())); 255 warrentScaleLabel.setBackground(Color.white); 256 warrentScaleLabel.setToolTipText(Bundle.getMessage("LayoutScaleHint")); 257 left = makePadPanel(warrentScaleLabel); 258 c.gridy = gridRow++; 259 gb.setConstraints(left, c); 260 main.add(left); 261 262 c.gridy = gridRow++; 263 JPanel southBtnPanel = new JPanel(); 264 southBtnPanel.add(clearNewDataButton); 265 southBtnPanel.add(updateProfileButton); 266 southBtnPanel.add(replaceProfileButton); 267 southBtnPanel.add(deleteProfileButton); 268 southBtnPanel.add(saveDefaultsButton); 269 main.add(southBtnPanel, c); 270 271 add(main, BorderLayout.CENTER); 272 useCurrentSpeedStepsCheckBox.addActionListener((ActionEvent e) -> { 273 useCurrentSpeedSteps = ((JCheckBox) e.getSource()).isSelected(); 274 speedStepFrom.setEnabled(!useCurrentSpeedSteps); 275 speedStepIncr.setEnabled(!useCurrentSpeedSteps); 276 speedStepTo.setEnabled(!useCurrentSpeedSteps); 277 }); 278 DocumentListener docListener = new DocumentListener() { 279 @Override 280 public void changedUpdate(DocumentEvent e) { 281 warn(); 282 } 283 @Override 284 public void removeUpdate(DocumentEvent e) { 285 warn(); 286 } 287 @Override 288 public void insertUpdate(DocumentEvent e) { 289 warn(); 290 } 291 public void warn() { 292 int sf; 293 int st; 294 try { 295 sf =Integer.parseInt(speedStepFrom.getText()); 296 } catch(NumberFormatException ex) { 297 sf = 0; 298 } 299 try { 300 st =Integer.parseInt(speedStepTo.getText()); 301 } catch(NumberFormatException ex) { 302 st = 128; 303 } 304 305 if (st > sf) { 306 labelSpeedStepIncrement.updateLabel(Bundle.getMessage("LabelStepIncr")); 307 } else { 308 labelSpeedStepIncrement.updateLabel(Bundle.getMessage("LabelStepDecr")); 309 } 310 } 311 }; 312 313 speedStepFrom.getDocument().addDocumentListener(docListener); 314 speedStepTo.getDocument().addDocumentListener(docListener); 315 316 reBox.addActionListener(e -> { 317 getSpeedSteps(); 318 }); 319 profileButton.addActionListener((ActionEvent e) -> { 320 profile = true; 321 setupProfile(); 322 }); 323 cancelButton.addActionListener((ActionEvent e) -> { 324 cancelButton(); 325 }); 326 testButton.addActionListener((ActionEvent e) -> { 327 test = true; 328 testButton(); 329 }); 330 testCancelButton.addActionListener((ActionEvent e) -> { 331 cancelButton(); 332 }); 333 viewButton.addActionListener((ActionEvent e) -> { 334 viewRosterProfileData(); 335 }); 336 337 viewNewButton.addActionListener((ActionEvent e) -> { 338 viewNewProfileData(); 339 }); 340 341 saveDefaultsButton.addActionListener((ActionEvent e) -> { 342 doSaveSettings(); 343 }); 344 clearNewDataButton.addActionListener((ActionEvent e) -> { 345 clearNewData(); 346 }); 347 viewMergedButton.addActionListener((ActionEvent e) -> { 348 viewMergedData(); 349 }); 350 updateProfileButton.addActionListener((ActionEvent e) -> { 351 updateSpeedProfileWithResults(); 352 }); 353 replaceProfileButton.addActionListener((ActionEvent e) -> { 354 removeSpeedProfile(); 355 updateSpeedProfileWithResults(); 356 }); 357 deleteProfileButton.addActionListener((ActionEvent e) -> { 358 removeSpeedProfile(); 359 }); 360 361 setButtonStates(true); 362 // Attempt to reload last values */ 363 doLoad(); 364 365 } 366 367 static void addRow(JPanel main, GridBagLayout gb, GridBagConstraints c, int row, Component left, Component right) { 368 c.gridx = 0; 369 c.gridy = row; 370 gb.setConstraints(left, c); 371 main.add(left); 372 c.gridx = 1; 373 gb.setConstraints(right, c); 374 main.add(right); 375 } 376 377 static JPanel makePadPanel(Component comp) { 378 JPanel panel = new JPanel(); 379 panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); 380 panel.add(Box.createRigidArea(new java.awt.Dimension(20, 20))); 381 panel.add(comp); 382 return panel; 383 } 384 385 private static class MakeLabelPanel extends JPanel 386 { 387 private Component comp; 388 private JLabel label; 389 public MakeLabelPanel (String text, Component comp) { 390 this.comp = comp; 391 this.label = new JLabel(Bundle.getMessage(text)); 392 this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); 393 this.add(this.label); 394 this.add(this.comp); 395 } 396 public void updateLabel(String text) { 397 this.label.setText(text); 398 } 399 } 400 401 SensorDetails sensorA; 402 SensorDetails sensorB; 403 RosterEntry re; 404 DccThrottle t; 405 int throttleSpeedSteps; 406 int finishSpeedStep; 407 protected int stepIncr; 408 protected int profileStep; 409 protected float profileSpeed; 410 protected float profileIncrement; 411 protected int profileSpeedStepMode; 412 protected float profileSensorDelay; 413 protected float profileBlockLength; 414 protected boolean useCurrentSpeedSteps; 415 protected int useCurrentSpeedSteps_index; 416 protected List<Integer> speedSettingsToUse; 417 RosterSpeedProfile rosterSpeedProfile; 418 419 protected float profileSpeedAtStart; 420 421 void setupProfile() { 422 String text; 423 finishSpeedStep = 0; 424 stepIncr = 1; 425 profileStep = 1; 426 profileSensorDelay = 0.0f; 427 useCurrentSpeedSteps = useCurrentSpeedStepsCheckBox.isSelected(); 428 useCurrentSpeedSteps_index = 0; 429 speedSettingsToUse = new ArrayList<Integer>(); 430 try { 431 profileBlockLength = Float.parseFloat(lengthField.getText()); 432 if (lengthUnit.getSelection() != null && "IN".equals(lengthUnit.getSelection().getActionCommand())) { 433 profileBlockLength = profileBlockLength * 25.4f ; 434 } 435 } catch (NumberFormatException e) { 436 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorLengthInvalid")); 437 return; 438 } 439 text = sensorDelay.getText(); 440 if (text != null && text.trim().length() > 0) { 441 try { 442 profileSensorDelay = Float.parseFloat(sensorDelay.getText()); 443 } catch (Exception e) { 444 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorSensorDelayInvalid")); 445 return; 446 } 447 } 448 setButtonStates(false); 449 if (sensorA == null) { 450 try { 451 sensorA = new SensorDetails(sensorAPanel.getNamedBean()); 452 } catch (Exception e) { 453 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorSensorNotFound", Bundle.getMessage("LabelStartSensor"))); 454 setButtonStates(true); 455 return; 456 } 457 } else { 458 Sensor tmpSen = null; 459 try { 460 tmpSen = sensorAPanel.getNamedBean(); 461 } catch (Exception e) { 462 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorSensorNotFound", Bundle.getMessage("LabelStartSensor"))); 463 setButtonStates(true); 464 return; 465 } 466 if (tmpSen != sensorA.getSensor()) { 467 sensorA.resetDetails(); 468 sensorA = new SensorDetails(tmpSen); 469 } 470 } 471 if (sensorB == null) { 472 try { 473 sensorB = new SensorDetails(sensorBPanel.getNamedBean()); 474 } catch (Exception e) { 475 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorSensorNotFound", Bundle.getMessage("LabelFinishSensor"))); 476 setButtonStates(true); 477 return; 478 } 479 480 } else { 481 Sensor tmpSen = null; 482 try { 483 tmpSen = sensorBPanel.getNamedBean(); 484 } catch (Exception e) { 485 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorSensorNotFound", Bundle.getMessage("LabelFinishSensor"))); 486 setButtonStates(true); 487 return; 488 } 489 if (tmpSen != sensorB.getSensor()) { 490 sensorB.resetDetails(); 491 sensorB = new SensorDetails(tmpSen); 492 } 493 } 494 if (middleBlockSensor == null) { 495 try { 496 middleBlockSensor = new SensorDetails(sensorCPanel.getNamedBean()); 497 } catch (Exception e) { 498 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorSensorNotFound", Bundle.getMessage("LabelBlockSensor"))); 499 setButtonStates(true); 500 return; 501 } 502 } else { 503 Sensor tmpSen = null; 504 try { 505 tmpSen = sensorCPanel.getNamedBean(); 506 } catch (Exception e) { 507 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorSensorNotFound", Bundle.getMessage("LabelBlockSensor"))); 508 setButtonStates(true); 509 return; 510 } 511 if (tmpSen != middleBlockSensor.getSensor()) { 512 middleBlockSensor.resetDetails(); 513 middleBlockSensor = new SensorDetails(tmpSen); 514 } 515 } 516 if ( re == null ) { 517 //if (reBox.getSelectedRosterEntries().length == 0) { 518 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorNoRosterSelected")); 519 log.warn("No Roster Entry selected."); 520 setButtonStates(true); 521 return; 522 } 523 524 text = speedStepFrom.getText(); 525 if (text != null && text.trim().length() > 0) { 526 try { 527 profileStep = Integer.parseInt(text); 528 if (!speedStepNumOK(profileStep, "LabelStartStep")) { 529 setButtonStates(true); 530 return; 531 } 532 } catch (Exception e) { 533 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorSpeedStep", Bundle.getMessage("LabelStartStep"))); 534 setButtonStates(true); 535 return; 536 } 537 } 538 text = speedStepTo.getText(); 539 if (text != null && text.trim().length() > 0) { 540 try { 541 finishSpeedStep = Integer.parseInt(text); 542 if (!speedStepNumOK(finishSpeedStep, "LabelFinishStep")) { 543 setButtonStates(true); 544 return; 545 } 546 } catch (Exception e) { 547 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorSpeedStep", Bundle.getMessage("LabelFinishStep"))); 548 setButtonStates(true); 549 return; 550 } 551 } else { 552 finishSpeedStep = throttleSpeedSteps; 553 } 554 text = speedStepIncr.getText(); 555 if (text != null && text.trim().length() > 0) { 556 try { 557 stepIncr = Integer.parseInt(text); 558 } catch (NumberFormatException e) { 559 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorSpeedStep", Bundle.getMessage("LabelStepIncr"))); 560 setButtonStates(true); 561 return; 562 } 563 // just incase someone uses the old way. 564 stepIncr = Math.abs(stepIncr); 565 if (!speedStepNumOK(stepIncr, "LabelStepIncr")) { 566 setButtonStates(true); 567 return; 568 } 569 // now set the increment negative if required. 570 if (profileStep > finishSpeedStep ) { 571 stepIncr *= -1; 572 } 573 } else { 574 speedSettingsToUse = new ArrayList<Integer>(); 575 for ( var speedEntry : speeds.entrySet() ) { 576 speedSettingsToUse.add(speedEntry.getKey()); 577 } 578 } 579 throttleState = 0; 580 if (re.getSpeedProfile() != null 581 && re.getSpeedProfile().getProfileSpeeds() != null 582 && re.getSpeedProfile().getProfileSpeeds().entrySet().size() > 0) { 583 for ( var speedEntry : re.getSpeedProfile().getProfileSpeeds().entrySet() ) { 584 speedSettingsToUse.add(speedEntry.getKey()); 585 } 586 } 587 boolean ok = InstanceManager.throttleManagerInstance().requestThrottle(re, this, true); // we have a mechanism for steal / share 588 if (!ok) { 589 log.warn("Throttle for locomotive {} could not be set up.", re.getId()); 590 setButtonStates(true); 591 return; 592 } 593 // Wait for throttle be correct and then run the profile 594 jmri.util.ThreadingUtil.newThread(new Runnable() { 595 @Override 596 public void run() { 597 int count = 0; 598 int trys = 10; 599 while (throttleState == 0 && count < trys) { 600 try { 601 Thread.sleep(1000); 602 log.debug("Wait"); 603 } catch (Exception ex) { 604 log.warn("Throttle for locomotive {} could not be set up.", re.getId()); 605 setButtonStates(true); 606 return; 607 } 608 count++; 609 } 610 log.debug("Run"); 611 if (throttleState != 1) { 612 log.warn("No Throttle, Aborting"); 613 setButtonStates(true); 614 return; 615 } 616 runProfile(); 617 } 618 }).start(); 619 620 } 621 622 boolean speedStepNumOK(int num, String step) { 623 if (num < 1 || num > throttleSpeedSteps ) { 624 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorSpeedStep", Bundle.getMessage(step), throttleSpeedSteps)); 625 setButtonStates(true); 626 return false; 627 } 628 return true; 629 } 630 631 javax.swing.Timer overRunTimer = null; 632 633 private volatile int throttleState = 0; // zero waiting, -1 no throttle (message already shown), 1 634 635 void getSpeedSteps() { 636 if (! (reBox.getSelectedItem() instanceof RosterEntry)) { 637 throttleStatus.setText(""); 638 return; 639 } 640 re = (RosterEntry)reBox.getSelectedItem(); 641 // release existing throttle if present 642 throttleState = 0; 643 throttleStatus.setText(Bundle.getMessage("ThrottleAcquiring")); 644 boolean ok = InstanceManager.throttleManagerInstance().requestThrottle(re, this, true); // we have a mechanism for steal / share 645 if (!ok) { 646 throttleStatus.setText(Bundle.getMessage("ThrottleErrorNotAquired")); 647 log.warn("Throttle for locomotive {} could not be set up.", re.getId()); 648 setButtonStates(true); 649 return; 650 } 651 // Wait for throttle and set up maxspeedsteps 652 jmri.util.ThreadingUtil.newThread(new Runnable() { 653 @Override 654 public void run() { 655 int count = 0; 656 int trys = 10; 657 while (throttleState == 0 && count < trys) { 658 try { 659 Thread.sleep(1000); 660 log.debug("Wait"); 661 } catch (Exception ex) { 662 log.warn("Throttle for locomotive {} could not be set up.", re.getId()); 663 return; 664 } 665 count++; 666 } 667 log.debug("Run"); 668 if (throttleState != 1) { 669 log.warn("No Throttle, Aborting"); 670 setButtonStates(true); 671 return; 672 } 673 throttleSpeedSteps = t.getSpeedStepMode().numSteps; 674 throttleStatus.setText(Bundle.getMessage("ThrottleAcquired",t.getLocoAddress(),throttleSpeedSteps)); 675 releaseThrottle(); 676 } 677 }).start(); 678 679 } 680 681 @Override 682 public void notifyThrottleFound(DccThrottle _throttle) { 683 t = _throttle; 684 if (t == null) { 685 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorThrottleNotFound")); 686 log.warn("null throttle returned for train {} during automatic initialization.", re.getId()); 687 setButtonStates(true); 688 throttleState = -1; 689 return; 690 } 691 if (log.isDebugEnabled()) { 692 log.debug("throttle address = {}", t.getLocoAddress().toString()); 693 } 694 t.addPropertyChangeListener(throttleListener); 695 throttleState = 1; 696 } 697 698 private void runProfile() { 699 if (!useCurrentSpeedSteps) { 700 SpeedStepMode speedStepMode = t.getSpeedStepMode(); 701 profileIncrement = t.getSpeedIncrement(); 702 profileSpeedStepMode = speedStepMode.numSteps; 703 if (finishSpeedStep == 0) { 704 if (profileIncrement < 0) { 705 finishSpeedStep = 2; 706 } else { 707 finishSpeedStep = profileSpeedStepMode; 708 } 709 } 710 log.debug("Speed step mode {}", profileSpeedStepMode); 711 profileSpeedAtStart= Math.min(finishSpeedStep, profileStep) * profileIncrement ; 712 profileSpeed = profileIncrement * profileStep; 713 } else { 714 profileSpeed = (float)speedSettingsToUse.get(useCurrentSpeedSteps_index)/1000; 715 profileSpeedAtStart = profileSpeed; 716 } 717 if (profile) { 718 startSensor = middleBlockSensor.getSensor(); 719 finishSensor = sensorB.getSensor(); 720 startListener = new PropertyChangeListener() { 721 @Override 722 public void propertyChange(PropertyChangeEvent e) { 723 if (e.getPropertyName().equals("KnownState")) { 724 if (((Integer) e.getNewValue()) == Sensor.ACTIVE) { 725 startTiming(); 726 } 727 if (((Integer) e.getNewValue()) == Sensor.INACTIVE) { 728 stopLoco(); 729 } 730 } 731 } 732 }; 733 finishListener = new PropertyChangeListener() { 734 @Override 735 public void propertyChange(PropertyChangeEvent e) { 736 if (e.getPropertyName().equals("KnownState")) { 737 if (((Integer) e.getNewValue()) == Sensor.ACTIVE) { 738 stopCurrentSpeedStep(); 739 } 740 } 741 } 742 }; 743 744 isForward = true; 745 startProfile(); 746 } else { 747 // Speed test. 748 // Once back and forth 749 stepIncr = 1; 750 profileStep = Integer.parseInt(speedStepTest.getText()); 751 finishSpeedStep = profileStep; 752 profileSpeed = profileIncrement * profileStep; 753 startSensor = middleBlockSensor.getSensor(); 754 finishSensor = sensorB.getSensor(); 755 startListener = new PropertyChangeListener() { 756 @Override 757 public void propertyChange(PropertyChangeEvent e) { 758 if (e.getPropertyName().equals("KnownState")) { 759 if (((Integer) e.getNewValue()) == Sensor.ACTIVE) { 760 startTiming(); 761 } 762 if (((Integer) e.getNewValue()) == Sensor.INACTIVE) { 763 stopLoco(); 764 } 765 } 766 } 767 }; 768 finishListener = new PropertyChangeListener() { 769 @Override 770 public void propertyChange(PropertyChangeEvent e) { 771 if (e.getPropertyName().equals("KnownState")) { 772 if (((Integer) e.getNewValue()) == Sensor.ACTIVE) { 773 stopCurrentSpeedStep(); 774 } 775 } 776 } 777 }; 778 779 isForward = true; 780 startProfile(); 781 } 782 } 783 784 void setButtonStates(boolean state) { 785 cancelButton.setEnabled(!state); 786 profileButton.setEnabled(state); 787 testButton.setEnabled(state); 788 testCancelButton.setEnabled(!state); 789 viewButton.setEnabled(state); 790 deleteProfileButton.setEnabled(state); 791 if (state && speeds.size() > 0) { 792 viewNewButton.setEnabled(true); 793 viewMergedButton.setEnabled(true); 794 replaceProfileButton.setEnabled(true); 795 updateProfileButton.setEnabled(true); 796 clearNewDataButton.setEnabled(true); 797 } else { 798 viewNewButton.setEnabled(false); 799 viewMergedButton.setEnabled(false); 800 replaceProfileButton.setEnabled(false); 801 updateProfileButton.setEnabled(false); 802 clearNewDataButton.setEnabled(false); 803 } 804 if (state) { 805 sourceLabel.setText(" "); 806 profile = false; 807 test = false; 808 } 809 if (sensorA != null) { 810 sensorA.resetDetails(); 811 } 812 if (sensorB != null) { 813 sensorB.resetDetails(); 814 } 815 if (middleBlockSensor != null) { 816 middleBlockSensor.resetDetails(); 817 } 818 } 819 820 @Override 821 public void notifyFailedThrottleRequest(jmri.LocoAddress address, String reason) { 822 throttleStatus.setText(Bundle.getMessage("ThrottleErrorNotAquiredWithReason", reason)); 823 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorFailThrottleRequest")); 824 log.error("Throttle request for {} failed because {}", address, reason); 825 setButtonStates(true); 826 throttleState = -1; 827 } 828 829 /** 830 * Profiling on a stolen or shared throttle is invalid 831 * <p> 832 * {@inheritDoc} 833 */ 834 @Override 835 public void notifyDecisionRequired(jmri.LocoAddress address, DecisionType question) { 836 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorNoStealing")); 837 throttleStatus.setText(Bundle.getMessage("ErrorNoStealing")); 838 InstanceManager.throttleManagerInstance().cancelThrottleRequest(address, this); 839 setButtonStates(true); 840 throttleState = -1; 841 } 842 843 PropertyChangeListener startListener = null; 844 PropertyChangeListener finishListener = null; 845 PropertyChangeListener middleListener = null; 846 847 Sensor startSensor; 848 Sensor finishSensor; 849 SensorDetails middleBlockSensor; 850 851 void startProfile() { 852 stepCalculated = false; 853 sourceLabel.setText(Bundle.getMessage("StatusLabelNextRun")); 854 if (isForward) { 855 finishSensor = sensorB.getSensor(); 856 } else { 857 finishSensor = sensorA.getSensor(); 858 } 859 startSensor = middleBlockSensor.getSensor(); 860 startSensor.addPropertyChangeListener(startListener); 861 finishSensor.addPropertyChangeListener(finishListener); 862 t.setIsForward(!isForward); 863 // this switching back and forward helps if the throttle was stolen. 864 // the sleeps are needed as some systems dont like a speed setting right after a direction setting. 865 // If we had guarenteed access to the Dispatcher frame we could use 866 // Thread.sleep(InstanceManager.getDefault(DispatcherFrame.class).getMinThrottleInterval() * 2) 867 try { 868 Thread.sleep(250); 869 } catch (InterruptedException e) { 870 // Nothing I can do. 871 } 872 873 t.setIsForward(isForward); 874 try { 875 Thread.sleep(250); 876 } catch (InterruptedException e) { 877 // Nothing I can do. 878 } 879 880 log.debug("Set speed to [{}] isForward [{}] Increment [{}] Step [{}] SpeedStepMode [{}]", 881 profileSpeed, isForward, profileIncrement, profileStep, profileSpeedStepMode); 882 t.setSpeedSetting(profileSpeed); 883 sourceLabel.setText(Bundle.getMessage("StatusLabelBlockToGoActive")); 884 } 885 886 boolean isForward = true; 887 888 void startTiming() { 889 startTime = System.nanoTime(); 890 if (!useCurrentSpeedSteps) { 891 sourceLabel.setText(Bundle.getMessage("StatusLabelCurrentRun", 892 (isForward ? Bundle.getMessage("LabelTestStepFwd") : Bundle.getMessage("LabelTestStepRev")), 893 profileStep, finishSpeedStep)); 894 } else { 895 sourceLabel.setText(Bundle.getMessage("StatusLabelCurrentRun", 896 (isForward ? Bundle.getMessage("LabelTestStepFwd") : Bundle.getMessage("LabelTestStepRev")), 897 Float.toString(profileSpeed*100.0f) + "%", Float.toString((float)speedSettingsToUse.get(speedSettingsToUse.size()-1)/10)+"%")); 898 } 899 } 900 901 boolean stepCalculated = false; 902 903 void stopCurrentSpeedStep() { 904 finishTime = System.nanoTime(); 905 stepCalculated = true; 906 finishSensor.removePropertyChangeListener(finishListener); 907 sourceLabel.setText(Bundle.getMessage("StatusLabelCalculating")); 908 if (profileSpeed/2 > profileSpeedAtStart) { 909 log.debug("Divide by [{}] [{}]",profileSpeed/2,profileSpeedAtStart); 910 t.setSpeedSetting(profileSpeed / 2); 911 } else { 912 t.setSpeedSetting(profileSpeedAtStart); 913 } 914 915 calculateSpeed(); 916 sourceLabel.setText(Bundle.getMessage("StatusLabelWaitingToClear")); 917 } 918 919 void stopLoco() { 920 921 if (!stepCalculated) { 922 return; 923 } 924 925 startSensor.removePropertyChangeListener(startListener); 926 finishSensor.removePropertyChangeListener(finishListener); 927 928 isForward = !isForward; 929 // Increment and test 930 if (isForward) { 931 if (!useCurrentSpeedSteps) { 932 profileSpeed = profileIncrement * stepIncr + profileSpeed; 933 profileStep += stepIncr; 934 } else { 935 useCurrentSpeedSteps_index++; 936 if (useCurrentSpeedSteps_index < speedSettingsToUse.size()) { 937 profileSpeed = (float)speedSettingsToUse.get(useCurrentSpeedSteps_index)/1000; 938 } 939 } 940 if (( !useCurrentSpeedSteps && stepIncr > 0 && profileStep > finishSpeedStep) 941 || ( !useCurrentSpeedSteps && stepIncr < 0 && profileStep < finishSpeedStep) 942 || (useCurrentSpeedSteps && useCurrentSpeedSteps_index >= speedSettingsToUse.size())) { 943 t.setSpeedSetting(0.0f); 944 if (!profile) { 945 // there are only the 2 fields on screen to be updated after a test 946 speedStepTestFwd.setText(RosterSpeedProfile.convertMMSToScaleSpeedWithUnits(testSpeedFwd,true)); 947 speedStepTestRev.setText(RosterSpeedProfile.convertMMSToScaleSpeedWithUnits(testSpeedRev,true)); 948 } 949 releaseThrottle(); 950 //updateSpeedProfileWithResults(); 951 setButtonStates(true); 952 return; 953 } 954 } 955 // Loco may have been brought to half-speed in stopCurrentSpeedStep, so wait for that to take effect then stop & restart 956 javax.swing.Timer stopTimer = new javax.swing.Timer(2500, new java.awt.event.ActionListener() { 957 @Override 958 public void actionPerformed(java.awt.event.ActionEvent e) { 959 960 // finally command the stop 961 t.setSpeedSetting(0.0f); 962 // and a second later, restart going the other way 963 javax.swing.Timer restartTimer = new javax.swing.Timer(1000, new java.awt.event.ActionListener() { 964 @Override 965 public void actionPerformed(java.awt.event.ActionEvent e) { 966 startProfile(); 967 } 968 }); 969 restartTimer.setRepeats(false); 970 restartTimer.start(); 971 } 972 }); 973 stopTimer.setRepeats(false); 974 stopTimer.start(); 975 } 976 977 void calculateSpeed() { 978 float duration = (((float) (finishTime - startTime)) / 1000000000); // convert to seconds 979 duration = duration - (profileSensorDelay / 1000); // allow for time differences between sensor delays 980 float speed = profileBlockLength / duration; 981 log.debug("Step: {} duration: {} length: {} speed: {}", 982 profileStep, duration, profileBlockLength, speed); 983 984 985 if (profile) { 986 // save results to table 987 int iSpeedStep = Math.round(profileSpeed * 1000); 988 if (!speeds.containsKey(iSpeedStep)) { 989 speeds.put(iSpeedStep, new SpeedStep()); 990 } 991 SpeedStep ss = speeds.get(iSpeedStep); 992 if (isForward) { 993 ss.setForwardSpeed(speed); 994 } else { 995 ss.setReverseSpeed(speed); 996 } 997 save = true; 998 } else { 999 // testing, save results to the 2 fields. 1000 if (isForward) { 1001 testSpeedFwd = speed; 1002 } else { 1003 testSpeedRev = speed; 1004 } 1005 } 1006 } 1007 1008 /** 1009 * Merge the new data into the existing speedprofile, or create if not 1010 * current, and save. Clear new data. 1011 */ 1012 void updateSpeedProfileWithResults() { 1013 cancelButton(); 1014 RosterSpeedProfile rosterSpeedProfile = re.getSpeedProfile(); 1015 if (rosterSpeedProfile == null) { 1016 rosterSpeedProfile = new RosterSpeedProfile(re); 1017 re.setSpeedProfile(rosterSpeedProfile); 1018 } 1019 for (Map.Entry<Integer, SpeedStep> entry : speeds.entrySet()) { 1020 rosterSpeedProfile.setSpeed(entry.getKey(), entry.getValue().getForwardSpeed(), entry.getValue().getReverseSpeed()); 1021 } 1022 re.updateFile(); 1023 Roster.getDefault().writeRoster(); 1024 clearNewData(); 1025 setButtonStates(true); 1026 save = false; 1027 } 1028 1029 /** 1030 * Merge the current profile with the new data in a temp area and show. 1031 */ 1032 void viewMergedData() { 1033 // create a new temporay rosterspeedentry 1034 RosterEntry tmpRe = new RosterEntry(); 1035 RosterSpeedProfile tmpRsp = new RosterSpeedProfile(tmpRe); 1036 // reference the current one. 1037 RosterSpeedProfile rosterSpeedProfile = re.getSpeedProfile(); 1038 //copy across the profile data 1039 for (Integer i : rosterSpeedProfile.getProfileSpeeds().keySet()) { 1040 tmpRsp.setSpeed(i, rosterSpeedProfile.getProfileSpeeds().get(i).getForwardSpeed(), rosterSpeedProfile.getProfileSpeeds().get(i).getReverseSpeed()); 1041 } 1042 //copy, merge the newdata speed points 1043 for (Map.Entry<Integer, SpeedStep> entry : speeds.entrySet()) { 1044 tmpRsp.setSpeed(entry.getKey(), entry.getValue().getForwardSpeed(), entry.getValue().getReverseSpeed()); 1045 } 1046 // show, its a bit convoluted, to get the speed table 1047 // we have to set the new profile in the tmp rosterentry 1048 // and ask for it back as a speedtable. 1049 tmpRe.setSpeedProfile(tmpRsp); 1050 RosterSpeedProfile tmpSp = tmpRe.getSpeedProfile(); 1051 if (tmpSp != null) { 1052 if (table != null) { 1053 table.dispose(); 1054 } 1055 table = new SpeedProfileTable(tmpSp, tmpRe.getId()); 1056 table.setVisible(true); 1057 return; 1058 } 1059 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorNoSpeedProfile")); 1060 setButtonStates(true); 1061 } 1062 1063 void clearNewData() { 1064 speeds.clear(); 1065 } 1066 1067 void removeSpeedProfile() { 1068 cancelButton(); 1069 RosterSpeedProfile rosterSpeedProfile = re.getSpeedProfile(); 1070 if (rosterSpeedProfile != null) { 1071 rosterSpeedProfile.clearCurrentProfile(); 1072 } 1073 re.updateFile(); 1074 Roster.getDefault().writeRoster(); 1075 save = false; 1076 } 1077 1078 /** 1079 * View the new data collected we create a dummy entry and file with 1080 * collected data 1081 */ 1082 void viewNewProfileData() { 1083 RosterEntry tmpRe = new RosterEntry(); 1084 RosterSpeedProfile rosterSpeedProfile = tmpRe.getSpeedProfile(); 1085 if (rosterSpeedProfile == null) { 1086 rosterSpeedProfile = new RosterSpeedProfile(tmpRe); 1087 tmpRe.setSpeedProfile(rosterSpeedProfile); 1088 } 1089 for (Map.Entry<Integer, SpeedStep> entry : speeds.entrySet()) { 1090 rosterSpeedProfile.setSpeed(entry.getKey(), entry.getValue().getForwardSpeed(), entry.getValue().getReverseSpeed()); 1091 } 1092 1093 RosterSpeedProfile speedProfile = tmpRe.getSpeedProfile(); 1094 if (speedProfile != null) { 1095 if (table != null) { 1096 table.dispose(); 1097 } 1098 table = new SpeedProfileTable(speedProfile, tmpRe.getId()); 1099 table.setVisible(true); 1100 return; 1101 } 1102 1103 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorNoSpeedProfile")); 1104 setButtonStates(true); 1105 } 1106 1107 /** 1108 * View the current speedprofile table entrys 1109 */ 1110 void viewRosterProfileData() { 1111 if (reBox.getSelectedRosterEntries().length == 0) { 1112 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorNoRosterSelected")); 1113 setButtonStates(true); 1114 return; 1115 } 1116 re = reBox.getSelectedRosterEntries()[0]; 1117 if (re != null) { 1118 RosterSpeedProfile speedProfile = re.getSpeedProfile(); 1119 if (speedProfile != null) { 1120 if (table != null) { 1121 table.dispose(); 1122 } 1123 table = new SpeedProfileTable(re.getSpeedProfile(), re.getId()); 1124 table.setVisible(true); 1125 return; 1126 } 1127 } 1128 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorNoSpeedProfile")); 1129 setButtonStates(true); 1130 } 1131 1132 /** 1133 * If we have a throttle, set speed zero and release 1134 */ 1135 private void releaseThrottle() { 1136 if (t != null) { 1137 t.removePropertyChangeListener(throttleListener); 1138 t.setSpeedSetting(0.0f); 1139 try { 1140 Thread.sleep(250); 1141 } catch (InterruptedException e) { 1142 log.warn("Wait interupted, release throttle immediatlely"); 1143 } 1144 log.debug("releaseing[{}]", t.getLocoAddress().getNumber()); 1145 InstanceManager.throttleManagerInstance().releaseThrottle(t, this); 1146 t = null; 1147 } 1148 } 1149 1150 /** 1151 * We are canceling, release throttle, reset sensors. 1152 */ 1153 1154 void cancelButton() { 1155 releaseThrottle(); 1156 if (t != null) { 1157 t.removePropertyChangeListener(throttleListener); 1158 t.setSpeedSetting(0.0f); 1159 try { 1160 Thread.sleep(250); 1161 } catch (InterruptedException e) { 1162 // Nothing I can do. 1163 } 1164 1165 InstanceManager.throttleManagerInstance().releaseThrottle(t, this); 1166 t = null; 1167 } 1168 if (startSensor != null) { 1169 startSensor.removePropertyChangeListener(startListener); 1170 } 1171 if (finishSensor != null) { 1172 finishSensor.removePropertyChangeListener(finishListener); 1173 } 1174 if (middleListener != null) { 1175 middleBlockSensor.getSensor().removePropertyChangeListener(middleListener); 1176 } 1177 setButtonStates(true); 1178 } 1179 1180 void testButton() { 1181 // TODO Should also test that the step is no greater than those available on the throttle. 1182 try { 1183 Integer.parseInt(speedStepTest.getText()); 1184 } catch (NumberFormatException e) { 1185 JmriJOptionPane.showMessageDialog(this, 1186 Bundle.getMessage("ErrorSpeedStep", Bundle.getMessage("LabelTestStep"))); 1187 return; 1188 } 1189 setupProfile(); 1190 1191 } 1192 1193 // Removed for now 1194 // Doesnot work 1195 // not referenced 1196 /* 1197 void stopTrainTest() { 1198 float sectionlength; 1199 try { 1200 sectionlength = Float.parseFloat(lengthField.getText()); 1201 if ("IN".equals(lengthUnit.getSelection().getActionCommand())) { 1202 sectionlength = sectionlength * 25.4f ; 1203 } 1204 } catch (Exception e) { 1205 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorLengthInvalid")); 1206 return; 1207 } 1208 re.getSpeedProfile().changeLocoSpeed(t, sectionlength, 0.0f); 1209 setButtonStates(true); 1210 startSensor.removePropertyChangeListener(startListener); 1211 } 1212 */ 1213 long startTime; 1214 long finishTime; 1215 1216 ArrayList<Double> forwardOverRuns = new ArrayList<>(); 1217 ArrayList<Double> reverseOverRuns = new ArrayList<>(); 1218 1219 JPanel update; 1220 1221 static class SensorDetails { 1222 1223 Sensor sensor = null; 1224 long inactiveDelay = 0; 1225 long activeDelay = 0; 1226 boolean usingGlobal = false; 1227 1228 SensorDetails(Sensor sen) { 1229 sensor = sen; 1230 usingGlobal = sen.getUseDefaultTimerSettings(); 1231 activeDelay = sen.getSensorDebounceGoingActiveTimer(); 1232 inactiveDelay = sen.getSensorDebounceGoingInActiveTimer(); 1233 } 1234 1235 void setupSensor() { 1236 sensor.setUseDefaultTimerSettings(false); 1237 sensor.setSensorDebounceGoingActiveTimer(0); 1238 sensor.setSensorDebounceGoingInActiveTimer(0); 1239 } 1240 1241 void resetDetails() { 1242 sensor.setUseDefaultTimerSettings(usingGlobal); 1243 sensor.setSensorDebounceGoingActiveTimer(activeDelay); 1244 sensor.setSensorDebounceGoingInActiveTimer(inactiveDelay); 1245 } 1246 1247 Sensor getSensor() { 1248 return sensor; 1249 } 1250 1251 } 1252 1253 TreeMap<Integer, SpeedStep> speeds = new TreeMap<>(); 1254 1255 static class SpeedStep { 1256 1257 float forward = 0.0f; 1258 float reverse = 0.0f; 1259 1260 SpeedStep() { 1261 } 1262 1263 void setForwardSpeed(float speed) { 1264 forward = speed; 1265 } 1266 1267 void setReverseSpeed(float speed) { 1268 reverse = speed; 1269 } 1270 1271 float getForwardSpeed() { 1272 return forward; 1273 } 1274 1275 float getReverseSpeed() { 1276 return reverse; 1277 } 1278 } 1279 1280 /* 1281 * Start of code for saving and restoring the settings 1282 */ 1283 1284 /** 1285 * Save current sensor and block information to file 1286 */ 1287 private void doSaveSettings() { 1288 log.debug("Start storing SpeedProfiler settings..."); 1289 1290 // Create root element 1291 Element root = new Element(XML_ROOT, XML_NAMESPACE); 1292 1293 Element values; 1294 1295 // Store configuration 1296 root.addContent(values = new Element("configuration")); 1297 if (lengthField.getText().length() > 0) { 1298 values.addContent(new Element("length").addContent(lengthField.getText())); 1299 } 1300 if (createSensorsCheckBox.isSelected()) { 1301 values.addContent(new Element("createMissingSensors").addContent("true")); 1302 } 1303 if (lengthUnit.getSelection() != null) { 1304 String selectedValue = lengthUnit.getSelection().getActionCommand(); 1305 values.addContent(new Element("lengthUnit").addContent(selectedValue)); 1306 } 1307 if (sensorDelay.getText().length() > 0) { 1308 values.addContent(new Element("sensordelay").addContent(sensorDelay.getText())); 1309 } 1310 // Store values 1311 //if (sensorAPanel.getNamedBean(). > 0) { 1312 // Create sensors element 1313 root.addContent(values = new Element("sensors")); 1314 1315 // Store start sensor 1316 Element e = new Element("sensor"); 1317 e.addContent(new Element("sensorname").addContent("sensorAPanel")); 1318 e.addContent(new Element("sensorvalue").addContent(sensorAPanel.getDisplayName())); 1319 values.addContent(e); 1320 e = new Element("sensor"); 1321 e.addContent(new Element("sensorname").addContent("sensorBPanel")); 1322 e.addContent(new Element("sensorvalue").addContent(sensorBPanel.getDisplayName())); 1323 values.addContent(e); 1324 e = new Element("sensor"); 1325 e.addContent(new Element("sensorname").addContent("sensorCPanel")); 1326 e.addContent(new Element("sensorvalue").addContent(sensorCPanel.getDisplayName())); 1327 values.addContent(e); 1328 root.addContent(values = new Element("steps")); 1329 if (speedStepFrom.getText().length() > 0) { 1330 values.addContent(new Element("speedStepFrom").addContent(speedStepFrom.getText())); 1331 } 1332 if (speedStepTo.getText().length() > 0) { 1333 values.addContent(new Element("speedStepTo").addContent(speedStepTo.getText())); 1334 } 1335 if (speedStepIncr.getText().length() > 0) { 1336 values.addContent(new Element("speedStepIncr").addContent(speedStepIncr.getText())); 1337 } 1338 1339 try { 1340 ProfileUtils.getAuxiliaryConfiguration(ProfileManager.getDefault().getActiveProfile()) 1341 .putConfigurationFragment(JDOMUtil.toW3CElement(root), true); 1342 } catch (JDOMException ex) { 1343 log.error("Unable to create create XML", ex); 1344 } 1345 1346 log.debug("...done"); 1347 } 1348 1349 /** 1350 * Load the Block and sensor information previously saved. 1351 */ 1352 private void doLoad() { 1353 Element root; 1354 1355 //set default 1356 lengthUnitMm.setSelected(true); 1357 1358 log.debug("Check if there's anything to load"); 1359 try { 1360 root = JDOMUtil.toJDOMElement(ProfileUtils.getAuxiliaryConfiguration(ProfileManager.getDefault().getActiveProfile()) 1361 .getConfigurationFragment(XML_ROOT, XML_NAMESPACE, true)); 1362 } catch (NullPointerException ex) { 1363 // expected if never saved before 1364 log.debug("Nothing to load"); 1365 return; 1366 } 1367 1368 log.debug("Start loading SpeedProfiler settings..."); 1369 1370 // First read configuration 1371 if (root.getChild("configuration") != null) { 1372 List<Element> l = root.getChild("configuration").getChildren(); 1373 if (log.isDebugEnabled()) { 1374 log.debug("readFile sees {} configurations", l.size()); 1375 } 1376 for (int i = 0; i < l.size(); i++) { 1377 Element e = l.get(i); 1378 switch (e.getName()) { 1379 case "length": 1380 lengthField.setText(e.getValue()); 1381 break; 1382 case "sensordelay": 1383 sensorDelay.setText(e.getValue()); 1384 break; 1385 case "lengthUnit": 1386 if ("IN".equals(e.getValue())) { 1387 lengthUnitInches.setSelected(true); 1388 } else { 1389 lengthUnitMm.setSelected(true); 1390 } 1391 break; 1392 case "createMissingSensors": 1393 if ("true".equals(e.getValue())) { 1394 createSensorsCheckBox.setSelected(true); 1395 } 1396 break; 1397 1398 default: 1399 log.warn("Invalid field in PanelProSpeedProfiler.xml"); 1400 } 1401 } 1402 } 1403 // Now read sensor information 1404 if (root.getChild("sensors") != null) { 1405 List<Element> l = root.getChild("sensors").getChildren("sensor"); 1406 if (log.isDebugEnabled()) { 1407 log.debug("readFile sees {} sensors", l.size()); 1408 } 1409 SensorManager manager = InstanceManager.getDefault(SensorManager.class); 1410 for (int i = 0; i < l.size(); i++) { 1411 Element e = l.get(i); 1412 String sensorType = e.getChild("sensorname").getText(); 1413 if (createSensorsCheckBox.isSelected() && manager.getSensor(e.getChild("sensorvalue").getText()) == null) { 1414 try { 1415 manager.newSensor(e.getChild("sensorvalue").getText(),null); 1416 } catch (IllegalArgumentException iex) { 1417 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorSensorCannotCreate", e.getChild("sensorvalue").getText())); 1418 } 1419 } 1420 switch (sensorType) { 1421 case "sensorAPanel": 1422 sensorAPanel.setDefaultNamedBean(manager.getSensor(e.getChild("sensorvalue").getText())); 1423 break; 1424 case "sensorBPanel": 1425 sensorBPanel.setDefaultNamedBean(manager.getSensor(e.getChild("sensorvalue").getText())); 1426 break; 1427 case "sensorCPanel": 1428 sensorCPanel.setDefaultNamedBean(manager.getSensor(e.getChild("sensorvalue").getText())); 1429 break; 1430 default: 1431 log.warn("Invalid Sensor found in DecoderProSpeedProfile.xml"); 1432 } 1433 } 1434 } 1435 if (root.getChild("steps") != null) { 1436 List<Element> l = root.getChild("steps").getChildren(); 1437 for (int i = 0; i < l.size(); i++) { 1438 Element e = l.get(i); 1439 switch (e.getName()) { 1440 case "speedStepFrom": 1441 speedStepFrom.setText(e.getValue()); 1442 break; 1443 case "speedStepTo": 1444 speedStepTo.setText(e.getValue()); 1445 break; 1446 case "speedStepIncr": 1447 speedStepIncr.setText(e.getValue()); 1448 break; 1449 default: 1450 log.warn("Invalid field in steps of PanelProSpeedProfiler.xml"); 1451 } 1452 } 1453 } 1454 1455 log.debug("...done"); 1456 } 1457 1458 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SpeedProfilePanel.class); 1459 1460}