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