001package jmri.jmrix.can.cbus.node; 002 003import java.util.TimerTask; 004import jmri.util.TimerUtil; 005 006/** 007 * Class to handle Timers for a CbusNode. 008 * 009 * @author Steve Young Copyright (C) 2019,2020 010 */ 011public class CbusNodeTimerManager { 012 private final CbusBasicNodeWithManagers _node; 013 014 protected int fetchNvTimeoutCount; 015 private TimerTask nextNvTimerTask; 016 protected int fetchEvVarTimeoutCount; 017 private TimerTask nextEvTimerTask; 018 protected int numEvTimeoutCount; 019 private TimerTask numEvTimerTask; 020 protected int allEvTimeoutCount; 021 protected TimerTask allEvTimerTask; 022 protected int paramRequestTimeoutCount; 023 private TimerTask allParamTask; 024 private TimerTask sendEditNvTask; 025 private TimerTask sendEditEvTask; 026 protected TimerTask sendEnumTask; 027 protected int sendEvErrorCount; 028 protected int _sendNVErrorCount; 029 030 public static int SINGLE_MESSAGE_TIMEOUT_TIME = 1500; 031 032 /** 033 * Create a new CbusNodeTimers 034 * 035 * @param node The Node 036 */ 037 public CbusNodeTimerManager ( CbusBasicNodeWithManagers node ){ 038 _node = node; 039 resetTimeOutCounts(); 040 } 041 042 /** 043 * See if any timers are running, ie waiting for a response from a physical Node. 044 * 045 * @return true if timers are running else false 046 */ 047 protected boolean hasActiveTimers(){ 048 049 return allParamTask != null 050 || allEvTimerTask != null 051 || nextEvTimerTask != null 052 || nextNvTimerTask != null 053 || sendEnumTask != null 054 || sendEditEvTask != null 055 || sendEditNvTask != null 056 || numEvTimerTask != null; 057 } 058 059 // stop any timers running 060 protected void cancelTimers(){ 061 clearSendEnumTimeout(); 062 clearsendEditEvTimeout(); 063 clearsendEditNvTimeout(); 064 clearAllParamTimeout(); 065 clearAllEvTimeout(); 066 clearNextEvVarTimeout(); 067 clearNextNvVarTimeout(); 068 clearNumEvTimeout(); 069 } 070 071 protected final void resetTimeOutCounts(){ 072 fetchNvTimeoutCount = 0; 073 fetchEvVarTimeoutCount = 0; 074 numEvTimeoutCount = 0; 075 allEvTimeoutCount = 0; 076 paramRequestTimeoutCount = 0; 077 sendEvErrorCount = 0; 078 } 079 080 /** 081 * Stop timer for a single NV fetch request. 082 */ 083 protected void clearNextNvVarTimeout(){ 084 if (nextNvTimerTask != null ) { 085 nextNvTimerTask.cancel(); 086 nextNvTimerTask = null; 087 fetchNvTimeoutCount = 0; 088 } 089 } 090 091 /** 092 * Start timer for a single Node Variable request. 093 * 094 * If 10 failed requests aborts loop and sets number of NV's to unknown 095 */ 096 protected void setNextNvVarTimeout() { 097 nextNvTimerTask = new TimerTask() { 098 @Override 099 public void run() { 100 nextNvTimerTask = null; 101 fetchNvTimeoutCount++; 102 if ( fetchNvTimeoutCount == 1 ) { 103 log.info("NV Fetch from node {} timed out",_node.getNodeNumber() ); // 104 } 105 else if ( fetchNvTimeoutCount == 10 ) { 106 log.error("Aborting NV Fetch from node {}",_node.getNodeNumber() ); // 107 _node.getNodeNvManager().reset(); 108 _node.getNodeParamManager().setParameter(5,-1); // reset number of NV's to unknown and force refresh 109 } 110 111 _node.getTableModel().triggerUrgentFetch(); 112 113 } 114 }; 115 TimerUtil.schedule(nextNvTimerTask, SINGLE_MESSAGE_TIMEOUT_TIME); 116 } 117 118 /** 119 * Stop timer for a single event variable request. 120 */ 121 protected void clearNextEvVarTimeout(){ 122 if (nextEvTimerTask != null ) { 123 nextEvTimerTask.cancel(); 124 nextEvTimerTask = null; 125 fetchEvVarTimeoutCount = 0; 126 } 127 } 128 129 /** 130 * Start timer for a single event variable request. 131 * 132 * If 10 failed requests aborts loop and sets events to 0 133 * @param eventVarIndex Event Variable Index 134 * @param eventString User Friendly Event Text 135 */ 136 protected void setNextEvVarTimeout(int eventVarIndex, String eventString) { 137 nextEvTimerTask = new TimerTask() { 138 @Override 139 public void run() { 140 nextEvTimerTask = null; 141 fetchEvVarTimeoutCount++; 142 if ( fetchEvVarTimeoutCount == 1 ) { 143 log.info("Event Var fetch Timeout from Node {} event {}index {}", 144 _node.getNodeStats().getNodeNumberName(),eventString,eventVarIndex); 145 } 146 if ( fetchEvVarTimeoutCount == 10 ) { 147 log.error("Aborting Event Variable fetch from Node {} Event {}Index {}", 148 _node.getNodeStats().getNodeNumberName(),eventString,eventVarIndex); 149 _node.getNodeEventManager().resetNodeEvents(); 150 fetchEvVarTimeoutCount = 0; 151 } 152 153 _node.getTableModel().triggerUrgentFetch(); 154 } 155 }; 156 TimerUtil.schedule(nextEvTimerTask, SINGLE_MESSAGE_TIMEOUT_TIME); 157 } 158 159 /** 160 * Stop timer for event total RQEVN request. 161 */ 162 protected void clearNumEvTimeout(){ 163 if (numEvTimerTask != null ) { 164 numEvTimerTask.cancel(); 165 numEvTimerTask = null; 166 } 167 numEvTimeoutCount = 0; 168 } 169 170 /** 171 * Start timer for event total RQEVN request. 172 * 173 * If 10 failed requests aborts loop and sets event number to 0 174 */ 175 protected void setNumEvTimeout() { 176 numEvTimerTask = new TimerTask() { 177 @Override 178 public void run() { 179 numEvTimerTask = null; 180 if ( _node.getNodeEventManager().getTotalNodeEvents() < 0 ) { 181 182 numEvTimeoutCount++; 183 // the process will be re-attempted by the background fetch routine, 184 // we don't start it here to give a little bit more time for network / node to recover. 185 if ( numEvTimeoutCount == 1 ) { 186 log.info("No reponse to RQEVN ( Get Total Events ) from node {}", _node ); 187 } 188 if ( numEvTimeoutCount == 10 ) { 189 log.info("Aborting requests for Total Events from node {}", _node ); 190 _node.getNodeEventManager().resetNodeEvents(); 191 numEvTimeoutCount = 0; 192 } 193 } 194 } 195 }; 196 TimerUtil.schedule(numEvTimerTask, ( 5000 ) ); 197 } 198 199 /** 200 * Stop timer for an ALL event by index fetch request. 201 */ 202 protected void clearAllEvTimeout(){ 203 if (allEvTimerTask != null ) { 204 allEvTimerTask.cancel(); 205 allEvTimerTask = null; 206 } 207 } 208 209 /** 210 * Starts timer for an ALL event by index fetch request. 211 * <p> 212 * This has a higher chance of failing as 213 * we could be expecting up to 255 CAN Frames in response. 214 * 215 * If fails, re-sends the NERD to the physical node 216 * Aborts on 10 failed requests 217 */ 218 protected void setAllEvTimeout() { 219 allEvTimerTask = new TimerTask() { 220 @Override 221 public void run() { 222 clearAllEvTimeout(); 223 if ( _node.getNodeEventManager().getOutstandingIndexNodeEvents() > 0 ) { 224 allEvTimeoutCount++; 225 226 if ( allEvTimeoutCount < 10 ) { 227 log.warn("Re-attempting event index fetch from node {}", _node ); 228 log.warn("NUMEV reports {} events, {} outstanding via ENRSP.", 229 _node.getNodeEventManager().getTotalNodeEvents(), 230 _node.getNodeEventManager().getOutstandingIndexNodeEvents()); 231 setAllEvTimeout(); 232 _node.send.nERD( _node.getNodeNumber() ); 233 } 234 else { 235 log.warn("Aborting whole event / node / index fetch from node {}", _node ); 236 _node.getNodeEventManager().resetNodeEvents(); 237 } 238 } 239 } 240 }; 241 TimerUtil.schedule(allEvTimerTask, ( 5000 ) ); 242 } 243 244 /** 245 * Stop timer for a single parameter fetch 246 */ 247 protected void clearAllParamTimeout(){ 248 if (allParamTask != null ) { 249 allParamTask.cancel(); 250 allParamTask = null; 251 } 252 } 253 254 /** 255 * Start timer for a Parameter request 256 * If 10 timeouts are counted, aborts loop, sets 8 parameters to 0 257 * and node events array to 0 258 * @param index Parameter Index 259 */ 260 protected void setAllParamTimeout( int index) { 261 clearAllParamTimeout(); // resets if timer already running 262 allParamTask = new TimerTask() { 263 @Override 264 public void run() { 265 allParamTask = null; 266 if ( paramRequestTimeoutCount == 0 ) { 267 log.warn("No response to parameter {} request from node {}", index ,_node ); 268 } 269 paramRequestTimeoutCount++; 270 if ( paramRequestTimeoutCount == 10 ) { 271 log.warn("Aborting requests to parameter {} for node {}",index,_node ); 272 if (_node instanceof CbusNode) { 273 ((CbusNode) _node).nodeOnNetwork(false); 274 } 275 } 276 } 277 }; 278 TimerUtil.schedule(allParamTask, ( SINGLE_MESSAGE_TIMEOUT_TIME ) ); 279 } 280 281 /** 282 * Stop timer for Teaching NV Node Variables 283 */ 284 protected void clearsendEditNvTimeout(){ 285 if (sendEditNvTask != null ) { 286 sendEditNvTask.cancel(); 287 sendEditNvTask = null; 288 } 289 } 290 291 /** 292 * Start timer for Teaching NV Node Variables 293 * If no response received, increases error count and resumes loop to teach next NV 294 * which handles the error 295 */ 296 protected void setsendEditNvTimeout() { 297 if (!(_node instanceof CbusNode )){ 298 return; 299 } 300 301 sendEditNvTask = new TimerTask() { 302 @Override 303 public void run() { 304 sendEditNvTask = null; 305 // log.info(" getsendsWRACKonNVSET {} ",getsendsWRACKonNVSET() ); 306 if ( ((CbusNode)_node).getsendsWRACKonNVSET() ) { 307 log.warn("teach nv timeout"); 308 _sendNVErrorCount++; 309 } 310 _node.getNodeNvManager().sendNextNvToNode(); 311 } 312 }; 313 TimerUtil.schedule(sendEditNvTask, ( SINGLE_MESSAGE_TIMEOUT_TIME ) ); 314 } 315 316 /** 317 * Stops timer for Teaching Events 318 */ 319 protected void clearsendEditEvTimeout(){ 320 if (sendEditEvTask != null ) { 321 sendEditEvTask.cancel(); 322 sendEditEvTask = null; 323 } 324 } 325 326 /** 327 * Start timer for Teaching Events 328 * On timeout, ie Node does not Respond with a success message, 329 * stops Learn Loop and takes node out of Learn Mode. 330 */ 331 protected void setsendEditEvTimeout() { 332 sendEditEvTask = new TimerTask() { 333 @Override 334 public void run() { 335 log.info("Late / no response from node while teaching event"); 336 sendEditEvTask = null; 337 sendEvErrorCount++; 338 339 // stop loop and take node out of learn mode 340 _node.getNodeEventManager().nextEvInArray=999; 341 _node.getNodeEventManager().teachNewEvLoop(); 342 } 343 }; 344 TimerUtil.schedule(sendEditEvTask, ( SINGLE_MESSAGE_TIMEOUT_TIME ) ); 345 } 346 347 /** 348 * Stops timer for CAN ID Self Enumeration Timeout 349 */ 350 protected void clearSendEnumTimeout(){ 351 if (sendEnumTask != null ) { 352 sendEnumTask.cancel(); 353 sendEnumTask = null; 354 } 355 } 356 357 /** 358 * Starts timer for CAN ID Self Enumeration Timeout 359 * If no response adds warning to console log 360 */ 361 protected void setsendEnumTimeout() { 362 sendEnumTask = new TimerTask() { 363 @Override 364 public void run() { 365 log.warn("Late response from node while request CAN ID Self Enumeration"); 366 sendEnumTask = null; 367 // popup dialogue? 368 } 369 }; 370 TimerUtil.schedule(sendEnumTask, ( SINGLE_MESSAGE_TIMEOUT_TIME ) ); 371 } 372 373 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CbusNodeTimerManager.class); 374 375}