001package jmri.jmrix.dccpp.swing.mon;
002
003import java.awt.BorderLayout;
004import java.awt.Dimension;
005import java.awt.event.ActionEvent;
006
007import javax.swing.Box;
008import javax.swing.BoxLayout;
009import javax.swing.JButton;
010import javax.swing.JLabel;
011import javax.swing.JPanel;
012import javax.swing.JToggleButton;
013import javax.swing.SwingConstants;
014
015import org.slf4j.Logger;
016import org.slf4j.LoggerFactory;
017
018import jmri.jmrix.dccpp.DCCppListener;
019import jmri.jmrix.dccpp.DCCppMessage;
020import jmri.jmrix.dccpp.DCCppReply;
021import jmri.jmrix.dccpp.DCCppSystemConnectionMemo;
022import jmri.jmrix.dccpp.DCCppTrafficController;
023import jmri.jmrix.dccpp.serial.SerialDCCppPacketizer;
024
025/**
026 * Panel displaying (and logging) DCC++ messages derived from DCCppMonFrame.
027 *
028 * @author Bob Jacobsen Copyright (C) 2002
029 * @author Paul Bender Copyright (C) 2004-2014
030 * @author Giorgio Terdina Copyright (C) 2007
031 * @author Mark Underwood Copyright (C) 2015
032 * @author Costin Grigoras Copyright (C) 2019
033 */
034public class DCCppMonPane extends jmri.jmrix.AbstractMonPane implements DCCppListener {
035    private static final long serialVersionUID = 1L;
036
037    final java.util.ResourceBundle rb = java.util.ResourceBundle.getBundle("jmri.jmrix.dccpp.swing.DCCppSwingBundle"); // NOI18N
038
039    protected DCCppTrafficController tc = null;
040    protected DCCppSystemConnectionMemo memo = null;
041
042    protected SerialDCCppPacketizer serialDCCppTC = null;
043
044    protected final JPanel serialPane = new JPanel();
045    protected final JLabel queuedEntriesLabel = new JLabel("", SwingConstants.LEFT); // NOI18N
046    protected final JToggleButton pauseRefreshButton = new JToggleButton();
047    protected final JButton clearRefreshQueueButton = new JButton();
048
049    @Override
050    public String getTitle() {
051        return (rb.getString("DCCppMonFrameTitle")); // NOI18N
052    }
053
054    @Override
055    public void initContext(final Object context) {
056        if (context instanceof DCCppSystemConnectionMemo) {
057            memo = (DCCppSystemConnectionMemo) context;
058            tc = memo.getDCCppTrafficController();
059            // connect to the TrafficController
060            tc.addDCCppListener(~0, this);
061
062            if ((tc instanceof SerialDCCppPacketizer) && tc.getCommandStation().isFunctionRefreshRequired()) {
063                serialDCCppTC = (SerialDCCppPacketizer) tc;
064
065                pauseRefreshButton.setSelected(!serialDCCppTC.isActiveRefresh());
066
067                refreshQueuedMessages();
068
069                add(serialPane, BorderLayout.PAGE_END);
070            }
071        }
072    }
073
074    /*
075     * (non-Javadoc)
076     *
077     * @see jmri.jmrix.AbstractMonPane#initComponents()
078     */
079    @Override
080    public void initComponents() {
081        super.initComponents();
082
083        // Create the background function refreshing-related buttons and add
084        // them to a panel. The panel however will only be added if the traffic
085        // controller is an instance of SerialDCCppPacketizer and FunctionRefreshRequired by the command station
086        final JLabel functionLabel = new JLabel(Bundle.getMessage("LabelFunctionRefresh"), SwingConstants.LEFT); // NOI18N
087
088        pauseRefreshButton.setText(Bundle.getMessage("ButtonPauseRefresh")); // NOI18N
089        pauseRefreshButton.setVisible(true);
090        pauseRefreshButton.setToolTipText(Bundle.getMessage("TooltipPauseRefresh")); // NOI18N
091        // the selected state of pauseRefreshButton will be set when the context
092        // is created, in initContext()
093
094        clearRefreshQueueButton.setText(Bundle.getMessage("ButtonClearRefreshQueue")); // NOI18N
095        clearRefreshQueueButton.setVisible(true);
096        clearRefreshQueueButton.setToolTipText(Bundle.getMessage("TooltipClearRefreshQueue")); // NOI18N
097
098        serialPane.setLayout(new BoxLayout(serialPane, BoxLayout.LINE_AXIS));
099        serialPane.add(functionLabel);
100        serialPane.add(Box.createRigidArea(new Dimension(5, 0)));
101        serialPane.add(pauseRefreshButton);
102        serialPane.add(Box.createRigidArea(new Dimension(5, 0)));
103        serialPane.add(clearRefreshQueueButton);
104        serialPane.add(Box.createRigidArea(new Dimension(5, 0)));
105        serialPane.add(queuedEntriesLabel);
106
107        pauseRefreshButton.addActionListener((final java.awt.event.ActionEvent e) -> {
108            pauseButtonEvent(e);
109        });
110
111        clearRefreshQueueButton.addActionListener((final java.awt.event.ActionEvent e) -> {
112            clearButtonEvent(e);
113        });
114    }
115
116    /**
117     * @param e
118     */
119    private void clearButtonEvent(final ActionEvent e) {
120        if (serialDCCppTC != null)
121            serialDCCppTC.clearRefreshQueue();
122
123        refreshQueuedMessages();
124    }
125
126    /**
127     * @param e
128     */
129    private void pauseButtonEvent(final ActionEvent e) {
130        final JToggleButton source = (JToggleButton) e.getSource();
131
132        if (serialDCCppTC != null)
133            serialDCCppTC.setActiveRefresh(!source.isSelected());
134    }
135
136    /**
137     * Initialize the data source.
138     */
139    @Override
140    protected void init() {
141    }
142
143    @Override
144    public void dispose() {
145        // disconnect from the LnTrafficController
146        tc.removeDCCppListener(~0, this);
147        // and unwind swing
148        super.dispose();
149    }
150
151    private int previouslyQueuedMessages = -1;
152
153    public synchronized void refreshQueuedMessages() {
154        if (serialDCCppTC != null) {
155            final int currentlyQueuedMessages = serialDCCppTC.getQueueLength();
156
157            if (currentlyQueuedMessages != previouslyQueuedMessages) {
158                queuedEntriesLabel.setText(Bundle.getMessage("LabelQueuedEntries", String.valueOf(currentlyQueuedMessages))); // NOI18N
159
160                clearRefreshQueueButton.setEnabled(currentlyQueuedMessages > 0);
161
162                previouslyQueuedMessages = currentlyQueuedMessages;
163            }
164        }
165    }
166
167    @Override
168    public synchronized void message(final DCCppReply l) {
169        // receive a DCC++ message and log it
170        // display the raw data if requested
171        if (log.isDebugEnabled()) {
172            log.debug("Message in Monitor: '{}' opcode {}", l, Character.toString(l.getOpCodeChar()));
173        }
174
175        logMessage("", "RX: ", l);
176    }
177
178    // listen for the messages to the Base Station
179    @Override
180    public synchronized void message(final DCCppMessage l) {
181        // display the raw data if requested
182        logMessage("", "TX: ", l);
183
184        refreshQueuedMessages();
185    }
186
187    // Handle a timeout notification
188    @Override
189    public void notifyTimeout(final DCCppMessage msg) {
190        log.debug("Notified of timeout on message '{}'", msg);
191    }
192
193    /**
194     * Nested class to create one of these using old-style defaults
195     */
196    static public class Default extends jmri.util.swing.JmriNamedPaneAction {
197        private static final long serialVersionUID = 1L;
198
199        public Default() {
200            super(java.util.ResourceBundle.getBundle("jmri.jmrix.dccpp.swing.DCCppSwingBundle").getString("DCCppMonFrameTitle"), DCCppMonPane.class.getName());
201            setContext(jmri.InstanceManager.getDefault(DCCppSystemConnectionMemo.class));
202        }
203    }
204
205    private final static Logger log = LoggerFactory.getLogger(DCCppMonPane.class);
206}