diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/components/ActionsList.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/components/ActionsList.tsx index 09017de..8aae3b2 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/components/ActionsList.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/components/ActionsList.tsx @@ -35,7 +35,7 @@ const ActionsList: React.FC = ({ const handleRenameAction = (newName: string) => { if (!selectedAction.actionId) return; - const event = renameAction(selectedAction.actionId, newName); + const event = renameAction(selectedProduct.productId, selectedAction.actionId, newName); if (event) { upsertProductOrEventApi({ diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx index f68b6b8..a6a1fa9 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx @@ -72,7 +72,7 @@ function ConveyorMechanics() { const validOption = option as | "default" | "spawn" | "swap" | "delay" | "despawn"; setActiveOption(validOption); - const event = updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionType: validOption, }); @@ -88,7 +88,7 @@ function ConveyorMechanics() { const handleRenameAction = (newName: string) => { if (!selectedEventData || !selectedPointData) return; - const event = updateAction(selectedPointData.action.actionUuid, { actionName: newName }); + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionName: newName }); if (event) { updateBackend( @@ -102,7 +102,7 @@ function ConveyorMechanics() { const handleSpawnCountChange = (value: string) => { if (!selectedEventData || !selectedPointData) return; - const event = updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { spawnCount: value === "inherit" ? "inherit" : parseFloat(value), }); @@ -118,7 +118,7 @@ function ConveyorMechanics() { const handleSpawnIntervalChange = (value: string) => { if (!selectedEventData || !selectedPointData) return; - const event = updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { spawnInterval: value === "inherit" ? "inherit" : parseFloat(value), }); @@ -134,7 +134,7 @@ function ConveyorMechanics() { const handleMaterialSelect = (material: string) => { if (!selectedEventData || !selectedPointData) return; - const event = updateAction(selectedPointData.action.actionUuid, { material }); + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { material }); if (event) { updateBackend( @@ -148,7 +148,7 @@ function ConveyorMechanics() { const handleDelayChange = (value: string) => { if (!selectedEventData || !selectedPointData) return; - const event = updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { delay: value === "inherit" ? "inherit" : parseFloat(value), }); @@ -271,7 +271,7 @@ function ConveyorMechanics() {
- +
diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx index 993c46b..3e11e54 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx @@ -6,6 +6,7 @@ import { useSelectedEventData, useSelectedProduct } from "../../../../../../stor import { useProductStore } from "../../../../../../store/simulation/useProductStore"; import ProcessAction from "../actions/ProcessAction"; import ActionsList from "../components/ActionsList"; +import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi"; function MachineMechanics() { const [activeOption, setActiveOption] = useState<"default" | "process">("default"); @@ -14,6 +15,9 @@ function MachineMechanics() { const { getPointByUuid, updateAction } = useProductStore(); const { selectedProduct } = useSelectedProduct(); + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + useEffect(() => { if (selectedEventData) { const point = getPointByUuid( @@ -28,31 +32,54 @@ function MachineMechanics() { } }, [selectedProduct, selectedEventData, getPointByUuid]); + const updateBackend = ( + productName: string, + productId: string, + organization: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productId: productId, + organization: organization, + eventDatas: eventData + }) + } + const handleActionTypeChange = (option: string) => { if (!selectedEventData || !selectedPointData) return; const validOption = option as "process"; setActiveOption(validOption); - updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionType: validOption, }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } }; const handleRenameAction = (newName: string) => { if (!selectedPointData) return; - updateAction(selectedPointData.action.actionUuid, { actionName: newName }); + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionName: newName }); }; const handleProcessTimeChange = (value: string) => { if (!selectedPointData) return; - updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { processTime: parseFloat(value), }); }; const handleMaterialSelect = (material: string) => { if (!selectedPointData) return; - updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { swapMaterial: material, }); }; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx index e56741f..df5c358 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx @@ -59,7 +59,7 @@ function RoboticArmMechanics() { const handleRenameAction = (newName: string) => { if (!selectedAction.actionId) return; - const event = updateAction(selectedAction.actionId, { actionName: newName }); + const event = updateAction(selectedProduct.productId, selectedAction.actionId, { actionName: newName }); if (selectedPointData) { const updatedActions = selectedPointData.actions.map((action) => @@ -101,7 +101,7 @@ function RoboticArmMechanics() { if (!selectedAction.actionId || !selectedPointData) return; const [x, y, z] = value.split(",").map(Number); - const event = updateAction(selectedAction.actionId, { + const event = updateAction(selectedProduct.productId, selectedAction.actionId, { process: { startPoint: [x, y, z] as [number, number, number], endPoint: selectedPointData.actions.find((a) => a.actionUuid === selectedAction.actionId)?.process.endPoint || null, @@ -122,7 +122,7 @@ function RoboticArmMechanics() { if (!selectedAction.actionId || !selectedPointData) return; const [x, y, z] = value.split(",").map(Number); - const event = updateAction(selectedAction.actionId, { + const event = updateAction(selectedProduct.productId, selectedAction.actionId, { process: { startPoint: selectedPointData.actions.find((a) => a.actionUuid === selectedAction.actionId)?.process.startPoint || null, endPoint: [x, y, z] as [number, number, number], @@ -181,7 +181,7 @@ function RoboticArmMechanics() { const handleDeleteAction = (actionUuid: string) => { if (!selectedPointData) return; - const event = removeAction(actionUuid); + const event = removeAction(selectedProduct.productId, actionUuid); if (event) { updateBackend( diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx index c85da9f..3273ce4 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx @@ -6,6 +6,7 @@ import { useSelectedEventData, useSelectedProduct } from "../../../../../../stor import { useProductStore } from "../../../../../../store/simulation/useProductStore"; import StorageAction from "../actions/StorageAction"; import ActionsList from "../components/ActionsList"; +import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi"; function StorageMechanics() { const [activeOption, setActiveOption] = useState<"default" | "store" | "spawn">("default"); @@ -14,6 +15,9 @@ function StorageMechanics() { const { getPointByUuid, updateAction } = useProductStore(); const { selectedProduct } = useSelectedProduct(); + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + useEffect(() => { if (selectedEventData) { const point = getPointByUuid( @@ -28,26 +32,67 @@ function StorageMechanics() { } }, [selectedProduct, selectedEventData, getPointByUuid]); + const updateBackend = ( + productName: string, + productId: string, + organization: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productId: productId, + organization: organization, + eventDatas: eventData + }) + } + const handleActionTypeChange = (option: string) => { if (!selectedEventData || !selectedPointData) return; const validOption = option as "store" | "spawn"; setActiveOption(validOption); - updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionType: validOption, }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } }; const handleRenameAction = (newName: string) => { if (!selectedPointData) return; - updateAction(selectedPointData.action.actionUuid, { actionName: newName }); + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionName: newName }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } }; const handleCapacityChange = (value: string) => { if (!selectedPointData) return; - updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { storageCapacity: parseInt(value), }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } }; // Get current values from store diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx index 28eb61f..7839168 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx @@ -10,6 +10,7 @@ import { import { useProductStore } from "../../../../../../store/simulation/useProductStore"; import TravelAction from "../actions/TravelAction"; import ActionsList from "../components/ActionsList"; +import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi"; function VehicleMechanics() { const [activeOption, setActiveOption] = useState<"default" | "travel">("default"); @@ -18,8 +19,11 @@ function VehicleMechanics() { const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction } = useProductStore(); const { selectedProduct } = useSelectedProduct(); + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + useEffect(() => { - if (selectedEventData) { + if (selectedEventData && selectedEventData.data.type === 'vehicle') { const point = getPointByUuid( selectedProduct.productId, selectedEventData.data.modelUuid, @@ -33,11 +37,34 @@ function VehicleMechanics() { } }, [selectedProduct, selectedEventData, getPointByUuid]); + const updateBackend = ( + productName: string, + productId: string, + organization: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productId: productId, + organization: organization, + eventDatas: eventData + }) + } + const handleSpeedChange = (value: string) => { if (!selectedEventData) return; - updateEvent(selectedProduct.productId, selectedEventData.data.modelUuid, { + const event = updateEvent(selectedProduct.productId, selectedEventData.data.modelUuid, { speed: parseFloat(value), }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } }; const handleActionTypeChange = (option: string) => { @@ -45,28 +72,64 @@ function VehicleMechanics() { const validOption = option as "travel"; setActiveOption(validOption); - updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionType: validOption, }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } }; const handleRenameAction = (newName: string) => { if (!selectedPointData) return; - updateAction(selectedPointData.action.actionUuid, { actionName: newName }); + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionName: newName }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } }; const handleLoadCapacityChange = (value: string) => { if (!selectedPointData) return; - updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { loadCapacity: parseFloat(value), }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } }; const handleUnloadDurationChange = (value: string) => { if (!selectedPointData) return; - updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { unLoadDuration: parseFloat(value), }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } }; const handlePickPointChange = (value: string) => { diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx index 1434446..86e4e7b 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx @@ -1,137 +1,132 @@ -import React, { useRef, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { - AddIcon, - RemoveIcon, - ResizeHeightIcon, + AddIcon, + RemoveIcon, + ResizeHeightIcon, } from "../../../../../icons/ExportCommonIcons"; import LabledDropdown from "../../../../../ui/inputs/LabledDropdown"; import RenameInput from "../../../../../ui/inputs/RenameInput"; import { handleResize } from "../../../../../../functions/handleResizePannel"; +import { useProductStore } from "../../../../../../store/simulation/useProductStore"; +import { useSelectedProduct } from "../../../../../../store/simulation/useSimulationStore"; -const Trigger: React.FC = () => { - // State to hold the list of triggers - const [triggers, setTriggers] = useState(["Trigger 1"]); - const [selectedTrigger, setSelectedTrigger] = useState("Trigger 1"); - const [activeOption, setActiveOption] = useState("onComplete"); - const triggersContainerRef = useRef(null); +type TriggerProps = { + selectedPointData?: PointsScheme | undefined; + type?: 'Conveyor' | 'Vehicle' | 'RoboticArm' | 'Machine' | 'StorageUnit'; +}; - // States for dropdowns - const [triggeredModel, setTriggeredModel] = useState([]); - const [triggeredPoint, setTriggeredPoint] = useState([]); - const [triggeredAction, setTriggeredAction] = useState([]); +const Trigger = ({ selectedPointData, type }: TriggerProps) => { + const [currentAction, setCurrentAction] = useState(); + const { selectedProduct } = useSelectedProduct(); + const { getActionByUuid } = useProductStore(); + const [triggers, setTriggers] = useState([]); + const [selectedTrigger, setSelectedTrigger] = useState(); + const [activeOption, setActiveOption] = useState("onComplete"); + const triggersContainerRef = useRef(null); - // Function to handle adding a new trigger - const addTrigger = (): void => { - const newTrigger = `Trigger ${triggers.length + 1}`; - setTriggers([...triggers, newTrigger]); // Add new trigger to the state + useEffect(() => { + if (!selectedPointData) return; + if (type === 'Conveyor' || type === 'Vehicle' || type === 'Machine' || type === 'StorageUnit') { + setCurrentAction((selectedPointData as ConveyorPointSchema).action.actionUuid); + } + }, [selectedPointData]); - // Initialize the states for the new trigger - setTriggeredModel([...triggeredModel, ""]); - setTriggeredPoint([...triggeredPoint, ""]); - setTriggeredAction([...triggeredAction, ""]); - }; + useEffect(() => { + if (!currentAction || !selectedProduct) return; + const action = getActionByUuid(selectedProduct.productId, currentAction); + setTriggers(action?.triggers || []); + setSelectedTrigger(action?.triggers[0] || undefined); + }, [currentAction, selectedProduct]); - // Function to handle removing a trigger - const removeTrigger = (index: number): void => { - setTriggers(triggers.filter((_, i) => i !== index)); // Remove trigger by index - setTriggeredModel(triggeredModel.filter((_, i) => i !== index)); - setTriggeredPoint(triggeredPoint.filter((_, i) => i !== index)); - setTriggeredAction(triggeredAction.filter((_, i) => i !== index)); - }; + const addTrigger = (): void => { + }; - return ( -
-
-
Trigger
- -
-
-
-
- {triggers.map((trigger: any, index: number) => ( -
setSelectedTrigger(trigger)} - > - - {triggers.length > 1 && ( - - )} -
- ))} -
- +
+
+
+
+ {triggers.map((trigger) => ( +
setSelectedTrigger(trigger)} + > + + {triggers.length > 1 && ( + + )} +
+ ))} +
+ +
+
+
{selectedTrigger?.triggerName}
+ setActiveOption(option)} + /> +
+ { }} + /> + { }} + /> + { }} + /> +
+
+
-
-
{selectedTrigger}
- setActiveOption(option)} - /> -
- { - const newModel = [...triggeredModel]; - newModel[0] = option; - setTriggeredModel(newModel); - }} - /> - { - const newPoint = [...triggeredPoint]; - newPoint[0] = option; - setTriggeredPoint(newPoint); - }} - /> - { - const newAction = [...triggeredAction]; - newAction[0] = option; - setTriggeredAction(newAction); - }} - /> -
-
-
- - ); + ); }; export default Trigger; diff --git a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts index a351d73..ee5c283 100644 --- a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts @@ -187,7 +187,7 @@ function processLoadedModel( }, ]); - if (item.eventData.type === "vehicle") { + if (item.eventData.type === "Vehicle") { const vehicleEvent: VehicleEventSchema = { modelUuid: item.modelUuid, modelName: item.modelName, @@ -202,11 +202,11 @@ function processLoadedModel( rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], action: { actionUuid: THREE.MathUtils.generateUUID(), - actionName: "Vehicle Action", + actionName: "Action 1", actionType: "travel", unLoadDuration: 5, loadCapacity: 10, - steeringAngle:0, + steeringAngle: 0, pickUpPoint: null, unLoadPoint: null, triggers: [] @@ -254,7 +254,7 @@ function processLoadedModel( rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], action: { actionUuid: THREE.MathUtils.generateUUID(), - actionName: "Process Action", + actionName: "Action 1", actionType: "process", processTime: 10, swapMaterial: "material-id", @@ -279,7 +279,7 @@ function processLoadedModel( actions: [ { actionUuid: THREE.MathUtils.generateUUID(), - actionName: "Pick and Place", + actionName: "Action 1", actionType: "pickAndPlace", process: { startPoint: [0, 0, 0], diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index c718ec3..64656a0 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -186,22 +186,59 @@ async function handleModelLoad( state: "idle", type: 'transfer', speed: 1, - points: data.points.map((point: THREE.Vector3, index: number) => ({ - uuid: THREE.MathUtils.generateUUID(), - position: [point.x, point.y, point.z], - rotation: [0, 0, 0], - action: { - actionUuid: THREE.MathUtils.generateUUID(), - actionName: `Action ${index}`, - actionType: 'default', - material: 'Default Material', - delay: 0, - spawnInterval: 5, - spawnCount: 1, - triggers: [] + points: data.points.map((point: THREE.Vector3, index: number) => { + const triggers: TriggerSchema[] = []; + + if (data.points && index < data.points.length - 1) { + triggers.push({ + triggerUuid: THREE.MathUtils.generateUUID(), + triggerName: `Trigger 1`, + triggerType: "onComplete", + delay: 0, + triggeredAsset: { + triggeredModel: { + modelName: newFloorItem.modelName, + modelUuid: newFloorItem.modelUuid + }, + triggeredPoint: { + pointName: `Point`, + pointUuid: "" + }, + triggeredAction: { + actionName: `Action 1`, + actionUuid: "" + } + } + }); } - })) + + return { + uuid: THREE.MathUtils.generateUUID(), + position: [point.x, point.y, point.z], + rotation: [0, 0, 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: `Action 1`, + actionType: 'default', + material: 'Default Material', + delay: 0, + spawnInterval: 5, + spawnCount: 1, + triggers: triggers + } + }; + }) }; + + for (let i = 0; i < ConveyorEvent.points.length - 1; i++) { + const currentPoint = ConveyorEvent.points[i]; + const nextPoint = ConveyorEvent.points[i + 1]; + + if (currentPoint.action.triggers.length > 0) { + currentPoint.action.triggers[0].triggeredAsset!.triggeredPoint.pointUuid = nextPoint.uuid; + currentPoint.action.triggers[0].triggeredAsset!.triggeredAction!.actionUuid = nextPoint.action.actionUuid; + } + } addEvent(ConveyorEvent); eventData.points = ConveyorEvent.points.map(point => ({ uuid: point.uuid, @@ -228,7 +265,7 @@ async function handleModelLoad( actionType: "travel", unLoadDuration: 5, loadCapacity: 10, - steeringAngle:0, + steeringAngle: 0, pickUpPoint: null, unLoadPoint: null, triggers: [] diff --git a/app/src/modules/scene/controls/selectionControls/moveControls.tsx b/app/src/modules/scene/controls/selectionControls/moveControls.tsx index d371806..cc9ce50 100644 --- a/app/src/modules/scene/controls/selectionControls/moveControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/moveControls.tsx @@ -9,6 +9,7 @@ import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifie import { useEventsStore } from "../../../../store/simulation/useEventsStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; +import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi"; function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) { const { camera, controls, gl, scene, pointer, raycaster } = useThree(); @@ -16,10 +17,28 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje const { toggleView } = useToggleView(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); + const { selectedProduct } = useSelectedProduct(); const { floorItems, setFloorItems } = useFloorItems(); const { socket } = useSocketStore(); const itemsData = useRef([]); + 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 (!camera || !scene || toggleView || !itemsGroupRef.current) return; @@ -190,10 +209,19 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje }) } if (productData) { - useProductStore.getState().updateEvent(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid, { + const event = useProductStore.getState().updateEvent(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid, { position: [worldPosition.x, worldPosition.y, worldPosition.z], rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], }) + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } } } @@ -203,9 +231,6 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje return updatedItems; }); - const email = localStorage.getItem("email"); - const organization = email ? email.split("@")[1].split(".")[0] : "default"; - //REST // await setFloorItemApi( diff --git a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx index 08667b4..7ea7045 100644 --- a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx @@ -8,6 +8,7 @@ import * as Types from "../../../../types/world/worldTypes"; import { useEventsStore } from "../../../../store/simulation/useEventsStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; +import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi"; function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, boundingBoxRef }: any) { const { camera, controls, gl, scene, pointer, raycaster } = useThree(); @@ -15,10 +16,28 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo const { toggleView } = useToggleView(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); + const { selectedProduct } = useSelectedProduct(); const { floorItems, setFloorItems } = useFloorItems(); const { socket } = useSocketStore(); const itemsData = useRef([]); + 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 + }) + } + const prevPointerPosition = useRef(null); useEffect(() => { @@ -190,10 +209,19 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo }) } if (productData) { - useProductStore.getState().updateEvent(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid, { + const event = useProductStore.getState().updateEvent(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid, { position: [worldPosition.x, worldPosition.y, worldPosition.z], rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], }) + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } } } @@ -203,9 +231,6 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo return updatedItems; }); - const email = localStorage.getItem("email"); - const organization = email ? email.split("@")[1].split(".")[0] : "default"; - //REST // await setFloorItemApi( diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx index c6ec316..e178a81 100644 --- a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx +++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx @@ -1,22 +1,29 @@ import React, { useEffect, useRef, useState } from "react"; import * as THREE from "three"; import { useEventsStore } from "../../../../../store/simulation/useEventsStore"; -import useModuleStore from "../../../../../store/useModuleStore"; +import useModuleStore, { useSubModuleStore } from "../../../../../store/useModuleStore"; import { TransformControls } from "@react-three/drei"; import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys"; import { useSelectedEventSphere, useSelectedEventData, + useIsDragging, + useIsRotating, } from "../../../../../store/simulation/useSimulationStore"; +import { useThree } from "@react-three/fiber"; function PointsCreator() { + const { gl, raycaster, scene, pointer, camera } = useThree(); + const { subModule } = useSubModuleStore(); const { events, updatePoint, getPointByUuid, getEventByModelUuid } = useEventsStore(); const { activeModule } = useModuleStore(); const transformRef = useRef(null); const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null); const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); const { selectedEventSphere, setSelectedEventSphere, clearSelectedEventSphere, } = useSelectedEventSphere(); - const { selectedEventData, setSelectedEventData, clearSelectedEventData } = useSelectedEventData(); + const { setSelectedEventData, clearSelectedEventData } = useSelectedEventData(); + const { isDragging } = useIsDragging(); + const { isRotating } = useIsRotating(); useEffect(() => { if (selectedEventSphere) { @@ -72,6 +79,53 @@ function PointsCreator() { } }; + useEffect(() => { + const canvasElement = gl.domElement; + + let drag = false; + let isMouseDown = false; + + const onMouseDown = () => { + isMouseDown = true; + drag = false; + }; + + const onMouseUp = () => { + if (selectedEventSphere && !drag) { + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster + .intersectObjects(scene.children, true) + .filter( + (intersect) => + intersect.object.name === ('Event-Sphere') + ); + if (intersects.length === 0) { + clearSelectedEventSphere(); + setTransformMode(null); + } + } + } + + const onMouseMove = () => { + if (isMouseDown) { + drag = true; + } + }; + + if (subModule === 'mechanics') { + canvasElement.addEventListener("mousedown", onMouseDown); + canvasElement.addEventListener("mouseup", onMouseUp); + canvasElement.addEventListener("mousemove", onMouseMove); + } + + return () => { + canvasElement.removeEventListener("mousedown", onMouseDown); + canvasElement.removeEventListener("mouseup", onMouseUp); + canvasElement.removeEventListener("mousemove", onMouseMove); + }; + + }, [gl, subModule, selectedEventSphere]); + return ( <> {activeModule === "simulation" && ( @@ -92,12 +146,6 @@ function PointsCreator() { sphereRefs.current[point.uuid] ); }} - onPointerMissed={() => { - if (selectedEventData?.data.type !== 'vehicle') { - clearSelectedEventSphere(); - } - setTransformMode(null); - }} key={`${i}-${j}`} position={new THREE.Vector3(...point.position)} // rotation={new THREE.Euler(...point.rotation)} @@ -122,10 +170,6 @@ function PointsCreator() { sphereRefs.current[event.point.uuid] ); }} - onPointerMissed={() => { - clearSelectedEventSphere(); - setTransformMode(null); - }} position={new THREE.Vector3(...event.point.position)} // rotation={new THREE.Euler(...event.point.rotation)} userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} @@ -148,10 +192,6 @@ function PointsCreator() { sphereRefs.current[event.point.uuid] ); }} - onPointerMissed={() => { - clearSelectedEventSphere(); - setTransformMode(null); - }} position={new THREE.Vector3(...event.point.position)} // rotation={new THREE.Euler(...event.point.rotation)} userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} @@ -174,10 +214,6 @@ function PointsCreator() { sphereRefs.current[event.point.uuid] ); }} - onPointerMissed={() => { - clearSelectedEventSphere(); - setTransformMode(null); - }} position={new THREE.Vector3(...event.point.position)} // rotation={new THREE.Euler(...event.point.rotation)} userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} diff --git a/app/src/modules/simulation/machine/machine.tsx b/app/src/modules/simulation/machine/machine.tsx index 32c3b8e..e9d2dea 100644 --- a/app/src/modules/simulation/machine/machine.tsx +++ b/app/src/modules/simulation/machine/machine.tsx @@ -1,7 +1,43 @@ -import React from 'react' +import React, { useEffect } from 'react' import MachineInstances from './instances/machineInstances' +import { useMachineStore } from '../../../store/simulation/useMachineStore' +import { useSelectedProduct } from '../../../store/simulation/useSimulationStore'; function Machine() { + const { addMachine, addCurrentAction, removeMachine } = useMachineStore(); + const { selectedProduct } = useSelectedProduct(); + + const machineSample: MachineEventSchema[] = [ + { + modelUuid: "machine-1234-5678-9012", + modelName: "CNC Milling Machine", + position: [10, 0, 5], + rotation: [0, 0, 0], + state: "idle", + type: "machine", + point: { + uuid: "machine-point-9876-5432-1098", + position: [10, 0.5, 5.2], + rotation: [0, 0, 0], + action: { + actionUuid: "machine-action-2468-1357-8024", + actionName: "Metal Processing", + actionType: "process", + processTime: 10, + swapMaterial: "steel", + triggers: [] + } + } + } + ]; + + useEffect(() => { + removeMachine(machineSample[0].modelUuid); + addMachine(selectedProduct.productId, machineSample[0]); + + // addCurrentAction(machineSample[0].modelUuid, machineSample[0].point.action.actionUuid); + }, []) + return ( <> diff --git a/app/src/modules/simulation/products/events/addOrRemoveEventsInProducts.tsx b/app/src/modules/simulation/products/events/addOrRemoveEventsInProducts.tsx index 9eececc..715aa37 100644 --- a/app/src/modules/simulation/products/events/addOrRemoveEventsInProducts.tsx +++ b/app/src/modules/simulation/products/events/addOrRemoveEventsInProducts.tsx @@ -53,7 +53,17 @@ function AddOrRemoveEventsInProducts() { const canvasElement = gl.domElement; if (!canvasElement) return; - let intersects = raycaster.intersectObjects(scene.children, true); + const intersects = raycaster + .intersectObjects(scene.children, true) + .filter( + (intersect) => + !intersect.object.name.includes("Roof") && + !intersect.object.name.includes("MeasurementReference") && + !intersect.object.name.includes("agv-collider") && + !(intersect.object.type === "GridHelper") && + !(intersect.object?.parent?.name.includes('zones')) && + !(intersect.object?.parent?.name.includes('Zone')) + ); if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) { let currentObject = intersects[0].object; @@ -116,6 +126,7 @@ function AddOrRemoveEventsInProducts() { }; }, [gl, subModule, selectedProduct, selectedAsset]); + return ( <> ) diff --git a/app/src/modules/simulation/products/products.tsx b/app/src/modules/simulation/products/products.tsx index ee1ac42..921dd96 100644 --- a/app/src/modules/simulation/products/products.tsx +++ b/app/src/modules/simulation/products/products.tsx @@ -7,7 +7,7 @@ import { upsertProductOrEventApi } from '../../../services/simulation/UpsertProd import { getAllProductsApi } from '../../../services/simulation/getallProductsApi'; function Products() { - const { products, addProduct, setProducts } = useProductStore(); + const { addProduct, setProducts } = useProductStore(); const { setSelectedProduct } = useSelectedProduct(); useEffect(() => { @@ -27,9 +27,6 @@ function Products() { }) }, []) - useEffect(() => { - }, []) - return ( <> diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index 6bde587..af3ffe3 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -170,8 +170,8 @@ function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) { } } const logStatus = (id: string, status: string) => { - // console.log(id + "," + status); - console.log( status); + // + } return ( diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 5ca0ec5..757a9ef 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -23,7 +23,7 @@ function Simulation() { }, [events]) useEffect(() => { - // console.log('products: ', products); + console.log('products: ', products); }, [products]) return ( diff --git a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx index efefa0c..dcb46f4 100644 --- a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx +++ b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef, useState } from "react"; -import { useThree } from "@react-three/fiber"; +import { useFrame, useThree } from "@react-three/fiber"; import * as THREE from "three"; import { useSubModuleStore } from "../../../../store/useModuleStore"; import { useSelectedAsset } from "../../../../store/simulation/useSimulationStore"; @@ -7,21 +7,28 @@ 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 { QuadraticBezierLine } from "@react-three/drei"; +import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi"; +import { useDeleteTool } from "../../../../store/store"; interface ConnectionLine { id: string; - start: THREE.Vector3; - end: THREE.Vector3; - mid: THREE.Vector3; + startPointUuid: string; + endPointUuid: string; trigger: TriggerSchema; } function TriggerConnector() { - const { gl, raycaster, scene } = useThree(); + const { gl, raycaster, scene, pointer, camera } = useThree(); const { subModule } = useSubModuleStore(); - const { products, getPointByUuid, getIsEventInProduct, getActionByUuid, addTrigger, addEvent, getEventByModelUuid } = useProductStore(); + const { products, getPointByUuid, getIsEventInProduct, getActionByUuid, addTrigger, removeTrigger, addEvent, getEventByModelUuid, getProductById } = useProductStore(); const { selectedAsset, clearSelectedAsset } = useSelectedAsset(); const { selectedProduct } = useSelectedProduct(); + const [hoveredLineKey, setHoveredLineKey] = useState(null); + const groupRefs = useRef>({}); + const [helperlineColor, setHelperLineColor] = useState("red"); + const [currentLine, setCurrentLine] = useState<{ start: THREE.Vector3; end: THREE.Vector3; mid: THREE.Vector3; } | null>(null); + const { deleteTool } = useDeleteTool(); const [firstSelectedPoint, setFirstSelectedPoint] = useState<{ productId: string; @@ -32,52 +39,99 @@ function TriggerConnector() { const [connections, setConnections] = useState([]); + 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(() => { const newConnections: ConnectionLine[] = []; - products.forEach(product => { - product.eventDatas.forEach(event => { - if ('points' in event) { - event.points.forEach(point => { - if ('action' in point && point.action?.triggers) { - point.action.triggers.forEach(trigger => { - if (trigger.triggeredAsset) { - const targetPoint = getPointByUuid( - product.productId, - trigger.triggeredAsset.triggeredModel.modelUuid, - trigger.triggeredAsset.triggeredPoint.pointUuid - ); + const product = getProductById(selectedProduct.productId); + if (!product || products.length === 0) return; - if (targetPoint) { - const startPos = new THREE.Vector3(...point.position); - const endPos = new THREE.Vector3(...targetPoint.position); - const midPos = new THREE.Vector3() - .addVectors(startPos, endPos) - .multiplyScalar(0.5) - .add(new THREE.Vector3(0, 2, 0)); - - newConnections.push({ - id: `${point.uuid}-${targetPoint.uuid}-${trigger.triggerUuid}`, - start: startPos, - end: endPos, - mid: midPos, - trigger - }); - } - } + product.eventDatas.forEach(event => { + // Handle Conveyor points + if (event.type === "transfer" && 'points' in event) { + event.points.forEach(point => { + if (point.action?.triggers) { + point.action.triggers.forEach(trigger => { + if (trigger.triggeredAsset) { + newConnections.push({ + id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, + startPointUuid: point.uuid, + endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + trigger + }); + } + }); + } + }); + } + // Handle Vehicle point + else if (event.type === "vehicle" && 'point' in event) { + const point = event.point; + if (point.action?.triggers) { + point.action.triggers.forEach(trigger => { + if (trigger.triggeredAsset) { + newConnections.push({ + id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, + startPointUuid: point.uuid, + endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + trigger }); } }); } - }); + } + // Handle Robotic Arm points + else if (event.type === "roboticArm" && 'point' in event) { + const point = event.point; + point.actions?.forEach(action => { + action.triggers?.forEach(trigger => { + if (trigger.triggeredAsset) { + newConnections.push({ + id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, + startPointUuid: point.uuid, + endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + trigger + }); + } + }); + }); + } + // Handle Machine point + else if (event.type === "machine" && 'point' in event) { + const point = event.point; + if (point.action?.triggers) { + point.action.triggers.forEach(trigger => { + if (trigger.triggeredAsset) { + newConnections.push({ + id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, + startPointUuid: point.uuid, + endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + trigger + }); + } + }); + } + } }); setConnections(newConnections); - }, [products]); - - useEffect(() => { - console.log('connections: ', connections); - }, connections) + }, [products, selectedProduct.productId]); useEffect(() => { const canvasElement = gl.domElement; @@ -111,15 +165,31 @@ function TriggerConnector() { if (drag) return; evt.preventDefault(); - const intersects = raycaster.intersectObjects(scene.children, true); - if (intersects.length === 0) return; + const intersects = raycaster + .intersectObjects(scene.children, true) + .filter( + (intersect) => + intersect.object.name === ('Event-Sphere') + ); + if (intersects.length === 0) { + setFirstSelectedPoint(null); + return; + }; const currentObject = intersects[0].object; - if (!currentObject || currentObject.name !== 'Event-Sphere') return; + if (!currentObject || currentObject.name !== 'Event-Sphere') { + setFirstSelectedPoint(null); + return; + }; const modelUuid = currentObject.userData.modelUuid; const pointUuid = currentObject.userData.pointUuid; + if (firstSelectedPoint && firstSelectedPoint.pointUuid === pointUuid) { + setFirstSelectedPoint(null); + return; + } + if (selectedProduct && getIsEventInProduct(selectedProduct.productId, modelUuid)) { const point = getPointByUuid( @@ -128,7 +198,12 @@ function TriggerConnector() { pointUuid ); - if (!point) return; + const event = getEventByModelUuid(selectedProduct.productId, modelUuid); + + if (!point || !event) { + setFirstSelectedPoint(null); + return; + }; let actionUuid: string | undefined; if ('action' in point && point.action) { @@ -152,12 +227,12 @@ function TriggerConnector() { delay: 0, triggeredAsset: { triggeredModel: { - modelName: currentObject.parent?.parent?.name || 'Unknown', + modelName: event.modelName || 'Unknown', modelUuid: modelUuid }, triggeredPoint: { - pointName: currentObject.name, - pointUuid: pointUuid + pointName: 'Point', + pointUuid: point.uuid }, triggeredAction: actionUuid ? { actionName: getActionByUuid(selectedProduct.productId, actionUuid)?.actionName || 'Action', @@ -167,7 +242,16 @@ function TriggerConnector() { }; if (firstSelectedPoint.actionUuid) { - addTrigger(firstSelectedPoint.actionUuid, trigger); + const event = addTrigger(selectedProduct.productId, firstSelectedPoint.actionUuid, trigger); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } } setFirstSelectedPoint(null); } @@ -184,7 +268,12 @@ function TriggerConnector() { pointUuid ); - if (!point) return; + const event = getEventByModelUuid(selectedProduct.productId, modelUuid); + + if (!point || !event) { + setFirstSelectedPoint(null); + return; + }; let actionUuid: string | undefined; if ('action' in point && point.action) { @@ -200,12 +289,12 @@ function TriggerConnector() { delay: 0, triggeredAsset: { triggeredModel: { - modelName: currentObject.parent?.parent?.name || 'Unknown', + modelName: event.modelName || 'Unknown', modelUuid: modelUuid }, triggeredPoint: { - pointName: currentObject.name, - pointUuid: pointUuid + pointName: 'Point', + pointUuid: point.uuid }, triggeredAction: actionUuid ? { actionName: getActionByUuid(selectedProduct.productId, actionUuid)?.actionName || 'Action', @@ -215,13 +304,24 @@ function TriggerConnector() { }; if (firstSelectedPoint.actionUuid) { - addTrigger(firstSelectedPoint.actionUuid, trigger); + const event = addTrigger(selectedProduct.productId, firstSelectedPoint.actionUuid, trigger); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } } setFirstSelectedPoint(null); + } else if (firstSelectedPoint) { + setFirstSelectedPoint(null); } }; - if (subModule === 'simulations') { + if (subModule === 'simulations' && !deleteTool) { canvasElement.addEventListener("mousedown", onMouseDown); canvasElement.addEventListener("mouseup", onMouseUp); canvasElement.addEventListener("mousemove", onMouseMove); @@ -235,11 +335,149 @@ function TriggerConnector() { canvasElement.removeEventListener('contextmenu', handleRightClick); }; - }, [gl, subModule, selectedProduct, firstSelectedPoint]); + }, [gl, subModule, selectedProduct, firstSelectedPoint, deleteTool]); + + + useFrame(() => { + if (firstSelectedPoint) { + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster.intersectObjects(scene.children, true).filter( + (intersect) => + !intersect.object.name.includes("Roof") && + !intersect.object.name.includes("agv-collider") && + !intersect.object.name.includes("MeasurementReference") && + !intersect.object.parent?.name.includes("Zone") && + !(intersect.object.type === "GridHelper") && + !(intersect.object.type === "Line2") + ); + + let point: THREE.Vector3 | null = null; + + if (intersects.length > 0) { + point = intersects[0].point; + if (point.y < 0.05) { + point = new THREE.Vector3(point.x, 0.05, point.z); + } + } + + const sphereIntersects = raycaster.intersectObjects(scene.children, true).filter((intersect) => intersect.object.name === ('Event-Sphere')); + + if (sphereIntersects.length > 0 && sphereIntersects[0].object.uuid === firstSelectedPoint.pointUuid) { + setHelperLineColor('red'); + setCurrentLine(null); + return; + } + + const startPoint = getWorldPositionFromScene(firstSelectedPoint.pointUuid); + + if (point && startPoint) { + if (sphereIntersects.length > 0) { + point = sphereIntersects[0].object.getWorldPosition(new THREE.Vector3()); + } + const distance = startPoint.distanceTo(point); + const heightFactor = Math.max(0.5, distance * 0.2); + const midPoint = new THREE.Vector3( + (startPoint.x + point.x) / 2, + Math.max(startPoint.y, point.y) + heightFactor, + (startPoint.z + point.z) / 2 + ); + + const endPoint = point; + + setCurrentLine({ + start: startPoint, + mid: midPoint, + end: endPoint, + }); + + setHelperLineColor(sphereIntersects.length > 0 ? "#6cf542" : "red"); + } else { + setCurrentLine(null); + } + } else { + setCurrentLine(null); + } + + }) + + const getWorldPositionFromScene = (pointUuid: string): THREE.Vector3 | null => { + const pointObj = scene.getObjectByProperty("uuid", pointUuid); + if (!pointObj) return null; + + const worldPosition = new THREE.Vector3(); + pointObj.getWorldPosition(worldPosition); + return worldPosition; + }; + + const removeConnection = (connection: ConnectionLine) => { + if (connection.trigger.triggerUuid) { + const event = removeTrigger(selectedProduct.productId, connection.trigger.triggerUuid); + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } + } + }; return ( - <> - + + {connections.map((connection) => { + const startPoint = getWorldPositionFromScene(connection.startPointUuid); + const endPoint = getWorldPositionFromScene(connection.endPointUuid); + + if (!startPoint || !endPoint) return null; + + const distance = startPoint.distanceTo(endPoint); + const heightFactor = Math.max(0.5, distance * 0.2); + const midPoint = new THREE.Vector3( + (startPoint.x + endPoint.x) / 2, + Math.max(startPoint.y, endPoint.y) + heightFactor, + (startPoint.z + endPoint.z) / 2 + ); + + return ( + (groupRefs.current[connection.id] = el!)} + start={startPoint.toArray()} + end={endPoint.toArray()} + mid={midPoint.toArray()} + color={deleteTool && hoveredLineKey === connection.id ? "red" : "#42a5f5"} + lineWidth={4} + dashed={deleteTool && hoveredLineKey === connection.id ? false : true} + dashSize={0.75} + dashScale={20} + onPointerOver={() => setHoveredLineKey(connection.id)} + onPointerOut={() => setHoveredLineKey(null)} + onClick={() => { + if (deleteTool) { + setHoveredLineKey(null); + setCurrentLine(null); + removeConnection(connection); + } + }} + userData={connection.trigger} + /> + ); + })} + + {currentLine && ( + + )} + ); } diff --git a/app/src/modules/simulation/ui/vehicle/vehicleUI.tsx b/app/src/modules/simulation/ui/vehicle/vehicleUI.tsx index 5dec724..2a124cd 100644 --- a/app/src/modules/simulation/ui/vehicle/vehicleUI.tsx +++ b/app/src/modules/simulation/ui/vehicle/vehicleUI.tsx @@ -1,12 +1,16 @@ import React, { useEffect, useRef, useState } from 'react'; +import * as Types from "../../../../types/world/worldTypes"; import startPoint from "../../../../assets/gltf-glb/arrow_green.glb"; -import startEnd from "../../../../assets/gltf-glb/arrow_red.glb"; import * as THREE from "three"; +import startEnd from "../../../../assets/gltf-glb/arrow_red.glb"; import { useGLTF } from '@react-three/drei'; import { useFrame, useThree } from '@react-three/fiber'; -import { useSelectedEventSphere } from '../../../../store/simulation/useSimulationStore'; +import { useSelectedEventSphere, useIsDragging, useIsRotating } from '../../../../store/simulation/useSimulationStore'; import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'; -import * as Types from "../../../../types/world/worldTypes"; +import { useProductStore } from '../../../../store/simulation/useProductStore'; +import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; +import { upsertProductOrEventApi } from '../../../../services/simulation/UpsertProductOrEventApi'; + const VehicleUI = () => { const { scene: startScene } = useGLTF(startPoint) as any; const { scene: endScene } = useGLTF(startEnd) as any; @@ -14,67 +18,60 @@ const VehicleUI = () => { const endMarker = useRef(null); const prevMousePos = useRef({ x: 0, y: 0 }); const { selectedEventSphere } = useSelectedEventSphere(); - const { vehicles, updateVehicle } = useVehicleStore(); + const { selectedProduct } = useSelectedProduct(); + const { getVehicleById } = useVehicleStore(); + const { updateEvent } = useProductStore(); const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 0, 0]); const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 0, 0]); const [startRotation, setStartRotation] = useState<[number, number, number]>([0, 0, 0]); const [endRotation, setEndRotation] = useState<[number, number, number]>([0, 0, 0]); - const [isDragging, setIsDragging] = useState<"start" | "end" | null>(null); - const [isRotating, setIsRotating] = useState<"start" | "end" | null>(null); + const { isDragging, setIsDragging } = useIsDragging(); + const { isRotating, setIsRotating } = useIsRotating(); const { raycaster } = useThree(); const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)); const state: Types.ThreeState = useThree(); const controls: any = state.controls; + 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) return; - const selectedVehicle = vehicles.find( - (vehicle: any) => vehicle.modelUuid === selectedEventSphere.userData.modelUuid - ); + const selectedVehicle = getVehicleById(selectedEventSphere.userData.modelUuid); if (selectedVehicle?.point?.action) { const { pickUpPoint, unLoadPoint } = selectedVehicle.point.action; if (pickUpPoint) { - const pickupPosition = new THREE.Vector3( - pickUpPoint.position.x, - pickUpPoint.position.y, - pickUpPoint.position.z - ); - const pickupRotation = new THREE.Vector3( - pickUpPoint.rotation.x, - pickUpPoint.rotation.y, - pickUpPoint.rotation.z - ); - pickupPosition.y = 0; - setStartPosition([pickupPosition.x, 0, pickupPosition.z]); - setStartRotation([pickupRotation.x, pickupRotation.y, pickupRotation.z]); + setStartPosition([pickUpPoint.position.x, 0, pickUpPoint.position.z]); + setStartRotation([pickUpPoint.rotation.x, pickUpPoint.rotation.y, pickUpPoint.rotation.z]); } else { const defaultLocal = new THREE.Vector3(0, 0, 1.5); const defaultWorld = selectedEventSphere.localToWorld(defaultLocal); - defaultWorld.y = 0; setStartPosition([defaultWorld.x, 0, defaultWorld.z]); setStartRotation([0, 0, 0]); } if (unLoadPoint) { - const unLoadPosition = new THREE.Vector3( - unLoadPoint.position.x, - unLoadPoint.position.y, - unLoadPoint.position.z - ); - const unLoadRotation = new THREE.Vector3( - unLoadPoint.rotation.x, - unLoadPoint.rotation.y, - unLoadPoint.position.z - ); - unLoadPosition.y = 0; // Force y to 0 - setEndPosition([unLoadPosition.x, 0, unLoadPosition.z]); - setEndRotation([unLoadRotation.x, unLoadRotation.y, unLoadRotation.z]); + setEndPosition([unLoadPoint.position.x, 0, unLoadPoint.position.z]); + setEndRotation([unLoadPoint.rotation.x, unLoadPoint.rotation.y, unLoadPoint.rotation.z]); } else { const defaultLocal = new THREE.Vector3(0, 0, -1.5); const defaultWorld = selectedEventSphere.localToWorld(defaultLocal); - defaultWorld.y = 0; // Force y to 0 setEndPosition([defaultWorld.x, 0, defaultWorld.z]); setEndRotation([0, 0, 0]); } @@ -87,7 +84,6 @@ const VehicleUI = () => { const intersects = raycaster.ray.intersectPlane(plane.current, intersectPoint); if (intersects) { - intersectPoint.y = 0; // Force y to 0 if (isDragging === "start") { setStartPosition([intersectPoint.x, 0, intersectPoint.z]); } @@ -108,12 +104,12 @@ const VehicleUI = () => { if (marker) { const rotationSpeed = 10; - marker.rotation.y += deltaX * rotationSpeed; if (isRotating === 'start') { - setStartRotation([marker.rotation.x, marker.rotation.y, marker.rotation.z]) + const y = startRotation[1] + deltaX * rotationSpeed; + setStartRotation([0, y, 0]); } else { - - setEndRotation([marker.rotation.x, marker.rotation.y, marker.rotation.z]) + const y = endRotation[1] + deltaX * rotationSpeed; + setEndRotation([0, y, 0]); } } }); @@ -142,43 +138,34 @@ const VehicleUI = () => { setIsRotating(null); if (selectedEventSphere?.userData.modelUuid) { - const updatedVehicle = vehicles.find( - (vehicle) => vehicle.modelUuid === selectedEventSphere.userData.modelUuid - ); + const updatedVehicle = getVehicleById(selectedEventSphere.userData.modelUuid); if (updatedVehicle) { - updateVehicle(selectedEventSphere.userData.modelUuid, { + const event = updateEvent(selectedProduct.productId, selectedEventSphere.userData.modelUuid, { point: { ...updatedVehicle.point, action: { ...updatedVehicle.point?.action, pickUpPoint: { - position: { - x: startPosition[0], - y: startPosition[1], - z: startPosition[2], - }, - rotation: { - x: startRotation[0], - y: startRotation[1], - z: startRotation[2], - }, + position: { x: startPosition[0], y: startPosition[1], z: startPosition[2], }, + rotation: { x: 0, y: startRotation[1], z: 0, }, }, unLoadPoint: { - position: { - x: endPosition[0], - y: endPosition[1], - z: endPosition[2], - }, - rotation: { - x: endRotation[0], - y: endRotation[1], - z: endRotation[2], - }, + position: { x: endPosition[0], y: endPosition[1], z: endPosition[2], }, + rotation: { x: 0, y: endRotation[1], z: 0, }, }, }, }, - }); + }) + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } } } }; @@ -208,6 +195,7 @@ const VehicleUI = () => { object={startScene} ref={startMarker} position={startPosition} + rotation={startRotation} onPointerDown={(e: any) => { e.stopPropagation(); handlePointerDown(e, "start", "start"); @@ -224,6 +212,7 @@ const VehicleUI = () => { object={endScene} ref={endMarker} position={endPosition} + rotation={endRotation} onPointerDown={(e: any) => { e.stopPropagation(); handlePointerDown(e, "end", "end"); diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx index 2f0b235..9ca7355 100644 --- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx +++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx @@ -11,7 +11,7 @@ interface VehicleAnimatorProps { reset: () => void; currentPhase: string; agvUuid: number; - agvDetail: any; + agvDetail: VehicleStatus; } function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetail, reset }: VehicleAnimatorProps) { @@ -32,7 +32,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai let startTime: number; let fixedInterval: number; let coveredDistance = progressRef.current; - let objectRotation = (agvDetail.point?.action?.pickUpPoint?.rotation || { x: 0, y: 0, z: 0 }) as { x: number; y: number; z: number }; + let objectRotation = (agvDetail.point?.action?.pickUpPoint?.rotation || { x: 0, y: 0, z: 0 }) as { x: number; y: number; z: number } | undefined; useEffect(() => { @@ -67,10 +67,9 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai setRestingRotation(true); decrementVehicleLoad(agvDetail.modelUuid, 0); const object = scene.getObjectByProperty('uuid', agvUuid); - console.log('currentPhase: ', currentPhase); if (object) { object.position.set(agvDetail.position[0], agvDetail.position[1], agvDetail.position[2]); - object.rotation.set(objectRotation.x, objectRotation.y, objectRotation.z); + object.rotation.set(agvDetail.rotation[0], agvDetail.rotation[1], agvDetail.rotation[2]); } } }, [isReset, isPlaying]) @@ -132,7 +131,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai } if (progressRef.current >= totalDistance) { - if (restRotation) { + if (restRotation && objectRotation) { const targetQuaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(objectRotation.x, objectRotation.y, objectRotation.z)); object.quaternion.slerp(targetQuaternion, delta * 2); const angleDiff = object.quaternion.angleTo(targetQuaternion); diff --git a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx index 6a81d3a..412a4ba 100644 --- a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx +++ b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx @@ -30,7 +30,8 @@ function VehicleInstance({ agvDetail }: any) { ); function vehicleStatus(modelId: string, status: string) { - // console.log(`${modelId} , ${status}); + // console.log(`${modelId} , ${status}`); + } // Function to reset everything @@ -44,7 +45,7 @@ function VehicleInstance({ agvDetail }: any) { const increment = () => { if (isIncrememtable.current) { - incrementVehicleLoad(agvDetail.modelUuid, 2); + incrementVehicleLoad(agvDetail.modelUuid, 10); isIncrememtable.current = false; } } @@ -69,6 +70,7 @@ function VehicleInstance({ agvDetail }: any) { increment(); }, 5000); + if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity) { const toDrop = computePath( agvDetail.point.action.pickUpPoint.position, diff --git a/app/src/modules/simulation/vehicle/vehicles.tsx b/app/src/modules/simulation/vehicle/vehicles.tsx index 7badec5..19f049c 100644 --- a/app/src/modules/simulation/vehicle/vehicles.tsx +++ b/app/src/modules/simulation/vehicle/vehicles.tsx @@ -1,220 +1,48 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect } from "react"; import VehicleInstances from "./instances/vehicleInstances"; import { useVehicleStore } from "../../../store/simulation/useVehicleStore"; -import { useFloorItems } from "../../../store/store"; -import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; +import { useSelectedEventData, useSelectedEventSphere, useSelectedProduct } from "../../../store/simulation/useSimulationStore"; import VehicleUI from "../ui/vehicle/vehicleUI"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; -function Vehicles() { +import { useProductStore } from "../../../store/simulation/useProductStore"; - const { vehicles, addVehicle } = useVehicleStore(); +function Vehicles() { + const { products, getProductById } = useProductStore(); + const { selectedProduct } = useSelectedProduct(); + const { vehicles, addVehicle, clearvehicles } = useVehicleStore(); const { selectedEventSphere } = useSelectedEventSphere(); const { selectedEventData } = useSelectedEventData(); - const { floorItems } = useFloorItems(); const { isPlaying } = usePlayButtonStore(); - const [vehicleStatusSample, setVehicleStatusSample] = useState< - VehicleEventSchema[] - >([ - { - modelUuid: "9356f710-4727-4b50-bdb2-9c1e747ecc74", - modelName: "AGV", - position: [97.9252965204558, 0, 37.96138815638661], - rotation: [0, 0, 0], - state: "idle", - type: "vehicle", - speed: 2.5, - point: { - uuid: "point-789", - position: [0, 1, 0], - rotation: [0, 0, 0], - action: { - actionUuid: "action-456", - actionName: "Deliver to Zone A", - actionType: "travel", - unLoadDuration: 10, - loadCapacity: 2, - steeringAngle:0, - pickUpPoint: { position: { x: 98.71483985219794, y: 0, z: 28.66321267938962 }, rotation: { x: 0, y: 0, z: 0 } }, - unLoadPoint: { position: { x: 105.71483985219794, y: 0, z: 28.66321267938962 }, rotation: { x: 0, y: 0, z: 0 } }, - triggers: [ - { - triggerUuid: "trig-001", - triggerName: "Start Travel", - triggerType: "onComplete", - delay: 0, - triggeredAsset: { - triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" }, - triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" }, - triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" } - } - }, - { - triggerUuid: "trig-002", - triggerName: "Complete Travel", - triggerType: "onComplete", - delay: 2, - triggeredAsset: null - } - ] - } + useEffect(() => { + if (selectedProduct.productId) { + const product = getProductById(selectedProduct.productId); + if (product) { + clearvehicles(); + product.eventDatas.forEach(events => { + if (events.type === 'vehicle') { + addVehicle(selectedProduct.productId, events); + } + }); } - }, - { - modelUuid: "b06960bb-3d2e-41f7-a646-335f389c68b4", - modelName: "AGV", - position: [89.61609306554463, 0, 33.634136622267356], - rotation: [0, 0, 0], - state: "idle", - type: "vehicle", - speed: 2.5, - point: { - uuid: "point-789", - position: [0, 1, 0], - rotation: [0, 0, 0], - action: { - actionUuid: "action-456", - actionName: "Deliver to Zone A", - actionType: "travel", - unLoadDuration: 10, - loadCapacity: 2, - steeringAngle:0, - pickUpPoint: null, - unLoadPoint: null, - triggers: [ - { - triggerUuid: "trig-001", - triggerName: "Start Travel", - triggerType: "onStart", - delay: 0, - triggeredAsset: { - triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" }, - triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" }, - triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" } - } - }, - { - triggerUuid: "trig-002", - triggerName: "Complete Travel", - triggerType: "onComplete", - delay: 2, - triggeredAsset: null - } - ] - } - } - }, - // { - // modelUuid: "cd7d0584-0684-42b4-b051-9e882c1914aa", - // modelName: "AGV", - // position: [105.90938758014703, 0, 31.584209911095215], - // rotation: [0, 0, 0], - // state: "idle", - // type: "vehicle", - // speed: 2.5, - // point: { - // uuid: "point-789", - // position: [0, 1, 0], - // rotation: [0, 0, 0], - // action: { - // actionUuid: "action-456", - // actionName: "Deliver to Zone A", - // actionType: "travel", - // unLoadDuration: 10, - // loadCapacity: 2, - // steeringAngle:0, - // pickUpPoint: null, - // unLoadPoint: null, - // triggers: [ - // { - // triggerUuid: "trig-001", - // triggerName: "Start Travel", - // triggerType: "onStart", - // delay: 0, - // triggeredAsset: { - // triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" }, - // triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" }, - // triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" } - // } - // }, - // { - // triggerUuid: "trig-002", - // triggerName: "Complete Travel", - // triggerType: "onComplete", - // delay: 2, - // triggeredAsset: null - // } - // ] - // } - // } - // }, - // { - // modelUuid: "e729a4f1-11d2-4778-8d6a-468f1b4f6b79", - // modelName: "forklift", - // position: [98.85729337188162, 0, 38.36616546567653], - // rotation: [0, 0, 0], - // state: "idle", - // type: "vehicle", - // speed: 2.5, - // point: { - // uuid: "point-789", - // position: [0, 1, 0], - // rotation: [0, 0, 0], - // action: { - // actionUuid: "action-456", - // actionName: "Deliver to Zone A", - // actionType: "travel", - // unLoadDuration: 15, - // loadCapacity: 5, - // steeringAngle:0, - // pickUpPoint: null, - // unLoadPoint: null, - // triggers: [ - // { - // triggerUuid: "trig-001", - // triggerName: "Start Travel", - // triggerType: "onStart", - // delay: 0, - // triggeredAsset: { - // triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" }, - // triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" }, - // triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" } - // } - // }, - // { - // triggerUuid: "trig-002", - // triggerName: "Complete Travel", - // triggerType: "onComplete", - // delay: 2, - // triggeredAsset: null - // } - // ] - // } - // } - // } - ]); + } + }, [selectedProduct, products]); + useEffect(() => { // console.log('vehicles: ', vehicles); }, [vehicles]) - useEffect(() => { - addVehicle("123", vehicleStatusSample[0]); - addVehicle('123', vehicleStatusSample[1]); - // addVehicle('123', vehicleStatusSample[2]); - }, []); - - return ( <> + + {selectedEventSphere && selectedEventData?.data.type === "vehicle" && !isPlaying && < VehicleUI /> } + ); } -export default Vehicles; - - - +export default Vehicles; \ No newline at end of file diff --git a/app/src/modules/visualization/RealTimeVisulization.tsx b/app/src/modules/visualization/RealTimeVisulization.tsx index dadaf9c..a8f44b4 100644 --- a/app/src/modules/visualization/RealTimeVisulization.tsx +++ b/app/src/modules/visualization/RealTimeVisulization.tsx @@ -66,8 +66,7 @@ const RealTimeVisulization: React.FC = () => { const { selectedZone, setSelectedZone } = useSelectedZoneStore(); const { setRightSelect } = useRightSelected(); - const { editWidgetOptions, setEditWidgetOptions } = - useEditWidgetOptionsStore(); + const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore(); const { rightClickSelected, setRightClickSelected } = useRightClickSelected(); const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false); const { setFloatingWidget } = useFloatingWidget(); diff --git a/app/src/store/simulation/useArmBotStore.ts b/app/src/store/simulation/useArmBotStore.ts index 642762f..3c54596 100644 --- a/app/src/store/simulation/useArmBotStore.ts +++ b/app/src/store/simulation/useArmBotStore.ts @@ -10,6 +10,7 @@ interface ArmBotStore { modelUuid: string, updates: Partial> ) => void; + clearArmBots: () => void; addCurrentAction: (modelUuid: string, actionUuid: string) => void; removeCurrentAction: (modelUuid: string) => void; @@ -39,14 +40,17 @@ export const useArmBotStore = create()( addArmBot: (productId, event) => { set((state) => { - state.armBots.push({ - ...event, - productId, - isActive: false, - idleTime: 0, - activeTime: 0, - state: 'idle', - }); + const exists = state.armBots.some(a => a.modelUuid === event.modelUuid); + if (!exists) { + state.armBots.push({ + ...event, + productId, + isActive: false, + idleTime: 0, + activeTime: 0, + state: 'idle', + }); + } }); }, @@ -65,6 +69,12 @@ export const useArmBotStore = create()( }); }, + clearArmBots: () => { + set((state) => { + state.armBots = []; + }); + }, + addCurrentAction: (modelUuid, actionUuid) => { set((state) => { const armBot = state.armBots.find(a => a.modelUuid === modelUuid); diff --git a/app/src/store/simulation/useConveyorStore.ts b/app/src/store/simulation/useConveyorStore.ts index 15dbf34..862ce79 100644 --- a/app/src/store/simulation/useConveyorStore.ts +++ b/app/src/store/simulation/useConveyorStore.ts @@ -10,6 +10,7 @@ interface ConveyorStore { modelUuid: string, updates: Partial> ) => void; + clearConveyors: () => void; setConveyorActive: (modelUuid: string, isActive: boolean) => void; setConveyorState: (modelUuid: string, newState: ConveyorStatus['state']) => void; @@ -30,14 +31,17 @@ export const useConveyorStore = create()( addConveyor: (productId, event) => { set((state) => { - state.conveyors.push({ - ...event, - productId, - isActive: false, - idleTime: 0, - activeTime: 0, - state: 'idle', - }); + const exists = state.conveyors.some(c => c.modelUuid === event.modelUuid); + if (!exists) { + state.conveyors.push({ + ...event, + productId, + isActive: false, + idleTime: 0, + activeTime: 0, + state: 'idle', + }); + } }); }, @@ -56,6 +60,12 @@ export const useConveyorStore = create()( }); }, + clearConveyors: () => { + set((state) => { + state.conveyors = []; + }); + }, + setConveyorActive: (modelUuid, isActive) => { set((state) => { const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); diff --git a/app/src/store/simulation/useEventsStore.ts b/app/src/store/simulation/useEventsStore.ts index 2d92fc2..5580eb1 100644 --- a/app/src/store/simulation/useEventsStore.ts +++ b/app/src/store/simulation/useEventsStore.ts @@ -7,7 +7,7 @@ type EventsStore = { // Event-level actions addEvent: (event: EventsSchema) => void; removeEvent: (modelUuid: string) => void; - updateEvent: (modelUuid: string, updates: Partial) => void; + updateEvent: (modelUuid: string, updates: Partial) => EventsSchema | undefined; // Point-level actions addPoint: (modelUuid: string, point: ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema) => void; @@ -49,7 +49,9 @@ export const useEventsStore = create()( // Event-level actions addEvent: (event) => { set((state) => { - state.events.push(event); + if (!state.events.some(e => 'modelUuid' in e && e.modelUuid === event.modelUuid)) { + state.events.push(event); + } }); }, @@ -60,12 +62,15 @@ export const useEventsStore = create()( }, updateEvent: (modelUuid, updates) => { + let updatedEvent: EventsSchema | undefined; set((state) => { const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); if (event) { Object.assign(event, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); } }); + return updatedEvent; }, // Point-level actions @@ -73,9 +78,14 @@ export const useEventsStore = create()( set((state) => { const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); if (event && 'points' in event) { - (event as ConveyorEventSchema).points.push(point as ConveyorPointSchema); + const existingPoint = (event as ConveyorEventSchema).points.find(p => p.uuid === point.uuid); + if (!existingPoint) { + (event as ConveyorEventSchema).points.push(point as ConveyorPointSchema); + } } else if (event && 'point' in event) { - (event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point = point as any; + if (!(event as any).point || (event as any).point.uuid !== point.uuid) { + (event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point = point as any; + } } }); }, @@ -110,14 +120,15 @@ export const useEventsStore = create()( 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) { + if (point && (!point.action || point.action.actionUuid !== action.actionUuid)) { point.action = action as any; } } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { - if ('action' in (event as any).point) { - (event as any).point.action = action; - } else if ('actions' in (event as any).point) { - (event as any).point.actions.push(action); + const point = (event as any).point; + if ('action' in point && (!point.action || point.action.actionUuid !== action.actionUuid)) { + point.action = action; + } else if ('actions' in point && !point.actions.some((a: any) => a.actionUuid === action.actionUuid)) { + point.actions.push(action); } } }); @@ -180,18 +191,22 @@ export const useEventsStore = create()( if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { if (point.action && point.action.actionUuid === actionUuid) { - point.action.triggers.push(trigger); + if (!point.action.triggers.some(t => t.triggerUuid === trigger.triggerUuid)) { + point.action.triggers.push(trigger); + } return; } } } else if ('point' in event) { - const point = (event as any).point; + const point: MachinePointSchema | VehiclePointSchema = (event as any).point; if ('action' in point && point.action.actionUuid === actionUuid) { - point.action.triggers.push(trigger); + if (!point.action.triggers.some(t => t.triggerUuid === trigger.triggerUuid)) { + point.action.triggers.push(trigger); + } return; } else if ('actions' in point) { - const action = point.actions.find((a: any) => a.actionUuid === actionUuid); - if (action) { + const action = (point as RoboticArmPointSchema).actions.find((a) => a.actionUuid === actionUuid); + if (action && !action.triggers.some(t => t.triggerUuid === trigger.triggerUuid)) { action.triggers.push(trigger); return; } diff --git a/app/src/store/simulation/useMachineStore.ts b/app/src/store/simulation/useMachineStore.ts index cc927f7..9a564ac 100644 --- a/app/src/store/simulation/useMachineStore.ts +++ b/app/src/store/simulation/useMachineStore.ts @@ -4,23 +4,23 @@ import { immer } from 'zustand/middleware/immer'; interface MachineStore { machines: MachineStatus[]; - // Actions addMachine: (productId: string, machine: MachineEventSchema) => void; removeMachine: (modelUuid: string) => void; updateMachine: ( modelUuid: string, updates: Partial> ) => void; + clearMachines: () => void; + + addCurrentAction: (modelUuid: string, actionUuid: string) => void; + removeCurrentAction: (modelUuid: string) => void; - // Status updates setMachineActive: (modelUuid: string, isActive: boolean) => void; setMachineState: (modelUuid: string, newState: MachineStatus['state']) => void; - // Time tracking incrementActiveTime: (modelUuid: string, incrementBy: number) => void; incrementIdleTime: (modelUuid: string, incrementBy: number) => void; - // Helpers getMachineById: (modelUuid: string) => MachineStatus | undefined; getMachinesByProduct: (productId: string) => MachineStatus[]; getMachinesBystate: (state: string) => MachineStatus[]; @@ -32,17 +32,19 @@ export const useMachineStore = create()( immer((set, get) => ({ machines: [], - // Actions addMachine: (productId, machine) => { set((state) => { - state.machines.push({ - ...machine, - productId, - isActive: false, - idleTime: 0, - activeTime: 0, - state: 'idle', - }); + const exists = state.machines.some(m => m.modelUuid === machine.modelUuid); + if (!exists) { + state.machines.push({ + ...machine, + productId, + isActive: false, + idleTime: 0, + activeTime: 0, + state: 'idle', + }); + } }); }, @@ -61,7 +63,36 @@ export const useMachineStore = create()( }); }, - // Status updates + clearMachines: () => { + set((state) => { + state.machines = []; + }); + }, + + addCurrentAction: (modelUuid) => { + set((state) => { + const armBot = state.machines.find(a => a.modelUuid === modelUuid); + if (armBot) { + const action = armBot.point.action; + if (action) { + armBot.currentAction = { + actionUuid: action.actionUuid, + actionName: action.actionName, + }; + } + } + }); + }, + + removeCurrentAction: (modelUuid) => { + set((state) => { + const armBot = state.machines.find(a => a.modelUuid === modelUuid); + if (armBot) { + armBot.currentAction = undefined; + } + }); + }, + setMachineActive: (modelUuid, isActive) => { set((state) => { const machine = state.machines.find(m => m.modelUuid === modelUuid); @@ -80,7 +111,6 @@ export const useMachineStore = create()( }); }, - // Time tracking incrementActiveTime: (modelUuid, incrementBy) => { set((state) => { const machine = state.machines.find(m => m.modelUuid === modelUuid); @@ -99,7 +129,6 @@ export const useMachineStore = create()( }); }, - // Helpers getMachineById: (modelUuid) => { return get().machines.find(m => m.modelUuid === modelUuid); }, diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts index 4f2b546..b7448f2 100644 --- a/app/src/store/simulation/useProductStore.ts +++ b/app/src/store/simulation/useProductStore.ts @@ -33,27 +33,30 @@ type ProductsStore = { pointUuid: string, action: ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] ) => EventsSchema | undefined; - removeAction: (actionUuid: string) => EventsSchema | undefined; + removeAction: (productId: string, actionUuid: string) => EventsSchema | undefined; updateAction: ( + productId: string, actionUuid: string, updates: Partial ) => EventsSchema | undefined; // Trigger-level actions addTrigger: ( + productId: string, actionUuid: string, trigger: TriggerSchema - ) => void; - removeTrigger: (triggerUuid: string) => void; + ) => EventsSchema | undefined; + removeTrigger: (productId: string, triggerUuid: string) => EventsSchema | undefined; updateTrigger: ( + productId: string, triggerUuid: string, updates: Partial ) => void; // Renaming functions renameProduct: (productId: string, newName: string) => void; - renameAction: (actionUuid: string, newName: string) => EventsSchema | undefined; - renameTrigger: (triggerUuid: string, newName: string) => void; + renameAction: (productId: string, actionUuid: string, newName: string) => EventsSchema | undefined; + renameTrigger: (productId: string, triggerUuid: string, newName: string) => void; // Helper functions getProductById: (productId: string) => { productName: string; productId: string; eventDatas: EventsSchema[] } | undefined; @@ -71,12 +74,15 @@ export const useProductStore = create()( // Product-level actions addProduct: (productName, productId) => { set((state) => { - const newProduct = { - productName, - productId: productId, - eventDatas: [] - }; - state.products.push(newProduct); + const existingProduct = state.products.find(p => p.productId === productId); + if (!existingProduct) { + const newProduct = { + productName, + productId: productId, + eventDatas: [] + }; + state.products.push(newProduct); + } }); }, @@ -106,7 +112,10 @@ export const useProductStore = create()( set((state) => { const product = state.products.find(p => p.productId === productId); if (product) { - product.eventDatas.push(event); + const existingEvent = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === event.modelUuid); + if (!existingEvent) { + product.eventDatas.push(event); + } } }); }, @@ -120,7 +129,7 @@ export const useProductStore = create()( }); }, - deleteEvent: (modelUuid: string) => { + deleteEvent: (modelUuid) => { set((state) => { for (const product of state.products) { product.eventDatas = product.eventDatas.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid); @@ -150,9 +159,15 @@ export const useProductStore = create()( if (product) { const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); if (event && 'points' in event) { - (event as ConveyorEventSchema).points.push(point as ConveyorPointSchema); + const existingPoint = (event as ConveyorEventSchema).points.find(p => p.uuid === point.uuid); + if (!existingPoint) { + (event as ConveyorEventSchema).points.push(point as ConveyorPointSchema); + } } else if (event && 'point' in event) { - (event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point = point as any; + const existingPoint = (event as any).point?.uuid === point.uuid; + if (!existingPoint) { + (event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point = point as any; + } } } }); @@ -198,17 +213,22 @@ export const useProductStore = create()( const event = product.eventDatas.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) { + if (point && (!point.action || point.action.actionUuid !== action.actionUuid)) { point.action = action as any; updatedEvent = JSON.parse(JSON.stringify(event)); } } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { if ('action' in (event as any).point) { - (event as any).point.action = action; - updatedEvent = JSON.parse(JSON.stringify(event)); + if (!(event as any).point.action || (event as any).point.action.actionUuid !== action.actionUuid) { + (event as any).point.action = action; + updatedEvent = JSON.parse(JSON.stringify(event)); + } } else if ('actions' in (event as any).point) { - (event as any).point.actions.push(action); - updatedEvent = JSON.parse(JSON.stringify(event)); + const existingAction = (event as any).point.actions.find((a: any) => a.actionUuid === action.actionUuid); + if (!existingAction) { + (event as any).point.actions.push(action); + updatedEvent = JSON.parse(JSON.stringify(event)); + } } } } @@ -216,10 +236,11 @@ export const useProductStore = create()( return updatedEvent; }, - removeAction: (actionUuid: string) => { + removeAction: (productId, actionUuid) => { let updatedEvent: EventsSchema | undefined; set((state) => { - for (const product of state.products) { + const product = state.products.find(p => p.productId === productId); + if (product) { for (const event of product.eventDatas) { if ('points' in event) { // Handle ConveyorEventSchema @@ -248,10 +269,11 @@ export const useProductStore = create()( return updatedEvent; }, - updateAction: (actionUuid, updates) => { + updateAction: (productId, actionUuid, updates) => { let updatedEvent: EventsSchema | undefined; set((state) => { - for (const product of state.products) { + const product = state.products.find(p => p.productId === productId); + if (product) { for (const event of product.eventDatas) { if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { @@ -283,26 +305,40 @@ export const useProductStore = create()( }, // Trigger-level actions - addTrigger: (actionUuid, trigger) => { + addTrigger: (productId, actionUuid, trigger) => { + let updatedEvent: EventsSchema | undefined; set((state) => { - for (const product of state.products) { + const product = state.products.find(p => p.productId === productId); + if (product) { for (const event of product.eventDatas) { if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { if (point.action && point.action.actionUuid === actionUuid) { - point.action.triggers.push(trigger); + const existingTrigger = point.action.triggers.find(t => t.triggerUuid === trigger.triggerUuid); + if (!existingTrigger) { + point.action.triggers.push(trigger); + updatedEvent = JSON.parse(JSON.stringify(event)); + } return; } } } else if ('point' in event) { const point = (event as any).point; if ('action' in point && point.action.actionUuid === actionUuid) { - point.action.triggers.push(trigger); + const existingTrigger = point.action.triggers.find((t: any) => t.triggerUuid === trigger.triggerUuid); + if (!existingTrigger) { + point.action.triggers.push(trigger); + updatedEvent = JSON.parse(JSON.stringify(event)); + } return; } else if ('actions' in point) { const action = point.actions.find((a: any) => a.actionUuid === actionUuid); if (action) { - action.triggers.push(trigger); + const existingTrigger = action.triggers.find((t: any) => t.triggerUuid === trigger.triggerUuid); + if (!existingTrigger) { + action.triggers.push(trigger); + updatedEvent = JSON.parse(JSON.stringify(event)); + } return; } } @@ -310,26 +346,41 @@ export const useProductStore = create()( } } }); + return updatedEvent; }, - removeTrigger: (triggerUuid) => { + removeTrigger: (productId, triggerUuid) => { + let updatedEvent: EventsSchema | undefined; set((state) => { - for (const product of state.products) { + const product = state.products.find(p => p.productId === productId); + if (product) { for (const event of product.eventDatas) { if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { if (point.action && 'triggers' in point.action) { - point.action.triggers = point.action.triggers.filter(t => t.triggerUuid !== triggerUuid); + const Trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid); + if (Trigger) { + point.action.triggers = point.action.triggers.filter(t => t.triggerUuid !== triggerUuid); + updatedEvent = JSON.parse(JSON.stringify(event)); + } } } } else if ('point' in event) { const point = (event as any).point; if ('action' in point && 'triggers' in point.action) { - point.action.triggers = point.action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); + const Trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (Trigger) { + point.action.triggers = point.action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); + updatedEvent = JSON.parse(JSON.stringify(event)); + } } else if ('actions' in point) { for (const action of point.actions) { if ('triggers' in action) { - action.triggers = action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); + const Trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (Trigger) { + action.triggers = action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); + updatedEvent = JSON.parse(JSON.stringify(event)); + } } } } @@ -337,11 +388,13 @@ export const useProductStore = create()( } } }); + return updatedEvent; }, - updateTrigger: (triggerUuid, updates) => { + updateTrigger: (productId, triggerUuid, updates) => { set((state) => { - for (const product of state.products) { + const product = state.products.find(p => p.productId === productId); + if (product) { for (const event of product.eventDatas) { if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { @@ -388,10 +441,11 @@ export const useProductStore = create()( }); }, - renameAction: (actionUuid, newName) => { + renameAction: (productId, actionUuid, newName) => { let updatedEvent: EventsSchema | undefined; set((state) => { - for (const product of state.products) { + const product = state.products.find(p => p.productId === productId); + if (product) { for (const event of product.eventDatas) { if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { @@ -422,9 +476,10 @@ export const useProductStore = create()( return updatedEvent; }, - renameTrigger: (triggerUuid, newName) => { + renameTrigger: (productId, triggerUuid, newName) => { set((state) => { - for (const product of state.products) { + const product = state.products.find(p => p.productId === productId); + if (product) { for (const event of product.eventDatas) { if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { diff --git a/app/src/store/simulation/useSimulationStore.ts b/app/src/store/simulation/useSimulationStore.ts index 5085688..6e4321f 100644 --- a/app/src/store/simulation/useSimulationStore.ts +++ b/app/src/store/simulation/useSimulationStore.ts @@ -114,4 +114,36 @@ export const useSelectedAction = create()( }); }, })) +); + +interface IsDraggingState { + isDragging: "start" | "end" | null; + setIsDragging: (state: "start" | "end" | null) => void; +} + +export const useIsDragging = create()( + immer((set) => ({ + isDragging: null, + setIsDragging: (state) => { + set((s) => { + s.isDragging = state; + }); + }, + })) +); + +interface IsRotatingState { + isRotating: "start" | "end" | null; + setIsRotating: (state: "start" | "end" | null) => void; +} + +export const useIsRotating = create()( + immer((set) => ({ + isRotating: null, + setIsRotating: (state) => { + set((s) => { + s.isRotating = state; + }); + }, + })) ); \ No newline at end of file diff --git a/app/src/store/simulation/useStorageUnitStore.ts b/app/src/store/simulation/useStorageUnitStore.ts index d729708..aec2f12 100644 --- a/app/src/store/simulation/useStorageUnitStore.ts +++ b/app/src/store/simulation/useStorageUnitStore.ts @@ -4,26 +4,22 @@ import { immer } from 'zustand/middleware/immer'; interface StorageUnitStore { storageUnits: StorageUnitStatus[]; - // Actions addStorageUnit: (productId: string, storageUnit: StorageEventSchema) => void; removeStorageUnit: (modelUuid: string) => void; updateStorageUnit: ( modelUuid: string, updates: Partial> ) => void; + clearStorageUnits: () => void; - // Status updates setStorageUnitActive: (modelUuid: string, isActive: boolean) => void; setStorageUnitState: (modelUuid: string, newState: StorageUnitStatus['state']) => void; - // Load updates updateStorageUnitLoad: (modelUuid: string, incrementBy: number) => void; - // Time tracking incrementActiveTime: (modelUuid: string, incrementBy: number) => void; incrementIdleTime: (modelUuid: string, incrementBy: number) => void; - // Helpers getStorageUnitById: (modelUuid: string) => StorageUnitStatus | undefined; getStorageUnitsByProduct: (productId: string) => StorageUnitStatus[]; getStorageUnitsBystate: (state: string) => StorageUnitStatus[]; @@ -37,18 +33,20 @@ export const useStorageUnitStore = create()( immer((set, get) => ({ storageUnits: [], - // Actions addStorageUnit: (productId, storageUnit) => { set((state) => { - state.storageUnits.push({ - ...storageUnit, - productId, - isActive: false, - idleTime: 0, - activeTime: 0, - currentLoad: 0, - state: 'idle', - }); + const exists = state.storageUnits.some(s => s.modelUuid === storageUnit.modelUuid); + if (!exists) { + state.storageUnits.push({ + ...storageUnit, + productId, + isActive: false, + idleTime: 0, + activeTime: 0, + currentLoad: 0, + state: 'idle', + }); + } }); }, @@ -67,7 +65,12 @@ export const useStorageUnitStore = create()( }); }, - // Status updates + clearStorageUnits: () => { + set(() => ({ + storageUnits: [], + })); + }, + setStorageUnitActive: (modelUuid, isActive) => { set((state) => { const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); @@ -86,7 +89,6 @@ export const useStorageUnitStore = create()( }); }, - // Load updates updateStorageUnitLoad: (modelUuid, incrementBy) => { set((state) => { const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); @@ -96,7 +98,6 @@ export const useStorageUnitStore = create()( }); }, - // Time tracking incrementActiveTime: (modelUuid, incrementBy) => { set((state) => { const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); @@ -115,7 +116,6 @@ export const useStorageUnitStore = create()( }); }, - // Helpers getStorageUnitById: (modelUuid) => { return get().storageUnits.find(s => s.modelUuid === modelUuid); }, diff --git a/app/src/store/simulation/useVehicleStore.ts b/app/src/store/simulation/useVehicleStore.ts index a5ea3be..ecef809 100644 --- a/app/src/store/simulation/useVehicleStore.ts +++ b/app/src/store/simulation/useVehicleStore.ts @@ -20,6 +20,7 @@ interface VehiclesStore { modelUuid: string, updates: Partial> ) => void; + clearvehicles: () => void; setVehicleActive: (modelUuid: string, isActive: boolean) => void; updateSteeringAngle: (modelUuid: string, steeringAngle: number) => void; @@ -41,15 +42,18 @@ export const useVehicleStore = create()( addVehicle: (productId, event) => { set((state) => { - state.vehicles.push({ - ...event, - productId, - isActive: false, - idleTime: 0, - activeTime: 0, - currentLoad: 0, - distanceTraveled: 0, - }); + const exists = state.vehicles.some(v => v.modelUuid === event.modelUuid); + if (!exists) { + state.vehicles.push({ + ...event, + productId, + isActive: false, + idleTime: 0, + activeTime: 0, + currentLoad: 0, + distanceTraveled: 0, + }); + } }); }, @@ -68,6 +72,12 @@ export const useVehicleStore = create()( }); }, + clearvehicles: () => { + set((state) => { + state.vehicles = []; + }); + }, + setVehicleActive: (modelUuid, isActive) => { set((state) => { const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid); diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index cb71864..c7ccd8b 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -88,6 +88,7 @@ interface StoragePointSchema { actionType: "store"; materials: { materialName: string; materialId: string; }[]; storageCapacity: number; + triggers: TriggerSchema[]; }; } @@ -143,6 +144,10 @@ interface MachineStatus extends MachineEventSchema { isActive: boolean; idleTime: number; activeTime: number; + currentAction?: { + actionUuid: string; + actionName: string; + }; } interface ArmBotStatus extends RoboticArmEventSchema {