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 >= 0 && index < _parameters.length ) { 098 099 _parameters[index] = newval; 100 _node.notifyPropertyChangeListener("PARAMETER", null, null); 101 } else { 102 log.debug("Node {} parameter index {} outside of {} parameters, not set", _node, index, _parameters.length - 1 ); 103 } 104 } 105 106 /** 107 * Get Number of outstanding unknown Parameters to be fetched from a CbusNode 108 * 109 * @return Number of outstanding Parameters, else 8 110 */ 111 public int getOutstandingParams(){ 112 113 if (_parameters == null){ 114 return 8; // CBUS Spec minimum 8 parameters, likely value 20 115 } 116 117 int count = 0; 118 for (int i = 1; i < _parameters.length; i++) { 119 if ( _parameters[i] == -1 ) { 120 count++; 121 } 122 } 123 return count; 124 } 125 126 /** 127 * Get a Single Parameter value 128 * <p> 129 * eg. for param. array [3,1,2,3] index 2 returns 2 130 * <p> 131 * Para 0 Number of parameters 132 * <p> 133 * Para 1 The manufacturer ID 134 * <p> 135 * Para 2 Minor code version as an alphabetic character (ASCII) 136 * <p> 137 * Para 3 Manufacturer module identifier as a HEX numeric 138 * <p> 139 * Para 4 Number of supported events as a HEX numeric 140 * <p> 141 * Para 5 Number of Event Variables per event as a HEX numeric 142 * <p> 143 * Para 6 Number of supported Node Variables as a HEX numeric 144 * <p> 145 * Para 7 Major version 146 * <p> 147 * Para 8 Node flags 148 * <p> 149 * Para 9 Processor type 150 * <p> 151 * Para 10 Bus type 152 * <p> 153 * Para 11-14 load address, 4 bytes 154 * <p> 155 * Para 15-18 CPU manufacturer's id as read from the chip config space, 4 bytes 156 * <p> 157 * Para 19 CPU manufacturer code 158 * <p> 159 * Para 20 Beta revision (numeric), or 0 if release 160 * 161 * @param index of which parameter, 0 gives the total parameters 162 * @return Full Parameter value for a particular index, -1 if unknown 163 */ 164 public int getParameter(int index) { 165 if ( _parameters == null ) { 166 return -1; 167 } 168 try { 169 return _parameters[index]; 170 } 171 catch (java.lang.ArrayIndexOutOfBoundsException e) { 172 return -1; 173 } 174 } 175 176 /** 177 * Get array of All parameters 178 * 179 * @return Full Parameter array, index 0 is total parameters 180 */ 181 public int[] getParameters() { 182 return _parameters; 183 } 184 185 /** 186 * Get the Parameter String in Hex Byte Format 187 * <p> 188 * eg. for param. array [3,1,2,3] returns "010203" 189 * 190 * @return Full Parameter String WITHOUT leading number of parameters 191 */ 192 public String getParameterHexString() { 193 if (getParameters()==null) { 194 return ""; 195 } else { 196 return jmri.util.StringUtil.hexStringFromInts(getParameters()).replaceAll("\\s","").substring(2); 197 } 198 } 199 200 protected void clearParameters() { 201 _parameters = null; 202 _nodeTraitsSet = false; 203 } 204 205 /** 206 * Get the Node Type 207 * 208 * @return eg. MERG Command Station CANCMD Firmware 4d Node 65534 209 */ 210 public String getNodeTypeString(){ 211 StringBuilder n = new StringBuilder(100); 212 n.append (CbusNodeConstants.getManu(getParameter(1))) 213 .append (" ") 214 .append( CbusNodeConstants.getModuleTypeExtra(getParameter(1),getParameter(3))) 215 .append(" ") 216 .append( CbusNodeConstants.getModuleType(getParameter(1),getParameter(3))) 217 .append (" ") 218 .append (Bundle.getMessage("FirmwareVer",getParameter(7),Character.toString((char) getParameter(2) ))); 219 if ((getParameter(0)>19) && (getParameter(20)>0) ){ 220 n.append (Bundle.getMessage("FWBeta")) 221 .append (getParameter(20)) 222 .append (" "); 223 } 224 n.append (Bundle.getMessage("CbusNode")) 225 .append (_node.getNodeNumber()); 226 return n.toString(); 227 } 228 229 public void requestEventTot() { 230 if ( _node.getNodeTimerManager().hasActiveTimers() ){ 231 return; 232 } 233 _node.getNodeTimerManager().setNumEvTimeout(); 234 _node.send.rQEVN( _node.getNodeNumber() ); 235 } 236 237 /** 238 * Request a single Parameter from a Physical Node 239 * <p> 240 * Will not send the request if there are existing active timers. 241 * Starts Parameter timeout 242 * 243 * @param param Parameter Index Number, Index 0 is total parameters 244 */ 245 public void requestParam(int param){ 246 if ( _node.getNodeTimerManager().hasActiveTimers() ){ 247 return; 248 } 249 _node.getNodeTimerManager().setAllParamTimeout(param); 250 _node.send.rQNPN( _node.getNodeNumber(), param ); 251 } 252 253 /** 254 * Check if current node firmware is equal to or newer than provided version 255 * 256 * Note that CBUS firmware start with beta builds, followed by a release 257 * with the beta build set to 0, e.g.: 258 * major.minor Beta 1 259 * major.minor Beta 2 260 * ... 261 * major.minor Beta x 262 * major.minor Beta 0 263 * 264 * @param major New FW major version 265 * @param minor New FW minor version 266 * @param beta New FW beta build number 267 * @return true if current node firmware is equal to or newer than provided version 268 */ 269 public boolean isFwEqualOrNewer(int major, int minor, int beta) { 270 if (major > getParameter(MAJOR_VER_IDX)) { 271 return false; 272 } else if (major < getParameter(MAJOR_VER_IDX)) { 273 return true; 274 } else { 275 // Major ver is equal, test minor 276 if (minor > getParameter(MINOR_VER_IDX)) { 277 return false; 278 } else if (minor < getParameter(MINOR_VER_IDX)) { 279 return true; 280 } else { 281 // Major and minor are equal, test beta 282 if (beta == 0) { 283 // Release is always newer than any beta 284 return true; 285 } else if (beta > getParameter(BETA_REV_IDX)) { 286 return false; 287 } else { 288 return true; 289 } 290 } 291 } 292 } 293 294 private boolean sentParamRequest(int paramToCheck){ 295 if ( getParameter(paramToCheck) < 0 ) { 296 requestParam(paramToCheck); 297 return true; 298 } 299 return false; 300 } 301 302 /** 303 * Send a request for the next unknown parameter to the physical node 304 * 305 */ 306 protected void sendRequestNextParam(){ 307 if ( _parameters == null ) { 308 requestParam(0); 309 return; 310 } 311 if ( sentParamRequest(1) // Manufacturer ID 312 || ( sentParamRequest(3) ) // Module ID 313 || ( sentParamRequest(6) ) ) { // initialise NV's 314 return; 315 } 316 317 if ( sentParamRequest(5) // get number event variables per event 318 || ( sentParamRequest(7) ) // get firmware pt1 319 || ( sentParamRequest(2) ) ) { // get firmware pt2 320 return; 321 } 322 323 finishedWhenGotMainParams(); 324 325 for (int i = 1; i < _parameters.length; i++) { 326 if ( sentParamRequest(i) ) { 327 return; 328 } 329 } 330 } 331 332 private void finishedWhenGotMainParams(){ 333 334 if (!( _node instanceof CbusNode)){ 335 return; 336 } 337 338 if (( ( (CbusNode) _node).getCsNum() > -1 ) && ( _commandStationIdentified == false ) ) { 339 // notify command station located 340 log.info("Node type: {}",getNodeTypeString() ); 341 _commandStationIdentified = true; 342 } 343 344 // set node traits, eg CANPAN v1 send wrack on nv set, CANCMD v4 numevents 0 345 // only do this once 346 if (!_nodeTraitsSet ) { 347 CbusNodeConstants.setTraits((CbusNode) _node); 348 _nodeTraitsSet = true; 349 } 350 351 // now traits are known request num. of events 352 if ( _node.getNodeEventManager().getTotalNodeEvents()<0 ){ 353 requestEventTot(); 354 } 355 } 356 357 private static final Logger log = LoggerFactory.getLogger(CbusNodeParameterManager.class); 358 359}