001package jmri.jmrix.loconet.locoio;
002
003import java.beans.PropertyChangeEvent;
004import javax.swing.JButton;
005import javax.swing.JLabel;
006import javax.swing.JTextField;
007import jmri.jmrix.loconet.LnConstants;
008import org.slf4j.Logger;
009import org.slf4j.LoggerFactory;
010
011/**
012 * Basic Configurer for LocoIO hardware.
013 * Marked Legacy/Deprecated since 2017 version 4.12.
014 * <p>
015 * This code derives the SV values from the user-selected mode and address; this
016 * is different from earlier versions where the user was expected to do the
017 * derivation manually. This derivation is complicated by the fact that the
018 * "mode" SV[port.0] in the LocoIO doesn't fully specify the operation being
019 * done - additional bits in "v2" SV[port.2] are also used. For example, 0x80 is
020 * both turnout closed and turnout high. We read and write the mode SV _last_ to
021 * handle this.
022 * <p>
023 * The "addr" field is constructed from (or causes the construction of,
024 * depending on whether we are reading or writing...) value1 and value2. In
025 * particular, value2 requires knowledge of the mode being set. When "capturing"
026 * a turnout address (where we don't have a mode setting) we presume that the
027 * address seen in the OPC_SW_REQ packet is for a fixed contact, and interpret
028 * the bits in that context.
029 * <p>
030 * The timeout code is modelled after that in jmri.jmrix.AbstractProgrammer,
031 * though there are significant modifications.
032 *
033 * @author Bob Jacobsen Copyright (C) 2001
034 */
035public class LocoIOTableModel
036        extends javax.swing.table.AbstractTableModel
037        implements java.beans.PropertyChangeListener {
038
039    private LocoIOData liodata;
040    private boolean inHex;
041    //private String maxSizeMode = "";
042
043    /**
044     * Define the number of rows in the table, which is also the number of
045     * "channels" in a single LocoIO unit.
046     */
047    private int _numRows = 16;
048
049    /**
050     * Define the contents of the individual columns.
051     */
052    public static final int PINCOLUMN = 0;     // pin number
053    public static final int MODECOLUMN = 1;    // what makes this happen?
054    public static final int ADDRCOLUMN = 2;    // what address is involved?
055    public static final int SV0COLUMN = 3;     //  SV config code
056    public static final int SV1COLUMN = 4;     //  SV Value1
057    public static final int SV2COLUMN = 5;     //  SV Value2
058    public static final int CAPTURECOLUMN = 6; // "capture" button
059    public static final int READCOLUMN = 7;    // "read" button
060    public static final int WRITECOLUMN = 8;   // "write" button
061    public static final int HIGHESTCOLUMN = WRITECOLUMN + 1;
062
063    private final String[] msg = new String[_numRows];
064
065    /**
066     * Reference to the JTextField which should receive status info.
067     */
068    private JTextField status = null;
069
070    /*
071     * Reference to JLabel for firmware version.
072     */
073    //private JLabel     firmware = null;
074    //private JLabel     locobuffer = null;
075    /**
076     * Primary constructor. Initializes all the arrays.
077     * @param ldata the data.
078     */
079    public LocoIOTableModel(LocoIOData ldata) {
080        super();
081
082        inHex = true;
083        // references to external resources
084        liodata = ldata;
085        ldata.addPropertyChangeListener(this);
086    }
087
088    @Override
089    public void propertyChange(PropertyChangeEvent evt) {
090        // String s = "LocoIOTableModel: " + evt.getPropertyName() + " := " + evt.getNewValue() + " from " + evt.getSource();
091        if (evt.getPropertyName().equals("PortChange")) { // NOI18N
092            Integer i = (Integer) evt.getNewValue();
093            int v = i;
094            // log.debug("{} ROW = {}", i, v);
095            fireTableRowsUpdated(v, v);
096        }
097    }
098
099    // basic methods for AbstractTableModel implementation
100    @Override
101    public int getRowCount() {
102        return _numRows;
103    }
104
105    @Override
106    public int getColumnCount() {
107        return HIGHESTCOLUMN;
108    }
109
110    @Override
111    public String getColumnName(int col) {
112        switch (col) {
113            case PINCOLUMN:
114                return Bundle.getMessage("ColumnPort");
115            case MODECOLUMN:
116                return Bundle.getMessage("ColumnAction");
117            case ADDRCOLUMN:
118                return Bundle.getMessage("AddressCol");
119            case SV0COLUMN:
120                return "SV"; // NOI18N
121            case SV1COLUMN:
122                return "Value1"; // NOI18N
123            case SV2COLUMN:
124                return "Value2"; // NOI18N
125            case CAPTURECOLUMN:
126                return "";
127            case READCOLUMN:
128                return "";
129            case WRITECOLUMN:
130                return "";
131            default:
132                return "unknown"; // NOI18N
133        }
134    }
135
136    @Override
137    public Class<?> getColumnClass(int col) {
138        switch (col) {
139            case PINCOLUMN:
140                return String.class;
141            case MODECOLUMN:
142                return String.class;
143            case ADDRCOLUMN:
144                return String.class;
145            case SV0COLUMN:
146                return String.class;
147            case SV1COLUMN:
148                return String.class;
149            case SV2COLUMN:
150                return String.class;
151            case CAPTURECOLUMN:
152                return JButton.class;
153            case READCOLUMN:
154                return JButton.class;
155            case WRITECOLUMN:
156                return JButton.class;
157            default:
158                return null;
159        }
160    }
161
162    @Override
163    public boolean isCellEditable(int row, int col) {
164        switch (col) {
165            case PINCOLUMN:
166                return false;
167            case MODECOLUMN:
168                return true;
169            case ADDRCOLUMN:
170                return true;
171            case SV0COLUMN:
172                return false;
173            case SV1COLUMN:
174                return false;
175            case SV2COLUMN:
176                return false;
177            case CAPTURECOLUMN:
178                return true;
179            case READCOLUMN:
180                return true;
181            case WRITECOLUMN:
182                return true;
183            default:
184                return false;
185        }
186    }
187
188    @Override
189    public Object getValueAt(int row, int col) {
190        switch (col) {
191            case PINCOLUMN:
192                return Integer.toString(row + 1);  // Ports 1 to 16
193            case MODECOLUMN:
194                return liodata.getMode(row);
195            case ADDRCOLUMN:
196                return (liodata.getAddr(row) == 0 ? ("<" + Bundle.getMessage("None").toLowerCase() + ">") : Integer.toString(liodata.getAddr(row)));
197            case SV0COLUMN:
198                return (inHex) ? "0x" + Integer.toHexString(liodata.getSV(row)) : "" + liodata.getSV(row);
199            case SV1COLUMN:
200                return (inHex) ? "0x" + Integer.toHexString(liodata.getV1(row)) : "" + liodata.getV1(row);
201            case SV2COLUMN:
202                return (inHex) ? "0x" + Integer.toHexString(liodata.getV2(row)) : "" + liodata.getV2(row);
203            case CAPTURECOLUMN:
204                return Bundle.getMessage("ButtonCapture");
205            case READCOLUMN:
206                return Bundle.getMessage("ButtonRead");
207            case WRITECOLUMN:
208                return Bundle.getMessage("ButtonWrite");
209            default:
210                return "unknown"; // NOI18N
211        }
212    }
213
214    public int getPreferredWidth(int col) {
215        switch (col) {
216            case PINCOLUMN:
217                return new JLabel(" 16 ").getPreferredSize().width; // NOI18N
218            case MODECOLUMN:
219                return new JLabel("1234567890123456789012345678901234567890").getPreferredSize().width; // NOI18N
220            case ADDRCOLUMN:
221                return new JLabel(getColumnName(ADDRCOLUMN)).getPreferredSize().width;
222            case SV0COLUMN:
223            case SV1COLUMN:
224            case SV2COLUMN:
225                return new JLabel(" 0xFF ").getPreferredSize().width; // NOI18N
226            case CAPTURECOLUMN:
227                return new JButton(Bundle.getMessage("ButtonCapture")).getPreferredSize().width;
228            case READCOLUMN:
229                return new JButton(Bundle.getMessage("ButtonRead")).getPreferredSize().width;
230            case WRITECOLUMN:
231                return new JButton(Bundle.getMessage("ButtonWrite")).getPreferredSize().width;
232            default:
233                return new JLabel(" <unknown> ").getPreferredSize().width;
234        }
235    }
236
237    @Override
238    public void setValueAt(Object value, int row, int col) {
239        if (col == MODECOLUMN) {
240            if (liodata.getLocoIOModeList().isValidModeValue(value)) {
241                liodata.setMode(row, (String) value);
242                liodata.setLIM(row, (String) value);
243                LocoIOMode l = liodata.getLIM(row);
244                if (l != null) {
245                    liodata.setSV(row, l.getSV());
246                    liodata.setV1(row, l, liodata.getAddr(row));
247                    liodata.setV2(row, l, liodata.getAddr(row));
248
249                    msg[row] = "Packet: " + LnConstants.OPC_NAME(l.getOpCode()) + " " // NOI18N
250                            + Integer.toHexString(liodata.getV1(row)) + " "
251                            + Integer.toHexString(liodata.getV2(row)) + " <CHK>"; // NOI18N
252                    if (status != null) {
253                        status.setText(msg[row]);
254                    }
255                    fireTableRowsUpdated(row, row);
256                }
257            }
258        } else if (col == ADDRCOLUMN) {
259            int a;
260            if (((String) (value)).startsWith("0x")) {
261                a = Integer.valueOf(((String) value).substring(2), 16);
262            } else {
263                try {
264                    a = Integer.valueOf((String) value, 10);
265                } catch (NumberFormatException ne) {
266                    log.warn("Enter a hex or decimal number for the Port Address first");
267                    return;
268                }
269            }
270            if (a < 1) {
271                a = 1;
272            }
273            if (a > 0xFFF) {
274                a = 0xFFF;
275            }
276            liodata.setAddr(row, a);
277            // ignore default start-up entry, created in #getValueAt(int, int)
278            if (!(("<" + Bundle.getMessage("None").toLowerCase() + ">").equals(liodata.getMode(row)))) {
279                LocoIOMode l = liodata.getLIM(row);
280                liodata.setV1(row, l, a);
281                liodata.setV2(row, l, a);
282
283                int opcode = (l == null) ? 0 : l.getOpCode();
284                msg[row] = "Packet: " + LnConstants.OPC_NAME(opcode) // NOI18N
285                        + " " + Integer.toHexString(liodata.getV1(row))
286                        + " " + Integer.toHexString(liodata.getV2(row))
287                        + " <CHK>"; // NOI18N
288
289                if (status != null) {
290                    status.setText(msg[row]);
291                }
292            } else {
293                log.warn("Select an option from the Mode drop down first");
294            }
295            fireTableRowsUpdated(row, row);
296        } else if (col == CAPTURECOLUMN) {
297            // start a capture operation
298            liodata.captureValues(row);
299        } else if (col == READCOLUMN) {
300            // start a read operation
301            liodata.readValues(row);
302
303        } else if (col == WRITECOLUMN) {
304            // start a write operation
305            liodata.writeValues(row);
306        }
307    }
308
309    // public static String[] getValidOnModes() { return validmodes.getValidModes(); }
310    public void dispose() {
311        log.debug("dispose"); // NOI18N
312    }
313
314    private final static Logger log = LoggerFactory.getLogger(LocoIOTableModel.class);
315
316}