001package jmri.jmrix.can.cbus.node; 002 003import java.util.TimerTask; 004import jmri.util.TimerUtil; 005 006import org.slf4j.Logger; 007import org.slf4j.LoggerFactory; 008 009/** 010 * Class to handle Timers for a CbusNode. 011 * 012 * @author Steve Young Copyright (C) 2019,2020 013 */ 014public class CbusNodeTimerManager { 015 private final CbusBasicNodeWithManagers _node; 016 017 protected int fetchNvTimeoutCount; 018 private TimerTask nextNvTimerTask; 019 protected int fetchEvVarTimeoutCount; 020 private TimerTask nextEvTimerTask; 021 protected int numEvTimeoutCount; 022 private TimerTask numEvTimerTask; 023 protected int allEvTimeoutCount; 024 protected TimerTask allEvTimerTask; 025 protected int paramRequestTimeoutCount; 026 private TimerTask allParamTask; 027 private TimerTask sendEditNvTask; 028 private TimerTask sendEditEvTask; 029 protected TimerTask sendEnumTask; 030 protected int sendEvErrorCount; 031 protected int _sendNVErrorCount; 032 033 public static int SINGLE_MESSAGE_TIMEOUT_TIME = 1500; 034 035 /** 036 * Create a new CbusNodeTimers 037 * 038 * @param node The Node 039 */ 040 public CbusNodeTimerManager ( CbusBasicNodeWithManagers node ){ 041 _node = node; 042 resetTimeOutCounts(); 043 } 044 045 /** 046 * See if any timers are running, ie waiting for a response from a physical Node. 047 * 048 * @return true if timers are running else false 049 */ 050 protected boolean hasActiveTimers(){ 051 052 return allParamTask != null 053 || allEvTimerTask != null 054 || nextEvTimerTask != null 055 || nextNvTimerTask != null 056 || sendEnumTask != null 057 || sendEditEvTask != null 058 || sendEditNvTask != null 059 || numEvTimerTask != null; 060 } 061 062 // stop any timers running 063 protected void cancelTimers(){ 064 clearSendEnumTimeout(); 065 clearsendEditEvTimeout(); 066 clearsendEditNvTimeout(); 067 clearAllParamTimeout(); 068 clearAllEvTimeout(); 069 clearNextEvVarTimeout(); 070 clearNextNvVarTimeout(); 071 clearNumEvTimeout(); 072 } 073 074 protected final void resetTimeOutCounts(){ 075 fetchNvTimeoutCount = 0; 076 fetchEvVarTimeoutCount = 0; 077 numEvTimeoutCount = 0; 078 allEvTimeoutCount = 0; 079 paramRequestTimeoutCount = 0; 080 sendEvErrorCount = 0; 081 } 082 083 /** 084 * Stop timer for a single NV fetch request. 085 */ 086 protected void clearNextNvVarTimeout(){ 087 if (nextNvTimerTask != null ) { 088 nextNvTimerTask.cancel(); 089 nextNvTimerTask = null; 090 fetchNvTimeoutCount = 0; 091 } 092 } 093 094 /** 095 * Start timer for a single Node Variable request. 096 * 097 * If 10 failed requests aborts loop and sets number of NV's to unknown 098 */ 099 protected void setNextNvVarTimeout() { 100 nextNvTimerTask = new TimerTask() { 101 @Override 102 public void run() { 103 nextNvTimerTask = null; 104 fetchNvTimeoutCount++; 105 if ( fetchNvTimeoutCount == 1 ) { 106 log.info("NV Fetch from node {} timed out",_node.getNodeNumber() ); // 107 } 108 else if ( fetchNvTimeoutCount == 10 ) { 109 log.error("Aborting NV Fetch from node {}",_node.getNodeNumber() ); // 110 _node.getNodeNvManager().reset(); 111 _node.getNodeParamManager().setParameter(5,-1); // reset number of NV's to unknown and force refresh 112 } 113 114 _node.getTableModel().triggerUrgentFetch(); 115 116 } 117 }; 118 TimerUtil.schedule(nextNvTimerTask, SINGLE_MESSAGE_TIMEOUT_TIME); 119 } 120 121 /** 122 * Stop timer for a single event variable request. 123 */ 124 protected void clearNextEvVarTimeout(){ 125 if (nextEvTimerTask != null ) { 126 nextEvTimerTask.cancel(); 127 nextEvTimerTask = null; 128 fetchEvVarTimeoutCount = 0; 129 } 130 } 131 132 /** 133 * Start timer for a single event variable request. 134 * 135 * If 10 failed requests aborts loop and sets events to 0 136 * @param eventVarIndex Event Variable Index 137 * @param eventString User Friendly Event Text 138 */ 139 protected void setNextEvVarTimeout(int eventVarIndex, String eventString) { 140 nextEvTimerTask = new TimerTask() { 141 @Override 142 public void run() { 143 nextEvTimerTask = null; 144 fetchEvVarTimeoutCount++; 145 if ( fetchEvVarTimeoutCount == 1 ) { 146 log.info("Event Var fetch Timeout from Node {} event {}index {}", 147 _node.getNodeStats().getNodeNumberName(),eventString,eventVarIndex); 148 } 149 if ( fetchEvVarTimeoutCount == 10 ) { 150 log.error("Aborting Event Variable fetch from Node {} Event {}Index {}", 151 _node.getNodeStats().getNodeNumberName(),eventString,eventVarIndex); 152 _node.getNodeEventManager().resetNodeEvents(); 153 fetchEvVarTimeoutCount = 0; 154 } 155 156 _node.getTableModel().triggerUrgentFetch(); 157 } 158 }; 159 TimerUtil.schedule(nextEvTimerTask, SINGLE_MESSAGE_TIMEOUT_TIME); 160 } 161 162 /** 163 * Stop timer for event total RQEVN request. 164 */ 165 protected void clearNumEvTimeout(){ 166 if (numEvTimerTask != null ) { 167 numEvTimerTask.cancel(); 168 numEvTimerTask = null; 169 } 170 numEvTimeoutCount = 0; 171 } 172 173 /** 174 * Start timer for event total RQEVN request. 175 * 176 * If 10 failed requests aborts loop and sets event number to 0 177 */ 178 protected void setNumEvTimeout() { 179 numEvTimerTask = new TimerTask() { 180 @Override 181 public void run() { 182 numEvTimerTask = null; 183 if ( _node.getNodeEventManager().getTotalNodeEvents() < 0 ) { 184 185 numEvTimeoutCount++; 186 // the process will be re-attempted by the background fetch routine, 187 // we don't start it here to give a little bit more time for network / node to recover. 188 if ( numEvTimeoutCount == 1 ) { 189 log.info("No reponse to RQEVN ( Get Total Events ) from node {}", _node ); 190 } 191 if ( numEvTimeoutCount == 10 ) { 192 log.info("Aborting requests for Total Events from node {}", _node ); 193 _node.getNodeEventManager().resetNodeEvents(); 194 numEvTimeoutCount = 0; 195 } 196 } 197 } 198 }; 199 TimerUtil.schedule(numEvTimerTask, ( 5000 ) ); 200 } 201 202 /** 203 * Stop timer for an ALL event by index fetch request. 204 */ 205 protected void clearAllEvTimeout(){ 206 if (allEvTimerTask != null ) { 207 allEvTimerTask.cancel(); 208 allEvTimerTask = null; 209 } 210 } 211 212 /** 213 * Starts timer for an ALL event by index fetch request. 214 * <p> 215 * This has a higher chance of failing as 216 * we could be expecting up to 255 CAN Frames in response. 217 * 218 * If fails, re-sends the NERD to the physical node 219 * Aborts on 10 failed requests 220 */ 221 protected void setAllEvTimeout() { 222 allEvTimerTask = new TimerTask() { 223 @Override 224 public void run() { 225 clearAllEvTimeout(); 226 if ( _node.getNodeEventManager().getOutstandingIndexNodeEvents() > 0 ) { 227 allEvTimeoutCount++; 228 229 if ( allEvTimeoutCount < 10 ) { 230 log.warn("Re-attempting whole event / node / index fetch from node {}", _node ); 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 /** 349 * Stops timer for CAN ID Self Enumeration Timeout 350 */ 351 protected void clearSendEnumTimeout(){ 352 if (sendEnumTask != null ) { 353 sendEnumTask.cancel(); 354 sendEnumTask = null; 355 } 356 } 357 358 /** 359 * Starts timer for CAN ID Self Enumeration Timeout 360 * If no response adds warning to console log 361 */ 362 protected void setsendEnumTimeout() { 363 sendEnumTask = new TimerTask() { 364 @Override 365 public void run() { 366 log.warn("Late response from node while request CAN ID Self Enumeration"); 367 sendEnumTask = null; 368 // popup dialogue? 369 } 370 }; 371 TimerUtil.schedule(sendEnumTask, ( SINGLE_MESSAGE_TIMEOUT_TIME ) ); 372 } 373 374 private static final Logger log = LoggerFactory.getLogger(CbusNodeTimerManager.class); 375 376}