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