简体   繁体   中英

Drools - How to apply advanced filtering using a window?

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):

  • If a motion sensor sends 20 updates within 2 seconds, the time of the first update and time of the last update is logged.
  • If a motion sensor sends 6 updates in 1 second, then another 6 updates after 5 seconds, nothing happens, as we expect more motion sensor updates to properly classify it as motion.
  • If a temperature sensor sends 10 updates within 1 second, and the average of all 10 pings is <= 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
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM