new human event mangaer
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import InputRange from "../../../../../ui/inputs/InputRange";
|
||||
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
|
||||
import SwapAction from "./SwapAction";
|
||||
|
||||
interface AssemblyActionProps {
|
||||
@@ -10,6 +11,15 @@ interface AssemblyActionProps {
|
||||
disabled?: boolean,
|
||||
onChange: (value: number) => void;
|
||||
};
|
||||
assemblyCount: {
|
||||
value: number;
|
||||
min: number;
|
||||
max: number;
|
||||
step: number;
|
||||
defaultValue: string,
|
||||
disabled: false,
|
||||
onChange: (value: number) => void;
|
||||
}
|
||||
swapOptions: string[];
|
||||
swapDefaultOption: string;
|
||||
onSwapSelect: (value: string) => void;
|
||||
@@ -18,6 +28,7 @@ interface AssemblyActionProps {
|
||||
|
||||
const AssemblyAction: React.FC<AssemblyActionProps> = ({
|
||||
processTime,
|
||||
assemblyCount,
|
||||
swapOptions,
|
||||
swapDefaultOption,
|
||||
onSwapSelect,
|
||||
@@ -34,6 +45,21 @@ const AssemblyAction: React.FC<AssemblyActionProps> = ({
|
||||
onClick={() => { }}
|
||||
onChange={processTime.onChange}
|
||||
/>
|
||||
|
||||
{assemblyCount && (
|
||||
<InputWithDropDown
|
||||
label="Assembly Count"
|
||||
value={assemblyCount.value.toString()}
|
||||
min={assemblyCount.min}
|
||||
max={assemblyCount.max}
|
||||
disabled={assemblyCount.disabled}
|
||||
defaultValue={assemblyCount.defaultValue}
|
||||
step={assemblyCount.step}
|
||||
activeOption="unit"
|
||||
onClick={() => { }}
|
||||
onChange={(value) => assemblyCount.onChange(parseInt(value))}
|
||||
/>
|
||||
)}
|
||||
<SwapAction
|
||||
options={swapOptions}
|
||||
defaultOption={swapDefaultOption}
|
||||
|
||||
@@ -11,7 +11,7 @@ interface WorkerActionProps {
|
||||
disabled?: boolean;
|
||||
onChange: (value: string) => void;
|
||||
};
|
||||
loadCount?: {
|
||||
loadCount: {
|
||||
value: number;
|
||||
min: number;
|
||||
max: number;
|
||||
|
||||
@@ -20,6 +20,7 @@ function HumanMechanics() {
|
||||
const [activeOption, setActiveOption] = useState<"worker" | "assembly">("worker");
|
||||
const [speed, setSpeed] = useState("0.5");
|
||||
const [loadCount, setLoadCount] = useState(0);
|
||||
const [assemblyCount, setAssemblyCount] = useState(0);
|
||||
const [loadCapacity, setLoadCapacity] = useState("1");
|
||||
const [processTime, setProcessTime] = useState(10);
|
||||
const [swappedMaterial, setSwappedMaterial] = useState("Default material");
|
||||
@@ -56,6 +57,7 @@ function HumanMechanics() {
|
||||
setLoadCapacity(firstAction.loadCapacity.toString());
|
||||
setActiveOption(firstAction.actionType);
|
||||
setLoadCount(firstAction.loadCount || 0);
|
||||
setAssemblyCount(firstAction.assemblyCount || 0);
|
||||
setProcessTime(firstAction.processTime || 10);
|
||||
setSwappedMaterial(firstAction.swapMaterial || "Default material");
|
||||
}
|
||||
@@ -84,6 +86,7 @@ function HumanMechanics() {
|
||||
setActiveOption(newCurrentAction.actionType);
|
||||
setLoadCapacity(newCurrentAction.loadCapacity.toString());
|
||||
setLoadCount(newCurrentAction.loadCount || 0);
|
||||
setAssemblyCount(newCurrentAction.assemblyCount || 0);
|
||||
|
||||
if (newCurrentAction.actionType === 'assembly') {
|
||||
setProcessTime(newCurrentAction.processTime || 10);
|
||||
@@ -200,6 +203,28 @@ function HumanMechanics() {
|
||||
setLoadCount(value);
|
||||
};
|
||||
|
||||
const handleAssemblyCountChange = (value: number) => {
|
||||
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
|
||||
|
||||
const updatedAction = { ...currentAction, assemblyCount: value };
|
||||
const updatedActions = selectedPointData.actions.map(action => action.actionUuid === updatedAction.actionUuid ? updatedAction : action);
|
||||
const updatedPoint = { ...selectedPointData, actions: updatedActions };
|
||||
|
||||
const event = updateAction(
|
||||
selectedProduct.productUuid,
|
||||
selectedAction.actionId,
|
||||
updatedAction
|
||||
);
|
||||
|
||||
if (event) {
|
||||
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
|
||||
}
|
||||
|
||||
setCurrentAction(updatedAction);
|
||||
setSelectedPointData(updatedPoint);
|
||||
setAssemblyCount(value);
|
||||
};
|
||||
|
||||
const handleProcessTimeChange = (value: number) => {
|
||||
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
|
||||
|
||||
@@ -281,6 +306,7 @@ function HumanMechanics() {
|
||||
actionName: `Action ${selectedPointData.actions.length + 1}`,
|
||||
actionType: "worker",
|
||||
loadCount: 1,
|
||||
assemblyCount: 1,
|
||||
loadCapacity: 1,
|
||||
processTime: 10,
|
||||
triggers: [],
|
||||
@@ -389,7 +415,7 @@ function HumanMechanics() {
|
||||
}}
|
||||
loadCount={{
|
||||
value: loadCount,
|
||||
min: 0,
|
||||
min: 1,
|
||||
max: 20,
|
||||
step: 1,
|
||||
defaultValue: "1",
|
||||
@@ -407,6 +433,15 @@ function HumanMechanics() {
|
||||
max: 60,
|
||||
onChange: handleProcessTimeChange,
|
||||
}}
|
||||
assemblyCount={{
|
||||
value: assemblyCount,
|
||||
min: 1,
|
||||
max: 20,
|
||||
step: 1,
|
||||
defaultValue: "1",
|
||||
disabled: false,
|
||||
onChange: handleAssemblyCountChange,
|
||||
}}
|
||||
swapOptions={["Default material", "Material 1", "Material 2", "Material 3"]}
|
||||
swapDefaultOption={swappedMaterial}
|
||||
onSwapSelect={handleMaterialChange}
|
||||
|
||||
@@ -265,6 +265,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
|
||||
actionName: "Action 1",
|
||||
actionType: "worker",
|
||||
loadCount: 1,
|
||||
assemblyCount: 1,
|
||||
loadCapacity: 1,
|
||||
processTime: 10,
|
||||
triggers: []
|
||||
|
||||
@@ -384,6 +384,7 @@ async function handleModelLoad(
|
||||
actionName: "Action 1",
|
||||
actionType: "worker",
|
||||
loadCount: 1,
|
||||
assemblyCount: 1,
|
||||
loadCapacity: 1,
|
||||
processTime: 10,
|
||||
triggers: []
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import { useEffect, useRef, useCallback, useState } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { useFrame } from '@react-three/fiber';
|
||||
import { useSceneContext } from '../../../../../scene/sceneContext';
|
||||
import useModuleStore from '../../../../../../store/useModuleStore';
|
||||
import { usePauseButtonStore, useAnimationPlaySpeed } from '../../../../../../store/usePlayButtonStore';
|
||||
|
||||
interface ModelAnimatorProps {
|
||||
asset: Asset;
|
||||
gltfScene: THREE.Object3D;
|
||||
}
|
||||
|
||||
export function ModelAnimator({
|
||||
asset,
|
||||
gltfScene,
|
||||
}: ModelAnimatorProps) {
|
||||
const mixerRef = useRef<THREE.AnimationMixer>();
|
||||
const actions = useRef<{ [name: string]: THREE.AnimationAction }>({});
|
||||
const [previousAnimation, setPreviousAnimation] = useState<string | null>(null);
|
||||
|
||||
const blendFactor = useRef(0);
|
||||
const blendDuration = 0.5;
|
||||
|
||||
const { speed } = useAnimationPlaySpeed();
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const { assetStore } = useSceneContext();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { setAnimations, setAnimationComplete } = assetStore();
|
||||
|
||||
const handleAnimationComplete = useCallback(() => {
|
||||
if (asset.animationState) {
|
||||
setAnimationComplete(asset.modelUuid, true);
|
||||
}
|
||||
}, [asset.animationState]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!gltfScene || !gltfScene.animations || gltfScene.animations.length === 0) return;
|
||||
|
||||
mixerRef.current = new THREE.AnimationMixer(gltfScene);
|
||||
|
||||
gltfScene.animations.forEach((clip: THREE.AnimationClip) => {
|
||||
const action = mixerRef.current!.clipAction(clip);
|
||||
actions.current[clip.name] = action;
|
||||
});
|
||||
|
||||
const animationNames = gltfScene.animations.map(clip => clip.name);
|
||||
setAnimations(asset.modelUuid, animationNames);
|
||||
|
||||
return () => {
|
||||
mixerRef.current?.stopAllAction();
|
||||
mixerRef.current = undefined;
|
||||
};
|
||||
}, [gltfScene]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!asset.animationState || !mixerRef.current) return;
|
||||
|
||||
const { current, loopAnimation, isPlaying } = asset.animationState;
|
||||
const currentAction = actions.current[current];
|
||||
const previousAction = previousAnimation ? actions.current[previousAnimation] : null;
|
||||
|
||||
if (isPlaying && currentAction && !isPaused) {
|
||||
blendFactor.current = 0;
|
||||
|
||||
currentAction.reset();
|
||||
currentAction.setLoop(loopAnimation ? THREE.LoopRepeat : THREE.LoopOnce, loopAnimation ? Infinity : 1);
|
||||
currentAction.clampWhenFinished = true;
|
||||
|
||||
if (previousAction && previousAction !== currentAction) {
|
||||
previousAction.crossFadeTo(currentAction, blendDuration, false);
|
||||
}
|
||||
|
||||
currentAction.play();
|
||||
mixerRef.current.addEventListener('finished', handleAnimationComplete);
|
||||
setPreviousAnimation(current);
|
||||
} else {
|
||||
Object.values(actions.current).forEach((action) => action.stop());
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (mixerRef.current) {
|
||||
mixerRef.current.removeEventListener('finished', handleAnimationComplete);
|
||||
}
|
||||
};
|
||||
}, [asset.animationState?.current, asset.animationState?.isCompleted, asset.animationState?.isPlaying, isPaused, activeModule]);
|
||||
|
||||
useFrame((_, delta) => {
|
||||
if (mixerRef.current) {
|
||||
mixerRef.current.update(delta * (activeModule === 'simulation' ? speed : 1));
|
||||
}
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -404,6 +404,7 @@ const CopyPasteControls3D = ({
|
||||
actionName: "Action 1",
|
||||
actionType: "worker",
|
||||
loadCapacity: 1,
|
||||
assemblyCount: 1,
|
||||
loadCount: 1,
|
||||
processTime: 10,
|
||||
triggers: []
|
||||
|
||||
@@ -405,6 +405,7 @@ const DuplicationControls3D = ({
|
||||
actionName: "Action 1",
|
||||
actionType: "worker",
|
||||
loadCapacity: 1,
|
||||
assemblyCount: 1,
|
||||
loadCount: 1,
|
||||
processTime: 10,
|
||||
triggers: []
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createContext, useContext, useMemo } from 'react';
|
||||
import { createContext, useContext, useMemo, useRef } from 'react';
|
||||
|
||||
import { createAssetStore, AssetStoreType } from '../../store/builder/useAssetStore';
|
||||
import { createWallAssetStore, WallAssetStoreType } from '../../store/builder/useWallAssetStore';
|
||||
@@ -38,6 +38,8 @@ type SceneContextValue = {
|
||||
storageUnitStore: StorageUnitStoreType;
|
||||
humanStore: HumanStoreType;
|
||||
|
||||
humanEventManagerRef: React.RefObject<HumanEventManagerState>;
|
||||
|
||||
clearStores: () => void;
|
||||
|
||||
layout: 'Main Layout' | 'Comparison Layout';
|
||||
@@ -71,6 +73,8 @@ export function SceneProvider({
|
||||
const storageUnitStore = useMemo(() => createStorageUnitStore(), []);
|
||||
const humanStore = useMemo(() => createHumanStore(), []);
|
||||
|
||||
const humanEventManagerRef = useRef<HumanEventManagerState>({ humanStates: [] });
|
||||
|
||||
const clearStores = useMemo(() => () => {
|
||||
assetStore.getState().clearAssets();
|
||||
wallAssetStore.getState().clearWallAssets();
|
||||
@@ -87,6 +91,7 @@ export function SceneProvider({
|
||||
vehicleStore.getState().clearVehicles();
|
||||
storageUnitStore.getState().clearStorageUnits();
|
||||
humanStore.getState().clearHumans();
|
||||
humanEventManagerRef.current.humanStates = [];
|
||||
}, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore]);
|
||||
|
||||
const contextValue = useMemo(() => (
|
||||
@@ -106,6 +111,7 @@ export function SceneProvider({
|
||||
vehicleStore,
|
||||
storageUnitStore,
|
||||
humanStore,
|
||||
humanEventManagerRef,
|
||||
clearStores,
|
||||
layout
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useResetButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||
import { useFrame } from '@react-three/fiber'
|
||||
import { useSceneContext } from '../../../../scene/sceneContext';
|
||||
import { useProductContext } from '../../../products/productContext';
|
||||
// import { findConveyorSubsequence } from '../../../simulator/functions/getConveyorSequencesInProduct';
|
||||
|
||||
function ConveyorInstance({ conveyor }: { readonly conveyor: ConveyorStatus }) {
|
||||
|
||||
const { materialStore, conveyorStore, productStore } = useSceneContext();
|
||||
const { getProductById } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { getMaterialsByCurrentModelUuid, materials } = materialStore();
|
||||
const { isReset } = useResetButtonStore();
|
||||
const { getMaterialsByCurrentModelUuid } = materialStore();
|
||||
const { setConveyorPaused } = conveyorStore();
|
||||
|
||||
useEffect(() => {
|
||||
useFrame(() => {
|
||||
const product = getProductById(selectedProduct.productUuid);
|
||||
if (!product) return;
|
||||
|
||||
const conveyorMaterials = getMaterialsByCurrentModelUuid(conveyor.modelUuid);
|
||||
if (conveyorMaterials && conveyorMaterials?.length > 0) {
|
||||
if (conveyorMaterials && conveyorMaterials.length > 0) {
|
||||
|
||||
const hasPausedMaterials = conveyorMaterials.some(material => material.isPaused);
|
||||
|
||||
@@ -52,7 +52,7 @@ function ConveyorInstance({ conveyor }: { readonly conveyor: ConveyorStatus }) {
|
||||
// }
|
||||
// });
|
||||
|
||||
}, [materials, conveyor.modelUuid, getMaterialsByCurrentModelUuid, setConveyorPaused, isReset, selectedProduct.productUuid, getProductById]);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('conveyor: ', conveyor);
|
||||
@@ -61,7 +61,7 @@ function ConveyorInstance({ conveyor }: { readonly conveyor: ConveyorStatus }) {
|
||||
return (
|
||||
<>
|
||||
</>
|
||||
)
|
||||
};
|
||||
);
|
||||
}
|
||||
|
||||
export default ConveyorInstance
|
||||
@@ -1,195 +1,142 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useEffect } 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, productStore, assetStore } = useSceneContext();
|
||||
const { getHumanById, setCurrentPhase } = humanStore();
|
||||
const { humanStore, productStore, assetStore, humanEventManagerRef } = useSceneContext();
|
||||
const { getHumanById, setCurrentPhase, removeCurrentAction } = humanStore();
|
||||
const { getAssetById } = assetStore();
|
||||
const { getActionByUuid } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
|
||||
const stateRef = useRef({
|
||||
humanStates: new Map<string, {
|
||||
callbacks: (() => void)[],
|
||||
actionQueue: { actionType: "worker" | "assembly", actionUuid: string, actionName: string }[],
|
||||
isCooldown: boolean
|
||||
}>(),
|
||||
callbackCounts: new Map<string, Map<string, number>>(),
|
||||
isMonitoring: false
|
||||
});
|
||||
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const { isReset } = useResetButtonStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (isReset) {
|
||||
stateRef.current.humanStates.clear();
|
||||
stateRef.current.callbackCounts.clear();
|
||||
stateRef.current.isMonitoring = false;
|
||||
if ((isReset || !isPlaying) && humanEventManagerRef.current) {
|
||||
humanEventManagerRef.current.humanStates = [];
|
||||
}
|
||||
}, [isReset]);
|
||||
}, [isReset, isPlaying]);
|
||||
|
||||
const addHumanToMonitor = (humanId: string, callback: () => void, actionUuid: string) => {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, actionUuid || '') as HumanAction | undefined;
|
||||
const human = getHumanById(humanId);
|
||||
const action = getActionByUuid(selectedProduct.productUuid, actionUuid);
|
||||
if (!human || !action || (action.actionType !== 'assembly' && action.actionType !== 'worker') || !humanEventManagerRef.current) return;
|
||||
|
||||
if (!action) return;
|
||||
|
||||
const actionType = action.actionType;
|
||||
if (actionType !== "worker" && actionType !== "assembly") return;
|
||||
|
||||
if (!stateRef.current.callbackCounts.has(humanId)) {
|
||||
stateRef.current.callbackCounts.set(humanId, new Map());
|
||||
let state = humanEventManagerRef.current.humanStates.find(h => h.humanId === humanId);
|
||||
if (!state) {
|
||||
state = { humanId, actionQueue: [], isCooldown: false };
|
||||
humanEventManagerRef.current.humanStates.push(state);
|
||||
}
|
||||
|
||||
const actionCounts = stateRef.current.callbackCounts.get(humanId)!;
|
||||
if (!actionCounts.has(actionUuid)) {
|
||||
actionCounts.set(actionUuid, 0);
|
||||
}
|
||||
|
||||
const currentCount = actionCounts.get(actionUuid)!;
|
||||
if (actionType === 'worker' && currentCount >= action.loadCount) {
|
||||
const existingAction = state.actionQueue.find(a => a.actionUuid === actionUuid);
|
||||
if (existingAction) {
|
||||
const currentCount = existingAction.count ?? 0;
|
||||
if (existingAction.actionType === 'worker') {
|
||||
if (currentCount < existingAction.maxLoadCount) {
|
||||
existingAction.callback = callback;
|
||||
existingAction.isMonitored = true;
|
||||
existingAction.isCompleted = false;
|
||||
}
|
||||
} else if (existingAction.actionType === 'assembly') {
|
||||
if (currentCount < existingAction.maxAssemblyCount) {
|
||||
existingAction.callback = callback;
|
||||
existingAction.isMonitored = true;
|
||||
existingAction.isCompleted = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!stateRef.current.humanStates.has(humanId)) {
|
||||
stateRef.current.humanStates.set(humanId, {
|
||||
callbacks: [],
|
||||
actionQueue: [],
|
||||
isCooldown: false
|
||||
});
|
||||
}
|
||||
|
||||
const humanState = stateRef.current.humanStates.get(humanId)!;
|
||||
humanState.callbacks.push(callback);
|
||||
humanState.actionQueue.push({ actionType, actionUuid, actionName: action.actionName });
|
||||
|
||||
stateRef.current.isMonitoring = true;
|
||||
state.actionQueue.push({
|
||||
actionType: action.actionType,
|
||||
actionUuid,
|
||||
actionName: action.actionName,
|
||||
maxLoadCount: action.loadCount ?? 0,
|
||||
maxAssemblyCount: action.assemblyCount ?? 0,
|
||||
count: 0,
|
||||
isMonitored: true,
|
||||
isCompleted: false,
|
||||
callback
|
||||
});
|
||||
};
|
||||
|
||||
const removeHumanFromMonitor = (humanId: string) => {
|
||||
// stateRef.current.humanStates.delete(humanId);
|
||||
const removeHumanFromMonitor = (humanId: string, actionUuid: string) => {
|
||||
if (!humanEventManagerRef.current) return;
|
||||
const state = humanEventManagerRef.current.humanStates.find(h => h.humanId === humanId);
|
||||
if (!state) return;
|
||||
|
||||
if (stateRef.current.humanStates.size === 0) {
|
||||
stateRef.current.isMonitoring = false;
|
||||
const action = state.actionQueue.find(a => a.actionUuid === actionUuid);
|
||||
if (action) {
|
||||
action.callback = undefined;
|
||||
action.isMonitored = false;
|
||||
}
|
||||
};
|
||||
|
||||
const getCallbackCount = (humanId: string, actionUuid: string) => {
|
||||
if (!stateRef.current.callbackCounts.has(humanId)) return 0;
|
||||
return stateRef.current.callbackCounts.get(humanId)!.get(actionUuid) || 0;
|
||||
};
|
||||
|
||||
const incrementCallbackCount = (humanId: string, actionUuid: string) => {
|
||||
if (!stateRef.current.callbackCounts.has(humanId)) {
|
||||
stateRef.current.callbackCounts.set(humanId, new Map());
|
||||
}
|
||||
const actionCounts = stateRef.current.callbackCounts.get(humanId)!;
|
||||
const currentCount = actionCounts.get(actionUuid) || 0;
|
||||
actionCounts.set(actionUuid, currentCount + 1);
|
||||
};
|
||||
|
||||
useFrame(() => {
|
||||
if (!stateRef.current.isMonitoring || !isPlaying || isPaused) return;
|
||||
if (!humanEventManagerRef.current || humanEventManagerRef.current.humanStates.length === 0 || !isPlaying || isPaused) return;
|
||||
|
||||
stateRef.current.humanStates.forEach((humanState, humanId) => {
|
||||
if (humanState.callbacks.length === 0 || humanState.isCooldown) return;
|
||||
for (const humanState of humanEventManagerRef.current.humanStates) {
|
||||
if (humanState.isCooldown) continue;
|
||||
|
||||
const actionQueue = humanState.actionQueue;
|
||||
if (!actionQueue || actionQueue.length === 0) return;
|
||||
const { humanId, actionQueue } = humanState;
|
||||
if (!actionQueue || actionQueue.length === 0) continue;
|
||||
|
||||
const action = actionQueue.find(a => !a.isCompleted);
|
||||
if (!action || !action.isMonitored || !action.callback) continue;
|
||||
|
||||
const { actionType: expectedActionType, actionUuid } = actionQueue[0];
|
||||
const human = getHumanById(humanId);
|
||||
const humanAsset = getAssetById(humanId);
|
||||
const action = getActionByUuid(selectedProduct.productUuid, actionUuid) as HumanAction | undefined;
|
||||
const currentAction = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '') as HumanAction | undefined;
|
||||
|
||||
if (!humanAsset || !human || !action || action.actionType !== expectedActionType) return;
|
||||
|
||||
const currentCount = getCallbackCount(humanId, actionUuid);
|
||||
|
||||
const currentAction = getActionByUuid(selectedProduct.productUuid, human.currentAction?.actionUuid || '') as HumanAction | undefined;
|
||||
if (!human || !humanAsset || !currentAction) continue;
|
||||
if (human.isActive || human.state !== "idle" || humanAsset.animationState?.current !== 'idle') continue;
|
||||
|
||||
let conditionMet = false;
|
||||
|
||||
if (expectedActionType === "worker") {
|
||||
if (currentAction && currentAction.actionType === 'worker' && currentCount >= currentAction.loadCount) {
|
||||
humanState.callbacks.shift();
|
||||
actionQueue.shift();
|
||||
if (humanState.callbacks.length === 0) {
|
||||
removeHumanFromMonitor(humanId);
|
||||
}
|
||||
return;
|
||||
if (currentAction.actionType === 'worker') {
|
||||
if (action.actionType === 'worker' && human.currentLoad < currentAction.loadCapacity) {
|
||||
conditionMet = true;
|
||||
} else if (action.actionType === 'assembly') {
|
||||
conditionMet = true;
|
||||
}
|
||||
|
||||
if (currentAction && currentAction.actionType === 'worker') {
|
||||
conditionMet = (
|
||||
!human.isActive &&
|
||||
human.state === "idle" &&
|
||||
humanAsset.animationState?.current === 'idle' &&
|
||||
human.currentLoad < currentAction.loadCapacity
|
||||
);
|
||||
|
||||
if (conditionMet && actionUuid !== human.currentAction?.actionUuid) {
|
||||
setCurrentPhase(human.modelUuid, 'init');
|
||||
}
|
||||
} else {
|
||||
conditionMet = (
|
||||
!human.isActive &&
|
||||
human.state === "idle" &&
|
||||
humanAsset.animationState?.current === 'idle'
|
||||
);
|
||||
if (conditionMet && actionUuid !== human.currentAction?.actionUuid) {
|
||||
setCurrentPhase(human.modelUuid, 'init');
|
||||
}
|
||||
}
|
||||
|
||||
} else if (expectedActionType === "assembly") {
|
||||
if (currentAction && currentAction.actionType === 'worker') {
|
||||
conditionMet = (
|
||||
!human.isActive &&
|
||||
human.state === "idle" &&
|
||||
humanAsset.animationState?.current === 'idle' &&
|
||||
human.currentLoad < currentAction.loadCapacity
|
||||
);
|
||||
if (conditionMet && actionUuid !== human.currentAction?.actionUuid) {
|
||||
setCurrentPhase(human.modelUuid, 'init');
|
||||
}
|
||||
} else {
|
||||
conditionMet = (
|
||||
!human.isActive &&
|
||||
human.state === "idle" &&
|
||||
humanAsset.animationState?.current === 'idle'
|
||||
)
|
||||
} else if (currentAction.actionType === 'assembly') {
|
||||
if (action.actionType === 'assembly') {
|
||||
conditionMet = true;
|
||||
} else if (action.actionType === 'worker' && human.currentLoad < currentAction.loadCapacity) {
|
||||
conditionMet = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (conditionMet) {
|
||||
const callback = humanState.callbacks.shift();
|
||||
actionQueue.shift();
|
||||
|
||||
if (callback) {
|
||||
callback();
|
||||
incrementCallbackCount(humanId, actionUuid);
|
||||
if (action.actionUuid !== human.currentAction?.actionUuid) {
|
||||
setCurrentPhase(human.modelUuid, 'init');
|
||||
removeCurrentAction(human.modelUuid);
|
||||
}
|
||||
|
||||
if (humanState.callbacks.length === 0) {
|
||||
removeHumanFromMonitor(humanId);
|
||||
} else {
|
||||
humanState.isCooldown = true;
|
||||
setTimeout(() => {
|
||||
humanState.isCooldown = false;
|
||||
}, 1000);
|
||||
action.callback();
|
||||
action.count = (action.count ?? 0) + 1;
|
||||
action.isMonitored = false;
|
||||
if ((action.actionType === 'worker' && action.count >= action.maxLoadCount) ||
|
||||
(action.actionType === 'assembly' && action.count >= action.maxAssemblyCount)) {
|
||||
action.isCompleted = true;
|
||||
}
|
||||
humanState.isCooldown = true;
|
||||
setTimeout(() => {
|
||||
humanState.isCooldown = false;
|
||||
}, 1000);
|
||||
|
||||
removeHumanFromMonitor(human.modelUuid, action.actionUuid);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}, 0);
|
||||
|
||||
return {
|
||||
addHumanToMonitor,
|
||||
removeHumanFromMonitor,
|
||||
removeHumanFromMonitor
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useFrame, useThree } from '@react-three/fiber';
|
||||
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 AssemblerAnimatorProps {
|
||||
path: [number, number, number][];
|
||||
handleCallBack: () => void;
|
||||
reset: () => void;
|
||||
human: HumanStatus;
|
||||
}
|
||||
|
||||
function AssemblerAnimator({ path, handleCallBack, human, reset }: Readonly<AssemblerAnimatorProps>) {
|
||||
const { humanStore, assetStore, productStore } = useSceneContext();
|
||||
const { getActionByUuid } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { getHumanById } = humanStore();
|
||||
const { setCurrentAnimation } = assetStore();
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { speed } = useAnimationPlaySpeed();
|
||||
const { isReset, setReset } = useResetButtonStore();
|
||||
const progressRef = useRef<number>(0);
|
||||
const completedRef = useRef<boolean>(false);
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
const [objectRotation, setObjectRotation] = useState<[number, number, number] | null>((action as HumanAction)?.assemblyPoint?.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;
|
||||
if (human.currentPhase === 'init-assembly' && path.length > 0) {
|
||||
setCurrentPath(path);
|
||||
setObjectRotation((action as HumanAction)?.assemblyPoint?.rotation ?? null);
|
||||
}
|
||||
}, [human.currentPhase, path, objectRotation, selectedProduct, human.currentAction?.actionUuid]);
|
||||
|
||||
useEffect(() => {
|
||||
completedRef.current = false;
|
||||
}, [currentPath]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isReset || !isPlaying) {
|
||||
reset();
|
||||
setCurrentPath([]);
|
||||
completedRef.current = false;
|
||||
progressRef.current = 0;
|
||||
setReset(false);
|
||||
setRestingRotation(true);
|
||||
const object = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
const humanData = getHumanById(human.modelUuid);
|
||||
if (object && humanData) {
|
||||
object.position.set(humanData.position[0], humanData.position[1], humanData.position[2]);
|
||||
object.rotation.set(humanData.rotation[0], humanData.rotation[1], humanData.rotation[2]);
|
||||
}
|
||||
}
|
||||
}, [isReset, isPlaying]);
|
||||
|
||||
const lastTimeRef = useRef(performance.now());
|
||||
|
||||
useFrame(() => {
|
||||
const now = performance.now();
|
||||
const delta = (now - lastTimeRef.current) / 1000;
|
||||
lastTimeRef.current = now;
|
||||
|
||||
const object = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
if (!object || currentPath.length < 2) return;
|
||||
if (isPaused || !isPlaying) return;
|
||||
|
||||
let totalDistance = 0;
|
||||
const distances = [];
|
||||
let accumulatedDistance = 0;
|
||||
let index = 0;
|
||||
const rotationSpeed = 1.5;
|
||||
|
||||
for (let i = 0; i < currentPath.length - 1; i++) {
|
||||
const start = new THREE.Vector3(...currentPath[i]);
|
||||
const end = new THREE.Vector3(...currentPath[i + 1]);
|
||||
const segmentDistance = start.distanceTo(end);
|
||||
distances.push(segmentDistance);
|
||||
totalDistance += segmentDistance;
|
||||
}
|
||||
|
||||
while (index < distances.length && progressRef.current > accumulatedDistance + distances[index]) {
|
||||
accumulatedDistance += distances[index];
|
||||
index++;
|
||||
}
|
||||
|
||||
if (index < distances.length) {
|
||||
const start = new THREE.Vector3(...currentPath[index]);
|
||||
const end = new THREE.Vector3(...currentPath[index + 1]);
|
||||
const segmentDistance = distances[index];
|
||||
|
||||
const targetQuaternion = new THREE.Quaternion().setFromRotationMatrix(
|
||||
new THREE.Matrix4().lookAt(start, end, new THREE.Vector3(0, 1, 0))
|
||||
);
|
||||
const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
|
||||
targetQuaternion.multiply(y180);
|
||||
|
||||
const angle = object.quaternion.angleTo(targetQuaternion);
|
||||
if (angle < 0.01) {
|
||||
object.quaternion.copy(targetQuaternion);
|
||||
} else {
|
||||
const step = rotationSpeed * delta * speed * human.speed;
|
||||
object.quaternion.rotateTowards(targetQuaternion, step);
|
||||
}
|
||||
|
||||
const isAligned = angle < 0.01;
|
||||
|
||||
if (isAligned) {
|
||||
progressRef.current += delta * (speed * human.speed);
|
||||
const t = (progressRef.current - accumulatedDistance) / segmentDistance;
|
||||
const position = start.clone().lerp(end, t);
|
||||
object.position.copy(position);
|
||||
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
|
||||
} else {
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (progressRef.current >= totalDistance) {
|
||||
if (restRotation && objectRotation) {
|
||||
const targetEuler = new THREE.Euler(0, objectRotation[1], 0);
|
||||
const baseQuaternion = new THREE.Quaternion().setFromEuler(targetEuler);
|
||||
const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
|
||||
const targetQuaternion = baseQuaternion.multiply(y180);
|
||||
|
||||
const angle = object.quaternion.angleTo(targetQuaternion);
|
||||
if (angle < 0.01) {
|
||||
object.quaternion.copy(targetQuaternion);
|
||||
setRestingRotation(false);
|
||||
} else {
|
||||
const step = rotationSpeed * delta * speed * human.speed;
|
||||
object.quaternion.rotateTowards(targetQuaternion, step);
|
||||
}
|
||||
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (progressRef.current >= totalDistance) {
|
||||
setRestingRotation(true);
|
||||
progressRef.current = 0;
|
||||
setCurrentPath([]);
|
||||
handleCallBack();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{currentPath.length > 0 && (
|
||||
<group visible={false}>
|
||||
<Line points={currentPath} color="blue" lineWidth={3} />
|
||||
{currentPath.map((point, index) => (
|
||||
<mesh key={index} position={point}>
|
||||
<sphereGeometry args={[0.1, 16, 16]} />
|
||||
<meshStandardMaterial color="red" />
|
||||
</mesh>
|
||||
))}
|
||||
</group>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default AssemblerAnimator;
|
||||
@@ -50,7 +50,7 @@ const MaterialAnimator = ({ human }: { human: HumanStatus; }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{hasLoad && (action as HumanAction).actionType === 'worker' && human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup') && (
|
||||
{hasLoad && action && (action as HumanAction).actionType === 'worker' && human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup') && (
|
||||
<MaterialModel
|
||||
matRef={meshRef}
|
||||
materialId={human.currentMaterials[0].materialId || ''}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useFrame, useThree } from '@react-three/fiber';
|
||||
import * as THREE from 'three';
|
||||
import { Line } from '@react-three/drei';
|
||||
@@ -6,7 +6,7 @@ import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useRese
|
||||
import { useSceneContext } from '../../../../scene/sceneContext';
|
||||
import { useProductContext } from '../../../products/productContext';
|
||||
|
||||
interface HumanAnimatorProps {
|
||||
interface WorkerAnimatorProps {
|
||||
path: [number, number, number][];
|
||||
handleCallBack: () => void;
|
||||
reset: () => void;
|
||||
@@ -14,7 +14,7 @@ interface HumanAnimatorProps {
|
||||
human: HumanStatus;
|
||||
}
|
||||
|
||||
function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProcess }: Readonly<HumanAnimatorProps>) {
|
||||
function WorkerAnimator({ path, handleCallBack, human, reset, startUnloadingProcess }: Readonly<WorkerAnimatorProps>) {
|
||||
const { humanStore, assetStore, productStore } = useSceneContext();
|
||||
const { getActionByUuid } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
@@ -29,7 +29,7 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
const movingForward = useRef<boolean>(true);
|
||||
const completedRef = useRef<boolean>(false);
|
||||
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 [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();
|
||||
@@ -39,15 +39,12 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
if (human.currentPhase === 'init-pickup' && path.length > 0) {
|
||||
setCurrentPath(path);
|
||||
setObjectRotation((action as HumanAction).pickUpPoint?.rotation ?? null)
|
||||
} else if (human.currentPhase === 'init-assembly' && path.length > 0) {
|
||||
setObjectRotation((action as HumanAction)?.assemblyPoint?.rotation ?? null)
|
||||
setCurrentPath(path);
|
||||
setObjectRotation((action as HumanAction).pickUpPoint?.rotation ?? null);
|
||||
} else if (human.currentPhase === 'pickup-drop' && path.length > 0) {
|
||||
setObjectRotation((action as HumanAction)?.dropPoint?.rotation ?? null)
|
||||
setObjectRotation((action as HumanAction)?.dropPoint?.rotation ?? null);
|
||||
setCurrentPath(path);
|
||||
} else if (human.currentPhase === 'drop-pickup' && path.length > 0) {
|
||||
setObjectRotation((action as HumanAction)?.pickUpPoint?.rotation ?? null)
|
||||
setObjectRotation((action as HumanAction)?.pickUpPoint?.rotation ?? null);
|
||||
setCurrentPath(path);
|
||||
}
|
||||
}, [human.currentPhase, path, objectRotation, selectedProduct, human.currentAction?.actionUuid]);
|
||||
@@ -72,7 +69,7 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
object.rotation.set(humanData.rotation[0], humanData.rotation[1], humanData.rotation[2]);
|
||||
}
|
||||
}
|
||||
}, [isReset, isPlaying])
|
||||
}, [isReset, isPlaying]);
|
||||
|
||||
const lastTimeRef = useRef(performance.now());
|
||||
|
||||
@@ -109,7 +106,9 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
const end = new THREE.Vector3(...currentPath[index + 1]);
|
||||
const segmentDistance = distances[index];
|
||||
|
||||
const targetQuaternion = new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().lookAt(start, end, new THREE.Vector3(0, 1, 0)));
|
||||
const targetQuaternion = new THREE.Quaternion().setFromRotationMatrix(
|
||||
new THREE.Matrix4().lookAt(start, end, new THREE.Vector3(0, 1, 0))
|
||||
);
|
||||
const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
|
||||
targetQuaternion.multiply(y180);
|
||||
|
||||
@@ -118,13 +117,7 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
object.quaternion.copy(targetQuaternion);
|
||||
} else {
|
||||
const step = rotationSpeed * delta * speed * human.speed;
|
||||
const angle = object.quaternion.angleTo(targetQuaternion);
|
||||
|
||||
if (angle < step) {
|
||||
object.quaternion.copy(targetQuaternion);
|
||||
} else {
|
||||
object.quaternion.rotateTowards(targetQuaternion, step);
|
||||
}
|
||||
object.quaternion.rotateTowards(targetQuaternion, step);
|
||||
}
|
||||
|
||||
const isAligned = angle < 0.01;
|
||||
@@ -134,13 +127,13 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
const t = (progressRef.current - accumulatedDistance) / segmentDistance;
|
||||
const position = start.clone().lerp(end, t);
|
||||
object.position.copy(position);
|
||||
if (human.currentMaterials.length > 0 && action?.actionType === 'worker' && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup')) {
|
||||
if (human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup')) {
|
||||
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
|
||||
} else {
|
||||
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
|
||||
}
|
||||
} else {
|
||||
if (human.currentMaterials.length > 0 && action?.actionType === 'worker' && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup')) {
|
||||
if (human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup')) {
|
||||
setCurrentAnimation(human.modelUuid, 'idle_with_box', true, true, true);
|
||||
} else {
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
@@ -151,7 +144,6 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
if (progressRef.current >= totalDistance) {
|
||||
if (restRotation && objectRotation) {
|
||||
const targetEuler = new THREE.Euler(0, objectRotation[1], 0);
|
||||
|
||||
const baseQuaternion = new THREE.Quaternion().setFromEuler(targetEuler);
|
||||
const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
|
||||
const targetQuaternion = baseQuaternion.multiply(y180);
|
||||
@@ -162,21 +154,14 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
setRestingRotation(false);
|
||||
} else {
|
||||
const step = rotationSpeed * delta * speed * human.speed;
|
||||
const angle = object.quaternion.angleTo(targetQuaternion);
|
||||
|
||||
if (angle < step) {
|
||||
object.quaternion.copy(targetQuaternion);
|
||||
} else {
|
||||
object.quaternion.rotateTowards(targetQuaternion, step);
|
||||
}
|
||||
}
|
||||
|
||||
if (human.currentMaterials.length > 0) {
|
||||
setCurrentAnimation(human.modelUuid, 'idle_with_box', true, true, true);
|
||||
} else {
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
object.quaternion.rotateTowards(targetQuaternion, step);
|
||||
}
|
||||
|
||||
setCurrentAnimation(
|
||||
human.modelUuid,
|
||||
human.currentMaterials.length > 0 ? 'idle_with_box' : 'idle',
|
||||
true, true, true
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -196,7 +181,6 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
return (
|
||||
<>
|
||||
{currentPath.length > 0 && (
|
||||
// helper
|
||||
<group visible={false}>
|
||||
<Line points={currentPath} color="blue" lineWidth={3} />
|
||||
{currentPath.map((point, index) => (
|
||||
@@ -208,7 +192,7 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
</group>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default HumanAnimator
|
||||
export default WorkerAnimator;
|
||||
@@ -0,0 +1,221 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { NavMeshQuery } from '@recast-navigation/core';
|
||||
import { useNavMesh } from '../../../../../../store/builder/store';
|
||||
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../../store/usePlayButtonStore';
|
||||
import { useTriggerHandler } from '../../../../triggers/triggerHandler/useTriggerHandler';
|
||||
import { useSceneContext } from '../../../../../scene/sceneContext';
|
||||
import { useProductContext } from '../../../../products/productContext';
|
||||
|
||||
import AssemblerAnimator from '../../animator/assemblerAnimator';
|
||||
|
||||
function AssemblerInstance({ human }: { human: HumanStatus }) {
|
||||
const { navMesh } = useNavMesh();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { scene } = useThree();
|
||||
const { assetStore, materialStore, humanStore, productStore } = useSceneContext();
|
||||
const { setMaterial } = materialStore();
|
||||
const { triggerPointActions } = useTriggerHandler();
|
||||
const { setCurrentAnimation, resetAnimation, getAssetById } = assetStore();
|
||||
const { getActionByUuid } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { setHumanActive, setHumanState, decrementHumanLoad, removeLastMaterial, setCurrentPhase } = humanStore();
|
||||
|
||||
const [path, setPath] = useState<[number, number, number][]>([]);
|
||||
const isPausedRef = useRef<boolean>(false);
|
||||
const isSpeedRef = useRef<number>(0);
|
||||
const { speed } = useAnimationPlaySpeed();
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
const processStartTimeRef = useRef<number | null>(null);
|
||||
const processTimeRef = useRef<number>(0);
|
||||
const processAnimationIdRef = useRef<number | null>(null);
|
||||
const accumulatedPausedTimeRef = useRef<number>(0);
|
||||
const lastPauseTimeRef = useRef<number | null>(null);
|
||||
const hasLoggedHalfway = useRef(false);
|
||||
const hasLoggedCompleted = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
isPausedRef.current = isPaused;
|
||||
}, [isPaused]);
|
||||
|
||||
useEffect(() => {
|
||||
isSpeedRef.current = speed;
|
||||
}, [speed]);
|
||||
|
||||
const computePath = useCallback((start: [number, number, number], end: [number, number, number]) => {
|
||||
try {
|
||||
const navMeshQuery = new NavMeshQuery(navMesh);
|
||||
let startPoint = new THREE.Vector3(start[0], start[1], start[2]);
|
||||
let endPoint = new THREE.Vector3(end[0], end[1], end[2]);
|
||||
const { path: segmentPath } = navMeshQuery.computePath(startPoint, endPoint);
|
||||
if (
|
||||
segmentPath.length > 0 &&
|
||||
Math.round(segmentPath[segmentPath.length - 1].x) == Math.round(endPoint.x) &&
|
||||
Math.round(segmentPath[segmentPath.length - 1].z) == Math.round(endPoint.z)
|
||||
) {
|
||||
return segmentPath?.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
|
||||
} else {
|
||||
console.log("There is no path here...Choose valid path")
|
||||
const { path: segmentPaths } = navMeshQuery.computePath(startPoint, startPoint);
|
||||
return segmentPaths.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
|
||||
}
|
||||
} catch {
|
||||
console.error("Failed to compute path");
|
||||
return [];
|
||||
}
|
||||
}, [navMesh]);
|
||||
|
||||
function humanStatus(modelId: string, status: string) {
|
||||
// console.log(`${modelId} , ${status}`);
|
||||
}
|
||||
|
||||
function reset() {
|
||||
setCurrentPhase(human.modelUuid, 'init');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
resetAnimation(human.modelUuid);
|
||||
setPath([]);
|
||||
if (processAnimationIdRef.current) {
|
||||
cancelAnimationFrame(processAnimationIdRef.current);
|
||||
processAnimationIdRef.current = null;
|
||||
}
|
||||
processStartTimeRef.current = null;
|
||||
processTimeRef.current = 0;
|
||||
accumulatedPausedTimeRef.current = 0;
|
||||
lastPauseTimeRef.current = null;
|
||||
hasLoggedHalfway.current = false;
|
||||
hasLoggedCompleted.current = false;
|
||||
const object = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
if (object && human) {
|
||||
object.position.set(human.position[0], human.position[1], human.position[2]);
|
||||
object.rotation.set(human.rotation[0], human.rotation[1], human.rotation[2]);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying) {
|
||||
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' && human.currentPhase === 'init') {
|
||||
const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
if (!humanMesh) return;
|
||||
|
||||
const toPickupPath = computePath(humanMesh.position.toArray(), (action as HumanAction)?.assemblyPoint?.position || [0, 0, 0]);
|
||||
setPath(toPickupPath);
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setCurrentPhase(human.modelUuid, 'init-assembly');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Human is waiting for material in assembly');
|
||||
} else if (!human.isActive && human.state === 'idle' && human.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');
|
||||
setCurrentPhase(human.modelUuid, 'assembling');
|
||||
setHumanActive(human.modelUuid, true);
|
||||
|
||||
processStartTimeRef.current = performance.now();
|
||||
processTimeRef.current = (action as HumanAction).processTime || 0;
|
||||
accumulatedPausedTimeRef.current = 0;
|
||||
lastPauseTimeRef.current = null;
|
||||
hasLoggedHalfway.current = false;
|
||||
hasLoggedCompleted.current = false;
|
||||
|
||||
if (!processAnimationIdRef.current) {
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
||||
}
|
||||
}
|
||||
} else if (human.isActive && human.state === 'running' && human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current === 'working_standing' && humanAsset.animationState?.isCompleted) {
|
||||
if ((action as HumanAction).assemblyPoint && human.currentPhase === 'assembling') {
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setCurrentPhase(human.modelUuid, 'waiting');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Human is waiting for material in assembly');
|
||||
|
||||
decrementHumanLoad(human.modelUuid, 1);
|
||||
const material = removeLastMaterial(human.modelUuid);
|
||||
if (material) {
|
||||
triggerPointActions((action as HumanAction), material.materialId);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reset()
|
||||
}
|
||||
}, [human, 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 || !(action as HumanAction).processTime || !action) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPausedRef.current) {
|
||||
if (!lastPauseTimeRef.current) {
|
||||
lastPauseTimeRef.current = now;
|
||||
}
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
||||
return;
|
||||
} else if (lastPauseTimeRef.current) {
|
||||
accumulatedPausedTimeRef.current += now - lastPauseTimeRef.current;
|
||||
lastPauseTimeRef.current = null;
|
||||
}
|
||||
|
||||
const elapsed = (now - processStartTimeRef.current - accumulatedPausedTimeRef.current) * isSpeedRef.current;
|
||||
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, (action as HumanAction).swapMaterial || 'Default Material');
|
||||
}
|
||||
humanStatus(human.modelUuid, `🟡 Human ${human.modelUuid} reached halfway in assembly.`);
|
||||
}
|
||||
|
||||
if (elapsed >= totalProcessTimeMs && !hasLoggedCompleted.current) {
|
||||
hasLoggedCompleted.current = true;
|
||||
setCurrentAnimation(human.modelUuid, 'working_standing', true, true, true);
|
||||
if (processAnimationIdRef.current) {
|
||||
cancelAnimationFrame(processAnimationIdRef.current);
|
||||
processAnimationIdRef.current = null;
|
||||
}
|
||||
humanStatus(human.modelUuid, `✅ Human ${human.modelUuid} completed assembly process.`);
|
||||
return;
|
||||
}
|
||||
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
||||
}, [human.modelUuid, human.currentMaterials]);
|
||||
|
||||
function handleCallBack() {
|
||||
if (human.currentPhase === 'init-assembly') {
|
||||
setCurrentPhase(human.modelUuid, 'waiting');
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Reached assembly point, waiting for material');
|
||||
setPath([]);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<AssemblerAnimator
|
||||
path={path}
|
||||
handleCallBack={handleCallBack}
|
||||
human={human}
|
||||
reset={reset}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AssemblerInstance;
|
||||
@@ -0,0 +1,615 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { NavMeshQuery } from '@recast-navigation/core';
|
||||
import { useNavMesh } from '../../../../../../store/builder/store';
|
||||
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../../store/usePlayButtonStore';
|
||||
import { useTriggerHandler } from '../../../../triggers/triggerHandler/useTriggerHandler';
|
||||
import { useSceneContext } from '../../../../../scene/sceneContext';
|
||||
import { useProductContext } from '../../../../products/productContext';
|
||||
|
||||
import WorkerAnimator from '../../animator/workerAnimator';
|
||||
|
||||
function WorkerInstance({ human }: { human: HumanStatus }) {
|
||||
const { navMesh } = useNavMesh();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { scene } = useThree();
|
||||
const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext();
|
||||
const { removeMaterial, setEndTime, setIsVisible } = materialStore();
|
||||
const { getStorageUnitById } = storageUnitStore();
|
||||
const { getArmBotById } = armBotStore();
|
||||
const { getConveyorById } = conveyorStore();
|
||||
const { getVehicleById } = vehicleStore();
|
||||
const { getMachineById } = machineStore();
|
||||
const { triggerPointActions } = useTriggerHandler();
|
||||
const { setCurrentAnimation, resetAnimation, getAssetById } = assetStore();
|
||||
const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { setHumanActive, setHumanState, clearCurrentMaterials, setHumanLoad, setHumanScheduled, decrementHumanLoad, removeLastMaterial, incrementIdleTime, incrementActiveTime, resetTime, setCurrentPhase } = humanStore();
|
||||
|
||||
const [path, setPath] = useState<[number, number, number][]>([]);
|
||||
const pauseTimeRef = useRef<number | null>(null);
|
||||
const idleTimeRef = useRef<number>(0);
|
||||
const activeTimeRef = useRef<number>(0);
|
||||
const isPausedRef = useRef<boolean>(false);
|
||||
const isSpeedRef = useRef<number>(0);
|
||||
const { speed } = useAnimationPlaySpeed();
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const previousTimeRef = useRef<number | null>(null);
|
||||
const animationFrameIdRef = useRef<number | null>(null);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
useEffect(() => {
|
||||
isPausedRef.current = isPaused;
|
||||
}, [isPaused]);
|
||||
|
||||
useEffect(() => {
|
||||
isSpeedRef.current = speed;
|
||||
}, [speed]);
|
||||
|
||||
const computePath = useCallback((start: [number, number, number], end: [number, number, number]) => {
|
||||
try {
|
||||
const navMeshQuery = new NavMeshQuery(navMesh);
|
||||
let startPoint = new THREE.Vector3(start[0], start[1], start[2]);
|
||||
let endPoint = new THREE.Vector3(end[0], end[1], end[2]);
|
||||
const { path: segmentPath } = navMeshQuery.computePath(startPoint, endPoint);
|
||||
if (
|
||||
segmentPath.length > 0 &&
|
||||
Math.round(segmentPath[segmentPath.length - 1].x) == Math.round(endPoint.x) &&
|
||||
Math.round(segmentPath[segmentPath.length - 1].z) == Math.round(endPoint.z)
|
||||
) {
|
||||
return segmentPath?.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
|
||||
} else {
|
||||
console.log("There is no path here...Choose valid path")
|
||||
const { path: segmentPaths } = navMeshQuery.computePath(startPoint, startPoint);
|
||||
return segmentPaths.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
|
||||
}
|
||||
} catch {
|
||||
console.error("Failed to compute path");
|
||||
return [];
|
||||
}
|
||||
}, [navMesh]);
|
||||
|
||||
function humanStatus(modelId: string, status: string) {
|
||||
// console.log(`${modelId} , ${status}`);
|
||||
}
|
||||
|
||||
function reset() {
|
||||
setCurrentPhase(human.modelUuid, 'init');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanScheduled(human.modelUuid, false);
|
||||
setHumanLoad(human.modelUuid, 0);
|
||||
resetAnimation(human.modelUuid);
|
||||
setPath([]);
|
||||
isPausedRef.current = false;
|
||||
pauseTimeRef.current = 0;
|
||||
resetTime(human.modelUuid)
|
||||
activeTimeRef.current = 0
|
||||
idleTimeRef.current = 0
|
||||
previousTimeRef.current = null
|
||||
if (animationFrameIdRef.current !== null) {
|
||||
cancelAnimationFrame(animationFrameIdRef.current)
|
||||
animationFrameIdRef.current = null
|
||||
}
|
||||
const object = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
if (object && human) {
|
||||
object.position.set(human.position[0], human.position[1], human.position[2]);
|
||||
object.rotation.set(human.rotation[0], human.rotation[1], human.rotation[2]);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying) {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
if (!action || action.actionType !== 'worker' || !action.pickUpPoint || !action.dropPoint) return;
|
||||
|
||||
if (!human.isActive && human.state === 'idle' && human.currentPhase === 'init') {
|
||||
const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
if (!humanMesh) return;
|
||||
|
||||
const toPickupPath = computePath(humanMesh.position.toArray(), action?.pickUpPoint?.position || [0, 0, 0]);
|
||||
|
||||
setPath(toPickupPath);
|
||||
setCurrentPhase(human.modelUuid, 'init-pickup');
|
||||
setHumanState(human.modelUuid, 'running');
|
||||
setHumanActive(human.modelUuid, true);
|
||||
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Started from init, heading to pickup');
|
||||
return;
|
||||
} else if (!human.isActive && human.state === 'idle' && human.currentPhase === 'picking') {
|
||||
if (humanAsset && human.currentLoad === action.loadCapacity && human.currentMaterials.length > 0 && human.currentLoad > 0 && humanAsset.animationState?.current === 'pickup' && humanAsset.animationState?.isCompleted) {
|
||||
if (action.pickUpPoint && action.dropPoint) {
|
||||
const toDrop = computePath(action.pickUpPoint.position || [0, 0, 0], action.dropPoint.position || [0, 0, 0]);
|
||||
setPath(toDrop);
|
||||
setCurrentPhase(human.modelUuid, 'pickup-drop');
|
||||
setHumanState(human.modelUuid, 'running');
|
||||
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Started from pickup point, heading to drop point');
|
||||
}
|
||||
} else if (human.currentMaterials.length > 0 && human.currentLoad > 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' && human.currentPhase === 'dropping' && human.currentLoad === 0) {
|
||||
if (action.pickUpPoint && action.dropPoint) {
|
||||
const dropToPickup = computePath(action.dropPoint.position || [0, 0, 0], action.pickUpPoint.position || [0, 0, 0]);
|
||||
setPath(dropToPickup);
|
||||
setCurrentPhase(human.modelUuid, 'drop-pickup');
|
||||
setHumanState(human.modelUuid, 'running');
|
||||
setHumanActive(human.modelUuid, true);
|
||||
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Started from dropping point, heading to pickup point');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reset()
|
||||
}
|
||||
}, [human, human.currentAction, human.currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
|
||||
|
||||
function handleCallBack() {
|
||||
if (human.currentPhase === 'init-pickup') {
|
||||
setCurrentPhase(human.modelUuid, 'picking');
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Reached pickup point, waiting for material');
|
||||
setPath([]);
|
||||
} else if (human.currentPhase === 'pickup-drop') {
|
||||
setCurrentPhase(human.modelUuid, 'dropping');
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
humanStatus(human.modelUuid, 'Reached drop point');
|
||||
setPath([]);
|
||||
} else if (human.currentPhase === 'drop-pickup') {
|
||||
setCurrentPhase(human.modelUuid, 'picking');
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setHumanScheduled(human.modelUuid, false);
|
||||
setPath([]);
|
||||
clearCurrentMaterials(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Reached pickup point again, cycle complete');
|
||||
}
|
||||
}
|
||||
|
||||
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 ((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') {
|
||||
if (action) {
|
||||
handleMaterialDropToConveyor(model);
|
||||
}
|
||||
} else if (model.type === 'machine') {
|
||||
if (action) {
|
||||
handleMaterialDropToMachine(model);
|
||||
}
|
||||
} else if (model.type === 'roboticArm') {
|
||||
if (action) {
|
||||
handleMaterialDropToArmBot(model);
|
||||
}
|
||||
} else if (model.type === 'storageUnit') {
|
||||
if (action) {
|
||||
handleMaterialDropToStorageUnit(model);
|
||||
}
|
||||
} else if (model.type === 'vehicle') {
|
||||
if (action) {
|
||||
handleMaterialDropToVehicle(model);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const droppedMaterial = human.currentLoad;
|
||||
handleMaterialDropByDefault(droppedMaterial);
|
||||
}
|
||||
} else {
|
||||
const droppedMaterial = human.currentLoad;
|
||||
handleMaterialDropByDefault(droppedMaterial);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(startUnloadingProcess);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
if (model.point.action.actionType === 'store') {
|
||||
loopMaterialDropToStorage(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
model.modelUuid,
|
||||
model.point.action.storageCapacity,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToStorage(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
storageUnitId: string,
|
||||
storageMaxCapacity: number,
|
||||
action: HumanAction
|
||||
) {
|
||||
const storageUnit = getStorageUnitById(storageUnitId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!storageUnit || humanCurrentLoad <= 0 || storageUnit.currentLoad >= storageMaxCapacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0 && storageUnit.currentLoad < storageMaxCapacity) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextDrop = () => {
|
||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToStorage(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
storageUnitId,
|
||||
storageMaxCapacity,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextDrop);
|
||||
}
|
||||
};
|
||||
waitForNextDrop();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
const conveyor = getConveyorById(model.modelUuid);
|
||||
if (conveyor) {
|
||||
loopMaterialDropToConveyor(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
conveyor.modelUuid,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToConveyor(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
conveyorId: string,
|
||||
action: HumanAction
|
||||
) {
|
||||
const conveyor = getConveyorById(conveyorId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!conveyor || humanCurrentLoad <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextDrop = () => {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToConveyor(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
conveyorId,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextDrop);
|
||||
}
|
||||
};
|
||||
waitForNextDrop();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||
const armBot = getArmBotById(model.modelUuid);
|
||||
if (armBot && armBot.state === 'idle' && !armBot.isActive) {
|
||||
loopMaterialDropToArmBot(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
model.modelUuid,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToArmBot(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
armBotId: string,
|
||||
action: HumanAction
|
||||
) {
|
||||
const armBot = getArmBotById(armBotId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!armBot || armBot.state !== 'idle' || armBot.isActive || humanCurrentLoad <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextTransfer = () => {
|
||||
const currentArmBot = getArmBotById(armBotId);
|
||||
if (currentArmBot && currentArmBot.state === 'idle' && !currentArmBot.isActive) {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToArmBot(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
armBotId,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
};
|
||||
waitForNextTransfer();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||
const vehicle = getVehicleById(model.modelUuid);
|
||||
if (vehicle && vehicle.state === 'idle' && !vehicle.isActive) {
|
||||
loopMaterialDropToVehicle(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
model.modelUuid,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToVehicle(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
vehicleId: string,
|
||||
action: HumanAction
|
||||
) {
|
||||
const vehicle = getVehicleById(vehicleId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!vehicle || vehicle.state !== 'idle' || vehicle.isActive || humanCurrentLoad <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextTransfer = () => {
|
||||
const currentVehicle = getVehicleById(vehicleId);
|
||||
if (currentVehicle && currentVehicle.state === 'idle' && !currentVehicle.isActive) {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToVehicle(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
vehicleId,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
};
|
||||
waitForNextTransfer();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||
const machine = getMachineById(model.modelUuid);
|
||||
if (machine && machine.state === 'idle' && !machine.isActive) {
|
||||
loopMaterialDropToMachine(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
model.modelUuid,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToMachine(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
machineId: string,
|
||||
action: HumanAction
|
||||
) {
|
||||
const machine = getMachineById(machineId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!machine || machine.state !== 'idle' || machine.isActive || humanCurrentLoad <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextTransfer = () => {
|
||||
const currentMachine = getMachineById(machineId);
|
||||
if (currentMachine && currentMachine.state === 'idle' && !currentMachine.isActive) {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToMachine(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
machineId,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
};
|
||||
waitForNextTransfer();
|
||||
}
|
||||
}
|
||||
|
||||
function handleMaterialDropByDefault(droppedMaterial: number) {
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
if (humanAsset?.animationState?.current !== 'drop') {
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
}
|
||||
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
const remainingMaterials = droppedMaterial - 1;
|
||||
decrementHumanLoad(human.modelUuid, 1);
|
||||
const material = removeLastMaterial(human.modelUuid);
|
||||
|
||||
if (material) {
|
||||
setEndTime(material.materialId, performance.now());
|
||||
removeMaterial(material.materialId);
|
||||
}
|
||||
|
||||
if (remainingMaterials > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
requestAnimationFrame(() => handleMaterialDropByDefault(remainingMaterials));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => handleMaterialDropByDefault(droppedMaterial));
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<WorkerAnimator
|
||||
path={path}
|
||||
handleCallBack={handleCallBack}
|
||||
human={human}
|
||||
reset={reset}
|
||||
startUnloadingProcess={startUnloadingProcess}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default WorkerInstance;
|
||||
@@ -1,36 +1,20 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { NavMeshQuery } from '@recast-navigation/core';
|
||||
import { useNavMesh } from '../../../../../store/builder/store';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
|
||||
import { useSceneContext } from '../../../../scene/sceneContext';
|
||||
import { useProductContext } from '../../../products/productContext';
|
||||
|
||||
import HumanAnimator from '../animator/humanAnimator';
|
||||
import MaterialAnimator from '../animator/materialAnimator';
|
||||
import AssemblerInstance from './actions/assemberInstance';
|
||||
import WorkerInstance from './actions/workerInstance';
|
||||
|
||||
function HumanInstance({ human }: { human: HumanStatus }) {
|
||||
const { navMesh } = useNavMesh();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { scene } = useThree();
|
||||
const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext();
|
||||
const { removeMaterial, setEndTime, setMaterial, setIsVisible } = materialStore();
|
||||
const { getStorageUnitById } = storageUnitStore();
|
||||
const { getArmBotById } = armBotStore();
|
||||
const { getConveyorById } = conveyorStore();
|
||||
const { getVehicleById } = vehicleStore();
|
||||
const { getMachineById } = machineStore();
|
||||
const { triggerPointActions } = useTriggerHandler();
|
||||
const { setCurrentAnimation, resetAnimation, getAssetById } = assetStore();
|
||||
const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = productStore();
|
||||
const { humanStore, productStore } = useSceneContext();
|
||||
const { getActionByUuid } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { setHumanActive, setHumanState, clearCurrentMaterials, setHumanLoad, setHumanScheduled, decrementHumanLoad, removeLastMaterial, incrementIdleTime, incrementActiveTime, resetTime, setCurrentPhase } = humanStore();
|
||||
const { incrementIdleTime, incrementActiveTime } = humanStore();
|
||||
|
||||
const [path, setPath] = useState<[number, number, number][]>([]);
|
||||
const pauseTimeRef = useRef<number | null>(null);
|
||||
const idleTimeRef = useRef<number>(0);
|
||||
const activeTimeRef = useRef<number>(0);
|
||||
const isPausedRef = useRef<boolean>(false);
|
||||
@@ -39,14 +23,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const previousTimeRef = useRef<number | null>(null);
|
||||
const animationFrameIdRef = useRef<number | null>(null);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
const processStartTimeRef = useRef<number | null>(null);
|
||||
const processTimeRef = useRef<number>(0);
|
||||
const processAnimationIdRef = useRef<number | null>(null);
|
||||
const accumulatedPausedTimeRef = useRef<number>(0);
|
||||
const lastPauseTimeRef = useRef<number | null>(null);
|
||||
const hasLoggedHalfway = useRef(false);
|
||||
const hasLoggedCompleted = useRef(false);
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
|
||||
useEffect(() => {
|
||||
isPausedRef.current = isPaused;
|
||||
@@ -56,259 +33,6 @@ function HumanInstance({ human }: { human: HumanStatus }) {
|
||||
isSpeedRef.current = speed;
|
||||
}, [speed]);
|
||||
|
||||
const computePath = useCallback((start: [number, number, number], end: [number, number, number]) => {
|
||||
try {
|
||||
const navMeshQuery = new NavMeshQuery(navMesh);
|
||||
let startPoint = new THREE.Vector3(start[0], start[1], start[2]);
|
||||
let endPoint = new THREE.Vector3(end[0], end[1], end[2]);
|
||||
const { path: segmentPath } = navMeshQuery.computePath(startPoint, endPoint);
|
||||
if (
|
||||
segmentPath.length > 0 &&
|
||||
Math.round(segmentPath[segmentPath.length - 1].x) == Math.round(endPoint.x) &&
|
||||
Math.round(segmentPath[segmentPath.length - 1].z) == Math.round(endPoint.z)
|
||||
) {
|
||||
return segmentPath?.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
|
||||
} else {
|
||||
console.log("There is no path here...Choose valid path")
|
||||
const { path: segmentPaths } = navMeshQuery.computePath(startPoint, startPoint);
|
||||
return segmentPaths.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
|
||||
}
|
||||
} catch {
|
||||
console.error("Failed to compute path");
|
||||
return [];
|
||||
}
|
||||
}, [navMesh]);
|
||||
|
||||
function humanStatus(modelId: string, status: string) {
|
||||
// console.log(`${modelId} , ${status}`);
|
||||
}
|
||||
|
||||
function reset() {
|
||||
setCurrentPhase(human.modelUuid, 'init');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanScheduled(human.modelUuid, false);
|
||||
setHumanLoad(human.modelUuid, 0);
|
||||
resetAnimation(human.modelUuid);
|
||||
setPath([]);
|
||||
isPausedRef.current = false;
|
||||
pauseTimeRef.current = 0;
|
||||
resetTime(human.modelUuid)
|
||||
activeTimeRef.current = 0
|
||||
idleTimeRef.current = 0
|
||||
previousTimeRef.current = null
|
||||
if (animationFrameIdRef.current !== null) {
|
||||
cancelAnimationFrame(animationFrameIdRef.current)
|
||||
animationFrameIdRef.current = null
|
||||
}
|
||||
if (processAnimationIdRef.current) {
|
||||
cancelAnimationFrame(processAnimationIdRef.current);
|
||||
processAnimationIdRef.current = null;
|
||||
}
|
||||
processStartTimeRef.current = null;
|
||||
processTimeRef.current = 0;
|
||||
accumulatedPausedTimeRef.current = 0;
|
||||
lastPauseTimeRef.current = null;
|
||||
hasLoggedHalfway.current = false;
|
||||
hasLoggedCompleted.current = false;
|
||||
const object = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
if (object && human) {
|
||||
object.position.set(human.position[0], human.position[1], human.position[2]);
|
||||
object.rotation.set(human.rotation[0], human.rotation[1], human.rotation[2]);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying) {
|
||||
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' && human.currentPhase === 'init') {
|
||||
|
||||
const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
if (!humanMesh) return;
|
||||
|
||||
const toPickupPath = computePath(humanMesh.position.toArray(), (action as HumanAction)?.assemblyPoint?.position || [0, 0, 0]);
|
||||
setPath(toPickupPath);
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setCurrentPhase(human.modelUuid, 'init-assembly');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Human is waiting for material in assembly');
|
||||
} else if (!human.isActive && human.state === 'idle' && human.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');
|
||||
setCurrentPhase(human.modelUuid, 'assembling');
|
||||
setHumanActive(human.modelUuid, true);
|
||||
|
||||
processStartTimeRef.current = performance.now();
|
||||
processTimeRef.current = (action as HumanAction).processTime || 0;
|
||||
accumulatedPausedTimeRef.current = 0;
|
||||
lastPauseTimeRef.current = null;
|
||||
hasLoggedHalfway.current = false;
|
||||
hasLoggedCompleted.current = false;
|
||||
|
||||
if (!processAnimationIdRef.current) {
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
||||
}
|
||||
}
|
||||
} else if (human.isActive && human.state === 'running' && human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current === 'working_standing' && humanAsset.animationState?.isCompleted) {
|
||||
if ((action as HumanAction).assemblyPoint && human.currentPhase === 'assembling') {
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setCurrentPhase(human.modelUuid, 'waiting');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setHumanScheduled(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Human is waiting for material in assembly');
|
||||
|
||||
decrementHumanLoad(human.modelUuid, 1);
|
||||
const material = removeLastMaterial(human.modelUuid);
|
||||
if (material) {
|
||||
triggerPointActions((action as HumanAction), material.materialId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
reset()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [human, 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 || !(action as HumanAction).processTime || !action) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPausedRef.current) {
|
||||
if (!lastPauseTimeRef.current) {
|
||||
lastPauseTimeRef.current = now;
|
||||
}
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
||||
return;
|
||||
} else if (lastPauseTimeRef.current) {
|
||||
accumulatedPausedTimeRef.current += now - lastPauseTimeRef.current;
|
||||
lastPauseTimeRef.current = null;
|
||||
}
|
||||
|
||||
const elapsed = (now - processStartTimeRef.current - accumulatedPausedTimeRef.current) * isSpeedRef.current;
|
||||
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, (action as HumanAction).swapMaterial || 'Default Material');
|
||||
}
|
||||
humanStatus(human.modelUuid, `🟡 Human ${human.modelUuid} reached halfway in assembly.`);
|
||||
}
|
||||
|
||||
if (elapsed >= totalProcessTimeMs && !hasLoggedCompleted.current) {
|
||||
hasLoggedCompleted.current = true;
|
||||
setCurrentAnimation(human.modelUuid, 'working_standing', true, true, true);
|
||||
if (processAnimationIdRef.current) {
|
||||
cancelAnimationFrame(processAnimationIdRef.current);
|
||||
processAnimationIdRef.current = null;
|
||||
}
|
||||
humanStatus(human.modelUuid, `✅ Human ${human.modelUuid} completed assembly process.`);
|
||||
return;
|
||||
}
|
||||
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
||||
}, [human.modelUuid, human.currentMaterials]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying) {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
if (!action || action.actionType !== 'worker' || !action.pickUpPoint || !action.dropPoint) return;
|
||||
|
||||
if (!human.isActive && human.state === 'idle' && human.currentPhase === 'init') {
|
||||
|
||||
const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
if (!humanMesh) return;
|
||||
|
||||
const toPickupPath = computePath(humanMesh.position.toArray(), action?.pickUpPoint?.position || [0, 0, 0]);
|
||||
setPath(toPickupPath);
|
||||
setCurrentPhase(human.modelUuid, 'init-pickup');
|
||||
setHumanState(human.modelUuid, 'running');
|
||||
setHumanActive(human.modelUuid, true);
|
||||
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Started from init, heading to pickup');
|
||||
return;
|
||||
} else if (!human.isActive && human.state === 'idle' && human.currentPhase === 'picking') {
|
||||
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(action.pickUpPoint.position || [0, 0, 0], action.dropPoint.position || [0, 0, 0]);
|
||||
setPath(toDrop);
|
||||
setCurrentPhase(human.modelUuid, 'pickup-drop');
|
||||
setHumanState(human.modelUuid, 'running');
|
||||
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Started from pickup point, heading to drop point');
|
||||
}
|
||||
} else if (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' && human.currentPhase === 'dropping' && human.currentLoad === 0) {
|
||||
if (action.pickUpPoint && action.dropPoint) {
|
||||
const dropToPickup = computePath(action.dropPoint.position || [0, 0, 0], action.pickUpPoint.position || [0, 0, 0]);
|
||||
setPath(dropToPickup);
|
||||
setCurrentPhase(human.modelUuid, 'drop-pickup');
|
||||
setHumanState(human.modelUuid, 'running');
|
||||
setHumanActive(human.modelUuid, true);
|
||||
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Started from dropping point, heading to pickup point');
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
reset()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [human, human.currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
|
||||
|
||||
function handleCallBack() {
|
||||
if (human.currentPhase === 'init-pickup') {
|
||||
setCurrentPhase(human.modelUuid, 'picking');
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Reached pickup point, waiting for material');
|
||||
setPath([]);
|
||||
} if (human.currentPhase === 'init-assembly') {
|
||||
setCurrentPhase(human.modelUuid, 'waiting');
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Reached assembly point, waiting for material');
|
||||
setPath([]);
|
||||
} else if (human.currentPhase === 'pickup-drop') {
|
||||
setCurrentPhase(human.modelUuid, 'dropping');
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
humanStatus(human.modelUuid, 'Reached drop point');
|
||||
setPath([]);
|
||||
} else if (human.currentPhase === 'drop-pickup') {
|
||||
setCurrentPhase(human.modelUuid, 'picking');
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setHumanScheduled(human.modelUuid, false);
|
||||
setPath([]);
|
||||
clearCurrentMaterials(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Reached pickup point again, cycle complete');
|
||||
}
|
||||
}
|
||||
|
||||
function animate(currentTime: number) {
|
||||
if (previousTimeRef.current === null) {
|
||||
previousTimeRef.current = currentTime;
|
||||
@@ -353,438 +77,15 @@ 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 ((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') {
|
||||
if (action) {
|
||||
handleMaterialDropToConveyor(model);
|
||||
}
|
||||
} else if (model.type === 'machine') {
|
||||
if (action) {
|
||||
handleMaterialDropToMachine(model);
|
||||
}
|
||||
} else if (model.type === 'roboticArm') {
|
||||
if (action) {
|
||||
handleMaterialDropToArmBot(model);
|
||||
}
|
||||
} else if (model.type === 'storageUnit') {
|
||||
if (action) {
|
||||
handleMaterialDropToStorageUnit(model);
|
||||
}
|
||||
} else if (model.type === 'vehicle') {
|
||||
if (action) {
|
||||
handleMaterialDropToVehicle(model);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const droppedMaterial = human.currentLoad;
|
||||
handleMaterialDropByDefault(droppedMaterial);
|
||||
}
|
||||
} else {
|
||||
const droppedMaterial = human.currentLoad;
|
||||
handleMaterialDropByDefault(droppedMaterial);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(startUnloadingProcess);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
if (model.point.action.actionType === 'store') {
|
||||
loopMaterialDropToStorage(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
model.modelUuid,
|
||||
model.point.action.storageCapacity,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToStorage(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
storageUnitId: string,
|
||||
storageMaxCapacity: number,
|
||||
action: HumanAction
|
||||
) {
|
||||
const storageUnit = getStorageUnitById(storageUnitId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!storageUnit || humanCurrentLoad <= 0 || storageUnit.currentLoad >= storageMaxCapacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0 && storageUnit.currentLoad < storageMaxCapacity) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextDrop = () => {
|
||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToStorage(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
storageUnitId,
|
||||
storageMaxCapacity,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextDrop);
|
||||
}
|
||||
};
|
||||
waitForNextDrop();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
const conveyor = getConveyorById(model.modelUuid);
|
||||
if (conveyor) {
|
||||
loopMaterialDropToConveyor(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
conveyor.modelUuid,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToConveyor(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
conveyorId: string,
|
||||
action: HumanAction
|
||||
) {
|
||||
const conveyor = getConveyorById(conveyorId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!conveyor || humanCurrentLoad <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextDrop = () => {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToConveyor(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
conveyorId,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextDrop);
|
||||
}
|
||||
};
|
||||
waitForNextDrop();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||
const armBot = getArmBotById(model.modelUuid);
|
||||
if (armBot && armBot.state === 'idle' && !armBot.isActive) {
|
||||
loopMaterialDropToArmBot(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
model.modelUuid,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToArmBot(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
armBotId: string,
|
||||
action: HumanAction
|
||||
) {
|
||||
const armBot = getArmBotById(armBotId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!armBot || armBot.state !== 'idle' || armBot.isActive || humanCurrentLoad <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextTransfer = () => {
|
||||
const currentArmBot = getArmBotById(armBotId);
|
||||
if (currentArmBot && currentArmBot.state === 'idle' && !currentArmBot.isActive) {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToArmBot(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
armBotId,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
};
|
||||
waitForNextTransfer();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||
const vehicle = getVehicleById(model.modelUuid);
|
||||
if (vehicle && vehicle.state === 'idle' && !vehicle.isActive) {
|
||||
loopMaterialDropToVehicle(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
model.modelUuid,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToVehicle(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
vehicleId: string,
|
||||
action: HumanAction
|
||||
) {
|
||||
const vehicle = getVehicleById(vehicleId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!vehicle || vehicle.state !== 'idle' || vehicle.isActive || humanCurrentLoad <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextTransfer = () => {
|
||||
const currentVehicle = getVehicleById(vehicleId);
|
||||
if (currentVehicle && currentVehicle.state === 'idle' && !currentVehicle.isActive) {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToVehicle(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
vehicleId,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
};
|
||||
waitForNextTransfer();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||
const machine = getMachineById(model.modelUuid);
|
||||
if (machine && machine.state === 'idle' && !machine.isActive) {
|
||||
loopMaterialDropToMachine(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
model.modelUuid,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToMachine(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
machineId: string,
|
||||
action: HumanAction
|
||||
) {
|
||||
const machine = getMachineById(machineId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!machine || machine.state !== 'idle' || machine.isActive || humanCurrentLoad <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextTransfer = () => {
|
||||
const currentMachine = getMachineById(machineId);
|
||||
if (currentMachine && currentMachine.state === 'idle' && !currentMachine.isActive) {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToMachine(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
machineId,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
};
|
||||
waitForNextTransfer();
|
||||
}
|
||||
}
|
||||
|
||||
function handleMaterialDropByDefault(droppedMaterial: number) {
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
if (humanAsset?.animationState?.current !== 'drop') {
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
}
|
||||
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
const remainingMaterials = droppedMaterial - 1;
|
||||
decrementHumanLoad(human.modelUuid, 1);
|
||||
const material = removeLastMaterial(human.modelUuid);
|
||||
|
||||
if (material) {
|
||||
setEndTime(material.materialId, performance.now());
|
||||
removeMaterial(material.materialId);
|
||||
}
|
||||
|
||||
if (remainingMaterials > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
requestAnimationFrame(() => handleMaterialDropByDefault(remainingMaterials));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => handleMaterialDropByDefault(droppedMaterial));
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<HumanAnimator
|
||||
path={path}
|
||||
handleCallBack={handleCallBack}
|
||||
human={human}
|
||||
reset={reset}
|
||||
startUnloadingProcess={startUnloadingProcess}
|
||||
/>
|
||||
{action && action.actionType === 'worker' &&
|
||||
<WorkerInstance human={human} />
|
||||
}
|
||||
{action && action.actionType === 'assembly' &&
|
||||
<AssemblerInstance human={human} />
|
||||
}
|
||||
|
||||
<MaterialAnimator human={human} />
|
||||
</>
|
||||
|
||||
@@ -325,48 +325,17 @@ export function useTriggerHandler() {
|
||||
if (model?.type === 'vehicle') {
|
||||
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
|
||||
if (human) {
|
||||
if (human.isActive === false && human.state === 'idle') {
|
||||
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
|
||||
setIsPaused(materialId, true);
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
|
||||
if (vehicle) {
|
||||
addVehicleToMonitor(vehicle.modelUuid, () => {
|
||||
addHumanToMonitor(human.modelUuid, () => {
|
||||
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, () => {
|
||||
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
|
||||
setIsPaused(materialId, true);
|
||||
handleAction(action, materialId);
|
||||
|
||||
} else {
|
||||
|
||||
// Handle current action using Event Manager
|
||||
setIsPaused(materialId, true);
|
||||
|
||||
addVehicleToMonitor(vehicle.modelUuid, () => {
|
||||
handleAction(action, materialId);
|
||||
})
|
||||
}
|
||||
}
|
||||
}, action.actionUuid)
|
||||
}, action.actionUuid)
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if (model?.type === 'transfer') {
|
||||
@@ -374,61 +343,30 @@ export function useTriggerHandler() {
|
||||
if (human) {
|
||||
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);
|
||||
|
||||
addConveyorToMonitor(conveyor.modelUuid, () => {
|
||||
addHumanToMonitor(human.modelUuid, () => {
|
||||
handleAction(action, materialId);
|
||||
}, action.actionUuid)
|
||||
}, [materialId])
|
||||
}
|
||||
}, action.actionUuid)
|
||||
const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
|
||||
if (conveyor) {
|
||||
addConveyorToMonitor(conveyor.modelUuid, () => {
|
||||
addHumanToMonitor(human.modelUuid, () => {
|
||||
setIsPaused(materialId, true);
|
||||
handleAction(action, materialId);
|
||||
}, action.actionUuid)
|
||||
}, [materialId])
|
||||
}
|
||||
}
|
||||
} else if (model?.type === 'machine') {
|
||||
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
|
||||
if (human) {
|
||||
if (human.isActive === false && human.state === 'idle') {
|
||||
const machine = getMachineById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
|
||||
if (machine) {
|
||||
if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) {
|
||||
setIsPaused(materialId, true);
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
const machine = getMachineById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
|
||||
if (machine) {
|
||||
addMachineToMonitor(machine.modelUuid, () => {
|
||||
addHumanToMonitor(human.modelUuid, () => {
|
||||
setIsPaused(materialId, true);
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
|
||||
handleAction(action, materialId);
|
||||
} else {
|
||||
|
||||
// Handle current action using Event Manager
|
||||
setIsPaused(materialId, true);
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
|
||||
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);
|
||||
handleAction(action, materialId);
|
||||
} else {
|
||||
|
||||
// Handle current action using Event Manager
|
||||
setIsPaused(materialId, true);
|
||||
|
||||
addMachineToMonitor(machine.modelUuid, () => {
|
||||
handleAction(action, materialId);
|
||||
})
|
||||
}
|
||||
}
|
||||
}, action.actionUuid);
|
||||
}, action.actionUuid);
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -465,7 +403,6 @@ export function useTriggerHandler() {
|
||||
addHumanToMonitor(human.modelUuid, () => {
|
||||
handleAction(action, materialId)
|
||||
}, action.actionUuid);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
21
app/src/types/simulationTypes.d.ts
vendored
21
app/src/types/simulationTypes.d.ts
vendored
@@ -102,6 +102,7 @@ interface HumanAction {
|
||||
pickUpPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
|
||||
dropPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
|
||||
loadCount: number;
|
||||
assemblyCount: number;
|
||||
loadCapacity: number;
|
||||
triggers: TriggerSchema[];
|
||||
}
|
||||
@@ -261,6 +262,26 @@ interface HumanStatus extends HumanEventSchema {
|
||||
};
|
||||
}
|
||||
|
||||
type HumanEventState = {
|
||||
humanId: string;
|
||||
actionQueue: {
|
||||
actionType: 'worker' | 'assembly';
|
||||
actionUuid: string;
|
||||
actionName: string;
|
||||
maxLoadCount: number;
|
||||
maxAssemblyCount: number;
|
||||
count?: number;
|
||||
isMonitored: boolean;
|
||||
isCompleted: boolean;
|
||||
callback?: () => void;
|
||||
}[];
|
||||
isCooldown: boolean;
|
||||
};
|
||||
|
||||
type HumanEventManagerState = {
|
||||
humanStates: HumanEventState[];
|
||||
};
|
||||
|
||||
// Materials
|
||||
interface MaterialSchema {
|
||||
materialId: string;
|
||||
|
||||
Reference in New Issue
Block a user