001package jmri.jmrix.openlcb.swing.idtool;
002
003import java.awt.event.*;
004import java.io.*;
005
006import javax.swing.*;
007import jmri.jmrix.can.CanSystemConnectionMemo;
008import jmri.util.swing.WrapLayout;
009
010import org.openlcb.*;
011import org.openlcb.implementations.*;
012import org.openlcb.swing.*;
013
014
015/**
016 * Pane for identifying a physical node by
017 * doing memory operations (hence lighting its activity lights)
018 * until cancelled.
019 *
020 * @author Bob Jacobsen Copyright (C) 2024
021 * @since 5.7.4
022 */
023public class IdToolPane extends jmri.util.swing.JmriPanel
024        implements jmri.jmrix.can.swing.CanPanelInterface {
025
026    protected CanSystemConnectionMemo memo;
027    Connection connection;
028    NodeID nid;
029
030    MimicNodeStore store;
031    MemoryConfigurationService service;
032    NodeSelector nodeSelector;
033
034    public String getTitle(String menuTitle) {
035        return Bundle.getMessage("TitleIdTool");
036    }
037
038    static final int CHUNKSIZE = 64;
039
040    JButton gb;
041    JButton cb;
042    boolean cancelled = false;
043    boolean running = false;
044
045    @Override
046    public void initComponents(CanSystemConnectionMemo memo) {
047        this.memo = memo;
048        this.connection = memo.get(Connection.class);
049        this.nid = memo.get(NodeID.class);
050
051        store = memo.get(MimicNodeStore.class);
052        EventTable stdEventTable = memo.get(OlcbInterface.class).getEventTable();
053        if (stdEventTable == null) {
054            log.error("no OLCB EventTable found");
055            return;
056        }
057        service = memo.get(MemoryConfigurationService.class);
058
059        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
060
061        // Add to GUI here
062        var ns = new JPanel();
063        ns.setLayout(new WrapLayout());
064        add(ns);
065        nodeSelector = new org.openlcb.swing.NodeSelector(store, Integer.MAX_VALUE);
066        ns.add(nodeSelector);
067
068        var bb = new JPanel();
069        bb.setLayout(new WrapLayout());
070        add(bb);
071
072        gb = new JButton(Bundle.getMessage("ButtonId"));
073        bb.add(gb);
074        gb.addActionListener(this::pushedGetButton);
075
076        cb = new JButton(Bundle.getMessage("ButtonCancel"));
077        bb.add(cb);
078        cb.addActionListener(this::pushedCancel);
079
080        setRunning(false);
081    }
082
083    public IdToolPane() {
084    }
085
086    @Override
087    public void dispose() {
088        // and complete this
089        super.dispose();
090    }
091
092    @Override
093    public String getHelpTarget() {
094        return "package.jmri.jmrix.openlcb.swing.idtool.IdToolPane";
095    }
096
097    @Override
098    public String getTitle() {
099        if (memo != null) {
100            return (memo.getUserName() + " Ident Tool");
101        }
102        return getTitle(Bundle.getMessage("TitleIdTool"));
103    }
104
105    void pushedCancel(ActionEvent e) {
106        if (running) {
107            cancelled = true;
108        }
109    }
110
111    void setRunning(boolean t) {
112        if (t) {
113            gb.setEnabled(false);
114            cb.setEnabled(true);
115        } else {
116            gb.setEnabled(true);
117            cb.setEnabled(false);
118        }
119        running = t;
120    }
121
122    int space = 0xFF;
123
124    NodeID farID = new NodeID("0.0.0.0.0.0");
125
126    static final int PERIOD = 250;  // delay in milliseconds 
127    
128    MemoryConfigurationService.McsReadHandler cbr =
129        new MemoryConfigurationService.McsReadHandler() {
130            @Override
131            public void handleFailure(int errorCode) {
132                setRunning(false);
133                if (errorCode == 0x1082) {
134                    log.debug("Stopping read due to 0x1082 status");
135                } if (errorCode == 0x1081) {
136                    log.error("Read failed. Address space not known");
137                } else {
138                    log.error("Read failed. Error code is {}", String.format("%04X", errorCode));
139                }
140            }
141
142            @Override
143            public void handleReadData(NodeID dest, int readSpace, long readAddress, byte[] readData) {
144                log.trace("read succeed with {} bytes at {}", readData.length, readAddress);
145                // fire another from same address
146                if (!cancelled) {
147                    // send after a delay
148                    jmri.util.ThreadingUtil.runOnLayoutDelayed(() -> {
149                        service.requestRead(farID, space, 0,
150                                        CHUNKSIZE,
151                                        cbr);
152                    }, PERIOD);
153                } else {
154                    setRunning(false);
155                    cancelled = false;
156                    log.debug("Complete");
157                }
158            }
159        };
160
161
162    /**
163     * Starts reading from node and writing to file process
164     * @param e not used
165     */
166    void pushedGetButton(ActionEvent e) {
167        setRunning(true);
168        farID = nodeSelector.getSelectedNodeID();
169        service.requestRead(farID, space, 0, CHUNKSIZE, cbr);  // assume starting address is zero
170    }
171
172    byte[] bytes = new byte[CHUNKSIZE];
173    int bytesRead;          // Number bytes read into the bytes[] array from the file. Used for put operation only.
174    InputStream inputStream;
175    int address;
176
177    /**
178     * Nested class to create one of these using old-style defaults
179     */
180    public static class Default extends jmri.jmrix.can.swing.CanNamedPaneAction {
181
182        public Default() {
183            super("Openlcb Ident Tool",
184                    new jmri.util.swing.sdi.JmriJFrameInterface(),
185                    IdToolPane.class.getName(),
186                    jmri.InstanceManager.getDefault(jmri.jmrix.can.CanSystemConnectionMemo.class));
187        }
188    }
189
190    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(IdToolPane.class);
191}