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:
parent
f37750023f
commit
b7a5908884
|
@ -48,6 +48,7 @@ import NavMesh from "../simulation/vehicle/navMesh/navMesh";
|
|||
import CalculateAreaGroup from "./groups/calculateAreaGroup";
|
||||
import LayoutImage from "./layout/layoutImage";
|
||||
import AssetsGroup from "./assetGroup/assetsGroup";
|
||||
import { Bvh } from "@react-three/drei";
|
||||
|
||||
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.
|
||||
|
@ -203,13 +204,16 @@ export default function Builder() {
|
|||
<>
|
||||
<Ground grid={grid} plane={plane} />
|
||||
|
||||
<Bvh firstHitOnly>
|
||||
<DistanceText key={toggleView} />
|
||||
</Bvh>
|
||||
|
||||
<ReferenceDistanceText
|
||||
key={refTextupdate}
|
||||
line={ReferenceLineMesh.current}
|
||||
/>
|
||||
|
||||
<Bvh firstHitOnly>
|
||||
<SocketResponses
|
||||
floorPlanGroup={floorPlanGroup}
|
||||
lines={lines}
|
||||
|
@ -226,6 +230,7 @@ export default function Builder() {
|
|||
zoneGroup={zoneGroup}
|
||||
dragPointControls={dragPointControls}
|
||||
/>
|
||||
</Bvh>
|
||||
|
||||
<WallsAndWallItems
|
||||
CSGGroup={CSGGroup}
|
||||
|
@ -237,6 +242,7 @@ export default function Builder() {
|
|||
hoveredDeletableWallItem={hoveredDeletableWallItem}
|
||||
/>
|
||||
|
||||
<Bvh firstHitOnly>
|
||||
<FloorItemsGroup
|
||||
itemsGroup={itemsGroup}
|
||||
hoveredDeletableFloorItem={hoveredDeletableFloorItem}
|
||||
|
@ -309,6 +315,7 @@ export default function Builder() {
|
|||
<NavMesh lines={lines} />
|
||||
|
||||
<LayoutImage />
|
||||
</Bvh>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -10,18 +10,12 @@ const Ground = ({ grid, plane }: any) => {
|
|||
<mesh
|
||||
ref={grid}
|
||||
name="Grid"
|
||||
position={
|
||||
!toggleView
|
||||
? CONSTANTS.gridConfig.position3D
|
||||
: CONSTANTS.gridConfig.position2D
|
||||
}
|
||||
position={!toggleView ? CONSTANTS.gridConfig.position3D : CONSTANTS.gridConfig.position2D}
|
||||
>
|
||||
<gridHelper
|
||||
args={[
|
||||
gridValue.size,
|
||||
gridValue.divisions,
|
||||
// CONSTANTS.gridConfig.size,
|
||||
// CONSTANTS.gridConfig.divisions,
|
||||
CONSTANTS.gridConfig.primaryColor,
|
||||
CONSTANTS.gridConfig.secondaryColor,
|
||||
]}
|
||||
|
@ -30,18 +24,11 @@ const Ground = ({ grid, plane }: any) => {
|
|||
<mesh
|
||||
ref={plane}
|
||||
rotation-x={CONSTANTS.planeConfig.rotation}
|
||||
position={
|
||||
!toggleView
|
||||
? CONSTANTS.planeConfig.position3D
|
||||
: CONSTANTS.planeConfig.position2D
|
||||
}
|
||||
position={!toggleView ? CONSTANTS.planeConfig.position3D : CONSTANTS.planeConfig.position2D}
|
||||
name="Plane"
|
||||
receiveShadow
|
||||
>
|
||||
<planeGeometry
|
||||
args={[planeValue.width, planeValue.height]}
|
||||
// args={[CONSTANTS.planeConfig.width, CONSTANTS.planeConfig.height]}
|
||||
/>
|
||||
<planeGeometry args={[planeValue.width, planeValue.height]} />
|
||||
<meshBasicMaterial color={CONSTANTS.planeConfig.color} />
|
||||
</mesh>
|
||||
</mesh>
|
||||
|
|
|
@ -47,6 +47,7 @@ export default function Shadows() {
|
|||
if (lightRef.current && targetRef.current && controls) {
|
||||
lightRef.current.position.copy(lightPosition);
|
||||
targetRef.current.position.copy(controlsTarget);
|
||||
gl.shadowMap.needsUpdate = true;
|
||||
}
|
||||
};
|
||||
}, [shadowWorker, controls]);
|
||||
|
@ -94,13 +95,6 @@ export default function Shadows() {
|
|||
rotation={CONSTANTS.shadowConfig.shadowMaterialRotation}
|
||||
receiveShadow
|
||||
>
|
||||
{/* <planeGeometry
|
||||
args={[CONSTANTS.planeConfig.width, CONSTANTS.planeConfig.height]}
|
||||
/>
|
||||
<shadowMaterial
|
||||
opacity={CONSTANTS.shadowConfig.shadowMaterialOpacity}
|
||||
transparent
|
||||
/> */}
|
||||
<planeGeometry args={[planeValue.width, planeValue.height]} />
|
||||
<shadowMaterial
|
||||
opacity={CONSTANTS.shadowConfig.shadowMaterialOpacity}
|
||||
|
|
|
@ -9,15 +9,12 @@ import Simulation from "../simulation/simulation";
|
|||
import Collaboration from "../collaboration/collaboration";
|
||||
|
||||
export default function Scene() {
|
||||
const map = useMemo(
|
||||
() => [
|
||||
const map = useMemo(() => [
|
||||
{ name: "forward", keys: ["ArrowUp", "w", "W"] },
|
||||
{ name: "backward", keys: ["ArrowDown", "s", "S"] },
|
||||
{ name: "left", keys: ["ArrowLeft", "a", "A"] },
|
||||
{ name: "right", keys: ["ArrowRight", "d", "D"] },
|
||||
],
|
||||
[]
|
||||
);
|
||||
], []);
|
||||
|
||||
return (
|
||||
<KeyboardControls map={map}>
|
||||
|
|
|
@ -5,6 +5,7 @@ import Controls from '../controls/controls';
|
|||
import { Environment } from '@react-three/drei'
|
||||
|
||||
import background from "../../../assets/textures/hdr/mudroadpuresky2k.hdr";
|
||||
import { MovingClouds } from '../clouds/clouds';
|
||||
|
||||
function Setup() {
|
||||
return (
|
||||
|
@ -17,6 +18,8 @@ function Setup() {
|
|||
|
||||
<PostProcessing />
|
||||
|
||||
<MovingClouds />
|
||||
|
||||
<Environment files={background} environmentIntensity={1.5} />
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -2,36 +2,43 @@ import React, { useEffect } from 'react'
|
|||
import { useMaterialStore } from '../../../../../store/simulation/useMaterialStore';
|
||||
import { useConveyorStore } from '../../../../../store/simulation/useConveyorStore';
|
||||
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 }) {
|
||||
const { getProductById } = useProductStore();
|
||||
const { selectedProduct } = useSelectedProduct();
|
||||
const { materials, getMaterialsByCurrentModelUuid } = useMaterialStore();
|
||||
const { isReset } = useResetButtonStore();
|
||||
|
||||
const { setConveyorPaused } = useConveyorStore();
|
||||
|
||||
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);
|
||||
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) {
|
||||
setConveyorPaused(conveyor.modelUuid, true);
|
||||
} else {
|
||||
setConveyorPaused(conveyor.modelUuid, false);
|
||||
currentSubSequence.forEach(subConveyor => {
|
||||
if (subConveyor.type === 'transfer') {
|
||||
setConveyorPaused(subConveyor.modelUuid, shouldPauseSubsequence);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [materials, conveyor.modelUuid, getMaterialsByCurrentModelUuid, setConveyorPaused, isReset, selectedProduct.productId, getProductById]);
|
||||
|
||||
}, [materials, conveyor.modelUuid, getMaterialsByCurrentModelUuid, setConveyorPaused, isReset]);
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('conveyor: ', conveyor);
|
||||
}, [conveyor])
|
||||
|
||||
return (
|
||||
<>
|
||||
</>
|
||||
)
|
||||
return null;
|
||||
}
|
||||
|
||||
export default ConveyorInstance
|
||||
export default React.memo(ConveyorInstance);
|
|
@ -52,7 +52,7 @@ function Products() {
|
|||
});
|
||||
}
|
||||
}
|
||||
}, [selectedProduct, products, isReset]);
|
||||
}, [selectedProduct, products, isReset, isPlaying]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedProduct.productId) {
|
||||
|
@ -66,7 +66,7 @@ function Products() {
|
|||
});
|
||||
}
|
||||
}
|
||||
}, [selectedProduct, products, isReset]);
|
||||
}, [selectedProduct, products, isReset, isPlaying]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedProduct.productId) {
|
||||
|
@ -80,7 +80,7 @@ function Products() {
|
|||
});
|
||||
}
|
||||
}
|
||||
}, [selectedProduct, products, isReset]);
|
||||
}, [selectedProduct, products, isReset, isPlaying]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedProduct.productId) {
|
||||
|
@ -94,7 +94,7 @@ function Products() {
|
|||
});
|
||||
}
|
||||
}
|
||||
}, [selectedProduct, products, isReset]);
|
||||
}, [selectedProduct, products, isReset, isPlaying]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedProduct.productId) {
|
||||
|
|
|
@ -13,6 +13,7 @@ import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore
|
|||
import { useStorageUnitStore } from '../../../../../store/simulation/useStorageUnitStore';
|
||||
import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore';
|
||||
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
|
||||
import { useCheckActiveRoboticArmsInSubsequence } from '../../../simulator/functions/checkActiveRoboticArmsInSubsequence';
|
||||
|
||||
function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
||||
|
||||
|
@ -30,13 +31,16 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||
const { setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore();
|
||||
const { decrementVehicleLoad, removeLastMaterial } = useVehicleStore();
|
||||
const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = useStorageUnitStore();
|
||||
const { setIsVisible, getMaterialById } = useMaterialStore();
|
||||
const { setIsVisible, setIsPaused, getMaterialById } = useMaterialStore();
|
||||
const { selectedProduct } = useSelectedProduct();
|
||||
const { getActionByUuid, getEventByActionUuid, getEventByModelUuid } = useProductStore();
|
||||
const { getActionByUuid, getEventByActionUuid, getEventByModelUuid, getProductById } = useProductStore();
|
||||
const { triggerPointActions } = useTriggerHandler();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { isReset } = useResetButtonStore();
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const checkActiveRoboticArms = useCheckActiveRoboticArmsInSubsequence();
|
||||
|
||||
const lastRemoved = useRef<{ type: string, materialId: string } | null>(null);
|
||||
|
||||
function firstFrame() {
|
||||
startTime = performance.now();
|
||||
|
@ -63,6 +67,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||
removeLastStorageMaterial(previousModel.modelUuid);
|
||||
updateCurrentLoad(previousModel.modelUuid, -1)
|
||||
}
|
||||
lastRemoved.current = { type: previousModel.type, materialId: armBot.currentAction.materialId };
|
||||
} else {
|
||||
setIsVisible(armBot.currentAction.materialId, false);
|
||||
}
|
||||
|
@ -76,11 +81,20 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||
|
||||
if (armBot.currentAction) {
|
||||
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) {
|
||||
const model = getEventByModelUuid(selectedProduct.productId, action?.triggers[0].triggeredAsset?.triggeredModel.modelUuid);
|
||||
if (!model) return;
|
||||
if (model.type === 'transfer') {
|
||||
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 === 'vehicle') {
|
||||
|
@ -287,6 +301,14 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||
setArmBotState(armBot.modelUuid, "idle")
|
||||
setCurrentPhase("rest");
|
||||
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) => {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -138,6 +138,7 @@ export function useTriggerHandler() {
|
|||
if (armBot.isActive === false && armBot.state === 'idle') {
|
||||
|
||||
// Handle current action from arm bot
|
||||
setIsPaused(materialId, true);
|
||||
handleAction(action, materialId);
|
||||
|
||||
} else {
|
||||
|
@ -291,6 +292,9 @@ export function useTriggerHandler() {
|
|||
// Machine to Robotic Arm
|
||||
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
|
||||
const material = getMaterialById(materialId);
|
||||
|
||||
setIsPaused(materialId, true);
|
||||
|
||||
if (material) {
|
||||
const action = getActionByUuid(selectedProduct.productId, trigger.triggeredAsset.triggeredAction.actionUuid);
|
||||
const armBot = getArmBotById(trigger.triggeredAsset?.triggeredModel.modelUuid);
|
||||
|
@ -302,7 +306,7 @@ export function useTriggerHandler() {
|
|||
})
|
||||
|
||||
setCurrentLocation(material.materialId, {
|
||||
modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid,
|
||||
modelUuid: material.current.modelUuid,
|
||||
pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
|
||||
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
|
||||
});
|
||||
|
@ -338,7 +342,7 @@ export function useTriggerHandler() {
|
|||
const material = getMaterialById(materialId);
|
||||
if (material) {
|
||||
|
||||
setIsPaused(material.materialId, false);
|
||||
// setIsPaused(material.materialId, false);
|
||||
|
||||
setPreviousLocation(material.materialId, {
|
||||
modelUuid: material.current.modelUuid,
|
||||
|
@ -477,7 +481,7 @@ export function useTriggerHandler() {
|
|||
const material = getMaterialById(materialId);
|
||||
if (material) {
|
||||
|
||||
setIsPaused(material.materialId, false);
|
||||
// setIsPaused(material.materialId, false);
|
||||
|
||||
const action = getActionByUuid(selectedProduct.productId, trigger.triggeredAsset.triggeredAction.actionUuid);
|
||||
const machine = getMachineById(trigger.triggeredAsset?.triggeredModel.modelUuid);
|
||||
|
@ -498,7 +502,7 @@ export function useTriggerHandler() {
|
|||
})
|
||||
|
||||
setCurrentLocation(material.materialId, {
|
||||
modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid,
|
||||
modelUuid: material.current.modelUuid,
|
||||
pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
|
||||
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
|
||||
});
|
||||
|
|
|
@ -280,26 +280,34 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
|||
unLoadDuration: number,
|
||||
action: VehicleAction
|
||||
) {
|
||||
startTime = performance.now();
|
||||
const fixedInterval = unLoadDuration * (1000 / speed);
|
||||
let lastIncrementTime = performance.now();
|
||||
let pauseStartTime: number | null = null;
|
||||
let totalPausedDuration = 0;
|
||||
const fixedInterval = (unLoadDuration * 1000) / speed;
|
||||
|
||||
const dropLoop = () => {
|
||||
if (isPausedRef.current) {
|
||||
pauseTimeRef.current ??= performance.now();
|
||||
const dropLoop = (currentTime: number) => {
|
||||
const conveyor = getConveyorById(conveyorId);
|
||||
|
||||
if (isPausedRef.current || (conveyor && conveyor.isPaused)) {
|
||||
if (pauseStartTime === null) {
|
||||
pauseStartTime = currentTime;
|
||||
}
|
||||
requestAnimationFrame(dropLoop);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pauseTimeRef.current) {
|
||||
const pauseDuration = performance.now() - pauseTimeRef.current;
|
||||
startTime += pauseDuration;
|
||||
pauseTimeRef.current = null;
|
||||
// If we were paused but now resumed
|
||||
if (pauseStartTime !== null) {
|
||||
totalPausedDuration += currentTime - pauseStartTime;
|
||||
pauseStartTime = null;
|
||||
}
|
||||
|
||||
const elapsedTime = performance.now() - startTime;
|
||||
const conveyor = getConveyorById(conveyorId);
|
||||
if (elapsedTime >= fixedInterval) {
|
||||
if (conveyor && !conveyor.isPaused && vehicleCurrentLoad > 0) {
|
||||
// Adjust for paused time
|
||||
const adjustedCurrentTime = currentTime - totalPausedDuration;
|
||||
const elapsedSinceLastIncrement = adjustedCurrentTime - lastIncrementTime;
|
||||
|
||||
if (elapsedSinceLastIncrement >= fixedInterval) {
|
||||
if (conveyor && vehicleCurrentLoad > 0) {
|
||||
decrementVehicleLoad(vehicleId, 1);
|
||||
vehicleCurrentLoad -= 1;
|
||||
|
||||
|
@ -308,18 +316,18 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
|||
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) {
|
||||
startTime = performance.now();
|
||||
requestAnimationFrame(dropLoop);
|
||||
}
|
||||
} else if (!conveyor?.isActive) {
|
||||
requestAnimationFrame(dropLoop);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(dropLoop);
|
||||
}
|
||||
};
|
||||
dropLoop();
|
||||
|
||||
requestAnimationFrame(dropLoop);
|
||||
}
|
||||
|
||||
function handleMaterialDropToArmBot(model: RoboticArmEventSchema) {
|
||||
|
|
Loading…
Reference in New Issue