001package jmri.jmrix.openlcb.swing.send;
002
003import java.awt.event.ActionEvent;
004import java.awt.event.ActionListener;
005import java.awt.BorderLayout;
006import java.awt.Dimension;
007
008import javax.swing.Box;
009import javax.swing.BoxLayout;
010import javax.swing.JButton;
011import javax.swing.JCheckBox;
012import javax.swing.JComboBox;
013import javax.swing.JComponent;
014import javax.swing.JFormattedTextField;
015import javax.swing.JLabel;
016import javax.swing.JPanel;
017import javax.swing.JSeparator;
018import javax.swing.JTextField;
019import javax.swing.JToggleButton;
020
021import jmri.jmrix.can.CanListener;
022import jmri.jmrix.can.CanMessage;
023import jmri.jmrix.can.CanReply;
024import jmri.jmrix.can.CanSystemConnectionMemo;
025import jmri.jmrix.can.TrafficController;
026import jmri.jmrix.can.cbus.CbusAddress;
027import jmri.jmrix.openlcb.swing.ClientActions;
028import jmri.util.StringUtil;
029import jmri.util.javaworld.GridLayout2;
030import jmri.util.swing.WrapLayout;
031
032import org.openlcb.*;
033import org.openlcb.can.AliasMap;
034import org.openlcb.implementations.MemoryConfigurationService;
035import org.openlcb.swing.EventIdTextField;
036import org.openlcb.swing.NodeSelector;
037import org.openlcb.swing.MemorySpaceSelector;
038
039/**
040 * User interface for sending OpenLCB CAN frames to exercise the system
041 * <p>
042 * When sending a sequence of operations:
043 * <ul>
044 * <li>Send the next message and start a timer
045 * <li>When the timer trips, repeat if buttons still down.
046 * </ul>
047 *
048 * @author Bob Jacobsen Copyright (C) 2008, 2012
049 *
050 */
051public class OpenLcbCanSendPane extends jmri.jmrix.can.swing.CanPanel implements CanListener {
052
053    // member declarations
054    final JLabel jLabel1 = new JLabel();
055    final JButton sendButton = new JButton();
056    final JTextField packetTextField = new JTextField(60);
057
058    // internal members to hold sequence widgets
059    static final int MAXSEQUENCE = 4;
060    final JTextField[] mPacketField = new JTextField[MAXSEQUENCE];
061    final JCheckBox[] mUseField = new JCheckBox[MAXSEQUENCE];
062    final JTextField[] mDelayField = new JTextField[MAXSEQUENCE];
063    final JToggleButton mRunButton = new JToggleButton(Bundle.getMessage("ButtonGo"));
064
065    final JTextField srcAliasField = new JTextField(4);
066    NodeSelector nodeSelector;
067    final JFormattedTextField sendEventField = new EventIdTextField();// NOI18N
068    final JTextField datagramContentsField = new JTextField("20 61 00 00 00 00 08");  // NOI18N
069    final JTextField configNumberField = new JTextField("40");                        // NOI18N
070    final JTextField configAddressField = new JTextField("000000");                   // NOI18N
071    final JTextField readDataField = new JTextField(60);
072    final JTextField writeDataField = new JTextField(60);
073    final MemorySpaceSelector addrSpace = new MemorySpaceSelector(0xFF);
074    final JComboBox<String> validitySelector = new JComboBox<String>(new String[]{Bundle.getMessage("ValiditySelectorUnknown"), 
075        Bundle.getMessage("ValiditySelectorValid"), Bundle.getMessage("ValiditySelectorInvalid")});
076    
077    JButton cdiButton;
078    
079    Connection connection;
080    AliasMap aliasMap;
081    NodeID srcNodeID;
082    MemoryConfigurationService mcs;
083    MimicNodeStore store;
084    OlcbInterface iface;
085    ClientActions actions;
086
087    public OpenLcbCanSendPane() {
088        // most of the action is in initComponents
089    }
090
091    @Override
092    public void initComponents(CanSystemConnectionMemo memo) {
093        super.initComponents(memo);
094        iface = memo.get(OlcbInterface.class);
095        actions = new ClientActions(iface, memo);
096        tc = memo.getTrafficController();
097        tc.addCanListener(this);
098        connection = memo.get(org.openlcb.Connection.class);
099        srcNodeID = memo.get(org.openlcb.NodeID.class);
100        aliasMap = memo.get(org.openlcb.can.AliasMap.class);
101
102        // register request for notification
103        Connection.ConnectionListener cl = new Connection.ConnectionListener() {
104            @Override
105            public void connectionActive(Connection c) {
106                log.debug("connection active");
107                // load the alias field
108                srcAliasField.setText(Integer.toHexString(aliasMap.getAlias(srcNodeID)));
109            }
110        };
111        connection.registerStartNotification(cl);
112
113        mcs = memo.get(MemoryConfigurationService.class);
114        store = memo.get(MimicNodeStore.class);
115        nodeSelector = new NodeSelector(store);
116        nodeSelector.addActionListener (new ActionListener () {
117            @Override
118            public void actionPerformed(ActionEvent e) {
119                setCdiButton();
120                jmri.util.ThreadingUtil.runOnGUIDelayed( ()->{ 
121                    setCdiButton(); 
122                }, 500);
123            }
124        });
125
126        // start window layout
127        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
128
129        // handle single-packet part
130        add(getSendSinglePacketJPanel());
131
132        add(new JSeparator());
133
134        // Configure the sequence
135        add(new JLabel(Bundle.getMessage("ConfigureSendSequence")));
136        JPanel pane2 = new JPanel();
137        pane2.setLayout(new GridLayout2(MAXSEQUENCE + 2, 4));
138        pane2.add(new JLabel(""));
139        pane2.add(new JLabel(Bundle.getMessage("ConfigureSend")));
140        pane2.add(new JLabel(Bundle.getMessage("ConfigureSendPacket")));
141        pane2.add(new JLabel(Bundle.getMessage("ConfigureWait")));
142        for (int i = 0; i < MAXSEQUENCE; i++) {
143            pane2.add(new JLabel(Integer.toString(i + 1)));
144            mUseField[i] = new JCheckBox();
145            mPacketField[i] = new JTextField(20);
146            mDelayField[i] = new JTextField(10);
147            pane2.add(mUseField[i]);
148            pane2.add(mPacketField[i]);
149            pane2.add(mDelayField[i]);
150        }
151        add(pane2);
152        add(mRunButton); // below rows
153
154        mRunButton.addActionListener(this::runButtonActionPerformed);
155
156        // special packet forms
157        add(new JSeparator());
158        
159        pane2 = new JPanel();
160        pane2.setLayout(new WrapLayout());
161        add(pane2);
162        pane2.add(new JLabel(Bundle.getMessage("SpecialSendControlFrame")));
163        pane2.add(srcAliasField);
164        JButton b;
165        b = new JButton(Bundle.getMessage("SpecialSendCIM"));
166        b.addActionListener(this::sendCimPerformed);
167        pane2.add(b);
168
169        // send OpenLCB messages
170        add(new JSeparator());
171
172        pane2 = new JPanel();
173        pane2.setLayout(new WrapLayout());
174        add(pane2);
175        pane2.add(new JLabel(Bundle.getMessage("OpenLCBMessagesGlobal")));
176        b = new JButton(Bundle.getMessage("OpenLCBMessagesVerify"));
177        b.addActionListener(this::sendVerifyNodeGlobal);
178        pane2.add(b);
179        b = new JButton(Bundle.getMessage("OpenLCBMessagesNodeID"));
180        b.addActionListener(this::sendVerifyNodeGlobalID);
181        pane2.add(b);
182
183        // event messages 
184        add(new JSeparator());
185        
186        var insert = new JPanel();
187        insert.setLayout(new WrapLayout());
188        insert.add(sendEventField);
189        insert.add(validitySelector);
190        
191        
192        add(addLineLabel(Bundle.getMessage("EventMessagesEventID"), insert));
193        pane2 = new JPanel();
194        pane2.setLayout(new WrapLayout());
195        add(pane2);
196        b = new JButton(Bundle.getMessage("EventMessagesGlobalIdentify"));
197        b.addActionListener(this::sendGlobalIdentifyEvents);
198        pane2.add(b);
199        b = new JButton(Bundle.getMessage("EventMessagesEventProduced"));
200        b.addActionListener(this::sendEventPerformed);
201        pane2.add(b);
202        pane2 = new JPanel();
203        pane2.setLayout(new WrapLayout());
204        add(pane2);
205        b = new JButton(Bundle.getMessage("EventMessagesIdentifyConsumers"));
206        b.addActionListener(this::sendReqConsumers);
207        pane2.add(b);
208        b = new JButton(Bundle.getMessage("EventMessagesConsumerIdentified"));
209        b.addActionListener(this::sendConsumerID);
210        pane2.add(b);
211        b = new JButton(Bundle.getMessage("EventMessagesIdentifyProducers"));
212        b.addActionListener(this::sendReqProducers);
213        pane2.add(b);
214        b = new JButton(Bundle.getMessage("EventMessagesProducerIdentified"));
215        b.addActionListener(this::sendProducerID);
216        pane2.add(b);
217
218        // addressed messages
219        add(new JSeparator());
220        add(addLineLabel(Bundle.getMessage("AddressedMessagesMessageTo"), nodeSelector));
221        pane2 = new JPanel();
222        pane2.setLayout(new WrapLayout());
223        add(pane2);
224        b = new JButton(Bundle.getMessage("AddressedMessagesIdentifyEvents"));
225        b.addActionListener(this::sendRequestEvents);
226        pane2.add(b);
227        b = new JButton(Bundle.getMessage("AddressedMessagesPIPRequest"));
228        b.addActionListener(this::sendRequestPip);
229        pane2.add(b);
230        b = new JButton(Bundle.getMessage("AddressedMessagesSNIPRequest"));
231        b.addActionListener(this::sendRequestSnip);
232        pane2.add(b);
233
234        add(new JSeparator());
235
236        pane2 = new JPanel();
237        pane2.setLayout(new WrapLayout());
238        add(pane2);
239        b = new JButton(Bundle.getMessage("AddressedMessagesDatagram"));
240        b.addActionListener(this::sendDatagramPerformed);
241        pane2.add(b);
242        pane2.add(new JLabel(Bundle.getMessage("AddressedMessagesContents")));
243        datagramContentsField.setColumns(45);
244        pane2.add(datagramContentsField);
245        b = new JButton(Bundle.getMessage("AddressedMessagesPositiveReply"));
246        b.addActionListener(this::sendDatagramReply);
247        pane2.add(b);
248
249        // send OpenLCB Configuration message
250        add(new JSeparator());
251
252        pane2 = new JPanel();
253        pane2.setLayout(new WrapLayout());
254        add(pane2);
255        
256        pane2.add(new JLabel(Bundle.getMessage("ConfigMessagesMemoryRequest")));
257        pane2.add(configAddressField);
258        pane2.add(new JLabel(Bundle.getMessage("ConfigMessagesAddressSpace")));
259        pane2.add(addrSpace);
260        pane2 = new JPanel();
261        pane2.setLayout(new WrapLayout());
262        add(pane2);
263        pane2.add(new JLabel(Bundle.getMessage("ConfigMessagesByteCount")));
264        pane2.add(configNumberField);
265        b = new JButton(Bundle.getMessage("ConfigMessagesRead"));
266        b.addActionListener(this::readPerformed);
267        pane2.add(b);
268        pane2.add(new JLabel(Bundle.getMessage("ConfigMessagesData")));
269        pane2.add(readDataField);
270
271        pane2 = new JPanel();
272        pane2.setLayout(new WrapLayout());
273        add(pane2);
274        b = new JButton(Bundle.getMessage("ConfigMessagesWrite"));
275        b.addActionListener(this::writePerformed);
276        pane2.add(b);
277        pane2.add(new JLabel(Bundle.getMessage("ConfigMessagesData")));
278        writeDataField.setText("00 00");   // NOI18N
279        pane2.add(writeDataField);
280
281        pane2 = new JPanel();
282        pane2.setLayout(new WrapLayout());
283        add(pane2);
284
285        var restartButton = new JButton(Bundle.getMessage("ConfigMessagesRestart"));
286        pane2.add(restartButton);
287        restartButton.addActionListener(this::restartNode);
288        
289        cdiButton = new JButton(Bundle.getMessage("ConfigMessagesOpenCDI"));
290        pane2.add(cdiButton);
291        cdiButton.addActionListener(e -> openCdiPane());
292        cdiButton.setToolTipText(Bundle.getMessage("ConfigMessagesOpenCDItt"));
293        setCdiButton(); // get initial state
294
295        var clearCacheButton = new JButton(Bundle.getMessage("ConfigMessagesClearCDI"));
296        pane2.add(clearCacheButton);
297        clearCacheButton.addActionListener(this::clearCache);
298        clearCacheButton.setToolTipText(Bundle.getMessage("ConfigMessagesClearCDItt"));
299
300        // listen for mimic store changes to set CDI button
301        store.addPropertyChangeListener(e -> {
302            setCdiButton();
303        });
304        jmri.util.ThreadingUtil.runOnGUIDelayed( ()->{ 
305            setCdiButton(); 
306        }, 500);
307    }
308
309    /**
310     * Set whether Open CDI button is enabled based on whether
311     * the selected node has CDI in its PIP
312     */
313    protected void setCdiButton() {
314        var nodeID = nodeSelector.getSelectedNodeID();
315        if (nodeID == null) { 
316            cdiButton.setEnabled(false);
317            log.debug("null nodeID disables cdiButton");
318            return;
319        }
320        var pip = store.getProtocolIdentification(nodeID);
321        if (pip == null || pip.getProtocols() == null) { 
322            cdiButton.setEnabled(false);
323            log.debug("null pip info disables cdiButton");
324            return;
325        }
326        boolean setValue = 
327            pip.getProtocols()
328                .contains(org.openlcb.ProtocolIdentification.Protocol.ConfigurationDescription);
329        cdiButton.setEnabled(setValue);
330        log.debug("cdiButton set {} from PIP info", setValue);
331    }
332    
333    private JPanel getSendSinglePacketJPanel() {
334        JPanel outer = new JPanel();
335        outer.setLayout(new BoxLayout(outer, BoxLayout.X_AXIS));
336        
337        JPanel pane1 = new JPanel();
338        pane1.setLayout(new BoxLayout(pane1, BoxLayout.Y_AXIS));
339
340        jLabel1.setText(Bundle.getMessage("SinglePacketLabel"));
341        jLabel1.setVisible(true);
342
343        sendButton.setText(Bundle.getMessage("SinglePacketSend"));
344        sendButton.setVisible(true);
345        sendButton.setToolTipText(Bundle.getMessage("SinglePacketSendTt"));
346
347        packetTextField.setToolTipText(Bundle.getMessage("SinglePacketText"));
348        packetTextField.setMaximumSize(packetTextField.getPreferredSize());
349
350        pane1.add(jLabel1);
351        pane1.add(packetTextField);
352        pane1.add(sendButton);
353        pane1.add(Box.createVerticalGlue());
354
355        sendButton.addActionListener(this::sendButtonActionPerformed);
356        
357        outer.add(Box.createHorizontalGlue());
358        outer.add(pane1);
359        outer.add(Box.createHorizontalGlue());
360        return outer;
361    }
362
363    @Override
364    public String getHelpTarget() {
365        return "package.jmri.jmrix.openlcb.swing.send.OpenLcbCanSendFrame";  // NOI18N
366    }
367
368    @Override
369    public String getTitle() {
370        if (memo != null) {
371            return (memo.getUserName() + " " + Bundle.getMessage("Title"));
372        }
373        return Bundle.getMessage("Title");
374    }
375
376    JComponent addLineLabel(String text) {
377        return addLineLabel(text, null);
378    }
379
380    JComponent addLineLabel(String text, JComponent c) {
381        JLabel lab = new JLabel(text);
382        JPanel p = new JPanel();
383        p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
384        if (c != null) {
385            p.add(lab, BorderLayout.EAST);
386            if (c instanceof JTextField) {
387                int height = lab.getMinimumSize().height+4;
388                int width = c.getMinimumSize().width;
389                Dimension d = new Dimension(width, height);
390                c.setMaximumSize(d);
391            }
392            p.add(c);
393        } else {
394            p.add(lab, BorderLayout.EAST);
395        }
396        p.add(Box.createHorizontalGlue());
397        return p;
398    }
399
400    public void sendButtonActionPerformed(java.awt.event.ActionEvent e) {
401        String input = packetTextField.getText();
402        // TODO check input + feedback on error. Too easy to cause NPE
403        CanMessage m = createPacket(input);
404        log.debug("sendButtonActionPerformed: {}",m);
405        tc.sendCanMessage(m, this);
406    }
407
408    public void sendCimPerformed(java.awt.event.ActionEvent e) {
409        String data = "[10700" + srcAliasField.getText() + "]";  // NOI18N
410        log.debug("sendCimPerformed: |{}|",data);
411        CanMessage m = createPacket(data);
412        log.debug("sendCimPerformed");
413        tc.sendCanMessage(m, this);
414    }
415
416    NodeID destNodeID() {
417        return nodeSelector.getSelectedNodeID();
418    }
419
420    EventID eventID() {
421        return new EventID(jmri.util.StringUtil.bytesFromHexString(sendEventField.getText()
422                .replace(".", " ")));
423    }
424
425    public void sendVerifyNodeGlobal(java.awt.event.ActionEvent e) {
426        Message m = new VerifyNodeIDNumberGlobalMessage(srcNodeID);
427        connection.put(m, null);
428    }
429
430    public void sendVerifyNodeGlobalID(java.awt.event.ActionEvent e) {
431        Message m = new VerifyNodeIDNumberGlobalMessage(srcNodeID, destNodeID());
432        connection.put(m, null);
433    }
434
435    public void sendRequestEvents(java.awt.event.ActionEvent e) {
436        Message m = new IdentifyEventsAddressedMessage(srcNodeID, destNodeID());
437        connection.put(m, null);
438    }
439
440    public void sendRequestPip(java.awt.event.ActionEvent e) {
441        Message m = new ProtocolIdentificationRequestMessage(srcNodeID, destNodeID());
442        connection.put(m, null);
443    }
444
445    public void sendRequestSnip(java.awt.event.ActionEvent e) {
446        Message m = new SimpleNodeIdentInfoRequestMessage(srcNodeID, destNodeID());
447        connection.put(m, null);
448    }
449
450    public void sendGlobalIdentifyEvents(java.awt.event.ActionEvent e) {
451        Message m = new IdentifyEventsGlobalMessage(srcNodeID);
452        connection.put(m, null);
453    }
454
455    public void sendEventPerformed(java.awt.event.ActionEvent e) {
456        Message m = new ProducerConsumerEventReportMessage(srcNodeID, eventID());
457        connection.put(m, null);
458    }
459
460    public void sendReqConsumers(java.awt.event.ActionEvent e) {
461        Message m = new IdentifyConsumersMessage(srcNodeID, eventID());
462        connection.put(m, null);
463    }
464
465    EventState validity() {
466        switch (validitySelector.getSelectedIndex()) {
467            case 1 : return EventState.Valid;
468            case 2 : return EventState.Invalid;
469            case 0 : 
470            default: return EventState.Unknown;
471        }
472    }
473    
474    public void sendConsumerID(java.awt.event.ActionEvent e) {
475        Message m = new ConsumerIdentifiedMessage(srcNodeID, eventID(), validity());
476        connection.put(m, null);
477    }
478
479    public void sendReqProducers(java.awt.event.ActionEvent e) {
480        Message m = new IdentifyProducersMessage(srcNodeID, eventID());
481        connection.put(m, null);
482    }
483
484    public void sendProducerID(java.awt.event.ActionEvent e) {
485        Message m = new ProducerIdentifiedMessage(srcNodeID, eventID(), validity());
486        connection.put(m, null);
487    }
488
489    public void sendDatagramPerformed(java.awt.event.ActionEvent e) {
490        Message m = new DatagramMessage(srcNodeID, destNodeID(),
491                jmri.util.StringUtil.bytesFromHexString(datagramContentsField.getText()));
492        connection.put(m, null);
493    }
494
495    public void sendDatagramReply(java.awt.event.ActionEvent e) {
496        Message m = new DatagramAcknowledgedMessage(srcNodeID, destNodeID());
497        connection.put(m, null);
498    }
499
500    public void restartNode(java.awt.event.ActionEvent e) {
501        Message m = new DatagramMessage(srcNodeID, destNodeID(),
502                new byte[] {0x20, (byte) 0xA9});
503        connection.put(m, null);        
504    }
505    
506    public void clearCache(java.awt.event.ActionEvent e) {
507        jmri.jmrix.openlcb.swing.DropCdiCache.drop(destNodeID(), memo.get(OlcbInterface.class));
508    }
509    
510    public void readPerformed(java.awt.event.ActionEvent e) {
511        int space = addrSpace.getMemorySpace();
512        long addr = Integer.parseInt(configAddressField.getText(), 16);
513        int length = Integer.parseInt(configNumberField.getText());
514        mcs.requestRead(destNodeID(), space, addr,
515                length, new MemoryConfigurationService.McsReadHandler() {
516                    @Override
517                    public void handleReadData(NodeID dest, int space, long address, byte[] data) {
518                        log.debug("Read data received {} bytes",data.length);
519                        readDataField.setText(jmri.util.StringUtil.hexStringFromBytes(data));
520                    }
521
522                    @Override
523                    public void handleFailure(int errorCode) {
524                        log.warn("OpenLCB read failed: 0x{}", Integer.toHexString
525                                (errorCode));
526                    }
527                });
528    }
529
530    public void writePerformed(java.awt.event.ActionEvent e) {
531        int space = addrSpace.getMemorySpace();
532        long addr = Integer.parseInt(configAddressField.getText(), 16);
533        byte[] content = jmri.util.StringUtil.bytesFromHexString(writeDataField.getText());
534        mcs.requestWrite(destNodeID(), space, addr, content, new MemoryConfigurationService.McsWriteHandler() {
535            @Override
536            public void handleSuccess() {
537                // no action required on success
538            }
539
540            @Override
541            public void handleFailure(int errorCode) {
542                log.warn("OpenLCB write failed:  0x{}", Integer.toHexString
543                        (errorCode));
544            }
545        });
546    }
547
548    public void openCdiPane() {
549        actions.openCdiWindow(destNodeID(), destNodeID().toString());
550    }
551
552    // control sequence operation
553    int mNextSequenceElement = 0;
554    javax.swing.Timer timer = null;
555
556    /**
557     * Internal routine to handle timer starts and restarts
558     * @param delay milliseconds to delay
559     */
560    protected void restartTimer(int delay) {
561        if (timer == null) {
562            timer = new javax.swing.Timer(delay, e -> sendNextItem());
563        }
564        timer.stop();
565        timer.setInitialDelay(delay);
566        timer.setRepeats(false);
567        timer.start();
568    }
569
570    /**
571     * Internal routine to handle a timeout and send next item
572     */
573    protected synchronized void timeout() {
574        sendNextItem();
575    }
576
577    /**
578     * Run button pressed down, start the sequence operation
579     * @param e event from GUI
580     *
581     */
582    public void runButtonActionPerformed(java.awt.event.ActionEvent e) {
583        if (!mRunButton.isSelected()) {
584            return;
585        }
586        // make sure at least one is checked
587        boolean ok = false;
588        for (int i = 0; i < MAXSEQUENCE; i++) {
589            if (mUseField[i].isSelected()) {
590                ok = true;
591            }
592        }
593        if (!ok) {
594            mRunButton.setSelected(false);
595            return;
596        }
597        // start the operation
598        mNextSequenceElement = 0;
599        sendNextItem();
600    }
601
602    /**
603     * Echo has been heard, start delay for next packet
604     */
605    void startSequenceDelay() {
606        // at the start, mNextSequenceElement contains index we're
607        // working on
608        int delay = Integer.parseInt(mDelayField[mNextSequenceElement].getText());
609        // increment to next line at completion
610        mNextSequenceElement++;
611        // start timer
612        restartTimer(delay);
613    }
614
615    /**
616     * Send next item; may be used for the first item or when a delay has
617     * elapsed.
618     */
619    void sendNextItem() {
620        // check if still running
621        if (!mRunButton.isSelected()) {
622            return;
623        }
624        // have we run off the end?
625        if (mNextSequenceElement >= MAXSEQUENCE) {
626            // past the end, go back
627            mNextSequenceElement = 0;
628        }
629        // is this one enabled?
630        if (mUseField[mNextSequenceElement].isSelected()) {
631            // make the packet
632            CanMessage m = createPacket(mPacketField[mNextSequenceElement].getText());
633            // send it
634            tc.sendCanMessage(m, this);
635            startSequenceDelay();
636        } else {
637            // ask for the next one
638            mNextSequenceElement++;
639            sendNextItem();
640        }
641    }
642
643    /**
644     * Create a well-formed message from a String String is expected to be space
645     * seperated hex bytes or CbusAddress, e.g.: 12 34 56 +n4e1
646     * @param s string of spaced hex byte codes
647     * @return The packet, with contents filled-in
648     */
649    CanMessage createPacket(String s) {
650        CanMessage m;
651        // Try to convert using CbusAddress class to reuse a little code
652        CbusAddress a = new CbusAddress(s);
653        if (a.check()) {
654            m = a.makeMessage(tc.getCanid());
655        } else {
656            m = new CanMessage(tc.getCanid());
657            // check for header
658            if (s.charAt(0) == '[') {           // NOI18N
659                // extended header
660                m.setExtended(true);
661                int i = s.indexOf(']');       // NOI18N
662                String h = s.substring(1, i);
663                m.setHeader(Integer.parseInt(h, 16));
664                s = s.substring(i + 1);
665            } else if (s.charAt(0) == '(') {  // NOI18N
666                // standard header
667                int i = s.indexOf(')');       // NOI18N
668                String h = s.substring(1, i);
669                m.setHeader(Integer.parseInt(h, 16));
670                s = s.substring(i + 1);
671            }
672            // Try to get hex bytes
673            byte[] b = StringUtil.bytesFromHexString(s);
674            m.setNumDataElements(b.length);
675            // Use &0xff to ensure signed bytes are stored as unsigned ints
676            for (int i = 0; i < b.length; i++) {
677                m.setElement(i, b[i] & 0xff);
678            }
679        }
680        return m;
681    }
682
683    /**
684     * Don't pay attention to messages
685     */
686    @Override
687    public void message(CanMessage m) {
688        // ignore outgoing messages
689    }
690
691    /**
692     * Don't pay attention to replies
693     */
694    @Override
695    public void reply(CanReply m) {
696        // ignore incoming replies
697    }
698
699    /**
700     * When the window closes, stop any sequences running
701     */
702    @Override
703    public void dispose() {
704        mRunButton.setSelected(false);
705        super.dispose();
706    }
707
708    // private data
709    private TrafficController tc = null; // was CanInterface
710    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(OpenLcbCanSendPane.class);
711
712}