• 一种动态联动的实现方法


    安防领域中的联动规则

    有安防领域相关的开发经历的人知道,IPCamera可以配置使能“侦测”功能,并且指定仅针对图像传感器的某个区载进行侦测。除了基本的“移动侦测"外,侦测的功能点还有细化的类别,如人员侦测、车辆侦测、烟雾侦测等。这些侦测配置后若被触发,会在IPCamera内部产生一些事件;这事件发生时,IPC内部会有相应的动作,例如:邮件报告、图片上传、视频上传等;还可以配置IPC蜂鸣器报警、外置LED灯闪烁等(若有相应的硬件外设)。在IPC的Web界面,用户可以方便地配置这些选项:

    IPCamera动态联动配置

    如何实现这一功能呢?笔者咨询过相关在安防产业工作的朋友,得到的方案有两种:

    • IPC内部产生的事件种类是固定的,能够做出动作也是有限的,那么直接使用C/C++编码实现这些逻辑;
    • 将事件与动作分别独立为应用中的子模块,使用“胶水”语言Lua将二者联动起来;

    朋友也坦言,第一种方案在代码库中存在很久了,代码逻辑错踪复杂;尤其是一些定制类的IPCamera项目,需求不同会造成开发人员花费不少的时间在动态联动的代码修改上。而第二种方案,虽然在项目开发上可以缩短时间,但只有少部分项目中有,接口也不一样,帮助不大:第一种方案仍是团队内部项目开发的主要方法。

    动态联动的问题抽象

    本文中,笔者将此类动态联动的问题抽象化,并提供上面提到的第二种方法的演示实现。假设IPCamera内部的图像处理会产生五个维度的状态量,名称分别为:
    ex_var1/ex_var2/ex_var3/ex_var4/ex_var5

    这五个状态量,与三个联动规则相关(即事件,当判定条件为真时会触发);也可能会产生另外三个联动规则不为真时的动作。也就是说,三个联动规则需要这五个变量来决定是否为真。笔者将这些信息写入到配置文件中由演示应用读取:

    {
      "linked-vars": {
        "ex_var1": 1,
        "ex_var2": 2,
        "ex_var3": 3,
        "ex_var4": 4.2,
        "ex_var5": 5
        },
        "linked-action": [
        {
          "rule": "(ex_var1 + ex_var2 * 2 + ex_var3 * 3) > 14",
          "true": "echo 'The planet Earth is freaking too hot'",
          "false": "echo 'The planet Earth is freaking too cold'"
        },
        {
          "rule": "math.floor(ex_var4 * 5 / 4) > ex_var5",
          "true": "echo 'Note that floor(ex_var4 * 5 / 4) > ex_var5'",
          "false": "echo 'Note that floor(ex_var4 * 5 / 4) <= ex_var5'"
        },
        {
          "rule": "(ex_var1 + ex_var2 + ex_var3 + ex_var4 + ex_var4) >= 16",
          "true": "echo 'All example values summed up greater or equal to 16.'",
          "false": "echo 'All example values summed up less than 16'"
        }
      ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    注意,上面的rule即一个动态联动规则是否判定为真的条件。它是一个Lua语句;这里只一行语句,在具体的实践层面上,可以是多行的计算(这里只支持一个语句)。上面还给出当事件联动为真时要执行的动作,例如,当全球平均温度大于14(度)时,就会触发事件:

    echo 'The planet Earth is freaking too hot'
    
    • 1

    也就是简单地调用/bin/sh输出一行信息。当该条件为假时,则会触发另一个动作(可选)。

    动态联动规则的实现

    上面的JSON配置文件描述了我们抽象化的动态联动问题。笔者编写的演示例子,会根据上面的信息构造一个Lua脚本,用于计算三个联动规则是否成立:

    local ex_var1 = 1
    local ex_var3 = 3
    local ex_var5 = 5
    local ex_var2 = 2
    local ex_var4 = 4.2
    function rulefunc_1()
            return (ex_var1 + ex_var2 * 2 + ex_var3 * 3) > 14
    end
    function rulefunc_2()
            return math.floor(ex_var4 * 5 / 4) > ex_var5
    end
    function rulefunc_3()
            return (ex_var1 + ex_var2 + ex_var3 + ex_var4 + ex_var4) >= 16
    end
    function rulefunc_all()
            local link_tval_ = 0
            link_tval_ = link_tval_ + ex_var1
            link_tval_ = link_tval_ + ex_var3
            link_tval_ = link_tval_ + ex_var5
            link_tval_ = link_tval_ + ex_var2
            link_tval_ = link_tval_ + ex_var4
            return link_tval_
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    根据Lua引用规则说明,五个演示变量都是上面构造的四个函数的上级变量(upvalue)。注意,上面的Lua函数rulefunc_2只引用了两个变量;也就是说,一些联动规则并不需要全部的状态量。当上面的代码编译为Lua字节码后,如何通一个函数的上级变量索引来更新所有的上级变量呢?这正是rulefunc_all函数的用处:它引用了全部五个变量,它的名称是固定的,虽然它不会被调用,但我们可以通过它来更新该构造脚本的全部上级变量。之后,这个脚本会使用luaL_dostring API预编译为一个Lua状态机:

    rext_any_t rext_lua_new(const struct rext_var * lvar, int * errp) {
        ...
        luaL_openlibs(L); /* load standard library */
        if (script != NULL && script[0] != '\0') {
            int ret;
            ret = luaL_dostring(L, script);
            if (ret != 0) {
                *errp = EINVAL;
                lua_close(L);
                L = NULL;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    注意到,上面的配置信息给出了五个演示变量的初始值。这些值在判定一个联动规则是否被触发前,需要被更新。笔者采用的方法,正是上面提到的,通过函数rulefunc_all来更新:

    int rext_upval_set(rext_any_t anyt, const struct rext_var * rfunc,
        int upindex, const struct rext_var * rval)
    {
      ...
          ret = rext_pushval(anyt, rval);
        if (ret < 0) {
            lua_settop(L, oldtop);
            return -7;
        }
    
        valp = lua_setupvalue(L, ntop, upindex);
        if (valp == NULL) {
            lua_settop(L, oldtop);
            return -8;
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    之后,笔者简单编了5个变量的值,来测试演示动态联动:

        let t_map: HashMap<&str, f64> = HashMap::from([
            ("ex_var1", 1f64),
            ("ex_var2", 2f64),
            ("ex_var3", 3f64),
            ("ex_var4", 4f64),
            ("ex_var5", 5f64),
        ]);
        linked.act(&t_map);
    
        let t_map: HashMap<&str, f64> = HashMap::from([
            ("ex_var1", 2f64),
            ("ex_var2", 3f64),
            ("ex_var3", 4f64),
            ("ex_var4", 5f64),
            ("ex_var5", 6f64),
        ]);
        linked.act(&t_map);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    动态联动的演示代码与结果

    笔者提供了联动规则的演示代码,感兴趣的可以从此处获取。它的正常运行需要Ubuntu系统下安装Lua5.1开发库及Rust编译器:

    sudo apt install lua5.1 liblua5.1-0-dev rust-full bindgen
    
    • 1

    笔者运行的结果如下:

    Created Lua-state machine: 0x562e74aecfc0
    Inserting ex_var5 => 1
    Inserting ex_var4 => 2
    Inserting ex_var3 => 3
    Inserting ex_var1 => 4
    Inserting ex_var2 => 5
    Lua upvalues found: 5
    The planet Earth is freaking too cold
    false: 0 => (ex_var1 + ex_var2 * 2 + ex_var3 * 3) > 14
    Note that floor(ex_var4 * 5 / 4) <= ex_var5
    false: 0 => math.floor(ex_var4 * 5 / 4) > ex_var5
    All example values summed up less than 16
    false: 0 => (ex_var1 + ex_var2 + ex_var3 + ex_var4 + ex_var4) >= 16
    
    The planet Earth is freaking too hot
    true : 0 => (ex_var1 + ex_var2 * 2 + ex_var3 * 3) > 14
    Note that floor(ex_var4 * 5 / 4) <= ex_var5
    false: 0 => math.floor(ex_var4 * 5 / 4) > ex_var5
    All example values summed up greater or equal to 16.
    true : 0 => (ex_var1 + ex_var2 + ex_var3 + ex_var4 + ex_var4) >= 16
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    至此,我们就可以避免使用C/C++实现复杂的联动规则是否为真的判断逻辑了。这不仅仅是提高了开发效率,而且还避免过多的编码可能会引入的缺陷。

  • 相关阅读:
    全国大学生数学竞赛(非数学专业)习题精讲等相关资源
    针对Docker容器的可视化管理工具—DockerUI
    第07章 文本信息提取
    freeswitch如何判断挂机方
    jetson 裁剪之后从sd卡版本移植到emmc版本
    elasticsearch 之时间类型
    (十二)Spring IoC注解式开发
    SpringCloud 学习笔记(3 / 3)
    聊聊“死锁“
    LinkedList相较于Arravlist的特点/优化(面试笔记总结速记)
  • 原文地址:https://blog.csdn.net/yeholmes/article/details/136791807