規則引擎設計


概述

所謂規則引擎,指的是if some condition match then trigger some thing的機制。condition是一系列的expression,比如設備狀態變更為離線(屬性),考勤有人通過閘機(事件);trigger一系列的action,比如存儲到數據庫、發出告警信息。乃至於觸發其他設備的動作,比如溫度過高則判斷火災則觸發噴淋聯動。

將rule抽象出來,讓用戶可以自由定義,就是設計規則。關聯好rule和action,本質就是一種回調注冊機制,當condition匹配時,代碼會query用戶已經定義的action,並依次觸發(當然還有服務端寫死的一些action)。

可以match的condition,以及可以trigger的action,都是我們實現定義好的模版,用戶可以在rule/action里面填入自定義的參數。因此設計規則引擎就是設計一種模版語言,前端可以渲染這種語言讓用戶填充參數,后端可以解析這種語言實現對應的邏輯。因為這相當於設計一種語言。舉個例子,如果寫python的話,可以直接用python寫condition,然后eval計算結果。對於Java而言,開源的drools本質上是一門腳本語言,讓客戶端去解析這個工作量太大,不太可能采用;而urule的開源版功能過於孱弱,只能拿來參考。不過如果使用工作流引擎,比如flowable,內部集成了DMN決策樹,本質上就是一個規則引擎,可以考慮直接使用,不過我們這里簡化了一下用了自己的實現。

condition和action的設計依賴於設備本身能提供的功能,因此需要將設備具體的型號和能支持的condition/action關聯起來。舉例來說,如果考勤機支持溫度上報,那么就可以支持定義體溫報警。不支持的話,用戶就沒法設置體溫報警。

規則引擎核心數據

由於我們使用網關作為中間層通信,mqtt協議的通信都是異步的。因此我們把rule都定義為設備觸發的event,event關聯到具體的字段(即屬性,attr)。attr的類型(如bool、枚舉值、日期、時間、整數、浮點和字符串)決定了關聯的前端控件(一般就是開關、inputbox)和操作符(大於小於等於,字符串包含、正則匹配等)。用戶拼接attr和值,組成復雜表達式,即建立了rule. event我們做最小化拆解,和設備本身無關,即廠商的model要么支持某個event,要么不支持,不存在只支持一部分的可能。

能夠觸發的action,一種是設備支持的,另一種是我們服務支持的。和event一樣,我們需要定義允許用戶自由觸發的action(以及參數),然后把這些action和設備型號關聯起來。用戶創建rule以后,選擇服務/設備支持的action並填入必須的參數,自由組合即可。

這里不同於阿里雲的設計,因為我們所有的設備都需要我們自己接入,所以清楚這個event和cmd的邊界,而阿里雲作為PAAS平台,他是允許所有設備接入的,所以不能預定義這些東西。

這里仍然使用門禁類設備舉例說明。支持event包括:

  1. 考勤數據上報(EVENT_UPLOAD_ATTEND),attr包括訪客身份(personId, personName),時間(timestamp,類型為time), 人臉圖像(image,類型)和進出方向(direction);
  2. 體溫數據上報(EVENT_UPLOAD_TEMP),attr包括訪客身份(personId, personName),時間(timestamp,類型為time),體溫(temperature, 浮點數);

這里很多時候1和2是同時上報的,但是我們仍然按着最小化原則拆分成兩個事件,這樣就可以區分支持體溫測量的設備型號,和不支持的。類似的,環境檢測儀的數據可能一次上報很多條,我們仍然分別拆分成不同的事件,以便自由組合condition。

而這里可以trigger的action包括:

  1. 系統服務。比如告警,可以預定義幾種告警給用戶使用。包括:站內信、離線推送、短信推送、微信推送和電話報警等,作為參數給用戶組合。
  2. 門禁設備本身支持的action,比如遙控開門等;
  3. 其他物聯設備支持的action,比如工地的廣播等;

用戶可以做以下組合:

對EVENT_UPLOAD_ATTEND,if timestamp >= 21:00 and direction=2 then trigger alarm(param=站內信),即21點之后有人從門禁離開則使用站內信告警。

對於EVENT_UPLOAD_TEMP,if temperature >= 37.3 then trigger broadcaster 125 action 1 template 5,如果有人體溫大於37.0度,則使用工地中id為125的廣播執行動作1(假設就是語言廣播),廣播內容為模版5(預定義好的體溫告警)。顯然這里模版5可能會用到event中用戶的名字,所以還要把event傳入作為action的參數。

存儲設計

首先定義iot_rule_base,即event/action的預定義(該表的內容目前是直接寫庫的,不通過界面編輯),例如:

{
  	id: int,
    rule_code:str, //規則唯一描述符,如EVENT_UPLOAD_BODY_TEMP體溫上報,或者ACTION_SYS_NOTIFY系統通知
    device_type: int, //設備類型,為0標示全局支持
    rule_type: int, //事件還是動作
    name: str, //事件名、簡單描述
    params:{  //json schema
      {
        "type": "object",
        "properties": {
          "time": {
            "type": "string",
            "title": "發生時間"
          },
          "direction": {
            "type": "integer",
            "title": "方向",
            "enum": [
              1,
              2
            ],
            "enumDesc": "進;出"
          },
          "personId": {
            "type": "string",
            "title": "用戶ID"
          }
        }
      }
    }
}

然后我們定義iot_device_model表,將設備類型和event/action關聯起來:

{
    id: long
    type: int,
    name: str, //型號的描述,比如:人臉識別(無測溫),人臉識別(含測溫)
    events:[1,2,3], //支持的事件,json array
    actions:[1,2,3] //支持的動作,json array
}

當前版本的設備型號關聯預定義事件和動作還未顯示在界面上,V2要加上(在設備型號編輯界面)。這里標示設備型號支持的事件和動作。

下面定義規則,即iot_rule_trigger

{
    id: long,
    group_code: str, //定義規則的組織
    device_type: int, //設備類型
    device_model: int, //設備型號ID;如果為0標示該類型的所有型號
    device_ids: [], //觸發規則的設備列表;如果是該型號的全部設備,傳入[0]
    event_code: str, //觸發的事件唯一標示
    condition: str, //規則表達式(供后端解析)
    dom: str, //前端對應dom描述(供前端渲染)
    memo: str, //規則備注
}

v2版本將actions加了一層抽象,即 執行庫。執行=動作+參數,即預設的參數化的動作。執行會關聯到規則。定義為iot_rule_exec:

{
	id: long,
  group_code: str, //定義執行的組織
  name: str, //執行的名稱
  params: {
  	"actOn":[{
        code:"ACTION_SYS_NOTIFY", 
        delay: 0, //延遲執行時間,單位:秒
        pushType:1, 
        cycle: 30, //最小觸發周期,單位:秒
        level:1, //0-普通消息,1-4:x級
        targets:[int] //用戶id
    	},{
      	code: "ACTION_SPRAY_SWITCH",
        delay: 300,
        children:[{ //子事件,嚴格按順序執行
        	code:"ACTION_SYS_NOTIFY",
          delay: 0,
          pushType:1,
          level: 0,
          targets:[]
        }]
      }],
    "actOff":[]
  }, //json,觸發規則時動作
  rules: [], //關聯的規則列表
}

params固定有參數 act_on 和 act_off 標示進入/離開規則時觸發的動作列表;后面是一個樹狀結構:同一級別在數組內的數據,可以並行運行; children 指定的子事件,必須在父事件之后運行。

多個執行之間是並行關系,沒有順序。

action/event數據結構設計

使用json schema語法,並做以下擴展:

  1. enum可以使用 enumCode ,表明后台字典的 featCode;如果是簡單枚舉,使用標准語法(參考上面的direction),此時enumDesc里面是以 英文分號 分割的中文描述;此外如果有 parentCode 字段,標示對應字典項還要篩選父節點(參考字典相關的API);還是以 direction 為例,假設字典代碼為 ATTEND_DIR ,那么定義形式如下:
{
  "type": "object",
  "properties": {
    "direction": {
      "type": "integer",
      "title": "方向",
      "enumCode": "ATTEND_DIR"
    }
  }
}

如果不用enumCode,直接把進出選項寫出來,那就是:

{
  "type": "object",
  "properties": {
    "direction": {
      "type": "integer",
      "title": "方向",
      "enum": [	//這里直接給了可選值和對應的desc
        1,
        2
      ],
      "enumDesc": "進;出"
    }
  }
}
  1. 有一個固定字段 time 標示事件發生的時間點,可以針對其做一些日期、星期、以及時間的配置。比如配置僅工作日的8-18點之間觸發規則;但是這里要使用一些和通用格式不同的特殊配置。這個版本需求 暫時不做;
  2. 如果要對某些特殊用戶的考勤進行告警,可能需要 personId 配置規則時加上人員選擇器。 暫時不做;
  3. 某些字段會有 unit 屬性,表示單位的中文描述,比如分貝等,用於發送消息或者前端展示;
  4. event第一層有一個 eventType 屬性,默認是0,表示可自動解除;1表示不可自動解除;像AI報警/安全帽報警這種,屬於不可解除的報警,此時每次事件觸發都會創建新的告警記錄,必須用戶手動解除(換句話說此時執行中設置的“同質消息頻率”選項是無效的。)
    condition語法

condition這里使用mongo query表達式,類似q參數最開始的設計,上面例子的表達式就是:

{
    "key1":{"$lt": 8}, //key1小於8; $lt(<), $gt(>), $le(<=), $ge(>=), $ne(!=), $eq(=)
    "key2":{"$ge": "37.3"}, //key2大於等於37.3
    "$or":[{"key3":{"$regex": "\d{3}"}},{"key4": "3"}], //key3滿足正則,或者key4=3,使用$like表示包含
    "key4":{"$in":[1, 2, 3]}, //key4=1或2或3
    "key6.0": 10, //數組第一個元素是10
    "key7.subKey1":{"$lt": 10}, //object的子元素小於10

}

前端為了方便自己解析,可以使用另外一套語法,即前面提到的dom字段,服務器不會解析該字段。

現有Action

告警消息

rule_code: ACTION_SYS_NOTIFY 
{
  delay: 0, //延遲時間,單位:秒
  pushType:1, //推送類型,位運算,1:站內信,2:離線推送,4:短信,8:電話,16:智能廣播,32:微信
  broaderIds: [1], //用戶選擇關聯的音柱id,注意政企端是沒有音柱可選的,所以這個字段必然為空或者沒有
  cycle:60, //最小觸發周期,單位秒,在此周期內同一類型的告警最多發一條通知
  level:1, //0-普通消息,1-4:風險等級
  targets:[],//推送目標id,注意如果是通過政企端設置的規則,則推送到政企端;否則推送到項目端
  targetRoles: [], //推送角色id,如果用戶選擇全部,則roleId=-1
  muteTimes:[["19:00", "07:00"]],//勿擾時間段,注意如果左邊的更大,表明這個時間段是跨天的
  muteType: 1, //靜默推送類型,也是位運算
}

噴淋聯動

rule_code: ACTION_SPRAY_SWITCH

{
  turnOn: bool, //打開還是關閉
  delay: 0, //延遲打開
  deviceIds: [], //選擇噴淋設備
}

理論上用戶可以設置的action需要根據項目目前擁有的設備型號決定:后端獲取項目的所有設備,然后將所有action取去重並集,返回給前端。

當前版本只需支持告警通知。

參考文獻

  1. 從0到1:構建強大且易用的規則引擎


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM