Good day everyone,
Currently I'm working with a simple temperature sensor that basically updates 4 times a second on the current temperature, and filter using the following rule in Drools:
rule "temperature detected"
when
$acc: Number ( doubleValue > 22.0 ) from accumulate(
$sensorMessage: SensorMessage($topic: topic, $timeStamp: timestamp, $data: data) over window:time ( 10s ) from entry-point "temperature_sensor",
average ( $sensorMessage.getDouble("temperature", -100) )
)
then
logger.info("Received temperature data > 22.0 -> " + $acc);
end
This, however, logs the console after EVERY sensor update as long as the accumulated temperature average is larger than 22, over a window of 10 seconds.
This of course, is far from ideal.
Would it be possible to, after a sensor update has been received, continue listening UNTIL no more updates are received for, say, 3 seconds. Then log the starting time of when a sensor update first is detected, and the ending time. And only log these two timestamps if at least 10 updates have been received altogether, and some criterium is met.
Example scenarios (T
being some target temperature):
<= T
, nothing happens, however, if it does exceed T
, we log a single temperature "alert".This is what I came-up with after few hours. Some requirements are not clear for me, like what is 'the last update is logged', each one is 'the last'. Following may still require a work to do, but you may get general idea: we have single object in the session we update and monitor.
import java.lang.Double;
import java.util.ArrayList;
declare SensorMessage
@role(event)
@timestamp(timestamp)
@expires(10s)
end
rule "temperature monitoring"
when
$meta: SensorMetadata()
$acc: Double () from accumulate(
$message: SensorMessage() over window:time(1s),
average ($message.getTemperature())
)
then
$meta.setTemperature($acc);
update($meta);
end
rule "temperature activated"
when
$meta: SensorMetadata(highTemperature == false, temperature >= $meta.targetTemperature)
$lastMessage: SensorMessage($lastTimestamp: timestamp)
not (SensorMessage(timestamp > $lastMessage.timestamp))
then
$meta.setLastActivated($lastMessage.getTimestamp());
$meta.setHighTemperature(true);
update($meta);
System.out.printf("temperature activated %.2f%n", $meta.getTemperature());
end
rule "temperature deactivated"
when
$meta: SensorMetadata(highTemperature == true, temperature < $meta.targetTemperature)
$lastMessage: SensorMessage($lastTimestamp: timestamp)
not (SensorMessage(timestamp > $lastMessage.timestamp))
then
$meta.setHighTemperature(false);
update($meta);
System.out.printf("temperature deactivated %.2f%n", $meta.getTemperature());
end
rule "throttle state activated"
when
$meta: SensorMetadata(throttleState == false)
$lastMessage: SensorMessage($lastTimestamp: timestamp)
not (SensorMessage(timestamp > $lastMessage.timestamp))
$messages: ArrayList(size > 20) from collect(
$message: SensorMessage() over window:time(1s)
)
then
$meta.setLastThrottled($lastMessage.getTimestamp());
$meta.setThrottleState(true);
update($meta);
System.out.printf("throttle state activated %d%n", $messages.size());
end
rule "throttle state deactivated"
when
$meta: SensorMetadata(throttleState == true)
$lastMessage: SensorMessage($lastTimestamp: timestamp)
not (SensorMessage(timestamp > $lastMessage.timestamp))
$messages: ArrayList(size <= 20) from collect(
$message: SensorMessage() over window:time(1s)
)
then
$meta.setThrottleState(false);
update($meta);
System.out.printf("throttle state deactivated %d%n", $messages.size());
end
the test
@DroolsSession(resources = "file:src/main/resources/draft/rule.drl", showStateTransitionPopup = true, ignoreRules = "* monitoring")
public class PlaygroundTest extends DroolsAssert {
@RegisterExtension
public DroolsAssert droolsAssert = this;
@Test
@TestRules(ignore = "throttle *", expectedCount = {
"2", "temperature activated",
"2", "temperature deactivated" })
public void testTemperatureActivationDeactivation() throws IOException {
insert(new SensorMetadata(22));
Date date = new Date(0);
for (int i = 0; i < 100; i++) {
double temperature = i == 80 ? 100 : i == 81 ? -100 : i < 50 ? i : 1 / i;
insertAndFire(new SensorMessage(date, temperature));
date = addMilliseconds(date, 1);
advanceTime(MILLISECONDS, 1);
}
advanceTime(10, SECONDS);
assertFactsCount(1);
}
@Test
@TestRules(ignore = "temperature *", expectedCount = {
"2", "throttle state activated",
"2", "throttle state deactivated" })
public void testThrottleMode() throws IOException {
insert(new SensorMetadata(22));
Date date = new Date(0);
for (int i = 0; i < 100; i++) {
insertAndFire(new SensorMessage(date, 22));
int advanceTime = i * 3;
date = addMilliseconds(date, advanceTime);
advanceTime(MILLISECONDS, advanceTime);
}
advanceTime(10, SECONDS);
assertFactsCount(1);
}
}
and models
public class SensorMessage {
private Date timestamp;
private double temperature;
public SensorMessage(Date timestamp, double temperature) {
this.timestamp = timestamp;
this.temperature = temperature;
}
// getters
}
public class SensorMetadata {
private volatile Date lastActivated;
private volatile Date lastThrottled;
private double temperature;
private boolean highTemperature;
private boolean throttleState;
private double targetTemperature;
public SensorMetadata(double targetTemperature) {
this.targetTemperature = targetTemperature;
}
// getters and setters
}