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 a6a1fa9..4f96674 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx @@ -103,7 +103,7 @@ function ConveyorMechanics() { const handleSpawnCountChange = (value: string) => { if (!selectedEventData || !selectedPointData) return; const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { - spawnCount: value === "inherit" ? "inherit" : parseFloat(value), + spawnCount: parseFloat(value), }); if (event) { @@ -119,7 +119,7 @@ function ConveyorMechanics() { const handleSpawnIntervalChange = (value: string) => { if (!selectedEventData || !selectedPointData) return; const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { - spawnInterval: value === "inherit" ? "inherit" : parseFloat(value), + spawnInterval: parseFloat(value), }); if (event) { @@ -149,7 +149,7 @@ function ConveyorMechanics() { const handleDelayChange = (value: string) => { if (!selectedEventData || !selectedPointData) return; const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { - delay: value === "inherit" ? "inherit" : parseFloat(value), + delay: parseFloat(value), }); if (event) { @@ -271,7 +271,7 @@ function ConveyorMechanics() {
- +
diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/spawnActionHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/spawnActionHandler.ts new file mode 100644 index 0000000..c28f1d0 --- /dev/null +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/spawnActionHandler.ts @@ -0,0 +1,129 @@ +import { useCallback, useEffect, useRef } from "react"; +import * as THREE from 'three'; +import { useFrame } from "@react-three/fiber"; +import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore"; +import { useProductStore } from "../../../../../store/simulation/useProductStore"; +import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; + +export function useSpawnHandler() { + const { addMaterial } = useMaterialStore(); + const { getModelUuidByActionUuid } = useProductStore(); + const { selectedProduct } = useSelectedProduct(); + const lastSpawnTime = useRef(null); + const startTime = useRef(null); + const spawnCountRef = useRef(0); + const spawnParams = useRef<{ + material: string; + intervalMs: number; + totalCount: number; + point: ConveyorPointSchema; + } | null>(null); + + const clearCurrentSpawn = useCallback(() => { + lastSpawnTime.current = null; + startTime.current = null; + spawnCountRef.current = 0; + spawnParams.current = null; + }, []); + + const spawnLogStatus = (materialUuid: string, status: string) => { + // console.log(`${materialUuid}, ${status}`); + } + + const createNewMaterial = useCallback((materialType: string, point: ConveyorPointSchema) => { + const modelUuid = getModelUuidByActionUuid(selectedProduct.productId, point.action.actionUuid); + if (!modelUuid || !point.uuid) return; + + const newMaterial: MaterialSchema = { + materialId: THREE.MathUtils.generateUUID(), + materialName: `${materialType}-${Date.now()}`, + materialType: materialType, + isActive: false, + isVisible: true, + isRendered: true, + current: { + modelUuid: modelUuid, + pointUuid: point.uuid, + actionUuid: point.action?.actionUuid || '' + }, + weight: 1, + cost: 1 + }; + + addMaterial(newMaterial); + return newMaterial; + }, [addMaterial]); + + useFrame(() => { + if (!spawnParams.current || !startTime.current) return; + + const currentTime = performance.now(); + const { material, intervalMs, totalCount, point } = spawnParams.current; + const isFirstSpawn = lastSpawnTime.current === null; + const elapsed = currentTime - startTime.current; + + // First spawn + if (isFirstSpawn) { + if (elapsed >= intervalMs) { + const createdMaterial = createNewMaterial(material, point); + if (createdMaterial) { + spawnLogStatus(createdMaterial.materialId, `[${elapsed.toFixed(2)}ms] Spawned ${material} (1/${totalCount})`); + } + lastSpawnTime.current = currentTime; + spawnCountRef.current = 1; + + if (totalCount <= 1) { + clearCurrentSpawn(); + } + } + return; + } + + // Subsequent spawns + if (lastSpawnTime.current !== null) { + const timeSinceLast = currentTime - lastSpawnTime.current; + if (timeSinceLast >= intervalMs) { + const count = spawnCountRef.current + 1; + const createdMaterial = createNewMaterial(material, point); + if (createdMaterial) { + spawnLogStatus(createdMaterial.materialId, `[${elapsed.toFixed(2)}ms] Spawned ${material} (1/${totalCount})`); + } + lastSpawnTime.current = currentTime; + spawnCountRef.current = count; + + if (count >= totalCount) { + clearCurrentSpawn(); + } + } + } + }); + + const handleSpawn = useCallback((point: ConveyorPointSchema) => { + if (!point.action || point.action.actionType !== 'spawn') return; + + const { material, spawnInterval = 0, spawnCount = 1 } = point.action; + const intervalMs = spawnInterval * 1000; + + clearCurrentSpawn(); + + spawnParams.current = { + material, + intervalMs, + totalCount: spawnCount, + point: point + }; + + startTime.current = performance.now(); + }, [clearCurrentSpawn]); + + useEffect(() => { + return () => { + clearCurrentSpawn(); + }; + }, [clearCurrentSpawn]); + + return { + handleSpawn, + clearCurrentSpawn + }; +} \ No newline at end of file diff --git a/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts b/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts new file mode 100644 index 0000000..e10e115 --- /dev/null +++ b/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts @@ -0,0 +1,40 @@ +import { useEffect } from "react"; +import { useSpawnHandler } from "./actionHandler/spawnActionHandler"; + +// Conveyor Actions +export function useConveyorActions(point: ConveyorPointSchema) { + const { handleSpawn } = useSpawnHandler(); + + useEffect(() => { + if (!point.action) return; + + const { actionType, material, delay, spawnInterval, spawnCount } = point.action; + + const handleAction = () => { + switch (actionType) { + case 'default': + console.log(`Default conveyor action at point ${point.uuid}`); + break; + case 'spawn': + console.log(`Spawning material ${material} at point ${point.uuid}`); + handleSpawn(point); + break; + case 'swap': + console.log(`Swapping to material ${material} at point ${point.uuid}`); + break; + case 'delay': + console.log(`Delaying for ${delay}ms at point ${point.uuid}`); + break; + case 'despawn': + console.log(`Despawning material at point ${point.uuid}`); + break; + } + }; + + handleAction(); + + return () => { + // Cleanup if needed + }; + }, [point]); +} \ No newline at end of file diff --git a/app/src/modules/simulation/actions/machine/useMachineActions.ts b/app/src/modules/simulation/actions/machine/useMachineActions.ts new file mode 100644 index 0000000..0ac9865 --- /dev/null +++ b/app/src/modules/simulation/actions/machine/useMachineActions.ts @@ -0,0 +1,23 @@ +import { useEffect } from 'react'; + +// Machine Actions +export function useMachineActions(point: MachinePointSchema) { + useEffect(() => { + if (!point.action) return; + + const { actionType, processTime, swapMaterial } = point.action; + + const handleAction = () => { + if (actionType === 'process') { + console.log(`Machine processing for ${processTime}ms at point ${point.uuid}`); + if (swapMaterial) { + console.log(`Swapping material to ${swapMaterial}`); + } + } + }; + + return () => { + // Cleanup if needed + }; + }, [point]); +} \ No newline at end of file diff --git a/app/src/modules/simulation/actions/roboticArm/useRoboticArmActions.ts b/app/src/modules/simulation/actions/roboticArm/useRoboticArmActions.ts new file mode 100644 index 0000000..50a6196 --- /dev/null +++ b/app/src/modules/simulation/actions/roboticArm/useRoboticArmActions.ts @@ -0,0 +1,26 @@ +import { useEffect } from 'react'; + +// Robotic Arm Actions +export function useRoboticArmActions(point: RoboticArmPointSchema) { + useEffect(() => { + point.actions.forEach(action => { + const { actionType, process } = action; + + const handleAction = () => { + if (actionType === 'pickAndPlace') { + console.log(`Robotic arm pick and place at point ${point.uuid}`); + if (process.startPoint) { + console.log(`Start point: ${process.startPoint}`); + } + if (process.endPoint) { + console.log(`End point: ${process.endPoint}`); + } + } + }; + }); + + return () => { + // Cleanup if needed + }; + }, [point]); +} \ No newline at end of file diff --git a/app/src/modules/simulation/actions/storageUnit/useStorageUnitActions.ts b/app/src/modules/simulation/actions/storageUnit/useStorageUnitActions.ts new file mode 100644 index 0000000..6ffa8ad --- /dev/null +++ b/app/src/modules/simulation/actions/storageUnit/useStorageUnitActions.ts @@ -0,0 +1,22 @@ +import { useEffect } from 'react'; + +// Storage Actions +export function useStorageActions(point: StoragePointSchema) { + useEffect(() => { + if (!point.action) return; + + const { actionType, materials, storageCapacity } = point.action; + + const handleAction = () => { + if (actionType === 'store') { + console.log(`Storage action at point ${point.uuid}`); + console.log(`Materials: ${materials.map(m => m.materialName).join(', ')}`); + console.log(`Capacity: ${storageCapacity}`); + } + }; + + return () => { + // Cleanup if needed + }; + }, [point]); +} \ No newline at end of file diff --git a/app/src/modules/simulation/actions/temp.md b/app/src/modules/simulation/actions/temp.md deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/modules/simulation/actions/useActionHandler.ts b/app/src/modules/simulation/actions/useActionHandler.ts new file mode 100644 index 0000000..0975e83 --- /dev/null +++ b/app/src/modules/simulation/actions/useActionHandler.ts @@ -0,0 +1,25 @@ +import { useConveyorActions } from "./conveyor/useConveyorActions"; +import { useMachineActions } from "./machine/useMachineActions"; +import { useRoboticArmActions } from "./roboticArm/useRoboticArmActions"; +import { useStorageActions } from "./storageUnit/useStorageUnitActions"; +import { useVehicleActions } from "./vehicle/useVehicleActions"; + +// Master hook that selects the appropriate action handler +export function useActionHandler(point: PointsScheme) { + if ('actions' in point) { + // Robotic Arm + useRoboticArmActions(point); + } else if (point.action.actionType === 'travel') { + // Vehicle + useVehicleActions(point as VehiclePointSchema); + } else if (point.action.actionType === 'process') { + // Machine + useMachineActions(point as MachinePointSchema); + } else if (point.action.actionType === 'store') { + // Storage + useStorageActions(point as StoragePointSchema); + } else { + // Conveyor + useConveyorActions(point as ConveyorPointSchema); + } +} \ No newline at end of file diff --git a/app/src/modules/simulation/actions/vehicle/useVehicleActions.ts b/app/src/modules/simulation/actions/vehicle/useVehicleActions.ts new file mode 100644 index 0000000..7a6ce4d --- /dev/null +++ b/app/src/modules/simulation/actions/vehicle/useVehicleActions.ts @@ -0,0 +1,26 @@ +import { useEffect } from 'react'; + +// Vehicle Actions +export function useVehicleActions(point: VehiclePointSchema) { + useEffect(() => { + if (!point.action) return; + + const { actionType, unLoadDuration, loadCapacity, steeringAngle } = point.action; + + const handleAction = () => { + if (actionType === 'travel') { + console.log(`Vehicle travel action at point ${point.uuid}`); + if (point.action.pickUpPoint) { + console.log(`Pick up at: ${JSON.stringify(point.action.pickUpPoint)}`); + } + if (point.action.unLoadPoint) { + console.log(`Unload at: ${JSON.stringify(point.action.unLoadPoint)}`); + } + } + }; + + return () => { + // Cleanup if needed + }; + }, [point]); +} \ No newline at end of file diff --git a/app/src/modules/simulation/machine/instances/machineInstances.tsx b/app/src/modules/simulation/machine/instances/machineInstances.tsx index 8536cac..594265d 100644 --- a/app/src/modules/simulation/machine/instances/machineInstances.tsx +++ b/app/src/modules/simulation/machine/instances/machineInstances.tsx @@ -6,8 +6,8 @@ function MachineInstances() { const { machines } = useMachineStore(); return ( <> - {machines.map((val: MachineStatus) => ( - + {machines.map((machine: MachineStatus) => ( + ))} diff --git a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx index 466e235..01a5e43 100644 --- a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx +++ b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx @@ -1,9 +1,22 @@ -import React from 'react' +import React, { useEffect, useState } from 'react' +import * as THREE from 'three'; +import MaterialAnimator from '../animator/materialAnimator' -function MaterialInstance() { - return ( - <> - ) +function MaterialInstance({ material }: { material: MaterialSchema }) { + const [position, setPosition] = useState(); + const [rotation, setRotation] = useState(); + + useEffect(() => { + // console.log('material: ', material); + }, [material]) + + return ( + <> + + + + + ) } export default MaterialInstance \ No newline at end of file diff --git a/app/src/modules/simulation/materials/instances/materialInstances.tsx b/app/src/modules/simulation/materials/instances/materialInstances.tsx index 519cba9..1864f0f 100644 --- a/app/src/modules/simulation/materials/instances/materialInstances.tsx +++ b/app/src/modules/simulation/materials/instances/materialInstances.tsx @@ -1,14 +1,20 @@ -import React from 'react' +import React, { useEffect } from 'react' import MaterialInstance from './instance/materialInstance' -import MaterialAnimator from './animator/materialAnimator' +import { useMaterialStore } from '../../../../store/simulation/useMaterialStore'; function MaterialInstances() { + const { materials } = useMaterialStore(); + + useEffect(() => { + // console.log('materials: ', materials); + }, [materials]) + return ( <> - - - + {materials.map((material: MaterialSchema) => + + )} ) diff --git a/app/src/modules/simulation/products/temp.md b/app/src/modules/simulation/products/temp.md deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index efacd53..7fe3c50 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/simulator/simulator.tsx b/app/src/modules/simulation/simulator/simulator.tsx index c4f1c40..66a1dc3 100644 --- a/app/src/modules/simulation/simulator/simulator.tsx +++ b/app/src/modules/simulation/simulator/simulator.tsx @@ -1,18 +1,238 @@ -import { useEffect } from 'react' -import { useProductStore } from '../../../store/simulation/useProductStore' +import { useProductStore } from '../../../store/simulation/useProductStore'; +import { useActionHandler } from '../actions/useActionHandler'; function Simulator() { const { products } = useProductStore(); - useEffect(() => { - // console.log('products: ', products); - }, [products]) + const executionOrder = determineExecutionOrder(products); - return ( - <> + executionOrder.forEach(point => { + useActionHandler(point); + }); - - ) + function determineExecutionSequences(products: productsSchema): PointsScheme[][] { + // Create maps for all points + const pointMap = new Map(); + const allPoints: PointsScheme[] = []; + + // First pass: collect all points + products.forEach(product => { + product.eventDatas.forEach(event => { + if (event.type === 'transfer') { + event.points.forEach(point => { + pointMap.set(point.uuid, point); + allPoints.push(point); + }); + } else if (event.type === 'vehicle' || + event.type === 'machine' || + event.type === 'storageUnit' || + event.type === 'roboticArm') { + pointMap.set(event.point.uuid, event.point); + allPoints.push(event.point); + } + }); + }); + + // Build complete dependency graph + const dependencyGraph = new Map(); + const reverseDependencyGraph = new Map(); + const triggeredPoints = new Set(); + + allPoints.forEach(point => { + const triggers = extractTriggersFromPoint(point); + const dependencies: string[] = []; + + triggers.forEach(trigger => { + const targetUuid = trigger.triggeredAsset?.triggeredPoint?.pointUuid; + if (targetUuid && pointMap.has(targetUuid)) { + dependencies.push(targetUuid); + triggeredPoints.add(targetUuid); + + if (!reverseDependencyGraph.has(targetUuid)) { + reverseDependencyGraph.set(targetUuid, []); + } + reverseDependencyGraph.get(targetUuid)!.push(point.uuid); + } + }); + + dependencyGraph.set(point.uuid, dependencies); + }); + + // Identify independent root points (points that trigger others but aren't triggered themselves) + const rootPoints = allPoints.filter(point => { + const hasOutgoingTriggers = extractTriggersFromPoint(point).some( + t => t.triggeredAsset?.triggeredPoint?.pointUuid + ); + return hasOutgoingTriggers && !triggeredPoints.has(point.uuid); + }); + + // For each root point, build its complete trigger chain + const executionSequences: PointsScheme[][] = []; + + function buildSequence(startUuid: string): PointsScheme[] { + const sequence: PointsScheme[] = []; + const visited = new Set(); + + function traverse(uuid: string) { + if (visited.has(uuid)) return; + visited.add(uuid); + + const point = pointMap.get(uuid); + if (point) { + sequence.push(point); + } + + // Follow forward dependencies + const nextPoints = dependencyGraph.get(uuid) || []; + nextPoints.forEach(nextUuid => traverse(nextUuid)); + } + + traverse(startUuid); + return sequence; + } + + // Build sequences for all root points + rootPoints.forEach(root => { + executionSequences.push(buildSequence(root.uuid)); + }); + + // Handle any triggered points not reachable from roots (isolated chains) + const processedPoints = new Set( + executionSequences.flat().map(p => p.uuid) + ); + + allPoints.forEach(point => { + if (triggeredPoints.has(point.uuid) && !processedPoints.has(point.uuid)) { + executionSequences.push(buildSequence(point.uuid)); + } + }); + + return executionSequences; + } + + function determineExecutionOrder(products: productsSchema): PointsScheme[] { + // Create maps for all events and points + const eventMap = new Map(); + const pointMap = new Map(); + const allPoints: PointsScheme[] = []; + + // First pass: collect all points + products.forEach(product => { + product.eventDatas.forEach(event => { + eventMap.set(event.modelUuid, event); + + if (event.type === 'transfer') { + event.points.forEach(point => { + pointMap.set(point.uuid, point); + allPoints.push(point); + }); + } else if (event.type === 'vehicle' || + event.type === 'machine' || + event.type === 'storageUnit') { + pointMap.set(event.point.uuid, event.point); + allPoints.push(event.point); + } else if (event.type === 'roboticArm') { + pointMap.set(event.point.uuid, event.point); + allPoints.push(event.point); + } + }); + }); + + // Build dependency graphs + const graph = new Map(); + const reverseGraph = new Map(); + const allTriggeredPoints = new Set(); + + allPoints.forEach(point => { + const triggers = extractTriggersFromPoint(point); + const dependencies: string[] = []; + + triggers.forEach(trigger => { + const targetUuid = trigger.triggeredAsset?.triggeredPoint?.pointUuid; + if (targetUuid && pointMap.has(targetUuid)) { + dependencies.push(targetUuid); + allTriggeredPoints.add(targetUuid); + + if (!reverseGraph.has(targetUuid)) { + reverseGraph.set(targetUuid, []); + } + reverseGraph.get(targetUuid)!.push(point.uuid); + } + }); + + graph.set(point.uuid, dependencies); + }); + + // Identify root points (points that trigger others but aren't triggered themselves) + const rootPoints = allPoints + .filter(point => !allTriggeredPoints.has(point.uuid)) + .filter(point => { + // Only include roots that actually have triggers pointing FROM them + const triggers = extractTriggersFromPoint(point); + return triggers.some(t => t.triggeredAsset?.triggeredPoint?.pointUuid); + }); + + // If no root points found but we have triggered points, find the earliest triggers + if (rootPoints.length === 0 && allTriggeredPoints.size > 0) { + // This handles cases where we have circular dependencies + // but still want to include the triggered points + const minTriggerCount = Math.min( + ...Array.from(allTriggeredPoints) + .map(uuid => (graph.get(uuid) || []).length) + ); + const potentialRoots = Array.from(allTriggeredPoints) + .filter(uuid => (graph.get(uuid) || []).length === minTriggerCount); + + rootPoints.push(...potentialRoots.map(uuid => pointMap.get(uuid)!)); + } + + // Topological sort only for triggered points + const visited = new Set(); + const temp = new Set(); + const order: string[] = []; + let hasCycle = false; + + function visit(node: string) { + if (temp.has(node)) { + hasCycle = true; + return; + } + if (visited.has(node)) return; + + temp.add(node); + + const dependencies = reverseGraph.get(node) || []; + for (const dep of dependencies) { + visit(dep); + } + + temp.delete(node); + visited.add(node); + order.push(node); + } + + // Start processing from root points + rootPoints.forEach(root => visit(root.uuid)); + + // Convert UUIDs back to points and filter out untriggered points + const triggeredPoints = order + .map(uuid => pointMap.get(uuid)!) + .filter(point => allTriggeredPoints.has(point.uuid) || + rootPoints.some(root => root.uuid === point.uuid)); + + return triggeredPoints; + } + + function extractTriggersFromPoint(point: PointsScheme): TriggerSchema[] { + if ('actions' in point) { + return point.actions.flatMap(action => action.triggers); + } else if ('action' in point) { + return point.action.triggers; + } + return []; + } + + return <>; } -export default Simulator \ No newline at end of file +export default Simulator; \ No newline at end of file diff --git a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx index fcc840d..7b29e2f 100644 --- a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx +++ b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx @@ -9,8 +9,8 @@ function VehicleInstances() { return ( <> - {vehicles.map((val: VehicleStatus) => - + {vehicles.map((vehicle: VehicleStatus) => + )} diff --git a/app/src/store/simulation/useMaterialStore.ts b/app/src/store/simulation/useMaterialStore.ts index 56a35a7..ba75460 100644 --- a/app/src/store/simulation/useMaterialStore.ts +++ b/app/src/store/simulation/useMaterialStore.ts @@ -4,16 +4,39 @@ import { immer } from 'zustand/middleware/immer'; type MaterialsStore = { materials: MaterialsSchema; - addMaterial: (material: MaterialSchema) => void; - removeMaterial: (materialId: string) => void; - updateMaterial: (materialId: string, updates: Partial) => void; + addMaterial: (material: MaterialSchema) => MaterialSchema | undefined; + removeMaterial: (materialId: string) => MaterialSchema | undefined; + updateMaterial: (materialId: string, updates: Partial) => MaterialSchema | undefined; - setStartTime: (materialId: string, startTime: string) => void; - setEndTime: (materialId: string, endTime: string) => void; - setCost: (materialId: string, cost: number) => void; - setWeight: (materialId: string, weight: number) => void; + setCurrentLocation: ( + materialId: string, + location: { + modelUuid: string; + pointUuid: string; + actionUuid: string; + } + ) => MaterialSchema | undefined; + + setNextLocation: ( + materialId: string, + location?: { + modelUuid: string; + pointUuid: string; + actionUuid: string; + } | null + ) => MaterialSchema | undefined; + + setStartTime: (materialId: string, startTime: string) => MaterialSchema | undefined; + setEndTime: (materialId: string, endTime: string) => MaterialSchema | undefined; + setCost: (materialId: string, cost: number) => MaterialSchema | undefined; + setWeight: (materialId: string, weight: number) => MaterialSchema | undefined; + setIsActive: (materialId: string, isActive: boolean) => MaterialSchema | undefined; + setIsVisible: (materialId: string, isVisible: boolean) => MaterialSchema | undefined; + setIsRendered: (materialId: string, isRendered: boolean) => MaterialSchema | undefined; getMaterialById: (materialId: string) => MaterialSchema | undefined; + getMaterialsByPoint: (pointUuid: string) => MaterialSchema[]; + getMaterialsByModel: (modelUuid: string) => MaterialSchema[]; }; export const useMaterialStore = create()( @@ -21,56 +44,161 @@ export const useMaterialStore = create()( materials: [], addMaterial: (material) => { + let updatedMaterial: MaterialSchema | undefined; set((state) => { state.materials.push(material); }); + return updatedMaterial; }, removeMaterial: (materialId) => { + let updatedMaterial: MaterialSchema | undefined; set((state) => { - state.materials = state.materials.filter(m => m.materialId !== materialId); + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + state.materials.filter(m => m.materialId !== material.materialId); + updatedMaterial = JSON.parse(JSON.stringify(material)); + } }); + return updatedMaterial; }, updateMaterial: (materialId, updates) => { + let updatedMaterial: MaterialSchema | undefined; set((state) => { const material = state.materials.find(m => m.materialId === materialId); if (material) { Object.assign(material, updates); + updatedMaterial = JSON.parse(JSON.stringify(material)); } }); + return updatedMaterial; + }, + + setCurrentLocation: (materialId, location) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.current = location; + updatedMaterial = JSON.parse(JSON.stringify(material)); + } + }); + return updatedMaterial; + }, + + setNextLocation: (materialId, location) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.next = location || undefined; + updatedMaterial = JSON.parse(JSON.stringify(material)); + } + }); + return updatedMaterial; }, setStartTime: (materialId, startTime) => { + let updatedMaterial: MaterialSchema | undefined; set((state) => { const material = state.materials.find(m => m.materialId === materialId); - if (material) material.startTime = startTime; + if (material) { + material.startTime = startTime + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; }); + return updatedMaterial; }, setEndTime: (materialId, endTime) => { + let updatedMaterial: MaterialSchema | undefined; set((state) => { const material = state.materials.find(m => m.materialId === materialId); - if (material) material.endTime = endTime; + if (material) { + material.endTime = endTime; + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; }); + return updatedMaterial; }, setCost: (materialId, cost) => { + let updatedMaterial: MaterialSchema | undefined; set((state) => { const material = state.materials.find(m => m.materialId === materialId); - if (material) material.cost = cost; + if (material) { + material.cost = cost; + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; }); + return updatedMaterial; }, setWeight: (materialId, weight) => { + let updatedMaterial: MaterialSchema | undefined; set((state) => { const material = state.materials.find(m => m.materialId === materialId); - if (material) material.weight = weight; + if (material) { + material.weight = weight; + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; }); + return updatedMaterial; + }, + + setIsActive: (materialId, isActive) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.isActive = isActive; + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; + }); + return updatedMaterial; + }, + + setIsVisible: (materialId, isVisible) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.isVisible = isVisible; + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; + }); + return updatedMaterial; + }, + + setIsRendered: (materialId, isRendered) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.isRendered = isRendered; + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; + }); + return updatedMaterial; }, getMaterialById: (materialId) => { return get().materials.find(m => m.materialId === materialId); }, + + getMaterialsByPoint: (pointUuid) => { + return get().materials.filter(m => + m.current?.pointUuid === pointUuid || + m.next?.pointUuid === pointUuid + ); + }, + + getMaterialsByModel: (modelUuid) => { + return get().materials.filter(m => + m.current?.modelUuid === modelUuid || + m.next?.modelUuid === modelUuid + ); + }, })) -); +); \ No newline at end of file diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts index c0be958..c24ec63 100644 --- a/app/src/store/simulation/useProductStore.ts +++ b/app/src/store/simulation/useProductStore.ts @@ -63,6 +63,7 @@ type ProductsStore = { getEventByModelUuid: (productId: string, modelUuid: string) => EventsSchema | undefined; getPointByUuid: (productId: string, modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined; getActionByUuid: (productId: string, actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined; + getModelUuidByActionUuid: (productId: string, actionUuid: string) => (string) | undefined; getTriggerByUuid: (productId: string, triggerUuid: string) => TriggerSchema | undefined; getIsEventInProduct: (productId: string, modelUuid: string) => boolean; }; @@ -573,6 +574,30 @@ export const useProductStore = create()( return undefined; }, + getModelUuidByActionUuid: (productId, actionUuid) => { + const product = get().products.find(p => p.productId === productId); + if (!product) return undefined; + + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action?.actionUuid === actionUuid) { + return event.modelUuid; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action?.actionUuid === actionUuid) { + return event.modelUuid; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) return event.modelUuid; + } + } + } + return undefined; + }, + getTriggerByUuid: (productId, triggerUuid) => { const product = get().products.find(p => p.productId === productId); if (!product) return undefined; diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index 11d5156..a33056e 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -27,9 +27,9 @@ interface ConveyorPointSchema { actionName: string; actionType: "default" | "spawn" | "swap" | "delay" | "despawn"; material: string; - delay: number | "inherit"; - spawnInterval: number | "inherit"; - spawnCount: number | "inherit"; + delay: number; + spawnInterval: number; + spawnCount: number; triggers: TriggerSchema[]; }; } @@ -180,13 +180,27 @@ interface StorageUnitStatus extends StorageEventSchema { interface MaterialSchema { materialId: string; - materialName: string; + materialName: stri9ng; materialType: string; isActive: boolean; + isVisible: boolean; + isRendered: boolean; startTime?: string; endTime?: string; cost?: number; weight?: number; + + current: { + modelUuid: string; + pointUuid: string; + actionUuid: string; + }; + + next?: { + modelUuid: string; + pointUuid: string; + actionUuid: string; + }; } type MaterialsSchema = MaterialSchema[]; \ No newline at end of file