pillar Jig half way completed

This commit is contained in:
2025-08-06 18:19:54 +05:30
parent 76f295d9b9
commit a08cec33ab
27 changed files with 1506 additions and 1031 deletions

View File

@@ -17,255 +17,242 @@ import SkeletonUI from "../../templates/SkeletonUI";
// ------------------------------------- // -------------------------------------
interface AssetProp { interface AssetProp {
filename: string; filename: string;
thumbnail?: string; thumbnail?: string;
category: string; category: string;
description?: string; description?: string;
tags: string; tags: string;
url?: string; url?: string;
uploadDate?: number; uploadDate?: number;
isArchieve?: boolean; isArchieve?: boolean;
animated?: boolean; animated?: boolean;
price?: number; price?: number;
CreatedBy?: string; CreatedBy?: string;
} }
interface CategoryListProp { interface CategoryListProp {
assetImage?: string; assetImage?: string;
assetName?: string; assetName?: string;
categoryImage: string; categoryImage: string;
category: string; category: string;
} }
const Assets: React.FC = () => { const Assets: React.FC = () => {
const { setSelectedItem } = useSelectedItem(); const { setSelectedItem } = useSelectedItem();
const [searchValue, setSearchValue] = useState<string>(""); const [searchValue, setSearchValue] = useState<string>("");
const [selectedCategory, setSelectedCategory] = useState<string | null>(null); const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
const [categoryAssets, setCategoryAssets] = useState<AssetProp[]>([]); const [categoryAssets, setCategoryAssets] = useState<AssetProp[]>([]);
const [filtereredAssets, setFiltereredAssets] = useState<AssetProp[]>([]); const [filtereredAssets, setFiltereredAssets] = useState<AssetProp[]>([]);
const [categoryList, setCategoryList] = useState<CategoryListProp[]>([]); const [categoryList, setCategoryList] = useState<CategoryListProp[]>([]);
const [isLoading, setisLoading] = useState<boolean>(false); // Loading state for assets const [isLoading, setisLoading] = useState<boolean>(false); // Loading state for assets
const handleSearchChange = (value: string) => { const handleSearchChange = (value: string) => {
const searchTerm = value.toLowerCase(); const searchTerm = value.toLowerCase();
setSearchValue(value); setSearchValue(value);
if (searchTerm.trim() === "" && !selectedCategory) { if (searchTerm.trim() === "" && !selectedCategory) {
setCategoryAssets([]); setCategoryAssets([]);
return; return;
} }
const filteredModels = filtereredAssets?.filter((model) => { const filteredModels = filtereredAssets?.filter((model) => {
if (!model?.tags || !model?.filename || !model?.category) return false; if (!model?.tags || !model?.filename || !model?.category) return false;
if (searchTerm.startsWith(":") && searchTerm.length > 1) { if (searchTerm.startsWith(":") && searchTerm.length > 1) {
const tagSearchTerm = searchTerm.slice(1); const tagSearchTerm = searchTerm.slice(1);
return model.tags.toLowerCase().includes(tagSearchTerm); return model.tags.toLowerCase().includes(tagSearchTerm);
} else if (selectedCategory) { } else if (selectedCategory) {
return ( return (
model.category model.category
.toLowerCase() .toLowerCase()
.includes(selectedCategory.toLowerCase()) && .includes(selectedCategory.toLowerCase()) &&
model.filename.toLowerCase().includes(searchTerm) model.filename.toLowerCase().includes(searchTerm)
); );
} else { } else {
return model.filename.toLowerCase().includes(searchTerm); return model.filename.toLowerCase().includes(searchTerm);
} }
}); });
setCategoryAssets(filteredModels); setCategoryAssets(filteredModels);
};
useEffect(() => {
const filteredAssets = async () => {
try {
const filt = await fetchAssets();
setFiltereredAssets(filt);
} catch {
echo.error("Filter asset not found");
}
}; };
filteredAssets();
}, [categoryAssets]);
useEffect(() => { useEffect(() => {
setCategoryList([ const filteredAssets = async () => {
{ category: "Fenestration", categoryImage: feneration }, try {
{ category: "Vehicles", categoryImage: vehicle }, const filt = await fetchAssets();
{ category: "Workstation", categoryImage: workStation }, setFiltereredAssets(filt);
{ category: "Machines", categoryImage: machines }, } catch {
{ category: "Workers", categoryImage: worker }, echo.error("Filter asset not found");
{ category: "Storage", categoryImage: storage },
{ category: "Safety", categoryImage: safety },
{ category: "Office", categoryImage: office },
]);
}, []);
const fetchCategoryAssets = async (asset: any) => {
setisLoading(true);
setSelectedCategory(asset);
try {
const res = await getCategoryAsset(asset);
setCategoryAssets(res);
setFiltereredAssets(res);
setisLoading(false); // End loading
// eslint-disable-next-line
} catch (error) {
echo.error("failed to fetch assets");
setisLoading(false);
}
};
return (
<div className="assets-container-main">
<Search onChange={handleSearchChange} />
<div className="assets-list-section">
<section>
{(() => {
if (isLoading) {
return <SkeletonUI type="asset" />; // Show skeleton when loading
} }
if (searchValue) { };
return ( filteredAssets();
<div className="assets-result"> }, [categoryAssets]);
<div className="assets-wrapper">
<div className="searched-content">
<p>Results for {searchValue}</p>
</div>
<div className="assets-container">
{categoryAssets?.map((asset: any, index: number) => (
<div
key={`${index}-${asset.filename}`}
className="assets"
id={asset.filename}
title={asset.filename}
>
<img
src={asset?.thumbnail}
alt={asset.filename}
className="asset-image"
onPointerDown={() => {
setSelectedItem({
name: asset.filename,
id: asset.AssetID,
type:
asset.type === "undefined"
? undefined
: asset.type
});
}}
/>
<div className="asset-name"> useEffect(() => {
{asset.filename setCategoryList([
.split("_") { category: "Fenestration", categoryImage: feneration },
.map( { category: "Vehicles", categoryImage: vehicle },
(word: any) => { category: "Workstation", categoryImage: workStation },
word.charAt(0).toUpperCase() + word.slice(1) { category: "Machines", categoryImage: machines },
) { category: "Workers", categoryImage: worker },
.join(" ")} { category: "Storage", categoryImage: storage },
</div> { category: "Safety", categoryImage: safety },
</div> { category: "Office", categoryImage: office },
))} ]);
</div> }, []);
</div>
</div>
);
}
if (selectedCategory) { const fetchCategoryAssets = async (asset: any) => {
return ( setisLoading(true);
<div className="assets-wrapper"> setSelectedCategory(asset);
<h2> try {
{selectedCategory} const res = await getCategoryAsset(asset);
<button setCategoryAssets(res);
className="back-button" setFiltereredAssets(res);
id="asset-backButtom" setisLoading(false); // End loading
onClick={() => { // eslint-disable-next-line
setSelectedCategory(null); } catch (error) {
setCategoryAssets([]); echo.error("failed to fetch assets");
}} setisLoading(false);
> }
Back };
</button>
</h2>
<div className="assets-container">
{categoryAssets?.map((asset: any, index: number) => (
<div
key={`${index}-${asset}`}
className="assets"
id={asset.filename}
title={asset.filename}
>
<img
src={asset?.thumbnail}
alt={asset.filename}
className="asset-image"
onPointerDown={() => {
setSelectedItem({
name: asset.filename,
id: asset.AssetID,
type:
asset.type === "undefined"
? undefined
: asset.type,
category: asset.category,
subCategory: asset.subCategory
});
}}
/>
<div className="asset-name">
{asset.filename
.split("_")
.map(
(word: any) =>
word.charAt(0).toUpperCase() + word.slice(1)
)
.join(" ")}
</div>
</div>
))}
{categoryAssets.length === 0 && (
<div className="no-asset">
🚧 The asset shelf is empty. We're working on filling it
up!
</div>
)}
</div>
</div>
);
}
return ( return (
<div className="assets-wrapper"> <div className="assets-container-main">
<h2>Categories</h2> <Search onChange={handleSearchChange} />
<div className="categories-container"> <div className="assets-list-section">
{Array.from( <section>
new Set(categoryList.map((asset) => asset.category)) {(() => {
).map((category, index) => { if (isLoading) {
const categoryInfo = categoryList.find( return <SkeletonUI type="asset" />; // Show skeleton when loading
(asset) => asset.category === category }
); if (searchValue) {
return ( return (
<div <div className="assets-result">
key={`${index}-${category}`} <div className="assets-wrapper">
className="category" <div className="searched-content">
id={category} <p>Results for {searchValue}</p>
onClick={() => fetchCategoryAssets(category)} </div>
> <div className="assets-container">
<img {categoryAssets?.map((asset: any, index: number) => (
src={categoryInfo?.categoryImage ?? ""} <div
alt={category} key={`${index}-${asset.filename}`}
className="category-image" className="assets"
draggable={false} id={asset.filename}
/> title={asset.filename}
<div className="category-name">{category}</div> >
</div> <img
); src={asset?.thumbnail}
})} alt={asset.filename}
</div> className="asset-image"
</div> onPointerDown={() => {
); setSelectedItem({
})()} name: asset.filename,
</section> id: asset.AssetID,
</div> type: asset.type === "undefined" ? undefined : asset.type
</div> });
); }}
/>
<div className="asset-name">
{asset.filename
.split("_")
.map(
(word: any) =>
word.charAt(0).toUpperCase() + word.slice(1)
)
.join(" ")}
</div>
</div>
))}
</div>
</div>
</div>
);
}
if (selectedCategory) {
return (
<div className="assets-wrapper">
<h2>
{selectedCategory}
<button
className="back-button"
id="asset-backButtom"
onClick={() => {
setSelectedCategory(null);
setCategoryAssets([]);
}}
>
Back
</button>
</h2>
<div className="assets-container">
{categoryAssets?.map((asset: any, index: number) => (
<div
key={`${index}-${asset}`}
className="assets"
id={asset.filename}
title={asset.filename}
>
<img
src={asset?.thumbnail}
alt={asset.filename}
className="asset-image"
onPointerDown={() => {
setSelectedItem({
name: asset.filename,
id: asset.AssetID,
type: asset.type === "undefined" ? undefined : asset.type,
category: asset.category,
subType: asset.subType
});
}}
/>
<div className="asset-name">
{asset.filename.split("_").map((word: any) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ")}
</div>
</div>
))}
{categoryAssets.length === 0 && (
<div className="no-asset">
🚧 The asset shelf is empty. We're working on filling it up!
</div>
)}
</div>
</div>
);
}
return (
<div className="assets-wrapper">
<h2>Categories</h2>
<div className="categories-container">
{Array.from(
new Set(categoryList.map((asset) => asset.category))
).map((category, index) => {
const categoryInfo = categoryList.find(
(asset) => asset.category === category
);
return (
<div
key={`${index}-${category}`}
className="category"
id={category}
onClick={() => fetchCategoryAssets(category)}
>
<img
src={categoryInfo?.categoryImage ?? ""}
alt={category}
className="category-image"
draggable={false}
/>
<div className="category-name">{category}</div>
</div>
);
})}
</div>
</div>
);
})()}
</section>
</div>
</div>
);
}; };
export default Assets; export default Assets;

View File

@@ -119,6 +119,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
rotation: [item.rotation.x, item.rotation.y, item.rotation.z], rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle", state: "idle",
type: "vehicle", type: "vehicle",
subType: item.eventData.subType as VehicleEventSchema['subType'] || '',
speed: 1, speed: 1,
point: { point: {
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
@@ -151,6 +152,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
rotation: [item.rotation.x, item.rotation.y, item.rotation.z], rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle", state: "idle",
type: "transfer", type: "transfer",
subType: item.eventData.subType || '',
speed: 1, speed: 1,
points: item.eventData.points?.map((point: any, index: number) => ({ points: item.eventData.points?.map((point: any, index: number) => ({
uuid: point.uuid || THREE.MathUtils.generateUUID(), uuid: point.uuid || THREE.MathUtils.generateUUID(),
@@ -177,6 +179,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
rotation: [item.rotation.x, item.rotation.y, item.rotation.z], rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle", state: "idle",
type: "machine", type: "machine",
subType: item.eventData.subType || '',
point: { point: {
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0], position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
@@ -200,6 +203,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
rotation: [item.rotation.x, item.rotation.y, item.rotation.z], rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle", state: "idle",
type: "roboticArm", type: "roboticArm",
subType: item.eventData.subType || '',
speed: 1, speed: 1,
point: { point: {
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
@@ -228,6 +232,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
rotation: [item.rotation.x, item.rotation.y, item.rotation.z], rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle", state: "idle",
type: "storageUnit", type: "storageUnit",
subType: item.eventData.subType || '',
point: { point: {
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0], position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
@@ -250,6 +255,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
rotation: [item.rotation.x, item.rotation.y, item.rotation.z], rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle", state: "idle",
type: "human", type: "human",
subType: item.eventData.subType || '',
speed: 1, speed: 1,
point: { point: {
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
@@ -270,6 +276,31 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
} }
} }
addEvent(humanEvent); addEvent(humanEvent);
} else if (item.eventData.type === 'Crane') {
const craneEvent: CraneEventSchema = {
modelUuid: item.modelUuid,
modelName: item.modelName,
position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle",
type: "crane",
subType: item.eventData.subType as CraneEventSchema['subType'] || 'pillarJib',
point: {
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0],
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "pickAndDrop",
maxPickUpCount: 1,
triggers: []
}
]
}
}
addEvent(craneEvent);
} }
} else { } else {
assets.push({ assets.push({

View File

@@ -1,13 +1,13 @@
import * as THREE from "three"; import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import * as Types from "../../../../types/world/worldTypes"; import * as Types from "../../../../types/world/worldTypes";
import { retrieveGLTF, storeGLTF } from "../../../../utils/indexDB/idbUtils"; import { retrieveGLTF, storeGLTF } from "../../../../utils/indexDB/idbUtils";
// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
import { Socket } from "socket.io-client"; import { Socket } from "socket.io-client";
import * as CONSTANTS from "../../../../types/world/worldConstants"; import * as CONSTANTS from "../../../../types/world/worldConstants";
import PointsCalculator from "../../../simulation/events/points/functions/pointsCalculator"; import PointsCalculator from "../../../simulation/events/points/functions/pointsCalculator";
import { getUserData } from "../../../../functions/getUserData"; import { getUserData } from "../../../../functions/getUserData";
// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
async function addAssetModel( async function addAssetModel(
scene: THREE.Scene, scene: THREE.Scene,
@@ -165,7 +165,7 @@ async function handleModelLoad(
if (!data || !data.points) return; if (!data || !data.points) return;
const eventData: any = { type: selectedItem.type }; const eventData: any = { type: selectedItem.type, subType: selectedItem.subType };
if (selectedItem.type === "Conveyor") { if (selectedItem.type === "Conveyor") {
const ConveyorEvent: ConveyorEventSchema = { const ConveyorEvent: ConveyorEventSchema = {
@@ -175,6 +175,7 @@ async function handleModelLoad(
rotation: newFloorItem.rotation, rotation: newFloorItem.rotation,
state: "idle", state: "idle",
type: "transfer", type: "transfer",
subType: selectedItem.subType || '',
speed: 1, speed: 1,
points: data.points.map((point: THREE.Vector3, index: number) => { points: data.points.map((point: THREE.Vector3, index: number) => {
const triggers: TriggerSchema[] = []; const triggers: TriggerSchema[] = [];
@@ -243,6 +244,7 @@ async function handleModelLoad(
rotation: newFloorItem.rotation, rotation: newFloorItem.rotation,
state: "idle", state: "idle",
type: "vehicle", type: "vehicle",
subType: selectedItem.subType || '',
speed: 1, speed: 1,
point: { point: {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
@@ -280,6 +282,7 @@ async function handleModelLoad(
rotation: newFloorItem.rotation, rotation: newFloorItem.rotation,
state: "idle", state: "idle",
type: "roboticArm", type: "roboticArm",
subType: selectedItem.subType || '',
speed: 1, speed: 1,
point: { point: {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
@@ -313,6 +316,7 @@ async function handleModelLoad(
rotation: newFloorItem.rotation, rotation: newFloorItem.rotation,
state: "idle", state: "idle",
type: "machine", type: "machine",
subType: selectedItem.subType || '',
point: { point: {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z], position: [data.points[0].x, data.points[0].y, data.points[0].z],
@@ -341,6 +345,7 @@ async function handleModelLoad(
rotation: newFloorItem.rotation, rotation: newFloorItem.rotation,
state: "idle", state: "idle",
type: "storageUnit", type: "storageUnit",
subType: selectedItem.subType || '',
point: { point: {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z], position: [data.points[0].x, data.points[0].y, data.points[0].z],
@@ -368,6 +373,7 @@ async function handleModelLoad(
rotation: newFloorItem.rotation, rotation: newFloorItem.rotation,
state: "idle", state: "idle",
type: "human", type: "human",
subType: selectedItem.subType || '',
speed: 1, speed: 1,
point: { point: {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
@@ -393,6 +399,36 @@ async function handleModelLoad(
position: humanEvent.point.position, position: humanEvent.point.position,
rotation: humanEvent.point.rotation, rotation: humanEvent.point.rotation,
} }
} else if (selectedItem.type === "Crane") {
const craneEvent: CraneEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "crane",
subType: selectedItem.subType || '',
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "pickAndDrop",
maxPickUpCount: 1,
triggers: []
}
]
}
}
addEvent(craneEvent);
eventData.point = {
uuid: craneEvent.point.uuid,
position: craneEvent.point.position,
rotation: craneEvent.point.rotation,
}
} }
const completeData = { const completeData = {
@@ -401,11 +437,7 @@ async function handleModelLoad(
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
assetId: newFloorItem.assetId, assetId: newFloorItem.assetId,
position: newFloorItem.position, position: newFloorItem.position,
rotation: { rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z, },
x: model.rotation.x,
y: model.rotation.y,
z: model.rotation.z,
},
isLocked: false, isLocked: false,
isVisible: true, isVisible: true,
socketId: socket.id, socketId: socket.id,
@@ -422,11 +454,7 @@ async function handleModelLoad(
modelName: completeData.modelName, modelName: completeData.modelName,
assetId: completeData.assetId, assetId: completeData.assetId,
position: completeData.position, position: completeData.position,
rotation: [ rotation: [completeData.rotation.x, completeData.rotation.y, completeData.rotation.z,] as [number, number, number],
completeData.rotation.x,
completeData.rotation.y,
completeData.rotation.z,
] as [number, number, number],
isLocked: completeData.isLocked, isLocked: completeData.isLocked,
isCollidable: false, isCollidable: false,
isVisible: completeData.isVisible, isVisible: completeData.isVisible,
@@ -442,11 +470,7 @@ async function handleModelLoad(
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
assetId: newFloorItem.assetId, assetId: newFloorItem.assetId,
position: newFloorItem.position, position: newFloorItem.position,
rotation: { rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z, },
x: model.rotation.x,
y: model.rotation.y,
z: model.rotation.z,
},
isLocked: false, isLocked: false,
isVisible: true, isVisible: true,
socketId: socket.id, socketId: socket.id,
@@ -462,11 +486,7 @@ async function handleModelLoad(
modelName: data.modelName, modelName: data.modelName,
assetId: data.assetId, assetId: data.assetId,
position: data.position, position: data.position,
rotation: [data.rotation.x, data.rotation.y, data.rotation.z] as [ rotation: [data.rotation.x, data.rotation.y, data.rotation.z] as [number, number, number],
number,
number,
number
],
isLocked: data.isLocked, isLocked: data.isLocked,
isCollidable: false, isCollidable: false,
isVisible: data.isVisible, isVisible: data.isVisible,

View File

@@ -1,12 +1,9 @@
import * as THREE from 'three'; import * as THREE from 'three';
import { CameraControls } from '@react-three/drei'; import { CameraControls } from '@react-three/drei';
import { ThreeEvent } from '@react-three/fiber'; import { ThreeEvent, useThree } from '@react-three/fiber';
import { useCallback } from 'react'; import { useCallback, useEffect, useRef } from 'react';
import { ProductStoreType } from '../../../../../../store/simulation/useProductStore';
import { EventStoreType } from '../../../../../../store/simulation/useEventsStore';
import { Socket } from 'socket.io-client';
import { useActiveTool, useToolMode } from '../../../../../../store/builder/store'; import { useActiveTool, useDeletableFloorItem, useSelectedFloorItem, useToggleView } from '../../../../../../store/builder/store';
import useModuleStore, { useSubModuleStore } from '../../../../../../store/useModuleStore'; import useModuleStore, { useSubModuleStore } from '../../../../../../store/useModuleStore';
import { useSocketStore } from '../../../../../../store/builder/store'; import { useSocketStore } from '../../../../../../store/builder/store';
import { useSceneContext } from '../../../../../scene/sceneContext'; import { useSceneContext } from '../../../../../scene/sceneContext';
@@ -14,60 +11,60 @@ import { useProductContext } from '../../../../../simulation/products/productCon
import { useVersionContext } from '../../../../version/versionContext'; import { useVersionContext } from '../../../../version/versionContext';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { getUserData } from '../../../../../../functions/getUserData'; import { getUserData } from '../../../../../../functions/getUserData';
import { useLeftData, useTopData } from '../../../../../../store/visualization/useZone3DWidgetStore';
import { useSelectedAsset } from '../../../../../../store/simulation/useSimulationStore';
import { upsertProductOrEventApi } from '../../../../../../services/simulation/products/UpsertProductOrEventApi';
// import { deleteFloorItem } from '../../../../../../services/factoryBuilder/asset/floorAsset/deleteFloorItemApi'; // import { deleteFloorItem } from '../../../../../../services/factoryBuilder/asset/floorAsset/deleteFloorItemApi';
export function useModelEventHandlers({ export function useModelEventHandlers({
controls,
boundingBox, boundingBox,
groupRef, groupRef,
toggleView,
deletableFloorItem,
setDeletableFloorItem,
setSelectedFloorItem,
gl,
setTop,
setLeft,
getIsEventInProduct,
getEventByModelUuid,
setSelectedAsset,
clearSelectedAsset,
removeAsset,
updateBackend,
leftDrag,
rightDrag
}: { }: {
controls: CameraControls | any,
boundingBox: THREE.Box3 | null, boundingBox: THREE.Box3 | null,
groupRef: React.RefObject<THREE.Group>, groupRef: React.RefObject<THREE.Group>,
toggleView: boolean,
deletableFloorItem: THREE.Object3D | null,
setDeletableFloorItem: (item: THREE.Object3D | null) => void,
setSelectedFloorItem: (item: THREE.Object3D | null) => void,
gl: THREE.WebGLRenderer,
setTop: (top: number) => void,
setLeft: (left: number) => void,
getIsEventInProduct: (productUuid: string, modelUuid: string) => boolean,
getEventByModelUuid: (modelUuid: string) => EventsSchema | undefined,
setSelectedAsset: (EventData: EventsSchema) => void,
clearSelectedAsset: () => void,
removeAsset: (modelUuid: string) => void,
updateBackend: (productName: string, productUuid: string, projectId: string, event: EventsSchema) => void,
leftDrag: React.MutableRefObject<boolean>,
rightDrag: React.MutableRefObject<boolean>
}) { }) {
const { controls, gl } = useThree();
const { activeTool } = useActiveTool(); const { activeTool } = useActiveTool();
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const { toggleView } = useToggleView();
const { subModule } = useSubModuleStore(); const { subModule } = useSubModuleStore();
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const { eventStore, productStore } = useSceneContext(); const { eventStore, productStore, assetStore } = useSceneContext();
const { removeAsset } = assetStore();
const { removeEvent } = eventStore();
const { getIsEventInProduct, addPoint, deleteEvent } = productStore();
const { getEventByModelUuid } = eventStore();
const { setSelectedAsset, clearSelectedAsset } = useSelectedAsset();
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
const { setSelectedFloorItem } = useSelectedFloorItem();
const { selectedProductStore } = useProductContext(); const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore(); const { selectedProduct } = selectedProductStore();
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore(); const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams(); const { projectId } = useParams();
const { userId, organization } = getUserData(); const { userId, organization } = getUserData();
const leftDrag = useRef(false);
const isLeftMouseDown = useRef(false);
const rightDrag = useRef(false);
const isRightMouseDown = useRef(false);
const { setTop } = useTopData();
const { setLeft } = useLeftData();
const updateBackend = (
productName: string,
productUuid: string,
projectId: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productUuid: productUuid,
projectId: projectId,
eventDatas: eventData,
versionId: selectedVersion?.versionId || '',
});
};
const handleDblClick = (asset: Asset) => { const handleDblClick = (asset: Asset) => {
if (asset && activeTool === "cursor" && boundingBox && groupRef.current && activeModule === 'builder') { if (asset && activeTool === "cursor" && boundingBox && groupRef.current && activeModule === 'builder') {
@@ -117,8 +114,8 @@ export function useModelEventHandlers({
const response = socket.emit('v1:model-asset:delete', data) const response = socket.emit('v1:model-asset:delete', data)
eventStore.getState().removeEvent(asset.modelUuid); removeEvent(asset.modelUuid);
const updatedEvents = productStore.getState().deleteEvent(asset.modelUuid); const updatedEvents = deleteEvent(asset.modelUuid);
updatedEvents.forEach((event) => { updatedEvents.forEach((event) => {
updateBackend( updateBackend(
@@ -157,7 +154,7 @@ export function useModelEventHandlers({
} }
} }
const event = productStore.getState().addPoint(selectedProduct.productUuid, asset.modelUuid, conveyorPoint); const event = addPoint(selectedProduct.productUuid, asset.modelUuid, conveyorPoint);
if (event) { if (event) {
updateBackend( updateBackend(
@@ -169,7 +166,6 @@ export function useModelEventHandlers({
} }
} }
} }
} }
}; };
@@ -228,6 +224,50 @@ export function useModelEventHandlers({
} }
} }
useEffect(() => {
const canvasElement = gl.domElement;
const onPointerDown = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown.current = true;
leftDrag.current = false;
}
if (evt.button === 2) {
isRightMouseDown.current = true;
rightDrag.current = false;
}
};
const onPointerMove = () => {
if (isLeftMouseDown.current) {
leftDrag.current = true;
}
if (isRightMouseDown.current) {
rightDrag.current = true;
}
};
const onPointerUp = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown.current = false;
}
if (evt.button === 2) {
isRightMouseDown.current = false;
}
};
canvasElement.addEventListener('pointerdown', onPointerDown);
canvasElement.addEventListener('pointermove', onPointerMove);
canvasElement.addEventListener('pointerup', onPointerUp);
return () => {
canvasElement.removeEventListener('pointerdown', onPointerDown);
canvasElement.removeEventListener('pointermove', onPointerMove);
canvasElement.removeEventListener('pointerup', onPointerUp);
}
}, [gl])
return { return {
handleDblClick, handleDblClick,
handleClick, handleClick,

View File

@@ -1,76 +1,34 @@
import * as THREE from 'three'; import * as THREE from 'three';
import { useCallback, useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils'; import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils';
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { ThreeEvent, useThree } from '@react-three/fiber'; import { useDeletableFloorItem, useSelectedAssets, useSelectedFloorItem, useToggleView, useToolMode } from '../../../../../store/builder/store';
import { useActiveTool, useDeletableFloorItem, useSelectedAssets, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store';
import { AssetBoundingBox } from '../../functions/assetBoundingBox'; import { AssetBoundingBox } from '../../functions/assetBoundingBox';
import { CameraControls } from '@react-three/drei'; import useModuleStore from '../../../../../store/useModuleStore';
import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore';
import { useLeftData, useTopData } from '../../../../../store/visualization/useZone3DWidgetStore';
import { useSelectedAsset } from '../../../../../store/simulation/useSimulationStore';
import { useProductContext } from '../../../../simulation/products/productContext';
import { useParams } from 'react-router-dom';
import { getUserData } from '../../../../../functions/getUserData';
import { useSceneContext } from '../../../../scene/sceneContext'; import { useSceneContext } from '../../../../scene/sceneContext';
import { useVersionContext } from '../../../version/versionContext';
import { SkeletonUtils } from 'three-stdlib'; import { SkeletonUtils } from 'three-stdlib';
import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi';
import { getAssetIksApi } from '../../../../../services/simulation/ik/getAssetIKs'; import { getAssetIksApi } from '../../../../../services/simulation/ik/getAssetIKs';
import { ModelAnimator } from './animator/modelAnimator'; import { ModelAnimator } from './animator/modelAnimator';
import { useModelEventHandlers } from './eventHandlers/useEventHandlers';
function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendered: boolean, loader: GLTFLoader }) { function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendered: boolean, loader: GLTFLoader }) {
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
const savedTheme: string = localStorage.getItem("theme") || "light"; const savedTheme: string = localStorage.getItem("theme") || "light";
const { controls, gl } = useThree();
const { activeTool } = useActiveTool();
const { toolMode } = useToolMode(); const { toolMode } = useToolMode();
const { toggleView } = useToggleView(); const { toggleView } = useToggleView();
const { subModule } = useSubModuleStore();
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const { assetStore, eventStore, productStore } = useSceneContext(); const { assetStore } = useSceneContext();
const { removeAsset, resetAnimation } = assetStore(); const { resetAnimation } = assetStore();
const { setTop } = useTopData(); const { setDeletableFloorItem } = useDeletableFloorItem();
const { setLeft } = useLeftData();
const { getIsEventInProduct, addPoint } = productStore();
const { getEventByModelUuid } = eventStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { setSelectedAsset, clearSelectedAsset } = useSelectedAsset();
const { socket } = useSocketStore();
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem(); const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const leftDrag = useRef(false);
const isLeftMouseDown = useRef(false);
const rightDrag = useRef(false);
const isRightMouseDown = useRef(false);
const [gltfScene, setGltfScene] = useState<GLTF["scene"] | null>(null); const [gltfScene, setGltfScene] = useState<GLTF["scene"] | null>(null);
const [boundingBox, setBoundingBox] = useState<THREE.Box3 | null>(null); const [boundingBox, setBoundingBox] = useState<THREE.Box3 | null>(null);
const [isSelected, setIsSelected] = useState(false); const [isSelected, setIsSelected] = useState(false);
const groupRef = useRef<THREE.Group>(null); const groupRef = useRef<THREE.Group>(null);
const [ikData, setIkData] = useState<any>(); const [ikData, setIkData] = useState<any>();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { userId, organization } = getUserData();
const { projectId } = useParams();
const { selectedAssets } = useSelectedAssets(); const { selectedAssets } = useSelectedAssets();
const updateBackend = (
productName: string,
productUuid: string,
projectId: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productUuid: productUuid,
projectId: projectId,
eventDatas: eventData,
versionId: selectedVersion?.versionId || '',
});
};
useEffect(() => { useEffect(() => {
if (!ikData && asset.eventData && asset.eventData.type === 'ArmBot') { if (!ikData && asset.eventData && asset.eventData.type === 'ArmBot') {
getAssetIksApi(asset.assetId).then((data) => { getAssetIksApi(asset.assetId).then((data) => {
@@ -82,17 +40,6 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere
} }
}, [asset.modelUuid, ikData]) }, [asset.modelUuid, ikData])
useEffect(() => {
if (gltfScene) {
gltfScene.traverse((child: any) => {
if (child.isMesh) {
child.castShadow = true;
child.receiveShadow = true;
}
})
}
}, [gltfScene]);
useEffect(() => { useEffect(() => {
setDeletableFloorItem(null); setDeletableFloorItem(null);
if (selectedFloorItem === null || selectedFloorItem.userData.modelUuid !== asset.modelUuid) { if (selectedFloorItem === null || selectedFloorItem.userData.modelUuid !== asset.modelUuid) {
@@ -106,285 +53,6 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere
} }
}, [isRendered, selectedFloorItem]) }, [isRendered, selectedFloorItem])
useEffect(() => {
const loadModel = async () => {
try {
// Check Cache
const assetId = asset.assetId;
const cachedModel = THREE.Cache.get(assetId);
if (cachedModel) {
const clone: any = SkeletonUtils.clone(cachedModel.scene);
clone.animations = cachedModel.animations || [];
setGltfScene(clone);
calculateBoundingBox(clone);
return;
}
// Check IndexedDB
const indexedDBModel = await retrieveGLTF(assetId);
if (indexedDBModel) {
const blobUrl = URL.createObjectURL(indexedDBModel);
loader.load(blobUrl, (gltf) => {
URL.revokeObjectURL(blobUrl);
THREE.Cache.remove(blobUrl);
THREE.Cache.add(assetId, gltf);
setGltfScene(gltf.scene.clone());
calculateBoundingBox(gltf.scene);
},
undefined,
(error) => {
echo.error(`[IndexedDB] Error loading ${asset.modelName}:`);
URL.revokeObjectURL(blobUrl);
}
);
return;
}
// Fetch from Backend
const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${assetId}`;
const handleBackendLoad = async (gltf: GLTF) => {
try {
const response = await fetch(modelUrl);
const modelBlob = await response.blob();
await storeGLTF(assetId, modelBlob);
THREE.Cache.add(assetId, gltf);
setGltfScene(gltf.scene.clone());
calculateBoundingBox(gltf.scene);
} catch (error) {
console.error(`[Backend] Error storing/loading ${asset.modelName}:`, error);
}
};
loader.load(modelUrl,
handleBackendLoad,
undefined,
(error) => {
echo.error(`[Backend] Error loading ${asset.modelName}:`);
}
);
} catch (err) {
console.error("Failed to load model:", asset.assetId, err);
}
};
const calculateBoundingBox = (scene: THREE.Object3D) => {
const box = new THREE.Box3().setFromObject(scene);
setBoundingBox(box);
};
loadModel();
}, []);
const handleDblClick = (asset: Asset) => {
if (asset) {
if (activeTool === "cursor" && boundingBox && groupRef.current && activeModule === 'builder') {
const size = boundingBox.getSize(new THREE.Vector3());
const center = boundingBox.getCenter(new THREE.Vector3());
const front = new THREE.Vector3(0, 0, 1);
groupRef.current.localToWorld(front);
front.sub(groupRef.current.position).normalize();
const distance = Math.max(size.x, size.y, size.z) * 2;
const newPosition = center.clone().addScaledVector(front, distance);
(controls as CameraControls).setPosition(
newPosition.x,
newPosition.y,
newPosition.z,
true
);
(controls as CameraControls).setTarget(center.x, center.y, center.z, true);
(controls as CameraControls).fitToBox(groupRef.current, true, {
cover: true,
paddingTop: 5,
paddingLeft: 5,
paddingBottom: 5,
paddingRight: 5,
});
setSelectedFloorItem(groupRef.current);
}
}
};
const handleClick = (evt: ThreeEvent<MouseEvent>, asset: Asset) => {
if (leftDrag.current || toggleView) return;
if (activeTool === 'delete' && deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) {
//REST
// const response = await deleteFloorItem(organization, asset.modelUuid, asset.modelName);
//SOCKET
const data = {
organization,
modelUuid: asset.modelUuid,
modelName: asset.modelName,
socketId: socket.id,
userId,
versionId: selectedVersion?.versionId || '',
projectId
}
const response = socket.emit('v1:model-asset:delete', data)
eventStore.getState().removeEvent(asset.modelUuid);
const updatedEvents = productStore.getState().deleteEvent(asset.modelUuid);
updatedEvents.forEach((event) => {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
})
if (response) {
removeAsset(asset.modelUuid);
echo.success("Model Removed!");
}
} else if (activeModule === 'simulation' && subModule === "simulations" && activeTool === 'pen') {
if (asset.eventData && asset.eventData.type === 'Conveyor') {
const intersectedPoint = evt.point;
const localPosition = groupRef.current?.worldToLocal(intersectedPoint.clone());
if (localPosition) {
const conveyorPoint: ConveyorPointSchema = {
uuid: THREE.MathUtils.generateUUID(),
position: [localPosition?.x, localPosition?.y, localPosition?.z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: `Action 1`,
actionType: 'default',
material: 'Default Material',
delay: 0,
spawnInterval: 5,
spawnCount: 1,
triggers: []
}
}
const event = addPoint(selectedProduct.productUuid, asset.modelUuid, conveyorPoint);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
}
}
}
}
const handlePointerOver = useCallback((asset: Asset) => {
if (activeTool === "delete" && activeModule === 'builder') {
if (deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) {
return;
} else {
setDeletableFloorItem(groupRef.current);
}
}
}, [activeTool, activeModule, deletableFloorItem]);
const handlePointerOut = useCallback((evt: ThreeEvent<MouseEvent>, asset: Asset) => {
if (evt.intersections.length === 0 && activeTool === "delete" && deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) {
setDeletableFloorItem(null);
}
}, [activeTool, deletableFloorItem]);
const handleContextMenu = (asset: Asset, evt: ThreeEvent<MouseEvent>) => {
if (rightDrag.current || toggleView) return;
if (activeTool === "cursor" && subModule === 'simulations') {
if (asset.modelUuid) {
const canvasElement = gl.domElement;
const isInProduct = getIsEventInProduct(selectedProduct.productUuid, asset.modelUuid);
if (isInProduct) {
const event = getEventByModelUuid(asset.modelUuid);
if (event) {
setSelectedAsset(event);
const canvasRect = canvasElement.getBoundingClientRect();
const relativeX = evt.clientX - canvasRect.left;
const relativeY = evt.clientY - canvasRect.top;
setTop(relativeY);
setLeft(relativeX);
} else {
clearSelectedAsset();
}
} else {
const event = getEventByModelUuid(asset.modelUuid);
if (event) {
setSelectedAsset(event)
const canvasRect = canvasElement.getBoundingClientRect();
const relativeX = evt.clientX - canvasRect.left;
const relativeY = evt.clientY - canvasRect.top;
setTop(relativeY);
setLeft(relativeX);
} else {
clearSelectedAsset()
}
}
} else {
clearSelectedAsset()
}
} else {
clearSelectedAsset()
}
}
useEffect(() => {
const canvasElement = gl.domElement;
const onPointerDown = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown.current = true;
leftDrag.current = false;
}
if (evt.button === 2) {
isRightMouseDown.current = true;
rightDrag.current = false;
}
};
const onPointerMove = () => {
if (isLeftMouseDown.current) {
leftDrag.current = true;
}
if (isRightMouseDown.current) {
rightDrag.current = true;
}
};
const onPointerUp = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown.current = false;
}
if (evt.button === 2) {
isRightMouseDown.current = false;
}
};
canvasElement.addEventListener('pointerdown', onPointerDown);
canvasElement.addEventListener('pointermove', onPointerMove);
canvasElement.addEventListener('pointerup', onPointerUp);
return () => {
canvasElement.removeEventListener('pointerdown', onPointerDown);
canvasElement.removeEventListener('pointermove', onPointerMove);
canvasElement.removeEventListener('pointerup', onPointerUp);
}
}, [gl])
useEffect(() => { useEffect(() => {
if (selectedAssets.length > 0) { if (selectedAssets.length > 0) {
if (selectedAssets.some((selectedAsset: THREE.Object3D) => selectedAsset.userData.modelUuid === asset.modelUuid)) { if (selectedAssets.some((selectedAsset: THREE.Object3D) => selectedAsset.userData.modelUuid === asset.modelUuid)) {
@@ -397,6 +65,89 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere
} }
}, [selectedAssets]) }, [selectedAssets])
useEffect(() => {
if (gltfScene) {
gltfScene.traverse((child: any) => {
if (child.isMesh) {
child.castShadow = true;
child.receiveShadow = true;
}
})
}
}, [gltfScene]);
useEffect(() => {
// Calculate Bounding Box
const calculateBoundingBox = (scene: THREE.Object3D) => {
const box = new THREE.Box3().setFromObject(scene);
setBoundingBox(box);
};
// Check Cache
const assetId = asset.assetId;
const cachedModel = THREE.Cache.get(assetId);
if (cachedModel) {
const clone: any = SkeletonUtils.clone(cachedModel.scene);
clone.animations = cachedModel.animations || [];
setGltfScene(clone);
calculateBoundingBox(clone);
return;
}
// Check IndexedDB
retrieveGLTF(assetId).then((indexedDBModel) => {
if (indexedDBModel) {
const blobUrl = URL.createObjectURL(indexedDBModel);
loader.load(
blobUrl,
(gltf) => {
URL.revokeObjectURL(blobUrl);
THREE.Cache.remove(blobUrl);
THREE.Cache.add(assetId, gltf);
setGltfScene(gltf.scene.clone());
calculateBoundingBox(gltf.scene);
},
undefined,
(error) => {
echo.error(`[IndexedDB] Error loading ${asset.modelName}:`);
URL.revokeObjectURL(blobUrl);
}
);
return;
}
// Fetch from Backend
const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${assetId}`;
loader.load(
modelUrl,
(gltf: GLTF) => {
fetch(modelUrl)
.then((response) => response.blob())
.then((modelBlob) => storeGLTF(assetId, modelBlob))
.then(() => {
THREE.Cache.add(assetId, gltf);
setGltfScene(gltf.scene.clone());
calculateBoundingBox(gltf.scene);
})
.catch((error) => {
console.error(
`[Backend] Error storing/loading ${asset.modelName}:`,
error
);
});
},
undefined,
(error) => {
echo.error(`[Backend] Error loading ${asset.modelName}:`);
}
);
}).catch((err) => {
console.error("Failed to load model:", asset.assetId, err);
});
}, []);
const { handleDblClick, handleClick, handlePointerOver, handlePointerOut, handleContextMenu } = useModelEventHandlers({ boundingBox, groupRef });
return ( return (
<group <group
key={asset.modelUuid} key={asset.modelUuid}

View File

@@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from "react";
import { useThree, useFrame } from "@react-three/fiber"; import { useThree, useFrame } from "@react-three/fiber";
import { Group, Vector3 } from "three"; import { Group, Vector3 } from "three";
import { CameraControls } from '@react-three/drei'; import { CameraControls } from '@react-three/drei';
import { useLimitDistance, useRenderDistance, useSelectedFloorItem } from '../../../../store/builder/store'; import { useLimitDistance, useRenderDistance, useSelectedFloorItem, useToggleView } from '../../../../store/builder/store';
import { useSelectedAsset } from '../../../../store/simulation/useSimulationStore'; import { useSelectedAsset } from '../../../../store/simulation/useSimulationStore';
import { useSceneContext } from '../../../scene/sceneContext'; import { useSceneContext } from '../../../scene/sceneContext';
@@ -12,7 +12,7 @@ import { GLTFLoader } from "three/examples/jsm/Addons";
const distanceWorker = new Worker(new URL("../../../../services/factoryBuilder/webWorkers/distanceWorker.js", import.meta.url)); const distanceWorker = new Worker(new URL("../../../../services/factoryBuilder/webWorkers/distanceWorker.js", import.meta.url));
function Models({ loader }: { loader: GLTFLoader }) { function Models({ loader }: { loader: GLTFLoader }) {
const { controls, camera, raycaster, pointer, gl } = useThree(); const { controls, camera } = useThree();
const assetGroupRef = useRef<Group>(null); const assetGroupRef = useRef<Group>(null);
const { assetStore } = useSceneContext(); const { assetStore } = useSceneContext();
const { assets } = assetStore(); const { assets } = assetStore();
@@ -43,28 +43,6 @@ function Models({ loader }: { loader: GLTFLoader }) {
} }
}); });
useEffect(() => {
// const canvasElement = gl.domElement;
// const onClick = () => {
// if (!assetGroupRef.current || assetGroupRef.current.children.length === 0) return;
// raycaster.setFromCamera(pointer, camera);
// const intersects = raycaster.intersectObjects(assetGroupRef.current.children, true);
// if (intersects.length > 0) {
// console.log('intersects: ', intersects);
// }
// }
// canvasElement.addEventListener('click', onClick);
// return () => {
// canvasElement.removeEventListener('click', onClick);
// }
}, [camera])
return ( return (
<group <group
name='Asset Group' name='Asset Group'

View File

@@ -140,7 +140,7 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
const updatedWallAsset = updateWallAsset(wallAsset.modelUuid, { const updatedWallAsset = updateWallAsset(wallAsset.modelUuid, {
wallUuid: intersect.object.userData.wallUuid, wallUuid: intersect.object.userData.wallUuid,
position: [newPoint.x, wallAsset.wallAssetType === 'fixed-move' ? 0 : intersect.point.y, newPoint.z], position: [newPoint.x, wallAsset.wallAssetType === 'fixedMove' ? 0 : intersect.point.y, newPoint.z],
rotation: [wallRotation.x, wallRotation.y, wallRotation.z], rotation: [wallRotation.x, wallRotation.y, wallRotation.z],
}); });
@@ -190,7 +190,7 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
updateWallAsset(wallAsset.modelUuid, { updateWallAsset(wallAsset.modelUuid, {
wallUuid: intersect.object.userData.wallUuid, wallUuid: intersect.object.userData.wallUuid,
position: [newPoint.x, wallAsset.wallAssetType === 'fixed-move' ? 0 : intersect.point.y, newPoint.z], position: [newPoint.x, wallAsset.wallAssetType === 'fixedMove' ? 0 : intersect.point.y, newPoint.z],
rotation: [wallRotation.x, wallRotation.y, wallRotation.z], rotation: [wallRotation.x, wallRotation.y, wallRotation.z],
}); });
} }

View File

@@ -51,9 +51,9 @@ function WallAssetCreator() {
modelName: selectedItem.name, modelName: selectedItem.name,
modelUuid: MathUtils.generateUUID(), modelUuid: MathUtils.generateUUID(),
wallUuid: wall.wallUuid, wallUuid: wall.wallUuid,
wallAssetType: selectedItem.subCategory, wallAssetType: selectedItem.subType,
assetId: selectedItem.id, assetId: selectedItem.id,
position: [closestPoint.x, selectedItem.subCategory === "fixed-move" ? 0 : intersect.point.y, closestPoint.z], position: [closestPoint.x, selectedItem.subType === "fixedMove" ? 0 : intersect.point.y, closestPoint.z],
rotation: [wallRotation.x, wallRotation.y, wallRotation.z], rotation: [wallRotation.x, wallRotation.y, wallRotation.z],
isLocked: false, isLocked: false,
isVisible: true, isVisible: true,

View File

@@ -39,10 +39,7 @@ const CopyPasteControls3D = ({
const [isPasting, setIsPasting] = useState(false); const [isPasting, setIsPasting] = useState(false);
const [relativePositions, setRelativePositions] = useState<THREE.Vector3[]>([]); const [relativePositions, setRelativePositions] = useState<THREE.Vector3[]>([]);
const [centerOffset, setCenterOffset] = useState<THREE.Vector3 | null>(null); const [centerOffset, setCenterOffset] = useState<THREE.Vector3 | null>(null);
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ left: false, right: false, });
left: false,
right: false,
});
const calculateRelativePositions = useCallback((objects: THREE.Object3D[]) => { const calculateRelativePositions = useCallback((objects: THREE.Object3D[]) => {
if (objects.length === 0) return { center: new THREE.Vector3(), relatives: [] }; if (objects.length === 0) return { center: new THREE.Vector3(), relatives: [] };
@@ -227,6 +224,7 @@ const CopyPasteControls3D = ({
const eventData: any = { const eventData: any = {
type: pastedAsset.userData.eventData.type, type: pastedAsset.userData.eventData.type,
subType: pastedAsset.userData.eventData.subType,
}; };
if (pastedAsset.userData.eventData.type === "Conveyor") { if (pastedAsset.userData.eventData.type === "Conveyor") {
@@ -237,6 +235,7 @@ const CopyPasteControls3D = ({
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
state: "idle", state: "idle",
type: 'transfer', type: 'transfer',
subType: pastedAsset.userData.eventData.subType || '',
speed: 1, speed: 1,
points: updatedEventData.points.map((point: any, index: number) => ({ points: updatedEventData.points.map((point: any, index: number) => ({
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
@@ -269,6 +268,7 @@ const CopyPasteControls3D = ({
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
state: "idle", state: "idle",
type: "vehicle", type: "vehicle",
subType: pastedAsset.userData.eventData.subType || '',
speed: 1, speed: 1,
point: { point: {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
@@ -307,6 +307,7 @@ const CopyPasteControls3D = ({
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
state: "idle", state: "idle",
type: "roboticArm", type: "roboticArm",
subType: pastedAsset.userData.eventData.subType || '',
speed: 1, speed: 1,
point: { point: {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
@@ -341,6 +342,7 @@ const CopyPasteControls3D = ({
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
state: "idle", state: "idle",
type: "machine", type: "machine",
subType: pastedAsset.userData.eventData.subType || '',
point: { point: {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]], position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]],
@@ -369,6 +371,7 @@ const CopyPasteControls3D = ({
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
state: "idle", state: "idle",
type: "storageUnit", type: "storageUnit",
subType: pastedAsset.userData.eventData.subType || '',
point: { point: {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]], position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]],
@@ -396,6 +399,7 @@ const CopyPasteControls3D = ({
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
state: "idle", state: "idle",
type: "human", type: "human",
subType: pastedAsset.userData.eventData.subType || '',
speed: 1, speed: 1,
point: { point: {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
@@ -421,6 +425,36 @@ const CopyPasteControls3D = ({
position: humanEvent.point.position, position: humanEvent.point.position,
rotation: humanEvent.point.rotation rotation: humanEvent.point.rotation
}; };
} else if (pastedAsset.userData.eventData.type === "Crane") {
const craneEvent: CraneEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
state: "idle",
type: "crane",
subType: pastedAsset.userData.eventData.subType || '',
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]],
rotation: [updatedEventData.point.rotation[0], updatedEventData.point.rotation[1], updatedEventData.point.rotation[2]],
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "pickAndDrop",
maxPickUpCount: 1,
triggers: []
}
]
}
}
addEvent(craneEvent);
eventData.point = {
uuid: craneEvent.point.uuid,
position: craneEvent.point.position,
rotation: craneEvent.point.rotation
};
} }
newFloorItem.eventData = eventData; newFloorItem.eventData = eventData;

View File

@@ -37,10 +37,7 @@ const DuplicationControls3D = ({
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null); const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
const [initialPositions, setInitialPositions] = useState<Record<string, THREE.Vector3>>({}); const [initialPositions, setInitialPositions] = useState<Record<string, THREE.Vector3>>({});
const [isDuplicating, setIsDuplicating] = useState(false); const [isDuplicating, setIsDuplicating] = useState(false);
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ left: false, right: false, });
left: false,
right: false,
});
const calculateDragOffset = useCallback((point: THREE.Object3D, hitPoint: THREE.Vector3) => { const calculateDragOffset = useCallback((point: THREE.Object3D, hitPoint: THREE.Vector3) => {
const pointPosition = new THREE.Vector3().copy(point.position); const pointPosition = new THREE.Vector3().copy(point.position);
@@ -228,6 +225,7 @@ const DuplicationControls3D = ({
const eventData: any = { const eventData: any = {
type: duplicatedAsset.userData.eventData.type, type: duplicatedAsset.userData.eventData.type,
subType: duplicatedAsset.userData.eventData.subType,
}; };
if (duplicatedAsset.userData.eventData.type === "Conveyor") { if (duplicatedAsset.userData.eventData.type === "Conveyor") {
@@ -238,6 +236,7 @@ const DuplicationControls3D = ({
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
state: "idle", state: "idle",
type: 'transfer', type: 'transfer',
subType: duplicatedAsset.userData.eventData.subType || '',
speed: 1, speed: 1,
points: updatedEventData.points.map((point: any, index: number) => ({ points: updatedEventData.points.map((point: any, index: number) => ({
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
@@ -270,6 +269,7 @@ const DuplicationControls3D = ({
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
state: "idle", state: "idle",
type: "vehicle", type: "vehicle",
subType: duplicatedAsset.userData.eventData.subType || '',
speed: 1, speed: 1,
point: { point: {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
@@ -308,6 +308,7 @@ const DuplicationControls3D = ({
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
state: "idle", state: "idle",
type: "roboticArm", type: "roboticArm",
subType: duplicatedAsset.userData.eventData.subType || '',
speed: 1, speed: 1,
point: { point: {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
@@ -342,6 +343,7 @@ const DuplicationControls3D = ({
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
state: "idle", state: "idle",
type: "machine", type: "machine",
subType: duplicatedAsset.userData.eventData.subType || '',
point: { point: {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]], position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]],
@@ -370,6 +372,7 @@ const DuplicationControls3D = ({
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
state: "idle", state: "idle",
type: "storageUnit", type: "storageUnit",
subType: duplicatedAsset.userData.eventData.subType || '',
point: { point: {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]], position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]],
@@ -397,6 +400,7 @@ const DuplicationControls3D = ({
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
state: "idle", state: "idle",
type: "human", type: "human",
subType: duplicatedAsset.userData.eventData.subType || '',
speed: 1, speed: 1,
point: { point: {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
@@ -422,6 +426,36 @@ const DuplicationControls3D = ({
position: humanEvent.point.position, position: humanEvent.point.position,
rotation: humanEvent.point.rotation rotation: humanEvent.point.rotation
}; };
} else if (duplicatedAsset.userData.eventData.type === "Crane") {
const craneEvent: CraneEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
state: "idle",
type: "crane",
subType: duplicatedAsset.userData.eventData.subType || '',
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]],
rotation: [updatedEventData.point.rotation[0], updatedEventData.point.rotation[1], updatedEventData.point.rotation[2]],
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "pickAndDrop",
maxPickUpCount: 1,
triggers: []
}
]
}
}
addEvent(craneEvent);
eventData.point = {
uuid: craneEvent.point.uuid,
position: craneEvent.point.position,
rotation: craneEvent.point.rotation
};
} }
newFloorItem.eventData = eventData; newFloorItem.eventData = eventData;

View File

@@ -19,6 +19,7 @@ import { createConveyorStore, ConveyorStoreType } from '../../store/simulation/u
import { createVehicleStore, VehicleStoreType } from '../../store/simulation/useVehicleStore'; import { createVehicleStore, VehicleStoreType } from '../../store/simulation/useVehicleStore';
import { createStorageUnitStore, StorageUnitStoreType } from '../../store/simulation/useStorageUnitStore'; import { createStorageUnitStore, StorageUnitStoreType } from '../../store/simulation/useStorageUnitStore';
import { createHumanStore, HumanStoreType } from '../../store/simulation/useHumanStore'; import { createHumanStore, HumanStoreType } from '../../store/simulation/useHumanStore';
import { createCraneStore, CraneStoreType } from '../../store/simulation/useCraneStore';
type SceneContextValue = { type SceneContextValue = {
@@ -41,6 +42,7 @@ type SceneContextValue = {
vehicleStore: VehicleStoreType; vehicleStore: VehicleStoreType;
storageUnitStore: StorageUnitStoreType; storageUnitStore: StorageUnitStoreType;
humanStore: HumanStoreType; humanStore: HumanStoreType;
craneStore: CraneStoreType;
humanEventManagerRef: React.RefObject<HumanEventManagerState>; humanEventManagerRef: React.RefObject<HumanEventManagerState>;
@@ -78,6 +80,7 @@ export function SceneProvider({
const vehicleStore = useMemo(() => createVehicleStore(), []); const vehicleStore = useMemo(() => createVehicleStore(), []);
const storageUnitStore = useMemo(() => createStorageUnitStore(), []); const storageUnitStore = useMemo(() => createStorageUnitStore(), []);
const humanStore = useMemo(() => createHumanStore(), []); const humanStore = useMemo(() => createHumanStore(), []);
const craneStore = useMemo(() => createCraneStore(), []);
const humanEventManagerRef = useRef<HumanEventManagerState>({ humanStates: [] }); const humanEventManagerRef = useRef<HumanEventManagerState>({ humanStates: [] });
@@ -98,8 +101,9 @@ export function SceneProvider({
vehicleStore.getState().clearVehicles(); vehicleStore.getState().clearVehicles();
storageUnitStore.getState().clearStorageUnits(); storageUnitStore.getState().clearStorageUnits();
humanStore.getState().clearHumans(); humanStore.getState().clearHumans();
craneStore.getState().clearCranes();
humanEventManagerRef.current.humanStates = []; humanEventManagerRef.current.humanStates = [];
}, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, undoRedo2DStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore]); }, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, undoRedo2DStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, craneStore]);
const contextValue = useMemo(() => ( const contextValue = useMemo(() => (
{ {
@@ -119,11 +123,12 @@ export function SceneProvider({
vehicleStore, vehicleStore,
storageUnitStore, storageUnitStore,
humanStore, humanStore,
craneStore,
humanEventManagerRef, humanEventManagerRef,
clearStores, clearStores,
layout layout
} }
), [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, undoRedo2DStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, clearStores, layout]); ), [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, undoRedo2DStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, craneStore, clearStores, layout]);
return ( return (
<SceneContext.Provider value={contextValue}> <SceneContext.Provider value={contextValue}>

View File

@@ -0,0 +1,15 @@
import React from 'react'
import CraneInstances from './instances/craneInstances'
function Crane() {
return (
<>
<CraneInstances />
</>
)
}
export default Crane

View File

@@ -0,0 +1,133 @@
import * as THREE from 'three';
import { useEffect, useMemo } from 'react';
import { useThree } from '@react-three/fiber';
function PillarJibAnimator({ crane }: { crane: CraneStatus }) {
const { scene } = useThree();
useEffect(() => {
const model = scene.getObjectByProperty('uuid', crane.modelUuid);
if (model) {
const base = model.getObjectByName('base');
const trolley = model.getObjectByName('trolley');
const hook = model.getObjectByName('hook');
if (base && trolley && hook) {
let trolleyDir = 1;
let hookDir = 1;
const trolleySpeed = 0.01;
const hookSpeed = 0.01;
const rotationSpeed = 0.005;
const trolleyMinOffset = -1;
const trolleyMaxOffset = 1.75;
const hookMinOffset = 0.25;
const hookMaxOffset = -1.5;
const originalTrolleyX = trolley.position.x;
const originalHookY = hook.position.y;
const animate = () => {
if (base) {
base.rotation.y += rotationSpeed;
}
if (trolley) {
trolley.position.x += trolleyDir * trolleySpeed;
if (trolley.position.x >= originalTrolleyX + trolleyMaxOffset ||
trolley.position.x <= originalTrolleyX + trolleyMinOffset) {
trolleyDir *= -1;
}
}
if (hook) {
hook.position.y += hookDir * hookSpeed;
if (hook.position.y >= originalHookY + hookMinOffset ||
hook.position.y <= originalHookY + hookMaxOffset) {
hookDir *= -1;
}
}
requestAnimationFrame(animate);
};
animate();
}
}
}, [crane, scene]);
return (
<PillarJibHelper crane={crane} />
);
}
export default PillarJibAnimator;
function PillarJibHelper({ crane }: { crane: CraneStatus }) {
const { scene } = useThree();
const { geometry, position } = useMemo(() => {
const model = scene.getObjectByProperty('uuid', crane.modelUuid);
if (!model) return { geometry: null, position: null };
const base = model.getObjectByName('base');
const trolley = model.getObjectByName('trolley');
const hook = model.getObjectByName('hook');
if (!base || !trolley || !hook) return { geometry: null, position: null };
const baseWorld = new THREE.Vector3();
base.getWorldPosition(baseWorld);
const trolleyWorld = new THREE.Vector3();
trolley.getWorldPosition(trolleyWorld);
const hookWorld = new THREE.Vector3();
hook.getWorldPosition(hookWorld);
const distFromBase = new THREE.Vector2(trolleyWorld.x - baseWorld.x, trolleyWorld.z - baseWorld.z).length();
const outerRadius = distFromBase + 1.75;
const innerRadius = Math.max(distFromBase - 1, 0.05);
const height = (0.25 - (-1.5));
const cylinderYPosition = hookWorld.y + (height / 2) + (-1.5 + 0.25) / 2;
const shape = new THREE.Shape();
shape.absarc(0, 0, outerRadius, 0, Math.PI * 2, false);
const hole = new THREE.Path();
hole.absarc(0, 0, innerRadius, 0, Math.PI * 2, true);
shape.holes.push(hole);
const extrudeSettings = {
depth: height,
bevelEnabled: false,
steps: 1
};
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
const position: [number, number, number] = [baseWorld.x, cylinderYPosition, baseWorld.z];
return { geometry, position };
}, [scene, crane.modelUuid]);
if (!geometry || !position) return null;
return (
<mesh
geometry={geometry}
position={position}
rotation={[Math.PI / 2, 0, 0]}
>
<meshStandardMaterial
color={0x888888}
metalness={0.5}
roughness={0.4}
side={THREE.DoubleSide}
transparent={true}
opacity={0.3}
/>
</mesh>
);
}

View File

@@ -0,0 +1,24 @@
import React from 'react'
import PillarJibInstance from './instance/pillarJibInstance';
import { useSceneContext } from '../../../scene/sceneContext';
function CraneInstances() {
const { craneStore } = useSceneContext();
const { cranes } = craneStore();
return (
<>
{cranes.map((crane: CraneStatus) => (
<React.Fragment key={crane.modelUuid}>
{crane.subType === "pillarJib" &&
<PillarJibInstance crane={crane} />
}
</React.Fragment>
))}
</>
)
}
export default CraneInstances

View File

@@ -0,0 +1,14 @@
import PillarJibAnimator from '../animator/pillarJibAnimator'
function PillarJibInstance({ crane }: { crane: CraneStatus }) {
return (
<>
<PillarJibAnimator crane={crane} />
</>
)
}
export default PillarJibInstance

View File

@@ -40,7 +40,7 @@ function PointsCalculator(
} }
return { return {
points: [worldTopMiddle] points: [new THREE.Vector3(worldTopMiddle.x, worldTopMiddle.y + 0.1, worldTopMiddle.z)]
}; };
} }

View File

@@ -18,6 +18,7 @@ function PointInstances() {
machine: "purple", machine: "purple",
storageUnit: "red", storageUnit: "red",
human: "white", human: "white",
crane: "yellow",
}; };
return ( return (

View File

@@ -173,6 +173,22 @@ function TriggerConnector() {
}); });
}); });
} }
// Handle Human point
else if (event.type === "crane" && 'point' in event) {
const point = event.point;
point.actions?.forEach(action => {
action.triggers?.forEach(trigger => {
if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) {
newConnections.push({
id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`,
startPointUuid: point.uuid,
endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
trigger
});
}
});
});
}
}); });
setConnections(newConnections); setConnections(newConnections);

View File

@@ -10,7 +10,7 @@ import { useParams } from 'react-router-dom';
import { useVersionContext } from '../../builder/version/versionContext'; import { useVersionContext } from '../../builder/version/versionContext';
function Products() { function Products() {
const { armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, layout, productStore } = useSceneContext(); const { armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, craneStore, layout, productStore } = useSceneContext();
const { products, getProductById, addProduct, setProducts } = productStore(); const { products, getProductById, addProduct, setProducts } = productStore();
const { selectedProductStore } = useProductContext(); const { selectedProductStore } = useProductContext();
const { setMainProduct } = useMainProduct(); const { setMainProduct } = useMainProduct();
@@ -20,7 +20,8 @@ function Products() {
const { addMachine, clearMachines } = machineStore(); const { addMachine, clearMachines } = machineStore();
const { addConveyor, clearConveyors } = conveyorStore(); const { addConveyor, clearConveyors } = conveyorStore();
const { setCurrentMaterials, clearStorageUnits, updateCurrentLoad, addStorageUnit } = storageUnitStore(); const { setCurrentMaterials, clearStorageUnits, updateCurrentLoad, addStorageUnit } = storageUnitStore();
const { addHuman, addCurrentAction, clearHumans } = humanStore(); const { addHuman, addCurrentAction: addCurrentActionHuman, clearHumans } = humanStore();
const { addCrane, addCurrentAction: addCurrentActionCrane, clearCranes } = craneStore();
const { isReset } = useResetButtonStore(); const { isReset } = useResetButtonStore();
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { mainProduct } = useMainProduct(); const { mainProduct } = useMainProduct();
@@ -164,7 +165,25 @@ function Products() {
addHuman(selectedProduct.productUuid, events); addHuman(selectedProduct.productUuid, events);
if (events.point.actions.length > 0) { if (events.point.actions.length > 0) {
addCurrentAction(events.modelUuid, events.point.actions[0].actionUuid); addCurrentActionHuman(events.modelUuid, events.point.actions[0].actionUuid);
}
}
});
}
}
}, [selectedProduct, products, isReset, isPlaying]);
useEffect(() => {
if (selectedProduct.productUuid) {
const product = getProductById(selectedProduct.productUuid);
if (product) {
clearCranes();
product.eventDatas.forEach(events => {
if (events.type === 'crane') {
addCrane(selectedProduct.productUuid, events);
if (events.point.actions.length > 0) {
addCurrentActionCrane(events.modelUuid, events.point.actions[0].actionUuid);
} }
} }
}); });

View File

@@ -7,6 +7,7 @@ import Materials from './materials/materials';
import Machine from './machine/machine'; import Machine from './machine/machine';
import StorageUnit from './storageUnit/storageUnit'; import StorageUnit from './storageUnit/storageUnit';
import Human from './human/human'; import Human from './human/human';
import Crane from './crane/crane';
import Simulator from './simulator/simulator'; import Simulator from './simulator/simulator';
import Products from './products/products'; import Products from './products/products';
import Trigger from './triggers/trigger'; import Trigger from './triggers/trigger';
@@ -55,6 +56,8 @@ function Simulation() {
<Human /> <Human />
<Crane />
<Simulator /> <Simulator />
<SimulationAnalysis /> <SimulationAnalysis />

View File

@@ -3,74 +3,74 @@ import { io } from "socket.io-client";
import * as CONSTANTS from "../../types/world/worldConstants"; import * as CONSTANTS from "../../types/world/worldConstants";
export const useSocketStore = create<any>((set: any, get: any) => ({ export const useSocketStore = create<any>((set: any, get: any) => ({
socket: null, socket: null,
initializeSocket: ( initializeSocket: (
email?: string, email?: string,
organization?: string, organization?: string,
token?: string, token?: string,
refreshToken?: string refreshToken?: string
) => { ) => {
const existingSocket = get().socket; const existingSocket = get().socket;
if (existingSocket) { if (existingSocket) {
return; return;
} }
const socket = io( const socket = io(
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder_v1`, `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder_v1`,
{ {
reconnection: true, reconnection: true,
auth: { token, refreshToken }, auth: { token, refreshToken },
} }
); );
const visualizationSocket = io( const visualizationSocket = io(
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Visualization_v1`, `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Visualization_v1`,
{ {
reconnection: true, reconnection: true,
auth: { token, refreshToken }, auth: { token, refreshToken },
} }
); );
const dashBoardSocket = io( const dashBoardSocket = io(
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/dashboard`, `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/dashboard`,
{ {
reconnection: true, reconnection: true,
auth: { token, refreshToken }, auth: { token, refreshToken },
} }
); );
const projectSocket = io( const projectSocket = io(
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/project`, `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/project`,
{ {
reconnection: true, reconnection: true,
auth: { token, refreshToken }, auth: { token, refreshToken },
} }
); );
const threadSocket = io( const threadSocket = io(
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/thread`, `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/thread`,
{ {
reconnection: true, reconnection: true,
auth: { token, refreshToken }, auth: { token, refreshToken },
} }
); );
set({ set({
socket, socket,
visualizationSocket, visualizationSocket,
dashBoardSocket, dashBoardSocket,
projectSocket, projectSocket,
threadSocket, threadSocket,
}); });
}, },
disconnectSocket: () => { disconnectSocket: () => {
set((state: any) => { set((state: any) => {
state.socket?.disconnect(); state.socket?.disconnect();
state.visualizationSocket?.disconnect(); state.visualizationSocket?.disconnect();
state.dashBoardSocket?.disconnect(); state.dashBoardSocket?.disconnect();
state.projectSocket?.disconnect(); state.projectSocket?.disconnect();
state.threadSocket?.disconnect(); state.threadSocket?.disconnect();
return { socket: null }; return { socket: null };
}); });
}, },
})); }));
// export const useSocketStore = create<any>((set: any, get: any) => ({ // export const useSocketStore = create<any>((set: any, get: any) => ({
// socket: null, // socket: null,
@@ -128,507 +128,507 @@ export const useSocketStore = create<any>((set: any, get: any) => ({
// }, // },
// })); // }));
export const useLoadingProgress = create<{ export const useLoadingProgress = create<{
loadingProgress: number; loadingProgress: number;
setLoadingProgress: (x: number) => void; setLoadingProgress: (x: number) => void;
}>((set) => ({ }>((set) => ({
loadingProgress: 1, loadingProgress: 1,
setLoadingProgress: (x: number) => set({ loadingProgress: x }), setLoadingProgress: (x: number) => set({ loadingProgress: x }),
})); }));
export const useOrganization = create<any>((set: any) => ({ export const useOrganization = create<any>((set: any) => ({
organization: "", organization: "",
setOrganization: (x: any) => set(() => ({ organization: x })), setOrganization: (x: any) => set(() => ({ organization: x })),
})); }));
export const useToggleView = create<any>((set: any) => ({ export const useToggleView = create<any>((set: any) => ({
toggleView: false, toggleView: false,
setToggleView: (x: any) => set(() => ({ toggleView: x })), setToggleView: (x: any) => set(() => ({ toggleView: x })),
})); }));
export const useRoomsState = create<any>((set: any) => ({ export const useRoomsState = create<any>((set: any) => ({
roomsState: [], roomsState: [],
setRoomsState: (x: any) => set(() => ({ roomsState: x })), setRoomsState: (x: any) => set(() => ({ roomsState: x })),
})); }));
export const useSelectedItem = create<any>((set: any) => ({ export const useSelectedItem = create<any>((set: any) => ({
selectedItem: { selectedItem: {
name: "", name: "",
id: "", id: "",
type: undefined, type: undefined,
category: "", category: "",
subCatergory: "", subType: "",
}, },
setSelectedItem: (x: any) => set(() => ({ selectedItem: x })), setSelectedItem: (x: any) => set(() => ({ selectedItem: x })),
})); }));
export const useNavMesh = create<any>((set: any) => ({ export const useNavMesh = create<any>((set: any) => ({
navMesh: null, navMesh: null,
setNavMesh: (x: any) => set({ navMesh: x }), setNavMesh: (x: any) => set({ navMesh: x }),
})); }));
export const useSelectedAssets = create<any>((set: any) => ({ export const useSelectedAssets = create<any>((set: any) => ({
selectedAssets: [], selectedAssets: [],
setSelectedAssets: (x: any) => set(() => ({ selectedAssets: x })), setSelectedAssets: (x: any) => set(() => ({ selectedAssets: x })),
})); }));
export const useLayers = create<any>((set: any) => ({ export const useLayers = create<any>((set: any) => ({
Layers: 1, Layers: 1,
setLayers: (x: any) => set(() => ({ Layers: x })), setLayers: (x: any) => set(() => ({ Layers: x })),
})); }));
export const useCamPosition = create<any>((set: any) => ({ export const useCamPosition = create<any>((set: any) => ({
camPosition: { x: undefined, y: undefined, z: undefined }, camPosition: { x: undefined, y: undefined, z: undefined },
setCamPosition: (newCamPosition: any) => set({ camPosition: newCamPosition }), setCamPosition: (newCamPosition: any) => set({ camPosition: newCamPosition }),
})); }));
export const useMenuVisible = create<any>((set: any) => ({ export const useMenuVisible = create<any>((set: any) => ({
menuVisible: false, menuVisible: false,
setMenuVisible: (x: any) => set(() => ({ menuVisible: x })), setMenuVisible: (x: any) => set(() => ({ menuVisible: x })),
})); }));
export const useToolMode = create<any>((set: any) => ({ export const useToolMode = create<any>((set: any) => ({
toolMode: null, toolMode: null,
setToolMode: (x: any) => set(() => ({ toolMode: x })), setToolMode: (x: any) => set(() => ({ toolMode: x })),
})); }));
export const useSelectedWallItem = create<any>((set: any) => ({ export const useSelectedWallItem = create<any>((set: any) => ({
selectedWallItem: null, selectedWallItem: null,
setSelectedWallItem: (x: any) => set(() => ({ selectedWallItem: x })), setSelectedWallItem: (x: any) => set(() => ({ selectedWallItem: x })),
})); }));
export const useSelectedFloorItem = create<any>((set: any) => ({ export const useSelectedFloorItem = create<any>((set: any) => ({
selectedFloorItem: null, selectedFloorItem: null,
setSelectedFloorItem: (x: any) => set(() => ({ selectedFloorItem: x })), setSelectedFloorItem: (x: any) => set(() => ({ selectedFloorItem: x })),
})); }));
export const useDeletableFloorItem = create<any>((set: any) => ({ export const useDeletableFloorItem = create<any>((set: any) => ({
deletableFloorItem: null, deletableFloorItem: null,
setDeletableFloorItem: (x: any) => set(() => ({ deletableFloorItem: x })), setDeletableFloorItem: (x: any) => set(() => ({ deletableFloorItem: x })),
})); }));
export const useSetScale = create<any>((set: any) => ({ export const useSetScale = create<any>((set: any) => ({
scale: null, scale: null,
setScale: (x: any) => set(() => ({ scale: x })), setScale: (x: any) => set(() => ({ scale: x })),
})); }));
export const useRoofVisibility = create<any>((set: any) => ({ export const useRoofVisibility = create<any>((set: any) => ({
roofVisibility: false, roofVisibility: false,
setRoofVisibility: (x: any) => set(() => ({ roofVisibility: x })), setRoofVisibility: (x: any) => set(() => ({ roofVisibility: x })),
})); }));
export const useWallVisibility = create<any>((set: any) => ({ export const useWallVisibility = create<any>((set: any) => ({
wallVisibility: false, wallVisibility: false,
setWallVisibility: (x: any) => set(() => ({ wallVisibility: x })), setWallVisibility: (x: any) => set(() => ({ wallVisibility: x })),
})); }));
export const useShadows = create<any>((set: any) => ({ export const useShadows = create<any>((set: any) => ({
shadows: false, shadows: false,
setShadows: (x: any) => set(() => ({ shadows: x })), setShadows: (x: any) => set(() => ({ shadows: x })),
})); }));
export const useSunPosition = create<any>((set: any) => ({ export const useSunPosition = create<any>((set: any) => ({
sunPosition: { x: undefined, y: undefined, z: undefined }, sunPosition: { x: undefined, y: undefined, z: undefined },
setSunPosition: (newSuntPosition: any) => setSunPosition: (newSuntPosition: any) =>
set({ sunPosition: newSuntPosition }), set({ sunPosition: newSuntPosition }),
})); }));
export const useRemoveLayer = create<any>((set: any) => ({ export const useRemoveLayer = create<any>((set: any) => ({
removeLayer: false, removeLayer: false,
setRemoveLayer: (x: any) => set(() => ({ removeLayer: x })), setRemoveLayer: (x: any) => set(() => ({ removeLayer: x })),
})); }));
export const useRemovedLayer = create<any>((set: any) => ({ export const useRemovedLayer = create<any>((set: any) => ({
removedLayer: null, removedLayer: null,
setRemovedLayer: (x: any) => set(() => ({ removedLayer: x })), setRemovedLayer: (x: any) => set(() => ({ removedLayer: x })),
})); }));
export const useProjectName = create<any>((set: any) => ({ export const useProjectName = create<any>((set: any) => ({
projectName: "Creating Your Project", projectName: "Creating Your Project",
setProjectName: (x: any) => set({ projectName: x }), setProjectName: (x: any) => set({ projectName: x }),
})); }));
export const useActiveLayer = create<any>((set: any) => ({ export const useActiveLayer = create<any>((set: any) => ({
activeLayer: 1, activeLayer: 1,
setActiveLayer: (x: any) => set({ activeLayer: x }), setActiveLayer: (x: any) => set({ activeLayer: x }),
})); }));
export const useResetCamera = create<any>((set: any) => ({ export const useResetCamera = create<any>((set: any) => ({
resetCamera: false, resetCamera: false,
setResetCamera: (x: any) => set({ resetCamera: x }), setResetCamera: (x: any) => set({ resetCamera: x }),
})); }));
export const useAddAction = create<any>((set: any) => ({ export const useAddAction = create<any>((set: any) => ({
addAction: null, addAction: null,
setAddAction: (x: any) => set({ addAction: x }), setAddAction: (x: any) => set({ addAction: x }),
})); }));
export const useActiveTool = create<any>((set: any) => ({ export const useActiveTool = create<any>((set: any) => ({
activeTool: "cursor", activeTool: "cursor",
setActiveTool: (x: any) => set({ activeTool: x }), setActiveTool: (x: any) => set({ activeTool: x }),
})); }));
export const useActiveSubTool = create<any>((set: any) => ({ export const useActiveSubTool = create<any>((set: any) => ({
activeSubTool: "cursor", activeSubTool: "cursor",
setActiveSubTool: (x: any) => set({ activeSubTool: x }), setActiveSubTool: (x: any) => set({ activeSubTool: x }),
})); }));
export const useElevation = create<any>((set: any) => ({ export const useElevation = create<any>((set: any) => ({
elevation: 45, elevation: 45,
setElevation: (x: any) => set({ elevation: x }), setElevation: (x: any) => set({ elevation: x }),
})); }));
export const useAzimuth = create<any>((set: any) => ({ export const useAzimuth = create<any>((set: any) => ({
azimuth: -160, azimuth: -160,
setAzimuth: (x: any) => set({ azimuth: x }), setAzimuth: (x: any) => set({ azimuth: x }),
})); }));
export const useRenderDistance = create<any>((set: any) => ({ export const useRenderDistance = create<any>((set: any) => ({
renderDistance: 40, renderDistance: 40,
setRenderDistance: (x: any) => set({ renderDistance: x }), setRenderDistance: (x: any) => set({ renderDistance: x }),
})); }));
export const useCamMode = create<any>((set: any) => ({ export const useCamMode = create<any>((set: any) => ({
camMode: "ThirdPerson", camMode: "ThirdPerson",
setCamMode: (x: any) => set({ camMode: x }), setCamMode: (x: any) => set({ camMode: x }),
})); }));
export const useUserName = create<any>((set: any) => ({ export const useUserName = create<any>((set: any) => ({
userName: "", userName: "",
setUserName: (x: any) => set({ userName: x }), setUserName: (x: any) => set({ userName: x }),
})); }));
export const useRenameModeStore = create<any>((set: any) => ({ export const useRenameModeStore = create<any>((set: any) => ({
isRenameMode: false, isRenameMode: false,
setIsRenameMode: (state: boolean) => set({ isRenameMode: state }), setIsRenameMode: (state: boolean) => set({ isRenameMode: state }),
})); }));
export const useObjectPosition = create<any>((set: any) => ({ export const useObjectPosition = create<any>((set: any) => ({
objectPosition: { x: undefined, y: undefined, z: undefined }, objectPosition: { x: undefined, y: undefined, z: undefined },
setObjectPosition: (newObjectPosition: any) => setObjectPosition: (newObjectPosition: any) =>
set({ objectPosition: newObjectPosition }), set({ objectPosition: newObjectPosition }),
})); }));
export const useObjectRotation = create<any>((set: any) => ({ export const useObjectRotation = create<any>((set: any) => ({
objectRotation: { x: undefined, y: undefined, z: undefined }, objectRotation: { x: undefined, y: undefined, z: undefined },
setObjectRotation: (newObjectRotation: any) => setObjectRotation: (newObjectRotation: any) =>
set({ objectRotation: newObjectRotation }), set({ objectRotation: newObjectRotation }),
})); }));
export const useDrieTemp = create<any>((set: any) => ({ export const useDrieTemp = create<any>((set: any) => ({
drieTemp: undefined, drieTemp: undefined,
setDrieTemp: (x: any) => set({ drieTemp: x }), setDrieTemp: (x: any) => set({ drieTemp: x }),
})); }));
export const useActiveUsers = create<any>((set: any) => ({ export const useActiveUsers = create<any>((set: any) => ({
activeUsers: [], activeUsers: [],
setActiveUsers: (callback: (prev: any[]) => any[] | any[]) => setActiveUsers: (callback: (prev: any[]) => any[] | any[]) =>
set((state: { activeUsers: any[] }) => ({ set((state: { activeUsers: any[] }) => ({
activeUsers: activeUsers:
typeof callback === "function" ? callback(state.activeUsers) : callback, typeof callback === "function" ? callback(state.activeUsers) : callback,
})), })),
})); }));
export const useDrieUIValue = create<any>((set: any) => ({ export const useDrieUIValue = create<any>((set: any) => ({
drieUIValue: { touch: null, temperature: null, humidity: null }, drieUIValue: { touch: null, temperature: null, humidity: null },
setDrieUIValue: (x: any) => setDrieUIValue: (x: any) =>
set((state: any) => ({ drieUIValue: { ...state.drieUIValue, ...x } })), set((state: any) => ({ drieUIValue: { ...state.drieUIValue, ...x } })),
setTouch: (value: any) => setTouch: (value: any) =>
set((state: any) => ({ set((state: any) => ({
drieUIValue: { ...state.drieUIValue, touch: value }, drieUIValue: { ...state.drieUIValue, touch: value },
})), })),
setTemperature: (value: any) => setTemperature: (value: any) =>
set((state: any) => ({ set((state: any) => ({
drieUIValue: { ...state.drieUIValue, temperature: value }, drieUIValue: { ...state.drieUIValue, temperature: value },
})), })),
setHumidity: (value: any) => setHumidity: (value: any) =>
set((state: any) => ({ set((state: any) => ({
drieUIValue: { ...state.drieUIValue, humidity: value }, drieUIValue: { ...state.drieUIValue, humidity: value },
})), })),
})); }));
export const usezoneTarget = create<any>((set: any) => ({ export const usezoneTarget = create<any>((set: any) => ({
zoneTarget: [], zoneTarget: [],
setZoneTarget: (x: any) => set({ zoneTarget: x }), setZoneTarget: (x: any) => set({ zoneTarget: x }),
})); }));
export const usezonePosition = create<any>((set: any) => ({ export const usezonePosition = create<any>((set: any) => ({
zonePosition: [], zonePosition: [],
setZonePosition: (x: any) => set({ zonePosition: x }), setZonePosition: (x: any) => set({ zonePosition: x }),
})); }));
interface EditPositionState { interface EditPositionState {
Edit: boolean; Edit: boolean;
setEdit: (value: boolean) => void; setEdit: (value: boolean) => void;
} }
export const useEditPosition = create<EditPositionState>((set) => ({ export const useEditPosition = create<EditPositionState>((set) => ({
Edit: false, Edit: false,
setEdit: (value) => set({ Edit: value }), setEdit: (value) => set({ Edit: value }),
})); }));
export const useAsset3dWidget = create<any>((set: any) => ({ export const useAsset3dWidget = create<any>((set: any) => ({
widgetSelect: "", widgetSelect: "",
setWidgetSelect: (x: any) => set({ widgetSelect: x }), setWidgetSelect: (x: any) => set({ widgetSelect: x }),
})); }));
export const useWidgetSubOption = create<any>((set: any) => ({ export const useWidgetSubOption = create<any>((set: any) => ({
widgetSubOption: "2D", widgetSubOption: "2D",
setWidgetSubOption: (x: any) => set({ widgetSubOption: x }), setWidgetSubOption: (x: any) => set({ widgetSubOption: x }),
})); }));
export const useLimitDistance = create<any>((set: any) => ({ export const useLimitDistance = create<any>((set: any) => ({
limitDistance: true, limitDistance: true,
setLimitDistance: (x: any) => set({ limitDistance: x }), setLimitDistance: (x: any) => set({ limitDistance: x }),
})); }));
export const useTileDistance = create<any>((set: any) => ({ export const useTileDistance = create<any>((set: any) => ({
gridValue: { gridValue: {
size: CONSTANTS.gridConfig.size, size: CONSTANTS.gridConfig.size,
divisions: CONSTANTS.gridConfig.divisions, divisions: CONSTANTS.gridConfig.divisions,
}, },
planeValue: { planeValue: {
height: CONSTANTS.planeConfig.height, height: CONSTANTS.planeConfig.height,
width: CONSTANTS.planeConfig.width, width: CONSTANTS.planeConfig.width,
}, },
setGridValue: (value: any) => setGridValue: (value: any) =>
set((state: any) => ({ set((state: any) => ({
gridValue: { ...state.gridValue, ...value }, gridValue: { ...state.gridValue, ...value },
})), })),
setPlaneValue: (value: any) => setPlaneValue: (value: any) =>
set((state: any) => ({ set((state: any) => ({
planeValue: { ...state.planeValue, ...value }, planeValue: { ...state.planeValue, ...value },
})), })),
})); }));
export const usePlayAgv = create<any>((set, get) => ({ export const usePlayAgv = create<any>((set, get) => ({
PlayAgv: [], PlayAgv: [],
setPlayAgv: (updateFn: (prev: any[]) => any[]) => setPlayAgv: (updateFn: (prev: any[]) => any[]) =>
set({ PlayAgv: updateFn(get().PlayAgv) }), set({ PlayAgv: updateFn(get().PlayAgv) }),
})); }));
// Define the Asset type // Define the Asset type
type Asset = { type Asset = {
id: string; id: string;
name: string; name: string;
position?: [number, number, number]; // Optional: 3D position position?: [number, number, number]; // Optional: 3D position
rotation?: { x: number; y: number; z: number }; // Optional: Euler rotation rotation?: { x: number; y: number; z: number }; // Optional: Euler rotation
}; };
// Zustand store type // Zustand store type
type ZoneAssetState = { type ZoneAssetState = {
zoneAssetId: Asset | null; zoneAssetId: Asset | null;
setZoneAssetId: (asset: Asset | null) => void; setZoneAssetId: (asset: Asset | null) => void;
}; };
// Zustand store // Zustand store
export const useZoneAssetId = create<ZoneAssetState>((set) => ({ export const useZoneAssetId = create<ZoneAssetState>((set) => ({
zoneAssetId: null, zoneAssetId: null,
setZoneAssetId: (asset) => set({ zoneAssetId: asset }), setZoneAssetId: (asset) => set({ zoneAssetId: asset }),
})); }));
// version visible hidden // version visible hidden
interface VersionHistoryState { interface VersionHistoryState {
viewVersionHistory: boolean; viewVersionHistory: boolean;
setVersionHistoryVisible: (value: boolean) => void; setVersionHistoryVisible: (value: boolean) => void;
} }
const useVersionHistoryVisibleStore = create<VersionHistoryState>((set) => ({ const useVersionHistoryVisibleStore = create<VersionHistoryState>((set) => ({
viewVersionHistory: false, viewVersionHistory: false,
setVersionHistoryVisible: (value) => set({ viewVersionHistory: value }), setVersionHistoryVisible: (value) => set({ viewVersionHistory: value }),
})); }));
export default useVersionHistoryVisibleStore; export default useVersionHistoryVisibleStore;
interface ShortcutStore { interface ShortcutStore {
showShortcuts: boolean; showShortcuts: boolean;
setShowShortcuts: (value: boolean) => void; setShowShortcuts: (value: boolean) => void;
toggleShortcuts: () => void; toggleShortcuts: () => void;
} }
export const useShortcutStore = create<ShortcutStore>((set) => ({ export const useShortcutStore = create<ShortcutStore>((set) => ({
showShortcuts: false, showShortcuts: false,
setShowShortcuts: (value) => set({ showShortcuts: value }), setShowShortcuts: (value) => set({ showShortcuts: value }),
toggleShortcuts: () => toggleShortcuts: () =>
set((state) => ({ showShortcuts: !state.showShortcuts })), set((state) => ({ showShortcuts: !state.showShortcuts })),
})); }));
export const useMachineCount = create<any>((set: any) => ({ export const useMachineCount = create<any>((set: any) => ({
machineCount: 0, machineCount: 0,
setMachineCount: (x: any) => set({ machineCount: x }), setMachineCount: (x: any) => set({ machineCount: x }),
})); }));
export const useMachineUptime = create<any>((set: any) => ({ export const useMachineUptime = create<any>((set: any) => ({
machineActiveTime: 0, machineActiveTime: 0,
setMachineActiveTime: (x: any) => set({ machineActiveTime: x }), setMachineActiveTime: (x: any) => set({ machineActiveTime: x }),
})); }));
export const useMachineDowntime = create<any>((set: any) => ({ export const useMachineDowntime = create<any>((set: any) => ({
machineIdleTime: 0, machineIdleTime: 0,
setMachineIdleTime: (x: any) => set({ machineIdleTime: x }), setMachineIdleTime: (x: any) => set({ machineIdleTime: x }),
})); }));
export const useMaterialCycle = create<any>((set: any) => ({ export const useMaterialCycle = create<any>((set: any) => ({
materialCycleTime: 0, materialCycleTime: 0,
setMaterialCycleTime: (x: any) => set({ materialCycleTime: x }), setMaterialCycleTime: (x: any) => set({ materialCycleTime: x }),
})); }));
export const useThroughPutData = create<any>((set: any) => ({ export const useThroughPutData = create<any>((set: any) => ({
throughputData: 0, throughputData: 0,
setThroughputData: (x: any) => set({ throughputData: x }), setThroughputData: (x: any) => set({ throughputData: x }),
})); }));
export const useProductionCapacityData = create<any>((set: any) => ({ export const useProductionCapacityData = create<any>((set: any) => ({
productionCapacityData: 0, productionCapacityData: 0,
setProductionCapacityData: (x: any) => set({ productionCapacityData: x }), setProductionCapacityData: (x: any) => set({ productionCapacityData: x }),
})); }));
export const useProcessBar = create<any>((set: any) => ({ export const useProcessBar = create<any>((set: any) => ({
processBar: [], processBar: [],
setProcessBar: (x: any) => set({ processBar: x }), setProcessBar: (x: any) => set({ processBar: x }),
})); }));
export const useDfxUpload = create<any>((set: any) => ({ export const useDfxUpload = create<any>((set: any) => ({
dfxuploaded: [], dfxuploaded: [],
dfxWallGenerate: [], dfxWallGenerate: [],
objValue: { x: 0, y: 0, z: 0 }, objValue: { x: 0, y: 0, z: 0 },
setDfxUploaded: (x: any) => set({ dfxuploaded: x }), setDfxUploaded: (x: any) => set({ dfxuploaded: x }),
setDxfWallGenerate: (x: any) => set({ dfxWallGenerate: x }), setDxfWallGenerate: (x: any) => set({ dfxWallGenerate: x }),
setObjValue: (x: any) => set({ objValue: x }), setObjValue: (x: any) => set({ objValue: x }),
})); }));
type InputValuesStore = { type InputValuesStore = {
inputValues: Record<string, string>; inputValues: Record<string, string>;
setInputValues: (values: Record<string, string>) => void; setInputValues: (values: Record<string, string>) => void;
updateInputValue: (label: string, value: string) => void; // <- New updateInputValue: (label: string, value: string) => void; // <- New
}; };
export const useInputValues = create<InputValuesStore>((set) => ({ export const useInputValues = create<InputValuesStore>((set) => ({
inputValues: {}, inputValues: {},
setInputValues: (values) => set({ inputValues: values }), setInputValues: (values) => set({ inputValues: values }),
updateInputValue: (label, value) => updateInputValue: (label, value) =>
set((state) => ({ set((state) => ({
inputValues: { inputValues: {
...state.inputValues, ...state.inputValues,
[label]: value, [label]: value,
}, },
})), })),
})); }));
export interface ROISummaryData { export interface ROISummaryData {
productName: string; productName: string;
roiPercentage: number; roiPercentage: number;
paybackPeriod: number; paybackPeriod: number;
totalCost: number; totalCost: number;
revenueGenerated: number; revenueGenerated: number;
netProfit: number; netProfit: number;
netLoss: number; netLoss: number;
} }
interface ROISummaryStore { interface ROISummaryStore {
roiSummary: ROISummaryData; roiSummary: ROISummaryData;
setRoiSummaryData: (values: ROISummaryData) => void; setRoiSummaryData: (values: ROISummaryData) => void;
} }
export const useROISummaryData = create<ROISummaryStore>((set) => ({ export const useROISummaryData = create<ROISummaryStore>((set) => ({
roiSummary: { roiSummary: {
productName: "", productName: "",
roiPercentage: 0, roiPercentage: 0,
paybackPeriod: 0, paybackPeriod: 0,
totalCost: 0, totalCost: 0,
revenueGenerated: 0, revenueGenerated: 0,
netProfit: 0, netProfit: 0,
netLoss: 0, netLoss: 0,
}, },
setRoiSummaryData: (values) => set({ roiSummary: values }), setRoiSummaryData: (values) => set({ roiSummary: values }),
})); }));
interface CompareStore { interface CompareStore {
comparePopUp: boolean; comparePopUp: boolean;
setComparePopUp: (value: boolean) => void; setComparePopUp: (value: boolean) => void;
toggleComparePopUp: () => void; toggleComparePopUp: () => void;
} }
export const useCompareStore = create<CompareStore>((set) => ({ export const useCompareStore = create<CompareStore>((set) => ({
comparePopUp: false, comparePopUp: false,
setComparePopUp: (value) => set({ comparePopUp: value }), setComparePopUp: (value) => set({ comparePopUp: value }),
toggleComparePopUp: () => toggleComparePopUp: () =>
set((state) => ({ comparePopUp: !state.comparePopUp })), set((state) => ({ comparePopUp: !state.comparePopUp })),
})); }));
// Save state store // Save state store
interface SaveVersionStore { interface SaveVersionStore {
isVersionSaved: boolean; isVersionSaved: boolean;
setIsVersionSaved: (value: boolean) => void; setIsVersionSaved: (value: boolean) => void;
} }
export const useSaveVersion = create<SaveVersionStore>((set) => ({ export const useSaveVersion = create<SaveVersionStore>((set) => ({
isVersionSaved: false, isVersionSaved: false,
setIsVersionSaved: (value: boolean) => set({ isVersionSaved: value }), setIsVersionSaved: (value: boolean) => set({ isVersionSaved: value }),
})); }));
interface ViewSceneState { interface ViewSceneState {
viewSceneLabels: boolean; viewSceneLabels: boolean;
setViewSceneLabels: (value: boolean | ((prev: boolean) => boolean)) => void; setViewSceneLabels: (value: boolean | ((prev: boolean) => boolean)) => void;
} }
export const useViewSceneStore = create<ViewSceneState>((set) => ({ export const useViewSceneStore = create<ViewSceneState>((set) => ({
viewSceneLabels: getInitialViewSceneLabels(), viewSceneLabels: getInitialViewSceneLabels(),
setViewSceneLabels: (value) => { setViewSceneLabels: (value) => {
set((state) => { set((state) => {
const newValue = const newValue =
typeof value === "function" ? value(state.viewSceneLabels) : value; typeof value === "function" ? value(state.viewSceneLabels) : value;
// Store in localStorage manually // Store in localStorage manually
localStorage.setItem("viewSceneLabels", JSON.stringify(newValue)); localStorage.setItem("viewSceneLabels", JSON.stringify(newValue));
return { viewSceneLabels: newValue }; return { viewSceneLabels: newValue };
}); });
}, },
})); }));
function getInitialViewSceneLabels(): boolean { function getInitialViewSceneLabels(): boolean {
if (typeof window === "undefined") return false; // SSR safety if (typeof window === "undefined") return false; // SSR safety
const saved = localStorage.getItem("viewSceneLabels"); const saved = localStorage.getItem("viewSceneLabels");
return saved ? JSON.parse(saved) : false; return saved ? JSON.parse(saved) : false;
} }
export interface CompareProduct { export interface CompareProduct {
productUuid: string; productUuid: string;
productName: string; productName: string;
simulationData: { simulationData: {
// costPerUnit: number; // costPerUnit: number;
// workingDaysPerYear: number; // workingDaysPerYear: number;
// shiftLength: number; // shiftLength: number;
// shiftsPerDay: number; // shiftsPerDay: number;
roiPercentage: number; roiPercentage: number;
// paybackPeriod: number; // paybackPeriod: number;
// totalCost: number; // totalCost: number;
// revenueGenerated: number; // revenueGenerated: number;
netProfit: number; netProfit: number;
productionCapacity: number; productionCapacity: number;
paybackPeriod: number; paybackPeriod: number;
// netLoss: number; // netLoss: number;
machineIdleTime: number; machineIdleTime: number;
machineActiveTime: number; machineActiveTime: number;
throughputData: number; throughputData: number;
}; };
} }
export const useCompareProductDataStore = create<{ export const useCompareProductDataStore = create<{
compareProductsData: CompareProduct[]; compareProductsData: CompareProduct[];
setCompareProductsData: (x: CompareProduct[]) => void; setCompareProductsData: (x: CompareProduct[]) => void;
}>((set) => ({ }>((set) => ({
compareProductsData: [], compareProductsData: [],
setCompareProductsData: (x) => set({ compareProductsData: x }), setCompareProductsData: (x) => set({ compareProductsData: x }),
})); }));
export const useSelectedComment = create<any>((set: any) => ({ export const useSelectedComment = create<any>((set: any) => ({
selectedComment: null, selectedComment: null,
setSelectedComment: (x: any) => set({ selectedComment: x }), setSelectedComment: (x: any) => set({ selectedComment: x }),
position2Dstate: {}, position2Dstate: {},
setPosition2Dstate: (x: any) => set({ position2Dstate: x }), setPosition2Dstate: (x: any) => set({ position2Dstate: x }),
commentPositionState: null, commentPositionState: null,
setCommentPositionState: (x: any) => set({ commentPositionState: x }), setCommentPositionState: (x: any) => set({ commentPositionState: x }),
})); }));
export const useSelectedPath = create<any>((set: any) => ({ export const useSelectedPath = create<any>((set: any) => ({
selectedPath: "auto", selectedPath: "auto",
setSelectedPath: (x: any) => set({ selectedPath: x }), setSelectedPath: (x: any) => set({ selectedPath: x }),
})); }));

View File

@@ -0,0 +1,263 @@
import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
interface CraneStore {
cranes: CraneStatus[];
addCrane: (productUuid: string, event: CraneEventSchema) => void;
removeCrane: (modelUuid: string) => void;
updateCrane: (
modelUuid: string,
updates: Partial<Omit<CraneStatus, "modelUuid" | "productUuid">>
) => void;
clearCranes: () => void;
setCurrentPhase: (modelUuid: string, phase: string) => void;
addCurrentAction: (modelUuid: string, actionUuid: string) => void;
removeCurrentAction: (modelUuid: string) => void;
setCraneActive: (modelUuid: string, isActive: boolean) => void;
setCraneScheduled: (modelUuid: string, isScheduled: boolean) => void;
setCraneLoad: (modelUuid: string, load: number) => void;
setCraneState: (modelUuid: string, newState: CraneStatus["state"]) => void;
incrementCraneLoad: (modelUuid: string, incrementBy: number) => void;
decrementCraneLoad: (modelUuid: string, decrementBy: number) => void;
addCurrentMaterial: (modelUuid: string, materialType: string, materialId: string) => void;
setCurrentMaterials: (modelUuid: string, materials: { materialType: string; materialId: string }[]) => void;
removeLastMaterial: (modelUuid: string) => { materialType: string; materialId: string } | undefined;
getLastMaterial: (modelUuid: string) => { materialType: string; materialId: string } | undefined;
clearCurrentMaterials: (modelUuid: string) => void;
incrementActiveTime: (modelUuid: string, incrementBy: number) => void;
incrementIdleTime: (modelUuid: string, incrementBy: number) => void;
resetTime: (modelUuid: string) => void;
getCraneById: (modelUuid: string) => CraneStatus | undefined;
getCranesByProduct: (productUuid: string) => CraneStatus[];
getActiveCranes: () => CraneStatus[];
}
export const createCraneStore = () => {
return create<CraneStore>()(
immer((set, get) => ({
cranes: [],
addCrane: (productUuid, event) => {
set((state) => {
const exists = state.cranes.some(c => c.modelUuid === event.modelUuid);
if (!exists) {
state.cranes.push({
...event,
productUuid,
currentPhase: 'init',
isActive: false,
isScheduled: false,
idleTime: 0,
activeTime: 0,
currentLoad: 0,
currentMaterials: []
});
}
});
},
removeCrane: (modelUuid) => {
set((state) => {
state.cranes = state.cranes.filter(c => c.modelUuid !== modelUuid);
});
},
updateCrane: (modelUuid, updates) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane) {
Object.assign(crane, updates);
}
});
},
clearCranes: () => {
set((state) => {
state.cranes = [];
});
},
setCurrentPhase: (modelUuid, phase) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane) {
crane.currentPhase = phase;
}
});
},
addCurrentAction: (modelUuid, actionUuid) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane) {
const action = crane.point.actions.find(a => a.actionUuid === actionUuid);
if (action) {
crane.currentAction = {
actionUuid: action.actionUuid,
actionName: action.actionName
};
}
}
});
},
removeCurrentAction: (modelUuid) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane) {
crane.currentAction = undefined;
}
});
},
setCraneActive: (modelUuid, isActive) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane) {
crane.isActive = isActive;
}
});
},
setCraneScheduled: (modelUuid, isScheduled) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane) {
crane.isScheduled = isScheduled;
}
});
},
setCraneLoad: (modelUuid, load) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane) {
crane.currentLoad = load;
}
});
},
setCraneState: (modelUuid, newState) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane) {
crane.state = newState;
}
});
},
incrementCraneLoad: (modelUuid, incrementBy) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane) {
crane.currentLoad += incrementBy;
}
});
},
decrementCraneLoad: (modelUuid, decrementBy) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane) {
crane.currentLoad = Math.max(0, crane.currentLoad - decrementBy);
}
});
},
addCurrentMaterial: (modelUuid, materialType, materialId) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane) {
crane.currentMaterials.push({ materialType, materialId });
}
});
},
setCurrentMaterials: (modelUuid, materials) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane) {
crane.currentMaterials = materials;
}
});
},
removeLastMaterial: (modelUuid) => {
let removed;
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane && crane.currentMaterials.length > 0) {
removed = JSON.parse(JSON.stringify(crane.currentMaterials.pop()));
}
});
return removed;
},
getLastMaterial: (modelUuid) => {
const crane = get().cranes.find(c => c.modelUuid === modelUuid);
if (crane && crane.currentMaterials.length > 0) {
return crane.currentMaterials[crane.currentMaterials.length - 1];
}
return undefined;
},
clearCurrentMaterials: (modelUuid) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane) {
crane.currentMaterials = [];
}
});
},
incrementActiveTime: (modelUuid, incrementBy) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane) {
crane.activeTime += incrementBy;
}
});
},
incrementIdleTime: (modelUuid, incrementBy) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane) {
crane.idleTime += incrementBy;
}
});
},
resetTime: (modelUuid) => {
set((state) => {
const crane = state.cranes.find(c => c.modelUuid === modelUuid);
if (crane) {
crane.activeTime = 0;
crane.idleTime = 0;
}
});
},
getCraneById: (modelUuid) => {
return get().cranes.find(c => c.modelUuid === modelUuid);
},
getCranesByProduct: (productUuid) => {
return get().cranes.filter(c => c.productUuid === productUuid);
},
getActiveCranes: () => {
return get().cranes.filter(c => c.isActive);
}
}))
);
};
export type CraneStoreType = ReturnType<typeof createCraneStore>;

View File

@@ -11,24 +11,24 @@ type EventsStore = {
updateEvent: (modelUuid: string, updates: Partial<EventsSchema>) => EventsSchema | undefined; updateEvent: (modelUuid: string, updates: Partial<EventsSchema>) => EventsSchema | undefined;
// Point-level actions // Point-level actions
addPoint: (modelUuid: string, point: ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema) => void; addPoint: (modelUuid: string, point: PointsScheme) => void;
removePoint: (modelUuid: string, pointUuid: string) => void; removePoint: (modelUuid: string, pointUuid: string) => void;
updatePoint: ( updatePoint: (
modelUuid: string, modelUuid: string,
pointUuid: string, pointUuid: string,
updates: Partial<ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema> updates: Partial<PointsScheme>
) => EventsSchema | undefined; ) => EventsSchema | undefined;
// Action-level actions // Action-level actions
addAction: ( addAction: (
modelUuid: string, modelUuid: string,
pointUuid: string, pointUuid: string,
action: ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] action: ConveyorAction | VehicleAction | RoboticArmAction | MachineAction | StorageAction | HumanAction | CraneAction
) => void; ) => void;
removeAction: (actionUuid: string) => void; removeAction: (actionUuid: string) => void;
updateAction: ( updateAction: (
actionUuid: string, actionUuid: string,
updates: Partial<ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']> updates: Partial<ConveyorAction | VehicleAction | RoboticArmAction | MachineAction | StorageAction | HumanAction | CraneAction>
) => void; ) => void;
// Trigger-level actions // Trigger-level actions
@@ -38,8 +38,8 @@ type EventsStore = {
// Helper functions // Helper functions
getEventByModelUuid: (modelUuid: string) => EventsSchema | undefined; getEventByModelUuid: (modelUuid: string) => EventsSchema | undefined;
getPointByUuid: (modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined; getPointByUuid: (modelUuid: string, pointUuid: string) => PointsScheme | undefined;
getActionByUuid: (actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined; getActionByUuid: (actionUuid: string) => (ConveyorAction | VehicleAction | RoboticArmAction | MachineAction | StorageAction | HumanAction | CraneAction) | undefined;
getTriggerByUuid: (triggerUuid: string) => TriggerSchema | undefined; getTriggerByUuid: (triggerUuid: string) => TriggerSchema | undefined;
}; };

View File

@@ -18,13 +18,13 @@ type ProductsStore = {
updateEvent: (productUuid: string, modelUuid: string, updates: Partial<EventsSchema>) => EventsSchema | undefined; updateEvent: (productUuid: string, modelUuid: string, updates: Partial<EventsSchema>) => EventsSchema | undefined;
// Point-level actions // Point-level actions
addPoint: (productUuid: string, modelUuid: string, point: ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | HumanPointSchema) => EventsSchema | undefined; addPoint: (productUuid: string, modelUuid: string, point: PointsScheme) => EventsSchema | undefined;
removePoint: (productUuid: string, modelUuid: string, pointUuid: string) => EventsSchema | undefined; removePoint: (productUuid: string, modelUuid: string, pointUuid: string) => EventsSchema | undefined;
updatePoint: ( updatePoint: (
productUuid: string, productUuid: string,
modelUuid: string, modelUuid: string,
pointUuid: string, pointUuid: string,
updates: Partial<ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | HumanPointSchema> updates: Partial<PointsScheme>
) => EventsSchema | undefined; ) => EventsSchema | undefined;
// Action-level actions // Action-level actions
@@ -32,13 +32,13 @@ type ProductsStore = {
productUuid: string, productUuid: string,
modelUuid: string, modelUuid: string,
pointUuid: string, pointUuid: string,
action: ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['actions'][0] action: ConveyorAction | VehicleAction | RoboticArmAction | MachineAction | StorageAction | HumanAction | CraneAction
) => EventsSchema | undefined; ) => EventsSchema | undefined;
removeAction: (productUuid: string, actionUuid: string) => EventsSchema | undefined; removeAction: (productUuid: string, actionUuid: string) => EventsSchema | undefined;
updateAction: ( updateAction: (
productUuid: string, productUuid: string,
actionUuid: string, actionUuid: string,
updates: Partial<ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['actions'][0]> updates: Partial<ConveyorAction | VehicleAction | RoboticArmAction | MachineAction | StorageAction | HumanAction | CraneAction>
) => EventsSchema | undefined; ) => EventsSchema | undefined;
// Trigger-level actionss // Trigger-level actionss
@@ -65,9 +65,9 @@ type ProductsStore = {
getEventByActionUuid: (productUuid: string, actionUuid: string) => EventsSchema | undefined; getEventByActionUuid: (productUuid: string, actionUuid: string) => EventsSchema | undefined;
getEventByTriggerUuid: (productUuid: string, triggerUuid: string) => EventsSchema | undefined; getEventByTriggerUuid: (productUuid: string, triggerUuid: string) => EventsSchema | undefined;
getEventByPointUuid: (productUuid: string, pointUuid: string) => EventsSchema | undefined; getEventByPointUuid: (productUuid: string, pointUuid: string) => EventsSchema | undefined;
getPointByUuid: (productUuid: string, modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | HumanPointSchema | undefined; getPointByUuid: (productUuid: string, modelUuid: string, pointUuid: string) => PointsScheme | undefined;
getActionByUuid: (productUuid: string, actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['actions'][0]) | undefined; getActionByUuid: (productUuid: string, actionUuid: string) => (ConveyorAction | VehicleAction | RoboticArmAction | MachineAction | StorageAction | HumanAction | CraneAction) | undefined;
getActionByPointUuid: (productUuid: string, pointUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['actions'][0]) | undefined; getActionByPointUuid: (productUuid: string, pointUuid: string) => (ConveyorAction | VehicleAction | RoboticArmAction | MachineAction | StorageAction | HumanAction | CraneAction) | undefined;
getModelUuidByPointUuid: (productUuid: string, actionUuid: string) => (string) | undefined; getModelUuidByPointUuid: (productUuid: string, actionUuid: string) => (string) | undefined;
getModelUuidByActionUuid: (productUuid: string, actionUuid: string) => (string) | undefined; getModelUuidByActionUuid: (productUuid: string, actionUuid: string) => (string) | undefined;
getPointUuidByActionUuid: (productUuid: string, actionUuid: string) => (string) | undefined; getPointUuidByActionUuid: (productUuid: string, actionUuid: string) => (string) | undefined;
@@ -375,6 +375,15 @@ export const createProductStore = () => {
return; return;
} }
} }
} else if (event.type === "crane") {
if ('actions' in point) {
const index = point.actions.findIndex((a: any) => a.actionUuid === actionUuid);
if (index !== -1) {
point.actions.splice(index, 1);
updatedEvent = JSON.parse(JSON.stringify(event));
return;
}
}
} else if ('action' in point && point.action?.actionUuid === actionUuid) { } else if ('action' in point && point.action?.actionUuid === actionUuid) {
point.action = undefined; point.action = undefined;
updatedEvent = JSON.parse(JSON.stringify(event)); updatedEvent = JSON.parse(JSON.stringify(event));

View File

@@ -32,6 +32,7 @@ interface Asset {
}; };
eventData?: { eventData?: {
type: string; type: string;
subType: string;
point?: { point?: {
uuid: string; uuid: string;
position: [number, number, number]; position: [number, number, number];

View File

@@ -1,4 +1,5 @@
// Base Types // Base Types
interface AssetEventSchema { interface AssetEventSchema {
modelUuid: string; modelUuid: string;
modelName: string; modelName: string;
@@ -19,7 +20,9 @@ interface TriggerSchema {
} | null; } | null;
} }
// Actions // Actions
interface ConveyorAction { interface ConveyorAction {
actionUuid: string; actionUuid: string;
actionName: string; actionName: string;
@@ -107,9 +110,19 @@ interface HumanAction {
triggers: TriggerSchema[]; triggers: TriggerSchema[];
} }
type Action = ConveyorAction | VehicleAction | RoboticArmAction | MachineAction | StorageAction | HumanAction; interface CraneAction {
actionUuid: string;
actionName: string;
actionType: "pickAndDrop";
maxPickUpCount: number;
triggers: TriggerSchema[];
}
type Action = ConveyorAction | VehicleAction | RoboticArmAction | MachineAction | StorageAction | HumanAction | CraneAction;
// Points // Points
interface ConveyorPointSchema { interface ConveyorPointSchema {
uuid: string; uuid: string;
position: [number, number, number]; position: [number, number, number];
@@ -152,46 +165,69 @@ interface HumanPointSchema {
actions: HumanAction[]; actions: HumanAction[];
} }
type PointsScheme = | ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | HumanPointSchema; interface CranePointSchema {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
actions: CraneAction[];
}
type PointsScheme = | ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | HumanPointSchema | CranePointSchema;
// Events // Events
interface ConveyorEventSchema extends AssetEventSchema { interface ConveyorEventSchema extends AssetEventSchema {
type: "transfer"; type: "transfer";
subType: string;
speed: number; speed: number;
points: ConveyorPointSchema[]; points: ConveyorPointSchema[];
} }
interface VehicleEventSchema extends AssetEventSchema { interface VehicleEventSchema extends AssetEventSchema {
type: "vehicle"; type: "vehicle";
subType: "manual" | "automatic" | "semiAutomatic" | '';
speed: number; speed: number;
point: VehiclePointSchema; point: VehiclePointSchema;
} }
interface RoboticArmEventSchema extends AssetEventSchema { interface RoboticArmEventSchema extends AssetEventSchema {
type: "roboticArm"; type: "roboticArm";
subType: string;
speed: number; speed: number;
point: RoboticArmPointSchema; point: RoboticArmPointSchema;
} }
interface MachineEventSchema extends AssetEventSchema { interface MachineEventSchema extends AssetEventSchema {
type: "machine"; type: "machine";
subType: string;
point: MachinePointSchema; point: MachinePointSchema;
} }
interface StorageEventSchema extends AssetEventSchema { interface StorageEventSchema extends AssetEventSchema {
type: "storageUnit"; type: "storageUnit";
subType: string;
point: StoragePointSchema; point: StoragePointSchema;
} }
interface HumanEventSchema extends AssetEventSchema { interface HumanEventSchema extends AssetEventSchema {
type: "human"; type: "human";
subType: string;
speed: number; speed: number;
point: HumanPointSchema; point: HumanPointSchema;
} }
type EventsSchema = | ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema | HumanEventSchema; interface CraneEventSchema extends AssetEventSchema {
type: "crane";
subType: "pillarJib" | '';
point: CranePointSchema;
}
type EventsSchema = | ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema | HumanEventSchema | CraneEventSchema;
// Statuses // Statuses
interface ConveyorStatus extends ConveyorEventSchema { interface ConveyorStatus extends ConveyorEventSchema {
productUuid: string; productUuid: string;
isActive: boolean; isActive: boolean;
@@ -262,6 +298,24 @@ interface HumanStatus extends HumanEventSchema {
}; };
} }
interface CraneStatus extends CraneEventSchema {
productUuid: string;
currentPhase: string;
isActive: boolean;
isScheduled: boolean;
idleTime: number;
activeTime: number;
currentLoad: number;
currentMaterials: { materialType: string; materialId: string; }[];
currentAction?: {
actionUuid: string;
actionName: string;
};
}
// Event Manager
type HumanEventState = { type HumanEventState = {
humanId: string; humanId: string;
actionQueue: { actionQueue: {
@@ -282,7 +336,9 @@ type HumanEventManagerState = {
humanStates: HumanEventState[]; humanStates: HumanEventState[];
}; };
// Materials // Materials
interface MaterialSchema { interface MaterialSchema {
materialId: string; materialId: string;
materialName: string; materialName: string;
@@ -316,14 +372,18 @@ interface MaterialSchema {
type MaterialsSchema = MaterialSchema[]; type MaterialsSchema = MaterialSchema[];
// Products // Products
type productsSchema = { type productsSchema = {
productName: string; productName: string;
productUuid: string; productUuid: string;
eventDatas: EventsSchema[]; eventDatas: EventsSchema[];
}[]; }[];
// Material History // Material History
interface MaterialHistoryEntry { interface MaterialHistoryEntry {
material: MaterialSchema; material: MaterialSchema;
removedAt: string; removedAt: string;
@@ -331,7 +391,8 @@ interface MaterialHistoryEntry {
type MaterialHistorySchema = MaterialHistoryEntry[]; type MaterialHistorySchema = MaterialHistoryEntry[];
//IK
// IK Constraints
type Link = { type Link = {
index: number; index: number;
@@ -350,3 +411,38 @@ type IK = {
maxheight?: number; maxheight?: number;
minheight?: number; minheight?: number;
}; };
// Conveyor Spline Points
type NormalConveyor = {
type: 'normal';
points: [number, number, number][][];
}
type YJunctionConveyor = {
type: 'y-junction';
points: [number, number, number][][];
}
type CurvedConveyor = {
type: 'curved';
points: [number, number, number][][];
}
type ConveyorPoints = NormalConveyor | YJunctionConveyor | CurvedConveyor;
// Crane Constraints
type PillarJibCrane = {
trolleySpeed: number;
hookSpeed: number;
rotationSpeed: number;
trolleyMinOffset: number
trolleyMaxOffset: number;
hookMinOffset: number;
hookMaxOffset: number;
}
type CraneConstraints = PillarJibCrane;

View File

@@ -198,6 +198,7 @@ export type FloorItemType = {
isVisible: boolean; isVisible: boolean;
eventData?: { eventData?: {
type: string; type: string;
subType: string;
point?: { point?: {
uuid: string; uuid: string;
position: [number, number, number]; position: [number, number, number];
@@ -221,7 +222,7 @@ export type setFloorItemSetState = React.Dispatch<React.SetStateAction<FloorItem
// Configuration for wall items, including model, scale, position, and rotation // Configuration for wall items, including model, scale, position, and rotation
interface WallItem { interface WallItem {
type: "fixed-move" | "free-move" | undefined; type: "fixedMove" | "freeMove" | undefined;
model?: THREE.Group; model?: THREE.Group;
modelUuid?: string; modelUuid?: string;
assetId: string; assetId: string;