From 3d6e065dd5dd6c3a284c74b4614e5700e7c8c7e5 Mon Sep 17 00:00:00 2001 From: JGerla Date: Tue, 13 Jan 2026 11:32:23 +0100 Subject: [PATCH] feat: added rule to prevent one belief being connected to both inferredBelief inputs ref: N25B-433 --- .../visualProgrammingUI/HandleRules.ts | 1 + .../nodes/BasicBeliefNode.tsx | 4 ++- .../nodes/BeliefGlobals.ts | 25 ++++++++++++++++++ .../visualProgrammingUI/nodes/BeliefReduce.ts | 12 --------- .../nodes/InferredBeliefNode.tsx | 26 ++++++++++++------- .../visualProgrammingUI/nodes/NormNode.tsx | 4 +-- .../visualProgrammingUI/nodes/TriggerNode.tsx | 6 ++--- 7 files changed, 51 insertions(+), 27 deletions(-) create mode 100644 src/pages/VisProgPage/visualProgrammingUI/nodes/BeliefGlobals.ts delete mode 100644 src/pages/VisProgPage/visualProgrammingUI/nodes/BeliefReduce.ts diff --git a/src/pages/VisProgPage/visualProgrammingUI/HandleRules.ts b/src/pages/VisProgPage/visualProgrammingUI/HandleRules.ts index a04282c..aca415e 100644 --- a/src/pages/VisProgPage/visualProgrammingUI/HandleRules.ts +++ b/src/pages/VisProgPage/visualProgrammingUI/HandleRules.ts @@ -43,3 +43,4 @@ export const noSelfConnections : HandleRule = } + diff --git a/src/pages/VisProgPage/visualProgrammingUI/nodes/BasicBeliefNode.tsx b/src/pages/VisProgPage/visualProgrammingUI/nodes/BasicBeliefNode.tsx index 1891f6a..fd3a115 100644 --- a/src/pages/VisProgPage/visualProgrammingUI/nodes/BasicBeliefNode.tsx +++ b/src/pages/VisProgPage/visualProgrammingUI/nodes/BasicBeliefNode.tsx @@ -10,6 +10,7 @@ import {allowOnlyConnectionsFromType} from "../HandleRules.ts"; import useFlowStore from '../VisProgStores.tsx'; import { TextField } from '../../../../components/TextField.tsx'; import { MultilineTextField } from '../../../../components/MultilineTextField.tsx'; +import {noMatchingLeftRightBelief} from "./BeliefGlobals.ts"; /** * The default data structure for a BasicBelief node @@ -185,7 +186,8 @@ export default function BasicBeliefNode(props: NodeProps) { )} diff --git a/src/pages/VisProgPage/visualProgrammingUI/nodes/BeliefGlobals.ts b/src/pages/VisProgPage/visualProgrammingUI/nodes/BeliefGlobals.ts new file mode 100644 index 0000000..1299052 --- /dev/null +++ b/src/pages/VisProgPage/visualProgrammingUI/nodes/BeliefGlobals.ts @@ -0,0 +1,25 @@ +import { type Node } from '@xyflow/react'; +import {type HandleRule, ruleResult} from "../HandleRuleLogic.ts"; +import useFlowStore from "../VisProgStores.tsx"; +import {BasicBeliefReduce} from "./BasicBeliefNode.tsx"; +import {type InferredBeliefNodeData, InferredBeliefReduce} from "./InferredBeliefNode.tsx"; + +export function BeliefGlobals(beliefNode: Node, nodes: Node[]) { + switch (beliefNode.type) { + case 'basic_belief': + return BasicBeliefReduce(beliefNode, nodes); + case 'inferred_belief': + return InferredBeliefReduce(beliefNode, nodes); + } +} + +export const noMatchingLeftRightBelief : HandleRule = (connection, _)=> { + const { nodes } = useFlowStore.getState(); + const thisNode = nodes.find(node => node.id === connection.target && node.type === 'inferred_belief'); + if (!thisNode) return ruleResult.satisfied; + const iBelief = (thisNode.data as InferredBeliefNodeData).inferredBelief; + + return (iBelief.left === connection.source || iBelief.right === connection.source) + ? ruleResult.notSatisfied("Connecting one belief to both input handles of an inferred belief node is not allowed") + : ruleResult.satisfied; +} \ No newline at end of file diff --git a/src/pages/VisProgPage/visualProgrammingUI/nodes/BeliefReduce.ts b/src/pages/VisProgPage/visualProgrammingUI/nodes/BeliefReduce.ts deleted file mode 100644 index 0710fe7..0000000 --- a/src/pages/VisProgPage/visualProgrammingUI/nodes/BeliefReduce.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { type Node } from '@xyflow/react'; -import {BasicBeliefReduce} from "./BasicBeliefNode.tsx"; -import {InferredBeliefReduce} from "./InferredBeliefNode.tsx"; - -export function BeliefReduce(beliefNode: Node, nodes: Node[]) { - switch (beliefNode.type) { - case 'basic_belief': - return BasicBeliefReduce(beliefNode, nodes); - case 'inferred_belief': - return InferredBeliefReduce(beliefNode, nodes); - } -} \ No newline at end of file diff --git a/src/pages/VisProgPage/visualProgrammingUI/nodes/InferredBeliefNode.tsx b/src/pages/VisProgPage/visualProgrammingUI/nodes/InferredBeliefNode.tsx index 7ad723a..9e6df6b 100644 --- a/src/pages/VisProgPage/visualProgrammingUI/nodes/InferredBeliefNode.tsx +++ b/src/pages/VisProgPage/visualProgrammingUI/nodes/InferredBeliefNode.tsx @@ -7,7 +7,7 @@ import { import {useState} from "react"; import { Toolbar } from '../components/NodeComponents.tsx'; import {type HandleRule, type RuleResult, ruleResult} from "../HandleRuleLogic.ts"; -import {BeliefReduce} from "./BeliefReduce.ts"; +import {BeliefGlobals, noMatchingLeftRightBelief} from "./BeliefGlobals.ts"; import {MultiConnectionHandle, SingleConnectionHandle} from "../components/RuleBasedHandle.tsx"; import {allowOnlyConnectionsFromType} from "../HandleRules.ts"; import useFlowStore from "../VisProgStores.tsx"; @@ -30,7 +30,7 @@ export type InferredBeliefNodeData = { * and a left and right BeliefNode (can be both an inferred and a basic belief) * in the form of their corresponding id's */ -type InferredBelief = { +export type InferredBelief = { left: string | undefined, operator: boolean, right: string | undefined, @@ -106,12 +106,16 @@ const noBeliefCycles : HandleRule = (connection, _): RuleResult => { * @returns {RuleResult} */ function checkForCycle(targetNodeId: string, currentNodeId: string) : RuleResult { - const outgoingBeliefs = getOutgoers({id: currentNodeId}, nodes, edges).filter(node => node.type === 'inferred_belief'); + const outgoingBeliefs = getOutgoers({id: currentNodeId}, nodes, edges) + .filter(node => node.type === 'inferred_belief'); + if (outgoingBeliefs.length === 0) return ruleResult.satisfied; if (outgoingBeliefs.some(node => node.id === targetNodeId)) return ruleResult .notSatisfied(defaultErrorMessage); - const next = outgoingBeliefs.map(node => checkForCycle(targetNodeId, node.id)).find(result => !result.isSatisfied); + const next = outgoingBeliefs.map(node => checkForCycle(targetNodeId, node.id)) + .find(result => !result.isSatisfied); + return next ? next : ruleResult.satisfied; @@ -123,6 +127,7 @@ const noBeliefCycles : HandleRule = (connection, _): RuleResult => { } + /** * Defines how an InferredBelief node should be rendered * @param {NodeProps} props - Node properties provided by React Flow, including `id` and `data`. @@ -169,17 +174,20 @@ export default function InferredBeliefNode(props: NodeProps) {/* outgoing connections */} {/* incoming connections */} @@ -202,9 +210,9 @@ export function InferredBeliefReduce(node: Node, nodes: Node[]) { const result: Record = { id: node.id, - left: BeliefReduce(leftBelief, nodes), + left: BeliefGlobals(leftBelief, nodes), operator: data.inferredBelief.operator ? "AND" : "OR", - right: BeliefReduce(rightBelief, nodes), + right: BeliefGlobals(rightBelief, nodes), }; return result diff --git a/src/pages/VisProgPage/visualProgrammingUI/nodes/NormNode.tsx b/src/pages/VisProgPage/visualProgrammingUI/nodes/NormNode.tsx index d54f6d1..380f1ad 100644 --- a/src/pages/VisProgPage/visualProgrammingUI/nodes/NormNode.tsx +++ b/src/pages/VisProgPage/visualProgrammingUI/nodes/NormNode.tsx @@ -9,7 +9,7 @@ import { TextField } from '../../../../components/TextField'; import {MultiConnectionHandle, SingleConnectionHandle} from "../components/RuleBasedHandle.tsx"; import {allowOnlyConnectionsFromHandle, allowOnlyConnectionsFromType} from "../HandleRules.ts"; import useFlowStore from '../VisProgStores'; -import {BeliefReduce} from "./BeliefReduce.ts"; +import {BeliefGlobals} from "./BeliefGlobals.ts"; /** * The default data dot a phase node @@ -108,7 +108,7 @@ export function NormReduce(node: Node, nodes: Node[]) { const conditionNode = nodes.find((node) => node.id === data.condition); // In case something went wrong, and our condition doesn't actually exist; if (conditionNode == undefined) return result; - result["condition"] = BeliefReduce(conditionNode, nodes) + result["condition"] = BeliefGlobals(conditionNode, nodes) } return result } diff --git a/src/pages/VisProgPage/visualProgrammingUI/nodes/TriggerNode.tsx b/src/pages/VisProgPage/visualProgrammingUI/nodes/TriggerNode.tsx index e0d93ba..2bdc586 100644 --- a/src/pages/VisProgPage/visualProgrammingUI/nodes/TriggerNode.tsx +++ b/src/pages/VisProgPage/visualProgrammingUI/nodes/TriggerNode.tsx @@ -12,7 +12,7 @@ import {allowOnlyConnectionsFromHandle, allowOnlyConnectionsFromType} from "../H import useFlowStore from '../VisProgStores'; import { PlanReduce, type Plan } from '../components/Plan'; import PlanEditorDialog from '../components/PlanEditor'; -import {BeliefReduce} from "./BeliefReduce.ts"; +import {BeliefGlobals} from "./BeliefGlobals.ts"; /** * The default data structure for a Trigger node @@ -86,13 +86,13 @@ export default function TriggerNode(props: NodeProps) { /** * Reduces each Trigger, including its children down into its core data. * @param node - The Trigger node to reduce. - * @param _nodes - The list of all nodes in the current flow graph. + * @param nodes - The list of all nodes in the current flow graph. * @returns A simplified object containing the node label and its list of triggers. */ export function TriggerReduce(node: Node, nodes: Node[]) { const data = node.data as TriggerNodeData; const conditionNode = data.condition ? nodes.find((n)=>n.id===data.condition) : undefined - const conditionData = conditionNode ? BeliefReduce(conditionNode, nodes) : "" + const conditionData = conditionNode ? BeliefGlobals(conditionNode, nodes) : "" return { id: node.id, condition: conditionData, // Make sure we have a condition before reducing, or default to ""