Files
Dwinzo_dev/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx

335 lines
14 KiB
TypeScript

import React, { useCallback, useEffect, useRef, useState } from 'react';
import VehicleAnimator from '../animator/vehicleAnimator';
import * as THREE from 'three';
import { NavMeshQuery } from '@recast-navigation/core';
import { useNavMesh } from '../../../../../store/builder/store';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore';
import { useStorageUnitStore } from '../../../../../store/simulation/useStorageUnitStore';
import { useMaterialStore } from '../../../../../store/simulation/useMaterialStore';
import { useProductStore } from '../../../../../store/simulation/useProductStore';
import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore';
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
import MaterialAnimator from '../animator/materialAnimator';
type Timer = {
start: number | null;
active: boolean;
};
function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) {
const { navMesh } = useNavMesh();
const { isPlaying } = usePlayButtonStore();
const { removeMaterial, setEndTime } = useMaterialStore();
const { getStorageUnitById } = useStorageUnitStore();
const { triggerPointActions } = useTriggerHandler();
const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = useProductStore();
const { selectedProduct } = useSelectedProduct();
const { vehicles, setVehicleActive, setVehicleState, setVehiclePicking, clearCurrentMaterials, setVehicleLoad, decrementVehicleLoad, removeLastMaterial } = useVehicleStore();
const [currentPhase, setCurrentPhase] = useState<string>('stationed');
const [path, setPath] = useState<[number, number, number][]>([]);
const pauseTimeRef = useRef<number | null>(null);
const isPausedRef = useRef<boolean>(false);
let startTime: number;
let fixedInterval: number;
const { speed } = useAnimationPlaySpeed();
const { isPaused } = usePauseButtonStore();
useEffect(() => {
isPausedRef.current = isPaused;
}, [isPaused]);
const computePath = useCallback(
(start: any, end: any) => {
try {
const navMeshQuery = new NavMeshQuery(navMesh);
const { path: segmentPath } = navMeshQuery.computePath(start, end);
return (
segmentPath?.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || []
);
} catch {
echo.error("Failed to compute path");
return [];
}
},
[navMesh]
);
function vehicleStatus(modelId: string, status: string) {
//
}
// Function to reset everything
function reset() {
setCurrentPhase('stationed');
setVehicleActive(agvDetail.modelUuid, false);
setVehiclePicking(agvDetail.modelUuid, false);
setVehicleState(agvDetail.modelUuid, 'idle');
setVehicleLoad(agvDetail.modelUuid, 0);
setPath([]);
startTime = 0;
isPausedRef.current = false;
pauseTimeRef.current = 0;
}
useEffect(() => {
if (isPlaying) {
if (!agvDetail.point.action.unLoadPoint || !agvDetail.point.action.pickUpPoint) return;
if (!agvDetail.isActive && agvDetail.state === 'idle' && 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');
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') {
if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity && agvDetail.currentMaterials.length > 0) {
if (agvDetail.point.action.pickUpPoint && agvDetail.point.action.unLoadPoint) {
const toDrop = computePath(
agvDetail.point.action.pickUpPoint.position,
agvDetail.point.action.unLoadPoint.position
);
setPath(toDrop);
setCurrentPhase('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) {
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');
setVehicleState(agvDetail.modelUuid, 'running');
setVehiclePicking(agvDetail.modelUuid, false);
setVehicleActive(agvDetail.modelUuid, true);
vehicleStatus(agvDetail.modelUuid, 'Started from dropping point, heading to pickup point');
}
}
} else {
reset()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [vehicles, currentPhase, path, isPlaying]);
function handleCallBack() {
if (currentPhase === 'stationed-pickup') {
setCurrentPhase('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');
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');
setVehicleState(agvDetail.modelUuid, 'idle');
setVehiclePicking(agvDetail.modelUuid, true);
setVehicleActive(agvDetail.modelUuid, false);
setPath([]);
clearCurrentMaterials(agvDetail.modelUuid)
vehicleStatus(agvDetail.modelUuid, 'Reached pickup point again, cycle complete');
}
}
function startUnloadingProcess() {
if (agvDetail.point.action.triggers.length > 0) {
const trigger = getTriggerByUuid(selectedProduct.productId, agvDetail.point.action.triggers[0].triggerUuid);
const model = getEventByModelUuid(selectedProduct.productId, trigger?.triggeredAsset?.triggeredModel?.modelUuid || '');
if (trigger && model) {
if (model.type === 'transfer') {
const action = getActionByUuid(selectedProduct.productId, agvDetail.point.action.actionUuid);
if (action) {
handleMaterialDropToConveyor(action);
}
} else if (model.type === 'machine') {
//
} else if (model.type === 'roboticArm') {
const action = getActionByUuid(selectedProduct.productId, agvDetail.point.action.actionUuid);
if (action) {
handleMaterialDropToArmBot(action);
}
} else if (model.type === 'storageUnit') {
const action = getActionByUuid(selectedProduct.productId, agvDetail.point.action.actionUuid);
if (action) {
handleMaterialDropToStorageUnit(action);
}
}
} else {
const droppedMaterial = agvDetail.currentLoad;
startTime = performance.now();
handleMaterialDropByDefault(droppedMaterial);
}
} else {
const droppedMaterial = agvDetail.currentLoad;
startTime = performance.now();
handleMaterialDropByDefault(droppedMaterial);
}
}
function handleMaterialDropToStorageUnit(action: Action) {
if (action.triggers.length > 0 && action.triggers[0].triggeredAsset?.triggeredModel.modelUuid) {
const storageUnit = getStorageUnitById(action.triggers[0].triggeredAsset?.triggeredModel.modelUuid);
if (storageUnit) {
if (storageUnit.point.action.actionType === 'store') {
handleMaterialDropToStorage(
agvDetail.modelUuid,
agvDetail.currentLoad,
agvDetail.point.action.unLoadDuration,
storageUnit.modelUuid,
storageUnit.point.action.storageCapacity,
agvDetail.point.action
);
}
}
}
}
function handleMaterialDropToStorage(
vehicleId: string,
vehicleCurrentLoad: number,
unLoadDuration: number,
storageUnitId: string,
storageMaxCapacity: number,
action: VehicleAction
) {
startTime = performance.now();
const fixedInterval = ((unLoadDuration / vehicleCurrentLoad) * (1000 / speed));
const unloadLoop = () => {
if (isPausedRef.current) {
pauseTimeRef.current ??= performance.now();
requestAnimationFrame(unloadLoop);
return;
}
if (pauseTimeRef.current) {
const pauseDuration = performance.now() - pauseTimeRef.current;
startTime += pauseDuration;
pauseTimeRef.current = null;
}
const elapsedTime = performance.now() - startTime;
const storageUnit = getStorageUnitById(storageUnitId);
if (elapsedTime >= fixedInterval) {
if (storageUnit && agvDetail &&
storageUnit.currentLoad < storageMaxCapacity &&
vehicleCurrentLoad > 0) {
decrementVehicleLoad(vehicleId, 1);
vehicleCurrentLoad -= 1;
const material = removeLastMaterial(vehicleId);
if (material) {
triggerPointActions(action, material.materialId);
}
if (vehicleCurrentLoad > 0 && storageUnit.currentLoad < storageMaxCapacity) {
startTime = performance.now();
requestAnimationFrame(unloadLoop);
}
}
} else {
requestAnimationFrame(unloadLoop);
}
};
const storageUnit = getStorageUnitById(storageUnitId);
if (storageUnit && vehicleCurrentLoad > 0 && storageUnit?.currentLoad < storageMaxCapacity) {
unloadLoop();
}
}
function handleMaterialDropToConveyor(action: Action) {
if (agvDetail.currentLoad > 1) {
//
} else if (agvDetail.currentLoad === 1 && agvDetail.currentMaterials.length === 1) {
triggerPointActions(action, agvDetail.currentMaterials[0].materialId);
decrementVehicleLoad(agvDetail.modelUuid, 1);
removeLastMaterial(agvDetail.modelUuid);
}
}
function handleMaterialDropToArmBot(action: Action) {
if (agvDetail.currentLoad > 1) {
//
} else if (agvDetail.currentLoad === 1 && agvDetail.currentMaterials.length === 1) {
triggerPointActions(action, agvDetail.currentMaterials[0].materialId);
}
}
function handleMaterialDropByDefault(droppedMaterial: number) {
if (isPausedRef.current) {
pauseTimeRef.current ??= performance.now();
requestAnimationFrame(() => handleMaterialDropByDefault(droppedMaterial));
return;
}
if (pauseTimeRef.current) {
const pauseDuration = performance.now() - pauseTimeRef.current;
startTime += pauseDuration;
pauseTimeRef.current = null;
}
const elapsedTime = performance.now() - startTime;
const unLoadDuration = agvDetail.point.action.unLoadDuration;
fixedInterval = ((unLoadDuration / agvDetail.currentLoad) * (1000 / speed));
if (elapsedTime >= fixedInterval) {
let droppedMat = droppedMaterial - 1;
decrementVehicleLoad(agvDetail.modelUuid, 1);
const material = removeLastMaterial(agvDetail.modelUuid);
if (material) {
setEndTime(material.materialId, performance.now());
removeMaterial(material.materialId);
}
if (droppedMat > 0) {
startTime = performance.now();
requestAnimationFrame(() => handleMaterialDropByDefault(droppedMat));
} else {
return;
}
} else {
requestAnimationFrame(() => handleMaterialDropByDefault(droppedMaterial));
}
}
return (
<>
<VehicleAnimator
path={path}
handleCallBack={handleCallBack}
currentPhase={currentPhase}
agvUuid={agvDetail?.modelUuid}
agvDetail={agvDetail}
reset={reset}
startUnloadingProcess={startUnloadingProcess}
/>
<MaterialAnimator agvDetail={agvDetail} />
</>
);
}
export default VehicleInstance;