记录一下自己对jetlinks 2.0x场景联动代码理解

概要

场景联动是规则引擎中,一种业务逻辑的可视化编程方式,您可以通过可视化的方式定义设备之间联动规则。当触发条件指定的事件或属性变化事件发生时,系统通过判断执行条件是否已满足,来决定是否执行规则中定义的执行动作。如果满足执行条件,则执行定义的执行动作;反之则不执行,本文章不介绍场景联动的具体使用,场景联动的具体使用请参看场景联动使用步骤
,本文章根据自己理解,记录下jetlinks场景联动的实现逻辑.

整体架构流程

1.SceneService实现CommandLineRunner接口重写run方法,run方法会在Spring 容器初始化完成之后被调用

  public void run(String... args) {
        createQuery()
            .where()
            .is(SceneEntity::getState, RuleInstanceState.started)//查询rule_scene中被启动的场景
            .fetch()
            .flatMap(e -> Mono
                .defer(() -> ruleEngine.startRule(e.getId(), e.toRule().getModel()).then())//调用jetlinks的规则引擎,执行规则
                .onErrorResume(err -> {
                    log.warn("启动场景[{}]失败", e.getName(), err);
                    return Mono.empty();
                }))
            .subscribe();
    }

2.SceneEntity的toRule会生成RuleInstance,ruleInstance会生成规则链

  public RuleInstance toRule() {
        SceneRule rule = copyTo(new SceneRule());

        RuleInstance instance = new RuleInstance();
        instance.setId(getId());
        RuleModel model = rule.toModel();//生成规则模型
        model.addConfiguration(RuleEngineConstants.ruleCreatorIdKey, modifierId);
        model.addConfiguration(RuleEngineConstants.ruleName, getName());
        instance.setModel(model);
        return instance;
    }

3.SceneRule toModel

    public RuleModel toModel() {
        validate();
        RuleModel model = new RuleModel();
        model.setId(id);
        model.setName(name);
        model.setType("scene");

        RuleNodeModel sceneNode = new RuleNodeModel();
        sceneNode.setId(id);
        sceneNode.setName(name);
        sceneNode.setConfiguration(FastBeanCopier.copy(this, new HashMap<>()));
        sceneNode.setExecutor(SceneTaskExecutorProvider.EXECUTOR);

        //传递数据到下级节点
        sceneNode.addConfiguration(AbstractExecutionContext.RECORD_DATA_TO_HEADER, true);
        sceneNode.addConfiguration(AbstractExecutionContext.RECORD_DATA_TO_HEADER_KEY, CONTEXT_KEY_SCENE_OUTPUT);

        //触发器
        trigger.applyModel(model, sceneNode);//将SceneTaskExecutorProvider加入执行链
        model.getNodes().add(sceneNode);
        if (CollectionUtils.isNotEmpty(actions)) {

            int index = 1;
            RuleNodeModel preNode = null;
            SceneAction preAction = null;
            for (SceneAction action : actions) {
                RuleNodeModel actionNode = new RuleNodeModel();
                actionNode.setId("action_" + index);
                actionNode.setName("动作_" + index);
                action.applyNode(actionNode);
                //并行
                if (parallel) {
                    model.link(sceneNode, actionNode);
                }
                //串行
                else {
                    //串行的时候 标记记录每一个动作的数据到header中,用于进行条件判断或者数据引用
                    actionNode.addConfiguration(AbstractExecutionContext.RECORD_DATA_TO_HEADER, true);
                    actionNode.addConfiguration(AbstractExecutionContext.RECORD_DATA_TO_HEADER_KEY, actionNode.getId());

                    if (preNode == null) {
                        //场景节点->第一个动作节点
                        model.link(sceneNode, preNode = actionNode);
                    } else {
                        //上一个节点->当前动作节点
                        RuleLink link = model.link(preNode, actionNode);
                        //设置上一个节点到此节点的输出条件
                        if (CollectionUtils.isNotEmpty(preAction.getTerms())) {
                            link.setCondition(TermsConditionEvaluator.createCondition(trigger.refactorTerm("this", preAction.getTerms())));
                        }
                        preNode = actionNode;
                    }
                }
                model.getNodes().add(actionNode);
                preAction = action;
                index++;
            }
        }

        //使用分支条件时
        if (CollectionUtils.isNotEmpty(branches)) {
            int branchIndex = 0;
            for (SceneConditionAction branch : branches) {
                branchIndex++;

                List<SceneActions> group = branch.getThen();

                if (CollectionUtils.isNotEmpty(group)) {
                    int groupIndex = 0;
                    for (SceneActions actions : group) {
                        groupIndex++;
                        if (actions != null && CollectionUtils.isNotEmpty(actions.getActions())) {
                            int actionIndex = 1;
                            RuleNodeModel preNode = null;
                            SceneAction preAction = null;
                            for (SceneAction action : actions.getActions()) {
                                RuleNodeModel actionNode = new RuleNodeModel();
                                actionNode.setId(createBranchActionId(branchIndex, groupIndex, actionIndex));
                                actionNode.setName("条件" + branchIndex + "_分组" + groupIndex + "_动作" + actionIndex);

                                action.applyNode(actionNode);//会根据不同的执行器,像链中加入不同的TaskExecutorProvider
                                //串行
                                if (!actions.isParallel()) {
                                    //串行的时候 标记记录每一个动作的数据到header中,用于进行条件判断或者数据引用
                                    actionNode.addConfiguration(RuleData.RECORD_DATA_TO_HEADER, true);
                                    actionNode.addConfiguration(RuleData.RECORD_DATA_TO_HEADER_KEY, actionNode.getId());
                                    actionNode.addConfiguration(ACTION_KEY_BRANCH_INDEX, branchIndex);
                                    actionNode.addConfiguration(ACTION_KEY_GROUP_INDEX, groupIndex);
                                    actionNode.addConfiguration(ACTION_KEY_ACTION_INDEX, actionIndex);

                                    if (preNode != null) {
                                        //上一个节点->当前动作节点
                                        RuleLink link = model.link(preNode, actionNode);
                                        //设置上一个节点到此节点的输出条件
                                        if (CollectionUtils.isNotEmpty(preAction.getTerms())) {
                                            link.setCondition(TermsConditionEvaluator.createCondition(trigger.refactorTerm("this", preAction.getTerms())));
                                        }
                                    } else if (Objects.equals(trigger.getType(), ManualTriggerProvider.PROVIDER)) {
                                        model.link(sceneNode, actionNode);
                                    }

                                    preNode = actionNode;
                                } else {
                                    if (Objects.equals(trigger.getType(), ManualTriggerProvider.PROVIDER)) {
                                        model.link(sceneNode, actionNode);
                                    }
                                }

                                model.getNodes().add(actionNode);
                                preAction = action;
                                actionIndex++;
                            }
                        }
                    }
                }


            }
        }

        return model;

    }

场景联动的执行链组装基本结束

小结

`下篇文章会对具体的一些TaskExecutorProvider的接口的类进行解析

JetLinks开源物联网平台基于Java8、Spring Boot 2.x、WebFlux、Netty、Vert.x、Reactor等开发,是一个开箱即用,可二次开发的企业级物联网基础平台。平台实现了物联网相关的众多基础功能,能帮助你快速建立物联网相关业务系统。 JetLinks开源物联网平台核心特性: 支持统一物模型管理,多种设备,多种厂家,统一管理。 统一设备连接管理,多协议适配(TCP、MQTT、UDP、CoAP、HTTP等),屏蔽网络编程复杂性,灵活接入不同厂家不同协议的设备。 灵活的规则引擎,设备告警,消息通知,数据转发。可基于SQL进行复杂的数据处理逻辑。 地理位置:统一管理地理位置信息,支持区域搜索。 数据可视化:实现拖拽配置数据图表,设备组态等。 JetLinks开源物联网平台技术栈: Spring Boot 2.2.x Spring WebFlux 响应式Web支持 R2DBC 响应式关系型数据库驱动 Project Reactor 响应式编程框架 Netty、Vert.x 高性能网络编程框架 ElasticSearch 全文检索,日志,时序数据存储 PostgreSQL 业务功能数据管理 hsweb framework 4 业务功能基础框架     JetLinks开源物联网平台 更新日志: v1.9 1、增加设备独立物模型支持,可给单独的设备配置物模型. 2、基本实现GB28181国标视频设备接入,支持直播,云台控制,级联操作.(选配模块) 3、RabbitMQ增加routeKey配置,可在配置文件中指定device.message.writer.rabbitmq.consumer-route-key和device.message.writer.rabbitmq.producer-route-key.(Pro) 4、当设置了device.message.writer.rabbitmq.consumer=false时,不创建MQ消费者.(Pro) 5、设备支持独立物模型,可单独配置设备的物模型. 6、适配tdengine 2.0.16.0,优化sql长度策略. (pro) 7、优化规则引擎编辑器,实现组件模块化动态加载.(Pro) 8、修复启动服务时,如果某个产品物模型发布失败,导致后面的产品终止发布的问题. 9、增加ignoreLatest消息头,message.addHeader("ignoreLatest",true) 忽略记录最新数据到数据库. 10、修复租户下操作设备告警提示无权限.(Pro) 11、优化租户在解绑成员时,同时解绑成员的资产信息.(Pro) 12、优化子设备消息回复处理 13、物模型属性增加存储方式功能,可配置部分属性不存储. 14、增加虚拟属性功能,可通过规则来计算出虚拟属性值.(Pro) 15、增加租户成员绑定(TenantMemberBindEvent),解绑(TenantMemberUnBindEvent)事件.可通过spring-event订阅处理此事件.(Pro) 16、优化子设备状态检查,当检查子设备状态时,将会尝试发送ChildDeviceMessage<DeviceStateCheckMessage>给网关,处理后返回ChildDeviceMessageReply<DeviceStateCheckMessageReply>. 17、增加ClickHouse设备数据存储策略支持.(Pro) 18、增加权限过滤功能,可配置禁止赋予自己没有的权限给其他用户.hsweb.permission.filter相关配置 19、设备和产品的租户绑定逻辑优化: 绑定设备时,自动绑定产品.解绑产品时,自动解绑设备.(Pro) 20、用户管理增加租户权限控制.(Pro) 21、当向keepOnline的设备发送消息时,如果原始连接已断开,将返回CONNECTION_LOST错误. 22、设置keepOnline的会话将被持久化,重启服务后自动恢复.(Pro) 23、默认关闭设备最新数据存储,通过jetlinks.device.storage.enable-last-data-in-db=true开启.(Pro) 24、属性物模型增加属性值来源,配置为手动时,在发送修改属性指令(WritePropertyMessage)时,将直接生效,不会发送到设备. 25、优化租户资产解绑逻辑,当删除数据时,解绑资产全部的绑定关系.(Pro) 26、用户管理,机构管理增加租户端支持,租户可以自己管理自己的用户和机构.(Pro)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值