001package jmri.jmrix.ieee802154.swing.nodeconfig;
002
003import java.awt.Container;
004import java.awt.FlowLayout;
005import javax.swing.BorderFactory;
006import javax.swing.BoxLayout;
007import javax.swing.JLabel;
008import javax.swing.JPanel;
009import javax.swing.border.Border;
010import jmri.jmrix.ieee802154.IEEE802154Node;
011import jmri.jmrix.ieee802154.IEEE802154TrafficController;
012import org.slf4j.Logger;
013import org.slf4j.LoggerFactory;
014
015/**
016 * Frame for user configuration of IEEE 802.15.4 nodes. 
017 * Derived from node configuration for c/mri nodes.
018 *
019 * @author Bob Jacobsen Copyright (C) 2004
020 * @author Dave Duchamp Copyright (C) 2004
021 * @author Paul Bender Copyright (C) 2013
022 */
023public class NodeConfigFrame extends jmri.util.JmriJFrame {
024
025    protected javax.swing.JComboBox<String> nodeAddrField = new javax.swing.JComboBox<String>();
026    protected javax.swing.JComboBox<String> nodeAddr64Field = new javax.swing.JComboBox<String>();
027    protected javax.swing.JButton addButton = new javax.swing.JButton(Bundle.getMessage("ButtonAdd"));
028    protected javax.swing.JButton editButton = new javax.swing.JButton(Bundle.getMessage("ButtonEdit"));
029    protected javax.swing.JButton deleteButton = new javax.swing.JButton(Bundle.getMessage("ButtonDelete"));
030    protected javax.swing.JButton doneButton = new javax.swing.JButton(Bundle.getMessage("ButtonDone"));
031    protected javax.swing.JButton updateButton = new javax.swing.JButton(Bundle.getMessage("ButtonUpdate"));
032    protected javax.swing.JButton cancelButton = new javax.swing.JButton(Bundle.getMessage("ButtonCancel"));
033
034    protected javax.swing.JLabel statusText1 = new javax.swing.JLabel();
035    protected javax.swing.JLabel statusText2 = new javax.swing.JLabel();
036    protected javax.swing.JLabel statusText3 = new javax.swing.JLabel();
037
038    protected javax.swing.JPanel panel2 = new JPanel();
039    protected javax.swing.JPanel panel2a = new JPanel();
040
041    protected boolean changedNode = false;  // true if a node was changed, deleted, or added
042    protected boolean editMode = false;     // true if in edit mode
043
044    protected IEEE802154Node curNode = null;    // IEEE802154 Node being editted
045
046    protected boolean errorInStatus1 = false;
047    protected boolean errorInStatus2 = false;
048    protected String stdStatus1 = Bundle.getMessage("NotesStd1");
049    protected String stdStatus2 = Bundle.getMessage("NotesStd2");
050    protected String stdStatus3 = Bundle.getMessage("NotesStd3");
051    protected String editStatus1 = Bundle.getMessage("NotesEdit1");
052    protected String editStatus2 = Bundle.getMessage("NotesEdit2");
053    protected String editStatus3 = Bundle.getMessage("NotesEdit3");
054
055    private IEEE802154TrafficController itc = null;
056
057    /**
058     * Constructor method
059     * @param tc connector for node
060     */
061    public NodeConfigFrame(IEEE802154TrafficController tc) {
062        super();
063        addHelpMenu("package.jmri.jmrix.ieee802154.swing.nodeconfig.NodeConfigFrame", true);
064        itc = tc;
065    }
066
067    /**
068     * Initialize the config window
069     */
070    @Override
071    public void initComponents() {
072        setTitle(Bundle.getMessage("WindowTitle"));
073        Container contentPane = getContentPane();
074        contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
075
076        contentPane.add(initAddressPanel());
077        contentPane.add(initNotesPanel());
078        contentPane.add(initButtonPanel());
079
080
081        // pack for display
082        pack();
083    }
084
085
086    /*
087     * Initilaize the address panel.
088     */
089    protected JPanel initAddressPanel(){
090        // Set up node address and node type
091        JPanel panel1 = new JPanel();
092        panel1.setLayout(new BoxLayout(panel1, BoxLayout.Y_AXIS));
093        JPanel panel11 = new JPanel();
094        panel11.setLayout(new FlowLayout());
095        panel11.add(new JLabel(Bundle.getMessage("LabelNodeAddress") + " "));
096        panel11.add(nodeAddrField);
097        nodeAddrField.addActionListener(new java.awt.event.ActionListener() {
098
099            @Override
100            public void actionPerformed(java.awt.event.ActionEvent e) {
101                nodeSelected();
102            }
103        });
104        nodeAddrField.setToolTipText(Bundle.getMessage("TipNodeAddress"));
105        panel11.add(new JLabel(Bundle.getMessage("LabelNodeAddress64") + " "));
106        panel11.add(nodeAddr64Field);
107        nodeAddr64Field.setToolTipText(Bundle.getMessage("TipNodeAddress64"));
108        nodeAddr64Field.addActionListener(new java.awt.event.ActionListener() {
109
110            @Override
111            public void actionPerformed(java.awt.event.ActionEvent e) {
112                nodeAddrField.setSelectedIndex(nodeAddr64Field.getSelectedIndex());
113            }
114        });
115
116        initAddressBoxes();
117        panel1.add(panel11);
118        return panel1;
119    }
120
121    
122    /*
123     * Initialize the notes panel.
124     */
125    protected JPanel initNotesPanel(){
126        // Set up the notes panel
127        JPanel panel3 = new JPanel();
128        panel3.setLayout(new BoxLayout(panel3, BoxLayout.Y_AXIS));
129        JPanel panel31 = new JPanel();
130        panel31.setLayout(new FlowLayout());
131        statusText1.setText(stdStatus1);
132        statusText1.setVisible(true);
133        panel31.add(statusText1);
134        JPanel panel32 = new JPanel();
135        panel32.setLayout(new FlowLayout());
136        statusText2.setText(stdStatus2);
137        statusText2.setVisible(true);
138        panel32.add(statusText2);
139        JPanel panel33 = new JPanel();
140        panel33.setLayout(new FlowLayout());
141        statusText3.setText(stdStatus3);
142        statusText3.setVisible(true);
143        panel33.add(statusText3);
144        panel3.add(panel31);
145        panel3.add(panel32);
146        panel3.add(panel33);
147        Border panel3Border = BorderFactory.createEtchedBorder();
148        Border panel3Titled = BorderFactory.createTitledBorder(panel3Border,
149                Bundle.getMessage("BoxLabelNotes"));
150        panel3.setBorder(panel3Titled);
151        return panel3;
152    }
153
154    /*
155     * Initialize the Button panel.
156     */
157    protected JPanel initButtonPanel(){
158        // Set up buttons
159        JPanel panel4 = new JPanel();
160        panel4.setLayout(new FlowLayout());
161        addButton.setText(Bundle.getMessage("ButtonAdd"));
162        addButton.setVisible(true);
163        addButton.setToolTipText(Bundle.getMessage("TipAddButton"));
164        addButton.addActionListener(new java.awt.event.ActionListener() {
165            @Override
166            public void actionPerformed(java.awt.event.ActionEvent e) {
167                addButtonActionPerformed();
168            }
169        });
170        panel4.add(addButton);
171        editButton.setText(Bundle.getMessage("ButtonEdit"));
172        editButton.setVisible(true);
173        editButton.setToolTipText(Bundle.getMessage("TipEditButton"));
174        panel4.add(editButton);
175        editButton.addActionListener(new java.awt.event.ActionListener() {
176            @Override
177            public void actionPerformed(java.awt.event.ActionEvent e) {
178                editButtonActionPerformed();
179            }
180        });
181        panel4.add(deleteButton);
182        deleteButton.setText(Bundle.getMessage("ButtonDelete"));
183        deleteButton.setVisible(true);
184        deleteButton.setToolTipText(Bundle.getMessage("TipDeleteButton"));
185        panel4.add(deleteButton);
186        deleteButton.addActionListener(new java.awt.event.ActionListener() {
187            @Override
188            public void actionPerformed(java.awt.event.ActionEvent e) {
189                deleteButtonActionPerformed();
190            }
191        });
192        panel4.add(doneButton);
193        doneButton.setText(Bundle.getMessage("ButtonDone"));
194        doneButton.setVisible(true);
195        doneButton.setToolTipText(Bundle.getMessage("TipDoneButton"));
196        panel4.add(doneButton);
197        doneButton.addActionListener(new java.awt.event.ActionListener() {
198            @Override
199            public void actionPerformed(java.awt.event.ActionEvent e) {
200                doneButtonActionPerformed();
201            }
202        });
203        panel4.add(updateButton);
204        updateButton.setText(Bundle.getMessage("ButtonUpdate"));
205        updateButton.setVisible(true);
206        updateButton.setToolTipText(Bundle.getMessage("TipUpdateButton"));
207        panel4.add(updateButton);
208        updateButton.addActionListener(new java.awt.event.ActionListener() {
209            @Override
210            public void actionPerformed(java.awt.event.ActionEvent e) {
211                updateButtonActionPerformed();
212            }
213        });
214        updateButton.setVisible(false);
215        panel4.add(cancelButton);
216        cancelButton.setText(Bundle.getMessage("ButtonCancel"));
217        cancelButton.setVisible(true);
218        cancelButton.setToolTipText(Bundle.getMessage("TipCancelButton"));
219        panel4.add(cancelButton);
220        cancelButton.addActionListener(new java.awt.event.ActionListener() {
221            @Override
222            public void actionPerformed(java.awt.event.ActionEvent e) {
223                cancelButtonActionPerformed();
224            }
225        });
226        cancelButton.setVisible(false);
227        return panel4;
228    }
229
230    /**
231     * Method to handle add button
232     */
233    public void addButtonActionPerformed() {
234        // create a new Add Frame and display it.
235        jmri.util.JmriJFrame addFrame = new AddNodeFrame(itc);
236        try {
237           addFrame.initComponents();
238        } catch(Exception ex) {
239           log.error("Exception initializing Frame: {}",ex.toString());
240           return;
241        }
242        addFrame.setVisible(true);
243    }
244
245    /**
246     * Method to handle edit button
247     */
248    public void editButtonActionPerformed() {
249        // Find IEEE802154 Node address
250        String nodeAddress = readNodeAddress();
251        if (nodeAddress.equals("")) {
252            return;
253        }
254        // get the IEEE802154Node corresponding to this node address
255        curNode = (IEEE802154Node) itc.getNodeFromAddress(nodeAddress);
256        if (curNode == null) {
257            statusText1.setText(Bundle.getMessage("Error4"));
258            statusText1.setVisible(true);
259            errorInStatus1 = true;
260            resetNotes2();
261            return;
262        }
263
264        // create a new Edit Frame and display it.
265        jmri.util.JmriJFrame editFrame = new EditNodeFrame(itc,curNode);
266        try {
267           editFrame.initComponents();
268        } catch(Exception ex) {
269           log.error("Exception initializing Frame: {}",ex.toString());
270           return;
271        }
272        editFrame.setVisible(true);
273
274    }
275
276    /**
277     * Method to handle delete button
278     */
279    public void deleteButtonActionPerformed() {
280        // Find IEEE802154 Node address
281        String nodeAddress = readNodeAddress();
282        if (nodeAddress.equals("")) {
283            return;
284        }
285        // get the IEEE802154Node corresponding to this node address
286        curNode = (IEEE802154Node) itc.getNodeFromAddress(nodeAddress);
287        if (curNode == null) {
288            statusText1.setText(Bundle.getMessage("Error4"));
289            statusText1.setVisible(true);
290            errorInStatus1 = true;
291            resetNotes2();
292            return;
293        }
294        // confirm deletion with the user
295        if (javax.swing.JOptionPane.OK_OPTION == javax.swing.JOptionPane.showConfirmDialog(
296                this, Bundle.getMessage("ConfirmDelete1") + "\n"
297                + Bundle.getMessage("ConfirmDelete2"), Bundle.getMessage("ConfirmDeleteTitle"),
298                javax.swing.JOptionPane.OK_CANCEL_OPTION,
299                javax.swing.JOptionPane.WARNING_MESSAGE)) {
300            // delete this node
301            itc.deleteNode(nodeAddress);
302            // provide user feedback
303            resetNotes();
304            statusText1.setText(Bundle.getMessage("FeedBackDelete") + " " + nodeAddress);
305            errorInStatus1 = true;
306            changedNode = true;
307        } else {
308            // reset as needed
309            resetNotes();
310        }
311        initAddressBoxes();
312    }
313
314    /**
315     * Method to handle done button
316     */
317    public void doneButtonActionPerformed() {
318        if (editMode) {
319            // Reset 
320            editMode = false;
321            curNode = null;
322            // Switch buttons
323            addButton.setVisible(true);
324            editButton.setVisible(true);
325            deleteButton.setVisible(true);
326            doneButton.setVisible(true);
327            updateButton.setVisible(false);
328            cancelButton.setVisible(false);
329        }
330        if (changedNode) {
331            // Remind user to Save new configuration
332            javax.swing.JOptionPane.showMessageDialog(this,
333                    Bundle.getMessage("Reminder1") + "\n" + Bundle.getMessage("Reminder2"),
334                    Bundle.getMessage("ReminderTitle"),
335                    javax.swing.JOptionPane.INFORMATION_MESSAGE);
336        }
337        setVisible(false);
338        dispose();
339    }
340
341    /**
342     * Method to handle update button
343     */
344    public void updateButtonActionPerformed() {
345        // get node information from window
346
347        // check consistency of node information
348        if (!checkConsistency()) {
349            return;
350        }
351        // update node paramaters
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        // refresh notes panel
365        statusText2.setText(stdStatus2);
366        statusText3.setText(stdStatus3);
367        // provide user feedback
368        statusText1.setText(Bundle.getMessage("FeedBackUpdate") + " " + readNodeAddress());
369        errorInStatus1 = true;
370    }
371
372    /**
373     * Method to handle cancel button
374     */
375    public void cancelButtonActionPerformed() {
376        // Reset 
377        editMode = false;
378        curNode = null;
379        // Switch buttons
380        addButton.setVisible(true);
381        editButton.setVisible(true);
382        deleteButton.setVisible(true);
383        doneButton.setVisible(true);
384        updateButton.setVisible(false);
385        cancelButton.setVisible(false);
386        // refresh notes panel
387        statusText1.setText(stdStatus1);
388        statusText2.setText(stdStatus2);
389        statusText3.setText(stdStatus3);
390    }
391
392    /**
393     * Method to close the window when the close box is clicked
394     */
395    @Override
396    public void windowClosing(java.awt.event.WindowEvent e) {
397        doneButtonActionPerformed();
398        super.windowClosing(e);
399    }
400
401    /**
402     * Method to set node parameters The node must exist, and be in 'curNode'
403     */
404    protected void setNodeParameters() {
405    }
406
407    /**
408     * Method to reset the notes error after error display
409     */
410    private void resetNotes() {
411        if (errorInStatus1) {
412            if (editMode) {
413                statusText1.setText(editStatus1);
414            } else {
415                statusText1.setText(stdStatus1);
416            }
417            errorInStatus1 = false;
418        }
419        resetNotes2();
420    }
421
422    /**
423     * Reset the second line of Notes area
424     */
425    private void resetNotes2() {
426        if (errorInStatus2) {
427            if (editMode) {
428                statusText1.setText(editStatus2);
429            } else {
430                statusText2.setText(stdStatus2);
431            }
432            errorInStatus2 = false;
433        }
434    }
435
436    /**
437     * Read node address from the nodeAddressField or nodeAddr64Field 
438     * as appropriate and return as a string.  
439     *
440     * @return String containing the short (two byte) address of the node.
441     *         if the two byte node address is either "FF FF" or "FF FE",
442     *         returns the long (64 bit) address.
443     */
444    private String readNodeAddress() {
445        String addr = "";
446        addr = (String) nodeAddrField.getSelectedItem();
447        if (addr.equals("FF FF ") || addr.equals("FF FE ")) {
448            addr = (String) nodeAddr64Field.getSelectedItem();
449        }
450        return (addr);
451    }
452
453    /**
454     * Check for consistency errors by node type Returns 'true' if successful,
455     * 'false' if an error was detected. If an error is detected, a suitable
456     * error message is placed in the Notes area
457     * @return always true
458     */
459    protected boolean checkConsistency() {
460        return true;
461    }
462
463    // Initialize the drop down box for the address lists.
464    protected void initAddressBoxes() {
465        IEEE802154Node current = null;
466        nodeAddrField.removeAllItems();
467        nodeAddr64Field.removeAllItems();
468        for (int i = 0; i < itc.getNumNodes(); i++) {
469            current = (IEEE802154Node) itc.getNode(i);
470            nodeAddrField.insertItemAt(jmri.util.StringUtil.hexStringFromBytes(current.getUserAddress()), i);
471            nodeAddr64Field.insertItemAt(jmri.util.StringUtil.hexStringFromBytes(current.getGlobalAddress()), i);
472        }
473        nodeAddrField.insertItemAt("", 0);
474        nodeAddrField.setEditable(true);
475        nodeAddr64Field.insertItemAt("", 0);
476    }
477
478    // Update the display when the selected node changes.
479    protected void nodeSelected() {
480        nodeAddr64Field.setSelectedIndex(nodeAddrField.getSelectedIndex());
481    }
482
483    private final static Logger log = LoggerFactory.getLogger(NodeConfigFrame.class);
484
485}