001package jmri.jmrix.lenz.swing.stackmon;
002
003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
004import java.awt.Dimension;
005import java.awt.FlowLayout;
006import javax.swing.BoxLayout;
007import javax.swing.JButton;
008import javax.swing.JLabel;
009import javax.swing.JMenuBar;
010import javax.swing.JPanel;
011import javax.swing.JScrollPane;
012import javax.swing.JTextField;
013import jmri.jmrix.lenz.XNetConstants;
014import jmri.jmrix.lenz.XNetListener;
015import jmri.jmrix.lenz.XNetMessage;
016import jmri.jmrix.lenz.XNetReply;
017import jmri.jmrix.lenz.XNetTrafficController;
018import org.slf4j.Logger;
019import org.slf4j.LoggerFactory;
020
021/**
022 * This frame provides a method for searching the command station stack.
023 * <p>
024 * Current functionality is to search the stack and delete entries.
025 * Future capabilities may include the ability to set the status of function buttons.
026 *
027 * @author Paul Bender Copyright (C) 2005-2010
028 */
029public class StackMonFrame extends jmri.util.JmriJFrame implements XNetListener {
030
031    // buttons currently (4.8) not displayed
032    final JButton nextButton = new JButton(Bundle.getMessage("NextButtonLabel"));
033    final JButton previousButton = new JButton(Bundle.getMessage("PreviousButtonLabel"));
034    final JButton deleteButton = new JButton(Bundle.getMessage("ButtonDelete"));
035    final JButton refreshButton = new JButton(Bundle.getMessage("RefreshButtonLabel"));
036    final JLabel currentStatus = new JLabel(" ");
037
038    final JTextField adrTextField = new javax.swing.JTextField(4);
039
040    StackMonDataModel stackModel;
041    javax.swing.JTable stackTable;
042
043    // flag to know if Get All or Get Next/Previous was pressed
044    private boolean _getAll = false;
045
046    protected XNetTrafficController tc;
047
048    public StackMonFrame(jmri.jmrix.lenz.XNetSystemConnectionMemo memo) {
049        super();
050        // Configure GUI components
051        stackModel = new StackMonDataModel(1, 4, memo);
052        stackTable = new javax.swing.JTable(stackModel);
053
054        // Add listener object to retrieve the next entry
055        nextButton.addActionListener(e -> getNextEntry());
056
057        // Set the Next button to visible
058        nextButton.setVisible(true);
059        // add listener object to retrieve the previous entry
060        previousButton.addActionListener(e -> getPreviousEntry());
061
062        // Set the Previous button to visible.
063        previousButton.setVisible(true);
064        // The previous function is not currently implemented on the 
065        // command station, so we're going to disable the button for now
066        previousButton.setEnabled(false);
067
068        // Set the Delete button to visible
069        deleteButton.setVisible(true);
070        // add listener object to remove the current entry
071        deleteButton.addActionListener(e -> deleteEntry());
072
073        // Set the nextButton to visible
074        refreshButton.setVisible(true);
075        // add listener object to retrieve the next entry
076        refreshButton.addActionListener(e -> getAllEntries());
077
078        // Set the adrTextField to visible
079        adrTextField.setVisible(true);
080
081        // general GUI config
082        setTitle(Bundle.getMessage("MenuItemCSDatabaseManager"));
083        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
084
085        // install items in GUI
086        JPanel pane1 = new JPanel();
087        pane1.setLayout(new FlowLayout());
088
089        pane1.add(refreshButton);
090        getContentPane().add(pane1);
091        //pane1.setMaximumSize(pane1.getSize());
092
093        JPanel manualPanel = new JPanel();
094        manualPanel.setLayout(new FlowLayout());
095        manualPanel.add(previousButton);
096        manualPanel.add(nextButton);
097        manualPanel.add(deleteButton);
098
099        //getContentPane().add(manualPanel); // not working?
100        JPanel pane2 = new JPanel();
101        pane2.setLayout(new FlowLayout());
102        pane2.add(adrTextField);
103        //getContentPane().add(pane2); // not working?
104
105        JPanel pane3 = new JPanel();
106        pane3.setLayout(new FlowLayout());
107        pane3.add(currentStatus);
108        //getContentPane().add(pane3); // not working?
109
110        // Set up the JTable in a Scroll Pane
111        JScrollPane stackPane = new JScrollPane(stackTable);
112        stackPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
113        stackModel.initTable(stackTable, this);
114        getContentPane().add(stackPane);
115
116        addHelpMenu("package.jmri.jmrix.lenz.stackmon.StackMonFrame", true);
117
118        pack();
119
120        tc = memo.getXNetTrafficController();
121
122        tc.addXNetListener(~0, this);
123    }
124
125    @Override
126    public void addNotify() {
127        super.addNotify();
128
129        // resize frame to account for menubar
130        JMenuBar jMenuBar = getJMenuBar();
131        if (jMenuBar != null) {
132            int jMenuBarHeight = jMenuBar.getPreferredSize().height;
133            Dimension dimension = getSize();
134            dimension.height += jMenuBarHeight;
135            setSize(dimension);
136        }
137    }
138
139    /**
140     * Request ALL entries.
141     */
142    private void getAllEntries() {
143        stackModel.clearData();
144        _getAll = true;
145        getNextEntry();
146    }
147
148    /**
149     * Request the next entry.
150     */
151    private void getNextEntry() {
152        int address = 0;
153        if (!adrTextField.getText().equals("")) {
154            address = Integer.parseInt(adrTextField.getText());
155        }
156        XNetMessage msg = XNetMessage.getNextAddressOnStackMsg(address, true);
157        tc.sendXNetMessage(msg, this);
158    }
159
160    /**
161     * Request the next entry by ID.
162     */
163    private void getNextEntry(int address) {
164        XNetMessage msg = XNetMessage.getNextAddressOnStackMsg(address, true);
165        tc.sendXNetMessage(msg, this);
166    }
167
168    /**
169     * Request the previous entry.
170     */
171    private void getPreviousEntry() {
172        int address = 0;
173        if (!adrTextField.getText().equals("")) {
174            address = Integer.parseInt(adrTextField.getText());
175        }
176        XNetMessage msg = XNetMessage.getNextAddressOnStackMsg(address, false);
177        tc.sendXNetMessage(msg, this);
178    }
179
180    /**
181     * Remove the current entry.
182     */
183    private void deleteEntry() {
184        int address;
185        if (!adrTextField.getText().equals("")) {
186            address = Integer.parseInt(adrTextField.getText());
187            XNetMessage msg = XNetMessage.getDeleteAddressOnStackMsg(address);
188            tc.sendXNetMessage(msg, this);
189        }
190    }
191
192    /**
193     * Request the status of the current address.
194     */
195    @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", 
196        justification = "This is part of work in progress code to allow display of all information about the locomotives in the stack.")
197    private void requestStatus() {
198        int address;
199        if (!adrTextField.getText().equals("")) {
200            address = Integer.parseInt(adrTextField.getText());
201            XNetMessage msg = XNetMessage.getLocomotiveInfoRequestMsg(address);
202            tc.sendXNetMessage(msg, this);
203        }
204    }
205
206    /**
207     * Request the momentary/continuous status of functions for the 
208     * current address.
209     */
210    @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", 
211            justification = "This is part of work in progress code to allow display of all information about the locomotives in the stack.")
212    private void requestFunctionStatus() {
213        int address;
214        if (!adrTextField.getText().equals("")) {
215            address = Integer.parseInt(adrTextField.getText());
216            XNetMessage msg = XNetMessage.getLocomotiveFunctionStatusMsg(address);
217            tc.sendXNetMessage(msg, this);
218        }
219    }
220
221    // The XNet Listener Interface
222
223    /**
224     * Receive information from the command station.
225     */
226    @Override
227    public void message(XNetReply r) {
228        if (r.getElement(0) == XNetConstants.LOCO_INFO_RESPONSE) {
229            int address = r.getThrottleMsgAddr();
230            Integer intAddress = address;
231            switch (r.getElement(1)) {
232                case XNetConstants.LOCO_SEARCH_RESPONSE_N:
233                    currentStatus.setText(Bundle.getMessage("SearchNormal"));
234                    adrTextField.setText("" + address);
235                    stackModel.updateData(intAddress, Bundle.getMessage("SearchNormal"));
236                    // Request Address Status
237                    // requestStatus();
238                    // requestFunctionStatus();
239                    if (_getAll) {
240                        getNextEntry(address);
241                    }
242                    break;
243                case XNetConstants.LOCO_SEARCH_RESPONSE_DH:
244                    currentStatus.setText(Bundle.getMessage("SearchDH"));
245                    adrTextField.setText("" + r.getThrottleMsgAddr());
246                    stackModel.updateData(intAddress, Bundle.getMessage("SearchDH"));
247                    // Request Address Status
248                    // requestStatus();
249                    // requestFunctionStatus();
250                    if (_getAll) {
251                        getNextEntry(address);
252                    }
253                    break;
254                case XNetConstants.LOCO_SEARCH_RESPONSE_MU_BASE:
255                    currentStatus.setText(Bundle.getMessage("SearchMUBase"));
256                    adrTextField.setText("" + r.getThrottleMsgAddr());
257                    stackModel.updateData(intAddress, Bundle.getMessage("SearchMUBase"));
258                    // Request Address Status
259                    // requestStatus();
260                    // requestFunctionStatus();
261                    if (_getAll) {
262                        getNextEntry(address);
263                    }
264                    break;
265                case XNetConstants.LOCO_SEARCH_RESPONSE_MU:
266                    currentStatus.setText(Bundle.getMessage("SearchMU"));
267                    adrTextField.setText("" + r.getThrottleMsgAddr());
268                    stackModel.updateData(intAddress, Bundle.getMessage("SearchMU"));
269                    // Request Address Status
270                    // requestStatus();
271                    // requestFunctionStatus();
272                    if (_getAll) {
273                        getNextEntry(address);
274                    }
275                    break;
276                case XNetConstants.LOCO_SEARCH_NO_RESULT:
277                    currentStatus.setText(Bundle.getMessage("SearchFail"));
278                    adrTextField.setText("" + r.getThrottleMsgAddr());
279                    if (_getAll) {
280                        _getAll = false;  //finished getting all entries
281                    }
282                    break;
283                default:
284                    if (log.isDebugEnabled()) {
285                        log.debug("not search result");
286                    }
287            }
288        }
289
290    }
291
292    /**
293     * Receive information sent by the computer to the command station.
294     */
295    @Override
296    public void message(XNetMessage m) {
297    }
298
299    /**
300     * Handle a timeout notification.
301     */
302    @Override
303    public void notifyTimeout(XNetMessage msg) {
304        if (log.isDebugEnabled()) {
305            log.debug("Notified of timeout on message{}", msg.toString());
306        }
307    }
308
309    // Register for logging
310    private static final Logger log = LoggerFactory.getLogger(StackMonFrame.class);
311
312}