Compare commits
12 Commits
simulation
...
simulation
| Author | SHA1 | Date | |
|---|---|---|---|
| 686c4e60c6 | |||
| 967f1741b0 | |||
| 1e901c327d | |||
| cc074a5913 | |||
| 8e491a0002 | |||
| f7e4f5c580 | |||
| 64885f246e | |||
| 16cf1b96cc | |||
| ee319c28e4 | |||
| 939f6e5086 | |||
| 318ac6d939 | |||
| db9c9fb8b5 |
@@ -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>
|
||||||
|
|||||||
@@ -150,7 +150,6 @@ const ArmBotMechanics: React.FC = () => {
|
|||||||
modeluuid: updatedPath.modeluuid,
|
modeluuid: updatedPath.modeluuid,
|
||||||
eventData: { type: "ArmBot", points: updatedPath.points }
|
eventData: { type: "ArmBot", points: updatedPath.points }
|
||||||
}
|
}
|
||||||
console.log('data: ', data);
|
|
||||||
|
|
||||||
socket.emit('v2:model-asset:updateEventData', data);
|
socket.emit('v2:model-asset:updateEventData', data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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];
|
||||||
@@ -128,10 +142,17 @@ 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">
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ const Agv: React.FC<ProcessContainerProps> = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{pair.points.slice(1).map((point, idx) => (
|
{pair.points.slice(1).map((point, idx) => (
|
||||||
<mesh position={[point.x, point.y, point.z]} key={idx}>
|
<mesh position={[point.x, point.y, point.z]} key={idx} visible={!isPlaying}>
|
||||||
<sphereGeometry args={[0.3, 15, 15]} />
|
<sphereGeometry args={[0.3, 15, 15]} />
|
||||||
<meshStandardMaterial color="red" />
|
<meshStandardMaterial color="red" />
|
||||||
</mesh>
|
</mesh>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -456,7 +456,7 @@ export default function PathNavigator({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group name="path-navigator-lines">
|
<group name="path-navigator-lines" visible={!isPlaying} >
|
||||||
{toPickupPath.length > 0 && (
|
{toPickupPath.length > 0 && (
|
||||||
<Line
|
<Line
|
||||||
points={toPickupPath}
|
points={toPickupPath}
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ const ArmBot = ({ armBots, setArmBots, setStaticMachines }: ArmBotProps) => {
|
|||||||
material: "default",
|
material: "default",
|
||||||
triggerId: '',
|
triggerId: '',
|
||||||
actions: bot.points.actions,
|
actions: bot.points.actions,
|
||||||
connections: bot.points.connections
|
connections: bot.points.connections,
|
||||||
|
isActive: false
|
||||||
}));
|
}));
|
||||||
setArmBots(initialStates);
|
setArmBots(initialStates);
|
||||||
}, [simulationStates, isReset]);
|
}, [simulationStates, isReset]);
|
||||||
|
|||||||
@@ -46,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 {
|
||||||
|
|||||||
@@ -1,9 +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 MaterialInstances from "./MaterialInstances";
|
||||||
|
import { Line } from "react-chartjs-2";
|
||||||
|
import { QuadraticBezierLine } from "@react-three/drei";
|
||||||
|
|
||||||
|
|
||||||
interface StaticMachineState {
|
interface StaticMachineState {
|
||||||
@@ -70,6 +72,7 @@ const IKAnimationController = ({
|
|||||||
const { isPlaying } = usePlayButtonStore();;
|
const { isPlaying } = usePlayButtonStore();;
|
||||||
const statusRef = useRef("idle");
|
const statusRef = useRef("idle");
|
||||||
const { simulationStates } = useSimulationStates();
|
const { simulationStates } = useSimulationStates();
|
||||||
|
const { isReset } = useResetButtonStore();
|
||||||
|
|
||||||
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);
|
||||||
@@ -78,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(
|
||||||
@@ -116,7 +126,7 @@ const IKAnimationController = ({
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const processCurves = useMemo(() => {
|
const processedCurves = useMemo(() => {
|
||||||
if (!isPlaying) return [];
|
if (!isPlaying) return [];
|
||||||
|
|
||||||
return processes.map(process => {
|
return processes.map(process => {
|
||||||
@@ -164,8 +174,8 @@ const IKAnimationController = ({
|
|||||||
|
|
||||||
const activeProcess = useMemo(() => {
|
const activeProcess = useMemo(() => {
|
||||||
if (!selectedTrigger) return null;
|
if (!selectedTrigger) return null;
|
||||||
return processCurves.find(p => p?.triggerId === selectedTrigger);
|
return processedCurves.find(p => p?.triggerId === selectedTrigger);
|
||||||
}, [processCurves, selectedTrigger]);
|
}, [processedCurves, selectedTrigger]);
|
||||||
|
|
||||||
// Initial movement to rest position
|
// Initial movement to rest position
|
||||||
useFrame((_, delta) => {
|
useFrame((_, delta) => {
|
||||||
@@ -229,6 +239,9 @@ const IKAnimationController = ({
|
|||||||
currentStatus = "processing";
|
currentStatus = "processing";
|
||||||
const segmentProgress = (newProgress - restToStartEnd) / (processEnd - restToStartEnd);
|
const segmentProgress = (newProgress - restToStartEnd) / (processEnd - restToStartEnd);
|
||||||
currentPosition = processCurve.getPoint(segmentProgress);
|
currentPosition = processCurve.getPoint(segmentProgress);
|
||||||
|
if (statusRef.current !== "processing") {
|
||||||
|
updateConveyorOrStaticMachineStatusOnStart(selectedTrigger);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Returning to rest position
|
// Returning to rest position
|
||||||
currentStatus = "returning to rest";
|
currentStatus = "returning to rest";
|
||||||
@@ -245,7 +258,7 @@ const IKAnimationController = ({
|
|||||||
|
|
||||||
// Only trigger when the entire animation is complete (newProgress === 1)
|
// Only trigger when the entire animation is complete (newProgress === 1)
|
||||||
if (newProgress === 1 && currentStatus === "returning to rest") {
|
if (newProgress === 1 && currentStatus === "returning to rest") {
|
||||||
updateConveyorOrStaticMachineStatus(selectedTrigger);
|
updateConveyorOrStaticMachineStatusOnEnd(selectedTrigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
bone.position.copy(currentPosition);
|
bone.position.copy(currentPosition);
|
||||||
@@ -254,7 +267,45 @@ const IKAnimationController = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateConveyorOrStaticMachineStatus = (selectedTrigger: string) => {
|
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);
|
const currentProcess = processes.find(p => p.triggerId === selectedTrigger);
|
||||||
if (currentProcess) {
|
if (currentProcess) {
|
||||||
const triggerId = currentProcess.triggerId;
|
const triggerId = currentProcess.triggerId;
|
||||||
@@ -296,7 +347,7 @@ const IKAnimationController = ({
|
|||||||
if (matchedMachine.type === "Conveyor") {
|
if (matchedMachine.type === "Conveyor") {
|
||||||
setArmBots((prev) =>
|
setArmBots((prev) =>
|
||||||
prev.map((arm) => {
|
prev.map((arm) => {
|
||||||
if (arm.uuid === uuid) {
|
if (arm.uuid === uuid && arm.isActive === true) {
|
||||||
return {
|
return {
|
||||||
...arm,
|
...arm,
|
||||||
isActive: false,
|
isActive: false,
|
||||||
@@ -314,7 +365,15 @@ const IKAnimationController = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return (
|
||||||
|
<>
|
||||||
|
<MaterialInstances
|
||||||
|
statusRef={statusRef}
|
||||||
|
ikSolver={ikSolver}
|
||||||
|
targetBoneName={targetBoneName}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default IKAnimationController;
|
export default IKAnimationController;
|
||||||
@@ -3,37 +3,26 @@ import * as THREE from 'three';
|
|||||||
import { Box } from '@react-three/drei';
|
import { Box } from '@react-three/drei';
|
||||||
|
|
||||||
type MaterialInstancesProps = {
|
type MaterialInstancesProps = {
|
||||||
groupRef: React.RefObject<THREE.Group>;
|
statusRef: React.RefObject<string>;
|
||||||
activeCurve: any;
|
|
||||||
progress: number;
|
|
||||||
ikSolver: any;
|
ikSolver: any;
|
||||||
targetBoneName: string;
|
targetBoneName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
function MaterialInstances({
|
function MaterialInstances({
|
||||||
groupRef,
|
statusRef,
|
||||||
activeCurve,
|
|
||||||
progress,
|
|
||||||
ikSolver,
|
ikSolver,
|
||||||
targetBoneName
|
targetBoneName
|
||||||
}: MaterialInstancesProps) {
|
}: MaterialInstancesProps) {
|
||||||
const { endToRestRange, startToEndRange } = activeCurve;
|
if (!ikSolver) return null;
|
||||||
|
|
||||||
// Show the box from when we reach the start point until we leave the end point
|
const targetBone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBoneName);
|
||||||
const shouldShow = (progress >= startToEndRange[0] && progress < startToEndRange[1] && progress >= endToRestRange[0]);
|
|
||||||
|
|
||||||
if (!shouldShow || !ikSolver) return null;
|
|
||||||
|
|
||||||
const targetBone = ikSolver.mesh.skeleton.bones.find(
|
|
||||||
(b: any) => b.name === targetBoneName
|
|
||||||
);
|
|
||||||
if (!targetBone) return null;
|
if (!targetBone) return null;
|
||||||
|
|
||||||
const worldPos = new THREE.Vector3();
|
const worldPos = new THREE.Vector3();
|
||||||
targetBone.getWorldPosition(worldPos);
|
targetBone.getWorldPosition(worldPos);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box args={[0.5, 0.5, 0.5]} position={worldPos}>
|
<Box args={[0.5, 0.5, 0.5]} position={worldPos} visible={statusRef.current === 'processing'}>
|
||||||
<meshStandardMaterial color="orange" />
|
<meshStandardMaterial color="orange" />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -40,6 +40,37 @@ function PathConnector({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObje
|
|||||||
};
|
};
|
||||||
const existingTargets = point.connections.targets || [];
|
const existingTargets = point.connections.targets || [];
|
||||||
|
|
||||||
|
// Check connection limits
|
||||||
|
const toPath = simulationStates.find(p => p.modeluuid === toModelUUID);
|
||||||
|
if (toPath) {
|
||||||
|
// Check if we already have this type of connection
|
||||||
|
const hasConveyor = existingTargets.some(t => {
|
||||||
|
const targetPath = simulationStates.find(p => p.modeluuid === t.modelUUID);
|
||||||
|
return targetPath?.type === "Conveyor";
|
||||||
|
});
|
||||||
|
const hasArmBot = existingTargets.some(t => {
|
||||||
|
const targetPath = simulationStates.find(p => p.modeluuid === t.modelUUID);
|
||||||
|
return targetPath?.type === "ArmBot";
|
||||||
|
});
|
||||||
|
const hasVehicle = existingTargets.some(t => {
|
||||||
|
const targetPath = simulationStates.find(p => p.modeluuid === t.modelUUID);
|
||||||
|
return targetPath?.type === "Vehicle";
|
||||||
|
});
|
||||||
|
|
||||||
|
if (toPath.type === "Conveyor" && hasConveyor) {
|
||||||
|
console.log("Conveyor can only connect to one other conveyor");
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
if (toPath.type === "ArmBot" && hasArmBot) {
|
||||||
|
console.log("Conveyor can only connect to one ArmBot");
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
if (toPath.type === "Vehicle" && hasVehicle) {
|
||||||
|
console.log("Conveyor can only connect to one Vehicle");
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!existingTargets.some((target) => target.modelUUID === newTarget.modelUUID && target.pointUUID === newTarget.pointUUID)) {
|
if (!existingTargets.some((target) => target.modelUUID === newTarget.modelUUID && target.pointUUID === newTarget.pointUUID)) {
|
||||||
return {
|
return {
|
||||||
...point,
|
...point,
|
||||||
@@ -66,6 +97,36 @@ function PathConnector({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObje
|
|||||||
};
|
};
|
||||||
const existingTargets = point.connections.targets || [];
|
const existingTargets = point.connections.targets || [];
|
||||||
|
|
||||||
|
// Check connection limits
|
||||||
|
const fromPath = simulationStates.find(p => p.modeluuid === fromModelUUID);
|
||||||
|
if (fromPath) {
|
||||||
|
const hasConveyor = existingTargets.some(t => {
|
||||||
|
const targetPath = simulationStates.find(p => p.modeluuid === t.modelUUID);
|
||||||
|
return targetPath?.type === "Conveyor";
|
||||||
|
});
|
||||||
|
const hasArmBot = existingTargets.some(t => {
|
||||||
|
const targetPath = simulationStates.find(p => p.modeluuid === t.modelUUID);
|
||||||
|
return targetPath?.type === "ArmBot";
|
||||||
|
});
|
||||||
|
const hasVehicle = existingTargets.some(t => {
|
||||||
|
const targetPath = simulationStates.find(p => p.modeluuid === t.modelUUID);
|
||||||
|
return targetPath?.type === "Vehicle";
|
||||||
|
});
|
||||||
|
|
||||||
|
if (fromPath.type === "Conveyor" && hasConveyor) {
|
||||||
|
console.log("Conveyor can only connect to one other conveyor");
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
if (fromPath.type === "ArmBot" && hasArmBot) {
|
||||||
|
console.log("Conveyor can only connect to one ArmBot");
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
if (fromPath.type === "Vehicle" && hasVehicle) {
|
||||||
|
console.log("Conveyor can only connect to one Vehicle");
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!existingTargets.some((target) => target.modelUUID === reverseTarget.modelUUID && target.pointUUID === reverseTarget.pointUUID)) {
|
if (!existingTargets.some((target) => target.modelUUID === reverseTarget.modelUUID && target.pointUUID === reverseTarget.pointUUID)) {
|
||||||
return {
|
return {
|
||||||
...point,
|
...point,
|
||||||
@@ -494,21 +555,59 @@ function PathConnector({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObje
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For non-Vehicle paths, check if already connected
|
// For Conveyors, check connection limits in BOTH DIRECTIONS
|
||||||
if (intersected.userData.path.type !== "Vehicle") {
|
if (firstSelected && (firstPath?.type === "Conveyor" || secondPath?.type === "Conveyor")) {
|
||||||
const isAlreadyConnected = simulationStates.some((path) => {
|
const checkConveyorLimits = (path: any, pointUUID: string) => {
|
||||||
if (path.type === "Conveyor") {
|
if (path?.type === "Conveyor") {
|
||||||
return path.points.some(
|
const point = path.points.find((p: { uuid: string }) => p.uuid === pointUUID);
|
||||||
(point) =>
|
if (point) {
|
||||||
point.uuid === sphereUUID &&
|
return {
|
||||||
point.connections.targets.length > 0
|
hasConveyor: point.connections.targets.some((t: { modelUUID: string }) => {
|
||||||
);
|
const targetPath = simulationStates.find((p: { modeluuid: string }) => p.modeluuid === t.modelUUID);
|
||||||
|
return targetPath?.type === "Conveyor";
|
||||||
|
}),
|
||||||
|
hasArmBot: point.connections.targets.some((t: { modelUUID: string }) => {
|
||||||
|
const targetPath = simulationStates.find((p: { modeluuid: string }) => p.modeluuid === t.modelUUID);
|
||||||
|
return targetPath?.type === "ArmBot";
|
||||||
|
}),
|
||||||
|
hasVehicle: point.connections.targets.some((t: { modelUUID: string }) => {
|
||||||
|
const targetPath = simulationStates.find(p => p.modeluuid === t.modelUUID);
|
||||||
|
return targetPath?.type === "Vehicle";
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return { hasConveyor: false, hasArmBot: false, hasVehicle: false };
|
||||||
});
|
};
|
||||||
|
|
||||||
if (isAlreadyConnected) {
|
const firstConveyorLimits = checkConveyorLimits(firstPath, firstSelected?.sphereUUID);
|
||||||
console.log("Conveyor point is already connected. Ignoring.");
|
const secondConveyorLimits = checkConveyorLimits(secondPath, sphereUUID);
|
||||||
|
|
||||||
|
// Check if trying to connect two conveyors
|
||||||
|
if (firstPath?.type === "Conveyor" && secondPath?.type === "Conveyor") {
|
||||||
|
if (firstConveyorLimits.hasConveyor || secondConveyorLimits.hasConveyor) {
|
||||||
|
console.log("Conveyor can only connect to one other conveyor");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if trying to connect to an ArmBot when already connected to one
|
||||||
|
if (secondPath?.type === "ArmBot" && firstConveyorLimits.hasArmBot) {
|
||||||
|
console.log("Conveyor can only connect to one ArmBot");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (firstPath?.type === "ArmBot" && secondConveyorLimits.hasArmBot) {
|
||||||
|
console.log("Conveyor can only connect to one ArmBot");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if trying to connect to a Vehicle when already connected to one
|
||||||
|
if (secondPath?.type === "Vehicle" && firstConveyorLimits.hasVehicle) {
|
||||||
|
console.log("Conveyor can only connect to one Vehicle");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (firstPath?.type === "Vehicle" && secondConveyorLimits.hasVehicle) {
|
||||||
|
console.log("Conveyor can only connect to one Vehicle");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -762,6 +861,45 @@ function PathConnector({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObje
|
|||||||
!(firstPath?.type === 'Conveyor' || firstPath?.type === 'StaticMachine' ||
|
!(firstPath?.type === 'Conveyor' || firstPath?.type === 'StaticMachine' ||
|
||||||
secondPath?.type === 'Conveyor' || secondPath?.type === 'StaticMachine');
|
secondPath?.type === 'Conveyor' || secondPath?.type === 'StaticMachine');
|
||||||
|
|
||||||
|
// NEW: Check conveyor connection limits
|
||||||
|
let isConveyorAtMaxConnections = false;
|
||||||
|
if (firstPath?.type === 'Conveyor' || secondPath?.type === 'Conveyor') {
|
||||||
|
const conveyorPath = firstPath?.type === 'Conveyor' ? firstPath : secondPath;
|
||||||
|
const otherPath = firstPath?.type === 'Conveyor' ? secondPath : firstPath;
|
||||||
|
|
||||||
|
if (conveyorPath) {
|
||||||
|
const conveyorPoint = Array.isArray(conveyorPath.points)
|
||||||
|
? conveyorPath.points.find((p: { uuid: string }) => p.uuid ===
|
||||||
|
(firstPath?.type === 'Conveyor' ? firstSelected.sphereUUID : sphereUUID))
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (conveyorPoint) {
|
||||||
|
const hasConveyor = conveyorPoint.connections.targets.some((t: { modelUUID: string }) => {
|
||||||
|
const targetPath = simulationStates.find((p: { modeluuid: string }) => p.modeluuid === t.modelUUID);
|
||||||
|
return targetPath?.type === 'Conveyor';
|
||||||
|
});
|
||||||
|
const hasArmBot = conveyorPoint.connections.targets.some((t: { modelUUID: string }) => {
|
||||||
|
const targetPath = simulationStates.find((p: { modeluuid: string }) => p.modeluuid === t.modelUUID);
|
||||||
|
return targetPath?.type === 'ArmBot';
|
||||||
|
});
|
||||||
|
const hasVehicle = conveyorPoint.connections.targets.some((t: { modelUUID: string }) => {
|
||||||
|
const targetPath = simulationStates.find(p => p.modeluuid === t.modelUUID);
|
||||||
|
return targetPath?.type === 'Vehicle';
|
||||||
|
});
|
||||||
|
|
||||||
|
if (otherPath?.type === 'Conveyor' && hasConveyor) {
|
||||||
|
isConveyorAtMaxConnections = true;
|
||||||
|
}
|
||||||
|
if (otherPath?.type === 'ArmBot' && hasArmBot) {
|
||||||
|
isConveyorAtMaxConnections = true;
|
||||||
|
}
|
||||||
|
if (otherPath?.type === 'Vehicle' && hasVehicle) {
|
||||||
|
isConveyorAtMaxConnections = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!isDuplicateConnection &&
|
!isDuplicateConnection &&
|
||||||
!isVehicleToVehicle &&
|
!isVehicleToVehicle &&
|
||||||
@@ -773,6 +911,7 @@ function PathConnector({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObje
|
|||||||
!isArmBotToArmBot &&
|
!isArmBotToArmBot &&
|
||||||
!isArmBotToInvalidType &&
|
!isArmBotToInvalidType &&
|
||||||
!isArmBotAlreadyConnectedToStatic &&
|
!isArmBotAlreadyConnectedToStatic &&
|
||||||
|
!isConveyorAtMaxConnections && // NEW: Check conveyor limits
|
||||||
firstSelected.sphereUUID !== sphereUUID &&
|
firstSelected.sphereUUID !== sphereUUID &&
|
||||||
firstSelected.modelUUID !== modelUUID &&
|
firstSelected.modelUUID !== modelUUID &&
|
||||||
(firstSelected.isCorner || isConnectable) &&
|
(firstSelected.isCorner || isConnectable) &&
|
||||||
@@ -957,7 +1096,6 @@ function PathConnector({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObje
|
|||||||
state.modeluuid === connection2.model
|
state.modeluuid === connection2.model
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log("updatedPaths: ", updatedPaths);
|
|
||||||
updateBackend(updatedPaths);
|
updateBackend(updatedPaths);
|
||||||
|
|
||||||
setSimulationStates(updatedStates);
|
setSimulationStates(updatedStates);
|
||||||
|
|||||||
@@ -169,9 +169,9 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
|
|
||||||
const spawnPoint = findSpawnPoint(process);
|
const spawnPoint = findSpawnPoint(process);
|
||||||
if (!spawnPoint || !spawnPoint.actions) {
|
if (!spawnPoint || !spawnPoint.actions) {
|
||||||
console.log(
|
// console.log(
|
||||||
`Process ${process.id} has no valid spawn point or actions`
|
// `Process ${process.id} has no valid spawn point or actions`
|
||||||
);
|
// );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,10 +255,10 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
|
|
||||||
// Check connection status with debugging
|
// Check connection status with debugging
|
||||||
const isConnected = isConnectedToActiveArmBot(process.id);
|
const isConnected = isConnectedToActiveArmBot(process.id);
|
||||||
console.log(
|
// console.log(
|
||||||
`Process ${process.id} animation - connected:`,
|
// `Process ${process.id} animation - connected:`,
|
||||||
isConnected
|
// isConnected
|
||||||
);
|
// );
|
||||||
|
|
||||||
if (isConnected) {
|
if (isConnected) {
|
||||||
// Stop all animations when connected to active arm bot
|
// Stop all animations when connected to active arm bot
|
||||||
@@ -293,9 +293,9 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
currentTime - processState.processDelayStartTime >=
|
currentTime - processState.processDelayStartTime >=
|
||||||
effectiveDelayTime
|
effectiveDelayTime
|
||||||
) {
|
) {
|
||||||
console.log(
|
// console.log(
|
||||||
`Process ${process.id} delay completed, resuming animation`
|
// `Process ${process.id} delay completed, resuming animation`
|
||||||
);
|
// );
|
||||||
newStates[process.id] = {
|
newStates[process.id] = {
|
||||||
...processState,
|
...processState,
|
||||||
isProcessDelaying: false,
|
isProcessDelaying: false,
|
||||||
@@ -331,9 +331,9 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
[];
|
[];
|
||||||
|
|
||||||
if (path.length < 2) {
|
if (path.length < 2) {
|
||||||
console.log(
|
// console.log(
|
||||||
`Process ${process.id} has insufficient path points: ${path.length}`
|
// `Process ${process.id} has insufficient path points: ${path.length}`
|
||||||
);
|
// );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,9 +348,9 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
|
|
||||||
const currentRef = gltf?.scene ? obj.ref.current : obj.ref.current;
|
const currentRef = gltf?.scene ? obj.ref.current : obj.ref.current;
|
||||||
if (!currentRef) {
|
if (!currentRef) {
|
||||||
console.log(
|
// console.log(
|
||||||
`No reference for object ${objectId}, skipping animation`
|
// `No reference for object ${objectId}, skipping animation`
|
||||||
);
|
// );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,9 +378,9 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
stateRef.currentDelayDuration / speedRef.current;
|
stateRef.currentDelayDuration / speedRef.current;
|
||||||
|
|
||||||
if (currentTime - stateRef.delayStartTime >= effectiveDelayTime) {
|
if (currentTime - stateRef.delayStartTime >= effectiveDelayTime) {
|
||||||
console.log(
|
// console.log(
|
||||||
`Delay complete for object ${objectId}, resuming animation`
|
// `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;
|
||||||
@@ -408,9 +408,9 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
|
|
||||||
// Skip non-animating objects
|
// Skip non-animating objects
|
||||||
if (!stateRef.isAnimating) {
|
if (!stateRef.isAnimating) {
|
||||||
console.log(
|
// console.log(
|
||||||
`Object ${objectId} not animating, skipping animation updates`
|
// `Object ${objectId} not animating, skipping animation updates`
|
||||||
);
|
// );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,9 +454,9 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
|
|
||||||
if (shouldHide) {
|
if (shouldHide) {
|
||||||
if (isAgvPicking) {
|
if (isAgvPicking) {
|
||||||
console.log(
|
// console.log(
|
||||||
`AGV picking at last point for object ${objectId}, hiding object`
|
// `AGV picking at last point for object ${objectId}, hiding object`
|
||||||
);
|
// );
|
||||||
updatedObjects[objectId] = {
|
updatedObjects[objectId] = {
|
||||||
...obj,
|
...obj,
|
||||||
visible: false,
|
visible: false,
|
||||||
@@ -517,14 +517,14 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
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(
|
// console.log(
|
||||||
`Boosting progress for object ${objectId} after delay`
|
// `Boosting progress for object ${objectId} after delay`
|
||||||
);
|
// );
|
||||||
} else {
|
} else {
|
||||||
stateRef.progress += movement / distance;
|
stateRef.progress += movement / distance;
|
||||||
console.log(
|
// console.log(
|
||||||
`Object ${objectId} progress: ${stateRef.progress.toFixed(3)}`
|
// `Object ${objectId} progress: ${stateRef.progress.toFixed(3)}`
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle point transition
|
// Handle point transition
|
||||||
@@ -564,9 +564,9 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
|
|||||||
|
|
||||||
// Log if no animation is occurring when it should
|
// Log if no animation is occurring when it should
|
||||||
if (!animationOccurring && !isConnected) {
|
if (!animationOccurring && !isConnected) {
|
||||||
console.log(
|
// console.log(
|
||||||
`Warning: No animation occurring for process ${process.id} despite not being connected`
|
// `Warning: No animation occurring for process ${process.id} despite not being connected`
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
newStates[process.id] = {
|
newStates[process.id] = {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
usePlayButtonStore,
|
usePlayButtonStore,
|
||||||
useResetButtonStore,
|
useResetButtonStore,
|
||||||
} from "../../../store/usePlayButtonStore";
|
} from "../../../store/usePlayButtonStore";
|
||||||
import { usePlayAgv } from "../../../store/store";
|
import { usePlayAgv, useSimulationStates } from "../../../store/store";
|
||||||
|
|
||||||
interface ArmBotProcess {
|
interface ArmBotProcess {
|
||||||
triggerId: string;
|
triggerId: string;
|
||||||
@@ -82,6 +82,7 @@ export const useProcessAnimation = (
|
|||||||
const [animationStates, setAnimationStates] = useState<Record<string, EnhancedProcessAnimationState>>({});
|
const [animationStates, setAnimationStates] = useState<Record<string, EnhancedProcessAnimationState>>({});
|
||||||
const speedRef = useRef<number>(speed);
|
const speedRef = useRef<number>(speed);
|
||||||
const { PlayAgv, setPlayAgv } = usePlayAgv();
|
const { PlayAgv, setPlayAgv } = usePlayAgv();
|
||||||
|
const { simulationStates } = useSimulationStates();
|
||||||
|
|
||||||
// Effect hooks
|
// Effect hooks
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -514,8 +515,6 @@ export const useProcessAnimation = (
|
|||||||
|
|
||||||
newTriggerCounts[triggerKey] = (newTriggerCounts[triggerKey] || 0) + 1;
|
newTriggerCounts[triggerKey] = (newTriggerCounts[triggerKey] || 0) + 1;
|
||||||
|
|
||||||
shouldLog = true;
|
|
||||||
|
|
||||||
newTriggerLogs.push({ timestamp: currentTime, pointId: point.uuid, objectId, triggerId: trigger.uuid, });
|
newTriggerLogs.push({ timestamp: currentTime, pointId: point.uuid, objectId, triggerId: trigger.uuid, });
|
||||||
|
|
||||||
const connections = point.connections?.targets || [];
|
const connections = point.connections?.targets || [];
|
||||||
@@ -523,13 +522,19 @@ export const useProcessAnimation = (
|
|||||||
connections.forEach((connection) => {
|
connections.forEach((connection) => {
|
||||||
const connectedModelUUID = connection.modelUUID;
|
const connectedModelUUID = connection.modelUUID;
|
||||||
|
|
||||||
const matchingArmPath = armBotPaths.find((path) => path.modeluuid === connectedModelUUID);
|
const isConveyor = simulationStates.find((state) => state.modeluuid === connectedModelUUID && state.type === "Conveyor");
|
||||||
|
|
||||||
if (matchingArmPath) {
|
if (!isConveyor) {
|
||||||
deferredArmBotUpdates.current.push({
|
const matchingArmPath = armBotPaths.find((path) => path.modeluuid === connectedModelUUID);
|
||||||
uuid: connectedModelUUID,
|
|
||||||
triggerId: trigger.uuid,
|
if (matchingArmPath) {
|
||||||
});
|
deferredArmBotUpdates.current.push({
|
||||||
|
uuid: connectedModelUUID,
|
||||||
|
triggerId: trigger.uuid,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
shouldLog = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -592,6 +597,7 @@ export const useProcessAnimation = (
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// console.log('deferredArmBotUpdates: ', deferredArmBotUpdates);
|
||||||
if (deferredArmBotUpdates.current.length > 0) {
|
if (deferredArmBotUpdates.current.length > 0) {
|
||||||
const updates = [...deferredArmBotUpdates.current];
|
const updates = [...deferredArmBotUpdates.current];
|
||||||
deferredArmBotUpdates.current = [];
|
deferredArmBotUpdates.current = [];
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -710,4 +711,4 @@ input {
|
|||||||
.multi-email-invite-input.active {
|
.multi-email-invite-input.active {
|
||||||
border: 1px solid var(--accent-color);
|
border: 1px solid var(--accent-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
129
app/src/types/simulationTypes.d.ts
vendored
129
app/src/types/simulationTypes.d.ts
vendored
@@ -161,3 +161,132 @@ export type EventData = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
interface AssetEventSchema {
|
||||||
|
modelUuid: string;
|
||||||
|
modelName: string;
|
||||||
|
position: [number, number, number];
|
||||||
|
rotation: [number, number, number];
|
||||||
|
state: "idle" | "running" | "stopped" | "disabled" | "error";
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TriggerSchema {
|
||||||
|
triggerUuid: string;
|
||||||
|
triggerName: string;
|
||||||
|
triggerType: "onComplete" | "onStart" | "onStop" | "delay" | "onError";
|
||||||
|
delay: number;
|
||||||
|
triggeredAsset: {
|
||||||
|
triggeredModel: { modelName: string, modelUuid: string };
|
||||||
|
triggeredAction: { actionName: string, actionUuid: string };
|
||||||
|
} | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TransferPointSchema {
|
||||||
|
uuid: string;
|
||||||
|
position: [number, number, number];
|
||||||
|
rotation: [number, number, number];
|
||||||
|
actions: {
|
||||||
|
actionUuid: string;
|
||||||
|
actionName: string;
|
||||||
|
actionType: "default" | "spawn" | "swap" | "despawn";
|
||||||
|
material: string | "inherit";
|
||||||
|
delay: number | "inherit";
|
||||||
|
spawnInterval: number | "inherit";
|
||||||
|
spawnCount: number | "inherit";
|
||||||
|
triggers: TriggerSchema[] | [];
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VehiclePointSchema {
|
||||||
|
uuid: string;
|
||||||
|
position: [number, number, number];
|
||||||
|
rotation: [number, number, number];
|
||||||
|
actions: {
|
||||||
|
actionUuid: string;
|
||||||
|
actionName: string;
|
||||||
|
actionType: "travel";
|
||||||
|
material: string;
|
||||||
|
unLoadDuration: number;
|
||||||
|
loadCapacity: number;
|
||||||
|
pickUpPoint: { x: number; y: number, z: number } | {};
|
||||||
|
unLoadPoint: { x: number; y: number, z: number } | {};
|
||||||
|
triggers: TriggerSchema[] | [];
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RoboticArmPointSchema {
|
||||||
|
uuid: string;
|
||||||
|
position: [number, number, number];
|
||||||
|
rotation: [number, number, number];
|
||||||
|
actions: {
|
||||||
|
actionUuid: string;
|
||||||
|
actionName: string;
|
||||||
|
actionType: "pickAndPlace";
|
||||||
|
process: { startPoint: string; endPoint: string };
|
||||||
|
triggers: TriggerSchema[] | [];
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MachinePointSchema {
|
||||||
|
uuid: string;
|
||||||
|
position: [number, number, number];
|
||||||
|
rotation: [number, number, number];
|
||||||
|
actions: {
|
||||||
|
actionUuid: string;
|
||||||
|
actionName: string;
|
||||||
|
actionType: "process";
|
||||||
|
processTime: number;
|
||||||
|
swapMaterial: string;
|
||||||
|
triggers: TriggerSchema[] | [];
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StoragePointSchema {
|
||||||
|
uuid: string;
|
||||||
|
position: [number, number, number];
|
||||||
|
rotation: [number, number, number];
|
||||||
|
actions: {
|
||||||
|
actionUuid: string;
|
||||||
|
actionName: string;
|
||||||
|
actionType: "storage";
|
||||||
|
materials: { materialName: string; materialId: string; quantity: number }[];
|
||||||
|
storageCapacity: number;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TransferEventSchema extends AssetEventSchema {
|
||||||
|
type: "transfer";
|
||||||
|
speed: number;
|
||||||
|
points: TransferPointSchema[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VehicleSchemaEvent extends AssetEventSchema {
|
||||||
|
type: "vehicle";
|
||||||
|
speed: number;
|
||||||
|
point: VehiclePointSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RoboticArmSchemaEvent extends AssetEventSchema {
|
||||||
|
type: "roboticArm";
|
||||||
|
speed: number;
|
||||||
|
point: RoboticArmPointSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MachineSchemaEvent extends AssetEventSchema {
|
||||||
|
type: "machine";
|
||||||
|
point: MachinePointSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StorageSchemaEvent extends AssetEventSchema {
|
||||||
|
type: "storageUnit";
|
||||||
|
point: StoragePointSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
type EventsSchema = TransferEventSchema | VehicleSchemaEvent | RoboticArmSchemaEvent | MachineSchemaEvent | StorageSchemaEvent | [];
|
||||||
|
|
||||||
|
type productsSchema = {
|
||||||
|
productName: string;
|
||||||
|
productId: string;
|
||||||
|
eventsData: EventsSchema[];
|
||||||
|
}[] | []
|
||||||
Reference in New Issue
Block a user