001package jmri.jmrix.dccpp.swing.mon;
002
003import jmri.jmrix.dccpp.DCCppListener;
004import jmri.jmrix.dccpp.DCCppMessage;
005import jmri.jmrix.dccpp.DCCppReply;
006import jmri.jmrix.dccpp.DCCppSystemConnectionMemo;
007import jmri.jmrix.dccpp.DCCppTrafficController;
008import jmri.jmrix.dccpp.serial.SerialDCCppPacketizer;
009
010import java.awt.BorderLayout;
011import java.awt.Dimension;
012import java.awt.event.ActionEvent;
013
014import javax.swing.*;
015
016/**
017 * Frame displaying (and logging) DCCpp command messages.
018 *
019 * @author Bob Jacobsen Copyright (C) 2001
020 * @author Chuck Catania  Copyright (C) 2014, 2016, 2017
021 * @author mstevetodd  Copyright (C) 2021
022 */
023public class DCCppMonFrame extends jmri.jmrix.AbstractMonFrame implements DCCppListener {
024
025    // member declarations
026    final java.util.ResourceBundle rb = java.util.ResourceBundle.getBundle("jmri.jmrix.dccpp.swing.DCCppSwingBundle"); // NOI18N
027
028    private DCCppTrafficController tc = null;
029    private DCCppSystemConnectionMemo _memo = null;
030
031    private SerialDCCppPacketizer serialDCCppTC = null;
032
033    private final JPanel serialPane = new JPanel();
034    private final JLabel queuedEntriesLabel = new JLabel("", SwingConstants.LEFT); // NOI18N
035    private final JToggleButton pauseRefreshButton = new JToggleButton();
036    private final JButton clearRefreshQueueButton = new JButton();
037    private final JCheckBox displayTranslatedCheckBox = new JCheckBox(Bundle.getMessage("ButtonShowTranslation"));
038
039    private final String doNotDisplayTranslatedCheck = this.getClass().getName() + ".DoNotDisplayTranslated"; // NOI18N
040
041    public DCCppMonFrame(DCCppSystemConnectionMemo memo) {
042        super();
043        _memo = memo;        
044    }
045
046    @Override
047    protected String title() {
048        return rb.getString("DCCppMonFrameTitle")+" (" + _memo.getSystemPrefix() + ")";  // NOI18N
049    }
050
051    @Override
052    protected void init() {
053
054        // connect to TrafficController
055        tc = _memo.getDCCppTrafficController();
056        tc.addDCCppListener(~0, this);
057        
058        if ((tc instanceof SerialDCCppPacketizer) && tc.getCommandStation().isFunctionRefreshRequired()) {
059            serialDCCppTC = (SerialDCCppPacketizer) tc;
060
061            pauseRefreshButton.setSelected(!serialDCCppTC.isActiveRefresh());
062
063            refreshQueuedMessages();
064
065            add(serialPane, BorderLayout.PAGE_END);
066        }
067
068        // Create the background function refreshing-related buttons and add
069        // them to a panel. Will be hidden if not required by command station.
070        final JLabel functionLabel = new JLabel(Bundle.getMessage("LabelFunctionRefresh"), SwingConstants.LEFT); // NOI18N
071
072        pauseRefreshButton.setText(Bundle.getMessage("ButtonPauseRefresh")); // NOI18N
073        pauseRefreshButton.setVisible(true);
074        pauseRefreshButton.setToolTipText(Bundle.getMessage("TooltipPauseRefresh")); // NOI18N
075        // the selected state of pauseRefreshButton will be set when the context
076        // is created
077
078        clearRefreshQueueButton.setText(Bundle.getMessage("ButtonClearRefreshQueue")); // NOI18N
079        clearRefreshQueueButton.setVisible(true);
080        clearRefreshQueueButton.setToolTipText(Bundle.getMessage("TooltipClearRefreshQueue")); // NOI18N
081
082        serialPane.setLayout(new BoxLayout(serialPane, BoxLayout.LINE_AXIS));
083        serialPane.add(functionLabel);
084        serialPane.add(Box.createRigidArea(new Dimension(5, 0)));
085        serialPane.add(pauseRefreshButton);
086        serialPane.add(Box.createRigidArea(new Dimension(5, 0)));
087        serialPane.add(clearRefreshQueueButton);
088        serialPane.add(Box.createRigidArea(new Dimension(5, 0)));
089        serialPane.add(queuedEntriesLabel);
090
091        pauseRefreshButton.addActionListener((final java.awt.event.ActionEvent e) -> {
092            pauseButtonEvent(e);
093        });
094
095        clearRefreshQueueButton.addActionListener((final java.awt.event.ActionEvent e) -> {
096            clearButtonEvent(e);
097        });
098        
099        pack();        
100    }
101    
102    /**
103     * Define system-specific help item.
104     */
105//    @Override
106//    protected void setHelp() {
107//        addHelpMenu("package.jmri.jmrix.DCCpp.DCCpp.DCCppmon.DCCppMonFrame", true);  // NOI18N
108//    }
109    
110    //-------------------
111    //  Transmit Packets
112    //-------------------
113    @Override
114    public synchronized void message(DCCppMessage l) {
115
116        // display the decoded data
117        StringBuilder text = new StringBuilder();
118        if ( displayTranslatedCheckBox.isSelected() ) {
119            text.append("TX: ");
120            text.append(l.toMonitorString());
121        }
122        text.append("\n");
123
124        nextLine(text.toString(), (rawCheckBox.isSelected() ? l.toString() : ""));
125        refreshQueuedMessages();
126
127    }
128
129    @Override
130    public void message(DCCppReply l) {
131        // receive a DCC++ message and log it
132        // display the raw data if requested
133        if (log.isDebugEnabled()) {
134            log.debug("Message in Monitor: '{}' opcode {}", l, Character.toString(l.getOpCodeChar()));
135        }
136
137        StringBuilder text = new StringBuilder();
138        if ( displayTranslatedCheckBox.isSelected() ) {
139            text.append(" RX: ");
140            text.append(l.toMonitorString());
141        }
142        text.append("\n");
143
144        nextLine( text.toString(), (rawCheckBox.isSelected() ? l.toString() : ""));
145
146        //enable or disable the refresh pane based on support by command station
147        if (l.isStatusReply()) { 
148            if (tc.getCommandStation().isFunctionRefreshRequired()) { 
149                serialPane.setVisible(true);
150            } else {
151                serialPane.setVisible(false);
152            }
153        }
154       
155    }
156
157    @Override
158    public void notifyTimeout(DCCppMessage msg) {
159        // TODO Auto-generated method stub
160        
161    }
162
163    private void clearButtonEvent(final ActionEvent e) {
164        if (serialDCCppTC != null)
165            serialDCCppTC.clearRefreshQueue();
166
167        refreshQueuedMessages();
168    }
169    
170    private void pauseButtonEvent(final ActionEvent e) {
171        final JToggleButton source = (JToggleButton) e.getSource();
172
173        if (serialDCCppTC != null)
174            serialDCCppTC.setActiveRefresh(!source.isSelected());
175    }
176
177    private int previouslyQueuedMessages = -1;
178
179    public synchronized void refreshQueuedMessages() {
180        if (serialDCCppTC != null) {
181            final int currentlyQueuedMessages = serialDCCppTC.getQueueLength();
182
183            if (currentlyQueuedMessages != previouslyQueuedMessages) {
184                queuedEntriesLabel.setText(Bundle.getMessage("LabelQueuedEntries", String.valueOf(currentlyQueuedMessages))); // NOI18N
185
186                clearRefreshQueueButton.setEnabled(currentlyQueuedMessages > 0);
187
188                previouslyQueuedMessages = currentlyQueuedMessages;
189            }
190        }
191    }
192
193    private void displayTranslatedEvent(final ActionEvent e) {
194        if ( neitherCheckBoxSelected() ) {
195            rawCheckBox.setSelected(true);
196        }
197    }
198
199    private void rawCheckBoxEvent(final ActionEvent e) {
200        if ( neitherCheckBoxSelected() ) {
201            displayTranslatedCheckBox.setSelected(true);
202        }
203    }
204
205    private boolean neitherCheckBoxSelected() {
206        return (!( displayTranslatedCheckBox.isSelected()) ) && (!(rawCheckBox.isSelected()));
207    }
208
209    @Override
210    public JPanel getCheckBoxPanel(){
211        JPanel a = super.getCheckBoxPanel();
212        a.add(displayTranslatedCheckBox,0);
213        displayTranslatedCheckBox.addActionListener(this::displayTranslatedEvent);
214        rawCheckBox.addActionListener(this::rawCheckBoxEvent);
215        
216        displayTranslatedCheckBox.setSelected(!userPrefs.getSimplePreferenceState(doNotDisplayTranslatedCheck));
217        rawCheckBoxEvent(null); // if neither raw on tranalated selected, display translated.
218        return a;
219    }
220
221    @Override
222    public void dispose() {
223        if ( tc != null ) {
224            tc.removeDCCppListener(~0, this);
225        }
226        if (userPrefs!=null) {
227            userPrefs.setSimplePreferenceState(doNotDisplayTranslatedCheck, !displayTranslatedCheckBox.isSelected());
228        }
229        super.dispose();
230    }
231
232    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DCCppMonFrame.class);
233
234}