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 192 for (int i = 0; i < _nvArray.length; i++) { 193 if ( _nvArray[i] < 0 ) { 194 // start NV request timer 195 196 _node.getNodeTimerManager().setNextNvVarTimeout(); 197 _node.send.nVRD( _node.getNodeNumber(), i ); 198 return; 199 } 200 } 201 } 202 203 /** 204 * Send and teach updated Node Variables to this node 205 * 206 * @param newnv array of variables, index 0 i the array is total variables 207 */ 208 public void sendNvsToNode( int[] newnv ) { 209 210 // log.info("start loop to send nv's , nv 1 is {}",newnv[1]); 211 newNvsToTeach = newnv; 212 nextNvInLoop = 1; // start from 1 not 0 as 0 is the total num. nv's 213 TEACH_OUTSTANDING_NVS = true; 214 _node.getNodeTimerManager()._sendNVErrorCount = 0 ; 215 216 // check length of new array 217 // log.info("array size {}",newNvsToTeach.length); 218 219 sendNextNvToNode(); 220 221 } 222 223 /** 224 * Loop for NV teaching 225 */ 226 protected void sendNextNvToNode() { 227 228 if ( _node.getNodeTimerManager().hasActiveTimers() ) { 229 return; 230 } 231 232 for (int i = nextNvInLoop; i < _nvArray.length; i++) { 233 if ( newNvsToTeach[i] != _nvArray[i] ) { 234 _node.getNodeTimerManager().setsendEditNvTimeout(); 235 _node.send.nVSET( _node.getNodeNumber() ,i, newNvsToTeach[i] ); 236 nextNvInLoop = i; 237 return; 238 } 239 } 240 241 log.info( "TEACH NV COMPLETE {}", Bundle.getMessage("NdCompleteNVar", String.valueOf(_node.getNodeTimerManager()._sendNVErrorCount) , _node ) ); 242 TEACH_OUTSTANDING_NVS = false; 243 _node.notifyPropertyChangeListener("TEACHNVCOMPLETE", null, _node.getNodeTimerManager()._sendNVErrorCount); 244 245 // refresh all nvs from node if error 246 if ( _node.getNodeTimerManager()._sendNVErrorCount > 0 ){ // user notified in _mainPane 247 248 int [] myarray = new int[(_node.getNodeParamManager().getParameter(6)+1)]; // +1 to account for index 0 being the NV count 249 java.util.Arrays.fill(myarray, -1); 250 myarray[0] = _node.getNodeParamManager().getParameter(6); 251 setNVs(myarray); 252 253 _node.getTableModel().startUrgentFetch(); 254 255 } 256 _node.getNodeTimerManager()._sendNVErrorCount = 0; 257 } 258 259 /** 260 * Get the NV String in Hex Byte Format 261 * <p> 262 * eg. for NV array [3,1,2,255] returns "0102FF" 263 * 264 * @return Full NV String WITHOUT leading number of NVs 265 */ 266 public String getNvHexString() { 267 if ( getNvArray() == null ) { 268 return ""; 269 } else { 270 return jmri.util.StringUtil.hexStringFromInts(getNvArray()).replaceAll("\\s","").substring(2); 271 } 272 } 273 274 /** 275 * @return descriptive string 276 */ 277 @Override 278 public String toString() { 279 return "Node Variables"; 280 } 281 282 private static final Logger log = LoggerFactory.getLogger(CbusNodeNVManager.class); 283 284}