diff --git a/public/icons/status000.svg b/public/icons/status000.svg new file mode 100644 index 0000000..4188e96 --- /dev/null +++ b/public/icons/status000.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/status001.svg b/public/icons/status001.svg new file mode 100644 index 0000000..9d0e44e --- /dev/null +++ b/public/icons/status001.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/status010.svg b/public/icons/status010.svg new file mode 100644 index 0000000..cf6f4b7 --- /dev/null +++ b/public/icons/status010.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/status011.svg b/public/icons/status011.svg new file mode 100644 index 0000000..01f6e65 --- /dev/null +++ b/public/icons/status011.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/status100.svg b/public/icons/status100.svg new file mode 100644 index 0000000..2e55f91 --- /dev/null +++ b/public/icons/status100.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/status101.svg b/public/icons/status101.svg new file mode 100644 index 0000000..895a34a --- /dev/null +++ b/public/icons/status101.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/status110.svg b/public/icons/status110.svg new file mode 100644 index 0000000..0a35fb0 --- /dev/null +++ b/public/icons/status110.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/status111.svg b/public/icons/status111.svg new file mode 100644 index 0000000..2184d91 --- /dev/null +++ b/public/icons/status111.svg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/register-node.ts b/src/components/register-node.ts index bcfc817..605272d 100644 --- a/src/components/register-node.ts +++ b/src/components/register-node.ts @@ -201,6 +201,19 @@ G6.registerNode( name: "node-icon", }); + // status + const status = ((cfg.status || 0) as number).toString(2).padStart(3, "0"); + addShape("image", { + attrs: { + x: 204, + y: 3, + height: 18, + width: 18, + img: `./icons/status${status}.svg`, + }, + name: "status-icon", + }); + // name text addShape("text", { attrs: { @@ -219,7 +232,7 @@ G6.registerNode( if (cfg.debug) { addShape("image", { attrs: { - x: 200, + x: 192, y: 4, height: 16, width: 16, @@ -232,7 +245,7 @@ G6.registerNode( if (cfg.disabled) { addShape("image", { attrs: { - x: 200 - (cfg.debug ? 18 : 0), + x: 200 - (cfg.debug ? 25 : 8), y: 4, height: 16, width: 16, diff --git a/src/misc/b3type.ts b/src/misc/b3type.ts index a06af44..24ef9e5 100644 --- a/src/misc/b3type.ts +++ b/src/misc/b3type.ts @@ -15,6 +15,9 @@ export interface NodeArg { default?: unknown; options?: NodeArgOption[]; } + +type Status = "success" | "running" | "failure"; + export interface NodeDef { name: string; type: NodeType; @@ -25,6 +28,7 @@ export interface NodeDef { doc?: string; color?: string; icon?: string; + status?: Exclude<`${Status}` | `!${Status}` | `?${Status}` | `&${Status}`, "!running">[]; } export interface NodeModel { @@ -65,6 +69,7 @@ export interface TreeGraphData extends G6TreeGraphData { highlightInput?: boolean; highlightOutput?: boolean; highlightGray?: boolean; + status?: number; } export const unknownNodeDef: NodeDef = { diff --git a/src/misc/b3util.ts b/src/misc/b3util.ts index 0b31edd..e0cb0eb 100644 --- a/src/misc/b3util.ts +++ b/src/misc/b3util.ts @@ -207,16 +207,72 @@ export const createNode = (data: TreeGraphData, includeChildren: boolean = true) return node; }; +const toStatusFlag = (data: TreeGraphData) => { + let status = 0; + data.def.status?.forEach((s) => { + switch (s) { + case "success": + status |= 1 << 2; + break; + case "failure": + status |= 1 << 1; + break; + case "running": + status |= 1 << 0; + break; + } + }); + return status; +}; + +const appendStatusFlag = (data: TreeGraphData, childStatus: number) => { + const childSuccess = (childStatus >> 2) & 1; + const childFailure = (childStatus >> 1) & 1; + const childRunning = (childStatus >> 0) & 1; + let status = data.status!; + if (data.def.status?.length) { + data.def.status?.forEach((s) => { + switch (s) { + case "!success": + status |= childFailure << 2; + break; + case "!failure": + status |= childSuccess << 1; + break; + case "?success": + status |= childSuccess << 2; + break; + case "?failure": + status |= childFailure << 1; + break; + case "?running": + status |= childRunning << 0; + break; + } + }); + data.status = status; + } else { + data.status = status | childStatus; + } +}; + export const refreshTreeDataId = (data: TreeGraphData, id?: number) => { if (!id) { id = 1; } + const status = toStatusFlag(data); data.id = (id++).toString(); + data.status = status; if (data.children) { + let childStatus = 0; data.children.forEach((child) => { child.parent = data.id; id = refreshTreeDataId(child, id); + if (child.status && !child.disabled) { + childStatus |= child.status; + } }); + appendStatusFlag(data, childStatus); } return id; }; diff --git a/src/misc/template.ts b/src/misc/template.ts index d9dd306..d83a40a 100644 --- a/src/misc/template.ts +++ b/src/misc/template.ts @@ -4,12 +4,14 @@ export const zhNodeDef = () => { { name: "AlwaysFail", type: "Decorator", + status: ["failure", "?running"], desc: "始终返回失败", doc: "+ 只能有一个子节点,多个仅执行第一个\n+ 不管子节点是否成功都返回`失败`\n", }, { name: "AlwaysSuccess", type: "Decorator", + status: ["success", "?running"], desc: "始终返回成功", doc: "+ 只能有一个子节点,多个仅执行第一个\n+ 不管子节点是否成功都返回`成功`\n", }, @@ -17,6 +19,7 @@ export const zhNodeDef = () => { name: "Assert", type: "Decorator", desc: "断言", + status: ["success"], args: [ { name: "message", @@ -29,6 +32,7 @@ export const zhNodeDef = () => { { name: "Calculate", type: "Action", + status: ["success"], desc: "简单的数值公式计算", args: [ { @@ -43,6 +47,7 @@ export const zhNodeDef = () => { { name: "Check", type: "Condition", + status: ["success", "failure"], desc: "检查True或False", args: [ { @@ -56,12 +61,14 @@ export const zhNodeDef = () => { { name: "Clear", type: "Action", + status: ["success"], desc: "清除变量", output: ["清除的变量名"], }, { name: "Concat", type: "Action", + status: ["success", "failure"], desc: "将两个输入合并为一个数组,并返回新数组", input: ["数组1", "数组2"], output: ["新数组"], @@ -70,6 +77,7 @@ export const zhNodeDef = () => { { name: "Filter", type: "Action", + status: ["success", "failure"], desc: "返回满足条件的元素", input: ["输入数组"], output: ["变量", "新数组"], @@ -78,6 +86,7 @@ export const zhNodeDef = () => { { name: "ForEach", type: "Action", + status: ["success", "running", "failure"], desc: "遍历数组", input: ["数组"], output: ["变量"], @@ -86,6 +95,7 @@ export const zhNodeDef = () => { { name: "Includes", type: "Condition", + status: ["success", "failure"], desc: "判断元素是否在数组中", input: ["数组", "元素"], doc: "+ 若输入的元素不合法,返回`失败`\n+ 只有数组包含元素时返回`成功`,否则返回`失败`\n", @@ -93,6 +103,7 @@ export const zhNodeDef = () => { { name: "Index", type: "Action", + status: ["success", "failure"], desc: "索引输入的数组或对象", args: [ { @@ -108,18 +119,21 @@ export const zhNodeDef = () => { { name: "Inverter", type: "Decorator", + status: ["!success", "!failure", "?running"], desc: "反转子节点运行结果", doc: "+ 只能有一个子节点,多个仅执行第一个\n+ 当子节点返回`成功`时返回`失败`\n+ 当子节点返回`失败`时返回`成功`\n", }, { name: "IsNull", type: "Condition", + status: ["success", "failure"], desc: "判断变量是否不存在", input: ["判断的变量"], }, { name: "IsStatus", type: "Condition", + status: ["success", "failure"], desc: "检查子节点状态", args: [ { @@ -147,6 +161,7 @@ export const zhNodeDef = () => { { name: "Let", type: "Action", + status: ["success"], desc: "定义新的变量名", input: ["变量名"], output: ["新变量名"], @@ -154,6 +169,7 @@ export const zhNodeDef = () => { { name: "Listen", type: "Decorator", + status: ["success"], desc: "侦听事件", input: ["目标对象?"], output: ["事件参数?", "事件目标?"], @@ -191,6 +207,7 @@ export const zhNodeDef = () => { { name: "Log", type: "Action", + status: ["success"], desc: "打印日志", args: [ { @@ -227,6 +244,7 @@ export const zhNodeDef = () => { { name: "Loop", type: "Action", + status: ["success", "running", "failure"], desc: "循环执行", input: ["循环次数?"], args: [ @@ -241,30 +259,35 @@ export const zhNodeDef = () => { { name: "NotNull", type: "Condition", + status: ["success", "failure"], desc: "判断变量是否存在", input: ["判断的变量"], }, { name: "Now", type: "Action", + status: ["success"], desc: "获取当前时间", output: ["当前时间"], }, { name: "Once", type: "Decorator", + status: ["success", "failure", "?running"], desc: "只执行一次", doc: "+ 只能有一个子节点,多个仅执行第一个\n+ 第一次执行完全部子节点时返回`成功`,之后永远返回`失败`", }, { name: "Parallel", type: "Composite", + status: ["success", "?running"], desc: "并行执行", doc: "+ 并行执行所有子节点\n+ 当有子节点返回`运行中`时,返回`运行中`状态\n+ 执行完所有子节点后,返回`成功`", }, { name: "Push", type: "Action", + status: ["success", "failure"], desc: "向数组中添加元素", input: ["数组", "元素"], doc: "+ 当变量`数组`不是数组类型时返回`失败`\n+ 其余返回`成功`\n", @@ -272,6 +295,7 @@ export const zhNodeDef = () => { { name: "Random", type: "Action", + status: ["success"], desc: "返回一个随机数", input: ["最小值?", "最大值?"], args: [ @@ -296,6 +320,7 @@ export const zhNodeDef = () => { { name: "RandomIndex", type: "Action", + status: ["success", "failure"], desc: "随机返回输入的其中一个!", input: ["输入目标"], output: ["随机目标"], @@ -304,6 +329,7 @@ export const zhNodeDef = () => { { name: "RepeatUntilFailure", type: "Decorator", + status: ["!success", "failure", "?running"], desc: "一直尝试直到子节点返回失败", input: ["最大循环次数?"], args: [ @@ -318,6 +344,7 @@ export const zhNodeDef = () => { { name: "RepeatUntilSuccess", type: "Decorator", + status: ["?success", "failure", "?running"], desc: "一直尝试直到子节点返回成功", input: ["最大循环次数?"], args: [ @@ -333,11 +360,13 @@ export const zhNodeDef = () => { name: "Selector", type: "Composite", desc: "选择执行", + status: ["?success", "?failure", "?running"], doc: "+ 一直往下执行,直到有子节点返回`成功`则返回`成功`\n+ 若全部节点返回`失败`则返回`失败`", }, { name: "Sequence", type: "Composite", + status: ["?success", "?failure", "?running"], desc: "顺序执行", doc: "+ 一直往下执行,只有当所有子节点都返回`成功`, 才返回`成功`\n+ 若子节点返回`失败`,则直接返回`失败`状态\n+ 其余情况返回`运行中`状态\n", }, @@ -345,6 +374,7 @@ export const zhNodeDef = () => { name: "Timeout", type: "Decorator", desc: "超时", + status: ["?success", "?running", "failure"], args: [ { name: "time", @@ -357,6 +387,7 @@ export const zhNodeDef = () => { { name: "Wait", type: "Action", + status: ["success", "running"], desc: "等待", input: ["等待时间?"], args: [