001package jmri.jmrit.etcs.dmi.swing;
002
003import java.awt.Color;
004import java.awt.Font;
005import java.awt.event.ActionEvent;
006import java.util.*;
007
008import javax.annotation.Nonnull;
009import javax.swing.*;
010
011import jmri.InstanceManager;
012import jmri.Timebase;
013import jmri.jmrit.etcs.ResourceUtil;
014
015/**
016 * Class to demonstrate features of ERTMS DMI Panel G,
017 * Automatic Train Operation and clock.
018 * @author Steve Young Copyright (C) 2024
019 */
020public class DmiPanelG extends JPanel {
021
022    private final DmiPanel main;
023    private final JLabel timeLabel;
024    private final JLabel g2g3g4LabelTop;
025    private final JLabel g2g3g4LabelBottom;
026
027    private final Timebase clock;
028
029    private final transient java.beans.PropertyChangeListener clockListener;
030
031    private final JButton g1Button;
032    private final JButton g2Button;
033    private final JButton g3Button;
034    private final JLabel g4Label;
035    private final JLabel g3LabelMins;
036    private final JLabel g3LabelSecs;
037    private final JButton g5Button;
038
039    public DmiPanelG(@Nonnull DmiPanel mainPanel){
040        super();
041        setLayout(null);
042
043        setBackground(DmiPanel.BACKGROUND_COLOUR);
044        setBounds(334, 315, 246, 150);
045
046        main = mainPanel;
047        JToggleButton g12PositionButton = new JToggleButton();
048
049        g12PositionButton.setBounds(63,100,120,50);
050        g12PositionButton.setBorder(javax.swing.BorderFactory.createLineBorder(Color.black, 1));
051        g12PositionButton.setBackground(DmiPanel.BACKGROUND_COLOUR);
052
053        g12PositionButton.setIcon(ResourceUtil.getImageIcon("DR_03.bmp"));
054        add(g12PositionButton);
055
056        g12PositionButton.setFocusable(false);
057
058        // position G13
059        timeLabel = new JLabel();
060        timeLabel.setBounds(183, 100, 63, 50);
061        timeLabel.setForeground(DmiPanel.GREY);
062        timeLabel.setBackground(DmiPanel.BACKGROUND_COLOUR);
063        timeLabel.setBorder(javax.swing.BorderFactory.createLineBorder(Color.black, 1));
064        timeLabel.setFont(new Font(DmiPanel.FONT_NAME, Font.PLAIN, 13));
065        timeLabel.setHorizontalAlignment(SwingConstants.CENTER);
066        add(timeLabel);
067
068        clock = InstanceManager.getDefault(jmri.Timebase.class);
069        clockListener = (java.beans.PropertyChangeEvent e) -> update();
070        clock.addMinuteChangeListener(clockListener);
071        DmiPanelG.this.update();
072
073        g2g3g4LabelTop = new JLabel();
074        g2g3g4LabelTop.setBounds(49,4,147,25);
075        g2g3g4LabelTop.setForeground(DmiPanel.GREY);
076        g2g3g4LabelTop.setBackground(DmiPanel.BACKGROUND_COLOUR);
077        g2g3g4LabelTop.setFont(new Font(DmiPanel.FONT_NAME, Font.PLAIN, 13));
078        g2g3g4LabelTop.setHorizontalAlignment(SwingConstants.CENTER);
079        add(g2g3g4LabelTop);
080
081        g2g3g4LabelBottom = new JLabel();
082        g2g3g4LabelBottom.setBounds(49,21,147,24);
083        g2g3g4LabelBottom.setForeground(DmiPanel.GREY);
084        g2g3g4LabelBottom.setBackground(DmiPanel.BACKGROUND_COLOUR);
085        g2g3g4LabelBottom.setFont(new Font(DmiPanel.FONT_NAME, Font.PLAIN, 13));
086        g2g3g4LabelBottom.setHorizontalAlignment(SwingConstants.CENTER);
087        add(g2g3g4LabelBottom);
088
089        g1Button = new JButton();
090        g1Button.setBorder(DmiPanel.BORDER_NORMAL);
091        g1Button.setFocusable(false);
092        g1Button.setVisible(false);
093        g1Button.setBackground(DmiPanel.BACKGROUND_COLOUR);
094        g1Button.setContentAreaFilled(false); // Make the button transparent
095        g1Button.addActionListener(this::gButtonPressed);
096        g1Button.setName("g1Button");
097
098        g2Button = new JButton(); // stopping accuracy
099        g2Button.setBorder(DmiPanel.BORDER_NORMAL);
100        g2Button.setFocusable(false);
101        g2Button.setVisible(false);
102        g2Button.setBackground(DmiPanel.BACKGROUND_COLOUR);
103        g2Button.setContentAreaFilled(false); // Make the button transparent
104        g2Button.addActionListener(this::gButtonPressed);
105        g2Button.setName("g2Button");
106
107        g3Button = new JButton();
108        g3Button.setBorder(DmiPanel.BORDER_NORMAL);
109        g3Button.setFocusable(false);
110        g3Button.setVisible(false);
111        g3Button.setBackground(DmiPanel.BACKGROUND_COLOUR);
112        g3Button.setContentAreaFilled(false); // Make the button transparent
113        g3Button.addActionListener(this::gButtonPressed);
114        g3Button.setName("g3Button");
115
116        g3LabelMins = new JLabel();
117        g3LabelMins.setBounds(49+49,12,24,24);
118        g3LabelMins.setForeground(DmiPanel.GREY);
119        g3LabelMins.setBackground(DmiPanel.BACKGROUND_COLOUR);
120        g3LabelMins.setFont(new Font(DmiPanel.FONT_NAME, Font.PLAIN, 17));
121        g3LabelMins.setHorizontalAlignment(SwingConstants.RIGHT);
122        add(g3LabelMins);
123
124        g3LabelSecs = new JLabel();
125        g3LabelSecs.setBounds(49+49+24,14,24,24);
126        g3LabelSecs.setForeground(DmiPanel.GREY);
127        g3LabelSecs.setBackground(DmiPanel.BACKGROUND_COLOUR);
128        g3LabelSecs.setFont(new Font(DmiPanel.FONT_NAME, Font.PLAIN, 13));
129        g3LabelSecs.setHorizontalAlignment(SwingConstants.LEFT);
130        add(g3LabelSecs);
131
132        g4Label = new JLabel();
133        g4Label.setVisible(false);
134        g4Label.setBackground(DmiPanel.BACKGROUND_COLOUR);
135
136        g5Button = new JButton();
137        g5Button.setBorder(DmiPanel.BORDER_NORMAL);
138        g5Button.setFocusable(false);
139        g5Button.setVisible(false);
140        g5Button.setBackground(DmiPanel.BACKGROUND_COLOUR);
141        g5Button.setContentAreaFilled(false); // Make the button transparent
142        g5Button.addActionListener(this::gButtonPressed);
143        g5Button.setName("g5Button");
144
145        g1Button.setBounds(0,0,49,50);
146        g2Button.setBounds(49,0,49,50);
147        g3Button.setBounds(49+49,0,49,50);
148        g4Label.setBounds(49+49+49,0,49,50);
149        g5Button.setBounds(49+49+49+49,0,49,50);
150
151        add(g1Button);
152        add(g2Button);
153        add(g3Button);
154        add(g4Label);
155        add(g5Button);
156    }
157
158    /**
159     * Set Automatic Train Operation Mode.
160     * @param mode the new ATO Mode.
161     * 0: No ATO 
162     * 1: ATO selected
163     * 2: ATO Ready for Engagement
164     * 3: ATO Engaged
165     * 4: ATO Disengaging
166     * 5: ATO failure
167     */
168    protected void setAtoMode(int mode){
169        switch(mode){
170            case 1:
171                g1Button.setIcon(ResourceUtil.getImageIcon("ATO_01.bmp"));
172                g1Button.setDisabledIcon(ResourceUtil.getImageIcon("ATO_01.bmp"));
173                break;
174            case 2:
175                g1Button.setIcon(ResourceUtil.getImageIcon("ATO_02.bmp"));
176                g1Button.setDisabledIcon(ResourceUtil.getImageIcon("ATO_02.bmp"));
177                g1Button.setActionCommand(DmiPanel.PROP_CHANGE_ATO_DRIVER_REQUEST_START);
178                break;
179            case 3:
180                g1Button.setIcon(ResourceUtil.getImageIcon("ATO_03.bmp"));
181                g1Button.setDisabledIcon(ResourceUtil.getImageIcon("ATO_03.bmp"));
182                g1Button.setActionCommand(DmiPanel.PROP_CHANGE_ATO_DRIVER_REQUEST_STOP);
183                break;
184            case 4:
185                g1Button.setIcon(ResourceUtil.getImageIcon("ATO_04.bmp"));
186                g1Button.setDisabledIcon(ResourceUtil.getImageIcon("ATO_04.bmp"));
187                g1Button.setActionCommand(DmiPanel.PROP_CHANGE_ATO_DRIVER_REQUEST_STOP);
188                break;
189            case 5:
190                g1Button.setIcon(ResourceUtil.getImageIcon("ATO_05.bmp"));
191                g1Button.setDisabledIcon(ResourceUtil.getImageIcon("ATO_05.bmp"));
192                break;
193            case 0:
194            default:
195                g1Button.setIcon(null);
196                g1Button.setDisabledIcon(null);
197        }
198        g1Button.setEnabled( mode > 1 && mode < 5);
199        g1Button.setVisible(mode != 0);
200    }
201
202    /**
203     * Set Stopping accuracy symbol visible.
204     * Only valid in ATO Mode.
205     * @param acc -2: Hidden, -1: Undershot 0: Accurate 1: Overshot
206     */
207    protected void setStoppingAccuracy(int acc){
208        switch (acc){
209            case -1:
210                g2Button.setIcon(ResourceUtil.getImageIcon("ATO_07.bmp"));
211                break;
212            case  0:
213                g2Button.setIcon(ResourceUtil.getImageIcon("ATO_08.bmp"));
214                break;
215            case  1:
216                g2Button.setIcon(ResourceUtil.getImageIcon("ATO_06.bmp"));
217                 break;
218            case -2:
219            default:
220                
221        }
222        g2Button.setVisible(acc != -2);
223    }
224
225    protected void setStoppingPointLabel(String station, String eta){
226        g2g3g4LabelTop.setText(station);
227        g2g3g4LabelBottom.setText(eta);
228        g2g3g4LabelTop.setVisible(!station.isBlank());
229        g2g3g4LabelBottom.setVisible(!eta.isBlank());
230    }
231
232    protected void setDwellTime(int mins, int secs){
233        g3LabelMins.setVisible(mins > 0);
234        g3LabelSecs.setVisible(secs > -1);
235        g3LabelMins.setText(String.valueOf(mins));
236        g3LabelSecs.setText(( mins > 0 ? ":" : "")  + ( secs < 10 ? "0": "")+ secs);
237    }
238
239    protected void setDoorIcon(int mode){
240        switch (mode){
241            case 10:
242            case 11:
243            case 12:
244            case 13:
245            case 14:
246            case 15:
247            case 16:
248                g4Label.setIcon(ResourceUtil.getImageIcon("ATO_"+mode+".bmp"));
249                break;
250            case 0:
251            default:
252                g4Label.setIcon(null);
253        }
254        g4Label.setVisible(mode != 0);
255    }
256
257    protected void setSkipStoppingPoint(int mode){
258        g5Button.setEnabled(false);
259        switch (mode){
260            case 17:
261                g5Button.setActionCommand(DmiPanel.PROP_CHANGE_SKIP_STOPPING_POINT_INACTIVE_DRIVER);
262                g5Button.setIcon(ResourceUtil.getImageIcon("ATO_17.bmp"));
263                g5Button.setDisabledIcon(ResourceUtil.getImageIcon("ATO_"+mode+".bmp"));
264                g5Button.setEnabled(true);
265                break;
266            case 18:
267                g5Button.setIcon(ResourceUtil.getImageIcon("ATO_18.bmp"));
268                g5Button.setDisabledIcon(ResourceUtil.getImageIcon("ATO_"+mode+".bmp"));
269                break;
270            case 19:
271                g5Button.setActionCommand(DmiPanel.PROP_CHANGE_SKIP_STOPPING_POINT_REQUEST_DRIVER);
272                g5Button.setIcon(ResourceUtil.getImageIcon("ATO_19.bmp"));
273                g5Button.setDisabledIcon(ResourceUtil.getImageIcon("ATO_"+mode+".bmp"));
274                g5Button.setEnabled(true);
275                break;
276            case 0:
277            default:
278                g5Button.setIcon(null);
279                g5Button.setDisabledIcon(null);
280        }
281        g5Button.setVisible(mode != 0);
282    }
283
284    private void gButtonPressed(ActionEvent e){
285        main.firePropertyChange(e.getActionCommand(), false, true);
286    }
287
288    // todo - display seconds, add routine to TimeBase ??
289    @SuppressWarnings("deprecation") // Date.getHours, getMinutes, getSeconds
290    public void update() {
291        Date now = clock.getTime();
292        int hours = now.getHours();
293        int minutes = now.getMinutes();
294
295        String time = ( hours > 9 ? "" : "0" ) + hours + ":" + ( minutes > 9 ? "" : "0" ) + minutes;
296        timeLabel.setText(time);
297    }
298
299    public void dispose(){
300        clock.removeMinuteChangeListener(clockListener);
301    }
302
303}