Merge remote-tracking branch 'origin/main-dev' into feature/agv-edit

This commit is contained in:
2025-07-05 11:20:50 +05:30
68 changed files with 3868 additions and 860 deletions

View File

@@ -6,146 +6,128 @@ import PositionInput from "../customInput/PositionInputs";
import RotationInput from "../customInput/RotationInput"; import RotationInput from "../customInput/RotationInput";
import { useSelectedFloorItem, useObjectPosition, useObjectRotation } from "../../../../store/builder/store"; import { useSelectedFloorItem, useObjectPosition, useObjectRotation } from "../../../../store/builder/store";
import { useSceneContext } from "../../../../modules/scene/sceneContext"; import { useSceneContext } from "../../../../modules/scene/sceneContext";
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
interface UserData { interface UserData {
id: number; // Unique identifier for the user data id: number;
label: string; // Label of the user data field label: string;
value: string; // Value of the user data field value: string;
} }
const AssetProperties: React.FC = () => { const AssetProperties: React.FC = () => {
const [userData, setUserData] = useState<UserData[]>([]); // State to track user data const [userData, setUserData] = useState<UserData[]>([]);
const [nextId, setNextId] = useState(1); // Unique ID for new entries const { selectedFloorItem } = useSelectedFloorItem();
const { selectedFloorItem } = useSelectedFloorItem(); const { objectPosition } = useObjectPosition();
const { objectPosition } = useObjectPosition(); const { objectRotation } = useObjectRotation();
const { objectRotation } = useObjectRotation(); const { assetStore } = useSceneContext();
const { assetStore } = useSceneContext(); const { assets, setCurrentAnimation } = assetStore();
const { assets, setCurrentAnimation } = assetStore() const { loopAnimation } = useBuilderStore();
const [hoveredIndex, setHoveredIndex] = useState<any>(null); const [hoveredIndex, setHoveredIndex] = useState<any>(null);
const [isPlaying, setIsplaying] = useState(false);
// Function to handle adding new user data const handleAddUserData = () => {
const handleAddUserData = () => {
const newUserData: UserData = {
id: nextId,
label: `Property ${nextId}`,
value: "",
}; };
setUserData([...userData, newUserData]);
setNextId(nextId + 1); // Increment the ID for the next entry
};
// Function to update the value of a user data entry const handleUserDataChange = (id: number, newValue: string) => {
const handleUserDataChange = (id: number, newValue: string) => { };
setUserData((prevUserData) =>
prevUserData.map((data) =>
data.id === id ? { ...data, value: newValue } : data
)
);
};
// Remove user data const handleRemoveUserData = (id: number) => {
const handleRemoveUserData = (id: number) => { };
setUserData((prevUserData) =>
prevUserData.filter((data) => data.id !== id)
);
};
const handleAnimationClick = (animation: string) => { const handleAnimationClick = (animation: string) => {
if (selectedFloorItem && selectedFloorItem.animationState) { if (selectedFloorItem) {
const isPlaying = selectedFloorItem.animationState?.playing || false; setCurrentAnimation(selectedFloorItem.uuid, animation, true, loopAnimation, true);
setCurrentAnimation(selectedFloorItem.uuid, animation, !isPlaying); }
} }
}
if (!selectedFloorItem) return null; if (!selectedFloorItem) return null;
return ( return (
<div className="asset-properties-container"> <div className="asset-properties-container">
{/* Name */} {/* Name */}
<div className="header">{selectedFloorItem.userData.modelName}</div> <div className="header">{selectedFloorItem.userData.modelName}</div>
<section> <section>
{objectPosition.x && objectPosition.z && {objectPosition &&
<PositionInput <PositionInput
onChange={() => { }} onChange={() => { }}
value1={parseFloat(objectPosition.x.toFixed(5))} value1={parseFloat(objectPosition.x.toFixed(5))}
value2={parseFloat(objectPosition.z.toFixed(5))} value2={parseFloat(objectPosition.z.toFixed(5))}
/> />
} }
{objectRotation.y && {objectRotation &&
<RotationInput <RotationInput
onChange={() => { }} onChange={() => { }}
value={parseFloat(objectRotation.y.toFixed(5))} value={parseFloat(objectRotation.y.toFixed(5))}
/> />
} }
</section> </section>
<section> <section>
<div className="header">Render settings</div> <div className="header">Render settings</div>
<InputToggle inputKey="visible" label="Visible" /> <InputToggle inputKey="visible" label="Visible" />
<InputToggle inputKey="frustumCull" label="Frustum cull" /> <InputToggle inputKey="frustumCull" label="Frustum cull" />
</section> </section>
<section> <section>
<div className="header">User Data</div> <div className="header">User Data</div>
{userData.map((data) => ( {userData.map((data) => (
<div className="input-container"> <div className="input-container">
<InputWithDropDown <InputWithDropDown
key={data.id} key={data.id}
label={data.label} label={data.label}
value={data.value} value={data.value}
editableLabel editableLabel
onChange={(newValue) => handleUserDataChange(data.id, newValue)} // Pass the change handler onChange={(newValue) => handleUserDataChange(data.id, newValue)}
/> />
<div <div
className="remove-button" className="remove-button"
onClick={() => handleRemoveUserData(data.id)} onClick={() => handleRemoveUserData(data.id)}
> >
<RemoveIcon /> <RemoveIcon />
</div> </div>
</div> </div>
))} ))}
{/* Add new user data */} {/* Add new user data */}
<div className="optimize-button" onClick={handleAddUserData}> <div className="optimize-button" onClick={handleAddUserData}>
+ Add + Add
</div>
</section>
<div style={{ display: "flex", flexDirection: "column", outline: "1px solid var(--border-color)" }}>
{selectedFloorItem.uuid && <div style={{ display: "flex", alignItems: "center", justifyContent: "center" }}>Animations</div>}
{assets.map((asset) => (
<div key={asset.modelUuid} className="asset-item">
{asset.modelUuid === selectedFloorItem.uuid &&
asset.animations &&
asset.animations.length > 0 &&
asset.animations.map((animation, index) => (
<div
key={index}
style={{ gap: "15px", cursor: "pointer", padding: "5px" }}
>
<div
onClick={() => handleAnimationClick(animation)}
onMouseEnter={() => setHoveredIndex(index)}
onMouseLeave={() => setHoveredIndex(null)}
style={{
height: "20px",
width: "100%",
borderRadius: "5px",
background:
hoveredIndex === index
? "#7b4cd3"
: "transparent",
}}
>
{animation.charAt(0).toUpperCase() +
animation.slice(1).toLowerCase()}
</div>
</div> </div>
))} </section>
</div> <div style={{ display: "flex", flexDirection: "column", outline: "1px solid var(--border-color)" }}>
))} {selectedFloorItem.uuid && <div style={{ display: "flex", alignItems: "center", justifyContent: "center" }}>Animations</div>}
</div> {assets.map((asset) => (
</div> <div key={asset.modelUuid} className="asset-item">
); {asset.modelUuid === selectedFloorItem.uuid &&
asset.animations &&
asset.animations.length > 0 &&
asset.animations.map((animation, index) => (
<div
key={index}
style={{ gap: "15px", cursor: "pointer", padding: "5px" }}
>
<div
onClick={() => handleAnimationClick(animation)}
onMouseEnter={() => setHoveredIndex(index)}
onMouseLeave={() => setHoveredIndex(null)}
style={{
height: "20px",
width: "100%",
borderRadius: "5px",
background:
hoveredIndex === index
? "#7b4cd3"
: "transparent",
}}
>
{animation.charAt(0).toUpperCase() +
animation.slice(1).toLowerCase()}
</div>
</div>
))}
</div>
))}
</div>
</div>
);
}; };
export default AssetProperties; export default AssetProperties;

View File

@@ -12,13 +12,17 @@ import { zoneCameraUpdate } from "../../../../services/visulization/zone/zoneCam
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { getUserData } from "../../../../functions/getUserData"; import { getUserData } from "../../../../functions/getUserData";
import { useVersionContext } from "../../../../modules/builder/version/versionContext"; import { useVersionContext } from "../../../../modules/builder/version/versionContext";
import { useSceneContext } from "../../../../modules/scene/sceneContext";
const ZoneProperties: React.FC = () => { const ZoneProperties: React.FC = () => {
const { Edit, setEdit } = useEditPosition(); const { Edit, setEdit } = useEditPosition();
const { selectedZone, setSelectedZone } = useSelectedZoneStore(); const { selectedZone, setSelectedZone } = useSelectedZoneStore();
const { zonePosition, setZonePosition } = usezonePosition(); const { zonePosition, setZonePosition } = usezonePosition();
const { zoneTarget, setZoneTarget } = usezoneTarget(); const { zoneTarget, setZoneTarget } = usezoneTarget();
const { zones, setZones } = useZones(); // const { zones, setZones } = useZones();
const { assetStore, zoneStore } = useSceneContext();
const { zones, setZoneName } = zoneStore()
const { projectId } = useParams(); const { projectId } = useParams();
const { userName, userId, organization, email } = getUserData(); const { userName, userId, organization, email } = getUserData();
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
@@ -34,10 +38,11 @@ const ZoneProperties: React.FC = () => {
let zonesdata = { let zonesdata = {
zoneUuid: selectedZone.zoneUuid, zoneUuid: selectedZone.zoneUuid,
viewPortposition: zonePosition, viewPortPosition: zonePosition,
viewPortCenter: zoneTarget, viewPortTarget: zoneTarget,
}; };
let response = await zoneCameraUpdate(zonesdata, organization, projectId, selectedVersion?.versionId || ""); let response = await zoneCameraUpdate(zonesdata, organization, projectId, selectedVersion?.versionId || "");
// console.log('response: ', response); // console.log('response: ', response);
if (response.message === "zone updated") { if (response.message === "zone updated") {
@@ -63,13 +68,14 @@ const ZoneProperties: React.FC = () => {
let response = await zoneCameraUpdate(zonesdata, organization, projectId, selectedVersion?.versionId || ""); let response = await zoneCameraUpdate(zonesdata, organization, projectId, selectedVersion?.versionId || "");
if (response.message === "zone updated") { if (response.message === "zone updated") {
setSelectedZone((prev) => ({ ...prev, zoneName: newName })); setSelectedZone((prev) => ({ ...prev, zoneName: newName }));
setZones((prevZones: any[]) => setZoneName(selectedZone.zoneUuid, newName)
prevZones.map((zone) => // setZones((prevZones: any[]) =>
zone.zoneUuid === selectedZone.zoneUuid // prevZones.map((zone) =>
? { ...zone, zoneName: newName } // zone.zoneUuid === selectedZone.zoneUuid
: zone // ? { ...zone, zoneName: newName }
) // : zone
); // )
// );
} else { } else {
// console.log(response?.message); // console.log(response?.message);
} }
@@ -81,6 +87,7 @@ const ZoneProperties: React.FC = () => {
setSelectedZone((prev) => ({ ...prev, [key]: newValue })); setSelectedZone((prev) => ({ ...prev, [key]: newValue }));
} }
const checkZoneNameDuplicate = (name: string) => { const checkZoneNameDuplicate = (name: string) => {
console.log('zones: ', zones);
return zones.some( return zones.some(
(zone: any) => (zone: any) =>
zone.zoneName?.trim().toLowerCase() === name?.trim().toLowerCase() && zone.zoneName?.trim().toLowerCase() === name?.trim().toLowerCase() &&

View File

@@ -8,6 +8,7 @@ import VehicleMechanics from "./mechanics/vehicleMechanics";
import RoboticArmMechanics from "./mechanics/roboticArmMechanics"; import RoboticArmMechanics from "./mechanics/roboticArmMechanics";
import MachineMechanics from "./mechanics/machineMechanics"; import MachineMechanics from "./mechanics/machineMechanics";
import StorageMechanics from "./mechanics/storageMechanics"; import StorageMechanics from "./mechanics/storageMechanics";
import HumanMechanics from "./mechanics/humanMechanics";
import { AddIcon } from "../../../../icons/ExportCommonIcons"; import { AddIcon } from "../../../../icons/ExportCommonIcons";
import { handleAddEventToProduct } from "../../../../../modules/simulation/events/points/functions/handleAddEventToProduct"; import { handleAddEventToProduct } from "../../../../../modules/simulation/events/points/functions/handleAddEventToProduct";
import { useProductContext } from "../../../../../modules/simulation/products/productContext"; import { useProductContext } from "../../../../../modules/simulation/products/productContext";
@@ -60,6 +61,8 @@ const EventProperties: React.FC = () => {
return "machine"; return "machine";
case "storageUnit": case "storageUnit":
return "storageUnit"; return "storageUnit";
case "human":
return "human";
default: default:
return null; return null;
} }
@@ -79,6 +82,7 @@ const EventProperties: React.FC = () => {
{assetType === "roboticArm" && <RoboticArmMechanics />} {assetType === "roboticArm" && <RoboticArmMechanics />}
{assetType === "machine" && <MachineMechanics />} {assetType === "machine" && <MachineMechanics />}
{assetType === "storageUnit" && <StorageMechanics />} {assetType === "storageUnit" && <StorageMechanics />}
{assetType === "human" && <HumanMechanics />}
</> </>
)} )}
{!currentEventData && selectedEventSphere && ( {!currentEventData && selectedEventSphere && (

View File

@@ -1,95 +1,70 @@
import React from "react"; import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import EyeDropInput from "../../../../../ui/inputs/EyeDropInput";
interface TravelActionProps { interface TravelActionProps {
loadCapacity: { loadCapacity: {
value: string; value: string;
min: number; min: number;
max: number; max: number;
defaultValue: string; defaultValue: string;
onChange: (value: string) => void; onChange: (value: string) => void;
}; };
unloadDuration: { unloadDuration: {
value: string; value: string;
min: number; min: number;
max: number; max: number;
defaultValue: string; defaultValue: string;
onChange: (value: string) => void; onChange: (value: string) => void;
}; };
pickPoint?: { clearPoints: () => void;
value: string;
onChange: (value: string) => void;
};
unloadPoint?: {
value: string;
onChange: (value: string) => void;
};
clearPoints: () => void;
} }
const TravelAction: React.FC<TravelActionProps> = ({ const TravelAction: React.FC<TravelActionProps> = ({
loadCapacity, loadCapacity,
unloadDuration, unloadDuration,
pickPoint, clearPoints,
unloadPoint,
clearPoints,
}) => { }) => {
return ( return (
<> <>
<InputWithDropDown <InputWithDropDown
label="Load Capacity" label="Load Capacity"
value={loadCapacity.value} value={loadCapacity.value}
min={loadCapacity.min} min={loadCapacity.min}
max={loadCapacity.max} max={loadCapacity.max}
defaultValue={loadCapacity.defaultValue} defaultValue={loadCapacity.defaultValue}
step={1} step={1}
activeOption="s" activeOption="unit"
onClick={() => {}} onClick={() => { }}
onChange={loadCapacity.onChange} onChange={loadCapacity.onChange}
/> />
<InputWithDropDown <InputWithDropDown
label="Unload Duration" label="Unload Duration"
value={unloadDuration.value} value={unloadDuration.value}
min={unloadDuration.min} min={unloadDuration.min}
max={unloadDuration.max} max={unloadDuration.max}
defaultValue={unloadDuration.defaultValue} defaultValue={unloadDuration.defaultValue}
step={0.1} step={0.1}
activeOption="s" activeOption="s"
onClick={() => {}} onClick={() => { }}
onChange={unloadDuration.onChange} onChange={unloadDuration.onChange}
/> />
<div className="selected-actions-list"> <div className="selected-actions-list">
<div className="value-field-container"> <div className="value-field-container">
<div className="label">Reset</div> <div className="label">Reset</div>
<button <button
id="rest-button" id="rest-button"
type="button" type="button"
className="regularDropdown-container" className="regularDropdown-container"
onClick={() => { onClick={() => {
clearPoints(); clearPoints();
}} }}
> >
Clear Clear
</button> </button>
</div> </div>
</div> </div>
{pickPoint && ( </>
<EyeDropInput );
label="Pick Point"
value={pickPoint.value}
onChange={pickPoint.onChange}
/>
)}
{unloadPoint && (
<EyeDropInput
label="Unload Point"
value={unloadPoint.value}
onChange={unloadPoint.onChange}
/>
)}
</>
);
}; };
export default TravelAction; export default TravelAction;

View File

@@ -0,0 +1,54 @@
import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
interface WorkerActionProps {
loadCapacity: {
value: string;
min: number;
max: number;
step: number;
defaultValue: string;
disabled?: boolean,
onChange: (value: string) => void;
};
clearPoints: () => void;
}
const WorkerAction: React.FC<WorkerActionProps> = ({
loadCapacity,
clearPoints,
}) => {
return (
<>
<InputWithDropDown
label="Load Capacity"
value={loadCapacity.value}
min={loadCapacity.min}
max={loadCapacity.max}
disabled={loadCapacity.disabled}
defaultValue={loadCapacity.defaultValue}
step={loadCapacity.step}
activeOption="unit"
onClick={() => { }}
onChange={loadCapacity.onChange}
/>
<div className="selected-actions-list">
<div className="value-field-container">
<div className="label">Reset</div>
<button
id="rest-button"
type="button"
className="regularDropdown-container"
onClick={() => {
clearPoints();
}}
>
Clear
</button>
</div>
</div>
</>
);
};
export default WorkerAction;

View File

@@ -0,0 +1,292 @@
import { useEffect, useState } from "react";
import { MathUtils } from "three";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import Trigger from "../trigger/Trigger";
import ActionsList from "../components/ActionsList";
import { useSelectedEventData, useSelectedAction, useSelectedAnimation } from "../../../../../../store/simulation/useSimulationStore";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
import { useVersionContext } from "../../../../../../modules/builder/version/versionContext";
import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
import { useParams } from "react-router-dom";
import WorkerAction from "../actions/workerAction";
function HumanMechanics() {
const [activeOption, setActiveOption] = useState<"worker">("worker");
const [speed, setSpeed] = useState("0.5");
const [loadCapacity, setLoadCapacity] = useState("1");
const [currentAction, setCurrentAction] = useState<HumanAction | undefined>();
const [selectedPointData, setSelectedPointData] = useState<HumanPointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { productStore } = useSceneContext();
const { getPointByUuid, updateEvent, updateAction, addAction, removeAction, getEventByModelUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { selectedAction, setSelectedAction, clearSelectedAction } = useSelectedAction();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
useEffect(() => {
if (selectedEventData && selectedEventData.data.type === "human") {
const point = getPointByUuid(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint
) as HumanPointSchema | undefined;
if (point?.action) {
setSelectedPointData(point);
setCurrentAction(point.action);
setSelectedAction(point.action.actionUuid, point.action.actionName);
setSpeed((
getEventByModelUuid(
selectedProduct.productUuid,
selectedEventData?.data.modelUuid || ""
) as HumanEventSchema | undefined
)?.speed?.toString() || "1");
setLoadCapacity(point.action.loadCapacity.toString());
}
} else {
clearSelectedAction();
}
}, [selectedEventData, selectedProduct]);
useEffect(() => {
if (selectedEventData && selectedProduct.productUuid) {
const point = getPointByUuid(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint
) as HumanPointSchema | undefined;
if (point?.action) {
setSelectedPointData(point);
setCurrentAction(point.action);
setActiveOption(point.action.actionType);
setSelectedAction(point.action.actionUuid, point.action.actionName);
}
} else {
clearSelectedAction();
setCurrentAction(undefined);
setSpeed("0.5");
setLoadCapacity("1");
}
}, [selectedEventData, selectedProduct, selectedAction]);
const updateBackend = (
productName: string,
productUuid: string,
projectId: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName,
productUuid,
projectId,
eventDatas: eventData,
versionId: selectedVersion?.versionId || "",
});
};
const handleSelectActionType = (actionType: string) => {
if (!selectedAction.actionId || !currentAction || !selectedPointData) return;
const updatedAction = { ...currentAction, actionType: actionType as "worker" };
const updatedPoint = { ...selectedPointData, action: updatedAction };
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
updatedAction
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
setCurrentAction(updatedAction);
setSelectedPointData(updatedPoint);
};
const handleSpeedChange = (value: string) => {
if (!selectedEventData) return;
const numericValue = parseFloat(value);
if (isNaN(numericValue)) return;
const updatedEvent = {
...selectedEventData.data,
speed: numericValue
} as HumanEventSchema;
const event = updateEvent(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
updatedEvent
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
setSpeed(value);
};
const handleLoadCapacityChange = (value: string) => {
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
const updatedAction = { ...currentAction };
updatedAction.loadCapacity = parseInt(value)
const updatedPoint = { ...selectedPointData, action: updatedAction };
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
updatedAction
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
setCurrentAction(updatedAction);
setSelectedPointData(updatedPoint);
setLoadCapacity(value);
};
const handleClearPoints = () => {
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
const updatedAction = { ...currentAction };
delete updatedAction.pickUpPoint;
delete updatedAction.dropPoint;
const updatedPoint = { ...selectedPointData, action: updatedAction };
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
updatedAction
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
setCurrentAction(updatedAction);
setSelectedPointData(updatedPoint);
};
const handleAddAction = () => {
if (!selectedEventData || !selectedPointData) return;
const newAction: HumanAction = {
actionUuid: MathUtils.generateUUID(),
actionName: `Action`,
actionType: "worker",
loadCapacity: 1,
triggers: [],
};
const event = addAction(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint,
newAction
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
const updatedPoint = { ...selectedPointData, action: newAction };
setSelectedPointData(updatedPoint);
setSelectedAction(newAction.actionUuid, newAction.actionName);
};
const handleDeleteAction = () => {
if (!selectedPointData) return;
const event = removeAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
const updatedPoint = { ...selectedPointData, action: undefined as any };
setSelectedPointData(updatedPoint);
clearSelectedAction();
setCurrentAction(undefined);
};
return (
<>
<div className="global-props section">
<div className="property-list-container">
<div className="property-item">
<InputWithDropDown
label="Speed"
value={speed}
min={0}
step={0.1}
defaultValue="0.5"
max={10}
activeOption="m/s"
onClick={() => { }}
onChange={handleSpeedChange}
/>
</div>
</div>
</div>
<section>
<ActionsList
selectedPointData={selectedPointData}
multipleAction={false}
handleAddAction={handleAddAction}
handleDeleteAction={handleDeleteAction}
/>
{selectedAction.actionId && currentAction && (
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput value={selectedAction.actionName || ""} canEdit={false} />
</div>
<div className="selected-actions-list">
<LabledDropdown
label="Action Type"
defaultOption={activeOption}
options={["worker"]}
onSelect={handleSelectActionType}
disabled={true}
/>
</div>
<WorkerAction
loadCapacity={{
value: loadCapacity,
min: 1,
max: 5,
step: 1,
defaultValue: "1",
disabled: true,
onChange: handleLoadCapacityChange,
}}
clearPoints={handleClearPoints}
/>
<div className="tirgger">
<Trigger selectedPointData={selectedPointData as any} type="Human" />
</div>
</div>
)}
</section>
</>
);
}
export default HumanMechanics;

View File

@@ -274,14 +274,6 @@ function VehicleMechanics() {
onChange: handleUnloadDurationChange, onChange: handleUnloadDurationChange,
}} }}
clearPoints={handleClearPoints} clearPoints={handleClearPoints}
// pickPoint={{
// value: currentPickPoint,
// onChange: handlePickPointChange,
// }}
// unloadPoint={{
// value: currentUnloadPoint,
// onChange: handleUnloadPointChange,
// }}
/> />
)} )}
</div> </div>

View File

@@ -13,7 +13,7 @@ import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
type TriggerProps = { type TriggerProps = {
selectedPointData?: PointsScheme | undefined; selectedPointData?: PointsScheme | undefined;
type?: "Conveyor" | "Vehicle" | "RoboticArm" | "Machine" | "StorageUnit"; type?: "Conveyor" | "Vehicle" | "RoboticArm" | "Machine" | "StorageUnit" | "Human";
}; };
const Trigger = ({ selectedPointData, type }: TriggerProps) => { const Trigger = ({ selectedPointData, type }: TriggerProps) => {
@@ -38,7 +38,7 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
if (type === "Conveyor" || type === "Vehicle" || type === "Machine" || type === "StorageUnit") { if (type === "Conveyor" || type === "Vehicle" || type === "Machine" || type === "StorageUnit") {
actionUuid = (selectedPointData as | ConveyorPointSchema | VehiclePointSchema | MachinePointSchema | StoragePointSchema).action?.actionUuid; actionUuid = (selectedPointData as | ConveyorPointSchema | VehiclePointSchema | MachinePointSchema | StoragePointSchema).action?.actionUuid;
} else if (type === "RoboticArm" && selectedAction.actionId) { } else if ((type === "RoboticArm" || type === "Human") && selectedAction.actionId) {
actionUuid = selectedAction.actionId; actionUuid = selectedAction.actionId;
} }
@@ -365,18 +365,16 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
} }
/> />
</button> </button>
{triggers.length > 1 && ( <button
<button id="remove-trigger-button"
id="remove-trigger-button" className="remove-button"
className="remove-button" onClick={(e) => {
onClick={(e) => { e.stopPropagation();
e.stopPropagation(); handleRemoveTrigger(trigger.triggerUuid);
handleRemoveTrigger(trigger.triggerUuid); }}
}} >
> <RemoveIcon />
<RemoveIcon /> </button>
</button>
)}
</div> </div>
))} ))}
</div> </div>

View File

@@ -28,9 +28,9 @@ const VersionHistory = () => {
const handleSelectVersion = (version: Version) => { const handleSelectVersion = (version: Version) => {
if (!projectId) return; if (!projectId) return;
getVersionDataApi(projectId, version.versionId).then((verdionData) => { getVersionDataApi(projectId, version.versionId).then((versionData) => {
setSelectedVersion(version); setSelectedVersion(version);
// console.log(verdionData); // console.log(versionData);
}).catch((err) => { }).catch((err) => {
// console.log(err); // console.log(err);
}) })

View File

@@ -8,6 +8,7 @@ type InputWithDropDownProps = {
max?: number; max?: number;
step?: number; step?: number;
defaultValue?: string; defaultValue?: string;
disabled?: boolean;
options?: string[]; // Array of dropdown options options?: string[]; // Array of dropdown options
activeOption?: string; // The currently active dropdown option activeOption?: string; // The currently active dropdown option
onClick?: () => void; onClick?: () => void;
@@ -23,6 +24,7 @@ const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
max, max,
step, step,
defaultValue, defaultValue,
disabled = false,
options, options,
activeOption, activeOption,
onClick, onClick,
@@ -54,6 +56,7 @@ const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
type="number" type="number"
defaultValue={value} defaultValue={value}
// value={value} // value={value}
disabled={disabled}
onChange={(e) => { onChange={(e) => {
onChange(e.target.value); onChange(e.target.value);
}} }}

View File

@@ -9,9 +9,10 @@ interface RenameInputProps {
value: string; value: string;
onRename?: (newText: string) => void; onRename?: (newText: string) => void;
checkDuplicate?: (name: string) => boolean; checkDuplicate?: (name: string) => boolean;
canEdit?: boolean;
} }
const RenameInput: React.FC<RenameInputProps> = ({ value, onRename, checkDuplicate }) => { const RenameInput: React.FC<RenameInputProps> = ({ value, onRename, checkDuplicate, canEdit = true }) => {
const [isEditing, setIsEditing] = useState(false); const [isEditing, setIsEditing] = useState(false);
const [text, setText] = useState(value); const [text, setText] = useState(value);
const [isDuplicate, setIsDuplicate] = useState(false); const [isDuplicate, setIsDuplicate] = useState(false);
@@ -28,13 +29,15 @@ const RenameInput: React.FC<RenameInputProps> = ({ value, onRename, checkDuplica
}, [text, checkDuplicate]); }, [text, checkDuplicate]);
const handleDoubleClick = () => { const handleDoubleClick = () => {
setIsEditing(true); if (canEdit) {
setTimeout(() => inputRef.current?.focus(), 0); setIsEditing(true);
setTimeout(() => inputRef.current?.focus(), 0);
}
}; };
const handleBlur = () => { const handleBlur = () => {
if(isDuplicate) return if (isDuplicate) return
setIsEditing(false); setIsEditing(false);
if (onRename && !isDuplicate) { if (onRename && !isDuplicate) {
onRename(text); onRename(text);

View File

@@ -44,16 +44,19 @@ const DropDownList: React.FC<DropDownListProps> = ({
remove, remove,
}) => { }) => {
const [isOpen, setIsOpen] = useState<boolean>(defaultOpen); const [isOpen, setIsOpen] = useState<boolean>(defaultOpen);
const { zones } = useZones(); // const { zones } = useZones();
const handleToggle = () => { const handleToggle = () => {
setIsOpen((prev) => !prev); // Toggle the state setIsOpen((prev) => !prev); // Toggle the state
}; };
const [zoneDataList, setZoneDataList] = useState<ZoneData[]>([]); const [zoneDataList, setZoneDataList] = useState<ZoneData[]>([]);
const { assetStore } = useSceneContext(); // const { assetStore } = useSceneContext();
const { assetStore, zoneStore } = useSceneContext();
const { assets } = assetStore(); const { assets } = assetStore();
const { zones } = zoneStore()
const isPointInsidePolygon = ( const isPointInsidePolygon = (
point: [number, number], point: [number, number],
polygon: [number, number][] polygon: [number, number][]
@@ -76,7 +79,7 @@ const DropDownList: React.FC<DropDownListProps> = ({
}; };
useEffect(() => { useEffect(() => {
const updatedZoneList: ZoneData[] = zones?.map((zone: Zone) => { const updatedZoneList: ZoneData[] = zones?.map((zone: any) => {
const polygon2D = zone.points.map((p: [number, number, number]) => [ const polygon2D = zone.points.map((p: [number, number, number]) => [
p[0], p[0],
p[2], p[2],

View File

@@ -46,7 +46,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const { selectedZone, setSelectedZone } = useSelectedZoneStore(); const { selectedZone, setSelectedZone } = useSelectedZoneStore();
const { zoneAssetId, setZoneAssetId } = useZoneAssetId(); const { zoneAssetId, setZoneAssetId } = useZoneAssetId();
const { zones, setZones } = useZones();
const { setSubModule } = useSubModuleStore(); const { setSubModule } = useSubModuleStore();
const [expandedZones, setExpandedZones] = useState<Record<string, boolean>>({}); const [expandedZones, setExpandedZones] = useState<Record<string, boolean>>({});
const { projectId } = useParams(); const { projectId } = useParams();
@@ -55,6 +55,8 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
const { organization } = getUserData(); const { organization } = getUserData();
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore(); const { selectedVersion } = selectedVersionStore();
const { zoneStore } = useSceneContext();
const { zones, setZoneName } = zoneStore()
useEffect(() => { useEffect(() => {
useSelectedZoneStore.getState().setSelectedZone({ useSelectedZoneStore.getState().setSelectedZone({
@@ -92,8 +94,8 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
lockedPanels: response?.lockedPanels ?? [], lockedPanels: response?.lockedPanels ?? [],
widgets: response?.widgets ?? [], widgets: response?.widgets ?? [],
zoneUuid: response?.zoneUuid, zoneUuid: response?.zoneUuid,
zoneViewPortTarget: response?.viewPortCenter ?? [], zoneViewPortTarget: response?.viewPortTarget ?? [],
zoneViewPortPosition: response?.viewPortposition ?? [], zoneViewPortPosition: response?.viewPortPosition ?? [],
}); });
} catch (error) { } catch (error) {
echo.error("Failed to select zone"); echo.error("Failed to select zone");
@@ -123,13 +125,14 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
const response = await zoneCameraUpdate(zonesdata, organization, projectId, selectedVersion?.versionId || ""); const response = await zoneCameraUpdate(zonesdata, organization, projectId, selectedVersion?.versionId || "");
if (response.message === "zone updated") { if (response.message === "zone updated") {
setSelectedZone((prev) => ({ ...prev, zoneName: newName })); setSelectedZone((prev) => ({ ...prev, zoneName: newName }));
setZones((prevZones: any[]) => setZoneName(selectedZone.zoneUuid, newName)
prevZones.map((zone) => // setZones((prevZones: any[]) =>
zone.zoneUuid === selectedZone.zoneUuid // prevZones.map((zone) =>
? { ...zone, zoneName: newName } // zone.zoneUuid === selectedZone.zoneUuid
: zone // ? { ...zone, zoneName: newName }
) // : zone
); // )
// );
} }
} }

View File

@@ -3,12 +3,14 @@ import { useEffect, useMemo, useRef, useState } from 'react'
import { useThree } from '@react-three/fiber'; import { useThree } from '@react-three/fiber';
import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store'; import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
import { upsertAisleApi } from '../../../../services/factoryBuilder/aisle/upsertAisleApi';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { useVersionContext } from '../../version/versionContext'; import { useVersionContext } from '../../version/versionContext';
import { useSceneContext } from '../../../scene/sceneContext'; import { useSceneContext } from '../../../scene/sceneContext';
import ReferenceAisle from './referenceAisle'; import ReferenceAisle from './referenceAisle';
import ReferencePoint from '../../point/reference/referencePoint'; import ReferencePoint from '../../point/reference/referencePoint';
import { getUserData } from '../../../../functions/getUserData';
// import { upsertAisleApi } from '../../../../services/factoryBuilder/aisle/upsertAisleApi';
function AisleCreator() { function AisleCreator() {
const { scene, camera, raycaster, gl, pointer } = useThree(); const { scene, camera, raycaster, gl, pointer } = useThree();
@@ -23,6 +25,7 @@ function AisleCreator() {
const isLeftMouseDown = useRef(false); const isLeftMouseDown = useRef(false);
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore(); const { selectedVersion } = selectedVersionStore();
const { userId, organization } = getUserData();
const { projectId } = useParams(); const { projectId } = useParams();
const [tempPoints, setTempPoints] = useState<Point[]>([]); const [tempPoints, setTempPoints] = useState<Point[]>([]);
@@ -106,7 +109,22 @@ function AisleCreator() {
}; };
addAisle(aisle); addAisle(aisle);
if (projectId) { if (projectId) {
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
// API
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
// SOCKET
socket.emit('v1:model-aisle:add', {
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization,
aisleUuid: aisle.aisleUuid,
points: aisle.points,
type: aisle.type
})
} }
setTempPoints([newPoint]); setTempPoints([newPoint]);
} }
@@ -129,7 +147,22 @@ function AisleCreator() {
}; };
addAisle(aisle); addAisle(aisle);
if (projectId) { if (projectId) {
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
// API
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
// SOCKET
socket.emit('v1:model-aisle:add', {
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization,
aisleUuid: aisle.aisleUuid,
points: aisle.points,
type: aisle.type
})
} }
setTempPoints([newPoint]); setTempPoints([newPoint]);
} }
@@ -151,7 +184,22 @@ function AisleCreator() {
}; };
addAisle(aisle); addAisle(aisle);
if (projectId) { if (projectId) {
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
// API
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
// SOCKET
socket.emit('v1:model-aisle:add', {
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization,
aisleUuid: aisle.aisleUuid,
points: aisle.points,
type: aisle.type
})
} }
setTempPoints([newPoint]); setTempPoints([newPoint]);
} }
@@ -172,7 +220,22 @@ function AisleCreator() {
}; };
addAisle(aisle); addAisle(aisle);
if (projectId) { if (projectId) {
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
// API
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
// SOCKET
socket.emit('v1:model-aisle:add', {
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization,
aisleUuid: aisle.aisleUuid,
points: aisle.points,
type: aisle.type
})
} }
setTempPoints([newPoint]); setTempPoints([newPoint]);
} }
@@ -195,7 +258,22 @@ function AisleCreator() {
}; };
addAisle(aisle); addAisle(aisle);
if (projectId) { if (projectId) {
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
// API
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
// SOCKET
socket.emit('v1:model-aisle:add', {
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization,
aisleUuid: aisle.aisleUuid,
points: aisle.points,
type: aisle.type
})
} }
setTempPoints([newPoint]); setTempPoints([newPoint]);
} }
@@ -217,7 +295,22 @@ function AisleCreator() {
}; };
addAisle(aisle); addAisle(aisle);
if (projectId) { if (projectId) {
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
// API
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
// SOCKET
socket.emit('v1:model-aisle:add', {
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization,
aisleUuid: aisle.aisleUuid,
points: aisle.points,
type: aisle.type
})
} }
setTempPoints([newPoint]); setTempPoints([newPoint]);
} }
@@ -238,7 +331,22 @@ function AisleCreator() {
}; };
addAisle(aisle); addAisle(aisle);
if (projectId) { if (projectId) {
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
// API
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
// SOCKET
socket.emit('v1:model-aisle:add', {
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization,
aisleUuid: aisle.aisleUuid,
points: aisle.points,
type: aisle.type
})
} }
setTempPoints([newPoint]); setTempPoints([newPoint]);
} }
@@ -260,7 +368,22 @@ function AisleCreator() {
}; };
addAisle(aisle); addAisle(aisle);
if (projectId) { if (projectId) {
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
// API
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
// SOCKET
socket.emit('v1:model-aisle:add', {
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization,
aisleUuid: aisle.aisleUuid,
points: aisle.points,
type: aisle.type
})
} }
setTempPoints([newPoint]); setTempPoints([newPoint]);
} }

View File

@@ -1,4 +1,4 @@
import * as THREE from "three" import * as THREE from "three"
import { useEffect } from 'react' import { useEffect } from 'react'
import { getFloorAssets } from '../../../services/factoryBuilder/asset/floorAsset/getFloorItemsApi'; import { getFloorAssets } from '../../../services/factoryBuilder/asset/floorAsset/getFloorItemsApi';
import { useLoadingProgress, useRenameModeStore, useSelectedFloorItem, useSelectedItem, useSocketStore } from '../../../store/builder/store'; import { useLoadingProgress, useRenameModeStore, useSelectedFloorItem, useSelectedItem, useSocketStore } from '../../../store/builder/store';
@@ -226,7 +226,8 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
modelUuid: item.modelUuid, modelUuid: item.modelUuid,
modelName: item.modelName, modelName: item.modelName,
position: item.position, position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z], state: "idle", rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle",
type: "storageUnit", type: "storageUnit",
point: { point: {
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
@@ -242,6 +243,30 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
} }
}; };
addEvent(storageEvent); addEvent(storageEvent);
} else if (item.eventData.type === 'Human') {
const humanEvent: HumanEventSchema = {
modelUuid: item.modelUuid,
modelName: item.modelName,
position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle",
type: "human",
speed: 1,
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],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCapacity: 1,
triggers: []
}
}
}
addEvent(humanEvent);
} }
} else { } else {
assets.push({ assets.push({

View File

@@ -170,7 +170,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 };
if (selectedItem.type === "Conveyor") { if (selectedItem.type === "Conveyor") {
const ConveyorEvent: ConveyorEventSchema = { const ConveyorEvent: ConveyorEventSchema = {
@@ -360,6 +360,35 @@ async function handleModelLoad(
position: storageEvent.point.position, position: storageEvent.point.position,
rotation: storageEvent.point.rotation, rotation: storageEvent.point.rotation,
}; };
} else if (selectedItem.type === "Human") {
const humanEvent: HumanEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: newFloorItem.rotation,
state: "idle",
type: "human",
speed: 1,
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCapacity: 1,
triggers: []
}
}
}
addEvent(humanEvent);
eventData.point = {
uuid: humanEvent.point.uuid,
position: humanEvent.point.position,
rotation: humanEvent.point.rotation,
}
} }
const completeData = { const completeData = {

View File

@@ -6,7 +6,7 @@ import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import { ThreeEvent, useFrame, useThree } from '@react-three/fiber'; import { ThreeEvent, useFrame, useThree } from '@react-three/fiber';
import { useActiveTool, useDeletableFloorItem, useLimitDistance, useRenderDistance, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store'; import { useActiveTool, useDeletableFloorItem, useLimitDistance, useRenderDistance, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store';
import { AssetBoundingBox } from '../../functions/assetBoundingBox'; import { AssetBoundingBox } from '../../functions/assetBoundingBox';
import { CameraControls, Html } from '@react-three/drei'; import { CameraControls } from '@react-three/drei';
import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore'; import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore';
import { useLeftData, useTopData } from '../../../../../store/visualization/useZone3DWidgetStore'; import { useLeftData, useTopData } from '../../../../../store/visualization/useZone3DWidgetStore';
import { useSelectedAsset } from '../../../../../store/simulation/useSimulationStore'; import { useSelectedAsset } from '../../../../../store/simulation/useSimulationStore';
@@ -15,6 +15,8 @@ import { useParams } from 'react-router-dom';
import { getUserData } from '../../../../../functions/getUserData'; import { getUserData } from '../../../../../functions/getUserData';
import { useSceneContext } from '../../../../scene/sceneContext'; import { useSceneContext } from '../../../../scene/sceneContext';
import { useVersionContext } from '../../../version/versionContext'; import { useVersionContext } from '../../../version/versionContext';
import { SkeletonUtils } from 'three-stdlib';
import { useAnimationPlaySpeed } from '../../../../../store/usePlayButtonStore';
function Model({ asset }: { readonly asset: Asset }) { function Model({ asset }: { readonly asset: Asset }) {
const { camera, controls, gl } = useThree(); const { camera, controls, gl } = useThree();
@@ -22,8 +24,9 @@ function Model({ asset }: { readonly asset: Asset }) {
const { toggleView } = useToggleView(); const { toggleView } = useToggleView();
const { subModule } = useSubModuleStore(); const { subModule } = useSubModuleStore();
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const { speed } = useAnimationPlaySpeed();
const { assetStore, eventStore, productStore } = useSceneContext(); const { assetStore, eventStore, productStore } = useSceneContext();
const { assets, removeAsset, setAnimations } = assetStore(); const { removeAsset, setAnimations, resetAnimation, setAnimationComplete, setCurrentAnimation: setAnmationAnimation } = assetStore();
const { setTop } = useTopData(); const { setTop } = useTopData();
const { setLeft } = useLeftData(); const { setLeft } = useLeftData();
const { getIsEventInProduct } = productStore(); const { getIsEventInProduct } = productStore();
@@ -33,7 +36,7 @@ function Model({ asset }: { readonly asset: Asset }) {
const { setSelectedAsset, clearSelectedAsset } = useSelectedAsset(); const { setSelectedAsset, clearSelectedAsset } = useSelectedAsset();
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem(); const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
const { setSelectedFloorItem } = useSelectedFloorItem(); const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { limitDistance } = useLimitDistance(); const { limitDistance } = useLimitDistance();
const { renderDistance } = useRenderDistance(); const { renderDistance } = useRenderDistance();
const [isRendered, setIsRendered] = useState(false); const [isRendered, setIsRendered] = useState(false);
@@ -46,13 +49,18 @@ function Model({ asset }: { readonly asset: Asset }) {
const { selectedVersion } = selectedVersionStore(); const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams(); const { projectId } = useParams();
const { userId, organization } = getUserData(); const { userId, organization } = getUserData();
const [animationNames, setAnimationNames] = useState<string[]>([]);
const mixerRef = useRef<THREE.AnimationMixer>(); const mixerRef = useRef<THREE.AnimationMixer>();
const actions = useRef<{ [name: string]: THREE.AnimationAction }>({}); const actions = useRef<{ [name: string]: THREE.AnimationAction }>({});
const [previousAnimation, setPreviousAnimation] = useState<string | null>(null);
const blendFactor = useRef(0);
const blendDuration = 0.5;
useEffect(() => { useEffect(() => {
setDeletableFloorItem(null); setDeletableFloorItem(null);
}, [activeModule, toolMode]) if (selectedFloorItem === null) {
resetAnimation(asset.modelUuid);
}
}, [activeModule, toolMode, selectedFloorItem])
useEffect(() => { useEffect(() => {
const loader = new GLTFLoader(); const loader = new GLTFLoader();
@@ -62,40 +70,21 @@ function Model({ asset }: { readonly asset: Asset }) {
loader.setDRACOLoader(dracoLoader); loader.setDRACOLoader(dracoLoader);
const loadModel = async () => { const loadModel = async () => {
try { try {
// Check Cache
// const assetId = asset.assetId;
// const cachedModel = THREE.Cache.get(assetId);
// if (cachedModel) {
// setGltfScene(cachedModel.scene.clone());
// calculateBoundingBox(cachedModel.scene);
// return;
// }
// Check Cache // Check Cache
// const assetId = asset.assetId;
// console.log('assetId: ', assetId);
// const cachedModel = THREE.Cache.get(assetId);
// console.log('cachedModel: ', cachedModel);
// if (cachedModel) {
// setGltfScene(cachedModel.scene.clone());
// calculateBoundingBox(cachedModel.scene);
// return;
// }
const assetId = asset.assetId; const assetId = asset.assetId;
const cachedModel = THREE.Cache.get(assetId); const cachedModel = THREE.Cache.get(assetId);
if (cachedModel) { if (cachedModel) {
const clonedScene = cachedModel.scene.clone(); const clone: any = SkeletonUtils.clone(cachedModel.scene);
clonedScene.animations = cachedModel.animations || []; clone.animations = cachedModel.animations || [];
setGltfScene(clonedScene); setGltfScene(clone);
calculateBoundingBox(clonedScene); calculateBoundingBox(clone);
if (cachedModel.animations && clonedScene.animations.length > 0) { if (cachedModel.animations && clone.animations.length > 0) {
const animationName = clonedScene.animations.map((clip: any) => clip.name); const animationName = clone.animations.map((clip: any) => clip.name);
setAnimationNames(animationName)
setAnimations(asset.modelUuid, animationName) setAnimations(asset.modelUuid, animationName)
mixerRef.current = new THREE.AnimationMixer(clonedScene); mixerRef.current = new THREE.AnimationMixer(clone);
clonedScene.animations.forEach((animation: any) => { clone.animations.forEach((animation: any) => {
const action = mixerRef.current!.clipAction(animation); const action = mixerRef.current!.clipAction(animation);
actions.current[animation.name] = action; actions.current[animation.name] = action;
}); });
@@ -293,28 +282,50 @@ function Model({ asset }: { readonly asset: Asset }) {
clearSelectedAsset() clearSelectedAsset()
} }
} }
const handleAnimationComplete = useCallback(() => {
if (asset.animationState) {
setAnimationComplete(asset.modelUuid, true);
}
}, [asset.animationState]);
useFrame((_, delta) => { useFrame((_, delta) => {
if (mixerRef.current) { if (mixerRef.current) {
mixerRef.current.update(delta); mixerRef.current.update(delta * speed);
} }
}); });
useEffect(() => { useEffect(() => {
const handlePlay = (clipName: string) => { if (!asset.animationState || !mixerRef.current) return;
if (!mixerRef.current) return;
const { current, loopAnimation, isPlaying } = asset.animationState;
const currentAction = actions.current[current];
const previousAction = previousAnimation ? actions.current[previousAnimation] : null;
if (isPlaying && currentAction) {
blendFactor.current = 0;
currentAction.reset();
currentAction.setLoop(loopAnimation ? THREE.LoopRepeat : THREE.LoopOnce, loopAnimation ? Infinity : 1);
currentAction.clampWhenFinished = true;
if (previousAction && previousAction !== currentAction) {
previousAction.crossFadeTo(currentAction, blendDuration, false);
}
currentAction.play();
mixerRef.current.addEventListener('finished', handleAnimationComplete);
setPreviousAnimation(current);
} else {
Object.values(actions.current).forEach((action) => action.stop()); Object.values(actions.current).forEach((action) => action.stop());
}
const action = actions.current[clipName]; return () => {
if (action && asset.animationState?.playing) { if (mixerRef.current) {
action.reset().setLoop(THREE.LoopOnce, 1).play(); mixerRef.current.removeEventListener('finished', handleAnimationComplete);
} }
}; };
}, [asset.animationState?.current, asset.animationState?.isPlaying]);
handlePlay(asset.animationState?.current || '');
}, [asset])
return ( return (
<group <group
@@ -362,17 +373,6 @@ function Model({ asset }: { readonly asset: Asset }) {
<AssetBoundingBox boundingBox={boundingBox} /> <AssetBoundingBox boundingBox={boundingBox} />
) )
)} )}
{/* <group >
<Html>
<div style={{ position: 'absolute', }}>
{animationNames.map((name) => (
<button key={name} onClick={() => handlePlay(name)} style={{ margin: 4 }}>
{name}
</button>
))}
</div>
</Html>
</group> */}
</group > </group >
); );
} }

View File

@@ -10,8 +10,8 @@ import { useParams } from 'react-router-dom';
import { useVersionContext } from '../version/versionContext'; import { useVersionContext } from '../version/versionContext';
import { useSceneContext } from '../../scene/sceneContext'; import { useSceneContext } from '../../scene/sceneContext';
import { upsertAisleApi } from '../../../services/factoryBuilder/aisle/upsertAisleApi'; // import { upsertAisleApi } from '../../../services/factoryBuilder/aisle/upsertAisleApi';
import { deleteAisleApi } from '../../../services/factoryBuilder/aisle/deleteAisleApi'; // import { deleteAisleApi } from '../../../services/factoryBuilder/aisle/deleteAisleApi';
// import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi'; // import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi';
// import { deleteWallApi } from '../../../services/factoryBuilder/wall/deleteWallApi'; // import { deleteWallApi } from '../../../services/factoryBuilder/wall/deleteWallApi';
// import { upsertFloorApi } from '../../../services/factoryBuilder/floor/upsertFloorApi'; // import { upsertFloorApi } from '../../../services/factoryBuilder/floor/upsertFloorApi';
@@ -159,7 +159,22 @@ function Point({ point }: { readonly point: Point }) {
const updatedAisles = getAislesByPointId(point.pointUuid); const updatedAisles = getAislesByPointId(point.pointUuid);
if (updatedAisles.length > 0 && projectId) { if (updatedAisles.length > 0 && projectId) {
updatedAisles.forEach((updatedAisle) => { updatedAisles.forEach((updatedAisle) => {
upsertAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || '')
// API
// upsertAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || '');
// SOCKET
socket.emit('v1:model-aisle:add', {
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization,
aisleUuid: updatedAisle.aisleUuid,
points: updatedAisle.points,
type: updatedAisle.type
})
}) })
} }
} else if (point.pointType === 'Wall') { } else if (point.pointType === 'Wall') {
@@ -238,7 +253,22 @@ function Point({ point }: { readonly point: Point }) {
if (removedAisles.length > 0) { if (removedAisles.length > 0) {
removedAisles.forEach(aisle => { removedAisles.forEach(aisle => {
if (projectId) { if (projectId) {
deleteAisleApi(aisle.aisleUuid, projectId, selectedVersion?.versionId || '')
// API
// deleteAisleApi(aisle.aisleUuid, projectId, selectedVersion?.versionId || '');
// SOCKET
const data = {
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization,
aisleUuid: aisle.aisleUuid
}
socket.emit('v1:model-aisle:delete', data);
} }
}); });
setHoveredPoint(null); setHoveredPoint(null);

View File

@@ -20,7 +20,7 @@ function ZoneCreator() {
const { activeLayer } = useActiveLayer(); const { activeLayer } = useActiveLayer();
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const { zoneStore } = useSceneContext(); const { zoneStore } = useSceneContext();
const { addZone, getZonePointById, getZoneByPoints } = zoneStore(); const { zones, addZone, getZonePointById, getZoneByPoints } = zoneStore();
const drag = useRef(false); const drag = useRef(false);
const isLeftMouseDown = useRef(false); const isLeftMouseDown = useRef(false);
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
@@ -32,6 +32,7 @@ function ZoneCreator() {
const [isCreating, setIsCreating] = useState(false); const [isCreating, setIsCreating] = useState(false);
const { zoneColor, zoneHeight, snappedPosition, snappedPoint, setSnappedPoint, setSnappedPosition } = useBuilderStore(); const { zoneColor, zoneHeight, snappedPosition, snappedPoint, setSnappedPoint, setSnappedPosition } = useBuilderStore();
useEffect(() => { useEffect(() => {
const canvasElement = gl.domElement; const canvasElement = gl.domElement;
@@ -92,7 +93,7 @@ function ZoneCreator() {
if (tempPoints.length > 2 && isCreating && snappedPoint && snappedPoint.pointUuid === tempPoints[0].pointUuid) { if (tempPoints.length > 2 && isCreating && snappedPoint && snappedPoint.pointUuid === tempPoints[0].pointUuid) {
const zone: Zone = { const zone: Zone = {
zoneUuid: THREE.MathUtils.generateUUID(), zoneUuid: THREE.MathUtils.generateUUID(),
zoneName: "Zone", zoneName: `Zone `,
points: tempPoints, points: tempPoints,
zoneColor, zoneColor,
zoneHeight, zoneHeight,

View File

@@ -6,7 +6,7 @@ export default function SocketResponses() {
useEffect(() => { useEffect(() => {
socket.on("v1:model-asset:response:add", (data: any) => { socket.on("v1:model-asset:response:add", (data: any) => {
console.log('data: ', data); // console.log('data: ', data);
}); });
return () => { return () => {

View File

@@ -332,6 +332,34 @@ const CopyPasteControls = ({
position: storageEvent.point.position, position: storageEvent.point.position,
rotation: storageEvent.point.rotation rotation: storageEvent.point.rotation
}; };
} else if (obj.userData.eventData.type === "Human") {
const humanEvent: HumanEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
state: "idle",
type: "human",
speed: 1,
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]],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCapacity: 1,
triggers: []
}
}
}
addEvent(humanEvent);
eventData.point = {
uuid: humanEvent.point.uuid,
position: humanEvent.point.position,
rotation: humanEvent.point.rotation
};
} }
newFloorItem.eventData = eventData; newFloorItem.eventData = eventData;

View File

@@ -306,6 +306,34 @@ const DuplicationControls = ({
position: storageEvent.point.position, position: storageEvent.point.position,
rotation: storageEvent.point.rotation rotation: storageEvent.point.rotation
}; };
} else if (obj.userData.eventData.type === "Human") {
const humanEvent: HumanEventSchema = {
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
position: newFloorItem.position,
rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
state: "idle",
type: "human",
speed: 1,
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]],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1",
actionType: "worker",
loadCapacity: 1,
triggers: []
}
}
}
addEvent(humanEvent);
eventData.point = {
uuid: humanEvent.point.uuid,
position: humanEvent.point.position,
rotation: humanEvent.point.rotation
};
} }
newFloorItem.eventData = eventData; newFloorItem.eventData = eventData;

View File

@@ -16,6 +16,7 @@ import { createMachineStore, MachineStoreType } from '../../store/simulation/use
import { createConveyorStore, ConveyorStoreType } from '../../store/simulation/useConveyorStore'; import { createConveyorStore, ConveyorStoreType } from '../../store/simulation/useConveyorStore';
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';
type SceneContextValue = { type SceneContextValue = {
@@ -35,6 +36,7 @@ type SceneContextValue = {
conveyorStore: ConveyorStoreType; conveyorStore: ConveyorStoreType;
vehicleStore: VehicleStoreType; vehicleStore: VehicleStoreType;
storageUnitStore: StorageUnitStoreType; storageUnitStore: StorageUnitStoreType;
humanStore: HumanStoreType;
clearStores: () => void; clearStores: () => void;
@@ -67,6 +69,7 @@ export function SceneProvider({
const conveyorStore = useMemo(() => createConveyorStore(), []); const conveyorStore = useMemo(() => createConveyorStore(), []);
const vehicleStore = useMemo(() => createVehicleStore(), []); const vehicleStore = useMemo(() => createVehicleStore(), []);
const storageUnitStore = useMemo(() => createStorageUnitStore(), []); const storageUnitStore = useMemo(() => createStorageUnitStore(), []);
const humanStore = useMemo(() => createHumanStore(), []);
const clearStores = useMemo(() => () => { const clearStores = useMemo(() => () => {
assetStore.getState().clearAssets(); assetStore.getState().clearAssets();
@@ -83,7 +86,8 @@ export function SceneProvider({
conveyorStore.getState().clearConveyors(); conveyorStore.getState().clearConveyors();
vehicleStore.getState().clearVehicles(); vehicleStore.getState().clearVehicles();
storageUnitStore.getState().clearStorageUnits(); storageUnitStore.getState().clearStorageUnits();
}, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore]); humanStore.getState().clearHumans();
}, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore]);
const contextValue = useMemo(() => ( const contextValue = useMemo(() => (
{ {
@@ -101,10 +105,11 @@ export function SceneProvider({
conveyorStore, conveyorStore,
vehicleStore, vehicleStore,
storageUnitStore, storageUnitStore,
humanStore,
clearStores, clearStores,
layout layout
} }
), [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, clearStores, layout]); ), [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, clearStores, layout]);
return ( return (
<SceneContext.Provider value={contextValue}> <SceneContext.Provider value={contextValue}>

View File

@@ -0,0 +1,36 @@
import { useCallback } from "react";
import { useSceneContext } from "../../../../scene/sceneContext";
import { useProductContext } from "../../../products/productContext";
export function useWorkerHandler() {
const { materialStore, humanStore, productStore } = useSceneContext();
const { getMaterialById } = materialStore();
const { getModelUuidByActionUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { incrementHumanLoad, addCurrentMaterial } = humanStore();
const workerLogStatus = (materialUuid: string, status: string) => {
echo.info(`${materialUuid}, ${status}`);
}
const handleWorker = useCallback((action: HumanAction, materialId?: string) => {
if (!action || action.actionType !== 'worker' || !materialId) return;
const material = getMaterialById(materialId);
if (!material) return;
const modelUuid = getModelUuidByActionUuid(selectedProduct.productUuid, action.actionUuid);
if (!modelUuid) return;
incrementHumanLoad(modelUuid, 1);
addCurrentMaterial(modelUuid, material.materialType, material.materialId);
workerLogStatus(material.materialName, `performing worker action`);
}, [getMaterialById]);
return {
handleWorker,
};
}

View File

@@ -0,0 +1,36 @@
import { useEffect, useCallback } from 'react';
import { useWorkerHandler } from './actionHandler/useWorkerHandler';
export function useHumanActions() {
const { handleWorker } = useWorkerHandler();
const handleWorkerAction = useCallback((action: HumanAction, materialId: string) => {
handleWorker(action, materialId);
}, [handleWorker]);
const handleHumanAction = useCallback((action: HumanAction, materialId: string) => {
if (!action) return;
switch (action.actionType) {
case 'worker':
handleWorkerAction(action, materialId);
break;
default:
console.warn(`Unknown Human action type: ${action.actionType}`);
}
}, [handleWorkerAction]);
const cleanup = useCallback(() => {
}, []);
useEffect(() => {
return () => {
cleanup();
};
}, [cleanup]);
return {
handleHumanAction,
cleanup
};
}

View File

@@ -27,7 +27,7 @@ export function useStorageActions() {
default: default:
console.warn(`Unknown storage action type: ${action.actionType}`); console.warn(`Unknown storage action type: ${action.actionType}`);
} }
}, [handleStoreAction]); }, [handleStoreAction, handleRetrieveAction]);
const cleanup = useCallback(() => { const cleanup = useCallback(() => {
}, []); }, []);

View File

@@ -7,6 +7,7 @@ import { useMachineActions } from "./machine/useMachineActions";
import { useRoboticArmActions } from "./roboticArm/useRoboticArmActions"; import { useRoboticArmActions } from "./roboticArm/useRoboticArmActions";
import { useStorageActions } from "./storageUnit/useStorageUnitActions"; import { useStorageActions } from "./storageUnit/useStorageUnitActions";
import { useVehicleActions } from "./vehicle/useVehicleActions"; import { useVehicleActions } from "./vehicle/useVehicleActions";
import { useHumanActions } from "./human/useHumanActions";
import { useCallback, useEffect } from "react"; import { useCallback, useEffect } from "react";
export function useActionHandler() { export function useActionHandler() {
@@ -17,6 +18,7 @@ export function useActionHandler() {
const { handleRoboticArmAction, cleanup: cleanupRoboticArm } = useRoboticArmActions(); const { handleRoboticArmAction, cleanup: cleanupRoboticArm } = useRoboticArmActions();
const { handleMachineAction, cleanup: cleanupMachine } = useMachineActions(); const { handleMachineAction, cleanup: cleanupMachine } = useMachineActions();
const { handleStorageAction, cleanup: cleanupStorage } = useStorageActions(); const { handleStorageAction, cleanup: cleanupStorage } = useStorageActions();
const { handleHumanAction, cleanup: cleanupHuman } = useHumanActions();
const handleAction = useCallback((action: Action, materialId?: string) => { const handleAction = useCallback((action: Action, materialId?: string) => {
if (!action) return; if (!action) return;
@@ -37,6 +39,9 @@ export function useActionHandler() {
case 'store': case 'retrieve': case 'store': case 'retrieve':
handleStorageAction(action as StorageAction, materialId as string); handleStorageAction(action as StorageAction, materialId as string);
break; break;
case 'worker':
handleHumanAction(action as HumanAction, materialId as string);
break;
default: default:
console.warn(`Unknown action type: ${(action as Action).actionType}`); console.warn(`Unknown action type: ${(action as Action).actionType}`);
} }
@@ -44,7 +49,7 @@ export function useActionHandler() {
echo.error("Failed to handle action"); echo.error("Failed to handle action");
console.error("Error handling action:", error); console.error("Error handling action:", error);
} }
}, [handleConveyorAction, handleVehicleAction, handleRoboticArmAction, handleMachineAction, handleStorageAction,]); }, [handleConveyorAction, handleVehicleAction, handleRoboticArmAction, handleMachineAction, handleStorageAction, handleHumanAction]);
const cleanup = useCallback(() => { const cleanup = useCallback(() => {
cleanupConveyor(); cleanupConveyor();
@@ -52,7 +57,8 @@ export function useActionHandler() {
cleanupRoboticArm(); cleanupRoboticArm();
cleanupMachine(); cleanupMachine();
cleanupStorage(); cleanupStorage();
}, [cleanupConveyor, cleanupVehicle, cleanupRoboticArm, cleanupMachine, cleanupStorage,]); cleanupHuman();
}, [cleanupConveyor, cleanupVehicle, cleanupRoboticArm, cleanupMachine, cleanupStorage, cleanupHuman]);
useEffect(() => { useEffect(() => {
return () => { return () => {

View File

@@ -1,19 +1,15 @@
import React, { useEffect } from 'react' import { useEffect } from 'react'
import { useInputValues, useProductionCapacityData, useThroughPutData } from '../../../../store/builder/store' import { useInputValues, useProductionCapacityData, useThroughPutData } from '../../../../store/builder/store'
import { usePlayButtonStore } from '../../../../store/usePlayButtonStore'; import { usePlayButtonStore } from '../../../../store/usePlayButtonStore';
import { useProductContext } from '../../products/productContext';
export default function ProductionCapacityData() { export default function ProductionCapacityData() {
const { throughputData } = useThroughPutData() const { throughputData } = useThroughPutData()
const { productionCapacityData, setProductionCapacityData } = useProductionCapacityData() const { setProductionCapacityData } = useProductionCapacityData()
const { inputValues } = useInputValues(); const { inputValues } = useInputValues();
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
useEffect(() => { useEffect(() => {
if (!isPlaying) { if (!isPlaying) {
setProductionCapacityData(0); setProductionCapacityData(0);
} }
}, [isPlaying]); }, [isPlaying]);
@@ -26,8 +22,6 @@ export default function ProductionCapacityData() {
const Monthly_working_days = workingDaysPerYear / 12; const Monthly_working_days = workingDaysPerYear / 12;
const Production_capacity_per_month = throughputData * Monthly_working_days; const Production_capacity_per_month = throughputData * Monthly_working_days;
setProductionCapacityData(Number(Production_capacity_per_month.toFixed(2))); setProductionCapacityData(Number(Production_capacity_per_month.toFixed(2)));
} }
}, [throughputData, inputValues, isPlaying]); }, [throughputData, inputValues, isPlaying]);

View File

@@ -1,18 +1,9 @@
import React, { useEffect } from 'react'
import { usePlayButtonStore } from '../../../store/usePlayButtonStore'
import ProductionCapacityData from './productionCapacity/productionCapacityData' import ProductionCapacityData from './productionCapacity/productionCapacityData'
import ThroughPutData from './throughPut/throughPutData' import ThroughPutData from './throughPut/throughPutData'
import ROIData from './ROI/roiData' import ROIData from './ROI/roiData'
function SimulationAnalysis() { function SimulationAnalysis() {
const { isPlaying } = usePlayButtonStore()
// useEffect(()=>{
// if (isPlaying) {
//
// } else {
//
// }
// },[isPlaying])
return ( return (
<> <>
<ThroughPutData /> <ThroughPutData />

View File

@@ -352,6 +352,37 @@ function PointsCreator() {
</mesh> </mesh>
</group> </group>
); );
} else if (usedEvent.type === "human") {
const point = usedEvent.point;
return (
<group
key={`${index}-${usedEvent.modelUuid}`}
position={usedEvent.position}
rotation={usedEvent.rotation}
>
<mesh
name="Event-Sphere"
uuid={point.uuid}
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
onClick={(e) => {
e.stopPropagation();
if (toolMode === 'cursor') {
setSelectedEventSphere(
sphereRefs.current[point.uuid]
);
}
}}
position={new THREE.Vector3(...point.position)}
userData={{
modelUuid: usedEvent.modelUuid,
pointUuid: point.uuid,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="white" />
</mesh>
</group>
);
} else { } else {
return null; return null;
} }

View File

@@ -1,4 +1,3 @@
import React from 'react'
import PointsCreator from './creator/pointsCreator' import PointsCreator from './creator/pointsCreator'
function Points() { function Points() {

View File

@@ -152,6 +152,22 @@ function TriggerConnector() {
}); });
} }
} }
// Handle Human point
else if (event.type === "human" && 'point' in event) {
const point = event.point;
if (point.action?.triggers) {
point.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

@@ -0,0 +1,76 @@
import { useEffect, useRef } from 'react';
import { useFrame } from '@react-three/fiber';
import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../store/usePlayButtonStore';
import { useSceneContext } from '../../../scene/sceneContext';
type HumanCallback = {
humanId: string;
callback: () => void;
};
export function useHumanEventManager() {
const { humanStore } = useSceneContext();
const { getHumanById } = humanStore();
const callbacksRef = useRef<HumanCallback[]>([]);
const isMonitoringRef = useRef(false);
const { isPlaying } = usePlayButtonStore();
const { isPaused } = usePauseButtonStore();
const { isReset } = useResetButtonStore();
useEffect(() => {
if (isReset) {
callbacksRef.current = [];
}
}, [isReset])
// Add a new human to monitor
const addHumanToMonitor = (humanId: string, callback: () => void) => {
// Avoid duplicates
if (!callbacksRef.current.some((entry) => entry.humanId === humanId)) {
callbacksRef.current.push({ humanId, callback });
}
// Start monitoring if not already running
if (!isMonitoringRef.current) {
isMonitoringRef.current = true;
}
};
// Remove a human from monitoring
const removeHumanFromMonitor = (humanId: string) => {
callbacksRef.current = callbacksRef.current.filter(
(entry) => entry.humanId !== humanId
);
// Stop monitoring if no more humans to track
if (callbacksRef.current.length === 0) {
isMonitoringRef.current = false;
}
};
// Check human states every frame
useFrame(() => {
if (!isMonitoringRef.current || callbacksRef.current.length === 0 || !isPlaying || isPaused) return;
callbacksRef.current.forEach(({ humanId, callback }) => {
const human = getHumanById(humanId);
if (human && human.isActive === false && human.state === 'idle' && human.isPicking && human.currentLoad < human.point.action.loadCapacity) {
callback();
removeHumanFromMonitor(humanId); // Remove after triggering
}
});
});
// Cleanup on unmount
useEffect(() => {
return () => {
callbacksRef.current = [];
isMonitoringRef.current = false;
};
}, []);
return {
addHumanToMonitor,
removeHumanFromMonitor,
};
}

View File

@@ -0,0 +1,40 @@
import { useEffect, useState } from 'react'
import { useSelectedAnimation, useSelectedEventSphere } from '../../../store/simulation/useSimulationStore';
import { usePlayButtonStore } from '../../../store/usePlayButtonStore';
import { useSceneContext } from '../../scene/sceneContext';
import HumanInstances from './instances/humanInstances'
import HumanUi from './instances/instance/humanUi';
function Human() {
const { humanStore } = useSceneContext();
const { getHumanById } = humanStore();
const { selectedAnimation } = useSelectedAnimation();
const { selectedEventSphere } = useSelectedEventSphere();
const { isPlaying } = usePlayButtonStore();
const [isVehicleSelected, setIsHumanSelected] = useState(false);
useEffect(() => {
if (selectedEventSphere) {
const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid);
if (selectedHuman) {
setIsHumanSelected(true);
} else {
setIsHumanSelected(false);
}
}
}, [getHumanById, selectedEventSphere, selectedAnimation])
return (
<>
<HumanInstances />
{isVehicleSelected && selectedEventSphere && !isPlaying &&
<HumanUi />
}
</>
)
}
export default Human

View File

@@ -0,0 +1,191 @@
import { useEffect, useRef, useState } from 'react'
import { useFrame, useThree } from '@react-three/fiber';
import * as THREE from 'three';
import { Line } from '@react-three/drei';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
import { useSceneContext } from '../../../../scene/sceneContext';
interface HumanAnimatorProps {
path: [number, number, number][];
handleCallBack: () => void;
reset: () => void;
startUnloadingProcess: () => void;
currentPhase: string;
human: HumanStatus;
}
function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, startUnloadingProcess }: Readonly<HumanAnimatorProps>) {
const { humanStore, assetStore } = useSceneContext();
const { getHumanById } = humanStore();
const { setCurrentAnimation } = assetStore();
const { isPaused } = usePauseButtonStore();
const { isPlaying } = usePlayButtonStore();
const { speed } = useAnimationPlaySpeed();
const { isReset, setReset } = useResetButtonStore();
const progressRef = useRef<number>(0);
const movingForward = useRef<boolean>(true);
const completedRef = useRef<boolean>(false);
const [objectRotation, setObjectRotation] = useState<[number, number, number] | null>(human.point?.action?.pickUpPoint?.rotation || [0, 0, 0])
const [restRotation, setRestingRotation] = useState<boolean>(true);
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
const { scene } = useThree();
useEffect(() => {
if (currentPhase === 'init-pickup' && path.length > 0) {
setCurrentPath(path);
setObjectRotation(human.point.action.pickUpPoint?.rotation ?? null)
} else if (currentPhase === 'pickup-drop' && path.length > 0) {
setObjectRotation(human.point.action?.dropPoint?.rotation ?? null)
setCurrentPath(path);
} else if (currentPhase === 'drop-pickup' && path.length > 0) {
setObjectRotation(human.point.action?.pickUpPoint?.rotation ?? null)
setCurrentPath(path);
}
}, [currentPhase, path, objectRotation]);
useEffect(() => {
completedRef.current = false;
}, [currentPath]);
useEffect(() => {
if (isReset || !isPlaying) {
reset();
setCurrentPath([]);
completedRef.current = false;
movingForward.current = true;
progressRef.current = 0;
setReset(false);
setRestingRotation(true);
const object = scene.getObjectByProperty('uuid', human.modelUuid);
const humanData = getHumanById(human.modelUuid);
if (object && humanData) {
object.position.set(humanData.position[0], humanData.position[1], humanData.position[2]);
object.rotation.set(humanData.rotation[0], humanData.rotation[1], humanData.rotation[2]);
}
}
}, [isReset, isPlaying])
const lastTimeRef = useRef(performance.now());
useFrame(() => {
const now = performance.now();
const delta = (now - lastTimeRef.current) / 1000;
lastTimeRef.current = now;
const object = scene.getObjectByProperty('uuid', human.modelUuid);
if (!object || currentPath.length < 2) return;
if (isPaused) return;
let totalDistance = 0;
const distances = [];
let accumulatedDistance = 0;
let index = 0;
const rotationSpeed = 1;
for (let i = 0; i < currentPath.length - 1; i++) {
const start = new THREE.Vector3(...currentPath[i]);
const end = new THREE.Vector3(...currentPath[i + 1]);
const segmentDistance = start.distanceTo(end);
distances.push(segmentDistance);
totalDistance += segmentDistance;
}
while (index < distances.length && progressRef.current > accumulatedDistance + distances[index]) {
accumulatedDistance += distances[index];
index++;
}
if (index < distances.length) {
const start = new THREE.Vector3(...currentPath[index]);
const end = new THREE.Vector3(...currentPath[index + 1]);
const segmentDistance = distances[index];
const targetQuaternion = new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().lookAt(start, end, new THREE.Vector3(0, 1, 0)));
const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
targetQuaternion.multiply(y180);
const angle = object.quaternion.angleTo(targetQuaternion);
if (angle < 0.01) {
object.quaternion.copy(targetQuaternion);
} else {
object.quaternion.slerp(targetQuaternion, delta * rotationSpeed * speed * human.speed * 5);
}
const isAligned = angle < 0.01;
if (isAligned) {
progressRef.current += delta * (speed * human.speed);
const t = (progressRef.current - accumulatedDistance) / segmentDistance;
const position = start.clone().lerp(end, t);
object.position.copy(position);
if (human.currentMaterials.length > 0) {
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
} else {
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
}
} else {
if (human.currentMaterials.length > 0) {
setCurrentAnimation(human.modelUuid, 'idle_with_box', true, true, true);
} else {
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
}
}
}
if (progressRef.current >= totalDistance) {
if (restRotation && objectRotation) {
const targetEuler = new THREE.Euler(0, objectRotation[1], 0);
const baseQuaternion = new THREE.Quaternion().setFromEuler(targetEuler);
const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
const targetQuaternion = baseQuaternion.multiply(y180);
const angle = object.quaternion.angleTo(targetQuaternion);
if (angle < 0.01) {
object.quaternion.copy(targetQuaternion);
setRestingRotation(false);
} else {
object.quaternion.slerp(targetQuaternion, delta * rotationSpeed * speed * human.speed * 4);
}
if (human.currentMaterials.length > 0) {
setCurrentAnimation(human.modelUuid, 'idle_with_box', true, true, true);
} else {
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
}
return;
}
}
if (progressRef.current >= totalDistance) {
setRestingRotation(true);
progressRef.current = 0;
movingForward.current = !movingForward.current;
setCurrentPath([]);
handleCallBack();
if (currentPhase === 'pickup-drop') {
requestAnimationFrame(startUnloadingProcess);
}
}
});
return (
<>
{currentPath.length > 0 && (
// helper
<group visible={false}>
<Line points={currentPath} color="blue" lineWidth={3} />
{currentPath.map((point, index) => (
<mesh key={index} position={point}>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="red" />
</mesh>
))}
</group>
)}
</>
)
}
export default HumanAnimator

View File

@@ -0,0 +1,44 @@
import { useEffect, useRef, useState } from 'react';
import { useThree } from '@react-three/fiber';
import * as THREE from 'three';
import { MaterialModel } from '../../../materials/instances/material/materialModel';
const MaterialAnimator = ({ human }: { human: HumanStatus }) => {
const meshRef = useRef<any>(null!);
const [hasLoad, setHasLoad] = useState(false);
const { scene } = useThree();
useEffect(() => {
setHasLoad(human.currentLoad > 0);
}, [human.currentLoad]);
useEffect(() => {
if (!hasLoad || !meshRef.current) return;
const humanModel = scene.getObjectByProperty("uuid", human.modelUuid) as THREE.Object3D;
if (!humanModel) return;
const bone = humanModel.getObjectByName('PlaceObjectRefBone') as THREE.Bone;
if (bone) {
bone.add(meshRef.current);
meshRef.current.position.set(0, 0, 0);
meshRef.current.rotation.set(0, 0, 0);
meshRef.current.scale.set(1, 1, 1);
}
}, [hasLoad, human.modelUuid]);
return (
<>
{hasLoad && human.currentMaterials.length > 0 && (
<MaterialModel
matRef={meshRef}
materialId={human.currentMaterials[0].materialId || ''}
materialType={human.currentMaterials[0].materialType || 'Default material'}
/>
)}
</>
);
};
export default MaterialAnimator;

View File

@@ -0,0 +1,20 @@
import React from 'react'
import HumanInstance from './instance/humanInstance';
import { useSceneContext } from '../../../scene/sceneContext';
function HumanInstances() {
const { humanStore } = useSceneContext();
const { humans } = humanStore();
return (
<>
{humans.map((human: HumanStatus) => (
<React.Fragment key={human.modelUuid}>
<HumanInstance human={human} />
</React.Fragment>
))}
</>
)
}
export default HumanInstances

View File

@@ -0,0 +1,688 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import * as THREE from 'three';
import { useThree } from '@react-three/fiber';
import { NavMeshQuery } from '@recast-navigation/core';
import { useNavMesh } from '../../../../../store/builder/store';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
import { useSceneContext } from '../../../../scene/sceneContext';
import { useProductContext } from '../../../products/productContext';
import HumanAnimator from '../animator/humanAnimator';
import MaterialAnimator from '../animator/materialAnimator';
function HumanInstance({ human }: { human: HumanStatus }) {
const { navMesh } = useNavMesh();
const { isPlaying } = usePlayButtonStore();
const { scene } = useThree();
const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext();
const { removeMaterial, setEndTime } = materialStore();
const { getStorageUnitById } = storageUnitStore();
const { getArmBotById } = armBotStore();
const { getConveyorById } = conveyorStore();
const { getVehicleById } = vehicleStore();
const { getMachineById } = machineStore();
const { triggerPointActions } = useTriggerHandler();
const { setCurrentAnimation, resetAnimation, getAssetById } = assetStore();
const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { setHumanActive, setHumanState, setHumanPicking, clearCurrentMaterials, setHumanLoad, decrementHumanLoad, removeLastMaterial, incrementIdleTime, incrementActiveTime, resetTime } = humanStore();
const [currentPhase, setCurrentPhase] = useState<string>('init');
const [path, setPath] = useState<[number, number, number][]>([]);
const pauseTimeRef = useRef<number | null>(null);
const idleTimeRef = useRef<number>(0);
const activeTimeRef = useRef<number>(0);
const isPausedRef = useRef<boolean>(false);
const isSpeedRef = useRef<number>(0);
const { speed } = useAnimationPlaySpeed();
const { isPaused } = usePauseButtonStore();
const previousTimeRef = useRef<number | null>(null);
const animationFrameIdRef = useRef<number | null>(null);
const humanAsset = getAssetById(human.modelUuid);
useEffect(() => {
isPausedRef.current = isPaused;
}, [isPaused]);
useEffect(() => {
isSpeedRef.current = speed;
}, [speed]);
const computePath = useCallback(
(start: any, end: any) => {
try {
const navMeshQuery = new NavMeshQuery(navMesh);
const { path: segmentPath } = navMeshQuery.computePath(start, end);
if (
segmentPath.length > 0 &&
Math.round(segmentPath[segmentPath.length - 1].x) == Math.round(end.x) &&
Math.round(segmentPath[segmentPath.length - 1].z) == Math.round(end.z)
) {
return segmentPath?.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
} else {
console.log("There is no path here...Choose valid path")
const { path: segmentPaths } = navMeshQuery.computePath(start, start);
return segmentPaths.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
}
} catch {
console.error("Failed to compute path");
return [];
}
}, [navMesh]);
function humanStatus(modelId: string, status: string) {
// console.log(`${modelId} , ${status}`);
}
function reset() {
setCurrentPhase('init');
setHumanActive(human.modelUuid, false);
setHumanPicking(human.modelUuid, false);
setHumanState(human.modelUuid, 'idle');
setHumanLoad(human.modelUuid, 0);
resetAnimation(human.modelUuid);
setPath([]);
isPausedRef.current = false;
pauseTimeRef.current = 0;
resetTime(human.modelUuid)
activeTimeRef.current = 0
idleTimeRef.current = 0
previousTimeRef.current = null
if (animationFrameIdRef.current !== null) {
cancelAnimationFrame(animationFrameIdRef.current)
animationFrameIdRef.current = null
}
const object = scene.getObjectByProperty('uuid', human.modelUuid);
if (object && human) {
object.position.set(human.position[0], human.position[1], human.position[2]);
object.rotation.set(human.rotation[0], human.rotation[1], human.rotation[2]);
}
}
useEffect(() => {
if (isPlaying) {
if (!human.point.action.pickUpPoint || !human.point.action.dropPoint) return;
if (!human.isActive && human.state === 'idle' && currentPhase === 'init') {
const toPickupPath = computePath(
new THREE.Vector3(human?.position[0], human?.position[1], human?.position[2]),
new THREE.Vector3(
human?.point?.action?.pickUpPoint?.position?.[0] ?? 0,
human?.point?.action?.pickUpPoint?.position?.[1] ?? 0,
human?.point?.action?.pickUpPoint?.position?.[2] ?? 0
)
);
setPath(toPickupPath);
setCurrentPhase('init-pickup');
setHumanState(human.modelUuid, 'running');
setHumanPicking(human.modelUuid, false);
setHumanActive(human.modelUuid, true);
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
humanStatus(human.modelUuid, 'Started from init, heading to pickup');
return;
} else if (!human.isActive && human.state === 'idle' && currentPhase === 'picking') {
if (humanAsset && human.currentLoad === human.point.action.loadCapacity && human.currentMaterials.length > 0 && humanAsset.animationState?.current === 'pickup' && humanAsset.animationState?.isCompleted) {
if (human.point.action.pickUpPoint && human.point.action.dropPoint) {
const toDrop = computePath(
new THREE.Vector3(
human.point.action.pickUpPoint.position?.[0] ?? 0,
human.point.action.pickUpPoint.position?.[1] ?? 0,
human.point.action.pickUpPoint.position?.[2] ?? 0
),
new THREE.Vector3(
human.point.action.dropPoint.position?.[0] ?? 0,
human.point.action.dropPoint.position?.[1] ?? 0,
human.point.action.dropPoint.position?.[2] ?? 0
)
);
setPath(toDrop);
setCurrentPhase('pickup-drop');
setHumanState(human.modelUuid, 'running');
setHumanPicking(human.modelUuid, false);
setHumanPicking(human.modelUuid, true);
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
humanStatus(human.modelUuid, 'Started from pickup point, heading to drop point');
}
} else if (human.currentLoad === human.point.action.loadCapacity && human.currentMaterials.length > 0) {
setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
}
} else if (!human.isActive && human.state === 'idle' && currentPhase === 'dropping' && human.currentLoad === 0) {
if (human.point.action.pickUpPoint && human.point.action.dropPoint) {
const dropToPickup = computePath(
new THREE.Vector3(
human.point.action.dropPoint.position?.[0] ?? 0,
human.point.action.dropPoint.position?.[1] ?? 0,
human.point.action.dropPoint.position?.[2] ?? 0
),
new THREE.Vector3(
human.point.action.pickUpPoint.position?.[0] ?? 0,
human.point.action.pickUpPoint.position?.[1] ?? 0,
human.point.action.pickUpPoint.position?.[2] ?? 0
)
);
setPath(dropToPickup);
setCurrentPhase('drop-pickup');
setHumanState(human.modelUuid, 'running');
setHumanPicking(human.modelUuid, false);
setHumanActive(human.modelUuid, true);
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
humanStatus(human.modelUuid, 'Started from dropping point, heading to pickup point');
}
}
} else {
reset()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [human, currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
function handleCallBack() {
if (currentPhase === 'init-pickup') {
setCurrentPhase('picking');
setHumanState(human.modelUuid, 'idle');
setHumanPicking(human.modelUuid, true);
setHumanActive(human.modelUuid, false);
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
humanStatus(human.modelUuid, 'Reached pickup point, waiting for material');
setPath([]);
} else if (currentPhase === 'pickup-drop') {
setCurrentPhase('dropping');
setHumanState(human.modelUuid, 'idle');
setHumanPicking(human.modelUuid, false);
setHumanActive(human.modelUuid, false);
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
humanStatus(human.modelUuid, 'Reached drop point');
setPath([]);
} else if (currentPhase === 'drop-pickup') {
setCurrentPhase('picking');
setHumanState(human.modelUuid, 'idle');
setHumanPicking(human.modelUuid, true);
setHumanActive(human.modelUuid, false);
setPath([]);
clearCurrentMaterials(human.modelUuid);
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
humanStatus(human.modelUuid, 'Reached pickup point again, cycle complete');
}
}
function animate(currentTime: number) {
if (previousTimeRef.current === null) {
previousTimeRef.current = currentTime;
}
const deltaTime = (currentTime - previousTimeRef.current) / 1000;
previousTimeRef.current = currentTime;
if (human.isActive) {
if (!isPausedRef.current) {
activeTimeRef.current += deltaTime * isSpeedRef.current;
}
} else {
if (!isPausedRef.current) {
idleTimeRef.current += deltaTime * isSpeedRef.current;
}
}
animationFrameIdRef.current = requestAnimationFrame(animate);
}
useEffect(() => {
if (!isPlaying) return
if (!human.isActive) {
const roundedActiveTime = Math.round(activeTimeRef.current);
incrementActiveTime(human.modelUuid, roundedActiveTime);
activeTimeRef.current = 0;
} else {
const roundedIdleTime = Math.round(idleTimeRef.current);
incrementIdleTime(human.modelUuid, roundedIdleTime);
idleTimeRef.current = 0;
}
if (animationFrameIdRef.current === null) {
animationFrameIdRef.current = requestAnimationFrame(animate);
}
return () => {
if (animationFrameIdRef.current !== null) {
cancelAnimationFrame(animationFrameIdRef.current);
animationFrameIdRef.current = null;
}
};
}, [human, isPlaying]);
function startUnloadingProcess() {
const humanAsset = getAssetById(human.modelUuid);
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
if (human.point.action.triggers.length > 0) {
const trigger = getTriggerByUuid(selectedProduct.productUuid, human.point.action.triggers[0]?.triggerUuid);
const model = getEventByModelUuid(selectedProduct.productUuid, trigger?.triggeredAsset?.triggeredModel?.modelUuid || '');
if (trigger && model) {
if (model.type === 'transfer') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToConveyor(model);
}
} else if (model.type === 'machine') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToMachine(model);
}
} else if (model.type === 'roboticArm') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToArmBot(model);
}
} else if (model.type === 'storageUnit') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToStorageUnit(model);
}
} else if (model.type === 'vehicle') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToVehicle(model);
}
}
} else {
const droppedMaterial = human.currentLoad;
handleMaterialDropByDefault(droppedMaterial);
}
} else {
const droppedMaterial = human.currentLoad;
handleMaterialDropByDefault(droppedMaterial);
}
} else {
requestAnimationFrame(startUnloadingProcess);
}
}
function handleMaterialDropToStorageUnit(model: StorageEventSchema) {
const humanAsset = getAssetById(human.modelUuid);
if (model && humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
}
const checkAnimation = () => {
if (humanAsset?.animationState?.isCompleted) {
if (model.point.action.actionType === 'store') {
loopMaterialDropToStorage(
human.modelUuid,
human.currentLoad,
model.modelUuid,
model.point.action.storageCapacity,
human.point.action
);
}
} else {
requestAnimationFrame(checkAnimation);
}
};
checkAnimation();
}
function loopMaterialDropToStorage(
humanId: string,
humanCurrentLoad: number,
storageUnitId: string,
storageMaxCapacity: number,
action: HumanAction
) {
const storageUnit = getStorageUnitById(storageUnitId);
const humanAsset = getAssetById(human.modelUuid);
if (!storageUnit || humanCurrentLoad <= 0 || storageUnit.currentLoad >= storageMaxCapacity) {
return;
}
decrementHumanLoad(humanId, 1);
humanCurrentLoad -= 1;
const material = removeLastMaterial(humanId);
if (material) {
triggerPointActions(action, material.materialId);
}
if (humanCurrentLoad > 0 && storageUnit.currentLoad < storageMaxCapacity) {
resetAnimation(human.modelUuid);
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
const waitForNextDrop = () => {
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
loopMaterialDropToStorage(
humanId,
humanCurrentLoad,
storageUnitId,
storageMaxCapacity,
action
);
} else {
requestAnimationFrame(waitForNextDrop);
}
};
waitForNextDrop();
}
}
function handleMaterialDropToConveyor(model: ConveyorEventSchema) {
const humanAsset = getAssetById(human.modelUuid);
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
}
const checkAnimation = () => {
if (humanAsset?.animationState?.isCompleted) {
const conveyor = getConveyorById(model.modelUuid);
if (conveyor) {
loopMaterialDropToConveyor(
human.modelUuid,
human.currentLoad,
conveyor.modelUuid,
human.point.action
);
}
} else {
requestAnimationFrame(checkAnimation);
}
};
checkAnimation();
}
function loopMaterialDropToConveyor(
humanId: string,
humanCurrentLoad: number,
conveyorId: string,
action: HumanAction
) {
const conveyor = getConveyorById(conveyorId);
const humanAsset = getAssetById(human.modelUuid);
if (!conveyor || humanCurrentLoad <= 0) {
return;
}
decrementHumanLoad(humanId, 1);
humanCurrentLoad -= 1;
const material = removeLastMaterial(humanId);
if (material) {
triggerPointActions(action, material.materialId);
}
if (humanCurrentLoad > 0) {
resetAnimation(human.modelUuid);
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
const waitForNextDrop = () => {
if (humanAsset?.animationState?.isCompleted) {
loopMaterialDropToConveyor(
humanId,
humanCurrentLoad,
conveyorId,
action
);
} else {
requestAnimationFrame(waitForNextDrop);
}
};
waitForNextDrop();
}
}
function handleMaterialDropToArmBot(model: RoboticArmEventSchema) {
const humanAsset = getAssetById(human.modelUuid);
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
}
const checkAnimation = () => {
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
const armBot = getArmBotById(model.modelUuid);
if (armBot && armBot.state === 'idle' && !armBot.isActive) {
loopMaterialDropToArmBot(
human.modelUuid,
human.currentLoad,
model.modelUuid,
human.point.action
);
}
} else {
requestAnimationFrame(checkAnimation);
}
};
checkAnimation();
}
function loopMaterialDropToArmBot(
humanId: string,
humanCurrentLoad: number,
armBotId: string,
action: HumanAction
) {
const armBot = getArmBotById(armBotId);
const humanAsset = getAssetById(human.modelUuid);
if (!armBot || armBot.state !== 'idle' || armBot.isActive || humanCurrentLoad <= 0) {
return;
}
decrementHumanLoad(humanId, 1);
humanCurrentLoad -= 1;
const material = removeLastMaterial(humanId);
if (material) {
triggerPointActions(action, material.materialId);
}
if (humanCurrentLoad > 0) {
resetAnimation(human.modelUuid);
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
const waitForNextTransfer = () => {
const currentArmBot = getArmBotById(armBotId);
if (currentArmBot && currentArmBot.state === 'idle' && !currentArmBot.isActive) {
if (humanAsset?.animationState?.isCompleted) {
loopMaterialDropToArmBot(
humanId,
humanCurrentLoad,
armBotId,
action
);
} else {
requestAnimationFrame(waitForNextTransfer);
}
} else {
requestAnimationFrame(waitForNextTransfer);
}
};
waitForNextTransfer();
}
}
function handleMaterialDropToVehicle(model: VehicleEventSchema) {
const humanAsset = getAssetById(human.modelUuid);
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
}
const checkAnimation = () => {
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
const vehicle = getVehicleById(model.modelUuid);
if (vehicle && vehicle.state === 'idle' && !vehicle.isActive) {
loopMaterialDropToVehicle(
human.modelUuid,
human.currentLoad,
model.modelUuid,
human.point.action
);
}
} else {
requestAnimationFrame(checkAnimation);
}
};
checkAnimation();
}
function loopMaterialDropToVehicle(
humanId: string,
humanCurrentLoad: number,
vehicleId: string,
action: HumanAction
) {
const vehicle = getVehicleById(vehicleId);
const humanAsset = getAssetById(human.modelUuid);
if (!vehicle || vehicle.state !== 'idle' || vehicle.isActive || humanCurrentLoad <= 0) {
return;
}
decrementHumanLoad(humanId, 1);
humanCurrentLoad -= 1;
const material = removeLastMaterial(humanId);
if (material) {
triggerPointActions(action, material.materialId);
}
if (humanCurrentLoad > 0) {
resetAnimation(human.modelUuid);
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
const waitForNextTransfer = () => {
const currentVehicle = getVehicleById(vehicleId);
if (currentVehicle && currentVehicle.state === 'idle' && !currentVehicle.isActive) {
if (humanAsset?.animationState?.isCompleted) {
loopMaterialDropToVehicle(
humanId,
humanCurrentLoad,
vehicleId,
action
);
} else {
requestAnimationFrame(waitForNextTransfer);
}
} else {
requestAnimationFrame(waitForNextTransfer);
}
};
waitForNextTransfer();
}
}
function handleMaterialDropToMachine(model: MachineEventSchema) {
const humanAsset = getAssetById(human.modelUuid);
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
}
const checkAnimation = () => {
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
const machine = getMachineById(model.modelUuid);
if (machine && machine.state === 'idle' && !machine.isActive) {
loopMaterialDropToMachine(
human.modelUuid,
human.currentLoad,
model.modelUuid,
human.point.action
);
}
} else {
requestAnimationFrame(checkAnimation);
}
};
checkAnimation();
}
function loopMaterialDropToMachine(
humanId: string,
humanCurrentLoad: number,
machineId: string,
action: HumanAction
) {
const machine = getMachineById(machineId);
const humanAsset = getAssetById(human.modelUuid);
if (!machine || machine.state !== 'idle' || machine.isActive || humanCurrentLoad <= 0) {
return;
}
decrementHumanLoad(humanId, 1);
humanCurrentLoad -= 1;
const material = removeLastMaterial(humanId);
if (material) {
triggerPointActions(action, material.materialId);
}
if (humanCurrentLoad > 0) {
resetAnimation(human.modelUuid);
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
const waitForNextTransfer = () => {
const currentMachine = getMachineById(machineId);
if (currentMachine && currentMachine.state === 'idle' && !currentMachine.isActive) {
if (humanAsset?.animationState?.isCompleted) {
loopMaterialDropToMachine(
humanId,
humanCurrentLoad,
machineId,
action
);
} else {
requestAnimationFrame(waitForNextTransfer);
}
} else {
requestAnimationFrame(waitForNextTransfer);
}
};
waitForNextTransfer();
}
}
function handleMaterialDropByDefault(droppedMaterial: number) {
const humanAsset = getAssetById(human.modelUuid);
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
}
if (humanAsset?.animationState?.isCompleted) {
const remainingMaterials = droppedMaterial - 1;
decrementHumanLoad(human.modelUuid, 1);
const material = removeLastMaterial(human.modelUuid);
if (material) {
setEndTime(material.materialId, performance.now());
removeMaterial(material.materialId);
}
if (remainingMaterials > 0) {
resetAnimation(human.modelUuid);
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
requestAnimationFrame(() => handleMaterialDropByDefault(remainingMaterials));
}
return;
}
requestAnimationFrame(() => handleMaterialDropByDefault(droppedMaterial));
}
return (
<>
<HumanAnimator
path={path}
handleCallBack={handleCallBack}
currentPhase={currentPhase}
human={human}
reset={reset}
startUnloadingProcess={startUnloadingProcess}
/>
<MaterialAnimator human={human} />
</>
)
}
export default HumanInstance

View File

@@ -0,0 +1,263 @@
import { useEffect, useRef, useState } from 'react'
import { useGLTF } from '@react-three/drei';
import { useFrame, useThree } from '@react-three/fiber';
import { useIsDragging, useIsRotating, useSelectedAction, useSelectedEventSphere } from '../../../../../store/simulation/useSimulationStore';
import { useProductContext } from '../../../products/productContext';
import { useSceneContext } from '../../../../scene/sceneContext';
import { Group, Plane, Vector3 } from 'three';
import { useVersionContext } from '../../../../builder/version/versionContext';
import { useParams } from 'react-router-dom';
import startPoint from "../../../../../assets/gltf-glb/ui/arrow_green.glb";
import startEnd from "../../../../../assets/gltf-glb/ui/arrow_red.glb";
import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi';
function HumanUi() {
const { scene: startScene } = useGLTF(startPoint) as any;
const { scene: endScene } = useGLTF(startEnd) as any;
const startMarker = useRef<Group>(null);
const endMarker = useRef<Group>(null);
const outerGroup = useRef<Group>(null);
const prevMousePos = useRef({ x: 0, y: 0 });
const { controls, raycaster } = useThree();
const { selectedEventSphere } = useSelectedEventSphere();
const { selectedProductStore } = useProductContext();
const { humanStore, productStore } = useSceneContext();
const { selectedProduct } = selectedProductStore();
const { humans, getHumanById } = humanStore();
const { updateEvent } = productStore();
const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 1, 0]);
const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 1, 0]);
const [startRotation, setStartRotation] = useState<[number, number, number]>([0, 0, 0]);
const [endRotation, setEndRotation] = useState<[number, number, number]>([0, 0, 0]);
const { isDragging, setIsDragging } = useIsDragging();
const { isRotating, setIsRotating } = useIsRotating();
const plane = useRef(new Plane(new Vector3(0, 1, 0), 0));
const [selectedHumanData, setSelectedHumanData] = useState<{
position: [number, number, number];
rotation: [number, number, number];
}>({ position: [0, 0, 0], rotation: [0, 0, 0] });
const { selectedAction } = useSelectedAction();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const updateBackend = (
productName: string,
productUuid: string,
projectId: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productUuid: productUuid,
projectId: projectId,
eventDatas: eventData,
versionId: selectedVersion?.versionId || '',
});
};
useEffect(() => {
if (!selectedEventSphere) return;
const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid);
if (!selectedHuman || !selectedHuman.point?.action) return;
setSelectedHumanData({
position: selectedHuman.position,
rotation: selectedHuman.rotation,
});
const action = selectedHuman.point.action;
if (action.pickUpPoint?.position && outerGroup.current) {
const worldPos = new Vector3(...action.pickUpPoint.position);
const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
setStartPosition([localPosition.x, 0.5, localPosition.z]);
setStartRotation(action.pickUpPoint.rotation || [0, 0, 0]);
}
if (action.dropPoint?.position && outerGroup.current) {
const worldPos = new Vector3(...action.dropPoint.position);
const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
setEndPosition([localPosition.x, 0.5, localPosition.z]);
setEndRotation(action.dropPoint.rotation || [0, 0, 0]);
}
}, [selectedEventSphere, outerGroup.current, selectedAction, humans]);
const handlePointerDown = (
e: any,
state: "start" | "end",
rotation: "start" | "end"
) => {
if (e.object.name === "handle") {
const normalizedX = (e.clientX / window.innerWidth) * 2 - 1;
const normalizedY = -(e.clientY / window.innerHeight) * 2 + 1;
prevMousePos.current = { x: normalizedX, y: normalizedY };
setIsRotating(rotation);
if (controls) (controls as any).enabled = false;
setIsDragging(null);
} else {
setIsDragging(state);
setIsRotating(null);
if (controls) (controls as any).enabled = false;
}
};
const handlePointerUp = () => {
(controls as any).enabled = true;
setIsDragging(null);
setIsRotating(null);
if (selectedEventSphere?.userData.modelUuid && selectedAction.actionId) {
const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid);
if (selectedHuman && outerGroup.current && startMarker.current && endMarker.current) {
const worldPosStart = new Vector3(...startPosition);
const globalStartPosition = outerGroup.current.localToWorld(worldPosStart.clone());
const worldPosEnd = new Vector3(...endPosition);
const globalEndPosition = outerGroup.current.localToWorld(worldPosEnd.clone());
const updatedAction = {
...selectedHuman.point.action,
pickUpPoint: {
position: [globalStartPosition.x, globalStartPosition.y, globalStartPosition.z] as [number, number, number],
rotation: startRotation
},
dropPoint: {
position: [globalEndPosition.x, globalEndPosition.y, globalEndPosition.z] as [number, number, number],
rotation: endRotation
}
};
const event = updateEvent(
selectedProduct.productUuid,
selectedEventSphere.userData.modelUuid,
{
...selectedHuman,
point: {
...selectedHuman.point,
action: updatedAction
}
}
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
}
}
};
useFrame(() => {
if (!isDragging || !plane.current || !raycaster || !outerGroup.current) return;
const intersectPoint = new Vector3();
const intersects = raycaster.ray.intersectPlane(
plane.current,
intersectPoint
);
if (!intersects) return;
const localPoint = outerGroup?.current.worldToLocal(intersectPoint.clone());
if (isDragging === "start") {
setStartPosition([localPoint.x, 0.5, localPoint.z]);
} else if (isDragging === "end") {
setEndPosition([localPoint.x, 0.5, localPoint.z]);
}
});
useFrame((state) => {
if (!isRotating) return;
const currentPointerX = state.pointer.x;
const deltaX = currentPointerX - prevMousePos.current.x;
prevMousePos.current.x = currentPointerX;
const marker =isRotating === "start" ? startMarker.current : endMarker.current;
if (marker) {
const rotationSpeed = 10;
marker.rotation.y += deltaX * rotationSpeed;
if (isRotating === "start") {
setStartRotation([
marker.rotation.x,
marker.rotation.y,
marker.rotation.z,
]);
} else {
setEndRotation([
marker.rotation.x,
marker.rotation.y,
marker.rotation.z,
]);
}
}
});
useEffect(() => {
const handleGlobalPointerUp = () => {
setIsDragging(null);
setIsRotating(null);
if (controls) (controls as any).enabled = true;
handlePointerUp();
};
if (isDragging || isRotating) {
window.addEventListener("pointerup", handleGlobalPointerUp);
}
return () => {
window.removeEventListener("pointerup", handleGlobalPointerUp);
};
}, [isDragging, isRotating, startPosition, startRotation, endPosition, endRotation]);
return (
<>
{selectedHumanData && (
<group
position={selectedHumanData.position}
ref={outerGroup}
>
<primitive
name="startMarker"
object={startScene}
ref={startMarker}
position={startPosition}
rotation={startRotation}
onPointerDown={(e: any) => {
e.stopPropagation();
handlePointerDown(e, "start", "start");
}}
onPointerMissed={() => {
(controls as any).enabled = true;
setIsDragging(null);
setIsRotating(null);
}}
/>
<primitive
name="endMarker"
object={endScene}
ref={endMarker}
position={endPosition}
rotation={endRotation}
onPointerDown={(e: any) => {
e.stopPropagation();
handlePointerDown(e, "end", "end");
}}
onPointerMissed={() => {
(controls as any).enabled = true;
setIsDragging(null);
setIsRotating(null);
}}
/>
</group>
)}
</>
)
}
export default HumanUi

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, layout, productStore } = useSceneContext(); const { armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, 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,6 +20,7 @@ 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, clearHumans } = humanStore();
const { isReset } = useResetButtonStore(); const { isReset } = useResetButtonStore();
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { mainProduct } = useMainProduct(); const { mainProduct } = useMainProduct();
@@ -153,6 +154,20 @@ function Products() {
} }
}, [selectedProduct, products, isReset, isPlaying]); }, [selectedProduct, products, isReset, isPlaying]);
useEffect(() => {
if (selectedProduct.productUuid) {
const product = getProductById(selectedProduct.productUuid);
if (product) {
clearHumans();
product.eventDatas.forEach(events => {
if (events.type === 'human') {
addHuman(selectedProduct.productUuid, events);
}
});
}
}
}, [selectedProduct, products, isReset, isPlaying]);
return ( return (
<> <>

View File

@@ -1,4 +1,4 @@
import React, { useEffect } from 'react'; import { useEffect } from 'react';
import Vehicles from './vehicle/vehicles'; import Vehicles from './vehicle/vehicles';
import Points from './events/points/points'; import Points from './events/points/points';
import Conveyor from './conveyor/conveyor'; import Conveyor from './conveyor/conveyor';
@@ -6,6 +6,7 @@ import RoboticArm from './roboticArm/roboticArm';
import Materials from './materials/materials'; 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 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';
@@ -52,6 +53,8 @@ function Simulation() {
<StorageUnit /> <StorageUnit />
<Human />
<Simulator /> <Simulator />
<SimulationAnalysis /> <SimulationAnalysis />

View File

@@ -124,7 +124,8 @@ function determineExecutionMachineSequences(products: productsSchema): EventsSch
event.type === 'vehicle' || event.type === 'vehicle' ||
event.type === 'machine' || event.type === 'machine' ||
event.type === 'storageUnit' || event.type === 'storageUnit' ||
event.type === 'roboticArm' event.type === 'roboticArm' ||
event.type === 'human'
) { ) {
pointToEventMap.set(event.point.uuid, event); pointToEventMap.set(event.point.uuid, event);
allPoints.push(event.point); allPoints.push(event.point);

View File

@@ -16,7 +16,8 @@ export async function determineExecutionMachineSequences(products: productsSchem
event.type === 'vehicle' || event.type === 'vehicle' ||
event.type === 'machine' || event.type === 'machine' ||
event.type === 'storageUnit' || event.type === 'storageUnit' ||
event.type === 'roboticArm' event.type === 'roboticArm' ||
event.type === 'human'
) { ) {
pointToEventMap.set(event.point.uuid, event); pointToEventMap.set(event.point.uuid, event);
allPoints.push(event.point); allPoints.push(event.point);

View File

@@ -19,7 +19,9 @@ export function determineExecutionOrder(products: productsSchema): PointsScheme[
} else if (event.type === 'vehicle' || } else if (event.type === 'vehicle' ||
event.type === 'machine' || event.type === 'machine' ||
event.type === 'storageUnit' || event.type === 'storageUnit' ||
event.type === 'roboticArm') { event.type === 'roboticArm' ||
event.type === 'human'
) {
pointMap.set(event.point.uuid, event.point); pointMap.set(event.point.uuid, event.point);
allPoints.push(event.point); allPoints.push(event.point);
} }

View File

@@ -16,7 +16,9 @@ export async function determineExecutionSequences(products: productsSchema): Pro
} else if (event.type === 'vehicle' || } else if (event.type === 'vehicle' ||
event.type === 'machine' || event.type === 'machine' ||
event.type === 'storageUnit' || event.type === 'storageUnit' ||
event.type === 'roboticArm') { event.type === 'roboticArm' ||
event.type === 'human'
) {
pointMap.set(event.point.uuid, event.point); pointMap.set(event.point.uuid, event.point);
allPoints.push(event.point); allPoints.push(event.point);
} }

View File

@@ -91,7 +91,8 @@ function determineExecutionMachineSequences(products: productsSchema): EventsSch
event.type === 'vehicle' || event.type === 'vehicle' ||
event.type === 'machine' || event.type === 'machine' ||
event.type === 'storageUnit' || event.type === 'storageUnit' ||
event.type === 'roboticArm' event.type === 'roboticArm' ||
event.type === 'human'
) { ) {
pointToEventMap.set(event.point.uuid, event); pointToEventMap.set(event.point.uuid, event);
allPoints.push(event.point); allPoints.push(event.point);

View File

@@ -4,7 +4,6 @@ import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayB
import { determineExecutionOrder } from './functions/determineExecutionOrder'; import { determineExecutionOrder } from './functions/determineExecutionOrder';
import { useProductContext } from '../products/productContext'; import { useProductContext } from '../products/productContext';
import { useSceneContext } from '../../scene/sceneContext'; import { useSceneContext } from '../../scene/sceneContext';
import { useCompareProductDataStore } from '../../../store/builder/store';
function Simulator() { function Simulator() {
const { selectedProductStore } = useProductContext(); const { selectedProductStore } = useProductContext();

View File

@@ -43,7 +43,7 @@ const VehicleUI = () => {
const outerGroup = useRef<Group>(null); const outerGroup = useRef<Group>(null);
const state: Types.ThreeState = useThree(); const state: Types.ThreeState = useThree();
const controls: any = state.controls; const controls: any = state.controls;
const [selectedVehicleData, setSelectedVechicleData] = useState<{ position: [number, number, number]; rotation: [number, number, number]; }>({ position: [0, 0, 0], rotation: [0, 0, 0] }); const [selectedVehicleData, setSelectedVehicleData] = useState<{ position: [number, number, number]; rotation: [number, number, number]; }>({ position: [0, 0, 0], rotation: [0, 0, 0] });
const CIRCLE_RADIUS = 0.8; const CIRCLE_RADIUS = 0.8;
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore(); const { selectedVersion } = selectedVersionStore();
@@ -71,7 +71,7 @@ const VehicleUI = () => {
); );
if (selectedVehicle) { if (selectedVehicle) {
setSelectedVechicleData({ setSelectedVehicleData({
position: selectedVehicle.position, position: selectedVehicle.position,
rotation: selectedVehicle.rotation, rotation: selectedVehicle.rotation,
}); });

View File

@@ -6,9 +6,10 @@ import { useVehicleEventManager } from '../../vehicle/eventManager/useVehicleEve
import { useMachineEventManager } from '../../machine/eventManager/useMachineEventManager'; import { useMachineEventManager } from '../../machine/eventManager/useMachineEventManager';
import { useSceneContext } from '../../../scene/sceneContext'; import { useSceneContext } from '../../../scene/sceneContext';
import { useProductContext } from '../../products/productContext'; import { useProductContext } from '../../products/productContext';
import { useHumanEventManager } from '../../human/eventManager/useHumanEventManager';
export function useTriggerHandler() { export function useTriggerHandler() {
const { materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, productStore } = useSceneContext(); const { materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext();
const { selectedProductStore } = useProductContext(); const { selectedProductStore } = useProductContext();
const { handleAction } = useActionHandler(); const { handleAction } = useActionHandler();
const { selectedProduct } = selectedProductStore(); const { selectedProduct } = selectedProductStore();
@@ -19,7 +20,9 @@ export function useTriggerHandler() {
const { addConveyorToMonitor } = useConveyorEventManager(); const { addConveyorToMonitor } = useConveyorEventManager();
const { addVehicleToMonitor } = useVehicleEventManager(); const { addVehicleToMonitor } = useVehicleEventManager();
const { addMachineToMonitor } = useMachineEventManager(); const { addMachineToMonitor } = useMachineEventManager();
const { addHumanToMonitor } = useHumanEventManager();
const { getVehicleById } = vehicleStore(); const { getVehicleById } = vehicleStore();
const { getHumanById } = humanStore();
const { getMachineById } = machineStore(); const { getMachineById } = machineStore();
const { getStorageUnitById } = storageUnitStore(); const { getStorageUnitById } = storageUnitStore();
const { getMaterialById, setCurrentLocation, setNextLocation, setPreviousLocation, setIsPaused, setIsVisible, setEndTime } = materialStore(); const { getMaterialById, setCurrentLocation, setNextLocation, setPreviousLocation, setIsPaused, setIsVisible, setEndTime } = materialStore();
@@ -149,23 +152,51 @@ export function useTriggerHandler() {
const model = getEventByModelUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid); const model = getEventByModelUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
if (model?.type === 'vehicle') { if (model?.type === 'vehicle') {
const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid); const armBot = getArmBotById(trigger.triggeredAsset?.triggeredModel.modelUuid);
if (vehicle) { if (armBot) {
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) { if (armBot.isActive === false && armBot.state === 'idle') {
// Handle current action from vehicle const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
setIsPaused(materialId, true); if (vehicle) {
handleAction(action, materialId); if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
// Handle current action from vehicle
} else { setIsPaused(materialId, true);
// Handle current action using Event Manager
setIsPaused(materialId, true);
addVehicleToMonitor(vehicle.modelUuid,
() => {
handleAction(action, materialId); handleAction(action, materialId);
} else {
// Handle current action using Event Manager
setIsPaused(materialId, true);
addVehicleToMonitor(vehicle.modelUuid,
() => {
handleAction(action, materialId);
}
)
} }
) }
} else {
setIsPaused(materialId, true);
addArmBotToMonitor(armBot.modelUuid, () => {
const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (vehicle) {
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
// Handle current action from vehicle
setIsPaused(materialId, true);
handleAction(action, materialId);
} else {
// Handle current action using Event Manager
setIsPaused(materialId, true);
addVehicleToMonitor(vehicle.modelUuid,
() => {
handleAction(action, materialId);
}
)
}
}
})
} }
} }
} else if (model?.type === 'machine') { } else if (model?.type === 'machine') {
@@ -191,26 +222,25 @@ export function useTriggerHandler() {
} }
} else { } else {
setIsPaused(materialId, true); setIsPaused(materialId, true);
addArmBotToMonitor(armBot.modelUuid, addArmBotToMonitor(armBot.modelUuid, () => {
() => { const machine = getMachineById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
const machine = getMachineById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); if (machine) {
if (machine) { if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) {
if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) { setIsPaused(materialId, true);
setIsPaused(materialId, true); handleAction(action, materialId);
handleAction(action, materialId); } else {
} else {
// Handle current action using Event Manager // Handle current action using Event Manager
setIsPaused(materialId, true); setIsPaused(materialId, true);
addMachineToMonitor(machine.modelUuid, addMachineToMonitor(machine.modelUuid,
() => { () => {
handleAction(action, materialId); handleAction(action, materialId);
} }
) )
}
} }
} }
}
); );
} }
} }
@@ -256,6 +286,242 @@ export function useTriggerHandler() {
} else if (toEvent?.type === 'storageUnit') { } else if (toEvent?.type === 'storageUnit') {
// Transfer to Storage Unit // Transfer to Storage Unit
} else if (toEvent?.type === 'human') {
// Transfer to Human
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialById(materialId);
if (material) {
// Handle current action of the material
handleAction(action, materialId);
if (material.next) {
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: material.next.modelUuid,
pointUuid: material.next.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
});
setNextLocation(material.materialId, null);
if (action) {
if (human) {
if (action && action.triggers.length > 0 &&
action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid &&
action.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid &&
action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid) {
const model = getEventByModelUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
if (model?.type === 'vehicle') {
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
if (human) {
if (human.isActive === false && human.state === 'idle') {
const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
if (vehicle) {
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
// Handle current action from vehicle
setIsVisible(materialId, false);
setIsPaused(materialId, true);
handleAction(action, materialId);
} else {
// Handle current action using Event Manager
setIsPaused(materialId, true);
addVehicleToMonitor(vehicle.modelUuid,
() => {
setIsVisible(materialId, false);
handleAction(action, materialId);
}
)
}
}
} else {
setIsPaused(materialId, true);
addHumanToMonitor(human.modelUuid, () => {
const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (vehicle) {
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
// Handle current action from vehicle
setIsVisible(materialId, false);
setIsPaused(materialId, true);
handleAction(action, materialId);
} else {
// Handle current action using Event Manager
setIsPaused(materialId, true);
addVehicleToMonitor(vehicle.modelUuid,
() => {
setIsVisible(materialId, false);
handleAction(action, materialId);
}
)
}
}
})
}
}
} else if (model?.type === 'transfer') {
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
if (human) {
if (human.isActive === false && human.state === 'idle') {
const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
if (conveyor) {
if (!conveyor.isPaused) {
// Handle current action from vehicle
setIsVisible(materialId, false);
setIsPaused(materialId, true);
handleAction(action, materialId);
} else {
// Handle current action using Event Manager
setIsPaused(materialId, true);
addConveyorToMonitor(conveyor.modelUuid,
() => {
setIsVisible(materialId, false);
handleAction(action, materialId);
}
)
}
}
} else {
setIsPaused(materialId, true);
addHumanToMonitor(human.modelUuid, () => {
const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (conveyor) {
if (!conveyor.isPaused) {
// Handle current action from vehicle
setIsVisible(materialId, false);
setIsPaused(materialId, true);
handleAction(action, materialId);
} else {
// Handle current action using Event Manager
setIsPaused(materialId, true);
addConveyorToMonitor(conveyor.modelUuid,
() => {
setIsVisible(materialId, false);
handleAction(action, materialId);
}
)
}
}
})
}
}
} else if (model?.type === 'machine') {
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
if (human) {
if (human.isActive === false && human.state === 'idle') {
const machine = getMachineById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
if (machine) {
if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) {
setIsPaused(materialId, true);
setIsVisible(materialId, false);
handleAction(action, materialId);
} else {
// Handle current action using Event Manager
setIsPaused(materialId, true);
addMachineToMonitor(machine.modelUuid,
() => {
setIsVisible(materialId, false);
handleAction(action, materialId);
}
)
}
}
} else {
setIsPaused(materialId, true);
addHumanToMonitor(human.modelUuid, () => {
const machine = getMachineById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (machine) {
if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) {
setIsPaused(materialId, true);
setIsVisible(materialId, false);
handleAction(action, materialId);
} else {
// Handle current action using Event Manager
setIsPaused(materialId, true);
addMachineToMonitor(machine.modelUuid,
() => {
setIsVisible(materialId, false);
handleAction(action, materialId);
}
)
}
}
}
);
}
}
} else {
if (human.isActive === false && human.state === 'idle') {
// Handle current action from arm bot
setIsPaused(materialId, true);
setIsVisible(materialId, false);
handleAction(action, materialId);
} else {
// Handle current action using Event Manager
setIsPaused(materialId, true);
addHumanToMonitor(human.modelUuid,
() => {
setIsVisible(materialId, false);
handleAction(action, materialId)
}
);
}
}
} else {
if (human.isActive === false && human.state === 'idle') {
// Handle current action from arm bot
setIsPaused(materialId, true);
setIsVisible(materialId, false);
handleAction(action, materialId);
} else {
// Handle current action using Event Manager
setIsPaused(materialId, true);
addHumanToMonitor(human.modelUuid,
() => {
setIsVisible(materialId, false);
handleAction(action, materialId)
}
);
}
}
}
}
}
}
}
} }
} else if (fromEvent?.type === 'vehicle') { } else if (fromEvent?.type === 'vehicle') {
if (toEvent?.type === 'transfer') { if (toEvent?.type === 'transfer') {
@@ -322,18 +588,25 @@ export function useTriggerHandler() {
setNextLocation(material.materialId, null); setNextLocation(material.materialId, null);
setIsVisible(materialId, false);
if (action && armBot) { if (action && armBot) {
if (armBot.isActive === false && armBot.state === 'idle') { if (armBot.isActive === false && armBot.state === 'idle') {
// Handle current action from arm bot // Handle current action from arm bot
setIsVisible(materialId, false);
handleAction(action, materialId); handleAction(action, materialId);
} else { } else {
// Event Manager Needed addArmBotToMonitor(armBot.modelUuid,
() => {
setIsVisible(materialId, false);
handleAction(action, materialId);
}
)
} }
} }
@@ -379,6 +652,52 @@ export function useTriggerHandler() {
} }
} }
} }
} else if (toEvent?.type === 'human') {
// Vehicle to Human
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialById(materialId);
if (material) {
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid,
pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
});
setNextLocation(material.materialId, null);
if (action && human) {
if (human.isActive === false && human.state === 'idle') {
// Handle current action from arm bot
setIsVisible(materialId, false);
handleAction(action, materialId);
} else {
addHumanToMonitor(human.modelUuid,
() => {
setIsVisible(materialId, false);
handleAction(action, materialId);
}
)
}
}
}
}
} }
} else if (fromEvent?.type === 'machine') { } else if (fromEvent?.type === 'machine') {
if (toEvent?.type === 'transfer') { if (toEvent?.type === 'transfer') {
@@ -485,6 +804,135 @@ export function useTriggerHandler() {
} else if (toEvent?.type === 'storageUnit') { } else if (toEvent?.type === 'storageUnit') {
// Machine to Storage Unit // Machine to Storage Unit
} else if (toEvent?.type === 'human') {
// Machine to Human
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialById(materialId);
setIsPaused(materialId, true);
if (material) {
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
});
setNextLocation(material.materialId, null);
setIsVisible(materialId, false);
if (action && human) {
if (human.isActive === false && human.state === 'idle') {
// Handle current action from arm bot
const model = getEventByModelUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (model?.type === 'transfer') {
const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (conveyor) {
const previousModel = getEventByModelUuid(selectedProduct.productUuid, material.previous?.modelUuid || '');
if (previousModel) {
if (previousModel.type === 'transfer' && previousModel.modelUuid === model.modelUuid) {
handleAction(action, materialId)
} else {
addConveyorToMonitor(conveyor.modelUuid,
() => {
handleAction(action, materialId)
}
)
}
} else {
handleAction(action, materialId)
}
// handleAction(action, materialId)
}
} else if (model?.type === 'vehicle') {
const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (vehicle) {
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
// Handle current action from vehicle
setIsPaused(materialId, true);
handleAction(action, materialId);
} else {
// Handle current action using Event Manager
setIsPaused(materialId, true);
addVehicleToMonitor(vehicle.modelUuid,
() => {
handleAction(action, materialId);
}
)
}
}
} else {
handleAction(action, materialId)
}
} else {
// Handle current action using Event Manager
addHumanToMonitor(human.modelUuid, () => {
const model = getEventByModelUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (model?.type === 'transfer') {
const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (conveyor) {
const previousModel = getEventByModelUuid(selectedProduct.productUuid, material.previous?.modelUuid || '');
if (previousModel) {
if (previousModel.type === 'transfer' && previousModel.modelUuid === model.modelUuid) {
handleAction(action, materialId)
} else {
addConveyorToMonitor(conveyor.modelUuid,
() => {
handleAction(action, materialId)
}
)
}
} else {
handleAction(action, materialId)
}
}
} else if (model?.type === 'vehicle') {
const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
if (vehicle) {
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
// Handle current action from vehicle
setIsPaused(materialId, true);
handleAction(action, materialId);
} else {
// Handle current action using Event Manager
setIsPaused(materialId, true);
addVehicleToMonitor(vehicle.modelUuid,
() => {
handleAction(action, materialId);
}
)
}
}
} else {
handleAction(action, materialId)
}
}
);
}
}
}
}
} }
} else if (fromEvent?.type === 'roboticArm') { } else if (fromEvent?.type === 'roboticArm') {
if (toEvent?.type === 'transfer') { if (toEvent?.type === 'transfer') {
@@ -758,6 +1206,53 @@ export function useTriggerHandler() {
} }
} }
} }
} else if (toEvent?.type === 'human') {
// Robotic Arm to Human
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialById(materialId);
if (material) {
setIsPaused(material.materialId, false);
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
setNextLocation(material.materialId, null);
if (action) {
if (human) {
if (human.isActive === false && human.state === 'idle' && human.isPicking && human.currentLoad < human.point.action.loadCapacity) {
setIsVisible(materialId, false);
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid,
pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
});
// Handle current action from human
handleAction(action, materialId);
} else {
// Event Manager Needed
}
}
}
}
}
} }
} else if (fromEvent?.type === 'storageUnit') { } else if (fromEvent?.type === 'storageUnit') {
if (toEvent?.type === 'transfer') { if (toEvent?.type === 'transfer') {
@@ -775,6 +1270,326 @@ export function useTriggerHandler() {
} else if (toEvent?.type === 'storageUnit') { } else if (toEvent?.type === 'storageUnit') {
// Storage Unit to Storage Unit // Storage Unit to Storage Unit
} else if (toEvent?.type === 'human') {
// Storage Unit to Human
}
} else if (fromEvent?.type === 'human') {
if (toEvent?.type === 'transfer') {
// Human to Transfer
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialById(materialId);
if (material) {
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid,
pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
});
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
if (action && action.triggers.length > 0 &&
action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid &&
action.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid &&
action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid) {
const model = getEventByModelUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
if (model?.type === 'roboticArm') {
handleAction(action, material.materialId);
} else if (model?.type === 'vehicle') {
const nextAction = getActionByUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset?.triggeredAction.actionUuid);
if (action) {
handleAction(action, material.materialId);
const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: trigger.triggeredAsset?.triggeredModel.modelUuid,
pointUuid: trigger.triggeredAsset?.triggeredPoint?.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
});
setNextLocation(material.materialId, null);
if (nextAction) {
if (vehicle) {
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '',
pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid || '',
actionUuid: action.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid || '',
});
setNextLocation(material.materialId, null);
setIsVisible(materialId, false);
setIsPaused(materialId, false);
// Handle current action from vehicle
handleAction(nextAction, materialId);
} else {
// Handle current action using Event Manager
setIsPaused(materialId, true);
addVehicleToMonitor(vehicle.modelUuid,
() => {
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '',
pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid || '',
actionUuid: action.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid || '',
});
setNextLocation(material.materialId, null);
setIsPaused(materialId, false);
setIsVisible(materialId, false);
handleAction(nextAction, materialId);
}
)
}
}
}
}
} else if (model?.type === 'transfer') {
const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
if (conveyor) {
setNextLocation(material.materialId, {
modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid,
pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid,
})
setIsPaused(material.materialId, false);
setIsVisible(material.materialId, true);
handleAction(action, material.materialId);
}
} else {
setNextLocation(material.materialId, {
modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid,
pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid,
})
handleAction(action, material.materialId);
}
} else if (action) {
setNextLocation(material.materialId, null)
handleAction(action, material.materialId);
}
}
}
} else if (toEvent?.type === 'vehicle') {
// Human to Vehicle
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialById(materialId);
if (material) {
setIsPaused(material.materialId, false);
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
const vehicle = getVehicleById(trigger.triggeredAsset?.triggeredModel.modelUuid);
setNextLocation(material.materialId, null);
if (action) {
if (vehicle) {
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
setIsVisible(materialId, false);
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid,
pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
});
// Handle current action from vehicle
handleAction(action, materialId);
} else {
// Event Manager Needed
}
}
}
}
}
} else if (toEvent?.type === 'machine') {
// Human to Machine
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialById(materialId);
if (material) {
// setIsPaused(material.materialId, false);
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
const machine = getMachineById(trigger.triggeredAsset?.triggeredModel.modelUuid);
setNextLocation(material.materialId, null);
if (action) {
if (machine) {
if (machine.isActive === false && machine.state === 'idle') {
setIsVisible(materialId, false);
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
});
// Handle current action from machine
handleAction(action, materialId);
} else {
// Event Manager Needed
}
}
}
}
}
} else if (toEvent?.type === 'roboticArm') {
// Human to Robotic Arm
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialById(materialId);
if (material) {
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
const armBot = getArmBotById(trigger.triggeredAsset?.triggeredModel.modelUuid);
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid,
pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
});
setNextLocation(material.materialId, null);
setIsVisible(materialId, false);
if (action && armBot) {
if (armBot.isActive === false && armBot.state === 'idle') {
// Handle current action from arm bot
handleAction(action, materialId);
} else {
// Event Manager Needed
}
}
}
}
} else if (toEvent?.type === 'storageUnit') {
// Human to Storage Unit
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialById(materialId);
if (material) {
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
const storageUnit = getStorageUnitById(trigger.triggeredAsset?.triggeredModel.modelUuid);
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid,
pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
});
setNextLocation(material.materialId, null);
setIsVisible(materialId, false);
if (action && storageUnit) {
if (storageUnit.currentLoad < storageUnit.point.action.storageCapacity) {
// Handle current action from vehicle
handleAction(action, materialId);
} else {
// Event Manager Needed
}
}
}
}
} else if (toEvent?.type === 'human') {
// Human to Human
} }
} }
} }

View File

@@ -3,12 +3,7 @@ import { useThree, useFrame } from '@react-three/fiber';
import * as THREE from 'three'; import * as THREE from 'three';
import { MaterialModel } from '../../../materials/instances/material/materialModel'; import { MaterialModel } from '../../../materials/instances/material/materialModel';
type MaterialAnimatorProps = { const MaterialAnimator = ({ agvDetail }: { agvDetail: VehicleStatus }) => {
agvDetail: VehicleStatus;
};
const MaterialAnimator = ({ agvDetail }: MaterialAnimatorProps) => {
const meshRef = useRef<any>(null!); const meshRef = useRef<any>(null!);
const [hasLoad, setHasLoad] = useState(false); const [hasLoad, setHasLoad] = useState(false);
const { scene } = useThree(); const { scene } = useThree();

View File

@@ -71,7 +71,6 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
} }
}, [isReset, isPlaying]) }, [isReset, isPlaying])
const lastTimeRef = useRef(performance.now()); const lastTimeRef = useRef(performance.now());
useFrame(() => { useFrame(() => {
@@ -283,88 +282,6 @@ function DraggableSphere({
); );
} }
// function DraggableLineSegment({
// index,
// start,
// end,
// updatePoints,
// isAnyDragging,
// setIsAnyDragging,
// }: {
// index: number;
// start: THREE.Vector3;
// end: THREE.Vector3;
// updatePoints: (i0: number, p0: THREE.Vector3, i1: number, p1: THREE.Vector3) => void;
// isAnyDragging: string;
// setIsAnyDragging: (val: string) => void;
// }) {
// const meshRef = useRef<THREE.Mesh>(null);
// const { gl, raycaster, controls } = useThree();
// const { activeTool } = useActiveTool();
// const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
// const dragStart = useRef<THREE.Vector3 | null>(null);
// const onPointerDown = () => {
// if (activeTool !== 'pen' || isAnyDragging) return; // <-- Skip if dragging sphere
// setIsAnyDragging("line");
// gl.domElement.style.cursor = 'grabbing';
// if (controls) (controls as any).enabled = false;
// };
// const onPointerMove = (e: ThreeEvent<PointerEvent>) => {
// if (isAnyDragging !== "line" || activeTool !== 'pen') return;
// const intersect = new THREE.Vector3();
// if (raycaster.ray.intersectPlane(plane, intersect)) {
// if (!dragStart.current) dragStart.current = intersect.clone();
// const offset = new THREE.Vector3().subVectors(intersect, dragStart.current);
// const newStart = start.clone().add(offset);
// const newEnd = end.clone().add(offset);
// updatePoints(index, newStart, index + 1, newEnd);
// }
// };
// const onPointerUp = () => {
// if (activeTool !== 'pen') return;
// setIsAnyDragging("");
// dragStart.current = null;
// gl.domElement.style.cursor = 'default';
// if (controls) (controls as any).enabled = true;
// };
// const noopRaycast = (raycaster: THREE.Raycaster, intersects: THREE.Intersection[]) => { };
// return (
// <mesh
// ref={meshRef}
// onPointerDown={onPointerDown}
// onPointerMove={onPointerMove}
// onPointerUp={onPointerUp}
// onPointerMissed={onPointerUp}
// raycast={isAnyDragging === "point" ? noopRaycast : undefined} // ✅ Safe
// >
// <Line points={[start, end]} color="blue" lineWidth={5} />
// </mesh>
// );
// // return (
// // <mesh
// // ref={meshRef}
// // onPointerDown={onPointerDown}
// // onPointerMove={onPointerMove}
// // onPointerUp={onPointerUp}
// // onPointerMissed={onPointerUp}
// // // raycast={isAnyDragging === 'point' ? () => null : undefined}
// // raycast={isAnyDragging === "point" ? () => { } : undefined}
// // // pointerEvents={isAnyDragging === "point" ? "none" : "auto"} // ✅ the correct way
// // >
// // <Line points={[start, end]} color="blue" lineWidth={5} />
// // </mesh>
// // );
// }
function DraggableLineSegment({ function DraggableLineSegment({
index, index,
start, start,
@@ -426,207 +343,3 @@ function DraggableLineSegment({
/> />
); );
} }
// These are recently edited files. Do not suggest code that has been deleted.
// function DraggableSphere({
// index,
// position,
// onMove,
// isAnyDragging,
// setIsAnyDragging,
// }: {
// index: number;
// position: THREE.Vector3;
// onMove: (index: number, pos: THREE.Vector3) => void;
// isAnyDragging: boolean;
// setIsAnyDragging: (val: boolean) => void;
// }) {
// const meshRef = useRef<THREE.Mesh>(null);
// const { gl, controls, raycaster } = useThree();
// const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); // XZ plane
// const isDragging = useRef(false);
// const { activeTool } = useActiveTool();
// const onPointerDown = (e: ThreeEvent<PointerEvent>) => {
// if (activeTool !== 'pen') return;
// isDragging.current = true;
// gl.domElement.style.cursor = 'grabbing';
// if (controls) {
// (controls as any).enabled = false;
// }
// };
// const onPointerMove = (e: ThreeEvent<PointerEvent>) => {
// if (!isDragging.current || activeTool !== 'pen') return;
// const intersect = new THREE.Vector3();
// if (raycaster.ray.intersectPlane(plane, intersect)) {
// meshRef.current!.position.copy(intersect);
// onMove(index, intersect);
// }
// };
// const onPointerUp = () => {
// if (activeTool !== 'pen') return;
// isDragging.current = false;
// gl.domElement.style.cursor = 'default';
// if (controls) {
// (controls as any).enabled = true;
// }
// };
// return (
// <mesh
// ref={meshRef}
// position={position}
// onPointerDown={onPointerDown}
// onPointerMove={onPointerMove}
// onPointerUp={onPointerUp}
// onPointerMissed={onPointerUp}
// >
// <sphereGeometry args={[0.2, 16, 16]} />
// <meshStandardMaterial color="red" />
// </mesh>
// );
// }
// function DraggableLineSegment({
// index,
// start,
// end,
// updatePoints,
// isAnyDragging,
// setIsAnyDragging,
// }: {
// index: number;
// start: THREE.Vector3;
// end: THREE.Vector3;
// updatePoints: (i0: number, p0: THREE.Vector3, i1: number, p1: THREE.Vector3) => void;
// isAnyDragging: boolean;
// setIsAnyDragging: (val: boolean) => void;
// }) {
// const meshRef = useRef<THREE.Mesh>(null);
// const { gl, camera, controls, raycaster } = useThree();
// const { activeTool } = useActiveTool(); // 👈 Get active tool
// const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
// const isDragging = useRef(false);
// const dragStart = useRef<THREE.Vector3 | null>(null);
// const onPointerDown = () => {
// if (activeTool !== 'pen') return; // 👈 Only allow when tool is 'pen'
// isDragging.current = true;
// gl.domElement.style.cursor = 'grabbing';
// if (controls) (controls as any).enabled = false;
// };
// const onPointerMove = (e: ThreeEvent<PointerEvent>) => {
// if (!isDragging.current || activeTool !== 'pen') return;
// const intersect = new THREE.Vector3();
// if (raycaster.ray.intersectPlane(plane, intersect)) {
// if (!dragStart.current) {
// dragStart.current = intersect.clone();
// }
// const offset = new THREE.Vector3().subVectors(intersect, dragStart.current);
// const newStart = start.clone().add(offset);
// const newEnd = end.clone().add(offset);
// updatePoints(index, newStart, index + 1, newEnd);
// }
// };
// const onPointerUp = () => {
// if (activeTool !== 'pen') return;
// isDragging.current = false;
// dragStart.current = null;
// gl.domElement.style.cursor = 'default';
// if (controls) (controls as any).enabled = true;
// };
// return (
// <mesh
// ref={meshRef}
// onPointerDown={onPointerDown}
// onPointerMove={onPointerMove}
// onPointerUp={onPointerUp}
// onPointerMissed={onPointerUp}
// >
// <Line points={[start, end]} color="blue" lineWidth={10} />
// </mesh>
// );
// }
// return (
// <>
// {selectedPath === "auto" && <group>
// {/* {currentPath.map((pos, i) => {
// if (i < currentPath.length - 1) {
// return (
// <DraggableLineSegment
// key={i}
// index={i}
// start={new THREE.Vector3(...currentPath[i])}
// end={new THREE.Vector3(...currentPath[i + 1])}
// updatePoints={(i0, p0, i1, p1) => {
// const updated = [...currentPath];
// updated[i0] = p0.toArray() as [number, number, number];
// updated[i1] = p1.toArray() as [number, number, number];
// setCurrentPath(updated);
// }}
// />
// );
// }
// return null;
// })} */}
// {currentPath.length > 0 && (
// <group onPointerMissed={() => { if (controls) (controls as any).enabled = true; }}>
// <Line points={currentPath} color="blue" lineWidth={3} />
// {currentPath.map((pos, i) => {
// if (i < currentPath.length - 1) {
// return (
// <React.Fragment key={i}>
// <DraggableSphere
// key={i}
// index={i}
// position={new THREE.Vector3(...pos)}
// onMove={updatePoint}
// />
// {/* <DraggableLineSegment
// key={i}
// index={i}
// start={new THREE.Vector3(...currentPath[i])}
// end={new THREE.Vector3(...currentPath[i + 1])}
// updatePoints={(i0, p0, i1, p1) => {
// const updated = [...currentPath];
// updated[i0] = p0.toArray() as [number, number, number];
// updated[i1] = p1.toArray() as [number, number, number];
// setCurrentPath(updated);
// }}
// /> */}
// </React.Fragment>
// )
// }
// }
// )}
// </group >
// )
// }
// </group >
// }
// </>
// );

View File

@@ -1,21 +1,23 @@
import { useCallback, useEffect, useRef, useState } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react';
import VehicleAnimator from '../animator/vehicleAnimator';
import * as THREE from 'three'; import * as THREE from 'three';
import { NavMeshQuery } from '@recast-navigation/core'; import { NavMeshQuery } from '@recast-navigation/core';
import { useNavMesh, useSelectedPath } from '../../../../../store/builder/store'; import { useNavMesh, useSelectedPath } from '../../../../../store/builder/store';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler'; import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
import MaterialAnimator from '../animator/materialAnimator';
import { useSceneContext } from '../../../../scene/sceneContext'; import { useSceneContext } from '../../../../scene/sceneContext';
import { useProductContext } from '../../../products/productContext'; import { useProductContext } from '../../../products/productContext';
import InteractivePoints from '../animator/interactivePoint'; import InteractivePoints from '../animator/interactivePoint';
import MaterialAnimator from '../animator/materialAnimator';
import VehicleAnimator from '../animator/vehicleAnimator';
function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) { function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) {
const { navMesh } = useNavMesh(); const { navMesh } = useNavMesh();
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { materialStore, armBotStore, conveyorStore, vehicleStore, storageUnitStore, productStore } = useSceneContext(); const { materialStore, armBotStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, productStore } = useSceneContext();
const { removeMaterial, setEndTime } = materialStore(); const { removeMaterial, setEndTime } = materialStore();
const { getStorageUnitById } = storageUnitStore(); const { getStorageUnitById } = storageUnitStore();
const { getHumanById } = humanStore();
const { getArmBotById } = armBotStore(); const { getArmBotById } = armBotStore();
const { getConveyorById } = conveyorStore(); const { getConveyorById } = conveyorStore();
const { triggerPointActions } = useTriggerHandler(); const { triggerPointActions } = useTriggerHandler();
@@ -149,7 +151,6 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
} }
}, [vehicles, currentPhase, path, isPlaying, selectedPath]); }, [vehicles, currentPhase, path, isPlaying, selectedPath]);
function animate(currentTime: number) { function animate(currentTime: number) {
if (previousTimeRef.current === null) { if (previousTimeRef.current === null) {
previousTimeRef.current = currentTime; previousTimeRef.current = currentTime;
@@ -196,7 +197,6 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
}; };
}, [agvDetail, isPlaying]); }, [agvDetail, isPlaying]);
function handleCallBack() { function handleCallBack() {
if (currentPhase === 'stationed-pickup') { if (currentPhase === 'stationed-pickup') {
setCurrentPhase('picking'); setCurrentPhase('picking');
@@ -246,6 +246,11 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
if (action) { if (action) {
handleMaterialDropToStorageUnit(model); handleMaterialDropToStorageUnit(model);
} }
} else if (model.type === 'human') {
const action = getActionByUuid(selectedProduct.productUuid, agvDetail.point.action.actionUuid);
if (action) {
handleMaterialDropToHuman(model);
}
} }
} else { } else {
const droppedMaterial = agvDetail.currentLoad; const droppedMaterial = agvDetail.currentLoad;
@@ -259,6 +264,79 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
} }
} }
function handleMaterialDropToHuman(model: HumanEventSchema) {
if (model) {
if (model.point.action.actionType === 'worker') {
loopMaterialDropToHuman(
agvDetail.modelUuid,
agvDetail.currentLoad,
agvDetail.point.action.unLoadDuration,
model.modelUuid,
model.point.action.loadCapacity,
agvDetail.point.action
);
}
}
}
function loopMaterialDropToHuman(
vehicleId: string,
vehicleCurrentLoad: number,
unLoadDuration: number,
humanId: string,
storageMaxCapacity: number,
action: VehicleAction
) {
startTime = performance.now();
const fixedInterval = ((unLoadDuration / vehicleCurrentLoad) * (1000 / isSpeedRef.current));
const unloadLoop = () => {
if (isPausedRef.current) {
pauseTimeRef.current ??= performance.now();
requestAnimationFrame(unloadLoop);
return;
}
if (pauseTimeRef.current) {
const pauseDuration = performance.now() - pauseTimeRef.current;
startTime += pauseDuration;
pauseTimeRef.current = null;
}
const elapsedTime = performance.now() - startTime;
const human = getHumanById(humanId);
if (elapsedTime >= fixedInterval) {
if (human && agvDetail &&
human.currentLoad < storageMaxCapacity &&
vehicleCurrentLoad > 0) {
decrementVehicleLoad(vehicleId, 1);
vehicleCurrentLoad -= 1;
const material = removeLastMaterial(vehicleId);
if (material) {
triggerPointActions(action, material.materialId);
}
if (vehicleCurrentLoad > 0 && human.currentLoad < storageMaxCapacity) {
startTime = performance.now();
requestAnimationFrame(unloadLoop);
}
}
} else {
requestAnimationFrame(unloadLoop);
}
};
const human = getHumanById(humanId);
if (human && vehicleCurrentLoad > 0 && human?.currentLoad < storageMaxCapacity) {
unloadLoop();
}
}
function handleMaterialDropToStorageUnit(model: StorageEventSchema) { function handleMaterialDropToStorageUnit(model: StorageEventSchema) {
if (model) { if (model) {
if (model.point.action.actionType === 'store') { if (model.point.action.actionType === 'store') {
@@ -527,10 +605,4 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
); );
} }
export default VehicleInstance; export default VehicleInstance;

View File

@@ -5,20 +5,20 @@ import { useSceneContext } from "../../../scene/sceneContext";
import { useViewSceneStore } from "../../../../store/builder/store"; import { useViewSceneStore } from "../../../../store/builder/store";
function VehicleInstances() { function VehicleInstances() {
const { vehicleStore } = useSceneContext(); const { vehicleStore } = useSceneContext();
const { vehicles } = vehicleStore(); const { vehicles } = vehicleStore();
const { viewSceneLabels } = useViewSceneStore(); const { viewSceneLabels } = useViewSceneStore();
return ( return (
<> <>
{vehicles.map((vehicle: VehicleStatus) => ( {vehicles.map((vehicle: VehicleStatus) => (
<React.Fragment key={vehicle.modelUuid}> <React.Fragment key={vehicle.modelUuid}>
<VehicleInstance agvDetail={vehicle} /> <VehicleInstance agvDetail={vehicle} />
{viewSceneLabels && <VehicleContentUi vehicle={vehicle} />} {viewSceneLabels && <VehicleContentUi vehicle={vehicle} />}
</React.Fragment> </React.Fragment>
))} ))}
</> </>
); );
} }
export default VehicleInstances; export default VehicleInstances;

View File

@@ -17,6 +17,7 @@ import { useWidgetStore } from "../../store/useWidgetStore";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { getUserData } from "../../functions/getUserData"; import { getUserData } from "../../functions/getUserData";
import { useVersionContext } from "../builder/version/versionContext"; import { useVersionContext } from "../builder/version/versionContext";
import { useSceneContext } from "../scene/sceneContext";
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
@@ -28,6 +29,7 @@ type FormattedZoneData = Record<
points: []; points: [];
lockedPanels: Side[]; lockedPanels: Side[];
zoneUuid: string; zoneUuid: string;
zoneName: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
zoneViewPortPosition: number[]; zoneViewPortPosition: number[];
widgets: Widget[]; widgets: Widget[];
@@ -64,6 +66,9 @@ const RealTimeVisulization: React.FC = () => {
const { selectedVersion } = selectedVersionStore(); const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams(); const { projectId } = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
const { zoneStore } = useSceneContext();
const { zones } = zoneStore();
OuterClick({ OuterClick({
contextClassName: [ contextClassName: [
@@ -82,6 +87,7 @@ const RealTimeVisulization: React.FC = () => {
useEffect(() => { useEffect(() => {
if (!projectId || !selectedVersion) return; if (!projectId || !selectedVersion) return;
getZone2dData(organization, projectId, selectedVersion?.versionId || '').then((response) => { getZone2dData(organization, projectId, selectedVersion?.versionId || '').then((response) => {
// console.log('response: ', response);
if (!response) return; if (!response) return;
// if (response.status === 401) { // if (response.status === 401) {
// console.log("force logout"); // console.log("force logout");
@@ -94,19 +100,21 @@ const RealTimeVisulization: React.FC = () => {
const formattedData = response.reduce<FormattedZoneData>( const formattedData = response.reduce<FormattedZoneData>(
(acc, zone) => { (acc, zone) => {
acc[zone.zoneName] = { acc[zone.zoneUuid] = {
activeSides: [], activeSides: [],
panelOrder: [], panelOrder: [],
lockedPanels: [], lockedPanels: [],
points: zone.points, points: zone.points,
zoneUuid: zone.zoneUuid, zoneUuid: zone.zoneUuid,
zoneViewPortTarget: zone.viewPortCenter, zoneName: zone.zoneName,
zoneViewPortPosition: zone.viewPortposition, zoneViewPortTarget: zone.viewPortTarget,
zoneViewPortPosition: zone.viewPortPosition,
widgets: [], widgets: [],
}; };
return acc; return acc;
}, {} }, {}
); );
// console.log('formattedData: ', formattedData);
setZonesData(formattedData); setZonesData(formattedData);
}) })
@@ -119,13 +127,14 @@ const RealTimeVisulization: React.FC = () => {
if (!selectedZone) return prev; if (!selectedZone) return prev;
return { return {
...prev, ...prev,
[selectedZone.zoneName]: { [selectedZone.zoneUuid]: {
...prev[selectedZone.zoneName], // Keep existing properties ...prev[selectedZone.zoneUuid], // Keep existing properties
activeSides: selectedZone.activeSides || [], activeSides: selectedZone.activeSides || [],
panelOrder: selectedZone.panelOrder || [], panelOrder: selectedZone.panelOrder || [],
lockedPanels: selectedZone.lockedPanels || [], lockedPanels: selectedZone.lockedPanels || [],
points: selectedZone.points || [], points: selectedZone.points || [],
zoneUuid: selectedZone.zoneUuid || "", zoneUuid: selectedZone.zoneUuid || "",
zoneName: selectedZone.zoneName || "",
zoneViewPortTarget: selectedZone.zoneViewPortTarget || [], zoneViewPortTarget: selectedZone.zoneViewPortTarget || [],
zoneViewPortPosition: selectedZone.zoneViewPortPosition || [], zoneViewPortPosition: selectedZone.zoneViewPortPosition || [],
widgets: selectedZone.widgets || [], widgets: selectedZone.widgets || [],

View File

@@ -34,6 +34,7 @@ interface DisplayZoneProps {
points: []; points: [];
widgets: Widget[]; widgets: Widget[];
zoneUuid: string; zoneUuid: string;
zoneName: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
zoneViewPortPosition: number[]; zoneViewPortPosition: number[];
}; };
@@ -111,8 +112,8 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
setShowLeftArrow(isOverflowing && canScrollLeft); setShowLeftArrow(isOverflowing && canScrollLeft);
setShowRightArrow(isOverflowing && canScrollRight); setShowRightArrow(isOverflowing && canScrollRight);
// console.log('canScrollRight: ', canScrollRight); //
// console.log('isOverflowing: ', isOverflowing); //
} }
}, []); }, []);
@@ -180,9 +181,10 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
// setSelectedChartId(null); // setSelectedChartId(null);
let response = await getSelect2dZoneData(zoneUuid, organization, projectId, selectedVersion?.versionId || ''); let response = await getSelect2dZoneData(zoneUuid, organization, projectId, selectedVersion?.versionId || '');
// console.log('response2d: ', response);
//
let res = await getFloatingZoneData(zoneUuid, organization, projectId, selectedVersion?.versionId || ''); let res = await getFloatingZoneData(zoneUuid, organization, projectId, selectedVersion?.versionId || '');
// console.log("resFloating: ", res); //
setFloatingWidget(res); setFloatingWidget(res);
// Set the selected zone in the store // Set the selected zone in the store
@@ -201,8 +203,8 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
widgets: response.widgets || [], widgets: response.widgets || [],
points: response.points || [], points: response.points || [],
zoneUuid: zoneUuid, zoneUuid: zoneUuid,
zoneViewPortTarget: response.viewPortCenter || {}, zoneViewPortTarget: response.viewPortTarget || [],
zoneViewPortPosition: response.viewPortposition || {}, zoneViewPortPosition: response.viewPortPosition || [],
}); });
} catch (error) { } catch (error) {
echo.error("Failed to select zone"); echo.error("Failed to select zone");
@@ -238,20 +240,22 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
> >
{Object.keys(zonesData).length !== 0 ? ( {Object.keys(zonesData).length !== 0 ? (
<> <>
{Object.keys(zonesData).map((zoneName, index) => ( {Object.values(zonesData).map((zone, index) => (
<div <>
key={index} { }
className={`zone ${selectedZone.zoneName === zoneName ? "active" : "" <div
}`} key={index}
onClick={() => { className={`zone ${selectedZone.zoneUuid === zone.zoneUuid ? "active" : ""
}`}
onClick={() => {
console.log('zonesData: ', zonesData); handleSelect2dZoneData(zonesData[zone.zoneUuid]?.zoneUuid, zone.zoneName)
handleSelect2dZoneData(zonesData[zoneName]?.zoneUuid, zoneName) }
} }
} >
> {zone.zoneName}
{zoneName} </div>
</div> </>
))} ))}
</> </>
) : ( ) : (

View File

@@ -10,6 +10,7 @@ import {
export default function ZoneCentreTarget() { export default function ZoneCentreTarget() {
const { selectedZone } = useSelectedZoneStore(); const { selectedZone } = useSelectedZoneStore();
//
const [previousZoneCentre, setPreviousZoneCentre] = useState<number[] | null>( const [previousZoneCentre, setPreviousZoneCentre] = useState<number[] | null>(
null null
); );

View File

@@ -56,7 +56,6 @@ const UserAuth: React.FC = () => {
try { try {
const projects = await recentlyViewed(organization, res.message.userId); const projects = await recentlyViewed(organization, res.message.userId);
console.log('projects: ', projects);
if (res.message.isShare) { if (res.message.isShare) {
if (Object.values(projects.RecentlyViewed).length > 0) { if (Object.values(projects.RecentlyViewed).length > 0) {
const firstId = (Object.values(projects?.RecentlyViewed || {})[0] as any)?._id; const firstId = (Object.values(projects?.RecentlyViewed || {})[0] as any)?._id;

View File

@@ -1,9 +1,6 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
export const getZonesApi = async ( export const getZonesApi = async (projectId: string, versionId: string,) => {
projectId: string,
versionId: string,
) => {
try { try {
const response = await fetch(`${url_Backend_dwinzo}/api/V1/zones/${projectId}/${versionId}`, { const response = await fetch(`${url_Backend_dwinzo}/api/V1/zones/${projectId}/${versionId}`, {
method: "GET", method: "GET",

View File

@@ -1,38 +1,37 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
export const upsertProductOrEventApi = async (body: any) => { export const upsertProductOrEventApi = async (body: any) => {
try { try {
const response = await fetch( const response = await fetch(
`${url_Backend_dwinzo}/api/V1/ProductUpsert`, `${url_Backend_dwinzo}/api/V1/ProductUpsert`,
{ {
method: "POST", method: "POST",
headers: { headers: {
Authorization: "Bearer <access_token>", Authorization: "Bearer <access_token>",
"Content-Type": "application/json", "Content-Type": "application/json",
token: localStorage.getItem("token") || "", token: localStorage.getItem("token") || "",
refresh_token: localStorage.getItem("refreshToken") || "", refresh_token: localStorage.getItem("refreshToken") || "",
}, },
body: JSON.stringify(body), body: JSON.stringify(body),
} }
); );
const newAccessToken = response.headers.get("x-access-token"); const newAccessToken = response.headers.get("x-access-token");
if (newAccessToken) { if (newAccessToken) {
//console.log("New token received:", newAccessToken); localStorage.setItem("token", newAccessToken);
localStorage.setItem("token", newAccessToken); }
}
if (!response.ok) { if (!response.ok) {
console.error("Failed to add product or event"); console.error("Failed to add product or event");
} }
const result = await response.json(); const result = await response.json();
return result; return result;
} catch (error) { } catch (error) {
echo.error("Failed to upsert product Or eventApi"); echo.error("Failed to upsert product Or eventApi");
if (error instanceof Error) { if (error instanceof Error) {
console.log(error.message); console.log(error.message);
} else { } else {
console.log("An unknown error occurred"); console.log("An unknown error occurred");
}
} }
}
}; };

View File

@@ -3,7 +3,7 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_UR
export const zoneCameraUpdate = async (zoneData: {}, organization: string, projectId?: string, versionId?: string) => { export const zoneCameraUpdate = async (zoneData: {}, organization: string, projectId?: string, versionId?: string) => {
try { try {
const response = await fetch(`${url_Backend_dwinzo}/api/V1/zones`, { const response = await fetch(`${url_Backend_dwinzo}/api/V1/upsertZone`, {
method: "POST", method: "POST",
headers: { headers: {
Authorization: "Bearer <access_token>", // Replace with actual token Authorization: "Bearer <access_token>", // Replace with actual token

View File

@@ -22,7 +22,9 @@ interface AssetsStore {
// Animation controls // Animation controls
setAnimations: (modelUuid: string, animations: string[]) => void; setAnimations: (modelUuid: string, animations: string[]) => void;
setCurrentAnimation: (modelUuid: string, current: string, isPlaying: boolean) => void; setCurrentAnimation: (modelUuid: string, current: string, isPlaying: boolean, loopAnimation: boolean, isCompleted: boolean) => void;
setAnimationComplete: (modelUuid: string, isCompleted: boolean) => void;
resetAnimation: (modelUuid: string) => void;
addAnimation: (modelUuid: string, animation: string) => void; addAnimation: (modelUuid: string, animation: string) => void;
removeAnimation: (modelUuid: string, animation: string) => void; removeAnimation: (modelUuid: string, animation: string) => void;
@@ -149,22 +151,44 @@ export const createAssetStore = () => {
if (asset) { if (asset) {
asset.animations = animations; asset.animations = animations;
if (!asset.animationState) { if (!asset.animationState) {
asset.animationState = { current: '', playing: false }; asset.animationState = { current: '', isPlaying: false, loopAnimation: true, isCompleted: true };
} }
} }
}); });
}, },
setCurrentAnimation: (modelUuid, current, isPlaying) => { setCurrentAnimation: (modelUuid, current, isPlaying, loopAnimation, isCompleted) => {
set((state) => { set((state) => {
const asset = state.assets.find(a => a.modelUuid === modelUuid); const asset = state.assets.find(a => a.modelUuid === modelUuid);
if (asset?.animationState) { if (asset?.animationState) {
asset.animationState.current = current; asset.animationState.current = current;
asset.animationState.playing = isPlaying; asset.animationState.isPlaying = isPlaying;
asset.animationState.loopAnimation = loopAnimation;
asset.animationState.isCompleted = isCompleted;
} }
}); });
}, },
setAnimationComplete: (modelUuid, isCompleted) => {
set((state) => {
const asset = state.assets.find(a => a.modelUuid === modelUuid);
if (asset?.animationState) {
asset.animationState.isCompleted = isCompleted;
}
});
},
resetAnimation: (modelUuid) => {
set((state) => {
const asset = state.assets.find(a => a.modelUuid === modelUuid);
if (asset?.animationState) {
asset.animationState.current = '';
asset.animationState.isPlaying = true;
asset.animationState.loopAnimation = true;
asset.animationState.isCompleted = true; }
});
},
addAnimation: (modelUuid, animation) => { addAnimation: (modelUuid, animation) => {
set((state) => { set((state) => {
const asset = state.assets.find(a => a.modelUuid === modelUuid); const asset = state.assets.find(a => a.modelUuid === modelUuid);
@@ -184,7 +208,7 @@ export const createAssetStore = () => {
if (asset?.animations) { if (asset?.animations) {
asset.animations = asset.animations.filter(a => a !== animation); asset.animations = asset.animations.filter(a => a !== animation);
if (asset.animationState?.current === animation) { if (asset.animationState?.current === animation) {
asset.animationState.playing = false; asset.animationState.isPlaying = false;
asset.animationState.current = ''; asset.animationState.current = '';
} }
} }

View File

@@ -15,6 +15,7 @@ interface BuilderState {
// Floor Asset // Floor Asset
selectedFloorAsset: Object3D | null; selectedFloorAsset: Object3D | null;
loopAnimation: boolean;
// Wall Settings // Wall Settings
selectedWall: Object3D | null; selectedWall: Object3D | null;
@@ -64,6 +65,7 @@ interface BuilderState {
// Setters - Floor Asset // Setters - Floor Asset
setSelectedFloorAsset: (asset: Object3D | null) => void; setSelectedFloorAsset: (asset: Object3D | null) => void;
setLoopAnimation: (loop: boolean) => void;
// Setters - Wall // Setters - Wall
setSelectedWall: (wall: Object3D | null) => void; setSelectedWall: (wall: Object3D | null) => void;
@@ -118,6 +120,7 @@ export const useBuilderStore = create<BuilderState>()(
deletableWallAsset: null, deletableWallAsset: null,
selectedFloorAsset: null, selectedFloorAsset: null,
loopAnimation: true,
selectedWall: null, selectedWall: null,
wallThickness: 0.5, wallThickness: 0.5,
@@ -197,6 +200,12 @@ export const useBuilderStore = create<BuilderState>()(
}); });
}, },
setLoopAnimation(loopAnimation: boolean) {
set((state) => {
state.loopAnimation = loopAnimation;
});
},
// === Setters: Wall === // === Setters: Wall ===
setSelectedWall: (wall: Object3D | null) => { setSelectedWall: (wall: Object3D | null) => {

View File

@@ -0,0 +1,238 @@
import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
interface HumansStore {
humans: HumanStatus[];
addHuman: (productUuid: string, event: HumanEventSchema) => void;
removeHuman: (modelUuid: string) => void;
updateHuman: (
modelUuid: string,
updates: Partial<Omit<HumanStatus, "modelUuid" | "productUuid">>
) => void;
clearHumans: () => void;
setHumanActive: (modelUuid: string, isActive: boolean) => void;
setHumanPicking: (modelUuid: string, isPicking: boolean) => void;
setHumanLoad: (modelUuid: string, load: number) => void;
setHumanState: (
modelUuid: string,
newState: HumanStatus["state"]
) => void;
incrementHumanLoad: (modelUuid: string, incrementBy: number) => void;
decrementHumanLoad: (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;
incrementDistanceTraveled: (modelUuid: string, incrementBy: number) => void;
resetTime: (modelUuid: string) => void;
getHumanById: (modelUuid: string) => HumanStatus | undefined;
getHumansByProduct: (productUuid: string) => HumanStatus[];
getActiveHumans: () => HumanStatus[];
}
export const createHumanStore = () => {
return create<HumansStore>()(
immer((set, get) => ({
humans: [],
addHuman: (productUuid, event) => {
set((state) => {
const exists = state.humans.some(h => h.modelUuid === event.modelUuid);
if (!exists) {
state.humans.push({
...event,
productUuid,
isActive: false,
isPicking: false,
idleTime: 0,
activeTime: 0,
currentLoad: 0,
currentMaterials: [],
distanceTraveled: 0
});
}
});
},
removeHuman: (modelUuid) => {
set((state) => {
state.humans = state.humans.filter(h => h.modelUuid !== modelUuid);
});
},
updateHuman: (modelUuid, updates) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
Object.assign(human, updates);
}
});
},
clearHumans: () => {
set((state) => {
state.humans = [];
});
},
setHumanActive: (modelUuid, isActive) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.isActive = isActive;
}
});
},
setHumanPicking: (modelUuid, isPicking) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.isPicking = isPicking;
}
});
},
setHumanLoad: (modelUuid, load) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.currentLoad = load;
}
});
},
setHumanState: (modelUuid, newState) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.state = newState;
}
});
},
incrementHumanLoad: (modelUuid, incrementBy) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.currentLoad += incrementBy;
}
});
},
decrementHumanLoad: (modelUuid, decrementBy) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.currentLoad -= decrementBy;
}
});
},
addCurrentMaterial: (modelUuid, materialType, materialId) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.currentMaterials.push({ materialType, materialId });
}
});
},
setCurrentMaterials: (modelUuid, materials) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.currentMaterials = materials;
}
});
},
removeLastMaterial: (modelUuid) => {
let removed;
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human && human.currentMaterials.length > 0) {
removed = JSON.parse(JSON.stringify(human.currentMaterials.pop()));
}
});
return removed;
},
getLastMaterial: (modelUuid) => {
const human = get().humans.find(h => h.modelUuid === modelUuid);
if (human && human.currentMaterials.length > 0) {
return human.currentMaterials[human.currentMaterials.length - 1];
}
return undefined;
},
clearCurrentMaterials: (modelUuid) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.currentMaterials = [];
}
});
},
incrementActiveTime: (modelUuid, incrementBy) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.activeTime += incrementBy;
}
});
},
incrementIdleTime: (modelUuid, incrementBy) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.idleTime += incrementBy;
}
});
},
incrementDistanceTraveled: (modelUuid, incrementBy) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.distanceTraveled += incrementBy;
}
});
},
resetTime: (modelUuid) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.activeTime = 0;
human.idleTime = 0;
}
});
},
getHumanById: (modelUuid) => {
return get().humans.find(h => h.modelUuid === modelUuid);
},
getHumansByProduct: (productUuid) => {
return get().humans.filter(h => h.productUuid === productUuid);
},
getActiveHumans: () => {
return get().humans.filter(h => h.isActive);
}
}))
);
};
export type HumanStoreType = ReturnType<typeof createHumanStore>;

View File

@@ -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'] action: ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['action']
) => 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']> updates: Partial<ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['action']>
) => EventsSchema | undefined; ) => EventsSchema | undefined;
// Trigger-level actionss // Trigger-level actionss
@@ -276,6 +276,15 @@ export const createProductStore = () => {
return; return;
} }
} }
} else if (event.type === "human") {
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

@@ -146,6 +146,40 @@ export const useSelectedAction = create<SelectedActionState>()(
})) }))
); );
interface SelectedAnimationState {
selectedAnimation: {
animationUuid: string;
animationName: string;
animationType: "behaviour" | "animatedTravel";
animation: string | null;
travelPoints?: { startPoint: [number, number, number] | null; endPoint: [number, number, number] | null; }
} | null;
setSelectedAnimation: (animation: {
animationUuid: string;
animationName: string;
animationType: "behaviour" | "animatedTravel";
animation: string | null;
travelPoints?: { startPoint: [number, number, number] | null; endPoint: [number, number, number] | null; }
}) => void;
clearSelectedAnimation: () => void;
}
export const useSelectedAnimation = create<SelectedAnimationState>()(
immer((set) => ({
selectedAnimation: null,
setSelectedAnimation: (animation) => {
set((state) => {
state.selectedAnimation = animation;
});
},
clearSelectedAnimation: () => {
set((state) => {
state.selectedAnimation = null;
});
},
}))
);
interface IsDraggingState { interface IsDraggingState {
isDragging: "start" | "end" | null; isDragging: "start" | "end" | null;
setIsDragging: (state: "start" | "end" | null) => void; setIsDragging: (state: "start" | "end" | null) => void;

View File

@@ -26,7 +26,9 @@ interface Asset {
animations?: string[]; animations?: string[];
animationState?: { animationState?: {
current: string; current: string;
playing: boolean; isPlaying: boolean;
loopAnimation: boolean;
isCompleted: boolean;
}; };
eventData?: { eventData?: {
type: string; type: string;

View File

@@ -1,3 +1,4 @@
// Base Types
interface AssetEventSchema { interface AssetEventSchema {
modelUuid: string; modelUuid: string;
modelName: string; modelName: string;
@@ -18,69 +19,7 @@ interface TriggerSchema {
} | null; } | null;
} }
interface ConveyorPointSchema { // Actions
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
action: ConveyorAction;
}
interface VehiclePointSchema {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
action: VehicleAction;
}
interface RoboticArmPointSchema {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
actions: RoboticArmAction[];
}
interface MachinePointSchema {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
action: MachineAction;
}
interface StoragePointSchema {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
action: StorageAction;
}
interface ConveyorEventSchema extends AssetEventSchema {
type: "transfer";
speed: number;
points: ConveyorPointSchema[];
}
interface VehicleEventSchema extends AssetEventSchema {
type: "vehicle";
speed: number;
point: VehiclePointSchema;
}
interface RoboticArmEventSchema extends AssetEventSchema {
type: "roboticArm";
speed: number;
point: RoboticArmPointSchema;
}
interface MachineEventSchema extends AssetEventSchema {
type: "machine";
point: MachinePointSchema;
}
interface StorageEventSchema extends AssetEventSchema {
type: "storageUnit";
point: StoragePointSchema;
}
interface ConveyorAction { interface ConveyorAction {
actionUuid: string; actionUuid: string;
actionName: string; actionName: string;
@@ -130,19 +69,101 @@ interface StorageAction {
triggers: TriggerSchema[]; triggers: TriggerSchema[];
} }
type Action = ConveyorAction | VehicleAction | RoboticArmAction | MachineAction | StorageAction; interface HumanAction {
actionUuid: string;
actionName: string;
actionType: "worker";
pickUpPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
dropPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
loadCapacity: number;
triggers: TriggerSchema[];
}
type PointsScheme = ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema; type Action = ConveyorAction | VehicleAction | RoboticArmAction | MachineAction | StorageAction | HumanAction;
type EventsSchema = ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema; // Points
interface ConveyorPointSchema {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
action: ConveyorAction;
}
type productsSchema = { interface VehiclePointSchema {
productName: string; uuid: string;
productUuid: string; position: [number, number, number];
eventDatas: EventsSchema[]; rotation: [number, number, number];
}[] action: VehicleAction;
}
interface RoboticArmPointSchema {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
actions: RoboticArmAction[];
}
interface MachinePointSchema {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
action: MachineAction;
}
interface StoragePointSchema {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
action: StorageAction;
}
interface HumanPointSchema {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
action: HumanAction;
}
type PointsScheme = | ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | HumanPointSchema;
// Events
interface ConveyorEventSchema extends AssetEventSchema {
type: "transfer";
speed: number;
points: ConveyorPointSchema[];
}
interface VehicleEventSchema extends AssetEventSchema {
type: "vehicle";
speed: number;
point: VehiclePointSchema;
}
interface RoboticArmEventSchema extends AssetEventSchema {
type: "roboticArm";
speed: number;
point: RoboticArmPointSchema;
}
interface MachineEventSchema extends AssetEventSchema {
type: "machine";
point: MachinePointSchema;
}
interface StorageEventSchema extends AssetEventSchema {
type: "storageUnit";
point: StoragePointSchema;
}
interface HumanEventSchema extends AssetEventSchema {
type: "human";
speed: number;
point: HumanPointSchema;
}
type EventsSchema = | ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema | HumanEventSchema;
// Statuses
interface ConveyorStatus extends ConveyorEventSchema { interface ConveyorStatus extends ConveyorEventSchema {
productUuid: string; productUuid: string;
isActive: boolean; isActive: boolean;
@@ -197,6 +218,25 @@ interface StorageUnitStatus extends StorageEventSchema {
currentMaterials: { materialType: string; materialId: string; }[]; currentMaterials: { materialType: string; materialId: string; }[];
} }
interface HumanStatus extends HumanEventSchema {
productUuid: string;
isActive: boolean;
isPicking: boolean;
idleTime: number;
activeTime: number;
currentLoad: number;
currentMaterials: { materialType: string; materialId: string; }[];
distanceTraveled: number;
currentAction?: {
actionUuid: string;
actionName: string;
animationUuid: string;
materialType?: string | null;
materialId?: string | null;
};
}
// Materials
interface MaterialSchema { interface MaterialSchema {
materialId: string; materialId: string;
materialName: string; materialName: string;
@@ -230,6 +270,14 @@ interface MaterialSchema {
type MaterialsSchema = MaterialSchema[]; type MaterialsSchema = MaterialSchema[];
// Products
type productsSchema = {
productName: string;
productUuid: string;
eventDatas: EventsSchema[];
}[];
// Material History
interface MaterialHistoryEntry { interface MaterialHistoryEntry {
material: MaterialSchema; material: MaterialSchema;
removedAt: string; removedAt: string;