thisisIO
(Michael)
October 31, 2019, 11:51am
1
Hello,
i have 2 rules in on file.
Please tell me why do i get this error and how can i solve it?
PS
If i put this
var Timer timer = null
var Number repeatTimer
at the top of the rule (before first rule) i get another error
Please advice me what have i done wrong?
Thanks a lot!
rossko57
(Rossko57)
October 31, 2019, 12:02pm
2
You certainly have to put any declarations at the top, before rules.
Your second error is about the var repeatTimer
, which you have now declared inside the rule.
When the rule finishes, that local variable is destroyed.
When the timer runs later, and wants to use repeatTimer
…
A cure is to declare the var globally, so that it persists between rule runs. At the top, outside of rules.
In general it is more helpful to copy/paste code in code fences here, than use screenshots.
1 Like
thisisIO
(Michael)
October 31, 2019, 12:11pm
3
rossko57:
Your second error is about the var repeatTimer
, which you have now declared inside the rule.
When the rule finishes, that local variable is destroyed.
When the timer runs later, and wants to use repeatTimer
…
A cure is to declare the var globally, so that it persists between rule runs. At the top, outside of rules.
In general it is more helpful to copy/paste code in code fences here, than use screenshots.
Thanks i will try!
And one more error
Captured with Lightshot
I use it, so why do i get it?
rossko57
(Rossko57)
October 31, 2019, 12:16pm
4
Because you fully specify the path of ReentrantLock, you are not using the import.
thisisIO
(Michael)
October 31, 2019, 12:32pm
5
rossko57:
rossko57:
In general it is more helpful to copy/paste code in code fences here, than use screenshots.
Sorry i use screenshot here because so i can aim on the error.
But is the code needed, here it is.
import java.util.concurrent.locks.ReentrantLock
val Number MODE_OFF = 0
val Number MODE_STANDBY = 1
val Number MODE_ACTIVE = 2
val Number MODE_FINISHED = 3
//val Number DEBOUNCE_TIME = 90000
val Number TELEPERIOD_ACTIVE = 30
val Number TELEPERIOD_OFF = 300
var Timer timer = null
var Number repeatTimer
val String messagePrefix = "householdDevices.rules | Washingmachine | "
var java.util.concurrent.locks.ReentrantLock finishLock = new java.util.concurrent.locks.ReentrantLock()
//------------------------------------------------------------------------//
// WASCHINGMACHINE
//------------------------------------------------------------------------//
rule "Washingmachine Consumption"
when
Item TasmotaSocketDryerEnergyPower received update
then
if (WashingmachineState.state == MODE_ACTIVE) {
WashingmachineTime.postUpdate((now.millis - new DateTime(WashingmachineStartTime.state.toString).millis)/1000/60)
WashingmachineEnergy.postUpdate(TasmotaSocketDryerEnergyTotal.deltaSince(new DateTime(WashingmachineStartTime.state.toString)) as Number)
}
if (TasmotaSocketDryerEnergyPower.state < 1) WashingmachineState.postUpdate(MODE_OFF)
else if (TasmotaSocketDryerEnergyPower.state > 10) {
WashingmachineState.postUpdate(MODE_ACTIVE)
}
else if (TasmotaSocketDryerEnergyPower.state < 3) {
if (WashingmachineState.state == MODE_OFF) WashingmachineState.postUpdate(MODE_STANDBY)
else if (WashingmachineState.state == MODE_ACTIVE) {
finishLock.lock()
try {
Thread::sleep(90000)
if (TasmotaSocketDryerEnergyPower.state < 3) {
WashingmachineState.postUpdate(MODE_FINISHED)
}
} finally {
finishLock.unlock()
}
}
}
end
rule "Washing Mashine State / Notifications / Tele Period Change"
when
Item WashingmachineState changed
then
var String currentTime = String::format( "%1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS", new java.util.Date)
val String currentHoursMinutes = String::format( "%1$tH:%1$tM", new java.util.Date)
if (WashingmachineState.state == MODE_ACTIVE || WashingmachineState.state == MODE_STANDBY) {
if (WashingmachineState.state == MODE_ACTIVE) {
//Resetting all values
WashingmachineStartTime.postUpdate(now.toString)
WashingmachineEndTime.postUpdate(NULL)
WashingmachineTime.postUpdate(NULL)
WashingmachineEnergy.postUpdate(0)
var message = "Washing started (ACTIVE): " + currentTime
logInfo("householdDevices.rules", message)
sendTelegram("bot", messagePrefix + message)
}
TasmotaSocketDryerTelePeriod.sendCommand(TELEPERIOD_ACTIVE)
var message = "Tasmota Tele set to 30 (ACTIVE or STANDBY): " + currentTime
logInfo("householdDevices.rules", message)
sendTelegram("bot", messagePrefix + message)
}
if (WashingmachineState.state == MODE_OFF) {
TasmotaSocketDryerTelePeriod.sendCommand(TELEPERIOD_OFF)
var message = "Tasmota Tele set to 300 (OFF): " + currentTime
logInfo("householdDevices.rules", message)
sendTelegram("bot", messagePrefix + message)
}
timer = null
repeatTimer = 60000
timer = createTimer(now, [|
if (WashingmachineState.state == MODE_FINISHED) {
WashingmachineEndTime.postUpdate(now.toString)
timer.reschedule(now.plusMillis(repeatTimer.intValue))
repeatTimer = ((repeatTimer as Number)* 2).intValue
//logInfo("householdDevices.rules", "Washingmachine finished. Timer " + repeatTimer)
var WashingmachineEndCounter = transform("JS", "duration.js", ((now.millis - new DateTime(WashingmachineEndTime.state.toString).millis)/1000/60).toString)
var message = "Finished"
logInfo("householdDevices.rules", message)
sendTelegram("bot", messagePrefix + message)
}
])
end
Sorry did not understood what do i need to do and how to solve it(((
Could you please tell.
Thanks!
rossko57
(Rossko57)
October 31, 2019, 12:51pm
6
You’ve given the full path to ReentrantLock. No import is needed.
Alternatively, use the import -
import java.util.concurrent.locks.ReentrantLock
var ReentrantLock finishLock = new ReentrantLock()
1 Like
rlkoshak
(Rich Koshak)
October 31, 2019, 4:13pm
7
I have to come in with the obligatory “this code has many antipatterns for Rules DSL.” See Why have my Rules stopped running? Why Thread::sleep is a bad idea for a full discussion.
ReentrantLocks are super dangerous to use in Rules DSL. There are cases where you cannot guarantee they get unlocked.
You only get five simultaneously running Rules. When you use ReentrantLocks and long sleeps you end up with Rules sitting around consuming a runtime thread waiting for access to the lock. In this case, if your Washingmachine Consumption Rule triggers five times in that minute and a half with a state of 3, none of your other Rules will be able to run for that time. If there is an error that bypasses the finally (which is possible in Rules DSL), none of your Rules will be able to run until OH restarts.
A far safer approach would be to use Timers instead of locks.
var Timer activeTimer = null
...
if(TasmotaSocketDryerEnergyPower.state >= 3) {
activeTimer.cancel()
activeTimer = null
}
if (WashingmachineState.state == MODE_OFF) WashingmachineState.postUpdate(MODE_STANDBY)
else if (WashingmachineState.state == MODE_ACTIVE) {
if(activeTimer !== null && !activeTimer.hasTerminated) activeTimer.reschedule(now.plusSeconds(90))
else {
activeTimer = createTimer(now.plusSeconds(90), [ |
WashingmachineState.postUpdate(MODE_FINISHED)
activeTimer = null
]
}
}
The above does the same thing that your dangerous code appears to do (i.e. wait for the power to be under 3 for 90 seconds before setting the mode to MODE_FINISHED) without any risks to the rest of your Rules becoming unable to run.
1 Like
thisisIO
(Michael)
October 31, 2019, 4:31pm
8
Thanks a lot!
I noticed that this rule works unstable, i thought it was problem with OH. But now i know it!
I got this script from mainterner ThomDietrich. Here is the post https://community.openhab.org/t/washing-machine-state-machine/15587
If this rule is not so good, it needs to be changed) I spent with it lot of time, thinking bad on OH, but the problem was very easy to solve, just change to timers)))
Thanks one more time!
rlkoshak
(Rich Koshak)
October 31, 2019, 4:57pm
9
Time passes, OH changes, and we learn more. That post was first published over three years ago.
1 Like