001package jmri.jmrix.secsi.nodeconfig;
002
003import java.awt.Container;
004import java.awt.FlowLayout;
005import javax.swing.BorderFactory;
006import javax.swing.BoxLayout;
007import javax.swing.JComboBox;
008import javax.swing.JLabel;
009import javax.swing.JPanel;
010import javax.swing.border.Border;
011import jmri.jmrix.secsi.SerialNode;
012import jmri.jmrix.secsi.SerialSensorManager;
013import jmri.jmrix.secsi.SecsiSystemConnectionMemo;
014import org.slf4j.Logger;
015import org.slf4j.LoggerFactory;
016
017/**
018 * Frame for user configuration of serial nodes.
019 *
020 * @author Bob Jacobsen Copyright (C) 2004, 2007, 2008
021 * @author Dave Duchamp Copyright (C) 2004, 2006
022 */
023public class NodeConfigFrame extends jmri.util.JmriJFrame {
024
025    private SecsiSystemConnectionMemo memo;
026
027    protected javax.swing.JTextField nodeAddrField = new javax.swing.JTextField(3);
028    protected javax.swing.JLabel nodeAddrStatic = new javax.swing.JLabel("000");
029    protected javax.swing.JComboBox<String> nodeTypeBox;
030
031    protected javax.swing.JButton addButton = new javax.swing.JButton(Bundle.getMessage("ButtonAdd"));
032    protected javax.swing.JButton editButton = new javax.swing.JButton(Bundle.getMessage("ButtonEdit"));
033    protected javax.swing.JButton deleteButton = new javax.swing.JButton(Bundle.getMessage("ButtonDelete"));
034    protected javax.swing.JButton doneButton = new javax.swing.JButton(Bundle.getMessage("ButtonDone"));
035    protected javax.swing.JButton updateButton = new javax.swing.JButton(Bundle.getMessage("ButtonUpdate"));
036    protected javax.swing.JButton cancelButton = new javax.swing.JButton(Bundle.getMessage("ButtonCancel"));
037
038    protected javax.swing.JLabel statusText1 = new javax.swing.JLabel();
039    protected javax.swing.JLabel statusText2 = new javax.swing.JLabel();
040    protected javax.swing.JLabel statusText3 = new javax.swing.JLabel();
041
042    protected boolean changedNode = false;  // true if a node was changed, deleted, or added
043    protected boolean editMode = false;     // true if in edit mode
044    private boolean checkEnabled = jmri.InstanceManager.getDefault(jmri.configurexml.ShutdownPreferences.class).isStoreCheckEnabled();
045
046    protected SerialNode curNode = null;    // Serial Node being editted
047    protected int nodeAddress = 0;          // Node address
048    protected int nodeType = SerialNode.DAUGHTER; // Node type
049
050    protected boolean errorInStatus1 = false;
051    protected boolean errorInStatus2 = false;
052    protected String stdStatus1 = Bundle.getMessage("NotesStd1");
053    protected String stdStatus2 = Bundle.getMessage("NotesStd2");
054    protected String stdStatus3 = Bundle.getMessage("NotesStd3");
055    protected String editStatus1 = Bundle.getMessage("NotesEdit1");
056    protected String editStatus2 = Bundle.getMessage("NotesEdit2");
057    protected String editStatus3 = Bundle.getMessage("NotesEdit3");
058
059    /**
060     * Constructor method
061     * @param _memo system connection.
062     */
063    public NodeConfigFrame(SecsiSystemConnectionMemo _memo) {
064        super();
065        memo = _memo;
066    }
067
068    /**
069     * Initialize the node config window.
070     */
071    @Override
072    public void initComponents() {
073        setTitle(Bundle.getMessage("ConfigNodesTitle"));
074
075        Container contentPane = getContentPane();
076        contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
077
078        // Set up node address and node type
079        JPanel panel1 = new JPanel();
080        panel1.setLayout(new BoxLayout(panel1, BoxLayout.Y_AXIS));
081
082        // panel11 is the node address and type
083        JPanel panel11 = new JPanel();
084        panel11.setLayout(new FlowLayout());
085        panel11.add(new JLabel(Bundle.getMessage("LabelNodeAddress") + " "));
086        panel11.add(nodeAddrField);
087        nodeAddrField.setToolTipText(Bundle.getMessage("TipNodeAddress"));
088        nodeAddrField.setText("0");
089        panel11.add(nodeAddrStatic);
090        nodeAddrStatic.setVisible(false);
091        panel11.add(new JLabel("   " + Bundle.getMessage("LabelNodeType") + " "));
092        nodeTypeBox = new JComboBox<String>(SerialNode.getBoardNames());
093        panel11.add(nodeTypeBox);
094        nodeTypeBox.setToolTipText(Bundle.getMessage("TipNodeType"));
095        contentPane.add(panel11);
096
097        // Set up the notes panel
098        JPanel panel3 = new JPanel();
099        panel3.setLayout(new BoxLayout(panel3, BoxLayout.Y_AXIS));
100        JPanel panel31 = new JPanel();
101        panel31.setLayout(new FlowLayout());
102        statusText1.setText(stdStatus1);
103        statusText1.setVisible(true);
104        panel31.add(statusText1);
105        JPanel panel32 = new JPanel();
106        panel32.setLayout(new FlowLayout());
107        statusText2.setText(stdStatus2);
108        statusText2.setVisible(true);
109        panel32.add(statusText2);
110        JPanel panel33 = new JPanel();
111        panel33.setLayout(new FlowLayout());
112        statusText3.setText(stdStatus3);
113        statusText3.setVisible(true);
114        panel33.add(statusText3);
115        panel3.add(panel31);
116        panel3.add(panel32);
117        panel3.add(panel33);
118        Border panel3Border = BorderFactory.createEtchedBorder();
119        Border panel3Titled = BorderFactory.createTitledBorder(panel3Border,
120                Bundle.getMessage("BoxLabelNotes"));
121        panel3.setBorder(panel3Titled);
122        contentPane.add(panel3);
123
124        // Set up buttons
125        JPanel panel4 = new JPanel();
126        panel4.setLayout(new FlowLayout());
127        addButton.setText(Bundle.getMessage("ButtonAdd"));
128        addButton.setVisible(true);
129        addButton.setToolTipText(Bundle.getMessage("TipAddButton"));
130        addButton.addActionListener(new java.awt.event.ActionListener() {
131            @Override
132            public void actionPerformed(java.awt.event.ActionEvent e) {
133                addButtonActionPerformed();
134            }
135        });
136        panel4.add(addButton);
137        editButton.setText(Bundle.getMessage("ButtonEdit"));
138        editButton.setVisible(true);
139        editButton.setToolTipText(Bundle.getMessage("TipEditButton"));
140        panel4.add(editButton);
141        editButton.addActionListener(new java.awt.event.ActionListener() {
142            @Override
143            public void actionPerformed(java.awt.event.ActionEvent e) {
144                editButtonActionPerformed();
145            }
146        });
147        panel4.add(deleteButton);
148        deleteButton.setText(Bundle.getMessage("ButtonDelete"));
149        deleteButton.setVisible(true);
150        deleteButton.setToolTipText(Bundle.getMessage("TipDeleteButton"));
151        panel4.add(deleteButton);
152        deleteButton.addActionListener(new java.awt.event.ActionListener() {
153            @Override
154            public void actionPerformed(java.awt.event.ActionEvent e) {
155                deleteButtonActionPerformed();
156            }
157        });
158        panel4.add(doneButton);
159        doneButton.setText(Bundle.getMessage("ButtonDone"));
160        doneButton.setVisible(true);
161        doneButton.setToolTipText(Bundle.getMessage("TipDoneButton"));
162        panel4.add(doneButton);
163        doneButton.addActionListener(new java.awt.event.ActionListener() {
164            @Override
165            public void actionPerformed(java.awt.event.ActionEvent e) {
166                doneButtonActionPerformed();
167            }
168        });
169        panel4.add(updateButton);
170        updateButton.setText(Bundle.getMessage("ButtonUpdate"));
171        updateButton.setVisible(true);
172        updateButton.setToolTipText(Bundle.getMessage("TipUpdateButton"));
173        panel4.add(updateButton);
174        updateButton.addActionListener(new java.awt.event.ActionListener() {
175            @Override
176            public void actionPerformed(java.awt.event.ActionEvent e) {
177                updateButtonActionPerformed();
178            }
179        });
180        updateButton.setVisible(false);
181        panel4.add(cancelButton);
182        cancelButton.setText(Bundle.getMessage("ButtonCancel"));
183        cancelButton.setVisible(true);
184        cancelButton.setToolTipText(Bundle.getMessage("TipCancelButton"));
185        panel4.add(cancelButton);
186        cancelButton.addActionListener(new java.awt.event.ActionListener() {
187            @Override
188            public void actionPerformed(java.awt.event.ActionEvent e) {
189                cancelButtonActionPerformed();
190            }
191        });
192        cancelButton.setVisible(false);
193        contentPane.add(panel4);
194
195        // pack for display
196        pack();
197    }
198
199    /**
200     * Handle Add button.
201     */
202    public void addButtonActionPerformed() {
203        // Check that a node with this address does not exist
204        int nodeAddress = readNodeAddress();
205        if (nodeAddress < 0) {
206            return;
207        }
208        // get a SerialNode corresponding to this node address if one exists
209        curNode = (SerialNode) memo.getTrafficController().getNodeFromAddress(nodeAddress);
210        if (curNode != null) {
211            log.debug("Asked for new node address {}", Integer.toString(nodeAddress));
212            statusText1.setText(Bundle.getMessage("Error1", Integer.toString(nodeAddress)));
213            statusText1.setVisible(true);
214            errorInStatus1 = true;
215            resetNotes2();
216            return;
217        }
218        nodeType = nodeTypeBox.getSelectedIndex();
219
220        // all ready, create the new node
221        curNode = new SerialNode(nodeAddress, nodeType, memo.getTrafficController());
222        // configure the new node
223        setNodeParameters();
224        // register any orphan sensors that this node may have
225        ((SerialSensorManager)memo.getSensorManager()).registerSensorsForNode(curNode);
226        // reset after succefully adding node
227        resetNotes();
228        changedNode = true;
229        // provide user feedback
230        statusText1.setText(Bundle.getMessage("FeedBackAdd") + " "
231                + Integer.toString(nodeAddress));
232        errorInStatus1 = true;
233    }
234
235    /**
236     * Handle Edit button.
237     */
238    public void editButtonActionPerformed() {
239        // Find Serial Node address
240        nodeAddress = readNodeAddress();
241        if (nodeAddress < 0) {
242            return;
243        }
244        // get the SerialNode corresponding to this node address
245        curNode = (SerialNode) memo.getTrafficController().getNodeFromAddress(nodeAddress);
246        if (curNode == null) {
247            statusText1.setText(Bundle.getMessage("Error4"));
248            statusText1.setVisible(true);
249            errorInStatus1 = true;
250            resetNotes2();
251            return;
252        }
253        // Set up static node address
254        nodeAddrStatic.setText(Integer.toString(nodeAddress));
255        nodeAddrField.setVisible(false);
256        nodeAddrStatic.setVisible(true);
257        // get information for this node and set up combo box
258        nodeType = curNode.getNodeType();
259        nodeTypeBox.setSelectedIndex(nodeType);
260        // Switch buttons
261        editMode = true;
262        addButton.setVisible(false);
263        editButton.setVisible(false);
264        deleteButton.setVisible(false);
265        doneButton.setVisible(false);
266        updateButton.setVisible(true);
267        cancelButton.setVisible(true);
268        // Switch to edit notes
269        statusText1.setText(editStatus1);
270        statusText2.setText(editStatus2);
271        statusText3.setText(editStatus3);
272    }
273
274    /**
275     * Handle Delete button.
276     */
277    public void deleteButtonActionPerformed() {
278        // Find Serial Node address
279        int nodeAddress = readNodeAddress();
280        if (nodeAddress < 0) {
281            return;
282        }
283        // get the SerialNode corresponding to this node address
284        curNode = (SerialNode) memo.getTrafficController().getNodeFromAddress(nodeAddress);
285        if (curNode == null) {
286            statusText1.setText(Bundle.getMessage("Error4"));
287            statusText1.setVisible(true);
288            errorInStatus1 = true;
289            resetNotes2();
290            return;
291        }
292        // confirm deletion with the user
293        if (javax.swing.JOptionPane.OK_OPTION == javax.swing.JOptionPane.showConfirmDialog(
294                this, Bundle.getMessage("ConfirmDelete1") + "\n"
295                + Bundle.getMessage("ConfirmDelete2"), Bundle.getMessage("ConfirmDeleteTitle"),
296                javax.swing.JOptionPane.OK_CANCEL_OPTION,
297                javax.swing.JOptionPane.WARNING_MESSAGE)) {
298            // delete this node
299            memo.getTrafficController().deleteNode(nodeAddress);
300            // provide user feedback
301            resetNotes();
302            statusText1.setText(Bundle.getMessage("FeedBackDelete") + " "
303                    + Integer.toString(nodeAddress));
304            errorInStatus1 = true;
305            changedNode = true;
306        } else {
307            // reset as needed
308            resetNotes();
309        }
310    }
311
312    /**
313     * Handle Done button.
314     */
315    public void doneButtonActionPerformed() {
316        if (editMode) {
317            // Reset
318            editMode = false;
319            curNode = null;
320            // Switch buttons
321            addButton.setVisible(true);
322            editButton.setVisible(true);
323            deleteButton.setVisible(true);
324            doneButton.setVisible(true);
325            updateButton.setVisible(false);
326            cancelButton.setVisible(false);
327            nodeAddrField.setVisible(true);
328            nodeAddrStatic.setVisible(false);
329        }
330        if (changedNode && !checkEnabled) {
331            // Remind user to Save new configuration
332            javax.swing.JOptionPane.showMessageDialog(this,
333                    Bundle.getMessage("ReminderNode1") + "\n" + Bundle.getMessage("Reminder2"),
334                    Bundle.getMessage("ReminderTitle"),
335                    javax.swing.JOptionPane.INFORMATION_MESSAGE);
336        }
337        setVisible(false);
338        dispose();
339    }
340
341    /**
342     * Handle Update button.
343     */
344    public void updateButtonActionPerformed() {
345        // update node information
346        nodeType = nodeTypeBox.getSelectedIndex();
347        log.debug("update performed: was {} request {}", curNode.getNodeType(), nodeType);
348        if (curNode.getNodeType() != nodeType) {
349            // node type has changed
350            curNode.setNodeType(nodeType);
351        }
352        setNodeParameters();
353        changedNode = true;
354        // Reset Edit Mode
355        editMode = false;
356        curNode = null;
357        // Switch buttons
358        addButton.setVisible(true);
359        editButton.setVisible(true);
360        deleteButton.setVisible(true);
361        doneButton.setVisible(true);
362        updateButton.setVisible(false);
363        cancelButton.setVisible(false);
364        // make node address editable again
365        nodeAddrField.setVisible(true);
366        nodeAddrStatic.setVisible(false);
367        // refresh notes panel
368        statusText2.setText(stdStatus2);
369        statusText3.setText(stdStatus3);
370        // provide user feedback
371        statusText1.setText(Bundle.getMessage("FeedBackUpdate") + " "
372                + Integer.toString(nodeAddress));
373        errorInStatus1 = true;
374    }
375
376    /**
377     * Handle Cancel button.
378     */
379    public void cancelButtonActionPerformed() {
380        // Reset
381        editMode = false;
382        curNode = null;
383        // Switch buttons
384        addButton.setVisible(true);
385        editButton.setVisible(true);
386        deleteButton.setVisible(true);
387        doneButton.setVisible(true);
388        updateButton.setVisible(false);
389        cancelButton.setVisible(false);
390        // make node address editable again
391        nodeAddrField.setVisible(true);
392        nodeAddrStatic.setVisible(false);
393        // refresh notes panel
394        statusText1.setText(stdStatus1);
395        statusText2.setText(stdStatus2);
396        statusText3.setText(stdStatus3);
397    }
398
399    /**
400     * Do the done action if the window is closed early.
401     */
402    @Override
403    public void windowClosing(java.awt.event.WindowEvent e) {
404        doneButtonActionPerformed();
405    }
406
407    /**
408     * Method to set node parameters The node must exist, and be in 'curNode'
409     * Also, the node type must be set and in 'nodeType'
410     */
411    void setNodeParameters() {
412        // set curNode type
413        curNode.setNodeType(nodeType);
414        // Cause reinitialization of this Node to reflect these parameters
415        memo.getTrafficController().initializeSerialNode(curNode);
416    }
417
418    /**
419     * Method to reset the notes error after error display
420     */
421    private void resetNotes() {
422        if (errorInStatus1) {
423            if (editMode) {
424                statusText1.setText(editStatus1);
425            } else {
426                statusText1.setText(stdStatus1);
427            }
428            errorInStatus1 = false;
429        }
430        resetNotes2();
431    }
432
433    /**
434     * Reset the second line of Notes area
435     */
436    private void resetNotes2() {
437        if (errorInStatus2) {
438            if (editMode) {
439                statusText1.setText(editStatus2);
440            } else {
441                statusText2.setText(stdStatus2);
442            }
443            errorInStatus2 = false;
444        }
445    }
446
447    /**
448     * Read node address and check for legal range If successful, a node address
449     * in the range 0-255 is returned. If not successful, -1 is returned and an
450     * appropriate error message is placed in statusText1.
451     */
452    private int readNodeAddress() {
453        int addr = -1;
454        try {
455            addr = Integer.parseInt(nodeAddrField.getText());
456        } catch (Exception e) {
457            statusText1.setText(Bundle.getMessage("Error5"));
458            statusText1.setVisible(true);
459            errorInStatus1 = true;
460            resetNotes2();
461            return -1;
462        }
463        if ((addr < 0) || (addr > 255)) {
464            statusText1.setText(Bundle.getMessage("Error6"));
465            statusText1.setVisible(true);
466            errorInStatus1 = true;
467            resetNotes2();
468            return -1;
469        }
470        return (addr);
471    }
472
473    private final static Logger log = LoggerFactory.getLogger(NodeConfigFrame.class);
474
475}