human to conveyor, conveyor to human multiple actions completed

This commit is contained in:
2025-07-17 09:44:08 +05:30
parent e9053ccd1b
commit fe09c3df56
47 changed files with 1155 additions and 1008 deletions

View File

@@ -256,14 +256,17 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
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: "Action 1",
actionType: "worker",
loadCapacity: 1,
triggers: []
}
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCount: 1,
loadCapacity: 1,
processTime: 10,
triggers: []
}
]
}
}
addEvent(humanEvent);

View File

@@ -373,14 +373,17 @@ async function handleModelLoad(
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCapacity: 1,
triggers: []
}
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCount: 1,
loadCapacity: 1,
processTime: 10,
triggers: []
}
]
}
}
addEvent(humanEvent);

View File

@@ -15,7 +15,6 @@ import {
useToolMode,
useRenderDistance,
useLimitDistance,
useLoadingProgress,
} from "../../store/builder/store";
////////// 3D Function Imports //////////
@@ -57,7 +56,6 @@ export default function Builder() {
const { projectId } = useParams();
const { setHoveredPoint, setHoveredLine } = useBuilderStore();
const { userId, organization } = getUserData();
const { loadingProgress } = useLoadingProgress();
useEffect(() => {
if (!toggleView) {
@@ -117,7 +115,8 @@ export default function Builder() {
<CalculateAreaGroup />
{loadingProgress == 0 && <NavMesh />}
<NavMesh />
<DxfFile />
<LayoutImage />

View File

@@ -1,9 +1,8 @@
import { useEffect, useRef } from 'react';
import { useActiveLayer, useDfxUpload, useSocketStore, useToggleView, useUpdateScene } from '../../../store/builder/store';
import { useActiveLayer, useDfxUpload, useSocketStore, useToggleView } from '../../../store/builder/store';
import { LineBasicMaterial, Line } from 'three';
import { TransformControls } from '@react-three/drei';
import { getWallPointsFromBlueprint } from './functions/getWallPointsFromBlueprint';
import * as Types from '../../../types/world/worldTypes';
import { useParams } from 'react-router-dom';
import { getUserData } from '../../../functions/getUserData';
import { useVersionContext } from '../version/versionContext';

View File

@@ -347,13 +347,17 @@ const CopyPasteControls3D = ({
uuid: THREE.MathUtils.generateUUID(),
position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]],
rotation: [updatedEventData.point.rotation[0], updatedEventData.point.rotation[1], updatedEventData.point.rotation[2]],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCapacity: 1,
triggers: []
}
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCapacity: 1,
loadCount: 1,
processTime: 10,
triggers: []
}
]
}
}
addEvent(humanEvent);

View File

@@ -321,13 +321,17 @@ const DuplicationControls3D = ({
uuid: THREE.MathUtils.generateUUID(),
position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]],
rotation: [updatedEventData.point.rotation[0], updatedEventData.point.rotation[1], updatedEventData.point.rotation[2]],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCapacity: 1,
triggers: []
}
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCapacity: 1,
loadCount: 1,
processTime: 10,
triggers: []
}
]
}
}
addEvent(humanEvent);

View File

@@ -6,7 +6,6 @@ import {
useElevation,
useShadows,
useSunPosition,
useWallItems,
useTileDistance,
} from "../../../store/builder/store";
import * as CONSTANTS from "../../../types/world/worldConstants";
@@ -25,13 +24,12 @@ export default function Shadows() {
const { controls, gl } = useThree();
const { elevation, setElevation } = useElevation();
const { azimuth, setAzimuth } = useAzimuth();
const { wallItems } = useWallItems();
const { planeValue } = useTileDistance();
useEffect(() => {
gl.shadowMap.enabled = true;
gl.shadowMap.type = THREE.PCFShadowMap;
}, [gl, wallItems]);
}, [gl]);
useEffect(() => {
if (lightRef.current && targetRef.current) {

View File

@@ -3,7 +3,7 @@ import { useSceneContext } from "../../../../scene/sceneContext";
export function useDespawnHandler() {
const { materialStore } = useSceneContext();
const { getMaterialById, removeMaterial, setEndTime } = materialStore();
const { getMaterialById, removeMaterial, setEndTime, clearLocations } = materialStore();
const deSpawnLogStatus = (materialUuid: string, status: string) => {
echo.info(`${materialUuid}, ${status}`);
@@ -17,6 +17,7 @@ export function useDespawnHandler() {
setEndTime(material.materialId, performance.now());
removeMaterial(material.materialId);
clearLocations(material.materialId);
deSpawnLogStatus(material.materialName, `Despawned`);

View File

@@ -8,7 +8,7 @@ export function useAssemblyHandler() {
const { getModelUuidByActionUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { incrementHumanLoad, addCurrentMaterial } = humanStore();
const { incrementHumanLoad, addCurrentMaterial, addCurrentAction } = humanStore();
const assemblyLogStatus = (materialUuid: string, status: string) => {
echo.info(`${materialUuid}, ${status}`);
@@ -24,6 +24,7 @@ export function useAssemblyHandler() {
if (!modelUuid) return;
incrementHumanLoad(modelUuid, 1);
addCurrentAction(modelUuid, action.actionUuid);
addCurrentMaterial(modelUuid, material.materialType, material.materialId);
assemblyLogStatus(material.materialName, `performing assembly action`);

View File

@@ -8,7 +8,7 @@ export function useWorkerHandler() {
const { getModelUuidByActionUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { incrementHumanLoad, addCurrentMaterial } = humanStore();
const { incrementHumanLoad, incrementLoadCount, addCurrentMaterial, addCurrentAction } = humanStore();
const workerLogStatus = (materialUuid: string, status: string) => {
echo.info(`${materialUuid}, ${status}`);
@@ -24,6 +24,8 @@ export function useWorkerHandler() {
if (!modelUuid) return;
incrementHumanLoad(modelUuid, 1);
incrementLoadCount(modelUuid, 1);
addCurrentAction(modelUuid, action.actionUuid);
addCurrentMaterial(modelUuid, material.materialType, material.materialId);
workerLogStatus(material.materialName, `performing worker action`);

View File

@@ -11,7 +11,7 @@ export function useRetrieveHandler() {
const { getModelUuidByActionUuid, getPointUuidByActionUuid, getEventByModelUuid, getActionByUuid } = productStore();
const { getStorageUnitById, getLastMaterial, updateCurrentLoad, removeLastMaterial } = storageUnitStore();
const { getVehicleById, incrementVehicleLoad, addCurrentMaterial } = vehicleStore();
const { getHumanById, incrementHumanLoad, addCurrentMaterial: addCurrentMaterialToHuman } = humanStore();
const { getHumanById, incrementHumanLoad, incrementLoadCount, addCurrentMaterial: addCurrentMaterialToHuman } = humanStore();
const { getAssetById, setCurrentAnimation } = assetStore();
const { selectedProduct } = selectedProductStore();
const { getArmBotById, addCurrentAction } = armBotStore();
@@ -298,14 +298,15 @@ export function useRetrieveHandler() {
} else if (triggeredModel.type === 'human') {
const human = getHumanById(triggeredModel.modelUuid);
const humanAsset = getAssetById(triggeredModel.modelUuid);
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
if (human && !human.isScheduled && human.state === 'idle' && human.currentLoad < human.point.action.loadCapacity) {
if (human && !human.isScheduled && human.state === 'idle' && human.currentLoad < (action as HumanAction).loadCapacity) {
if (humanAsset && humanAsset.animationState?.current === 'idle') {
setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
} else if (humanAsset && humanAsset.animationState?.current === 'pickup' && humanAsset.animationState.isCompleted) {
const lastMaterial = getLastMaterial(storageUnit.modelUuid);
if (lastMaterial) {
if (human.currentLoad < human.point.action.loadCapacity) {
if (action && human.currentLoad < (action as HumanAction).loadCapacity) {
const material = createNewMaterial(
lastMaterial.materialId,
lastMaterial.materialType,
@@ -315,10 +316,11 @@ export function useRetrieveHandler() {
removeLastMaterial(storageUnit.modelUuid);
updateCurrentLoad(storageUnit.modelUuid, -1);
incrementHumanLoad(human.modelUuid, 1);
incrementLoadCount(human.modelUuid, 1);
addCurrentMaterialToHuman(human.modelUuid, material.materialType, material.materialId);
retrieveLogStatus(material.materialName, `is picked by ${human.modelName}`);
}
if (human.currentLoad + 1 < human.point.action.loadCapacity) {
if (human.currentLoad + 1 < (action as HumanAction).loadCapacity) {
}
}
}

View File

@@ -6,65 +6,92 @@ import { useSceneContext } from '../../../scene/sceneContext';
type ConveyorCallback = {
conveyorId: string;
callback: () => void;
except: string[];
};
export function useConveyorEventManager() {
const { conveyorStore } = useSceneContext();
const { getConveyorById } = conveyorStore();
const callbacksRef = useRef<ConveyorCallback[]>([]);
const { materialStore } = useSceneContext();
const { getMaterialsByCurrentModelUuid } = materialStore();
const callbacksRef = useRef<Map<string, ConveyorCallback[]>>(new Map());
const isCooldownRef = useRef<Map<string, boolean>>(new Map());
const isMonitoringRef = useRef(false);
const { isPlaying } = usePlayButtonStore();
const { isPaused } = usePauseButtonStore();
const { isReset } = useResetButtonStore();
useEffect(() => {
if (isReset) {
callbacksRef.current = [];
callbacksRef.current.clear();
isCooldownRef.current.clear();
isMonitoringRef.current = false;
}
}, [isReset])
}, [isReset]);
// Add a new conveyor to monitor
const addConveyorToMonitor = (conveyorId: string, callback: () => void) => {
// Avoid duplicates
if (!callbacksRef.current.some((entry) => entry.conveyorId === conveyorId)) {
callbacksRef.current.push({ conveyorId, callback });
const addConveyorToMonitor = (conveyorId: string, callback: () => void, except?: string[]) => {
if (!callbacksRef.current.has(conveyorId)) {
callbacksRef.current.set(conveyorId, []);
}
// Start monitoring if not already running
if (!isMonitoringRef.current) {
isMonitoringRef.current = true;
}
callbacksRef.current.get(conveyorId)!.push({ conveyorId, callback, except: except || [] });
isMonitoringRef.current = true;
};
// Remove a conveyor from monitoring
const removeConveyorFromMonitor = (conveyorId: string) => {
callbacksRef.current = callbacksRef.current.filter(
(entry) => entry.conveyorId !== conveyorId
);
// Stop monitoring if no more conveyors to track
if (callbacksRef.current.length === 0) {
callbacksRef.current.delete(conveyorId);
isCooldownRef.current.delete(conveyorId);
if (callbacksRef.current.size === 0) {
isMonitoringRef.current = false;
}
};
// Check conveyor states every frame
useFrame(() => {
if (!isMonitoringRef.current || callbacksRef.current.length === 0 || !isPlaying || isPaused) return;
if (!isMonitoringRef.current || !isPlaying || isPaused) return;
callbacksRef.current.forEach(({ conveyorId, callback }) => {
const conveyor = getConveyorById(conveyorId);
if (conveyor?.isPaused === false) {
callback();
removeConveyorFromMonitor(conveyorId); // Remove after triggering
callbacksRef.current.forEach((queue, conveyorId) => {
if (!queue || queue.length === 0) return;
if (isCooldownRef.current.get(conveyorId)) return;
const { callback, except } = queue[0];
const conveyorMaterials = getMaterialsByCurrentModelUuid(conveyorId);
let conditionMet = false;
if (!conveyorMaterials || conveyorMaterials.length === 0) {
conditionMet = true;
} else {
const pausedMaterials = conveyorMaterials.filter(m => m.isPaused);
if (pausedMaterials.length === 0) {
conditionMet = true;
} else {
const allPausedInExcept = pausedMaterials.filter(m => !except.includes(m.materialId));
if (allPausedInExcept.length === 0) {
conditionMet = true;
}
}
}
if (conditionMet) {
queue.shift();
if (callback) callback();
if (queue.length === 0) {
removeConveyorFromMonitor(conveyorId);
} else {
isCooldownRef.current.set(conveyorId, true);
setTimeout(() => {
isCooldownRef.current.set(conveyorId, false);
}, 1000);
}
}
});
});
// Cleanup on unmount
useEffect(() => {
return () => {
callbacksRef.current = [];
callbacksRef.current.clear();
isCooldownRef.current.clear();
isMonitoringRef.current = false;
};
}, []);
@@ -73,4 +100,4 @@ export function useConveyorEventManager() {
addConveyorToMonitor,
removeConveyorFromMonitor,
};
}
}

View File

@@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import * as THREE from "three";
import { useSubModuleStore } from "../../../../store/useModuleStore";
import { useSelectedAction, useSelectedAsset } from "../../../../store/simulation/useSimulationStore";
import { useSelectedAction, useSelectedAsset, useSelectedEventData } from "../../../../store/simulation/useSimulationStore";
import { handleAddEventToProduct } from "../points/functions/handleAddEventToProduct";
import { QuadraticBezierLine } from "@react-three/drei";
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
@@ -38,6 +38,7 @@ function TriggerConnector() {
const { selectedAction } = useSelectedAction();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { setSelectedEventData } = useSelectedEventData();
const { projectId } = useParams();
const [firstSelectedPoint, setFirstSelectedPoint] = useState<{
@@ -62,6 +63,10 @@ function TriggerConnector() {
eventDatas: eventData,
versionId: selectedVersion?.versionId || '',
})
if (firstSelectedPoint?.pointUuid) {
setSelectedEventData(eventData, firstSelectedPoint.pointUuid)
}
}
useEffect(() => {
@@ -155,8 +160,8 @@ function TriggerConnector() {
// Handle Human point
else if (event.type === "human" && 'point' in event) {
const point = event.point;
if (point.action?.triggers) {
point.action.triggers.forEach(trigger => {
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}`,
@@ -166,7 +171,7 @@ function TriggerConnector() {
});
}
});
}
});
}
});

View File

@@ -2,14 +2,19 @@ import { useEffect, useRef } from 'react';
import { useFrame } from '@react-three/fiber';
import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../store/usePlayButtonStore';
import { useSceneContext } from '../../../scene/sceneContext';
import { useProductContext } from '../../products/productContext';
export function useHumanEventManager() {
const { humanStore } = useSceneContext();
const { getHumanById } = humanStore();
const { humanStore, productStore } = useSceneContext();
const { getHumanById, clearLoadCount } = humanStore();
const { getActionByUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const callbacksRef = useRef<Map<string, (() => void)[]>>(new Map());
const isMonitoringRef = useRef(false);
const actionQueueRef = useRef<Map<string, { actionType: "worker" | "assembly", actionUuid: string }[]>>(new Map());
const isCooldownRef = useRef<Map<string, boolean>>(new Map());
const isMonitoringRef = useRef(false);
const { isPlaying } = usePlayButtonStore();
const { isPaused } = usePauseButtonStore();
@@ -18,23 +23,34 @@ export function useHumanEventManager() {
useEffect(() => {
if (isReset) {
callbacksRef.current.clear();
actionQueueRef.current.clear();
isCooldownRef.current.clear();
isMonitoringRef.current = false;
}
}, [isReset]);
const addHumanToMonitor = (humanId: string, callback: () => void) => {
const addHumanToMonitor = (humanId: string, callback: () => void, actionUuid: string) => {
const action = getActionByUuid(selectedProduct.productUuid, actionUuid || '') as HumanAction | undefined;
if (!action) return;
const actionType = action.actionType;
if (actionType !== "worker" && actionType !== "assembly") return;
if (!callbacksRef.current.has(humanId)) {
callbacksRef.current.set(humanId, []);
actionQueueRef.current.set(humanId, []);
}
callbacksRef.current.get(humanId)!.push(callback);
if (!isMonitoringRef.current) {
isMonitoringRef.current = true;
}
callbacksRef.current.get(humanId)!.push(callback);
actionQueueRef.current.get(humanId)!.push({ actionType, actionUuid });
isMonitoringRef.current = true;
};
const removeHumanFromMonitor = (humanId: string) => {
callbacksRef.current.delete(humanId);
actionQueueRef.current.delete(humanId);
isCooldownRef.current.delete(humanId);
if (callbacksRef.current.size === 0) {
@@ -43,47 +59,80 @@ export function useHumanEventManager() {
};
useFrame(() => {
if (!isMonitoringRef.current || !isPlaying || isPaused) return;
callbacksRef.current.forEach((queue, humanId) => {
if (queue.length === 0 || isCooldownRef.current.get(humanId)) return;
const actionQueue = actionQueueRef.current.get(humanId);
if (!actionQueue || actionQueue.length === 0) return;
const { actionType: expectedActionType, actionUuid } = actionQueue[0];
const human = getHumanById(humanId);
const actionType = human?.point.action.actionType;
const action = getActionByUuid(selectedProduct.productUuid, actionUuid) as HumanAction | undefined;
if (!human || !action || action.actionType !== expectedActionType) return;
let conditionMet = false;
if (actionType === 'worker') {
conditionMet = Boolean(
human &&
human.isActive === false &&
human.state === 'idle' &&
human.isScheduled === false &&
human.currentLoad < human.point.action.loadCapacity
);
} else if (actionType === 'assembly') {
conditionMet = Boolean(
human &&
human.isActive === false &&
human.state === 'idle' &&
human.isScheduled === false
);
const currentAction = getActionByUuid(selectedProduct.productUuid, human.currentAction?.actionUuid || '') as HumanAction | undefined;
if (expectedActionType === "worker") {
if (currentAction && currentAction.actionType === 'worker') {
conditionMet = (
!human.isActive &&
human.state === "idle" &&
human.currentLoad < currentAction.loadCapacity
);
if (human.totalLoadCount >= currentAction.loadCount) {
queue.shift();
actionQueue.shift();
if (queue.length === 0) {
removeHumanFromMonitor(humanId);
}
return;
}
} else {
conditionMet = (
!human.isActive &&
human.state === "idle" &&
human.currentLoad < action.loadCapacity
);
}
} else if (expectedActionType === "assembly") {
if (currentAction && currentAction.actionType === 'worker') {
conditionMet = (
!human.isActive &&
human.state === "idle" &&
human.currentLoad < currentAction.loadCapacity
);
} else {
conditionMet = (
!human.isActive &&
human.state === "idle" &&
human.currentLoad < action.loadCapacity
)
}
if (conditionMet) {
clearLoadCount(human.modelUuid);
}
}
if (conditionMet) {
const callback = queue.shift();
if (callback) {
callback();
actionQueue.shift();
if (queue.length === 0) {
removeHumanFromMonitor(humanId);
} else {
isCooldownRef.current.set(humanId, true);
setTimeout(() => {
isCooldownRef.current.set(humanId, false);
}, 1000);
}
if (callback) callback();
if (queue.length === 0) {
removeHumanFromMonitor(humanId);
} else {
isCooldownRef.current.set(humanId, true);
setTimeout(() => {
isCooldownRef.current.set(humanId, false);
}, 1000);
}
}
});
@@ -92,8 +141,9 @@ export function useHumanEventManager() {
useEffect(() => {
return () => {
callbacksRef.current.clear();
isMonitoringRef.current = false;
actionQueueRef.current.clear();
isCooldownRef.current.clear();
isMonitoringRef.current = false;
};
}, []);

View File

@@ -4,6 +4,7 @@ import * as THREE from 'three';
import { Line } from '@react-three/drei';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
import { useSceneContext } from '../../../../scene/sceneContext';
import { useProductContext } from '../../../products/productContext';
interface HumanAnimatorProps {
path: [number, number, number][];
@@ -15,7 +16,10 @@ interface HumanAnimatorProps {
}
function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, startUnloadingProcess }: Readonly<HumanAnimatorProps>) {
const { humanStore, assetStore } = useSceneContext();
const { humanStore, assetStore, productStore } = useSceneContext();
const { getActionByUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { getHumanById } = humanStore();
const { setCurrentAnimation } = assetStore();
const { isPaused } = usePauseButtonStore();
@@ -25,26 +29,29 @@ function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, start
const progressRef = useRef<number>(0);
const movingForward = useRef<boolean>(true);
const completedRef = useRef<boolean>(false);
const [objectRotation, setObjectRotation] = useState<[number, number, number] | null>(human.point?.action?.pickUpPoint?.rotation || [0, 0, 0])
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const [objectRotation, setObjectRotation] = useState<[number, number, number] | null>((action as HumanAction)?.pickUpPoint?.rotation || [0, 0, 0])
const [restRotation, setRestingRotation] = useState<boolean>(true);
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
const { scene } = useThree();
useEffect(() => {
if (!human.currentAction?.actionUuid) return;
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
if (currentPhase === 'init-pickup' && path.length > 0) {
setCurrentPath(path);
setObjectRotation(human.point.action.pickUpPoint?.rotation ?? null)
setObjectRotation((action as HumanAction).pickUpPoint?.rotation ?? null)
} else if (currentPhase === 'init_assembly' && path.length > 0) {
setObjectRotation(human.point.action?.assemblyPoint?.rotation ?? null)
setObjectRotation((action as HumanAction)?.assemblyPoint?.rotation ?? null)
setCurrentPath(path);
} else if (currentPhase === 'pickup-drop' && path.length > 0) {
setObjectRotation(human.point.action?.dropPoint?.rotation ?? null)
setObjectRotation((action as HumanAction)?.dropPoint?.rotation ?? null)
setCurrentPath(path);
} else if (currentPhase === 'drop-pickup' && path.length > 0) {
setObjectRotation(human.point.action?.pickUpPoint?.rotation ?? null)
setObjectRotation((action as HumanAction)?.pickUpPoint?.rotation ?? null)
setCurrentPath(path);
}
}, [currentPhase, path, objectRotation]);
}, [currentPhase, path, objectRotation, selectedProduct, human.currentAction?.actionUuid]);
useEffect(() => {
completedRef.current = false;
@@ -77,7 +84,7 @@ function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, start
const object = scene.getObjectByProperty('uuid', human.modelUuid);
if (!object || currentPath.length < 2) return;
if (isPaused) return;
if (isPaused || !isPlaying) return;
let totalDistance = 0;
const distances = [];
@@ -128,13 +135,13 @@ function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, start
const t = (progressRef.current - accumulatedDistance) / segmentDistance;
const position = start.clone().lerp(end, t);
object.position.copy(position);
if (human.currentMaterials.length > 0) {
if (human.currentMaterials.length > 0 && action?.actionType === 'worker' && (currentPhase !== 'init-pickup' && currentPhase !== 'init_assembly')) {
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
} else {
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
}
} else {
if (human.currentMaterials.length > 0) {
if (human.currentMaterials.length > 0 && action?.actionType === 'worker' && (currentPhase !== 'init-pickup' && currentPhase !== 'init_assembly')) {
setCurrentAnimation(human.modelUuid, 'idle_with_box', true, true, true);
} else {
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);

View File

@@ -2,11 +2,18 @@ import { useEffect, useRef, useState } from 'react';
import { useThree } from '@react-three/fiber';
import * as THREE from 'three';
import { MaterialModel } from '../../../materials/instances/material/materialModel';
import { useSceneContext } from '../../../../scene/sceneContext';
import { useProductContext } from '../../../products/productContext';
const MaterialAnimator = ({ human }: { human: HumanStatus }) => {
const MaterialAnimator = ({ human, currentPhase }: { human: HumanStatus, currentPhase: string; }) => {
const { productStore } = useSceneContext();
const { getActionByUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const meshRef = useRef<any>(null!);
const [hasLoad, setHasLoad] = useState(false);
const [isAttached, setIsAttached] = useState(false);
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const { scene } = useThree();
useEffect(() => {
@@ -39,11 +46,11 @@ const MaterialAnimator = ({ human }: { human: HumanStatus }) => {
meshRef.current.visible = true;
setIsAttached(true);
}
}, [hasLoad, human.modelUuid, scene]);
}, [hasLoad, human.modelUuid, scene, currentPhase]);
return (
<>
{hasLoad && human.point.action.actionType === 'worker' && human.currentMaterials.length > 0 && (
{hasLoad && (action as HumanAction).actionType === 'worker' && human.currentMaterials.length > 0 && currentPhase !== 'init-pickup' && currentPhase !== 'init_assembly' && (
<MaterialModel
matRef={meshRef}
materialId={human.currentMaterials[0].materialId || ''}

View File

@@ -16,7 +16,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
const { isPlaying } = usePlayButtonStore();
const { scene } = useThree();
const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext();
const { removeMaterial, setEndTime, setMaterial } = materialStore();
const { removeMaterial, setEndTime, setMaterial, setIsVisible } = materialStore();
const { getStorageUnitById } = storageUnitStore();
const { getArmBotById } = armBotStore();
const { getConveyorById } = conveyorStore();
@@ -120,15 +120,21 @@ function HumanInstance({ human }: { human: HumanStatus }) {
useEffect(() => {
if (isPlaying) {
if (!human.point.action.assemblyPoint || human.point.action.actionType === 'worker') return;
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
if (!action || !(action as HumanAction).assemblyPoint || (action as HumanAction).actionType === 'worker') return;
if (!human.isActive && human.state === 'idle' && (currentPhase === 'init' || currentPhase === 'picking')) {
const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
if (!humanMesh) return;
if (!human.isActive && human.state === 'idle' && currentPhase === 'init') {
const toPickupPath = computePath(
new THREE.Vector3(human?.position[0], human?.position[1], human?.position[2]),
new THREE.Vector3(...humanMesh.position.toArray()),
new THREE.Vector3(
human?.point?.action?.assemblyPoint?.position?.[0] ?? 0,
human?.point?.action?.assemblyPoint?.position?.[1] ?? 0,
human?.point?.action?.assemblyPoint?.position?.[2] ?? 0
(action as HumanAction)?.assemblyPoint?.position?.[0] ?? 0,
(action as HumanAction)?.assemblyPoint?.position?.[1] ?? 0,
(action as HumanAction)?.assemblyPoint?.position?.[2] ?? 0
)
);
setPath(toPickupPath);
@@ -138,6 +144,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
humanStatus(human.modelUuid, 'Human is waiting for material in assembly');
} else if (!human.isActive && human.state === 'idle' && currentPhase === 'waiting') {
if (human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current !== 'working_standing') {
setCurrentAnimation(human.modelUuid, 'working_standing', true, true, false);
setHumanState(human.modelUuid, 'running');
@@ -145,7 +152,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
setHumanActive(human.modelUuid, true);
processStartTimeRef.current = performance.now();
processTimeRef.current = human.point.action.processTime || 0;
processTimeRef.current = (action as HumanAction).processTime || 0;
accumulatedPausedTimeRef.current = 0;
lastPauseTimeRef.current = null;
hasLoggedHalfway.current = false;
@@ -156,7 +163,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}
}
} else if (human.isActive && human.state === 'running' && human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current === 'working_standing' && humanAsset.animationState?.isCompleted) {
if (human.point.action.assemblyPoint && currentPhase === 'assembling') {
if ((action as HumanAction).assemblyPoint && currentPhase === 'assembling') {
setHumanState(human.modelUuid, 'idle');
setCurrentPhase('waiting');
setHumanActive(human.modelUuid, false);
@@ -167,7 +174,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
decrementHumanLoad(human.modelUuid, 1);
const material = removeLastMaterial(human.modelUuid);
if (material) {
triggerPointActions(human.point.action, material.materialId);
triggerPointActions((action as HumanAction), material.materialId);
}
}
}
@@ -179,9 +186,11 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}, [human, currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
const trackAssemblyProcess = useCallback(() => {
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const now = performance.now();
if (!processStartTimeRef.current || !human.point.action.processTime) {
if (!processStartTimeRef.current || !(action as HumanAction).processTime || !action) {
return;
}
@@ -197,12 +206,12 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}
const elapsed = (now - processStartTimeRef.current - accumulatedPausedTimeRef.current) * isSpeedRef.current;
const totalProcessTimeMs = human.point.action.processTime * 1000;
const totalProcessTimeMs = ((action as HumanAction).processTime || 1) * 1000;
if (elapsed >= totalProcessTimeMs / 2 && !hasLoggedHalfway.current) {
hasLoggedHalfway.current = true;
if (human.currentMaterials.length > 0) {
setMaterial(human.currentMaterials[0].materialId, human.point.action.swapMaterial || 'Default Material');
setMaterial(human.currentMaterials[0].materialId, (action as HumanAction).swapMaterial || 'Default Material');
}
humanStatus(human.modelUuid, `🟡 Human ${human.modelUuid} reached halfway in assembly.`);
}
@@ -219,19 +228,24 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
}, [human.modelUuid, human.point.action.processTime, human.currentMaterials]);
}, [human.modelUuid, human.currentMaterials]);
useEffect(() => {
if (isPlaying) {
if (!human.point.action.pickUpPoint || !human.point.action.dropPoint || human.point.action.actionType === 'assembly') return;
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
if (!action || action.actionType !== 'worker' || !action.pickUpPoint || !action.dropPoint) return;
if (!human.isActive && human.state === 'idle' && (currentPhase === 'init' || currentPhase === 'waiting')) {
const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
if (!humanMesh) return;
if (!human.isActive && human.state === 'idle' && currentPhase === 'init') {
const toPickupPath = computePath(
new THREE.Vector3(human?.position[0], human?.position[1], human?.position[2]),
new THREE.Vector3(...humanMesh.position.toArray()),
new THREE.Vector3(
human?.point?.action?.pickUpPoint?.position?.[0] ?? 0,
human?.point?.action?.pickUpPoint?.position?.[1] ?? 0,
human?.point?.action?.pickUpPoint?.position?.[2] ?? 0
action?.pickUpPoint?.position?.[0] ?? 0,
action?.pickUpPoint?.position?.[1] ?? 0,
action?.pickUpPoint?.position?.[2] ?? 0
)
);
setPath(toPickupPath);
@@ -242,18 +256,18 @@ function HumanInstance({ human }: { human: HumanStatus }) {
humanStatus(human.modelUuid, 'Started from init, heading to pickup');
return;
} else if (!human.isActive && human.state === 'idle' && currentPhase === 'picking') {
if (humanAsset && human.currentLoad === human.point.action.loadCapacity && human.currentMaterials.length > 0 && humanAsset.animationState?.current === 'pickup' && humanAsset.animationState?.isCompleted) {
if (human.point.action.pickUpPoint && human.point.action.dropPoint) {
if (humanAsset && human.currentLoad === action.loadCapacity && human.currentMaterials.length > 0 && humanAsset.animationState?.current === 'pickup' && humanAsset.animationState?.isCompleted) {
if (action.pickUpPoint && action.dropPoint) {
const toDrop = computePath(
new THREE.Vector3(
human.point.action.pickUpPoint.position?.[0] ?? 0,
human.point.action.pickUpPoint.position?.[1] ?? 0,
human.point.action.pickUpPoint.position?.[2] ?? 0
action.pickUpPoint.position?.[0] ?? 0,
action.pickUpPoint.position?.[1] ?? 0,
action.pickUpPoint.position?.[2] ?? 0
),
new THREE.Vector3(
human.point.action.dropPoint.position?.[0] ?? 0,
human.point.action.dropPoint.position?.[1] ?? 0,
human.point.action.dropPoint.position?.[2] ?? 0
action.dropPoint.position?.[0] ?? 0,
action.dropPoint.position?.[1] ?? 0,
action.dropPoint.position?.[2] ?? 0
)
);
setPath(toDrop);
@@ -262,21 +276,24 @@ function HumanInstance({ human }: { human: HumanStatus }) {
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
humanStatus(human.modelUuid, 'Started from pickup point, heading to drop point');
}
} else if (human.currentLoad === human.point.action.loadCapacity && human.currentMaterials.length > 0 && humanAsset?.animationState?.current !== 'pickup') {
} else if (human.currentLoad === action.loadCapacity && human.currentMaterials.length > 0 && humanAsset?.animationState?.current !== 'pickup') {
if (human.currentMaterials[0]?.materialId) {
setIsVisible(human.currentMaterials[0]?.materialId, false);
}
setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
}
} else if (!human.isActive && human.state === 'idle' && currentPhase === 'dropping' && human.currentLoad === 0) {
if (human.point.action.pickUpPoint && human.point.action.dropPoint) {
if (action.pickUpPoint && action.dropPoint) {
const dropToPickup = computePath(
new THREE.Vector3(
human.point.action.dropPoint.position?.[0] ?? 0,
human.point.action.dropPoint.position?.[1] ?? 0,
human.point.action.dropPoint.position?.[2] ?? 0
action.dropPoint.position?.[0] ?? 0,
action.dropPoint.position?.[1] ?? 0,
action.dropPoint.position?.[2] ?? 0
),
new THREE.Vector3(
human.point.action.pickUpPoint.position?.[0] ?? 0,
human.point.action.pickUpPoint.position?.[1] ?? 0,
human.point.action.pickUpPoint.position?.[2] ?? 0
action.pickUpPoint.position?.[0] ?? 0,
action.pickUpPoint.position?.[1] ?? 0,
action.pickUpPoint.position?.[2] ?? 0
)
);
setPath(dropToPickup);
@@ -373,38 +390,35 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}, [human, isPlaying]);
function startUnloadingProcess() {
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const humanAsset = getAssetById(human.modelUuid);
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
}
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
if (human.point.action.triggers.length > 0) {
const trigger = getTriggerByUuid(selectedProduct.productUuid, human.point.action.triggers[0]?.triggerUuid);
if ((action as HumanAction).triggers.length > 0) {
const trigger = getTriggerByUuid(selectedProduct.productUuid, (action as HumanAction).triggers[0]?.triggerUuid);
const model = getEventByModelUuid(selectedProduct.productUuid, trigger?.triggeredAsset?.triggeredModel?.modelUuid || '');
if (trigger && model) {
if (model.type === 'transfer') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToConveyor(model);
}
} else if (model.type === 'machine') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToMachine(model);
}
} else if (model.type === 'roboticArm') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToArmBot(model);
}
} else if (model.type === 'storageUnit') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToStorageUnit(model);
}
} else if (model.type === 'vehicle') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToVehicle(model);
}
@@ -423,6 +437,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}
function handleMaterialDropToStorageUnit(model: StorageEventSchema) {
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const humanAsset = getAssetById(human.modelUuid);
if (model && humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
@@ -436,7 +451,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
human.currentLoad,
model.modelUuid,
model.point.action.storageCapacity,
human.point.action
(action as HumanAction)
);
}
} else {
@@ -490,6 +505,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}
function handleMaterialDropToConveyor(model: ConveyorEventSchema) {
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const humanAsset = getAssetById(human.modelUuid);
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
@@ -503,7 +519,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
human.modelUuid,
human.currentLoad,
conveyor.modelUuid,
human.point.action
(action as HumanAction)
);
}
} else {
@@ -555,6 +571,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}
function handleMaterialDropToArmBot(model: RoboticArmEventSchema) {
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const humanAsset = getAssetById(human.modelUuid);
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
@@ -568,7 +585,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
human.modelUuid,
human.currentLoad,
model.modelUuid,
human.point.action
(action as HumanAction)
);
}
} else {
@@ -625,6 +642,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}
function handleMaterialDropToVehicle(model: VehicleEventSchema) {
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const humanAsset = getAssetById(human.modelUuid);
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
@@ -638,7 +656,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
human.modelUuid,
human.currentLoad,
model.modelUuid,
human.point.action
(action as HumanAction)
);
}
} else {
@@ -695,6 +713,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}
function handleMaterialDropToMachine(model: MachineEventSchema) {
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
const humanAsset = getAssetById(human.modelUuid);
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
@@ -708,7 +727,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
human.modelUuid,
human.currentLoad,
model.modelUuid,
human.point.action
(action as HumanAction)
);
}
} else {
@@ -804,7 +823,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
startUnloadingProcess={startUnloadingProcess}
/>
<MaterialAnimator human={human} />
<MaterialAnimator currentPhase={currentPhase} human={human} />
</>
)
}

View File

@@ -27,7 +27,7 @@ function HumanUi() {
const { humanStore, productStore } = useSceneContext();
const { selectedProduct } = selectedProductStore();
const { humans, getHumanById } = humanStore();
const { updateEvent } = productStore();
const { updateEvent, updateAction, getActionByUuid } = productStore();
const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 1, 0]);
const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 1, 0]);
const [assemblyPosition, setAssemblyPosition] = useState<[number, number, number]>([0, 1, 0]);
@@ -49,9 +49,8 @@ function HumanUi() {
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const selectedHuman = selectedEventSphere ? getHumanById(selectedEventSphere.userData.modelUuid) : null;
const actionType = selectedHuman?.point?.action?.actionType || null;
const isAssembly = actionType === 'assembly';
const currentAction = getActionByUuid(selectedProduct.productUuid, selectedAction.actionId || '');
const isAssembly = currentAction?.actionType === 'assembly';
const updateBackend = (
productName: string,
@@ -69,10 +68,10 @@ function HumanUi() {
};
useEffect(() => {
if (!selectedEventSphere) return;
if (!selectedEventSphere || !selectedAction?.actionId) return;
const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid);
if (!selectedHuman || !selectedHuman.point?.action) return;
if (!selectedHuman || !selectedHuman.point?.actions) return;
setSelectedHumanData({
position: selectedHuman.position,
@@ -87,7 +86,8 @@ function HumanUi() {
);
}
const action = selectedHuman.point.action;
const action = selectedHuman.point.actions.find(a => a.actionUuid === selectedAction.actionId);
if (!action) return;
if (isAssembly) {
if (action.assemblyPoint?.position && outerGroup.current) {
@@ -170,44 +170,46 @@ function HumanUi() {
if (!selectedEventSphere?.userData.modelUuid || !selectedAction?.actionId) return;
const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid);
if (!selectedHuman || !outerGroup.current) return;
if (!selectedHuman || !outerGroup.current || !currentAction) return;
let updatedAction;
const updatedActions = selectedHuman.point.actions.map(action => {
if (action.actionUuid !== currentAction.actionUuid) return action;
if (isAssembly) {
if (!assemblyMarker.current) return;
if (isAssembly) {
if (!assemblyMarker.current || !outerGroup.current) return action;
const worldPosAssembly = new Vector3(...assemblyPosition);
const globalAssemblyPosition = outerGroup.current.localToWorld(worldPosAssembly.clone());
const worldPosAssembly = new Vector3(...assemblyPosition);
const globalAssemblyPosition = outerGroup.current.localToWorld(worldPosAssembly.clone());
updatedAction = {
...selectedHuman.point.action,
assemblyPoint: {
position: [globalAssemblyPosition.x, globalAssemblyPosition.y, globalAssemblyPosition.z] as [number, number, number],
rotation: assemblyRotation
},
};
} else {
if (!startMarker.current || !endMarker.current) return;
return {
...action,
assemblyPoint: {
position: [globalAssemblyPosition.x, globalAssemblyPosition.y, globalAssemblyPosition.z] as [number, number, number],
rotation: assemblyRotation
},
};
} else {
if (!startMarker.current || !endMarker.current || !outerGroup.current) return action;
const worldPosStart = new Vector3(...startPosition);
const globalStartPosition = outerGroup.current.localToWorld(worldPosStart.clone());
const worldPosStart = new Vector3(...startPosition);
const globalStartPosition = outerGroup.current.localToWorld(worldPosStart.clone());
const worldPosEnd = new Vector3(...endPosition);
const globalEndPosition = outerGroup.current.localToWorld(worldPosEnd.clone());
const worldPosEnd = new Vector3(...endPosition);
const globalEndPosition = outerGroup.current.localToWorld(worldPosEnd.clone());
updatedAction = {
...selectedHuman.point.action,
pickUpPoint: {
position: [globalStartPosition.x, globalStartPosition.y, globalStartPosition.z] as [number, number, number],
rotation: startRotation,
},
dropPoint: {
position: [globalEndPosition.x, globalEndPosition.y, globalEndPosition.z] as [number, number, number],
rotation: endRotation,
},
};
}
return {
...action,
pickUpPoint: {
position: [globalStartPosition.x, globalStartPosition.y, globalStartPosition.z] as [number, number, number],
rotation: startRotation,
},
dropPoint: {
position: [globalEndPosition.x, globalEndPosition.y, globalEndPosition.z] as [number, number, number],
rotation: endRotation,
},
};
}
});
const event = updateEvent(
selectedProduct.productUuid,
@@ -216,7 +218,7 @@ function HumanUi() {
...selectedHuman,
point: {
...selectedHuman.point,
action: updatedAction,
actions: updatedActions,
},
}
);

View File

@@ -12,7 +12,8 @@ function MaterialInstance({ material }: { readonly material: MaterialSchema }) {
const matRef: any = useRef();
const { scene } = useThree();
const { selectedProductStore } = useProductContext();
const { productStore } = useSceneContext();
const { productStore, materialStore } = useSceneContext();
const { setIsPaused } = materialStore();
const { getModelUuidByPointUuid, getPointByUuid, getEventByModelUuid, getActionByPointUuid } = productStore();
const { selectedProduct } = selectedProductStore();
const { speed } = useAnimationPlaySpeed();
@@ -80,16 +81,23 @@ function MaterialInstance({ material }: { readonly material: MaterialSchema }) {
return 1;
}
if (event.type === 'human') {
return event.speed;
}
} else {
return 1;
}
}
const callTrigger = () => {
if (!material.next) return;
const action = getActionByPointUuid(selectedProduct.productUuid, material.next.pointUuid);
if (action) {
triggerPointActions(action, material.materialId);
if (material.next) {
const action = getActionByPointUuid(selectedProduct.productUuid, material.next.pointUuid);
if (action) {
triggerPointActions(action, material.materialId);
}
} else {
setIsPaused(material.materialId, true);
}
}
@@ -97,7 +105,7 @@ function MaterialInstance({ material }: { readonly material: MaterialSchema }) {
<>
{material.isRendered &&
<MaterialModel materialId={material.materialId} matRef={matRef} materialType={material.materialType} visible={material.isVisible} position={position} />
<MaterialModel materialId={material.materialId} matRef={matRef} materialType={material.materialType} visible={material.isVisible} position={position} rotation={[rotation.x, rotation.y, rotation.z]} />
}
<MaterialAnimator

View File

@@ -20,7 +20,7 @@ function Products() {
const { addMachine, clearMachines } = machineStore();
const { addConveyor, clearConveyors } = conveyorStore();
const { setCurrentMaterials, clearStorageUnits, updateCurrentLoad, addStorageUnit } = storageUnitStore();
const { addHuman, clearHumans } = humanStore();
const { addHuman, addCurrentAction, clearHumans } = humanStore();
const { isReset } = useResetButtonStore();
const { isPlaying } = usePlayButtonStore();
const { mainProduct } = useMainProduct();
@@ -162,6 +162,10 @@ function Products() {
product.eventDatas.forEach(events => {
if (events.type === 'human') {
addHuman(selectedProduct.productUuid, events);
if (events.point.actions.length > 0) {
addCurrentAction(events.modelUuid, events.point.actions[0].actionUuid);
}
}
});
}

View File

@@ -330,9 +330,6 @@ export function useTriggerHandler() {
if (vehicle) {
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
// Handle current action from vehicle
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
handleAction(action, materialId);
@@ -343,88 +340,51 @@ export function useTriggerHandler() {
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addVehicleToMonitor(vehicle.modelUuid,
() => {
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
handleAction(action, materialId);
}
)
addVehicleToMonitor(vehicle.modelUuid, () => {
handleAction(action, materialId);
})
}
}
} else {
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addHumanToMonitor(human.modelUuid, () => {
const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (vehicle) {
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
// Handle current action from vehicle
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
handleAction(action, materialId);
} else {
// Handle current action using Event Manager
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addVehicleToMonitor(vehicle.modelUuid,
() => {
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
handleAction(action, materialId);
}
)
addVehicleToMonitor(vehicle.modelUuid, () => {
handleAction(action, materialId);
})
}
}
})
}, action.actionUuid)
}
}
} else if (model?.type === 'transfer') {
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
if (human) {
if (human.isActive === false && human.state === 'idle') {
const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addHumanToMonitor(human.modelUuid, () => {
const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (conveyor) {
// Handle current action using Event Manager
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addConveyorToMonitor(conveyor.modelUuid,
() => {
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
handleAction(action, materialId);
}
)
addConveyorToMonitor(conveyor.modelUuid, () => {
handleAction(action, materialId);
}, [materialId])
}
} else {
setIsPaused(materialId, true);
addHumanToMonitor(human.modelUuid, () => {
const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (conveyor) {
// Handle current action using Event Manager
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addConveyorToMonitor(conveyor.modelUuid,
() => {
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
handleAction(action, materialId);
}
)
}
})
}
}, action.actionUuid)
}
} else if (model?.type === 'machine') {
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
@@ -434,9 +394,6 @@ export function useTriggerHandler() {
if (machine) {
if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) {
setIsPaused(materialId, true);
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
setHumanScheduled(human.modelUuid, true);
handleAction(action, materialId);
} else {
@@ -445,46 +402,31 @@ export function useTriggerHandler() {
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addMachineToMonitor(machine.modelUuid,
() => {
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
handleAction(action, materialId);
}
)
addMachineToMonitor(machine.modelUuid, () => {
handleAction(action, materialId);
})
}
}
} else {
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addHumanToMonitor(human.modelUuid, () => {
const machine = getMachineById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (machine) {
if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) {
setIsPaused(materialId, true);
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
setHumanScheduled(human.modelUuid, true);
handleAction(action, materialId);
} else {
// Handle current action using Event Manager
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addMachineToMonitor(machine.modelUuid,
() => {
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
handleAction(action, materialId);
}
)
addMachineToMonitor(machine.modelUuid, () => {
handleAction(action, materialId);
})
}
}
}
);
}, action.actionUuid);
}
}
} else {
@@ -492,23 +434,16 @@ export function useTriggerHandler() {
// Handle current action from arm bot
setIsPaused(materialId, true);
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
handleAction(action, materialId);
} else {
// Handle current action using Event Manager
setHumanScheduled(human.modelUuid, true);
setIsPaused(materialId, true);
addHumanToMonitor(human.modelUuid,
() => {
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
handleAction(action, materialId)
}
);
addHumanToMonitor(human.modelUuid, () => {
handleAction(action, materialId)
}, action.actionUuid);
}
}
@@ -517,9 +452,6 @@ export function useTriggerHandler() {
// Handle current action from arm bot
setIsPaused(materialId, true);
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
setHumanScheduled(human.modelUuid, true);
handleAction(action, materialId);
@@ -528,14 +460,9 @@ export function useTriggerHandler() {
// Handle current action using Event Manager
setIsPaused(materialId, true);
setHumanScheduled(human.modelUuid, true);
addHumanToMonitor(human.modelUuid,
() => {
if (action.actionType === 'worker') {
setIsVisible(materialId, false);
}
handleAction(action, materialId)
}
);
addHumanToMonitor(human.modelUuid, () => {
handleAction(action, materialId)
}, action.actionUuid);
}
}
@@ -698,27 +625,12 @@ export function useTriggerHandler() {
setNextLocation(material.materialId, null);
if (action && human) {
if (human.isActive === false && human.state === 'idle') {
// Handle current action from arm bot
setHumanScheduled(human.modelUuid, true);
addHumanToMonitor(human.modelUuid, () => {
setIsVisible(materialId, false);
setHumanScheduled(human.modelUuid, true);
handleAction(action, materialId);
} else {
setHumanScheduled(human.modelUuid, true);
addHumanToMonitor(human.modelUuid,
() => {
setIsVisible(materialId, false);
handleAction(action, materialId);
}
)
}
}, action.actionUuid)
}
}
}
@@ -964,8 +876,7 @@ export function useTriggerHandler() {
setHumanScheduled(human.modelUuid, true);
handleAction(action, materialId)
}
}
);
}, action.actionUuid);
}
}
}
@@ -1256,11 +1167,11 @@ export function useTriggerHandler() {
setNextLocation(material.materialId, null);
if (action) {
if (action && action.actionType === 'worker') {
if (human) {
if (human.isActive === false && human.state === 'idle' && !human.isScheduled && human.currentLoad < human.point.action.loadCapacity) {
if (human.isActive === false && human.state === 'idle' && !human.isScheduled && human.currentLoad < action.loadCapacity) {
setIsVisible(materialId, false);
@@ -1506,7 +1417,7 @@ export function useTriggerHandler() {
})
setIsPaused(material.materialId, false);
})
}, action.actionUuid)
} else {
setNextLocation(material.materialId, {
modelUuid: trigger.triggeredAsset?.triggeredModel.modelUuid || '',

View File

@@ -180,7 +180,6 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
setCurrentPath(updated);
};
return (
<>
{selectedPath === "auto" &&

View File

@@ -11,16 +11,20 @@ import InteractivePoints from '../animator/interactivePoint';
import MaterialAnimator from '../animator/materialAnimator';
import VehicleAnimator from '../animator/vehicleAnimator';
import { useHumanEventManager } from '../../../human/eventManager/useHumanEventManager';
function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) {
const { navMesh } = useNavMesh();
const { isPlaying } = usePlayButtonStore();
const { materialStore, armBotStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, productStore } = useSceneContext();
const { removeMaterial, setEndTime } = materialStore();
const { materialStore, armBotStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, productStore, assetStore } = useSceneContext();
const { removeMaterial, setEndTime, setIsVisible } = materialStore();
const { getStorageUnitById } = storageUnitStore();
const { getHumanById } = humanStore();
const { getHumanById, addCurrentAction } = humanStore();
const { getArmBotById } = armBotStore();
const { getConveyorById } = conveyorStore();
const { triggerPointActions } = useTriggerHandler();
const { setCurrentAnimation, getAssetById } = assetStore();
const { addHumanToMonitor } = useHumanEventManager();
const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
@@ -96,7 +100,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
}
useEffect(() => {
if (isPlaying || selectedPath === "auto") {
if (isPlaying && selectedPath === "auto") {
if (!agvDetail.point.action.unLoadPoint || !agvDetail.point.action.pickUpPoint) return;
if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'stationed') {
@@ -223,6 +227,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
if (agvDetail.point.action.triggers.length > 0) {
const trigger = getTriggerByUuid(selectedProduct.productUuid, agvDetail.point.action.triggers[0]?.triggerUuid);
const model = getEventByModelUuid(selectedProduct.productUuid, trigger?.triggeredAsset?.triggeredModel?.modelUuid || '');
const triggeredAction = getActionByUuid(selectedProduct.productUuid, trigger?.triggeredAsset?.triggeredAction?.actionUuid || '');
if (trigger && model) {
if (model.type === 'transfer') {
@@ -244,8 +249,8 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
}
} else if (model.type === 'human') {
const action = getActionByUuid(selectedProduct.productUuid, agvDetail.point.action.actionUuid);
if (action) {
handleMaterialDropToHuman(model);
if (action && (triggeredAction?.actionType === 'assembly' || triggeredAction?.actionType === 'worker')) {
handleMaterialDropToHuman(model, triggeredAction);
}
}
} else {
@@ -260,75 +265,56 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
}
}
function handleMaterialDropToHuman(model: HumanEventSchema) {
function handleMaterialDropToHuman(model: HumanEventSchema, action: HumanAction) {
if (model) {
if (model.point.action.actionType === 'worker') {
loopMaterialDropToHuman(
agvDetail.modelUuid,
agvDetail.currentLoad,
agvDetail.point.action.unLoadDuration,
model.modelUuid,
model.point.action.loadCapacity,
agvDetail.point.action
);
if (action.actionType === 'worker') {
addHumanToMonitor(model.modelUuid, () => {
loopMaterialDropToHuman(
agvDetail,
model.modelUuid,
agvDetail.point.action,
action.actionUuid
);
}, action.actionUuid || '')
}
}
}
function loopMaterialDropToHuman(
vehicleId: string,
vehicleCurrentLoad: number,
unLoadDuration: number,
vehicle: VehicleStatus,
humanId: string,
storageMaxCapacity: number,
action: VehicleAction
vehicleAction: VehicleAction,
humanActionId: string
) {
startTime = performance.now();
const fixedInterval = ((unLoadDuration / vehicleCurrentLoad) * (1000 / isSpeedRef.current));
let currentVehicleLoad = vehicle.currentLoad;
const unloadLoop = () => {
if (isPausedRef.current) {
pauseTimeRef.current ??= performance.now();
requestAnimationFrame(unloadLoop);
return;
}
if (pauseTimeRef.current) {
const pauseDuration = performance.now() - pauseTimeRef.current;
startTime += pauseDuration;
pauseTimeRef.current = null;
}
const elapsedTime = performance.now() - startTime;
const human = getHumanById(humanId);
const humanAsset = getAssetById(humanId);
const humanAction = human?.point.actions.find((action) => action.actionUuid === humanActionId);
if (elapsedTime >= fixedInterval) {
if (human && agvDetail &&
human.currentLoad < storageMaxCapacity &&
vehicleCurrentLoad > 0) {
if (!human || human.currentAction?.actionUuid !== humanAction?.actionUuid) return;
decrementVehicleLoad(vehicleId, 1);
vehicleCurrentLoad -= 1;
const material = removeLastMaterial(vehicleId);
if (material) {
triggerPointActions(action, material.materialId);
}
if (vehicleCurrentLoad > 0 && human.currentLoad < storageMaxCapacity) {
startTime = performance.now();
requestAnimationFrame(unloadLoop);
}
if (humanAsset && human.currentMaterials.length > 0 && humanAsset.animationState?.current === 'pickup' && humanAsset.animationState?.isCompleted) {
decrementVehicleLoad(vehicle.modelUuid, 1);
currentVehicleLoad -= 1;
const material = removeLastMaterial(vehicle.modelUuid);
if (material) {
setIsVisible(material.materialId, false);
}
} else {
requestAnimationFrame(unloadLoop);
} else if (!human.isActive && human.currentLoad < (humanAction?.loadCapacity || 0) && humanAsset?.animationState?.current === 'waiting') {
setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
}
setTimeout(() => {
requestAnimationFrame(unloadLoop);
}, 150)
};
const human = getHumanById(humanId);
if (human && vehicleCurrentLoad > 0 && human?.currentLoad < storageMaxCapacity) {
const humanAction = human?.point.actions.find((action) => action.actionUuid === humanActionId);
if (human && human.currentAction?.actionUuid !== humanActionId && human.currentLoad < (humanAction?.loadCapacity || 0)) {
addCurrentAction(humanId, humanActionId);
unloadLoop();
}
}

View File

@@ -1,25 +1,32 @@
import { useRef } from "react";
import { useNavMesh } from "../../../../store/builder/store";
import PolygonGenerator from "./polygonGenerator";
import NavMeshDetails from "./navMeshDetails";
import * as CONSTANTS from "../../../../types/world/worldConstants";
import * as Types from "../../../../types/world/worldTypes";
import { useLoadingProgress, useNavMesh, useTileDistance } from "../../../../store/builder/store";
import useModuleStore from "../../../../store/useModuleStore";
import PolygonGenerator from "./polygonGenerator";
import NavMeshDetails from "./navMeshDetails";
function NavMesh() {
let groupRef = useRef() as Types.RefGroup;
const { setNavMesh } = useNavMesh();
const { loadingProgress } = useLoadingProgress();
const { activeModule } = useModuleStore();
const { planeValue, gridValue } = useTileDistance();
return (
<>
<PolygonGenerator groupRef={groupRef} />
<NavMeshDetails setNavMesh={setNavMesh} groupRef={groupRef} />
{activeModule === 'simulation' && loadingProgress === 0 &&
<>
<PolygonGenerator groupRef={groupRef} />
<NavMeshDetails setNavMesh={setNavMesh} groupRef={groupRef} />
<group ref={groupRef} visible={false} name="Meshes">
<mesh rotation-x={CONSTANTS.planeConfig.rotation} position={CONSTANTS.planeConfig.position3D} receiveShadow>
<planeGeometry args={[300, 300]} />
<meshBasicMaterial color={CONSTANTS.planeConfig.color} />
</mesh>
</group>
<group ref={groupRef} visible={false} name="Meshes">
<mesh rotation-x={CONSTANTS.planeConfig.rotation} position={CONSTANTS.planeConfig.position3D} receiveShadow>
<planeGeometry args={[planeValue.width, planeValue.height]} />
</mesh>
</group>
</>
}
</>
)
}

View File

@@ -1,10 +1,9 @@
import React, { useEffect } from "react";
import React, { useEffect, useMemo } from "react";
import * as THREE from "three";
import { useThree } from "@react-three/fiber";
import { generateSoloNavMesh } from "@recast-navigation/generators";
import { init as initRecastNavigation } from "@recast-navigation/core";
import { DebugDrawer, getPositionsAndIndices } from "@recast-navigation/three";
import { useSceneContext } from "../../../scene/sceneContext";
import { useToggleView } from "../../../../store/builder/store";
interface NavMeshDetailsProps {
@@ -12,45 +11,48 @@ interface NavMeshDetailsProps {
groupRef: React.MutableRefObject<THREE.Group | null>;
}
const NAVMESH_CONFIG = {
// cellSize: 0.2,
// cellHeight: 0.7,
// walkableRadius: 0.5,
cellSize: 0.3,
cellHeight: 0.7,
walkableRadius: 0.7,
};
export default function NavMeshDetails({
setNavMesh,
groupRef,
}: NavMeshDetailsProps) {
const { aisleStore, wallStore } = useSceneContext();
const { aisles } = aisleStore();
const { scene } = useThree();
const { walls } = wallStore();
const { toggleView } = useToggleView();
const meshes = useMemo(() => {
return groupRef.current?.children.filter((child): child is THREE.Mesh =>
child instanceof THREE.Mesh
) || [];
}, [groupRef.current?.children]);
useEffect(() => {
if (toggleView) return;
if (toggleView || meshes.length === 0) return;
const initializeNavigation = async () => {
try {
await initRecastNavigation();
if (!groupRef.current || groupRef.current.children.length === 0) {
return;
}
const meshes = groupRef?.current?.children as THREE.Mesh[];
const [positions, indices] = getPositionsAndIndices(meshes);
// const cellSize = 0.2;
// const cellHeight = 0.7;
// const walkableRadius = 0.5;
const { cellSize, cellHeight, walkableRadius } = NAVMESH_CONFIG;
const cellSize = 0.3;
const cellHeight = 0.7;
const walkableRadius = 0.7;
const { success, navMesh } = generateSoloNavMesh(positions, indices, {
cs: cellSize,
ch: cellHeight,
walkableRadius: Math.round(walkableRadius / cellHeight),
});
if (!success || !navMesh) {
return;
}
if (!success || !navMesh) return;
setNavMesh(navMesh);
@@ -62,12 +64,12 @@ export default function NavMeshDetails({
debugDrawer.drawNavMesh(navMesh);
// scene.add(debugDrawer);
} catch (error) {
echo.error("Failed to initialize navigation")
console.error("Failed to initialize navigation:", error);
}
};
initializeNavigation();
}, [scene, groupRef, aisles, walls, toggleView]);
}, [meshes, setNavMesh, toggleView, scene]);
return null;
}

View File

@@ -76,42 +76,18 @@ export default function PolygonGenerator({
turf.lineString(line.map((p: any) => p?.position))
);
const validLineFeatures = lineFeatures.map(ls => {
const coords = ls.geometry.coordinates.map(coord => coord.join(','));
if (coords.length < 2) return null;
const start = coords[0];
const end = coords[coords.length - 1];
const middle = coords.slice(1, -1);
const seen = new Set<string>([start, end]);
const filteredMiddle: string[] = [];
for (const point of middle) {
if (!seen.has(point)) {
seen.add(point);
filteredMiddle.push(point);
}
let validLineFeatures = lineFeatures.filter((line) => {
const coords = line.geometry.coordinates;
return coords.length >= 2;
}).filter((line) => {
if (line.geometry.coordinates[0].toString() !== line.geometry.coordinates[1].toString()) {
return true;
}
})
const newCoords = [start, ...filteredMiddle, end];
if (validLineFeatures.length < 3) { validLineFeatures = [] }
if (newCoords.length >= 4) {
const resultCoords = newCoords.map(str => str.split(',').map(Number));
return {
...ls,
geometry: {
...ls.geometry,
coordinates: resultCoords,
},
};
}
return null;
}).filter(Boolean);
const polygons = turf.polygonize(turf.featureCollection(validLineFeatures as any) as any);
const polygons = turf.polygonize(turf.featureCollection(validLineFeatures));
renderWallGeometry(wallPoints);