Refactor scene components and add moving clouds feature

- Cleaned up the Ground and Shadows components for better readability and consistency.
- Introduced MovingClouds component to enhance the visual environment with animated clouds.
- Updated Scene component to include new cloud feature.
- Improved ConveyorInstance logic to handle product selection and conveyor state management.
- Enhanced RoboticArmInstance to check for active robotic arms in subsequences.
- Added utility functions to find conveyors and robotic arms in sequences for better simulation control.
- Refactored trigger handling to manage material pause states more effectively.
- Optimized vehicle instance logic for smoother operation during loading and unloading.
This commit is contained in:
Jerald-Golden-B 2025-05-16 18:11:58 +05:30
parent f37750023f
commit b7a5908884
14 changed files with 777 additions and 295 deletions

View File

@ -48,6 +48,7 @@ import NavMesh from "../simulation/vehicle/navMesh/navMesh";
import CalculateAreaGroup from "./groups/calculateAreaGroup"; import CalculateAreaGroup from "./groups/calculateAreaGroup";
import LayoutImage from "./layout/layoutImage"; import LayoutImage from "./layout/layoutImage";
import AssetsGroup from "./assetGroup/assetsGroup"; import AssetsGroup from "./assetGroup/assetsGroup";
import { Bvh } from "@react-three/drei";
export default function Builder() { export default function Builder() {
const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements. const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements.
@ -203,13 +204,16 @@ export default function Builder() {
<> <>
<Ground grid={grid} plane={plane} /> <Ground grid={grid} plane={plane} />
<Bvh firstHitOnly>
<DistanceText key={toggleView} /> <DistanceText key={toggleView} />
</Bvh>
<ReferenceDistanceText <ReferenceDistanceText
key={refTextupdate} key={refTextupdate}
line={ReferenceLineMesh.current} line={ReferenceLineMesh.current}
/> />
<Bvh firstHitOnly>
<SocketResponses <SocketResponses
floorPlanGroup={floorPlanGroup} floorPlanGroup={floorPlanGroup}
lines={lines} lines={lines}
@ -226,6 +230,7 @@ export default function Builder() {
zoneGroup={zoneGroup} zoneGroup={zoneGroup}
dragPointControls={dragPointControls} dragPointControls={dragPointControls}
/> />
</Bvh>
<WallsAndWallItems <WallsAndWallItems
CSGGroup={CSGGroup} CSGGroup={CSGGroup}
@ -237,6 +242,7 @@ export default function Builder() {
hoveredDeletableWallItem={hoveredDeletableWallItem} hoveredDeletableWallItem={hoveredDeletableWallItem}
/> />
<Bvh firstHitOnly>
<FloorItemsGroup <FloorItemsGroup
itemsGroup={itemsGroup} itemsGroup={itemsGroup}
hoveredDeletableFloorItem={hoveredDeletableFloorItem} hoveredDeletableFloorItem={hoveredDeletableFloorItem}
@ -309,6 +315,7 @@ export default function Builder() {
<NavMesh lines={lines} /> <NavMesh lines={lines} />
<LayoutImage /> <LayoutImage />
</Bvh>
</> </>
); );
} }

View File

@ -0,0 +1,90 @@
import * as THREE from 'three';
import { useRef, useState } from 'react';
import { useFrame } from '@react-three/fiber';
import { Clouds, Cloud } from '@react-three/drei';
interface CloudGroupProps {
initialX: number;
initialZ: number;
speed: number;
height: number;
}
function CloudGroup({ initialX, initialZ, speed, height }: CloudGroupProps) {
const group = useRef<THREE.Group>(null);
useFrame((_, delta) => {
if (group.current) {
group.current.position.x += delta * speed;
group.current.position.z += delta * speed * 0.5;
if (group.current.position.x > 500) group.current.position.x = -500;
if (group.current.position.z > 500) group.current.position.z = -500;
}
});
return (
<group ref={group} position={[initialX, height, initialZ]}>
<Clouds material={THREE.MeshBasicMaterial} frustumCulled={false} limit={10000}>
<Cloud
seed={Math.random() * 100}
bounds={[100, 15, 100]}
volume={50}
color="#eeeeee"
scale={4}
fade={10000}
/>
<Cloud
seed={Math.random() * 100}
bounds={[100, 15, 100]}
volume={50}
scale={6}
color="#ffffff"
fade={10000}
/>
<Cloud
seed={Math.random() * 100}
bounds={[100, 15, 100]}
volume={50}
scale={4}
fade={10000}
color="#f0f0f0"
/>
</Clouds>
</group>
);
}
export function MovingClouds() {
const savedTheme: string | null = localStorage.getItem("theme");
const [theme, setTheme] = useState(savedTheme || "light");
const cloudGroups = [
{ initialX: 0, initialZ: 0, speed: 8, height: 300 },
{ initialX: -300, initialZ: 100, speed: 10, height: 300 },
{ initialX: 200, initialZ: -150, speed: 4, height: 300 },
{ initialX: -400, initialZ: -200, speed: 7, height: 300 },
{ initialX: 400, initialZ: 300, speed: 5, height: 300 },
{ initialX: -200, initialZ: -300, speed: 7, height: 300 },
{ initialX: 300, initialZ: 200, speed: 10, height: 300 },
];
return (
<>
{theme === 'light' &&
<>
{cloudGroups.map((group, index) => (
<CloudGroup
key={index}
initialX={group.initialX}
initialZ={group.initialZ}
speed={group.speed}
height={group.height}
/>
))}
</>
}
</>
);
}

View File

@ -10,18 +10,12 @@ const Ground = ({ grid, plane }: any) => {
<mesh <mesh
ref={grid} ref={grid}
name="Grid" name="Grid"
position={ position={!toggleView ? CONSTANTS.gridConfig.position3D : CONSTANTS.gridConfig.position2D}
!toggleView
? CONSTANTS.gridConfig.position3D
: CONSTANTS.gridConfig.position2D
}
> >
<gridHelper <gridHelper
args={[ args={[
gridValue.size, gridValue.size,
gridValue.divisions, gridValue.divisions,
// CONSTANTS.gridConfig.size,
// CONSTANTS.gridConfig.divisions,
CONSTANTS.gridConfig.primaryColor, CONSTANTS.gridConfig.primaryColor,
CONSTANTS.gridConfig.secondaryColor, CONSTANTS.gridConfig.secondaryColor,
]} ]}
@ -30,18 +24,11 @@ const Ground = ({ grid, plane }: any) => {
<mesh <mesh
ref={plane} ref={plane}
rotation-x={CONSTANTS.planeConfig.rotation} rotation-x={CONSTANTS.planeConfig.rotation}
position={ position={!toggleView ? CONSTANTS.planeConfig.position3D : CONSTANTS.planeConfig.position2D}
!toggleView
? CONSTANTS.planeConfig.position3D
: CONSTANTS.planeConfig.position2D
}
name="Plane" name="Plane"
receiveShadow receiveShadow
> >
<planeGeometry <planeGeometry args={[planeValue.width, planeValue.height]} />
args={[planeValue.width, planeValue.height]}
// args={[CONSTANTS.planeConfig.width, CONSTANTS.planeConfig.height]}
/>
<meshBasicMaterial color={CONSTANTS.planeConfig.color} /> <meshBasicMaterial color={CONSTANTS.planeConfig.color} />
</mesh> </mesh>
</mesh> </mesh>

View File

@ -47,6 +47,7 @@ export default function Shadows() {
if (lightRef.current && targetRef.current && controls) { if (lightRef.current && targetRef.current && controls) {
lightRef.current.position.copy(lightPosition); lightRef.current.position.copy(lightPosition);
targetRef.current.position.copy(controlsTarget); targetRef.current.position.copy(controlsTarget);
gl.shadowMap.needsUpdate = true;
} }
}; };
}, [shadowWorker, controls]); }, [shadowWorker, controls]);
@ -94,13 +95,6 @@ export default function Shadows() {
rotation={CONSTANTS.shadowConfig.shadowMaterialRotation} rotation={CONSTANTS.shadowConfig.shadowMaterialRotation}
receiveShadow receiveShadow
> >
{/* <planeGeometry
args={[CONSTANTS.planeConfig.width, CONSTANTS.planeConfig.height]}
/>
<shadowMaterial
opacity={CONSTANTS.shadowConfig.shadowMaterialOpacity}
transparent
/> */}
<planeGeometry args={[planeValue.width, planeValue.height]} /> <planeGeometry args={[planeValue.width, planeValue.height]} />
<shadowMaterial <shadowMaterial
opacity={CONSTANTS.shadowConfig.shadowMaterialOpacity} opacity={CONSTANTS.shadowConfig.shadowMaterialOpacity}

View File

@ -9,15 +9,12 @@ import Simulation from "../simulation/simulation";
import Collaboration from "../collaboration/collaboration"; import Collaboration from "../collaboration/collaboration";
export default function Scene() { export default function Scene() {
const map = useMemo( const map = useMemo(() => [
() => [
{ name: "forward", keys: ["ArrowUp", "w", "W"] }, { name: "forward", keys: ["ArrowUp", "w", "W"] },
{ name: "backward", keys: ["ArrowDown", "s", "S"] }, { name: "backward", keys: ["ArrowDown", "s", "S"] },
{ name: "left", keys: ["ArrowLeft", "a", "A"] }, { name: "left", keys: ["ArrowLeft", "a", "A"] },
{ name: "right", keys: ["ArrowRight", "d", "D"] }, { name: "right", keys: ["ArrowRight", "d", "D"] },
], ], []);
[]
);
return ( return (
<KeyboardControls map={map}> <KeyboardControls map={map}>

View File

@ -5,6 +5,7 @@ import Controls from '../controls/controls';
import { Environment } from '@react-three/drei' import { Environment } from '@react-three/drei'
import background from "../../../assets/textures/hdr/mudroadpuresky2k.hdr"; import background from "../../../assets/textures/hdr/mudroadpuresky2k.hdr";
import { MovingClouds } from '../clouds/clouds';
function Setup() { function Setup() {
return ( return (
@ -17,6 +18,8 @@ function Setup() {
<PostProcessing /> <PostProcessing />
<MovingClouds />
<Environment files={background} environmentIntensity={1.5} /> <Environment files={background} environmentIntensity={1.5} />
</> </>
) )

View File

@ -2,36 +2,43 @@ import React, { useEffect } from 'react'
import { useMaterialStore } from '../../../../../store/simulation/useMaterialStore'; import { useMaterialStore } from '../../../../../store/simulation/useMaterialStore';
import { useConveyorStore } from '../../../../../store/simulation/useConveyorStore'; import { useConveyorStore } from '../../../../../store/simulation/useConveyorStore';
import { useResetButtonStore } from '../../../../../store/usePlayButtonStore'; import { useResetButtonStore } from '../../../../../store/usePlayButtonStore';
import { findConveyorInSequences } from '../../../simulator/functions/findConveyorInSequences';
import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore';
import { useProductStore } from '../../../../../store/simulation/useProductStore';
function ConveyorInstance({ conveyor }: { conveyor: ConveyorStatus }) { function ConveyorInstance({ conveyor }: { conveyor: ConveyorStatus }) {
const { getProductById } = useProductStore();
const { selectedProduct } = useSelectedProduct();
const { materials, getMaterialsByCurrentModelUuid } = useMaterialStore(); const { materials, getMaterialsByCurrentModelUuid } = useMaterialStore();
const { isReset } = useResetButtonStore(); const { isReset } = useResetButtonStore();
const { setConveyorPaused } = useConveyorStore(); const { setConveyorPaused } = useConveyorStore();
useEffect(() => { useEffect(() => {
const product = getProductById(selectedProduct.productId);
if (!product) return;
const sequenceInfo = findConveyorInSequences(product, conveyor.modelUuid);
if (!sequenceInfo) return;
const { currentSubSequence } = sequenceInfo;
const conveyorMaterials = getMaterialsByCurrentModelUuid(conveyor.modelUuid); const conveyorMaterials = getMaterialsByCurrentModelUuid(conveyor.modelUuid);
if (conveyorMaterials && conveyorMaterials?.length > 0) {
const hasPausedMaterials = conveyorMaterials.some(material => material.isPaused); if (conveyorMaterials && conveyorMaterials.length > 0) {
const shouldPauseSubsequence = currentSubSequence.some(subConveyor => {
if (subConveyor.type !== 'transfer') return false;
const subMaterials = getMaterialsByCurrentModelUuid(subConveyor.modelUuid);
return subMaterials?.some(m => m.isPaused) ?? false;
});
if (hasPausedMaterials) { currentSubSequence.forEach(subConveyor => {
setConveyorPaused(conveyor.modelUuid, true); if (subConveyor.type === 'transfer') {
} else { setConveyorPaused(subConveyor.modelUuid, shouldPauseSubsequence);
setConveyorPaused(conveyor.modelUuid, false);
} }
});
} }
}, [materials, conveyor.modelUuid, getMaterialsByCurrentModelUuid, setConveyorPaused, isReset, selectedProduct.productId, getProductById]);
}, [materials, conveyor.modelUuid, getMaterialsByCurrentModelUuid, setConveyorPaused, isReset]); return null;
useEffect(() => {
// console.log('conveyor: ', conveyor);
}, [conveyor])
return (
<>
</>
)
} }
export default ConveyorInstance export default React.memo(ConveyorInstance);

View File

@ -52,7 +52,7 @@ function Products() {
}); });
} }
} }
}, [selectedProduct, products, isReset]); }, [selectedProduct, products, isReset, isPlaying]);
useEffect(() => { useEffect(() => {
if (selectedProduct.productId) { if (selectedProduct.productId) {
@ -66,7 +66,7 @@ function Products() {
}); });
} }
} }
}, [selectedProduct, products, isReset]); }, [selectedProduct, products, isReset, isPlaying]);
useEffect(() => { useEffect(() => {
if (selectedProduct.productId) { if (selectedProduct.productId) {
@ -80,7 +80,7 @@ function Products() {
}); });
} }
} }
}, [selectedProduct, products, isReset]); }, [selectedProduct, products, isReset, isPlaying]);
useEffect(() => { useEffect(() => {
if (selectedProduct.productId) { if (selectedProduct.productId) {
@ -94,7 +94,7 @@ function Products() {
}); });
} }
} }
}, [selectedProduct, products, isReset]); }, [selectedProduct, products, isReset, isPlaying]);
useEffect(() => { useEffect(() => {
if (selectedProduct.productId) { if (selectedProduct.productId) {

View File

@ -13,6 +13,7 @@ import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore
import { useStorageUnitStore } from '../../../../../store/simulation/useStorageUnitStore'; import { useStorageUnitStore } from '../../../../../store/simulation/useStorageUnitStore';
import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore'; import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore';
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler'; import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
import { useCheckActiveRoboticArmsInSubsequence } from '../../../simulator/functions/checkActiveRoboticArmsInSubsequence';
function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
@ -30,13 +31,16 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
const { setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore(); const { setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore();
const { decrementVehicleLoad, removeLastMaterial } = useVehicleStore(); const { decrementVehicleLoad, removeLastMaterial } = useVehicleStore();
const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = useStorageUnitStore(); const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = useStorageUnitStore();
const { setIsVisible, getMaterialById } = useMaterialStore(); const { setIsVisible, setIsPaused, getMaterialById } = useMaterialStore();
const { selectedProduct } = useSelectedProduct(); const { selectedProduct } = useSelectedProduct();
const { getActionByUuid, getEventByActionUuid, getEventByModelUuid } = useProductStore(); const { getActionByUuid, getEventByActionUuid, getEventByModelUuid, getProductById } = useProductStore();
const { triggerPointActions } = useTriggerHandler(); const { triggerPointActions } = useTriggerHandler();
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { isReset } = useResetButtonStore(); const { isReset } = useResetButtonStore();
const { isPaused } = usePauseButtonStore(); const { isPaused } = usePauseButtonStore();
const checkActiveRoboticArms = useCheckActiveRoboticArmsInSubsequence();
const lastRemoved = useRef<{ type: string, materialId: string } | null>(null);
function firstFrame() { function firstFrame() {
startTime = performance.now(); startTime = performance.now();
@ -63,6 +67,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
removeLastStorageMaterial(previousModel.modelUuid); removeLastStorageMaterial(previousModel.modelUuid);
updateCurrentLoad(previousModel.modelUuid, -1) updateCurrentLoad(previousModel.modelUuid, -1)
} }
lastRemoved.current = { type: previousModel.type, materialId: armBot.currentAction.materialId };
} else { } else {
setIsVisible(armBot.currentAction.materialId, false); setIsVisible(armBot.currentAction.materialId, false);
} }
@ -76,11 +81,20 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
if (armBot.currentAction) { if (armBot.currentAction) {
const action = getActionByUuid(selectedProduct.productId, armBot.currentAction.actionUuid); const action = getActionByUuid(selectedProduct.productId, armBot.currentAction.actionUuid);
const model = getEventByModelUuid(selectedProduct.productId, action?.triggers[0].triggeredAsset?.triggeredModel.modelUuid || '');
if (action && action.triggers[0].triggeredAsset?.triggeredModel.modelUuid) { if (action && action.triggers[0].triggeredAsset?.triggeredModel.modelUuid) {
const model = getEventByModelUuid(selectedProduct.productId, action?.triggers[0].triggeredAsset?.triggeredModel.modelUuid);
if (!model) return; if (!model) return;
if (model.type === 'transfer') { if (model.type === 'transfer') {
setIsVisible(armBot.currentAction.materialId || '', true); setIsVisible(armBot.currentAction.materialId || '', true);
const product = getProductById(selectedProduct.productId);
if (product) {
const result = checkActiveRoboticArms(product, armBot.modelUuid);
// console.log('result: ', result);
// if (result?.hasActiveRoboticArm) {
// lastRemoved.current = null;
// }
}
} else if (model.type === 'machine') { } else if (model.type === 'machine') {
// //
} else if (model.type === 'vehicle') { } else if (model.type === 'vehicle') {
@ -287,6 +301,14 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
setArmBotState(armBot.modelUuid, "idle") setArmBotState(armBot.modelUuid, "idle")
setCurrentPhase("rest"); setCurrentPhase("rest");
setPath([]) setPath([])
if (lastRemoved.current) {
if (lastRemoved.current.type === 'transfer') {
setIsPaused(lastRemoved.current.materialId, true)
} else {
setIsPaused(lastRemoved.current.materialId, false)
}
}
} }
} }
const logStatus = (id: string, status: string) => { const logStatus = (id: string, status: string) => {

View File

@ -0,0 +1,188 @@
import { extractTriggersFromPoint } from "./extractTriggersFromPoint";
import { useArmBotStore } from "../../../../store/simulation/useArmBotStore";
export function getRoboticArmSequencesInProduct(
product: {
productName: string;
productId: string;
eventDatas: EventsSchema[];
}
): EventsSchema[][][] {
// Get all machine sequences for this product
const machineSequences = determineExecutionMachineSequences([product]);
const allRoboticArmSequences: EventsSchema[][][] = [];
// Process each machine sequence separately
for (const machineSequence of machineSequences) {
const roboticArmSequencesForThisMachineSequence: EventsSchema[][] = [];
let currentRoboticArmSequence: EventsSchema[] = [];
for (const event of machineSequence) {
if (event.type === 'roboticArm') {
// Add robotic arm to current sequence
currentRoboticArmSequence.push(event);
} else if (event.type === 'vehicle') {
// Vehicle encountered - split the sequence
if (currentRoboticArmSequence.length > 0) {
roboticArmSequencesForThisMachineSequence.push([...currentRoboticArmSequence]);
currentRoboticArmSequence = [];
}
}
// Other machine types continue the current sequence
}
// Add any remaining robotic arms in the current sequence
if (currentRoboticArmSequence.length > 0) {
roboticArmSequencesForThisMachineSequence.push([...currentRoboticArmSequence]);
}
if (roboticArmSequencesForThisMachineSequence.length > 0) {
allRoboticArmSequences.push(roboticArmSequencesForThisMachineSequence);
}
}
return allRoboticArmSequences;
}
export function findRoboticArmSubsequence(
product: {
productName: string;
productId: string;
eventDatas: EventsSchema[];
},
roboticArmModelUuid: string
): {
allSequences: EventsSchema[][][];
parentSequence: EventsSchema[][];
currentSubSequence: EventsSchema[];
} | null {
const allSequences = getRoboticArmSequencesInProduct(product);
for (const parentSequence of allSequences) {
for (const currentSubSequence of parentSequence) {
const hasTargetRoboticArm = currentSubSequence.some(
event => event.type === 'roboticArm' && event.modelUuid === roboticArmModelUuid
);
if (hasTargetRoboticArm) {
return {
allSequences,
parentSequence,
currentSubSequence
};
}
}
}
return null;
}
// React component/hook that uses the pure functions
export function useCheckActiveRoboticArmsInSubsequence() {
const { getArmBotById } = useArmBotStore();
return function (product: {
productName: string;
productId: string;
eventDatas: EventsSchema[];
}, roboticArmModelUuid: string) {
const result = findRoboticArmSubsequence(product, roboticArmModelUuid);
if (!result) return null;
const hasActiveRoboticArm = result.currentSubSequence.some(event => {
if (event.type === 'roboticArm' && event.modelUuid !== roboticArmModelUuid) {
const armBot = getArmBotById(event.modelUuid);
return armBot?.isActive;
}
return false;
});
return {
...result,
hasActiveRoboticArm
};
};
}
// Helper function to get machine sequences (simplified from your example)
function determineExecutionMachineSequences(products: productsSchema): EventsSchema[][] {
const pointToEventMap = new Map<string, EventsSchema>();
const allPoints: PointsScheme[] = [];
// First pass: map points to their corresponding events
products.forEach(product => {
product.eventDatas.forEach(event => {
if (event.type === 'transfer') {
event.points.forEach(point => {
pointToEventMap.set(point.uuid, event);
allPoints.push(point);
});
} else if (
event.type === 'vehicle' ||
event.type === 'machine' ||
event.type === 'storageUnit' ||
event.type === 'roboticArm'
) {
pointToEventMap.set(event.point.uuid, event);
allPoints.push(event.point);
}
});
});
// Build dependency graph
const dependencyGraph = new Map<string, string[]>();
const triggeredPoints = new Set<string>();
allPoints.forEach(point => {
const triggers = extractTriggersFromPoint(point);
const dependencies: string[] = [];
triggers.forEach(trigger => {
const targetUuid = trigger.triggeredAsset?.triggeredPoint?.pointUuid;
if (targetUuid && pointToEventMap.has(targetUuid)) {
dependencies.push(targetUuid);
triggeredPoints.add(targetUuid);
}
});
dependencyGraph.set(point.uuid, dependencies);
});
// Find root points (points that aren't triggered by others)
const rootPoints = allPoints.filter(point =>
!triggeredPoints.has(point.uuid) &&
dependencyGraph.get(point.uuid)?.length
);
const executionSequences: EventsSchema[][] = [];
function buildSequence(startUuid: string): EventsSchema[] {
const sequence: EventsSchema[] = [];
const visited = new Set<string>();
function traverse(uuid: string) {
if (visited.has(uuid)) return;
visited.add(uuid);
const event = pointToEventMap.get(uuid);
if (event && !sequence.includes(event)) {
sequence.push(event);
}
const nextPoints = dependencyGraph.get(uuid) || [];
nextPoints.forEach(nextUuid => traverse(nextUuid));
}
traverse(startUuid);
return sequence;
}
// Build sequences from root points
rootPoints.forEach(root => {
executionSequences.push(buildSequence(root.uuid));
});
return executionSequences;
}

View File

@ -0,0 +1,49 @@
import { getConveyorSequencesInProduct } from "./getConveyorSequencesForProduct";
export function findConveyorInSequences(
product: {
productName: string;
productId: string;
eventDatas: EventsSchema[];
},
conveyorUuid: string
): {
allSequences: EventsSchema[][][];
parentSequence: EventsSchema[][];
currentSubSequence: EventsSchema[];
} | null {
// Get all conveyor sequences
const allSequences = getConveyorSequencesInProduct(product);
// Search through all sequences
for (const parentSequence of allSequences) {
for (const currentSubSequence of parentSequence) {
for (const conveyor of currentSubSequence) {
// Check if this is the conveyor we're looking for
if (conveyor.modelUuid === conveyorUuid) {
return {
allSequences,
parentSequence,
currentSubSequence
};
}
// Also check points in case the UUID matches a point's conveyor
if (conveyor.type === 'transfer') {
for (const point of conveyor.points) {
if (point.uuid === conveyorUuid) {
return {
allSequences,
parentSequence,
currentSubSequence
};
}
}
}
}
}
}
// Conveyor not found
return null;
}

View File

@ -0,0 +1,126 @@
import { extractTriggersFromPoint } from "./extractTriggersFromPoint";
export function getConveyorSequencesInProduct(
product: {
productName: string;
productId: string;
eventDatas: EventsSchema[];
}
): EventsSchema[][][] { // Now returns array of array of arrays
// Get all machine sequences for this product
const machineSequences = determineExecutionMachineSequences([product]);
const allConveyorSequences: EventsSchema[][][] = [];
// Process each machine sequence separately
for (const machineSequence of machineSequences) {
const conveyorSequencesForThisMachineSequence: EventsSchema[][] = [];
let currentConveyorSequence: EventsSchema[] = [];
for (const event of machineSequence) {
if (event.type === 'transfer') {
// Add conveyor to current sequence
currentConveyorSequence.push(event);
} else if (event.type === 'vehicle') {
// Vehicle encountered - split the sequence
if (currentConveyorSequence.length > 0) {
conveyorSequencesForThisMachineSequence.push([...currentConveyorSequence]);
currentConveyorSequence = [];
}
}
// Other machine types don't affect the conveyor sequence
}
// Add any remaining conveyors in the current sequence
if (currentConveyorSequence.length > 0) {
conveyorSequencesForThisMachineSequence.push([...currentConveyorSequence]);
}
if (conveyorSequencesForThisMachineSequence.length > 0) {
allConveyorSequences.push(conveyorSequencesForThisMachineSequence);
}
}
return allConveyorSequences;
}
// Helper function to get machine sequences (simplified from your example)
function determineExecutionMachineSequences(products: productsSchema): EventsSchema[][] {
const pointToEventMap = new Map<string, EventsSchema>();
const allPoints: PointsScheme[] = [];
// First pass: map points to their corresponding events
products.forEach(product => {
product.eventDatas.forEach(event => {
if (event.type === 'transfer') {
event.points.forEach(point => {
pointToEventMap.set(point.uuid, event);
allPoints.push(point);
});
} else if (
event.type === 'vehicle' ||
event.type === 'machine' ||
event.type === 'storageUnit' ||
event.type === 'roboticArm'
) {
pointToEventMap.set(event.point.uuid, event);
allPoints.push(event.point);
}
});
});
// Build dependency graph
const dependencyGraph = new Map<string, string[]>();
const triggeredPoints = new Set<string>();
allPoints.forEach(point => {
const triggers = extractTriggersFromPoint(point);
const dependencies: string[] = [];
triggers.forEach(trigger => {
const targetUuid = trigger.triggeredAsset?.triggeredPoint?.pointUuid;
if (targetUuid && pointToEventMap.has(targetUuid)) {
dependencies.push(targetUuid);
triggeredPoints.add(targetUuid);
}
});
dependencyGraph.set(point.uuid, dependencies);
});
// Find root points (points that aren't triggered by others)
const rootPoints = allPoints.filter(point =>
!triggeredPoints.has(point.uuid) &&
dependencyGraph.get(point.uuid)?.length
);
const executionSequences: EventsSchema[][] = [];
function buildSequence(startUuid: string): EventsSchema[] {
const sequence: EventsSchema[] = [];
const visited = new Set<string>();
function traverse(uuid: string) {
if (visited.has(uuid)) return;
visited.add(uuid);
const event = pointToEventMap.get(uuid);
if (event && !sequence.includes(event)) {
sequence.push(event);
}
const nextPoints = dependencyGraph.get(uuid) || [];
nextPoints.forEach(nextUuid => traverse(nextUuid));
}
traverse(startUuid);
return sequence;
}
// Build sequences from root points
rootPoints.forEach(root => {
executionSequences.push(buildSequence(root.uuid));
});
return executionSequences;
}

View File

@ -138,6 +138,7 @@ export function useTriggerHandler() {
if (armBot.isActive === false && armBot.state === 'idle') { if (armBot.isActive === false && armBot.state === 'idle') {
// Handle current action from arm bot // Handle current action from arm bot
setIsPaused(materialId, true);
handleAction(action, materialId); handleAction(action, materialId);
} else { } else {
@ -291,6 +292,9 @@ export function useTriggerHandler() {
// Machine to Robotic Arm // Machine to Robotic Arm
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialById(materialId); const material = getMaterialById(materialId);
setIsPaused(materialId, true);
if (material) { if (material) {
const action = getActionByUuid(selectedProduct.productId, trigger.triggeredAsset.triggeredAction.actionUuid); const action = getActionByUuid(selectedProduct.productId, trigger.triggeredAsset.triggeredAction.actionUuid);
const armBot = getArmBotById(trigger.triggeredAsset?.triggeredModel.modelUuid); const armBot = getArmBotById(trigger.triggeredAsset?.triggeredModel.modelUuid);
@ -302,7 +306,7 @@ export function useTriggerHandler() {
}) })
setCurrentLocation(material.materialId, { setCurrentLocation(material.materialId, {
modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid, modelUuid: material.current.modelUuid,
pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid, actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
}); });
@ -338,7 +342,7 @@ export function useTriggerHandler() {
const material = getMaterialById(materialId); const material = getMaterialById(materialId);
if (material) { if (material) {
setIsPaused(material.materialId, false); // setIsPaused(material.materialId, false);
setPreviousLocation(material.materialId, { setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid, modelUuid: material.current.modelUuid,
@ -477,7 +481,7 @@ export function useTriggerHandler() {
const material = getMaterialById(materialId); const material = getMaterialById(materialId);
if (material) { if (material) {
setIsPaused(material.materialId, false); // setIsPaused(material.materialId, false);
const action = getActionByUuid(selectedProduct.productId, trigger.triggeredAsset.triggeredAction.actionUuid); const action = getActionByUuid(selectedProduct.productId, trigger.triggeredAsset.triggeredAction.actionUuid);
const machine = getMachineById(trigger.triggeredAsset?.triggeredModel.modelUuid); const machine = getMachineById(trigger.triggeredAsset?.triggeredModel.modelUuid);
@ -498,7 +502,7 @@ export function useTriggerHandler() {
}) })
setCurrentLocation(material.materialId, { setCurrentLocation(material.materialId, {
modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid, modelUuid: material.current.modelUuid,
pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid, actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
}); });

View File

@ -280,26 +280,34 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
unLoadDuration: number, unLoadDuration: number,
action: VehicleAction action: VehicleAction
) { ) {
startTime = performance.now(); let lastIncrementTime = performance.now();
const fixedInterval = unLoadDuration * (1000 / speed); let pauseStartTime: number | null = null;
let totalPausedDuration = 0;
const fixedInterval = (unLoadDuration * 1000) / speed;
const dropLoop = () => { const dropLoop = (currentTime: number) => {
if (isPausedRef.current) { const conveyor = getConveyorById(conveyorId);
pauseTimeRef.current ??= performance.now();
if (isPausedRef.current || (conveyor && conveyor.isPaused)) {
if (pauseStartTime === null) {
pauseStartTime = currentTime;
}
requestAnimationFrame(dropLoop); requestAnimationFrame(dropLoop);
return; return;
} }
if (pauseTimeRef.current) { // If we were paused but now resumed
const pauseDuration = performance.now() - pauseTimeRef.current; if (pauseStartTime !== null) {
startTime += pauseDuration; totalPausedDuration += currentTime - pauseStartTime;
pauseTimeRef.current = null; pauseStartTime = null;
} }
const elapsedTime = performance.now() - startTime; // Adjust for paused time
const conveyor = getConveyorById(conveyorId); const adjustedCurrentTime = currentTime - totalPausedDuration;
if (elapsedTime >= fixedInterval) { const elapsedSinceLastIncrement = adjustedCurrentTime - lastIncrementTime;
if (conveyor && !conveyor.isPaused && vehicleCurrentLoad > 0) {
if (elapsedSinceLastIncrement >= fixedInterval) {
if (conveyor && vehicleCurrentLoad > 0) {
decrementVehicleLoad(vehicleId, 1); decrementVehicleLoad(vehicleId, 1);
vehicleCurrentLoad -= 1; vehicleCurrentLoad -= 1;
@ -308,18 +316,18 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
triggerPointActions(action, material.materialId); triggerPointActions(action, material.materialId);
} }
// Update the last increment time (using adjusted time)
lastIncrementTime = adjustedCurrentTime;
}
}
// Continue the loop if there's more load to drop
if (vehicleCurrentLoad > 0) { if (vehicleCurrentLoad > 0) {
startTime = performance.now();
requestAnimationFrame(dropLoop);
}
} else if (!conveyor?.isActive) {
requestAnimationFrame(dropLoop);
}
} else {
requestAnimationFrame(dropLoop); requestAnimationFrame(dropLoop);
} }
}; };
dropLoop();
requestAnimationFrame(dropLoop);
} }
function handleMaterialDropToArmBot(model: RoboticArmEventSchema) { function handleMaterialDropToArmBot(model: RoboticArmEventSchema) {