updating UI

This commit is contained in:
Nalvazhuthi
2025-05-03 11:20:04 +05:30
47 changed files with 1770 additions and 1152 deletions

View File

@@ -1,11 +1,11 @@
import React from "react";
import { HelpIcon } from "../../icons/DashboardIcon";
import { useLogger } from "../log/LoggerContext";
import { HelpIcon } from "../icons/DashboardIcon";
import {
LogInfoIcon,
ErrorIcon,
WarningIcon,
} from "../../icons/ExportCommonIcons"; // Adjust path as needed
} from "../icons/ExportCommonIcons"; // Adjust path as needed
import { useLogger } from "../ui/log/LoggerContext";
const getLogIcon = (type: string) => {
switch (type) {
@@ -21,7 +21,7 @@ const getLogIcon = (type: string) => {
}
};
const Footer = () => {
const Footer: React.FC = () => {
const { logs, setIsLogListVisible } = useLogger();
const lastLog = logs.length > 0 ? logs[logs.length - 1] : null;
@@ -43,7 +43,10 @@ const Footer = () => {
</div>
<div className="logs-wrapper">
<div className="logs-detail" onClick={() => setIsLogListVisible(true)}>
<div
className={`logs-detail ${lastLog ? lastLog.type : ""}`}
onClick={() => setIsLogListVisible(true)}
>
{lastLog ? (
<>
<span className="log-icon">{getLogIcon(lastLog.type)}</span>

View File

@@ -12,7 +12,7 @@ export function ThroughputSummaryIcon() {
fillRule="evenodd"
clipRule="evenodd"
d="M13.9063 12.9046L14.2265 13.86L14.4378 14.5073C14.9906 16.2239 15.2594 17.2662 15.2594 17.7299C15.2594 18.8219 14.3742 19.7072 13.2822 19.7072C12.1902 19.7072 11.305 18.8219 11.305 17.7299C11.305 17.2106 11.6422 15.9654 12.3379 13.86L12.658 12.9046C12.8604 12.3082 13.704 12.3082 13.9063 12.9046ZM13.2822 7.84375C16.9222 7.84375 19.873 10.7945 19.873 14.4345C19.873 15.7565 19.4823 17.0219 18.7621 18.0974C18.5596 18.3999 18.1502 18.4809 17.8478 18.2784C17.5453 18.0758 17.4643 17.6665 17.6668 17.364C18.2428 16.5038 18.5548 15.4933 18.5548 14.4345C18.5548 11.5225 16.1942 9.16191 13.2822 9.16191C10.3702 9.16191 8.00956 11.5225 8.00956 14.4345C8.00956 15.4933 8.32153 16.5038 8.89752 17.364C9.10005 17.6665 9.01904 18.0758 8.71659 18.2784C8.41414 18.4809 8.00477 18.3999 7.80224 18.0974C7.08206 17.0219 6.69141 15.7565 6.69141 14.4345C6.69141 10.7945 9.6422 7.84375 13.2822 7.84375ZM13.2822 15.2247L13.0657 15.9238L12.9161 16.4319C12.7219 17.111 12.6231 17.5509 12.6231 17.7299C12.6231 18.0939 12.9182 18.389 13.2822 18.389C13.6462 18.389 13.9413 18.0939 13.9413 17.7299C13.9413 17.511 13.7936 16.9022 13.5044 15.9428L13.2822 15.2247Z"
fill="white"
fill="var(--text-color)"
/>
</svg>
);
@@ -33,7 +33,7 @@ export function ProductionCapacityIcon() {
fill-rule="evenodd"
clip-rule="evenodd"
d="M13.5167 7.88281H15.3741C15.668 7.88281 15.9053 8.09496 15.9053 8.35581V13.0575H9.079L9.00279 13.0583V8.35581C9.00279 8.09417 9.24007 7.88281 9.53393 7.88281H11.3921V10.251C11.3921 10.3138 11.4204 10.3735 11.4707 10.4183C11.5226 10.4633 11.5891 10.4879 11.6577 10.4875H13.2511C13.3201 10.4881 13.3868 10.4635 13.4389 10.4183C13.4625 10.3971 13.4815 10.3713 13.4949 10.3426C13.5082 10.3138 13.5157 10.2826 13.5167 10.251V7.88281ZM9.079 14.0389H15.921C16.4243 14.0491 16.9035 14.2561 17.2559 14.6157C17.6082 14.9752 17.8056 15.4585 17.8056 15.9619C17.8056 16.4653 17.6082 16.9486 17.2559 17.3082C16.9035 17.6677 16.4243 17.8748 15.921 17.885H9.079C8.82319 17.8901 8.56892 17.8442 8.33108 17.7499C8.09324 17.6556 7.87661 17.5147 7.69387 17.3357C7.51113 17.1566 7.36596 16.9428 7.26685 16.707C7.16775 16.4711 7.1167 16.2178 7.1167 15.9619C7.1167 15.7061 7.16775 15.4528 7.26685 15.2169C7.36596 14.981 7.51113 14.7673 7.69387 14.5882C7.87661 14.4091 8.09324 14.2683 8.33108 14.174C8.56892 14.0796 8.82319 14.0337 9.079 14.0389ZM10.4626 15.956C10.4626 16.1384 10.3902 16.3132 10.2613 16.4422C10.1323 16.5711 9.95748 16.6435 9.77514 16.6435C9.59281 16.6435 9.41794 16.5711 9.28901 16.4422C9.16008 16.3132 9.08764 16.1384 9.08764 15.956C9.08764 15.7737 9.16008 15.5988 9.28901 15.4699C9.41794 15.341 9.59281 15.2685 9.77514 15.2685C9.95748 15.2685 10.1323 15.341 10.2613 15.4699C10.3902 15.5988 10.4626 15.7737 10.4626 15.956ZM13.1914 15.956C13.1914 16.1384 13.119 16.3132 12.9901 16.4422C12.8611 16.5711 12.6863 16.6435 12.5039 16.6435C12.3216 16.6435 12.1467 16.5711 12.0178 16.4422C11.8889 16.3132 11.8164 16.1384 11.8164 15.956C11.8164 15.7737 11.8889 15.5988 12.0178 15.4699C12.1467 15.341 12.3216 15.2685 12.5039 15.2685C12.6863 15.2685 12.8611 15.341 12.9901 15.4699C13.119 15.5988 13.1914 15.7737 13.1914 15.956ZM15.2319 16.6427C15.4143 16.6427 15.5891 16.5703 15.7181 16.4414C15.847 16.3124 15.9194 16.1376 15.9194 15.9552C15.9194 15.7729 15.847 15.598 15.7181 15.4691C15.5891 15.3402 15.4143 15.2677 15.2319 15.2677C15.0496 15.2677 14.8747 15.3402 14.7458 15.4691C14.6169 15.598 14.5444 15.7729 14.5444 15.9552C14.5444 16.1376 14.6169 16.3124 14.7458 16.4414C14.8747 16.5703 15.0496 16.6427 15.2319 16.6427Z"
fill="white"
fill="var(--text-color)"
/>
</g>
<defs>
@@ -41,7 +41,7 @@ export function ProductionCapacityIcon() {
<rect
width="11"
height="11"
fill="white"
fill="var(--text-color)"
transform="translate(7 7)"
/>
</clipPath>
@@ -62,12 +62,12 @@ export function ROISummaryIcon() {
<rect y="0.515625" width="25" height="25" rx="12.5" fill="#28B9F3" />
<path
d="M6.00015 7.51562V19.0974H19.0002"
stroke="white"
stroke="var(--text-color)"
strokeLinecap="round"
/>
<path
d="M6.50037 15.0553L10.3102 11.847C10.6984 11.52 11.2701 11.5358 11.6397 11.8837L15.0095 15.0553L19.5004 11.2734"
stroke="white"
stroke="var(--text-color)"
strokeLinecap="round"
/>
</svg>
@@ -118,12 +118,12 @@ export function ROISummaryProductName() {
>
<path
d="M16.9999 6.48175L9.83331 2.89844L2.66669 6.48175V13.6484L9.83331 17.2317L16.9999 13.6484V6.48175Z"
stroke="#2B3344"
stroke="var(--text-color)"
stroke-linejoin="round"
/>
<path
d="M2.66669 6.47916L9.83331 10.0625M9.83331 10.0625V17.2291M9.83331 10.0625L16.9999 6.47916M13.4166 4.6875L6.25 8.27081"
stroke="#2B3344"
stroke="var(--text-color)"
stroke-linecap="round"
stroke-linejoin="round"
/>

View File

@@ -35,7 +35,7 @@ const ActionsList: React.FC<ActionsListProps> = ({
const handleRenameAction = (newName: string) => {
if (!selectedAction.actionId) return;
const event = renameAction(selectedAction.actionId, newName);
const event = renameAction(selectedProduct.productId, selectedAction.actionId, newName);
if (event) {
upsertProductOrEventApi({

View File

@@ -72,7 +72,7 @@ function ConveyorMechanics() {
const validOption = option as | "default" | "spawn" | "swap" | "delay" | "despawn";
setActiveOption(validOption);
const event = updateAction(selectedPointData.action.actionUuid, {
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, {
actionType: validOption,
});
@@ -88,7 +88,7 @@ function ConveyorMechanics() {
const handleRenameAction = (newName: string) => {
if (!selectedEventData || !selectedPointData) return;
const event = updateAction(selectedPointData.action.actionUuid, { actionName: newName });
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionName: newName });
if (event) {
updateBackend(
@@ -102,7 +102,7 @@ function ConveyorMechanics() {
const handleSpawnCountChange = (value: string) => {
if (!selectedEventData || !selectedPointData) return;
const event = updateAction(selectedPointData.action.actionUuid, {
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, {
spawnCount: value === "inherit" ? "inherit" : parseFloat(value),
});
@@ -118,7 +118,7 @@ function ConveyorMechanics() {
const handleSpawnIntervalChange = (value: string) => {
if (!selectedEventData || !selectedPointData) return;
const event = updateAction(selectedPointData.action.actionUuid, {
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, {
spawnInterval: value === "inherit" ? "inherit" : parseFloat(value),
});
@@ -134,7 +134,7 @@ function ConveyorMechanics() {
const handleMaterialSelect = (material: string) => {
if (!selectedEventData || !selectedPointData) return;
const event = updateAction(selectedPointData.action.actionUuid, { material });
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { material });
if (event) {
updateBackend(
@@ -148,7 +148,7 @@ function ConveyorMechanics() {
const handleDelayChange = (value: string) => {
if (!selectedEventData || !selectedPointData) return;
const event = updateAction(selectedPointData.action.actionUuid, {
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, {
delay: value === "inherit" ? "inherit" : parseFloat(value),
});
@@ -271,7 +271,7 @@ function ConveyorMechanics() {
</div>
</div>
<div className="tirgger">
<Trigger />
<Trigger selectedPointData={selectedPointData as any} type= {'Conveyor'}/>
</div>
</section>
</>

View File

@@ -6,6 +6,7 @@ import { useSelectedEventData, useSelectedProduct } from "../../../../../../stor
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
import ProcessAction from "../actions/ProcessAction";
import ActionsList from "../components/ActionsList";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi";
function MachineMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "process">("default");
@@ -14,6 +15,9 @@ function MachineMechanics() {
const { getPointByUuid, updateAction } = useProductStore();
const { selectedProduct } = useSelectedProduct();
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
useEffect(() => {
if (selectedEventData) {
const point = getPointByUuid(
@@ -28,31 +32,54 @@ function MachineMechanics() {
}
}, [selectedProduct, selectedEventData, getPointByUuid]);
const updateBackend = (
productName: string,
productId: string,
organization: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productId: productId,
organization: organization,
eventDatas: eventData
})
}
const handleActionTypeChange = (option: string) => {
if (!selectedEventData || !selectedPointData) return;
const validOption = option as "process";
setActiveOption(validOption);
updateAction(selectedPointData.action.actionUuid, {
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, {
actionType: validOption,
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
};
const handleRenameAction = (newName: string) => {
if (!selectedPointData) return;
updateAction(selectedPointData.action.actionUuid, { actionName: newName });
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionName: newName });
};
const handleProcessTimeChange = (value: string) => {
if (!selectedPointData) return;
updateAction(selectedPointData.action.actionUuid, {
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, {
processTime: parseFloat(value),
});
};
const handleMaterialSelect = (material: string) => {
if (!selectedPointData) return;
updateAction(selectedPointData.action.actionUuid, {
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, {
swapMaterial: material,
});
};

View File

@@ -59,7 +59,7 @@ function RoboticArmMechanics() {
const handleRenameAction = (newName: string) => {
if (!selectedAction.actionId) return;
const event = updateAction(selectedAction.actionId, { actionName: newName });
const event = updateAction(selectedProduct.productId, selectedAction.actionId, { actionName: newName });
if (selectedPointData) {
const updatedActions = selectedPointData.actions.map((action) =>
@@ -101,7 +101,7 @@ function RoboticArmMechanics() {
if (!selectedAction.actionId || !selectedPointData) return;
const [x, y, z] = value.split(",").map(Number);
const event = updateAction(selectedAction.actionId, {
const event = updateAction(selectedProduct.productId, selectedAction.actionId, {
process: {
startPoint: [x, y, z] as [number, number, number],
endPoint: selectedPointData.actions.find((a) => a.actionUuid === selectedAction.actionId)?.process.endPoint || null,
@@ -122,7 +122,7 @@ function RoboticArmMechanics() {
if (!selectedAction.actionId || !selectedPointData) return;
const [x, y, z] = value.split(",").map(Number);
const event = updateAction(selectedAction.actionId, {
const event = updateAction(selectedProduct.productId, selectedAction.actionId, {
process: {
startPoint: selectedPointData.actions.find((a) => a.actionUuid === selectedAction.actionId)?.process.startPoint || null,
endPoint: [x, y, z] as [number, number, number],
@@ -181,7 +181,7 @@ function RoboticArmMechanics() {
const handleDeleteAction = (actionUuid: string) => {
if (!selectedPointData) return;
const event = removeAction(actionUuid);
const event = removeAction(selectedProduct.productId, actionUuid);
if (event) {
updateBackend(

View File

@@ -6,6 +6,7 @@ import { useSelectedEventData, useSelectedProduct } from "../../../../../../stor
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
import StorageAction from "../actions/StorageAction";
import ActionsList from "../components/ActionsList";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi";
function StorageMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "store" | "spawn">("default");
@@ -14,6 +15,9 @@ function StorageMechanics() {
const { getPointByUuid, updateAction } = useProductStore();
const { selectedProduct } = useSelectedProduct();
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
useEffect(() => {
if (selectedEventData) {
const point = getPointByUuid(
@@ -28,26 +32,67 @@ function StorageMechanics() {
}
}, [selectedProduct, selectedEventData, getPointByUuid]);
const updateBackend = (
productName: string,
productId: string,
organization: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productId: productId,
organization: organization,
eventDatas: eventData
})
}
const handleActionTypeChange = (option: string) => {
if (!selectedEventData || !selectedPointData) return;
const validOption = option as "store" | "spawn";
setActiveOption(validOption);
updateAction(selectedPointData.action.actionUuid, {
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, {
actionType: validOption,
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
};
const handleRenameAction = (newName: string) => {
if (!selectedPointData) return;
updateAction(selectedPointData.action.actionUuid, { actionName: newName });
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionName: newName });
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
};
const handleCapacityChange = (value: string) => {
if (!selectedPointData) return;
updateAction(selectedPointData.action.actionUuid, {
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, {
storageCapacity: parseInt(value),
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
};
// Get current values from store

View File

@@ -10,6 +10,7 @@ import {
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
import TravelAction from "../actions/TravelAction";
import ActionsList from "../components/ActionsList";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi";
function VehicleMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "travel">("default");
@@ -18,8 +19,11 @@ function VehicleMechanics() {
const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction } = useProductStore();
const { selectedProduct } = useSelectedProduct();
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
useEffect(() => {
if (selectedEventData) {
if (selectedEventData && selectedEventData.data.type === 'vehicle') {
const point = getPointByUuid(
selectedProduct.productId,
selectedEventData.data.modelUuid,
@@ -33,11 +37,34 @@ function VehicleMechanics() {
}
}, [selectedProduct, selectedEventData, getPointByUuid]);
const updateBackend = (
productName: string,
productId: string,
organization: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productId: productId,
organization: organization,
eventDatas: eventData
})
}
const handleSpeedChange = (value: string) => {
if (!selectedEventData) return;
updateEvent(selectedProduct.productId, selectedEventData.data.modelUuid, {
const event = updateEvent(selectedProduct.productId, selectedEventData.data.modelUuid, {
speed: parseFloat(value),
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
};
const handleActionTypeChange = (option: string) => {
@@ -45,28 +72,64 @@ function VehicleMechanics() {
const validOption = option as "travel";
setActiveOption(validOption);
updateAction(selectedPointData.action.actionUuid, {
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, {
actionType: validOption,
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
};
const handleRenameAction = (newName: string) => {
if (!selectedPointData) return;
updateAction(selectedPointData.action.actionUuid, { actionName: newName });
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionName: newName });
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
};
const handleLoadCapacityChange = (value: string) => {
if (!selectedPointData) return;
updateAction(selectedPointData.action.actionUuid, {
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, {
loadCapacity: parseFloat(value),
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
};
const handleUnloadDurationChange = (value: string) => {
if (!selectedPointData) return;
updateAction(selectedPointData.action.actionUuid, {
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, {
unLoadDuration: parseFloat(value),
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
};
const handlePickPointChange = (value: string) => {

View File

@@ -1,4 +1,4 @@
import React, { useRef, useState } from "react";
import React, { useEffect, useRef, useState } from "react";
import {
AddIcon,
RemoveIcon,
@@ -7,38 +7,47 @@ import {
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import { handleResize } from "../../../../../../functions/handleResizePannel";
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
import { useSelectedProduct } from "../../../../../../store/simulation/useSimulationStore";
const Trigger: React.FC = () => {
// State to hold the list of triggers
const [triggers, setTriggers] = useState<string[]>(["Trigger 1"]);
const [selectedTrigger, setSelectedTrigger] = useState<string>("Trigger 1");
type TriggerProps = {
selectedPointData?: PointsScheme | undefined;
type?: 'Conveyor' | 'Vehicle' | 'RoboticArm' | 'Machine' | 'StorageUnit';
};
const Trigger = ({ selectedPointData, type }: TriggerProps) => {
const [currentAction, setCurrentAction] = useState<string | undefined>();
const { selectedProduct } = useSelectedProduct();
const { getActionByUuid } = useProductStore();
const [triggers, setTriggers] = useState<TriggerSchema[]>([]);
const [selectedTrigger, setSelectedTrigger] = useState<TriggerSchema | undefined>();
const [activeOption, setActiveOption] = useState("onComplete");
const triggersContainerRef = useRef<HTMLDivElement>(null);
// States for dropdowns
const [triggeredModel, setTriggeredModel] = useState<string[]>([]);
const [triggeredPoint, setTriggeredPoint] = useState<string[]>([]);
const [triggeredAction, setTriggeredAction] = useState<string[]>([]);
useEffect(() => {
if (!selectedPointData) return;
if (type === 'Conveyor' || type === 'Vehicle' || type === 'Machine' || type === 'StorageUnit') {
setCurrentAction((selectedPointData as ConveyorPointSchema).action.actionUuid);
}
}, [selectedPointData]);
useEffect(() => {
if (!currentAction || !selectedProduct) return;
const action = getActionByUuid(selectedProduct.productId, currentAction);
setTriggers(action?.triggers || []);
setSelectedTrigger(action?.triggers[0] || undefined);
}, [currentAction, selectedProduct]);
// Function to handle adding a new trigger
const addTrigger = (): void => {
const newTrigger = `Trigger ${triggers.length + 1}`;
setTriggers([...triggers, newTrigger]); // Add new trigger to the state
// Initialize the states for the new trigger
setTriggeredModel([...triggeredModel, ""]);
setTriggeredPoint([...triggeredPoint, ""]);
setTriggeredAction([...triggeredAction, ""]);
};
// Function to handle removing a trigger
const removeTrigger = (index: number): void => {
setTriggers(triggers.filter((_, i) => i !== index)); // Remove trigger by index
setTriggeredModel(triggeredModel.filter((_, i) => i !== index));
setTriggeredPoint(triggeredPoint.filter((_, i) => i !== index));
setTriggeredAction(triggeredAction.filter((_, i) => i !== index));
const removeTrigger = (triggerUuid: string): void => {
};
const triggeredModel = selectedTrigger?.triggeredAsset?.triggeredModel || { modelName: "Select Model", modelUuid: "" };
const triggeredPoint = selectedTrigger?.triggeredAsset?.triggeredPoint || { pointName: "Select Point", pointUuid: "" };
const triggeredAction = selectedTrigger?.triggeredAsset?.triggeredAction || { actionName: "Select Action", actionUuid: "" };
return (
<div className="trigger-wrapper">
<div className="header">
@@ -58,21 +67,19 @@ const Trigger: React.FC = () => {
style={{ height: "120px" }}
>
<div className="list-container">
{triggers.map((trigger: any, index: number) => (
{triggers.map((trigger) => (
<div
key={index}
className={`list-item ${
selectedTrigger === trigger ? "active" : ""
}`}
key={trigger.triggerUuid}
className={`list-item ${selectedTrigger?.triggerUuid === trigger.triggerUuid ? "active" : ""}`}
onClick={() => setSelectedTrigger(trigger)}
>
<button className="value" onClick={() => {}}>
<RenameInput value={trigger} onRename={() => {}} />
<button className="value" onClick={() => { }}>
<RenameInput value={trigger.triggerName} onRename={() => { }} />
</button>
{triggers.length > 1 && (
<button
className="remove-button"
onClick={() => removeTrigger(index)}
onClick={() => removeTrigger(trigger.triggerUuid)}
>
<RemoveIcon />
</button>
@@ -89,9 +96,9 @@ const Trigger: React.FC = () => {
</button>
</div>
<div className="trigger-item">
<div className="trigger-name">{selectedTrigger}</div>
<div className="trigger-name">{selectedTrigger?.triggerName}</div>
<LabledDropdown
label="Trigger on"
label="Trigger Type"
defaultOption={activeOption}
options={["onComplete", "onStart", "onStop", "delay"]}
onSelect={(option) => setActiveOption(option)}
@@ -99,33 +106,21 @@ const Trigger: React.FC = () => {
<div className="trigger-options">
<LabledDropdown
label="Triggered Object"
defaultOption={triggeredModel[0] || "Select Model"}
options={["Model 1", "Model 2", "Model 3"]}
onSelect={(option) => {
const newModel = [...triggeredModel];
newModel[0] = option;
setTriggeredModel(newModel);
}}
defaultOption={triggeredModel.modelName}
options={[]}
onSelect={(option) => { }}
/>
<LabledDropdown
label="Triggered Point"
defaultOption={triggeredPoint[0] || "Select Point"}
options={["Point 1", "Point 2", "Point 3"]}
onSelect={(option) => {
const newPoint = [...triggeredPoint];
newPoint[0] = option;
setTriggeredPoint(newPoint);
}}
defaultOption={triggeredPoint.pointName}
options={[]}
onSelect={(option) => { }}
/>
<LabledDropdown
label="Triggered Action"
defaultOption={triggeredAction[0] || "Select Action"}
options={["Action 1", "Action 2", "Action 3"]}
onSelect={(option) => {
const newAction = [...triggeredAction];
newAction[0] = option;
setTriggeredAction(newAction);
}}
defaultOption={triggeredAction.actionName}
options={[]}
onSelect={(option) => { }}
/>
</div>
</div>

View File

@@ -7,6 +7,7 @@ import {
SonarCrownIcon,
} from "../../icons/analysis";
import SemiCircleProgress from "./SemiCircleProgress";
import { ArrowIcon } from "../../icons/ExportCommonIcons";
const ROISummary = ({
roiSummaryData = {
@@ -129,7 +130,7 @@ const ROISummary = ({
</div>
<span className={`expand-icon ${isTableOpen ? "open" : ""}`}>
{isTableOpen ? "⌵" : "⌵"}
<ArrowIcon />
</span>
</div>
<div

View File

@@ -1,25 +1,38 @@
import React from "react";
const SemiCircleProgress = () => {
const progress = 50;
const SemiCircleProgress = ({ progress = 10 }) => {
const clampedProgress = Math.min(Math.max(progress, 0), 100);
const gradientProgress = clampedProgress * 0.5;
const radius = 80;
const strokeWidth = 20;
const circumference = Math.PI * radius;
const strokeDashoffset =
circumference - (clampedProgress / 100) * circumference;
return (
<div className="semi-circle-wrapper">
<div
className="semi-circle"
style={{
background: `conic-gradient(from 270deg, skyblue 0% ${gradientProgress}%, lightgray ${gradientProgress}% 100%)`,
}}
>
<div className="progress-cover"></div>
</div>
<div className="svg-half-donut">
<svg width="200" height="100" viewBox="0 0 200 100">
{/* Background track */}
<path
d="M20,100 A80,80 0 0,1 180,100"
fill="none"
stroke="lightgray"
strokeWidth={strokeWidth}
/>
{/* Progress track */}
<path
d="M20,100 A80,80 0 0,1 180,100"
fill="none"
stroke="skyblue"
strokeWidth={strokeWidth}
strokeDasharray={circumference}
strokeDashoffset={strokeDashoffset}
strokeLinecap="round"
/>
</svg>
<div className="label-wrapper">
<div className="label">{clampedProgress}%</div>
<div className="label-content">Years</div>
</div>
<div className="content">you're on track to hit it by July 2029</div>
</div>
);
};

View File

@@ -2,7 +2,6 @@
import React, { useState } from "react";
import {
LogListIcon,
TickIcon,
LogInfoIcon,
WarningIcon,
ErrorIcon,
@@ -38,8 +37,17 @@ const LogList: React.FC = () => {
: [...logs].filter((log) => log.type === selectedTab).reverse();
return (
<div className="log-list-container">
<div className="log-list-wrapper">
// eslint-disable-next-line
<div
className="log-list-container"
onClick={() => setIsLogListVisible(false)}
>
<div
className="log-list-wrapper"
onClick={(e) => {
e.stopPropagation();
}}
>
<div className="log-header">
<div className="log-header-wrapper">
<div className="icon">
@@ -47,9 +55,9 @@ const LogList: React.FC = () => {
</div>
<div className="head">Log List</div>
</div>
<div className="close" onClick={() => setIsLogListVisible(false)}>
{/* <CloseIcon /> */}X
</div>
<button className="close" onClick={() => setIsLogListVisible(false)}>
<CloseIcon />
</button>
</div>
{/* Tabs */}
@@ -69,13 +77,12 @@ const LogList: React.FC = () => {
<div className="log-entry-wrapper">
{filteredLogs.map((log) => (
<div key={log.id} className={`log-entry ${log.type}`}>
<div className="tick">
<TickIcon />
</div>
<div className="log-icon">{getLogIcon(log.type)}</div>
<div className="log-entry-message">
[{formatTimestamp(log.timestamp)}] [{log.type.toUpperCase()}]{" "}
{log.message}
<div className="log-entry-message-container">
<div className="log-entry-message">{log.message}</div>
<div className="message-time">
{formatTimestamp(log.timestamp)}
</div>
</div>
</div>
))}

View File

@@ -187,7 +187,7 @@ function processLoadedModel(
},
]);
if (item.eventData.type === "vehicle") {
if (item.eventData.type === "Vehicle") {
const vehicleEvent: VehicleEventSchema = {
modelUuid: item.modelUuid,
modelName: item.modelName,
@@ -202,11 +202,11 @@ function processLoadedModel(
rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Vehicle Action",
actionName: "Action 1",
actionType: "travel",
unLoadDuration: 5,
loadCapacity: 10,
steeringAngle:0,
steeringAngle: 0,
pickUpPoint: null,
unLoadPoint: null,
triggers: []
@@ -254,7 +254,7 @@ function processLoadedModel(
rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Process Action",
actionName: "Action 1",
actionType: "process",
processTime: 10,
swapMaterial: "material-id",
@@ -279,7 +279,7 @@ function processLoadedModel(
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Pick and Place",
actionName: "Action 1",
actionType: "pickAndPlace",
process: {
startPoint: [0, 0, 0],

View File

@@ -186,22 +186,59 @@ async function handleModelLoad(
state: "idle",
type: 'transfer',
speed: 1,
points: data.points.map((point: THREE.Vector3, index: number) => ({
points: data.points.map((point: THREE.Vector3, index: number) => {
const triggers: TriggerSchema[] = [];
if (data.points && index < data.points.length - 1) {
triggers.push({
triggerUuid: THREE.MathUtils.generateUUID(),
triggerName: `Trigger 1`,
triggerType: "onComplete",
delay: 0,
triggeredAsset: {
triggeredModel: {
modelName: newFloorItem.modelName,
modelUuid: newFloorItem.modelUuid
},
triggeredPoint: {
pointName: `Point`,
pointUuid: ""
},
triggeredAction: {
actionName: `Action 1`,
actionUuid: ""
}
}
});
}
return {
uuid: THREE.MathUtils.generateUUID(),
position: [point.x, point.y, point.z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: `Action ${index}`,
actionName: `Action 1`,
actionType: 'default',
material: 'Default Material',
delay: 0,
spawnInterval: 5,
spawnCount: 1,
triggers: []
triggers: triggers
}
}))
};
})
};
for (let i = 0; i < ConveyorEvent.points.length - 1; i++) {
const currentPoint = ConveyorEvent.points[i];
const nextPoint = ConveyorEvent.points[i + 1];
if (currentPoint.action.triggers.length > 0) {
currentPoint.action.triggers[0].triggeredAsset!.triggeredPoint.pointUuid = nextPoint.uuid;
currentPoint.action.triggers[0].triggeredAsset!.triggeredAction!.actionUuid = nextPoint.action.actionUuid;
}
}
addEvent(ConveyorEvent);
eventData.points = ConveyorEvent.points.map(point => ({
uuid: point.uuid,
@@ -228,7 +265,7 @@ async function handleModelLoad(
actionType: "travel",
unLoadDuration: 5,
loadCapacity: 10,
steeringAngle:0,
steeringAngle: 0,
pickUpPoint: null,
unLoadPoint: null,
triggers: []

View File

@@ -9,6 +9,7 @@ import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifie
import { useEventsStore } from "../../../../store/simulation/useEventsStore";
import { useProductStore } from "../../../../store/simulation/useProductStore";
import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore";
import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi";
function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) {
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
@@ -16,10 +17,28 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje
const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
const { selectedProduct } = useSelectedProduct();
const { floorItems, setFloorItems } = useFloorItems();
const { socket } = useSocketStore();
const itemsData = useRef<Types.FloorItems>([]);
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const updateBackend = (
productName: string,
productId: string,
organization: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productId: productId,
organization: organization,
eventDatas: eventData
})
}
useEffect(() => {
if (!camera || !scene || toggleView || !itemsGroupRef.current) return;
@@ -190,10 +209,19 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje
})
}
if (productData) {
useProductStore.getState().updateEvent(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid, {
const event = useProductStore.getState().updateEvent(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid, {
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
})
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
}
}
@@ -203,9 +231,6 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje
return updatedItems;
});
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "default";
//REST
// await setFloorItemApi(

View File

@@ -8,6 +8,7 @@ import * as Types from "../../../../types/world/worldTypes";
import { useEventsStore } from "../../../../store/simulation/useEventsStore";
import { useProductStore } from "../../../../store/simulation/useProductStore";
import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore";
import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi";
function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, boundingBoxRef }: any) {
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
@@ -15,10 +16,28 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
const { selectedProduct } = useSelectedProduct();
const { floorItems, setFloorItems } = useFloorItems();
const { socket } = useSocketStore();
const itemsData = useRef<Types.FloorItems>([]);
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const updateBackend = (
productName: string,
productId: string,
organization: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productId: productId,
organization: organization,
eventDatas: eventData
})
}
const prevPointerPosition = useRef<THREE.Vector2 | null>(null);
useEffect(() => {
@@ -190,10 +209,19 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
})
}
if (productData) {
useProductStore.getState().updateEvent(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid, {
const event = useProductStore.getState().updateEvent(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid, {
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
})
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
}
}
@@ -203,9 +231,6 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
return updatedItems;
});
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "default";
//REST
// await setFloorItemApi(

View File

@@ -1,22 +1,29 @@
import React, { useEffect, useRef, useState } from "react";
import * as THREE from "three";
import { useEventsStore } from "../../../../../store/simulation/useEventsStore";
import useModuleStore from "../../../../../store/useModuleStore";
import useModuleStore, { useSubModuleStore } from "../../../../../store/useModuleStore";
import { TransformControls } from "@react-three/drei";
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
import {
useSelectedEventSphere,
useSelectedEventData,
useIsDragging,
useIsRotating,
} from "../../../../../store/simulation/useSimulationStore";
import { useThree } from "@react-three/fiber";
function PointsCreator() {
const { gl, raycaster, scene, pointer, camera } = useThree();
const { subModule } = useSubModuleStore();
const { events, updatePoint, getPointByUuid, getEventByModelUuid } = useEventsStore();
const { activeModule } = useModuleStore();
const transformRef = useRef<any>(null);
const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null);
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
const { selectedEventSphere, setSelectedEventSphere, clearSelectedEventSphere, } = useSelectedEventSphere();
const { selectedEventData, setSelectedEventData, clearSelectedEventData } = useSelectedEventData();
const { setSelectedEventData, clearSelectedEventData } = useSelectedEventData();
const { isDragging } = useIsDragging();
const { isRotating } = useIsRotating();
useEffect(() => {
if (selectedEventSphere) {
@@ -72,6 +79,53 @@ function PointsCreator() {
}
};
useEffect(() => {
const canvasElement = gl.domElement;
let drag = false;
let isMouseDown = false;
const onMouseDown = () => {
isMouseDown = true;
drag = false;
};
const onMouseUp = () => {
if (selectedEventSphere && !drag) {
raycaster.setFromCamera(pointer, camera);
const intersects = raycaster
.intersectObjects(scene.children, true)
.filter(
(intersect) =>
intersect.object.name === ('Event-Sphere')
);
if (intersects.length === 0) {
clearSelectedEventSphere();
setTransformMode(null);
}
}
}
const onMouseMove = () => {
if (isMouseDown) {
drag = true;
}
};
if (subModule === 'mechanics') {
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
}
return () => {
canvasElement.removeEventListener("mousedown", onMouseDown);
canvasElement.removeEventListener("mouseup", onMouseUp);
canvasElement.removeEventListener("mousemove", onMouseMove);
};
}, [gl, subModule, selectedEventSphere]);
return (
<>
{activeModule === "simulation" && (
@@ -92,12 +146,6 @@ function PointsCreator() {
sphereRefs.current[point.uuid]
);
}}
onPointerMissed={() => {
if (selectedEventData?.data.type !== 'vehicle') {
clearSelectedEventSphere();
}
setTransformMode(null);
}}
key={`${i}-${j}`}
position={new THREE.Vector3(...point.position)}
// rotation={new THREE.Euler(...point.rotation)}
@@ -122,10 +170,6 @@ function PointsCreator() {
sphereRefs.current[event.point.uuid]
);
}}
onPointerMissed={() => {
clearSelectedEventSphere();
setTransformMode(null);
}}
position={new THREE.Vector3(...event.point.position)}
// rotation={new THREE.Euler(...event.point.rotation)}
userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }}
@@ -148,10 +192,6 @@ function PointsCreator() {
sphereRefs.current[event.point.uuid]
);
}}
onPointerMissed={() => {
clearSelectedEventSphere();
setTransformMode(null);
}}
position={new THREE.Vector3(...event.point.position)}
// rotation={new THREE.Euler(...event.point.rotation)}
userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }}
@@ -174,10 +214,6 @@ function PointsCreator() {
sphereRefs.current[event.point.uuid]
);
}}
onPointerMissed={() => {
clearSelectedEventSphere();
setTransformMode(null);
}}
position={new THREE.Vector3(...event.point.position)}
// rotation={new THREE.Euler(...event.point.rotation)}
userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }}

View File

@@ -1,7 +1,43 @@
import React from 'react'
import React, { useEffect } from 'react'
import MachineInstances from './instances/machineInstances'
import { useMachineStore } from '../../../store/simulation/useMachineStore'
import { useSelectedProduct } from '../../../store/simulation/useSimulationStore';
function Machine() {
const { addMachine, addCurrentAction, removeMachine } = useMachineStore();
const { selectedProduct } = useSelectedProduct();
const machineSample: MachineEventSchema[] = [
{
modelUuid: "machine-1234-5678-9012",
modelName: "CNC Milling Machine",
position: [10, 0, 5],
rotation: [0, 0, 0],
state: "idle",
type: "machine",
point: {
uuid: "machine-point-9876-5432-1098",
position: [10, 0.5, 5.2],
rotation: [0, 0, 0],
action: {
actionUuid: "machine-action-2468-1357-8024",
actionName: "Metal Processing",
actionType: "process",
processTime: 10,
swapMaterial: "steel",
triggers: []
}
}
}
];
useEffect(() => {
removeMachine(machineSample[0].modelUuid);
addMachine(selectedProduct.productId, machineSample[0]);
// addCurrentAction(machineSample[0].modelUuid, machineSample[0].point.action.actionUuid);
}, [])
return (
<>

View File

@@ -53,7 +53,17 @@ function AddOrRemoveEventsInProducts() {
const canvasElement = gl.domElement;
if (!canvasElement) return;
let intersects = raycaster.intersectObjects(scene.children, true);
const intersects = raycaster
.intersectObjects(scene.children, true)
.filter(
(intersect) =>
!intersect.object.name.includes("Roof") &&
!intersect.object.name.includes("MeasurementReference") &&
!intersect.object.name.includes("agv-collider") &&
!(intersect.object.type === "GridHelper") &&
!(intersect.object?.parent?.name.includes('zones')) &&
!(intersect.object?.parent?.name.includes('Zone'))
);
if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
let currentObject = intersects[0].object;
@@ -116,6 +126,7 @@ function AddOrRemoveEventsInProducts() {
};
}, [gl, subModule, selectedProduct, selectedAsset]);
return (
<></>
)

View File

@@ -7,7 +7,7 @@ import { upsertProductOrEventApi } from '../../../services/simulation/UpsertProd
import { getAllProductsApi } from '../../../services/simulation/getallProductsApi';
function Products() {
const { products, addProduct, setProducts } = useProductStore();
const { addProduct, setProducts } = useProductStore();
const { setSelectedProduct } = useSelectedProduct();
useEffect(() => {
@@ -27,9 +27,6 @@ function Products() {
})
}, [])
useEffect(() => {
}, [])
return (
<>

View File

@@ -170,8 +170,8 @@ function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) {
}
}
const logStatus = (id: string, status: string) => {
// console.log(id + "," + status);
console.log( status);
//
}
return (

View File

@@ -23,7 +23,7 @@ function Simulation() {
}, [events])
useEffect(() => {
// console.log('products: ', products);
console.log('products: ', products);
}, [products])
return (

View File

@@ -1,5 +1,5 @@
import { useEffect, useRef, useState } from "react";
import { useThree } from "@react-three/fiber";
import { useFrame, useThree } from "@react-three/fiber";
import * as THREE from "three";
import { useSubModuleStore } from "../../../../store/useModuleStore";
import { useSelectedAsset } from "../../../../store/simulation/useSimulationStore";
@@ -7,21 +7,28 @@ import { useProductStore } from "../../../../store/simulation/useProductStore";
import { useEventsStore } from "../../../../store/simulation/useEventsStore";
import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore";
import { handleAddEventToProduct } from "../../events/points/functions/handleAddEventToProduct";
import { QuadraticBezierLine } from "@react-three/drei";
import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi";
import { useDeleteTool } from "../../../../store/store";
interface ConnectionLine {
id: string;
start: THREE.Vector3;
end: THREE.Vector3;
mid: THREE.Vector3;
startPointUuid: string;
endPointUuid: string;
trigger: TriggerSchema;
}
function TriggerConnector() {
const { gl, raycaster, scene } = useThree();
const { gl, raycaster, scene, pointer, camera } = useThree();
const { subModule } = useSubModuleStore();
const { products, getPointByUuid, getIsEventInProduct, getActionByUuid, addTrigger, addEvent, getEventByModelUuid } = useProductStore();
const { products, getPointByUuid, getIsEventInProduct, getActionByUuid, addTrigger, removeTrigger, addEvent, getEventByModelUuid, getProductById } = useProductStore();
const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
const { selectedProduct } = useSelectedProduct();
const [hoveredLineKey, setHoveredLineKey] = useState<string | null>(null);
const groupRefs = useRef<Record<string, any>>({});
const [helperlineColor, setHelperLineColor] = useState<string>("red");
const [currentLine, setCurrentLine] = useState<{ start: THREE.Vector3; end: THREE.Vector3; mid: THREE.Vector3; } | null>(null);
const { deleteTool } = useDeleteTool();
const [firstSelectedPoint, setFirstSelectedPoint] = useState<{
productId: string;
@@ -32,52 +39,99 @@ function TriggerConnector() {
const [connections, setConnections] = useState<ConnectionLine[]>([]);
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const updateBackend = (
productName: string,
productId: string,
organization: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productId: productId,
organization: organization,
eventDatas: eventData
})
}
useEffect(() => {
const newConnections: ConnectionLine[] = [];
products.forEach(product => {
const product = getProductById(selectedProduct.productId);
if (!product || products.length === 0) return;
product.eventDatas.forEach(event => {
if ('points' in event) {
// Handle Conveyor points
if (event.type === "transfer" && 'points' in event) {
event.points.forEach(point => {
if ('action' in point && point.action?.triggers) {
if (point.action?.triggers) {
point.action.triggers.forEach(trigger => {
if (trigger.triggeredAsset) {
const targetPoint = getPointByUuid(
product.productId,
trigger.triggeredAsset.triggeredModel.modelUuid,
trigger.triggeredAsset.triggeredPoint.pointUuid
);
if (targetPoint) {
const startPos = new THREE.Vector3(...point.position);
const endPos = new THREE.Vector3(...targetPoint.position);
const midPos = new THREE.Vector3()
.addVectors(startPos, endPos)
.multiplyScalar(0.5)
.add(new THREE.Vector3(0, 2, 0));
newConnections.push({
id: `${point.uuid}-${targetPoint.uuid}-${trigger.triggerUuid}`,
start: startPos,
end: endPos,
mid: midPos,
id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`,
startPointUuid: point.uuid,
endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
trigger
});
}
}
});
}
});
}
// Handle Vehicle point
else if (event.type === "vehicle" && 'point' in event) {
const point = event.point;
if (point.action?.triggers) {
point.action.triggers.forEach(trigger => {
if (trigger.triggeredAsset) {
newConnections.push({
id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`,
startPointUuid: point.uuid,
endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
trigger
});
}
});
}
}
// Handle Robotic Arm points
else if (event.type === "roboticArm" && 'point' in event) {
const point = event.point;
point.actions?.forEach(action => {
action.triggers?.forEach(trigger => {
if (trigger.triggeredAsset) {
newConnections.push({
id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`,
startPointUuid: point.uuid,
endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
trigger
});
}
});
});
}
// Handle Machine point
else if (event.type === "machine" && 'point' in event) {
const point = event.point;
if (point.action?.triggers) {
point.action.triggers.forEach(trigger => {
if (trigger.triggeredAsset) {
newConnections.push({
id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`,
startPointUuid: point.uuid,
endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
trigger
});
}
});
}
}
});
setConnections(newConnections);
}, [products]);
useEffect(() => {
console.log('connections: ', connections);
}, connections)
}, [products, selectedProduct.productId]);
useEffect(() => {
const canvasElement = gl.domElement;
@@ -111,15 +165,31 @@ function TriggerConnector() {
if (drag) return;
evt.preventDefault();
const intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length === 0) return;
const intersects = raycaster
.intersectObjects(scene.children, true)
.filter(
(intersect) =>
intersect.object.name === ('Event-Sphere')
);
if (intersects.length === 0) {
setFirstSelectedPoint(null);
return;
};
const currentObject = intersects[0].object;
if (!currentObject || currentObject.name !== 'Event-Sphere') return;
if (!currentObject || currentObject.name !== 'Event-Sphere') {
setFirstSelectedPoint(null);
return;
};
const modelUuid = currentObject.userData.modelUuid;
const pointUuid = currentObject.userData.pointUuid;
if (firstSelectedPoint && firstSelectedPoint.pointUuid === pointUuid) {
setFirstSelectedPoint(null);
return;
}
if (selectedProduct && getIsEventInProduct(selectedProduct.productId, modelUuid)) {
const point = getPointByUuid(
@@ -128,7 +198,12 @@ function TriggerConnector() {
pointUuid
);
if (!point) return;
const event = getEventByModelUuid(selectedProduct.productId, modelUuid);
if (!point || !event) {
setFirstSelectedPoint(null);
return;
};
let actionUuid: string | undefined;
if ('action' in point && point.action) {
@@ -152,12 +227,12 @@ function TriggerConnector() {
delay: 0,
triggeredAsset: {
triggeredModel: {
modelName: currentObject.parent?.parent?.name || 'Unknown',
modelName: event.modelName || 'Unknown',
modelUuid: modelUuid
},
triggeredPoint: {
pointName: currentObject.name,
pointUuid: pointUuid
pointName: 'Point',
pointUuid: point.uuid
},
triggeredAction: actionUuid ? {
actionName: getActionByUuid(selectedProduct.productId, actionUuid)?.actionName || 'Action',
@@ -167,7 +242,16 @@ function TriggerConnector() {
};
if (firstSelectedPoint.actionUuid) {
addTrigger(firstSelectedPoint.actionUuid, trigger);
const event = addTrigger(selectedProduct.productId, firstSelectedPoint.actionUuid, trigger);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
}
setFirstSelectedPoint(null);
}
@@ -184,7 +268,12 @@ function TriggerConnector() {
pointUuid
);
if (!point) return;
const event = getEventByModelUuid(selectedProduct.productId, modelUuid);
if (!point || !event) {
setFirstSelectedPoint(null);
return;
};
let actionUuid: string | undefined;
if ('action' in point && point.action) {
@@ -200,12 +289,12 @@ function TriggerConnector() {
delay: 0,
triggeredAsset: {
triggeredModel: {
modelName: currentObject.parent?.parent?.name || 'Unknown',
modelName: event.modelName || 'Unknown',
modelUuid: modelUuid
},
triggeredPoint: {
pointName: currentObject.name,
pointUuid: pointUuid
pointName: 'Point',
pointUuid: point.uuid
},
triggeredAction: actionUuid ? {
actionName: getActionByUuid(selectedProduct.productId, actionUuid)?.actionName || 'Action',
@@ -215,13 +304,24 @@ function TriggerConnector() {
};
if (firstSelectedPoint.actionUuid) {
addTrigger(firstSelectedPoint.actionUuid, trigger);
const event = addTrigger(selectedProduct.productId, firstSelectedPoint.actionUuid, trigger);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
}
setFirstSelectedPoint(null);
} else if (firstSelectedPoint) {
setFirstSelectedPoint(null);
}
};
if (subModule === 'simulations') {
if (subModule === 'simulations' && !deleteTool) {
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
@@ -235,11 +335,149 @@ function TriggerConnector() {
canvasElement.removeEventListener('contextmenu', handleRightClick);
};
}, [gl, subModule, selectedProduct, firstSelectedPoint]);
}, [gl, subModule, selectedProduct, firstSelectedPoint, deleteTool]);
useFrame(() => {
if (firstSelectedPoint) {
raycaster.setFromCamera(pointer, camera);
const intersects = raycaster.intersectObjects(scene.children, true).filter(
(intersect) =>
!intersect.object.name.includes("Roof") &&
!intersect.object.name.includes("agv-collider") &&
!intersect.object.name.includes("MeasurementReference") &&
!intersect.object.parent?.name.includes("Zone") &&
!(intersect.object.type === "GridHelper") &&
!(intersect.object.type === "Line2")
);
let point: THREE.Vector3 | null = null;
if (intersects.length > 0) {
point = intersects[0].point;
if (point.y < 0.05) {
point = new THREE.Vector3(point.x, 0.05, point.z);
}
}
const sphereIntersects = raycaster.intersectObjects(scene.children, true).filter((intersect) => intersect.object.name === ('Event-Sphere'));
if (sphereIntersects.length > 0 && sphereIntersects[0].object.uuid === firstSelectedPoint.pointUuid) {
setHelperLineColor('red');
setCurrentLine(null);
return;
}
const startPoint = getWorldPositionFromScene(firstSelectedPoint.pointUuid);
if (point && startPoint) {
if (sphereIntersects.length > 0) {
point = sphereIntersects[0].object.getWorldPosition(new THREE.Vector3());
}
const distance = startPoint.distanceTo(point);
const heightFactor = Math.max(0.5, distance * 0.2);
const midPoint = new THREE.Vector3(
(startPoint.x + point.x) / 2,
Math.max(startPoint.y, point.y) + heightFactor,
(startPoint.z + point.z) / 2
);
const endPoint = point;
setCurrentLine({
start: startPoint,
mid: midPoint,
end: endPoint,
});
setHelperLineColor(sphereIntersects.length > 0 ? "#6cf542" : "red");
} else {
setCurrentLine(null);
}
} else {
setCurrentLine(null);
}
})
const getWorldPositionFromScene = (pointUuid: string): THREE.Vector3 | null => {
const pointObj = scene.getObjectByProperty("uuid", pointUuid);
if (!pointObj) return null;
const worldPosition = new THREE.Vector3();
pointObj.getWorldPosition(worldPosition);
return worldPosition;
};
const removeConnection = (connection: ConnectionLine) => {
if (connection.trigger.triggerUuid) {
const event = removeTrigger(selectedProduct.productId, connection.trigger.triggerUuid);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
}
};
return (
<>
</>
<group name="simulationConnectionGroup" >
{connections.map((connection) => {
const startPoint = getWorldPositionFromScene(connection.startPointUuid);
const endPoint = getWorldPositionFromScene(connection.endPointUuid);
if (!startPoint || !endPoint) return null;
const distance = startPoint.distanceTo(endPoint);
const heightFactor = Math.max(0.5, distance * 0.2);
const midPoint = new THREE.Vector3(
(startPoint.x + endPoint.x) / 2,
Math.max(startPoint.y, endPoint.y) + heightFactor,
(startPoint.z + endPoint.z) / 2
);
return (
<QuadraticBezierLine
key={connection.id}
ref={(el) => (groupRefs.current[connection.id] = el!)}
start={startPoint.toArray()}
end={endPoint.toArray()}
mid={midPoint.toArray()}
color={deleteTool && hoveredLineKey === connection.id ? "red" : "#42a5f5"}
lineWidth={4}
dashed={deleteTool && hoveredLineKey === connection.id ? false : true}
dashSize={0.75}
dashScale={20}
onPointerOver={() => setHoveredLineKey(connection.id)}
onPointerOut={() => setHoveredLineKey(null)}
onClick={() => {
if (deleteTool) {
setHoveredLineKey(null);
setCurrentLine(null);
removeConnection(connection);
}
}}
userData={connection.trigger}
/>
);
})}
{currentLine && (
<QuadraticBezierLine
start={currentLine.start.toArray()}
end={currentLine.end.toArray()}
mid={currentLine.mid.toArray()}
color={helperlineColor}
lineWidth={4}
dashed
dashSize={1}
dashScale={20}
/>
)}
</group>
);
}

View File

@@ -1,12 +1,16 @@
import React, { useEffect, useRef, useState } from 'react';
import * as Types from "../../../../types/world/worldTypes";
import startPoint from "../../../../assets/gltf-glb/arrow_green.glb";
import startEnd from "../../../../assets/gltf-glb/arrow_red.glb";
import * as THREE from "three";
import startEnd from "../../../../assets/gltf-glb/arrow_red.glb";
import { useGLTF } from '@react-three/drei';
import { useFrame, useThree } from '@react-three/fiber';
import { useSelectedEventSphere } from '../../../../store/simulation/useSimulationStore';
import { useSelectedEventSphere, useIsDragging, useIsRotating } from '../../../../store/simulation/useSimulationStore';
import { useVehicleStore } from '../../../../store/simulation/useVehicleStore';
import * as Types from "../../../../types/world/worldTypes";
import { useProductStore } from '../../../../store/simulation/useProductStore';
import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore';
import { upsertProductOrEventApi } from '../../../../services/simulation/UpsertProductOrEventApi';
const VehicleUI = () => {
const { scene: startScene } = useGLTF(startPoint) as any;
const { scene: endScene } = useGLTF(startEnd) as any;
@@ -14,67 +18,60 @@ const VehicleUI = () => {
const endMarker = useRef<THREE.Group>(null);
const prevMousePos = useRef({ x: 0, y: 0 });
const { selectedEventSphere } = useSelectedEventSphere();
const { vehicles, updateVehicle } = useVehicleStore();
const { selectedProduct } = useSelectedProduct();
const { getVehicleById } = useVehicleStore();
const { updateEvent } = useProductStore();
const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 0, 0]);
const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 0, 0]);
const [startRotation, setStartRotation] = useState<[number, number, number]>([0, 0, 0]);
const [endRotation, setEndRotation] = useState<[number, number, number]>([0, 0, 0]);
const [isDragging, setIsDragging] = useState<"start" | "end" | null>(null);
const [isRotating, setIsRotating] = useState<"start" | "end" | null>(null);
const { isDragging, setIsDragging } = useIsDragging();
const { isRotating, setIsRotating } = useIsRotating();
const { raycaster } = useThree();
const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0));
const state: Types.ThreeState = useThree();
const controls: any = state.controls;
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const updateBackend = (
productName: string,
productId: string,
organization: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productId: productId,
organization: organization,
eventDatas: eventData
})
}
useEffect(() => {
if (!selectedEventSphere) return;
const selectedVehicle = vehicles.find(
(vehicle: any) => vehicle.modelUuid === selectedEventSphere.userData.modelUuid
);
const selectedVehicle = getVehicleById(selectedEventSphere.userData.modelUuid);
if (selectedVehicle?.point?.action) {
const { pickUpPoint, unLoadPoint } = selectedVehicle.point.action;
if (pickUpPoint) {
const pickupPosition = new THREE.Vector3(
pickUpPoint.position.x,
pickUpPoint.position.y,
pickUpPoint.position.z
);
const pickupRotation = new THREE.Vector3(
pickUpPoint.rotation.x,
pickUpPoint.rotation.y,
pickUpPoint.rotation.z
);
pickupPosition.y = 0;
setStartPosition([pickupPosition.x, 0, pickupPosition.z]);
setStartRotation([pickupRotation.x, pickupRotation.y, pickupRotation.z]);
setStartPosition([pickUpPoint.position.x, 0, pickUpPoint.position.z]);
setStartRotation([pickUpPoint.rotation.x, pickUpPoint.rotation.y, pickUpPoint.rotation.z]);
} else {
const defaultLocal = new THREE.Vector3(0, 0, 1.5);
const defaultWorld = selectedEventSphere.localToWorld(defaultLocal);
defaultWorld.y = 0;
setStartPosition([defaultWorld.x, 0, defaultWorld.z]);
setStartRotation([0, 0, 0]);
}
if (unLoadPoint) {
const unLoadPosition = new THREE.Vector3(
unLoadPoint.position.x,
unLoadPoint.position.y,
unLoadPoint.position.z
);
const unLoadRotation = new THREE.Vector3(
unLoadPoint.rotation.x,
unLoadPoint.rotation.y,
unLoadPoint.position.z
);
unLoadPosition.y = 0; // Force y to 0
setEndPosition([unLoadPosition.x, 0, unLoadPosition.z]);
setEndRotation([unLoadRotation.x, unLoadRotation.y, unLoadRotation.z]);
setEndPosition([unLoadPoint.position.x, 0, unLoadPoint.position.z]);
setEndRotation([unLoadPoint.rotation.x, unLoadPoint.rotation.y, unLoadPoint.rotation.z]);
} else {
const defaultLocal = new THREE.Vector3(0, 0, -1.5);
const defaultWorld = selectedEventSphere.localToWorld(defaultLocal);
defaultWorld.y = 0; // Force y to 0
setEndPosition([defaultWorld.x, 0, defaultWorld.z]);
setEndRotation([0, 0, 0]);
}
@@ -87,7 +84,6 @@ const VehicleUI = () => {
const intersects = raycaster.ray.intersectPlane(plane.current, intersectPoint);
if (intersects) {
intersectPoint.y = 0; // Force y to 0
if (isDragging === "start") {
setStartPosition([intersectPoint.x, 0, intersectPoint.z]);
}
@@ -108,12 +104,12 @@ const VehicleUI = () => {
if (marker) {
const rotationSpeed = 10;
marker.rotation.y += deltaX * rotationSpeed;
if (isRotating === 'start') {
setStartRotation([marker.rotation.x, marker.rotation.y, marker.rotation.z])
const y = startRotation[1] + deltaX * rotationSpeed;
setStartRotation([0, y, 0]);
} else {
setEndRotation([marker.rotation.x, marker.rotation.y, marker.rotation.z])
const y = endRotation[1] + deltaX * rotationSpeed;
setEndRotation([0, y, 0]);
}
}
});
@@ -142,43 +138,34 @@ const VehicleUI = () => {
setIsRotating(null);
if (selectedEventSphere?.userData.modelUuid) {
const updatedVehicle = vehicles.find(
(vehicle) => vehicle.modelUuid === selectedEventSphere.userData.modelUuid
);
const updatedVehicle = getVehicleById(selectedEventSphere.userData.modelUuid);
if (updatedVehicle) {
updateVehicle(selectedEventSphere.userData.modelUuid, {
const event = updateEvent(selectedProduct.productId, selectedEventSphere.userData.modelUuid, {
point: {
...updatedVehicle.point,
action: {
...updatedVehicle.point?.action,
pickUpPoint: {
position: {
x: startPosition[0],
y: startPosition[1],
z: startPosition[2],
},
rotation: {
x: startRotation[0],
y: startRotation[1],
z: startRotation[2],
},
position: { x: startPosition[0], y: startPosition[1], z: startPosition[2], },
rotation: { x: 0, y: startRotation[1], z: 0, },
},
unLoadPoint: {
position: {
x: endPosition[0],
y: endPosition[1],
z: endPosition[2],
},
rotation: {
x: endRotation[0],
y: endRotation[1],
z: endRotation[2],
position: { x: endPosition[0], y: endPosition[1], z: endPosition[2], },
rotation: { x: 0, y: endRotation[1], z: 0, },
},
},
},
},
});
})
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
}
}
};
@@ -208,6 +195,7 @@ const VehicleUI = () => {
object={startScene}
ref={startMarker}
position={startPosition}
rotation={startRotation}
onPointerDown={(e: any) => {
e.stopPropagation();
handlePointerDown(e, "start", "start");
@@ -224,6 +212,7 @@ const VehicleUI = () => {
object={endScene}
ref={endMarker}
position={endPosition}
rotation={endRotation}
onPointerDown={(e: any) => {
e.stopPropagation();
handlePointerDown(e, "end", "end");

View File

@@ -11,7 +11,7 @@ interface VehicleAnimatorProps {
reset: () => void;
currentPhase: string;
agvUuid: number;
agvDetail: any;
agvDetail: VehicleStatus;
}
function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetail, reset }: VehicleAnimatorProps) {
@@ -32,7 +32,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
let startTime: number;
let fixedInterval: number;
let coveredDistance = progressRef.current;
let objectRotation = (agvDetail.point?.action?.pickUpPoint?.rotation || { x: 0, y: 0, z: 0 }) as { x: number; y: number; z: number };
let objectRotation = (agvDetail.point?.action?.pickUpPoint?.rotation || { x: 0, y: 0, z: 0 }) as { x: number; y: number; z: number } | undefined;
useEffect(() => {
@@ -67,10 +67,9 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
setRestingRotation(true);
decrementVehicleLoad(agvDetail.modelUuid, 0);
const object = scene.getObjectByProperty('uuid', agvUuid);
console.log('currentPhase: ', currentPhase);
if (object) {
object.position.set(agvDetail.position[0], agvDetail.position[1], agvDetail.position[2]);
object.rotation.set(objectRotation.x, objectRotation.y, objectRotation.z);
object.rotation.set(agvDetail.rotation[0], agvDetail.rotation[1], agvDetail.rotation[2]);
}
}
}, [isReset, isPlaying])
@@ -132,7 +131,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
}
if (progressRef.current >= totalDistance) {
if (restRotation) {
if (restRotation && objectRotation) {
const targetQuaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(objectRotation.x, objectRotation.y, objectRotation.z));
object.quaternion.slerp(targetQuaternion, delta * 2);
const angleDiff = object.quaternion.angleTo(targetQuaternion);

View File

@@ -30,7 +30,8 @@ function VehicleInstance({ agvDetail }: any) {
);
function vehicleStatus(modelId: string, status: string) {
// console.log(`${modelId} , ${status});
// console.log(`${modelId} , ${status}`);
}
// Function to reset everything
@@ -44,7 +45,7 @@ function VehicleInstance({ agvDetail }: any) {
const increment = () => {
if (isIncrememtable.current) {
incrementVehicleLoad(agvDetail.modelUuid, 2);
incrementVehicleLoad(agvDetail.modelUuid, 10);
isIncrememtable.current = false;
}
}
@@ -69,6 +70,7 @@ function VehicleInstance({ agvDetail }: any) {
increment();
}, 5000);
if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity) {
const toDrop = computePath(
agvDetail.point.action.pickUpPoint.position,

View File

@@ -1,220 +1,48 @@
import React, { useEffect, useState } from "react";
import React, { useEffect } from "react";
import VehicleInstances from "./instances/vehicleInstances";
import { useVehicleStore } from "../../../store/simulation/useVehicleStore";
import { useFloorItems } from "../../../store/store";
import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore";
import { useSelectedEventData, useSelectedEventSphere, useSelectedProduct } from "../../../store/simulation/useSimulationStore";
import VehicleUI from "../ui/vehicle/vehicleUI";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
function Vehicles() {
import { useProductStore } from "../../../store/simulation/useProductStore";
const { vehicles, addVehicle } = useVehicleStore();
function Vehicles() {
const { products, getProductById } = useProductStore();
const { selectedProduct } = useSelectedProduct();
const { vehicles, addVehicle, clearvehicles } = useVehicleStore();
const { selectedEventSphere } = useSelectedEventSphere();
const { selectedEventData } = useSelectedEventData();
const { floorItems } = useFloorItems();
const { isPlaying } = usePlayButtonStore();
const [vehicleStatusSample, setVehicleStatusSample] = useState<
VehicleEventSchema[]
>([
{
modelUuid: "9356f710-4727-4b50-bdb2-9c1e747ecc74",
modelName: "AGV",
position: [97.9252965204558, 0, 37.96138815638661],
rotation: [0, 0, 0],
state: "idle",
type: "vehicle",
speed: 2.5,
point: {
uuid: "point-789",
position: [0, 1, 0],
rotation: [0, 0, 0],
action: {
actionUuid: "action-456",
actionName: "Deliver to Zone A",
actionType: "travel",
unLoadDuration: 10,
loadCapacity: 2,
steeringAngle:0,
pickUpPoint: { position: { x: 98.71483985219794, y: 0, z: 28.66321267938962 }, rotation: { x: 0, y: 0, z: 0 } },
unLoadPoint: { position: { x: 105.71483985219794, y: 0, z: 28.66321267938962 }, rotation: { x: 0, y: 0, z: 0 } },
triggers: [
{
triggerUuid: "trig-001",
triggerName: "Start Travel",
triggerType: "onComplete",
delay: 0,
triggeredAsset: {
triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" },
triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" },
triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" }
useEffect(() => {
if (selectedProduct.productId) {
const product = getProductById(selectedProduct.productId);
if (product) {
clearvehicles();
product.eventDatas.forEach(events => {
if (events.type === 'vehicle') {
addVehicle(selectedProduct.productId, events);
}
},
{
triggerUuid: "trig-002",
triggerName: "Complete Travel",
triggerType: "onComplete",
delay: 2,
triggeredAsset: null
}
]
});
}
}
},
{
modelUuid: "b06960bb-3d2e-41f7-a646-335f389c68b4",
modelName: "AGV",
position: [89.61609306554463, 0, 33.634136622267356],
rotation: [0, 0, 0],
state: "idle",
type: "vehicle",
speed: 2.5,
point: {
uuid: "point-789",
position: [0, 1, 0],
rotation: [0, 0, 0],
action: {
actionUuid: "action-456",
actionName: "Deliver to Zone A",
actionType: "travel",
unLoadDuration: 10,
loadCapacity: 2,
steeringAngle:0,
pickUpPoint: null,
unLoadPoint: null,
triggers: [
{
triggerUuid: "trig-001",
triggerName: "Start Travel",
triggerType: "onStart",
delay: 0,
triggeredAsset: {
triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" },
triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" },
triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" }
}
},
{
triggerUuid: "trig-002",
triggerName: "Complete Travel",
triggerType: "onComplete",
delay: 2,
triggeredAsset: null
}
]
}
}
},
// {
// modelUuid: "cd7d0584-0684-42b4-b051-9e882c1914aa",
// modelName: "AGV",
// position: [105.90938758014703, 0, 31.584209911095215],
// rotation: [0, 0, 0],
// state: "idle",
// type: "vehicle",
// speed: 2.5,
// point: {
// uuid: "point-789",
// position: [0, 1, 0],
// rotation: [0, 0, 0],
// action: {
// actionUuid: "action-456",
// actionName: "Deliver to Zone A",
// actionType: "travel",
// unLoadDuration: 10,
// loadCapacity: 2,
// steeringAngle:0,
// pickUpPoint: null,
// unLoadPoint: null,
// triggers: [
// {
// triggerUuid: "trig-001",
// triggerName: "Start Travel",
// triggerType: "onStart",
// delay: 0,
// triggeredAsset: {
// triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" },
// triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" },
// triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" }
// }
// },
// {
// triggerUuid: "trig-002",
// triggerName: "Complete Travel",
// triggerType: "onComplete",
// delay: 2,
// triggeredAsset: null
// }
// ]
// }
// }
// },
// {
// modelUuid: "e729a4f1-11d2-4778-8d6a-468f1b4f6b79",
// modelName: "forklift",
// position: [98.85729337188162, 0, 38.36616546567653],
// rotation: [0, 0, 0],
// state: "idle",
// type: "vehicle",
// speed: 2.5,
// point: {
// uuid: "point-789",
// position: [0, 1, 0],
// rotation: [0, 0, 0],
// action: {
// actionUuid: "action-456",
// actionName: "Deliver to Zone A",
// actionType: "travel",
// unLoadDuration: 15,
// loadCapacity: 5,
// steeringAngle:0,
// pickUpPoint: null,
// unLoadPoint: null,
// triggers: [
// {
// triggerUuid: "trig-001",
// triggerName: "Start Travel",
// triggerType: "onStart",
// delay: 0,
// triggeredAsset: {
// triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" },
// triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" },
// triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" }
// }
// },
// {
// triggerUuid: "trig-002",
// triggerName: "Complete Travel",
// triggerType: "onComplete",
// delay: 2,
// triggeredAsset: null
// }
// ]
// }
// }
// }
]);
}, [selectedProduct, products]);
useEffect(() => {
// console.log('vehicles: ', vehicles);
}, [vehicles])
useEffect(() => {
addVehicle("123", vehicleStatusSample[0]);
addVehicle('123', vehicleStatusSample[1]);
// addVehicle('123', vehicleStatusSample[2]);
}, []);
return (
<>
<VehicleInstances />
{selectedEventSphere && selectedEventData?.data.type === "vehicle" && !isPlaying &&
< VehicleUI />
}
</>
);
}
export default Vehicles;

View File

@@ -66,8 +66,7 @@ const RealTimeVisulization: React.FC = () => {
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
const { setRightSelect } = useRightSelected();
const { editWidgetOptions, setEditWidgetOptions } =
useEditWidgetOptionsStore();
const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore();
const { rightClickSelected, setRightClickSelected } = useRightClickSelected();
const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false);
const { setFloatingWidget } = useFloatingWidget();

View File

@@ -28,9 +28,9 @@ import { createHandleDrop } from "../modules/visualization/functions/handleUiDro
import { useSelectedZoneStore } from "../store/visualization/useZoneStore";
import { useFloatingWidget } from "../store/visualization/useDroppedObjectsStore";
import { useLogger } from "../components/ui/log/LoggerContext";
import Footer from "../components/ui/footer/Footer";
import RenderOverlay from "../components/templates/Overlay";
import LogList from "../components/ui/log/LogList";
import Footer from "../components/footer/Footer";
const Project: React.FC = () => {
let navigate = useNavigate();
@@ -86,7 +86,7 @@ const Project: React.FC = () => {
{!selectedUser && (
<>
<KeyPressListener />
{/* {loadingProgress > 0 && <LoadingPage progress={loadingProgress} />} */}
{loadingProgress > 0 && <LoadingPage progress={loadingProgress} />}
{!isPlaying && (
<>
{toggleThreeD && <ModuleToggle />}
@@ -122,7 +122,7 @@ const Project: React.FC = () => {
}
onDragOver={(event) => event.preventDefault()}
>
{/* <Scene /> */}
<Scene />
</div>
{selectedUser && <FollowPerson />}
{isLogListVisible && (

View File

@@ -10,6 +10,7 @@ interface ArmBotStore {
modelUuid: string,
updates: Partial<Omit<ArmBotStatus, 'modelUuid' | 'productId'>>
) => void;
clearArmBots: () => void;
addCurrentAction: (modelUuid: string, actionUuid: string) => void;
removeCurrentAction: (modelUuid: string) => void;
@@ -39,6 +40,8 @@ export const useArmBotStore = create<ArmBotStore>()(
addArmBot: (productId, event) => {
set((state) => {
const exists = state.armBots.some(a => a.modelUuid === event.modelUuid);
if (!exists) {
state.armBots.push({
...event,
productId,
@@ -47,6 +50,7 @@ export const useArmBotStore = create<ArmBotStore>()(
activeTime: 0,
state: 'idle',
});
}
});
},
@@ -65,6 +69,12 @@ export const useArmBotStore = create<ArmBotStore>()(
});
},
clearArmBots: () => {
set((state) => {
state.armBots = [];
});
},
addCurrentAction: (modelUuid, actionUuid) => {
set((state) => {
const armBot = state.armBots.find(a => a.modelUuid === modelUuid);

View File

@@ -10,6 +10,7 @@ interface ConveyorStore {
modelUuid: string,
updates: Partial<Omit<ConveyorStatus, 'modelUuid' | 'productId'>>
) => void;
clearConveyors: () => void;
setConveyorActive: (modelUuid: string, isActive: boolean) => void;
setConveyorState: (modelUuid: string, newState: ConveyorStatus['state']) => void;
@@ -30,6 +31,8 @@ export const useConveyorStore = create<ConveyorStore>()(
addConveyor: (productId, event) => {
set((state) => {
const exists = state.conveyors.some(c => c.modelUuid === event.modelUuid);
if (!exists) {
state.conveyors.push({
...event,
productId,
@@ -38,6 +41,7 @@ export const useConveyorStore = create<ConveyorStore>()(
activeTime: 0,
state: 'idle',
});
}
});
},
@@ -56,6 +60,12 @@ export const useConveyorStore = create<ConveyorStore>()(
});
},
clearConveyors: () => {
set((state) => {
state.conveyors = [];
});
},
setConveyorActive: (modelUuid, isActive) => {
set((state) => {
const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid);

View File

@@ -7,7 +7,7 @@ type EventsStore = {
// Event-level actions
addEvent: (event: EventsSchema) => void;
removeEvent: (modelUuid: string) => void;
updateEvent: (modelUuid: string, updates: Partial<EventsSchema>) => void;
updateEvent: (modelUuid: string, updates: Partial<EventsSchema>) => EventsSchema | undefined;
// Point-level actions
addPoint: (modelUuid: string, point: ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema) => void;
@@ -49,7 +49,9 @@ export const useEventsStore = create<EventsStore>()(
// Event-level actions
addEvent: (event) => {
set((state) => {
if (!state.events.some(e => 'modelUuid' in e && e.modelUuid === event.modelUuid)) {
state.events.push(event);
}
});
},
@@ -60,12 +62,15 @@ export const useEventsStore = create<EventsStore>()(
},
updateEvent: (modelUuid, updates) => {
let updatedEvent: EventsSchema | undefined;
set((state) => {
const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
if (event) {
Object.assign(event, updates);
updatedEvent = JSON.parse(JSON.stringify(event));
}
});
return updatedEvent;
},
// Point-level actions
@@ -73,10 +78,15 @@ export const useEventsStore = create<EventsStore>()(
set((state) => {
const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
if (event && 'points' in event) {
const existingPoint = (event as ConveyorEventSchema).points.find(p => p.uuid === point.uuid);
if (!existingPoint) {
(event as ConveyorEventSchema).points.push(point as ConveyorPointSchema);
}
} else if (event && 'point' in event) {
if (!(event as any).point || (event as any).point.uuid !== point.uuid) {
(event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point = point as any;
}
}
});
},
@@ -110,14 +120,15 @@ export const useEventsStore = create<EventsStore>()(
const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
if (event && 'points' in event) {
const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid);
if (point) {
if (point && (!point.action || point.action.actionUuid !== action.actionUuid)) {
point.action = action as any;
}
} else if (event && 'point' in event && (event as any).point.uuid === pointUuid) {
if ('action' in (event as any).point) {
(event as any).point.action = action;
} else if ('actions' in (event as any).point) {
(event as any).point.actions.push(action);
const point = (event as any).point;
if ('action' in point && (!point.action || point.action.actionUuid !== action.actionUuid)) {
point.action = action;
} else if ('actions' in point && !point.actions.some((a: any) => a.actionUuid === action.actionUuid)) {
point.actions.push(action);
}
}
});
@@ -180,18 +191,22 @@ export const useEventsStore = create<EventsStore>()(
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
if (point.action && point.action.actionUuid === actionUuid) {
if (!point.action.triggers.some(t => t.triggerUuid === trigger.triggerUuid)) {
point.action.triggers.push(trigger);
}
return;
}
}
} else if ('point' in event) {
const point = (event as any).point;
const point: MachinePointSchema | VehiclePointSchema = (event as any).point;
if ('action' in point && point.action.actionUuid === actionUuid) {
if (!point.action.triggers.some(t => t.triggerUuid === trigger.triggerUuid)) {
point.action.triggers.push(trigger);
}
return;
} else if ('actions' in point) {
const action = point.actions.find((a: any) => a.actionUuid === actionUuid);
if (action) {
const action = (point as RoboticArmPointSchema).actions.find((a) => a.actionUuid === actionUuid);
if (action && !action.triggers.some(t => t.triggerUuid === trigger.triggerUuid)) {
action.triggers.push(trigger);
return;
}

View File

@@ -4,23 +4,23 @@ import { immer } from 'zustand/middleware/immer';
interface MachineStore {
machines: MachineStatus[];
// Actions
addMachine: (productId: string, machine: MachineEventSchema) => void;
removeMachine: (modelUuid: string) => void;
updateMachine: (
modelUuid: string,
updates: Partial<Omit<MachineStatus, 'modelUuid' | 'productId'>>
) => void;
clearMachines: () => void;
addCurrentAction: (modelUuid: string, actionUuid: string) => void;
removeCurrentAction: (modelUuid: string) => void;
// Status updates
setMachineActive: (modelUuid: string, isActive: boolean) => void;
setMachineState: (modelUuid: string, newState: MachineStatus['state']) => void;
// Time tracking
incrementActiveTime: (modelUuid: string, incrementBy: number) => void;
incrementIdleTime: (modelUuid: string, incrementBy: number) => void;
// Helpers
getMachineById: (modelUuid: string) => MachineStatus | undefined;
getMachinesByProduct: (productId: string) => MachineStatus[];
getMachinesBystate: (state: string) => MachineStatus[];
@@ -32,9 +32,10 @@ export const useMachineStore = create<MachineStore>()(
immer((set, get) => ({
machines: [],
// Actions
addMachine: (productId, machine) => {
set((state) => {
const exists = state.machines.some(m => m.modelUuid === machine.modelUuid);
if (!exists) {
state.machines.push({
...machine,
productId,
@@ -43,6 +44,7 @@ export const useMachineStore = create<MachineStore>()(
activeTime: 0,
state: 'idle',
});
}
});
},
@@ -61,7 +63,36 @@ export const useMachineStore = create<MachineStore>()(
});
},
// Status updates
clearMachines: () => {
set((state) => {
state.machines = [];
});
},
addCurrentAction: (modelUuid) => {
set((state) => {
const armBot = state.machines.find(a => a.modelUuid === modelUuid);
if (armBot) {
const action = armBot.point.action;
if (action) {
armBot.currentAction = {
actionUuid: action.actionUuid,
actionName: action.actionName,
};
}
}
});
},
removeCurrentAction: (modelUuid) => {
set((state) => {
const armBot = state.machines.find(a => a.modelUuid === modelUuid);
if (armBot) {
armBot.currentAction = undefined;
}
});
},
setMachineActive: (modelUuid, isActive) => {
set((state) => {
const machine = state.machines.find(m => m.modelUuid === modelUuid);
@@ -80,7 +111,6 @@ export const useMachineStore = create<MachineStore>()(
});
},
// Time tracking
incrementActiveTime: (modelUuid, incrementBy) => {
set((state) => {
const machine = state.machines.find(m => m.modelUuid === modelUuid);
@@ -99,7 +129,6 @@ export const useMachineStore = create<MachineStore>()(
});
},
// Helpers
getMachineById: (modelUuid) => {
return get().machines.find(m => m.modelUuid === modelUuid);
},

View File

@@ -33,27 +33,30 @@ type ProductsStore = {
pointUuid: string,
action: ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']
) => EventsSchema | undefined;
removeAction: (actionUuid: string) => EventsSchema | undefined;
removeAction: (productId: string, actionUuid: string) => EventsSchema | undefined;
updateAction: (
productId: string,
actionUuid: string,
updates: Partial<ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']>
) => EventsSchema | undefined;
// Trigger-level actions
addTrigger: (
productId: string,
actionUuid: string,
trigger: TriggerSchema
) => void;
removeTrigger: (triggerUuid: string) => void;
) => EventsSchema | undefined;
removeTrigger: (productId: string, triggerUuid: string) => EventsSchema | undefined;
updateTrigger: (
productId: string,
triggerUuid: string,
updates: Partial<TriggerSchema>
) => void;
// Renaming functions
renameProduct: (productId: string, newName: string) => void;
renameAction: (actionUuid: string, newName: string) => EventsSchema | undefined;
renameTrigger: (triggerUuid: string, newName: string) => void;
renameAction: (productId: string, actionUuid: string, newName: string) => EventsSchema | undefined;
renameTrigger: (productId: string, triggerUuid: string, newName: string) => void;
// Helper functions
getProductById: (productId: string) => { productName: string; productId: string; eventDatas: EventsSchema[] } | undefined;
@@ -71,12 +74,15 @@ export const useProductStore = create<ProductsStore>()(
// Product-level actions
addProduct: (productName, productId) => {
set((state) => {
const existingProduct = state.products.find(p => p.productId === productId);
if (!existingProduct) {
const newProduct = {
productName,
productId: productId,
eventDatas: []
};
state.products.push(newProduct);
}
});
},
@@ -106,8 +112,11 @@ export const useProductStore = create<ProductsStore>()(
set((state) => {
const product = state.products.find(p => p.productId === productId);
if (product) {
const existingEvent = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === event.modelUuid);
if (!existingEvent) {
product.eventDatas.push(event);
}
}
});
},
@@ -120,7 +129,7 @@ export const useProductStore = create<ProductsStore>()(
});
},
deleteEvent: (modelUuid: string) => {
deleteEvent: (modelUuid) => {
set((state) => {
for (const product of state.products) {
product.eventDatas = product.eventDatas.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid);
@@ -150,11 +159,17 @@ export const useProductStore = create<ProductsStore>()(
if (product) {
const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
if (event && 'points' in event) {
const existingPoint = (event as ConveyorEventSchema).points.find(p => p.uuid === point.uuid);
if (!existingPoint) {
(event as ConveyorEventSchema).points.push(point as ConveyorPointSchema);
}
} else if (event && 'point' in event) {
const existingPoint = (event as any).point?.uuid === point.uuid;
if (!existingPoint) {
(event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point = point as any;
}
}
}
});
},
@@ -198,28 +213,34 @@ export const useProductStore = create<ProductsStore>()(
const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
if (event && 'points' in event) {
const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid);
if (point) {
if (point && (!point.action || point.action.actionUuid !== action.actionUuid)) {
point.action = action as any;
updatedEvent = JSON.parse(JSON.stringify(event));
}
} else if (event && 'point' in event && (event as any).point.uuid === pointUuid) {
if ('action' in (event as any).point) {
if (!(event as any).point.action || (event as any).point.action.actionUuid !== action.actionUuid) {
(event as any).point.action = action;
updatedEvent = JSON.parse(JSON.stringify(event));
}
} else if ('actions' in (event as any).point) {
const existingAction = (event as any).point.actions.find((a: any) => a.actionUuid === action.actionUuid);
if (!existingAction) {
(event as any).point.actions.push(action);
updatedEvent = JSON.parse(JSON.stringify(event));
}
}
}
}
});
return updatedEvent;
},
removeAction: (actionUuid: string) => {
removeAction: (productId, actionUuid) => {
let updatedEvent: EventsSchema | undefined;
set((state) => {
for (const product of state.products) {
const product = state.products.find(p => p.productId === productId);
if (product) {
for (const event of product.eventDatas) {
if ('points' in event) {
// Handle ConveyorEventSchema
@@ -248,10 +269,11 @@ export const useProductStore = create<ProductsStore>()(
return updatedEvent;
},
updateAction: (actionUuid, updates) => {
updateAction: (productId, actionUuid, updates) => {
let updatedEvent: EventsSchema | undefined;
set((state) => {
for (const product of state.products) {
const product = state.products.find(p => p.productId === productId);
if (product) {
for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
@@ -283,26 +305,40 @@ export const useProductStore = create<ProductsStore>()(
},
// Trigger-level actions
addTrigger: (actionUuid, trigger) => {
addTrigger: (productId, actionUuid, trigger) => {
let updatedEvent: EventsSchema | undefined;
set((state) => {
for (const product of state.products) {
const product = state.products.find(p => p.productId === productId);
if (product) {
for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
if (point.action && point.action.actionUuid === actionUuid) {
const existingTrigger = point.action.triggers.find(t => t.triggerUuid === trigger.triggerUuid);
if (!existingTrigger) {
point.action.triggers.push(trigger);
updatedEvent = JSON.parse(JSON.stringify(event));
}
return;
}
}
} else if ('point' in event) {
const point = (event as any).point;
if ('action' in point && point.action.actionUuid === actionUuid) {
const existingTrigger = point.action.triggers.find((t: any) => t.triggerUuid === trigger.triggerUuid);
if (!existingTrigger) {
point.action.triggers.push(trigger);
updatedEvent = JSON.parse(JSON.stringify(event));
}
return;
} else if ('actions' in point) {
const action = point.actions.find((a: any) => a.actionUuid === actionUuid);
if (action) {
const existingTrigger = action.triggers.find((t: any) => t.triggerUuid === trigger.triggerUuid);
if (!existingTrigger) {
action.triggers.push(trigger);
updatedEvent = JSON.parse(JSON.stringify(event));
}
return;
}
}
@@ -310,26 +346,41 @@ export const useProductStore = create<ProductsStore>()(
}
}
});
return updatedEvent;
},
removeTrigger: (triggerUuid) => {
removeTrigger: (productId, triggerUuid) => {
let updatedEvent: EventsSchema | undefined;
set((state) => {
for (const product of state.products) {
const product = state.products.find(p => p.productId === productId);
if (product) {
for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
if (point.action && 'triggers' in point.action) {
const Trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid);
if (Trigger) {
point.action.triggers = point.action.triggers.filter(t => t.triggerUuid !== triggerUuid);
updatedEvent = JSON.parse(JSON.stringify(event));
}
}
}
} else if ('point' in event) {
const point = (event as any).point;
if ('action' in point && 'triggers' in point.action) {
const Trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid);
if (Trigger) {
point.action.triggers = point.action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid);
updatedEvent = JSON.parse(JSON.stringify(event));
}
} else if ('actions' in point) {
for (const action of point.actions) {
if ('triggers' in action) {
const Trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid);
if (Trigger) {
action.triggers = action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid);
updatedEvent = JSON.parse(JSON.stringify(event));
}
}
}
}
@@ -337,11 +388,13 @@ export const useProductStore = create<ProductsStore>()(
}
}
});
return updatedEvent;
},
updateTrigger: (triggerUuid, updates) => {
updateTrigger: (productId, triggerUuid, updates) => {
set((state) => {
for (const product of state.products) {
const product = state.products.find(p => p.productId === productId);
if (product) {
for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
@@ -388,10 +441,11 @@ export const useProductStore = create<ProductsStore>()(
});
},
renameAction: (actionUuid, newName) => {
renameAction: (productId, actionUuid, newName) => {
let updatedEvent: EventsSchema | undefined;
set((state) => {
for (const product of state.products) {
const product = state.products.find(p => p.productId === productId);
if (product) {
for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
@@ -422,9 +476,10 @@ export const useProductStore = create<ProductsStore>()(
return updatedEvent;
},
renameTrigger: (triggerUuid, newName) => {
renameTrigger: (productId, triggerUuid, newName) => {
set((state) => {
for (const product of state.products) {
const product = state.products.find(p => p.productId === productId);
if (product) {
for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {

View File

@@ -115,3 +115,35 @@ export const useSelectedAction = create<SelectedActionState>()(
},
}))
);
interface IsDraggingState {
isDragging: "start" | "end" | null;
setIsDragging: (state: "start" | "end" | null) => void;
}
export const useIsDragging = create<IsDraggingState>()(
immer((set) => ({
isDragging: null,
setIsDragging: (state) => {
set((s) => {
s.isDragging = state;
});
},
}))
);
interface IsRotatingState {
isRotating: "start" | "end" | null;
setIsRotating: (state: "start" | "end" | null) => void;
}
export const useIsRotating = create<IsRotatingState>()(
immer((set) => ({
isRotating: null,
setIsRotating: (state) => {
set((s) => {
s.isRotating = state;
});
},
}))
);

View File

@@ -4,26 +4,22 @@ import { immer } from 'zustand/middleware/immer';
interface StorageUnitStore {
storageUnits: StorageUnitStatus[];
// Actions
addStorageUnit: (productId: string, storageUnit: StorageEventSchema) => void;
removeStorageUnit: (modelUuid: string) => void;
updateStorageUnit: (
modelUuid: string,
updates: Partial<Omit<StorageUnitStatus, 'modelUuid' | 'productId'>>
) => void;
clearStorageUnits: () => void;
// Status updates
setStorageUnitActive: (modelUuid: string, isActive: boolean) => void;
setStorageUnitState: (modelUuid: string, newState: StorageUnitStatus['state']) => void;
// Load updates
updateStorageUnitLoad: (modelUuid: string, incrementBy: number) => void;
// Time tracking
incrementActiveTime: (modelUuid: string, incrementBy: number) => void;
incrementIdleTime: (modelUuid: string, incrementBy: number) => void;
// Helpers
getStorageUnitById: (modelUuid: string) => StorageUnitStatus | undefined;
getStorageUnitsByProduct: (productId: string) => StorageUnitStatus[];
getStorageUnitsBystate: (state: string) => StorageUnitStatus[];
@@ -37,9 +33,10 @@ export const useStorageUnitStore = create<StorageUnitStore>()(
immer((set, get) => ({
storageUnits: [],
// Actions
addStorageUnit: (productId, storageUnit) => {
set((state) => {
const exists = state.storageUnits.some(s => s.modelUuid === storageUnit.modelUuid);
if (!exists) {
state.storageUnits.push({
...storageUnit,
productId,
@@ -49,6 +46,7 @@ export const useStorageUnitStore = create<StorageUnitStore>()(
currentLoad: 0,
state: 'idle',
});
}
});
},
@@ -67,7 +65,12 @@ export const useStorageUnitStore = create<StorageUnitStore>()(
});
},
// Status updates
clearStorageUnits: () => {
set(() => ({
storageUnits: [],
}));
},
setStorageUnitActive: (modelUuid, isActive) => {
set((state) => {
const unit = state.storageUnits.find(s => s.modelUuid === modelUuid);
@@ -86,7 +89,6 @@ export const useStorageUnitStore = create<StorageUnitStore>()(
});
},
// Load updates
updateStorageUnitLoad: (modelUuid, incrementBy) => {
set((state) => {
const unit = state.storageUnits.find(s => s.modelUuid === modelUuid);
@@ -96,7 +98,6 @@ export const useStorageUnitStore = create<StorageUnitStore>()(
});
},
// Time tracking
incrementActiveTime: (modelUuid, incrementBy) => {
set((state) => {
const unit = state.storageUnits.find(s => s.modelUuid === modelUuid);
@@ -115,7 +116,6 @@ export const useStorageUnitStore = create<StorageUnitStore>()(
});
},
// Helpers
getStorageUnitById: (modelUuid) => {
return get().storageUnits.find(s => s.modelUuid === modelUuid);
},

View File

@@ -20,6 +20,7 @@ interface VehiclesStore {
modelUuid: string,
updates: Partial<Omit<VehicleStatus, 'modelUuid' | 'productId'>>
) => void;
clearvehicles: () => void;
setVehicleActive: (modelUuid: string, isActive: boolean) => void;
updateSteeringAngle: (modelUuid: string, steeringAngle: number) => void;
@@ -41,6 +42,8 @@ export const useVehicleStore = create<VehiclesStore>()(
addVehicle: (productId, event) => {
set((state) => {
const exists = state.vehicles.some(v => v.modelUuid === event.modelUuid);
if (!exists) {
state.vehicles.push({
...event,
productId,
@@ -50,6 +53,7 @@ export const useVehicleStore = create<VehiclesStore>()(
currentLoad: 0,
distanceTraveled: 0,
});
}
});
},
@@ -68,6 +72,12 @@ export const useVehicleStore = create<VehiclesStore>()(
});
},
clearvehicles: () => {
set((state) => {
state.vehicles = [];
});
},
setVehicleActive: (modelUuid, isActive) => {
set((state) => {
const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid);

View File

@@ -22,7 +22,11 @@ $text-button-color-dark: #f3f3fd;
// background colors
// ---------- light mode ----------
$background-color: linear-gradient(-45deg, #fcfdfd71 0%, #fcfdfd79 100%);
$background-color-solid-gradient: linear-gradient(-45deg, #fcfdfd 0%, #fcfdfd 100%);
$background-color-solid-gradient: linear-gradient(
-45deg,
#fcfdfd 0%,
#fcfdfd 100%
);
$background-color-solid: #fcfdfd;
$background-color-secondary: #fcfdfd4d;
$background-color-accent: #6f42c1;
@@ -45,7 +49,11 @@ $background-radial-gray-gradient: radial-gradient(
// ---------- dark mode ----------
$background-color-dark: linear-gradient(-45deg, #333333b3 0%, #2d2437b3 100%);
$background-color-solid-gradient-dark: linear-gradient(-45deg, #333333 0%, #2d2437 100%);
$background-color-solid-gradient-dark: linear-gradient(
-45deg,
#333333 0%,
#2d2437 100%
);
$background-color-solid-dark: #19191d;
$background-color-secondary-dark: #19191d99;
$background-color-accent-dark: #6f42c1;
@@ -104,6 +112,21 @@ $color3: #b186ff;
$color4: #8752e8;
$color5: #c7a8ff;
// log indication colors
// ------------ text -------------
$log-default-text-color: #6f42c1;
$log-info-text-color: #488ef6;
$log-warn-text-color: #f3a50c;
$log-error-text-color: #f65648;
$log-success-text-color: #43c06d;
// ------------ background -------------
$log-default-backgroung-color: #6e42c133;
$log-info-background-color: #488ef633;
$log-warn-background-color: #f3a50c33;
$log-error-background-color: #f6564833;
$log-success-background-color: #43c06d33;
// old variables
$accent-color: #6f42c1;
$accent-color-dark: #c4abf1;

View File

@@ -37,11 +37,9 @@
// old colors
--accent-color: #{$accent-color};
--highlight-accent-color: #{$highlight-accent-color};
--accent-gradient-color: #{$acent-gradient};
--faint-gradient-color: #{$faint-gradient};
--background-color-gray: #{$background-color-gray};
--border-color: #{$border-color};
--shadow-main-light: #{$shadow-color};
--box-shadow-light: 0px 2px 4px var(--shadow-main-light);
--box-shadow-medium: 0px 4px 8px var(--shadow-main-light);
@@ -75,7 +73,7 @@
--background-radial-gray-gradient: #{$background-radial-gray-gradient-dark};
// border colors
--border-color: #{$border-color};
--border-color: #{$border-color-dark};
--input-border-color: #{$input-border-color-dark};
--border-color-accent: #{$border-color-accent-dark};
@@ -89,11 +87,9 @@
// old colors
--accent-color: #{$accent-color-dark};
--highlight-accent-color: #{$highlight-accent-color-dark};
--accent-gradient-color: #{$acent-gradient-dark};
--faint-gradient-color: #{$faint-gradient-dark};
--background-color-gray: #{$background-color-gray-dark};
--border-color: #{$border-color-dark};
--shadow-main-dark: #{$shadow-color};
--box-shadow-light: 0px 2px 4px var(--shadow-main-dark);
--box-shadow-medium: 0px 4px 8px var(--shadow-main-dark);
@@ -137,6 +133,23 @@ body {
--color3: #{$color3};
--color4: #{$color4};
--color5: #{$color5};
--default-text-color: #{$log-default-text-color};
--log-info-text-color: #{$log-info-text-color};
--log-warn-text-color: #{$log-warn-text-color};
--log-error-text-color: #{$log-error-text-color};
--log-success-text-color: #{$log-success-text-color};
// ------------ background -------------
--log-default-background-color: #{$log-default-backgroung-color};
--log-info-background-color: #{$log-info-background-color};
--log-warn-background-color: #{$log-warn-background-color};
--log-error-background-color: #{$log-error-background-color};
--log-success-background-color: #{$log-success-background-color};
}
::-webkit-scrollbar {

View File

@@ -22,6 +22,7 @@ section,
transform: translate(0, -50%);
transition: all 0.2s;
box-shadow: $box-shadow-medium;
background: var(--background-color-solid);
canvas {
outline: none;
border: none;

View File

@@ -1,311 +0,0 @@
@use "../../abstracts/variables" as *;
@use "../../abstracts/mixins" as *;
.roiSummary-container {
.roiSummary-wrapper {
background-color: var(--background-color);
.product-info {
display: flex;
}
.playBack {
display: flex;
background-color: var(--background-color);
border-radius: 12px;
padding: 6px;
.info {
span {
font-size: var(--font-size-xlarge);
&:first-child {
color: #31C756;
}
&:last-child {
color: var(--text-color);
}
}
}
}
.roi-details {
display: flex;
align-items: center;
gap: 12px;
.progress-wrapper {
width: 250px;
display: flex;
flex-direction: column;
gap: 6px;
.content {
display: flex;
flex-direction: column;
gap: 3px;
align-items: center;
.key {
font-size: var(--font-size-xlarge);
color: var(--accent-color);
}
}
}
.roi-progress {
width: 100%;
}
.metrics {
display: flex;
flex-direction: column;
gap: 6px;
.metric-item {
width: 100%;
border-radius: 6px;
border: 1px solid #00FF56;
background: #17eb5d65;
display: flex;
flex-direction: column;
padding: 4px 6px;
&:last-child {
align-items: center;
}
.metric-label {
font-size: 10px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.metric-value {
text-align: center;
line-height: 20px;
}
}
.metric-wrapper {
display: flex;
gap: 6px;
.metric-item {
background-color: var(--background-color);
border: 1px solid var(--Grays-Gray-6, #F2F2F7);
}
}
}
}
.cost-breakdown {
background-color: var(--background-color);
border: 1px solid var(--text-disabled);
border-radius: 8px;
padding: 16px;
.breakdown-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-bottom: 16px;
.section-wrapper {
display: flex;
gap: 4px;
align-items: center;
}
.section-number {
font-size: 20px;
color: #00aaff;
}
.section-title {
font-size: var(--font-size-regular);
color: var(--text-color);
}
.expand-icon {
font-size: 16px;
color: var(--text-color);
cursor: pointer;
transform: rotate(90deg);
transition: transform 0.2s linear;
}
.expand-icon.open {
transform: rotate(0deg);
}
}
.breakdown-table {
width: 100%;
border-collapse: collapse;
border-radius: 8px;
th,
td {
padding: 8px;
text-align: left;
border-top: 1px solid var(--text-disabled);
border-bottom: 1px solid var(--text-disabled);
}
th:first-child,
td:first-child {
border-left: 1px solid var(--text-disabled);
}
th:last-child,
td:last-child {
border-right: 1px solid var(--text-disabled);
}
th {
background-color: var(--background-color);
color: #333;
}
.total-row,
.net-profit-row {
font-weight: bold;
color: #333;
}
}
}
.tips-section {
background-color: var(--background-color);
border-radius: 8px;
display: flex;
flex-direction: column;
gap: 6px;
padding: 12px;
.tip-header {
display: flex;
align-items: center;
.tip-title {
color: var(--text-color);
font-weight: 600;
}
}
.tip-description {
span {
font-size: var(--font-size-xlarge);
color: #34C759;
&:first-child {
color: #34C759;
}
&:nth-child(2) {
color: #488EF6;
}
}
}
}
.get-tips-button {
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
margin-top: 8px;
display: inline-block;
display: flex;
justify-content: flex-end;
background: none;
.btn {
color: var(--text-button-color);
background: var(--background-color-button);
padding: 4px 12px;
border-radius: #{$border-radius-large};
display: inline-block;
text-align: center;
}
}
}
.semi-circle-wrapper {
width: 100%;
height: 125px;
overflow-y: hidden;
position: relative;
.semi-circle {
width: 100%;
height: 250px;
border-radius: 50%;
position: relative;
}
.progress-cover {
position: absolute;
width: 75%;
height: 75%;
top: 12.5%;
left: 12.5%;
border-radius: 50%;
}
}
.label-wrapper {
.label {
font-size: var(--font-size-xxxlarge);
}
position: absolute;
bottom: 0%;
left: 50%;
transform: translate(-50%, 0%);
font-weight: bold;
font-size: 1.2rem;
color: #333;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
}
// Breakdown Table Open/Close Logic
.breakdown-table-wrapper {
&.closed {
max-height: 0;
padding: 0;
}
&.open {
max-height: 500px;
}
.breakdown-table {
width: 100%;
border-collapse: collapse;
th,
td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
}
}

View File

@@ -1,3 +1,6 @@
@use "../../abstracts/variables" as *;
@use "../../abstracts/mixins" as *;
.footer-wrapper {
position: absolute;
bottom: 0;
@@ -6,7 +9,7 @@
z-index: 1;
display: flex;
justify-content: space-between;
padding: 12px 24px;
padding: 2px 12px;
.selection-wrapper {
display: flex;
@@ -33,7 +36,6 @@
.logs-detail,
.version {
border-radius: 12px;
background: var(--background-color);
padding: 3px 6px;
@@ -43,15 +45,78 @@
gap: 6px;
}
.log {
background-color: var(--log-default-background-color);
.log-message {
color: var(--default-text-color);
}
}
.info {
background-color: var(--log-info-background-color);
.log-message {
color: var(--log-info-text-color);
}
}
.error {
background-color: var(--log-error-background-color);
.log-message {
color: var(--log-error-text-color);
}
}
.warning {
background-color: var(--log-warn-background-color);
.log-message {
color: var(--log-warn-text-color);
}
}
.success {
background-color: var(--log-success-background-color);
.log-message {
color: var(--log-success-text-color);
}
}
.logs-detail {
// background-color: #fff;
padding: 2px 12px;
cursor: pointer;
.log-icon {
@include flex-center;
}
.log-message {
max-width: 40vw;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.version {
font-size: var(--font-size-tiny);
display: flex;
gap: 6px;
.icon {
@include flex-center;
}
}
}
}

View File

@@ -1,8 +1,10 @@
@use "../../abstracts/variables" as *;
@use "../../abstracts/mixins" as *;
.log-list-container {
width: 100vw;
height: 100vh;
// background: var(--background-color-secondary);
// backdrop-filter: blur(2px);
background: var(--background-color-secondary);
.log-list-wrapper {
height: 50%;
@@ -19,6 +21,7 @@
flex-direction: column;
gap: 12px;
backdrop-filter: blur(50px);
outline: 1px solid var(--border-color);
.log-header {
display: flex;
@@ -31,8 +34,13 @@
}
.close {
// transform: scale(1.5);
@include flex-center;
height: 28px;
width: 28px;
cursor: pointer;
svg {
scale: 1.6;
}
}
}
@@ -46,7 +54,7 @@
}
.log-nav.active {
background-color: var(--accent-color);
background-color: var(--background-color-accent);
color: var(--text-button-color);
}
}
@@ -59,6 +67,8 @@
background: var(--background-color);
padding: 18px 10px;
border-radius: 16px;
outline: 1px solid var(--border-color);
outline-offset: -1px;
.log-entry {
padding: 4px;
@@ -68,6 +78,24 @@
align-items: center;
gap: 6px;
.log-icon {
@include flex-center;
}
.log-entry-message-container {
@include flex-space-between;
gap: 12px;
width: 100%;
.message-time {
font-size: var(--font-size-tiny);
font-weight: 300;
opacity: 0.8;
text-wrap: nowrap;
}
.log-entry-message{
width: 100%;
}
}
&:nth-child(odd) {
background: var(--background-color);
}

View File

@@ -1,3 +1,6 @@
@use "../../abstracts/variables" as *;
@use "../../abstracts/mixins" as *;
.analysis {
position: fixed;
top: 0;
@@ -19,12 +22,8 @@
.analysis-card {
min-width: 333px;
background: var(--background-color);
border-radius: 20px;
padding: 8px;
backdrop-filter: blur(10px);
outline: 1px solid var(--border-color);
outline-offset: -1px;
pointer-events: all;
.analysis-card-wrapper {
@@ -35,6 +34,9 @@
display: flex;
flex-direction: column;
gap: 14px;
backdrop-filter: blur(10px);
outline: 1px solid var(--border-color);
outline-offset: -1px;
.card-header {
width: 100%;
@@ -43,13 +45,13 @@
align-items: center;
.main-header {
color: var(--text-color);
line-height: 20px;
font-size: var(--font-size-regular);
}
.sub-header {
color: var(--input-text-color);
font-size: var(--font-size-tiny);
color: var(--text-button-color);
}
}
@@ -173,7 +175,7 @@
padding: 8px;
display: flex;
flex-direction: column;
gap: 16px;
gap: 12px;
&:first-child {
width: 85%;
@@ -189,21 +191,6 @@
align-items: center;
justify-content: end;
gap: 6px;
.progress-indicator {
padding-top: 10px;
}
.value-wrapper {
.value {
font-size: var(--font-size-xlarge);
}
.unit {
font-size: var(--font-size-small);
}
}
}
}
@@ -245,7 +232,7 @@
justify-content: space-between;
width: 100%;
gap: 6px;
padding-top: 3px;
.shift-wrapper {
display: flex;
align-items: center;
@@ -288,4 +275,276 @@
}
}
}
.roiSummary-wrapper {
max-width: 470px;
background-color: var(--background-color);
.product-info {
display: flex;
align-items: center;
gap: 6px;
}
.playBack {
display: flex;
align-items: center;
gap: 2px;
border-radius: 66px;
background: var(--background-color);
padding: 6px 12px;
border: 1px solid var(--border-color);
.info {
span {
font-size: var(--font-size-xlarge);
&:first-child {
color: #31c756;
}
&:last-child {
color: var(--text-color);
}
}
}
}
.roi-details {
display: flex;
align-items: center;
gap: 12px;
.progress-wrapper {
width: 250px;
display: flex;
flex-direction: column;
gap: 6px;
.content {
display: flex;
flex-direction: column;
gap: 3px;
align-items: center;
.key {
font-size: var(--font-size-xlarge);
color: #28B9F3;
}
}
}
.roi-progress {
width: 100%;
}
.metrics {
display: flex;
flex-direction: column;
gap: 6px;
.metric-item {
width: 100%;
border-radius: #{$border-radius-xxx};
border: 1px solid #00ff56;
background: #17eb5e42;
display: flex;
flex-direction: column;
padding: 4px 8px;
&:last-child {
align-items: center;
}
.metric-label {
opacity: 0.8;
font-size: 10px;
font-weight: 300;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.metric-value {
text-align: center;
line-height: 20px;
}
}
.metric-wrapper {
display: flex;
gap: 6px;
.metric-item {
border-radius: #{$border-radius-large};
background-color: var(--background-color);
border: 1px solid var(--border-color);
}
}
}
}
.cost-breakdown {
background-color: var(--background-color);
border: 1px solid var(--border-color);
border-radius: #{$border-radius-extra-large};
padding: 16px;
.breakdown-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
.section-wrapper {
display: flex;
gap: 4px;
align-items: center;
}
.section-number {
color: #00aaff;
}
.section-title {
font-size: var(--font-size-regular);
color: var(--text-color);
}
.expand-icon {
font-size: 16px;
color: var(--text-color);
cursor: pointer;
transform: rotate(90deg);
transition: transform 0.2s linear;
}
.expand-icon.open {
transform: rotate(0deg);
}
}
.breakdown-table {
width: 100%;
border-collapse: collapse;
border-radius: 8px;
overflow: hidden;
outline: 1px solid var(--border-color);
outline-offset: -1px;
margin-top: 12px;
th,
td {
color: var(--text-color);
padding: 8px;
text-align: left;
border: 1px solid var(--border-color);
}
th {
background-color: var(--background-color);
}
}
}
.tips-section {
background-color: var(--background-color);
border-radius: #{$border-radius-large};
outline: 1px solid var(--border-color);
display: flex;
flex-direction: column;
gap: 6px;
padding: 12px;
.tip-header {
display: flex;
align-items: center;
.tip-title {
color: var(--text-color);
font-weight: 600;
}
}
.tip-description {
span {
font-size: var(--font-size-xlarge);
color: #34c759;
&:first-child {
color: #34c759;
}
&:nth-child(2) {
color: #488ef6;
}
}
}
}
.get-tips-button {
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
margin-top: 8px;
display: inline-block;
display: flex;
justify-content: flex-end;
background: none;
.btn {
color: var(--text-button-color);
background: var(--background-color-button);
padding: 4px 12px;
border-radius: #{$border-radius-large};
display: inline-block;
text-align: center;
}
}
}
.svg-half-donut {
position: relative;
.label-wrapper {
width: 100%;
position: absolute;
bottom: 0px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.label {
font-size: var(--font-size-xlarge);
}
}
}
}
.breakdown-table-wrapper {
&.closed {
max-height: 0;
padding: 0;
}
&.open {
max-height: 500px;
}
.breakdown-table {
width: 100%;
border-collapse: collapse;
th,
td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
}
}
// Breakdown Table Open/Close Logic

View File

@@ -13,7 +13,6 @@
@use 'components/button';
@use 'components/form';
@use 'components/input';
@use 'components/layouts';
@use 'components/lists';
@use 'components/moduleToggle';
@use 'components/templates';
@@ -22,11 +21,10 @@
@use 'components/visualization/ui/styledWidgets';
@use 'components/visualization/floating/common';
@use 'components/marketPlace/marketPlace';
@use 'components/simulation/simulation';
@use 'components/menu/menu';
@use 'components/confirmationPopUp';
@use 'components/analysis/analysis';
@use 'components/analysis/ROISummary.scss';
@use 'components/simulation/simulation';
@use 'components/simulation/analysis';
@use 'components/logs/logs';
@use 'components/footer/footer.scss';

View File

@@ -88,6 +88,7 @@ interface StoragePointSchema {
actionType: "store";
materials: { materialName: string; materialId: string; }[];
storageCapacity: number;
triggers: TriggerSchema[];
};
}
@@ -143,6 +144,10 @@ interface MachineStatus extends MachineEventSchema {
isActive: boolean;
idleTime: number;
activeTime: number;
currentAction?: {
actionUuid: string;
actionName: string;
};
}
interface ArmBotStatus extends RoboticArmEventSchema {