simulation #66
@@ -73,7 +73,7 @@ const Assets: React.FC = () => {
|
|||||||
try {
|
try {
|
||||||
const filt = await fetchAssets();
|
const filt = await fetchAssets();
|
||||||
setFiltereredAssets(filt);
|
setFiltereredAssets(filt);
|
||||||
} catch {}
|
} catch { }
|
||||||
};
|
};
|
||||||
filteredAssets();
|
filteredAssets();
|
||||||
}, [categoryAssets]);
|
}, [categoryAssets]);
|
||||||
@@ -135,7 +135,7 @@ const Assets: React.FC = () => {
|
|||||||
const res = await getCategoryAsset(asset);
|
const res = await getCategoryAsset(asset);
|
||||||
setCategoryAssets(res);
|
setCategoryAssets(res);
|
||||||
setFiltereredAssets(res);
|
setFiltereredAssets(res);
|
||||||
} catch (error) {}
|
} catch (error) { }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
@@ -234,6 +234,7 @@ const Assets: React.FC = () => {
|
|||||||
src={categoryInfo?.categoryImage || ""}
|
src={categoryInfo?.categoryImage || ""}
|
||||||
alt={category}
|
alt={category}
|
||||||
className="category-image"
|
className="category-image"
|
||||||
|
draggable={false}
|
||||||
/>
|
/>
|
||||||
<div className="category-name">{category}</div>
|
<div className="category-name">{category}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -62,19 +62,25 @@ const ZoneProperties: React.FC = () => {
|
|||||||
: zone
|
: zone
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}else{
|
} else {
|
||||||
// console.log(response?.message);
|
// console.log(response?.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function handleVectorChange(key: "zoneViewPortTarget" | "zoneViewPortPosition", newValue: [number, number, number]) {
|
function handleVectorChange(key: "zoneViewPortTarget" | "zoneViewPortPosition", newValue: [number, number, number]) {
|
||||||
setSelectedZone((prev) => ({ ...prev, [key]: newValue }));
|
setSelectedZone((prev) => ({ ...prev, [key]: newValue }));
|
||||||
}
|
}
|
||||||
|
const checkZoneNameDuplicate = (name: string) => {
|
||||||
|
return zones.some(
|
||||||
|
(zone: any) =>
|
||||||
|
zone.zoneName.trim().toLowerCase() === name.trim().toLowerCase() &&
|
||||||
|
zone.zoneId !== selectedZone.zoneId
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="zone-properties-container">
|
<div className="zone-properties-container">
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<RenameInput value={selectedZone.zoneName} onRename={handleZoneNameChange} />
|
<RenameInput value={selectedZone.zoneName} onRename={handleZoneNameChange} checkDuplicate={checkZoneNameDuplicate} />
|
||||||
<div className="button" onClick={handleEditView}>
|
<div className="button" onClick={handleEditView}>
|
||||||
{Edit ? "Cancel" : "Edit"}
|
{Edit ? "Cancel" : "Edit"}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ const BarChartInput = (props: Props) => {
|
|||||||
|
|
||||||
fetchSavedInputes();
|
fetchSavedInputes();
|
||||||
|
|
||||||
}, [selectedChartId.id]);
|
}, [selectedChartId]);
|
||||||
|
|
||||||
// Sync Zustand state when component mounts
|
// Sync Zustand state when component mounts
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -138,7 +138,7 @@ const BarChartInput = (props: Props) => {
|
|||||||
<div className="inputs-wrapper">
|
<div className="inputs-wrapper">
|
||||||
<div className="datas">
|
<div className="datas">
|
||||||
<div className="datas__label">Title</div>
|
<div className="datas__label">Title</div>
|
||||||
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
|
<RenameInput value={widgetName || selectedChartId?.title} onRename={handleNameChange}/>
|
||||||
</div>
|
</div>
|
||||||
{[...Array(3)].map((_, index) => {
|
{[...Array(3)].map((_, index) => {
|
||||||
const inputKey = `input${index + 1}`;
|
const inputKey = `input${index + 1}`;
|
||||||
@@ -152,6 +152,7 @@ const BarChartInput = (props: Props) => {
|
|||||||
onUnselect={() => handleSelect(inputKey, null)}
|
onUnselect={() => handleSelect(inputKey, null)}
|
||||||
selectedValue={selections[inputKey]} // Load from Zustand
|
selectedValue={selections[inputKey]} // Load from Zustand
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
allSelections={selections}
|
||||||
/>
|
/>
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ const FleetEfficiencyInputComponent = (props: Props) => {
|
|||||||
const organization = email?.split("@")[1]?.split(".")[0]
|
const organization = email?.split("@")[1]?.split(".")[0]
|
||||||
const [isLoading, setLoading] = useState<boolean>(true);
|
const [isLoading, setLoading] = useState<boolean>(true);
|
||||||
|
|
||||||
|
const isSelected = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchZoneData = async () => {
|
const fetchZoneData = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -139,7 +143,7 @@ const FleetEfficiencyInputComponent = (props: Props) => {
|
|||||||
<div className="inputs-wrapper">
|
<div className="inputs-wrapper">
|
||||||
<div className="datas">
|
<div className="datas">
|
||||||
<div className="datas__label">Title</div>
|
<div className="datas__label">Title</div>
|
||||||
<RenameInput value={selectedChartId?.header || "untited"} onRename={handleNameChange}/>
|
<RenameInput value={widgetName || selectedChartId?.header} onRename={handleNameChange}/>
|
||||||
</div>
|
</div>
|
||||||
{[...Array(1)].map((_, index) => {
|
{[...Array(1)].map((_, index) => {
|
||||||
const inputKey = `input${index + 1}`;
|
const inputKey = `input${index + 1}`;
|
||||||
@@ -153,6 +157,7 @@ const FleetEfficiencyInputComponent = (props: Props) => {
|
|||||||
onUnselect={() => handleSelect(inputKey, null)}
|
onUnselect={() => handleSelect(inputKey, null)}
|
||||||
selectedValue={selections[inputKey]} // Load from Zustand
|
selectedValue={selections[inputKey]} // Load from Zustand
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
allSelections={selections}
|
||||||
/>
|
/>
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ const FlotingWidgetInput = (props: Props) => {
|
|||||||
<div className="inputs-wrapper">
|
<div className="inputs-wrapper">
|
||||||
<div className="datas">
|
<div className="datas">
|
||||||
<div className="datas__label">Title</div>
|
<div className="datas__label">Title</div>
|
||||||
<RenameInput value={selectedChartId?.header || "untited"} onRename={handleNameChange}/>
|
<RenameInput value={widgetName || selectedChartId?.header} onRename={handleNameChange}/>
|
||||||
</div>
|
</div>
|
||||||
{[...Array(6)].map((_, index) => {
|
{[...Array(6)].map((_, index) => {
|
||||||
const inputKey = `input${index + 1}`;
|
const inputKey = `input${index + 1}`;
|
||||||
@@ -153,6 +153,7 @@ const FlotingWidgetInput = (props: Props) => {
|
|||||||
onUnselect={() => handleSelect(inputKey, null)}
|
onUnselect={() => handleSelect(inputKey, null)}
|
||||||
selectedValue={selections[inputKey]} // Load from Zustand
|
selectedValue={selections[inputKey]} // Load from Zustand
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
allSelections={selections}
|
||||||
/>
|
/>
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ const LineGrapInput = (props: Props) => {
|
|||||||
<div className="inputs-wrapper">
|
<div className="inputs-wrapper">
|
||||||
<div className="datas">
|
<div className="datas">
|
||||||
<div className="datas__label">Title</div>
|
<div className="datas__label">Title</div>
|
||||||
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
|
<RenameInput value={widgetName || selectedChartId?.title} onRename={handleNameChange}/>
|
||||||
</div>
|
</div>
|
||||||
{[...Array(4)].map((_, index) => {
|
{[...Array(4)].map((_, index) => {
|
||||||
const inputKey = `input${index + 1}`;
|
const inputKey = `input${index + 1}`;
|
||||||
@@ -271,6 +271,7 @@ const LineGrapInput = (props: Props) => {
|
|||||||
onUnselect={() => handleSelect(inputKey, null)}
|
onUnselect={() => handleSelect(inputKey, null)}
|
||||||
selectedValue={selections[inputKey]} // Load from Zustand
|
selectedValue={selections[inputKey]} // Load from Zustand
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
allSelections={selections}
|
||||||
/>
|
/>
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ const PieChartInput = (props: Props) => {
|
|||||||
<div className="inputs-wrapper">
|
<div className="inputs-wrapper">
|
||||||
<div className="datas">
|
<div className="datas">
|
||||||
<div className="datas__label">Title</div>
|
<div className="datas__label">Title</div>
|
||||||
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
|
<RenameInput value={widgetName || selectedChartId?.title} onRename={handleNameChange}/>
|
||||||
</div>
|
</div>
|
||||||
{[...Array(2)].map((_, index) => {
|
{[...Array(2)].map((_, index) => {
|
||||||
const inputKey = `input${index + 1}`;
|
const inputKey = `input${index + 1}`;
|
||||||
@@ -152,6 +152,7 @@ const PieChartInput = (props: Props) => {
|
|||||||
onUnselect={() => handleSelect(inputKey, null)}
|
onUnselect={() => handleSelect(inputKey, null)}
|
||||||
selectedValue={selections[inputKey]} // Load from Zustand
|
selectedValue={selections[inputKey]} // Load from Zustand
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
allSelections={selections}
|
||||||
/>
|
/>
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ const Progress1Input = (props: Props) => {
|
|||||||
<div className="inputs-wrapper">
|
<div className="inputs-wrapper">
|
||||||
<div className="datas">
|
<div className="datas">
|
||||||
<div className="datas__label">Title</div>
|
<div className="datas__label">Title</div>
|
||||||
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
|
<RenameInput value={widgetName || selectedChartId?.title} onRename={handleNameChange}/>
|
||||||
</div>
|
</div>
|
||||||
{[...Array(1)].map((_, index) => {
|
{[...Array(1)].map((_, index) => {
|
||||||
const inputKey = `input${index + 1}`;
|
const inputKey = `input${index + 1}`;
|
||||||
@@ -146,6 +146,7 @@ const Progress1Input = (props: Props) => {
|
|||||||
onUnselect={() => handleSelect(inputKey, null)}
|
onUnselect={() => handleSelect(inputKey, null)}
|
||||||
selectedValue={selections[inputKey]} // Load from Zustand
|
selectedValue={selections[inputKey]} // Load from Zustand
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
allSelections={selections}
|
||||||
/>
|
/>
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ const Progress2Input = (props: Props) => {
|
|||||||
<div className="inputs-wrapper">
|
<div className="inputs-wrapper">
|
||||||
<div className="datas">
|
<div className="datas">
|
||||||
<div className="datas__label">Title</div>
|
<div className="datas__label">Title</div>
|
||||||
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
|
<RenameInput value={widgetName || selectedChartId?.title} onRename={handleNameChange}/>
|
||||||
</div>
|
</div>
|
||||||
{[...Array(2)].map((_, index) => {
|
{[...Array(2)].map((_, index) => {
|
||||||
const inputKey = `input${index + 1}`;
|
const inputKey = `input${index + 1}`;
|
||||||
@@ -146,6 +146,7 @@ const Progress2Input = (props: Props) => {
|
|||||||
onUnselect={() => handleSelect(inputKey, null)}
|
onUnselect={() => handleSelect(inputKey, null)}
|
||||||
selectedValue={selections[inputKey]} // Load from Zustand
|
selectedValue={selections[inputKey]} // Load from Zustand
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
allSelections={selections}
|
||||||
/>
|
/>
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ const WarehouseThroughputInputComponent = (props: Props) => {
|
|||||||
<div className="inputs-wrapper">
|
<div className="inputs-wrapper">
|
||||||
<div className="datas">
|
<div className="datas">
|
||||||
<div className="datas__label">Title</div>
|
<div className="datas__label">Title</div>
|
||||||
<RenameInput value={selectedChartId?.header || "untited"} onRename={handleNameChange}/>
|
<RenameInput value={widgetName || selectedChartId?.header} onRename={handleNameChange}/>
|
||||||
</div>
|
</div>
|
||||||
{[...Array(1)].map((_, index) => {
|
{[...Array(1)].map((_, index) => {
|
||||||
const inputKey = `input${index + 1}`;
|
const inputKey = `input${index + 1}`;
|
||||||
@@ -153,6 +153,7 @@ const WarehouseThroughputInputComponent = (props: Props) => {
|
|||||||
onUnselect={() => handleSelect(inputKey, null)}
|
onUnselect={() => handleSelect(inputKey, null)}
|
||||||
selectedValue={selections[inputKey]} // Load from Zustand
|
selectedValue={selections[inputKey]} // Load from Zustand
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
allSelections={selections}
|
||||||
/>
|
/>
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ const Widget2InputCard3D = (props: Props) => {
|
|||||||
onUnselect={() => handleSelect(inputKey, null)}
|
onUnselect={() => handleSelect(inputKey, null)}
|
||||||
selectedValue={selections[inputKey]} // Load from Zustand
|
selectedValue={selections[inputKey]} // Load from Zustand
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
allSelections={selections}
|
||||||
/>
|
/>
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ const Widget3InputCard3D = () => {
|
|||||||
onUnselect={() => handleSelect(inputKey, null)}
|
onUnselect={() => handleSelect(inputKey, null)}
|
||||||
selectedValue={selections[inputKey]} // Load from Zustand
|
selectedValue={selections[inputKey]} // Load from Zustand
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
allSelections={selections}
|
||||||
/>
|
/>
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ const Widget4InputCard3D = (props: Props) => {
|
|||||||
onUnselect={() => handleSelect(inputKey, null)}
|
onUnselect={() => handleSelect(inputKey, null)}
|
||||||
selectedValue={selections[inputKey]} // Load from Zustand
|
selectedValue={selections[inputKey]} // Load from Zustand
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
allSelections={selections}
|
||||||
/>
|
/>
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
|
|||||||
@@ -1,145 +1,3 @@
|
|||||||
// import React, { useState, useRef, useEffect } from "react";
|
|
||||||
|
|
||||||
// // Dropdown Item Component
|
|
||||||
// const DropdownItem = ({
|
|
||||||
// label,
|
|
||||||
// href,
|
|
||||||
// onClick,
|
|
||||||
// }: {
|
|
||||||
// label: string;
|
|
||||||
// href?: string;
|
|
||||||
// onClick?: () => void;
|
|
||||||
// }) => (
|
|
||||||
// <a
|
|
||||||
// href={href || "#"}
|
|
||||||
// className="dropdown-item"
|
|
||||||
// onClick={(e) => {
|
|
||||||
// e.preventDefault();
|
|
||||||
// onClick?.();
|
|
||||||
// }}
|
|
||||||
// >
|
|
||||||
// {label}
|
|
||||||
// </a>
|
|
||||||
// );
|
|
||||||
|
|
||||||
// // Nested Dropdown Component
|
|
||||||
// const NestedDropdown = ({
|
|
||||||
// label,
|
|
||||||
// children,
|
|
||||||
// onSelect,
|
|
||||||
// }: {
|
|
||||||
// label: string;
|
|
||||||
// children: React.ReactNode;
|
|
||||||
// onSelect: (selectedLabel: string) => void;
|
|
||||||
// }) => {
|
|
||||||
// const [open, setOpen] = useState(false);
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <div className="nested-dropdown">
|
|
||||||
// {/* Dropdown Trigger */}
|
|
||||||
// <div
|
|
||||||
// className={`dropdown-trigger ${open ? "open" : ""}`}
|
|
||||||
// onClick={() => setOpen(!open)} // Toggle submenu on click
|
|
||||||
// >
|
|
||||||
// {label} <span className="icon">{open ? "▼" : "▶"}</span>
|
|
||||||
// </div>
|
|
||||||
|
|
||||||
// {/* Submenu */}
|
|
||||||
// {open && (
|
|
||||||
// <div className="submenu">
|
|
||||||
// {React.Children.map(children, (child) => {
|
|
||||||
// if (React.isValidElement(child)) {
|
|
||||||
// // Clone the element and pass the `onSelect` prop only if it's expected
|
|
||||||
// return React.cloneElement(child as React.ReactElement<any>, { onSelect });
|
|
||||||
// }
|
|
||||||
// return child; // Return non-element children as-is
|
|
||||||
// })}
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// </div>
|
|
||||||
// );
|
|
||||||
// };
|
|
||||||
|
|
||||||
// // Recursive Function to Render Nested Data
|
|
||||||
// const renderNestedData = (
|
|
||||||
// data: Record<string, any>,
|
|
||||||
// onSelect: (selectedLabel: string) => void
|
|
||||||
// ) => {
|
|
||||||
// return Object.entries(data).map(([key, value]) => {
|
|
||||||
// if (typeof value === "object" && !Array.isArray(value)) {
|
|
||||||
// // If the value is an object, render it as a nested dropdown
|
|
||||||
// return (
|
|
||||||
// <NestedDropdown key={key} label={key} onSelect={onSelect}>
|
|
||||||
// {renderNestedData(value, onSelect)}
|
|
||||||
// </NestedDropdown>
|
|
||||||
// );
|
|
||||||
// } else if (Array.isArray(value)) {
|
|
||||||
// // If the value is an array, render each item as a dropdown item
|
|
||||||
// return value.map((item, index) => (
|
|
||||||
// <DropdownItem key={index} label={item} onClick={() => onSelect(item)} />
|
|
||||||
// ));
|
|
||||||
// } else {
|
|
||||||
// // If the value is a simple string, render it as a dropdown item
|
|
||||||
// return (
|
|
||||||
// <DropdownItem key={key} label={value} onClick={() => onSelect(value)} />
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
|
|
||||||
// // Main Multi-Level Dropdown Component
|
|
||||||
// const MultiLevelDropdown = ({ data }: { data: Record<string, any> }) => {
|
|
||||||
// const [open, setOpen] = useState(false);
|
|
||||||
// const [selectedLabel, setSelectedLabel] = useState("Dropdown trigger");
|
|
||||||
// const dropdownRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
// // Handle outer click to close the dropdown
|
|
||||||
// useEffect(() => {
|
|
||||||
// const handleClickOutside = (event: MouseEvent) => {
|
|
||||||
// if (
|
|
||||||
// dropdownRef.current &&
|
|
||||||
// !dropdownRef.current.contains(event.target as Node)
|
|
||||||
// ) {
|
|
||||||
// setOpen(false);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// document.addEventListener("mousedown", handleClickOutside);
|
|
||||||
// return () => {
|
|
||||||
// document.removeEventListener("mousedown", handleClickOutside);
|
|
||||||
// };
|
|
||||||
// }, []);
|
|
||||||
|
|
||||||
// // Handle selection of an item
|
|
||||||
// const handleSelect = (selectedLabel: string) => {
|
|
||||||
// setSelectedLabel(selectedLabel); // Update the dropdown trigger text
|
|
||||||
// setOpen(false); // Close the dropdown
|
|
||||||
// };
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <div className="multi-level-dropdown" ref={dropdownRef}>
|
|
||||||
// {/* Dropdown Trigger Button */}
|
|
||||||
// <button
|
|
||||||
// className={`dropdown-button ${open ? "open" : ""}`}
|
|
||||||
// onClick={() => setOpen(!open)} // Toggle main menu on click
|
|
||||||
// >
|
|
||||||
// {selectedLabel} <span className="icon">▾</span>
|
|
||||||
// </button>
|
|
||||||
|
|
||||||
// {/* Dropdown Menu */}
|
|
||||||
// {open && (
|
|
||||||
// <div className="dropdown-menu">
|
|
||||||
// <div className="dropdown-content">
|
|
||||||
// {renderNestedData(data, handleSelect)}
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// </div>
|
|
||||||
// );
|
|
||||||
// };
|
|
||||||
|
|
||||||
// export default MultiLevelDropdown;
|
|
||||||
|
|
||||||
import React, { useState, useRef, useEffect } from "react";
|
import React, { useState, useRef, useEffect } from "react";
|
||||||
import { ArrowIcon } from "../../icons/ExportCommonIcons";
|
import { ArrowIcon } from "../../icons/ExportCommonIcons";
|
||||||
|
|
||||||
@@ -147,11 +5,19 @@ import { ArrowIcon } from "../../icons/ExportCommonIcons";
|
|||||||
const DropdownItem = ({
|
const DropdownItem = ({
|
||||||
label,
|
label,
|
||||||
onClick,
|
onClick,
|
||||||
|
disabled = false,
|
||||||
}: {
|
}: {
|
||||||
label: string;
|
label: string;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
|
disabled?: boolean;
|
||||||
}) => (
|
}) => (
|
||||||
<div className="dropdown-item" onClick={onClick}>
|
<div
|
||||||
|
className={`dropdown-item ${disabled ? "disabled" : ""}`}
|
||||||
|
onClick={() => {
|
||||||
|
if (!disabled) onClick();
|
||||||
|
}}
|
||||||
|
style={{ cursor: disabled ? "not-allowed": "default", opacity: disabled ? 0.5 : 1 }}
|
||||||
|
>
|
||||||
{label}
|
{label}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -161,10 +27,12 @@ const NestedDropdown = ({
|
|||||||
label,
|
label,
|
||||||
fields,
|
fields,
|
||||||
onSelect,
|
onSelect,
|
||||||
|
disabledFields = [],
|
||||||
}: {
|
}: {
|
||||||
label: string;
|
label: string;
|
||||||
fields: string[];
|
fields: string[];
|
||||||
onSelect: (selectedData: { name: string; fields: string }) => void;
|
onSelect: (selectedData: { name: string; fields: string }) => void;
|
||||||
|
disabledFields?: string[];
|
||||||
}) => {
|
}) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
@@ -184,13 +52,17 @@ const NestedDropdown = ({
|
|||||||
</div>
|
</div>
|
||||||
{open && (
|
{open && (
|
||||||
<div className="submenu">
|
<div className="submenu">
|
||||||
{fields.map((field) => (
|
{fields.map((field) => {
|
||||||
<DropdownItem
|
const isDisabled = disabledFields.includes(`${label}-${field}`);
|
||||||
key={field}
|
return (
|
||||||
label={field}
|
<DropdownItem
|
||||||
onClick={() => onSelect({ name: label, fields: field })}
|
key={field}
|
||||||
/>
|
label={field}
|
||||||
))}
|
onClick={() => onSelect({ name: label, fields: field })}
|
||||||
|
disabled={isDisabled}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -203,6 +75,7 @@ interface MultiLevelDropdownProps {
|
|||||||
onSelect: (selectedData: { name: string; fields: string }) => void;
|
onSelect: (selectedData: { name: string; fields: string }) => void;
|
||||||
onUnselect: () => void;
|
onUnselect: () => void;
|
||||||
selectedValue?: { name: string; fields: string };
|
selectedValue?: { name: string; fields: string };
|
||||||
|
allSelections?: Record<string, { name: string; fields: string }>;
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +85,7 @@ const MultiLevelDropdown = ({
|
|||||||
onSelect,
|
onSelect,
|
||||||
onUnselect,
|
onUnselect,
|
||||||
selectedValue,
|
selectedValue,
|
||||||
|
allSelections = {},
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
}: MultiLevelDropdownProps) => {
|
}: MultiLevelDropdownProps) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
@@ -249,6 +123,14 @@ const MultiLevelDropdown = ({
|
|||||||
? `${selectedValue.name} - ${selectedValue.fields}`
|
? `${selectedValue.name} - ${selectedValue.fields}`
|
||||||
: "Dropdown trigger";
|
: "Dropdown trigger";
|
||||||
|
|
||||||
|
// Build list of disabled selections
|
||||||
|
const disabledFieldsList = Object.values(allSelections)
|
||||||
|
.filter(
|
||||||
|
(sel) =>
|
||||||
|
!(sel.name === selectedValue?.name && sel.fields === selectedValue?.fields)
|
||||||
|
)
|
||||||
|
.map((sel) => `${sel.name}-${sel.fields}`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="multi-level-dropdown" ref={dropdownRef}>
|
<div className="multi-level-dropdown" ref={dropdownRef}>
|
||||||
<button
|
<button
|
||||||
@@ -260,25 +142,23 @@ const MultiLevelDropdown = ({
|
|||||||
</button>
|
</button>
|
||||||
{open && (
|
{open && (
|
||||||
<div className="dropdown-menu">
|
<div className="dropdown-menu">
|
||||||
<div className="dropdown-content ">
|
<div className="dropdown-content">
|
||||||
|
{isLoading ? (
|
||||||
{/* loading list */}
|
<div className="loading" />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
{/* Unselect Option */}
|
<DropdownItem label="Unselect" onClick={handleItemUnselect} />
|
||||||
<DropdownItem label="Unselect" onClick={handleItemUnselect} />
|
{Object.entries(data).map(([key, value]) => (
|
||||||
{/* Nested Dropdown Items */}
|
<NestedDropdown
|
||||||
{
|
key={key}
|
||||||
isLoading ? <div className="loading" /> :
|
label={key}
|
||||||
Object.entries(data).map(([key, value]) => (
|
fields={Object.keys(value)}
|
||||||
<NestedDropdown
|
onSelect={handleItemSelect}
|
||||||
key={key}
|
disabledFields={disabledFieldsList}
|
||||||
label={key}
|
/>
|
||||||
fields={Object.keys(value)}
|
))}
|
||||||
onSelect={handleItemSelect}
|
</>
|
||||||
/>
|
)}
|
||||||
))
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,26 +1,42 @@
|
|||||||
import React, { useState, useRef, useEffect } from "react";
|
import React, { useState, useRef, useEffect } from "react";
|
||||||
|
|
||||||
|
// interface RenameInputProps {
|
||||||
|
// value: string;
|
||||||
|
// onRename?: (newText: string) => void;
|
||||||
|
// }
|
||||||
|
|
||||||
interface RenameInputProps {
|
interface RenameInputProps {
|
||||||
value: string;
|
value: string;
|
||||||
onRename?: (newText: string) => void;
|
onRename?: (newText: string) => void;
|
||||||
|
checkDuplicate?: (name: string) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RenameInput: React.FC<RenameInputProps> = ({ value, onRename }) => {
|
const RenameInput: React.FC<RenameInputProps> = ({ value, onRename, checkDuplicate }) => {
|
||||||
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 inputRef = useRef<HTMLInputElement | null>(null);
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setText(value); // Ensure state updates when parent value changes
|
setText(value);
|
||||||
}, [value]);
|
}, [value]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (checkDuplicate) {
|
||||||
|
setIsDuplicate(checkDuplicate(text));
|
||||||
|
}
|
||||||
|
}, [text, checkDuplicate]);
|
||||||
|
|
||||||
const handleDoubleClick = () => {
|
const handleDoubleClick = () => {
|
||||||
setIsEditing(true);
|
setIsEditing(true);
|
||||||
setTimeout(() => inputRef.current?.focus(), 0); // Focus the input after rendering
|
setTimeout(() => inputRef.current?.focus(), 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBlur = () => {
|
const handleBlur = () => {
|
||||||
|
|
||||||
|
if(isDuplicate) return
|
||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
if (onRename) {
|
if (onRename && !isDuplicate) {
|
||||||
onRename(text);
|
onRename(text);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -30,7 +46,7 @@ const RenameInput: React.FC<RenameInputProps> = ({ value, onRename }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
if (e.key === "Enter") {
|
if (e.key === "Enter" && !isDuplicate) {
|
||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
if (onRename) {
|
if (onRename) {
|
||||||
onRename(text);
|
onRename(text);
|
||||||
@@ -41,15 +57,18 @@ const RenameInput: React.FC<RenameInputProps> = ({ value, onRename }) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isEditing ? (
|
{isEditing ? (
|
||||||
<input
|
<>
|
||||||
ref={inputRef}
|
<input
|
||||||
type="text"
|
ref={inputRef}
|
||||||
value={text}
|
type="text"
|
||||||
onChange={handleChange}
|
value={text}
|
||||||
onBlur={handleBlur}
|
onChange={handleChange}
|
||||||
onKeyDown={handleKeyDown}
|
onBlur={handleBlur}
|
||||||
className="rename-input"
|
onKeyDown={handleKeyDown}
|
||||||
/>
|
className={`rename-input ${isDuplicate ? "input-error" : ""}`}
|
||||||
|
/>
|
||||||
|
{/* {isDuplicate && <div className="error-msg">Name already exists!</div>} */}
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<span onDoubleClick={handleDoubleClick} className="input-value">
|
<span onDoubleClick={handleDoubleClick} className="input-value">
|
||||||
{text}
|
{text}
|
||||||
@@ -58,5 +77,4 @@ const RenameInput: React.FC<RenameInputProps> = ({ value, onRename }) => {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
export default RenameInput
|
||||||
export default RenameInput;
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
RmoveIcon,
|
RmoveIcon,
|
||||||
} from "../../icons/ExportCommonIcons";
|
} from "../../icons/ExportCommonIcons";
|
||||||
import { useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
import { useFloorItems, useZoneAssetId } from "../../../store/store";
|
import { useFloorItems, useZoneAssetId, useZones } from "../../../store/store";
|
||||||
import { zoneCameraUpdate } from "../../../services/realTimeVisulization/zoneData/zoneCameraUpdation";
|
import { zoneCameraUpdate } from "../../../services/realTimeVisulization/zoneData/zoneCameraUpdation";
|
||||||
import { setFloorItemApi } from "../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
|
import { setFloorItemApi } from "../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
|||||||
const { activeModule, setActiveModule } = useModuleStore();
|
const { activeModule, setActiveModule } = 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>>(
|
||||||
{}
|
{}
|
||||||
@@ -100,19 +100,33 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
|||||||
function handleAssetClick(asset: Asset) {
|
function handleAssetClick(asset: Asset) {
|
||||||
setZoneAssetId(asset)
|
setZoneAssetId(asset)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleZoneNameChange(newName: string) {
|
async function handleZoneNameChange(newName: string) {
|
||||||
//zone apiiiiii
|
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0];
|
const organization = email?.split("@")[1]?.split(".")[0];
|
||||||
let zonesdata = {
|
|
||||||
|
const isDuplicate = zones.some(
|
||||||
|
(zone: any) =>
|
||||||
|
zone.zoneName.trim().toLowerCase() === newName.trim().toLowerCase() &&
|
||||||
|
zone.zoneId !== selectedZone.zoneId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isDuplicate) {
|
||||||
|
alert("Zone name already exists. Please choose a different name.");
|
||||||
|
return; // DO NOT update state
|
||||||
|
}
|
||||||
|
|
||||||
|
const zonesdata = {
|
||||||
zoneId: selectedZone.zoneId,
|
zoneId: selectedZone.zoneId,
|
||||||
zoneName: newName
|
zoneName: newName,
|
||||||
};
|
};
|
||||||
let response = await zoneCameraUpdate(zonesdata, organization);
|
|
||||||
|
const response = await zoneCameraUpdate(zonesdata, organization);
|
||||||
if (response.message === "updated successfully") {
|
if (response.message === "updated successfully") {
|
||||||
setSelectedZone((prev) => ({ ...prev, zoneName: newName }));
|
setSelectedZone((prev) => ({ ...prev, zoneName: newName }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleZoneAssetName(newName: string) {
|
async function handleZoneAssetName(newName: string) {
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0];
|
const organization = email?.split("@")[1]?.split(".")[0];
|
||||||
@@ -132,6 +146,13 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
|||||||
console.log('newName: ', newName);
|
console.log('newName: ', newName);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
const checkZoneNameDuplicate = (name: string) => {
|
||||||
|
return zones.some(
|
||||||
|
(zone: any) =>
|
||||||
|
zone.zoneName.trim().toLowerCase() === name.trim().toLowerCase() &&
|
||||||
|
zone.zoneId !== selectedZone.zoneId
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -146,7 +167,12 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
|||||||
className="value"
|
className="value"
|
||||||
onClick={() => handleSelectZone(item.id)}
|
onClick={() => handleSelectZone(item.id)}
|
||||||
>
|
>
|
||||||
<RenameInput value={item.name} onRename={handleZoneNameChange} />
|
<RenameInput
|
||||||
|
value={item.name}
|
||||||
|
onRename={handleZoneNameChange}
|
||||||
|
checkDuplicate={checkZoneNameDuplicate}
|
||||||
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="options-container">
|
<div className="options-container">
|
||||||
|
|||||||
@@ -79,8 +79,9 @@ const Agv: React.FC<ProcessContainerProps> = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{pathPoints.map((pair, i) => (
|
{pathPoints.map((pair, i) => (
|
||||||
<group key={i} visible={!isPlaying}>
|
<group key={i}>
|
||||||
<PathNavigator
|
<PathNavigator
|
||||||
|
key={i}
|
||||||
navMesh={navMesh}
|
navMesh={navMesh}
|
||||||
pathPoints={pair.points}
|
pathPoints={pair.points}
|
||||||
id={pair.modelUuid}
|
id={pair.modelUuid}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ function NavMeshCreator({ lines }: NavMeshCreatorProps) {
|
|||||||
<NavMeshDetails lines={lines} setNavMesh={setNavMesh} groupRef={groupRef} />
|
<NavMeshDetails lines={lines} setNavMesh={setNavMesh} groupRef={groupRef} />
|
||||||
|
|
||||||
<group ref={groupRef} visible={false} name="Meshes">
|
<group ref={groupRef} visible={false} name="Meshes">
|
||||||
<mesh rotation-x={CONSTANTS.planeConfig.rotation} position={CONSTANTS.planeConfig.position3D} name="Plane" receiveShadow>
|
<mesh rotation-x={CONSTANTS.planeConfig.rotation} position={CONSTANTS.planeConfig.position3D} receiveShadow>
|
||||||
<planeGeometry args={[300, 300]} />
|
<planeGeometry args={[300, 300]} />
|
||||||
<meshBasicMaterial color={CONSTANTS.planeConfig.color} />
|
<meshBasicMaterial color={CONSTANTS.planeConfig.color} />
|
||||||
</mesh>
|
</mesh>
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ import * as THREE from "three";
|
|||||||
import { useFrame, useThree } from "@react-three/fiber";
|
import { useFrame, useThree } from "@react-three/fiber";
|
||||||
import { NavMeshQuery } from "@recast-navigation/core";
|
import { NavMeshQuery } from "@recast-navigation/core";
|
||||||
import { Line } from "@react-three/drei";
|
import { Line } from "@react-three/drei";
|
||||||
import { useAnimationPlaySpeed, usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
import {
|
||||||
|
useAnimationPlaySpeed,
|
||||||
|
usePlayButtonStore,
|
||||||
|
} from "../../../store/usePlayButtonStore";
|
||||||
import { usePlayAgv } from "../../../store/store";
|
import { usePlayAgv } from "../../../store/store";
|
||||||
|
|
||||||
interface PathNavigatorProps {
|
interface PathNavigatorProps {
|
||||||
@@ -11,7 +14,7 @@ interface PathNavigatorProps {
|
|||||||
pathPoints: any;
|
pathPoints: any;
|
||||||
id: string;
|
id: string;
|
||||||
speed: number;
|
speed: number;
|
||||||
globalSpeed: number,
|
globalSpeed: number;
|
||||||
bufferTime: number;
|
bufferTime: number;
|
||||||
hitCount: number;
|
hitCount: number;
|
||||||
processes: any[];
|
processes: any[];
|
||||||
@@ -40,11 +43,21 @@ export default function PathNavigator({
|
|||||||
}: PathNavigatorProps) {
|
}: PathNavigatorProps) {
|
||||||
const [currentPhase, setCurrentPhase] = useState<Phase>("initial");
|
const [currentPhase, setCurrentPhase] = useState<Phase>("initial");
|
||||||
const [path, setPath] = useState<[number, number, number][]>([]);
|
const [path, setPath] = useState<[number, number, number][]>([]);
|
||||||
const [toPickupPath, setToPickupPath] = useState<[number, number, number][]>([]);
|
const [toPickupPath, setToPickupPath] = useState<[number, number, number][]>(
|
||||||
const [pickupDropPath, setPickupDropPath] = useState<[number, number, number][]>([]);
|
[]
|
||||||
const [dropPickupPath, setDropPickupPath] = useState<[number, number, number][]>([]);
|
);
|
||||||
const [initialPosition, setInitialPosition] = useState<THREE.Vector3 | null>(null);
|
const [pickupDropPath, setPickupDropPath] = useState<
|
||||||
const [initialRotation, setInitialRotation] = useState<THREE.Euler | null>(null);
|
[number, number, number][]
|
||||||
|
>([]);
|
||||||
|
const [dropPickupPath, setDropPickupPath] = useState<
|
||||||
|
[number, number, number][]
|
||||||
|
>([]);
|
||||||
|
const [initialPosition, setInitialPosition] = useState<THREE.Vector3 | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
const [initialRotation, setInitialRotation] = useState<THREE.Euler | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
const [boxVisible, setBoxVisible] = useState(false);
|
const [boxVisible, setBoxVisible] = useState(false);
|
||||||
|
|
||||||
const distancesRef = useRef<number[]>([]);
|
const distancesRef = useRef<number[]>([]);
|
||||||
@@ -61,11 +74,14 @@ export default function PathNavigator({
|
|||||||
|
|
||||||
const boxRef = useRef<THREE.Mesh | null>(null);
|
const boxRef = useRef<THREE.Mesh | null>(null);
|
||||||
|
|
||||||
const baseMaterials = useMemo(() => ({
|
const baseMaterials = useMemo(
|
||||||
Box: new THREE.MeshStandardMaterial({ color: 0x8b4513 }),
|
() => ({
|
||||||
Crate: new THREE.MeshStandardMaterial({ color: 0x00ff00 }),
|
Box: new THREE.MeshStandardMaterial({ color: 0x8b4513 }),
|
||||||
Default: new THREE.MeshStandardMaterial({ color: 0xcccccc })
|
Crate: new THREE.MeshStandardMaterial({ color: 0x00ff00 }),
|
||||||
}), []);
|
Default: new THREE.MeshStandardMaterial({ color: 0xcccccc }),
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const object = scene.getObjectByProperty("uuid", id);
|
const object = scene.getObjectByProperty("uuid", id);
|
||||||
@@ -135,7 +151,11 @@ export default function PathNavigator({
|
|||||||
const pickupToDropPath = computePath(pickup, drop);
|
const pickupToDropPath = computePath(pickup, drop);
|
||||||
const dropToPickupPath = computePath(drop, pickup);
|
const dropToPickupPath = computePath(drop, pickup);
|
||||||
|
|
||||||
if (toPickupPath.length && pickupToDropPath.length && dropToPickupPath.length) {
|
if (
|
||||||
|
toPickupPath.length &&
|
||||||
|
pickupToDropPath.length &&
|
||||||
|
dropToPickupPath.length
|
||||||
|
) {
|
||||||
setPickupDropPath(pickupToDropPath);
|
setPickupDropPath(pickupToDropPath);
|
||||||
setDropPickupPath(dropToPickupPath);
|
setDropPickupPath(dropToPickupPath);
|
||||||
setToPickupPath(toPickupPath);
|
setToPickupPath(toPickupPath);
|
||||||
@@ -163,7 +183,10 @@ export default function PathNavigator({
|
|||||||
}, [path]);
|
}, [path]);
|
||||||
|
|
||||||
function logAgvStatus(id: string, status: string) {
|
function logAgvStatus(id: string, status: string) {
|
||||||
// console.log(`AGV ${id}: ${status}`);
|
// console.log(
|
||||||
|
// `AGV ${id}: ${status}`
|
||||||
|
|
||||||
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
function findProcessByTargetModelUUID(processes: any, targetModelUUID: any) {
|
function findProcessByTargetModelUUID(processes: any, targetModelUUID: any) {
|
||||||
@@ -223,7 +246,9 @@ export default function PathNavigator({
|
|||||||
}, [processes, MaterialRef, boxVisible, scene, id, baseMaterials]);
|
}, [processes, MaterialRef, boxVisible, scene, id, baseMaterials]);
|
||||||
|
|
||||||
useFrame((_, delta) => {
|
useFrame((_, delta) => {
|
||||||
const currentAgv = (agvRef.current || []).find((agv: AGVData) => agv.vehicleId === id);
|
const currentAgv = (agvRef.current || []).find(
|
||||||
|
(agv: AGVData) => agv.vehicleId === id
|
||||||
|
);
|
||||||
|
|
||||||
if (!scene || !id || !isPlaying) return;
|
if (!scene || !id || !isPlaying) return;
|
||||||
|
|
||||||
@@ -243,6 +268,7 @@ export default function PathNavigator({
|
|||||||
const isAgvReady = () => {
|
const isAgvReady = () => {
|
||||||
if (!agvRef.current || agvRef.current.length === 0) return false;
|
if (!agvRef.current || agvRef.current.length === 0) return false;
|
||||||
if (!currentAgv) return false;
|
if (!currentAgv) return false;
|
||||||
|
|
||||||
return currentAgv.isActive && hitCount >= currentAgv.maxHitCount;
|
return currentAgv.isActive && hitCount >= currentAgv.maxHitCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -266,12 +292,19 @@ export default function PathNavigator({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isPlaying && currentPhase === "initial" && !hasReachedPickup.current) {
|
if (isPlaying && currentPhase === "initial" && !hasReachedPickup.current) {
|
||||||
const reached = moveAlongPath(object, path, distancesRef.current, speed, delta, progressRef);
|
const reached = moveAlongPath(
|
||||||
|
object,
|
||||||
|
path,
|
||||||
|
distancesRef.current,
|
||||||
|
speed,
|
||||||
|
delta,
|
||||||
|
progressRef
|
||||||
|
);
|
||||||
|
|
||||||
if (reached) {
|
if (reached) {
|
||||||
hasReachedPickup.current = true;
|
hasReachedPickup.current = true;
|
||||||
if (currentAgv) {
|
if (currentAgv) {
|
||||||
currentAgv.status = 'picking';
|
currentAgv.status = "picking";
|
||||||
}
|
}
|
||||||
logAgvStatus(id, "Reached pickup point, Waiting for material");
|
logAgvStatus(id, "Reached pickup point, Waiting for material");
|
||||||
}
|
}
|
||||||
@@ -287,20 +320,28 @@ export default function PathNavigator({
|
|||||||
progressRef.current = 0;
|
progressRef.current = 0;
|
||||||
logAgvStatus(id, "Started from pickup point, heading to drop point");
|
logAgvStatus(id, "Started from pickup point, heading to drop point");
|
||||||
if (currentAgv) {
|
if (currentAgv) {
|
||||||
currentAgv.status = 'toDrop';
|
currentAgv.status = "toDrop";
|
||||||
}
|
}
|
||||||
}, 0)
|
}, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPlaying && currentPhase === "toDrop") {
|
if (isPlaying && currentPhase === "toDrop") {
|
||||||
const reached = moveAlongPath(object, path, distancesRef.current, speed, delta, progressRef);
|
const reached = moveAlongPath(
|
||||||
|
object,
|
||||||
|
path,
|
||||||
|
distancesRef.current,
|
||||||
|
speed,
|
||||||
|
delta,
|
||||||
|
progressRef
|
||||||
|
);
|
||||||
|
|
||||||
if (reached && !isWaiting.current) {
|
if (reached && !isWaiting.current) {
|
||||||
isWaiting.current = true;
|
isWaiting.current = true;
|
||||||
logAgvStatus(id, "Reached drop point");
|
logAgvStatus(id, "Reached drop point");
|
||||||
if (currentAgv) {
|
if (currentAgv) {
|
||||||
currentAgv.status = 'droping';
|
currentAgv.status = "droping";
|
||||||
|
currentAgv.hitCount = currentAgv.hitCount--;
|
||||||
}
|
}
|
||||||
timeoutRef.current = setTimeout(() => {
|
timeoutRef.current = setTimeout(() => {
|
||||||
setPath([...dropPickupPath]);
|
setPath([...dropPickupPath]);
|
||||||
@@ -309,16 +350,26 @@ export default function PathNavigator({
|
|||||||
isWaiting.current = false;
|
isWaiting.current = false;
|
||||||
setBoxVisible(false);
|
setBoxVisible(false);
|
||||||
if (currentAgv) {
|
if (currentAgv) {
|
||||||
currentAgv.status = 'toPickup';
|
currentAgv.status = "toPickup";
|
||||||
}
|
}
|
||||||
logAgvStatus(id, "Started from droping point, heading to pickup point");
|
logAgvStatus(
|
||||||
|
id,
|
||||||
|
"Started from droping point, heading to pickup point"
|
||||||
|
);
|
||||||
}, bufferTime * 1000);
|
}, bufferTime * 1000);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPlaying && currentPhase === "toPickup") {
|
if (isPlaying && currentPhase === "toPickup") {
|
||||||
const reached = moveAlongPath(object, path, distancesRef.current, speed, delta, progressRef);
|
const reached = moveAlongPath(
|
||||||
|
object,
|
||||||
|
path,
|
||||||
|
distancesRef.current,
|
||||||
|
speed,
|
||||||
|
delta,
|
||||||
|
progressRef
|
||||||
|
);
|
||||||
|
|
||||||
if (reached) {
|
if (reached) {
|
||||||
if (currentAgv) {
|
if (currentAgv) {
|
||||||
@@ -326,14 +377,21 @@ export default function PathNavigator({
|
|||||||
}
|
}
|
||||||
setCurrentPhase("initial");
|
setCurrentPhase("initial");
|
||||||
if (currentAgv) {
|
if (currentAgv) {
|
||||||
currentAgv.status = 'picking';
|
currentAgv.status = "picking";
|
||||||
}
|
}
|
||||||
logAgvStatus(id, "Reached pickup point again, cycle complete");
|
logAgvStatus(id, "Reached pickup point again, cycle complete");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
moveAlongPath(object, path, distancesRef.current, speed, delta, progressRef);
|
moveAlongPath(
|
||||||
|
object,
|
||||||
|
path,
|
||||||
|
distancesRef.current,
|
||||||
|
speed,
|
||||||
|
delta,
|
||||||
|
progressRef
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
function moveAlongPath(
|
function moveAlongPath(
|
||||||
@@ -379,7 +437,11 @@ export default function PathNavigator({
|
|||||||
const targetRotationY = Math.atan2(targetDirection.x, targetDirection.z);
|
const targetRotationY = Math.atan2(targetDirection.x, targetDirection.z);
|
||||||
|
|
||||||
const rotationSpeed = Math.min(5 * delta, 1);
|
const rotationSpeed = Math.min(5 * delta, 1);
|
||||||
object.rotation.y = THREE.MathUtils.lerp(object.rotation.y, targetRotationY, rotationSpeed);
|
object.rotation.y = THREE.MathUtils.lerp(
|
||||||
|
object.rotation.y,
|
||||||
|
targetRotationY,
|
||||||
|
rotationSpeed
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -394,7 +456,7 @@ export default function PathNavigator({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group name="path-navigator-lines" visible={!isPlaying}>
|
<group name="path-navigator-lines">
|
||||||
{toPickupPath.length > 0 && (
|
{toPickupPath.length > 0 && (
|
||||||
<Line
|
<Line
|
||||||
points={toPickupPath}
|
points={toPickupPath}
|
||||||
|
|||||||
@@ -199,14 +199,12 @@ const SelectionControls: React.FC = () => {
|
|||||||
setDuplicatedObjects([]);
|
setDuplicatedObjects([]);
|
||||||
setSelectedAssets([]);
|
setSelectedAssets([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateBackend = async (updatedPaths: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => {
|
const updateBackend = async (updatedPaths: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => {
|
||||||
if (updatedPaths.length === 0) return;
|
if (updatedPaths.length === 0) return;
|
||||||
const email = localStorage.getItem("email");
|
const email = localStorage.getItem("email");
|
||||||
const organization = email ? email.split("@")[1].split(".")[0] : "";
|
const organization = email ? email.split("@")[1].split(".")[0] : "";
|
||||||
|
|
||||||
updatedPaths.forEach(async (updatedPath) => {
|
updatedPaths.forEach(async (updatedPath) => {
|
||||||
|
|
||||||
if (updatedPath.type === "Conveyor") {
|
if (updatedPath.type === "Conveyor") {
|
||||||
// await setEventApi(
|
// await setEventApi(
|
||||||
// organization,
|
// organization,
|
||||||
@@ -225,9 +223,7 @@ const SelectionControls: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
socket.emit("v2:model-asset:updateEventData", data);
|
socket.emit("v2:model-asset:updateEventData", data);
|
||||||
|
|
||||||
} else if (updatedPath.type === "Vehicle") {
|
} else if (updatedPath.type === "Vehicle") {
|
||||||
|
|
||||||
// await setEventApi(
|
// await setEventApi(
|
||||||
// organization,
|
// organization,
|
||||||
// updatedPath.modeluuid,
|
// updatedPath.modeluuid,
|
||||||
@@ -241,9 +237,7 @@ const SelectionControls: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
socket.emit("v2:model-asset:updateEventData", data);
|
socket.emit("v2:model-asset:updateEventData", data);
|
||||||
|
|
||||||
} else if (updatedPath.type === "StaticMachine") {
|
} else if (updatedPath.type === "StaticMachine") {
|
||||||
|
|
||||||
// await setEventApi(
|
// await setEventApi(
|
||||||
// organization,
|
// organization,
|
||||||
// updatedPath.modeluuid,
|
// updatedPath.modeluuid,
|
||||||
@@ -257,9 +251,7 @@ const SelectionControls: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
socket.emit("v2:model-asset:updateEventData", data);
|
socket.emit("v2:model-asset:updateEventData", data);
|
||||||
|
|
||||||
} else if (updatedPath.type === "ArmBot") {
|
} else if (updatedPath.type === "ArmBot") {
|
||||||
|
|
||||||
// await setEventApi(
|
// await setEventApi(
|
||||||
// organization,
|
// organization,
|
||||||
// updatedPath.modeluuid,
|
// updatedPath.modeluuid,
|
||||||
@@ -274,239 +266,135 @@ const SelectionControls: React.FC = () => {
|
|||||||
|
|
||||||
socket.emit("v2:model-asset:updateEventData", data);
|
socket.emit("v2:model-asset:updateEventData", data);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// const removeConnection = (modelUUID: any) => {
|
const removeConnections = (deletedModelUUIDs: string[]) => {
|
||||||
//
|
|
||||||
// const removedPath = simulationStates?.flatMap((state) => {
|
|
||||||
// let shouldInclude = false;
|
|
||||||
|
|
||||||
// if (state.type === "Conveyor") {
|
const deletedPointUUIDs = new Set<string>();
|
||||||
// state.points.forEach((point: any) => {
|
simulationStates.forEach(state => {
|
||||||
// const sourceMatch =
|
if (deletedModelUUIDs.includes(state.modeluuid)) {
|
||||||
// point.connections?.source?.modelUUID === modelUUID;
|
if (state.type === "Conveyor" && state.points) {
|
||||||
// const targetMatch = point.connections?.targets?.some(
|
state.points.forEach(point => {
|
||||||
// (target: any) => target.modelUUID === modelUUID
|
deletedPointUUIDs.add(point.uuid);
|
||||||
// );
|
});
|
||||||
|
} else if (state.points && 'uuid' in state.points) {
|
||||||
// if (sourceMatch || targetMatch) shouldInclude = true;
|
deletedPointUUIDs.add(state.points.uuid);
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (state.type === "Vehicle") {
|
|
||||||
// const targetMatch = state.points.connections?.targets?.some(
|
|
||||||
// (target: any) => target.modelUUID === modelUUID
|
|
||||||
// );
|
|
||||||
|
|
||||||
// if (targetMatch) shouldInclude = true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (state.type === "StaticMachine") {
|
|
||||||
// const targetMatch = state.points.connections?.targets?.some(
|
|
||||||
// (target: any) => target.modelUUID === modelUUID
|
|
||||||
// );
|
|
||||||
|
|
||||||
// if (targetMatch) shouldInclude = true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (state.type === "ArmBot") {
|
|
||||||
// const sourceMatch =
|
|
||||||
// state.points.connections?.source?.modelUUID === modelUUID;
|
|
||||||
// const targetMatch = state.points.connections?.targets?.some(
|
|
||||||
// (target: any) => target.modelUUID === modelUUID
|
|
||||||
// );
|
|
||||||
|
|
||||||
// const processMatch =
|
|
||||||
// state.points.actions?.processes?.some(
|
|
||||||
// (process: any) =>
|
|
||||||
// process.startPoint === modelUUID || process.endPoint === modelUUID
|
|
||||||
// ) ?? false;
|
|
||||||
|
|
||||||
// if (sourceMatch || targetMatch || processMatch) shouldInclude = true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return shouldInclude ? [state] : [];
|
|
||||||
// });
|
|
||||||
// updateBackend(removedPath);
|
|
||||||
//
|
|
||||||
// return removedPath;
|
|
||||||
// // updateBackend(updatedPaths);
|
|
||||||
|
|
||||||
// // setSimulationStates(updatedStates);
|
|
||||||
// };
|
|
||||||
// const removeConnection = (modelUUIDs: any[]) => {
|
|
||||||
//
|
|
||||||
// const removedPath = simulationStates?.flatMap((state) => {
|
|
||||||
// let shouldInclude = false;
|
|
||||||
|
|
||||||
// if (state.type === "Conveyor") {
|
|
||||||
// state.points.forEach((point: any) => {
|
|
||||||
// const sourceMatch = modelUUIDs.includes(
|
|
||||||
// point.connections?.source?.modelUUID
|
|
||||||
// );
|
|
||||||
// const targetMatch = point.connections?.targets?.some((target: any) =>
|
|
||||||
// modelUUIDs.includes(target.modelUUID)
|
|
||||||
// );
|
|
||||||
|
|
||||||
// if (sourceMatch || targetMatch) shouldInclude = true;
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (state.type === "Vehicle") {
|
|
||||||
// const targetMatch = state.points.connections?.targets?.some(
|
|
||||||
// (target: any) => modelUUIDs.includes(target.modelUUID)
|
|
||||||
// );
|
|
||||||
|
|
||||||
// if (targetMatch) shouldInclude = true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (state.type === "StaticMachine") {
|
|
||||||
// const targetMatch = state.points.connections?.targets?.some(
|
|
||||||
// (target: any) => modelUUIDs.includes(target.modelUUID)
|
|
||||||
// );
|
|
||||||
|
|
||||||
// if (targetMatch) shouldInclude = true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (state.type === "ArmBot") {
|
|
||||||
// const sourceMatch = modelUUIDs.includes(
|
|
||||||
// state.points.connections?.source?.modelUUID
|
|
||||||
// );
|
|
||||||
// const targetMatch = state.points.connections?.targets?.some(
|
|
||||||
// (target: any) => modelUUIDs.includes(target.modelUUID)
|
|
||||||
// );
|
|
||||||
|
|
||||||
// const processMatch =
|
|
||||||
// state.points.actions?.processes?.some(
|
|
||||||
// (process: any) =>
|
|
||||||
// modelUUIDs.includes(process.startPoint) ||
|
|
||||||
// modelUUIDs.includes(process.endPoint)
|
|
||||||
// ) ?? false;
|
|
||||||
|
|
||||||
// if (sourceMatch || targetMatch || processMatch) shouldInclude = true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return shouldInclude ? [state] : [];
|
|
||||||
// });
|
|
||||||
// updateBackend(removedPath);
|
|
||||||
//
|
|
||||||
// return removedPath;
|
|
||||||
// };
|
|
||||||
|
|
||||||
const removeConnection = (modelUUIDs: any[]) => {
|
|
||||||
const removedPath = simulationStates?.flatMap((state: any) => {
|
|
||||||
let shouldInclude = false;
|
|
||||||
|
|
||||||
// Conveyor type
|
|
||||||
if (state.type === "Conveyor") {
|
|
||||||
state.points.forEach((point: any) => {
|
|
||||||
const sourceMatch = modelUUIDs.includes(point.connections?.source?.modelUUID);
|
|
||||||
const targetMatch = point.connections?.targets?.some((target: any) => modelUUIDs.includes(target.modelUUID));
|
|
||||||
|
|
||||||
if (sourceMatch) {
|
|
||||||
point.connections.source = {};
|
|
||||||
shouldInclude = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetMatch) {
|
|
||||||
point.connections.targets = [];
|
|
||||||
shouldInclude = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vehicle & StaticMachine types
|
|
||||||
if (state.type === "Vehicle") {
|
|
||||||
const targets = state.points?.connections?.targets || [];
|
|
||||||
const targetMatch = targets.some((target: any) => modelUUIDs.includes(target.modelUUID));
|
|
||||||
|
|
||||||
if (targetMatch) {
|
|
||||||
state.points.connections.targets = [];
|
|
||||||
shouldInclude = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state.type === "StaticMachine") {
|
|
||||||
const targets = state.points?.connections?.targets || [];
|
|
||||||
const targetMatch = targets.some((target: any) => modelUUIDs.includes(target.modelUUID));
|
|
||||||
|
|
||||||
if (targetMatch) {
|
|
||||||
state.points.connections.targets = [];
|
|
||||||
shouldInclude = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArmBot type
|
|
||||||
if (state.type === "ArmBot") {
|
|
||||||
const sourceMatch = modelUUIDs.includes(state.points.connections?.source?.modelUUID);
|
|
||||||
|
|
||||||
const targetMatch = state.points.connections?.targets?.some(
|
|
||||||
(target: any) => modelUUIDs.includes(target.modelUUID)
|
|
||||||
);
|
|
||||||
// state.points.actions.processes = state.points.actions.processes.filter(
|
|
||||||
// (process: any) =>
|
|
||||||
// console.log(
|
|
||||||
// !modelUUIDs.includes(process.startPoint),
|
|
||||||
// !modelUUIDs.includes(process.endPoint),
|
|
||||||
// modelUUIDs,
|
|
||||||
// process.startPoint,
|
|
||||||
// process.endPoint
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
|
|
||||||
// shouldInclude = true;
|
|
||||||
|
|
||||||
// const processMatches = state.points.actions?.processes?.some(
|
|
||||||
// (process: any) =>
|
|
||||||
// console.log(
|
|
||||||
// "process: ",
|
|
||||||
// process,
|
|
||||||
// modelUUIDs,
|
|
||||||
// process.startPoint,
|
|
||||||
// process.endPoint
|
|
||||||
// )
|
|
||||||
// // modelUUIDs.includes(process.startPoint) ||
|
|
||||||
// // modelUUIDs.includes(process.endPoint)
|
|
||||||
// );
|
|
||||||
// const processMatch = state.points.actions?.processes?.some(
|
|
||||||
// (process: any) =>
|
|
||||||
// modelUUIDs.includes(String(process.startPoint)) ||
|
|
||||||
// modelUUIDs.includes(String(process.endPoint))
|
|
||||||
// );
|
|
||||||
|
|
||||||
// console.log("processMatch: ", processMatch);
|
|
||||||
if (sourceMatch) {
|
|
||||||
state.points.connections.source = {};
|
|
||||||
shouldInclude = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetMatch) {
|
|
||||||
state.points.connections.targets =
|
|
||||||
state.points.connections.targets.filter((target: any) => !modelUUIDs.includes(target.modelUUID));
|
|
||||||
shouldInclude = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log("processMatch: ", processMatch);
|
|
||||||
// if (processMatch) {
|
|
||||||
// state.points.actions.processes =
|
|
||||||
// state.points.actions.processes.filter((process: any) => {
|
|
||||||
// const shouldRemove =
|
|
||||||
// modelUUIDs.includes(process.startPoint) ||
|
|
||||||
// modelUUIDs.includes(process.endPoint);
|
|
||||||
|
|
||||||
// console.log("shouldRemove: ", shouldRemove);
|
|
||||||
// return !shouldRemove;
|
|
||||||
// });
|
|
||||||
// shouldInclude = true;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
return shouldInclude ? [state] : [];
|
|
||||||
});
|
});
|
||||||
|
|
||||||
updateBackend(removedPath);
|
const updatedStates = simulationStates.map((state) => {
|
||||||
return removedPath;
|
// Handle Conveyor
|
||||||
|
if (state.type === "Conveyor") {
|
||||||
|
const updatedConveyor: SimulationTypes.ConveyorEventsSchema = {
|
||||||
|
...state,
|
||||||
|
points: state.points.map((point) => {
|
||||||
|
return {
|
||||||
|
...point,
|
||||||
|
connections: {
|
||||||
|
...point.connections,
|
||||||
|
targets: point.connections.targets.filter(
|
||||||
|
(target) => !deletedModelUUIDs.includes(target.modelUUID)
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
return updatedConveyor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Vehicle
|
||||||
|
else if (state.type === "Vehicle") {
|
||||||
|
const updatedVehicle: SimulationTypes.VehicleEventsSchema = {
|
||||||
|
...state,
|
||||||
|
points: {
|
||||||
|
...state.points,
|
||||||
|
connections: {
|
||||||
|
...state.points.connections,
|
||||||
|
targets: state.points.connections.targets.filter(
|
||||||
|
(target) => !deletedModelUUIDs.includes(target.modelUUID)
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return updatedVehicle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle StaticMachine
|
||||||
|
else if (state.type === "StaticMachine") {
|
||||||
|
const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema =
|
||||||
|
{
|
||||||
|
...state,
|
||||||
|
points: {
|
||||||
|
...state.points,
|
||||||
|
connections: {
|
||||||
|
...state.points.connections,
|
||||||
|
targets: state.points.connections.targets.filter(
|
||||||
|
(target) => !deletedModelUUIDs.includes(target.modelUUID)
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return updatedStaticMachine;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle ArmBot
|
||||||
|
else if (state.type === "ArmBot") {
|
||||||
|
const updatedArmBot: SimulationTypes.ArmBotEventsSchema = {
|
||||||
|
...state,
|
||||||
|
points: {
|
||||||
|
...state.points,
|
||||||
|
connections: {
|
||||||
|
...state.points.connections,
|
||||||
|
targets: state.points.connections.targets.filter(
|
||||||
|
(target: any) => !deletedModelUUIDs.includes(target.modelUUID)
|
||||||
|
),
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
...state.points.actions,
|
||||||
|
processes: state.points.actions.processes?.filter((process) => {
|
||||||
|
// Check if trigger is from deleted model
|
||||||
|
const matchedStates = simulationStates.filter((s) => deletedModelUUIDs.includes(s.modeluuid));
|
||||||
|
|
||||||
|
if (matchedStates.length > 0) {
|
||||||
|
if (matchedStates[0]?.type === "StaticMachine") {
|
||||||
|
const trigPoints = matchedStates[0]?.points;
|
||||||
|
if (process.triggerId === trigPoints?.triggers?.uuid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (matchedStates[0]?.type === "Conveyor") {
|
||||||
|
const trigPoints = matchedStates[0]?.points;
|
||||||
|
if (Array.isArray(trigPoints)) {
|
||||||
|
const nonEmptyTriggers = trigPoints.filter((point) => point && point.triggers && point.triggers.length > 0);
|
||||||
|
const allTriggerUUIDs = nonEmptyTriggers.flatMap((point) => point.triggers).map((trigger) => trigger.uuid);
|
||||||
|
if (allTriggerUUIDs.includes(process.triggerId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if startPoint or endPoint is from deleted model
|
||||||
|
if (deletedPointUUIDs.has(process.startPoint) || deletedPointUUIDs.has(process.endPoint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return updatedArmBot;
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
});
|
||||||
|
|
||||||
|
const filteredStates = updatedStates.filter((state) => !deletedModelUUIDs.includes(state.modeluuid));
|
||||||
|
|
||||||
|
updateBackend(filteredStates);
|
||||||
|
setSimulationStates(filteredStates);
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteSelection = () => {
|
const deleteSelection = () => {
|
||||||
@@ -552,65 +440,17 @@ const SelectionControls: React.FC = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setSimulationStates((prevEvents: (| SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => {
|
setSimulationStates((prevEvents: (| SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => {
|
||||||
const updatedEvents = (prevEvents || []).filter(
|
const updatedEvents = (prevEvents || []).filter((event) => event.modeluuid !== selectedMesh.uuid);
|
||||||
(event) => event.modeluuid !== selectedMesh.uuid
|
|
||||||
);
|
|
||||||
return updatedEvents;
|
return updatedEvents;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
itemsGroupRef.current?.remove(selectedMesh);
|
itemsGroupRef.current?.remove(selectedMesh);
|
||||||
});
|
});
|
||||||
|
|
||||||
const allUUIDs = selectedAssets.map((val: any) => val.uuid);
|
const allUUIDs = selectedAssets.map((val: any) => val.uuid);
|
||||||
removeConnection(allUUIDs);
|
removeConnections(allUUIDs);
|
||||||
|
|
||||||
// const removedPath = simulationStates?.flatMap((path: any) => {
|
const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid));
|
||||||
// let shouldInclude = false;
|
|
||||||
|
|
||||||
// if (Array.isArray(path.points)) {
|
|
||||||
// path.points.forEach((point: any) => {
|
|
||||||
// const sourceMatch =
|
|
||||||
// point.connections?.source?.modelUUID === selectedAssets[0].uuid;
|
|
||||||
// const targetMatch = point.connections?.targets?.some(
|
|
||||||
// (target: any) => target.modelUUID === selectedAssets[0].uuid
|
|
||||||
// );
|
|
||||||
|
|
||||||
// if (sourceMatch) {
|
|
||||||
// point.connections.source = {};
|
|
||||||
// shouldInclude = true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (targetMatch) {
|
|
||||||
// point.connections.targets = [];
|
|
||||||
// shouldInclude = true;
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// } else {
|
|
||||||
// const sourceMatch =
|
|
||||||
// path.connections?.source?.modelUUID === selectedAssets[0].uuid;
|
|
||||||
// const targetMatch = path.connections?.targets?.some(
|
|
||||||
// (target: any) => target.modelUUID === selectedAssets[0].uuid
|
|
||||||
// );
|
|
||||||
|
|
||||||
// if (sourceMatch) {
|
|
||||||
// path.connections.source = {};
|
|
||||||
// shouldInclude = true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (targetMatch) {
|
|
||||||
// path.connections.targets = [];
|
|
||||||
// shouldInclude = true;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return shouldInclude ? [path] : [];
|
|
||||||
// });
|
|
||||||
// updateBackend(removedPath);
|
|
||||||
|
|
||||||
const updatedItems = floorItems.filter(
|
|
||||||
(item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid)
|
|
||||||
);
|
|
||||||
setFloorItems(updatedItems);
|
setFloorItems(updatedItems);
|
||||||
}
|
}
|
||||||
toast.success("Selected models removed!");
|
toast.success("Selected models removed!");
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import useModuleStore from "../../../store/useModuleStore";
|
|||||||
import { useSimulationStates } from "../../../store/store";
|
import { useSimulationStates } from "../../../store/store";
|
||||||
import * as SimulationTypes from '../../../types/simulationTypes';
|
import * as SimulationTypes from '../../../types/simulationTypes';
|
||||||
import { ArmbotInstances } from "./ArmBotInstances";
|
import { ArmbotInstances } from "./ArmBotInstances";
|
||||||
|
import { useResetButtonStore } from "../../../store/usePlayButtonStore";
|
||||||
|
|
||||||
interface ArmBotState {
|
interface ArmBotState {
|
||||||
uuid: string;
|
uuid: string;
|
||||||
@@ -12,7 +13,12 @@ interface ArmBotState {
|
|||||||
status: string;
|
status: string;
|
||||||
material: string;
|
material: string;
|
||||||
triggerId: string;
|
triggerId: string;
|
||||||
|
connections: {
|
||||||
|
source: { modelUUID: string; pointUUID: string };
|
||||||
|
targets: { modelUUID: string; pointUUID: string }[];
|
||||||
|
};
|
||||||
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
|
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
|
||||||
|
isActive?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StaticMachineState {
|
interface StaticMachineState {
|
||||||
@@ -33,6 +39,7 @@ const ArmBot = ({ armBots, setArmBots, setStaticMachines }: ArmBotProps) => {
|
|||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
const { scene } = useThree();
|
const { scene } = useThree();
|
||||||
const { simulationStates } = useSimulationStates();
|
const { simulationStates } = useSimulationStates();
|
||||||
|
const { isReset } = useResetButtonStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const filtered = simulationStates.filter((s): s is SimulationTypes.ArmBotEventsSchema => s.type === "ArmBot");
|
const filtered = simulationStates.filter((s): s is SimulationTypes.ArmBotEventsSchema => s.type === "ArmBot");
|
||||||
@@ -45,10 +52,12 @@ const ArmBot = ({ armBots, setArmBots, setStaticMachines }: ArmBotProps) => {
|
|||||||
status: "idle",
|
status: "idle",
|
||||||
material: "default",
|
material: "default",
|
||||||
triggerId: '',
|
triggerId: '',
|
||||||
actions: bot.points.actions
|
actions: bot.points.actions,
|
||||||
|
connections: bot.points.connections,
|
||||||
|
isActive: false
|
||||||
}));
|
}));
|
||||||
setArmBots(initialStates);
|
setArmBots(initialStates);
|
||||||
}, [simulationStates]);
|
}, [simulationStates, isReset]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
armBots.forEach((bot) => {
|
armBots.forEach((bot) => {
|
||||||
|
|||||||
@@ -18,16 +18,12 @@ interface ArmBotState {
|
|||||||
status: string;
|
status: string;
|
||||||
material: string;
|
material: string;
|
||||||
triggerId: string;
|
triggerId: string;
|
||||||
actions: {
|
connections: {
|
||||||
uuid: string;
|
source: { modelUUID: string; pointUUID: string };
|
||||||
name: string;
|
targets: { modelUUID: string; pointUUID: string }[];
|
||||||
speed: number;
|
|
||||||
processes: {
|
|
||||||
triggerId: string;
|
|
||||||
startPoint: string;
|
|
||||||
endPoint: string;
|
|
||||||
}[];
|
|
||||||
};
|
};
|
||||||
|
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
|
||||||
|
isActive?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StaticMachineState {
|
interface StaticMachineState {
|
||||||
@@ -50,7 +46,6 @@ export const ArmbotInstances: React.FC<ArmbotInstancesProps> = ({ index, armBot,
|
|||||||
const [processes, setProcesses] = useState<Process[]>([]);
|
const [processes, setProcesses] = useState<Process[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
if (armBot.actions.processes.length > 0) {
|
if (armBot.actions.processes.length > 0) {
|
||||||
const mappedProcesses = armBot.actions.processes.map((process) => {
|
const mappedProcesses = armBot.actions.processes.map((process) => {
|
||||||
return {
|
return {
|
||||||
@@ -87,6 +82,7 @@ export const ArmbotInstances: React.FC<ArmbotInstancesProps> = ({ index, armBot,
|
|||||||
rotation={armBot.rotation}
|
rotation={armBot.rotation}
|
||||||
processes={processes}
|
processes={processes}
|
||||||
armBot={armBot}
|
armBot={armBot}
|
||||||
|
setArmBots={setArmBots}
|
||||||
setStaticMachines={setStaticMachines}
|
setStaticMachines={setStaticMachines}
|
||||||
updateArmBotStatus={updateArmBotStatus}
|
updateArmBotStatus={updateArmBotStatus}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { useEffect, useMemo, useState, useRef } from "react";
|
import { useEffect, useMemo, useState, useRef } from "react";
|
||||||
import { useFrame } from "@react-three/fiber";
|
import { useFrame } from "@react-three/fiber";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
import { usePlayButtonStore, useResetButtonStore } from "../../../store/usePlayButtonStore";
|
||||||
import { useSimulationStates } from "../../../store/store";
|
import { useSimulationStates } from "../../../store/store";
|
||||||
|
import MaterialInstances from "./MaterialInstances";
|
||||||
|
import { Line } from "react-chartjs-2";
|
||||||
|
import { QuadraticBezierLine } from "@react-three/drei";
|
||||||
|
|
||||||
|
|
||||||
interface StaticMachineState {
|
interface StaticMachineState {
|
||||||
@@ -20,21 +23,17 @@ interface ArmBotState {
|
|||||||
status: string;
|
status: string;
|
||||||
material: string;
|
material: string;
|
||||||
triggerId: string;
|
triggerId: string;
|
||||||
actions: {
|
connections: {
|
||||||
uuid: string;
|
source: { modelUUID: string; pointUUID: string };
|
||||||
name: string;
|
targets: { modelUUID: string; pointUUID: string }[];
|
||||||
speed: number;
|
|
||||||
processes: {
|
|
||||||
triggerId: string;
|
|
||||||
startPoint: string;
|
|
||||||
endPoint: string;
|
|
||||||
}[];
|
|
||||||
};
|
};
|
||||||
|
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
|
||||||
|
isActive?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type IKAnimationControllerProps = {
|
type IKAnimationControllerProps = {
|
||||||
ikSolver: any;
|
ikSolver: any;
|
||||||
process: {
|
processes: {
|
||||||
triggerId: string;
|
triggerId: string;
|
||||||
startPoint: THREE.Vector3;
|
startPoint: THREE.Vector3;
|
||||||
endPoint: THREE.Vector3;
|
endPoint: THREE.Vector3;
|
||||||
@@ -46,19 +45,21 @@ type IKAnimationControllerProps = {
|
|||||||
logStatus: (status: string) => void;
|
logStatus: (status: string) => void;
|
||||||
groupRef: React.RefObject<THREE.Group>;
|
groupRef: React.RefObject<THREE.Group>;
|
||||||
armBot: ArmBotState;
|
armBot: ArmBotState;
|
||||||
|
setArmBots: React.Dispatch<React.SetStateAction<ArmBotState[]>>;
|
||||||
setStaticMachines: React.Dispatch<React.SetStateAction<StaticMachineState[]>>;
|
setStaticMachines: React.Dispatch<React.SetStateAction<StaticMachineState[]>>;
|
||||||
updateArmBotStatus: (status: string) => void;
|
updateArmBotStatus: (status: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const IKAnimationController = ({
|
const IKAnimationController = ({
|
||||||
ikSolver,
|
ikSolver,
|
||||||
process,
|
processes,
|
||||||
selectedTrigger,
|
selectedTrigger,
|
||||||
targetBoneName,
|
targetBoneName,
|
||||||
uuid,
|
uuid,
|
||||||
logStatus,
|
logStatus,
|
||||||
groupRef,
|
groupRef,
|
||||||
armBot,
|
armBot,
|
||||||
|
setArmBots,
|
||||||
setStaticMachines,
|
setStaticMachines,
|
||||||
updateArmBotStatus
|
updateArmBotStatus
|
||||||
}: IKAnimationControllerProps) => {
|
}: IKAnimationControllerProps) => {
|
||||||
@@ -68,19 +69,10 @@ const IKAnimationController = ({
|
|||||||
const [isInitializing, setIsInitializing] = useState(true);
|
const [isInitializing, setIsInitializing] = useState(true);
|
||||||
const restSpeed = 0.1;
|
const restSpeed = 0.1;
|
||||||
const restPosition = new THREE.Vector3(0, 2, 1.6);
|
const restPosition = new THREE.Vector3(0, 2, 1.6);
|
||||||
const { isPlaying } = usePlayButtonStore();
|
const { isPlaying } = usePlayButtonStore();;
|
||||||
|
const statusRef = useRef("idle");
|
||||||
const { simulationStates } = useSimulationStates();
|
const { simulationStates } = useSimulationStates();
|
||||||
|
const { isReset } = useResetButtonStore();
|
||||||
// Track previous states for comparison
|
|
||||||
const prevStateRef = useRef({
|
|
||||||
isInitializing: true,
|
|
||||||
needsInitialMovement: true,
|
|
||||||
selectedTrigger: "",
|
|
||||||
progress: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
// Track previous status for comparison
|
|
||||||
const prevStatusRef = useRef("");
|
|
||||||
|
|
||||||
const initialCurveRef = useRef<THREE.CatmullRomCurve3 | null>(null);
|
const initialCurveRef = useRef<THREE.CatmullRomCurve3 | null>(null);
|
||||||
const initialStartPositionRef = useRef<THREE.Vector3 | null>(null);
|
const initialStartPositionRef = useRef<THREE.Vector3 | null>(null);
|
||||||
@@ -89,6 +81,13 @@ const IKAnimationController = ({
|
|||||||
setProgress(0);
|
setProgress(0);
|
||||||
}, [selectedTrigger]);
|
}, [selectedTrigger]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setProgress(0);
|
||||||
|
setNeedsInitialMovement(true);
|
||||||
|
setInitialProgress(0);
|
||||||
|
setIsInitializing(true);
|
||||||
|
}, [isReset]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (ikSolver) {
|
if (ikSolver) {
|
||||||
const targetBone = ikSolver.mesh.skeleton.bones.find(
|
const targetBone = ikSolver.mesh.skeleton.bones.find(
|
||||||
@@ -102,32 +101,6 @@ const IKAnimationController = ({
|
|||||||
}
|
}
|
||||||
}, [ikSolver]);
|
}, [ikSolver]);
|
||||||
|
|
||||||
// Log state changes
|
|
||||||
useEffect(() => {
|
|
||||||
const prev = prevStateRef.current;
|
|
||||||
|
|
||||||
if (prev.isInitializing !== isInitializing) {
|
|
||||||
if (!isInitializing) {
|
|
||||||
logStatus(`[Arm ${uuid}] Completed initialization, now at rest position`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prev.needsInitialMovement !== needsInitialMovement && !needsInitialMovement) {
|
|
||||||
logStatus(`[Arm ${uuid}] Reached rest position, ready for animation`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prev.selectedTrigger !== selectedTrigger) {
|
|
||||||
logStatus(`[Arm ${uuid}] Processing new trigger: ${selectedTrigger}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update previous state
|
|
||||||
prevStateRef.current = {
|
|
||||||
isInitializing,
|
|
||||||
needsInitialMovement,
|
|
||||||
selectedTrigger,
|
|
||||||
progress
|
|
||||||
};
|
|
||||||
}, [isInitializing, needsInitialMovement, selectedTrigger, progress]);
|
|
||||||
|
|
||||||
const calculateInitialCurve = (startPosition: THREE.Vector3) => {
|
const calculateInitialCurve = (startPosition: THREE.Vector3) => {
|
||||||
const direction = new THREE.Vector3().subVectors(restPosition, startPosition);
|
const direction = new THREE.Vector3().subVectors(restPosition, startPosition);
|
||||||
@@ -154,56 +127,55 @@ const IKAnimationController = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const processedCurves = useMemo(() => {
|
const processedCurves = useMemo(() => {
|
||||||
if (isPlaying)
|
if (!isPlaying) return [];
|
||||||
return process.map((p) => {
|
|
||||||
const tempLift = 0.5;
|
|
||||||
const localStart = groupRef.current?.worldToLocal(p.startPoint.clone().add(new THREE.Vector3(0, tempLift, 0)));
|
|
||||||
const localEnd = groupRef.current?.worldToLocal(p.endPoint.clone().add(new THREE.Vector3(0, tempLift, 0)));
|
|
||||||
|
|
||||||
if (localStart && localEnd) {
|
return processes.map(process => {
|
||||||
|
const localStart = groupRef.current?.worldToLocal(process.startPoint.clone());
|
||||||
|
const localEnd = groupRef.current?.worldToLocal(process.endPoint.clone());
|
||||||
|
|
||||||
const mid = new THREE.Vector3(
|
if (!localStart || !localEnd) return null;
|
||||||
(localStart.x + localEnd.x) / 1,
|
|
||||||
Math.max(localStart.y, localEnd.y) + 0.8,
|
|
||||||
(localStart.z + localEnd.z) / 0.9
|
|
||||||
);
|
|
||||||
|
|
||||||
const points = [
|
const midPoint = new THREE.Vector3(
|
||||||
restPosition.clone(),
|
(localStart.x + localEnd.x) / 2,
|
||||||
localStart.clone(),
|
Math.max(localStart.y, localEnd.y) + 1,
|
||||||
mid.clone(),
|
(localStart.z + localEnd.z) / 2
|
||||||
localEnd.clone(),
|
);
|
||||||
restPosition.clone(),
|
const restToStartCurve = new THREE.CatmullRomCurve3([
|
||||||
];
|
restPosition,
|
||||||
const curve = new THREE.CatmullRomCurve3(points);
|
new THREE.Vector3().lerpVectors(restPosition, localStart, 0.5),
|
||||||
const restToStartDist = points[0].distanceTo(points[1]);
|
localStart
|
||||||
const startToEndDist = points[1].distanceTo(points[3]);
|
]);
|
||||||
const endToRestDist = points[3].distanceTo(points[4]);
|
|
||||||
|
|
||||||
const totalDist = restToStartDist + startToEndDist + endToRestDist;
|
const processCurve = new THREE.CatmullRomCurve3([
|
||||||
const restToStartRange = [0, restToStartDist / totalDist];
|
localStart,
|
||||||
const startToEndRange = [
|
midPoint,
|
||||||
restToStartRange[1],
|
localEnd
|
||||||
restToStartRange[1] + startToEndDist / totalDist,
|
]);
|
||||||
];
|
|
||||||
const endToRestRange = [startToEndRange[1], 1];
|
|
||||||
|
|
||||||
return {
|
const endToRestCurve = new THREE.CatmullRomCurve3([
|
||||||
trigger: p.triggerId,
|
localEnd,
|
||||||
curve,
|
new THREE.Vector3().lerpVectors(localEnd, restPosition, 0.5),
|
||||||
speed: p.speed,
|
restPosition
|
||||||
restToStartRange,
|
]);
|
||||||
startToEndRange,
|
|
||||||
endToRestRange,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [process, groupRef, isPlaying]);
|
|
||||||
|
|
||||||
const activeCurve = useMemo(() => {
|
return {
|
||||||
if (isPlaying && processedCurves)
|
triggerId: process.triggerId,
|
||||||
return processedCurves.find((c) => c?.trigger === selectedTrigger);
|
restToStartCurve,
|
||||||
}, [processedCurves, selectedTrigger, isPlaying]);
|
processCurve,
|
||||||
|
endToRestCurve,
|
||||||
|
speed: process.speed,
|
||||||
|
totalDistance:
|
||||||
|
restPosition.distanceTo(localStart) +
|
||||||
|
localStart.distanceTo(localEnd) +
|
||||||
|
localEnd.distanceTo(restPosition)
|
||||||
|
};
|
||||||
|
}).filter(Boolean);
|
||||||
|
}, [processes, isPlaying]);
|
||||||
|
|
||||||
|
const activeProcess = useMemo(() => {
|
||||||
|
if (!selectedTrigger) return null;
|
||||||
|
return processedCurves.find(p => p?.triggerId === selectedTrigger);
|
||||||
|
}, [processedCurves, selectedTrigger]);
|
||||||
|
|
||||||
// Initial movement to rest position
|
// Initial movement to rest position
|
||||||
useFrame((_, delta) => {
|
useFrame((_, delta) => {
|
||||||
@@ -231,85 +203,177 @@ const IKAnimationController = ({
|
|||||||
|
|
||||||
// Main animation loop
|
// Main animation loop
|
||||||
useFrame((_, delta) => {
|
useFrame((_, delta) => {
|
||||||
if (!ikSolver || !activeCurve || isInitializing || !isPlaying) return;
|
if (isInitializing || !isPlaying || !selectedTrigger || !activeProcess || !ikSolver) return;
|
||||||
|
|
||||||
const { curve, speed, restToStartRange, startToEndRange, endToRestRange } = activeCurve;
|
const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBoneName);
|
||||||
const targetBone = ikSolver.mesh.skeleton.bones.find(
|
if (!bone) return;
|
||||||
(b: any) => b.name === targetBoneName
|
|
||||||
);
|
|
||||||
if (!targetBone) return;
|
|
||||||
|
|
||||||
let currentSpeed = restSpeed;
|
const {
|
||||||
let currentStatus = "idle"; // Default status
|
restToStartCurve,
|
||||||
|
processCurve,
|
||||||
|
endToRestCurve,
|
||||||
|
speed,
|
||||||
|
totalDistance
|
||||||
|
} = activeProcess;
|
||||||
|
|
||||||
// Determine current phase and status
|
// Calculate current segment and progress
|
||||||
if (progress < restToStartRange[1]) {
|
const restToStartDist = restPosition.distanceTo(restToStartCurve.points[2]);
|
||||||
currentSpeed = restSpeed;
|
const processDist = processCurve.getLength();
|
||||||
currentStatus = "moving"; // Moving to start point
|
const endToRestDist = endToRestCurve.getLength();
|
||||||
} else if (progress >= startToEndRange[0] && progress < startToEndRange[1]) {
|
|
||||||
currentSpeed = speed;
|
|
||||||
currentStatus = "moving"; // Moving between points
|
|
||||||
if (1 - progress < 0.05) {
|
|
||||||
// Find the process that matches the current trigger
|
|
||||||
const currentProcess = process.find(p => p.triggerId === selectedTrigger);
|
|
||||||
if (currentProcess) {
|
|
||||||
const triggerId = currentProcess.triggerId;
|
|
||||||
|
|
||||||
const endPoint = armBot.actions.processes.find((process) => process.triggerId === triggerId)?.endPoint;
|
const restToStartEnd = restToStartDist / totalDistance;
|
||||||
|
const processEnd = (restToStartDist + processDist) / totalDistance;
|
||||||
|
|
||||||
// Search simulationStates for a StaticMachine that has a point matching this endPointId
|
setProgress(prev => {
|
||||||
const matchedStaticMachine = simulationStates.find(
|
let currentStatus = statusRef.current;
|
||||||
(state) =>
|
let currentPosition: THREE.Vector3;
|
||||||
state.type === "StaticMachine" &&
|
const newProgress = Math.min(prev + delta * ((currentStatus === 'returning to rest') ? restSpeed : speed), 1);
|
||||||
state.points?.uuid === endPoint// check for static machine with matching point uuid
|
|
||||||
) as any;
|
|
||||||
|
|
||||||
if (matchedStaticMachine) {
|
if (newProgress < restToStartEnd) {
|
||||||
|
// Moving from rest to start position
|
||||||
|
currentStatus = "moving to start";
|
||||||
|
const segmentProgress = newProgress / restToStartEnd;
|
||||||
|
currentPosition = restToStartCurve.getPoint(segmentProgress);
|
||||||
|
} else if (newProgress < processEnd) {
|
||||||
|
// Processing - moving from start to end
|
||||||
|
currentStatus = "processing";
|
||||||
|
const segmentProgress = (newProgress - restToStartEnd) / (processEnd - restToStartEnd);
|
||||||
|
currentPosition = processCurve.getPoint(segmentProgress);
|
||||||
|
if (statusRef.current !== "processing") {
|
||||||
|
updateConveyorOrStaticMachineStatusOnStart(selectedTrigger);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Returning to rest position
|
||||||
|
currentStatus = "returning to rest";
|
||||||
|
const segmentProgress = (newProgress - processEnd) / (1 - processEnd);
|
||||||
|
currentPosition = endToRestCurve.getPoint(segmentProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update status if changed
|
||||||
|
if (currentStatus !== statusRef.current) {
|
||||||
|
statusRef.current = currentStatus;
|
||||||
|
// updateArmBotStatus(currentStatus);
|
||||||
|
logStatus(`[Arm ${uuid}] Status: ${currentStatus}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only trigger when the entire animation is complete (newProgress === 1)
|
||||||
|
if (newProgress === 1 && currentStatus === "returning to rest") {
|
||||||
|
updateConveyorOrStaticMachineStatusOnEnd(selectedTrigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
bone.position.copy(currentPosition);
|
||||||
|
ikSolver.update();
|
||||||
|
return newProgress;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateConveyorOrStaticMachineStatusOnStart = (selectedTrigger: string) => {
|
||||||
|
const currentProcess = processes.find(p => p.triggerId === selectedTrigger);
|
||||||
|
if (currentProcess) {
|
||||||
|
const triggerId = currentProcess.triggerId;
|
||||||
|
|
||||||
|
const startPoint = armBot.actions.processes.find((process) => process.triggerId === triggerId)?.startPoint;
|
||||||
|
|
||||||
|
const matchedMachine = simulationStates.find((state) => {
|
||||||
|
if (state.type === "Conveyor") {
|
||||||
|
return (state).points.some(
|
||||||
|
(point) => point.uuid === startPoint
|
||||||
|
);
|
||||||
|
} else if (state.type === "StaticMachine") {
|
||||||
|
return state.points.uuid === startPoint;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (matchedMachine) {
|
||||||
|
if (matchedMachine.type === "Conveyor") {
|
||||||
|
logStatus(`[Arm ${uuid}] start point which is a conveyor (${matchedMachine.modelName})`);
|
||||||
|
} else {
|
||||||
|
logStatus(`[Arm ${uuid}] started form start point which is a static machine (${matchedMachine.modelName})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (matchedMachine.type === "StaticMachine") {
|
||||||
|
updateArmBotStatus('dropping');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchedMachine.type === "Conveyor") {
|
||||||
|
updateArmBotStatus('picking');
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateConveyorOrStaticMachineStatusOnEnd = (selectedTrigger: string) => {
|
||||||
|
const currentProcess = processes.find(p => p.triggerId === selectedTrigger);
|
||||||
|
if (currentProcess) {
|
||||||
|
const triggerId = currentProcess.triggerId;
|
||||||
|
|
||||||
|
const endPoint = armBot.actions.processes.find((process) => process.triggerId === triggerId)?.endPoint;
|
||||||
|
|
||||||
|
const matchedMachine = simulationStates.find((state) => {
|
||||||
|
if (state.type === "Conveyor") {
|
||||||
|
return (state).points.some(
|
||||||
|
(point) => point.uuid === endPoint
|
||||||
|
);
|
||||||
|
} else if (state.type === "StaticMachine") {
|
||||||
|
return state.points.uuid === endPoint;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (matchedMachine) {
|
||||||
|
if (matchedMachine.type === "Conveyor") {
|
||||||
|
logStatus(`[Arm ${uuid}] Reached end point which is a conveyor (${matchedMachine.modelName})`);
|
||||||
|
} else {
|
||||||
|
logStatus(`[Arm ${uuid}] Reached end point which is a static machine (${matchedMachine.modelName})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (matchedMachine.type === "StaticMachine") {
|
||||||
setStaticMachines((machines) => {
|
setStaticMachines((machines) => {
|
||||||
return machines.map((machine) => {
|
return machines.map((machine) => {
|
||||||
if (machine.uuid === matchedStaticMachine.modeluuid) {
|
if (machine.uuid === matchedMachine.modeluuid) {
|
||||||
return { ...machine, status: "running" };
|
return { ...machine, status: "running" };
|
||||||
} else {
|
} else {
|
||||||
return machine;
|
return machine;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
updateArmBotStatus('idle');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
if (matchedMachine.type === "Conveyor") {
|
||||||
|
setArmBots((prev) =>
|
||||||
|
prev.map((arm) => {
|
||||||
|
if (arm.uuid === uuid && arm.isActive === true) {
|
||||||
|
return {
|
||||||
|
...arm,
|
||||||
|
isActive: false,
|
||||||
|
status: "idle",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return arm;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
}
|
}
|
||||||
} else if (progress >= endToRestRange[0] && progress < 1) {
|
|
||||||
currentSpeed = restSpeed;
|
|
||||||
currentStatus = "moving"; // Returning to rest
|
|
||||||
} else if (progress >= 1) {
|
|
||||||
currentStatus = "idle"; // Completed cycle
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update status when it changes
|
return (
|
||||||
if (prevStatusRef.current !== currentStatus) {
|
<>
|
||||||
updateArmBotStatus(currentStatus);
|
<MaterialInstances
|
||||||
prevStatusRef.current = currentStatus;
|
statusRef={statusRef}
|
||||||
}
|
ikSolver={ikSolver}
|
||||||
|
targetBoneName={targetBoneName}
|
||||||
// Only update progress if we're not already at the end
|
/>
|
||||||
if (progress < 1) {
|
</>
|
||||||
setProgress((prev) => {
|
);
|
||||||
const next = prev + delta * currentSpeed;
|
|
||||||
return Math.min(next, 1); // Cap at 1
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update bone position based on progress
|
|
||||||
if (progress < 1) {
|
|
||||||
targetBone.position.copy(curve.getPoint(progress));
|
|
||||||
} else {
|
|
||||||
targetBone.position.copy(curve.getPoint(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
ikSolver.update();
|
|
||||||
});
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default IKAnimationController;
|
export default IKAnimationController;
|
||||||
@@ -23,16 +23,12 @@ interface ArmBotState {
|
|||||||
status: string;
|
status: string;
|
||||||
material: string;
|
material: string;
|
||||||
triggerId: string;
|
triggerId: string;
|
||||||
actions: {
|
connections: {
|
||||||
uuid: string;
|
source: { modelUUID: string; pointUUID: string };
|
||||||
name: string;
|
targets: { modelUUID: string; pointUUID: string }[];
|
||||||
speed: number;
|
|
||||||
processes: {
|
|
||||||
triggerId: string;
|
|
||||||
startPoint: string;
|
|
||||||
endPoint: string;
|
|
||||||
}[];
|
|
||||||
};
|
};
|
||||||
|
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
|
||||||
|
isActive?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const IkInstances = ({
|
const IkInstances = ({
|
||||||
@@ -43,6 +39,7 @@ const IkInstances = ({
|
|||||||
position,
|
position,
|
||||||
rotation,
|
rotation,
|
||||||
armBot,
|
armBot,
|
||||||
|
setArmBots,
|
||||||
setStaticMachines,
|
setStaticMachines,
|
||||||
updateArmBotStatus
|
updateArmBotStatus
|
||||||
}: {
|
}: {
|
||||||
@@ -53,6 +50,7 @@ const IkInstances = ({
|
|||||||
position: [number, number, number];
|
position: [number, number, number];
|
||||||
rotation: [number, number, number];
|
rotation: [number, number, number];
|
||||||
armBot: ArmBotState;
|
armBot: ArmBotState;
|
||||||
|
setArmBots: React.Dispatch<React.SetStateAction<ArmBotState[]>>;
|
||||||
setStaticMachines: React.Dispatch<React.SetStateAction<StaticMachineState[]>>;
|
setStaticMachines: React.Dispatch<React.SetStateAction<StaticMachineState[]>>;
|
||||||
updateArmBotStatus: (status: string) => void;
|
updateArmBotStatus: (status: string) => void;
|
||||||
}) => {
|
}) => {
|
||||||
@@ -134,13 +132,14 @@ const IkInstances = ({
|
|||||||
</group>
|
</group>
|
||||||
<IKAnimationController
|
<IKAnimationController
|
||||||
ikSolver={ikSolver}
|
ikSolver={ikSolver}
|
||||||
process={processes}
|
processes={processes}
|
||||||
selectedTrigger={selectedTrigger}
|
selectedTrigger={selectedTrigger}
|
||||||
targetBoneName={targetBoneName}
|
targetBoneName={targetBoneName}
|
||||||
uuid={uuid}
|
uuid={uuid}
|
||||||
logStatus={logStatus}
|
logStatus={logStatus}
|
||||||
groupRef={groupRef}
|
groupRef={groupRef}
|
||||||
armBot={armBot}
|
armBot={armBot}
|
||||||
|
setArmBots={setArmBots}
|
||||||
setStaticMachines={setStaticMachines}
|
setStaticMachines={setStaticMachines}
|
||||||
updateArmBotStatus={updateArmBotStatus}
|
updateArmBotStatus={updateArmBotStatus}
|
||||||
/>
|
/>
|
||||||
|
|||||||
31
app/src/modules/simulation/armbot/MaterialInstances.tsx
Normal file
31
app/src/modules/simulation/armbot/MaterialInstances.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import * as THREE from 'three';
|
||||||
|
import { Box } from '@react-three/drei';
|
||||||
|
|
||||||
|
type MaterialInstancesProps = {
|
||||||
|
statusRef: React.RefObject<string>;
|
||||||
|
ikSolver: any;
|
||||||
|
targetBoneName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function MaterialInstances({
|
||||||
|
statusRef,
|
||||||
|
ikSolver,
|
||||||
|
targetBoneName
|
||||||
|
}: MaterialInstancesProps) {
|
||||||
|
if (!ikSolver) return null;
|
||||||
|
|
||||||
|
const targetBone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBoneName);
|
||||||
|
if (!targetBone) return null;
|
||||||
|
|
||||||
|
const worldPos = new THREE.Vector3();
|
||||||
|
targetBone.getWorldPosition(worldPos);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box args={[0.5, 0.5, 0.5]} position={worldPos} visible={statusRef.current === 'processing'}>
|
||||||
|
<meshStandardMaterial color="orange" />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MaterialInstances;
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
import React, { useRef, useEffect, useMemo } from "react";
|
import React, { useRef, useEffect, useMemo, useCallback } from "react";
|
||||||
import { useLoader, useFrame } from "@react-three/fiber";
|
import { useLoader, useFrame } from "@react-three/fiber";
|
||||||
import { GLTFLoader } from "three-stdlib";
|
import { GLTFLoader } from "three-stdlib";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
@@ -8,8 +8,6 @@ import crate from "../../../assets/gltf-glb/crate_box.glb";
|
|||||||
import { useProcessAnimation } from "./useProcessAnimations";
|
import { useProcessAnimation } from "./useProcessAnimations";
|
||||||
import ProcessObject from "./processObject";
|
import ProcessObject from "./processObject";
|
||||||
import { ProcessData } from "./types";
|
import { ProcessData } from "./types";
|
||||||
import { useSimulationStates } from "../../../store/store";
|
|
||||||
import { retrieveGLTF } from "../../../utils/indexDB/idbUtils";
|
|
||||||
|
|
||||||
interface ArmBotState {
|
interface ArmBotState {
|
||||||
uuid: string;
|
uuid: string;
|
||||||
@@ -18,9 +16,18 @@ interface ArmBotState {
|
|||||||
status: string;
|
status: string;
|
||||||
material: string;
|
material: string;
|
||||||
triggerId: string;
|
triggerId: string;
|
||||||
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
|
connections: {
|
||||||
|
source: { modelUUID: string; pointUUID: string };
|
||||||
|
targets: { modelUUID: string; pointUUID: string }[];
|
||||||
|
};
|
||||||
|
actions: {
|
||||||
|
uuid: string;
|
||||||
|
name: string;
|
||||||
|
speed: number;
|
||||||
|
processes: { triggerId: string; startPoint: string; endPoint: string }[];
|
||||||
|
};
|
||||||
|
isActive?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ProcessContainerProps {
|
interface ProcessContainerProps {
|
||||||
processes: ProcessData[];
|
processes: ProcessData[];
|
||||||
setProcesses: React.Dispatch<React.SetStateAction<any[]>>;
|
setProcesses: React.Dispatch<React.SetStateAction<any[]>>;
|
||||||
@@ -36,7 +43,7 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
agvRef,
|
agvRef,
|
||||||
MaterialRef,
|
MaterialRef,
|
||||||
armBots,
|
armBots,
|
||||||
setArmBots
|
setArmBots,
|
||||||
}) => {
|
}) => {
|
||||||
const gltf = useLoader(GLTFLoader, crate) as GLTF;
|
const gltf = useLoader(GLTFLoader, crate) as GLTF;
|
||||||
const groupRef = useRef<THREE.Group>(null);
|
const groupRef = useRef<THREE.Group>(null);
|
||||||
@@ -58,19 +65,23 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
checkAndCountTriggers,
|
checkAndCountTriggers,
|
||||||
} = useProcessAnimation(processes, setProcesses, agvRef, armBots, setArmBots);
|
} = useProcessAnimation(processes, setProcesses, agvRef, armBots, setArmBots);
|
||||||
|
|
||||||
const baseMaterials = useMemo(() => ({
|
const baseMaterials = useMemo(
|
||||||
Box: new THREE.MeshStandardMaterial({ color: 0x8b4513 }),
|
() => ({
|
||||||
Crate: new THREE.MeshStandardMaterial({ color: 0x00ff00 }),
|
Box: new THREE.MeshStandardMaterial({ color: 0x8b4513 }),
|
||||||
Default: new THREE.MeshStandardMaterial(),
|
Crate: new THREE.MeshStandardMaterial({ color: 0x00ff00 }),
|
||||||
}), []);
|
Default: new THREE.MeshStandardMaterial(),
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Update material references for all spawned objects
|
// Update material references for all spawned objects
|
||||||
Object.entries(animationStates).forEach(([processId, processState]) => {
|
Object.entries(animationStates).forEach(([processId, processState]) => {
|
||||||
Object.keys(processState.spawnedObjects).forEach((objectId) => {
|
Object.keys(processState.spawnedObjects).forEach((objectId) => {
|
||||||
const entry = { processId, objectId, };
|
const entry = { processId, objectId };
|
||||||
|
|
||||||
const materialType = processState.spawnedObjects[objectId]?.currentMaterialType;
|
const materialType =
|
||||||
|
processState.spawnedObjects[objectId]?.currentMaterialType;
|
||||||
|
|
||||||
if (!materialType) {
|
if (!materialType) {
|
||||||
return;
|
return;
|
||||||
@@ -79,13 +90,17 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
const matRefArray = MaterialRef.current;
|
const matRefArray = MaterialRef.current;
|
||||||
|
|
||||||
// Find existing material group
|
// Find existing material group
|
||||||
const existing = matRefArray.find((entryGroup: { material: string; objects: any[] }) =>
|
const existing = matRefArray.find(
|
||||||
entryGroup.material === materialType
|
(entryGroup: { material: string; objects: any[] }) =>
|
||||||
|
entryGroup.material === materialType
|
||||||
);
|
);
|
||||||
|
|
||||||
if (existing) {
|
if (existing) {
|
||||||
// Check if this processId + objectId already exists
|
// Check if this processId + objectId already exists
|
||||||
const alreadyExists = existing.objects.some((o: any) => o.processId === entry.processId && o.objectId === entry.objectId);
|
const alreadyExists = existing.objects.some(
|
||||||
|
(o: any) =>
|
||||||
|
o.processId === entry.processId && o.objectId === entry.objectId
|
||||||
|
);
|
||||||
|
|
||||||
if (!alreadyExists) {
|
if (!alreadyExists) {
|
||||||
existing.objects.push(entry);
|
existing.objects.push(entry);
|
||||||
@@ -103,9 +118,31 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
|
|
||||||
// In processAnimator.tsx - only the relevant spawn logic part that needs fixes
|
// In processAnimator.tsx - only the relevant spawn logic part that needs fixes
|
||||||
|
|
||||||
|
// Add this function to ProcessAnimator component
|
||||||
|
const isConnectedToActiveArmBot = useCallback(
|
||||||
|
(processId: any) => {
|
||||||
|
// Check if any active armbot is connected to this process
|
||||||
|
return armBots.some((armbot) => {
|
||||||
|
if (!armbot.isActive) return false;
|
||||||
|
|
||||||
|
// Check if this armbot is connected to the process
|
||||||
|
return armbot.connections?.targets?.some((connection) => {
|
||||||
|
// Find the process that owns this modelUUID
|
||||||
|
const connectedProcess = processes.find((p) =>
|
||||||
|
p.paths?.some((path) => path.modeluuid === connection.modelUUID)
|
||||||
|
);
|
||||||
|
return connectedProcess?.id === processId;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[armBots, processes]
|
||||||
|
);
|
||||||
|
|
||||||
|
// First useFrame for spawn logic
|
||||||
useFrame(() => {
|
useFrame(() => {
|
||||||
// Spawn logic frame
|
// Spawn logic frame
|
||||||
const currentTime = clockRef.current.getElapsedTime() - elapsedBeforePauseRef.current;
|
const currentTime =
|
||||||
|
clockRef.current.getElapsedTime() - elapsedBeforePauseRef.current;
|
||||||
|
|
||||||
setAnimationStates((prev) => {
|
setAnimationStates((prev) => {
|
||||||
const newStates = { ...prev };
|
const newStates = { ...prev };
|
||||||
@@ -114,26 +151,47 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
const processState = newStates[process.id];
|
const processState = newStates[process.id];
|
||||||
if (!processState) return;
|
if (!processState) return;
|
||||||
|
|
||||||
|
// Check connection status
|
||||||
|
const isConnected = isConnectedToActiveArmBot(process.id);
|
||||||
|
|
||||||
if (processState.isProcessDelaying) {
|
if (processState.isProcessDelaying) {
|
||||||
// Existing delay handling logic...
|
// Existing delay handling logic...
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isConnected) {
|
||||||
|
newStates[process.id] = {
|
||||||
|
...processState,
|
||||||
|
nextSpawnTime: Infinity, // Prevent future spawns
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const spawnPoint = findSpawnPoint(process);
|
const spawnPoint = findSpawnPoint(process);
|
||||||
if (!spawnPoint || !spawnPoint.actions) return;
|
if (!spawnPoint || !spawnPoint.actions) {
|
||||||
|
// console.log(
|
||||||
|
// `Process ${process.id} has no valid spawn point or actions`
|
||||||
|
// );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const spawnAction = spawnPoint.actions.find(
|
const spawnAction = spawnPoint.actions.find(
|
||||||
(a) => a.isUsed && a.type === "Spawn"
|
(a) => a.isUsed && a.type === "Spawn"
|
||||||
);
|
);
|
||||||
if (!spawnAction) return;
|
if (!spawnAction) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const spawnInterval =
|
const spawnInterval =
|
||||||
typeof spawnAction.spawnInterval === "number"
|
typeof spawnAction.spawnInterval === "number"
|
||||||
? spawnAction.spawnInterval
|
? spawnAction.spawnInterval
|
||||||
: parseFloat(spawnAction.spawnInterval as string) || 0;
|
: parseFloat(spawnAction.spawnInterval || "0") || 0;
|
||||||
|
|
||||||
// Check if this is a zero interval spawn and we already spawned an object
|
// Check if this is a zero interval spawn and we already spawned an object
|
||||||
if (spawnInterval === 0 && processState.hasSpawnedZeroIntervalObject === true) {
|
if (
|
||||||
|
spawnInterval === 0 &&
|
||||||
|
processState.hasSpawnedZeroIntervalObject === true
|
||||||
|
) {
|
||||||
return; // Don't spawn more objects for zero interval
|
return; // Don't spawn more objects for zero interval
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,6 +207,15 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
baseMaterials
|
baseMaterials
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Initialize state properly to ensure animation
|
||||||
|
newObject.state = {
|
||||||
|
...newObject.state,
|
||||||
|
isAnimating: true,
|
||||||
|
isDelaying: false,
|
||||||
|
delayComplete: false,
|
||||||
|
progress: 0.005, // Start with tiny progress to ensure animation begins
|
||||||
|
};
|
||||||
|
|
||||||
// Update state with the new object and flag for zero interval
|
// Update state with the new object and flag for zero interval
|
||||||
newStates[process.id] = {
|
newStates[process.id] = {
|
||||||
...processState,
|
...processState,
|
||||||
@@ -171,6 +238,7 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Second useFrame for animation logic
|
||||||
useFrame((_, delta) => {
|
useFrame((_, delta) => {
|
||||||
// Animation logic frame
|
// Animation logic frame
|
||||||
const currentTime =
|
const currentTime =
|
||||||
@@ -181,8 +249,42 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
|
|
||||||
processedProcesses.forEach((process) => {
|
processedProcesses.forEach((process) => {
|
||||||
const processState = newStates[process.id];
|
const processState = newStates[process.id];
|
||||||
if (!processState) return;
|
if (!processState) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check connection status with debugging
|
||||||
|
const isConnected = isConnectedToActiveArmBot(process.id);
|
||||||
|
// console.log(
|
||||||
|
// `Process ${process.id} animation - connected:`,
|
||||||
|
// isConnected
|
||||||
|
// );
|
||||||
|
|
||||||
|
if (isConnected) {
|
||||||
|
// Stop all animations when connected to active arm bot
|
||||||
|
newStates[process.id] = {
|
||||||
|
...processState,
|
||||||
|
spawnedObjects: Object.entries(processState.spawnedObjects).reduce(
|
||||||
|
(acc, [id, obj]) => ({
|
||||||
|
...acc,
|
||||||
|
[id]: {
|
||||||
|
...obj,
|
||||||
|
state: {
|
||||||
|
...obj.state,
|
||||||
|
isAnimating: false, // Stop animation
|
||||||
|
isDelaying: false, // Clear delays
|
||||||
|
delayComplete: false, // Reset delays
|
||||||
|
progress: 0, // Reset progress
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{}
|
||||||
|
),
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process delay handling
|
||||||
if (processState.isProcessDelaying) {
|
if (processState.isProcessDelaying) {
|
||||||
const effectiveDelayTime =
|
const effectiveDelayTime =
|
||||||
processState.processDelayDuration / speedRef.current;
|
processState.processDelayDuration / speedRef.current;
|
||||||
@@ -191,6 +293,9 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
currentTime - processState.processDelayStartTime >=
|
currentTime - processState.processDelayStartTime >=
|
||||||
effectiveDelayTime
|
effectiveDelayTime
|
||||||
) {
|
) {
|
||||||
|
// console.log(
|
||||||
|
// `Process ${process.id} delay completed, resuming animation`
|
||||||
|
// );
|
||||||
newStates[process.id] = {
|
newStates[process.id] = {
|
||||||
...processState,
|
...processState,
|
||||||
isProcessDelaying: false,
|
isProcessDelaying: false,
|
||||||
@@ -214,26 +319,42 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
{}
|
{}
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
return newStates;
|
return;
|
||||||
} else {
|
} else {
|
||||||
return newStates;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure we have a valid path to follow
|
||||||
const path =
|
const path =
|
||||||
process.animationPath?.map((p) => new THREE.Vector3(p.x, p.y, p.z)) ||
|
process.animationPath?.map((p) => new THREE.Vector3(p.x, p.y, p.z)) ||
|
||||||
[];
|
[];
|
||||||
if (path.length < 2) return;
|
|
||||||
|
if (path.length < 2) {
|
||||||
|
// console.log(
|
||||||
|
// `Process ${process.id} has insufficient path points: ${path.length}`
|
||||||
|
// );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const updatedObjects = { ...processState.spawnedObjects };
|
const updatedObjects = { ...processState.spawnedObjects };
|
||||||
|
let animationOccurring = false; // Track if any animation is happening
|
||||||
|
|
||||||
Object.entries(processState.spawnedObjects).forEach(
|
Object.entries(processState.spawnedObjects).forEach(
|
||||||
([objectId, obj]) => {
|
([objectId, obj]) => {
|
||||||
if (!obj.visible) return;
|
if (!obj.visible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const currentRef = gltf?.scene ? obj.ref.current : obj.ref.current;
|
const currentRef = gltf?.scene ? obj.ref.current : obj.ref.current;
|
||||||
if (!currentRef) return;
|
if (!currentRef) {
|
||||||
|
// console.log(
|
||||||
|
// `No reference for object ${objectId}, skipping animation`
|
||||||
|
// );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize position for new objects
|
||||||
if (
|
if (
|
||||||
obj.position &&
|
obj.position &&
|
||||||
obj.state.currentIndex === 0 &&
|
obj.state.currentIndex === 0 &&
|
||||||
@@ -244,11 +365,22 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
|
|
||||||
const stateRef = obj.state;
|
const stateRef = obj.state;
|
||||||
|
|
||||||
|
// Ensure animation state is properly set for objects
|
||||||
|
if (!stateRef.isAnimating && !stateRef.isDelaying && !isConnected) {
|
||||||
|
stateRef.isAnimating = true;
|
||||||
|
stateRef.progress =
|
||||||
|
stateRef.progress > 0 ? stateRef.progress : 0.005;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle delay logic
|
||||||
if (stateRef.isDelaying) {
|
if (stateRef.isDelaying) {
|
||||||
const effectiveDelayTime =
|
const effectiveDelayTime =
|
||||||
stateRef.currentDelayDuration / speedRef.current;
|
stateRef.currentDelayDuration / speedRef.current;
|
||||||
|
|
||||||
if (currentTime - stateRef.delayStartTime >= effectiveDelayTime) {
|
if (currentTime - stateRef.delayStartTime >= effectiveDelayTime) {
|
||||||
|
// console.log(
|
||||||
|
// `Delay complete for object ${objectId}, resuming animation`
|
||||||
|
// );
|
||||||
stateRef.isDelaying = false;
|
stateRef.isDelaying = false;
|
||||||
stateRef.delayComplete = true;
|
stateRef.delayComplete = true;
|
||||||
stateRef.isAnimating = true;
|
stateRef.isAnimating = true;
|
||||||
@@ -274,8 +406,17 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stateRef.isAnimating) return;
|
// Skip non-animating objects
|
||||||
|
if (!stateRef.isAnimating) {
|
||||||
|
// console.log(
|
||||||
|
// `Object ${objectId} not animating, skipping animation updates`
|
||||||
|
// );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
animationOccurring = true; // Mark that animation is happening
|
||||||
|
|
||||||
|
// Handle point actions
|
||||||
const currentPointData = getPointDataForAnimationIndex(
|
const currentPointData = getPointDataForAnimationIndex(
|
||||||
process,
|
process,
|
||||||
stateRef.currentIndex
|
stateRef.currentIndex
|
||||||
@@ -300,51 +441,22 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
const nextPointIdx = stateRef.currentIndex + 1;
|
const nextPointIdx = stateRef.currentIndex + 1;
|
||||||
const isLastPoint = nextPointIdx >= path.length;
|
const isLastPoint = nextPointIdx >= path.length;
|
||||||
|
|
||||||
// if (isLastPoint) {
|
// Handle objects at the last point
|
||||||
// if (currentPointData?.actions) {
|
|
||||||
// const shouldStop = !hasNonInheritActions(
|
|
||||||
// currentPointData.actions
|
|
||||||
// );
|
|
||||||
// if (shouldStop) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (isLastPoint) {
|
|
||||||
// if (currentPointData?.actions) {
|
|
||||||
// const hasNonInherit = hasNonInheritActions(
|
|
||||||
// currentPointData.actions
|
|
||||||
// );
|
|
||||||
// if (!hasNonInherit) {
|
|
||||||
// // Remove the object if all actions are inherit
|
|
||||||
// updatedObjects[objectId] = {
|
|
||||||
// ...obj,
|
|
||||||
// visible: false,
|
|
||||||
// state: { ...stateRef, isAnimating: false },
|
|
||||||
// };
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// // No actions at last point - remove the object
|
|
||||||
// updatedObjects[objectId] = {
|
|
||||||
// ...obj,
|
|
||||||
// visible: false,
|
|
||||||
// state: { ...stateRef, isAnimating: false },
|
|
||||||
// };
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (isLastPoint) {
|
if (isLastPoint) {
|
||||||
const isAgvPicking = agvRef.current.some(
|
const isAgvPicking = agvRef.current.some(
|
||||||
(agv: any) => agv.processId === process.id && agv.status === "picking"
|
(agv: any) =>
|
||||||
|
agv.processId === process.id && agv.status === "picking"
|
||||||
);
|
);
|
||||||
|
|
||||||
const shouldHide = !currentPointData?.actions || !hasNonInheritActions(currentPointData.actions);
|
const shouldHide =
|
||||||
|
!currentPointData?.actions ||
|
||||||
|
!hasNonInheritActions(currentPointData.actions);
|
||||||
|
|
||||||
if (shouldHide) {
|
if (shouldHide) {
|
||||||
if (isAgvPicking) {
|
if (isAgvPicking) {
|
||||||
|
// console.log(
|
||||||
|
// `AGV picking at last point for object ${objectId}, hiding object`
|
||||||
|
// );
|
||||||
updatedObjects[objectId] = {
|
updatedObjects[objectId] = {
|
||||||
...obj,
|
...obj,
|
||||||
visible: false,
|
visible: false,
|
||||||
@@ -370,9 +482,11 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle stacked objects when AGV picks
|
||||||
if (tempStackedObjectsRef.current[objectId]) {
|
if (tempStackedObjectsRef.current[objectId]) {
|
||||||
const isAgvPicking = agvRef.current.some(
|
const isAgvPicking = agvRef.current.some(
|
||||||
(agv: any) => agv.processId === process.id && agv.status === "picking"
|
(agv: any) =>
|
||||||
|
agv.processId === process.id && agv.status === "picking"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isAgvPicking) {
|
if (isAgvPicking) {
|
||||||
@@ -386,22 +500,34 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
isAnimating: false,
|
isAnimating: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle normal animation progress for objects not at last point
|
||||||
if (!isLastPoint) {
|
if (!isLastPoint) {
|
||||||
const nextPoint = path[nextPointIdx];
|
const nextPoint = path[nextPointIdx];
|
||||||
const distance = path[stateRef.currentIndex].distanceTo(nextPoint);
|
const distance =
|
||||||
|
path[stateRef.currentIndex].distanceTo(nextPoint);
|
||||||
const effectiveSpeed = stateRef.speed * speedRef.current;
|
const effectiveSpeed = stateRef.speed * speedRef.current;
|
||||||
const movement = effectiveSpeed * delta;
|
const movement = effectiveSpeed * delta;
|
||||||
|
|
||||||
|
// Ensure progress is always moving forward
|
||||||
if (stateRef.delayComplete && stateRef.progress < 0.01) {
|
if (stateRef.delayComplete && stateRef.progress < 0.01) {
|
||||||
stateRef.progress = 0.05;
|
stateRef.progress = 0.05;
|
||||||
stateRef.delayComplete = false;
|
stateRef.delayComplete = false;
|
||||||
|
// console.log(
|
||||||
|
// `Boosting progress for object ${objectId} after delay`
|
||||||
|
// );
|
||||||
} else {
|
} else {
|
||||||
stateRef.progress += movement / distance;
|
stateRef.progress += movement / distance;
|
||||||
|
// console.log(
|
||||||
|
// `Object ${objectId} progress: ${stateRef.progress.toFixed(3)}`
|
||||||
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle point transition
|
||||||
if (stateRef.progress >= 1) {
|
if (stateRef.progress >= 1) {
|
||||||
stateRef.currentIndex = nextPointIdx;
|
stateRef.currentIndex = nextPointIdx;
|
||||||
stateRef.progress = 0;
|
stateRef.progress = 0;
|
||||||
@@ -420,7 +546,10 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
process,
|
process,
|
||||||
stateRef.currentIndex
|
stateRef.currentIndex
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// No action needed with newPointData here - will be handled in next frame
|
||||||
} else {
|
} else {
|
||||||
|
// Update position with lerp
|
||||||
currentRef.position.lerpVectors(
|
currentRef.position.lerpVectors(
|
||||||
path[stateRef.currentIndex],
|
path[stateRef.currentIndex],
|
||||||
nextPoint,
|
nextPoint,
|
||||||
@@ -433,6 +562,13 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Log if no animation is occurring when it should
|
||||||
|
if (!animationOccurring && !isConnected) {
|
||||||
|
// console.log(
|
||||||
|
// `Warning: No animation occurring for process ${process.id} despite not being connected`
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
|
||||||
newStates[process.id] = {
|
newStates[process.id] = {
|
||||||
...processState,
|
...processState,
|
||||||
spawnedObjects: updatedObjects,
|
spawnedObjects: updatedObjects,
|
||||||
|
|||||||
@@ -9,7 +9,12 @@ interface ArmBotState {
|
|||||||
status: string;
|
status: string;
|
||||||
material: string;
|
material: string;
|
||||||
triggerId: string;
|
triggerId: string;
|
||||||
|
connections: {
|
||||||
|
source: { modelUUID: string; pointUUID: string };
|
||||||
|
targets: { modelUUID: string; pointUUID: string }[];
|
||||||
|
};
|
||||||
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
|
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
|
||||||
|
isActive?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ProcessContainerProps {
|
interface ProcessContainerProps {
|
||||||
|
|||||||
@@ -1,450 +1,3 @@
|
|||||||
// import React, {
|
|
||||||
// useEffect,
|
|
||||||
// useMemo,
|
|
||||||
// useState,
|
|
||||||
// useCallback,
|
|
||||||
// useRef,
|
|
||||||
// } from "react";
|
|
||||||
// import { useSimulationStates } from "../../../store/store";
|
|
||||||
// import * as THREE from "three";
|
|
||||||
// import { useThree } from "@react-three/fiber";
|
|
||||||
// import {
|
|
||||||
// ConveyorEventsSchema,
|
|
||||||
// VehicleEventsSchema,
|
|
||||||
// } from "../../../types/world/worldTypes";
|
|
||||||
|
|
||||||
// // Type definitions
|
|
||||||
// export interface PointAction {
|
|
||||||
// uuid: string;
|
|
||||||
// name: string;
|
|
||||||
// type: string;
|
|
||||||
// material: string;
|
|
||||||
// delay: number | string;
|
|
||||||
// spawnInterval: string | number;
|
|
||||||
// isUsed: boolean;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export interface PointTrigger {
|
|
||||||
// uuid: string;
|
|
||||||
// bufferTime: number;
|
|
||||||
// name: string;
|
|
||||||
// type: string;
|
|
||||||
// isUsed: boolean;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export interface PathPoint {
|
|
||||||
// uuid: string;
|
|
||||||
// position: [number, number, number];
|
|
||||||
// actions: PointAction[];
|
|
||||||
// triggers: PointTrigger[];
|
|
||||||
// connections: {
|
|
||||||
// targets: Array<{ modelUUID: string }>;
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export interface SimulationPath {
|
|
||||||
// type: string;
|
|
||||||
// modeluuid: string;
|
|
||||||
// points: PathPoint[];
|
|
||||||
// pathPosition: [number, number, number];
|
|
||||||
// speed?: number;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export interface Process {
|
|
||||||
// id: string;
|
|
||||||
// paths: SimulationPath[];
|
|
||||||
// animationPath: THREE.Vector3[];
|
|
||||||
// pointActions: PointAction[][];
|
|
||||||
// pointTriggers: PointTrigger[][];
|
|
||||||
// speed: number;
|
|
||||||
// isActive: boolean;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// interface ProcessCreatorProps {
|
|
||||||
// onProcessesCreated: (processes: Process[]) => void;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Convert event schemas to SimulationPath
|
|
||||||
// function convertToSimulationPath(
|
|
||||||
// path: ConveyorEventsSchema | VehicleEventsSchema
|
|
||||||
// ): SimulationPath {
|
|
||||||
// const { modeluuid } = path;
|
|
||||||
|
|
||||||
// // Normalized action handler
|
|
||||||
// const normalizeAction = (action: any): PointAction => {
|
|
||||||
// return { ...action }; // Return exact copy with no modifications
|
|
||||||
// };
|
|
||||||
|
|
||||||
// // Normalized trigger handler
|
|
||||||
// const normalizeTrigger = (trigger: any): PointTrigger => {
|
|
||||||
// return { ...trigger }; // Return exact copy with no modifications
|
|
||||||
// };
|
|
||||||
|
|
||||||
// if (path.type === "Conveyor") {
|
|
||||||
// return {
|
|
||||||
// type: path.type,
|
|
||||||
// modeluuid,
|
|
||||||
// points: path.points.map((point) => ({
|
|
||||||
// uuid: point.uuid,
|
|
||||||
// position: point.position,
|
|
||||||
// actions: Array.isArray(point.actions)
|
|
||||||
// ? point.actions.map(normalizeAction)
|
|
||||||
// : point.actions
|
|
||||||
// ? [normalizeAction(point.actions)]
|
|
||||||
// : [],
|
|
||||||
// triggers: Array.isArray(point.triggers)
|
|
||||||
// ? point.triggers.map(normalizeTrigger)
|
|
||||||
// : point.triggers
|
|
||||||
// ? [normalizeTrigger(point.triggers)]
|
|
||||||
// : [],
|
|
||||||
// connections: {
|
|
||||||
// targets: point.connections.targets.map((target) => ({
|
|
||||||
// modelUUID: target.modelUUID,
|
|
||||||
// })),
|
|
||||||
// },
|
|
||||||
// })),
|
|
||||||
// pathPosition: path.position,
|
|
||||||
// speed:
|
|
||||||
// typeof path.speed === "string"
|
|
||||||
// ? parseFloat(path.speed) || 1
|
|
||||||
// : path.speed || 1,
|
|
||||||
// };
|
|
||||||
// } else {
|
|
||||||
// // For vehicle paths, handle the case where triggers might not exist
|
|
||||||
// return {
|
|
||||||
// type: path.type,
|
|
||||||
// modeluuid,
|
|
||||||
// points: [
|
|
||||||
// {
|
|
||||||
// uuid: path.points.uuid,
|
|
||||||
// position: path.points.position,
|
|
||||||
// actions: Array.isArray(path.points.actions)
|
|
||||||
// ? path.points.actions.map(normalizeAction)
|
|
||||||
// : path.points.actions
|
|
||||||
// ? [normalizeAction(path.points.actions)]
|
|
||||||
// : [],
|
|
||||||
// // For vehicle paths, since triggers might not exist in the schema,
|
|
||||||
// // we always define default to an empty array
|
|
||||||
// triggers: [],
|
|
||||||
// connections: {
|
|
||||||
// targets: path.points.connections.targets.map((target) => ({
|
|
||||||
// modelUUID: target.modelUUID,
|
|
||||||
// })),
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// pathPosition: path.position,
|
|
||||||
// speed: path.points.speed || 1,
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Helper function to create an empty process
|
|
||||||
// const createEmptyProcess = (): Process => ({
|
|
||||||
// id: `process-${Math.random().toString(36).substring(2, 11)}`,
|
|
||||||
// paths: [],
|
|
||||||
// animationPath: [],
|
|
||||||
// pointActions: [],
|
|
||||||
// pointTriggers: [], // Added point triggers array
|
|
||||||
// speed: 1,
|
|
||||||
// isActive: false,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// // Enhanced connection checking function
|
|
||||||
// function shouldReverseNextPath(
|
|
||||||
// currentPath: SimulationPath,
|
|
||||||
// nextPath: SimulationPath
|
|
||||||
// ): boolean {
|
|
||||||
// if (nextPath.points.length !== 3) return false;
|
|
||||||
|
|
||||||
// const currentLastPoint = currentPath.points[currentPath.points.length - 1];
|
|
||||||
// const nextFirstPoint = nextPath.points[0];
|
|
||||||
// const nextLastPoint = nextPath.points[nextPath.points.length - 1];
|
|
||||||
|
|
||||||
// // Check if current last connects to next last (requires reversal)
|
|
||||||
// const connectsToLast = currentLastPoint.connections.targets.some(
|
|
||||||
// (target) =>
|
|
||||||
// target.modelUUID === nextPath.modeluuid &&
|
|
||||||
// nextLastPoint.connections.targets.some(
|
|
||||||
// (t) => t.modelUUID === currentPath.modeluuid
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
|
|
||||||
// // Check if current last connects to next first (no reversal needed)
|
|
||||||
// const connectsToFirst = currentLastPoint.connections.targets.some(
|
|
||||||
// (target) =>
|
|
||||||
// target.modelUUID === nextPath.modeluuid &&
|
|
||||||
// nextFirstPoint.connections.targets.some(
|
|
||||||
// (t) => t.modelUUID === currentPath.modeluuid
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
|
|
||||||
// // Only reverse if connected to last point and not to first point
|
|
||||||
// return connectsToLast && !connectsToFirst;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Check if a point has a spawn action
|
|
||||||
// function hasSpawnAction(point: PathPoint): boolean {
|
|
||||||
// return point.actions.some((action) => action.type.toLowerCase() === "spawn");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Ensure spawn point is always at the beginning of the path
|
|
||||||
// function ensureSpawnPointIsFirst(path: SimulationPath): SimulationPath {
|
|
||||||
// if (path.points.length !== 3) return path;
|
|
||||||
|
|
||||||
// // If the third point has spawn action and first doesn't, reverse the array
|
|
||||||
// if (hasSpawnAction(path.points[2]) && !hasSpawnAction(path.points[0])) {
|
|
||||||
// return {
|
|
||||||
// ...path,
|
|
||||||
// points: [...path.points].reverse(),
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return path;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Updated path adjustment function
|
|
||||||
// function adjustPathPointsOrder(paths: SimulationPath[]): SimulationPath[] {
|
|
||||||
// if (paths.length < 1) return paths;
|
|
||||||
|
|
||||||
// const adjustedPaths = [...paths];
|
|
||||||
|
|
||||||
// // First ensure all paths have spawn points at the beginning
|
|
||||||
// for (let i = 0; i < adjustedPaths.length; i++) {
|
|
||||||
// adjustedPaths[i] = ensureSpawnPointIsFirst(adjustedPaths[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Then handle connections between paths
|
|
||||||
// for (let i = 0; i < adjustedPaths.length - 1; i++) {
|
|
||||||
// const currentPath = adjustedPaths[i];
|
|
||||||
// const nextPath = adjustedPaths[i + 1];
|
|
||||||
|
|
||||||
// if (shouldReverseNextPath(currentPath, nextPath)) {
|
|
||||||
// const reversedPoints = [
|
|
||||||
// nextPath.points[2],
|
|
||||||
// nextPath.points[1],
|
|
||||||
// nextPath.points[0],
|
|
||||||
// ];
|
|
||||||
|
|
||||||
// adjustedPaths[i + 1] = {
|
|
||||||
// ...nextPath,
|
|
||||||
// points: reversedPoints,
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return adjustedPaths;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Main hook for process creation
|
|
||||||
// export function useProcessCreation() {
|
|
||||||
// const { scene } = useThree();
|
|
||||||
// const [processes, setProcesses] = useState<Process[]>([]);
|
|
||||||
|
|
||||||
// const hasSpawnAction = useCallback((path: SimulationPath): boolean => {
|
|
||||||
// if (path.type !== "Conveyor") return false;
|
|
||||||
// return path.points.some((point) =>
|
|
||||||
// point.actions.some((action) => action.type.toLowerCase() === "spawn")
|
|
||||||
// );
|
|
||||||
// }, []);
|
|
||||||
|
|
||||||
// const createProcess = useCallback(
|
|
||||||
// (paths: SimulationPath[]): Process => {
|
|
||||||
// if (!paths || paths.length === 0) {
|
|
||||||
// return createEmptyProcess();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const animationPath: THREE.Vector3[] = [];
|
|
||||||
// const pointActions: PointAction[][] = [];
|
|
||||||
// const pointTriggers: PointTrigger[][] = []; // Added point triggers collection
|
|
||||||
// const processSpeed = paths[0]?.speed || 1;
|
|
||||||
|
|
||||||
// for (const path of paths) {
|
|
||||||
// for (const point of path.points) {
|
|
||||||
// if (path.type === "Conveyor") {
|
|
||||||
// const obj = scene.getObjectByProperty("uuid", point.uuid);
|
|
||||||
// if (!obj) {
|
|
||||||
// console.warn(`Object with UUID ${point.uuid} not found in scene`);
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const position = obj.getWorldPosition(new THREE.Vector3());
|
|
||||||
// animationPath.push(position.clone());
|
|
||||||
// pointActions.push(point.actions);
|
|
||||||
// pointTriggers.push(point.triggers); // Collect triggers for each point
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return {
|
|
||||||
// id: `process-${Math.random().toString(36).substring(2, 11)}`,
|
|
||||||
// paths,
|
|
||||||
// animationPath,
|
|
||||||
// pointActions,
|
|
||||||
// pointTriggers,
|
|
||||||
// speed: processSpeed,
|
|
||||||
// isActive: false,
|
|
||||||
// };
|
|
||||||
// },
|
|
||||||
// [scene]
|
|
||||||
// );
|
|
||||||
|
|
||||||
// const getAllConnectedPaths = useCallback(
|
|
||||||
// (
|
|
||||||
// initialPath: SimulationPath,
|
|
||||||
// allPaths: SimulationPath[],
|
|
||||||
// visited: Set<string> = new Set()
|
|
||||||
// ): SimulationPath[] => {
|
|
||||||
// const connectedPaths: SimulationPath[] = [];
|
|
||||||
// const queue: SimulationPath[] = [initialPath];
|
|
||||||
// visited.add(initialPath.modeluuid);
|
|
||||||
|
|
||||||
// const pathMap = new Map<string, SimulationPath>();
|
|
||||||
// allPaths.forEach((path) => pathMap.set(path.modeluuid, path));
|
|
||||||
|
|
||||||
// while (queue.length > 0) {
|
|
||||||
// const currentPath = queue.shift()!;
|
|
||||||
// connectedPaths.push(currentPath);
|
|
||||||
|
|
||||||
// // Process outgoing connections
|
|
||||||
// for (const point of currentPath.points) {
|
|
||||||
// for (const target of point.connections.targets) {
|
|
||||||
// if (!visited.has(target.modelUUID)) {
|
|
||||||
// const targetPath = pathMap.get(target.modelUUID);
|
|
||||||
// if (targetPath) {
|
|
||||||
// visited.add(target.modelUUID);
|
|
||||||
// queue.push(targetPath);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Process incoming connections
|
|
||||||
// for (const [uuid, path] of pathMap) {
|
|
||||||
// if (!visited.has(uuid)) {
|
|
||||||
// const hasConnectionToCurrent = path.points.some((point) =>
|
|
||||||
// point.connections.targets.some(
|
|
||||||
// (t) => t.modelUUID === currentPath.modeluuid
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
// if (hasConnectionToCurrent) {
|
|
||||||
// visited.add(uuid);
|
|
||||||
// queue.push(path);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return connectedPaths;
|
|
||||||
// },
|
|
||||||
// []
|
|
||||||
// );
|
|
||||||
|
|
||||||
// const createProcessesFromPaths = useCallback(
|
|
||||||
// (paths: SimulationPath[]): Process[] => {
|
|
||||||
// if (!paths || paths.length === 0) return [];
|
|
||||||
|
|
||||||
// const visited = new Set<string>();
|
|
||||||
// const processes: Process[] = [];
|
|
||||||
// const pathMap = new Map<string, SimulationPath>();
|
|
||||||
// paths.forEach((path) => pathMap.set(path.modeluuid, path));
|
|
||||||
|
|
||||||
// for (const path of paths) {
|
|
||||||
// if (!visited.has(path.modeluuid) && hasSpawnAction(path)) {
|
|
||||||
// const connectedPaths = getAllConnectedPaths(path, paths, visited);
|
|
||||||
// const adjustedPaths = adjustPathPointsOrder(connectedPaths);
|
|
||||||
// const process = createProcess(adjustedPaths);
|
|
||||||
// processes.push(process);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return processes;
|
|
||||||
// },
|
|
||||||
// [createProcess, getAllConnectedPaths, hasSpawnAction]
|
|
||||||
// );
|
|
||||||
|
|
||||||
// return {
|
|
||||||
// processes,
|
|
||||||
// createProcessesFromPaths,
|
|
||||||
// setProcesses,
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const ProcessCreator: React.FC<ProcessCreatorProps> = React.memo(
|
|
||||||
// ({ onProcessesCreated }) => {
|
|
||||||
// const { simulationStates } = useSimulationStates();
|
|
||||||
// const { createProcessesFromPaths } = useProcessCreation();
|
|
||||||
// const prevPathsRef = useRef<SimulationPath[]>([]);
|
|
||||||
// const prevProcessesRef = useRef<Process[]>([]);
|
|
||||||
|
|
||||||
// const convertedPaths = useMemo((): SimulationPath[] => {
|
|
||||||
// if (!simulationStates) return [];
|
|
||||||
// return simulationStates.map((path) =>
|
|
||||||
// convertToSimulationPath(
|
|
||||||
// path as ConveyorEventsSchema | VehicleEventsSchema
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
// }, [simulationStates]);
|
|
||||||
|
|
||||||
// // Enhanced dependency tracking that includes action and trigger types
|
|
||||||
// const pathsDependency = useMemo(() => {
|
|
||||||
// if (!convertedPaths) return null;
|
|
||||||
// return convertedPaths.map((path) => ({
|
|
||||||
// id: path.modeluuid,
|
|
||||||
// // Track all action types for each point
|
|
||||||
// actionSignature: path.points
|
|
||||||
// .map((point, index) =>
|
|
||||||
// point.actions.map((action) => `${index}-${action.type}`).join("|")
|
|
||||||
// )
|
|
||||||
// .join(","),
|
|
||||||
// // Track all trigger types for each point
|
|
||||||
// triggerSignature: path.points
|
|
||||||
// .map((point, index) =>
|
|
||||||
// point.triggers
|
|
||||||
// .map((trigger) => `${index}-${trigger.type}`)
|
|
||||||
// .join("|")
|
|
||||||
// )
|
|
||||||
// .join(","),
|
|
||||||
// connections: path.points
|
|
||||||
// .flatMap((p: PathPoint) =>
|
|
||||||
// p.connections.targets.map((t: { modelUUID: string }) => t.modelUUID)
|
|
||||||
// )
|
|
||||||
// .join(","),
|
|
||||||
// isActive: false,
|
|
||||||
// }));
|
|
||||||
// }, [convertedPaths]);
|
|
||||||
|
|
||||||
// // Force process recreation when paths change
|
|
||||||
// useEffect(() => {
|
|
||||||
// if (!convertedPaths || convertedPaths.length === 0) {
|
|
||||||
// if (prevProcessesRef.current.length > 0) {
|
|
||||||
// onProcessesCreated([]);
|
|
||||||
// prevProcessesRef.current = [];
|
|
||||||
// }
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Always regenerate processes if the pathsDependency has changed
|
|
||||||
// // This ensures action and trigger type changes will be detected
|
|
||||||
// const newProcesses = createProcessesFromPaths(convertedPaths);
|
|
||||||
// prevPathsRef.current = convertedPaths;
|
|
||||||
|
|
||||||
// // Always update processes when action or trigger types change
|
|
||||||
// onProcessesCreated(newProcesses);
|
|
||||||
// prevProcessesRef.current = newProcesses;
|
|
||||||
// }, [
|
|
||||||
// pathsDependency, // This now includes action and trigger types
|
|
||||||
// onProcessesCreated,
|
|
||||||
// convertedPaths,
|
|
||||||
// createProcessesFromPaths,
|
|
||||||
// ]);
|
|
||||||
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
|
|
||||||
// export default ProcessCreator;
|
|
||||||
|
|
||||||
import React, {
|
import React, {
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
@@ -456,6 +9,7 @@ import { useSimulationStates } from "../../../store/store";
|
|||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
import {
|
import {
|
||||||
|
ArmBotEventsSchema,
|
||||||
ConveyorEventsSchema,
|
ConveyorEventsSchema,
|
||||||
VehicleEventsSchema,
|
VehicleEventsSchema,
|
||||||
} from "../../../types/simulationTypes";
|
} from "../../../types/simulationTypes";
|
||||||
@@ -480,13 +34,14 @@ export interface PointTrigger {
|
|||||||
isUsed: boolean;
|
isUsed: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the connections type in your interfaces
|
||||||
export interface PathPoint {
|
export interface PathPoint {
|
||||||
uuid: string;
|
uuid: string;
|
||||||
position: [number, number, number];
|
position: [number, number, number];
|
||||||
actions: PointAction[];
|
actions: PointAction[];
|
||||||
triggers: PointTrigger[];
|
triggers: PointTrigger[];
|
||||||
connections: {
|
connections: {
|
||||||
targets: Array<{ modelUUID: string }>;
|
targets: Array<{ modelUUID: string; pointUUID?: string }>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -498,6 +53,14 @@ export interface SimulationPath {
|
|||||||
speed?: number;
|
speed?: number;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
}
|
}
|
||||||
|
export interface ArmBot {
|
||||||
|
type: string;
|
||||||
|
modeluuid: string;
|
||||||
|
points: PathPoint[];
|
||||||
|
pathPosition: [number, number, number];
|
||||||
|
speed?: number;
|
||||||
|
isActive: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Process {
|
export interface Process {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -515,7 +78,7 @@ interface ProcessCreatorProps {
|
|||||||
|
|
||||||
// Convert event schemas to SimulationPath
|
// Convert event schemas to SimulationPath
|
||||||
function convertToSimulationPath(
|
function convertToSimulationPath(
|
||||||
path: ConveyorEventsSchema | VehicleEventsSchema
|
path: ConveyorEventsSchema | VehicleEventsSchema | ArmBotEventsSchema
|
||||||
): SimulationPath {
|
): SimulationPath {
|
||||||
const { modeluuid } = path;
|
const { modeluuid } = path;
|
||||||
|
|
||||||
@@ -539,13 +102,13 @@ function convertToSimulationPath(
|
|||||||
actions: Array.isArray(point.actions)
|
actions: Array.isArray(point.actions)
|
||||||
? point.actions.map(normalizeAction)
|
? point.actions.map(normalizeAction)
|
||||||
: point.actions
|
: point.actions
|
||||||
? [normalizeAction(point.actions)]
|
? [normalizeAction(point.actions)]
|
||||||
: [],
|
: [],
|
||||||
triggers: Array.isArray(point.triggers)
|
triggers: Array.isArray(point.triggers)
|
||||||
? point.triggers.map(normalizeTrigger)
|
? point.triggers.map(normalizeTrigger)
|
||||||
: point.triggers
|
: point.triggers
|
||||||
? [normalizeTrigger(point.triggers)]
|
? [normalizeTrigger(point.triggers)]
|
||||||
: [],
|
: [],
|
||||||
connections: {
|
connections: {
|
||||||
targets: point.connections.targets.map((target) => ({
|
targets: point.connections.targets.map((target) => ({
|
||||||
modelUUID: target.modelUUID,
|
modelUUID: target.modelUUID,
|
||||||
@@ -559,6 +122,36 @@ function convertToSimulationPath(
|
|||||||
: path.speed || 1,
|
: path.speed || 1,
|
||||||
isActive: false, // Added missing property
|
isActive: false, // Added missing property
|
||||||
};
|
};
|
||||||
|
} else if (path.type === "ArmBot") {
|
||||||
|
return {
|
||||||
|
type: path.type,
|
||||||
|
modeluuid,
|
||||||
|
points: [
|
||||||
|
{
|
||||||
|
uuid: path.points.uuid,
|
||||||
|
position: path.points.position,
|
||||||
|
actions: Array.isArray(path.points.actions)
|
||||||
|
? path.points.actions.map(normalizeAction)
|
||||||
|
: path.points.actions
|
||||||
|
? [normalizeAction(path.points.actions)]
|
||||||
|
: [],
|
||||||
|
triggers: Array.isArray(path.points.triggers)
|
||||||
|
? path.points.triggers.map(normalizeTrigger)
|
||||||
|
: path.points.triggers
|
||||||
|
? [normalizeTrigger(path.points.triggers)]
|
||||||
|
: [],
|
||||||
|
connections: {
|
||||||
|
targets: path.points.connections.targets.map((target) => ({
|
||||||
|
modelUUID: target.modelUUID,
|
||||||
|
pointUUID: target.pointUUID, // Include if available
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pathPosition: path.position,
|
||||||
|
speed: path.points.actions?.speed || 1,
|
||||||
|
isActive: false,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
// For vehicle paths, handle the case where triggers might not exist
|
// For vehicle paths, handle the case where triggers might not exist
|
||||||
return {
|
return {
|
||||||
@@ -571,8 +164,8 @@ function convertToSimulationPath(
|
|||||||
actions: Array.isArray(path.points.actions)
|
actions: Array.isArray(path.points.actions)
|
||||||
? path.points.actions.map(normalizeAction)
|
? path.points.actions.map(normalizeAction)
|
||||||
: path.points.actions
|
: path.points.actions
|
||||||
? [normalizeAction(path.points.actions)]
|
? [normalizeAction(path.points.actions)]
|
||||||
: [],
|
: [],
|
||||||
triggers: [],
|
triggers: [],
|
||||||
connections: {
|
connections: {
|
||||||
targets: path.points.connections.targets.map((target) => ({
|
targets: path.points.connections.targets.map((target) => ({
|
||||||
@@ -831,7 +424,10 @@ const ProcessCreator: React.FC<ProcessCreatorProps> = React.memo(
|
|||||||
if (!simulationStates) return [];
|
if (!simulationStates) return [];
|
||||||
return simulationStates.map((path) =>
|
return simulationStates.map((path) =>
|
||||||
convertToSimulationPath(
|
convertToSimulationPath(
|
||||||
path as ConveyorEventsSchema | VehicleEventsSchema
|
path as
|
||||||
|
| ConveyorEventsSchema
|
||||||
|
| VehicleEventsSchema
|
||||||
|
| ArmBotEventsSchema
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}, [simulationStates]);
|
}, [simulationStates]);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,12 @@ interface ArmBotState {
|
|||||||
status: string;
|
status: string;
|
||||||
material: string;
|
material: string;
|
||||||
triggerId: string;
|
triggerId: string;
|
||||||
|
connections: {
|
||||||
|
source: { modelUUID: string; pointUUID: string };
|
||||||
|
targets: { modelUUID: string; pointUUID: string }[];
|
||||||
|
};
|
||||||
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
|
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
|
||||||
|
isActive?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StaticMachineState {
|
interface StaticMachineState {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useEffect } from 'react'
|
|||||||
import * as SimulationTypes from '../../../types/simulationTypes';
|
import * as SimulationTypes from '../../../types/simulationTypes';
|
||||||
import { useSimulationStates } from '../../../store/store';
|
import { useSimulationStates } from '../../../store/store';
|
||||||
import StaticMachineInstances from './staticMachineInstances';
|
import StaticMachineInstances from './staticMachineInstances';
|
||||||
|
import { useResetButtonStore } from '../../../store/usePlayButtonStore';
|
||||||
|
|
||||||
interface ArmBotState {
|
interface ArmBotState {
|
||||||
uuid: string;
|
uuid: string;
|
||||||
@@ -10,9 +11,13 @@ interface ArmBotState {
|
|||||||
status: string;
|
status: string;
|
||||||
material: string;
|
material: string;
|
||||||
triggerId: string;
|
triggerId: string;
|
||||||
|
connections: {
|
||||||
|
source: { modelUUID: string; pointUUID: string };
|
||||||
|
targets: { modelUUID: string; pointUUID: string }[];
|
||||||
|
};
|
||||||
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
|
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
|
||||||
|
isActive?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StaticMachineState {
|
interface StaticMachineState {
|
||||||
uuid: string;
|
uuid: string;
|
||||||
status: string;
|
status: string;
|
||||||
@@ -30,6 +35,7 @@ type StaticMachineProps = {
|
|||||||
function StaticMachine({ setArmBots, staticMachines, setStaticMachines }: StaticMachineProps) {
|
function StaticMachine({ setArmBots, staticMachines, setStaticMachines }: StaticMachineProps) {
|
||||||
|
|
||||||
const { simulationStates } = useSimulationStates();
|
const { simulationStates } = useSimulationStates();
|
||||||
|
const { isReset } = useResetButtonStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const filtered = simulationStates.filter((s): s is SimulationTypes.StaticMachineEventsSchema => s.type === "StaticMachine");
|
const filtered = simulationStates.filter((s): s is SimulationTypes.StaticMachineEventsSchema => s.type === "StaticMachine");
|
||||||
@@ -43,7 +49,7 @@ function StaticMachine({ setArmBots, staticMachines, setStaticMachines }: Static
|
|||||||
connectedArmBot: machine.points.connections.targets[0].modelUUID
|
connectedArmBot: machine.points.connections.targets[0].modelUUID
|
||||||
}));
|
}));
|
||||||
setStaticMachines(initialStates);
|
setStaticMachines(initialStates);
|
||||||
}, [simulationStates]);
|
}, [simulationStates, isReset]);
|
||||||
|
|
||||||
const updateArmBotTriggerAndMachineStatus = (armBotUuid: string, triggerId: string, machineId: string) => {
|
const updateArmBotTriggerAndMachineStatus = (armBotUuid: string, triggerId: string, machineId: string) => {
|
||||||
setArmBots((prevArmBots) => {
|
setArmBots((prevArmBots) => {
|
||||||
|
|||||||
@@ -104,8 +104,8 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
|||||||
setShowLeftArrow(isOverflowing && canScrollLeft);
|
setShowLeftArrow(isOverflowing && canScrollLeft);
|
||||||
setShowRightArrow(isOverflowing && canScrollRight);
|
setShowRightArrow(isOverflowing && canScrollRight);
|
||||||
|
|
||||||
console.log('canScrollRight: ', canScrollRight);
|
// console.log('canScrollRight: ', canScrollRight);
|
||||||
console.log('isOverflowing: ', isOverflowing);
|
// console.log('isOverflowing: ', isOverflowing);
|
||||||
|
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|||||||
@@ -520,37 +520,37 @@ const DroppedObjects: React.FC = () => {
|
|||||||
onPointerUp={handlePointerUp}
|
onPointerUp={handlePointerUp}
|
||||||
className="floating-wrapper"
|
className="floating-wrapper"
|
||||||
>
|
>
|
||||||
{zone.objects.map((obj, index) => {
|
{zone?.objects?.map((obj, index) => {
|
||||||
const topPosition =
|
const topPosition =
|
||||||
typeof obj.position.top === "number"
|
typeof obj?.position?.top === "number"
|
||||||
? `calc(${obj.position.top}px + ${
|
? `calc(${obj?.position?.top}px + ${
|
||||||
isPlaying && selectedZone.activeSides.includes("top")
|
isPlaying && selectedZone?.activeSides?.includes("top")
|
||||||
? `${heightMultiplier - 55}px`
|
? `${heightMultiplier - 55}px`
|
||||||
: "0px"
|
: "0px"
|
||||||
})`
|
})`
|
||||||
: "auto";
|
: "auto";
|
||||||
|
|
||||||
const leftPosition =
|
const leftPosition =
|
||||||
typeof obj.position.left === "number"
|
typeof obj?.position?.left === "number"
|
||||||
? `calc(${obj.position.left}px + ${
|
? `calc(${obj?.position?.left}px + ${
|
||||||
isPlaying && selectedZone.activeSides.includes("left")
|
isPlaying && selectedZone?.activeSides?.includes("left")
|
||||||
? `${widthMultiplier - 150}px`
|
? `${widthMultiplier - 150}px`
|
||||||
: "0px"
|
: "0px"
|
||||||
})`
|
})`
|
||||||
: "auto";
|
: "auto";
|
||||||
|
|
||||||
const rightPosition =
|
const rightPosition =
|
||||||
typeof obj.position.right === "number"
|
typeof obj?.position?.right === "number"
|
||||||
? `calc(${obj.position.right}px + ${
|
? `calc(${obj?.position?.right}px + ${
|
||||||
isPlaying && selectedZone.activeSides.includes("right")
|
isPlaying && selectedZone?.activeSides?.includes("right")
|
||||||
? `${widthMultiplier - 150}px`
|
? `${widthMultiplier - 150}px`
|
||||||
: "0px"
|
: "0px"
|
||||||
})`
|
})`
|
||||||
: "auto";
|
: "auto";
|
||||||
const bottomPosition =
|
const bottomPosition =
|
||||||
typeof obj.position.bottom === "number"
|
typeof obj?.position?.bottom === "number"
|
||||||
? `calc(${obj.position.bottom}px + ${
|
? `calc(${obj?.position?.bottom}px + ${
|
||||||
isPlaying && selectedZone.activeSides.includes("bottom")
|
isPlaying && selectedZone?.activeSides?.includes("bottom")
|
||||||
? `${heightMultiplier - 55}px`
|
? `${heightMultiplier - 55}px`
|
||||||
: "0px"
|
: "0px"
|
||||||
})`
|
})`
|
||||||
@@ -558,7 +558,7 @@ const DroppedObjects: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={`${zoneName}-${index}`}
|
key={obj.id}
|
||||||
className={`${obj.className} ${
|
className={`${obj.className} ${
|
||||||
selectedChartId?.id === obj.id && "activeChart"
|
selectedChartId?.id === obj.id && "activeChart"
|
||||||
} `}
|
} `}
|
||||||
|
|||||||
@@ -56,6 +56,12 @@ input {
|
|||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input-error {
|
||||||
|
border: 1px solid #f65648 !important;
|
||||||
|
outline: none !important;
|
||||||
|
color: #f65648;
|
||||||
|
}
|
||||||
|
|
||||||
.toggle-header-container {
|
.toggle-header-container {
|
||||||
@include flex-center;
|
@include flex-center;
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
@@ -344,7 +350,6 @@ input {
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.loading {
|
.loading {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@@ -364,9 +369,7 @@ input {
|
|||||||
left: -50%;
|
left: -50%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 50%;
|
width: 50%;
|
||||||
background: linear-gradient(to right,
|
background: linear-gradient(to right, var(--accent-color), transparent);
|
||||||
var(--accent-color),
|
|
||||||
transparent);
|
|
||||||
animation: loadingAnimation 1.2s linear infinite;
|
animation: loadingAnimation 1.2s linear infinite;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
@@ -381,8 +384,6 @@ input {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
|
|||||||
Reference in New Issue
Block a user