Issue with rule concurrency

  • Platform information:
  • Hardware: x86
  • OS: Ubuntu 16.04
  • Java: zulu-8
  • openHAB version: 2.2.0
  • Issue of the topic: rule concurrency

Hi, I’m a happy new user of openhab (coming from FHEM and iobroker) and I’m struggling a bit with my latest rule.

Basically I’m dynamically changing the color of a LED strip based on the current power consumption of my server
and restoring the previous color after a few seconds.

The rule is working, but if it runs concurrently it does not restore the current color of the LED strip correctly.
The second instance of the rule will pick up the wrong current value (set by the first instance).

I tried to use a lock, but it didn’t change anything.

Any help would be much appreciated. :slight_smile:
Alex

import java.util.concurrent.locks.ReentrantLock
val ReentrantLock lock  = new ReentrantLock()

rule "Power"
 when
     Item Power changed
 then

lock.lock()
    try {

logInfo("LED state: ", LED01_Color.state.toString)
var curhsb1 = new HSBType(LED01_Color.state.toString)
var curhsb2 = new HSBType(LED02_Color.state.toString)

var colorvalue  = Math::round((120 -((Float::parseFloat(Power.state.toString)-12) *120/24)))
if (colorvalue > 360 ) { colorvalue = 360}
if (colorvalue < 0 ) { colorvalue = 0}
var dyncolor  = new HSBType(colorvalue + ",100,100")

sendCommand(LED01_Color, dyncolor)
sendCommand(LED02_Color, dyncolor)

logInfo("LED dyncolor: ", dyncolor.toString)

val double roundedpower = Math::round(100 * (Float::parseFloat(Power.state.toString))) / 100.0
sendCommand(KodiIronsky_NachrichtAnzeigen,"Current power: " + roundedpower)

Thread::sleep(3000)
sendCommand(LED01_Color, curhsb1)
sendCommand(LED02_Color, curhsb2)

    } finally{
        lock.unlock()
    }

end

I thing the power will change permanantly and the rule runs mulpible.
You have to change the logic. e.g. use a crontab.

Thanks for your reply, but I don’t think so.

The power measurements come in every few seconds (sometimes every second, sometimes every 60 seconds or in between), because it’s a cheap energy meter with a 433MHz transmitter and the receiver doesn’t pick up all messages.

But that’s not the issue. There must be a way to have the same rule run concurrently without the instances interfering with each other OR keeping the second instance from running until the first instance finishes.

I think I found an easy way. I’m using a dummy openHAB switch as a semaphore now.

I’m checking at the beginning if it’s ON and do nothing, if it’s OFF I let the rest of the rule execute while setting the switch to ON during run time. Seems to work.

rule "Power"
when
Item Power changed
then

if (PowerRuleRunning.state == ON) {

} else {
 postUpdate(PowerRuleRunning, ON)
 logInfo("power.rules", "current color: " + LED01_Color.state.toString)
 var curhsb1 = new HSBType(LED01_Color.state.toString)
 var curhsb2 = new HSBType(LED02_Color.state.toString)

 var colorvalue = Math::round((120 - ((Float::parseFloat(Power.state.toString) - 12) * 120 / 24)))
 if (colorvalue > 360) {
  colorvalue = 360
 }
 if (colorvalue < 0) {
  colorvalue = 0
 }
 var dyncolor = new HSBType(colorvalue + ",100,100")

 sendCommand(LED01_Color, dyncolor)
 sendCommand(LED02_Color, dyncolor)

 logInfo("power.rules", "dyncolor: " + dyncolor.toString)

 val double roundedpower = Math::round(100 * (Float::parseFloat(Power.state.toString))) / 100.0
 sendCommand(KodiIronsky_NachrichtAnzeigen, "Current power: " + roundedpower)

 Thread::sleep(3000)
 sendCommand(LED01_Color, curhsb1)
 sendCommand(LED02_Color, curhsb2)

 postUpdate(PowerRuleRunning, OFF)
}
end

you would have a rule that doesn’t generated side effects (i.e. doesn’t use global variables, doesn’t send commands it pay updates.

since such rules are of limited use, then you need to decide what behavior you want and use a ReentrantLock to implement it in the way you want.

your two choices of behavior are to ignore events that trigger a rule while it is still running or wait until the rule finished running before processing subsequent events.

in your attention to use the ReentrantLock above you implement the second approach. your second rule does the first, i.e. ignore.

since you are happy with your second version I’m going to guess that the problem with the lock was you really wanted to ignore the subsequent events.

to implement that using the ReentrantLock use

if(lock.isLocked) return;
lock.lock()
    try {
...

I’m going from memory on the is locked call. search “ReentrantLock javadoc” to verify.

Many thanks for taking the time to give more insight. Since I’m still interested in a proper, elegant solution, I’ll try it again with the ReentrantLock sooner or later :).