001package jmri.jmrix.can.cbus.node; 002 003import java.beans.PropertyChangeListener; 004import java.beans.PropertyChangeEvent; 005import java.util.Arrays; 006 007import jmri.jmrix.can.CanSystemConnectionMemo; 008import jmri.jmrix.can.cbus.swing.modules.CbusConfigPaneProvider; 009import jmri.util.StringUtil; 010import jmri.util.ThreadingUtil; 011 012import org.slf4j.Logger; 013import org.slf4j.LoggerFactory; 014 015/** 016 * Table data model for display of CBUS Node Variables. 017 * 018 * @author Steve Young (c) 2019 019 * 020 */ 021public class CbusNodeNVTableDataModel extends javax.swing.table.AbstractTableModel 022 implements PropertyChangeListener { 023 024 private int[] newNVs; 025 private CbusNode nodeOfInterest; 026 027 // column order needs to match list in column tooltips 028 static public final int NV_NUMBER_COLUMN = 0; 029 static public final int NV_NAME_COLUMN = 1; 030 static public final int NV_CURRENT_VAL_COLUMN = 2; 031 static public final int NV_CURRENT_HEX_COLUMN = 3; 032 static public final int NV_CURRENT_BIT_COLUMN = 4; 033 static public final int NV_SELECT_COLUMN = 5; 034 static public final int NV_SELECT_HEX_COLUMN = 6; 035 static public final int NV_SELECT_BIT_COLUMN = 7; 036 static public final int MAX_COLUMN = 8; 037 038 public CbusNodeNVTableDataModel(CanSystemConnectionMemo memo, int row, int column ) { 039 log.debug("Starting MERG CBUS Node NV Table"); 040 } 041 042 /** {@inheritDoc} */ 043 @Override 044 public void propertyChange(PropertyChangeEvent ev){ 045 if (ev.getPropertyName().equals("SINGLENVUPDATE")) { 046 int newValue = (Integer) ev.getNewValue(); 047// resetNewNvs(); // Why do this for a single NV? 048 log.debug("SINGLENVUPDATE {}", newValue); 049 resetSingleNv(newValue); 050 fireTableRowsUpdated(newValue,newValue); 051 } 052 else if (ev.getPropertyName().equals("ALLNVUPDATE")) { 053 log.debug("ALLNVUPDATE"); 054 resetNewNvs(); 055 fireTableDataChanged(); 056 } 057 } 058 059 /** 060 * Return the number of rows to be displayed. 061 * {@inheritDoc} 062 */ 063 @Override 064 public int getRowCount() { 065 try { 066 return nodeOfInterest.getNodeNvManager().getTotalNVs(); 067 } catch (NullPointerException e) { 068 return 0; 069 } 070 } 071 072 /** 073 * {@inheritDoc} 074 */ 075 @Override 076 public int getColumnCount() { 077 return MAX_COLUMN; 078 } 079 080 /** 081 * Returns String of column name from column int 082 * used in table header 083 * {@inheritDoc} 084 * @param col int col number 085 */ 086 @Override 087 public String getColumnName(int col) { // not in any order 088 switch (col) { 089 case NV_NUMBER_COLUMN: 090 return ("NV"); 091 case NV_NAME_COLUMN: 092 return ("Name"); 093 case NV_CURRENT_VAL_COLUMN: 094 return ("Dec."); 095 case NV_CURRENT_HEX_COLUMN: 096 return ("Hex."); 097 case NV_CURRENT_BIT_COLUMN: 098 return ("Bin."); 099 case NV_SELECT_COLUMN: 100 return ("New Dec."); 101 case NV_SELECT_HEX_COLUMN: 102 return ("New Hex."); 103 case NV_SELECT_BIT_COLUMN: 104 return("New Bin."); 105 default: 106 return "unknown " + col; // NOI18N 107 } 108 } 109 110 /** 111 * Returns column class type. 112 * {@inheritDoc} 113 */ 114 @Override 115 public Class<?> getColumnClass(int col) { 116 switch (col) { 117 case NV_SELECT_HEX_COLUMN: 118 case NV_NAME_COLUMN: 119 case NV_SELECT_BIT_COLUMN: 120 case NV_CURRENT_HEX_COLUMN: 121 case NV_CURRENT_BIT_COLUMN: 122 return String.class; 123 default: 124 return Integer.class; 125 } 126 } 127 128 /** 129 * boolean return to edit table cell or not 130 * {@inheritDoc} 131 * @return boolean 132 */ 133 @Override 134 public boolean isCellEditable(int row, int col) { 135 switch (col) { 136 case NV_SELECT_COLUMN: 137 case NV_SELECT_HEX_COLUMN: 138 case NV_SELECT_BIT_COLUMN: 139 return true; 140 default: 141 return false; 142 } 143 } 144 145 /** 146 * Return table values 147 * {@inheritDoc} 148 * @param row int row number 149 * @param col int col number 150 */ 151 @Override 152 public Object getValueAt(int row, int col) { 153 154 if ( nodeOfInterest.getNodeNvManager().getTotalNVs() < 1 ) { 155 return null; 156 } 157 158 switch (col) { 159 case NV_NUMBER_COLUMN: 160 return (row +1); 161 case NV_NAME_COLUMN: 162 return CbusConfigPaneProvider.getProviderByNode(nodeOfInterest).getNVNameByIndex(row + 1); // NV indices start at 1 163 case NV_CURRENT_VAL_COLUMN: 164 return nodeOfInterest.getNodeNvManager().getNV(row+1); 165 case NV_CURRENT_HEX_COLUMN: 166 if ( nodeOfInterest.getNodeNvManager().getNV(row+1) > -1 ) { 167 return StringUtil.twoHexFromInt(nodeOfInterest.getNodeNvManager().getNV(row+1)); 168 } 169 else { 170 break; 171 } 172 case NV_CURRENT_BIT_COLUMN: 173 int num = nodeOfInterest.getNodeNvManager().getNV(row+1); 174 if ( num > -1 ) { 175 return (String.format("%8s", Integer.toBinaryString(num)).replace(' ', '0')).substring(0,4) + " " + 176 (String.format("%8s", Integer.toBinaryString(num)).replace(' ', '0')).substring(4,8); 177 } 178 else { 179 break; 180 } 181 case NV_SELECT_COLUMN: 182 if ( newNVs.length < row+1) { 183 return 0; 184 } 185 if ( newNVs[(row+1)] > -1 ) { 186 return newNVs[(row+1)]; 187 } else { 188 return nodeOfInterest.getNodeNvManager().getNV(row+1); 189 } 190 case NV_SELECT_HEX_COLUMN: 191 if ( newNVs.length <= row+1) { 192 break; 193 } 194 if (newNVs[(row+1)]>-1) { 195 return StringUtil.twoHexFromInt(newNVs[(row+1)]); 196 } 197 else { 198 break; 199 } 200 case NV_SELECT_BIT_COLUMN: 201 if ( newNVs.length <= row+1) { 202 break; 203 } 204 if (newNVs[(row+1)]>-1) { 205 return (String.format("%8s", Integer.toBinaryString(newNVs[(row+1)])).replace(' ', '0')).substring(0,4) + " " + 206 (String.format("%8s", Integer.toBinaryString(newNVs[(row+1)])).replace(' ', '0')).substring(4,8); 207 } else { 208 break; 209 } 210 default: 211 return null; 212 } 213 return ""; 214 } 215 216 /** 217 * {@inheritDoc} 218 */ 219 @Override 220 public void setValueAt(Object value, int row, int col) { 221 log.debug("set value {} row {} col {}",value,row,col); 222 if ( newNVs.length ==0) { 223 return; 224 } 225 switch (col) { 226 case NV_SELECT_COLUMN: 227 int newval = (int) value; 228 newNVs[(row+1)] = newval; 229 ThreadingUtil.runOnGUIEventually(() -> fireTableRowsUpdated(row,row)); 230 break; 231 case NV_SELECT_HEX_COLUMN: 232 newNVs[(row+1)] = StringUtil.getByte(0, ((String) value).trim()); 233 ThreadingUtil.runOnGUIEventually(() -> fireTableRowsUpdated(row,row)); 234 break; 235 case NV_SELECT_BIT_COLUMN: 236 try { 237 int newInt = Integer.parseInt(((String) value).replaceAll("\\s+",""), 2); 238 if (newInt > -1 && newInt < 256) { 239 newNVs[(row+1)] = newInt; 240 ThreadingUtil.runOnGUIEventually(() -> fireTableRowsUpdated(row,row)); 241 } 242 } 243 catch ( NumberFormatException e ){} 244 break; 245 default: 246 break; 247 } 248 } 249 250 /** 251 * Set the Node to be used in table. 252 * @param node the CbusNode of Interest to the NV Table 253 */ 254 public void setNode( CbusNode node){ 255 log.debug("setting array for node {}",node); 256 257 if ( nodeOfInterest != null ) { 258 nodeOfInterest.removePropertyChangeListener(this); 259 nodeOfInterest.setliveUpdate(false); 260 } 261 262 nodeOfInterest = node; 263 264 if ( nodeOfInterest == null ) { 265 return; 266 } 267 268 resetNewNvs(); 269 nodeOfInterest.addPropertyChangeListener(this); 270 fireTableDataChanged(); 271 272 } 273 274 /** 275 * Get the Node being used in table. 276 * 277 * @return the CbusNode of Interest 278 */ 279 public CbusNode getNode() { 280 return nodeOfInterest; 281 } 282 283 /** 284 * Checks if a single NV has been edited to a new value 285 * @param nvToCheck the single NV to check 286 * @return true if dirty, else false 287 */ 288 public boolean isSingleNvDirty( int nvToCheck ) { 289 return ( (int) getValueAt(nvToCheck,NV_CURRENT_VAL_COLUMN) ) != ( 290 (int) getValueAt(nvToCheck,NV_SELECT_COLUMN) ); 291 } 292 293 /** 294 * Checks if any NV has been edited to a new value 295 * @return true if any NV has been edited, else false 296 */ 297 public boolean isTableDirty() { 298 try { 299 for (int i = 0; i < getRowCount(); i++) { 300 if ( isSingleNvDirty(i) ) { 301 return true; 302 } 303 } 304 return false; 305 } 306 catch ( NullPointerException e ){ 307 return false; 308 } 309 } 310 311 /** 312 * Get count of changed NVs. 313 * @return number of changed NVs 314 */ 315 public int getCountDirty() { 316 int count = 0; 317 for (int i = 0; i < getRowCount(); i++) { 318 if ( isSingleNvDirty(i) ) { 319 count++; 320 } 321 } 322 return count; 323 } 324 325 /** 326 * Resets the edit NV value to match the actual NV value. 327 */ 328 public void resetNewNvs() { 329 330 // setup a new fixed length array to hold new nv values 331 if ( nodeOfInterest.getNodeNvManager().getNvArray() == null ) { 332 log.debug("Create newNVs[] of length 0"); 333 newNVs = new int[0]; 334 } 335 else { 336 log.debug("Create newNVs[] of length {}", nodeOfInterest.getNodeNvManager().getNvArray().length); 337 newNVs = new int[ ( nodeOfInterest.getNodeNvManager().getNvArray().length ) ]; 338 newNVs = Arrays.copyOf( 339 nodeOfInterest.getNodeNvManager().getNvArray(), 340 nodeOfInterest.getNodeNvManager().getNvArray().length); 341 } 342 343 for (int i = 0; i < getRowCount(); i++) { 344 345 setValueAt( getValueAt(i,NV_CURRENT_VAL_COLUMN), i, NV_SELECT_COLUMN); 346 } 347 } 348 349 /** 350 * Resets a single edit NV value to match the actual NV value. 351 * 352 * @param row row to reset 353 */ 354 public void resetSingleNv(int row) { 355 setValueAt( getValueAt(row,NV_CURRENT_VAL_COLUMN), row, NV_SELECT_COLUMN); 356 } 357 358 /** 359 * Get a backup node containing the edited NVs. 360 * @return a node which has the new NV's 361 */ 362 public CbusNodeFromBackup getChangedNode(){ 363 CbusNodeFromBackup temp = new CbusNodeFromBackup(nodeOfInterest,null); 364 temp.getNodeNvManager().setNVs(newNVs); 365 return temp; 366 } 367 368 /** 369 * De-registers the NV Table from receiving updates from the CbusNode. 370 */ 371 public void dispose(){ 372 if ( nodeOfInterest != null ) { 373 nodeOfInterest.removePropertyChangeListener(this); 374 nodeOfInterest.setliveUpdate(false); 375 } 376 } 377 378 private final static Logger log = LoggerFactory.getLogger(CbusNodeNVTableDataModel.class); 379}