001package jmri.jmrix.can.cbus.node; 002 003import static jmri.jmrix.can.cbus.node.CbusNodeConstants.*; 004 005import org.slf4j.Logger; 006import org.slf4j.LoggerFactory; 007 008/** 009 * Class to represent a Processing of CAN Frames for a CbusNode. 010 * 011 * @author Steve Young Copyright (C) 2019,2020 012 */ 013public class CbusNodeParameterManager { 014 private final CbusBasicNodeWithManagers _node; 015 private int[] _parameters; 016 private boolean _commandStationIdentified; 017 private boolean _nodeTraitsSet; 018 019 /** 020 * Create a new CbusNodeCanListener 021 * 022 * @param node The Node 023 */ 024 public CbusNodeParameterManager ( CbusBasicNodeWithManagers node ){ 025 _node = node; 026 _parameters = null; 027 _commandStationIdentified = false; 028 _nodeTraitsSet = false; 029 } 030 031 /** 032 * Set Node Parameters. 033 * <p> 034 * Para 0 Number of parameters 035 * <p> 036 * Para 1 The manufacturer ID 037 * <p> 038 * Para 2 Minor code version as an alphabetic character (ASCII) 039 * <p> 040 * Para 3 Manufacturer module identifier as a HEX numeric 041 * <p> 042 * Para 4 Number of supported events as a HEX numeric 043 * <p> 044 * Para 5 Number of Event Variables per event as a HEX numeric 045 * <p> 046 * Para 6 Number of supported Node Variables as a HEX numeric 047 * <p> 048 * Para 7 Major version 049 * <p> 050 * Para 8 Node flags 051 * <p> 052 * Para 9 Processor type 053 * <p> 054 * Para 10 Bus type 055 * <p> 056 * Para 11-14 load address, 4 bytes 057 * <p> 058 * Para 15-18 CPU manufacturer's id as read from the chip config space, 4 bytes 059 * <p> 060 * Para 19 CPU manufacturer code 061 * <p> 062 * Para 20 Beta revision (numeric), or 0 if release 063 * 064 * @param newparams set the node parameters 065 * 066 */ 067 public void setParameters( int[] newparams ) { 068 069 // log.warn("new params {}",newparams); 070 _parameters = new int [(newparams[0]+1)]; 071 for (int i = 0; i < _parameters.length; i++) { 072 setParameter(i,newparams[i]); 073 } 074 075 if ( getParameter(6) > -1 ) { 076 int [] myarray = new int[(getParameter(6)+1)]; // +1 to account for index 0 being the NV count 077 java.util.Arrays.fill(myarray, -1); 078 myarray[0] = getParameter(6); 079 _node.getNodeNvManager().setNVs(myarray); 080 } 081 } 082 083 /** 084 * Set a Single Node Parameter. 085 * Parameter array should be initialised before calling. 086 * Notifies PropertyChangeListener "PARAMETER" 087 * 088 * @param index Parameter Index, 089 * @param newval New Parameter Value, 0-255 090 */ 091 public void setParameter( int index, int newval ) { 092 if ( _parameters == null ){ 093 log.error("Parameter set before array initiaised"); 094 return; 095 } 096 log.debug("set parameter tot:{} index:{} newval:{}",_parameters.length,index,newval); 097 if ( index <= _parameters.length ) { 098 099 _parameters[index] = newval; 100 _node.notifyPropertyChangeListener("PARAMETER", null, null); 101 } 102 } 103 104 /** 105 * Get Number of outstanding unknown Parameters to be fetched from a CbusNode 106 * 107 * @return Number of outstanding Parameters, else 8 108 */ 109 public int getOutstandingParams(){ 110 111 if (_parameters == null){ 112 return 8; // CBUS Spec minimum 8 parameters, likely value 20 113 } 114 115 int count = 0; 116 for (int i = 1; i < _parameters.length; i++) { 117 if ( _parameters[i] == -1 ) { 118 count++; 119 } 120 } 121 return count; 122 } 123 124 /** 125 * Get a Single Parameter value 126 * <p> 127 * eg. for param. array [3,1,2,3] index 2 returns 2 128 * <p> 129 * Para 0 Number of parameters 130 * <p> 131 * Para 1 The manufacturer ID 132 * <p> 133 * Para 2 Minor code version as an alphabetic character (ASCII) 134 * <p> 135 * Para 3 Manufacturer module identifier as a HEX numeric 136 * <p> 137 * Para 4 Number of supported events as a HEX numeric 138 * <p> 139 * Para 5 Number of Event Variables per event as a HEX numeric 140 * <p> 141 * Para 6 Number of supported Node Variables as a HEX numeric 142 * <p> 143 * Para 7 Major version 144 * <p> 145 * Para 8 Node flags 146 * <p> 147 * Para 9 Processor type 148 * <p> 149 * Para 10 Bus type 150 * <p> 151 * Para 11-14 load address, 4 bytes 152 * <p> 153 * Para 15-18 CPU manufacturer's id as read from the chip config space, 4 bytes 154 * <p> 155 * Para 19 CPU manufacturer code 156 * <p> 157 * Para 20 Beta revision (numeric), or 0 if release 158 * 159 * @param index of which parameter, 0 gives the total parameters 160 * @return Full Parameter value for a particular index, -1 if unknown 161 */ 162 public int getParameter(int index) { 163 if ( _parameters == null ) { 164 return -1; 165 } 166 try { 167 return _parameters[index]; 168 } 169 catch (java.lang.ArrayIndexOutOfBoundsException e) { 170 return -1; 171 } 172 } 173 174 /** 175 * Get array of All parameters 176 * 177 * @return Full Parameter array, index 0 is total parameters 178 */ 179 public int[] getParameters() { 180 return _parameters; 181 } 182 183 /** 184 * Get the Parameter String in Hex Byte Format 185 * <p> 186 * eg. for param. array [3,1,2,3] returns "010203" 187 * 188 * @return Full Parameter String WITHOUT leading number of parameters 189 */ 190 public String getParameterHexString() { 191 if (getParameters()==null) { 192 return ""; 193 } else { 194 return jmri.util.StringUtil.hexStringFromInts(getParameters()).replaceAll("\\s","").substring(2); 195 } 196 } 197 198 protected void clearParameters() { 199 _parameters = null; 200 _nodeTraitsSet = false; 201 } 202 203 /** 204 * Get the Node Type 205 * 206 * @return eg. MERG Command Station CANCMD Firmware 4d Node 65534 207 */ 208 public String getNodeTypeString(){ 209 StringBuilder n = new StringBuilder(100); 210 n.append (CbusNodeConstants.getManu(getParameter(1))) 211 .append (" ") 212 .append( CbusNodeConstants.getModuleTypeExtra(getParameter(1),getParameter(3))) 213 .append(" ") 214 .append( CbusNodeConstants.getModuleType(getParameter(1),getParameter(3))) 215 .append (" ") 216 .append (Bundle.getMessage("FirmwareVer",getParameter(7),Character.toString((char) getParameter(2) ))); 217 if ((getParameter(0)>19) && (getParameter(20)>0) ){ 218 n.append (Bundle.getMessage("FWBeta")) 219 .append (getParameter(20)) 220 .append (" "); 221 } 222 n.append (Bundle.getMessage("CbusNode")) 223 .append (_node.getNodeNumber()); 224 return n.toString(); 225 } 226 227 public void requestEventTot() { 228 if ( _node.getNodeTimerManager().hasActiveTimers() ){ 229 return; 230 } 231 _node.getNodeTimerManager().setNumEvTimeout(); 232 _node.send.rQEVN( _node.getNodeNumber() ); 233 } 234 235 /** 236 * Request a single Parameter from a Physical Node 237 * <p> 238 * Will not send the request if there are existing active timers. 239 * Starts Parameter timeout 240 * 241 * @param param Parameter Index Number, Index 0 is total parameters 242 */ 243 public void requestParam(int param){ 244 if ( _node.getNodeTimerManager().hasActiveTimers() ){ 245 return; 246 } 247 _node.getNodeTimerManager().setAllParamTimeout(param); 248 _node.send.rQNPN( _node.getNodeNumber(), param ); 249 } 250 251 /** 252 * Check if current node firmware is equal to or newer than provided version 253 * 254 * Note that CBUS firmware start with beta builds, followed by a release 255 * with the beta build set to 0, e.g.: 256 * major.minor Beta 1 257 * major.minor Beta 2 258 * ... 259 * major.minor Beta x 260 * major.minor Beta 0 261 * 262 * @param major New FW major version 263 * @param minor New FW minor version 264 * @param beta New FW beta build number 265 * @return true if current node firmware is equal to or newer than provided version 266 */ 267 public boolean isFwEqualOrNewer(int major, int minor, int beta) { 268 if (major > getParameter(MAJOR_VER_IDX)) { 269 return false; 270 } else if (major < getParameter(MAJOR_VER_IDX)) { 271 return true; 272 } else { 273 // Major ver is equal, test minor 274 if (minor > getParameter(MINOR_VER_IDX)) { 275 return false; 276 } else if (minor < getParameter(MINOR_VER_IDX)) { 277 return true; 278 } else { 279 // Major and minor are equal, test beta 280 if (beta == 0) { 281 // Release is always newer than any beta 282 return true; 283 } else if (beta > getParameter(BETA_REV_IDX)) { 284 return false; 285 } else { 286 return true; 287 } 288 } 289 } 290 } 291 292 private boolean sentParamRequest(int paramToCheck){ 293 if ( getParameter(paramToCheck) < 0 ) { 294 requestParam(paramToCheck); 295 return true; 296 } 297 return false; 298 } 299 300 /** 301 * Send a request for the next unknown parameter to the physical node 302 * 303 */ 304 protected void sendRequestNextParam(){ 305 if ( _parameters == null ) { 306 requestParam(0); 307 return; 308 } 309 if ( sentParamRequest(1) // Manufacturer ID 310 || ( sentParamRequest(3) ) // Module ID 311 || ( sentParamRequest(6) ) ) { // initialise NV's 312 return; 313 } 314 315 if ( sentParamRequest(5) // get number event variables per event 316 || ( sentParamRequest(7) ) // get firmware pt1 317 || ( sentParamRequest(2) ) ) { // get firmware pt2 318 return; 319 } 320 321 finishedWhenGotMainParams(); 322 323 for (int i = 1; i < _parameters.length; i++) { 324 if ( sentParamRequest(i) ) { 325 return; 326 } 327 } 328 } 329 330 private void finishedWhenGotMainParams(){ 331 332 if (!( _node instanceof CbusNode)){ 333 return; 334 } 335 336 if (( ( (CbusNode) _node).getCsNum() > -1 ) && ( _commandStationIdentified == false ) ) { 337 // notify command station located 338 log.info("Node type: {}",getNodeTypeString() ); 339 _commandStationIdentified = true; 340 } 341 342 // set node traits, eg CANPAN v1 send wrack on nv set, CANCMD v4 numevents 0 343 // only do this once 344 if (!_nodeTraitsSet ) { 345 CbusNodeConstants.setTraits((CbusNode) _node); 346 _nodeTraitsSet = true; 347 } 348 349 // now traits are known request num. of events 350 if ( _node.getNodeEventManager().getTotalNodeEvents()<0 ){ 351 requestEventTot(); 352 } 353 } 354 355 private static final Logger log = LoggerFactory.getLogger(CbusNodeParameterManager.class); 356 357}