calculation in rule with global variable

Hi,

I want my Hue bulbs to take a predefined color depending on how many times I push a KNX wall switch. I have a sensor that reads when the switch is pushed and I have the commands to set my Hue bulbs.

However the rule to keep track of the counter seems to be hardest part. I found some examples of calculations around. However those use a calculation within the rule. For instance a countdown rule.

I think I need a variable which is “globally” defined instead of within the RHS.

Based on the examples I started like this;

Integer ibrcount = 0.
rule “Hue via schakelaar”
when
Event(source == “status licht living 1”, value == “off”)
then
ibrcount = ibrcount +1;
execute.command(“sts_Hue_counter”, ibrcount);
end

``

sts_Hue_counter is a virtual in memory command. There is a sensor linked to it. Consequently I would build a few rules depending on the value of that sensor and for instance at 5, reset sts_Hue_counter back to 0.

I know the above rule is wrong, but I hope it is clear why I cannot put in the rule itself?

Does anyone has a good suggestion for the correct syntax?

Thx!

Hi

I've got something similar running in my system.

I have a water flow sensor which starts a sequence within OpenRemote.

When the sensor goes off, the sequence is set to 0, which turns every thing off again.

Instead of using a timer in OpenRemote to move to the next step, you could use your KNX action to add 1 to the value and trigger the step.

You can also put a slider in your UI if you want to jump to a step.

The batch of rules I use can be found here, under the heading "Sequence of Steps"

https://github.com/openremote/Documentation/wiki/OpenRemote-Rules-examples

I've expanded this set of rules and the in-memory slider to include negative numbers.

If I move the slider to negative numbers, the rule sets a static scene.

But if the slider moves to a positive number, the timer triggers and the sequence takes over.

All it needed was to add rules that matched negative values and to set the range sensor for the slider from -10 to 20 (or however many steps to want to show)

As an extra, I have a couple of rules that take a button press on my Velbus GPO panel to set the sequence number value.

Button 4 = -4
Button 5 = -5
Button 6 = -6
Button 7 sets an interval timer in Velbus which triggers a random HEX value, in a separate rule.
Button 8 = 0 (switches everything off)

And a couple of buttons on my UI which set the sequence value to other negative values.

Hi Stuart,

thanks for your reply. It’s probabaly me, but I’m afraid it is not 100% clear what you do in your example and if it is really mapping to what I am trying to do.

The magic in your example happens in this rule:

rule “IM_Num01_Inc”
timer (int: 15s) // ***** This is the interval timer for each step ******
when
Event( source == “IM_Num01_Status”, $BathroomStep : value > 0 )
then
Integer iBath = Integer.parseInt($BathroomStep.toString()) ;
iBath = iBath + 1 ;
execute.command(“IM_Num01_Set”, iBath ) ;
end

``

Some questions to that.

  • your rule is triggered if the value is > 0. Above you have the reset rule. But it resets to 1. So it keeps getting fired right?

  • What it does is increase the VIM with 1 every 15s, right? As long as you pres that key? In my case I do not want it t be increased with an interval, or a slider. The goal is that I (or the kids) do not have to use a phone to set the hue bulbs. So the rule to add 1 to the counter does not get fired by a timer, and it also the value to be increased in the RHS, is not the value which is used in the LHS. In your case you use the value of the sensor in the LHS, also in the RHS.

btw, in my example I have value = “off” in the LHS. That is ecause that KNX button cannot push anything else but on/off. I would add an identical rule but then with “on”. Unless I could make the rule more generic, to fire only when the value changes.

Okay.

I get the picture.....

I'll put together a complete set of rules that you can use as a template.

I can't do it now, I'll try to grab some time tomorrow.

Hi

I've put a set of rules together that should do what you're looking for.

The only thing I haven't tried before is using two events.

If I'm understanding the instructions from other posts correctly, it should work.

You will need to create two new commands in your design.

An In-Memory command to set a value, with the name " I-M_Num_Set"

(I normally create a command that sets a value of 0, so I can use it with a button or slider in a UI)

Another In-Memory command to read the status.
Simply put "status" in the command value section

Then a level or range sensor to read the In-Memory value, give it the name "I-M_Num_Status"

You can just drop the attached Step-V1.drl file into your rules folder, so that you don't break your existing modeler.drl (designer rules).
Once you know the steps are working, you can paste the rules into the designer. (Then delete the .drl file before re-syncing your design)

Good luck.

Steps-V1.drl (3.26 KB)

Hi,

that is realy great!

Unfortunately, I do not have time to implment it right away. Will try to do so tomorow or after that.

But already one quick question.

I opened your drl file.

Just to be sure whether it is important or not. In your rule “I-M_Num_Inc” there are two events in the LHS. There seems to be no separator beteen those. But is there really not? In notepad it shows as spaces. But when I open the same file in wordpad in between the spaces there is a character which wordpad displays as
Â

``

print screen;

Auto Generated Inline Image 1.png

Do I need to bother about that Ã? if not, should there be no “or” or “and” kind of thing?

Pobably basic syntax, but I’m really not familiar with this kind of code.

And just for my understanding. The first event in the rule is actually not a condition, but we just use it to capture the value of I-M_Num_Status, right?

Many thanks!

There is no need for a separator between Event()'s on the LHS. Drools assumes AND there as default. The character displayed by wordpad is bogus. There should be none.

That's interesting.

I've never seen that happen before, probably a good idea to get rid of that extra character. (I copied and pasted that bit of text from your post)

As for the lack of "and", as I say, I haven't adventures into dual source rules on my own rules yet.

I believe it is correct, having looked at other posts and seen advice from people like Michal and Richard.

As for "IM_Num_Status", you are correct, it is in there to recover the current value from memory, adjust it and save it back again.

There is plenty of information on www.OpenRemote.org about creating these in memory commands and sensors (as well as GitHub)

Hi,

I finally was able to take some time. I implemented the rule from your example, mapped to my status command and sensor. The logic is indeed what I expect it to do. When syncin the controller the boot log is ok. No errors there anymore. So syntax seems to be ok. I have added the sensor to my UI so I can see what happens.

But whenever I try to trigger it there is a huge error in the drools log. See below. What could be the problem here?

I hope it is not some java version error, because I did quit some effort to get to the version I am on now, for some other problems.

I have the following packages in the top;

package org.openremote.controller.protocol
//package org.openremote.controller.model.event
global org.openremote.controller.statuscache.CommandFacade execute;
global org.openremote.controller.statuscache.SwitchFacade switches;
global org.openremote.controller.statuscache.LevelFacade levels;
//import org.openremote.controller.protocol.;
//import org.openremote.controller.model.event.
;
import java.util.;
import java.util.regex.
;

``

error log:

ERROR 2017-11-24 20:58:22,949 (Drools): Error in executing rule : status licht living 1:Exception executing consequence for rule “Hue Bram via schakelaar” in org.openremote.controller.protocol: java.lang.NumberFormatException: For input string: “”

    Event Switch Event (ID = 533307, Source = 'status licht living 1', Switch Value = 'on', Switch State = ON) not processed!

Exception executing consequence for rule “Hue Bram via schakelaar” in org.openremote.controller.protocol: java.lang.NumberFormatException: For input string: “”

    at org.drools.core.runtime.rule.impl.DefaultConsequenceExceptionHandler.handleException(DefaultConsequenceExceptionHandler.java:39)

    at org.drools.core.common.DefaultAgenda.fireActivation(DefaultAgenda.java:1100)

    at org.drools.core.phreak.RuleExecutor.fire(RuleExecutor.java:121)

    at org.drools.core.phreak.RuleExecutor.evaluateNetworkAndFire(RuleExecutor.java:74)

    at org.drools.core.common.DefaultAgenda.fireNextItem(DefaultAgenda.java:1007)

    at org.drools.core.common.DefaultAgenda.fireLoop(DefaultAgenda.java:1350)

    at org.drools.core.common.DefaultAgenda.fireAllRules(DefaultAgenda.java:1288)

    at org.drools.core.impl.StatefulKnowledgeSessionImpl.internalFireAllRules(StatefulKnowledgeSessionImpl.java:1306)

    at org.drools.core.impl.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:1297)

    at org.drools.core.impl.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:1278)

    at org.openremote.controller.statuscache.rules.RuleEngine.push(RuleEngine.java:203)

    at org.openremote.controller.statuscache.EventProcessorChain.push(EventProcessorChain.java:196)

    at org.openremote.controller.statuscache.StatusCache.update(StatusCache.java:293)

    at org.openremote.controller.model.sensor.Sensor.update(Sensor.java:367)

    at org.openremote.controller.model.sensor.Sensor$DeviceReader.run(Sensor.java:655)

    at java.lang.Thread.run(Thread.java:745)

Caused by: java.lang.NumberFormatException: For input string: “”

    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)

    at java.lang.Integer.parseInt(Integer.java:592)

    at java.lang.Integer.parseInt(Integer.java:615)

    at org.openremote.controller.protocol.Rule_Hue_Bram_via_schakelaar2025263123.defaultConsequence(Rule_Hue_Bram_via_schakelaar2025263123.java:7)

    at org.openremote.controller.protocol.Rule_Hue_Bram_via_schakelaar2025263123DefaultConsequenceInvokerGenerated.evaluate(Unknown Source)

    at org.openremote.controller.protocol.Rule_Hue_Bram_via_schakelaar2025263123DefaultConsequenceInvoker.evaluate(Unknown Source)

    at org.drools.core.common.DefaultAgenda.fireActivation(DefaultAgenda.java:1089)

    ... 14 more

ERROR 2017-11-24 20:58:22,952 (Drools): Root Cause:

java.lang.NumberFormatException: For input string: “”

    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)

    at java.lang.Integer.parseInt(Integer.java:592)

    at java.lang.Integer.parseInt(Integer.java:615)

    at org.openremote.controller.protocol.Rule_Hue_Bram_via_schakelaar2025263123.defaultConsequence(Rule_Hue_Bram_via_schakelaar2025263123.java:7)

    at org.openremote.controller.protocol.Rule_Hue_Bram_via_schakelaar2025263123DefaultConsequenceInvokerGenerated.evaluate(Unknown Source)

    at org.openremote.controller.protocol.Rule_Hue_Bram_via_schakelaar2025263123DefaultConsequenceInvoker.evaluate(Unknown Source)

    at org.drools.core.common.DefaultAgenda.fireActivation(DefaultAgenda.java:1089)

    at org.drools.core.phreak.RuleExecutor.fire(RuleExecutor.java:121)

    at org.drools.core.phreak.RuleExecutor.evaluateNetworkAndFire(RuleExecutor.java:74)

    at org.drools.core.common.DefaultAgenda.fireNextItem(DefaultAgenda.java:1007)

    at org.drools.core.common.DefaultAgenda.fireLoop(DefaultAgenda.java:1350)

    at org.drools.core.common.DefaultAgenda.fireAllRules(DefaultAgenda.java:1288)

    at org.drools.core.impl.StatefulKnowledgeSessionImpl.internalFireAllRules(StatefulKnowledgeSessionImpl.java:1306)

    at org.drools.core.impl.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:1297)

    at org.drools.core.impl.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:1278)

    at org.openremote.controller.statuscache.rules.RuleEngine.push(RuleEngine.java:203)

    at org.openremote.controller.statuscache.EventProcessorChain.push(EventProcessorChain.java:196)

    at org.openremote.controller.statuscache.StatusCache.update(StatusCache.java:293)

    at org.openremote.controller.model.sensor.Sensor.update(Sensor.java:367)

    at org.openremote.controller.model.sensor.Sensor$DeviceReader.run(Sensor.java:655)

    at java.lang.Thread.run(Thread.java:745)

``

I think it'll need the assistant of someone far better than me....

The only thing I can is repeated in the log is..

NumberFormatException

This is a wild guess, but could it be something like it's expecting a number and being presented with something else?

I just noticed I forgot to paste the rule itself. Maybe, there is sth there that might explain the error.

rule “Hue Bram via schakelaar”
when
Event( source == “snsr_BraM_Hue_counter”, $Step : value )
Event(source == “status licht living 1”, value == “on”)
then
Integer iStep = Integer.parseInt($Step.toString()) ;
iStep = iStep + 1 ;
execute.command(“sts_Bram_Hue_counter”, iStep ) ;
end

``

Auto Generated Inline Image 1.png

Auto Generated Inline Image 2.png

Update. I just solved the error, but I’m afraid we are still not at the end.

The error occured because the sensor had no value at startup. I solved it now by adding this rule:

rule “run at init”
then
execute.command(“sts_Bram_Hue_counter”,1) ;
end

``

Probably another solution would be to set the sensor as range instead of custom (as stated above, I know. My mistake. But I read in another topic that it was better to use custom sensor at all times). Will try that later.

But now, I can see in my UI, as soon as I push the wall sensor, so KNX gives it state “on”, it keeps adding. After one second or so, the sensor had value 15.

And if u think about it, it is logical. In the RHS, it changes a value which is used in the LHS. So it fires again. See drools log below.

Any suggestions, so I can add only once per push?

DEBUG 2017-11-25 16:13:35,346 (Drools): rule “Hue Bram via schakelaar” // (package org.openremote.controller.protocol)

    Declarations

            Declaration: "$Step: 14"

    LHS objects(antecedents)

            Class: "Switch"

            Fields:

                    Event Name:     "status licht living 1"

                    Event Value:    "on"

            Class: "CustomState"

            Fields:

                    Event Name:     "snsr_BraM_Hue_counter"

                    Event Value:    "14"

DEBUG 2017-11-25 16:13:35,848 (Drools): rule “Hue Bram via schakelaar” // (package org.openremote.controller.protocol)

    Declarations

            Declaration: "$Step: 15"

    LHS objects(antecedents)

            Class: "Switch"

            Fields:

                    Event Name:     "status licht living 1"

                    Event Value:    "on"

            Class: "CustomState"

            Fields:

                    Event Name:     "snsr_BraM_Hue_counter"

                    Event Value:    "15"

``

Please excuse me, I only work with Velbus systems.

The important information is that when a Velbus button is pressed, it shows as "pressed" for only a fraction of a second, until it's status returns to "released"

(Except when "Long_Pressed" is used)

So in my situations, the "pressed" state is present just long enough to trigger the rule.

So, my pure guess is that your KNX status is staying at "on" for a long time, so the rule conditions are met for longer.

I'm sure I could come up with a work around, but it might be better to look into why the KNX state stays "on"...

Just a thought...

You might resolve this by putting in a debounce timer.

For example :-

      Rule "Example"
      
      timer(int: 0 1s)
      
      when
      
      event ....

I solved the fact that it kept counting. KNX keeps the state of a group address, that is standard. Some wall switches can send integer values instead of on or off, but not all.

Below is the set of rules to obtain. However two concerns still:

  • it always adds twice. the rule gets fired a second time before the variable sts_schakelaar_Bram_counter is being reset (rule log at the bottom). I might add a delay in the rule, but I was hopeing for something more solid. Also can I add ms instead of seconds? One second would be too much. Or altnernatively, I would program my follow-on steps depending a value of + 2. But I doubt whether that would be reliable enough. Or maybe it can be done with some additional Java code?

  • More general. For this counter alone I have now at least 4 rules and 2 in memory commands. The amount of rules I have is getting quit large and I still have some ideas for future stuff. I have already about 40 rules and 20 in memory commands in total. What about performance? All these rules need constant polling, no? I have OR running on a pi 3B.

Many thanks for any suggestions!!

rule “run at init”
then
execute.command(“sts_Bram_Hue_counter”,0) ;
execute.command(“snsr_schakelaar_Bram_counter”,0) ;
end

rule “Hue Bram via schakelaar on”
when
Event(source == “status licht living 1”, value == “on”)
then
execute.command(“sts_schakelaar_Bram_counter”, 1 ) ;
end

rule “Hue Bram via schakelaar off”
when
Event(source == “status licht living 1”, value == “off”)
then
execute.command(“sts_schakelaar_Bram_counter”, 1 ) ;
end

rule “Hue Bram via schakelaar counter”
when
Event(source == “snsr_schakelaar_Bram_counter”, value == 1)
Event( source == “snsr_Bram_Hue_counter”, $Step : value )
then
execute.command(“sts_schakelaar_Bram_counter”, 0 ) ;
Integer iStep = Integer.parseInt($Step.toString()) ;
iStep = iStep + 1 ;
execute.command(“sts_Bram_Hue_counter”, iStep ) ;
end

``

rule log:

DEBUG 2017-11-26 10:28:02,230 (Drools): rule “Hue Bram via schakelaar on” // (package org.openremote.controller.protocol)

Declarations

LHS objects(antecedents)

Class: “Switch”

Fields:

Event Name: “status licht living 1”

Event Value: “on”

DEBUG 2017-11-26 10:28:02,550 (Drools): rule “Hue Bram via schakelaar counter” // (package org.openremote.controller.protocol)

Declarations

Declaration: “$Step: 9”

LHS objects(antecedents)

Class: “Range”

Fields:

Event Name: “snsr_schakelaar_Bram_counter”

Event Value: “1”

Class: “CustomState”

Fields:

Event Name: “snsr_Bram_Hue_counter”

Event Value: “9”

DEBUG 2017-11-26 10:28:02,651 (Drools): rule “Hue Bram via schakelaar counter” // (package org.openremote.controller.protocol)

Declarations

Declaration: “$Step: 10”

LHS objects(antecedents)

Class: “Range”

Fields:

Event Name: “snsr_schakelaar_Bram_counter”

Event Value: “1”

Class: “CustomState”

Fields:

Event Name: “snsr_Bram_Hue_counter”

Event Value: “10”

``

In the default CLOUD rules engine mode adding delay is the only option. You can do this delay in milliseconds, which is in fact default. Typical 300 ms is a good value for debouncing.

However, if you have the rule ending in the STRAM mode (it was a discussion here how to do it a while ago, search for fusion), then you can use a better construction like:

Integer iStep = Integer.parseInt($Step.toString()) ;
iStep = iStep + 1 ;
execute.command(“sts_Bram_Hue_counter”, iStep ) ;
end

rule “Hue Bram via schakelaar counter”
when
$e: Event(source == “snsr_schakelaar_Bram_counter”, value == 1)
Event( source == “snsr_Bram_Hue_counter”, $Step : value, this before $e )
then
execute.command(“sts_schakelaar_Bram_counter”, 0 ) ;

``

The rule will fire only if the flag was set to 1 after the counter was stable. After increasing it it wouldn’t be increase anymore without resetting and setting again the “snsr_schakelaar_Bram_counter” flag.

With regards to just how many rules you can run, I think that's the same as asking "how much water do I need to fill my swimming pool", without knowing how big the swimming pool is. :slight_smile:

For reference, I've got 7 different .drl files in my rules folder, within an OpenRemote V2.5 controller running on a 1Ghz, 1GB Ram netbook.

Each .drl file has between 5 and 30 rules in.

While for stream lining, it's better to put all rules in the modeller section of the designer, my coding skills being what they are, I find it better to break 1
.drl file by stuffing up a bit of code, rather than crash a perfectly working .drl file.

As you can see in the example I uploaded, each of my .drl files has an announcement (TTS) rule that lets me know that each .drl file has been included in the controller knowledge.

So far, I haven't noticed any lack of performance (due to rules alone) on any of the test machines I've got.

Each of my machines has a curl command in their rc.local files to let me know when they have been booted up.
I'm looking for a way to do the same on shutdown, but it's a very low priority issue.

So really, add as many rules as you like, until you notice that your particular machine starts to show any signs that it's struggling.

I'm sure Michal or Richard might have more educated / experienced answers on this topic.