import { NFNode } from "./node"; import { NFNodeCfg, NFNodeType } from "../types"; import { NFSequenceNode } from "./sequencenode"; import { NFProcess } from "../process"; import { NFError } from "../error"; /** * 包容网关(OR) * - 分叉:激活所有条件为真的出口;无条件出口始终激活;default 仅在其它皆不满足时激活 * - 汇聚:只等待本次分叉实际激活且可达该汇聚点的入边数 */ export class NFInclusiveNode extends NFNode{ /** * 出口顺序流 */ outSequences:NFSequenceNode[]; /** * 入口顺序流 */ inSequences:NFSequenceNode[]; /** * 已到达的入边数量(汇聚用) */ private arrivedCount:number = 0; /** * 期望到达的入边数量(由上游分叉按可达性写入;未设置时回退为全部入边) */ private expectedCount?:number; /** * @param cfg - 节点配置 * @param process - 所属流程 */ constructor(cfg:NFNodeCfg,process:NFProcess){ super(cfg,process); this.type = NFNodeType.INCLUSIVE; } /** * 汇聚后分叉:激活所有条件为真的出口。 * * @throws `1101` 当无匹配出口且无默认 / 无条件流 */ async run() { await super.run(); // 多入边:汇聚,等待已激活路径全部到达 if(this.inSequences.length > 1){ this.arrivedCount++; const expected = this.expectedCount ?? this.inSequences.length; if(this.arrivedCount < expected){ await this.process.saveGatewayNodeState(this.id,this.getState()); return; } // 汇聚完成,重置以便流程回环时复用 this.arrivedCount = 0; this.expectedCount = undefined; await this.process.clearGatewayNodeState(this.id); } // 分叉:多选出口(跳过 default,稍后按需补上) const toRun:NFSequenceNode[] = []; let defaultSeq:NFSequenceNode = null; for(let seq of this.outSequences){ if(seq.isDefaultFlow()){ defaultSeq = defaultSeq || seq; continue; } if(seq.match()){ toRun.push(seq); } } if(toRun.length === 0){ if(defaultSeq){ toRun.push(defaultSeq); }else{ // 兼容:未标记 default 时,尝试无条件出口 const fallback = this.outSequences.find(s=>!s.expr); if(fallback){ toRun.push(fallback); } } } if(toRun.length === 0){ throw new NFError("1101",[this.name || this.id]); } // 多分叉:按「各激活出口能否到达」为每个下游汇聚点设置期望令牌数 if(this.outSequences.length > 1){ await this.notifyDownstreamJoins(toRun); } for(let seq of toRun){ await seq.run(true); } } /** * 绑定入边 / 出边并重置汇聚计数。 * * @throws `1101` 当缺少入边或出边 */ init(){ this.outSequences = this.process.nodeManager.getSequenceNodes(this.id); this.inSequences = this.process.nodeManager.getSequenceNodes(this.id,true); if(!this.outSequences || this.outSequences.length === 0 || !this.inSequences || this.inSequences.length === 0){ throw new NFError("1101",[this.name || this.id]); } this.arrivedCount = 0; this.expectedCount = undefined; } /** * 导出运行时状态(持久化)。 * * @returns 已到达数与期望数 */ getState(){ return { arrivedCount:this.arrivedCount, expectedCount:this.expectedCount }; } /** * 恢复运行时状态(`getInstance`)。 * * @param state - 已到达数与期望数 */ restoreState(state:{arrivedCount?:number,expectedCount?:number}){ if(!state){ return; } if(typeof state.arrivedCount === 'number'){ this.arrivedCount = state.arrivedCount; } if(state.expectedCount !== undefined){ this.expectedCount = state.expectedCount; } } /** * 为下游包容汇聚网关设置期望到达数: * `expected` = 本次激活出口中图上可达该汇聚点的数量。 * * @param toRun - 本次激活的出口 */ private async notifyDownstreamJoins(toRun:NFSequenceNode[]){ const joinIds = this.collectDownstreamJoinIds(); for(let joinId of joinIds){ const join = this.process.nodeManager.getNodeById(joinId); if(!join){ continue; } let count = 0; for(let seq of toRun){ if(seq.dst && this.canReach(seq.dst, joinId)){ count++; } } if(count > 0){ join.expectedCount = count; join.arrivedCount = 0; await this.process.saveGatewayNodeState(join.id,join.getState()); } } } /** * 收集本网关下游所有多入边包容汇聚点。 * * @returns 汇聚节点 id 列表 */ private collectDownstreamJoinIds():string[]{ const visited:Set = new Set(); const joins:string[] = []; const queue:string[] = this.outSequences.map(s=>s.dst); while(queue.length > 0){ const id = queue.shift(); if(!id || visited.has(id)){ continue; } visited.add(id); const node = this.process.nodeManager.getNodeById(id); if(!node){ continue; } if(node instanceof NFInclusiveNode && node !== this && node.inSequences?.length > 1){ joins.push(node.id); } const outs = this.process.nodeManager.getSequenceNodes(id); for(let seq of outs){ if(seq?.dst){ queue.push(seq.dst); } } } return joins; } /** * 判断从图上 `fromId` 是否可能到达 `targetId`(探索全部出口)。 * * @param fromId - 起点节点 id * @param targetId - 目标节点 id * @param visited - 已访问集合 * @returns 是否可达 */ private canReach(fromId:string,targetId:string,visited?:Set):boolean{ if(fromId === targetId){ return true; } if(!visited){ visited = new Set(); } if(visited.has(fromId)){ return false; } visited.add(fromId); const outs = this.process.nodeManager.getSequenceNodes(fromId); for(let seq of outs){ if(seq?.dst && this.canReach(seq.dst, targetId, visited)){ return true; } } return false; } }