001package jmri.jmrix.can.cbus.node; 002 003import javax.annotation.Nonnull; 004import org.slf4j.Logger; 005import org.slf4j.LoggerFactory; 006 007/** 008 * Class to manage Node Variables for a CbusNode. 009 * 010 * @author Steve Young Copyright (C) 2019,2020 011 */ 012public class CbusNodeNVManager { 013 private final CbusBasicNodeWithManagers _node; 014 private int[] _nvArray; 015 private int[] newNvsToTeach; 016 private int nextNvInLoop; 017 private boolean TEACH_OUTSTANDING_NVS = false; 018 019 /** 020 * Create a new CbusNodeNVManager 021 * 022 * @param node The Node 023 */ 024 public CbusNodeNVManager ( CbusBasicNodeWithManagers node ){ 025 _node = node; 026 027 } 028 029 /** 030 * Reset this CbusNodeNVManager. 031 * Array is set to null and NV SendError Count 0. 032 */ 033 protected void reset() { 034 _nvArray = null; 035 _node.getNodeTimerManager()._sendNVErrorCount = 0; 036 } 037 038 /** 039 * Get Flag for if any outstanding Teach NV operations are due. 040 * @return true if outstanding teaches, else false. 041 */ 042 protected boolean teachOutstandingNvs() { 043 return TEACH_OUTSTANDING_NVS; 044 } 045 046 /** 047 * Set the Node Variables 048 * <p> 049 * 0th NV is total NVs 050 * so length of newnvs should already be num. of NV's +1 051 * 052 * @param newnvs an int array, the first value being the total number 053 * 054 */ 055 public void setNVs( @Nonnull int[] newnvs ) { 056 057 _nvArray = new int [(newnvs.length)]; // no need to compensate for index 0 being total 058 for (int i = 0; i < newnvs.length; i++) { 059 setNV(i,newnvs[i]); // will notify SINGLENVUPDATE 060 } 061 062 // Now ensure anyone only listening for ALLNVUPDATE sees the change 063 _node.notifyPropertyChangeListener("ALLNVUPDATE", null, null); 064 } 065 066 /** 067 * Set a single Node Variable 068 * <p> 069 * so Index 1 is NV1 .. Index 255 is NV255 070 * Index 0 is set by Node Parameter 071 * 072 * @param index NV Index 073 * @param newnv min 1, max 255 074 * 075 */ 076 public void setNV( int index, int newnv ) { 077 078 if ( _nvArray == null ){ 079 log.error("Attempted to set NV {} on a null NV Array on Node {}",index,_node); 080 return; 081 } 082 if (index == 0 ) { 083 if ( newnv != _node.getNodeParamManager().getParameter(6) 084 && _node.getNodeParamManager().getParameter(6) != -1 085 ){ 086 log.error("Node {} NV Count mismatch. Parameters report {} NVs, received set for {} NVs", 087 _node, _node.getNodeParamManager().getParameter(6), 088 newnv); 089 } 090 } 091 if (index < 0 || index > 255) { // 0 is total 092 log.error("Attempted to set Invalid NV {} on Node {}",index,_node); 093 return; 094 } 095 if (newnv < -1 || newnv > 255) { // -1 is unset 096 log.error("Attempted to set NV {} Invalid Value {} on Node {}",index,newnv,_node); 097 return; 098 } 099 _nvArray[index]=newnv; 100 if (index > 0) { 101 // Ignore index 0 (number of NVs) as this would pass a negative value in the property change 102 _node.notifyPropertyChangeListener("SINGLENVUPDATE",null,( index -1)); 103 } 104 105 } 106 107 /** 108 * Get the Node Variable int Array 109 * <p> 110 * 0th Index is total NVs 111 * so Index 1 is NV1 .. Index 255 is NV255 112 * 113 * @return Array of NV's, first in index is Total NV's 114 */ 115 public int[] getNvArray() { 116 return _nvArray; 117 } 118 119 /** 120 * Number of Node Variables on the node. 121 * @return 0 if number of NV's unknown, else number of NV's. 122 */ 123 public int getTotalNVs() { 124 if ( _nvArray==null){ 125 return 0; 126 } 127 return _nvArray[0]; 128 } 129 130 /** 131 * Get a specific Node Variable 132 * @param index NV Index 133 * @return -1 if NV's unknown, else Node Variable value. 134 * 135 */ 136 public int getNV ( int index ) { 137 if ( getTotalNVs() < 1 ){ 138 return -1; 139 } 140 return _nvArray[index]; 141 } 142 143 /** 144 * Get number of difference between this and another Nodes Node Variables 145 * @param testAgainst The CBUS Node to test against 146 * @return number of different NV's 147 * 148 */ 149 public int getNvDifference(CbusNode testAgainst){ 150 int count = 0; 151 for (int i = 0; i < _nvArray.length; i++) { 152 if (getNV(i) != testAgainst.getNodeNvManager().getNV(i)){ 153 count++; 154 } 155 } 156 return count; 157 } 158 159 /** 160 * Number of unknown Node Variables. 161 * i.e. not yet fetched from a physical node. 162 * @return -1 if number of NV's unknown, 0 if all NV's known, else number of outstanding. 163 * 164 */ 165 public int getOutstandingNvCount(){ 166 int count = 0; 167 if ( _nvArray == null ){ 168 return -1; 169 } 170 for (int i = 0; i < _nvArray.length; i++) { 171 if ( _nvArray[i] < 0 ) { 172 count ++; 173 } 174 } 175 return count; 176 } 177 178 /** 179 * Send a request for the next unknown Node Variable. 180 * <p> 181 * Only triggered from CBUS Node Manager. 182 * <p> 183 * Does NOT send if the node has existing outstanding requests. 184 * Expected response from node NVANS 185 */ 186 protected void sendNextNVToFetch(){ 187 188 if ( _node.getNodeTimerManager().hasActiveTimers() ) { 189 return; 190 } 191 if ( _nvArray == null ) { 192 log.debug("Node {} NV Array unset, unable to fetch NVs", _node); 193 return; 194 } 195 196 for (int i = 0; i < _nvArray.length; i++) { 197 if ( _nvArray[i] < 0 ) { 198 // start NV request timer 199 200 _node.getNodeTimerManager().setNextNvVarTimeout(); 201 _node.send.nVRD( _node.getNodeNumber(), i ); 202 return; 203 } 204 } 205 } 206 207 /** 208 * Send and teach updated Node Variables to this node 209 * 210 * @param newnv array of variables, index 0 i the array is total variables 211 */ 212 public void sendNvsToNode( int[] newnv ) { 213 214 // log.info("start loop to send nv's , nv 1 is {}",newnv[1]); 215 newNvsToTeach = newnv; 216 nextNvInLoop = 1; // start from 1 not 0 as 0 is the total num. nv's 217 TEACH_OUTSTANDING_NVS = true; 218 _node.getNodeTimerManager()._sendNVErrorCount = 0 ; 219 220 // check length of new array 221 // log.info("array size {}",newNvsToTeach.length); 222 223 sendNextNvToNode(); 224 225 } 226 227 /** 228 * Loop for NV teaching 229 */ 230 protected void sendNextNvToNode() { 231 232 if ( _node.getNodeTimerManager().hasActiveTimers() ) { 233 return; 234 } 235 if ( _nvArray == null || newNvsToTeach == null ) { 236 log.warn("Node {} NV Array unset, unable to teach NVs", _node); 237 return; 238 } 239 240 for (int i = nextNvInLoop; i < Math.min(_nvArray.length, newNvsToTeach.length); i++) { 241 if ( newNvsToTeach[i] != _nvArray[i] ) { 242 _node.getNodeTimerManager().setsendEditNvTimeout(); 243 _node.send.nVSET( _node.getNodeNumber() ,i, newNvsToTeach[i] ); 244 nextNvInLoop = i; 245 return; 246 } 247 } 248 249 log.info( "TEACH NV COMPLETE {}", Bundle.getMessage("NdCompleteNVar", String.valueOf(_node.getNodeTimerManager()._sendNVErrorCount) , _node ) ); 250 TEACH_OUTSTANDING_NVS = false; 251 _node.notifyPropertyChangeListener("TEACHNVCOMPLETE", null, _node.getNodeTimerManager()._sendNVErrorCount); 252 253 // refresh all nvs from node if error 254 if ( _node.getNodeTimerManager()._sendNVErrorCount > 0 ){ // user notified in _mainPane 255 256 int [] myarray = new int[(_node.getNodeParamManager().getParameter(6)+1)]; // +1 to account for index 0 being the NV count 257 java.util.Arrays.fill(myarray, -1); 258 myarray[0] = _node.getNodeParamManager().getParameter(6); 259 setNVs(myarray); 260 261 _node.getTableModel().startUrgentFetch(); 262 263 } 264 _node.getNodeTimerManager()._sendNVErrorCount = 0; 265 } 266 267 /** 268 * Get the NV String in Hex Byte Format 269 * <p> 270 * eg. for NV array [3,1,2,255] returns "0102FF" 271 * 272 * @return Full NV String WITHOUT leading number of NVs 273 */ 274 public String getNvHexString() { 275 if ( getNvArray() == null ) { 276 return ""; 277 } else { 278 return jmri.util.StringUtil.hexStringFromInts(getNvArray()).replaceAll("\\s","").substring(2); 279 } 280 } 281 282 /** 283 * @return descriptive string 284 */ 285 @Override 286 public String toString() { 287 return "Node Variables"; 288 } 289 290 private static final Logger log = LoggerFactory.getLogger(CbusNodeNVManager.class); 291 292}