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 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,29 +204,33 @@ export default function Builder() {
|
||||||
<>
|
<>
|
||||||
<Ground grid={grid} plane={plane} />
|
<Ground grid={grid} plane={plane} />
|
||||||
|
|
||||||
<DistanceText key={toggleView} />
|
<Bvh firstHitOnly>
|
||||||
|
<DistanceText key={toggleView} />
|
||||||
|
</Bvh>
|
||||||
|
|
||||||
<ReferenceDistanceText
|
<ReferenceDistanceText
|
||||||
key={refTextupdate}
|
key={refTextupdate}
|
||||||
line={ReferenceLineMesh.current}
|
line={ReferenceLineMesh.current}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SocketResponses
|
<Bvh firstHitOnly>
|
||||||
floorPlanGroup={floorPlanGroup}
|
<SocketResponses
|
||||||
lines={lines}
|
floorPlanGroup={floorPlanGroup}
|
||||||
floorGroup={floorGroup}
|
lines={lines}
|
||||||
floorGroupAisle={floorGroupAisle}
|
floorGroup={floorGroup}
|
||||||
scene={scene}
|
floorGroupAisle={floorGroupAisle}
|
||||||
onlyFloorlines={onlyFloorlines}
|
scene={scene}
|
||||||
itemsGroup={itemsGroup}
|
onlyFloorlines={onlyFloorlines}
|
||||||
isTempLoader={isTempLoader}
|
itemsGroup={itemsGroup}
|
||||||
tempLoader={tempLoader}
|
isTempLoader={isTempLoader}
|
||||||
currentLayerPoint={currentLayerPoint}
|
tempLoader={tempLoader}
|
||||||
floorPlanGroupPoint={floorPlanGroupPoint}
|
currentLayerPoint={currentLayerPoint}
|
||||||
floorPlanGroupLine={floorPlanGroupLine}
|
floorPlanGroupPoint={floorPlanGroupPoint}
|
||||||
zoneGroup={zoneGroup}
|
floorPlanGroupLine={floorPlanGroupLine}
|
||||||
dragPointControls={dragPointControls}
|
zoneGroup={zoneGroup}
|
||||||
/>
|
dragPointControls={dragPointControls}
|
||||||
|
/>
|
||||||
|
</Bvh>
|
||||||
|
|
||||||
<WallsAndWallItems
|
<WallsAndWallItems
|
||||||
CSGGroup={CSGGroup}
|
CSGGroup={CSGGroup}
|
||||||
|
@ -237,78 +242,80 @@ export default function Builder() {
|
||||||
hoveredDeletableWallItem={hoveredDeletableWallItem}
|
hoveredDeletableWallItem={hoveredDeletableWallItem}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FloorItemsGroup
|
<Bvh firstHitOnly>
|
||||||
itemsGroup={itemsGroup}
|
<FloorItemsGroup
|
||||||
hoveredDeletableFloorItem={hoveredDeletableFloorItem}
|
itemsGroup={itemsGroup}
|
||||||
AttachedObject={AttachedObject}
|
hoveredDeletableFloorItem={hoveredDeletableFloorItem}
|
||||||
floorGroup={floorGroup}
|
AttachedObject={AttachedObject}
|
||||||
tempLoader={tempLoader}
|
floorGroup={floorGroup}
|
||||||
isTempLoader={isTempLoader}
|
tempLoader={tempLoader}
|
||||||
plane={plane}
|
isTempLoader={isTempLoader}
|
||||||
/>
|
plane={plane}
|
||||||
|
/>
|
||||||
|
|
||||||
<FloorGroup
|
<FloorGroup
|
||||||
floorGroup={floorGroup}
|
floorGroup={floorGroup}
|
||||||
lines={lines}
|
lines={lines}
|
||||||
referencePole={referencePole}
|
referencePole={referencePole}
|
||||||
hoveredDeletablePillar={hoveredDeletablePillar}
|
hoveredDeletablePillar={hoveredDeletablePillar}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FloorPlanGroup
|
<FloorPlanGroup
|
||||||
floorPlanGroup={floorPlanGroup}
|
floorPlanGroup={floorPlanGroup}
|
||||||
floorPlanGroupLine={floorPlanGroupLine}
|
floorPlanGroupLine={floorPlanGroupLine}
|
||||||
floorPlanGroupPoint={floorPlanGroupPoint}
|
floorPlanGroupPoint={floorPlanGroupPoint}
|
||||||
floorGroup={floorGroup}
|
floorGroup={floorGroup}
|
||||||
currentLayerPoint={currentLayerPoint}
|
currentLayerPoint={currentLayerPoint}
|
||||||
dragPointControls={dragPointControls}
|
dragPointControls={dragPointControls}
|
||||||
hoveredDeletablePoint={hoveredDeletablePoint}
|
hoveredDeletablePoint={hoveredDeletablePoint}
|
||||||
hoveredDeletableLine={hoveredDeletableLine}
|
hoveredDeletableLine={hoveredDeletableLine}
|
||||||
plane={plane}
|
plane={plane}
|
||||||
line={line}
|
line={line}
|
||||||
lines={lines}
|
lines={lines}
|
||||||
onlyFloorline={onlyFloorline}
|
onlyFloorline={onlyFloorline}
|
||||||
onlyFloorlines={onlyFloorlines}
|
onlyFloorlines={onlyFloorlines}
|
||||||
ReferenceLineMesh={ReferenceLineMesh}
|
ReferenceLineMesh={ReferenceLineMesh}
|
||||||
LineCreated={LineCreated}
|
LineCreated={LineCreated}
|
||||||
isSnapped={isSnapped}
|
isSnapped={isSnapped}
|
||||||
ispreSnapped={ispreSnapped}
|
ispreSnapped={ispreSnapped}
|
||||||
snappedPoint={snappedPoint}
|
snappedPoint={snappedPoint}
|
||||||
isSnappedUUID={isSnappedUUID}
|
isSnappedUUID={isSnappedUUID}
|
||||||
isAngleSnapped={isAngleSnapped}
|
isAngleSnapped={isAngleSnapped}
|
||||||
anglesnappedPoint={anglesnappedPoint}
|
anglesnappedPoint={anglesnappedPoint}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ZoneGroup />
|
<ZoneGroup />
|
||||||
|
|
||||||
<FloorGroupAilse
|
<FloorGroupAilse
|
||||||
floorGroupAisle={floorGroupAisle}
|
floorGroupAisle={floorGroupAisle}
|
||||||
plane={plane}
|
plane={plane}
|
||||||
floorPlanGroupLine={floorPlanGroupLine}
|
floorPlanGroupLine={floorPlanGroupLine}
|
||||||
floorPlanGroupPoint={floorPlanGroupPoint}
|
floorPlanGroupPoint={floorPlanGroupPoint}
|
||||||
line={line}
|
line={line}
|
||||||
lines={lines}
|
lines={lines}
|
||||||
currentLayerPoint={currentLayerPoint}
|
currentLayerPoint={currentLayerPoint}
|
||||||
dragPointControls={dragPointControls}
|
dragPointControls={dragPointControls}
|
||||||
floorPlanGroup={floorPlanGroup}
|
floorPlanGroup={floorPlanGroup}
|
||||||
ReferenceLineMesh={ReferenceLineMesh}
|
ReferenceLineMesh={ReferenceLineMesh}
|
||||||
LineCreated={LineCreated}
|
LineCreated={LineCreated}
|
||||||
isSnapped={isSnapped}
|
isSnapped={isSnapped}
|
||||||
ispreSnapped={ispreSnapped}
|
ispreSnapped={ispreSnapped}
|
||||||
snappedPoint={snappedPoint}
|
snappedPoint={snappedPoint}
|
||||||
isSnappedUUID={isSnappedUUID}
|
isSnappedUUID={isSnappedUUID}
|
||||||
isAngleSnapped={isAngleSnapped}
|
isAngleSnapped={isAngleSnapped}
|
||||||
anglesnappedPoint={anglesnappedPoint}
|
anglesnappedPoint={anglesnappedPoint}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* <AssetsGroup /> */}
|
{/* <AssetsGroup /> */}
|
||||||
|
|
||||||
<MeasurementTool />
|
<MeasurementTool />
|
||||||
|
|
||||||
<CalculateAreaGroup />
|
<CalculateAreaGroup />
|
||||||
|
|
||||||
<NavMesh lines={lines} />
|
<NavMesh lines={lines} />
|
||||||
|
|
||||||
<LayoutImage />
|
<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}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -2,50 +2,37 @@ import { useTileDistance, useToggleView } from "../../../store/builder/store";
|
||||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||||
|
|
||||||
const Ground = ({ grid, plane }: any) => {
|
const Ground = ({ grid, plane }: any) => {
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
const { planeValue, gridValue } = useTileDistance();
|
const { planeValue, gridValue } = useTileDistance();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<mesh name="Ground">
|
<mesh name="Ground">
|
||||||
<mesh
|
<mesh
|
||||||
ref={grid}
|
ref={grid}
|
||||||
name="Grid"
|
name="Grid"
|
||||||
position={
|
position={!toggleView ? CONSTANTS.gridConfig.position3D : CONSTANTS.gridConfig.position2D}
|
||||||
!toggleView
|
>
|
||||||
? CONSTANTS.gridConfig.position3D
|
<gridHelper
|
||||||
: CONSTANTS.gridConfig.position2D
|
args={[
|
||||||
}
|
gridValue.size,
|
||||||
>
|
gridValue.divisions,
|
||||||
<gridHelper
|
CONSTANTS.gridConfig.primaryColor,
|
||||||
args={[
|
CONSTANTS.gridConfig.secondaryColor,
|
||||||
gridValue.size,
|
]}
|
||||||
gridValue.divisions,
|
/>
|
||||||
// CONSTANTS.gridConfig.size,
|
</mesh>
|
||||||
// CONSTANTS.gridConfig.divisions,
|
<mesh
|
||||||
CONSTANTS.gridConfig.primaryColor,
|
ref={plane}
|
||||||
CONSTANTS.gridConfig.secondaryColor,
|
rotation-x={CONSTANTS.planeConfig.rotation}
|
||||||
]}
|
position={!toggleView ? CONSTANTS.planeConfig.position3D : CONSTANTS.planeConfig.position2D}
|
||||||
/>
|
name="Plane"
|
||||||
</mesh>
|
receiveShadow
|
||||||
<mesh
|
>
|
||||||
ref={plane}
|
<planeGeometry args={[planeValue.width, planeValue.height]} />
|
||||||
rotation-x={CONSTANTS.planeConfig.rotation}
|
<meshBasicMaterial color={CONSTANTS.planeConfig.color} />
|
||||||
position={
|
</mesh>
|
||||||
!toggleView
|
</mesh>
|
||||||
? CONSTANTS.planeConfig.position3D
|
);
|
||||||
: CONSTANTS.planeConfig.position2D
|
|
||||||
}
|
|
||||||
name="Plane"
|
|
||||||
receiveShadow
|
|
||||||
>
|
|
||||||
<planeGeometry
|
|
||||||
args={[planeValue.width, planeValue.height]}
|
|
||||||
// args={[CONSTANTS.planeConfig.width, CONSTANTS.planeConfig.height]}
|
|
||||||
/>
|
|
||||||
<meshBasicMaterial color={CONSTANTS.planeConfig.color} />
|
|
||||||
</mesh>
|
|
||||||
</mesh>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Ground;
|
export default Ground;
|
||||||
|
|
|
@ -2,111 +2,105 @@ import { useRef, useEffect } from "react";
|
||||||
import { useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import {
|
import {
|
||||||
useAzimuth,
|
useAzimuth,
|
||||||
useElevation,
|
useElevation,
|
||||||
useShadows,
|
useShadows,
|
||||||
useSunPosition,
|
useSunPosition,
|
||||||
useFloorItems,
|
useFloorItems,
|
||||||
useWallItems,
|
useWallItems,
|
||||||
useTileDistance,
|
useTileDistance,
|
||||||
} from "../../../store/builder/store";
|
} from "../../../store/builder/store";
|
||||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||||
const shadowWorker = new Worker(
|
const shadowWorker = new Worker(
|
||||||
new URL(
|
new URL(
|
||||||
"../../../services/factoryBuilder/webWorkers/shadowWorker",
|
"../../../services/factoryBuilder/webWorkers/shadowWorker",
|
||||||
import.meta.url
|
import.meta.url
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
export default function Shadows() {
|
export default function Shadows() {
|
||||||
const { shadows, setShadows } = useShadows();
|
const { shadows, setShadows } = useShadows();
|
||||||
const { sunPosition, setSunPosition } = useSunPosition();
|
const { sunPosition, setSunPosition } = useSunPosition();
|
||||||
const lightRef = useRef<THREE.DirectionalLight | null>(null);
|
const lightRef = useRef<THREE.DirectionalLight | null>(null);
|
||||||
const targetRef = useRef<THREE.Object3D | null>(null);
|
const targetRef = useRef<THREE.Object3D | null>(null);
|
||||||
const { controls, gl } = useThree();
|
const { controls, gl } = useThree();
|
||||||
const { elevation, setElevation } = useElevation();
|
const { elevation, setElevation } = useElevation();
|
||||||
const { azimuth, setAzimuth } = useAzimuth();
|
const { azimuth, setAzimuth } = useAzimuth();
|
||||||
const { floorItems } = useFloorItems();
|
const { floorItems } = useFloorItems();
|
||||||
const { wallItems } = useWallItems();
|
const { wallItems } = useWallItems();
|
||||||
const { planeValue } = useTileDistance();
|
const { planeValue } = useTileDistance();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
gl.shadowMap.enabled = true;
|
gl.shadowMap.enabled = true;
|
||||||
gl.shadowMap.type = THREE.PCFShadowMap;
|
gl.shadowMap.type = THREE.PCFShadowMap;
|
||||||
}, [gl, floorItems, wallItems]);
|
}, [gl, floorItems, wallItems]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (lightRef.current && targetRef.current) {
|
if (lightRef.current && targetRef.current) {
|
||||||
lightRef.current.target = targetRef.current;
|
lightRef.current.target = targetRef.current;
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
shadowWorker.onmessage = (event) => {
|
shadowWorker.onmessage = (event) => {
|
||||||
const { lightPosition, controlsTarget } = event.data;
|
const { lightPosition, controlsTarget } = event.data;
|
||||||
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]);
|
||||||
|
|
||||||
|
const updateShadows = () => {
|
||||||
|
if (controls && shadowWorker) {
|
||||||
|
const offsetDistance = CONSTANTS.shadowConfig.shadowOffset;
|
||||||
|
const controlsTarget = (controls as any).getTarget();
|
||||||
|
shadowWorker.postMessage({ controlsTarget, sunPosition, offsetDistance });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, [shadowWorker, controls]);
|
|
||||||
|
|
||||||
const updateShadows = () => {
|
useEffect(() => {
|
||||||
if (controls && shadowWorker) {
|
if (controls && shadows) {
|
||||||
const offsetDistance = CONSTANTS.shadowConfig.shadowOffset;
|
updateShadows();
|
||||||
const controlsTarget = (controls as any).getTarget();
|
(controls as any).addEventListener("update", updateShadows);
|
||||||
shadowWorker.postMessage({ controlsTarget, sunPosition, offsetDistance });
|
return () => {
|
||||||
}
|
(controls as any).removeEventListener("update", updateShadows);
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
}, [controls, elevation, azimuth, shadows]);
|
||||||
|
|
||||||
useEffect(() => {
|
return (
|
||||||
if (controls && shadows) {
|
<>
|
||||||
updateShadows();
|
{/* {(lightRef.current?.shadow) &&
|
||||||
(controls as any).addEventListener("update", updateShadows);
|
<cameraHelper visible={shadows} args={[lightRef.current.shadow.camera]} />
|
||||||
return () => {
|
} */}
|
||||||
(controls as any).removeEventListener("update", updateShadows);
|
<directionalLight
|
||||||
};
|
ref={lightRef}
|
||||||
}
|
castShadow={shadows}
|
||||||
}, [controls, elevation, azimuth, shadows]);
|
shadow-mapSize-width={CONSTANTS.shadowConfig.shadowmapSizewidth}
|
||||||
|
shadow-mapSize-height={CONSTANTS.shadowConfig.shadowmapSizeheight}
|
||||||
return (
|
shadow-camera-far={CONSTANTS.shadowConfig.shadowcamerafar}
|
||||||
<>
|
shadow-camera-near={CONSTANTS.shadowConfig.shadowcameranear}
|
||||||
{/* {(lightRef.current?.shadow) &&
|
shadow-camera-top={CONSTANTS.shadowConfig.shadowcameratop}
|
||||||
<cameraHelper visible={shadows} args={[lightRef.current.shadow.camera]} />
|
shadow-camera-bottom={CONSTANTS.shadowConfig.shadowcamerabottom}
|
||||||
} */}
|
shadow-camera-left={CONSTANTS.shadowConfig.shadowcameraleft}
|
||||||
<directionalLight
|
shadow-camera-right={CONSTANTS.shadowConfig.shadowcameraright}
|
||||||
ref={lightRef}
|
shadow-bias={CONSTANTS.shadowConfig.shadowbias}
|
||||||
castShadow={shadows}
|
shadow-normalBias={CONSTANTS.shadowConfig.shadownormalBias}
|
||||||
shadow-mapSize-width={CONSTANTS.shadowConfig.shadowmapSizewidth}
|
/>
|
||||||
shadow-mapSize-height={CONSTANTS.shadowConfig.shadowmapSizeheight}
|
<object3D ref={targetRef} />
|
||||||
shadow-camera-far={CONSTANTS.shadowConfig.shadowcamerafar}
|
<mesh
|
||||||
shadow-camera-near={CONSTANTS.shadowConfig.shadowcameranear}
|
position={CONSTANTS.shadowConfig.shadowMaterialPosition}
|
||||||
shadow-camera-top={CONSTANTS.shadowConfig.shadowcameratop}
|
rotation={CONSTANTS.shadowConfig.shadowMaterialRotation}
|
||||||
shadow-camera-bottom={CONSTANTS.shadowConfig.shadowcamerabottom}
|
receiveShadow
|
||||||
shadow-camera-left={CONSTANTS.shadowConfig.shadowcameraleft}
|
>
|
||||||
shadow-camera-right={CONSTANTS.shadowConfig.shadowcameraright}
|
<planeGeometry args={[planeValue.width, planeValue.height]} />
|
||||||
shadow-bias={CONSTANTS.shadowConfig.shadowbias}
|
<shadowMaterial
|
||||||
shadow-normalBias={CONSTANTS.shadowConfig.shadownormalBias}
|
opacity={CONSTANTS.shadowConfig.shadowMaterialOpacity}
|
||||||
/>
|
transparent
|
||||||
<object3D ref={targetRef} />
|
/>
|
||||||
<mesh
|
</mesh>
|
||||||
position={CONSTANTS.shadowConfig.shadowMaterialPosition}
|
</>
|
||||||
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}
|
|
||||||
transparent
|
|
||||||
/>
|
|
||||||
</mesh>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,35 +9,32 @@ 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}>
|
||||||
<Canvas
|
<Canvas
|
||||||
eventPrefix="client"
|
eventPrefix="client"
|
||||||
gl={{ powerPreference: "high-performance", antialias: true }}
|
gl={{ powerPreference: "high-performance", antialias: true }}
|
||||||
onContextMenu={(e) => {
|
onContextMenu={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Setup />
|
<Setup />
|
||||||
|
|
||||||
<Collaboration />
|
<Collaboration />
|
||||||
|
|
||||||
<Builder />
|
<Builder />
|
||||||
|
|
||||||
<Simulation />
|
<Simulation />
|
||||||
|
|
||||||
<Visualization />
|
<Visualization />
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</KeyboardControls>
|
</KeyboardControls>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
@ -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);
|
|
@ -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) {
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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') {
|
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,
|
||||||
});
|
});
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vehicleCurrentLoad > 0) {
|
// Update the last increment time (using adjusted time)
|
||||||
startTime = performance.now();
|
lastIncrementTime = adjustedCurrentTime;
|
||||||
requestAnimationFrame(dropLoop);
|
|
||||||
}
|
|
||||||
} else if (!conveyor?.isActive) {
|
|
||||||
requestAnimationFrame(dropLoop);
|
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
// Continue the loop if there's more load to drop
|
||||||
|
if (vehicleCurrentLoad > 0) {
|
||||||
requestAnimationFrame(dropLoop);
|
requestAnimationFrame(dropLoop);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
dropLoop();
|
|
||||||
|
requestAnimationFrame(dropLoop);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMaterialDropToArmBot(model: RoboticArmEventSchema) {
|
function handleMaterialDropToArmBot(model: RoboticArmEventSchema) {
|
||||||
|
|
Loading…
Reference in New Issue