v2 #80

Merged
Vishnu merged 11 commits from v2 into main 2025-05-06 13:55:10 +00:00
29 changed files with 816 additions and 404 deletions

View File

@ -28,16 +28,6 @@ function RoboticArmMechanics() {
selectedEventData.data.modelUuid, selectedEventData.data.modelUuid,
selectedEventData.selectedPoint selectedEventData.selectedPoint
) as RoboticArmPointSchema | undefined; ) as RoboticArmPointSchema | undefined;
const action = getActionByUuid(selectedProduct.productId, selectedAction.actionId) as RoboticArmPointSchema["actions"][0] | undefined;
if (action) {
if (point?.actions) {
setSelectedPointData(point);
if (point.actions.length > 0 && action) {
setActiveOption(action.actionType as "default" | "pickAndPlace");
setSelectedAction(selectedAction.actionId, selectedAction.actionName);
}
}
} else {
if (point?.actions) { if (point?.actions) {
setSelectedPointData(point); setSelectedPointData(point);
if (point.actions.length > 0) { if (point.actions.length > 0) {
@ -45,7 +35,6 @@ function RoboticArmMechanics() {
setSelectedAction(point.actions[0].actionUuid, point.actions[0].actionName); setSelectedAction(point.actions[0].actionUuid, point.actions[0].actionName);
} }
} }
}
} else { } else {
clearSelectedAction(); clearSelectedAction();
} }

View File

@ -38,6 +38,14 @@ const SimulationPlayer: React.FC = () => {
const { isReset, setReset } = useResetButtonStore(); const { isReset, setReset } = useResetButtonStore();
const { subModule } = useSubModuleStore(); const { subModule } = useSubModuleStore();
useEffect(() => {
if (isReset) {
setTimeout(()=>{
setReset(false);
},0)
}
}, [isReset])
// Button functions // Button functions
const handleReset = () => { const handleReset = () => {
setReset(true); setReset(true);

View File

@ -0,0 +1,105 @@
import { useCallback, useEffect, useRef } from "react";
import { useFrame } from "@react-three/fiber";
import { usePlayButtonStore, usePauseButtonStore, useResetButtonStore } from "../../../../../store/usePlayButtonStore";
import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore";
interface DelayInstance {
delayEndTime: number;
materialId?: string;
action: ConveyorAction;
isPaused: boolean;
remainingTime: number;
}
export function useDelayHandler() {
const { isPlaying } = usePlayButtonStore();
const { isPaused } = usePauseButtonStore();
const { isReset } = useResetButtonStore();
const { setIsPaused } = useMaterialStore();
const activeDelays = useRef<Map<string, DelayInstance>>(new Map());
const cleanupDelay = useCallback(() => {
activeDelays.current.clear();
}, []);
useEffect(() => {
if (isReset) {
cleanupDelay();
}
}, [isReset, cleanupDelay]);
const delayLogStatus = (materialUuid: string, status: string) => {
// console.log(`${materialUuid}, ${status}`);
}
useEffect(() => {
const currentTime = performance.now();
activeDelays.current.forEach((delay) => {
if (isPaused && !delay.isPaused) {
delay.remainingTime = Math.max(0, delay.delayEndTime - currentTime);
delay.isPaused = true;
} else if (!isPaused && delay.isPaused) {
delay.delayEndTime = currentTime + delay.remainingTime;
delay.isPaused = false;
delay.remainingTime = 0;
}
});
}, [isPaused]);
useFrame(() => {
if (!isPlaying || isPaused) return;
const currentTime = performance.now();
const completedDelays: string[] = [];
activeDelays.current.forEach((delay, key) => {
if (!delay.isPaused && currentTime >= delay.delayEndTime && delay.materialId) {
delayLogStatus(delay.materialId, `Delay completed}`);
setIsPaused(delay.materialId, false);
completedDelays.push(key);
}
});
completedDelays.forEach(key => {
activeDelays.current.delete(key);
});
});
const handleDelay = useCallback((action: ConveyorAction, materialId?: string) => {
if (!action || action.actionType !== 'delay' || !isPlaying || !materialId) return;
const delayMs = (action.delay || 0) * 1000;
if (delayMs <= 0) return;
const key = materialId ? `${materialId}-${action.actionUuid}` : action.actionUuid;
// If this material already has a delay, cancel it
if (activeDelays.current.has(key)) {
activeDelays.current.delete(key);
}
activeDelays.current.set(key, {
delayEndTime: performance.now() + delayMs,
materialId,
action,
isPaused: false,
remainingTime: 0
});
delayLogStatus(materialId, `Started ${delayMs * 1000}s delay`);
setIsPaused(materialId, true);
}, [isPlaying]);
useEffect(() => {
return () => {
cleanupDelay();
};
}, [cleanupDelay]);
return {
handleDelay,
cleanupDelay
};
}

View File

@ -5,6 +5,7 @@ import { useMaterialStore } from "../../../../../store/simulation/useMaterialSto
import { useProductStore } from "../../../../../store/simulation/useProductStore"; import { useProductStore } from "../../../../../store/simulation/useProductStore";
import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore";
import { usePlayButtonStore, useAnimationPlaySpeed, usePauseButtonStore, useResetButtonStore } from "../../../../../store/usePlayButtonStore"; import { usePlayButtonStore, useAnimationPlaySpeed, usePauseButtonStore, useResetButtonStore } from "../../../../../store/usePlayButtonStore";
import { useConveyorStore } from "../../../../../store/simulation/useConveyorStore";
interface SpawnInstance { interface SpawnInstance {
lastSpawnTime: number | null; lastSpawnTime: number | null;
@ -23,6 +24,7 @@ interface SpawnInstance {
export function useSpawnHandler() { export function useSpawnHandler() {
const { addMaterial } = useMaterialStore(); const { addMaterial } = useMaterialStore();
const { getConveyorById } = useConveyorStore();
const { getModelUuidByActionUuid, getPointUuidByActionUuid } = useProductStore(); const { getModelUuidByActionUuid, getPointUuidByActionUuid } = useProductStore();
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { isPaused } = usePauseButtonStore(); const { isPaused } = usePauseButtonStore();
@ -57,11 +59,12 @@ export function useSpawnHandler() {
materialType: materialType, materialType: materialType,
isActive: false, isActive: false,
isVisible: true, isVisible: true,
isPaused: false,
isRendered: true, isRendered: true,
current: { current: {
modelUuid: modelUuid, modelUuid: modelUuid,
pointUuid: pointUuid, pointUuid: pointUuid,
actionUuid: action?.actionUuid || '' actionUuid: action.actionUuid
}, },
weight: 1, weight: 1,
cost: 1 cost: 1
@ -74,7 +77,6 @@ export function useSpawnHandler() {
newMaterial.next = { newMaterial.next = {
modelUuid: action.triggers[0].triggeredAsset?.triggeredModel.modelUuid, modelUuid: action.triggers[0].triggeredAsset?.triggeredModel.modelUuid,
pointUuid: action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid, pointUuid: action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid,
actionUuid: action.triggers[0].triggeredAsset?.triggeredAction?.actionUuid
} }
} }
@ -82,6 +84,15 @@ export function useSpawnHandler() {
return newMaterial; return newMaterial;
}, [addMaterial, getModelUuidByActionUuid, getPointUuidByActionUuid, selectedProduct.productId]); }, [addMaterial, getModelUuidByActionUuid, getPointUuidByActionUuid, selectedProduct.productId]);
const getConveyorPausedState = useCallback((action: ConveyorAction) => {
const modelUuid = getModelUuidByActionUuid(selectedProduct.productId, action.actionUuid);
if (!modelUuid) return false;
const conveyor = getConveyorById(modelUuid);
return conveyor?.isPaused ?? false;
}, [getConveyorById, getModelUuidByActionUuid, selectedProduct.productId]);
useEffect(() => { useEffect(() => {
const currentTime = performance.now(); const currentTime = performance.now();

View File

@ -0,0 +1,32 @@
import { useCallback } from "react";
import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore";
import { useProductStore } from "../../../../../store/simulation/useProductStore";
import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore";
import { usePlayButtonStore } from "../../../../../store/usePlayButtonStore";
export function useSwapHandler() {
const { addMaterial, getMaterialById, setMaterial } = useMaterialStore();
const { getPointUuidByActionUuid } = useProductStore();
const { selectedProduct } = useSelectedProduct();
const { isPlaying } = usePlayButtonStore();
const swapLogStatus = (materialUuid: string, status: string) => {
// console.log(`${materialUuid}, ${status}`);
}
const handleSwap = useCallback((action: ConveyorAction, materialId?: string) => {
if (!action || action.actionType !== 'swap' || !isPlaying || !materialId) return;
const { material: newMaterialType } = action;
const material = getMaterialById(materialId);
if (!material) return;
setMaterial(material.materialId, newMaterialType);
swapLogStatus(material.materialId, `Swapped to ${newMaterialType}`);
}, [addMaterial, getMaterialById, getPointUuidByActionUuid, isPlaying, setMaterial, selectedProduct.productId]);
return {
handleSwap,
};
}

View File

@ -1,32 +1,33 @@
import { useEffect, useCallback, useRef } from "react"; import { useEffect, useCallback } from "react";
import { useSpawnHandler } from "./actionHandler/useSpawnHandler"; import { useSpawnHandler } from "./actionHandler/useSpawnHandler";
import { useSwapHandler } from "./actionHandler/useSwapHandler";
import { useDelayHandler } from "./actionHandler/useDelayHandler";
export function useConveyorActions() { export function useConveyorActions() {
const { handleSpawn, clearCurrentSpawn } = useSpawnHandler(); const { handleSpawn, clearCurrentSpawn } = useSpawnHandler();
const { handleSwap } = useSwapHandler();
const { handleDelay, cleanupDelay } = useDelayHandler();
const handleDefaultAction = useCallback((action: ConveyorAction) => { const handleDefaultAction = useCallback((action: ConveyorAction) => {
console.log('action: ', action);
console.log(`Default conveyor action ${action.actionUuid}`);
}, []); }, []);
const handleSpawnAction = useCallback((action: ConveyorAction) => { const handleSpawnAction = useCallback((action: ConveyorAction) => {
handleSpawn(action); handleSpawn(action);
}, [handleSpawn]); }, [handleSpawn]);
const handleSwapAction = useCallback((action: ConveyorAction) => { const handleSwapAction = useCallback((action: ConveyorAction, materialId?: string) => {
console.log(`Swapping to material ${action.material}`); handleSwap(action, materialId);
}, []); }, [handleSwap]);
const handleDelayAction = useCallback((action: ConveyorAction) => { const handleDelayAction = useCallback((action: ConveyorAction, materialId?: string) => {
const delayMs = (action.delay || 0) * 1000; handleDelay(action, materialId);
console.log(`Delaying for ${delayMs}ms`); }, [handleDelay]);
}, []);
const handleDespawnAction = useCallback((action: ConveyorAction) => { const handleDespawnAction = useCallback((action: ConveyorAction) => {
console.log(`Despawning material`); console.log(`Despawning material`);
}, []); }, []);
const handleConveyorAction = useCallback((action: ConveyorAction) => { const handleConveyorAction = useCallback((action: ConveyorAction, materialId?: string) => {
if (!action) return; if (!action) return;
switch (action.actionType) { switch (action.actionType) {
@ -37,10 +38,10 @@ export function useConveyorActions() {
handleSpawnAction(action); handleSpawnAction(action);
break; break;
case 'swap': case 'swap':
handleSwapAction(action); handleSwapAction(action, materialId);
break; break;
case 'delay': case 'delay':
handleDelayAction(action); handleDelayAction(action, materialId);
break; break;
case 'despawn': case 'despawn':
handleDespawnAction(action); handleDespawnAction(action);
@ -52,7 +53,8 @@ export function useConveyorActions() {
const cleanup = useCallback(() => { const cleanup = useCallback(() => {
clearCurrentSpawn(); clearCurrentSpawn();
}, [clearCurrentSpawn]); cleanupDelay();
}, [clearCurrentSpawn, cleanupDelay]);
useEffect(() => { useEffect(() => {
return () => { return () => {

View File

@ -15,13 +15,13 @@ export function useActionHandler() {
const { handleMachineAction, cleanup: cleanupMachine } = useMachineActions(); const { handleMachineAction, cleanup: cleanupMachine } = useMachineActions();
const { handleStorageAction, cleanup: cleanupStorage } = useStorageActions(); const { handleStorageAction, cleanup: cleanupStorage } = useStorageActions();
const handleAction = useCallback((action: Action) => { const handleAction = useCallback((action: Action, materialId?: string) => {
if (!action) return; if (!action) return;
try { try {
switch (action.actionType) { switch (action.actionType) {
case 'default': case 'spawn': case 'swap': case 'delay': case 'despawn': case 'default': case 'spawn': case 'swap': case 'delay': case 'despawn':
handleConveyorAction(action as ConveyorAction); handleConveyorAction(action as ConveyorAction, materialId as string);
break; break;
case 'travel': case 'travel':
handleVehicleAction(action as VehicleAction); handleVehicleAction(action as VehicleAction);

View File

@ -1,6 +1,33 @@
import React from 'react' import React, { useEffect } from 'react'
import { useMaterialStore } from '../../../../../store/simulation/useMaterialStore';
import { useConveyorStore } from '../../../../../store/simulation/useConveyorStore';
import { useResetButtonStore } from '../../../../../store/usePlayButtonStore';
function ConveyorInstance({ conveyor }: { conveyor: ConveyorStatus }) {
const { materials, getMaterialsByCurrentModelUuid } = useMaterialStore();
const { isReset } = useResetButtonStore();
const { setConveyorPaused } = useConveyorStore();
useEffect(() => {
const conveyorMaterials = getMaterialsByCurrentModelUuid(conveyor.modelUuid);
if (conveyorMaterials && conveyorMaterials?.length > 0) {
const hasPausedMaterials = conveyorMaterials.some(material => material.isPaused);
if (hasPausedMaterials) {
setConveyorPaused(conveyor.modelUuid, true);
} else {
setConveyorPaused(conveyor.modelUuid, false);
}
}
}, [materials, conveyor.modelUuid, getMaterialsByCurrentModelUuid, setConveyorPaused, isReset]);
useEffect(() => {
// console.log('conveyor: ', conveyor);
}, [conveyor])
function ConveyorInstance() {
return ( return (
<> <>
</> </>

View File

@ -1,11 +1,17 @@
import React from 'react' import React from 'react'
import ConveyorInstance from './conveyorInstance/conveyorInstance' import ConveyorInstance from './conveyorInstance/conveyorInstance'
import { useConveyorStore } from '../../../../store/simulation/useConveyorStore'
function ConveyorInstances() { function ConveyorInstances() {
const { conveyors } = useConveyorStore();
return ( return (
<> <>
<ConveyorInstance /> {conveyors.map((conveyor: ConveyorStatus) =>
<ConveyorInstance conveyor={conveyor} key={conveyor.modelUuid} />
)}
</> </>
) )

View File

@ -2,6 +2,7 @@ import React, { useEffect, useState, useRef } from 'react';
import * as THREE from 'three'; import * as THREE from 'three';
import { useFrame, useThree } from '@react-three/fiber'; import { useFrame, useThree } from '@react-three/fiber';
import { usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; import { usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
import { useConveyorStore } from '../../../../../store/simulation/useConveyorStore';
interface MaterialAnimatorProps { interface MaterialAnimatorProps {
matRef: React.RefObject<THREE.Mesh>; matRef: React.RefObject<THREE.Mesh>;
@ -19,6 +20,7 @@ function MaterialAnimator({
const { scene } = useThree(); const { scene } = useThree();
const [targetPosition, setTargetPosition] = useState<THREE.Vector3 | null>(null); const [targetPosition, setTargetPosition] = useState<THREE.Vector3 | null>(null);
const [isAnimating, setIsAnimating] = useState(false); const [isAnimating, setIsAnimating] = useState(false);
const { getConveyorById } = useConveyorStore();
const animationState = useRef({ const animationState = useRef({
startTime: 0, startTime: 0,
startPosition: new THREE.Vector3(), startPosition: new THREE.Vector3(),
@ -29,7 +31,10 @@ function MaterialAnimator({
}); });
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { isPaused } = usePauseButtonStore(); const { isPaused: isGlobalPaused } = usePauseButtonStore();
const conveyor = getConveyorById(material.current.modelUuid);
const shouldPause = isGlobalPaused || material.isPaused || conveyor?.isPaused;
const getWorldPosition = (uuid: string): THREE.Vector3 | null => { const getWorldPosition = (uuid: string): THREE.Vector3 | null => {
const obj = scene.getObjectByProperty('uuid', uuid); const obj = scene.getObjectByProperty('uuid', uuid);
@ -39,7 +44,6 @@ function MaterialAnimator({
return position; return position;
}; };
// Handle target position changes and play state
useEffect(() => { useEffect(() => {
if (!isPlaying || !material.next?.pointUuid) { if (!isPlaying || !material.next?.pointUuid) {
setIsAnimating(false); setIsAnimating(false);
@ -58,22 +62,21 @@ function MaterialAnimator({
} }
}, [material.next?.pointUuid, isPlaying]); }, [material.next?.pointUuid, isPlaying]);
// Handle pause/unpause
useEffect(() => { useEffect(() => {
if (isPaused) { if (shouldPause) {
// Pause the animation
animationState.current.isPaused = true; animationState.current.isPaused = true;
setIsAnimating(false); setIsAnimating(false);
// Record the time when paused
animationState.current.pausedTime = performance.now() - animationState.current.startTime; animationState.current.pausedTime = performance.now() - animationState.current.startTime;
} else { } else {
// Resume the animation
animationState.current.isPaused = false; animationState.current.isPaused = false;
if (isPlaying && targetPosition && !isAnimating) { if (isPlaying && targetPosition && !isAnimating) {
// Resume from where we left off
animationState.current.startTime = performance.now() - animationState.current.pausedTime; animationState.current.startTime = performance.now() - animationState.current.pausedTime;
setIsAnimating(true); setIsAnimating(true);
} }
} }
}, [isPaused]); }, [shouldPause, isPlaying]);
useFrame(() => { useFrame(() => {
if (!matRef.current || !targetPosition || !isAnimating || animationState.current.isPaused || !isPlaying) { if (!matRef.current || !targetPosition || !isAnimating || animationState.current.isPaused || !isPlaying) {
@ -81,7 +84,6 @@ function MaterialAnimator({
} }
const currentTime = performance.now(); const currentTime = performance.now();
// Calculate elapsed time since animation start, minus any paused time
const elapsed = (currentTime - animationState.current.startTime) / 1000; const elapsed = (currentTime - animationState.current.startTime) / 1000;
const progress = Math.min(1, (currentSpeed * elapsed) / animationState.current.totalDistance); const progress = Math.min(1, (currentSpeed * elapsed) / animationState.current.totalDistance);
@ -95,6 +97,14 @@ function MaterialAnimator({
matRef.current.position.copy(targetPosition); matRef.current.position.copy(targetPosition);
setIsAnimating(false); setIsAnimating(false);
onAnimationComplete?.(); onAnimationComplete?.();
animationState.current = {
startTime: 0,
startPosition: new THREE.Vector3(),
totalDistance: 0,
pausedTime: 0,
isPaused: false,
lastFrameTime: 0
};
} }
}); });

View File

@ -11,7 +11,7 @@ import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHa
function MaterialInstance({ material }: { material: MaterialSchema }) { function MaterialInstance({ material }: { material: MaterialSchema }) {
const matRef: any = useRef(); const matRef: any = useRef();
const { scene } = useThree(); const { scene } = useThree();
const { getModelUuidByPointUuid, getPointByUuid, getEventByModelUuid, getActionByUuid } = useProductStore(); const { getModelUuidByPointUuid, getPointByUuid, getEventByModelUuid, getActionByUuid, getTriggerByUuid, getActionByPointUuid } = useProductStore();
const { selectedProduct } = useSelectedProduct(); const { selectedProduct } = useSelectedProduct();
const { speed } = useAnimationPlaySpeed(); const { speed } = useAnimationPlaySpeed();
const { triggerPointActions } = useTriggerHandler(); const { triggerPointActions } = useTriggerHandler();
@ -39,12 +39,12 @@ function MaterialInstance({ material }: { material: MaterialSchema }) {
const point = getPointByUuid(selectedProduct.productId, modelUuid, material.current.pointUuid); const point = getPointByUuid(selectedProduct.productId, modelUuid, material.current.pointUuid);
if (!point) { if (!point) {
return { position: new THREE.Vector3(0, 0, 0), rotation: new THREE.Vector3(0, 0, 0), currentSpeed: 1 }; return { position: new THREE.Vector3(0, 0, 0), rotation: new THREE.Vector3(0, 0, 0), currentSpeed: currentSpeed || 1 };
} }
const position = getWorldPositionFromScene(point.uuid); const position = getWorldPositionFromScene(point.uuid);
if (position) { if (position) {
return { position: position, rotation: new THREE.Vector3(0, 0, 0), currentSpeed: 1 }; return { position: position, rotation: new THREE.Vector3(0, 0, 0), currentSpeed: currentSpeed || 1 };
} }
return { return {
@ -54,6 +54,7 @@ function MaterialInstance({ material }: { material: MaterialSchema }) {
}; };
}, [material, getPointByUuid]); }, [material, getPointByUuid]);
function getCurrentSpeed(productId: string, modelUuid: string) { function getCurrentSpeed(productId: string, modelUuid: string) {
const event = getEventByModelUuid(productId, modelUuid) const event = getEventByModelUuid(productId, modelUuid)
if (event) { if (event) {
@ -84,13 +85,45 @@ function MaterialInstance({ material }: { material: MaterialSchema }) {
useEffect(() => { useEffect(() => {
// console.log('material: ', material); // console.log('material: ', material);
if (material.current && material.next) {
// console.log('current: ', material.current.pointUuid);
// console.log('next: ', material.next.pointUuid);
}
}, [material]) }, [material])
const callTrigger = () => { const callTrigger = () => {
const action = getActionByUuid(selectedProduct.productId, material.current.actionUuid) if (!material.next) return;
const fromModel = getEventByModelUuid(selectedProduct.productId, material.next.modelUuid);
if (!fromModel) return;
const fromPoint = getPointByUuid(selectedProduct.productId, fromModel.modelUuid, material.next.pointUuid);
if (!fromPoint) return;
if (fromModel.type === 'transfer') {
const toModel = getEventByModelUuid(selectedProduct.productId, material.next.modelUuid);
if (!toModel) return;
if (toModel.type === 'transfer') {
const action = getActionByPointUuid(selectedProduct.productId, material.next.pointUuid);
if (action) { if (action) {
triggerPointActions(action); triggerPointActions(action, material.materialId);
} }
} else if (toModel?.type === 'vehicle') {
// Transfer to Vehicle
} else if (toModel?.type === 'machine') {
// Transfer to Machine
} else if (toModel?.type === 'roboticArm') {
// Transfer to Robotic Arm
} else if (toModel?.type === 'storageUnit') {
// Transfer to Storage Unit
}
} else if (fromModel.type === 'vehicle') {
} else if (fromModel.type === 'machine') {
} else if (fromModel.type === 'roboticArm') {
} else if (fromModel.type === 'storageUnit') {
}
} }
return ( return (

View File

@ -32,7 +32,7 @@ export function MaterialModel({ materialType, matRef, ...props }: ModelProps) {
<group ref={matRef} {...props} dispose={null}> <group ref={matRef} {...props} dispose={null}>
<primitive <primitive
object={cloned} object={cloned}
scale={[0.25, 0.25, 0.25]} scale={[0.4, 0.4, 0.4]}
/> />
</group> </group>
); );

View File

@ -6,7 +6,7 @@ function MaterialInstances() {
const { materials } = useMaterialStore(); const { materials } = useMaterialStore();
useEffect(() => { useEffect(() => {
console.log('materials: ', materials); // console.log('materials: ', materials);
}, [materials]) }, [materials])
return ( return (

View File

@ -7,12 +7,14 @@ import { upsertProductOrEventApi } from '../../../services/simulation/UpsertProd
import { getAllProductsApi } from '../../../services/simulation/getallProductsApi'; import { getAllProductsApi } from '../../../services/simulation/getallProductsApi';
import { useVehicleStore } from '../../../store/simulation/useVehicleStore'; import { useVehicleStore } from '../../../store/simulation/useVehicleStore';
import { useArmBotStore } from '../../../store/simulation/useArmBotStore'; import { useArmBotStore } from '../../../store/simulation/useArmBotStore';
import { useConveyorStore } from '../../../store/simulation/useConveyorStore';
function Products() { function Products() {
const { products, getProductById, addProduct, setProducts } = useProductStore(); const { products, getProductById, addProduct, setProducts } = useProductStore();
const { selectedProduct, setSelectedProduct } = useSelectedProduct(); const { selectedProduct, setSelectedProduct } = useSelectedProduct();
const { addVehicle, clearvehicles } = useVehicleStore(); const { addVehicle, clearvehicles } = useVehicleStore();
const { addArmBot, clearArmBots } = useArmBotStore(); const { addArmBot, clearArmBots } = useArmBotStore();
const { addConveyor, clearConveyors } = useConveyorStore();
useEffect(() => { useEffect(() => {
const email = localStorage.getItem('email') const email = localStorage.getItem('email')
@ -59,6 +61,20 @@ function Products() {
} }
}, [selectedProduct, products]); }, [selectedProduct, products]);
useEffect(() => {
if (selectedProduct.productId) {
const product = getProductById(selectedProduct.productId);
if (product) {
clearConveyors();
product.eventDatas.forEach(events => {
if (events.type === 'transfer') {
addConveyor(selectedProduct.productId, events);
}
});
}
}
}, [selectedProduct, products]);
return ( return (
<> <>

View File

@ -1,17 +1,16 @@
import { useFrame } from '@react-three/fiber'; import { useFrame } from '@react-three/fiber';
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import * as THREE from 'three'; import * as THREE from 'three';
import { useLogger } from '../../../../../components/ui/log/LoggerContext'; import { MaterialModel } from '../../../materials/instances/material/materialModel';
type MaterialAnimatorProps = { type MaterialAnimatorProps = {
ikSolver: any; ikSolver: any;
armBot: any; armBot: ArmBotStatus;
currentPhase: string; currentPhase: string;
}; };
export default function MaterialAnimator({ ikSolver, armBot, currentPhase }: MaterialAnimatorProps) { export default function MaterialAnimator({ ikSolver, armBot, currentPhase }: MaterialAnimatorProps) {
const sphereRef = useRef<THREE.Mesh>(null); const sphereRef = useRef<any>(null);
const boneWorldPosition = new THREE.Vector3();
const [isRendered, setIsRendered] = useState<boolean>(false); const [isRendered, setIsRendered] = useState<boolean>(false);
useEffect(() => { useEffect(() => {
@ -41,7 +40,8 @@ export default function MaterialAnimator({ ikSolver, armBot, currentPhase }: Mat
const direction = new THREE.Vector3(); const direction = new THREE.Vector3();
direction.subVectors(boneWorldPos, boneTargetWorldPos).normalize(); direction.subVectors(boneWorldPos, boneTargetWorldPos).normalize();
const downwardDirection = direction.clone().negate(); const downwardDirection = direction.clone().negate();
const adjustedPosition = boneWorldPos.clone().addScaledVector(downwardDirection, -0.25);
const adjustedPosition = boneWorldPos.clone().addScaledVector(downwardDirection, -0.01);
//set position //set position
sphereRef.current.position.copy(adjustedPosition); sphereRef.current.position.copy(adjustedPosition);
@ -56,10 +56,10 @@ export default function MaterialAnimator({ ikSolver, armBot, currentPhase }: Mat
return ( return (
<> <>
{isRendered && ( {isRendered && (
<mesh ref={sphereRef}> <MaterialModel
<boxGeometry args={[0.5, 0.5, 0.5]} /> matRef={sphereRef}
<meshStandardMaterial color="yellow" /> materialType={armBot.currentAction?.materialType || 'Default material'}
</mesh> />
)} )}
</> </>
); );

View File

@ -1,39 +1,34 @@
import React, { useEffect, useMemo, useRef, useState } from 'react'; import React, { useEffect, useMemo, useRef, useState } from 'react';
import { useFrame } from '@react-three/fiber'; import { useFrame } from '@react-three/fiber';
import * as THREE from 'three'; import * as THREE from 'three';
import { Line } from '@react-three/drei'; import { Line, Text } from '@react-three/drei';
import { import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
useAnimationPlaySpeed,
usePauseButtonStore, type PointWithDegree = {
usePlayButtonStore, position: [number, number, number];
useResetButtonStore degree: number;
} from '../../../../../store/usePlayButtonStore'; };
function RoboticArmAnimator({ HandleCallback, restPosition, ikSolver, targetBone, armBot, path }: any) {
function RoboticArmAnimator({
HandleCallback,
restPosition,
ikSolver,
targetBone,
armBot,
path
}: any) {
const progressRef = useRef(0); const progressRef = useRef(0);
const curveRef = useRef<THREE.Vector3[] | null>(null); const curveRef = useRef<THREE.Vector3[] | null>(null);
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
const [circlePoints, setCirclePoints] = useState<[number, number, number][]>([]);
const [customCurvePoints, setCustomCurvePoints] = useState<THREE.Vector3[] | null>(null);
let curveHeight = 1.75
const totalDistanceRef = useRef(0); const totalDistanceRef = useRef(0);
const startTimeRef = useRef<number | null>(null); const startTimeRef = useRef<number | null>(null);
const segmentDistancesRef = useRef<number[]>([]); const segmentDistancesRef = useRef<number[]>([]);
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
const [circlePoints, setCirclePoints] = useState<[number, number, number][]>([]);
const [circlePointsWithDegrees, setCirclePointsWithDegrees] = useState<PointWithDegree[]>([]);
const [customCurvePoints, setCustomCurvePoints] = useState<THREE.Vector3[] | null>(null);
let curveHeight = 1.75
const CIRCLE_RADIUS = 1.6
// Zustand stores // Zustand stores
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { isPaused } = usePauseButtonStore(); const { isPaused } = usePauseButtonStore();
const { isReset } = useResetButtonStore(); const { isReset } = useResetButtonStore();
const { speed } = useAnimationPlaySpeed(); const { speed } = useAnimationPlaySpeed();
const CIRCLE_RADIUS = 1.6
// Update path state whenever `path` prop changes // Update path state whenever `path` prop changes
useEffect(() => { useEffect(() => {
setCurrentPath(path); setCurrentPath(path);
@ -47,7 +42,7 @@ function RoboticArmAnimator({
//Handle Reset Animation //Handle Reset Animation
useEffect(() => { useEffect(() => {
if (isReset) { if (isReset || !isPlaying) {
progressRef.current = 0; progressRef.current = 0;
curveRef.current = null; curveRef.current = null;
setCurrentPath([]); setCurrentPath([]);
@ -76,6 +71,29 @@ function RoboticArmAnimator({
} }
return points; return points;
} }
//Generate CirclePoints with Angle
function generateRingPointsWithDegrees(radius: number, segments: number, initialRotation: [number, number, number]) {
const points: { position: [number, number, number]; degree: number }[] = [];
for (let i = 0; i < segments; i++) {
const angleRadians = (i / segments) * Math.PI * 2;
const x = Math.cos(angleRadians) * radius;
const z = Math.sin(angleRadians) * radius;
const degree = (angleRadians * 180) / Math.PI; // Convert radians to degrees
points.push({
position: [x, 1.5, z],
degree,
});
}
return points;
}
// Handle circle points based on armBot position
useEffect(() => {
const points = generateRingPointsWithDegrees(CIRCLE_RADIUS, 64, armBot.rotation);
setCirclePointsWithDegrees(points)
}, [armBot.rotation]);
// Function for find nearest Circlepoints Index // Function for find nearest Circlepoints Index
const findNearestIndex = (nearestPoint: [number, number, number], points: [number, number, number][], epsilon = 1e-6) => { const findNearestIndex = (nearestPoint: [number, number, number], points: [number, number, number][], epsilon = 1e-6) => {
for (let i = 0; i < points.length; i++) { for (let i = 0; i < points.length; i++) {
@ -100,6 +118,30 @@ function RoboticArmAnimator({
}, circlePoints[0]); }, circlePoints[0]);
}; };
// Helper function to collect points and check forbidden degrees
const collectArcPoints = (startIdx: number, endIdx: number, clockwise: boolean) => {
const totalSegments = 64;
const arcPoints: [number, number, number][] = [];
let i = startIdx;
while (i !== (endIdx + (clockwise ? 1 : -1) + totalSegments) % totalSegments) {
const { degree, position } = circlePointsWithDegrees[i];
// Skip over
arcPoints.push(position);
i = (i + (clockwise ? 1 : -1) + totalSegments) % totalSegments;
}
return arcPoints;
};
//Range to restrict angle
const hasForbiddenDegrees = (arc: [number, number, number][]) => {
return arc.some(p => {
const idx = findNearestIndex(p, circlePoints);
const degree = circlePointsWithDegrees[idx]?.degree || 0;
return degree >= 271 && degree <= 300; // Forbidden range: 271° to 300°
});
};
// Handle nearest points and final path (including arc points) // Handle nearest points and final path (including arc points)
useEffect(() => { useEffect(() => {
if (circlePoints.length > 0 && currentPath.length > 0) { if (circlePoints.length > 0 && currentPath.length > 0) {
@ -116,31 +158,23 @@ function RoboticArmAnimator({
const indexOfNearestStart = findNearestIndex(nearestToStart, circlePoints); const indexOfNearestStart = findNearestIndex(nearestToStart, circlePoints);
const indexOfNearestEnd = findNearestIndex(nearestToEnd, circlePoints); const indexOfNearestEnd = findNearestIndex(nearestToEnd, circlePoints);
const clockwiseDistance = (indexOfNearestEnd - indexOfNearestStart + 64) % 64; const totalSegments = 64;
const counterClockwiseDistance = (indexOfNearestStart - indexOfNearestEnd + 64) % 64; const clockwiseDistance = (indexOfNearestEnd - indexOfNearestStart + totalSegments) % totalSegments;
const clockwiseIsShorter = clockwiseDistance <= counterClockwiseDistance; const counterClockwiseDistance = (indexOfNearestStart - indexOfNearestEnd + totalSegments) % totalSegments;
// Try both directions
const arcClockwise = collectArcPoints(indexOfNearestStart, indexOfNearestEnd, true);
const arcCounterClockwise = collectArcPoints(indexOfNearestStart, indexOfNearestEnd, false);
const clockwiseForbidden = hasForbiddenDegrees(arcClockwise);
const counterClockwiseForbidden = hasForbiddenDegrees(arcCounterClockwise);
let arcPoints: [number, number, number][] = []; let arcPoints: [number, number, number][] = [];
if (clockwiseIsShorter) { if (!clockwiseForbidden && (clockwiseDistance <= counterClockwiseDistance || counterClockwiseForbidden)) {
if (indexOfNearestStart <= indexOfNearestEnd) { arcPoints = arcClockwise;
arcPoints = circlePoints.slice(indexOfNearestStart, indexOfNearestEnd + 1);
} else { } else {
arcPoints = [ arcPoints = arcCounterClockwise;
...circlePoints.slice(indexOfNearestStart, 64),
...circlePoints.slice(0, indexOfNearestEnd + 1)
];
}
} else {
if (indexOfNearestStart >= indexOfNearestEnd) {
for (let i = indexOfNearestStart; i !== (indexOfNearestEnd - 1 + 64) % 64; i = (i - 1 + 64) % 64) {
arcPoints.push(circlePoints[i]);
}
} else {
for (let i = indexOfNearestStart; i !== (indexOfNearestEnd - 1 + 64) % 64; i = (i - 1 + 64) % 64) {
arcPoints.push(circlePoints[i]);
}
}
} }
const pathVectors = [ const pathVectors = [
@ -153,9 +187,7 @@ function RoboticArmAnimator({
new THREE.Vector3(end[0], end[1], end[2]) new THREE.Vector3(end[0], end[1], end[2])
]; ];
const pathSegments: [THREE.Vector3, THREE.Vector3][] = []; const pathSegments: [THREE.Vector3, THREE.Vector3][] = [];
for (let i = 0; i < pathVectors.length - 1; i++) { for (let i = 0; i < pathVectors.length - 1; i++) {
pathSegments.push([pathVectors[i], pathVectors[i + 1]]); pathSegments.push([pathVectors[i], pathVectors[i + 1]]);
} }
@ -165,13 +197,7 @@ function RoboticArmAnimator({
const totalDistance = segmentDistances.reduce((sum, d) => sum + d, 0); const totalDistance = segmentDistances.reduce((sum, d) => sum + d, 0);
totalDistanceRef.current = totalDistance; totalDistanceRef.current = totalDistance;
const movementSpeed = speed * armBot.speed;
const totalMoveTime = totalDistance / movementSpeed;
const segmentTimes = segmentDistances.map(distance => (distance / totalDistance) * totalMoveTime);
setCustomCurvePoints(pathVectors); setCustomCurvePoints(pathVectors);
} }
}, [circlePoints, currentPath]); }, [circlePoints, currentPath]);
@ -182,7 +208,6 @@ function RoboticArmAnimator({
const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone);
if (!bone) return; if (!bone) return;
if (isPlaying) { if (isPlaying) {
if (isReset) { if (isReset) {
bone.position.copy(restPosition); bone.position.copy(restPosition);
@ -227,12 +252,17 @@ function RoboticArmAnimator({
ikSolver.update(); ikSolver.update();
} }
} else if (!isPlaying && currentPath.length === 0) { } else if (!isPlaying && currentPath.length === 0) {
progressRef.current = 0;
startTimeRef.current = null;
setCurrentPath([]);
setCustomCurvePoints([]);
bone.position.copy(restPosition); bone.position.copy(restPosition);
} }
ikSolver.update(); ikSolver.update();
}); });
//Helper to Visible the Circle and Curve
return ( return (
<> <>
{customCurvePoints && customCurvePoints?.length >= 2 && currentPath && isPlaying && ( {customCurvePoints && customCurvePoints?.length >= 2 && currentPath && isPlaying && (
@ -245,10 +275,18 @@ function RoboticArmAnimator({
/> />
</mesh> </mesh>
)} )}
<mesh position={[armBot.position[0], armBot.position[1] + 1.5, armBot.position[2]]} rotation={[-Math.PI / 2, 0, 0]}> <group
position={[armBot.position[0], armBot.position[1] + 1.5, armBot.position[2]]}
rotation={[armBot.rotation[0], armBot.rotation[1], armBot.rotation[2]]}
>
{/* Green ring */}
<mesh rotation={[-Math.PI / 2, 0, 0]}>
<ringGeometry args={[CIRCLE_RADIUS, CIRCLE_RADIUS + 0.02, 64]} /> <ringGeometry args={[CIRCLE_RADIUS, CIRCLE_RADIUS + 0.02, 64]} />
<meshBasicMaterial color="green" side={THREE.DoubleSide} /> <meshBasicMaterial color="green" side={THREE.DoubleSide} />
</mesh> </mesh>
</group>
</> </>
); );
} }

View File

@ -5,7 +5,6 @@ import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '..
import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore'; import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore';
import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_1.glb"; import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_1.glb";
import { useThree } from "@react-three/fiber"; import { useThree } from "@react-three/fiber";
import useModuleStore from '../../../../../store/useModuleStore';
import * as THREE from "three"; import * as THREE from "three";
import MaterialAnimator from '../animator/materialAnimator'; import MaterialAnimator from '../animator/materialAnimator';
@ -24,16 +23,15 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
let startTime: number; let startTime: number;
//zustand //zustand
const { addCurrentAction, setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore(); const { addCurrentAction, setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore();
const { activeModule } = useModuleStore();
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { isReset, setReset } = useResetButtonStore(); const { isReset } = useResetButtonStore();
const { isPaused } = usePauseButtonStore(); const { isPaused } = usePauseButtonStore();
function firstFrame() { function firstFrame() {
startTime = performance.now(); startTime = performance.now();
step(); step();
} }
function step() { function step() {
if (isPausedRef.current) { if (isPausedRef.current) {
if (!pauseTimeRef.current) { if (!pauseTimeRef.current) {
@ -87,6 +85,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
logStatus(armBot.modelUuid, "Moving armBot from end point to rest position.") logStatus(armBot.modelUuid, "Moving armBot from end point to rest position.")
} }
} }
useEffect(() => { useEffect(() => {
isPausedRef.current = isPaused; isPausedRef.current = isPaused;
}, [isPaused]); }, [isPaused]);
@ -98,6 +97,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
setArmBotState(armBot.modelUuid, "idle") setArmBotState(armBot.modelUuid, "idle")
setCurrentPhase("init"); setCurrentPhase("init");
setPath([]) setPath([])
setIkSolver(null);
removeCurrentAction(armBot.modelUuid) removeCurrentAction(armBot.modelUuid)
isPausedRef.current = false isPausedRef.current = false
pauseTimeRef.current = null pauseTimeRef.current = null
@ -117,17 +117,17 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
useEffect(() => { useEffect(() => {
const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid); const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid);
if (targetMesh) { if (targetMesh) {
targetMesh.visible = activeModule !== "simulation" targetMesh.visible = (!isPlaying)
} }
const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone); const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone);
if (isPlaying) { if (!isReset && isPlaying) {
//Moving armBot from initial point to rest position. //Moving armBot from initial point to rest position.
if (!armBot?.isActive && armBot?.state == "idle" && currentPhase == "init") { if (!armBot?.isActive && armBot?.state == "idle" && currentPhase == "init") {
if (targetBones) {
setArmBotActive(armBot.modelUuid, true) setArmBotActive(armBot.modelUuid, true)
setArmBotState(armBot.modelUuid, "running") setArmBotState(armBot.modelUuid, "running")
setCurrentPhase("init-to-rest"); setCurrentPhase("init-to-rest");
if (targetBones) { let curve = createCurveBetweenTwoPoints(targetBones.position, restPosition)
let curve = createCurveBetweenTwoPoints(targetBones.position, targetBones.position)
if (curve) { if (curve) {
setPath(curve.points.map(point => [point.x, point.y, point.z])); setPath(curve.points.map(point => [point.x, point.y, point.z]));
} }
@ -138,13 +138,13 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) { else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) {
logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction") logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction")
const timeoutId = setTimeout(() => { const timeoutId = setTimeout(() => {
addCurrentAction(armBot.modelUuid, armBot.point.actions[0].actionUuid); addCurrentAction(armBot.modelUuid, armBot.point.actions[0].actionUuid, 'Material 2');
}, 3000); }, 3000);
return () => clearTimeout(timeoutId); return () => clearTimeout(timeoutId);
} }
//Moving to pickup point
else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) { else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) {
if (armBot.currentAction) { if (armBot.currentAction) {
setArmBotActive(armBot.modelUuid, true); setArmBotActive(armBot.modelUuid, true);
setArmBotState(armBot.modelUuid, "running"); setArmBotState(armBot.modelUuid, "running");
setCurrentPhase("rest-to-start"); setCurrentPhase("rest-to-start");
@ -158,15 +158,29 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
} }
logStatus(armBot.modelUuid, "Moving armBot from rest point to start position.") logStatus(armBot.modelUuid, "Moving armBot from rest point to start position.")
} }
// Moving to Pick to Drop position
else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "picking" && armBot.currentAction) { else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "picking" && armBot.currentAction) {
requestAnimationFrame(firstFrame); requestAnimationFrame(firstFrame);
} }
//Moving to drop point to restPosition
else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "dropping" && armBot.currentAction) { else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "dropping" && armBot.currentAction) {
requestAnimationFrame(firstFrame); requestAnimationFrame(firstFrame);
} }
} else {
logStatus(armBot.modelUuid, "Simulation Play Exited")
setArmBotActive(armBot.modelUuid, false)
setArmBotState(armBot.modelUuid, "idle")
setCurrentPhase("init");
setIkSolver(null);
setPath([])
isPausedRef.current = false
pauseTimeRef.current = null
isPausedRef.current = false
startTime = 0
removeCurrentAction(armBot.modelUuid)
} }
}, [currentPhase, armBot, isPlaying, ikSolver]) }, [currentPhase, armBot, isPlaying, isReset, ikSolver])
function createCurveBetweenTwoPoints(p1: any, p2: any) { function createCurveBetweenTwoPoints(p1: any, p2: any) {
@ -212,10 +226,14 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
} }
return ( return (
<>
{!isReset && isPlaying && (
<> <>
<IKInstance modelUrl={armModel} setIkSolver={setIkSolver} ikSolver={ikSolver} armBot={armBot} groupRef={groupRef} /> <IKInstance modelUrl={armModel} setIkSolver={setIkSolver} ikSolver={ikSolver} armBot={armBot} groupRef={groupRef} />
<RoboticArmAnimator HandleCallback={HandleCallback} restPosition={restPosition} ikSolver={ikSolver} targetBone={targetBone} armBot={armBot} <RoboticArmAnimator HandleCallback={HandleCallback} restPosition={restPosition} ikSolver={ikSolver} targetBone={targetBone} armBot={armBot}
logStatus={logStatus} path={path} currentPhase={currentPhase} /> logStatus={logStatus} path={path} currentPhase={currentPhase} />
</>
)}
<MaterialAnimator ikSolver={ikSolver} armBot={armBot} currentPhase={currentPhase} /> <MaterialAnimator ikSolver={ikSolver} armBot={armBot} currentPhase={currentPhase} />
</> </>
) )

View File

@ -11,7 +11,7 @@ type IKInstanceProps = {
modelUrl: string; modelUrl: string;
ikSolver: any; ikSolver: any;
setIkSolver: any setIkSolver: any
armBot: any; armBot: ArmBotStatus;
groupRef: any; groupRef: any;
}; };
function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKInstanceProps) { function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKInstanceProps) {
@ -57,12 +57,6 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKIns
rotationMin: new THREE.Vector3(0, 0, 0), rotationMin: new THREE.Vector3(0, 0, 0),
rotationMax: new THREE.Vector3(2, 0, 0), rotationMax: new THREE.Vector3(2, 0, 0),
}, },
// {
// index: 1,
// enabled: true,
// rotationMin: new THREE.Vector3(0, -Math.PI, 0),
// rotationMax: new THREE.Vector3(0, Math.PI, 0),
// },
{ index: 1, enabled: true, limitation: new THREE.Vector3(0, 1, 0) }, { index: 1, enabled: true, limitation: new THREE.Vector3(0, 1, 0) },
{ index: 0, enabled: false, limitation: new THREE.Vector3(0, 0, 0) }, { index: 0, enabled: false, limitation: new THREE.Vector3(0, 0, 0) },
], ],
@ -76,7 +70,7 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKIns
setSelectedArm(OOI.Target_Bone); setSelectedArm(OOI.Target_Bone);
scene.add(helper); // scene.add(helper);
}, [cloned, gltf, setIkSolver]); }, [cloned, gltf, setIkSolver]);
@ -86,10 +80,10 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKIns
setSelectedArm(groupRef.current?.getObjectByName(targetBoneName)) setSelectedArm(groupRef.current?.getObjectByName(targetBoneName))
}}> }}>
<primitive <primitive
uuid={"ArmBot-X200"} uuid={armBot.modelUuid}
object={cloned} object={cloned}
scale={[1, 1, 1]} scale={[1, 1, 1]}
name={`arm-bot11`} name={armBot.modelName}
/> />
</group> </group>
{/* {selectedArm && <TransformControls object={selectedArm} />} */} {/* {selectedArm && <TransformControls object={selectedArm} />} */}

View File

@ -1,24 +1,37 @@
import { useEffect } from "react"; import { useEffect, useState } from "react";
import RoboticArmInstances from "./instances/roboticArmInstances";
import { useArmBotStore } from "../../../store/simulation/useArmBotStore"; import { useArmBotStore } from "../../../store/simulation/useArmBotStore";
import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import ArmBotUI from "../ui/arm/armBotUI"; import ArmBotUI from "../ui/arm/armBotUI";
import RoboticArmInstances from "./instances/roboticArmInstances";
function RoboticArm() { function RoboticArm() {
const { armBots } = useArmBotStore(); const { armBots, getArmBotById } = useArmBotStore();
const { selectedEventSphere } = useSelectedEventSphere(); const { selectedEventSphere } = useSelectedEventSphere();
const { selectedEventData } = useSelectedEventData(); const { isPlaying } = usePlayButtonStore();
const [isArmBotSelected, setIsArmBotSelected] = useState(false);
useEffect(() => { useEffect(() => {
// console.log('armBots: ', armBots); // console.log('armBots: ', armBots);
}, [armBots]) }, [armBots])
useEffect(() => {
if (selectedEventSphere) {
const selectedArmBot = getArmBotById(selectedEventSphere.userData.modelUuid);
if (selectedArmBot) {
setIsArmBotSelected(true);
} else {
setIsArmBotSelected(false);
}
}
}, [selectedEventSphere])
return ( return (
<> <>
<RoboticArmInstances /> <RoboticArmInstances />
{selectedEventSphere && selectedEventData?.data.type === "roboticArm" && {isArmBotSelected && !isPlaying &&
< ArmBotUI /> < ArmBotUI />
} }

View File

@ -1,131 +1,128 @@
import { useCallback, useEffect, useRef } from 'react'; import { useCallback } from 'react';
import { useActionHandler } from '../../actions/useActionHandler'; import { useActionHandler } from '../../actions/useActionHandler';
import { useProductStore } from '../../../../store/simulation/useProductStore'; import { useProductStore } from '../../../../store/simulation/useProductStore';
import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore';
import { useMaterialStore } from '../../../../store/simulation/useMaterialStore'; import { useMaterialStore } from '../../../../store/simulation/useMaterialStore';
export function useTriggerHandler() { export function useTriggerHandler() {
const { getActionByUuid, getEventByTriggerUuid, getEventByModelUuid } = useProductStore(); const { getEventByTriggerUuid, getEventByModelUuid } = useProductStore();
const { handleAction } = useActionHandler(); const { handleAction } = useActionHandler();
const { getMaterialByCurrentModelUuid, setCurrentLocation, setNextLocation } = useMaterialStore(); const { setCurrentLocation, setNextLocation, getMaterialById } = useMaterialStore();
const { selectedProduct } = useSelectedProduct(); const { selectedProduct } = useSelectedProduct();
const handleTrigger = (trigger: TriggerSchema, actionUuid: string) => { const handleTrigger = (trigger: TriggerSchema, action: Action, materialId: string) => {
// const fromEvent = getEventByTriggerUuid(selectedProduct.productId, trigger.triggerUuid); const fromEvent = getEventByTriggerUuid(selectedProduct.productId, trigger.triggerUuid);
// console.log('fromEvent: ', fromEvent);
// const toEvent = getEventByModelUuid(selectedProduct.productId, trigger.triggeredAsset?.triggeredModel.modelUuid || ''); const toEvent = getEventByModelUuid(selectedProduct.productId, trigger.triggeredAsset?.triggeredModel.modelUuid || '');
// console.log('toEvent: ', toEvent);
// if (fromEvent?.type === 'transfer') { if (fromEvent?.type === 'transfer') {
// if (toEvent?.type === 'transfer') { if (toEvent?.type === 'transfer') {
// // console.log('toEvent: ', toEvent.type); // Transfer to Transfer
// // Transfer to Transfer if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
// const action = getActionByUuid(selectedProduct.productId, trigger.triggeredAsset?.triggeredAction?.actionUuid || ''); const material = getMaterialById(materialId);
// if (action && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { if (material) {
// const material = getMaterialByCurrentModelUuid(fromEvent.modelUuid); if (material.next) {
// if (material) {
// if (material.next &&
// action.triggers[0].triggeredAsset?.triggeredAction?.actionUuid &&
// action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid) {
// setCurrentLocation(material.materialId, material.next); setCurrentLocation(material.materialId, {
modelUuid: material.next.modelUuid,
pointUuid: material.next.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
});
// setNextLocation(material.materialId, { setNextLocation(material.materialId, {
// modelUuid: toEvent.modelUuid, modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid,
// pointUuid: action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid, pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
// actionUuid: action.triggers[0].triggeredAsset?.triggeredAction?.actionUuid });
// }); }
// } handleAction(action, materialId);
// handleAction(action); }
// } }
// } } else if (toEvent?.type === 'vehicle') {
// } else if (toEvent?.type === 'vehicle') { // Transfer to Vehicle
// // Transfer to Vehicle
// } else if (toEvent?.type === 'machine') { } else if (toEvent?.type === 'machine') {
// // Transfer to Machine // Transfer to Machine
// } else if (toEvent?.type === 'roboticArm') { } else if (toEvent?.type === 'roboticArm') {
// // Transfer to Robotic Arm // Transfer to Robotic Arm
// } else if (toEvent?.type === 'storageUnit') { } else if (toEvent?.type === 'storageUnit') {
// // Transfer to Storage Unit // Transfer to Storage Unit
// } }
// } else if (fromEvent?.type === 'vehicle') { } else if (fromEvent?.type === 'vehicle') {
// if (toEvent?.type === 'transfer') { if (toEvent?.type === 'transfer') {
// // Vehicle to Transfer // Vehicle to Transfer
// } else if (toEvent?.type === 'vehicle') { } else if (toEvent?.type === 'vehicle') {
// // Vehicle to Vehicle // Vehicle to Vehicle
// } else if (toEvent?.type === 'machine') { } else if (toEvent?.type === 'machine') {
// // Vehicle to Machine // Vehicle to Machine
// } else if (toEvent?.type === 'roboticArm') { } else if (toEvent?.type === 'roboticArm') {
// // Vehicle to Robotic Arm // Vehicle to Robotic Arm
// } else if (toEvent?.type === 'storageUnit') { } else if (toEvent?.type === 'storageUnit') {
// // Vehicle to Storage Unit // Vehicle to Storage Unit
// } }
// } else if (fromEvent?.type === 'machine') { } else if (fromEvent?.type === 'machine') {
// if (toEvent?.type === 'transfer') { if (toEvent?.type === 'transfer') {
// // Machine to Transfer // Machine to Transfer
// } else if (toEvent?.type === 'vehicle') { } else if (toEvent?.type === 'vehicle') {
// // Machine to Vehicle // Machine to Vehicle
// } else if (toEvent?.type === 'machine') { } else if (toEvent?.type === 'machine') {
// // Machine to Machine // Machine to Machine
// } else if (toEvent?.type === 'roboticArm') { } else if (toEvent?.type === 'roboticArm') {
// // Machine to Robotic Arm // Machine to Robotic Arm
// } else if (toEvent?.type === 'storageUnit') { } else if (toEvent?.type === 'storageUnit') {
// // Machine to Storage Unit // Machine to Storage Unit
// } }
// } else if (fromEvent?.type === 'roboticArm') { } else if (fromEvent?.type === 'roboticArm') {
// if (toEvent?.type === 'transfer') { if (toEvent?.type === 'transfer') {
// // Robotic Arm to Transfer // Robotic Arm to Transfer
// } else if (toEvent?.type === 'vehicle') { } else if (toEvent?.type === 'vehicle') {
// // Robotic Arm to Vehicle // Robotic Arm to Vehicle
// } else if (toEvent?.type === 'machine') { } else if (toEvent?.type === 'machine') {
// // Robotic Arm to Machine // Robotic Arm to Machine
// } else if (toEvent?.type === 'roboticArm') { } else if (toEvent?.type === 'roboticArm') {
// // Robotic Arm to Robotic Arm // Robotic Arm to Robotic Arm
// } else if (toEvent?.type === 'storageUnit') { } else if (toEvent?.type === 'storageUnit') {
// // Robotic Arm to Storage Unit // Robotic Arm to Storage Unit
// } }
// } else if (fromEvent?.type === 'storageUnit') { } else if (fromEvent?.type === 'storageUnit') {
// if (toEvent?.type === 'transfer') { if (toEvent?.type === 'transfer') {
// // Storage Unit to Transfer // Storage Unit to Transfer
// } else if (toEvent?.type === 'vehicle') { } else if (toEvent?.type === 'vehicle') {
// // Storage Unit to Vehicle // Storage Unit to Vehicle
// } else if (toEvent?.type === 'machine') { } else if (toEvent?.type === 'machine') {
// // Storage Unit to Machine // Storage Unit to Machine
// } else if (toEvent?.type === 'roboticArm') { } else if (toEvent?.type === 'roboticArm') {
// // Storage Unit to Robotic Arm // Storage Unit to Robotic Arm
// } else if (toEvent?.type === 'storageUnit') { } else if (toEvent?.type === 'storageUnit') {
// // Storage Unit to Storage Unit // Storage Unit to Storage Unit
// } }
// } }
} }
const triggerPointActions = useCallback((action: Action) => { const triggerPointActions = useCallback((action: Action, materialId: string) => {
if (!action) return; if (!action) return;
action.triggers.forEach(trigger => { action.triggers.forEach(trigger => {
@ -133,7 +130,7 @@ export function useTriggerHandler() {
case 'onStart': case 'onStart':
break; break;
case 'onComplete': case 'onComplete':
handleTrigger(trigger, action.actionUuid); handleTrigger(trigger, action, materialId);
break; break;
case 'onStop': case 'onStop':
break; break;

View File

@ -121,31 +121,65 @@ export default function useDraggableGLTF(onUpdate: OnUpdateCallback) {
// CONSTRAIN MOVEMENT HERE: // CONSTRAIN MOVEMENT HERE:
const centerX = selectedArmBot.position[0]; const centerX = selectedArmBot.position[0];
const centerZ = selectedArmBot.position[2]; const centerZ = selectedArmBot.position[2];
const minDistance = 1.2; // 1.4 radius const minDistance = 1.2;
const maxDistance = 2; // 2 radius const maxDistance = 2;
const deltaX = targetPosition.x - centerX; const delta = new THREE.Vector3(targetPosition.x - centerX, 0, targetPosition.z - centerZ);
const deltaZ = targetPosition.z - centerZ;
const distance = Math.sqrt(deltaX * deltaX + deltaZ * deltaZ);
if (distance < minDistance || distance > maxDistance) { // Create quaternion from rotation
const angle = Math.atan2(deltaZ, deltaX); const robotEuler = new THREE.Euler(selectedArmBot.rotation[0], selectedArmBot.rotation[1], selectedArmBot.rotation[2]);
const clampedDistance = Math.min( const robotQuaternion = new THREE.Quaternion().setFromEuler(robotEuler);
Math.max(distance, minDistance),
maxDistance // Inverse rotate
const inverseQuaternion = robotQuaternion.clone().invert();
delta.applyQuaternion(inverseQuaternion);
// Angle in robot local space
let relativeAngle = Math.atan2(delta.z, delta.x);
let angleDeg = (relativeAngle * 180) / Math.PI;
if (angleDeg < 0) {
angleDeg += 360;
}
// Clamp angle
if (angleDeg < 0 || angleDeg > 270) {
const distanceTo90 = Math.abs(angleDeg - 0);
const distanceTo270 = Math.abs(angleDeg - 270);
if (distanceTo90 < distanceTo270) {
angleDeg = 0;
} else {
return;
}
relativeAngle = (angleDeg * Math.PI) / 180;
}
// Distance clamp
const distance = delta.length();
const clampedDistance = Math.min(Math.max(distance, minDistance), maxDistance);
// Calculate local target
const finalLocal = new THREE.Vector3(
Math.cos(relativeAngle) * clampedDistance,
0,
Math.sin(relativeAngle) * clampedDistance
); );
targetPosition.x = centerX + Math.cos(angle) * clampedDistance; // Rotate back to world space
targetPosition.z = centerZ + Math.sin(angle) * clampedDistance; finalLocal.applyQuaternion(robotQuaternion);
}
targetPosition.x = centerX + finalLocal.x;
targetPosition.z = centerZ + finalLocal.z;
// Clamp Y axis if needed
targetPosition.y = Math.min(Math.max(targetPosition.y, 0.6), 1.9); targetPosition.y = Math.min(Math.max(targetPosition.y, 0.6), 1.9);
// Convert world position to local if object is nested inside a parent
// Convert to local if parent exists
if (parent) { if (parent) {
parent.worldToLocal(targetPosition); parent.worldToLocal(targetPosition);
} }
// Update object position // Update the object position
activeObjRef.current.position.copy(targetPosition); activeObjRef.current.position.copy(targetPosition);
} }
}; };

View File

@ -10,7 +10,7 @@ interface VehicleAnimatorProps {
handleCallBack: () => void; handleCallBack: () => void;
reset: () => void; reset: () => void;
currentPhase: string; currentPhase: string;
agvUuid: number; agvUuid: string;
agvDetail: VehicleStatus; agvDetail: VehicleStatus;
} }

View File

@ -7,7 +7,7 @@ import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore'; import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore';
import MaterialAnimator from '../animator/materialAnimator'; import MaterialAnimator from '../animator/materialAnimator';
function VehicleInstance({ agvDetail }: any) { function VehicleInstance({ agvDetail }: { agvDetail: VehicleStatus }) {
const { navMesh } = useNavMesh(); const { navMesh } = useNavMesh();
const vehicleRef: any = useRef(); const vehicleRef: any = useRef();
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
@ -74,6 +74,7 @@ function VehicleInstance({ agvDetail }: any) {
if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity && agvDetail.materialType) { if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity && agvDetail.materialType) {
if (agvDetail.point.action.pickUpPoint && agvDetail.point.action.unLoadPoint) {
const toDrop = computePath( const toDrop = computePath(
agvDetail.point.action.pickUpPoint.position, agvDetail.point.action.pickUpPoint.position,
agvDetail.point.action.unLoadPoint.position agvDetail.point.action.unLoadPoint.position
@ -84,7 +85,9 @@ function VehicleInstance({ agvDetail }: any) {
setVehicleActive(agvDetail.modelUuid, true); setVehicleActive(agvDetail.modelUuid, true);
vehicleStatus(agvDetail.modelUuid, 'Started from pickup point, heading to drop point'); 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' && currentPhase === 'dropping' && agvDetail.currentLoad === 0) {
if (agvDetail.point.action.pickUpPoint && agvDetail.point.action.unLoadPoint) {
const dropToPickup = computePath( const dropToPickup = computePath(
agvDetail.point.action.unLoadPoint.position, agvDetail.point.action.unLoadPoint.position,
agvDetail.point.action.pickUpPoint.position agvDetail.point.action.pickUpPoint.position
@ -97,6 +100,7 @@ function VehicleInstance({ agvDetail }: any) {
isIncrememtable.current = true; isIncrememtable.current = true;
} }
}
} else { } else {
reset() reset()
} }

View File

@ -1,26 +1,37 @@
import { useEffect } from "react"; import { useEffect, useState } from "react";
import VehicleInstances from "./instances/vehicleInstances";
import { useVehicleStore } from "../../../store/simulation/useVehicleStore"; import { useVehicleStore } from "../../../store/simulation/useVehicleStore";
import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore";
import VehicleUI from "../ui/vehicle/vehicleUI";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import VehicleUI from "../ui/vehicle/vehicleUI";
import VehicleInstances from "./instances/vehicleInstances";
function Vehicles() { function Vehicles() {
const { vehicles } = useVehicleStore(); const { vehicles, getVehicleById } = useVehicleStore();
const { selectedEventSphere } = useSelectedEventSphere(); const { selectedEventSphere } = useSelectedEventSphere();
const { selectedEventData } = useSelectedEventData();
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const [isVehicleSelected, setIsVehicleSelected] = useState(false);
useEffect(() => { useEffect(() => {
// console.log('vehicles: ', vehicles); // console.log('vehicles: ', vehicles);
}, [vehicles]) }, [vehicles])
useEffect(() => {
if (selectedEventSphere) {
const selectedVehicle = getVehicleById(selectedEventSphere.userData.modelUuid);
if (selectedVehicle) {
setIsVehicleSelected(true);
} else {
setIsVehicleSelected(false);
}
}
}, [selectedEventSphere])
return ( return (
<> <>
<VehicleInstances /> <VehicleInstances />
{selectedEventSphere && selectedEventData?.data.type === "vehicle" && !isPlaying && {isVehicleSelected && !isPlaying &&
< VehicleUI /> < VehicleUI />
} }

View File

@ -12,7 +12,7 @@ interface ArmBotStore {
) => void; ) => void;
clearArmBots: () => void; clearArmBots: () => void;
addCurrentAction: (modelUuid: string, actionUuid: string) => void; addCurrentAction: (modelUuid: string, actionUuid: string, materialType: string) => void;
removeCurrentAction: (modelUuid: string) => void; removeCurrentAction: (modelUuid: string) => void;
addAction: (modelUuid: string, action: RoboticArmPointSchema['actions'][number]) => void; addAction: (modelUuid: string, action: RoboticArmPointSchema['actions'][number]) => void;
@ -75,7 +75,7 @@ export const useArmBotStore = create<ArmBotStore>()(
}); });
}, },
addCurrentAction: (modelUuid, actionUuid) => { addCurrentAction: (modelUuid, actionUuid, materialType) => {
set((state) => { set((state) => {
const armBot = state.armBots.find(a => a.modelUuid === modelUuid); const armBot = state.armBots.find(a => a.modelUuid === modelUuid);
if (armBot) { if (armBot) {
@ -84,7 +84,7 @@ export const useArmBotStore = create<ArmBotStore>()(
armBot.currentAction = { armBot.currentAction = {
actionUuid: action.actionUuid, actionUuid: action.actionUuid,
actionName: action.actionName, actionName: action.actionName,
materialType: null materialType: materialType
}; };
} }
} }

View File

@ -14,6 +14,7 @@ interface ConveyorStore {
setConveyorActive: (modelUuid: string, isActive: boolean) => void; setConveyorActive: (modelUuid: string, isActive: boolean) => void;
setConveyorState: (modelUuid: string, newState: ConveyorStatus['state']) => void; setConveyorState: (modelUuid: string, newState: ConveyorStatus['state']) => void;
setConveyorPaused: (modelUuid: string, isPaused: boolean) => void;
incrementActiveTime: (modelUuid: string, incrementBy: number) => void; incrementActiveTime: (modelUuid: string, incrementBy: number) => void;
incrementIdleTime: (modelUuid: string, incrementBy: number) => void; incrementIdleTime: (modelUuid: string, incrementBy: number) => void;
@ -37,6 +38,7 @@ export const useConveyorStore = create<ConveyorStore>()(
...event, ...event,
productId, productId,
isActive: false, isActive: false,
isPaused: false,
idleTime: 0, idleTime: 0,
activeTime: 0, activeTime: 0,
state: 'idle', state: 'idle',
@ -84,6 +86,15 @@ export const useConveyorStore = create<ConveyorStore>()(
}); });
}, },
setConveyorPaused: (modelUuid, isPaused) => {
set((state) => {
const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid);
if (conveyor) {
conveyor.isPaused = isPaused;
}
});
},
incrementActiveTime: (modelUuid, incrementBy) => { incrementActiveTime: (modelUuid, incrementBy) => {
set((state) => { set((state) => {
const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid);

View File

@ -23,20 +23,22 @@ type MaterialsStore = {
location?: { location?: {
modelUuid: string; modelUuid: string;
pointUuid: string; pointUuid: string;
actionUuid: string;
} | null } | null
) => MaterialSchema | undefined; ) => MaterialSchema | undefined;
setMaterial: (materialId: string, materialType: string) => MaterialSchema | undefined;
setStartTime: (materialId: string, startTime: string) => MaterialSchema | undefined; setStartTime: (materialId: string, startTime: string) => MaterialSchema | undefined;
setEndTime: (materialId: string, endTime: string) => MaterialSchema | undefined; setEndTime: (materialId: string, endTime: string) => MaterialSchema | undefined;
setCost: (materialId: string, cost: number) => MaterialSchema | undefined; setCost: (materialId: string, cost: number) => MaterialSchema | undefined;
setWeight: (materialId: string, weight: number) => MaterialSchema | undefined; setWeight: (materialId: string, weight: number) => MaterialSchema | undefined;
setIsActive: (materialId: string, isActive: boolean) => MaterialSchema | undefined; setIsActive: (materialId: string, isActive: boolean) => MaterialSchema | undefined;
setIsVisible: (materialId: string, isVisible: boolean) => MaterialSchema | undefined; setIsVisible: (materialId: string, isVisible: boolean) => MaterialSchema | undefined;
setIsPaused: (materialId: string, isPlaying: boolean) => MaterialSchema | undefined;
setIsRendered: (materialId: string, isRendered: boolean) => MaterialSchema | undefined; setIsRendered: (materialId: string, isRendered: boolean) => MaterialSchema | undefined;
getMaterialById: (materialId: string) => MaterialSchema | undefined; getMaterialById: (materialId: string) => MaterialSchema | undefined;
getMaterialByCurrentModelUuid: (currentModelUuid: string) => MaterialSchema | undefined; getMaterialsByCurrentModelUuid: (currentModelUuid: string) => MaterialSchema[] | undefined;
getMaterialByCurrentPointUuid: (currentPointUuid: string) => MaterialSchema | undefined;
getMaterialsByPoint: (pointUuid: string) => MaterialSchema[]; getMaterialsByPoint: (pointUuid: string) => MaterialSchema[];
getMaterialsByModel: (modelUuid: string) => MaterialSchema[]; getMaterialsByModel: (modelUuid: string) => MaterialSchema[];
}; };
@ -107,6 +109,18 @@ export const useMaterialStore = create<MaterialsStore>()(
return updatedMaterial; return updatedMaterial;
}, },
setMaterial: (materialId, materialType) => {
let updatedMaterial: MaterialSchema | undefined;
set((state) => {
const material = state.materials.find(m => m.materialId === materialId);
if (material) {
material.materialType = materialType;
updatedMaterial = JSON.parse(JSON.stringify(material));
};
});
return updatedMaterial;
},
setStartTime: (materialId, startTime) => { setStartTime: (materialId, startTime) => {
let updatedMaterial: MaterialSchema | undefined; let updatedMaterial: MaterialSchema | undefined;
set((state) => { set((state) => {
@ -179,6 +193,18 @@ export const useMaterialStore = create<MaterialsStore>()(
return updatedMaterial; return updatedMaterial;
}, },
setIsPaused: (materialId, isPaused) => {
let updatedMaterial: MaterialSchema | undefined;
set((state) => {
const material = state.materials.find(m => m.materialId === materialId);
if (material) {
material.isPaused = isPaused;
updatedMaterial = JSON.parse(JSON.stringify(material));
};
});
return updatedMaterial;
},
setIsRendered: (materialId, isRendered) => { setIsRendered: (materialId, isRendered) => {
let updatedMaterial: MaterialSchema | undefined; let updatedMaterial: MaterialSchema | undefined;
set((state) => { set((state) => {
@ -195,8 +221,12 @@ export const useMaterialStore = create<MaterialsStore>()(
return get().materials.find(m => m.materialId === materialId); return get().materials.find(m => m.materialId === materialId);
}, },
getMaterialByCurrentModelUuid: (currentModelUuid) => { getMaterialsByCurrentModelUuid: (currentModelUuid) => {
return get().materials.find(m => m.current?.modelUuid === currentModelUuid); return get().materials.filter(m => m.current?.modelUuid === currentModelUuid);
},
getMaterialByCurrentPointUuid: (currentPointlUuid) => {
return get().materials.find(m => m.current?.pointUuid === currentPointlUuid);
}, },
getMaterialsByPoint: (pointUuid) => { getMaterialsByPoint: (pointUuid) => {

View File

@ -65,6 +65,7 @@ type ProductsStore = {
getEventByPointUuid: (productId: string, pointUuid: string) => EventsSchema | undefined; getEventByPointUuid: (productId: string, pointUuid: string) => EventsSchema | undefined;
getPointByUuid: (productId: string, modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined; getPointByUuid: (productId: string, modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined;
getActionByUuid: (productId: string, actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined; getActionByUuid: (productId: string, actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined;
getActionByPointUuid: (productId: string, pointUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined;
getModelUuidByPointUuid: (productId: string, actionUuid: string) => (string) | undefined; getModelUuidByPointUuid: (productId: string, actionUuid: string) => (string) | undefined;
getModelUuidByActionUuid: (productId: string, actionUuid: string) => (string) | undefined; getModelUuidByActionUuid: (productId: string, actionUuid: string) => (string) | undefined;
getPointUuidByActionUuid: (productId: string, actionUuid: string) => (string) | undefined; getPointUuidByActionUuid: (productId: string, actionUuid: string) => (string) | undefined;
@ -625,6 +626,27 @@ export const useProductStore = create<ProductsStore>()(
return undefined; return undefined;
}, },
getActionByPointUuid: (productId, pointUuid) => {
const product = get().products.find(p => p.productId === productId);
if (!product) return undefined;
for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
if (point.uuid === pointUuid) {
return point.action;
}
}
} else if ('point' in event) {
const point = (event as any).point;
if (point.uuid === pointUuid) {
return point.action;
}
}
}
return undefined;
},
getModelUuidByPointUuid: (productId, pointUuid) => { getModelUuidByPointUuid: (productId, pointUuid) => {
const product = get().products.find(p => p.productId === productId); const product = get().products.find(p => p.productId === productId);
if (!product) return undefined; if (!product) return undefined;

View File

@ -125,7 +125,6 @@ interface StorageAction {
actionUuid: string; actionUuid: string;
actionName: string; actionName: string;
actionType: "store"; actionType: "store";
materials: { materialName: string; materialId: string; }[];
storageCapacity: number; storageCapacity: number;
triggers: TriggerSchema[]; triggers: TriggerSchema[];
} }
@ -146,6 +145,7 @@ type productsSchema = {
interface ConveyorStatus extends ConveyorEventSchema { interface ConveyorStatus extends ConveyorEventSchema {
productId: string; productId: string;
isActive: boolean; isActive: boolean;
isPaused: boolean;
idleTime: number; idleTime: number;
activeTime: number; activeTime: number;
} }
@ -190,6 +190,7 @@ interface StorageUnitStatus extends StorageEventSchema {
idleTime: number; idleTime: number;
activeTime: number; activeTime: number;
currentLoad: number; currentLoad: number;
materials?: { materialName: string; materialId: string; }[];
} }
interface MaterialSchema { interface MaterialSchema {
@ -198,6 +199,7 @@ interface MaterialSchema {
materialType: string; materialType: string;
isActive: boolean; isActive: boolean;
isVisible: boolean; isVisible: boolean;
isPaused: boolean;
isRendered: boolean; isRendered: boolean;
startTime?: string; startTime?: string;
endTime?: string; endTime?: string;
@ -213,7 +215,6 @@ interface MaterialSchema {
next?: { next?: {
modelUuid: string; modelUuid: string;
pointUuid: string; pointUuid: string;
actionUuid: string;
}; };
} }