简体   繁体   English

如何在 Drools 中编写有状态和基于时间的规则?

[英]How to write Stateful and Time-based rules in Drools?

I'm trying to write a rule based engine using Drools.我正在尝试使用 Drools 编写基于规则的引擎。 The rules go like this:规则是这样的:

Rule "Alarm": If the status is "Alarm" then send notification right away.规则“警报”:如果状态为“警报”,则立即发送通知。

Rule "Warning": If the status is "Warning" then save the device ID in memory and wait for 5 minutes.规则“警告”:如果状态为“警告”,则将设备 ID 保存在内存中并等待 5 分钟。 If another message with the same device ID and status "Resolved" is received within 5 minutes, then cancel this rule.如果在 5 分钟内收到另一条具有相同设备 ID 和状态“已解决”的消息,则取消此规则。 Otherwise, upgrade the status to Alarm and trigger the Rule "Alarm".否则,将状态升级为警报并触发规则“警报”。

Rule "Resolved": If status is "Resolved" and device ID is already in memory, then clear the "Warning" for that device ID ie remove the device ID from memory.规则“已解决”:如果状态为“已解决”且设备 ID 已在内存中,则清除该设备 ID 的“警告”,即从内存中删除设备 ID。

Rule Alarm is easy and is working.规则警报很容易并且正在工作。 But rules Warning and Resolved are obviously not working in my code.但是规则警告和已解决显然在我的代码中不起作用。 How to write such rules in Drools?如何在 Drools 中编写这样的规则?

Here's the code I've written so far:这是我到目前为止编写的代码:

DroolsTest.java流口水测试.java

package com.sample;

import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderError;
import org.drools.builder.KnowledgeBuilderErrors;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.io.ResourceFactory;
import org.drools.logger.KnowledgeRuntimeLogger;
import org.drools.logger.KnowledgeRuntimeLoggerFactory;
import org.drools.runtime.StatefulKnowledgeSession;

public class DroolsTest {

    public static final void main(String[] args) {
        try {
            // load up the knowledge base
            KnowledgeBase kbase = readKnowledgeBase();
            StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
            KnowledgeRuntimeLogger logger = KnowledgeRuntimeLoggerFactory.newFileLogger(ksession, "test");
            // go !
            MachineMessage[] messages = new MachineMessage[4]; 
            messages[0] = new MachineMessage("1", "Warning");
            messages[1] = new MachineMessage("2", "Alarm");
            messages[2] = new MachineMessage("3", "Alarm");
            messages[3] = new MachineMessage("1", "Resolved");

            for(int i = 0; i < messages.length; i++)
            {
                ksession.insert(messages[i]);
                ksession.fireAllRules();
                Thread.sleep(9000);
            }
            logger.close();
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

    private static KnowledgeBase readKnowledgeBase() throws Exception {
        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add(ResourceFactory.newClassPathResource("Sample.drl"), ResourceType.DRL);
        KnowledgeBuilderErrors errors = kbuilder.getErrors();
        if (errors.size() > 0) {
            for (KnowledgeBuilderError error: errors) {
                System.err.println(error);
            }
            throw new IllegalArgumentException("Could not parse knowledge.");
        }
        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
        return kbase;
    }

    public static class MachineMessage {

        private String deviceID;
        private String status;

        public MachineMessage() { }
        public MachineMessage(String deviceID, String status) {
            this.deviceID = deviceID;
            this.status = status;           
        }

        public String getDeviceID() {
            return this.deviceID;
        }

        public void setDeviceID(String deviceID) {
            this.deviceID = deviceID;
        }

        public String getStatus() {
            return this.status;
        }

        public void setStatus(String status) {
            this.status = status;
        }
    }
}

Sample.drl样本.drl

package com.sample

import com.sample.DroolsTest.MachineMessage;

rule "Alarm"
when
    m : MachineMessage( status == "Alarm", myDeviceID : deviceID )
then
    System.out.println("Send notification to technician, device in Alarm state. Device ID: " +  myDeviceID );
end

rule "Warning"
when
    m : MachineMessage( status == "Warning", myDeviceID : deviceID )
then
    System.out.println("Wait for 5 minutes, device in Warning state. Device ID: " + myDeviceID);        
    // if we get Machine Message with the same device ID and Resolved status in 5 minutes then 
    // we'll not trigger the following line
    m.setStatus("Alarm");
    update(m);
end

rule "Resolved"
when
    m : MachineMessage( status == "Resolved", myDeviceID : deviceID )
then
    System.out.println("Device has resolved its warning state. Device ID: " + myDeviceID);

end

The actual output I'm getting is this:我得到的实际输出是这样的:

    Wait for 5 minutes, device in Warning state. Device ID: 1
    Send notification to technician, device in Alarm state. Device ID: 1
    Send notification to technician, device in Alarm state. Device ID: 2
    Send notification to technician, device in Alarm state. Device ID: 3
    Device has resolved its warning state. Device ID: 1

Whereas I wanted:而我想要:

    Wait for 5 minutes, device in Warning state. Device ID: 1
    Send notification to technician, device in Alarm state. Device ID: 2
    Send notification to technician, device in Alarm state. Device ID: 3
    Device has resolved its warning state. Device ID: 1

As per the Drools documentation on CEP Events are supposed to be immutable:根据关于 CEP事件的 Drools 文档应该是不可变的:

As a best practice, the application is allowed to populate un-populated event attributes (to enrich the event with inferred data), but already populated attributes should never be changed.作为最佳实践,允许应用程序填充未填充的事件属性(以使用推断数据丰富事件),但不应更改已填充的属性。

So clearly rule "Warning" is already failing the 1st requirement of CEP by the way it's proposed by OP (mutating the field "status").如此明确的规则“警告”已经按照 OP 提出的方式(改变字段“状态”)未能满足 CEP 的第一个要求。

A potential suggestion would be along the lines of:一个潜在的建议是:

rule "Warning"
when
    m : MachineMessage( status == "Warning", myDeviceID : deviceID )
    not( MachineMessage( status == "Resolved", deviceID == m.deviceID, this after[0s, 30s] m) )
then
    System.out.println("I waited for 5 minutes, device in Warning state not followed by Resolved. Device ID: " + myDeviceID);        
    insert(new MachineMessage(m.deviceID, "Alarm"));
end

which would NOT mutate any valorized fields of existing Events, and would actually make CEP knowledge inference trying to match the business requirements described by OP.这不会改变现有事件的任何已赋值字段,并且实际上会使 CEP 知识推理尝试匹配 OP 描述的业务需求。

In general the CEP example in the Drools manual of sprinkler/fire alarms seems to align with the business requirements described by OP.一般而言,Drools 手册中关于喷水灭火/火警的 CEP 示例似乎与 OP 描述的业务需求一致。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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