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