diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts index c4f9e6f..4abfcd3 100644 --- a/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts @@ -83,13 +83,13 @@ export function useSpawnHandler() { }, }; - if (action.triggers[0].triggeredAsset?.triggeredModel.modelUuid && - action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid && - action.triggers[0].triggeredAsset?.triggeredAction?.actionUuid + if (action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid && + action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid && + action.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid ) { newMaterial.next = { - modelUuid: action.triggers[0].triggeredAsset?.triggeredModel.modelUuid, - pointUuid: action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid, + modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid, + pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid, } } diff --git a/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts b/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts index 87e8ee1..3eb79af 100644 --- a/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts +++ b/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts @@ -55,9 +55,9 @@ export function useRetrieveHandler() { actionUuid: action.actionUuid }, current: { - modelUuid: action.triggers[0].triggeredAsset.triggeredModel.modelUuid, - pointUuid: action.triggers[0].triggeredAsset.triggeredPoint.pointUuid, - actionUuid: action.triggers[0].triggeredAsset.triggeredAction.actionUuid + modelUuid: action.triggers[0]?.triggeredAsset.triggeredModel.modelUuid, + pointUuid: action.triggers[0]?.triggeredAsset.triggeredPoint.pointUuid, + actionUuid: action.triggers[0]?.triggeredAsset.triggeredAction.actionUuid }, }; @@ -110,13 +110,13 @@ export function useRetrieveHandler() { return; } - if (retrieval.action.triggers.length === 0 || !retrieval.action.triggers[0].triggeredAsset) { + if (retrieval.action.triggers.length === 0 || !retrieval.action.triggers[0]?.triggeredAsset) { return; } const triggeredModel = getEventByModelUuid( selectedProduct.productId, - retrieval.action.triggers[0].triggeredAsset.triggeredModel.modelUuid + retrieval.action.triggers[0]?.triggeredAsset.triggeredModel.modelUuid ); if (!triggeredModel) return; @@ -160,10 +160,10 @@ export function useRetrieveHandler() { const lastMaterial = getLastMaterial(storageUnit.modelUuid); if (lastMaterial) { - if (retrieval.action.triggers[0].triggeredAsset.triggeredAction?.actionUuid) { - const action = getActionByUuid(selectedProduct.productId, retrieval.action.triggers[0].triggeredAsset.triggeredAction.actionUuid); - if (action && action.triggers.length > 0 && action.triggers[0].triggeredAsset?.triggeredModel.modelUuid) { - const model = getEventByModelUuid(selectedProduct.productId, action.triggers[0].triggeredAsset.triggeredModel.modelUuid); + if (retrieval.action.triggers[0]?.triggeredAsset.triggeredAction?.actionUuid) { + const action = getActionByUuid(selectedProduct.productId, retrieval.action.triggers[0]?.triggeredAsset.triggeredAction.actionUuid); + if (action && action.triggers.length > 0 && action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid) { + const model = getEventByModelUuid(selectedProduct.productId, action.triggers[0]?.triggeredAsset.triggeredModel.modelUuid); if (model) { if (model.type === 'vehicle') { const vehicle = getVehicleById(model.modelUuid); @@ -178,7 +178,7 @@ export function useRetrieveHandler() { addCurrentAction( triggeredModel.modelUuid, - retrieval.action.triggers[0].triggeredAsset.triggeredAction?.actionUuid ?? '', + retrieval.action.triggers[0]?.triggeredAsset.triggeredAction?.actionUuid ?? '', material.materialType, material.materialId ); @@ -195,7 +195,7 @@ export function useRetrieveHandler() { addCurrentAction( triggeredModel.modelUuid, - retrieval.action.triggers[0].triggeredAsset.triggeredAction?.actionUuid ?? '', + retrieval.action.triggers[0]?.triggeredAsset.triggeredAction?.actionUuid ?? '', material.materialType, material.materialId ); diff --git a/app/src/modules/simulation/conveyor/eventManager/useConveyorEventManager.ts b/app/src/modules/simulation/conveyor/eventManager/useConveyorEventManager.ts new file mode 100644 index 0000000..bdc014c --- /dev/null +++ b/app/src/modules/simulation/conveyor/eventManager/useConveyorEventManager.ts @@ -0,0 +1,65 @@ +import { useEffect, useRef } from 'react'; +import { useFrame } from '@react-three/fiber'; +import { useConveyorStore } from '../../../../store/simulation/useConveyorStore'; + +type ConveyorCallback = { + conveyorId: string; + callback: () => void; +}; + +export function useConveyorEventManager() { + const { getConveyorById } = useConveyorStore(); + const callbacksRef = useRef<ConveyorCallback[]>([]); + const isMonitoringRef = useRef(false); + + // Add a new conveyor to monitor + const addConveyorToMonitor = (conveyorId: string, callback: () => void) => { + // Avoid duplicates + if (!callbacksRef.current.some((entry) => entry.conveyorId === conveyorId)) { + callbacksRef.current.push({ conveyorId, callback }); + } + + // Start monitoring if not already running + if (!isMonitoringRef.current) { + isMonitoringRef.current = true; + } + }; + + // Remove a conveyor from monitoring + const removeConveyorFromMonitor = (conveyorId: string) => { + callbacksRef.current = callbacksRef.current.filter( + (entry) => entry.conveyorId !== conveyorId + ); + + // Stop monitoring if no more conveyors to track + if (callbacksRef.current.length === 0) { + isMonitoringRef.current = false; + } + }; + + // Check conveyor states every frame + useFrame(() => { + if (!isMonitoringRef.current || callbacksRef.current.length === 0) return; + + callbacksRef.current.forEach(({ conveyorId, callback }) => { + const conveyor = getConveyorById(conveyorId); + if (conveyor?.isPaused === false) { + callback(); + removeConveyorFromMonitor(conveyorId); // Remove after triggering + } + }); + }); + + // Cleanup on unmount + useEffect(() => { + return () => { + callbacksRef.current = []; + isMonitoringRef.current = false; + }; + }, []); + + return { + addConveyorToMonitor, + removeConveyorFromMonitor, + }; +} \ No newline at end of file diff --git a/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx b/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx index fa1b33b..76277f0 100644 --- a/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx +++ b/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx @@ -1,10 +1,10 @@ -import React, { useEffect } from 'react' +import { useEffect } from 'react' import { useMaterialStore } from '../../../../../store/simulation/useMaterialStore'; import { useConveyorStore } from '../../../../../store/simulation/useConveyorStore'; import { useResetButtonStore } from '../../../../../store/usePlayButtonStore'; -import { findConveyorInSequences } from '../../../simulator/functions/findConveyorInSequences'; import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore'; import { useProductStore } from '../../../../../store/simulation/useProductStore'; +// import { findConveyorSubsequence } from '../../../simulator/functions/getConveyorSequencesInProduct'; function ConveyorInstance({ conveyor }: { conveyor: ConveyorStatus }) { const { getProductById } = useProductStore(); @@ -17,28 +17,51 @@ function ConveyorInstance({ conveyor }: { conveyor: ConveyorStatus }) { const product = getProductById(selectedProduct.productId); if (!product) return; - const sequenceInfo = findConveyorInSequences(product, conveyor.modelUuid); - if (!sequenceInfo) return; - - const { currentSubSequence } = sequenceInfo; const conveyorMaterials = getMaterialsByCurrentModelUuid(conveyor.modelUuid); + if (conveyorMaterials && conveyorMaterials?.length > 0) { - if (conveyorMaterials && conveyorMaterials.length > 0) { - const shouldPauseSubsequence = currentSubSequence.some(subConveyor => { - if (subConveyor.type !== 'transfer') return false; - const subMaterials = getMaterialsByCurrentModelUuid(subConveyor.modelUuid); - return subMaterials?.some(m => m.isPaused) ?? false; - }); + const hasPausedMaterials = conveyorMaterials.some(material => material.isPaused); - currentSubSequence.forEach(subConveyor => { - if (subConveyor.type === 'transfer') { - setConveyorPaused(subConveyor.modelUuid, shouldPauseSubsequence); - } - }); + if (hasPausedMaterials) { + setConveyorPaused(conveyor.modelUuid, true); + } else { + setConveyorPaused(conveyor.modelUuid, false); + } + } else { + setConveyorPaused(conveyor.modelUuid, false); } + + // const conveyorSubsequence = findConveyorSubsequence(product, conveyor.modelUuid); + + // if (!conveyorSubsequence || !conveyorSubsequence.currentSubSequence) { + // setConveyorPaused(conveyor.modelUuid, false); + // return; + // } + + // const { currentSubSequence } = conveyorSubsequence; + + // const allMaterials = currentSubSequence.flatMap(event => + // getMaterialsByCurrentModelUuid(event.modelUuid) + // ); + + // const hasPausedMaterials = allMaterials.some(mat => mat?.isPaused); + + // currentSubSequence.forEach(event => { + // if (event.type === 'transfer') { + // setConveyorPaused(event.modelUuid, hasPausedMaterials); + // } + // }); + }, [materials, conveyor.modelUuid, getMaterialsByCurrentModelUuid, setConveyorPaused, isReset, selectedProduct.productId, getProductById]); - return null; -} + useEffect(() => { + // console.log('conveyor: ', conveyor); + }, [conveyor]) -export default React.memo(ConveyorInstance); \ No newline at end of file + return ( + <> + </> + ) +}; + +export default ConveyorInstance \ No newline at end of file diff --git a/app/src/modules/simulation/events/arrows/arrows.tsx b/app/src/modules/simulation/events/arrows/arrows.tsx new file mode 100644 index 0000000..fdb0c18 --- /dev/null +++ b/app/src/modules/simulation/events/arrows/arrows.tsx @@ -0,0 +1,207 @@ +import * as THREE from "three"; +import { useMemo, useRef } from "react"; +import { useThree } from "@react-three/fiber"; + +interface ConnectionLine { + id: string; + startPointUuid: string; + endPointUuid: string; + trigger: TriggerSchema; +} + +export function Arrows({ connections }: { connections: ConnectionLine[] }) { + const groupRef = useRef<THREE.Group>(null); + const { scene } = useThree(); + + const getWorldPositionFromScene = (uuid: string): THREE.Vector3 | null => { + const obj = scene.getObjectByProperty("uuid", uuid); + if (!obj) return null; + const pos = new THREE.Vector3(); + obj.getWorldPosition(pos); + return pos; + }; + + const createArrow = ( + key: string, + fullCurve: THREE.QuadraticBezierCurve3, + centerT: number, + segmentSize: number, + scale: number, + reverse = false + ) => { + const t1 = Math.max(0, centerT - segmentSize / 2); + const t2 = Math.min(1, centerT + segmentSize / 2); + const subCurve = getSubCurve(fullCurve, t1, t2, reverse); + + const shaftGeometry = new THREE.TubeGeometry(subCurve, 8, 0.01 * scale, 8, false); + + const end = subCurve.getPoint(1); + const tangent = subCurve.getTangent(1).normalize(); + + const arrowHeadLength = 0.15 * scale; + const arrowRadius = 0.01 * scale; + const arrowHeadRadius = arrowRadius * 2.5; + + const headGeometry = new THREE.ConeGeometry(arrowHeadRadius, arrowHeadLength, 8); + headGeometry.translate(0, arrowHeadLength / 2, 0); + + const rotation = new THREE.Quaternion().setFromUnitVectors( + new THREE.Vector3(0, 1, 0), + tangent + ); + + return ( + <group key={key}> + <mesh geometry={shaftGeometry}> + <meshStandardMaterial color="#42a5f5" /> + </mesh> + <mesh position={end} quaternion={rotation} geometry={headGeometry}> + <meshStandardMaterial color="#42a5f5" /> + </mesh> + </group> + ); + }; + + const getSubCurve = ( + curve: THREE.Curve<THREE.Vector3>, + t1: number, + t2: number, + reverse = false + ) => { + const divisions = 10; + const subPoints = Array.from({ length: divisions + 1 }, (_, i) => { + const t = THREE.MathUtils.lerp(t1, t2, i / divisions); + return curve.getPoint(t); + }); + + if (reverse) subPoints.reverse(); + + return new THREE.CatmullRomCurve3(subPoints); + }; + + const arrowGroups = connections.flatMap((connection) => { + const start = getWorldPositionFromScene(connection.startPointUuid); + const end = getWorldPositionFromScene(connection.endPointUuid); + if (!start || !end) return []; + + const isBidirectional = connections.some( + (other) => + other.startPointUuid === connection.endPointUuid && + other.endPointUuid === connection.startPointUuid + ); + + if (isBidirectional && connection.startPointUuid < connection.endPointUuid) { + const distance = start.distanceTo(end); + const heightFactor = Math.max(0.5, distance * 0.2); + const control = new THREE.Vector3( + (start.x + end.x) / 2, + Math.max(start.y, end.y) + heightFactor, + (start.z + end.z) / 2 + ); + const curve = new THREE.QuadraticBezierCurve3(start, control, end); + const scale = THREE.MathUtils.clamp(distance * 0.75, 0.5, 3); + + return [ + createArrow(connection.id + "-fwd", curve, 0.33, 0.25, scale, true), + createArrow(connection.id + "-bwd", curve, 0.66, 0.25, scale, false), + ]; + } + + if (!isBidirectional) { + const distance = start.distanceTo(end); + const heightFactor = Math.max(0.5, distance * 0.2); + const control = new THREE.Vector3( + (start.x + end.x) / 2, + Math.max(start.y, end.y) + heightFactor, + (start.z + end.z) / 2 + ); + const curve = new THREE.QuadraticBezierCurve3(start, control, end); + const scale = THREE.MathUtils.clamp(distance * 0.75, 0.5, 5); + + return [ + createArrow(connection.id, curve, 0.5, 0.3, scale) + ]; + } + + return []; + }); + + return <group ref={groupRef} name="connectionArrows">{arrowGroups}</group>; +} + +export function ArrowOnQuadraticBezier({ + start, + mid, + end, + color = "#42a5f5", +}: { + start: number[]; + mid: number[]; + end: number[]; + color?: string; +}) { + const minScale = 0.5; + const maxScale = 5; + const segmentSize = 0.3; + + const startVec = useMemo(() => new THREE.Vector3(...start), [start]); + const midVec = useMemo(() => new THREE.Vector3(...mid), [mid]); + const endVec = useMemo(() => new THREE.Vector3(...end), [end]); + + const fullCurve = useMemo( + () => new THREE.QuadraticBezierCurve3(startVec, midVec, endVec), + [startVec, midVec, endVec] + ); + + const distance = useMemo(() => startVec.distanceTo(endVec), [startVec, endVec]); + const scale = useMemo(() => THREE.MathUtils.clamp(distance * 0.75, minScale, maxScale), [distance]); + + const arrowHeadLength = 0.15 * scale; + const arrowRadius = 0.01 * scale; + const arrowHeadRadius = arrowRadius * 2.5; + + const curveLength = useMemo(() => fullCurve.getLength(), [fullCurve]); + const arrowHeadTOffset = useMemo(() => Math.min(arrowHeadLength / curveLength, 0.5), [arrowHeadLength, curveLength]); + + const endT = 1 - arrowHeadTOffset; + + const subCurve = useMemo(() => { + const t1 = Math.max(0, endT - segmentSize / 2); + const t2 = Math.min(1, endT + segmentSize / 2); + const divisions = 10; + const subPoints = Array.from({ length: divisions + 1 }, (_, i) => { + const t = THREE.MathUtils.lerp(t1, t2, i / divisions); + return fullCurve.getPoint(t); + }); + return new THREE.CatmullRomCurve3(subPoints); + }, [fullCurve, endT, segmentSize]); + + const tubeGeometry = useMemo( + () => new THREE.TubeGeometry(subCurve, 20, arrowRadius, 8, false), + [subCurve, arrowRadius] + ); + + const arrowPosition = useMemo(() => fullCurve.getPoint(1), [fullCurve]); + const arrowTangent = useMemo(() => fullCurve.getTangent(1).normalize(), [fullCurve]); + + const arrowRotation = useMemo(() => { + return new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), arrowTangent); + }, [arrowTangent]); + + const coneGeometry = useMemo(() => { + const geom = new THREE.ConeGeometry(arrowHeadRadius, arrowHeadLength, 8); + geom.translate(0, arrowHeadLength / 2, 0); + return geom; + }, [arrowHeadRadius, arrowHeadLength]); + + return ( + <group name="ArrowWithTube"> + <mesh name="ArrowWithTube" geometry={tubeGeometry}> + <meshStandardMaterial color={color} /> + </mesh> + <mesh name="ArrowWithTube" position={arrowPosition} quaternion={arrowRotation} geometry={coneGeometry}> + <meshStandardMaterial color={color} /> + </mesh> + </group> + ); +} \ No newline at end of file diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx index 05cfcf8..22ca63d 100644 --- a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx +++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx @@ -1,17 +1,22 @@ -import React, { useEffect, useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import * as THREE from "three"; import { useEventsStore } from "../../../../../store/simulation/useEventsStore"; +import { useProductStore } from "../../../../../store/simulation/useProductStore"; +import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; import useModuleStore, { useSubModuleStore } from "../../../../../store/useModuleStore"; import { TransformControls } from "@react-three/drei"; import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys"; import { useSelectedEventSphere, useSelectedEventData, } from "../../../../../store/simulation/useSimulationStore"; import { useThree } from "@react-three/fiber"; import { usePlayButtonStore } from "../../../../../store/usePlayButtonStore"; +import { upsertProductOrEventApi } from "../../../../../services/simulation/UpsertProductOrEventApi"; function PointsCreator() { const { gl, raycaster, scene, pointer, camera } = useThree(); const { subModule } = useSubModuleStore(); const { events, updatePoint, getPointByUuid, getEventByModelUuid } = useEventsStore(); + const { getEventByModelUuid: getEventByModelUuidFromProduct, updatePoint: updatePointFromProduct, getEventByModelUuid: getEventByModelUuidFromProduct2 } = useProductStore(); + const { selectedProduct } = useSelectedProduct(); const { activeModule } = useModuleStore(); const transformRef = useRef<any>(null); const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null); @@ -20,6 +25,23 @@ function PointsCreator() { const { setSelectedEventData, clearSelectedEventData } = useSelectedEventData(); const { isPlaying } = usePlayButtonStore(); + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + + const updateBackend = ( + productName: string, + productId: string, + organization: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productId: productId, + organization: organization, + eventDatas: eventData + }) + } + useEffect(() => { if (selectedEventSphere) { const eventData = getEventByModelUuid( @@ -68,11 +90,30 @@ function PointsCreator() { selectedEventSphere.position.y, selectedEventSphere.position.z, ]; - updatePoint( - selectedEventSphere.userData.modelUuid, - selectedEventSphere.userData.pointUuid, - point - ); + + const event = getEventByModelUuidFromProduct(selectedProduct.productId, selectedEventSphere.userData.modelUuid); + + if (event && selectedProduct.productId !== '') { + const updatedEvent = updatePointFromProduct( + selectedProduct.productId, + selectedEventSphere.userData.modelUuid, + selectedEventSphere.userData.pointUuid, + point + ) + if (updatedEvent) { + updatePoint( + selectedEventSphere.userData.modelUuid, + selectedEventSphere.userData.pointUuid, + point + ) + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + updatedEvent + ); + } + } } }; @@ -126,14 +167,20 @@ function PointsCreator() { <> <group name="EventPointsGroup" visible={!isPlaying}> {events.map((event, index) => { - if (event.type === "transfer") { + const updatedEvent = selectedProduct.productId !== '' + ? getEventByModelUuidFromProduct2(selectedProduct.productId, event.modelUuid) + : null; + + const usedEvent = updatedEvent || event; + + if (usedEvent.type === "transfer") { return ( <group - key={`${index}-${event.modelUuid}`} - position={event.position} - rotation={event.rotation} + key={`${index}-${usedEvent.modelUuid}`} + position={usedEvent.position} + rotation={usedEvent.rotation} > - {event.points.map((point, j) => ( + {usedEvent.points.map((point, j) => ( <mesh name="Event-Sphere" uuid={point.uuid} @@ -147,7 +194,7 @@ function PointsCreator() { key={`${index}-${point.uuid}`} position={new THREE.Vector3(...point.position)} userData={{ - modelUuid: event.modelUuid, + modelUuid: usedEvent.modelUuid, pointUuid: point.uuid, }} > @@ -157,27 +204,28 @@ function PointsCreator() { ))} </group> ); - } else if (event.type === "vehicle") { + } else if (usedEvent.type === "vehicle") { + const point = usedEvent.point; return ( <group - key={`${index}-${event.modelUuid}`} - position={event.position} - rotation={event.rotation} + key={`${index}-${usedEvent.modelUuid}`} + position={usedEvent.position} + rotation={usedEvent.rotation} > <mesh name="Event-Sphere" - uuid={event.point.uuid} - ref={(el) => (sphereRefs.current[event.point.uuid] = el!)} + uuid={point.uuid} + ref={(el) => (sphereRefs.current[point.uuid] = el!)} onClick={(e) => { e.stopPropagation(); setSelectedEventSphere( - sphereRefs.current[event.point.uuid] + sphereRefs.current[point.uuid] ); }} - position={new THREE.Vector3(...event.point.position)} + position={new THREE.Vector3(...point.position)} userData={{ - modelUuid: event.modelUuid, - pointUuid: event.point.uuid, + modelUuid: usedEvent.modelUuid, + pointUuid: point.uuid, }} > <sphereGeometry args={[0.1, 16, 16]} /> @@ -185,27 +233,28 @@ function PointsCreator() { </mesh> </group> ); - } else if (event.type === "roboticArm") { + } else if (usedEvent.type === "roboticArm") { + const point = usedEvent.point; return ( <group - key={`${index}-${event.modelUuid}`} - position={event.position} - rotation={event.rotation} + key={`${index}-${usedEvent.modelUuid}`} + position={usedEvent.position} + rotation={usedEvent.rotation} > <mesh name="Event-Sphere" - uuid={event.point.uuid} - ref={(el) => (sphereRefs.current[event.point.uuid] = el!)} + uuid={point.uuid} + ref={(el) => (sphereRefs.current[point.uuid] = el!)} onClick={(e) => { e.stopPropagation(); setSelectedEventSphere( - sphereRefs.current[event.point.uuid] + sphereRefs.current[point.uuid] ); }} - position={new THREE.Vector3(...event.point.position)} + position={new THREE.Vector3(...point.position)} userData={{ - modelUuid: event.modelUuid, - pointUuid: event.point.uuid, + modelUuid: usedEvent.modelUuid, + pointUuid: point.uuid, }} > <sphereGeometry args={[0.1, 16, 16]} /> @@ -213,27 +262,28 @@ function PointsCreator() { </mesh> </group> ); - } else if (event.type === "machine") { + } else if (usedEvent.type === "machine") { + const point = usedEvent.point; return ( <group - key={`${index}-${event.modelUuid}`} - position={event.position} - rotation={event.rotation} + key={`${index}-${usedEvent.modelUuid}`} + position={usedEvent.position} + rotation={usedEvent.rotation} > <mesh name="Event-Sphere" - uuid={event.point.uuid} - ref={(el) => (sphereRefs.current[event.point.uuid] = el!)} + uuid={point.uuid} + ref={(el) => (sphereRefs.current[point.uuid] = el!)} onClick={(e) => { e.stopPropagation(); setSelectedEventSphere( - sphereRefs.current[event.point.uuid] + sphereRefs.current[point.uuid] ); }} - position={new THREE.Vector3(...event.point.position)} + position={new THREE.Vector3(...point.position)} userData={{ - modelUuid: event.modelUuid, - pointUuid: event.point.uuid, + modelUuid: usedEvent.modelUuid, + pointUuid: point.uuid, }} > <sphereGeometry args={[0.1, 16, 16]} /> @@ -241,27 +291,28 @@ function PointsCreator() { </mesh> </group> ); - } else if (event.type === "storageUnit") { + } else if (usedEvent.type === "storageUnit") { + const point = usedEvent.point; return ( <group - key={`${index}-${event.modelUuid}`} - position={event.position} - rotation={event.rotation} + key={`${index}-${usedEvent.modelUuid}`} + position={usedEvent.position} + rotation={usedEvent.rotation} > <mesh name="Event-Sphere" - uuid={event.point.uuid} - ref={(el) => (sphereRefs.current[event.point.uuid] = el!)} + uuid={point.uuid} + ref={(el) => (sphereRefs.current[point.uuid] = el!)} onClick={(e) => { e.stopPropagation(); setSelectedEventSphere( - sphereRefs.current[event.point.uuid] + sphereRefs.current[point.uuid] ); }} - position={new THREE.Vector3(...event.point.position)} + position={new THREE.Vector3(...point.position)} userData={{ - modelUuid: event.modelUuid, - pointUuid: event.point.uuid, + modelUuid: usedEvent.modelUuid, + pointUuid: point.uuid, }} > <sphereGeometry args={[0.1, 16, 16]} /> @@ -274,6 +325,7 @@ function PointsCreator() { } })} </group> + {selectedEventSphere && transformMode && ( <TransformControls ref={transformRef} diff --git a/app/src/modules/simulation/events/temp.md b/app/src/modules/simulation/events/temp.md deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx b/app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx similarity index 95% rename from app/src/modules/simulation/triggers/connector/triggerConnector.tsx rename to app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx index dc8fb11..5ca87a5 100644 --- a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx +++ b/app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx @@ -6,11 +6,12 @@ import { useSelectedAction, useSelectedAsset } from "../../../../store/simulatio import { useProductStore } from "../../../../store/simulation/useProductStore"; import { useEventsStore } from "../../../../store/simulation/useEventsStore"; import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; -import { handleAddEventToProduct } from "../../events/points/functions/handleAddEventToProduct"; +import { handleAddEventToProduct } from "../points/functions/handleAddEventToProduct"; import { QuadraticBezierLine } from "@react-three/drei"; import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi"; import { useDeleteTool } from "../../../../store/builder/store"; import { usePlayButtonStore } from "../../../../store/usePlayButtonStore"; +import { ArrowOnQuadraticBezier, Arrows } from "../arrows/arrows"; interface ConnectionLine { id: string; @@ -368,6 +369,7 @@ function TriggerConnector() { !intersect.object.name.includes("Roof") && !intersect.object.name.includes("agv-collider") && !intersect.object.name.includes("MeasurementReference") && + !intersect.object.name.includes("ArrowWithTube") && !intersect.object.parent?.name.includes("Zone") && !(intersect.object.type === "GridHelper") && !(intersect.object.type === "Line2") @@ -487,18 +489,29 @@ function TriggerConnector() { ); })} + <Arrows connections={connections} /> + {currentLine && ( - <QuadraticBezierLine - start={currentLine.start.toArray()} - end={currentLine.end.toArray()} - mid={currentLine.mid.toArray()} - color={helperlineColor} - lineWidth={4} - dashed - dashSize={1} - dashScale={20} - /> + <> + <QuadraticBezierLine + start={currentLine.start.toArray()} + end={currentLine.end.toArray()} + mid={currentLine.mid.toArray()} + color={helperlineColor} + lineWidth={4} + dashed + dashSize={1} + dashScale={20} + /> + <ArrowOnQuadraticBezier + start={currentLine.start.toArray()} + mid={currentLine.mid.toArray()} + end={currentLine.end.toArray()} + color={helperlineColor} + /> + </> )} + </group> ); } diff --git a/app/src/modules/simulation/roboticArm/eventManager/useArmBotEventManager.ts b/app/src/modules/simulation/roboticArm/eventManager/useArmBotEventManager.ts new file mode 100644 index 0000000..97593b4 --- /dev/null +++ b/app/src/modules/simulation/roboticArm/eventManager/useArmBotEventManager.ts @@ -0,0 +1,65 @@ +import { useEffect, useRef } from 'react'; +import { useFrame } from '@react-three/fiber'; +import { useArmBotStore } from '../../../../store/simulation/useArmBotStore'; + +type ArmBotCallback = { + armBotId: string; + callback: () => void; +}; + +export function useArmBotEventManager() { + const { getArmBotById } = useArmBotStore(); + const callbacksRef = useRef<ArmBotCallback[]>([]); + const isMonitoringRef = useRef(false); + + // Add a new armbot to monitor + const addArmBotToMonitor = (armBotId: string, callback: () => void) => { + // Avoid duplicates + if (!callbacksRef.current.some((entry) => entry.armBotId === armBotId)) { + callbacksRef.current.push({ armBotId, callback }); + } + + // Start monitoring if not already running + if (!isMonitoringRef.current) { + isMonitoringRef.current = true; + } + }; + + // Remove an armbot from monitoring (e.g., when unmounted) + const removeArmBotFromMonitor = (armBotId: string) => { + callbacksRef.current = callbacksRef.current.filter( + (entry) => entry.armBotId !== armBotId + ); + + // Stop monitoring if no more armbots to track + if (callbacksRef.current.length === 0) { + isMonitoringRef.current = false; + } + }; + + // Check armbot states every frame + useFrame(() => { + if (!isMonitoringRef.current || callbacksRef.current.length === 0) return; + + callbacksRef.current.forEach(({ armBotId, callback }) => { + const armBot = getArmBotById(armBotId); + if (armBot?.isActive === false && armBot?.state === 'idle') { + callback(); + removeArmBotFromMonitor(armBotId); // Remove after triggering + } + }); + }); + + // Cleanup on unmount (optional, since useFrame auto-cleans) + useEffect(() => { + return () => { + callbacksRef.current = []; + isMonitoringRef.current = false; + }; + }, []); + + return { + addArmBotToMonitor, + removeArmBotFromMonitor, + }; +} \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index 1c8c447..ce5a280 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -13,7 +13,6 @@ import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore import { useStorageUnitStore } from '../../../../../store/simulation/useStorageUnitStore'; import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore'; import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler'; -import { useCheckActiveRoboticArmsInSubsequence } from '../../../simulator/functions/checkActiveRoboticArmsInSubsequence'; function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { @@ -27,15 +26,14 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { const pauseTimeRef = useRef<number | null>(null); const isPausedRef = useRef<boolean>(false); const isSpeedRef = useRef<any>(null); - const isIdleRef = useRef<boolean>(false); let startTime: number; - const { armBots, setArmBotActive, setArmBotState, removeCurrentAction, incrementActiveTime, incrementIdleTime } = useArmBotStore(); + const { setArmBotActive, setArmBotState, removeCurrentAction, incrementActiveTime, incrementIdleTime } = useArmBotStore(); const { decrementVehicleLoad, removeLastMaterial } = useVehicleStore(); const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = useStorageUnitStore(); const { setIsVisible, setIsPaused, getMaterialById } = useMaterialStore(); const { selectedProduct } = useSelectedProduct(); - const { getActionByUuid, getEventByActionUuid, getEventByModelUuid, getProductById } = useProductStore(); + const { getActionByUuid, getEventByActionUuid, getEventByModelUuid } = useProductStore(); const { triggerPointActions } = useTriggerHandler(); const { isPlaying } = usePlayButtonStore(); const { isReset } = useResetButtonStore(); @@ -47,7 +45,6 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { const animationFrameIdRef = useRef<number | null>(null); const previousTimeRef = useRef<number | null>(null); - const checkActiveRoboticArms = useCheckActiveRoboticArmsInSubsequence(); const lastRemoved = useRef<{ type: string, materialId: string } | null>(null); @@ -90,20 +87,11 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { if (armBot.currentAction) { const action = getActionByUuid(selectedProduct.productId, armBot.currentAction.actionUuid); - const model = getEventByModelUuid(selectedProduct.productId, action?.triggers[0].triggeredAsset?.triggeredModel.modelUuid || ''); - if (action && action.triggers[0].triggeredAsset?.triggeredModel.modelUuid) { + const model = getEventByModelUuid(selectedProduct.productId, action?.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); + if (action && action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid) { if (!model) return; if (model.type === 'transfer') { setIsVisible(armBot.currentAction.materialId || '', true); - - const product = getProductById(selectedProduct.productId); - if (product) { - const result = checkActiveRoboticArms(product, armBot.modelUuid); - // console.log('result: ', result); - // if (result?.hasActiveRoboticArm) { - // lastRemoved.current = null; - // } - } } else if (model.type === 'machine') { // } else if (model.type === 'vehicle') { @@ -116,6 +104,14 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { triggerPointActions(action, armBot.currentAction.materialId) removeCurrentAction(armBot.modelUuid) } + + if (lastRemoved.current) { + if (lastRemoved.current.type === 'transfer') { + setIsPaused(lastRemoved.current.materialId, true) + } else { + setIsPaused(lastRemoved.current.materialId, false) + } + } } } @@ -386,13 +382,6 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { setCurrentPhase("rest"); setPath([]) - if (lastRemoved.current) { - if (lastRemoved.current.type === 'transfer') { - setIsPaused(lastRemoved.current.materialId, true) - } else { - setIsPaused(lastRemoved.current.materialId, false) - } - } } } diff --git a/app/src/modules/simulation/simulator/functions/findConveyorInSequences.ts b/app/src/modules/simulation/simulator/functions/findConveyorInSequences.ts deleted file mode 100644 index 3c9ee44..0000000 --- a/app/src/modules/simulation/simulator/functions/findConveyorInSequences.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { getConveyorSequencesInProduct } from "./getConveyorSequencesForProduct"; - -export function findConveyorInSequences( - product: { - productName: string; - productId: string; - eventDatas: EventsSchema[]; - }, - conveyorUuid: string -): { - allSequences: EventsSchema[][][]; - parentSequence: EventsSchema[][]; - currentSubSequence: EventsSchema[]; -} | null { - // Get all conveyor sequences - const allSequences = getConveyorSequencesInProduct(product); - - // Search through all sequences - for (const parentSequence of allSequences) { - for (const currentSubSequence of parentSequence) { - for (const conveyor of currentSubSequence) { - // Check if this is the conveyor we're looking for - if (conveyor.modelUuid === conveyorUuid) { - return { - allSequences, - parentSequence, - currentSubSequence - }; - } - - // Also check points in case the UUID matches a point's conveyor - if (conveyor.type === 'transfer') { - for (const point of conveyor.points) { - if (point.uuid === conveyorUuid) { - return { - allSequences, - parentSequence, - currentSubSequence - }; - } - } - } - } - } - } - - // Conveyor not found - return null; -} \ No newline at end of file diff --git a/app/src/modules/simulation/simulator/functions/getConveyorSequencesForProduct.ts b/app/src/modules/simulation/simulator/functions/getConveyorSequencesInProduct.ts similarity index 65% rename from app/src/modules/simulation/simulator/functions/getConveyorSequencesForProduct.ts rename to app/src/modules/simulation/simulator/functions/getConveyorSequencesInProduct.ts index 3aaaa9b..8c2cc49 100644 --- a/app/src/modules/simulation/simulator/functions/getConveyorSequencesForProduct.ts +++ b/app/src/modules/simulation/simulator/functions/getConveyorSequencesInProduct.ts @@ -1,49 +1,79 @@ import { extractTriggersFromPoint } from "./extractTriggersFromPoint"; +// Gets all conveyor sequences split by non-transfer events export function getConveyorSequencesInProduct( product: { productName: string; productId: string; eventDatas: EventsSchema[]; } -): EventsSchema[][][] { // Now returns array of array of arrays - // Get all machine sequences for this product +): EventsSchema[][][] { const machineSequences = determineExecutionMachineSequences([product]); - const allConveyorSequences: EventsSchema[][][] = []; - // Process each machine sequence separately for (const machineSequence of machineSequences) { - const conveyorSequencesForThisMachineSequence: EventsSchema[][] = []; - let currentConveyorSequence: EventsSchema[] = []; + const conveyorSequencesForMachine: EventsSchema[][] = []; + let currentSequence: EventsSchema[] = []; for (const event of machineSequence) { if (event.type === 'transfer') { - // Add conveyor to current sequence - currentConveyorSequence.push(event); - } else if (event.type === 'vehicle') { - // Vehicle encountered - split the sequence - if (currentConveyorSequence.length > 0) { - conveyorSequencesForThisMachineSequence.push([...currentConveyorSequence]); - currentConveyorSequence = []; + currentSequence.push(event); + } else { + // Split sequence when non-transfer event is encountered + if (currentSequence.length > 0) { + conveyorSequencesForMachine.push([...currentSequence]); + currentSequence = []; } } - // Other machine types don't affect the conveyor sequence } - // Add any remaining conveyors in the current sequence - if (currentConveyorSequence.length > 0) { - conveyorSequencesForThisMachineSequence.push([...currentConveyorSequence]); + // Add the last sequence if it exists + if (currentSequence.length > 0) { + conveyorSequencesForMachine.push([...currentSequence]); } - if (conveyorSequencesForThisMachineSequence.length > 0) { - allConveyorSequences.push(conveyorSequencesForThisMachineSequence); + if (conveyorSequencesForMachine.length > 0) { + allConveyorSequences.push(conveyorSequencesForMachine); } } return allConveyorSequences; } +// Finds the subsequence containing a specific conveyor +export function findConveyorSubsequence( + product: { + productName: string; + productId: string; + eventDatas: EventsSchema[]; + }, + conveyorModelUuid: string +): { + allSequences: EventsSchema[][][]; + parentSequence: EventsSchema[][]; + currentSubSequence: EventsSchema[]; +} | null { + const allSequences = getConveyorSequencesInProduct(product); + + for (const parentSequence of allSequences) { + for (const currentSubSequence of parentSequence) { + const hasTargetConveyor = currentSubSequence.some( + event => event.type === 'transfer' && event.modelUuid === conveyorModelUuid + ); + + if (hasTargetConveyor) { + return { + allSequences, + parentSequence, + currentSubSequence + }; + } + } + } + + return null; +} + // Helper function to get machine sequences (simplified from your example) function determineExecutionMachineSequences(products: productsSchema): EventsSchema[][] { const pointToEventMap = new Map<string, EventsSchema>(); diff --git a/app/src/modules/simulation/triggers/trigger.tsx b/app/src/modules/simulation/triggers/trigger.tsx index 5f11709..0af5881 100644 --- a/app/src/modules/simulation/triggers/trigger.tsx +++ b/app/src/modules/simulation/triggers/trigger.tsx @@ -1,4 +1,4 @@ -import TriggerConnector from './connector/triggerConnector' +import TriggerConnector from '../events/triggerConnections/triggerConnector' function Trigger() { diff --git a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts index 7de5d29..67fecdb 100644 --- a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts +++ b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts @@ -7,12 +7,18 @@ import { useArmBotStore } from '../../../../store/simulation/useArmBotStore'; import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'; import { useMachineStore } from '../../../../store/simulation/useMachineStore'; import { useStorageUnitStore } from '../../../../store/simulation/useStorageUnitStore'; +import { useArmBotEventManager } from '../../roboticArm/eventManager/useArmBotEventManager'; +import { useConveyorStore } from '../../../../store/simulation/useConveyorStore'; +import { useConveyorEventManager } from '../../conveyor/eventManager/useConveyorEventManager'; export function useTriggerHandler() { const { handleAction } = useActionHandler(); const { selectedProduct } = useSelectedProduct(); const { getEventByTriggerUuid, getEventByModelUuid, getActionByUuid, getModelUuidByActionUuid } = useProductStore(); const { getArmBotById } = useArmBotStore(); + const { getConveyorById } = useConveyorStore(); + const { addArmBotToMonitor } = useArmBotEventManager(); + const { addConveyorToMonitor } = useConveyorEventManager(); const { getVehicleById } = useVehicleStore(); const { getMachineById } = useMachineStore(); const { getStorageUnitById } = useStorageUnitStore(); @@ -144,6 +150,10 @@ export function useTriggerHandler() { } else { // Event Manager Needed + setIsPaused(materialId, true); + addArmBotToMonitor(armBot.modelUuid, + () => handleAction(action, materialId) + ); } } @@ -178,12 +188,12 @@ export function useTriggerHandler() { setIsVisible(materialId, true); if (action && - action.triggers[0].triggeredAsset?.triggeredModel.modelUuid && - action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid + action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid && + action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid ) { setNextLocation(material.materialId, { - modelUuid: action.triggers[0].triggeredAsset?.triggeredModel.modelUuid, - pointUuid: action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid, + modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid, + pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid, }); handleAction(action, materialId); @@ -320,12 +330,42 @@ export function useTriggerHandler() { if (armBot.isActive === false && armBot.state === 'idle') { // Handle current action from arm bot - handleAction(action, materialId); + const model = getEventByModelUuid(selectedProduct.productId, action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); + if (model?.type === 'transfer') { + const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); + if (conveyor) { + addConveyorToMonitor(conveyor.modelUuid, + () => { + handleAction(action, materialId) + } + ) + // handleAction(action, materialId) + } + } else { + handleAction(action, materialId) + } } else { // Event Manager Needed - + addArmBotToMonitor(armBot.modelUuid, + () => { + const model = getEventByModelUuid(selectedProduct.productId, action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); + if (model?.type === 'transfer') { + const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); + if (conveyor) { + addConveyorToMonitor(conveyor.modelUuid, + () => { + handleAction(action, materialId) + } + ) + // handleAction(action, materialId) + } + } else { + handleAction(action, materialId) + } + } + ); } } } @@ -342,8 +382,6 @@ export function useTriggerHandler() { const material = getMaterialById(materialId); if (material) { - // setIsPaused(material.materialId, false); - setPreviousLocation(material.materialId, { modelUuid: material.current.modelUuid, pointUuid: material.current.pointUuid, @@ -359,22 +397,22 @@ export function useTriggerHandler() { const action = getActionByUuid(selectedProduct.productId, trigger.triggeredAsset.triggeredAction.actionUuid); if (action && action.triggers.length > 0 && - action.triggers[0].triggeredAsset?.triggeredModel.modelUuid && - action.triggers[0].triggeredAsset?.triggeredAction?.actionUuid && - action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid) { - const model = getEventByModelUuid(selectedProduct.productId, action.triggers[0].triggeredAsset?.triggeredModel.modelUuid); + action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid && + action.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid && + action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid) { + const model = getEventByModelUuid(selectedProduct.productId, action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid); if (model?.type === 'roboticArm') { handleAction(action, material.materialId); } else if (model?.type === 'vehicle') { - const nextAction = getActionByUuid(selectedProduct.productId, action.triggers[0].triggeredAsset?.triggeredAction.actionUuid); + const nextAction = getActionByUuid(selectedProduct.productId, action.triggers[0]?.triggeredAsset?.triggeredAction.actionUuid); if (action) { handleAction(action, material.materialId); - const vehicle = getVehicleById(action.triggers[0].triggeredAsset?.triggeredModel.modelUuid); + const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid); setPreviousLocation(material.materialId, { modelUuid: material.current.modelUuid, @@ -410,10 +448,19 @@ export function useTriggerHandler() { } } + } else if (model?.type === 'transfer') { + const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid); + if (conveyor) { + setNextLocation(material.materialId, { + modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid, + pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid, + }) + handleAction(action, material.materialId); + } } else { setNextLocation(material.materialId, { - modelUuid: action.triggers[0].triggeredAsset?.triggeredModel.modelUuid, - pointUuid: action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid, + modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid, + pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid, }) handleAction(action, material.materialId); diff --git a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx index 00837a4..7c7de03 100644 --- a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx +++ b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx @@ -217,7 +217,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) function startUnloadingProcess() { if (agvDetail.point.action.triggers.length > 0) { - const trigger = getTriggerByUuid(selectedProduct.productId, agvDetail.point.action.triggers[0].triggerUuid); + const trigger = getTriggerByUuid(selectedProduct.productId, agvDetail.point.action.triggers[0]?.triggerUuid); const model = getEventByModelUuid(selectedProduct.productId, trigger?.triggeredAsset?.triggeredModel?.modelUuid || ''); if (trigger && model) { diff --git a/app/src/store/simulation/useEventsStore.ts b/app/src/store/simulation/useEventsStore.ts index 5580eb1..dbba722 100644 --- a/app/src/store/simulation/useEventsStore.ts +++ b/app/src/store/simulation/useEventsStore.ts @@ -16,7 +16,7 @@ type EventsStore = { modelUuid: string, pointUuid: string, updates: Partial<ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema> - ) => void; + ) => EventsSchema | undefined; // Action-level actions addAction: ( @@ -101,17 +101,21 @@ export const useEventsStore = create<EventsStore>()( }, updatePoint: (modelUuid, pointUuid, updates) => { + let updatedEvent: EventsSchema | undefined; set((state) => { const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); if (event && 'points' in event) { const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); if (point) { Object.assign(point, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); } } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { Object.assign((event as any).point, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); } }); + return updatedEvent; }, // Action-level actions diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts index e7d394a..27b908a 100644 --- a/app/src/store/simulation/useProductStore.ts +++ b/app/src/store/simulation/useProductStore.ts @@ -24,7 +24,7 @@ type ProductsStore = { modelUuid: string, pointUuid: string, updates: Partial<ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema> - ) => void; + ) => EventsSchema | undefined; // Action-level actions addAction: ( @@ -195,6 +195,7 @@ export const useProductStore = create<ProductsStore>()( }, updatePoint: (productId, modelUuid, pointUuid, updates) => { + let updatedEvent: EventsSchema | undefined; set((state) => { const product = state.products.find(p => p.productId === productId); if (product) { @@ -203,12 +204,15 @@ export const useProductStore = create<ProductsStore>()( const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); if (point) { Object.assign(point, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); } } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { Object.assign((event as any).point, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); } } }); + return updatedEvent; }, // Action-level actions