001package jmri.jmrit.etcs.dmi.swing; 002 003import java.awt.*; 004import java.beans.PropertyChangeListener; 005 006import javax.annotation.CheckForNull; 007import javax.annotation.Nonnull; 008import javax.swing.*; 009import javax.swing.border.Border; 010 011import jmri.jmrit.etcs.*; 012import jmri.util.ThreadingUtil; 013 014import org.apiguardian.api.API; 015 016/** 017 * JPanel containing the ERTMS ETCS DMI. 018 * @author Steve Young Copyright (C) 2024 019 * @since 5.7.4 020 */ 021@API(status=API.Status.EXPERIMENTAL) 022public class DmiPanel extends JPanel { 023 024 public static final Color WHITE = Color.WHITE; 025 public static final Color BLACK = Color.BLACK; 026 public static final Color GREY = new Color(195,195,195); 027 public static final Color DARK_GREY = new Color (85,85,85); 028 public static final Color MEDIUM_GREY = new Color(150,150,150); 029 public static final Color DARK_BLUE = new Color(3,17,34); 030 public static final Color ORANGE = new Color(234,145,0); 031 public static final Color RED = new Color(191,0,2); 032 public static final Color YELLOW = new Color(223,223,0); 033 protected static final Color BACKGROUND_COLOUR = DARK_BLUE; 034 035 protected static final Border BORDER_ACK = BorderFactory.createLineBorder(DmiPanel.YELLOW , 2); 036 protected static final Border BORDER_NORMAL = BorderFactory.createLineBorder(DmiPanel.BACKGROUND_COLOUR , 2); 037 038 protected static final String FONT_NAME = "Helvetica"; 039 040 public static final String PROP_CHANGE_CABMESSAGE_ACK = "MessageAcknowledged"; 041 public static final String PROP_CHANGE_LEVEL_NTC_TRANSITION_ACK = "LevelNTCAcknowledged"; 042 public static final String PROP_CHANGE_LEVEL_0_TRANSITION_ACK = "Level0Acknowledged"; 043 public static final String PROP_CHANGE_LEVEL_1_TRANSITION_ACK = "Level1Acknowledged"; 044 public static final String PROP_CHANGE_LEVEL_2_TRANSITION_ACK = "Level2Acknowledged"; 045 public static final String PROP_CHANGE_LEVEL_3_TRANSITION_ACK = "Level3Acknowledged"; 046 047 public static final int MODE_NONE = 0; 048 public static final int MODE_SHUNTING = 1; 049 public static final int MODE_TRIP = 4; 050 public static final int MODE_POST_TRIP = 6; 051 public static final int MODE_ON_SIGHT = 7; 052 public static final int MODE_STAFF_RESPONSIBLE = 9; 053 public static final int MODE_FULL_SUPERVISION = 11; 054 public static final int MODE_NON_LEADING = 12; 055 public static final int MODE_STANDBY = 13; 056 public static final int MODE_REVERSING = 14; 057 public static final int MODE_UNFITTED = 16; 058 public static final int MODE_SYSTEM_FAILURE = 18; 059 public static final int MODE_NATIONAL_SYSTEM = 20; 060 public static final int MODE_LIMITED_SUPERVISION = 21; 061 public static final int MODE_AUTOMATIC_DRIVING = 23; 062 public static final int MODE_SUPERVISED_MANOEUVRE = 24; 063 064 public static final String PROP_CHANGE_MODE_SHUNTING_ACK = "ModeShuntingAcknowledged"; 065 public static final String PROP_CHANGE_MODE_TRIP_ACK = "ModeTripAcknowledged"; 066 public static final String PROP_CHANGE_MODE_ON_SIGHT_ACK = "ModeOnSightAcknowledged"; 067 public static final String PROP_CHANGE_MODE_STAFF_RESPONSIBLE_ACK = "ModeStaffResponsibleAcknowledged"; 068 public static final String PROP_CHANGE_MODE_REVERSING_ACK = "ModeReversingAcknowledged"; 069 public static final String PROP_CHANGE_MODE_UNFITTED_ACK = "ModeUnfittedAcknowledged"; 070 public static final String PROP_CHANGE_MODE_NATIONAL_SYSTEM_ACK = "ModeNationalSystemAcknowledged"; 071 public static final String PROP_CHANGE_MODE_LIMITED_SUPERVISION_ACK = "ModeLimitedSupervisionAcknowledged"; 072 public static final String PROP_CHANGE_TRACK_AHEAD_FREE_TRUE = "DriverAdvisesTrackAheadFree"; 073 074 public static final String PROP_CHANGE_ATO_DRIVER_REQUEST_START = "AtoDriverStart"; 075 public static final String PROP_CHANGE_ATO_DRIVER_REQUEST_STOP = "AtoDriverStop"; 076 public static final String PROP_CHANGE_SKIP_STOPPING_POINT_INACTIVE_DRIVER = "SkipStoppingPointInactive"; 077 public static final String PROP_CHANGE_SKIP_STOPPING_POINT_REQUEST_DRIVER = "SkipStoppingPointRequestByDriver"; 078 079 public static final String PROP_CHANGE_TUNNEL_STOP_AREA_ACK = "TunnelStopAreaAcknowledged"; 080 public static final String PROP_CHANGE_SOUND_HORN_ACK = "SoundHornAcknowledged"; 081 public static final String PROP_CHANGE_LOWER_PANT_ACK = "LowerPantographAcknowledged"; 082 public static final String PROP_CHANGE_RAISE_PANT_ACK = "RaisePantographAcknowledged"; 083 public static final String PROP_CHANGE_AIRCON_OPEN_ACK = "AirConOpenAcknowledged"; 084 public static final String PROP_CHANGE_AIRCON_CLOSE_ACK = "AirConCloseAcknowledged"; 085 public static final String PROP_CHANGE_NEUTRAL_START_ACK = "NeutralSectionStartAcknowledged"; 086 public static final String PROP_CHANGE_NEUTRAL_END_ACK = "NeutralSectionEndAcknowledged"; 087 public static final String PROP_CHANGE_NONSTOP_ACK = "NonStoppingAreaAcknowledged"; 088 public static final String PROP_CHANGE_INHIBIT_MAG_BRAKE_ACK = "InhibitMagShoeBrakeAcknowledged"; 089 public static final String PROP_CHANGE_INHIBIT_EDDY_BRAKE_ACK = "InhibitEddyCurrentBrakeAcknowledged"; 090 public static final String PROP_CHANGE_INHIBIT_REGEN_BRAKE_ACK = "InhibitRegenerativeBrakeAcknowledged"; 091 public static final String PROP_CHANGE_TRACTION_0_ACK = "NoTractionAcknowledge"; 092 public static final String PROP_CHANGE_TRACTION_25KV_ACK = "TractionSystemAC25kVAcknowledge"; 093 public static final String PROP_CHANGE_TRACTION_15KV_ACK = "TractionSystemAC15kVAcknowledge"; 094 public static final String PROP_CHANGE_TRACTION_3KV_ACK = "TractionSystemDC3kVAcknowledge"; 095 public static final String PROP_CHANGE_TRACTION_1_5KV_ACK = "TractionSystemDC1.5kVAcknowledge"; 096 public static final String PROP_CHANGE_TRACTION_750V_ACK = "TractionSystemDC600750VAcknowledge"; 097 098 protected static final String PROPERTY_CENTRE_TEXT = DmiPanel.class.getName()+"centreText"; 099 100 private final DmiPanelA panelA; 101 private final DmiPanelB panelB; 102 private final DmiPanelC panelC; 103 private final DmiPanelD panelD; 104 private final DmiPanelE panelE; 105 private final DmiPanelG panelG; 106 private final DmiFlashTimer flashTimer; 107 private int mode = 0; // unset 108 109 /** 110 * Create a new DmiPanel. 111 */ 112 public DmiPanel(){ 113 super(); 114 setPreferredSize(new Dimension(640,480)); 115 setLayout(null); // Set the layout manager to null 116 117 flashTimer = new DmiFlashTimer(this); 118 119 panelA = getPanelA(); 120 panelB = getPanelB(); 121 panelC = getPanelC(); 122 panelD = getPanelD(); 123 panelE = getPanelE(); 124 panelG = getPanelG(); 125 126 add(panelA); 127 add(panelB); 128 add(panelC); 129 add(panelD); 130 add(panelE); 131 add(getPanelF()); 132 add(panelG); 133 add(getPanelY()); 134 add(getPanelZ()); 135 136 } 137 138 // distance countdown bar 139 private DmiPanelA getPanelA() { 140 return new DmiPanelA(this); 141 } 142 143 // speedometer 144 private DmiPanelB getPanelB() { 145 return new DmiPanelB(this); 146 } 147 148 // larger icons under speedometer 149 private DmiPanelC getPanelC() { 150 return new DmiPanelC(this); 151 } 152 153 // planning area 154 private DmiPanelD getPanelD() { 155 return new DmiPanelD(this); 156 } 157 158 // messages 159 private DmiPanelE getPanelE() { 160 return new DmiPanelE(this); 161 } 162 163 // right hand side buttons 164 private DmiPanelF getPanelF() { 165 return new DmiPanelF(this); 166 } 167 168 // ATO and clock 169 private DmiPanelG getPanelG() { 170 return new DmiPanelG(this); 171 } 172 173 // top panel bar spacer 174 private JPanel getPanelY() { 175 JPanel p = new JPanel(); 176 p.setBackground(BACKGROUND_COLOUR); 177 p.setBounds(0, 0, 640, 15); 178 return p; 179 } 180 181 // bottom panel bar spacer 182 private JPanel getPanelZ() { 183 JPanel p = new JPanel(); 184 p.setBackground(BACKGROUND_COLOUR); 185 p.setLayout(null); 186 p.setBounds(0, 465, 640, 15); 187 JLabel jmriLabel = new JLabel("JMRI " + jmri.Version.getCanonicalVersion()); 188 jmriLabel.setFont(new Font(DmiPanel.FONT_NAME, Font.PLAIN, 9)); 189 jmriLabel.setForeground(BLACK); 190 jmriLabel.setLayout(null); 191 jmriLabel.setBounds(590, 2, 110 , 15); 192 p.add(jmriLabel); 193 return p; 194 } 195 196 /** 197 * Set the Maximum Speed on the Speed Dial. 198 * @param speed 140, 180, 250 or 400 199 */ 200 public void setMaxDialSpeed( int speed ) { 201 ThreadingUtil.runOnGUI( () -> panelB.setMaxDialSpeed(speed) ); 202 } 203 204 /** 205 * Set the speed value to be displayed by the dial and in centre of dial. 206 * @param speed no unit specified. 207 */ 208 public void setActualSpeed( float speed ) { 209 ThreadingUtil.runOnGUI( () -> panelB.setActualSpeed(speed) ); 210 } 211 212 /** 213 * Set the Centre Speedometer Circle and Dial Colour. 214 * Default is DmiPanel.GREY 215 * @param colour the colour to use. 216 */ 217 public void setCentreCircleAndDialColor ( Color colour ) { 218 ThreadingUtil.runOnGUI( () -> panelB.setCentreCircleAndDialColor(colour) ); 219 } 220 221 /** 222 * Set a list of Circular Speed Guide sections to display. 223 * @param list the list to display. 224 */ 225 public void setCsgSections(java.util.List<DmiCircularSpeedGuideSection> list){ 226 ThreadingUtil.runOnGUI( () -> panelB.setCsgSections(list) ); 227 } 228 229 /** 230 * Set a speed unit to be displayed in the dial. 231 * @param newVal the speed unit, for display purpose only. 232 */ 233 public void setDisplaySpeedUnit( String newVal ) { 234 ThreadingUtil.runOnGUI( () -> panelB.setDisplaySpeedUnit(newVal) ); 235 } 236 237 /** 238 * Set the ATO Target Advice Speed. 239 * @param newVal Target speed. 240 * Negative values hide the advice. 241 */ 242 public void setTargetAdviceSpeed(int newVal) { 243 ThreadingUtil.runOnGUI(() -> panelB.setTargetAdviceSpeed(newVal) ); 244 } 245 246 /** 247 * Set distance to the next Advice Change. 248 * @param distance to next advice. 249 * Negative values hide the advice. 250 */ 251 public void setNextAdviceChange(int distance) { 252 ThreadingUtil.runOnGUI( () -> panelD.setNextAdviceChange(distance) ); 253 } 254 255 /** 256 * Set the release speed. 257 * A negative value hides the speed. 258 * @param speed to display. 259 */ 260 public void setReleaseSpeed(int speed) { 261 ThreadingUtil.runOnGUI(() -> panelB.setReleaseSpeed(speed) ); 262 } 263 264 /** 265 * Set the text colour of the Release Speed. 266 * @param newColour the colour to use. 267 */ 268 public void setReleaseSpeedColour(Color newColour) { 269 ThreadingUtil.runOnGUI(() -> panelB.setReleaseSpeedColour( newColour )); 270 } 271 272 /** 273 * Set Level Transition Announcement Notification. 274 * Note that some valid options for ERTMS3.6 are invalid for ERTMS4 , 275 * e.g. 2, false. 276 * @param newLevel 277 * -2 : No notification displayed. 278 * -1 : NTC 279 * 0 : Level 0 280 * 1 : Level 1 Intermittent 281 * 2 : Level 2 282 * 3 : Level 3 283 * @param ackRequired true if acknowledgement required by driver, else false. 284 */ 285 public void setLevelTransition(int newLevel, boolean ackRequired) { 286 ThreadingUtil.runOnGUI( () -> panelC.setLevelTransition(newLevel, ackRequired) ); 287 } 288 289 /** 290 * Display Level Symbol. 291 * @param level 292 * -2 : No notification displayed. 293 * -1 : NTC 294 * 0 : Level 0 295 * 1 : Level 1 Intermittent 296 * 2 : Level 2 297 * 3 : Level 3 ( ERTMS < 4 ) 298 */ 299 public void setLevel(int level){ 300 ThreadingUtil.runOnGUI( () -> panelC.setLevel(level) ); 301 } 302 303 /** 304 * Set Mode. 305 * 0 - No Mode Displayed 306 * 1 - Shunting 307 * 4 - Trip 308 * 6 - Post Trip 309 * 7 - On Sight 310 * 9 - Staff Responsible 311 * 11 - Full Supervision Mode 312 * 12 - Non-leading 313 * 13 - Standby 314 * 14 - Reversing 315 * 16 - Unfitted 316 * 18 - System Failure 317 * 21 - Limited Supervision 318 * 23 - Automatic Driving ( From ERTMS4 ) 319 * 24 - Supervised Manoeuvre ( From ERTMS4 ) 320 * @param newMode the mode to display. 321 */ 322 public void setMode(int newMode){ 323 mode = newMode; 324 ThreadingUtil.runOnGUI(() -> { 325 panelB.setMode(newMode); 326 panelD.repaint(); // Panel D also has conditionals depending on mode. 327 }); 328 } 329 330 /** 331 * Set the display to acknowledge the transition to a new Mode. 332 * @param newMode the new Mode to request acknowledgement for. 333 */ 334 public void setModeAcknowledge(int newMode){ 335 ThreadingUtil.runOnGUI(() -> panelC.setModeAcknowledge(newMode) ); 336 } 337 338 /** 339 * Get the displayed operating mode. 340 * @return the current mode. 341 */ 342 protected int getMode(){ 343 return mode; 344 } 345 346 /** 347 * Add a TrackCondition Announcement to under the Dial. 348 * @param tc the Announcement to add. 349 */ 350 public void addAnnouncement( TrackCondition tc ) { 351 ThreadingUtil.runOnGUI( () -> panelB.addAnnouncement(tc) ); 352 } 353 354 /** 355 * Remove an Announcement from under the Dial. 356 * @param tc the Announcement to remove. 357 */ 358 public void removeAnnouncement ( TrackCondition tc ) { 359 ThreadingUtil.runOnGUI( () -> panelB.removeAnnouncement(tc) ); 360 } 361 362 /** 363 * Set a Limited Supervision Speed. 364 * A negative value hides the icon. 365 * @param spd the Limited Supervision Speed. 366 */ 367 public void setLimitedSupervisionSpeed(float spd) { 368 ThreadingUtil.runOnGUI( () -> panelA.setLimitedSupervisionSpeed(spd) ); 369 } 370 371 /** 372 * Set the distance to target bar. 373 * A negative value hides the field. 374 * Values displayed to nearest 10m. 375 * @param distance the distance to set. 376 */ 377 public void setDistanceToTarget(float distance){ 378 ThreadingUtil.runOnGUI( () -> panelA.setDistanceToTarget(distance) ); 379 } 380 381 /** 382 * Set the adhesion Factor symbol displayed. 383 * @param newVal true to display, else false. 384 */ 385 public void setAdhesionFactorOn(boolean newVal){ 386 ThreadingUtil.runOnGUI( () -> panelA.setAdhesionFactorOn(newVal) ); 387 } 388 389 /** 390 * Set if Intervention Symbol is displayed. 391 * @param newVal true to display, false to hide. 392 */ 393 public void setIntervetionSymbol(boolean newVal){ 394 ThreadingUtil.runOnGUI( () -> panelC.setIntervetionSymbol(newVal) ); 395 } 396 397 /** 398 * Set the Reversing Permitted symbol visible. 399 * @param newVal true to display, false to hide. 400 */ 401 public void setReversingPermittedSymbol(boolean newVal){ 402 ThreadingUtil.runOnGUI( () -> panelC.setReversingPermittedSymbol(newVal) ); 403 } 404 405 /** 406 * Set the Indication marker. 407 * Negative values not displayed. 408 * @param distance the distance at which to display the marker. 409 * @param whichSpeedChange the order of the speed change in the Movement Authority. 410 */ 411 public void setIndicationMarker(int distance, int whichSpeedChange ) { 412 ThreadingUtil.runOnGUI( () -> panelD.setIndicationMarkerLine(distance, whichSpeedChange) ); 413 } 414 415 /** 416 * Set Automatic Train Operation Mode. 417 * @param mode the new ATO Mode. 418 * 0: No ATO 419 * 1: ATO selected 420 * 2: ATO Ready for Engagement 421 * 3: ATO Engaged 422 * 4: ATO Disengaging 423 * 5: ATO failure 424 */ 425 protected void setAtoMode(int mode){ 426 ThreadingUtil.runOnGUI( () -> panelG.setAtoMode(mode) ); 427 } 428 429 /** 430 * Set the Coasting Symbol visible. 431 * Only valid to display if in ATO mode 432 * @param visible true to display, else false. 433 */ 434 public void setCoasting(boolean visible){ 435 ThreadingUtil.runOnGUI( () -> panelB.setCoasting(visible) ); 436 } 437 438 /** 439 * Set Stopping accuracy symbol visible. 440 * Only valid in ATO Mode. 441 * @param acc -2: Hidden, -1: Undershot 0: Accurate 1: Overshot 442 */ 443 public void setStoppingAccuracy(int acc){ 444 ThreadingUtil.runOnGUI( () -> panelG.setStoppingAccuracy(acc) ); 445 } 446 447 /** 448 * Set stopping point text. 449 * Only valid in ATO mode. 450 * @param station the next station. 451 * @param eta ETA of next station. 452 */ 453 public void setStoppingPointLabel(String station, String eta){ 454 ThreadingUtil.runOnGUI( () -> panelG.setStoppingPointLabel(station, eta) ); 455 } 456 457 /** 458 * Set remaining Station Dwell time. 459 * @param mins minutes remaining. 460 * @param secs seconds remaining. 461 */ 462 public void setDwellTime(int mins, int secs){ 463 ThreadingUtil.runOnGUI( () -> panelG.setDwellTime(mins, secs) ); 464 } 465 466 /** 467 * Set Door Icon. 468 * @param mode the icon code to display. 469 * 0: Unset 470 * 10: Request driver to open both sides doors 471 * 11: Request driver to open left doors 472 * 12: Request driver to open right doors 473 * 13: Doors are open 474 * 14: Request driver to close doors 475 * 15: Doors are being closed by ATO 476 * 16: Doors are closed 477 */ 478 public void setDoorIcon(int mode){ 479 ThreadingUtil.runOnGUI( () -> panelG.setDoorIcon(mode) ); 480 } 481 482 /** 483 * Set Skip Stopping Point Icon. 484 * @param mode the icon code to display. 485 * 0: Unset 486 * 17: Skip Stopping Point Inactive 487 * 18: Skip Stopping Point requested by ATO-TS 488 * 19: Skip Stopping Point requested by driver 489 */ 490 public void setSkipStoppingPoint(int mode){ 491 ThreadingUtil.runOnGUI( () -> panelG.setSkipStoppingPoint(mode) ); 492 } 493 494 /** 495 * Set the Direction Symbol and visibility. 496 * @param newDirection -1: Reverse, 0 Hidden, 1 Forwards. 497 */ 498 public void setSupervisedDirection(int newDirection) { 499 ThreadingUtil.runOnGUI( () -> panelB.setSupervisedDirection(newDirection) ); 500 } 501 502 /** 503 * No value displayed if distance < 1 504 * @param distance in m to stopping area. 505 * 506 */ 507 public void setTunnelStoppingDistance(int distance) { 508 ThreadingUtil.runOnGUI( () -> panelC.setTunnelStoppingDistance(distance) ); 509 } 510 511 /** 512 * Set Tunnel Stopping Icon Visible. 513 * @param visible true if visible, false hidden. 514 * @param ack true if Acknowledgement Required. 515 */ 516 public void setTunnelStoppingIconVisible(boolean visible, boolean ack){ 517 ThreadingUtil.runOnGUI( () -> panelC.setTunnelStoppingIconVisible(visible, ack) ); 518 } 519 520 /** 521 * Set the Safe Radio Connection Symbol. 522 * @param newVal -1 default, not displayed. 523 * 0 Connection Lost 524 * 1 Connection OK 525 */ 526 public void setSafeRadioConnection(int newVal) { 527 ThreadingUtil.runOnGUI( () -> panelE.setSafeRadioConnection(newVal) ); 528 } 529 530 /** 531 * Set the Track Ahead Free? Question visible. 532 * @param newVal true to display, false to hide. 533 */ 534 public void setTrackAheadFreeQuestionVisible(boolean newVal) { 535 ThreadingUtil.runOnGUI( () -> panelD.setTrackAheadFreeQuestionVisible(newVal) ); 536 } 537 538 /** 539 * Set the Scale on the Planning Area. 540 * 0 : 0 - 1000 541 * 1 : 0 - 2000 542 * 2 : 0 - 4000 543 * 3 : 0 - 8000 544 * 4 : 0 - 16000 545 * 5 : 0 - 32000 546 * @param scale the scale to use. 547 */ 548 public void setScale(int scale){ 549 ThreadingUtil.runOnGUI( () -> panelD.setScale(scale) ); 550 } 551 552 /** 553 * Reset the Movement Authorities to the supplied List. 554 * Existing MAs will be discarded. 555 * @param a List of MAs. 556 */ 557 public void resetMovementAuthorities(@Nonnull java.util.List<MovementAuthority> a) { 558 ThreadingUtil.runOnGUI( () -> panelD.resetMovementAuthorities(a) ); 559 } 560 561 /** 562 * Get a List of current Movement Authorities. 563 * @return List of MAs. 564 */ 565 public java.util.List<MovementAuthority> getMovementAuthorities() { 566 return panelD.getMovementAuthorities(); 567 } 568 569 /** 570 * Extend the Movement Authority. 571 * @param dma the Movement Authority to add to existing List. 572 */ 573 public void extendMovementAuthorities(@Nonnull MovementAuthority dma){ 574 ThreadingUtil.runOnGUI( () -> panelD.extendMovementAuthorities(dma) ); 575 } 576 577 /** 578 * Get the next Planning Track Announcement with the Movement Authority. 579 * @param mustBeStation true if only station data is required. 580 * @return the next Announcement, may be null if none within the Movement Authority. 581 */ 582 @CheckForNull 583 public TrackCondition getNextAnnouncement(boolean mustBeStation){ 584 java.util.List<MovementAuthority> mas = panelD.getMovementAuthorities(); 585 for ( MovementAuthority ma : mas ) { 586 java.util.List<TrackSection> tsList = ma.getTrackSections(); 587 for ( TrackSection ts : tsList ){ 588 java.util.List<TrackCondition> anList = ts.getAnnouncements(); 589 for ( TrackCondition tc : anList ){ 590 if ( !mustBeStation || tc instanceof StationTrackCondition ) { 591 return tc; 592 } 593 } 594 } 595 } 596 return null; 597 } 598 599 /** 600 * Advance the train. 601 * Updates planning panel. 602 * Updates distance to target. 603 * @param distance to advance. 604 */ 605 public void advance(int distance){ 606 ThreadingUtil.runOnGUI( () -> { 607 panelD.advance(distance); 608 panelA.advance(distance); 609 }); 610 } 611 612 /** 613 * Send a CabMessage to the Driver. 614 * @param msg the CabMessage to send. 615 */ 616 public void messageDriver(CabMessage msg) { 617 ThreadingUtil.runOnGUI( () -> panelE.addMessage(msg) ); 618 } 619 620 /** 621 * Remove a previously sent CabMessage from the display. 622 * @param messageId the messageId of the CabMessage to remove. 623 */ 624 public void removeMessage(String messageId) { 625 ThreadingUtil.runOnGUI( () -> panelE.removeMessage(messageId) ); 626 } 627 628 /** 629 * Play one of the DMI UI Sounds. 630 * <p> 631 * 1 - S1_toofast.wav - 2 secs, plays once. 632 * <p> 633 * 2 - S2_warning.wav - 3 secs, loops until stopped. 634 * <p> 635 * 3 - S_info.wav - 1 sec, plays once. 636 * <p> 637 * 4 - click.wav - 1 sec, plays once. 638 * @param sound which Sound, 639 */ 640 public void playDmiSound(int sound) throws IllegalArgumentException { 641 ResourceUtil.playDmiSound(sound); 642 } 643 644 /** 645 * Stop playing a DMI Sound. 646 * @param sound the sound to Stop, normally 2 which plays in a loop. 647 */ 648 public void stopDmiSound(int sound) { 649 ResourceUtil.stopDmiSound(sound); 650 } 651 652 /** 653 * Add a listener to synchronise panel flashing. 654 * @param pcl the listener to add. 655 * @param fast true if fast flashing, false for slow. 656 */ 657 protected void addFlashListener( PropertyChangeListener pcl, boolean fast ) { 658 flashTimer.addFlashListener(pcl, fast); 659 } 660 661 /** 662 * Remove a listener from panel Flash timer notifications. 663 * @param pcl the listener to remove. 664 * @param fast true if fast listener, false if slow. 665 */ 666 protected void removeFlashListener ( PropertyChangeListener pcl, boolean fast ) { 667 flashTimer.removeFlashListener(pcl, fast); 668 } 669 670 /** 671 * Fire a Property Change from this panel. 672 * Old value fired as empty String. 673 * @param property the property name. 674 * @param newVal the new value. 675 */ 676 protected void firePropertyChange(String property, String newVal) { 677 this.firePropertyChange(property, "", newVal); 678 } 679 680 @Override 681 public void setVisible(boolean newVal){ 682 ThreadingUtil.runOnGUI( () -> super.setVisible(newVal) ); 683 } 684 685 /** 686 * Dispose of any Listeners, e.g. Fast Clock. 687 */ 688 public void dispose(){ 689 panelG.dispose(); 690 panelC.dispose(); 691 flashTimer.dispose(); 692 } 693 694 // private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DmiPanel.class); 695 696}