Simple rules that work on openHAB 1 don't on openHAB 2

Seeing:

2017-02-21 17:28:20.002 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule Water Quality: An error occured during the script execution: Could not invoke method: org.eclipse.xtext.xbase.lib.DoubleExtensions.operator_plus(double,double) on instance: null

So I figured the problem was that I was trying to add something with null, so I added a log line that shows values for each var.

2017-02-21 17:28:20.001 [INFO ] [lipse.smarthome.model.script.Testing] - Water 16 10 7

Any ideas? The rule is:

rule "Water Quality"
when
  Time cron "0/10 * * * * ?"
then
  val double quality_a = Water_Quality_A.state
  val double quality_b = Water_Quality_B.state
  val double quality_c = Water_Quality_C.state
  logInfo("Testing", "Water " + quality_a + "  " + quality_b + "  " + quality_c)
  val double quality = (quality_a + quality_b + quality_c) / 3 
  postUpdate(Water_Quality_Out, quality)
end

Item definitions? I think you’d get that failure with String items

Number    Water_Quality_A                     "Water Quality A [%d ppm]"                              <water_quality> (Water)                                 {http="<[http://10.88.64.45/devStat.xml:30000:XSLT(water_quality_A.xsl)]"}
Number    Water_Quality_B                     "Water Quality B [%d ppm]"                              <water_quality> (Water)                                 {http="<[http://10.88.64.45/devStat.xml:30000:XSLT(water_quality_B.xsl)]"}
Number    Water_Quality_C                     "Water Quality C [%d ppm]"                              <water_quality> (Water)                                 {http="<[http://10.88.64.45/devStat.xml:30000:XSLT(water_quality_C.xsl)]"}

You’ll have to play with as DecimalType and so on, I never really grasp when/how to use these castings

Me as well. In if statements it seems that you do not need them but then in other situation you do.
This DSL approach is really a productivity killer. SInce I am not working with the setting up rules every day I always forget the illogical structure and then help myself by copying code bits which are working. Not great! After three years using OPENHAB this still very annoying.

have you tried commenting out the logInfo statement, could that be the problem (echo’ing out double vals as string) ?
if so replacing it with:

 logInfo("Testing", "Water " + Water_Quality_A.state + "  " + Water_Quality_B.state.toString + "  " + Water_Quality_C.state.toString)

Thinking on a bit, you see your logInfo, so it gets that far.
I reckon its still a casting issue - postUpdate action demands strings !
Use instead
Water_Quality_Out.postUpdate(quality)

Got it to work:

rule "Water Quality"
when
  Time cron "0/10 * * * * ?"
then
  val Number quality_a = Water_Quality_A.state
  val Number quality_b = Water_Quality_B.state
  val Number quality_c = Water_Quality_C.state
  val double quality = (quality_a + quality_b + quality_c) / 3
  postUpdate(Water_Quality_Out, quality)
end

Just not sure why I had to change val double to val Number only for openHAB 2.

@sipvoip, @rossko57, @Marty56,

Here are a few comments and suggestions.

  1. I’m surprised it ever worked in OH 1.x in the original code. I was never able to get the Rules DSL to be able to automatically convert from a .state all the way to a primitive. It shouldn’t have worked before and cannot explain why the rule was working before. I’ve seen surprising things like this posted in other threads, code that worked in OH 1.x that never should have worked. All I can surmize is there has been an update to the Rule’s DSL that makes it a little more strict about such things (this is a good thing).

  2. Don’t cast to DecimalType. Cast to Number (as sipvoip is doing in his working rule). Its a long explanation but suffice to say that you are more likely to experience “ambiguous method call” errors if you cast to DecimalType.

  3. The Rules DSL is smart enough to figure out what you want most of the time but dealing with numbers is one place where it has problems. By using val Number or .state as Number you are giving the Rules DSL a hint as to what type you need the state to be as you do further operations.

  4. Avoid the actions (postUpdate, sendCommand) and use the Item methods (Water_Quality_Out.postUpdate(quality)) unless you have to. The methods are much better able to handle converting from various types than the actions. The one case where using the actions is needed is where you have the name of an Item as a String but not the Item itself. Sometimes the actions appear to work better because it converts the Items passed to it to a String behind the scenes. But it isn’t always able to make that conversion toString so this approach can fail you in unexpected ways.

  5. There is one caveat to 4 and that is when dealing with Numbers. You will likley see problems if you use primitives or cast to DecimalType. If you always use Number (e.g. change val double quality to val Number quality =). The actions appear to work better because it secretly converts everything passed to it to Strings behind the scenes (when it can). If you cast to Number you will experience fewer problems over all.

  6. You do not have to cast to DecimalType or Number to do comparisons.

  7. You do have to cast to Number to do math.

What I usually do when dealing with Numbers is:

  1. Code without casting anything. vals and vars do not specify a type, do not use as anywhere. This covers 95% of all cases.

  2. If Designer indicates errors or warnings cast to Number. This will cover an additional 4% of all cases (with all its faults, Designer is really helpful).

  3. If errors like at the top of the thread occur during execution, cast to Number.

  4. If all else fails, call .toString on what you are passing to sendCommand or postUpdate (Action or method).

Following the above heuristic has yet to let me down in my own code or in helping others.

The deep down root of all of these problems has to do with the Rules DSL’s interactions with Java. That interaction places certain requirements on the classes from Java which are not always met. Consequently occasionally we see inconsistent behavior. This inconsistent behavior is most apparent when working with DecimalType and DateTimeType.

I hope this helps.

4 Likes

Hi Rick
Thank for taking time!
Very Helpful!

1 Like

Rich is the man! He spends a LOT of help helping people out.

1 Like