[SOLVED] Exception when setting timer = null

Hi

I am struggling with setting a timer correctly. I am actually uncertain as to the way to do it correctly and while I have read many examples in the forums, the timers seem to be implemented in different ways (which of course is not unusual)

The rule below is meant to run at midnight and calculate irrigation for the day.

import org.eclipse.smarthome.model.script.actions.Timer
var Timer itimer_am = null
var Timer itimer_pm = null
var morning_watering = null
var evening_watering = null
var Number vZone1 = null
var Number itimer_start_hour_am
var Number itimer_start_minute_am
var Number itimer_start_hour_pm
var Number itimer_start_minute_pm
var Number icurrent_time
var int delta_itimer_am
var int delta_itimer_pm
var Number irrigation_day

rule "Irrigation 1"
    when
    		//System started or
    		Item Test_Switch_1 received command ON or
    		Time is midnight
    	then
    		logInfo("Crib","Welcome to new irrigation rule")
    		Test_Switch_1.postUpdate(OFF)

    		// check if there is already an alarm timer; cancel it if present
    		itimer_am?.cancel
    		itimer_am = null
    		itimer_pm?.cancel
    		itimer_pm = null

    		switch(Season_Name.state){
    			case "AUTUMN":{
    				morning_watering = ON
    				evening_watering = ON
    				itimer_start_hour_am = 15
    				itimer_start_minute_am = 0
    				itimer_start_hour_pm = 15
    				itimer_start_minute_pm = 5
    				irrigation_day = 8 //1=Monday, 2=Tuesday, 3=Wednesday, 4=Thursday, 5=Friday, 6=Saturday, 7=Sunday, 8=everyday
    			}
    			case "WINTER":{
    				morning_watering = ON
    				evening_watering = OFF
    				itimer_start_hour_am = 7
    				itimer_start_minute_am = 0
    				itimer_start_hour_pm = 16
    				itimer_start_minute_pm = 0
    				irrigation_day = 8 //1=Monday, 2=Tuesday, 3=Wednesday, 4=Thursday, 5=Friday, 6=Saturday, 7=Sunday, 8=everyday
    			}
    			case "SPRING":{
    				morning_watering = ON
    				evening_watering = ON
    				itimer_start_hour_am = 7
    				itimer_start_minute_am = 0
    				itimer_start_hour_pm = 17
    				itimer_start_minute_pm = 0
    				irrigation_day = 8 //1=Monday, 2=Tuesday, 3=Wednesday, 4=Thursday, 5=Friday, 6=Saturday, 7=Sunday, 8=everyday
    			}
    			case "SUMMER":{
    				morning_watering = ON
    				evening_watering = ON
    				itimer_start_hour_am = 7
    				itimer_start_minute_am = 0
    				itimer_start_hour_pm = 18
    				itimer_start_minute_pm = 0
    				irrigation_day = 8 //1=Monday, 2=Tuesday, 3=Wednesday, 4=Thursday, 5=Friday, 6=Saturday, 7=Sunday, 8=everyday
    			}
    		}

    		// calculate current time [min]
    		var int icurrent_time
    		icurrent_time = now.getMinuteOfDay
    		icurrent_time = icurrent_time.intValue
    		logInfo("Crib","Current Time " + icurrent_time)		
    		
    		if ((morning_watering == ON) && (now.getDayOfWeek == irrigation_day) || (irrigation_day == 8)){
    			// calculate the alarm time [min]
    			var int alarm_timer_am
    			alarm_timer_am = (itimer_start_hour_am.intValue * 60) + itimer_start_minute_am.intValue
    			logInfo("Crib","alarm_timer_am " + alarm_timer_am)

    			// calculate the difference between the requested alarm time and current time (again in minutes)  
    			delta_itimer_am = (alarm_timer_am - icurrent_time)
    			logInfo("Crib","Delta time AM " + delta_itimer_am)

    			// add one day (1440 minutes) if alarm time for today already passed
    			if (icurrent_time > alarm_timer_am) {
    				delta_itimer_am = delta_itimer_am + 1440
    				logInfo("Crib","Delta time AM " + delta_itimer_am)
    			}

    			//create the timer
    			itimer_am = createTimer(now.plusMinutes(delta_itimer_am)) [|
    				logInfo("Crib","Irrigation AM Scheduler Running")
    				Commence_Irrigation.sendCommand(ON)
    				//itimer_am = null
    			]
    		}
    		if ((evening_watering == ON) && ((now.getDayOfWeek == irrigation_day) || (irrigation_day == 8))){
    			// calculate the alarm time [min]
    			var int alarm_timer_pm
    			alarm_timer_pm = (itimer_start_hour_pm.intValue * 60) + itimer_start_minute_pm.intValue
    			logInfo("Crib","alarm_timer_pm " + alarm_timer_pm)

    			// calculate the difference between the requested alarm time and current time (again in minutes)  
    			delta_itimer_pm = (alarm_timer_pm - icurrent_time)
    			logInfo("Crib","Delta time PM " + delta_itimer_pm)

    			// add one day (1440 minutes) if alarm time for today already passed
    			if (icurrent_time > alarm_timer_pm) {
    				delta_itimer_pm = delta_itimer_pm + 1440
    				logInfo("Crib","Delta time PM " + delta_itimer_pm)
    			}
    			
    			//create the timer
    			itimer_pm = createTimer(now.plusMinutes(delta_itimer_pm)) [|
    				logInfo("Crib","Irrigation PM Scheduler Running")
    				Commence_Irrigation.sendCommand(ON)
    				//itimer_pm = null
    			]
    		}
    end

However, I get an exception error at the itimer_am = null and itimer_pm = null parts in the timers.

I can see that it was discussed at the below post, but II can’t figure out how to correctly set the timer back to null outside of the timer - really not sure how to do that.
https://community.openhab.org/t/error-when-resetting-timer/33265/3

The rule seems to work fine with not setting the timer back to null, but I just wanted to double check whether there is anything wrong with this to avoid any unintended consequences.

If the entire exception is required to assist I can put that in.

Advice is very much appreciated

Don’t do that in OH2, it’s built-in.
That’s not just a “not necessary”, it can be harmful if it’s not the same source as built-in.

May we know a bit more about this error?
Error messages originating within timer blocks can be vague and misleading.

It shouldn’t be a problem, having a timer “reset itself to null” at the end of its execution. Not least because when you set up
xxx = createTimer(blah)
the xxx variable is really only a handle, a pointer, to the independent timer .
Anyway, it is common practice. There isn’t much to go wrong in your simple timer blocks.

Unrelated comments;
Lots of system stuff happens at midnight; to avoid competition I’d schedule the rule to run at ten past or suchlike.
In your Summer/Winter switch-case block, what happens if none of the cases are true. “Shouldn’t happen” - but code defensively, at least exploiting the default-case to pop a “Suprise!” message.

Thanks for your advice.

Below is an example of the error.

21:23:22.765 [ERROR] [org.quartz.core.JobRunShell          ] - Job DEFAULT.2019-05-05T21:23:22.754+10:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  <null>.itimer_am = <XNullLiteralImplCustom>
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
} ] threw an unhandled Exception:
java.lang.IllegalStateException: Cannot resolve proxy: irrigation.rules#/1
        at org.eclipse.xtext.common.types.util.JavaReflectAccess.getRawType(JavaReflectAccess.java:108) ~[?:?]
        at org.eclipse.xtext.common.types.util.JavaReflectAccess.getField(JavaReflectAccess.java:57) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._assignValueTo(XbaseInterpreter.java:1248) ~[?:?]
        at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter._assignValueTo(ScriptInterpreter.java:211) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.assignValueTo(XbaseInterpreter.java:1221) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:1213) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:216) ~[?:?]
        at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
        at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:190) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
        at com.sun.proxy.$Proxy181.apply(Unknown Source) ~[?:?]
        at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) ~[?:?]
        at org.quartz.core.JobRunShell.run(JobRunShell.java:202) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
        at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
21:23:22.864 [ERROR] [org.quartz.core.ErrorLogger          ] - Job (DEFAULT.2019-05-05T21:23:22.754+10:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  <null>.itimer_am = <XNullLiteralImplCustom>
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
} ] threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception.
        at org.quartz.core.JobRunShell.run(JobRunShell.java:213) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
        at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
Caused by: java.lang.IllegalStateException: Cannot resolve proxy: irrigation.rules#/1
        at org.eclipse.xtext.common.types.util.JavaReflectAccess.getRawType(JavaReflectAccess.java:108) ~[?:?]
        at org.eclipse.xtext.common.types.util.JavaReflectAccess.getField(JavaReflectAccess.java:57) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._assignValueTo(XbaseInterpreter.java:1248) ~[?:?]
        at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter._assignValueTo(ScriptInterpreter.java:211) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.assignValueTo(XbaseInterpreter.java:1221) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:1213) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:216) ~[?:?]
        at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
        at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:190) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
        at com.sun.proxy.$Proxy181.apply(Unknown Source) ~[?:?]
        at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) ~[?:?]
        at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[?:?]
        ... 1 more

Okay, that originates in a timer-scheduled block of code. My guess is that when it references its own handle in itimer_am it is already null.

How could that happen?
Creating and scheduling a timer results in an independent ‘thing’ - a little block of code due to go off in the future.
Optionally, you can keep a handle, a reference or pointer to the block, allowing you to cancel/reschedule etc.
Now, if you reload the rules file that spawned this timer - editing, say - it does not destroy the block of code.
That timer is still scheduled for later.
The reload does however destroy the handle by creating that variable afresh.
When that timer block does run, any reference in it to the handle will fail with a null error.

There’s no cure for this, outside of an openHAB reboot; you just have to remember any long-running timers when editing that may pop up later.

Do you think that might fit your case?

Yes - your explanation does fit. I have been editing this rule file quite intensively, because mostly my approach to programming is trial and error and I have been tweaking it quite a bit.

From this I realise that maybe after I finish with developing the rule and if I reboot I shouldn’t see the error at all.

Do I really need to set the timer to null? I have read examples where this is done and other where it isn’t so I am not entirely sure.

Yep, particularly relevant when you are spawning long-term timers.

Not if you don’t want to. The usual case is for use as a test by rules “is that Timer already running?” in order to cancel/reschedule or avoid creating a duplicate.
Those are generally wise use cases, though.

You cannot prevent the error, but if you do use the handle you can prevent an orphaned timer from doing something unwanted before it errors


my_handle = createTimer(now.plusMinutes(blah)) [ |
   if (my_handle !== null) {
      // do stuff 
   }
  my_handle = null
]
3 Likes

Thank you for taking the time to help me - it is very much appreciated