001package jmri.jmrix.can.cbus.node;
002
003import java.io.File;
004import java.util.ArrayList;
005
006import javax.annotation.Nonnull;
007import javax.swing.*;
008
009import jmri.GlobalProgrammerManager;
010import jmri.InstanceManager;
011import jmri.jmrit.decoderdefn.DecoderFile;
012import jmri.jmrit.decoderdefn.DecoderIndexFile;
013import jmri.jmrit.roster.RosterEntry;
014import jmri.jmrit.roster.Roster;
015import jmri.jmrit.symbolicprog.tabbedframe.PaneServiceProgFrame;
016import jmri.jmrix.can.CanSystemConnectionMemo;
017import jmri.jmrix.can.cbus.*;
018import jmri.util.ThreadingUtil;
019import jmri.util.swing.JmriJOptionPane;
020
021/**
022 * Table data model for display of CBUS Nodes
023 *
024 * @author Steve Young (c) 2019
025 * 
026 */
027public class CbusBasicNodeTable extends javax.swing.table.AbstractTableModel {
028
029    protected ArrayList<CbusNode> _mainArray;
030    protected final CanSystemConnectionMemo _memo;
031    protected CbusDccProgrammerManager progMan;
032
033    // column order needs to match list in column tooltips
034    public static final int NODE_NUMBER_COLUMN = 0; 
035    public static final int NODE_TYPE_NAME_COLUMN = 1; 
036    public static final int NODE_USER_NAME_COLUMN = 2;
037    public static final int NODE_RESYNC_BUTTON_COLUMN = 3;
038    public static final int NODE_EDIT_BUTTON_COLUMN = 4;
039    public static final int COMMAND_STAT_NUMBER_COLUMN = 5;
040    public static final int CANID_COLUMN = 6;
041    public static final int NODE_EVENTS_COLUMN = 7;
042    public static final int BYTES_REMAINING_COLUMN = 8;
043    public static final int NODE_TOTAL_BYTES_COLUMN = 9;
044    public static final int NODE_IN_LEARN_MODE_COLUMN = 10;
045    public static final int NODE_EVENT_INDEX_VALID_COLUMN = 11;
046    public static final int SESSION_BACKUP_STATUS_COLUMN = 12;
047    public static final int NUMBER_BACKUPS_COLUMN = 13;
048    public static final int LAST_BACKUP_COLUMN = 14;
049    public static final int MAX_COLUMN = 15;
050
051    public CbusBasicNodeTable(@Nonnull CanSystemConnectionMemo memo, int row, int column) {
052        _mainArray = new ArrayList<>();
053        _memo = memo;
054        var cfgMan = memo.get(CbusConfigurationManager.class);
055        if ( cfgMan != null ) {
056            progMan = cfgMan.get(GlobalProgrammerManager.class);
057            if ( progMan == null ) {
058                log.info("No Global Programmer available for NV programming");
059            }
060        } else {
061            log.info("No CbusConfigurationManager available for NV programming");
062        }
063    }
064
065    /**
066     * {@inheritDoc}
067     */
068    @Override
069    public int getRowCount() {
070        if ( _mainArray == null ) {
071            log.error("Node Table Array _mainArray not initialised");
072            return 0;
073        }
074        else {
075            return _mainArray.size();
076        }
077    }
078
079    /**
080     * {@inheritDoc}
081     */
082    @Override
083    public int getColumnCount() {
084        return MAX_COLUMN;
085    }
086    
087    /**
088     * {@inheritDoc}
089     */
090    @Override
091    public String getColumnName(int col) { // not in any order
092        switch (col) {
093            case CANID_COLUMN:
094                return Bundle.getMessage("CanID");
095            case NODE_NUMBER_COLUMN:
096                return Bundle.getMessage("NodeNumberCol");
097            case NODE_USER_NAME_COLUMN:
098                return Bundle.getMessage("UserName");
099            case NODE_RESYNC_BUTTON_COLUMN:
100                return Bundle.getMessage("ReSynchronizeButton");
101            case NODE_EDIT_BUTTON_COLUMN:
102                return Bundle.getMessage("EditButton");
103            case NODE_TYPE_NAME_COLUMN:
104                return Bundle.getMessage("ColumnType");
105            case COMMAND_STAT_NUMBER_COLUMN:
106                return Bundle.getMessage("CommandStationNumber");
107            case NODE_EVENTS_COLUMN:
108                return Bundle.getMessage("CbusEvents");
109            case NODE_TOTAL_BYTES_COLUMN:
110                return Bundle.getMessage("TotalBytes");
111            case BYTES_REMAINING_COLUMN:
112                return Bundle.getMessage("FetchProgress");
113            case NODE_IN_LEARN_MODE_COLUMN:
114                return Bundle.getMessage("LearnMode");
115            case NODE_EVENT_INDEX_VALID_COLUMN:
116                return Bundle.getMessage("EventIndexValid");
117            case SESSION_BACKUP_STATUS_COLUMN:
118                return("Backup Status");
119            case NUMBER_BACKUPS_COLUMN:
120                return("Num. Backups");
121            case LAST_BACKUP_COLUMN:
122                return("Last Backup");
123            default:
124                return "unknown " + col; // NOI18N
125        }
126    }
127    
128    /**
129     * {@inheritDoc}
130     */
131    @Override
132    public Class<?> getColumnClass(int col) {
133        if (_mainArray.isEmpty() || getValueAt(0, col)==null) {
134            return Object.class;
135        }
136        return getValueAt(0, col).getClass();
137    }
138    
139    /**
140     * {@inheritDoc}
141     * @return UserName and Resync Button columns true, else false
142     */
143    @Override
144    public boolean isCellEditable(int row, int col) {
145        return (col == NODE_USER_NAME_COLUMN || col == NODE_RESYNC_BUTTON_COLUMN
146                || col == NODE_EDIT_BUTTON_COLUMN);
147    }
148
149    /**
150     * {@inheritDoc}
151     */
152    @Override
153    public Object getValueAt(int row, int col) {
154        switch (col) {
155            case NODE_NUMBER_COLUMN:
156                return _mainArray.get(row).getNodeNumber();
157            case NODE_USER_NAME_COLUMN:
158                return _mainArray.get(row).getUserName();
159            case NODE_TYPE_NAME_COLUMN:
160                return _mainArray.get(row).getNodeStats().getNodeTypeName();
161            case CANID_COLUMN:
162                return _mainArray.get(row).getNodeCanId();
163            case COMMAND_STAT_NUMBER_COLUMN:
164                return _mainArray.get(row).getCsNum();
165            case NODE_EVENTS_COLUMN:
166                return _mainArray.get(row).getNodeEventManager().getTotalNodeEvents();
167            case NODE_TOTAL_BYTES_COLUMN:
168                return _mainArray.get(row).getNodeStats().totalNodeBytes();
169            case BYTES_REMAINING_COLUMN:
170                return _mainArray.get(row).getNodeStats().floatPercentageRemaining();
171            case NODE_IN_LEARN_MODE_COLUMN:
172                return _mainArray.get(row).getNodeInLearnMode();
173            case NODE_RESYNC_BUTTON_COLUMN:
174                return ("Re-Sync");
175            case NODE_EDIT_BUTTON_COLUMN:
176                return ("Edit");
177            case NODE_EVENT_INDEX_VALID_COLUMN:
178                return _mainArray.get(row).getNodeEventManager().isEventIndexValid();
179            case SESSION_BACKUP_STATUS_COLUMN:
180                return _mainArray.get(row).getNodeBackupManager().getSessionBackupStatus();
181            case NUMBER_BACKUPS_COLUMN:
182                return _mainArray.get(row).getNodeBackupManager().getNumCompleteBackups();
183            case LAST_BACKUP_COLUMN:
184                return _mainArray.get(row).getNodeBackupManager().getLastBackupTime();
185            default:
186                return null;
187        }
188    }
189    
190    /**
191     * {@inheritDoc}
192     */
193    @Override
194    public void setValueAt(Object value, int row, int col) {
195        switch (col) {
196            case NODE_USER_NAME_COLUMN:
197                _mainArray.get(row).setUserName( (String) value );
198                ThreadingUtil.runOnGUI( () -> fireTableCellUpdated(row, col) );
199                break;
200            case NODE_RESYNC_BUTTON_COLUMN:
201                _mainArray.get(row).saveForResync();
202                _mainArray.get(row).resetNodeAll();
203                if (this instanceof CbusNodeTableDataModel) {
204                    ((CbusNodeTableDataModel)this).setUrgentNode( _mainArray.get(row).getNodeNumber() );
205                    ((CbusNodeTableDataModel)this).startBackgroundFetch();
206                }
207                break;
208            case NODE_EDIT_BUTTON_COLUMN:
209                // Try to load a local xml file
210                String title = _mainArray.get(row).getName() + " (CBUS)";
211                DecoderFile decoderFile = InstanceManager.getDefault(DecoderIndexFile.class).fileFromTitle(title);
212                String userName = _mainArray.get(row).getUserName();
213                String nodeNumber = "CBUS_Node_" + Integer.toString(_mainArray.get(row).getNodeNumber());
214                if (!userName.equals("")) {
215                    nodeNumber = nodeNumber.concat("_" + userName);
216                }
217                if ((decoderFile != null) && (progMan != null)) {
218                    log.debug("decoder file: {}", decoderFile.getFileName()); // NOI18N
219                    // Look for an existing roster entry
220                    RosterEntry re = Roster.getDefault().getEntryForId(nodeNumber);
221                    if (re == null) {
222                        // Or create one
223                        re = new RosterEntry(new RosterEntry(), nodeNumber);
224                        re.setDecoderFamily("CBUS");
225                        re.setMfg(decoderFile.getMfg());
226                        re.setDecoderModel(decoderFile.getModel());
227                        re.setRoadNumber(Integer.toString(_mainArray.get(row).getNodeNumber()));
228                        re.setRoadName(userName);
229                    }
230                    String progTitle = "CBUS NV Programmer";
231                    String progFile = "programmers" + File.separator + "Cbus" + ".xml";
232                    JFrame p = new PaneServiceProgFrame(
233                        decoderFile, re, progTitle, progFile, progMan.getGlobalProgrammer());
234                    p.pack();
235                    p.setVisible(true);
236                } else {
237                    log.info("No xml, or no programmer found for node {}", title);
238                    JmriJOptionPane.showMessageDialog(null,
239                        "<html><h3>No programmer or no decoder file</h3><p>Use Node Variables tab</p></html>",
240                        "No xml, or no programmer for " + title, JmriJOptionPane.INFORMATION_MESSAGE);
241                }
242                break;
243            default:
244                log.debug("invalid column");
245                break;
246        }
247    }
248    
249    /**
250     * String array of Column Tool tips.
251     * Order needs to match column list.
252     */
253    public static final String[] COLUMNTOOLTIPS = {
254        null,
255        null,
256        null,
257        null,
258        "Edit Node Variables",
259        null,
260        null,
261        null,
262        null,
263        null,
264        null,
265        "Index invalid when an event addition or deletion has taken place since last fetch.",
266        "Session Backup Status",
267        "Total backups in xml file",
268        null
269
270    }; // Length = number of items in array should (at least) match number of columns
271
272    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CbusBasicNodeTable.class);
273
274}