From 7519aa90c622e37ad84f0189daa7e9326fe5438f Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Wed, 2 Jul 2025 15:07:31 +0530 Subject: [PATCH] feat: Implement human simulation features - Added human event handling in the simulation, including the ability to add, update, and remove human instances. - Introduced a new Human store to manage human states and actions. - Updated the simulation context to include human management. - Enhanced the Points and TriggerConnector components to support human interactions. - Refactored existing components to integrate human-related functionalities. - Added HumanInstance and HumanInstances components for rendering human entities in the simulation. - Updated TypeScript definitions to include human-related types and actions. --- .../properties/AssetProperties.tsx | 5 +- .../versionHisory/VersionHistory.tsx | 4 +- .../aisle/aisleCreator/aisleCreator.tsx | 141 ++++++++++- .../builder/asset/functions/addAssetModel.ts | 34 +++ .../builder/asset/models/model/model.tsx | 24 +- app/src/modules/builder/point/point.tsx | 38 ++- .../socket/socketResponses.dev.tsx | 2 +- app/src/modules/scene/sceneContext.tsx | 9 +- .../productionCapacityData.tsx | 10 +- .../analysis/simulationAnalysis.tsx | 11 +- .../events/points/creator/pointsCreator.tsx | 31 +++ .../simulation/events/points/points.tsx | 1 - .../triggerConnections/triggerConnector.tsx | 16 ++ app/src/modules/simulation/human/human.tsx | 13 + .../human/instances/humanInstances.tsx | 20 ++ .../instances/instance/humanInstance.tsx | 15 ++ .../modules/simulation/products/products.tsx | 17 +- app/src/modules/simulation/simulation.tsx | 5 +- .../simulation/simulator/simulator.tsx | 1 - .../vehicle/instances/vehicleInstances.tsx | 26 +- app/src/store/simulation/useHumanStore.ts | 239 ++++++++++++++++++ app/src/types/simulationTypes.d.ts | 188 ++++++++------ 22 files changed, 706 insertions(+), 144 deletions(-) create mode 100644 app/src/modules/simulation/human/human.tsx create mode 100644 app/src/modules/simulation/human/instances/humanInstances.tsx create mode 100644 app/src/modules/simulation/human/instances/instance/humanInstance.tsx create mode 100644 app/src/store/simulation/useHumanStore.ts diff --git a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx index 1ff41e7..818bd8a 100644 --- a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx @@ -51,9 +51,8 @@ const AssetProperties: React.FC = () => { }; const handleAnimationClick = (animation: string) => { - if (selectedFloorItem && selectedFloorItem.animationState) { - const isPlaying = selectedFloorItem.animationState?.playing || false; - setCurrentAnimation(selectedFloorItem.uuid, animation, !isPlaying); + if (selectedFloorItem) { + setCurrentAnimation(selectedFloorItem.uuid, animation, true); } } diff --git a/app/src/components/layout/sidebarRight/versionHisory/VersionHistory.tsx b/app/src/components/layout/sidebarRight/versionHisory/VersionHistory.tsx index 9d24ba0..a0255f4 100644 --- a/app/src/components/layout/sidebarRight/versionHisory/VersionHistory.tsx +++ b/app/src/components/layout/sidebarRight/versionHisory/VersionHistory.tsx @@ -28,9 +28,9 @@ const VersionHistory = () => { const handleSelectVersion = (version: Version) => { if (!projectId) return; - getVersionDataApi(projectId, version.versionId).then((verdionData) => { + getVersionDataApi(projectId, version.versionId).then((versionData) => { setSelectedVersion(version); - // console.log(verdionData); + // console.log(versionData); }).catch((err) => { // console.log(err); }) diff --git a/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx b/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx index 31ed1f6..af08226 100644 --- a/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx +++ b/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx @@ -3,12 +3,14 @@ import { useEffect, useMemo, useRef, useState } from 'react' import { useThree } from '@react-three/fiber'; import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store'; import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; -import { upsertAisleApi } from '../../../../services/factoryBuilder/aisle/upsertAisleApi'; import { useParams } from 'react-router-dom'; import { useVersionContext } from '../../version/versionContext'; import { useSceneContext } from '../../../scene/sceneContext'; import ReferenceAisle from './referenceAisle'; import ReferencePoint from '../../point/reference/referencePoint'; +import { getUserData } from '../../../../functions/getUserData'; + +// import { upsertAisleApi } from '../../../../services/factoryBuilder/aisle/upsertAisleApi'; function AisleCreator() { const { scene, camera, raycaster, gl, pointer } = useThree(); @@ -23,6 +25,7 @@ function AisleCreator() { const isLeftMouseDown = useRef(false); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); + const { userId, organization } = getUserData(); const { projectId } = useParams(); const [tempPoints, setTempPoints] = useState([]); @@ -106,7 +109,22 @@ function AisleCreator() { }; addAisle(aisle); if (projectId) { - upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + + // API + + // upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + + // SOCKET + + socket.emit('v1:model-aisle:add', { + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization, + aisleUuid: aisle.aisleUuid, + points: aisle.points, + type: aisle.type + }) } setTempPoints([newPoint]); } @@ -129,7 +147,22 @@ function AisleCreator() { }; addAisle(aisle); if (projectId) { - upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + + // API + + // upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + + // SOCKET + + socket.emit('v1:model-aisle:add', { + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization, + aisleUuid: aisle.aisleUuid, + points: aisle.points, + type: aisle.type + }) } setTempPoints([newPoint]); } @@ -151,7 +184,22 @@ function AisleCreator() { }; addAisle(aisle); if (projectId) { - upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + + // API + + // upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + + // SOCKET + + socket.emit('v1:model-aisle:add', { + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization, + aisleUuid: aisle.aisleUuid, + points: aisle.points, + type: aisle.type + }) } setTempPoints([newPoint]); } @@ -172,7 +220,22 @@ function AisleCreator() { }; addAisle(aisle); if (projectId) { - upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + + // API + + // upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + + // SOCKET + + socket.emit('v1:model-aisle:add', { + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization, + aisleUuid: aisle.aisleUuid, + points: aisle.points, + type: aisle.type + }) } setTempPoints([newPoint]); } @@ -195,7 +258,22 @@ function AisleCreator() { }; addAisle(aisle); if (projectId) { - upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + + // API + + // upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + + // SOCKET + + socket.emit('v1:model-aisle:add', { + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization, + aisleUuid: aisle.aisleUuid, + points: aisle.points, + type: aisle.type + }) } setTempPoints([newPoint]); } @@ -217,7 +295,22 @@ function AisleCreator() { }; addAisle(aisle); if (projectId) { - upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + + // API + + // upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + + // SOCKET + + socket.emit('v1:model-aisle:add', { + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization, + aisleUuid: aisle.aisleUuid, + points: aisle.points, + type: aisle.type + }) } setTempPoints([newPoint]); } @@ -238,7 +331,22 @@ function AisleCreator() { }; addAisle(aisle); if (projectId) { - upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + + // API + + // upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + + // SOCKET + + socket.emit('v1:model-aisle:add', { + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization, + aisleUuid: aisle.aisleUuid, + points: aisle.points, + type: aisle.type + }) } setTempPoints([newPoint]); } @@ -260,7 +368,22 @@ function AisleCreator() { }; addAisle(aisle); if (projectId) { - upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + + // API + + // upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + + // SOCKET + + socket.emit('v1:model-aisle:add', { + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization, + aisleUuid: aisle.aisleUuid, + points: aisle.points, + type: aisle.type + }) } setTempPoints([newPoint]); } diff --git a/app/src/modules/builder/asset/functions/addAssetModel.ts b/app/src/modules/builder/asset/functions/addAssetModel.ts index 7c85a49..978b45c 100644 --- a/app/src/modules/builder/asset/functions/addAssetModel.ts +++ b/app/src/modules/builder/asset/functions/addAssetModel.ts @@ -360,6 +360,40 @@ async function handleModelLoad( position: storageEvent.point.position, rotation: storageEvent.point.rotation, }; + } else if (selectedItem.type === "Human") { + const humanEvent: HumanEventSchema = { + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, + position: newFloorItem.position, + rotation: newFloorItem.rotation, + state: "idle", + type: "human", + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [data.points[0].x, data.points[0].y, data.points[0].z], + rotation: [0, 0, 0], + actions: [ + { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Action 1", + actionType: "animation", + animation: null, + loadCapacity: 1, + travelPoints: { + startPoint: null, + endPoint: null, + }, + triggers: [] + } + ] + } + } + addEvent(humanEvent); + eventData.point = { + uuid: humanEvent.point.uuid, + position: humanEvent.point.position, + rotation: humanEvent.point.rotation, + } } const completeData = { diff --git a/app/src/modules/builder/asset/models/model/model.tsx b/app/src/modules/builder/asset/models/model/model.tsx index 3c76664..b47fcd7 100644 --- a/app/src/modules/builder/asset/models/model/model.tsx +++ b/app/src/modules/builder/asset/models/model/model.tsx @@ -301,20 +301,21 @@ function Model({ asset }: { readonly asset: Asset }) { useEffect(() => { - const handlePlay = (clipName: string) => { + + if (asset.animationState && asset.animationState.playing) { if (!mixerRef.current) return; Object.values(actions.current).forEach((action) => action.stop()); - const action = actions.current[clipName]; + const action = actions.current[asset.animationState.current]; if (action && asset.animationState?.playing) { action.reset().setLoop(THREE.LoopOnce, 1).play(); } - }; + } else { + Object.values(actions.current).forEach((action) => action.stop()); + } - handlePlay(asset.animationState?.current || ''); - - }, [asset]) + }, [asset.animationState]) return ( ) )} - {/* - -
- {animationNames.map((name) => ( - - ))} -
- -
*/}
); } diff --git a/app/src/modules/builder/point/point.tsx b/app/src/modules/builder/point/point.tsx index 15777aa..73dec77 100644 --- a/app/src/modules/builder/point/point.tsx +++ b/app/src/modules/builder/point/point.tsx @@ -10,8 +10,8 @@ import { useParams } from 'react-router-dom'; import { useVersionContext } from '../version/versionContext'; import { useSceneContext } from '../../scene/sceneContext'; -import { upsertAisleApi } from '../../../services/factoryBuilder/aisle/upsertAisleApi'; -import { deleteAisleApi } from '../../../services/factoryBuilder/aisle/deleteAisleApi'; +// import { upsertAisleApi } from '../../../services/factoryBuilder/aisle/upsertAisleApi'; +// import { deleteAisleApi } from '../../../services/factoryBuilder/aisle/deleteAisleApi'; // import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi'; // import { deleteWallApi } from '../../../services/factoryBuilder/wall/deleteWallApi'; // import { upsertFloorApi } from '../../../services/factoryBuilder/floor/upsertFloorApi'; @@ -159,7 +159,22 @@ function Point({ point }: { readonly point: Point }) { const updatedAisles = getAislesByPointId(point.pointUuid); if (updatedAisles.length > 0 && projectId) { updatedAisles.forEach((updatedAisle) => { - upsertAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || '') + + // API + + // upsertAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || ''); + + // SOCKET + + socket.emit('v1:model-aisle:add', { + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization, + aisleUuid: updatedAisle.aisleUuid, + points: updatedAisle.points, + type: updatedAisle.type + }) }) } } else if (point.pointType === 'Wall') { @@ -238,7 +253,22 @@ function Point({ point }: { readonly point: Point }) { if (removedAisles.length > 0) { removedAisles.forEach(aisle => { if (projectId) { - deleteAisleApi(aisle.aisleUuid, projectId, selectedVersion?.versionId || '') + + // API + + // deleteAisleApi(aisle.aisleUuid, projectId, selectedVersion?.versionId || ''); + + // SOCKET + + const data = { + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization, + aisleUuid: aisle.aisleUuid + } + + socket.emit('v1:model-aisle:delete', data); } }); setHoveredPoint(null); diff --git a/app/src/modules/collaboration/socket/socketResponses.dev.tsx b/app/src/modules/collaboration/socket/socketResponses.dev.tsx index 5b8f772..f2ab238 100644 --- a/app/src/modules/collaboration/socket/socketResponses.dev.tsx +++ b/app/src/modules/collaboration/socket/socketResponses.dev.tsx @@ -6,7 +6,7 @@ export default function SocketResponses() { useEffect(() => { socket.on("v1:model-asset:response:add", (data: any) => { - console.log('data: ', data); + // console.log('data: ', data); }); return () => { diff --git a/app/src/modules/scene/sceneContext.tsx b/app/src/modules/scene/sceneContext.tsx index 4e48e51..67811c4 100644 --- a/app/src/modules/scene/sceneContext.tsx +++ b/app/src/modules/scene/sceneContext.tsx @@ -16,6 +16,7 @@ import { createMachineStore, MachineStoreType } from '../../store/simulation/use import { createConveyorStore, ConveyorStoreType } from '../../store/simulation/useConveyorStore'; import { createVehicleStore, VehicleStoreType } from '../../store/simulation/useVehicleStore'; import { createStorageUnitStore, StorageUnitStoreType } from '../../store/simulation/useStorageUnitStore'; +import { createHumanStore, HumanStoreType } from '../../store/simulation/useHumanStore'; type SceneContextValue = { @@ -35,6 +36,7 @@ type SceneContextValue = { conveyorStore: ConveyorStoreType; vehicleStore: VehicleStoreType; storageUnitStore: StorageUnitStoreType; + humanStore: HumanStoreType; clearStores: () => void; @@ -67,6 +69,7 @@ export function SceneProvider({ const conveyorStore = useMemo(() => createConveyorStore(), []); const vehicleStore = useMemo(() => createVehicleStore(), []); const storageUnitStore = useMemo(() => createStorageUnitStore(), []); + const humanStore = useMemo(() => createHumanStore(), []); const clearStores = useMemo(() => () => { assetStore.getState().clearAssets(); @@ -83,7 +86,8 @@ export function SceneProvider({ conveyorStore.getState().clearConveyors(); vehicleStore.getState().clearVehicles(); storageUnitStore.getState().clearStorageUnits(); - }, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore]); + humanStore.getState().clearHumans(); + }, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore]); const contextValue = useMemo(() => ( { @@ -101,10 +105,11 @@ export function SceneProvider({ conveyorStore, vehicleStore, storageUnitStore, + humanStore, clearStores, layout } - ), [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, clearStores, layout]); + ), [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, clearStores, layout]); return ( diff --git a/app/src/modules/simulation/analysis/productionCapacity/productionCapacityData.tsx b/app/src/modules/simulation/analysis/productionCapacity/productionCapacityData.tsx index 888a519..f7891e7 100644 --- a/app/src/modules/simulation/analysis/productionCapacity/productionCapacityData.tsx +++ b/app/src/modules/simulation/analysis/productionCapacity/productionCapacityData.tsx @@ -1,19 +1,15 @@ -import React, { useEffect } from 'react' +import { useEffect } from 'react' import { useInputValues, useProductionCapacityData, useThroughPutData } from '../../../../store/builder/store' import { usePlayButtonStore } from '../../../../store/usePlayButtonStore'; -import { useProductContext } from '../../products/productContext'; export default function ProductionCapacityData() { const { throughputData } = useThroughPutData() - const { productionCapacityData, setProductionCapacityData } = useProductionCapacityData() + const { setProductionCapacityData } = useProductionCapacityData() const { inputValues } = useInputValues(); const { isPlaying } = usePlayButtonStore(); - const { selectedProductStore } = useProductContext(); - const { selectedProduct } = selectedProductStore(); useEffect(() => { if (!isPlaying) { - setProductionCapacityData(0); } }, [isPlaying]); @@ -26,8 +22,6 @@ export default function ProductionCapacityData() { const Monthly_working_days = workingDaysPerYear / 12; const Production_capacity_per_month = throughputData * Monthly_working_days; - - setProductionCapacityData(Number(Production_capacity_per_month.toFixed(2))); } }, [throughputData, inputValues, isPlaying]); diff --git a/app/src/modules/simulation/analysis/simulationAnalysis.tsx b/app/src/modules/simulation/analysis/simulationAnalysis.tsx index db5a88b..2cfda53 100644 --- a/app/src/modules/simulation/analysis/simulationAnalysis.tsx +++ b/app/src/modules/simulation/analysis/simulationAnalysis.tsx @@ -1,18 +1,9 @@ -import React, { useEffect } from 'react' -import { usePlayButtonStore } from '../../../store/usePlayButtonStore' import ProductionCapacityData from './productionCapacity/productionCapacityData' import ThroughPutData from './throughPut/throughPutData' import ROIData from './ROI/roiData' function SimulationAnalysis() { - const { isPlaying } = usePlayButtonStore() - // useEffect(()=>{ - // if (isPlaying) { - // - // } else { - // - // } - // },[isPlaying]) + return ( <> diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx index 9f9cbb4..25e0b3d 100644 --- a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx +++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx @@ -352,6 +352,37 @@ function PointsCreator() { ); + } else if (usedEvent.type === "human") { + const point = usedEvent.point; + return ( + + (sphereRefs.current[point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + if (toolMode === 'cursor') { + setSelectedEventSphere( + sphereRefs.current[point.uuid] + ); + } + }} + position={new THREE.Vector3(...point.position)} + userData={{ + modelUuid: usedEvent.modelUuid, + pointUuid: point.uuid, + }} + > + + + + + ); } else { return null; } diff --git a/app/src/modules/simulation/events/points/points.tsx b/app/src/modules/simulation/events/points/points.tsx index 2a50f2d..c8b9d6d 100644 --- a/app/src/modules/simulation/events/points/points.tsx +++ b/app/src/modules/simulation/events/points/points.tsx @@ -1,4 +1,3 @@ -import React from 'react' import PointsCreator from './creator/pointsCreator' function Points() { diff --git a/app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx b/app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx index 0b8cb2d..918007a 100644 --- a/app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx +++ b/app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx @@ -152,6 +152,22 @@ function TriggerConnector() { }); } } + // Handle Human point + else if (event.type === "human" && 'point' in event) { + const point = event.point; + point.actions?.forEach(action => { + action.triggers?.forEach(trigger => { + if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) { + newConnections.push({ + id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, + startPointUuid: point.uuid, + endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + trigger + }); + } + }); + }); + } }); setConnections(newConnections); diff --git a/app/src/modules/simulation/human/human.tsx b/app/src/modules/simulation/human/human.tsx new file mode 100644 index 0000000..30608d4 --- /dev/null +++ b/app/src/modules/simulation/human/human.tsx @@ -0,0 +1,13 @@ +import HumanInstances from './instances/humanInstances' + +function Human() { + return ( + <> + + + + + ) +} + +export default Human \ No newline at end of file diff --git a/app/src/modules/simulation/human/instances/humanInstances.tsx b/app/src/modules/simulation/human/instances/humanInstances.tsx new file mode 100644 index 0000000..75849ad --- /dev/null +++ b/app/src/modules/simulation/human/instances/humanInstances.tsx @@ -0,0 +1,20 @@ +import React from 'react' +import HumanInstance from './instance/humanInstance'; +import { useSceneContext } from '../../../scene/sceneContext'; + +function HumanInstances() { + const { humanStore } = useSceneContext(); + const { humans } = humanStore(); + + return ( + <> + {humans.map((human: HumanStatus) => ( + + + + ))} + + ) +} + +export default HumanInstances \ No newline at end of file diff --git a/app/src/modules/simulation/human/instances/instance/humanInstance.tsx b/app/src/modules/simulation/human/instances/instance/humanInstance.tsx new file mode 100644 index 0000000..d8dc023 --- /dev/null +++ b/app/src/modules/simulation/human/instances/instance/humanInstance.tsx @@ -0,0 +1,15 @@ +import { useEffect } from 'react' + +function HumanInstance({ human }: { human: HumanStatus }) { + + useEffect(() => { + console.log('human: ', human); + }, [human]) + + return ( + <> + + ) +} + +export default HumanInstance \ No newline at end of file diff --git a/app/src/modules/simulation/products/products.tsx b/app/src/modules/simulation/products/products.tsx index e3ade5e..1478255 100644 --- a/app/src/modules/simulation/products/products.tsx +++ b/app/src/modules/simulation/products/products.tsx @@ -10,7 +10,7 @@ import { useParams } from 'react-router-dom'; import { useVersionContext } from '../../builder/version/versionContext'; function Products() { - const { armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, layout, productStore } = useSceneContext(); + const { armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, layout, productStore } = useSceneContext(); const { products, getProductById, addProduct, setProducts } = productStore(); const { selectedProductStore } = useProductContext(); const { setMainProduct } = useMainProduct(); @@ -20,6 +20,7 @@ function Products() { const { addMachine, clearMachines } = machineStore(); const { addConveyor, clearConveyors } = conveyorStore(); const { setCurrentMaterials, clearStorageUnits, updateCurrentLoad, addStorageUnit } = storageUnitStore(); + const { addHuman, clearHumans } = humanStore(); const { isReset } = useResetButtonStore(); const { isPlaying } = usePlayButtonStore(); const { mainProduct } = useMainProduct(); @@ -153,6 +154,20 @@ function Products() { } }, [selectedProduct, products, isReset, isPlaying]); + useEffect(() => { + if (selectedProduct.productUuid) { + const product = getProductById(selectedProduct.productUuid); + if (product) { + clearHumans(); + product.eventDatas.forEach(events => { + if (events.type === 'human') { + addHuman(selectedProduct.productUuid, events); + } + }); + } + } + }, [selectedProduct, products, isReset, isPlaying]); + return ( <> diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 3429393..bb9d91f 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import { useEffect } from 'react'; import Vehicles from './vehicle/vehicles'; import Points from './events/points/points'; import Conveyor from './conveyor/conveyor'; @@ -6,6 +6,7 @@ import RoboticArm from './roboticArm/roboticArm'; import Materials from './materials/materials'; import Machine from './machine/machine'; import StorageUnit from './storageUnit/storageUnit'; +import Human from './human/human'; import Simulator from './simulator/simulator'; import Products from './products/products'; import Trigger from './triggers/trigger'; @@ -52,6 +53,8 @@ function Simulation() { + + diff --git a/app/src/modules/simulation/simulator/simulator.tsx b/app/src/modules/simulation/simulator/simulator.tsx index 598d3d8..869ffa9 100644 --- a/app/src/modules/simulation/simulator/simulator.tsx +++ b/app/src/modules/simulation/simulator/simulator.tsx @@ -4,7 +4,6 @@ import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayB import { determineExecutionOrder } from './functions/determineExecutionOrder'; import { useProductContext } from '../products/productContext'; import { useSceneContext } from '../../scene/sceneContext'; -import { useCompareProductDataStore } from '../../../store/builder/store'; function Simulator() { const { selectedProductStore } = useProductContext(); diff --git a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx index 989242b..8f2b3f3 100644 --- a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx +++ b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx @@ -5,20 +5,20 @@ import { useSceneContext } from "../../../scene/sceneContext"; import { useViewSceneStore } from "../../../../store/builder/store"; function VehicleInstances() { - const { vehicleStore } = useSceneContext(); - const { vehicles } = vehicleStore(); - const { viewSceneLabels } = useViewSceneStore(); + const { vehicleStore } = useSceneContext(); + const { vehicles } = vehicleStore(); + const { viewSceneLabels } = useViewSceneStore(); - return ( - <> - {vehicles.map((vehicle: VehicleStatus) => ( - - - {viewSceneLabels && } - - ))} - - ); + return ( + <> + {vehicles.map((vehicle: VehicleStatus) => ( + + + {viewSceneLabels && } + + ))} + + ); } export default VehicleInstances; diff --git a/app/src/store/simulation/useHumanStore.ts b/app/src/store/simulation/useHumanStore.ts new file mode 100644 index 0000000..75b9c7d --- /dev/null +++ b/app/src/store/simulation/useHumanStore.ts @@ -0,0 +1,239 @@ +import { create } from "zustand"; +import { immer } from "zustand/middleware/immer"; + +interface HumansStore { + humans: HumanStatus[]; + + addHuman: (productUuid: string, event: HumanEventSchema) => void; + removeHuman: (modelUuid: string) => void; + updateHuman: ( + modelUuid: string, + updates: Partial> + ) => void; + clearHumans: () => void; + + setHumanActive: (modelUuid: string, isActive: boolean) => void; + setHumanPicking: (modelUuid: string, isPicking: boolean) => void; + setHumanLoad: (modelUuid: string, load: number) => void; + incrementHumanLoad: (modelUuid: string, incrementBy: number) => void; + decrementHumanLoad: (modelUuid: string, decrementBy: number) => void; + + addCurrentMaterial: (modelUuid: string, materialType: string, materialId: string) => void; + setCurrentMaterials: (modelUuid: string, materials: { materialType: string; materialId: string }[]) => void; + removeLastMaterial: (modelUuid: string) => { materialType: string; materialId: string } | undefined; + getLastMaterial: (modelUuid: string) => { materialType: string; materialId: string } | undefined; + clearCurrentMaterials: (modelUuid: string) => void; + + setCurrentAction: ( + modelUuid: string, + action: HumanStatus["currentAction"] + ) => void; + + incrementActiveTime: (modelUuid: string, incrementBy: number) => void; + incrementIdleTime: (modelUuid: string, incrementBy: number) => void; + incrementDistanceTraveled: (modelUuid: string, incrementBy: number) => void; + resetTime: (modelUuid: string) => void; + + getHumanById: (modelUuid: string) => HumanStatus | undefined; + getHumansByProduct: (productUuid: string) => HumanStatus[]; + getActiveHumans: () => HumanStatus[]; +} + +export const createHumanStore = () => { + return create()( + immer((set, get) => ({ + humans: [], + + addHuman: (productUuid, event) => { + set((state) => { + const exists = state.humans.some(h => h.modelUuid === event.modelUuid); + if (!exists) { + state.humans.push({ + ...event, + productUuid, + isActive: false, + isPicking: false, + idleTime: 0, + activeTime: 0, + currentLoad: 0, + currentMaterials: [], + distanceTraveled: 0 + }); + } + }); + }, + + removeHuman: (modelUuid) => { + set((state) => { + state.humans = state.humans.filter(h => h.modelUuid !== modelUuid); + }); + }, + + updateHuman: (modelUuid, updates) => { + set((state) => { + const human = state.humans.find(h => h.modelUuid === modelUuid); + if (human) { + Object.assign(human, updates); + } + }); + }, + + clearHumans: () => { + set((state) => { + state.humans = []; + }); + }, + + setHumanActive: (modelUuid, isActive) => { + set((state) => { + const human = state.humans.find(h => h.modelUuid === modelUuid); + if (human) { + human.isActive = isActive; + } + }); + }, + + setHumanPicking: (modelUuid, isPicking) => { + set((state) => { + const human = state.humans.find(h => h.modelUuid === modelUuid); + if (human) { + human.isPicking = isPicking; + } + }); + }, + + setHumanLoad: (modelUuid, load) => { + set((state) => { + const human = state.humans.find(h => h.modelUuid === modelUuid); + if (human) { + human.currentLoad = load; + } + }); + }, + + incrementHumanLoad: (modelUuid, incrementBy) => { + set((state) => { + const human = state.humans.find(h => h.modelUuid === modelUuid); + if (human) { + human.currentLoad += incrementBy; + } + }); + }, + + decrementHumanLoad: (modelUuid, decrementBy) => { + set((state) => { + const human = state.humans.find(h => h.modelUuid === modelUuid); + if (human) { + human.currentLoad -= decrementBy; + } + }); + }, + + addCurrentMaterial: (modelUuid, materialType, materialId) => { + set((state) => { + const human = state.humans.find(h => h.modelUuid === modelUuid); + if (human) { + human.currentMaterials.push({ materialType, materialId }); + } + }); + }, + + setCurrentMaterials: (modelUuid, materials) => { + set((state) => { + const human = state.humans.find(h => h.modelUuid === modelUuid); + if (human) { + human.currentMaterials = materials; + } + }); + }, + + removeLastMaterial: (modelUuid) => { + let removed; + set((state) => { + const human = state.humans.find(h => h.modelUuid === modelUuid); + if (human && human.currentMaterials.length > 0) { + removed = human.currentMaterials.pop(); + } + }); + return removed; + }, + + getLastMaterial: (modelUuid) => { + const human = get().humans.find(h => h.modelUuid === modelUuid); + if (human && human.currentMaterials.length > 0) { + return human.currentMaterials[human.currentMaterials.length - 1]; + } + return undefined; + }, + + clearCurrentMaterials: (modelUuid) => { + set((state) => { + const human = state.humans.find(h => h.modelUuid === modelUuid); + if (human) { + human.currentMaterials = []; + } + }); + }, + + setCurrentAction: (modelUuid, action) => { + set((state) => { + const human = state.humans.find(h => h.modelUuid === modelUuid); + if (human) { + human.currentAction = action; + } + }); + }, + + incrementActiveTime: (modelUuid, incrementBy) => { + set((state) => { + const human = state.humans.find(h => h.modelUuid === modelUuid); + if (human) { + human.activeTime += incrementBy; + } + }); + }, + + incrementIdleTime: (modelUuid, incrementBy) => { + set((state) => { + const human = state.humans.find(h => h.modelUuid === modelUuid); + if (human) { + human.idleTime += incrementBy; + } + }); + }, + + incrementDistanceTraveled: (modelUuid, incrementBy) => { + set((state) => { + const human = state.humans.find(h => h.modelUuid === modelUuid); + if (human) { + human.distanceTraveled += incrementBy; + } + }); + }, + + resetTime: (modelUuid) => { + set((state) => { + const human = state.humans.find(h => h.modelUuid === modelUuid); + if (human) { + human.activeTime = 0; + human.idleTime = 0; + } + }); + }, + + getHumanById: (modelUuid) => { + return get().humans.find(h => h.modelUuid === modelUuid); + }, + + getHumansByProduct: (productUuid) => { + return get().humans.filter(h => h.productUuid === productUuid); + }, + + getActiveHumans: () => { + return get().humans.filter(h => h.isActive); + } + })) + ); +}; + +export type HumanStoreType = ReturnType; diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index d5d2f00..1d2f364 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -1,3 +1,4 @@ +// Base Types interface AssetEventSchema { modelUuid: string; modelName: string; @@ -18,69 +19,7 @@ interface TriggerSchema { } | null; } -interface ConveyorPointSchema { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - action: ConveyorAction; -} - -interface VehiclePointSchema { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - action: VehicleAction; -} - -interface RoboticArmPointSchema { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - actions: RoboticArmAction[]; -} - -interface MachinePointSchema { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - action: MachineAction; -} - -interface StoragePointSchema { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - action: StorageAction; -} - -interface ConveyorEventSchema extends AssetEventSchema { - type: "transfer"; - speed: number; - points: ConveyorPointSchema[]; -} - -interface VehicleEventSchema extends AssetEventSchema { - type: "vehicle"; - speed: number; - point: VehiclePointSchema; -} - -interface RoboticArmEventSchema extends AssetEventSchema { - type: "roboticArm"; - speed: number; - point: RoboticArmPointSchema; -} - -interface MachineEventSchema extends AssetEventSchema { - type: "machine"; - point: MachinePointSchema; -} - -interface StorageEventSchema extends AssetEventSchema { - type: "storageUnit"; - point: StoragePointSchema; -} - +// Actions interface ConveyorAction { actionUuid: string; actionName: string; @@ -130,19 +69,100 @@ interface StorageAction { triggers: TriggerSchema[]; } -type Action = ConveyorAction | VehicleAction | RoboticArmAction | MachineAction | StorageAction; +interface HumanAction { + actionUuid: string; + actionName: string; + actionType: "animation" | "animatedTravel"; + animation: string | null; + loadCapacity: number; + travelPoints?: { startPoint: [number, number, number] | null; endPoint: [number, number, number] | null; } + triggers: TriggerSchema[]; +} -type PointsScheme = ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema; +type Action = ConveyorAction | VehicleAction | RoboticArmAction | MachineAction | StorageAction | HumanAction; -type EventsSchema = ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema; +// Points +interface ConveyorPointSchema { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + action: ConveyorAction; +} -type productsSchema = { - productName: string; - productUuid: string; - eventDatas: EventsSchema[]; -}[] +interface VehiclePointSchema { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + action: VehicleAction; +} +interface RoboticArmPointSchema { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + actions: RoboticArmAction[]; +} +interface MachinePointSchema { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + action: MachineAction; +} + +interface StoragePointSchema { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + action: StorageAction; +} + +interface HumanPointSchema { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + actions: HumanAction[]; +} + +type PointsScheme = | ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | HumanPointSchema; + +// Events +interface ConveyorEventSchema extends AssetEventSchema { + type: "transfer"; + speed: number; + points: ConveyorPointSchema[]; +} + +interface VehicleEventSchema extends AssetEventSchema { + type: "vehicle"; + speed: number; + point: VehiclePointSchema; +} + +interface RoboticArmEventSchema extends AssetEventSchema { + type: "roboticArm"; + speed: number; + point: RoboticArmPointSchema; +} + +interface MachineEventSchema extends AssetEventSchema { + type: "machine"; + point: MachinePointSchema; +} + +interface StorageEventSchema extends AssetEventSchema { + type: "storageUnit"; + point: StoragePointSchema; +} + +interface HumanEventSchema extends AssetEventSchema { + type: "human"; + point: HumanPointSchema; +} + +type EventsSchema = | ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema | HumanEventSchema; + +// Statuses interface ConveyorStatus extends ConveyorEventSchema { productUuid: string; isActive: boolean; @@ -197,6 +217,24 @@ interface StorageUnitStatus extends StorageEventSchema { currentMaterials: { materialType: string; materialId: string; }[]; } +interface HumanStatus extends HumanEventSchema { + productUuid: string; + isActive: boolean; + isPicking: boolean; + idleTime: number; + activeTime: number; + currentLoad: number; + currentMaterials: { materialType: string; materialId: string; }[]; + distanceTraveled: number; + currentAction?: { + actionUuid: string; + actionName: string; + materialType?: string | null; + materialId?: string | null; + }; +} + +// Materials interface MaterialSchema { materialId: string; materialName: string; @@ -230,6 +268,14 @@ interface MaterialSchema { type MaterialsSchema = MaterialSchema[]; +// Products +type productsSchema = { + productName: string; + productUuid: string; + eventDatas: EventsSchema[]; +}[]; + +// Material History interface MaterialHistoryEntry { material: MaterialSchema; removedAt: string;