added crane interialtion with other assets

This commit is contained in:
2025-08-13 18:19:17 +05:30
parent 01f0fa7cd7
commit b898c51927
25 changed files with 1107 additions and 148 deletions

View File

@@ -17,7 +17,7 @@ import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
import { useParams } from "react-router-dom";
function HumanMechanics() {
const [activeOption, setActiveOption] = useState<"worker" | "assembly">("worker");
const [activeOption, setActiveOption] = useState<"worker" | "assembly" | "operator">("worker");
const [speed, setSpeed] = useState("0.5");
const [loadCount, setLoadCount] = useState(0);
const [assemblyCount, setAssemblyCount] = useState(0);
@@ -78,7 +78,7 @@ function HumanMechanics() {
const newCurrentAction = getActionByUuid(selectedProduct.productUuid, actionUuid);
if (newCurrentAction && (newCurrentAction.actionType === 'assembly' || newCurrentAction?.actionType === 'worker')) {
if (newCurrentAction && (newCurrentAction.actionType === 'assembly' || newCurrentAction?.actionType === 'worker' || newCurrentAction?.actionType === "operator")) {
if (!selectedAction.actionId) {
setSelectedAction(newCurrentAction.actionUuid, newCurrentAction.actionName);
}
@@ -117,7 +117,7 @@ function HumanMechanics() {
const handleSelectActionType = (actionType: string) => {
if (!selectedAction.actionId || !currentAction || !selectedPointData) return;
const updatedAction = { ...currentAction, actionType: actionType as "worker" | "assembly" };
const updatedAction = { ...currentAction, actionType: actionType as "worker" | "assembly" | "operator" };
const updatedActions = selectedPointData.actions.map(action => action.actionUuid === updatedAction.actionUuid ? updatedAction : action);
const updatedPoint = { ...selectedPointData, actions: updatedActions };
@@ -397,12 +397,12 @@ function HumanMechanics() {
<LabledDropdown
label="Action Type"
defaultOption={activeOption}
options={["worker", "assembly"]}
options={["worker", "assembly", "operator"]}
onSelect={handleSelectActionType}
disabled={false}
/>
</div>
{currentAction.actionType === 'worker' &&
{(currentAction.actionType === 'worker' || currentAction.actionType === "operator") &&
<WorkerAction
loadCapacity={{
value: loadCapacity,

View File

@@ -24,7 +24,7 @@ export function usePickAndDropHandler() {
if (!modelUuid) return;
incrementCraneLoad(modelUuid, 1);
addCurrentAction(modelUuid, action.actionUuid);
addCurrentAction(modelUuid, action.actionUuid, material.materialType, materialId);
addCurrentMaterial(modelUuid, material.materialType, material.materialId);
pickAndDropLogStatus(material.materialName, `performing pickAndDrop action`);

View File

@@ -6,7 +6,7 @@ import { useProductContext } from "../../../products/productContext";
import { useHumanEventManager } from "../../../human/eventManager/useHumanEventManager";
export function useRetrieveHandler() {
const { materialStore, armBotStore, machineStore, vehicleStore, storageUnitStore, conveyorStore, productStore, humanStore, assetStore } = useSceneContext();
const { materialStore, armBotStore, machineStore, vehicleStore, storageUnitStore, conveyorStore, craneStore, productStore, humanStore, assetStore } = useSceneContext();
const { selectedProductStore } = useProductContext();
const { addMaterial } = materialStore();
const { getModelUuidByActionUuid, getPointUuidByActionUuid, getEventByModelUuid, getActionByUuid } = productStore();
@@ -14,6 +14,7 @@ export function useRetrieveHandler() {
const { getVehicleById, incrementVehicleLoad, addCurrentMaterial } = vehicleStore();
const { getConveyorById } = conveyorStore();
const { getHumanById, incrementHumanLoad, addCurrentMaterial: addCurrentMaterialToHuman } = humanStore();
const { getCraneById, incrementCraneLoad, addCurrentMaterial: addCurrentMaterialToCrane, addCurrentAction: addCurrentActionToCrane } = craneStore();
const { getAssetById, setCurrentAnimation } = assetStore();
const { selectedProduct } = selectedProductStore();
const { getArmBotById, addCurrentAction } = armBotStore();
@@ -28,6 +29,7 @@ export function useRetrieveHandler() {
const retrievalTimeRef = useRef<Map<string, number>>(new Map());
const retrievalCountRef = useRef<Map<string, number>>(new Map());
const monitoredHumansRef = useRef<Set<string>>(new Set());
const cranePickupLockRef = useRef<Map<string, boolean>>(new Map());
const [initialDelayComplete, setInitialDelayComplete] = useState(false);
const delayTimerRef = useRef<NodeJS.Timeout | null>(null);
@@ -450,7 +452,48 @@ export function useRetrieveHandler() {
}
}
}
} else if (triggeredModel?.type === 'crane') {
const crane = getCraneById(triggeredModel.modelUuid);
const action = getActionByUuid(selectedProduct.productUuid, crane?.currentAction?.actionUuid || '');
const currentCount = retrievalCountRef.current.get(actionUuid) ?? 0;
if (!crane) return;
const hasLock = cranePickupLockRef.current.get(crane.modelUuid) || false;
if (action && action.actionType === 'pickAndDrop' && !hasLock && !crane.isCarrying && !crane.isActive && crane.currentLoad < (action?.maxPickUpCount || 0)) {
const material = getLastMaterial(storageUnit.modelUuid);
if (material) {
incrementCraneLoad(crane.modelUuid, 1);
addCurrentActionToCrane(crane.modelUuid, action.actionUuid, material.materialType, material.materialId);
addCurrentMaterialToCrane(crane.modelUuid, material.materialType, material.materialId);
cranePickupLockRef.current.set(crane.modelUuid, true);
}
} else if (crane.isCarrying && crane.currentPhase === 'pickup-drop' && hasLock) {
cranePickupLockRef.current.set(crane.modelUuid, false);
const lastMaterial = getLastMaterial(storageUnit.modelUuid);
if (lastMaterial) {
const material = createNewMaterial(
lastMaterial.materialId,
lastMaterial.materialType,
storageUnit.point.action
);
if (material) {
removeLastMaterial(storageUnit.modelUuid);
updateCurrentLoad(storageUnit.modelUuid, -1);
retrievalCountRef.current.set(actionUuid, currentCount + 1);
}
}
} else if (!action) {
const action = getActionByUuid(selectedProduct.productUuid, retrieval.action.triggers[0]?.triggeredAsset.triggeredAction?.actionUuid || '');
if (action) {
addCurrentActionToCrane(crane.modelUuid, action.actionUuid, null, null);
}
}
}
});
if (hasChanges || completedActions.length > 0) {

View File

@@ -5,10 +5,13 @@ import { useSceneContext } from '../../../scene/sceneContext';
import { useProductContext } from '../../products/productContext';
export function useCraneEventManager() {
const { craneStore, productStore, assetStore, craneEventManagerRef } = useSceneContext();
const { craneStore, productStore, assetStore, vehicleStore, armBotStore, machineStore, craneEventManagerRef } = useSceneContext();
const { getCraneById, setCurrentPhase, removeCurrentAction } = craneStore();
const { getAssetById } = assetStore();
const { getActionByUuid } = productStore();
const { getVehicleById } = vehicleStore();
const { getArmBotById } = armBotStore();
const { getMachineById } = machineStore();
const { getActionByUuid, getEventByModelUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
@@ -78,17 +81,85 @@ export function useCraneEventManager() {
if (crane.isActive || crane.state !== "idle") continue;
if (currentCraneAction.actionType === 'pickAndDrop' && crane.currentLoad < currentCraneAction.maxPickUpCount) {
if (currentAction.actionUuid !== crane.currentAction?.actionUuid) {
setCurrentPhase(crane.modelUuid, 'init');
removeCurrentAction(crane.modelUuid);
const humanAction = getActionByUuid(selectedProduct.productUuid, currentCraneAction.triggers[0].triggeredAsset?.triggeredAction?.actionUuid || '');
if (humanAction) {
const nextEvent = getEventByModelUuid(selectedProduct.productUuid, humanAction.triggers[0].triggeredAsset?.triggeredModel?.modelUuid || '')
if (nextEvent) {
if (nextEvent.type === 'transfer') {
if (currentAction.actionUuid !== crane.currentAction?.actionUuid) {
setCurrentPhase(crane.modelUuid, 'init');
removeCurrentAction(crane.modelUuid);
}
craneState.isProcessing = false;
currentAction.callback();
setTimeout(() => {
completeCurrentAction(craneState);
}, 1000);
} else if (nextEvent.type === 'vehicle') {
const vehicle = getVehicleById(nextEvent.modelUuid);
if (vehicle && !vehicle.isActive && vehicle.currentPhase === 'picking') {
if (currentAction.actionUuid !== crane.currentAction?.actionUuid) {
setCurrentPhase(crane.modelUuid, 'init');
removeCurrentAction(crane.modelUuid);
}
craneState.isProcessing = false;
currentAction.callback();
setTimeout(() => {
completeCurrentAction(craneState);
}, 1000);
}
} else if (nextEvent.type === 'roboticArm') {
const armBot = getArmBotById(nextEvent.modelUuid);
if (armBot && !armBot.isActive) {
if (currentAction.actionUuid !== crane.currentAction?.actionUuid) {
setCurrentPhase(crane.modelUuid, 'init');
removeCurrentAction(crane.modelUuid);
}
craneState.isProcessing = false;
currentAction.callback();
setTimeout(() => {
completeCurrentAction(craneState);
}, 1000);
}
} else if (nextEvent.type === 'machine') {
const machine = getMachineById(nextEvent.modelUuid);
if (machine && !machine.isActive) {
if (currentAction.actionUuid !== crane.currentAction?.actionUuid) {
setCurrentPhase(crane.modelUuid, 'init');
removeCurrentAction(crane.modelUuid);
}
craneState.isProcessing = false;
currentAction.callback();
setTimeout(() => {
completeCurrentAction(craneState);
}, 1000);
}
} else {
if (currentAction.actionUuid !== crane.currentAction?.actionUuid) {
setCurrentPhase(crane.modelUuid, 'init');
removeCurrentAction(crane.modelUuid);
}
craneState.isProcessing = false;
currentAction.callback();
setTimeout(() => {
completeCurrentAction(craneState);
}, 1000);
}
}
}
craneState.isProcessing = false;
currentAction.callback();
setTimeout(() => {
completeCurrentAction(craneState);
}, 1000);
}
}
}, 0);

View File

@@ -0,0 +1,57 @@
import { useFrame, useThree } from '@react-three/fiber';
import { useEffect, useRef, useState } from 'react';
import * as THREE from 'three';
import { MaterialModel } from '../../../materials/instances/material/materialModel';
type MaterialAnimatorProps = {
crane: CraneStatus;
};
export default function MaterialAnimator({ crane }: Readonly<MaterialAnimatorProps>) {
const materialRef = useRef<any>(null);
const { scene } = useThree();
const [isRendered, setIsRendered] = useState<boolean>(false);
useEffect(() => {
if (crane.isCarrying) {
setIsRendered(true);
} else {
setIsRendered(false);
}
}, [crane.isCarrying]);
useFrame(() => {
const craneModel = scene.getObjectByProperty('uuid', crane.modelUuid);
if (!materialRef.current || !craneModel) return;
const base = craneModel.getObjectByName('hook');
if (!base) return;
if (crane.isCarrying) {
const boneWorldPos = new THREE.Vector3();
base.getWorldPosition(boneWorldPos);
const yOffset = -0.65;
boneWorldPos.y += yOffset;
materialRef.current.position.copy(boneWorldPos);
materialRef.current.up.set(0, 1, 0);
materialRef.current.lookAt(
materialRef.current.position.clone().add(new THREE.Vector3(0, 0, 1))
);
}
});
return (
<>
{isRendered && (
<MaterialModel
materialId={crane.currentAction?.materialId ?? ''}
matRef={materialRef}
materialType={crane.currentAction?.materialType ?? 'Default material'}
/>
)}
</>
);
}

View File

@@ -3,6 +3,8 @@ import * as THREE from 'three';
import { useFrame, useThree } from '@react-three/fiber';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
import { useSceneContext } from '../../../../scene/sceneContext';
import { useProductContext } from '../../../products/productContext';
import { dragAction } from '@use-gesture/react';
function PillarJibAnimator({
crane,
@@ -20,8 +22,12 @@ function PillarJibAnimator({
onAnimationComplete: (action: string) => void;
}) {
const { scene } = useThree();
const { assetStore } = useSceneContext();
const { assetStore, productStore, materialStore } = useSceneContext();
const { getActionByUuid, getPointByUuid } = productStore();
const { resetAsset } = assetStore();
const { setIsVisible } = materialStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { isPaused } = usePauseButtonStore();
const { isPlaying } = usePlayButtonStore();
const { isReset } = useResetButtonStore();
@@ -49,7 +55,9 @@ function PillarJibAnimator({
if (crane.currentPhase === 'init-pickup') {
if (crane.currentMaterials.length > 0) {
const material = scene.getObjectByProperty('uuid', crane.currentMaterials[0].materialId);
const materials = scene.getObjectsByProperty('uuid', crane.currentMaterials[0].materialId);
console.log('materials: ', materials);
const material = materials.find((material) => material.visible === true);
if (material) {
const materialWorld = new THREE.Vector3();
material.getWorldPosition(materialWorld);
@@ -62,12 +70,34 @@ function PillarJibAnimator({
);
}
}
} else if (crane.currentPhase === 'pickup-drop') {
if (crane.currentMaterials.length > 0) {
const action = getActionByUuid(selectedProduct.productUuid, crane?.currentAction?.actionUuid || '');
const humanAction = getActionByUuid(selectedProduct.productUuid, action?.triggers[0].triggeredAsset?.triggeredAction?.actionUuid || '');
if (humanAction) {
const point = getPointByUuid(selectedProduct.productUuid, humanAction.triggers[0].triggeredAsset?.triggeredModel?.modelUuid || '', humanAction.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid || '');
const eventModel = scene.getObjectByProperty('uuid', humanAction.triggers[0].triggeredAsset?.triggeredModel?.modelUuid);
if (point && eventModel) {
const pointLocal = new THREE.Vector3(...point.position);
const pointWorld = pointLocal.clone().applyMatrix4(eventModel.matrixWorld);
const startPoint = new THREE.Vector3(hookWorld.x, hookWorld.y, hookWorld.z);
const endPoint = new THREE.Vector3(pointWorld.x, pointWorld.y + 0.5, pointWorld.z);
setIsVisible(crane.currentMaterials[0].materialId, false);
setAnimationPhase('init-hook-adjust');
setPoints([startPoint, endPoint]);
}
}
}
}
}, [crane.currentPhase])
useEffect(() => {
const model = scene.getObjectByProperty('uuid', crane.modelUuid);
if (!model) return;
if (!model || !model.userData.fieldData) return;
const base = model.getObjectByName('base');
const trolley = model.getObjectByName('trolley');
@@ -84,10 +114,10 @@ function PillarJibAnimator({
const hookWorld = new THREE.Vector3();
hook.getWorldPosition(hookWorld);
const trolleyMinOffset = -1;
const trolleyMaxOffset = 1.75;
const hookMinOffset = 0.25;
const hookMaxOffset = -1.5;
const trolleyMinOffset = model.userData.trolleyMinOffset || -1;
const trolleyMaxOffset = model.userData.trolleyMaxOffset || 1.75;
const hookMinOffset = model.userData.hookMinOffset || 0.25;
const hookMaxOffset = model.userData.hookMaxOffset || -1.5;
const distFromBase = new THREE.Vector2(trolleyWorld.x - baseWorld.x, trolleyWorld.z - baseWorld.z).length();
const innerRadius = Math.max(distFromBase + trolleyMinOffset, 0.05);
@@ -159,18 +189,27 @@ function PillarJibAnimator({
}
const { animationData } = model.userData;
const hookSpeed = 0.01 * speed;
const rotationSpeed = 0.005 * speed;
const trolleySpeed = 0.01 * speed;
const hookSpeed = (model.userData.hookSpeed || 0.01) * speed;
const rotationSpeed = (model.userData.rotationSpeed || 0.005) * speed;
const trolleySpeed = (model.userData.trolleySpeed || 0.01) * speed;
const threshold = Math.max(0.01, 0.05 / speed);
switch (animationPhase) {
case 'init-hook-adjust': {
const hookWorld = new THREE.Vector3();
hook.getWorldPosition(hookWorld);
const direction = Math.sign((clampedPoints[0].y - baseWorld.y) - hookWorld.y);
hook.position.y += direction * hookSpeed;
const targetY = clampedPoints[0].y;
const direction = Math.sign(targetY - hookWorld.y);
if (parseFloat(Math.abs(hookWorld.y - clampedPoints[0].y).toFixed(2)) < 0.05) {
hook.position.y = THREE.MathUtils.lerp(
hook.position.y,
hook.position.y + direction * 0.1,
Math.min(hookSpeed, 0.9)
);
if (Math.abs(hookWorld.y - targetY) < threshold) {
hook.position.y = targetY - baseWorld.y;
setAnimationPhase('init-rotate-base');
}
break;
@@ -182,7 +221,6 @@ function PillarJibAnimator({
baseForward.sub(baseWorld);
const currentDir = new THREE.Vector2(baseForward.x, baseForward.z).normalize();
const targetWorld = clampedPoints[0];
const targetDir = new THREE.Vector2(
targetWorld.x - baseWorld.x,
@@ -192,13 +230,16 @@ function PillarJibAnimator({
const currentAngle = Math.atan2(currentDir.y, currentDir.x);
const targetAngle = Math.atan2(targetDir.y, targetDir.x);
let angleDiff = currentAngle - targetAngle;
angleDiff = Math.atan2(Math.sin(angleDiff), Math.cos(angleDiff));
if (parseFloat(Math.abs(angleDiff).toFixed(2)) > 0.05) {
base.rotation.y += Math.sign(angleDiff) * rotationSpeed;
if (Math.abs(angleDiff) > threshold) {
base.rotation.y = THREE.MathUtils.lerp(
base.rotation.y,
base.rotation.y - angleDiff,
Math.min(rotationSpeed, 0.9)
);
} else {
base.rotation.y += angleDiff;
base.rotation.y -= angleDiff;
const localTarget = trolley.parent.worldToLocal(clampedPoints[0].clone());
animationData.targetTrolleyX = localTarget?.x;
setAnimationPhase('init-move-trolley');
@@ -208,25 +249,32 @@ function PillarJibAnimator({
case 'init-move-trolley': {
const dx = animationData.targetTrolleyX - trolley.position.x;
const direction = Math.sign(dx);
trolley.position.x += direction * trolleySpeed;
if (parseFloat(Math.abs(dx).toFixed(2)) < 0.05) {
trolley.position.x = THREE.MathUtils.lerp(
trolley.position.x,
animationData.targetTrolleyX,
Math.min(trolleySpeed, 0.9)
);
if (Math.abs(dx) < threshold) {
trolley.position.x = animationData.targetTrolleyX;
animationData.finalHookTargetY = hook.position.y;
animationData.finalHookTargetY = clampedPoints[0].y - baseWorld.y;
setAnimationPhase('init-final-hook-adjust');
}
break;
}
case 'init-final-hook-adjust': {
const dy = animationData.finalHookTargetY - hook.position.y;
const direction = Math.sign(dy);
hook.position.y += direction * hookSpeed;
const targetY = clampedPoints[0].y - baseWorld.y;
if (parseFloat(Math.abs(dy).toFixed(2)) < 0.05) {
hook.position.y = animationData.finalHookTargetY;
hook.position.y = THREE.MathUtils.lerp(
hook.position.y,
targetY,
Math.min(hookSpeed, 0.9)
);
if (Math.abs(hook.position.y - targetY) < threshold) {
hook.position.y = targetY;
model.userData.animationData = {
originalHookY: hook.position.y,

View File

@@ -1,22 +1,51 @@
import { useMemo, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import * as THREE from "three";
import { useThree } from "@react-three/fiber";
import { Box, Sphere } from "@react-three/drei";
function PillarJibHelper({
crane,
points
points,
isHelperNeeded
}: {
crane: CraneStatus,
points: [THREE.Vector3, THREE.Vector3] | null;
isHelperNeeded: boolean;
}) {
const { scene } = useThree();
const [clampedPoints, setClampedPoints] = useState<[THREE.Vector3, THREE.Vector3]>();
const [isInside, setIsInside] = useState<[boolean, boolean]>([false, false]);
const baseWorldRef = useRef<THREE.Vector3 | null>(null);
const trolleyWorldRef = useRef<THREE.Vector3 | null>(null);
const hookWorldRef = useRef<THREE.Vector3 | null>(null);
useEffect(() => {
const model = scene.getObjectByProperty("uuid", crane.modelUuid);
if (!model) return;
const base = model.getObjectByName("base");
const trolley = model.getObjectByName("trolley");
const hook = model.getObjectByName("hook");
if (!base || !trolley || !hook) return;
const bw = new THREE.Vector3();
const tw = new THREE.Vector3();
const hw = new THREE.Vector3();
base.getWorldPosition(bw);
trolley.getWorldPosition(tw);
hook.getWorldPosition(hw);
baseWorldRef.current = bw;
trolleyWorldRef.current = tw;
hookWorldRef.current = hw;
}, [scene, crane.modelUuid]);
const { geometry, position } = useMemo(() => {
const model = scene.getObjectByProperty('uuid', crane.modelUuid);
if (!model) return { geometry: null, position: null };
if (!model || !baseWorldRef.current || !trolleyWorldRef.current || !hookWorldRef.current) return { geometry: null, position: null };
const base = model.getObjectByName('base');
const trolley = model.getObjectByName('trolley');
@@ -24,19 +53,16 @@ function PillarJibHelper({
if (!base || !trolley || !hook || !points) return { geometry: null, position: null };
const baseWorld = new THREE.Vector3();
base.getWorldPosition(baseWorld);
const baseWorld = baseWorldRef.current;
const trolleyWorld = new THREE.Vector3();
trolley.getWorldPosition(trolleyWorld);
const trolleyWorld = trolleyWorldRef.current;
const hookWorld = new THREE.Vector3();
hook.getWorldPosition(hookWorld);
const hookWorld = hookWorldRef.current;
const trolleyMinOffset = -1;
const trolleyMaxOffset = 1.75;
const hookMinOffset = 0.25;
const hookMaxOffset = -1.5;
const trolleyMinOffset = model.userData.trolleyMinOffset || -1;
const trolleyMaxOffset = model.userData.trolleyMaxOffset || 1.75;
const hookMinOffset = model.userData.hookMinOffset || 0.25;
const hookMaxOffset = model.userData.hookMaxOffset || -1.5;
const distFromBase = new THREE.Vector2(trolleyWorld.x - baseWorld.x, trolleyWorld.z - baseWorld.z).length();
const outerRadius = distFromBase + trolleyMaxOffset;
@@ -100,40 +126,44 @@ function PillarJibHelper({
return (
<>
<mesh
geometry={geometry}
position={position}
rotation={[Math.PI / 2, 0, 0]}
>
<meshStandardMaterial
color={0x888888}
metalness={0.5}
roughness={0.4}
side={THREE.DoubleSide}
transparent={true}
opacity={0.3}
/>
</mesh>
{isHelperNeeded &&
<>
<mesh
geometry={geometry}
position={position}
rotation={[Math.PI / 2, 0, 0]}
>
<meshStandardMaterial
color={0x888888}
metalness={0.5}
roughness={0.4}
side={THREE.DoubleSide}
transparent={true}
opacity={0.3}
/>
</mesh>
{points && points.map((point, i) => (
<Box
key={`original-${i}`}
position={point}
args={[0.15, 0.15, 0.15]}
>
<meshStandardMaterial color="yellow" />
</Box>
))}
{points && points.map((point, i) => (
<Box
key={`original-${i}`}
position={point}
args={[0.15, 0.15, 0.15]}
>
<meshStandardMaterial color="yellow" />
</Box>
))}
{clampedPoints && clampedPoints.map((point, i) => (
<Sphere
key={`clamped-${i}`}
position={point}
args={[0.1, 16, 16]}
>
<meshStandardMaterial color={isInside[i] ? 'lightgreen' : 'orange'} />
</Sphere>
))}
{clampedPoints && clampedPoints.map((point, i) => (
<Sphere
key={`clamped-${i}`}
position={point}
args={[0.1, 16, 16]}
>
<meshStandardMaterial color={isInside[i] ? 'lightgreen' : 'orange'} />
</Sphere>
))}
</>
}
</>
);
}

View File

@@ -3,36 +3,86 @@ import * as THREE from 'three'
import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
import { useSceneContext } from '../../../../scene/sceneContext';
import { useProductContext } from '../../../products/productContext';
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
import PillarJibAnimator from '../animator/pillarJibAnimator'
import PillarJibHelper from '../helper/pillarJibHelper'
import MaterialAnimator from '../animator/materialAnimator';
function PillarJibInstance({ crane }: { crane: CraneStatus }) {
const { isPlaying } = usePlayButtonStore();
const { craneStore, productStore } = useSceneContext();
const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = productStore();
const { getCraneById, setCurrentPhase } = craneStore();
const { craneStore, productStore, humanStore, assetStore } = useSceneContext();
const { triggerPointActions } = useTriggerHandler();
const { getActionByUuid } = productStore();
const { setCurrentPhase, setCraneActive, setIsCaryying, removeCurrentAction, removeLastMaterial, decrementCraneLoad } = craneStore();
const { setCurrentPhase: setCurrentPhaseHuman, setHumanActive, setHumanState, getHumanById } = humanStore();
const { setCurrentAnimation, getAssetById } = assetStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const [animationPhase, setAnimationPhase] = useState<string>('idle');
const [points, setPoints] = useState<[THREE.Vector3, THREE.Vector3] | null>(null);
const action = getActionByUuid(selectedProduct.productUuid, crane?.currentAction?.actionUuid || '');
const actionTriggers = action?.triggers || [];
const humanId = actionTriggers?.[0]?.triggeredAsset?.triggeredModel?.modelUuid ?? null;
const humanAsset = getAssetById(humanId || '');
const humanAction = getActionByUuid(selectedProduct.productUuid, actionTriggers?.[0]?.triggeredAsset?.triggeredAction?.actionUuid ?? '');
useEffect(() => {
if (isPlaying) {
const action = getActionByUuid(selectedProduct.productUuid, crane?.currentAction?.actionUuid || '');
if (!action || action.actionType !== 'pickAndDrop') return;
const human = getHumanById(humanId || '');
if (!human || !humanAsset || !humanId || !action || action.actionType !== 'pickAndDrop') return;
if (!crane.isActive && crane.currentPhase === 'init' && crane.currentMaterials.length > 0 && action.maxPickUpCount <= crane.currentMaterials.length) {
setCurrentPhase(crane.modelUuid, 'init-pickup');
} else if (crane.currentPhase === 'picking' && crane.currentMaterials.length > 0 && action.maxPickUpCount <= crane.currentMaterials.length && !crane.isCarrying) {
if (action.triggers.length > 0) {
if (humanAsset?.animationState?.current === "working_standing" && humanAsset?.animationState?.isCompleted && humanId && humanAction && humanAction.actionType === 'operator') {
setCurrentAnimation(humanId, 'idle', true, true, true);
setIsCaryying(crane.modelUuid, true);
setCurrentPhase(crane.modelUuid, 'pickup-drop');
} else {
setCurrentPhaseHuman(humanId, 'hooking');
setHumanActive(humanId, true);
setHumanState(humanId, 'running');
setCurrentAnimation(humanId, 'working_standing', true, false, false);
}
}
} else if (crane.currentPhase === 'dropping' && crane.currentMaterials.length > 0 && action.maxPickUpCount <= crane.currentMaterials.length && crane.isCarrying && human.currentPhase === 'hooking') {
setCurrentPhaseHuman(humanId, 'loadPoint-unloadPoint');
} else if (human.state === 'running' && human.currentPhase === 'unhooking') {
if (humanAsset?.animationState?.current === "working_standing" && humanAsset?.animationState?.isCompleted) {
setCurrentPhase(crane.modelUuid, 'init');
setCraneActive(crane.modelUuid, false);
setCurrentAnimation(humanId, 'idle', true, true, true);
setCurrentPhaseHuman(humanId, 'init');
setHumanActive(humanId, false);
setHumanState(humanId, 'idle');
handleMaterialDrop();
}
}
}
}, [crane])
}, [crane, humanAsset?.animationState?.isCompleted])
const handleMaterialDrop = () => {
if (humanAction && humanAction.actionType === 'operator') {
setIsCaryying(crane.modelUuid, false);
removeCurrentAction(crane.modelUuid);
const removedMaterial = removeLastMaterial(crane.modelUuid);
decrementCraneLoad(crane.modelUuid, 1);
if (removedMaterial && humanAction.triggers[0].triggeredAsset?.triggeredAction?.actionUuid) {
triggerPointActions(humanAction, removedMaterial.materialId);
}
}
}
const handleAnimationComplete = (action: string) => {
if (action === 'starting') {
setAnimationPhase('first-hook-adjust');
} else if (action === 'picking') {
setCurrentPhase(crane.modelUuid, 'picking');
} else if (action === 'dropping') {
setCurrentPhase(crane.modelUuid, 'dropping');
}
}
@@ -49,9 +99,12 @@ function PillarJibInstance({ crane }: { crane: CraneStatus }) {
onAnimationComplete={handleAnimationComplete}
/>
<MaterialAnimator crane={crane} />
<PillarJibHelper
crane={crane}
points={points}
isHelperNeeded={false}
/>
</>

View File

@@ -25,7 +25,7 @@ export function useHumanEventManager() {
const addHumanToMonitor = (humanId: string, callback: () => void, actionUuid: string) => {
const human = getHumanById(humanId);
const action = getActionByUuid(selectedProduct.productUuid, actionUuid);
if (!human || !action || (action.actionType !== 'assembly' && action.actionType !== 'worker') || !humanEventManagerRef.current) return;
if (!human || !action || (action.actionType !== 'assembly' && action.actionType !== 'worker' && action.actionType !== 'operator') || !humanEventManagerRef.current) return;
let state = humanEventManagerRef.current.humanStates.find(h => h.humanId === humanId);
if (!state) {
@@ -36,7 +36,7 @@ export function useHumanEventManager() {
const existingAction = state.actionQueue.find(a => a.actionUuid === actionUuid);
if (existingAction) {
const currentCount = existingAction.count ?? 0;
if (existingAction.actionType === 'worker') {
if (existingAction.actionType === 'worker' || existingAction.actionType === 'operator') {
if (currentCount < existingAction.maxLoadCount) {
existingAction.callback = callback;
existingAction.isMonitored = true;
@@ -98,8 +98,8 @@ export function useHumanEventManager() {
let conditionMet = false;
if (currentAction.actionType === 'worker') {
if (action.actionType === 'worker' && human.currentLoad < currentAction.loadCapacity) {
if (currentAction.actionType === 'worker' || currentAction.actionType === 'operator') {
if ((action.actionType === 'worker' || action.actionType === 'operator') && human.currentLoad < currentAction.loadCapacity) {
conditionMet = true;
} else if (action.actionType === 'assembly') {
conditionMet = true;
@@ -107,7 +107,7 @@ export function useHumanEventManager() {
} else if (currentAction.actionType === 'assembly') {
if (action.actionType === 'assembly') {
conditionMet = true;
} else if (action.actionType === 'worker' && human.currentLoad < currentAction.loadCapacity) {
} else if ((action.actionType === 'worker' || action.actionType === 'operator') && human.currentLoad < currentAction.loadCapacity) {
conditionMet = true;
}
}
@@ -121,7 +121,7 @@ export function useHumanEventManager() {
action.callback();
action.count = (action.count ?? 0) + 1;
action.isMonitored = false;
if ((action.actionType === 'worker' && action.count >= action.maxLoadCount) ||
if (((action.actionType === 'worker' || action.actionType === 'operator') && action.count >= action.maxLoadCount) ||
(action.actionType === 'assembly' && action.count >= action.maxAssemblyCount)) {
action.isCompleted = true;
}

View File

@@ -0,0 +1,182 @@
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 WorkerAnimatorProps {
path: [number, number, number][];
handleCallBack: () => void;
reset: () => void;
human: HumanStatus;
}
function OperatorAnimator({ path, handleCallBack, human, reset }: Readonly<WorkerAnimatorProps>) {
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 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 [restRotation, setRestingRotation] = useState<boolean>(true);
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
const { scene } = useThree();
useEffect(() => {
if (!human.currentAction?.actionUuid) return;
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
if (human.currentPhase === 'init-loadPoint' && path.length > 0) {
setCurrentPath(path);
setObjectRotation((action as HumanAction).pickUpPoint?.rotation ?? null);
} else if (human.currentPhase === 'loadPoint-unloadPoint' && path.length > 0) {
setObjectRotation((action as HumanAction)?.dropPoint?.rotation ?? null);
setCurrentPath(path);
} else if (human.currentPhase === 'unloadPoint-loadPoint' && path.length > 0) {
setObjectRotation((action as HumanAction)?.pickUpPoint?.rotation ?? null);
setCurrentPath(path);
}
}, [human.currentPhase, path, objectRotation, selectedProduct, human.currentAction?.actionUuid]);
useEffect(() => {
completedRef.current = false;
}, [currentPath]);
useEffect(() => {
if (isReset || !isPlaying) {
reset();
setCurrentPath([]);
completedRef.current = false;
movingForward.current = true;
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;
movingForward.current = !movingForward.current;
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 OperatorAnimator;

View File

@@ -0,0 +1,168 @@
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 OperatorAnimator from '../../animator/operatorAnimator';
function OperatorInstance({ human }: { human: HumanStatus }) {
const { navMesh } = useNavMesh();
const { isPlaying } = usePlayButtonStore();
const { scene } = useThree();
const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, craneStore, productStore } = useSceneContext();
const { removeMaterial, setEndTime, setIsVisible } = materialStore();
const { getStorageUnitById } = storageUnitStore();
const { getArmBotById } = armBotStore();
const { getConveyorById } = conveyorStore();
const { getVehicleById } = vehicleStore();
const { getMachineById } = machineStore();
const { getCraneById } = craneStore();
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 !== 'operator' || !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-loadPoint');
setHumanState(human.modelUuid, 'running');
setHumanActive(human.modelUuid, true);
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
humanStatus(human.modelUuid, 'Started from init, heading to loadPoint');
} else if (human.isActive && human.currentPhase === 'loadPoint-unloadPoint') {
if (action.pickUpPoint && action.dropPoint && humanAsset?.animationState?.current === 'idle') {
const toDrop = computePath(action.pickUpPoint.position || [0, 0, 0], action.dropPoint.position || [0, 0, 0]);
setPath(toDrop);
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
humanStatus(human.modelUuid, 'Started from loadPoint, heading to unloadPoint');
}
} else if (human.state === 'idle' && human.currentPhase === 'unhooking') {
setHumanState(human.modelUuid, 'running');
setHumanActive(human.modelUuid, true);
setCurrentAnimation(human.modelUuid, 'working_standing', true, false, false);
}
} else {
reset()
}
}, [human, human.currentAction, human.currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
function handleCallBack() {
if (human.currentPhase === 'init-loadPoint') {
setCurrentPhase(human.modelUuid, 'waiting');
setHumanState(human.modelUuid, 'idle');
setHumanActive(human.modelUuid, false);
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
humanStatus(human.modelUuid, 'Reached loadPoint point, waiting for material');
setPath([]);
} else if (human.currentPhase === 'loadPoint-unloadPoint') {
setCurrentPhase(human.modelUuid, 'unhooking');
setHumanState(human.modelUuid, 'idle');
setHumanActive(human.modelUuid, false);
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
humanStatus(human.modelUuid, 'Reached loadPoint point, waiting for material');
setPath([]);
}
}
return (
<>
<OperatorAnimator
path={path}
handleCallBack={handleCallBack}
human={human}
reset={reset}
/>
</>
)
}
export default OperatorInstance;

View File

@@ -6,6 +6,7 @@ import { useProductContext } from '../../../products/productContext';
import MaterialAnimator from '../animator/materialAnimator';
import AssemblerInstance from './actions/assemberInstance';
import WorkerInstance from './actions/workerInstance';
import OperatorInstance from './actions/operatorInstance';
function HumanInstance({ human }: { human: HumanStatus }) {
const { isPlaying } = usePlayButtonStore();
@@ -86,6 +87,9 @@ function HumanInstance({ human }: { human: HumanStatus }) {
{action && action.actionType === 'assembly' &&
<AssemblerInstance human={human} />
}
{action && action.actionType === 'operator' &&
<OperatorInstance human={human} />
}
<MaterialAnimator human={human} />
</>

View File

@@ -183,7 +183,7 @@ function Products() {
addCrane(selectedProduct.productUuid, events);
if (events.point.actions.length > 0) {
addCurrentActionCrane(events.modelUuid, events.point.actions[0].actionUuid);
addCurrentActionCrane(events.modelUuid, events.point.actions[0].actionUuid, null, null);
}
}
});

View File

@@ -125,7 +125,8 @@ function determineExecutionMachineSequences(products: productsSchema): EventsSch
event.type === 'machine' ||
event.type === 'storageUnit' ||
event.type === 'roboticArm' ||
event.type === 'human'
event.type === 'human' ||
event.type === 'crane'
) {
pointToEventMap.set(event.point.uuid, event);
allPoints.push(event.point);

View File

@@ -17,7 +17,8 @@ export async function determineExecutionMachineSequences(products: productsSchem
event.type === 'machine' ||
event.type === 'storageUnit' ||
event.type === 'roboticArm' ||
event.type === 'human'
event.type === 'human' ||
event.type === 'crane'
) {
pointToEventMap.set(event.point.uuid, event);
allPoints.push(event.point);

View File

@@ -20,7 +20,8 @@ export function determineExecutionOrder(products: productsSchema): PointsScheme[
event.type === 'machine' ||
event.type === 'storageUnit' ||
event.type === 'roboticArm' ||
event.type === 'human'
event.type === 'human' ||
event.type === 'crane'
) {
pointMap.set(event.point.uuid, event.point);
allPoints.push(event.point);

View File

@@ -17,7 +17,8 @@ export async function determineExecutionSequences(products: productsSchema): Pro
event.type === 'machine' ||
event.type === 'storageUnit' ||
event.type === 'roboticArm' ||
event.type === 'human'
event.type === 'human' ||
event.type === 'crane'
) {
pointMap.set(event.point.uuid, event.point);
allPoints.push(event.point);

View File

@@ -92,7 +92,8 @@ function determineExecutionMachineSequences(products: productsSchema): EventsSch
event.type === 'machine' ||
event.type === 'storageUnit' ||
event.type === 'roboticArm' ||
event.type === 'human'
event.type === 'human' ||
event.type === 'crane'
) {
pointToEventMap.set(event.point.uuid, event);
allPoints.push(event.point);

View File

@@ -25,7 +25,7 @@ export function useTriggerHandler() {
const { addCraneToMonitor } = useCraneEventManager();
const { getVehicleById } = vehicleStore();
const { getHumanById, setHumanScheduled } = humanStore();
const { getCraneById, setCraneScheduled } = craneStore();
const { getCraneById, setCraneScheduled, addCurrentAction } = craneStore();
const { getMachineById, setMachineActive } = machineStore();
const { getStorageUnitById } = storageUnitStore();
const { getMaterialById, setCurrentLocation, setNextLocation, setPreviousLocation, setIsPaused, setIsVisible, setEndTime } = materialStore();
@@ -427,20 +427,21 @@ export function useTriggerHandler() {
action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid) {
const model = getEventByModelUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
if (model?.type === 'transfer') {
if (model?.type === 'human') {
const crane = getCraneById(trigger.triggeredAsset?.triggeredModel.modelUuid);
if (crane) {
setIsPaused(materialId, true);
setCraneScheduled(crane.modelUuid, true);
const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (conveyor) {
addConveyorToMonitor(conveyor.modelUuid, () => {
const human = getHumanById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (human) {
addHumanToMonitor(human.modelUuid, () => {
addCurrentAction(crane.modelUuid, action.actionUuid, null, null);
addCraneToMonitor(crane.modelUuid, () => {
setIsPaused(materialId, true);
handleAction(action, materialId);
}, action.actionUuid)
})
}, action.triggers[0]?.triggeredAsset.triggeredAction.actionUuid)
}
}
}
@@ -620,6 +621,66 @@ export function useTriggerHandler() {
}
}
}
} else if (toEvent?.type === 'crane') {
// Vehicle to Human
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialById(materialId);
if (material) {
// Handle current action of the material
handleAction(action, materialId);
if (material.next) {
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
const crane = getCraneById(trigger.triggeredAsset?.triggeredModel.modelUuid);
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: material.next.modelUuid,
pointUuid: material.next.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
});
setNextLocation(material.materialId, null);
if (action) {
if (crane) {
if (action && action.triggers.length > 0 &&
action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid &&
action.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid &&
action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid) {
const model = getEventByModelUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
if (model?.type === 'human') {
const crane = getCraneById(trigger.triggeredAsset?.triggeredModel.modelUuid);
if (crane) {
setIsPaused(materialId, true);
setCraneScheduled(crane.modelUuid, true);
const human = getHumanById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (human) {
addHumanToMonitor(human.modelUuid, () => {
addCurrentAction(crane.modelUuid, action.actionUuid, null, null);
addCraneToMonitor(crane.modelUuid, () => {
setIsPaused(materialId, true);
handleAction(action, materialId);
}, action.actionUuid)
}, action.triggers[0]?.triggeredAsset.triggeredAction.actionUuid)
}
}
}
}
}
}
}
}
}
}
} else if (fromEvent?.type === 'machine') {
if (toEvent?.type === 'transfer') {
@@ -1408,6 +1469,134 @@ export function useTriggerHandler() {
setIsPaused(material.materialId, false);
}
}
} else if (material && action.actionType === 'operator') {
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid,
pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
});
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
if (action && action.triggers.length > 0 &&
action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid &&
action.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid &&
action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid) {
const model = getEventByModelUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
if (model?.type === 'roboticArm') {
handleAction(action, material.materialId);
} else if (model?.type === 'vehicle') {
const nextAction = getActionByUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset?.triggeredAction.actionUuid);
if (action) {
handleAction(action, material.materialId);
const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: trigger.triggeredAsset?.triggeredModel.modelUuid,
pointUuid: trigger.triggeredAsset?.triggeredPoint?.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
});
setNextLocation(material.materialId, null);
if (nextAction) {
if (vehicle) {
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '',
pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid || '',
actionUuid: action.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid || '',
});
setNextLocation(material.materialId, null);
setIsVisible(materialId, false);
setIsPaused(materialId, false);
// Handle current action from vehicle
handleAction(nextAction, materialId);
} else {
// Handle current action using Event Manager
setIsPaused(materialId, true);
addVehicleToMonitor(vehicle.modelUuid, () => {
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '',
pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid || '',
actionUuid: action.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid || '',
});
setNextLocation(material.materialId, null);
setIsPaused(materialId, false);
setIsVisible(materialId, false);
handleAction(nextAction, materialId);
})
}
}
}
}
} else if (model?.type === 'transfer') {
const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
if (conveyor) {
setNextLocation(material.materialId, {
modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid,
pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid,
})
setIsPaused(material.materialId, false);
setIsVisible(material.materialId, true);
handleAction(action, material.materialId);
}
} else {
setNextLocation(material.materialId, {
modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid,
pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid,
})
handleAction(action, material.materialId);
}
} else if (action) {
setNextLocation(material.materialId, null)
handleAction(action, material.materialId);
}
}
}

View File

@@ -13,30 +13,44 @@ const MaterialAnimator = ({ agvDetail }: { agvDetail: VehicleStatus }) => {
useEffect(() => {
const loadState = agvDetail.currentLoad > 0;
setHasLoad(loadState);
if (!loadState) {
setIsAttached(false);
if (meshRef.current?.parent) {
meshRef.current.parent.remove(meshRef.current);
const agvModel = scene.getObjectByProperty("uuid", agvDetail.modelUuid) as THREE.Object3D;
if (agvModel) {
const material = agvModel.getObjectByName('Sample-Material');
if (material) {
agvModel.remove(material);
}
}
}
}, [agvDetail.currentLoad]);
useFrame(() => {
// if (agvDetail.currentMaterials.length === 0 || agvDetail.currentLoad === 0) {
// const agvModel = scene.getObjectByProperty("uuid", agvDetail.modelUuid) as THREE.Object3D;
// if (agvModel) {
// const material = agvModel.getObjectByName('Sample-Material');
// if (material) {
// agvModel.remove(material);
// }
// }
// }
if (!hasLoad || !meshRef.current || isAttached) return;
const agvModel = scene.getObjectByProperty("uuid", agvDetail.modelUuid) as THREE.Object3D;
if (agvModel && !isAttached) {
if (meshRef.current.parent) {
meshRef.current.parent.remove(meshRef.current);
const material = agvModel.getObjectByName('Sample-Material');
if (material) {
agvModel.remove(material);
}
agvModel.add(meshRef.current);
meshRef.current.position.copy(offset);
meshRef.current.rotation.set(0, 0, 0);
meshRef.current.scale.set(1, 1, 1);
setIsAttached(true);
}
});
@@ -45,6 +59,7 @@ const MaterialAnimator = ({ agvDetail }: { agvDetail: VehicleStatus }) => {
<>
{hasLoad && agvDetail.currentMaterials.length > 0 && (
<MaterialModel
name={'Sample-Material'}
matRef={meshRef}
materialId={agvDetail.currentMaterials[0].materialId || ''}
materialType={agvDetail.currentMaterials[0].materialType || 'Default material'}

View File

@@ -183,7 +183,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
return (
<>
{selectedPath === "auto" &&
<group>
<group visible={false}>
{currentPath.map((pos, i) => {
if (i < currentPath.length - 1) {
return (

View File

@@ -12,24 +12,26 @@ import MaterialAnimator from '../animator/materialAnimator';
import VehicleAnimator from '../animator/vehicleAnimator';
import { useHumanEventManager } from '../../../human/eventManager/useHumanEventManager';
import { useCraneEventManager } from '../../../crane/eventManager/useCraneEventManager';
function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) {
const { navMesh } = useNavMesh();
const { isPlaying } = usePlayButtonStore();
const { materialStore, armBotStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, productStore, assetStore } = useSceneContext();
const { materialStore, armBotStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, craneStore, productStore, assetStore } = useSceneContext();
const { removeMaterial, setEndTime, setIsVisible } = materialStore();
const { getStorageUnitById } = storageUnitStore();
const { getHumanById, addCurrentAction, addCurrentMaterial, incrementHumanLoad } = humanStore();
const { getCraneById, addCurrentAction: addCraneAction, addCurrentMaterial: addCraneMaterial, incrementCraneLoad } = craneStore();
const { getArmBotById } = armBotStore();
const { getConveyorById } = conveyorStore();
const { triggerPointActions } = useTriggerHandler();
const { setCurrentAnimation, getAssetById } = assetStore();
const { addHumanToMonitor } = useHumanEventManager();
const { addCraneToMonitor } = useCraneEventManager();
const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { vehicles, setVehicleActive, setVehicleState, setVehiclePicking, clearCurrentMaterials, setVehicleLoad, decrementVehicleLoad, removeLastMaterial, getLastMaterial, incrementIdleTime, incrementActiveTime, resetTime } = vehicleStore();
const [currentPhase, setCurrentPhase] = useState<string>('stationed');
const { vehicles, setCurrentPhase, setVehicleActive, setVehicleState, setVehiclePicking, clearCurrentMaterials, setVehicleLoad, decrementVehicleLoad, removeLastMaterial, getLastMaterial, incrementIdleTime, incrementActiveTime, resetTime } = vehicleStore();
const [path, setPath] = useState<[number, number, number][]>([]);
const pauseTimeRef = useRef<number | null>(null);
const idleTimeRef = useRef<number>(0);
@@ -80,7 +82,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
// Function to reset everything
function reset() {
setCurrentPhase('stationed');
setCurrentPhase(agvDetail.modelUuid, 'stationed');
setVehicleActive(agvDetail.modelUuid, false);
setVehiclePicking(agvDetail.modelUuid, false);
setVehicleState(agvDetail.modelUuid, 'idle');
@@ -103,20 +105,20 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
if (isPlaying && selectedPath === "auto") {
if (!agvDetail.point.action.unLoadPoint || !agvDetail.point.action.pickUpPoint) return;
if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'stationed') {
if (!agvDetail.isActive && agvDetail.state === 'idle' && agvDetail.currentPhase === 'stationed') {
const toPickupPath = computePath(
new THREE.Vector3(agvDetail?.position[0], agvDetail?.position[1], agvDetail?.position[2]),
agvDetail?.point?.action?.pickUpPoint?.position
);
setPath(toPickupPath);
setCurrentPhase('stationed-pickup');
setCurrentPhase(agvDetail.modelUuid, 'stationed-pickup');
setVehicleState(agvDetail.modelUuid, 'running');
setVehiclePicking(agvDetail.modelUuid, false);
setVehicleActive(agvDetail.modelUuid, true);
vehicleStatus(agvDetail.modelUuid, 'Started from station, heading to pickup');
return;
} else if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'picking') {
} else if (!agvDetail.isActive && agvDetail.state === 'idle' && agvDetail.currentPhase === 'picking') {
if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity && agvDetail.currentMaterials.length > 0) {
if (agvDetail.point.action.pickUpPoint && agvDetail.point.action.unLoadPoint) {
const toDrop = computePath(
@@ -124,21 +126,21 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
agvDetail.point.action.unLoadPoint.position
);
setPath(toDrop);
setCurrentPhase('pickup-drop');
setCurrentPhase(agvDetail.modelUuid, 'pickup-drop');
setVehicleState(agvDetail.modelUuid, 'running');
setVehiclePicking(agvDetail.modelUuid, false);
setVehicleActive(agvDetail.modelUuid, true);
vehicleStatus(agvDetail.modelUuid, 'Started from pickup point, heading to drop point');
}
}
} else if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'dropping' && agvDetail.currentLoad === 0) {
} else if (!agvDetail.isActive && agvDetail.state === 'idle' && agvDetail.currentPhase === 'dropping' && agvDetail.currentLoad === 0) {
if (agvDetail.point.action.pickUpPoint && agvDetail.point.action.unLoadPoint) {
const dropToPickup = computePath(
agvDetail.point.action.unLoadPoint.position,
agvDetail.point.action.pickUpPoint.position
);
setPath(dropToPickup);
setCurrentPhase('drop-pickup');
setCurrentPhase(agvDetail.modelUuid, 'drop-pickup');
setVehicleState(agvDetail.modelUuid, 'running');
setVehiclePicking(agvDetail.modelUuid, false);
setVehicleActive(agvDetail.modelUuid, true);
@@ -149,7 +151,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
else {
reset()
}
}, [vehicles, currentPhase, path, isPlaying, selectedPath]);
}, [vehicles, agvDetail.currentPhase, path, isPlaying, selectedPath]);
function animate(currentTime: number) {
if (previousTimeRef.current === null) {
@@ -198,22 +200,22 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
}, [agvDetail, isPlaying]);
function handleCallBack() {
if (currentPhase === 'stationed-pickup') {
setCurrentPhase('picking');
if (agvDetail.currentPhase === 'stationed-pickup') {
setCurrentPhase(agvDetail.modelUuid, 'picking');
setVehicleState(agvDetail.modelUuid, 'idle');
setVehiclePicking(agvDetail.modelUuid, true);
setVehicleActive(agvDetail.modelUuid, false);
vehicleStatus(agvDetail.modelUuid, 'Reached pickup point, waiting for material');
setPath([]);
} else if (currentPhase === 'pickup-drop') {
setCurrentPhase('dropping');
} else if (agvDetail.currentPhase === 'pickup-drop') {
setCurrentPhase(agvDetail.modelUuid, 'dropping');
setVehicleState(agvDetail.modelUuid, 'idle');
setVehiclePicking(agvDetail.modelUuid, false);
setVehicleActive(agvDetail.modelUuid, false);
vehicleStatus(agvDetail.modelUuid, 'Reached drop point');
setPath([]);
} else if (currentPhase === 'drop-pickup') {
setCurrentPhase('picking');
} else if (agvDetail.currentPhase === 'drop-pickup') {
setCurrentPhase(agvDetail.modelUuid, 'picking');
setVehicleState(agvDetail.modelUuid, 'idle');
setVehiclePicking(agvDetail.modelUuid, true);
setVehicleActive(agvDetail.modelUuid, false);
@@ -252,6 +254,12 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
if (action && (triggeredAction?.actionType === 'assembly' || triggeredAction?.actionType === 'worker')) {
handleMaterialDropToHuman(model, triggeredAction);
}
} else if (model.type === 'crane') {
const action = getActionByUuid(selectedProduct.productUuid, agvDetail.point.action.actionUuid);
if (action && (triggeredAction?.actionType === 'pickAndDrop')) {
handleMaterialDropToCrane(model, triggeredAction);
addCraneAction(model.modelUuid, triggeredAction.actionUuid, null, null);
}
}
} else {
const droppedMaterial = agvDetail.currentLoad;
@@ -265,6 +273,64 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
}
}
function handleMaterialDropToCrane(model: CraneEventSchema, action: CraneAction) {
if (model) {
if (action.actionType === 'pickAndDrop') {
addCraneToMonitor(model.modelUuid, () => {
loopMaterialDropToCrane(
agvDetail,
model.modelUuid,
action.actionUuid
);
}, action.actionUuid || '')
}
}
}
function loopMaterialDropToCrane(
vehicle: VehicleStatus,
craneId: string,
craneActionId: string
) {
let currentVehicleLoad = vehicle.currentLoad;
const unloadLoop = () => {
const crane = getCraneById(craneId);
const craneaction = crane?.point.actions.find((action) => action.actionUuid === craneActionId);
if (!crane || crane.currentAction?.actionUuid !== craneaction?.actionUuid) return;
if (crane.isCarrying) {
decrementVehicleLoad(vehicle.modelUuid, 1);
currentVehicleLoad -= 1;
const material = removeLastMaterial(vehicle.modelUuid);
if (material) {
setIsVisible(material.materialId, false);
}
return;
} else if (!crane.isCarrying && !crane.isActive && crane.currentLoad < (craneaction?.maxPickUpCount || 0) && craneaction?.actionType === 'pickAndDrop') {
const material = getLastMaterial(vehicle.modelUuid);
if (material) {
incrementCraneLoad(craneId, 1);
addCraneAction(craneId, craneActionId, material.materialType, material.materialId);
addCraneMaterial(craneId, material.materialType, material.materialId);
}
}
setTimeout(() => {
requestAnimationFrame(unloadLoop);
}, 500)
};
const crane = getCraneById(craneId);
const craneaction = crane?.point.actions.find((action) => action.actionUuid === craneActionId);
if (crane && crane.currentLoad < (craneaction?.maxPickUpCount || 0)) {
setTimeout(() => {
unloadLoop();
}, 500)
}
}
function handleMaterialDropToHuman(model: HumanEventSchema, action: HumanAction) {
if (model) {
@@ -576,7 +642,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
<VehicleAnimator
path={path}
handleCallBack={handleCallBack}
currentPhase={currentPhase}
currentPhase={agvDetail.currentPhase}
agvUuid={agvDetail?.modelUuid}
agvDetail={agvDetail}
reset={reset}

View File

@@ -14,11 +14,12 @@ interface CraneStore {
setCurrentPhase: (modelUuid: string, phase: string) => void;
addCurrentAction: (modelUuid: string, actionUuid: string) => void;
addCurrentAction: (modelUuid: string, actionUuid: string, materialType: string | null, materialId: string | null) => void;
removeCurrentAction: (modelUuid: string) => void;
setCraneActive: (modelUuid: string, isActive: boolean) => void;
setCraneScheduled: (modelUuid: string, isScheduled: boolean) => void;
setIsCaryying: (modelUuid: string, isCarrying: boolean) => void;
setCraneLoad: (modelUuid: string, load: number) => void;
setCraneState: (modelUuid: string, newState: CraneStatus["state"]) => void;
incrementCraneLoad: (modelUuid: string, incrementBy: number) => void;
@@ -54,6 +55,7 @@ export const createCraneStore = () => {
currentPhase: 'init',
isActive: false,
isScheduled: false,
isCarrying: false,
idleTime: 0,
activeTime: 0,
currentLoad: 0,
@@ -93,7 +95,7 @@ export const createCraneStore = () => {
});
},
addCurrentAction: (modelUuid, actionUuid) => {
addCurrentAction: (modelUuid, actionUuid, materialType, materialId) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane) {
@@ -101,7 +103,9 @@ export const createCraneStore = () => {
if (action) {
crane.currentAction = {
actionUuid: action.actionUuid,
actionName: action.actionName
actionName: action.actionName,
materialType: materialType,
materialId: materialId
};
}
}
@@ -135,6 +139,15 @@ export const createCraneStore = () => {
});
},
setIsCaryying: (modelUuid, isCarrying) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane) {
crane.isCarrying = isCarrying;
}
});
},
setCraneLoad: (modelUuid, load) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);

View File

@@ -15,6 +15,7 @@ interface VehiclesStore {
deletePathPoint: (modelUuid: string, pathKey: keyof VehicleAction["paths"], pointId: string) => VehicleAction["paths"];
clearVehicles: () => void;
setCurrentPhase: (modelUuid: string, phase: string) => void;
setVehicleActive: (modelUuid: string, isActive: boolean) => void;
setVehiclePicking: (modelUuid: string, isPicking: boolean) => void;
updateSteeringAngle: (modelUuid: string, steeringAngle: number) => void;
@@ -52,6 +53,7 @@ export const createVehicleStore = () => {
state.vehicles.push({
...event,
productUuid,
currentPhase: 'stationed',
isActive: false,
isPicking: false,
idleTime: 0,
@@ -130,6 +132,15 @@ export const createVehicleStore = () => {
});
},
setCurrentPhase: (modelUuid, phase) => {
set((state) => {
const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid);
if (vehicle) {
vehicle.currentPhase = phase;
}
});
},
setVehicleActive: (modelUuid, isActive) => {
set((state) => {
const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid);

View File

@@ -98,7 +98,7 @@ interface StorageAction {
interface HumanAction {
actionUuid: string;
actionName: string;
actionType: "worker" | "assembly";
actionType: "worker" | "assembly" | "operator";
processTime: number;
swapMaterial?: string;
assemblyPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
@@ -264,6 +264,7 @@ interface ArmBotStatus extends RoboticArmEventSchema {
interface VehicleStatus extends VehicleEventSchema {
productUuid: string;
currentPhase: string;
isActive: boolean;
isPicking: boolean;
idleTime: number;
@@ -303,6 +304,7 @@ interface CraneStatus extends CraneEventSchema {
currentPhase: string;
isActive: boolean;
isScheduled: boolean;
isCarrying: boolean;
idleTime: number;
activeTime: number;
currentLoad: number;
@@ -310,6 +312,8 @@ interface CraneStatus extends CraneEventSchema {
currentAction?: {
actionUuid: string;
actionName: string;
materialType: string | null;
materialId: string | null;
};
}
@@ -319,7 +323,7 @@ interface CraneStatus extends CraneEventSchema {
type HumanEventState = {
humanId: string;
actionQueue: {
actionType: 'worker' | 'assembly';
actionType: 'worker' | 'assembly' | 'operator';
actionUuid: string;
actionName: string;
maxLoadCount: number;