001package jmri.jmrix.nce.clockmon; 002 003import java.awt.GridBagConstraints; 004import java.awt.GridBagLayout; 005import java.awt.Insets; 006import java.awt.event.ActionEvent; 007import java.beans.PropertyChangeEvent; 008import java.text.DecimalFormat; 009import java.util.ArrayList; 010import java.util.Date; 011 012import javax.swing.BorderFactory; 013import javax.swing.BoxLayout; 014import javax.swing.ButtonGroup; 015import javax.swing.JButton; 016import javax.swing.JCheckBox; 017import javax.swing.JLabel; 018import javax.swing.JPanel; 019import javax.swing.JRadioButton; 020import javax.swing.JTextField; 021import javax.swing.Timer; 022 023import org.slf4j.Logger; 024import org.slf4j.LoggerFactory; 025 026import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 027import jmri.InstanceManager; 028import jmri.Timebase; 029import jmri.TimebaseRateException; 030import jmri.jmrix.nce.*; 031import jmri.util.swing.JmriJOptionPane; 032 033/** 034 * Frame displaying and programming a NCE clock monitor. 035 * <p> 036 * Some of the message formats used in this class are Copyright NCE Inc. and 037 * used with permission as part of the JMRI project. That permission does not 038 * extend to uses in other software products. If you wish to use this code, 039 * algorithm or these message formats outside of JMRI, please contact NCE Inc 040 * for separate permission. 041 * 042 * Notes: 043 * 044 * 1. the commands for time don't include seconds so I had to use memory write 045 * to sync nce clock. 046 * 047 * 2. I tried fiddling with the internal nce clock loop values, didn't work. 048 * 049 * 3. to sync nce to internal clock: A. set an alarm about 5 seconds before next 050 * minute B. read nce clock C. compute error and record last X errors for 051 * correction calc D. adjust nce clock as needed E. reset alarm after next 052 * internal minute ticks 053 * 054 * 4. to sync internal to nce clock A. every so often, read nce clock and 055 * compare to internal B. compute error and record last X errors for correction 056 * calc C. adjust internal clock rate factor as needed 057 * 058 * 5. The clock message only seems to go out to the throttles on the tic of the 059 * minute. 060 * 061 * 6. The nce clock must be left running, or it doesn't tic and 062 * therefore doesn't go out over the bus. 063 * 064 * @author Ken Cameron Copyright (C) 2007, 2023 065 * derived from loconet.clockmonframe by Bob Jacobson Copyright (C) 2003 066 */ 067public class ClockMonPanel extends jmri.jmrix.nce.swing.NcePanel implements NceListener { 068 069 public static final int CS_CLOCK_MEM_SIZE = 0x10; 070 public static final int CS_CLOCK_SCALE = 0x00; 071 public static final int CS_CLOCK_TICK = 0x01; 072 public static final int CS_CLOCK_SECONDS = 0x02; 073 public static final int CS_CLOCK_MINUTES = 0x03; 074 public static final int CS_CLOCK_HOURS = 0x04; 075 public static final int CS_CLOCK_AMPM = 0x05; 076 public static final int CS_CLOCK_1224 = 0x06; 077 public static final int CS_CLOCK_STATUS = 0x0D; 078 public static final int CMD_CLOCK_SET_TIME_SIZE = 0x03; 079 public static final int CMD_CLOCK_SET_PARAM_SIZE = 0x02; 080 public static final int CMD_CLOCK_SET_RUN_SIZE = 0x01; 081 public static final int CMD_CLOCK_SET_REPLY_SIZE = 0x01; 082 public static final int CMD_MEM_SET_REPLY_SIZE = 0x01; 083 public static final int MAX_ERROR_ARRAY = 4; 084 public static final double MIN_POLLING_INTERVAL = 1.0; 085 public static final double MAX_POLLING_INTERVAL = 120; 086 public static final int CLOCKRATIO_MIN = 0; 087 public static final int CLOCKRATIO_MAX = 15; 088 public static final double DEFAULT_POLLING_INTERVAL = 5; 089 public static final double TARGET_SYNC_DELAY = 55; 090 public static final int SYNCMODE_OFF = 0; //0 - clocks independent 091 public static final int SYNCMODE_NCE_MASTER = 1; //1 - NCE sets Internal 092 public static final int SYNCMODE_INTERNAL_MASTER = 2; //2 - Internal sets NCE 093 public static final int WAIT_CMD_EXECUTION = 1000; 094 private static final long MAX_SECONDS_IN_DAY = 24 * 3600; 095 private static final double ESTIMATED_NCE_RATE_FACTOR = 0.92; 096 DecimalFormat fiveDigits = new DecimalFormat("0.00000"); 097 DecimalFormat fourDigits = new DecimalFormat("0.0000"); 098 DecimalFormat threeDigits = new DecimalFormat("0.000"); 099 DecimalFormat twoDigits = new DecimalFormat("0.00"); 100 101 private int waiting = 0; 102 private int clockMode = SYNCMODE_OFF; 103 private boolean waitingForCmdRead = false; 104 private boolean waitingForCmdStop = false; 105 private boolean waitingForCmdStart = false; 106 private boolean waitingForCmdRatio = false; 107 private boolean waitingForCmdTime = false; 108 private boolean waitingForCmd1224 = false; 109 private boolean updateTimeFromRead = false; 110 private boolean updateRatioFromRead = false; 111 private boolean updateFormatFromRead = false; 112 private boolean updateStatusFromRead = false; 113 private NceReply lastClockReadPacket = null; 114 //private Date lastClockReadAtTime; 115 private int nceLastHour; 116 private int nceLastMinute; 117 private int nceLastSecond; 118 private int nceLastRatio; 119 private boolean nceLastAmPm; 120 private boolean nceLast1224; 121 private boolean nceLastRunning; 122 private double internalLastRatio; 123 private boolean internalLastRunning; 124 private double pollingInterval = DEFAULT_POLLING_INTERVAL; 125 private final ArrayList<Double> priorDiffs = new ArrayList<>(); 126 private final ArrayList<Double> priorOffsetErrors = new ArrayList<>(); 127 private final ArrayList<Double> priorCorrections = new ArrayList<>(); 128 private double syncInterval = TARGET_SYNC_DELAY; 129 private int internalSyncInitStateCounter = 0; 130 private int internalSyncRunStateCounter = 0; 131 private double ncePidGainPv = 0.04; 132 private double ncePidGainIv = 0.01; 133 private double ncePidGainDv = 0.005; 134 private final double intPidGainPv = 0.02; 135 private final double intPidGainIv = 0.001; 136 private final double intPidGainDv = 0.01; 137 138 private final double rateChgMinimum = 0.001; 139 140 private int nceSyncInitStateCounter = 0; // NCE master sync initialzation state machine 141 private int nceSyncRunStateCounter = 0; // NCE master sync runtime state machine 142 private int alarmDisplayStateCounter = 0; // manages the display update from the alarm 143 144 Timebase internalClock; 145 Timer timerDisplayUpdate = null; 146 Timer alarmSyncUpdate = null; 147 148 JTextField hours = new JTextField(" 00"); 149 JTextField minutes = new JTextField(" 00"); 150 JTextField seconds = new JTextField(" 00"); 151 152 JTextField rateNce = new JTextField(" 1"); 153 JTextField amPm = new JTextField(2); 154 JCheckBox twentyFour = new JCheckBox(Bundle.getMessage("CheckBox24HourFormat")); 155 JTextField status = new JTextField(10); 156 157 JRadioButton setSyncModeNceMaster = new JRadioButton(Bundle.getMessage("ClockModeNCE")); 158 JRadioButton setSyncModeInternalMaster = new JRadioButton(Bundle.getMessage("ClockModeInternal")); 159 JRadioButton setSyncModeOff = new JRadioButton(Bundle.getMessage("ClockModeIndependent")); 160 161 JTextField internalDisplayStatus = new JTextField(60); 162 163 JTextField nceDisplayStatus = new JTextField(60); 164 165 JTextField pollingSpeed = new JTextField(5); 166 167 JTextField ncePidGainP = new JTextField(7); 168 JTextField ncePidGainI = new JTextField(7); 169 JTextField ncePidGainD = new JTextField(7); 170 JTextField intPidGainP = new JTextField(7); 171 JTextField intPidGainI = new JTextField(7); 172 JTextField intPidGainD = new JTextField(7); 173 174 transient java.beans.PropertyChangeListener minuteChangeListener; 175 176 JButton setSyncButton = new JButton(Bundle.getMessage("SetSyncMode")); 177 JButton setClockButton = new JButton(Bundle.getMessage("SetHoursMinutes")); 178 JButton setRatioButton = new JButton(Bundle.getMessage("SetRatio")); 179 JButton set1224Button = new JButton(Bundle.getMessage("Set12/24Mode")); 180 JButton setStopNceButton = new JButton(Bundle.getMessage("StopNceClock")); 181 JButton setStartNceButton = new JButton(Bundle.getMessage("StartNceClock")); 182 JButton readButton = new JButton(Bundle.getMessage("ReadAll")); 183 JButton setPollingSpeedButton = new JButton(Bundle.getMessage("SetInterfaceUpdRate")); 184 JButton setPidButton = new JButton(Bundle.getMessage("SetPid")); 185 186 private NceTrafficController tc = null; 187 188 public ClockMonPanel() { 189 super(); 190 } 191 192 /** 193 * {@inheritDoc} 194 */ 195 @Override 196 public void initContext(Object context) { 197 if (context instanceof NceSystemConnectionMemo) { 198 try { 199 initComponents((NceSystemConnectionMemo) context); 200 } catch (Exception e) { 201 log.error("NceClockMon initContext failed"); // NOI18N 202 } 203 } 204 } 205 206 /** 207 * {@inheritDoc} 208 */ 209 @Override 210 public String getHelpTarget() { 211 return "package.jmri.jmrix.nce.clockmon.ClockMonFrame"; 212 } 213 214 /** 215 * {@inheritDoc} 216 */ 217 @Override 218 public String getTitle() { 219 StringBuilder x = new StringBuilder(); 220 if (memo != null) { 221 x.append(memo.getUserName()); 222 } else { 223 x.append("NCE_"); 224 } 225 x.append(": "); 226 x.append(Bundle.getMessage("TitleNceClockMonitor")); 227 return x.toString(); 228 } 229 230 /** 231 * {@inheritDoc} 232 */ 233 @Override 234 public void initComponents(NceSystemConnectionMemo m) { 235 this.memo = m; 236 this.tc = m.getNceTrafficController(); 237 238 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 239 240 // Internal Clock Info Panel 241 JPanel panel = new JPanel(); 242 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 243 JPanel pane2 = new JPanel(); 244 GridBagLayout gLayout = new GridBagLayout(); 245 GridBagConstraints gConstraints = new GridBagConstraints(); 246 247 javax.swing.border.Border pane2Border = BorderFactory.createEtchedBorder(); 248 javax.swing.border.Border pane2Titled = BorderFactory.createTitledBorder(pane2Border, 249 Bundle.getMessage("InternalClockStatusBorderText")); 250 pane2.setBorder(pane2Titled); 251 pane2.add(internalDisplayStatus); 252 internalDisplayStatus.setEditable(false); 253 internalDisplayStatus.setBorder(BorderFactory.createEmptyBorder()); 254 add(pane2); 255 256 // NCE Clock Info Panel 257 pane2 = new JPanel(); 258 pane2Border = BorderFactory.createEtchedBorder(); 259 pane2Titled = BorderFactory.createTitledBorder(pane2Border, 260 Bundle.getMessage("NceClockStatusBorderText")); 261 pane2.setBorder(pane2Titled); 262 pane2.add(nceDisplayStatus); 263 nceDisplayStatus.setEditable(false); 264 nceDisplayStatus.setBorder(BorderFactory.createEmptyBorder()); 265 add(pane2); 266 267 // setting time items 268 pane2 = new JPanel(); 269 pane2Border = BorderFactory.createEtchedBorder(); 270 pane2Titled = BorderFactory.createTitledBorder(pane2Border, 271 Bundle.getMessage("SetClockValuesBorderText")); 272 pane2.setBorder(pane2Titled); 273 pane2.add(new JLabel(Bundle.getMessage("LabelTime"))); 274 pane2.add(hours); 275 hours.setToolTipText("0 - 23"); 276 pane2.add(new JLabel(Bundle.getMessage("LabelTimeSep"))); 277 pane2.add(minutes); 278 minutes.setToolTipText("0 - 59"); 279 pane2.add(new JLabel(Bundle.getMessage("LabelTimeSep"))); 280 pane2.add(seconds); 281 seconds.setToolTipText("0 - 59"); 282 seconds.setEditable(false); 283 pane2.add(new JLabel(" ")); 284 pane2.add(amPm); 285 amPm.setEditable(false); 286 pane2.add(new JLabel(" ")); 287 pane2.add(setClockButton); 288 add(pane2); 289 290 // set clock ratio items 291 pane2 = new JPanel(); 292 pane2Border = BorderFactory.createEtchedBorder(); 293 pane2Titled = BorderFactory.createTitledBorder(pane2Border, 294 Bundle.getMessage("SetClockRatioBorderText")); 295 pane2.setBorder(pane2Titled); 296 pane2.add(new JLabel(Bundle.getMessage("LabelRatio"))); 297 pane2.add(rateNce); 298 rateNce.setToolTipText(CLOCKRATIO_MIN + " - "+ CLOCKRATIO_MAX); 299 pane2.add(new JLabel(Bundle.getMessage("LabelToOne"))); 300 pane2.add(setRatioButton); 301 add(pane2); 302 303 // add 12/24 clock options 304 pane2 = new JPanel(); 305 pane2Border = BorderFactory.createEtchedBorder(); 306 pane2Titled = BorderFactory.createTitledBorder(pane2Border, 307 Bundle.getMessage("SetClock12/24ModeBorderText")); 308 pane2.setBorder(pane2Titled); 309 pane2.add(twentyFour); 310 pane2.add(new JLabel(" ")); 311 pane2.add(set1224Button); 312 add(pane2); 313 314 // pane2 = new JPanel(); 315 // pane2.setLayout(new BoxLayout(pane2, BoxLayout.X_AXIS)); 316 // pane2.add(new JLabel(" ")); 317 // pane2.add(status); 318 // add(pane2); 319 pane2 = new JPanel(); 320 pane2Border = BorderFactory.createEtchedBorder(); 321 pane2Titled = BorderFactory.createTitledBorder(pane2Border, 322 Bundle.getMessage("InterfaceCommandBorderText")); 323 pane2.setBorder(pane2Titled); 324 pane2.setLayout(gLayout); 325 gConstraints.gridx = 0; 326 gConstraints.gridy = 0; 327 gConstraints.gridwidth = 1; 328 gConstraints.gridheight = 1; 329 gConstraints.ipadx = 10; 330 gConstraints.ipady = 1; 331 gConstraints.insets = new Insets(1, 1, 1, 1); 332 pane2.add(setStartNceButton, gConstraints); 333 gConstraints.gridx++; 334 pane2.add(setStopNceButton, gConstraints); 335 gConstraints.gridx++; 336 pane2.add(readButton, gConstraints); 337 338 ButtonGroup modeGroup = new ButtonGroup(); 339 modeGroup.add(setSyncModeInternalMaster); 340 modeGroup.add(setSyncModeNceMaster); 341 modeGroup.add(setSyncModeOff); 342 343 gConstraints.gridx = 0; 344 gConstraints.gridy++; 345 gConstraints.gridwidth = 3; 346 pane2.add(setSyncModeNceMaster, gConstraints); 347 gConstraints.gridy++; 348 pane2.add(setSyncModeInternalMaster, gConstraints); 349 gConstraints.gridy++; 350 pane2.add(setSyncModeOff, gConstraints); 351 gConstraints.gridy++; 352 pane2.add(setSyncButton, gConstraints); 353 setSyncModeInternalMaster.setEnabled(true); 354 setSyncModeNceMaster.setEnabled(true); 355 if (tc.getUsbSystem() != NceTrafficController.USB_SYSTEM_NONE) { // needs memory commands to sync 356 setSyncModeInternalMaster.setEnabled(false); 357 setSyncModeNceMaster.setEnabled(false); 358 } 359 add(pane2); 360 361 // add polling speed 362 pane2 = new JPanel(); 363 pane2Border = BorderFactory.createEtchedBorder(); 364 pane2Titled = BorderFactory.createTitledBorder(pane2Border, 365 Bundle.getMessage("InterfaceUpdRateBorderText")); 366 pane2.setBorder(pane2Titled); 367 pane2.add(new JLabel(Bundle.getMessage("InterfaceUpdRate"))); 368 pane2.add(new JLabel(" ")); 369 pane2.add(pollingSpeed); 370 pollingSpeed.setText("" + pollingInterval); 371 pollingSpeed.setToolTipText(MIN_POLLING_INTERVAL + " - " + MAX_POLLING_INTERVAL); 372 pane2.add(new JLabel(" ")); 373 pane2.add(new JLabel(Bundle.getMessage("InterfaceUpdRateSufix"))); 374 pane2.add(new JLabel(" ")); 375 pane2.add(setPollingSpeedButton); 376 add(pane2); 377 378// // add PID values 379// gLayout = new GridBagLayout(); 380// gConstraints = new GridBagConstraints(); 381// pane2 = new JPanel(); 382// pane2Border = BorderFactory.createEtchedBorder(); 383// pane2Titled = BorderFactory.createTitledBorder(pane2Border, 384// Bundle.getMessage("InterfacePidBorderText")); 385// pane2.setBorder(pane2Titled); 386// pane2.setLayout(gLayout); 387// gConstraints.gridx = 0; 388// gConstraints.gridy = 0; 389// gConstraints.gridwidth = 1; 390// gConstraints.gridheight = 1; 391// gConstraints.ipadx = 10; 392// gConstraints.ipady = 1; 393// gConstraints.insets = new Insets(3, 3, 3, 3); 394// pane2.add(new JLabel(Bundle.getMessage("InterfacePidNce")), gConstraints); 395// gConstraints.gridx++; 396// pane2.add(new JLabel(Bundle.getMessage("InterfacePidGainP")), gConstraints); 397// gConstraints.gridx++; 398// pane2.add(ncePidGainP, gConstraints); 399// gConstraints.gridx++; 400// pane2.add(new JLabel(Bundle.getMessage("InterfacePidGainI")), gConstraints); 401// gConstraints.gridx++; 402// pane2.add(ncePidGainI, gConstraints); 403// gConstraints.gridx++; 404// pane2.add(new JLabel(Bundle.getMessage("InterfacePidGainD")), gConstraints); 405// gConstraints.gridx++; 406// pane2.add(ncePidGainD, gConstraints); 407// gConstraints.gridx++; 408// gConstraints.gridheight = 2; 409// pane2.add(setPidButton, gConstraints); 410// gConstraints.gridheight = 0; 411// gConstraints.gridx = 0; 412// gConstraints.gridy = 1; 413// pane2.add(new JLabel(Bundle.getMessage("InterfacePidInt")), gConstraints); 414// gConstraints.gridx++; 415// pane2.add(new JLabel(Bundle.getMessage("InterfacePidGainP")), gConstraints); 416// gConstraints.gridx++; 417// pane2.add(intPidGainP, gConstraints); 418// gConstraints.gridx++; 419// pane2.add(new JLabel(Bundle.getMessage("InterfacePidGainI")), gConstraints); 420// gConstraints.gridx++; 421// pane2.add(intPidGainI, gConstraints); 422// gConstraints.gridx++; 423// pane2.add(new JLabel(Bundle.getMessage("InterfacePidGainD")), gConstraints); 424// gConstraints.gridx++; 425// pane2.add(intPidGainD, gConstraints); 426// ncePidGainP.setText(fiveDigits.format(ncePidGainPv)); 427// ncePidGainI.setText(fiveDigits.format(ncePidGainIv)); 428// ncePidGainD.setText(fiveDigits.format(ncePidGainDv)); 429// intPidGainP.setText(fiveDigits.format(intPidGainPv)); 430// intPidGainI.setText(fiveDigits.format(intPidGainIv)); 431// intPidGainD.setText(fiveDigits.format(intPidGainDv)); 432// add(pane2); 433 // install "read" button handler 434 readButton.addActionListener((ActionEvent a) -> { 435 issueReadAllRequest(); 436 }); 437 // install "set" button handler 438 setClockButton.addActionListener((ActionEvent a) -> { 439 issueClockSet(Integer.parseInt(hours.getText().trim()), 440 Integer.parseInt(minutes.getText().trim()), 441 Integer.parseInt(seconds.getText().trim()) 442 ); 443 }); 444 // install "stop" clock button handler 445 setStopNceButton.addActionListener((ActionEvent a) -> { 446 issueClockStop(); 447 }); 448 // install "start" clock button handler 449 setStartNceButton.addActionListener((ActionEvent a) -> { 450 issueClockStart(); 451 }); 452 // install set fast clock ratio 453 setRatioButton.addActionListener((ActionEvent a) -> { 454 changeNceClockRatio(); 455 }); 456 // install set 12/24 button 457 set1224Button.addActionListener((ActionEvent a) -> { 458 issueClock1224(twentyFour.isSelected()); 459 }); 460 // install Sync Change Clock button 461 setSyncButton.addActionListener((ActionEvent a) -> { 462 changeSyncMode(); 463 }); 464 465 // install "setPolling" button handler 466 setPollingSpeedButton.addActionListener((ActionEvent a) -> { 467 changePollingSpeed(Double.parseDouble(pollingSpeed.getText().trim())); 468 }); 469 470 // install "setPid" button handler 471 setPidButton.addActionListener((ActionEvent a) -> { 472 changePidValues(); 473 }); 474 475 if (clockMode == SYNCMODE_OFF) { 476 setSyncModeOff.setSelected(true); 477 } 478 if (clockMode == SYNCMODE_INTERNAL_MASTER) { 479 setSyncModeInternalMaster.setSelected(true); 480 } 481 if (clockMode == SYNCMODE_NCE_MASTER) { 482 setSyncModeNceMaster.setSelected(true); 483 } 484 this.setSize(400, 300); 485 486 // Create a timebase listener for the Minute change events 487 internalClock = InstanceManager.getNullableDefault(jmri.Timebase.class); 488 if (internalClock == null) { 489 log.error("No Timebase Instance; clock will not run"); // NOI18N 490 return; 491 } 492 minuteChangeListener = (PropertyChangeEvent e) -> { 493 newInternalMinute(); 494 }; 495 internalClock.addMinuteChangeListener(minuteChangeListener); 496 497 // start display alarm timer 498 alarmDisplayUpdateHandler(); 499 } 500 501 // ignore replies 502 @Override 503 public void message(NceMessage m) { 504 log.error("clockmon message received: {}", m); 505 } // NOI18N 506 507 @Override 508 public void reply(NceReply r) { 509 log.trace("nceReplyCatcher() waiting: {} watingForRead: {} waitingForCmdTime: {} waitingForCmd1224: {} waitingForCmdRatio: {} waitingForCmdStop: {} waitingForCmdStart: {}", 510 waiting, waitingForCmdRead, waitingForCmdTime, waitingForCmd1224, waitingForCmdRatio, waitingForCmdStop, waitingForCmdStart); 511 if (waiting <= 0) { 512 log.debug("unexpected response"); 513 return; 514 } 515 waiting--; 516 if (waitingForCmdRead && r.getNumDataElements() == CS_CLOCK_MEM_SIZE) { 517 readClockPacket(r); 518 waitingForCmdRead = false; 519 callStateMachines(); 520 return; 521 } 522 if (waitingForCmdTime) { 523 if (r.getNumDataElements() != CMD_CLOCK_SET_REPLY_SIZE) { 524 log.error("NCE clock command reply, invalid length:{}", r.getNumDataElements()); 525 return; 526 } else { 527 waitingForCmdTime = false; 528 if (r.getElement(0) != NceMessage.NCE_OKAY) { 529 log.error("NCE set clock replied: {}", r.getElement(0)); 530 } 531 callStateMachines(); 532 return; 533 } 534 } 535 if (r.getNumDataElements() != CMD_CLOCK_SET_REPLY_SIZE) { 536 log.error("NCE clock command reply, invalid length:{}", r.getNumDataElements()); 537 return; 538 } else { 539 if (waitingForCmd1224) { 540 waitingForCmd1224 = false; 541 if (r.getElement(0) != NceMessage.NCE_OKAY) { 542 log.error("NCE set clock 12/24 replied:{}", r.getElement(0)); 543 } 544 callStateMachines(); 545 return; 546 } 547 if (waitingForCmdRatio) { 548 waitingForCmdRatio = false; 549 if (r.getElement(0) != NceMessage.NCE_OKAY) { 550 log.error("NCE clock ratio cmd replied:{}", r.getElement(0)); 551 } 552 callStateMachines(); 553 return; 554 } 555 if (waitingForCmdStop) { 556 waitingForCmdStop = false; 557 if (r.getElement(0) != NceMessage.NCE_OKAY) { 558 log.error("NCE clock stop cmd replied:{}", r.getElement(0)); 559 } 560 callStateMachines(); 561 return; 562 } 563 if (waitingForCmdStart) { 564 waitingForCmdStart = false; 565 if (r.getElement(0) != NceMessage.NCE_OKAY) { 566 log.error("NCE clock start cmd replied:{}", r.getElement(0)); 567 } 568 callStateMachines(); 569 return; 570 } 571 } 572 log.debug("unexpected response"); 573 } 574 575 private void callStateMachines() { 576 if (internalSyncInitStateCounter > 0) { 577 internalSyncInitStates(); 578 } 579 if (internalSyncRunStateCounter > 0) { 580 internalSyncRunStates(); 581 } 582 if (nceSyncInitStateCounter > 0) { 583 nceSyncInitStates(); 584 } 585 if (nceSyncRunStateCounter > 0) { 586 nceSyncRunStates(); 587 } 588 if (alarmDisplayStateCounter > 0) { 589 alarmDisplayStates(); 590 } 591 } 592 593 private void readClockPacket(NceReply r) { 594 NceReply priorClockReadPacket = lastClockReadPacket; 595 int priorNceRatio = nceLastRatio; 596 boolean priorNceRunning = nceLastRunning; 597 lastClockReadPacket = r; 598 //lastClockReadAtTime = internalClock.getTime(); 599 //log.debug("readClockPacket - at time: " + lastClockReadAtTime); 600 nceLastHour = r.getElement(CS_CLOCK_HOURS) & 0xFF; 601 nceLastMinute = r.getElement(CS_CLOCK_MINUTES) & 0xFF; 602 nceLastSecond = r.getElement(CS_CLOCK_SECONDS) & 0xFF; 603 nceLast1224 = r.getElement(CS_CLOCK_1224) == 1; 604 nceLastAmPm = r.getElement(CS_CLOCK_AMPM) == 'A'; 605 int sc = r.getElement(CS_CLOCK_SCALE) & 0xFF; 606 if (sc > 0) { 607 nceLastRatio = 250 / sc; 608 } 609 if (clockMode == SYNCMODE_NCE_MASTER) { 610 if (priorClockReadPacket != null && priorNceRatio != nceLastRatio) { 611 log.debug("NCE Change Rate from cab: prior vs last: {} vs {}", priorNceRatio, nceLastRatio); 612 rateNce.setText("" + nceLastRatio); 613 nceSyncInitStateCounter = 1; 614 nceSyncInitStates(); 615 } 616 } 617 nceLastRunning = r.getElement(CS_CLOCK_STATUS) != 1; 618 if (clockMode == SYNCMODE_NCE_MASTER) { 619 if (priorClockReadPacket != null && priorNceRunning != nceLastRunning) { 620 log.debug("NCE Stop/Start: prior vs last: {} vs {}", priorNceRunning, nceLastRunning); 621 if (nceLastRunning) { 622 nceSyncInitStateCounter = 1; 623 } else { 624 nceSyncInitStateCounter = -1; 625 } 626 nceSyncInitStates(); 627 internalClock.setRun(nceLastRunning); 628 } 629 } 630 updateSettingsFromNce(); 631 } 632 633 private void alarmDisplayUpdateHandler() { 634 if (pollingInterval < MIN_POLLING_INTERVAL || pollingInterval > MAX_POLLING_INTERVAL) { 635 JmriJOptionPane.showMessageDialog(this, 636 Bundle.getMessage("DIALOG_PolingIntOutOfRange", MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, pollingInterval), 637 Bundle.getMessage("DIALOG_NceClockMon"), 638 JmriJOptionPane.ERROR_MESSAGE); 639 pollingInterval = DEFAULT_POLLING_INTERVAL; 640 } 641 // initialize things if not running 642 alarmSetup(); 643 alarmDisplayStates(); 644 updateInternalClockDisplay(); 645 } 646 647 private void alarmSetup() { 648 // initialize things if not running 649 if (timerDisplayUpdate == null) { 650 timerDisplayUpdate = new Timer((int) (pollingInterval * 1000.0), (ActionEvent e) -> { 651 alarmDisplayUpdateHandler(); 652 }); 653 } 654 timerDisplayUpdate.setInitialDelay((1 * 1000)); 655 timerDisplayUpdate.setRepeats(true); // in case we run by 656 timerDisplayUpdate.start(); 657 alarmDisplayStateCounter = 1; 658 } 659 660 private void alarmSyncInit() { 661 // initialize things if not running 662 int delay = 1000; 663 if (alarmSyncUpdate == null) { 664 alarmSyncUpdate = new Timer(delay, (ActionEvent e) -> { 665 alarmSyncHandler(); 666 }); 667 if (clockMode == SYNCMODE_INTERNAL_MASTER) { 668 delay = (int) (syncInterval * 1000 / nceLastRatio); 669 alarmSyncUpdate.setRepeats(false); 670 } 671 if (clockMode == SYNCMODE_NCE_MASTER) { 672 delay = 10 * 1000; 673 alarmSyncUpdate.setRepeats(true); 674 } 675 alarmSyncUpdate.setInitialDelay(delay); 676 alarmSyncUpdate.setDelay(delay); 677 alarmSyncUpdate.stop(); 678 } 679 } 680 681 @SuppressWarnings("deprecation") // Date.getTime 682 private void alarmSyncStart() { 683 // initialize things if not running 684 Date now = internalClock.getTime(); 685 if (alarmSyncUpdate == null) { 686 alarmSyncInit(); 687 } 688 int delay = 60 * 1000; 689 if (clockMode == SYNCMODE_INTERNAL_MASTER) { 690 if (syncInterval - 3 - now.getSeconds() <= 0) { 691 delay = 10; // basically trigger right away 692 } else { 693 delay = (int) ((syncInterval - now.getSeconds()) * 1000 / internalClock.getRate()); 694 } 695 } 696 if (clockMode == SYNCMODE_NCE_MASTER) { 697 delay = 10 * 1000; 698 } 699 alarmSyncUpdate.setDelay(delay); 700 alarmSyncUpdate.setInitialDelay(delay); 701 alarmSyncUpdate.start(); 702 log.trace("alarmSyncStart delay: {} @ {}", delay, now); 703 } 704 705 private void alarmSyncHandler() { 706 if (clockMode == SYNCMODE_INTERNAL_MASTER) { 707 internalSyncRunStateCounter = 1; 708 internalSyncRunStates(); 709 } 710 if (clockMode == SYNCMODE_NCE_MASTER) { 711 if (nceSyncRunStateCounter == 0) { 712 nceSyncRunStateCounter = 1; 713 nceSyncRunStates(); 714 } 715 } 716 if (clockMode == SYNCMODE_OFF) { 717 alarmSyncUpdate.stop(); 718 } 719 if (alarmDisplayStateCounter == 0) { 720 alarmDisplayStateCounter = 1; 721 alarmDisplayStates(); 722 } 723 } 724 725 private void alarmDisplayStates() { 726 int priorState; 727 do { 728 log.trace("alarmDisplayStates: before: {} {}", alarmDisplayStateCounter, internalClock.getTime()); 729 priorState = alarmDisplayStateCounter; 730 switch (alarmDisplayStateCounter) { 731 case 0: 732 // inactive 733 break; 734 case 1: 735 // issue nce read 736 internalClockStatusCheck(); 737 issueReadOnlyRequest(); 738 alarmDisplayStateCounter++; 739 break; 740 case 2: 741 // wait for update 742 if (!waitingForCmdRead) { 743 alarmDisplayStateCounter++; 744 } 745 break; 746 case 3: 747 // update clock display 748 alarmDisplayStateCounter = 0; 749 updateNceClockDisplay(); 750 updateInternalClockDisplay(); 751 break; 752 default: 753 log.warn("Unexpected alarmDisplayStateCounter {} in alarmDisplayStates", alarmDisplayStateCounter); 754 break; 755 } 756 log.trace("alarmDisplayStates: after: {} {}", alarmDisplayStateCounter, internalClock.getTime()); 757 } while (priorState != alarmDisplayStateCounter); 758 } 759 760 private double getNceTime() { 761 double nceTime = 0; 762 if (lastClockReadPacket != null) { 763 nceTime = (lastClockReadPacket.getElement(CS_CLOCK_HOURS) * 3600) 764 + (lastClockReadPacket.getElement(CS_CLOCK_MINUTES) * 60) 765 + lastClockReadPacket.getElement(CS_CLOCK_SECONDS) 766 + (lastClockReadPacket.getElement(CS_CLOCK_TICK) * 0.25); 767 } 768 return (nceTime); 769 } 770 771 @SuppressWarnings("deprecation") // Date.getTime 772 private Date getNceDate() { 773 Date now = internalClock.getTime(); 774 if (lastClockReadPacket != null) { 775 now.setHours(lastClockReadPacket.getElement(CS_CLOCK_HOURS)); 776 now.setMinutes(lastClockReadPacket.getElement(CS_CLOCK_MINUTES)); 777 now.setSeconds(lastClockReadPacket.getElement(CS_CLOCK_SECONDS)); 778 } 779 return (now); 780 } 781 782 @SuppressWarnings("deprecation") // Date.getTime 783 private double getIntTime() { 784 Date now = internalClock.getTime(); 785 int ms = (int) (now.getTime() % 1000); 786 int ss = now.getSeconds(); 787 int mm = now.getMinutes(); 788 int hh = now.getHours(); 789 log.trace("getIntTime: {}:{}:{}.{}", hh, mm, ss, ms); 790 return ((hh * 60 * 60) + (mm * 60) + ss + (ms / 1000)); 791 } 792 793 private void changeNceClockRatio() { 794 String newRatioStr = rateNce.getText().trim(); 795 try { 796 int newRatio = Integer.parseInt(newRatioStr); 797 if ((newRatio <= CLOCKRATIO_MIN) || (newRatio > CLOCKRATIO_MAX)) { 798 throw new NumberFormatException(); 799 } 800 issueClockRatio(newRatio); 801 } catch (NumberFormatException e) { 802 JmriJOptionPane.showMessageDialog(this, 803 Bundle.getMessage("DIALOG_InvalidRatio", newRatioStr, CLOCKRATIO_MIN, CLOCKRATIO_MAX), 804 Bundle.getMessage("DIALOG_NceClockMon"), 805 JmriJOptionPane.ERROR_MESSAGE); 806 } 807 } 808 809 @SuppressWarnings("deprecation") // Date.getTime 810 private void internalSyncInitStates() { 811 Date now = internalClock.getTime(); 812 int priorState; 813 do { 814 if (internalSyncInitStateCounter != 0) { 815 log.trace("internalSyncInitStates begin: {} @ {}", internalSyncInitStateCounter, now); 816 } 817 priorState = internalSyncInitStateCounter; 818 switch (internalSyncInitStateCounter) { 819 case 0: 820 // do nothing, idle state 821 break; 822 case -1: 823 // cleanup, halt state 824 alarmSyncUpdate.stop(); 825 internalSyncInitStateCounter = 0; 826 internalSyncRunStateCounter = 0; 827 setClockButton.setEnabled(true); 828 setRatioButton.setEnabled(true); 829 set1224Button.setEnabled(true); 830 setStopNceButton.setEnabled(true); 831 setStartNceButton.setEnabled(true); 832 break; 833 case -3: 834 // stopping from internal clock 835 internalSyncRunStateCounter = 0; 836 alarmSyncUpdate.stop(); 837 issueClockStop(); 838 internalSyncInitStateCounter++; 839 break; 840 case -2: 841 // waiting for nce to stop 842 if (!waitingForCmdStop) { 843 internalSyncInitStateCounter = 0; 844 } 845 break; 846 case 1: 847 // get current values + initialize all values for sync operations 848 priorDiffs.clear(); 849 priorCorrections.clear(); 850 priorOffsetErrors.clear(); 851 syncInterval = TARGET_SYNC_DELAY; 852 // disable NCE clock options 853 setClockButton.setEnabled(false); 854 setRatioButton.setEnabled(false); 855 set1224Button.setEnabled(false); 856 setStopNceButton.setEnabled(false); 857 setStartNceButton.setEnabled(false); 858 // stop NCE clock 859 issueClockStop(); 860 internalSyncInitStateCounter++; 861 break; 862 case 2: 863 if (!waitingForCmdStop) { 864 internalSyncInitStateCounter++; 865 } 866 break; 867 case 3: 868 // set NCE ratio, mode etc... 869 issueClockRatio((int) internalClock.getRate()); 870 internalSyncInitStateCounter++; 871 break; 872 case 4: 873 if (!waitingForCmdRatio) { 874 internalSyncInitStateCounter++; 875 } 876 break; 877 case 5: 878 issueClock1224(true); 879 internalSyncInitStateCounter++; 880 break; 881 case 6: 882 if (!waitingForCmd1224) { 883 internalSyncInitStateCounter++; 884 } 885 break; 886 case 7: 887 // set initial NCE time 888 // set NCE from internal settings 889 // start NCE clock 890 now = internalClock.getTime(); 891 issueClockSet(now.getHours(), now.getMinutes(), now.getSeconds()); 892 internalSyncInitStateCounter++; 893 break; 894 case 8: 895 if (!waitingForCmdTime) { 896 internalSyncInitStateCounter++; 897 } 898 break; 899 case 9: 900 issueClockStart(); 901 internalSyncInitStateCounter++; 902 break; 903 case 10: 904 if (!waitingForCmdStart) { 905 internalSyncInitStateCounter++; 906 } 907 break; 908 case 11: 909 issueReadOnlyRequest(); 910 internalSyncInitStateCounter++; 911 break; 912 case 12: 913 if (!waitingForCmdRead) { 914 internalSyncInitStateCounter++; 915 } 916 break; 917 case 13: 918 updateNceClockDisplay(); 919 updateInternalClockDisplay(); 920 alarmSyncStart(); 921 internalSyncInitStateCounter++; 922 break; 923 case 14: 924 // initialization complete 925 internalSyncInitStateCounter = 0; 926 internalSyncRunStateCounter = 1; 927 log.trace("internalSyncState: init done"); 928 break; 929 default: 930 internalSyncInitStateCounter = 0; 931 log.error("Uninitialized value: internalSyncInitStateCounter"); 932 break; 933 } 934 } while (priorState != internalSyncInitStateCounter); 935 } 936 937 @SuppressWarnings("deprecation") // Date.getTime 938 private void internalSyncRunStates() { 939 double intTime; 940 double nceTime; 941 double diffTime; 942 Date now = internalClock.getTime(); // Date.getTime 943 if (internalSyncRunStateCounter != 0) { 944 log.trace("internalSyncRunStates: {} @ {}", internalSyncRunStateCounter, now); 945 } 946 int priorState; 947 do { 948 priorState = internalSyncRunStateCounter; 949 switch (internalSyncRunStateCounter) { 950 case -1: 951 // turn off any sync parts 952 internalSyncInitStateCounter = -1; 953 internalSyncInitStates(); 954 break; 955 case 1: 956 internalClockStatusCheck(); 957 // alarm fired, issue fresh nce reads 958 issueReadOnlyRequest(); 959 internalSyncRunStateCounter++; 960 break; 961 case 2: 962 case 6: 963 if (!waitingForCmdRead) { 964 internalSyncRunStateCounter++; 965 } 966 break; 967 case 3: 968 // compute error 969 nceTime = getNceTime(); 970 intTime = getIntTime(); 971 diffTime = intTime - nceTime; 972 if (log.isTraceEnabled()) { 973 log.trace("syncStates2 begin. NCE: {}{}:{}{}:{}{} Internal: {}{}:{}{}:{}{} diff: {}", 974 nceLastHour / 10, nceLastHour - ((nceLastHour / 10) * 10), 975 nceLastMinute / 10, nceLastMinute - ((nceLastMinute / 10) * 10), 976 nceLastSecond / 10, nceLastSecond - ((nceLastSecond / 10) * 10), 977 now.getHours() / 10, now.getHours() - ((now.getHours() / 10) * 10), 978 now.getMinutes() / 10, now.getMinutes() - ((now.getMinutes() / 10) * 10), 979 now.getSeconds() / 10, now.getSeconds() - ((now.getSeconds() / 10) * 10), 980 diffTime); 981 } 982 // save error to array 983 while (priorDiffs.size() >= MAX_ERROR_ARRAY) { 984 priorDiffs.remove(0); 985 } 986 priorDiffs.add(diffTime); 987 recomputeInternalSync(); 988 issueClockSet( 989 now.getHours(), 990 now.getMinutes(), 991 (int) syncInterval 992 ); 993 internalSyncRunStateCounter++; 994 break; 995 case 4: 996 if (!waitingForCmdTime) { 997 internalSyncRunStateCounter++; 998 } 999 break; 1000 case 5: 1001 issueReadOnlyRequest(); 1002 internalSyncRunStateCounter++; 1003 break; 1004 case 7: 1005 // compute offset delay 1006 intTime = now.getSeconds(); 1007 diffTime = TARGET_SYNC_DELAY - intTime; 1008 // save offset error to array 1009 while (priorOffsetErrors.size() >= MAX_ERROR_ARRAY) { 1010 priorOffsetErrors.remove(0); 1011 } 1012 priorOffsetErrors.add(diffTime); 1013 recomputeOffset(); 1014 if (log.isTraceEnabled()) { 1015 log.trace("syncState compute offset. NCE: {}{}:{}{}:{}{} Internal: {}{}:{}{}:{}{}", 1016 nceLastHour / 10, nceLastHour - ((nceLastHour / 10) * 10), 1017 nceLastMinute / 10, nceLastMinute - ((nceLastMinute / 10) * 10), 1018 nceLastSecond / 10, nceLastSecond - ((nceLastSecond / 10) * 10), 1019 now.getHours() / 10, now.getHours() - ((now.getHours() / 10) * 10), 1020 now.getMinutes() / 10, now.getMinutes() - ((now.getMinutes() / 10) * 10), 1021 now.getSeconds() / 10, now.getSeconds() - ((now.getSeconds() / 10) * 10)); 1022 } 1023 internalSyncRunStateCounter = 0; 1024 break; 1025 default: 1026 internalSyncRunStateCounter = 0; 1027 break; 1028 } 1029 } while (priorState != internalSyncRunStateCounter); 1030 } 1031 1032 @SuppressFBWarnings(value = "FE_FLOATING_POINT_EQUALITY", justification = "testing for change from stored value") 1033 private void internalClockStatusCheck() { 1034 // if change to internal clock 1035 if (clockMode == SYNCMODE_INTERNAL_MASTER) { 1036 if (internalLastRunning != internalClock.getRun()) { 1037 if (internalClock.getRun()) { 1038 internalSyncInitStateCounter = 1; 1039 } else { 1040 internalSyncInitStateCounter = -3; 1041 } 1042 internalSyncInitStates(); 1043 } 1044 // next line is the FE_FLOATING_POINT_EQUALITY annotated above 1045 if (internalLastRatio != internalClock.getRate()) { 1046 internalSyncInitStateCounter = 1; 1047 internalSyncInitStates(); 1048 } 1049 } 1050 internalLastRunning = internalClock.getRun(); 1051 internalLastRatio = internalClock.getRate(); 1052 } 1053 1054 private void changePidValues() { 1055 double p = 0; 1056 double i = 0; 1057 double d = 0; 1058 boolean ok = true; 1059 try { 1060 p = Double.parseDouble(ncePidGainP.getText().trim()); 1061 } catch (NumberFormatException e) { 1062 log.error("Invalid value: {}", ncePidGainP.getText().trim()); 1063 ok = false; 1064 } 1065 try { 1066 i = Double.parseDouble(ncePidGainI.getText().trim()); 1067 } catch (NumberFormatException e) { 1068 log.error("Invalid value: {}", ncePidGainP.getText().trim()); 1069 ok = false; 1070 } 1071 try { 1072 d = Double.parseDouble(ncePidGainD.getText().trim()); 1073 } catch (NumberFormatException e) { 1074 log.error("Invalid value: {}", ncePidGainP.getText().trim()); 1075 ok = false; 1076 } 1077 if (ok) { 1078 if (p < 0) { 1079 p = 0; 1080 } 1081 if (p > 1) { 1082 p = 1; 1083 } 1084 if (i < 0) { 1085 i = 0; 1086 } 1087 if (d > 1) { 1088 d = 1; 1089 } 1090 ncePidGainPv = p; 1091 ncePidGainIv = i; 1092 ncePidGainDv = d; 1093 ncePidGainP.setText(fiveDigits.format(p)); 1094 ncePidGainI.setText(fiveDigits.format(i)); 1095 ncePidGainD.setText(fiveDigits.format(d)); 1096 } 1097 } 1098 1099 private void recomputeOffset() { 1100 1101 double sumDiff = 0; 1102 if (priorOffsetErrors.size() > 1) { 1103 sumDiff = priorOffsetErrors.get(0) + priorOffsetErrors.get(1); 1104 } 1105 double avgDiff = sumDiff / 2; 1106 syncInterval = syncInterval + avgDiff; 1107 if (syncInterval < 30) { 1108 syncInterval = 30; 1109 } 1110 if (syncInterval > 58) { 1111 syncInterval = 58; 1112 } 1113 if (log.isTraceEnabled()) { 1114 Date now = internalClock.getTime(); 1115 StringBuilder txt = new StringBuilder(""); 1116 for (int i = 0; i < priorOffsetErrors.size(); i++) { 1117 txt.append(" ").append(priorOffsetErrors.get(i).doubleValue()); 1118 } 1119 log.trace("priorOffsetErrors: {}", txt); 1120 log.trace("syncOffset: {} avgDiff: {} @ {}", syncInterval, avgDiff, now); 1121 } 1122 } 1123 1124 private void recomputeInternalSync() { 1125 //Date now = internalClock.getTime(); 1126 double sumDiff = 0; 1127 double currError = 0; 1128 //double diffError = 0; 1129 //double avgDiff = 0; 1130 if (priorDiffs.size() > 0) { 1131 currError = priorDiffs.get(priorDiffs.size() - 1); 1132 //diffError = priorDiffs.get(priorDiffs.size() - 1).doubleValue() - ((Double) priorDiffs.get(0)).doubleValue(); 1133 } 1134 for (int i = 0; i < priorDiffs.size(); i++) { 1135 sumDiff = sumDiff + priorDiffs.get(i); 1136 } 1137 double corrDiff = 0; 1138 if (priorCorrections.size() > 0) { 1139 corrDiff = priorCorrections.get(priorCorrections.size() - 1) - priorCorrections.get(0); 1140 } 1141 double pCorr = currError * intPidGainPv; 1142 double iCorr = sumDiff * intPidGainIv; 1143 double dCorr = corrDiff * intPidGainDv; 1144 double newRateAdj = pCorr + iCorr + dCorr; 1145 // save correction to array 1146 while (priorCorrections.size() >= MAX_ERROR_ARRAY) { 1147 priorCorrections.remove(0); 1148 } 1149 priorCorrections.add(newRateAdj); 1150 syncInterval = syncInterval + newRateAdj; 1151 if (syncInterval > 57) { 1152 syncInterval = 57; 1153 } 1154 if (syncInterval < 40) { 1155 syncInterval = 40; 1156 } 1157 if (log.isTraceEnabled()) { 1158 StringBuilder txt = new StringBuilder(""); 1159 for (int i = 0; i < priorDiffs.size(); i++) { 1160 txt.append(" ").append(priorDiffs.get(i)); 1161 } 1162 log.trace("priorDiffs: {}", txt); 1163 log.trace("syncInterval: {} pCorr: {} iCorr: {} dCorr: {}", 1164 syncInterval, fiveDigits.format(pCorr), fiveDigits.format(iCorr), fiveDigits.format(dCorr)); 1165 } 1166 } 1167 1168 private void recomputeNceSync() { 1169 //Date now = internalClock.getTime(); 1170 double sumDiff = 0; 1171 double currError = 0; 1172 double diffError = 0; 1173 if (priorDiffs.size() > 0) { 1174 currError = priorDiffs.get(priorDiffs.size() - 1); 1175 diffError = priorDiffs.get(priorDiffs.size() - 1) - priorDiffs.get(0); 1176 } 1177 for (int i = 0; i < priorDiffs.size(); i++) { 1178 sumDiff = sumDiff + priorDiffs.get(i); 1179 } 1180 double corrDiff = 0; 1181 if (priorCorrections.size() > 0) { 1182 corrDiff = priorCorrections.get(priorCorrections.size() - 1) - priorCorrections.get(0); 1183 } 1184 double pCorr = currError * ncePidGainPv; 1185 double iCorr = diffError * ncePidGainIv; 1186 double dCorr = corrDiff * ncePidGainDv; 1187 double newRateAdj = pCorr + iCorr + dCorr; 1188 // if (newRateAdj > 0.5) { 1189 // newRateAdj = 0.5; 1190 //} 1191 //if (newRateAdj < -0.5) { 1192 // newRateAdj = -0.5; 1193 // } 1194 // save correction to array 1195 while (priorCorrections.size() >= MAX_ERROR_ARRAY) { 1196 priorCorrections.remove(0); 1197 } 1198 priorCorrections.add(newRateAdj); 1199 double oldInternalRate = internalClock.getRate(); 1200 double newInternalRate = oldInternalRate + newRateAdj; 1201 if (Math.abs(currError) > 60) { 1202 // don't try to drift, just reset 1203 nceSyncInitStateCounter = 1; 1204 nceSyncInitStates(); 1205 } else if (Math.abs(oldInternalRate - newInternalRate) >= rateChgMinimum) { 1206 try { 1207 internalClock.setRate(newInternalRate); 1208 if (log.isDebugEnabled()) { 1209 log.debug("changing internal rate: {}", newInternalRate); 1210 } 1211 } catch (TimebaseRateException e) { 1212 log.error("recomputeNceSync: Failed setting new internal rate: {}", newInternalRate); 1213 // just set the internal to NCE and set the clock 1214 nceSyncInitStateCounter = 1; 1215 nceSyncInitStates(); 1216 } 1217 } 1218 if (log.isTraceEnabled()) { 1219 StringBuilder txt = new StringBuilder(""); 1220 for (int i = priorDiffs.size() - 1; i >= 0; i--) { 1221 txt.append(" ").append(threeDigits.format(priorDiffs.get(i))); 1222 } 1223 log.trace("priorDiffs: {}", txt); 1224 txt = new StringBuilder(""); 1225 for (int i = priorCorrections.size() - 1; i >= 0; i--) { 1226 txt.append(" ").append(threeDigits.format(priorCorrections.get(i))); 1227 } 1228 log.trace("priorCorrections: {}", txt); 1229 log.trace("currError: {} pCorr: {} iCorr: {} dCorr: {} newInternalRate: {}", 1230 fiveDigits.format(currError), fiveDigits.format(pCorr), fiveDigits.format(iCorr), fiveDigits.format(dCorr), threeDigits.format(newInternalRate)); 1231 } 1232 } 1233 1234 private void changePollingSpeed(double newInterval) { 1235 if (newInterval < MIN_POLLING_INTERVAL || newInterval > MAX_POLLING_INTERVAL) { 1236 JmriJOptionPane.showMessageDialog(this, 1237 Bundle.getMessage("DIALOG_PolingOutOfRange", newInterval, MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL), 1238 Bundle.getMessage("DIALOG_NceClockMon"), 1239 JmriJOptionPane.ERROR_MESSAGE); 1240 } else { 1241 pollingInterval = newInterval; 1242 pollingSpeed.setText("" + pollingInterval); 1243 if (timerDisplayUpdate == null) { 1244 alarmSetup(); 1245 } 1246 timerDisplayUpdate.setDelay((int) (pollingInterval * 1000)); 1247 } 1248 } 1249 1250 private void changeSyncMode() { 1251 int oldMode = clockMode; 1252 int newMode = SYNCMODE_OFF; 1253 if (setSyncModeNceMaster.isSelected() == true) { 1254 newMode = SYNCMODE_NCE_MASTER; 1255 } 1256 if (setSyncModeInternalMaster.isSelected() == true) { 1257 newMode = SYNCMODE_INTERNAL_MASTER; 1258 } 1259 if (internalClock != null) { 1260 log.debug("changeSyncMode(): New Mode: {} Old Mode: {}", newMode, oldMode); 1261 if (oldMode != newMode) { 1262 clockMode = SYNCMODE_OFF; 1263 // some change so, change settings 1264 if (oldMode == SYNCMODE_OFF) { 1265 if (newMode == SYNCMODE_INTERNAL_MASTER) { 1266 log.debug("starting Internal mode"); 1267 internalSyncInitStateCounter = 1; 1268 internalSyncRunStateCounter = 0; 1269 internalSyncInitStates(); 1270 clockMode = SYNCMODE_INTERNAL_MASTER; 1271 } 1272 if (newMode == SYNCMODE_NCE_MASTER) { 1273 log.debug("starting NCE mode"); 1274 nceSyncInitStateCounter = 1; 1275 nceSyncRunStateCounter = 0; 1276 nceSyncInitStates(); 1277 clockMode = SYNCMODE_NCE_MASTER; 1278 } 1279 } else { 1280 if (oldMode == SYNCMODE_NCE_MASTER) { 1281 // clear nce sync 1282 nceSyncInitStateCounter = -1; 1283 nceSyncInitStates(); 1284 internalSyncInitStateCounter = 1; 1285 internalSyncInitStates(); 1286 } 1287 if (oldMode == SYNCMODE_INTERNAL_MASTER) { 1288 // clear internal mode 1289 internalSyncInitStateCounter = -1; 1290 internalSyncInitStates(); 1291 nceSyncInitStateCounter = 1; 1292 nceSyncInitStates(); 1293 } 1294 } 1295 } 1296 } 1297 } 1298 1299 private void nceSyncInitStates() { 1300 int priorState; 1301 do { 1302 if (log.isTraceEnabled()) { 1303 log.trace("Before nceSyncInitStateCounter: {} {}", nceSyncInitStateCounter, internalClock.getTime()); 1304 } 1305 priorState = nceSyncInitStateCounter; 1306 switch (nceSyncInitStateCounter) { 1307 case -1: 1308 // turn all this off 1309 if (alarmSyncUpdate != null) { 1310 alarmSyncUpdate.stop(); 1311 } 1312 // clear any old records 1313 priorDiffs.clear(); 1314 priorCorrections.clear(); 1315 nceSyncInitStateCounter = 0; 1316 nceSyncRunStateCounter = 0; 1317 break; 1318 case 0: 1319 // idle state 1320 break; 1321 case 1: 1322 // first init step 1323 log.debug("Init/Reset NCE Clock Sync"); 1324 // make sure other state is off 1325 nceSyncRunStateCounter = 0; 1326 // stop internal clock 1327 internalClock.setRun(false); 1328 if (alarmSyncUpdate != null) { 1329 alarmSyncUpdate.stop(); 1330 } 1331 // clear any old records 1332 priorDiffs.clear(); 1333 priorCorrections.clear(); 1334 // request all current nce values 1335 issueReadOnlyRequest(); 1336 nceSyncInitStateCounter++; 1337 break; 1338 case 2: 1339 // make sure the read only has happened 1340 if (!waitingForCmdRead) { 1341 nceSyncInitStateCounter++; 1342 } 1343 break; 1344 case 3: 1345 // set ratio, modes etc... 1346 try { 1347 internalClock.setRate(nceLastRatio * ESTIMATED_NCE_RATE_FACTOR); 1348 } catch (TimebaseRateException e) { 1349 log.error("nceSyncInitStates: failed to set internal clock rate: {}", nceLastRatio); 1350 } 1351 // get time from NCE settings and set internal clock 1352 setInternalClockFromNce(); 1353 internalClock.setRun(true); 1354 nceSyncInitStateCounter = 0; // init is done 1355 nceSyncRunStateCounter = 1; 1356 nceSyncRunStates(); 1357 alarmSyncStart(); 1358 updateNceClockDisplay(); 1359 updateInternalClockDisplay(); 1360 break; 1361 default: 1362 log.warn("Unexpected nceSyncInitStateCounter {} in nceSyncInitStates", nceSyncInitStateCounter); 1363 break; 1364 } 1365 log.trace("After nceSyncInitStateCounter: {} {}", nceSyncInitStateCounter, internalClock.getTime()); 1366 } while (priorState != nceSyncInitStateCounter); 1367 } 1368 1369 private void nceSyncRunStates() { 1370 double intTime; 1371 double nceTime; 1372 double diffTime; 1373 if (log.isTraceEnabled()) { 1374 log.trace("Before nceSyncRunStateCounter: {} {}", nceSyncRunStateCounter, internalClock.getTime()); 1375 } 1376 int priorState; 1377 do { 1378 priorState = nceSyncRunStateCounter; 1379 switch (nceSyncRunStateCounter) { 1380 case 1: // issue read for nce time 1381 issueReadOnlyRequest(); 1382 nceSyncRunStateCounter++; 1383 break; 1384 case 2: 1385 // did read happen?? 1386 if (!waitingForCmdRead) { 1387 nceSyncRunStateCounter++; 1388 } 1389 break; 1390 case 3: // compare internal with nce time 1391 intTime = getIntTime(); 1392 nceTime = getNceTime(); 1393 diffTime = nceTime - intTime; 1394 // deal with end of day reset 1395 if (diffTime > MAX_SECONDS_IN_DAY / 2) { 1396 diffTime = MAX_SECONDS_IN_DAY + nceTime - intTime; 1397 } else if (diffTime < MAX_SECONDS_IN_DAY / -2) { 1398 diffTime = nceTime; 1399 } 1400 if (log.isDebugEnabled()) { 1401 log.debug("new diffTime: {} = {} - {}", diffTime, nceTime, intTime); 1402 } 1403 // save error to array 1404 while (priorDiffs.size() >= MAX_ERROR_ARRAY) { 1405 priorDiffs.remove(0); 1406 } 1407 priorDiffs.add(diffTime); 1408 recomputeNceSync(); 1409 // initialize things if not running 1410 if (alarmSyncUpdate == null) { 1411 alarmSyncInit(); 1412 } 1413 updateNceClockDisplay(); 1414 updateInternalClockDisplay(); 1415 nceSyncRunStateCounter++; 1416 break; 1417 case 4: 1418 // wait for next minute 1419 nceSyncRunStateCounter = 0; 1420 break; 1421 default: 1422 log.warn("Unexpected state {} in nceSyncRunStates", nceSyncRunStateCounter); 1423 break; 1424 } 1425 } while (priorState != nceSyncRunStateCounter); 1426 if (log.isTraceEnabled()) { 1427 log.trace("After nceSyncRunStateCounter: {} {}", nceSyncRunStateCounter, internalClock.getTime()); 1428 } 1429 } 1430 1431 private void setInternalClockFromNce() { 1432 Date newTime = getNceDate(); 1433 internalClock.setTime(newTime); 1434 log.debug("setInternalClockFromNce nceClock: {}", newTime); 1435 } 1436 1437 private void updateSettingsFromNce() { 1438 if (updateTimeFromRead == true) { 1439 hours.setText("" + (nceLastHour / 10) + (nceLastHour - ((nceLastHour / 10) * 10))); 1440 minutes.setText("" + (nceLastMinute / 10) + (nceLastMinute - ((nceLastMinute / 10) * 10))); 1441 seconds.setText("" + (nceLastSecond / 10) + (nceLastSecond - ((nceLastSecond / 10) * 10))); 1442 if (nceLast1224) { 1443 twentyFour.setSelected(true); 1444 amPm.setText(" "); 1445 } else { 1446 twentyFour.setSelected(false); 1447 if (nceLastAmPm) { 1448 amPm.setText(Bundle.getMessage("TagAm")); 1449 } else { 1450 amPm.setText(Bundle.getMessage("TagPm")); 1451 } 1452 } 1453 updateTimeFromRead = false; 1454 } 1455 if (updateRatioFromRead == true) { 1456 rateNce.setText("" + nceLastRatio); 1457 updateRatioFromRead = false; 1458 } 1459 if (updateFormatFromRead == true) { 1460 if (nceLast1224) { 1461 twentyFour.setSelected(true); 1462 } else { 1463 twentyFour.setSelected(false); 1464 } 1465 updateFormatFromRead = false; 1466 } 1467 if (updateStatusFromRead == true) { 1468 if (nceLastRunning) { 1469 status.setText(Bundle.getMessage("TagRunning")); 1470 } else { 1471 status.setText(Bundle.getMessage("TagStopped")); 1472 } 1473 } 1474 } 1475 1476 private void updateNceClockDisplay() { 1477 String txt = nceLastRunning ? Bundle.getMessage("TagRunning") : Bundle.getMessage("TagStopped"); 1478 txt = txt + " " 1479 + (nceLastHour / 10) + (nceLastHour - ((nceLastHour / 10) * 10)) + Bundle.getMessage("LabelTimeSep") 1480 + (nceLastMinute / 10) + (nceLastMinute - ((nceLastMinute / 10) * 10)) + Bundle.getMessage("LabelTimeSep") 1481 + (nceLastSecond / 10) + (nceLastSecond - ((nceLastSecond / 10) * 10)); 1482 if (!nceLast1224) { 1483 if (nceLastAmPm) { 1484 txt = txt + " " + Bundle.getMessage("TagAm"); 1485 } else { 1486 txt = txt + " " + Bundle.getMessage("TagPm"); 1487 } 1488 } 1489 txt = txt + " " + Bundle.getMessage("LabelRatio") + " " 1490 + nceLastRatio + Bundle.getMessage("LabelToOne"); 1491 if (clockMode == SYNCMODE_NCE_MASTER) { 1492 txt = txt + " " + Bundle.getMessage("TagIsNceMaster"); 1493 double intTime = getIntTime(); 1494 double nceTime = getNceTime(); 1495 double diffTime = nceTime - intTime; 1496 txt = txt + " " + Bundle.getMessage("ClockError"); 1497 txt = txt + " " + threeDigits.format(diffTime); 1498 log.trace("intTime: {} nceTime: {} diffTime: {}", intTime, nceTime, diffTime); 1499 } 1500 nceDisplayStatus.setText(txt); 1501 } 1502 1503 @SuppressWarnings("deprecation") // Date.getTime 1504 private void updateInternalClockDisplay() { 1505 String txt = internalClock.getRun() ? Bundle.getMessage("TagRunning") : Bundle.getMessage("TagStopped"); 1506 Date now = internalClock.getTime(); 1507 txt = txt + " " 1508 + (now.getHours() / 10) + (now.getHours() - ((now.getHours() / 10) * 10)) 1509 + Bundle.getMessage("LabelTimeSep") 1510 + (now.getMinutes() / 10) + (now.getMinutes() - ((now.getMinutes() / 10) * 10)) 1511 + Bundle.getMessage("LabelTimeSep") 1512 + (now.getSeconds() / 10) + (now.getSeconds() - ((now.getSeconds() / 10) * 10)); 1513 txt = txt + " " 1514 + Bundle.getMessage("LabelRatio") + " " 1515 + threeDigits.format(internalClock.getRate()) + Bundle.getMessage("LabelToOne"); 1516 if (clockMode == SYNCMODE_INTERNAL_MASTER) { 1517 txt = txt + " " + Bundle.getMessage("TagIsInternalMaster"); 1518 double intTime = getIntTime(); 1519 double nceTime = getNceTime(); 1520 double diffTime = nceTime - intTime; 1521 txt = txt + " " + Bundle.getMessage("ClockError"); 1522 txt = txt + " " + threeDigits.format(diffTime); 1523 } 1524 internalDisplayStatus.setText(txt); 1525 } 1526 1527 private void issueReadOnlyRequest() { 1528 if (!waitingForCmdRead) { 1529 byte[] cmd = jmri.jmrix.nce.NceBinaryCommand.accMemoryRead(tc.csm.getClockAddr()); 1530 NceMessage cmdNce = jmri.jmrix.nce.NceMessage.createBinaryMessage(tc, cmd, CS_CLOCK_MEM_SIZE); 1531 waiting++; 1532 waitingForCmdRead = true; 1533 tc.sendNceMessage(cmdNce, this); 1534 // log.debug("issueReadOnlyRequest at " + internalClock.getTime()); 1535 } 1536 } 1537 1538 private void issueReadAllRequest() { 1539 if (!waitingForCmdRead) { 1540 byte[] cmd = jmri.jmrix.nce.NceBinaryCommand.accMemoryRead(tc.csm.getClockAddr()); 1541 NceMessage cmdNce = jmri.jmrix.nce.NceMessage.createBinaryMessage(tc, cmd, CS_CLOCK_MEM_SIZE); 1542 waiting++; 1543 waitingForCmdRead = true; 1544 tc.sendNceMessage(cmdNce, this); 1545 } 1546 updateTimeFromRead = true; 1547 updateRatioFromRead = true; 1548 updateFormatFromRead = true; 1549 updateStatusFromRead = true; 1550 } 1551 1552 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification="was previously marked with @SuppressWarnings, reason unknown") 1553 private void issueReadTimeRequest() { 1554 if (!waitingForCmdRead) { 1555 byte[] cmd = jmri.jmrix.nce.NceBinaryCommand.accMemoryRead(tc.csm.getClockAddr()); 1556 NceMessage cmdNce = jmri.jmrix.nce.NceMessage.createBinaryMessage(tc, cmd, CS_CLOCK_MEM_SIZE); 1557 waiting++; 1558 waitingForCmdRead = true; 1559 tc.sendNceMessage(cmdNce, this); 1560 } 1561 updateTimeFromRead = true; 1562 } 1563 1564 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification="was previously marked with @SuppressWarnings, reason unknown") 1565 private void issueReadRatioRequest() { 1566 if (!waitingForCmdRead) { 1567 byte[] cmd = jmri.jmrix.nce.NceBinaryCommand.accMemoryRead(tc.csm.getClockAddr()); 1568 NceMessage cmdNce = jmri.jmrix.nce.NceMessage.createBinaryMessage(tc, cmd, CS_CLOCK_MEM_SIZE); 1569 waiting++; 1570 waitingForCmdRead = true; 1571 tc.sendNceMessage(cmdNce, this); 1572 } 1573 updateRatioFromRead = true; 1574 } 1575 1576 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification="was previously marked with @SuppressWarnings, reason unknown") 1577 private void issueReadFormatRequest() { 1578 if (!waitingForCmdRead) { 1579 byte[] cmd = jmri.jmrix.nce.NceBinaryCommand.accMemoryRead(tc.csm.getClockAddr()); 1580 NceMessage cmdNce = jmri.jmrix.nce.NceMessage.createBinaryMessage(tc, cmd, CS_CLOCK_MEM_SIZE); 1581 waiting++; 1582 waitingForCmdRead = true; 1583 tc.sendNceMessage(cmdNce, this); 1584 } 1585 updateFormatFromRead = true; 1586 } 1587 1588 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification="was previously marked with @SuppressWarnings, reason unknown") 1589 private void issueReadStatusRequest() { 1590 if (!waitingForCmdRead) { 1591 byte[] cmd = jmri.jmrix.nce.NceBinaryCommand.accMemoryRead(tc.csm.getClockAddr()); 1592 NceMessage cmdNce = jmri.jmrix.nce.NceMessage.createBinaryMessage(tc, cmd, CS_CLOCK_MEM_SIZE); 1593 waiting++; 1594 waitingForCmdRead = true; 1595 tc.sendNceMessage(cmdNce, this); 1596 } 1597 updateStatusFromRead = true; 1598 } 1599 1600 private void issueClockSet(int hh, int mm, int ss) { 1601 if ((hh < 0) || (hh > 23)) { 1602 JmriJOptionPane.showMessageDialog(this, 1603 Bundle.getMessage("DIALOG_BadHour", hh), 1604 Bundle.getMessage("DIALOG_NceClockMon"), 1605 JmriJOptionPane.ERROR_MESSAGE); 1606 return; 1607 } 1608 if ((mm < 0) || (mm > 59)) { 1609 JmriJOptionPane.showMessageDialog(this, 1610 Bundle.getMessage("DIALOG_BadMinute", mm), 1611 Bundle.getMessage("DIALOG_NceClockMon"), 1612 JmriJOptionPane.ERROR_MESSAGE); 1613 return; 1614 } 1615 if ((ss < 0) || (ss > 59)) { 1616 JmriJOptionPane.showMessageDialog(this, 1617 Bundle.getMessage("DIALOG_BadSecond", ss), 1618 Bundle.getMessage("DIALOG_NceClockMon"), 1619 JmriJOptionPane.ERROR_MESSAGE); 1620 return; 1621 } 1622 issueClockSetMem(hh, mm, ss); 1623 } 1624 1625 private void issueClockSetMem(int hh, int mm, int ss) { 1626 byte[] b = new byte[3]; 1627 b[0] = (byte) ss; 1628 b[1] = (byte) mm; 1629 b[2] = (byte) hh; 1630 byte[] cmd = jmri.jmrix.nce.NceBinaryCommand.accMemoryWriteN(tc.csm.getClockAddr() + CS_CLOCK_SECONDS, b); 1631 NceMessage cmdNce = jmri.jmrix.nce.NceMessage.createBinaryMessage(tc, cmd, CMD_MEM_SET_REPLY_SIZE); 1632 waiting++; 1633 waitingForCmdTime = true; 1634 tc.sendNceMessage(cmdNce, this); 1635 } 1636 1637 private void issueClockRatio(int r) { 1638 byte[] cmd = jmri.jmrix.nce.NceBinaryCommand.accSetClockRatio(r); 1639 NceMessage cmdNce = jmri.jmrix.nce.NceMessage.createBinaryMessage(tc, cmd, CMD_CLOCK_SET_REPLY_SIZE); 1640 waiting++; 1641 waitingForCmdRatio = true; 1642 tc.sendNceMessage(cmdNce, this); 1643 } 1644 1645 private void issueClock1224(boolean mode) { 1646 byte[] cmd = jmri.jmrix.nce.NceBinaryCommand.accSetClock1224(mode); 1647 NceMessage cmdNce = jmri.jmrix.nce.NceMessage.createBinaryMessage(tc, cmd, CMD_CLOCK_SET_REPLY_SIZE); 1648 waiting++; 1649 waitingForCmd1224 = true; 1650 tc.sendNceMessage(cmdNce, this); 1651 } 1652 1653 private void issueClockStop() { 1654 byte[] cmd = jmri.jmrix.nce.NceBinaryCommand.accStopClock(); 1655 NceMessage cmdNce = jmri.jmrix.nce.NceMessage.createBinaryMessage(tc, cmd, CMD_CLOCK_SET_REPLY_SIZE); 1656 waiting++; 1657 waitingForCmdStop = true; 1658 tc.sendNceMessage(cmdNce, this); 1659 } 1660 1661 private void issueClockStart() { 1662 byte[] cmd = jmri.jmrix.nce.NceBinaryCommand.accStartClock(); 1663 NceMessage cmdNce = jmri.jmrix.nce.NceMessage.createBinaryMessage(tc, cmd, CMD_CLOCK_SET_REPLY_SIZE); 1664 waiting++; 1665 waitingForCmdStart = true; 1666 tc.sendNceMessage(cmdNce, this); 1667 } 1668 1669 /** 1670 * Handles minute notifications for NCE Clock Monitor/Synchronizer 1671 */ 1672 public void newInternalMinute() { 1673 // if (log.isDebugEnabled()) { 1674 // log.debug("newInternalMinute clockMode: " + clockMode + " nceInit: " + nceSyncInitStateCounter + " nceRun: " + nceSyncRunStateCounter); 1675 //} 1676 //NCE clock is running 1677 if (lastClockReadPacket != null && lastClockReadPacket.getElement(CS_CLOCK_STATUS) == 0) { 1678 if (clockMode == SYNCMODE_INTERNAL_MASTER) { 1679 // start alarm timer 1680 alarmSyncStart(); 1681 } 1682 } 1683 } 1684 1685 // handle window closing event 1686 public void windowClosing(java.awt.event.WindowEvent e) { 1687 setVisible(false); 1688 if (timerDisplayUpdate != null) { 1689 timerDisplayUpdate.stop(); 1690 } 1691 //super.windowClosing(e); 1692 } 1693 1694 @Override 1695 public void dispose() { 1696 // stop alarm 1697 if (timerDisplayUpdate != null) { 1698 timerDisplayUpdate.stop(); 1699 timerDisplayUpdate = null; 1700 } 1701 // Remove ourselves from the Timebase minute rollover event 1702 InstanceManager.getDefault(jmri.Timebase.class).removeMinuteChangeListener(minuteChangeListener); 1703 minuteChangeListener = null; 1704 1705 // take apart the JFrame 1706 super.dispose(); 1707 } 1708 1709 /** 1710 * Nested class to create one of these using old-style defaults 1711 */ 1712 static public class Default extends jmri.jmrix.nce.swing.NceNamedPaneAction { 1713 1714 public Default() { 1715 super("Open NCE Clock Monitor", 1716 new jmri.util.swing.sdi.JmriJFrameInterface(), 1717 ClockMonPanel.class.getName(), 1718 jmri.InstanceManager.getDefault(NceSystemConnectionMemo.class)); 1719 } 1720 } 1721 1722 private final static Logger log = LoggerFactory.getLogger(ClockMonPanel.class); 1723 1724}