Merge remote-tracking branch 'origin/simulation' into simulation-animation

This commit is contained in:
SreeNath14 2025-04-16 11:45:29 +05:30
commit b46b468e1c
43 changed files with 1501 additions and 952 deletions

View File

@ -2,7 +2,7 @@ import React, { useRef, useMemo, useCallback, useState } from "react";
import { InfoIcon, AddIcon, RemoveIcon, ResizeHeightIcon } from "../../../icons/ExportCommonIcons"; import { InfoIcon, AddIcon, RemoveIcon, ResizeHeightIcon } from "../../../icons/ExportCommonIcons";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import { useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store"; import { useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store";
import * as SimulationTypes from '../../../../types/simulation'; import * as SimulationTypes from '../../../../types/simulationTypes';
import LabledDropdown from "../../../ui/inputs/LabledDropdown"; import LabledDropdown from "../../../ui/inputs/LabledDropdown";
import { handleResize } from "../../../../functions/handleResizePannel"; import { handleResize } from "../../../../functions/handleResizePannel";
@ -225,19 +225,28 @@ const ArmBotMechanics: React.FC = () => {
}, [selectedPoint, selectedProcessIndex, handleProcessChange]); }, [selectedPoint, selectedProcessIndex, handleProcessChange]);
const handleTriggerSelect = useCallback((displayName: string, index: number) => { const handleTriggerSelect = useCallback((displayName: string, index: number) => {
const selected = connectedTriggers.find(t => t.displayName === displayName); const availableOptions = getFilteredTriggerOptions(index);
const selectedDisplayIndex = availableOptions.indexOf(displayName);
const filteredTriggers = connectedTriggers.filter(trigger =>
!selectedPoint?.actions.processes
?.filter((_, i) => i !== index)
.map(p => p.triggerId)
.includes(trigger.uuid)
);
const selected = filteredTriggers[selectedDisplayIndex];
if (!selected || !selectedPoint?.actions.processes) return; if (!selected || !selectedPoint?.actions.processes) return;
const oldProcess = selectedPoint.actions.processes[index]; const oldProcess = selectedPoint.actions.processes[index];
const updatedProcesses = [...selectedPoint.actions.processes]; const updatedProcesses = [...selectedPoint.actions.processes];
// Only reset start/end if new trigger invalidates them (your logic can expand this)
updatedProcesses[index] = { updatedProcesses[index] = {
...oldProcess, ...oldProcess,
triggerId: selected.uuid, triggerId: selected.uuid,
startPoint: oldProcess.startPoint || "", // preserve if exists startPoint: oldProcess.startPoint || "",
endPoint: oldProcess.endPoint || "" // preserve if exists endPoint: oldProcess.endPoint || ""
}; };
handleProcessChange(updatedProcesses); handleProcessChange(updatedProcesses);
@ -299,8 +308,10 @@ const ArmBotMechanics: React.FC = () => {
<InputWithDropDown <InputWithDropDown
key={`speed-${selectedPoint.uuid}`} key={`speed-${selectedPoint.uuid}`}
label="ArmBot Speed" label="ArmBot Speed"
min={0.1}
step={0.1}
value={selectedPoint.actions.speed.toString()} value={selectedPoint.actions.speed.toString()}
onChange={(value) => handleSpeedChange(parseInt(value))} onChange={(value) => handleSpeedChange(parseFloat(value))}
/> />
<div className="actions"> <div className="actions">

View File

@ -17,7 +17,7 @@ import {
useSocketStore, useSocketStore,
} from "../../../../store/store"; } from "../../../../store/store";
import * as THREE from "three"; import * as THREE from "three";
import * as SimulationTypes from "../../../../types/simulation"; import * as SimulationTypes from "../../../../types/simulationTypes";
import InputToggle from "../../../ui/inputs/InputToggle"; import InputToggle from "../../../ui/inputs/InputToggle";
import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt";

View File

@ -2,7 +2,7 @@ import React, { useRef, useMemo, useCallback } from "react";
import { InfoIcon } from "../../../icons/ExportCommonIcons"; import { InfoIcon } from "../../../icons/ExportCommonIcons";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import { useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store"; import { useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store";
import * as SimulationTypes from '../../../../types/simulation'; import * as SimulationTypes from '../../../../types/simulationTypes';
import LabledDropdown from "../../../ui/inputs/LabledDropdown"; import LabledDropdown from "../../../ui/inputs/LabledDropdown";
import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt";

View File

@ -2,7 +2,7 @@ import React, { useRef, useMemo } from "react";
import { InfoIcon } from "../../../icons/ExportCommonIcons"; import { InfoIcon } from "../../../icons/ExportCommonIcons";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import { useEditingPoint, useEyeDropMode, usePreviewPosition, useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store"; import { useEditingPoint, useEyeDropMode, usePreviewPosition, useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store";
import * as SimulationTypes from '../../../../types/simulation'; import * as SimulationTypes from '../../../../types/simulationTypes';
import PositionInput from "../customInput/PositionInputs"; import PositionInput from "../customInput/PositionInputs";
import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt";
import LabeledButton from "../../../ui/inputs/LabledButton"; import LabeledButton from "../../../ui/inputs/LabledButton";

View File

@ -21,14 +21,17 @@ const BarChartInput = (props: Props) => {
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0] const organization = email?.split("@")[1]?.split(".")[0]
const [isLoading, setLoading] = useState<boolean>(true);
useEffect(() => { useEffect(() => {
const fetchZoneData = async () => { const fetchZoneData = async () => {
try { try {
setLoading(true)
const response = await axios.get(`http://${iotApiUrl}/floatinput`); const response = await axios.get(`http://${iotApiUrl}/floatinput`);
if (response.status === 200) { if (response.status === 200) {
// console.log("dropdown data:", response.data); // console.log("dropdown data:", response.data);
setDropDownData(response.data); setDropDownData(response.data);
setLoading(false);
} else { } else {
console.log("Unexpected response:", response); console.log("Unexpected response:", response);
} }
@ -148,6 +151,7 @@ const BarChartInput = (props: Props) => {
onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)} onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand selectedValue={selections[inputKey]} // Load from Zustand
isLoading={isLoading}
/> />
<div className="icon"> <div className="icon">
<AddIcon /> <AddIcon />

View File

@ -21,14 +21,17 @@ const FleetEfficiencyInputComponent = (props: Props) => {
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0] const organization = email?.split("@")[1]?.split(".")[0]
const [isLoading, setLoading] = useState<boolean>(true);
useEffect(() => { useEffect(() => {
const fetchZoneData = async () => { const fetchZoneData = async () => {
try { try {
const response = await axios.get(`http://${iotApiUrl}/floatinput`); const response = await axios.get(`http://${iotApiUrl}/floatinput`);
setLoading(true)
if (response.status === 200) { if (response.status === 200) {
// console.log("dropdown data:", response.data); // console.log("dropdown data:", response.data);
setDropDownData(response.data); setDropDownData(response.data);
setLoading(false)
} else { } else {
console.log("Unexpected response:", response); console.log("Unexpected response:", response);
} }
@ -149,6 +152,7 @@ const FleetEfficiencyInputComponent = (props: Props) => {
onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)} onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand selectedValue={selections[inputKey]} // Load from Zustand
isLoading={isLoading}
/> />
<div className="icon"> <div className="icon">
<AddIcon /> <AddIcon />

View File

@ -21,14 +21,17 @@ const FlotingWidgetInput = (props: Props) => {
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0] const organization = email?.split("@")[1]?.split(".")[0]
const [isLoading, setLoading] = useState<boolean>(true);
useEffect(() => { useEffect(() => {
const fetchZoneData = async () => { const fetchZoneData = async () => {
try { try {
setLoading(true)
const response = await axios.get(`http://${iotApiUrl}/floatinput`); const response = await axios.get(`http://${iotApiUrl}/floatinput`);
if (response.status === 200) { if (response.status === 200) {
// console.log("dropdown data:", response.data); // console.log("dropdown data:", response.data);
setDropDownData(response.data); setDropDownData(response.data);
setLoading(false);
} else { } else {
console.log("Unexpected response:", response); console.log("Unexpected response:", response);
} }
@ -149,6 +152,7 @@ const FlotingWidgetInput = (props: Props) => {
onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)} onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand selectedValue={selections[inputKey]} // Load from Zustand
isLoading={isLoading}
/> />
<div className="icon"> <div className="icon">
<AddIcon /> <AddIcon />

View File

@ -140,14 +140,17 @@ const LineGrapInput = (props: Props) => {
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0] const organization = email?.split("@")[1]?.split(".")[0]
const [isLoading, setLoading] = useState<boolean>(true);
useEffect(() => { useEffect(() => {
const fetchZoneData = async () => { const fetchZoneData = async () => {
try { try {
setLoading(true)
const response = await axios.get(`http://${iotApiUrl}/floatinput`); const response = await axios.get(`http://${iotApiUrl}/floatinput`);
if (response.status === 200) { if (response.status === 200) {
// console.log("dropdown data:", response.data); // console.log("dropdown data:", response.data);
setDropDownData(response.data); setDropDownData(response.data);
setLoading(false)
} else { } else {
console.log("Unexpected response:", response); console.log("Unexpected response:", response);
} }
@ -267,6 +270,7 @@ const LineGrapInput = (props: Props) => {
onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)} onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand selectedValue={selections[inputKey]} // Load from Zustand
isLoading={isLoading}
/> />
<div className="icon"> <div className="icon">
<AddIcon /> <AddIcon />

View File

@ -21,14 +21,17 @@ const PieChartInput = (props: Props) => {
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0] const organization = email?.split("@")[1]?.split(".")[0]
const [isLoading, setLoading] = useState<boolean>(true);
useEffect(() => { useEffect(() => {
const fetchZoneData = async () => { const fetchZoneData = async () => {
try { try {
setLoading(true)
const response = await axios.get(`http://${iotApiUrl}/floatinput`); const response = await axios.get(`http://${iotApiUrl}/floatinput`);
if (response.status === 200) { if (response.status === 200) {
// console.log("dropdown data:", response.data); // console.log("dropdown data:", response.data);
setDropDownData(response.data); setDropDownData(response.data);
setLoading(false)
} else { } else {
console.log("Unexpected response:", response); console.log("Unexpected response:", response);
} }
@ -148,6 +151,7 @@ const PieChartInput = (props: Props) => {
onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)} onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand selectedValue={selections[inputKey]} // Load from Zustand
isLoading={isLoading}
/> />
<div className="icon"> <div className="icon">
<AddIcon /> <AddIcon />

View File

@ -21,14 +21,17 @@ const Progress1Input = (props: Props) => {
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0] const organization = email?.split("@")[1]?.split(".")[0]
const [isLoading, setLoading] = useState<boolean>(true);
useEffect(() => { useEffect(() => {
const fetchZoneData = async () => { const fetchZoneData = async () => {
try { try {
setLoading(true)
const response = await axios.get(`http://${iotApiUrl}/floatinput`); const response = await axios.get(`http://${iotApiUrl}/floatinput`);
if (response.status === 200) { if (response.status === 200) {
// console.log("dropdown data:", response.data); // console.log("dropdown data:", response.data);
setDropDownData(response.data); setDropDownData(response.data);
setLoading(false)
} else { } else {
console.log("Unexpected response:", response); console.log("Unexpected response:", response);
} }
@ -142,6 +145,7 @@ const Progress1Input = (props: Props) => {
onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)} onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand selectedValue={selections[inputKey]} // Load from Zustand
isLoading={isLoading}
/> />
<div className="icon"> <div className="icon">
<AddIcon /> <AddIcon />

View File

@ -21,14 +21,17 @@ const Progress2Input = (props: Props) => {
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0] const organization = email?.split("@")[1]?.split(".")[0]
const [isLoading, setLoading] = useState<boolean>(true);
useEffect(() => { useEffect(() => {
const fetchZoneData = async () => { const fetchZoneData = async () => {
try { try {
setLoading(true)
const response = await axios.get(`http://${iotApiUrl}/floatinput`); const response = await axios.get(`http://${iotApiUrl}/floatinput`);
if (response.status === 200) { if (response.status === 200) {
// console.log("dropdown data:", response.data); // console.log("dropdown data:", response.data);
setDropDownData(response.data); setDropDownData(response.data);
setLoading(false)
} else { } else {
console.log("Unexpected response:", response); console.log("Unexpected response:", response);
} }
@ -142,6 +145,7 @@ const Progress2Input = (props: Props) => {
onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)} onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand selectedValue={selections[inputKey]} // Load from Zustand
isLoading={isLoading}
/> />
<div className="icon"> <div className="icon">
<AddIcon /> <AddIcon />

View File

@ -21,14 +21,17 @@ const WarehouseThroughputInputComponent = (props: Props) => {
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0] const organization = email?.split("@")[1]?.split(".")[0]
const [isLoading, setLoading] = useState<boolean>(true);
useEffect(() => { useEffect(() => {
const fetchZoneData = async () => { const fetchZoneData = async () => {
try { try {
setLoading(true)
const response = await axios.get(`http://${iotApiUrl}/floatinput`); const response = await axios.get(`http://${iotApiUrl}/floatinput`);
if (response.status === 200) { if (response.status === 200) {
// console.log("dropdown data:", response.data); // console.log("dropdown data:", response.data);
setDropDownData(response.data); setDropDownData(response.data);
setLoading(false)
} else { } else {
console.log("Unexpected response:", response); console.log("Unexpected response:", response);
} }
@ -149,6 +152,7 @@ const WarehouseThroughputInputComponent = (props: Props) => {
onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)} onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand selectedValue={selections[inputKey]} // Load from Zustand
isLoading={isLoading}
/> />
<div className="icon"> <div className="icon">
<AddIcon /> <AddIcon />

View File

@ -21,14 +21,17 @@ const Widget2InputCard3D = (props: Props) => {
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0] const organization = email?.split("@")[1]?.split(".")[0]
const [isLoading, setLoading] = useState<boolean>(true);
useEffect(() => { useEffect(() => {
const fetchZoneData = async () => { const fetchZoneData = async () => {
try { try {
setLoading(true)
const response = await axios.get(`http://${iotApiUrl}/floatinput`); const response = await axios.get(`http://${iotApiUrl}/floatinput`);
if (response.status === 200) { if (response.status === 200) {
// console.log("dropdown data:", response.data); // console.log("dropdown data:", response.data);
setDropDownData(response.data); setDropDownData(response.data);
setLoading(false)
} else { } else {
console.log("Unexpected response:", response); console.log("Unexpected response:", response);
} }
@ -147,6 +150,7 @@ const Widget2InputCard3D = (props: Props) => {
onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)} onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand selectedValue={selections[inputKey]} // Load from Zustand
isLoading={isLoading}
/> />
<div className="icon"> <div className="icon">
<AddIcon /> <AddIcon />

View File

@ -19,14 +19,17 @@ const Widget3InputCard3D = () => {
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0] const organization = email?.split("@")[1]?.split(".")[0]
const [isLoading, setLoading] = useState<boolean>(true);
useEffect(() => { useEffect(() => {
const fetchZoneData = async () => { const fetchZoneData = async () => {
try { try {
setLoading(true)
const response = await axios.get(`http://${iotApiUrl}/getinput`); const response = await axios.get(`http://${iotApiUrl}/getinput`);
if (response.status === 200) { if (response.status === 200) {
// console.log("dropdown data:", response.data); // console.log("dropdown data:", response.data);
setDropDownData(response.data); setDropDownData(response.data);
setLoading(false)
} else { } else {
console.log("Unexpected response:", response); console.log("Unexpected response:", response);
} }
@ -140,6 +143,7 @@ const Widget3InputCard3D = () => {
onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)} onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand selectedValue={selections[inputKey]} // Load from Zustand
isLoading={isLoading}
/> />
<div className="icon"> <div className="icon">
<AddIcon /> <AddIcon />

View File

@ -21,14 +21,17 @@ const Widget4InputCard3D = (props: Props) => {
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0] const organization = email?.split("@")[1]?.split(".")[0]
const [isLoading, setLoading] = useState<boolean>(true);
useEffect(() => { useEffect(() => {
const fetchZoneData = async () => { const fetchZoneData = async () => {
try { try {
setLoading(true)
const response = await axios.get(`http://${iotApiUrl}/floatinput`); const response = await axios.get(`http://${iotApiUrl}/floatinput`);
if (response.status === 200) { if (response.status === 200) {
// console.log("dropdown data:", response.data); // console.log("dropdown data:", response.data);
setDropDownData(response.data); setDropDownData(response.data);
setLoading(false)
} else { } else {
console.log("Unexpected response:", response); console.log("Unexpected response:", response);
} }
@ -147,6 +150,7 @@ const Widget4InputCard3D = (props: Props) => {
onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)} onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} // Load from Zustand selectedValue={selections[inputKey]} // Load from Zustand
isLoading={isLoading}
/> />
<div className="icon"> <div className="icon">
<AddIcon /> <AddIcon />

View File

@ -4,7 +4,8 @@ import RenameInput from "./RenameInput";
type InputWithDropDownProps = { type InputWithDropDownProps = {
label: string; label: string;
value: string; value: string;
min?: number min?: number;
step?: number;
defaultValue?: string; defaultValue?: string;
options?: string[]; // Array of dropdown options options?: string[]; // Array of dropdown options
activeOption?: string; // The currently active dropdown option activeOption?: string; // The currently active dropdown option
@ -18,6 +19,7 @@ const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
label, label,
value, value,
min, min,
step,
defaultValue, defaultValue,
options, options,
activeOption, activeOption,
@ -45,6 +47,7 @@ const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
<div className="input default" id={separatedWords}> <div className="input default" id={separatedWords}>
<input <input
min={min} min={min}
step={step}
type="number" type="number"
defaultValue={value} defaultValue={value}
onChange={(e) => { onChange={(e) => {

View File

@ -203,6 +203,7 @@ interface MultiLevelDropdownProps {
onSelect: (selectedData: { name: string; fields: string }) => void; onSelect: (selectedData: { name: string; fields: string }) => void;
onUnselect: () => void; onUnselect: () => void;
selectedValue?: { name: string; fields: string }; selectedValue?: { name: string; fields: string };
isLoading?: boolean;
} }
// Main Multi-Level Dropdown Component // Main Multi-Level Dropdown Component
@ -211,6 +212,7 @@ const MultiLevelDropdown = ({
onSelect, onSelect,
onUnselect, onUnselect,
selectedValue, selectedValue,
isLoading = false,
}: MultiLevelDropdownProps) => { }: MultiLevelDropdownProps) => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null); const dropdownRef = useRef<HTMLDivElement>(null);
@ -261,19 +263,22 @@ const MultiLevelDropdown = ({
<div className="dropdown-content "> <div className="dropdown-content ">
{/* loading list */} {/* loading list */}
{/* <div className="loading" /> */}
{/* Unselect Option */} {/* Unselect Option */}
<DropdownItem label="Unselect" onClick={handleItemUnselect} /> <DropdownItem label="Unselect" onClick={handleItemUnselect} />
{/* Nested Dropdown Items */} {/* Nested Dropdown Items */}
{Object.entries(data).map(([key, value]) => ( {
<NestedDropdown isLoading ? <div className="loading" /> :
key={key} Object.entries(data).map(([key, value]) => (
label={key} <NestedDropdown
fields={Object.keys(value)} key={key}
onSelect={handleItemSelect} label={key}
/> fields={Object.keys(value)}
))} onSelect={handleItemSelect}
/>
))
}
</div> </div>
</div> </div>
)} )}

View File

@ -5,7 +5,7 @@ import { toast } from 'react-toastify';
import TempLoader from './tempLoader'; import TempLoader from './tempLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import * as Types from "../../../../types/world/worldTypes"; import * as Types from "../../../../types/world/worldTypes";
import * as SimulationTypes from "../../../../types/simulation"; import * as SimulationTypes from "../../../../types/simulationTypes";
import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils'; import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils';
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import { Socket } from 'socket.io-client'; import { Socket } from 'socket.io-client';
@ -225,7 +225,6 @@ async function handleModelLoad(
eventData as SimulationTypes.ConveyorEventsSchema eventData as SimulationTypes.ConveyorEventsSchema
]); ]);
console.log('data: ', data);
socket.emit("v2:model-asset:add", data); socket.emit("v2:model-asset:add", data);
} else if (res.type === "Vehicle") { } else if (res.type === "Vehicle") {
@ -365,7 +364,7 @@ async function handleModelLoad(
uuid: pointUUID, uuid: pointUUID,
position: res.points.position as [number, number, number], position: res.points.position as [number, number, number],
rotation: res.points.rotation as [number, number, number], rotation: res.points.rotation as [number, number, number],
actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', speed: 1, processes: [] }, actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', speed: 0.2, processes: [] },
triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' }, triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' },
connections: { source: { modelUUID: model.uuid, pointUUID: pointUUID }, targets: [] }, connections: { source: { modelUUID: model.uuid, pointUUID: pointUUID }, targets: [] },
} }

View File

@ -2,7 +2,7 @@ import { toast } from 'react-toastify';
import * as THREE from 'three'; import * as THREE from 'three';
import * as Types from "../../../../types/world/worldTypes"; import * as Types from "../../../../types/world/worldTypes";
import * as SimulationTypes from "../../../../types/simulation"; import * as SimulationTypes from "../../../../types/simulationTypes";
// import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; // import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi';
import { Socket } from 'socket.io-client'; import { Socket } from 'socket.io-client';
import { getFloorAssets } from '../../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; import { getFloorAssets } from '../../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi';

View File

@ -5,7 +5,7 @@ import * as THREE from 'three';
import * as CONSTANTS from '../../../types/world/worldConstants'; import * as CONSTANTS from '../../../types/world/worldConstants';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import * as Types from "../../../types/world/worldTypes"; import * as Types from "../../../types/world/worldTypes";
import * as SimulationTypes from "../../..//types/simulation"; import * as SimulationTypes from "../../../types/simulationTypes";
import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils'; import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils';
import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi'; import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi';
import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi';
@ -228,7 +228,6 @@ function processEventData(item: SimulationTypes.EventData, setSimulationStates:
} else if (item.eventData?.type === 'StaticMachine') { } else if (item.eventData?.type === 'StaticMachine') {
const data: any = item.eventData; const data: any = item.eventData;
item.eventData.points.position = [0, 1.5, 1]
data.modeluuid = item.modeluuid; data.modeluuid = item.modeluuid;
data.modelName = item.modelname; data.modelName = item.modelname;
data.position = item.position; data.position = item.position;

View File

@ -5,7 +5,7 @@ import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore,
import { toast } from "react-toastify"; import { toast } from "react-toastify";
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import * as Types from "../../../../types/world/worldTypes"; import * as Types from "../../../../types/world/worldTypes";
import * as SimulationTypes from "../../../../types/simulation"; import * as SimulationTypes from "../../../../types/simulationTypes";
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, selectionGroup, setDuplicatedObjects, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => { const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, selectionGroup, setDuplicatedObjects, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => {
@ -332,8 +332,8 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
} else if (eventData.type === 'StaticMachine' && eventData) { } else if (eventData.type === 'StaticMachine' && eventData) {
const createStaticMachinePoint = () => { const createStaticMachinePoint = () => {
const pointUUID = THREE.MathUtils.generateUUID(); const pointUUID = THREE.MathUtils.generateUUID();
const vehiclePoint = (eventData as SimulationTypes.StaticMachineEventsSchema)?.points; const staticMachinePoint = (eventData as SimulationTypes.StaticMachineEventsSchema)?.points;
const hasActions = vehiclePoint?.actions !== undefined; const hasActions = staticMachinePoint?.actions !== undefined;
const defaultAction = { const defaultAction = {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
@ -344,11 +344,11 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
return { return {
uuid: pointUUID, uuid: pointUUID,
position: vehiclePoint?.position, position: staticMachinePoint?.position,
// rotation: vehiclePoint?.rotation, rotation: staticMachinePoint?.rotation,
actions: hasActions actions: hasActions
? { ? {
...vehiclePoint.actions, ...staticMachinePoint.actions,
uuid: THREE.MathUtils.generateUUID() uuid: THREE.MathUtils.generateUUID()
} }
: defaultAction, : defaultAction,
@ -410,8 +410,8 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
} else if (eventData.type === 'ArmBot' && eventData) { } else if (eventData.type === 'ArmBot' && eventData) {
const createArmBotPoint = () => { const createArmBotPoint = () => {
const pointUUID = THREE.MathUtils.generateUUID(); const pointUUID = THREE.MathUtils.generateUUID();
const vehiclePoint = (eventData as SimulationTypes.ArmBotEventsSchema)?.points; const armBotPoint = (eventData as SimulationTypes.ArmBotEventsSchema)?.points;
const hasActions = vehiclePoint?.actions !== undefined; const hasActions = armBotPoint?.actions !== undefined;
const defaultAction = { const defaultAction = {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
@ -422,18 +422,19 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
return { return {
uuid: pointUUID, uuid: pointUUID,
position: vehiclePoint?.position, position: armBotPoint?.position,
// rotation: vehiclePoint?.rotation, rotation: armBotPoint?.rotation,
actions: hasActions actions: hasActions
? { ? {
...vehiclePoint.actions, ...armBotPoint.actions,
uuid: THREE.MathUtils.generateUUID() uuid: THREE.MathUtils.generateUUID(),
processes: []
} }
: defaultAction, : defaultAction,
triggers: { triggers: {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
name: vehiclePoint.triggers.name, name: armBotPoint.triggers.name,
type: vehiclePoint.triggers.type, type: armBotPoint.triggers.type,
}, },
connections: { connections: {
source: { modelUUID: obj.uuid, pointUUID }, source: { modelUUID: obj.uuid, pointUUID },

View File

@ -5,7 +5,7 @@ import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore,
import { toast } from "react-toastify"; import { toast } from "react-toastify";
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import * as Types from "../../../../types/world/worldTypes"; import * as Types from "../../../../types/world/worldTypes";
import * as SimulationTypes from "../../../../types/simulation"; import * as SimulationTypes from "../../../../types/simulationTypes";
import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
@ -246,7 +246,7 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
return { return {
uuid: pointUUID, uuid: pointUUID,
position: vehiclePoint?.position, position: vehiclePoint?.position,
// rotation: vehiclePoint?.rotation, rotation: vehiclePoint?.rotation,
actions: hasActions actions: hasActions
? { ? {
...vehiclePoint.actions, ...vehiclePoint.actions,
@ -311,8 +311,8 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
} else if (eventData.type === 'StaticMachine' && eventData) { } else if (eventData.type === 'StaticMachine' && eventData) {
const createStaticMachinePoint = () => { const createStaticMachinePoint = () => {
const pointUUID = THREE.MathUtils.generateUUID(); const pointUUID = THREE.MathUtils.generateUUID();
const vehiclePoint = (eventData as SimulationTypes.StaticMachineEventsSchema)?.points; const staticMachinePoint = (eventData as SimulationTypes.StaticMachineEventsSchema)?.points;
const hasActions = vehiclePoint?.actions !== undefined; const hasActions = staticMachinePoint?.actions !== undefined;
const defaultAction = { const defaultAction = {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
@ -323,11 +323,11 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
return { return {
uuid: pointUUID, uuid: pointUUID,
position: vehiclePoint?.position, position: staticMachinePoint?.position,
// rotation: vehiclePoint?.rotation, rotation: staticMachinePoint?.rotation,
actions: hasActions actions: hasActions
? { ? {
...vehiclePoint.actions, ...staticMachinePoint.actions,
uuid: THREE.MathUtils.generateUUID() uuid: THREE.MathUtils.generateUUID()
} }
: defaultAction, : defaultAction,
@ -389,8 +389,8 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
} else if (eventData.type === 'ArmBot' && eventData) { } else if (eventData.type === 'ArmBot' && eventData) {
const createArmBotPoint = () => { const createArmBotPoint = () => {
const pointUUID = THREE.MathUtils.generateUUID(); const pointUUID = THREE.MathUtils.generateUUID();
const vehiclePoint = (eventData as SimulationTypes.ArmBotEventsSchema)?.points; const armBotPoint = (eventData as SimulationTypes.ArmBotEventsSchema)?.points;
const hasActions = vehiclePoint?.actions !== undefined; const hasActions = armBotPoint?.actions !== undefined;
const defaultAction = { const defaultAction = {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
@ -401,18 +401,19 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
return { return {
uuid: pointUUID, uuid: pointUUID,
position: vehiclePoint?.position, position: armBotPoint?.position,
// rotation: vehiclePoint?.rotation, rotation: armBotPoint?.rotation,
actions: hasActions actions: hasActions
? { ? {
...vehiclePoint.actions, ...armBotPoint.actions,
uuid: THREE.MathUtils.generateUUID() uuid: THREE.MathUtils.generateUUID(),
processes: []
} }
: defaultAction, : defaultAction,
triggers: { triggers: {
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
name: vehiclePoint.triggers.name, name: armBotPoint.triggers.name,
type: vehiclePoint.triggers.type, type: armBotPoint.triggers.type,
}, },
connections: { connections: {
source: { modelUUID: obj.uuid, pointUUID }, source: { modelUUID: obj.uuid, pointUUID },

View File

@ -5,7 +5,7 @@ import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore,
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import * as Types from "../../../../types/world/worldTypes"; import * as Types from "../../../../types/world/worldTypes";
import * as SimulationTypes from "../../../../types/simulation"; import * as SimulationTypes from "../../../../types/simulationTypes";
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) { function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) {

View File

@ -5,7 +5,7 @@ import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore,
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import * as Types from "../../../../types/world/worldTypes"; import * as Types from "../../../../types/world/worldTypes";
import * as SimulationTypes from "../../../../types/simulation"; import * as SimulationTypes from "../../../../types/simulationTypes";
import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, boundingBoxRef }: any) { function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, boundingBoxRef }: any) {

View File

@ -8,7 +8,7 @@ import BoundingBox from "./boundingBoxHelper";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
// import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; // import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi';
import * as Types from "../../../../types/world/worldTypes"; import * as Types from "../../../../types/world/worldTypes";
import * as SimulationTypes from "../../../../types/simulation"; import * as SimulationTypes from "../../../../types/simulationTypes";
import DuplicationControls from "./duplicationControls"; import DuplicationControls from "./duplicationControls";
import CopyPasteControls from "./copyPasteControls"; import CopyPasteControls from "./copyPasteControls";
@ -205,7 +205,6 @@ const SelectionControls: React.FC = () => {
const organization = email ? email.split("@")[1].split(".")[0] : ""; const organization = email ? email.split("@")[1].split(".")[0] : "";
updatedPaths.forEach(async (updatedPath) => { updatedPaths.forEach(async (updatedPath) => {
if (updatedPath.type === "Conveyor") { if (updatedPath.type === "Conveyor") {
// await setEventApi( // await setEventApi(
// organization, // organization,
@ -224,9 +223,7 @@ const SelectionControls: React.FC = () => {
}; };
socket.emit("v2:model-asset:updateEventData", data); socket.emit("v2:model-asset:updateEventData", data);
} else if (updatedPath.type === "Vehicle") { } else if (updatedPath.type === "Vehicle") {
// await setEventApi( // await setEventApi(
// organization, // organization,
// updatedPath.modeluuid, // updatedPath.modeluuid,
@ -240,9 +237,7 @@ const SelectionControls: React.FC = () => {
}; };
socket.emit("v2:model-asset:updateEventData", data); socket.emit("v2:model-asset:updateEventData", data);
} else if (updatedPath.type === "StaticMachine") { } else if (updatedPath.type === "StaticMachine") {
// await setEventApi( // await setEventApi(
// organization, // organization,
// updatedPath.modeluuid, // updatedPath.modeluuid,
@ -256,9 +251,7 @@ const SelectionControls: React.FC = () => {
}; };
socket.emit("v2:model-asset:updateEventData", data); socket.emit("v2:model-asset:updateEventData", data);
} else if (updatedPath.type === "ArmBot") { } else if (updatedPath.type === "ArmBot") {
// await setEventApi( // await setEventApi(
// organization, // organization,
// updatedPath.modeluuid, // updatedPath.modeluuid,
@ -273,239 +266,135 @@ const SelectionControls: React.FC = () => {
socket.emit("v2:model-asset:updateEventData", data); socket.emit("v2:model-asset:updateEventData", data);
} }
}); });
}; };
// const removeConnection = (modelUUID: any) => { const removeConnections = (deletedModelUUIDs: string[]) => {
//
// const removedPath = simulationStates?.flatMap((state) => {
// let shouldInclude = false;
// if (state.type === "Conveyor") { const deletedPointUUIDs = new Set<string>();
// state.points.forEach((point: any) => { simulationStates.forEach(state => {
// const sourceMatch = if (deletedModelUUIDs.includes(state.modeluuid)) {
// point.connections?.source?.modelUUID === modelUUID; if (state.type === "Conveyor" && state.points) {
// const targetMatch = point.connections?.targets?.some( state.points.forEach(point => {
// (target: any) => target.modelUUID === modelUUID deletedPointUUIDs.add(point.uuid);
// ); });
} else if (state.points && 'uuid' in state.points) {
// if (sourceMatch || targetMatch) shouldInclude = true; deletedPointUUIDs.add(state.points.uuid);
// });
// }
// if (state.type === "Vehicle") {
// const targetMatch = state.points.connections?.targets?.some(
// (target: any) => target.modelUUID === modelUUID
// );
// if (targetMatch) shouldInclude = true;
// }
// if (state.type === "StaticMachine") {
// const targetMatch = state.points.connections?.targets?.some(
// (target: any) => target.modelUUID === modelUUID
// );
// if (targetMatch) shouldInclude = true;
// }
// if (state.type === "ArmBot") {
// const sourceMatch =
// state.points.connections?.source?.modelUUID === modelUUID;
// const targetMatch = state.points.connections?.targets?.some(
// (target: any) => target.modelUUID === modelUUID
// );
// const processMatch =
// state.points.actions?.processes?.some(
// (process: any) =>
// process.startPoint === modelUUID || process.endPoint === modelUUID
// ) ?? false;
// if (sourceMatch || targetMatch || processMatch) shouldInclude = true;
// }
// return shouldInclude ? [state] : [];
// });
// updateBackend(removedPath);
//
// return removedPath;
// // updateBackend(updatedPaths);
// // setSimulationStates(updatedStates);
// };
// const removeConnection = (modelUUIDs: any[]) => {
//
// const removedPath = simulationStates?.flatMap((state) => {
// let shouldInclude = false;
// if (state.type === "Conveyor") {
// state.points.forEach((point: any) => {
// const sourceMatch = modelUUIDs.includes(
// point.connections?.source?.modelUUID
// );
// const targetMatch = point.connections?.targets?.some((target: any) =>
// modelUUIDs.includes(target.modelUUID)
// );
// if (sourceMatch || targetMatch) shouldInclude = true;
// });
// }
// if (state.type === "Vehicle") {
// const targetMatch = state.points.connections?.targets?.some(
// (target: any) => modelUUIDs.includes(target.modelUUID)
// );
// if (targetMatch) shouldInclude = true;
// }
// if (state.type === "StaticMachine") {
// const targetMatch = state.points.connections?.targets?.some(
// (target: any) => modelUUIDs.includes(target.modelUUID)
// );
// if (targetMatch) shouldInclude = true;
// }
// if (state.type === "ArmBot") {
// const sourceMatch = modelUUIDs.includes(
// state.points.connections?.source?.modelUUID
// );
// const targetMatch = state.points.connections?.targets?.some(
// (target: any) => modelUUIDs.includes(target.modelUUID)
// );
// const processMatch =
// state.points.actions?.processes?.some(
// (process: any) =>
// modelUUIDs.includes(process.startPoint) ||
// modelUUIDs.includes(process.endPoint)
// ) ?? false;
// if (sourceMatch || targetMatch || processMatch) shouldInclude = true;
// }
// return shouldInclude ? [state] : [];
// });
// updateBackend(removedPath);
//
// return removedPath;
// };
const removeConnection = (modelUUIDs: any[]) => {
const removedPath = simulationStates?.flatMap((state: any) => {
let shouldInclude = false;
// Conveyor type
if (state.type === "Conveyor") {
state.points.forEach((point: any) => {
const sourceMatch = modelUUIDs.includes(point.connections?.source?.modelUUID);
const targetMatch = point.connections?.targets?.some((target: any) => modelUUIDs.includes(target.modelUUID));
if (sourceMatch) {
point.connections.source = {};
shouldInclude = true;
}
if (targetMatch) {
point.connections.targets = [];
shouldInclude = true;
}
});
}
// Vehicle & StaticMachine types
if (state.type === "Vehicle") {
const targets = state.points?.connections?.targets || [];
const targetMatch = targets.some((target: any) => modelUUIDs.includes(target.modelUUID));
if (targetMatch) {
state.points.connections.targets = [];
shouldInclude = true;
} }
} }
if (state.type === "StaticMachine") {
const targets = state.points?.connections?.targets || [];
const targetMatch = targets.some((target: any) => modelUUIDs.includes(target.modelUUID));
if (targetMatch) {
state.points.connections.targets = [];
shouldInclude = true;
}
}
// ArmBot type
if (state.type === "ArmBot") {
const sourceMatch = modelUUIDs.includes(state.points.connections?.source?.modelUUID);
const targetMatch = state.points.connections?.targets?.some(
(target: any) => modelUUIDs.includes(target.modelUUID)
);
// state.points.actions.processes = state.points.actions.processes.filter(
// (process: any) =>
// console.log(
// !modelUUIDs.includes(process.startPoint),
// !modelUUIDs.includes(process.endPoint),
// modelUUIDs,
// process.startPoint,
// process.endPoint
// )
// );
// shouldInclude = true;
// const processMatches = state.points.actions?.processes?.some(
// (process: any) =>
// console.log(
// "process: ",
// process,
// modelUUIDs,
// process.startPoint,
// process.endPoint
// )
// // modelUUIDs.includes(process.startPoint) ||
// // modelUUIDs.includes(process.endPoint)
// );
// const processMatch = state.points.actions?.processes?.some(
// (process: any) =>
// modelUUIDs.includes(String(process.startPoint)) ||
// modelUUIDs.includes(String(process.endPoint))
// );
// console.log("processMatch: ", processMatch);
if (sourceMatch) {
state.points.connections.source = {};
shouldInclude = true;
}
if (targetMatch) {
state.points.connections.targets =
state.points.connections.targets.filter((target: any) => !modelUUIDs.includes(target.modelUUID));
shouldInclude = true;
}
// console.log("processMatch: ", processMatch);
// if (processMatch) {
// state.points.actions.processes =
// state.points.actions.processes.filter((process: any) => {
// const shouldRemove =
// modelUUIDs.includes(process.startPoint) ||
// modelUUIDs.includes(process.endPoint);
// console.log("shouldRemove: ", shouldRemove);
// return !shouldRemove;
// });
// shouldInclude = true;
// }
}
return shouldInclude ? [state] : [];
}); });
updateBackend(removedPath); const updatedStates = simulationStates.map((state) => {
return removedPath; // Handle Conveyor
if (state.type === "Conveyor") {
const updatedConveyor: SimulationTypes.ConveyorEventsSchema = {
...state,
points: state.points.map((point) => {
return {
...point,
connections: {
...point.connections,
targets: point.connections.targets.filter(
(target) => !deletedModelUUIDs.includes(target.modelUUID)
),
},
};
}),
};
return updatedConveyor;
}
// Handle Vehicle
else if (state.type === "Vehicle") {
const updatedVehicle: SimulationTypes.VehicleEventsSchema = {
...state,
points: {
...state.points,
connections: {
...state.points.connections,
targets: state.points.connections.targets.filter(
(target) => !deletedModelUUIDs.includes(target.modelUUID)
),
},
},
};
return updatedVehicle;
}
// Handle StaticMachine
else if (state.type === "StaticMachine") {
const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema =
{
...state,
points: {
...state.points,
connections: {
...state.points.connections,
targets: state.points.connections.targets.filter(
(target) => !deletedModelUUIDs.includes(target.modelUUID)
),
},
},
};
return updatedStaticMachine;
}
// Handle ArmBot
else if (state.type === "ArmBot") {
const updatedArmBot: SimulationTypes.ArmBotEventsSchema = {
...state,
points: {
...state.points,
connections: {
...state.points.connections,
targets: state.points.connections.targets.filter(
(target: any) => !deletedModelUUIDs.includes(target.modelUUID)
),
},
actions: {
...state.points.actions,
processes: state.points.actions.processes?.filter((process) => {
// Check if trigger is from deleted model
const matchedStates = simulationStates.filter((s) => deletedModelUUIDs.includes(s.modeluuid));
if (matchedStates.length > 0) {
if (matchedStates[0]?.type === "StaticMachine") {
const trigPoints = matchedStates[0]?.points;
if (process.triggerId === trigPoints?.triggers?.uuid) {
return false;
}
} else if (matchedStates[0]?.type === "Conveyor") {
const trigPoints = matchedStates[0]?.points;
if (Array.isArray(trigPoints)) {
const nonEmptyTriggers = trigPoints.filter((point) => point && point.triggers && point.triggers.length > 0);
const allTriggerUUIDs = nonEmptyTriggers.flatMap((point) => point.triggers).map((trigger) => trigger.uuid);
if (allTriggerUUIDs.includes(process.triggerId)) {
return false;
}
}
}
}
// Check if startPoint or endPoint is from deleted model
if (deletedPointUUIDs.has(process.startPoint) || deletedPointUUIDs.has(process.endPoint)) {
return false;
}
return true;
}),
},
},
};
return updatedArmBot;
}
return state;
});
const filteredStates = updatedStates.filter((state) => !deletedModelUUIDs.includes(state.modeluuid));
updateBackend(filteredStates);
setSimulationStates(filteredStates);
}; };
const deleteSelection = () => { const deleteSelection = () => {
@ -551,65 +440,17 @@ const SelectionControls: React.FC = () => {
}); });
setSimulationStates((prevEvents: (| SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { setSimulationStates((prevEvents: (| SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => {
const updatedEvents = (prevEvents || []).filter( const updatedEvents = (prevEvents || []).filter((event) => event.modeluuid !== selectedMesh.uuid);
(event) => event.modeluuid !== selectedMesh.uuid
);
return updatedEvents; return updatedEvents;
} });
);
itemsGroupRef.current?.remove(selectedMesh); itemsGroupRef.current?.remove(selectedMesh);
}); });
const allUUIDs = selectedAssets.map((val: any) => val.uuid); const allUUIDs = selectedAssets.map((val: any) => val.uuid);
removeConnection(allUUIDs); removeConnections(allUUIDs);
// const removedPath = simulationStates?.flatMap((path: any) => { const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid));
// let shouldInclude = false;
// if (Array.isArray(path.points)) {
// path.points.forEach((point: any) => {
// const sourceMatch =
// point.connections?.source?.modelUUID === selectedAssets[0].uuid;
// const targetMatch = point.connections?.targets?.some(
// (target: any) => target.modelUUID === selectedAssets[0].uuid
// );
// if (sourceMatch) {
// point.connections.source = {};
// shouldInclude = true;
// }
// if (targetMatch) {
// point.connections.targets = [];
// shouldInclude = true;
// }
// });
// } else {
// const sourceMatch =
// path.connections?.source?.modelUUID === selectedAssets[0].uuid;
// const targetMatch = path.connections?.targets?.some(
// (target: any) => target.modelUUID === selectedAssets[0].uuid
// );
// if (sourceMatch) {
// path.connections.source = {};
// shouldInclude = true;
// }
// if (targetMatch) {
// path.connections.targets = [];
// shouldInclude = true;
// }
// }
// return shouldInclude ? [path] : [];
// });
// updateBackend(removedPath);
const updatedItems = floorItems.filter(
(item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid)
);
setFloorItems(updatedItems); setFloorItems(updatedItems);
} }
toast.success("Selected models removed!"); toast.success("Selected models removed!");

View File

@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
import { useThree } from "@react-three/fiber"; import { useThree } from "@react-three/fiber";
import useModuleStore from "../../../store/useModuleStore"; import useModuleStore from "../../../store/useModuleStore";
import { useSimulationStates } from "../../../store/store"; import { useSimulationStates } from "../../../store/store";
import * as SimulationTypes from '../../../types/simulation'; import * as SimulationTypes from '../../../types/simulationTypes';
import { ArmbotInstances } from "./ArmBotInstances"; import { ArmbotInstances } from "./ArmBotInstances";
interface ArmBotState { interface ArmBotState {
@ -12,26 +12,47 @@ interface ArmBotState {
status: string; status: string;
material: string; material: string;
triggerId: string; triggerId: string;
connections: any connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
isActive?: boolean;
} }
const ArmBot: React.FC = () => { interface StaticMachineState {
uuid: string;
status: string;
actions: { uuid: string; name: string; buffer: number; material: string; };
machineTriggerId: string;
connectedArmBot: string;
}
interface ArmBotProps {
armBots: ArmBotState[];
setArmBots: React.Dispatch<React.SetStateAction<ArmBotState[]>>;
setStaticMachines: React.Dispatch<React.SetStateAction<StaticMachineState[]>>;
}
const ArmBot = ({ armBots, setArmBots, setStaticMachines }: ArmBotProps) => {
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const { scene } = useThree(); const { scene } = useThree();
const { simulationStates } = useSimulationStates(); const { simulationStates } = useSimulationStates();
const [armBots, setArmBots] = useState<ArmBotState[]>([]);
useEffect(() => { useEffect(() => {
const filtered = simulationStates.filter((s): s is SimulationTypes.ArmBotEventsSchema => s.type === "ArmBot"); const filtered = simulationStates.filter((s): s is SimulationTypes.ArmBotEventsSchema => s.type === "ArmBot");
const initialStates: ArmBotState[] = filtered.map(bot => ({ const initialStates: ArmBotState[] = filtered
uuid: bot.modeluuid, .filter(bot => bot.points.connections.targets.length > 0)
position: bot.position, .map(bot => ({
rotation: bot.rotation, uuid: bot.modeluuid,
status: "idle", position: bot.position,
material: "default", rotation: bot.rotation,
triggerId: '', status: "idle",
connections: bot.points.connections material: "default",
})); triggerId: '',
actions: bot.points.actions,
connections: bot.points.connections
}));
setArmBots(initialStates); setArmBots(initialStates);
}, [simulationStates]); }, [simulationStates]);
@ -53,6 +74,7 @@ const ArmBot: React.FC = () => {
index={i} index={i}
armBot={bot} armBot={bot}
setArmBots={setArmBots} setArmBots={setArmBots}
setStaticMachines={setStaticMachines}
/> />
))} ))}
</> </>

View File

@ -1,5 +1,15 @@
import IkInstances from "./IkInstances"; import IkInstances from "./IkInstances";
import armModel from "../../../assets/gltf-glb/rigged/ik_arm_4.glb"; import armModel from "../../../assets/gltf-glb/rigged/ik_arm_4.glb";
import { useEffect, useState } from "react";
import { useThree } from "@react-three/fiber";
import { Vector3 } from "three";
interface Process {
triggerId: string;
startPoint?: Vector3;
endPoint?: Vector3;
speed: number;
}
interface ArmBotState { interface ArmBotState {
uuid: string; uuid: string;
@ -8,18 +18,74 @@ interface ArmBotState {
status: string; status: string;
material: string; material: string;
triggerId: string; triggerId: string;
connections: any connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
isActive?: boolean;
}
interface StaticMachineState {
uuid: string;
status: string;
actions: { uuid: string; name: string; buffer: number; material: string; };
machineTriggerId: string;
connectedArmBot: string;
} }
interface ArmbotInstancesProps { interface ArmbotInstancesProps {
index: number; index: number;
armBot: ArmBotState; armBot: ArmBotState;
setArmBots: (armBots: ArmBotState[]) => void; setArmBots: React.Dispatch<React.SetStateAction<ArmBotState[]>>;
setStaticMachines: React.Dispatch<React.SetStateAction<StaticMachineState[]>>;
} }
export const ArmbotInstances: React.FC<ArmbotInstancesProps> = ({ index, armBot, setArmBots }) => { export const ArmbotInstances: React.FC<ArmbotInstancesProps> = ({ index, armBot, setArmBots, setStaticMachines }) => {
const { scene } = useThree();
const [processes, setProcesses] = useState<Process[]>([]);
useEffect(() => {
if (armBot.actions.processes.length > 0) {
const mappedProcesses = armBot.actions.processes.map((process) => {
return {
triggerId: process.triggerId,
startPoint: scene.getObjectByProperty('uuid', process.startPoint)?.getWorldPosition(new Vector3()),
endPoint: scene.getObjectByProperty('uuid', process.endPoint)?.getWorldPosition(new Vector3()),
speed: armBot.actions.speed
};
});
setProcesses(mappedProcesses);
} else {
setProcesses([]);
}
}, [armBot, scene]);
const updateArmBotStatus = (status: string) => {
setArmBots((prevArmBots) => {
return prevArmBots.map(bot => {
if (bot.uuid === armBot.uuid) {
return { ...bot, status, triggerId: status === 'idle' ? '' : armBot.triggerId };
}
return bot;
});
});
};
return ( return (
<IkInstances key={index} modelUrl={armModel} position={armBot.position} rotation={armBot.rotation} /> <IkInstances
key={index}
uuid={armBot.uuid}
selectedTrigger={armBot.triggerId}
modelUrl={armModel}
position={armBot.position}
rotation={armBot.rotation}
processes={processes}
armBot={armBot}
setArmBots={setArmBots}
setStaticMachines={setStaticMachines}
updateArmBotStatus={updateArmBotStatus}
/>
); );
}; };

View File

@ -1,96 +1,338 @@
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState, useRef } from "react";
import { useFrame } from "@react-three/fiber"; import { useFrame } from "@react-three/fiber";
import * as THREE from "three"; import * as THREE from "three";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import { useSimulationStates } from "../../../store/store";
interface StaticMachineState {
uuid: string;
status: string;
actions: { uuid: string; name: string; buffer: number; material: string; };
machineTriggerId: string;
connectedArmBot: string;
}
interface ArmBotState {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
status: string;
material: string;
triggerId: string;
connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
isActive?: boolean;
}
type IKAnimationControllerProps = {
ikSolver: any;
process: {
triggerId: string;
startPoint: THREE.Vector3;
endPoint: THREE.Vector3;
speed: number;
}[];
selectedTrigger: string;
targetBoneName: string;
uuid: string;
logStatus: (status: string) => void;
groupRef: React.RefObject<THREE.Group>;
armBot: ArmBotState;
setArmBots: React.Dispatch<React.SetStateAction<ArmBotState[]>>;
setStaticMachines: React.Dispatch<React.SetStateAction<StaticMachineState[]>>;
updateArmBotStatus: (status: string) => void;
}
const IKAnimationController = ({ const IKAnimationController = ({
ikSolver, ikSolver,
process, process,
selectedTrigger, selectedTrigger,
targetBoneName, targetBoneName,
}: { uuid,
ikSolver: any; logStatus,
process: { groupRef,
trigger: string; armBot,
start: THREE.Vector3; setArmBots,
end: THREE.Vector3; setStaticMachines,
speed: number; updateArmBotStatus
}[]; }: IKAnimationControllerProps) => {
selectedTrigger: string;
targetBoneName: string;
}) => {
const [progress, setProgress] = useState(0); const [progress, setProgress] = useState(0);
const [initialProgress, setInitialProgress] = useState(0);
const [needsInitialMovement, setNeedsInitialMovement] = useState(true);
const [isInitializing, setIsInitializing] = useState(true);
const restSpeed = 0.1; const restSpeed = 0.1;
const restPosition = new THREE.Vector3(0, 2, 1.6);
const { isPlaying } = usePlayButtonStore();
const { simulationStates } = useSimulationStates();
// Track previous states for comparison
const prevStateRef = useRef({
isInitializing: true,
needsInitialMovement: true,
selectedTrigger: "",
progress: 0
});
// Track previous status for comparison
const prevStatusRef = useRef("");
const initialCurveRef = useRef<THREE.CatmullRomCurve3 | null>(null);
const initialStartPositionRef = useRef<THREE.Vector3 | null>(null);
useEffect(() => { useEffect(() => {
setProgress(0); setProgress(0);
}, [selectedTrigger]); }, [selectedTrigger]);
const processedCurves = useMemo(() => { useEffect(() => {
const restPosition = new THREE.Vector3(0.2, 2.3, 1.6); if (ikSolver) {
return process.map((p) => { const targetBone = ikSolver.mesh.skeleton.bones.find(
const mid = new THREE.Vector3( (b: any) => b.name === targetBoneName
(p.start.x + p.end.x) / 1,
Math.max(p.start.y, p.end.y) + 0.8,
(p.start.z + p.end.z) / 0.9
); );
const points = [ if (targetBone) {
restPosition.clone(), initialStartPositionRef.current = targetBone.position.clone();
p.start.clone(), calculateInitialCurve(targetBone.position);
mid.clone(), logStatus(`[Arm ${uuid}] Initializing IK system, starting position: ${targetBone.position.toArray()}`);
p.end.clone(), }
restPosition.clone(), }
]; }, [ikSolver]);
const curve = new THREE.CatmullRomCurve3(points);
const restToStartDist = points[0].distanceTo(points[1]);
const startToEndDist = points[1].distanceTo(points[3]);
const endToRestDist = points[3].distanceTo(points[4]);
const totalDist = restToStartDist + startToEndDist + endToRestDist; // Log state changes
const restToStartRange = [0, restToStartDist / totalDist]; useEffect(() => {
const startToEndRange = [ const prev = prevStateRef.current;
restToStartRange[1],
restToStartRange[1] + startToEndDist / totalDist,
];
const endToRestRange = [startToEndRange[1], 1];
return { if (prev.isInitializing !== isInitializing) {
trigger: p.trigger, if (!isInitializing) {
curve, logStatus(`[Arm ${uuid}] Completed initialization, now at rest position`);
speed: p.speed, }
restToStartRange, }
startToEndRange,
endToRestRange, if (prev.needsInitialMovement !== needsInitialMovement && !needsInitialMovement) {
}; logStatus(`[Arm ${uuid}] Reached rest position, ready for animation`);
});
}, [process]); }
if (prev.selectedTrigger !== selectedTrigger) {
logStatus(`[Arm ${uuid}] Processing new trigger: ${selectedTrigger}`);
const currentProcess = process.find(p => p.triggerId === prev.selectedTrigger);
if (currentProcess) {
const triggerId = currentProcess.triggerId;
const endPoint = armBot.actions.processes.find((process) => process.triggerId === triggerId)?.endPoint;
// Search simulationStates for a StaticMachine or Conveyor that has a point matching this endPointId
const matchedMachine = simulationStates.find((state) => {
if (state.type === "Conveyor") {
// For Conveyor, points is an array
return (state).points.some(
(point) => point.uuid === endPoint
);
} else if (state.type === "StaticMachine") {
// For StaticMachine, points is an object
return state.points.uuid === endPoint;
}
return false;
});
if (matchedMachine) {
// Log if the end point is a conveyor
if (matchedMachine.type === "Conveyor") {
logStatus(`[Arm ${uuid}] Reached end point which is a conveyor (${matchedMachine.modelName})`);
} else {
logStatus(`[Arm ${uuid}] Reached end point which is a static machine (${matchedMachine.modelName})`);
}
if (matchedMachine.type === "StaticMachine") {
setStaticMachines((machines) => {
return machines.map((machine) => {
if (machine.uuid === matchedMachine.modeluuid) {
return { ...machine, status: "running" };
} else {
return machine;
}
});
});
}
if (matchedMachine.type === "Conveyor") {
setArmBots((prev) =>
prev.map((arm) => {
if (arm.uuid === uuid) {
return {
...arm,
isActive: false
};
}
else {
return arm;
}
})
);
}
}
}
}
// Update previous state
prevStateRef.current = {
isInitializing,
needsInitialMovement,
selectedTrigger,
progress
};
}, [isInitializing, needsInitialMovement, selectedTrigger, progress]);
const calculateInitialCurve = (startPosition: THREE.Vector3) => {
const direction = new THREE.Vector3().subVectors(restPosition, startPosition);
const distance = direction.length();
direction.normalize();
const perpendicular = new THREE.Vector3(-direction.z, 0, direction.x).normalize();
const midHeight = 0.5;
const tiltAmount = 1;
const mid = new THREE.Vector3()
.addVectors(startPosition, restPosition)
.multiplyScalar(0.5)
.add(perpendicular.clone().multiplyScalar(distance * 0.3 * tiltAmount))
.add(new THREE.Vector3(0, midHeight, 0));
initialCurveRef.current = new THREE.CatmullRomCurve3([
startPosition,
new THREE.Vector3().lerpVectors(startPosition, mid, 0.33),
mid,
new THREE.Vector3().lerpVectors(mid, restPosition, 0.66),
restPosition
]);
};
const processedCurves = useMemo(() => {
if (isPlaying)
return process.map((p) => {
const tempLift = 0.5;
const localStart = groupRef.current?.worldToLocal(p.startPoint.clone().add(new THREE.Vector3(0, tempLift, 0)));
const localEnd = groupRef.current?.worldToLocal(p.endPoint.clone().add(new THREE.Vector3(0, tempLift, 0)));
if (localStart && localEnd) {
const mid = new THREE.Vector3(
(localStart.x + localEnd.x) / 1,
Math.max(localStart.y, localEnd.y) + 0.8,
(localStart.z + localEnd.z) / 0.9
);
const points = [
restPosition.clone(),
localStart.clone(),
mid.clone(),
localEnd.clone(),
restPosition.clone(),
];
const curve = new THREE.CatmullRomCurve3(points);
const restToStartDist = points[0].distanceTo(points[1]);
const startToEndDist = points[1].distanceTo(points[3]);
const endToRestDist = points[3].distanceTo(points[4]);
const totalDist = restToStartDist + startToEndDist + endToRestDist;
const restToStartRange = [0, restToStartDist / totalDist];
const startToEndRange = [
restToStartRange[1],
restToStartRange[1] + startToEndDist / totalDist,
];
const endToRestRange = [startToEndRange[1], 1];
return {
trigger: p.triggerId,
curve,
speed: p.speed,
restToStartRange,
startToEndRange,
endToRestRange,
};
}
});
}, [process, groupRef, isPlaying]);
const activeCurve = useMemo(() => { const activeCurve = useMemo(() => {
return processedCurves.find((c) => c.trigger === selectedTrigger); if (isPlaying && processedCurves)
}, [processedCurves, selectedTrigger]); return processedCurves.find((c) => c?.trigger === selectedTrigger);
}, [processedCurves, selectedTrigger, isPlaying]);
// Initial movement to rest position
useFrame((_, delta) => { useFrame((_, delta) => {
if (!ikSolver || !activeCurve) return; if (!ikSolver || !needsInitialMovement || !isInitializing || !initialCurveRef.current) return;
const { curve, speed, startToEndRange } = activeCurve; const targetBone = ikSolver.mesh.skeleton.bones.find(
(b: any) => b.name === targetBoneName
);
if (!targetBone) return;
setInitialProgress((prev) => {
const next = prev + delta * 0.5;
if (next >= 1) {
targetBone.position.copy(restPosition);
setNeedsInitialMovement(false);
setIsInitializing(false);
return 1;
}
targetBone.position.copy(initialCurveRef.current!.getPoint(next));
return next;
});
ikSolver.update();
});
// Main animation loop
useFrame((_, delta) => {
if (!ikSolver || !activeCurve || isInitializing || !isPlaying) return;
const { curve, speed, restToStartRange, startToEndRange, endToRestRange } = activeCurve;
const targetBone = ikSolver.mesh.skeleton.bones.find( const targetBone = ikSolver.mesh.skeleton.bones.find(
(b: any) => b.name === targetBoneName (b: any) => b.name === targetBoneName
); );
if (!targetBone) return; if (!targetBone) return;
let currentSpeed = restSpeed; let currentSpeed = restSpeed;
if (progress >= startToEndRange[0] && progress < startToEndRange[1]) { let currentStatus = "idle"; // Default status
// Determine current phase and status
if (progress < restToStartRange[1]) {
currentSpeed = restSpeed;
currentStatus = "moving"; // Moving to start point
} else if (progress >= startToEndRange[0] && progress < startToEndRange[1]) {
currentSpeed = speed; currentSpeed = speed;
currentStatus = "moving"; // Moving between points
} else if (progress >= endToRestRange[0] && progress < 1) {
currentSpeed = restSpeed;
currentStatus = "moving"; // Returning to rest
} else if (progress >= 1) {
currentStatus = "idle"; // Completed cycle
} }
setProgress((prev) => { // Update status when it changes
const next = prev + delta * currentSpeed; if (prevStatusRef.current !== currentStatus) {
if (next >= 1) { updateArmBotStatus(currentStatus);
targetBone.position.copy(curve.getPoint(1)); prevStatusRef.current = currentStatus;
return 1; }
}
targetBone.position.copy(curve.getPoint(next)); // Only update progress if we're not already at the end
return next; if (progress < 1) {
}); setProgress((prev) => {
const next = prev + delta * currentSpeed;
return Math.min(next, 1); // Cap at 1
});
}
// Update bone position based on progress
if (progress < 1) {
targetBone.position.copy(curve.getPoint(progress));
} else {
targetBone.position.copy(curve.getPoint(1));
}
ikSolver.update(); ikSolver.update();
}); });

View File

@ -1,43 +1,70 @@
import * as THREE from "three"; import * as THREE from "three";
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { useLoader } from "@react-three/fiber"; import { useFrame, useLoader } from "@react-three/fiber";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { clone } from "three/examples/jsm/utils/SkeletonUtils"; import { clone } from "three/examples/jsm/utils/SkeletonUtils";
import { CCDIKSolver, CCDIKHelper, } from "three/examples/jsm/animation/CCDIKSolver"; import { CCDIKSolver, CCDIKHelper, } from "three/examples/jsm/animation/CCDIKSolver";
import IKAnimationController from "./IKAnimationController"; import IKAnimationController from "./IKAnimationController";
import { TransformControls } from "@react-three/drei";
const IkInstances = ({ modelUrl, position, rotation }: { modelUrl: string; position: [number, number, number]; rotation: [number, number, number]; }) => { interface StaticMachineState {
uuid: string;
status: string;
actions: { uuid: string; name: string; buffer: number; material: string; };
machineTriggerId: string;
connectedArmBot: string;
}
interface ArmBotState {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
status: string;
material: string;
triggerId: string;
connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
isActive?: boolean;
}
const IkInstances = ({
uuid,
selectedTrigger,
modelUrl,
processes,
position,
rotation,
armBot,
setArmBots,
setStaticMachines,
updateArmBotStatus
}: {
uuid: string;
selectedTrigger: string;
modelUrl: string;
processes: any;
position: [number, number, number];
rotation: [number, number, number];
armBot: ArmBotState;
setArmBots: React.Dispatch<React.SetStateAction<ArmBotState[]>>;
setStaticMachines: React.Dispatch<React.SetStateAction<StaticMachineState[]>>;
updateArmBotStatus: (status: string) => void;
}) => {
const [ikSolver, setIkSolver] = useState<any>(null); const [ikSolver, setIkSolver] = useState<any>(null);
const [selectedTrigger, setSelectedTrigger] = useState<string>("idle");
const gltf = useLoader(GLTFLoader, modelUrl, (loader) => { const gltf = useLoader(GLTFLoader, modelUrl, (loader) => {
const draco = new DRACOLoader(); const draco = new DRACOLoader();
draco.setDecoderPath( draco.setDecoderPath("https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/");
"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/"
);
loader.setDRACOLoader(draco); loader.setDRACOLoader(draco);
}); });
const cloned = useMemo(() => clone(gltf.scene), [gltf]); const cloned = useMemo(() => clone(gltf.scene), [gltf]);
const groupRef = useRef<any>(null); const groupRef = useRef<any>(null);
const [selectedArm, setSelectedArm] = useState<THREE.Group>();
const targetBoneName = "Target"; const targetBoneName = "Target";
const skinnedMeshName = "link_0"; const skinnedMeshName = "link_0";
const process = useMemo(() => [
{
trigger: "Trigger1",
start: new THREE.Vector3(-0.75, 1.5, -2.2),
end: new THREE.Vector3(0, 1.2, 2.2),
speed: 0.25,
},
{
trigger: "Trigger2",
start: new THREE.Vector3(0, 1.2, 2.2),
end: new THREE.Vector3(0.75, 1.5, -2.2),
speed: 0.22,
}
], []);
useEffect(() => { useEffect(() => {
if (!gltf) return; if (!gltf) return;
const OOI: any = {}; const OOI: any = {};
@ -85,42 +112,37 @@ const IkInstances = ({ modelUrl, position, rotation }: { modelUrl: string; posit
}, [gltf]); }, [gltf]);
useEffect(() => {
const triggers = ['Trigger1', 'Trigger2'];
let index = 0;
const cycleTriggers = setInterval(() => { const logStatus = (status: string) => {
setSelectedTrigger(triggers[index]); // console.log(status);
index = (index + 1) % triggers.length; }
}, 10000);
return () => clearInterval(cycleTriggers);
}, []);
return ( return (
<> <>
<group <group
ref={groupRef} ref={groupRef}
onClick={(e) => { position={position}
e.stopPropagation(); rotation={rotation}
setSelectedArm(groupRef.current?.getObjectByName(targetBoneName))
}}
> >
<primitive <primitive
object={cloned} object={cloned}
position={position}
rotation={rotation}
scale={[1, 1, 1]} scale={[1, 1, 1]}
name={`arm-bot`} name={`arm-bot`}
/> />
</group> </group>
<IKAnimationController <IKAnimationController
ikSolver={ikSolver} ikSolver={ikSolver}
process={process} process={processes}
selectedTrigger={selectedTrigger} selectedTrigger={selectedTrigger}
targetBoneName={targetBoneName} targetBoneName={targetBoneName}
uuid={uuid}
logStatus={logStatus}
groupRef={groupRef}
armBot={armBot}
setArmBots={setArmBots}
setStaticMachines={setStaticMachines}
updateArmBotStatus={updateArmBotStatus}
/> />
{/* {selectedArm && <TransformControls object={selectedArm} />} */}
</> </>
); );
}; };

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
import * as THREE from "three"; import * as THREE from "three";
import * as SimulationTypes from "../../../types/simulation"; import * as SimulationTypes from "../../../types/simulationTypes";
import { useRef, useState, useEffect, useMemo } from "react"; import { useRef, useState, useEffect, useMemo } from "react";
import { Sphere, TransformControls } from "@react-three/drei"; import { Sphere, TransformControls } from "@react-three/drei";
import { import {
@ -66,45 +66,6 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec
}); });
}); });
const updateSimulationPaths = () => {
if (!selectedActionSphere) return;
const updatedPaths = simulationStates.map((path) => {
if (path.type === "Conveyor") {
return {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.points.uuid
? {
...point,
position: [
selectedActionSphere.points.position.x,
selectedActionSphere.points.position.y,
selectedActionSphere.points.position.z,
],
rotation: [
selectedActionSphere.points.rotation.x,
selectedActionSphere.points.rotation.y,
selectedActionSphere.points.rotation.z,
],
}
: point
),
};
} else {
return path;
}
}) as SimulationTypes.ConveyorEventsSchema[];
const updatedPath = updatedPaths.find(
(path) => path.type === "Conveyor" && path.points.some((point) => point.uuid === selectedActionSphere.points.uuid)
);
// console.log("Updated Path:", updatedPath);
setSimulationStates(updatedPaths);
};
useFrame(() => { useFrame(() => {
if (eyeDropMode) { if (eyeDropMode) {
raycaster.setFromCamera(pointer, camera); raycaster.setFromCamera(pointer, camera);
@ -445,7 +406,6 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec
ref={transformRef} ref={transformRef}
object={selectedActionSphere.points} object={selectedActionSphere.points}
mode={transformMode} mode={transformMode}
onMouseUp={updateSimulationPaths}
/> />
)} )}
</group> </group>

View File

@ -1,4 +1,4 @@
import React, { useRef, useEffect, useMemo } from "react"; import React, { useRef, useEffect, useMemo, useCallback } from "react";
import { useLoader, useFrame } from "@react-three/fiber"; import { useLoader, useFrame } from "@react-three/fiber";
import { GLTFLoader } from "three-stdlib"; import { GLTFLoader } from "three-stdlib";
import * as THREE from "three"; import * as THREE from "three";
@ -10,11 +10,27 @@ import ProcessObject from "./processObject";
import { ProcessData } from "./types"; import { ProcessData } from "./types";
interface ArmBotState {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
status: string;
material: string;
triggerId: string;
connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
isActive?: boolean;
}
interface ProcessContainerProps { interface ProcessContainerProps {
processes: ProcessData[]; processes: ProcessData[];
setProcesses: React.Dispatch<React.SetStateAction<any[]>>; setProcesses: React.Dispatch<React.SetStateAction<any[]>>;
agvRef: any; agvRef: any;
MaterialRef: any; MaterialRef: any;
armBots: ArmBotState[];
setArmBots: React.Dispatch<React.SetStateAction<ArmBotState[]>>;
} }
const ProcessAnimator: React.FC<ProcessContainerProps> = ({ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
@ -22,6 +38,8 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
setProcesses, setProcesses,
agvRef, agvRef,
MaterialRef, MaterialRef,
armBots,
setArmBots
}) => { }) => {
const gltf = useLoader(GLTFLoader, crate) as GLTF; const gltf = useLoader(GLTFLoader, crate) as GLTF;
const groupRef = useRef<THREE.Group>(null); const groupRef = useRef<THREE.Group>(null);
@ -41,7 +59,7 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
getPointDataForAnimationIndex, getPointDataForAnimationIndex,
processes: processedProcesses, processes: processedProcesses,
checkAndCountTriggers, checkAndCountTriggers,
} = useProcessAnimation(processes, setProcesses, agvRef); } = useProcessAnimation(processes, setProcesses, agvRef, armBots, setArmBots);
const baseMaterials = useMemo( const baseMaterials = useMemo(
() => ({ () => ({
@ -96,6 +114,29 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
// In processAnimator.tsx - only the relevant spawn logic part that needs fixes // In processAnimator.tsx - only the relevant spawn logic part that needs fixes
// Add this function to ProcessAnimator component
const isConnectedToActiveArmBot = useCallback(
(processId: any) => {
// Check if any active armbot is connected to this process
return armBots.some((armbot) => {
if (!armbot.isActive) return false;
// Check if this armbot is connected to the process
return armbot.connections?.targets?.some((connection: any) => {
// Find the process that owns this modelUUID
const connectedProcess = processes.find((p) =>
p.paths?.some((path) => path.modeluuid === connection.modelUUID)
);
return connectedProcess?.id === processId;
});
});
},
[armBots, processes]
);
// In processAnimator.tsx - only the relevant spawn logic part that needs fixes
useFrame(() => { useFrame(() => {
// Spawn logic frame // Spawn logic frame
const currentTime = const currentTime =
@ -113,6 +154,14 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
return; return;
} }
if (isConnectedToActiveArmBot(process.id)) {
newStates[process.id] = {
...processState,
nextSpawnTime: Infinity, // Prevent future spawns
};
return;
}
const spawnPoint = findSpawnPoint(process); const spawnPoint = findSpawnPoint(process);
if (!spawnPoint || !spawnPoint.actions) return; if (!spawnPoint || !spawnPoint.actions) return;
@ -180,6 +229,29 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
const processState = newStates[process.id]; const processState = newStates[process.id];
if (!processState) return; if (!processState) return;
if (isConnectedToActiveArmBot(process.id)) {
newStates[process.id] = {
...processState,
spawnedObjects: Object.entries(processState.spawnedObjects).reduce(
(acc, [id, obj]) => ({
...acc,
[id]: {
...obj,
state: {
...obj.state,
isAnimating: false, // Stop animation
isDelaying: false, // Clear delays
delayComplete: false, // Reset delays
progress: 0, // Reset progress
},
},
}),
{}
),
};
return;
}
if (processState.isProcessDelaying) { if (processState.isProcessDelaying) {
const effectiveDelayTime = const effectiveDelayTime =
processState.processDelayDuration / speedRef.current; processState.processDelayDuration / speedRef.current;
@ -444,7 +516,6 @@ const ProcessAnimator: React.FC<ProcessContainerProps> = ({
return newStates; return newStates;
}); });
}); });
if (!processedProcesses || processedProcesses.length === 0) { if (!processedProcesses || processedProcesses.length === 0) {
return null; return null;
} }

View File

@ -2,11 +2,28 @@ import React, { useState } from "react";
import ProcessCreator from "./processCreator"; import ProcessCreator from "./processCreator";
import ProcessAnimator from "./processAnimator"; import ProcessAnimator from "./processAnimator";
interface ArmBotState {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
status: string;
material: string;
triggerId: string;
connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
isActive?: boolean;
}
interface ProcessContainerProps { interface ProcessContainerProps {
processes: any[]; processes: any[];
setProcesses: React.Dispatch<React.SetStateAction<any[]>>; setProcesses: React.Dispatch<React.SetStateAction<any[]>>;
agvRef: any; agvRef: any;
MaterialRef: any; MaterialRef: any;
armBots: ArmBotState[];
setArmBots: React.Dispatch<React.SetStateAction<ArmBotState[]>>;
} }
const ProcessContainer: React.FC<ProcessContainerProps> = ({ const ProcessContainer: React.FC<ProcessContainerProps> = ({
@ -14,6 +31,8 @@ const ProcessContainer: React.FC<ProcessContainerProps> = ({
setProcesses, setProcesses,
agvRef, agvRef,
MaterialRef, MaterialRef,
armBots,
setArmBots
}) => { }) => {
return ( return (
<> <>
@ -23,6 +42,8 @@ const ProcessContainer: React.FC<ProcessContainerProps> = ({
setProcesses={setProcesses} setProcesses={setProcesses}
agvRef={agvRef} agvRef={agvRef}
MaterialRef={MaterialRef} MaterialRef={MaterialRef}
armBots={armBots}
setArmBots={setArmBots}
/> />
</> </>
); );

View File

@ -12,7 +12,7 @@ import {
ArmBotEventsSchema, ArmBotEventsSchema,
ConveyorEventsSchema, ConveyorEventsSchema,
VehicleEventsSchema, VehicleEventsSchema,
} from "../../../types/simulation"; } from "../../../types/simulationTypes";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
// Type definitions // Type definitions

View File

@ -39,7 +39,7 @@ export interface ProcessPath {
pathRotation: number[]; pathRotation: number[];
speed: number; speed: number;
type: "Conveyor" | "Vehicle" | "ArmBot"; type: "Conveyor" | "Vehicle" | "ArmBot";
isActive: boolean; isActive: boolean
} }
export interface ProcessData { export interface ProcessData {

View File

@ -45,10 +45,27 @@ interface PlayAgvState {
setPlayAgv: (data: any) => void; setPlayAgv: (data: any) => void;
} }
interface ArmBotState {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
status: string;
material: string;
triggerId: string;
connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
isActive?: boolean;
}
export const useProcessAnimation = ( export const useProcessAnimation = (
processes: ProcessData[], processes: ProcessData[],
setProcesses: React.Dispatch<React.SetStateAction<any[]>>, setProcesses: React.Dispatch<React.SetStateAction<any[]>>,
agvRef: any agvRef: any,
armBots: ArmBotState[],
setArmBots: React.Dispatch<React.SetStateAction<ArmBotState[]>>
) => { ) => {
// State and refs initialization // State and refs initialization
const { isPlaying, setIsPlaying } = usePlayButtonStore(); const { isPlaying, setIsPlaying } = usePlayButtonStore();
@ -58,18 +75,13 @@ export const useProcessAnimation = (
const clockRef = useRef<THREE.Clock>(new THREE.Clock()); const clockRef = useRef<THREE.Clock>(new THREE.Clock());
const pauseTimeRef = useRef<number>(0); const pauseTimeRef = useRef<number>(0);
const elapsedBeforePauseRef = useRef<number>(0); const elapsedBeforePauseRef = useRef<number>(0);
const animationStatesRef = useRef< const animationStatesRef = useRef<Record<string, EnhancedProcessAnimationState>>({});
Record<string, EnhancedProcessAnimationState>
>({});
const { speed } = useAnimationPlaySpeed(); const { speed } = useAnimationPlaySpeed();
const prevIsPlaying = useRef<boolean | null>(null); const prevIsPlaying = useRef<boolean | null>(null);
const [internalResetFlag, setInternalResetFlag] = useState(false); const [internalResetFlag, setInternalResetFlag] = useState(false);
const [animationStates, setAnimationStates] = useState< const [animationStates, setAnimationStates] = useState<Record<string, EnhancedProcessAnimationState>>({});
Record<string, EnhancedProcessAnimationState>
>({});
const speedRef = useRef<number>(speed); const speedRef = useRef<number>(speed);
const armBotRef = useRef<any[]>([]); const { PlayAgv, setPlayAgv } = usePlayAgv();
// Effect hooks // Effect hooks
useEffect(() => { useEffect(() => {
@ -111,8 +123,7 @@ export const useProcessAnimation = (
if (isPaused) { if (isPaused) {
pauseTimeRef.current = clockRef.current.getElapsedTime(); pauseTimeRef.current = clockRef.current.getElapsedTime();
} else if (pauseTimeRef.current > 0) { } else if (pauseTimeRef.current > 0) {
const pausedDuration = const pausedDuration = clockRef.current.getElapsedTime() - pauseTimeRef.current;
clockRef.current.getElapsedTime() - pauseTimeRef.current;
elapsedBeforePauseRef.current += pausedDuration; elapsedBeforePauseRef.current += pausedDuration;
} }
}, [isPaused]); }, [isPaused]);
@ -161,8 +172,9 @@ export const useProcessAnimation = (
// Initialize AGVs for each process first // Initialize AGVs for each process first
processes.forEach((process) => { processes.forEach((process) => {
// Find all vehicle paths for this process // Find all vehicle paths for this process
const vehiclePaths = const vehiclePaths = process.paths?.filter(
process.paths?.filter((path) => path.type === "Vehicle") || []; (path) => path.type === "Vehicle"
) || [];
// Initialize AGVs for each vehicle path // Initialize AGVs for each vehicle path
vehiclePaths.forEach((vehiclePath) => { vehiclePaths.forEach((vehiclePath) => {
@ -187,8 +199,8 @@ export const useProcessAnimation = (
maxHitCount: maxHitCount || 0, maxHitCount: maxHitCount || 0,
isActive: false, isActive: false,
hitCount: 0, hitCount: 0,
status: "stationed", status: 'stationed',
lastUpdated: 0, lastUpdated: 0
}); });
} }
} }
@ -449,6 +461,8 @@ export const useProcessAnimation = (
[handleMaterialSwap] [handleMaterialSwap]
); );
const deferredArmBotUpdates = useRef<{ uuid: string; triggerId: string }[]>([]);
// Trigger counting system // Trigger counting system
const checkAndCountTriggers = useCallback( const checkAndCountTriggers = useCallback(
( (
@ -468,9 +482,7 @@ export const useProcessAnimation = (
const point = getPointDataForAnimationIndex(process, currentPointIndex); const point = getPointDataForAnimationIndex(process, currentPointIndex);
if (!point?.triggers) return prev; if (!point?.triggers) return prev;
const onHitTriggers = point.triggers.filter( const onHitTriggers = point.triggers.filter((t: Trigger) => t.type === "On-Hit" && t.isUsed);
(t: Trigger) => t.type === "On-Hit" && t.isUsed
);
if (onHitTriggers.length === 0) return prev; if (onHitTriggers.length === 0) return prev;
@ -478,58 +490,47 @@ export const useProcessAnimation = (
const newTriggerLogs = [...processState.triggerLogs]; const newTriggerLogs = [...processState.triggerLogs];
let shouldLog = false; let shouldLog = false;
// Find all vehicle paths for this process const vehiclePaths = process.paths.filter((path) => path.type === "Vehicle");
const vehiclePaths = process.paths.filter( const armBotPaths = process.paths.filter((path) => path.type === "ArmBot");
(path) => path.type === "Vehicle"
);
// Find all ArmBot paths for this process
const armBotPaths = process.paths.filter(
(path) => path.type === "ArmBot"
);
// Check if any vehicle is active for this process
const activeVehicles = vehiclePaths.filter((path) => { const activeVehicles = vehiclePaths.filter((path) => {
const vehicleId = path.modeluuid; const vehicleId = path.modeluuid;
const vehicleEntry = agvRef.current.find( const vehicleEntry = agvRef.current.find((v: any) => v.vehicleId === vehicleId && v.processId === processId);
(v: any) => v.vehicleId === vehicleId && v.processId === processId
);
return vehicleEntry?.isActive; return vehicleEntry?.isActive;
}); });
// Check if any ArmBot is active for this process // Check if any ArmBot is active for this process
const activeArmBots = armBotPaths.filter((path) => { // const activeArmBots = armBotPaths.filter((path) => {
const armBotId = path.modeluuid; // const armBotId = path.modeluuid;
const armBotEntry = armBotRef.current.find( // const armBotEntry = armBots.find((a: any) => a.uuid === armBotId);
(a: any) => a.armBotId === armBotId && a.processId === processId // return armBotEntry;
); // });
return armBotEntry?.isActive;
});
// Only count triggers if no vehicles and no ArmBots are active for this process // Only count triggers if no vehicles and no ArmBots are active for this process
if (activeVehicles.length === 0 && activeArmBots.length === 0) {
if (activeVehicles.length === 0) {
onHitTriggers.forEach((trigger: Trigger) => { onHitTriggers.forEach((trigger: Trigger) => {
const triggerKey = `${point.uuid}-${trigger.uuid}`; const connections = point.connections?.targets || [];
newTriggerCounts[triggerKey] =
(newTriggerCounts[triggerKey] || 0) + 1; connections.forEach((connection) => {
shouldLog = true; const connectedModelUUID = connection.modelUUID;
newTriggerLogs.push({
timestamp: currentTime, const matchingArmPath = armBotPaths.find((path) => path.modeluuid === connectedModelUUID);
pointId: point.uuid,
objectId, if (matchingArmPath) {
triggerId: trigger.uuid, deferredArmBotUpdates.current.push({
uuid: connectedModelUUID,
triggerId: trigger.uuid,
});
}
}); });
}); });
} }
let processTotalHits = Object.values(newTriggerCounts).reduce( let processTotalHits = Object.values(newTriggerCounts).reduce((a, b) => a + b, 0);
(a, b) => a + b,
0
);
// Handle logic for vehicles and ArmBots when a trigger is hit // Handle logic for vehicles and ArmBots when a trigger is hit
if (shouldLog) { if (shouldLog) {
// Handle vehicle logic (existing code)
vehiclePaths.forEach((vehiclePath) => { vehiclePaths.forEach((vehiclePath) => {
if (vehiclePath.points?.length > 0) { if (vehiclePath.points?.length > 0) {
const vehiclePoint = vehiclePath.points[0]; const vehiclePoint = vehiclePath.points[0];
@ -568,48 +569,6 @@ export const useProcessAnimation = (
} }
} }
}); });
// Handle ArmBot logic (new code)
armBotPaths.forEach((armBotPath) => {
if (armBotPath.points?.length > 0) {
const armBotPoint = armBotPath.points[0];
const action = armBotPoint.actions?.[0];
const maxHitCount = action?.hitCount;
if (maxHitCount !== undefined) {
const armBotId = armBotPath.modeluuid;
let armBotEntry = armBotRef.current.find(
(a: any) =>
a.armBotId === armBotId && a.processId === processId
);
if (!armBotEntry) {
armBotEntry = {
processId,
armBotId,
maxHitCount: maxHitCount,
isActive: false,
hitCount: 0,
status: "stationed",
};
armBotRef.current.push(armBotEntry);
console.log('armBotEntry: ', armBotEntry);
}
if (!armBotEntry.isActive) {
armBotEntry.hitCount++;
armBotEntry.lastUpdated = currentTime;
if (armBotEntry.hitCount >= armBotEntry.maxHitCount) {
armBotEntry.isActive = true;
// Reset trigger counts when ArmBot activates, similar to vehicle
newTriggerCounts = {};
processTotalHits = 0;
}
}
}
}
});
} }
return { return {
@ -622,9 +581,24 @@ export const useProcessAnimation = (
}, },
}; };
}); });
}, }, []);
[]
); useEffect(() => {
if (deferredArmBotUpdates.current.length > 0) {
const updates = [...deferredArmBotUpdates.current];
deferredArmBotUpdates.current = [];
setArmBots((prev) =>
prev.map((bot) => {
const update = updates.find((u) => u.uuid === bot.uuid);
return update
? { ...bot, triggerId: update.triggerId, isActive: true }
: bot;
})
);
}
}, [animationStates]);
// Utility functions // Utility functions
const hasNonInheritActions = useCallback( const hasNonInheritActions = useCallback(
@ -632,9 +606,7 @@ export const useProcessAnimation = (
return actions.some( return actions.some(
(action) => action.isUsed && action.type !== "Inherit" (action) => action.isUsed && action.type !== "Inherit"
); );
}, }, []);
[]
);
const getPointDataForAnimationIndex = useCallback( const getPointDataForAnimationIndex = useCallback(
(process: ProcessData, index: number): ProcessPoint | null => { (process: ProcessData, index: number): ProcessPoint | null => {

View File

@ -6,42 +6,69 @@ import useModuleStore from "../../store/useModuleStore";
import ProcessContainer from "./process/processContainer"; import ProcessContainer from "./process/processContainer";
import Agv from "../builder/agv/agv"; import Agv from "../builder/agv/agv";
import ArmBot from "./armbot/ArmBot"; import ArmBot from "./armbot/ArmBot";
import StaticMachine from "./staticMachine/staticMachine";
interface ArmBotState {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
status: string;
material: string;
triggerId: string;
connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
isActive?: boolean;
}
interface StaticMachineState {
uuid: string;
status: string;
actions: { uuid: string; name: string; buffer: number; material: string; };
machineTriggerId: string;
connectedArmBot: string;
}
function Simulation() { function Simulation() {
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const pathsGroupRef = useRef() as React.MutableRefObject<THREE.Group>; const pathsGroupRef = useRef() as React.MutableRefObject<THREE.Group>;
const [processes, setProcesses] = useState<any[]>([]); const [armBots, setArmBots] = useState<ArmBotState[]>([]);
const [staticMachines, setStaticMachines] = useState<StaticMachineState[]>([]);
const [processes, setProcesses] = useState<any[]>([]);
const agvRef = useRef([]);
const MaterialRef = useRef([]);
const agvRef = useRef([]); return (
const MaterialRef = useRef([]);
const { simulationStates } = useSimulationStates();
return (
<>
{activeModule === "simulation" && (
<> <>
<PathCreation pathsGroupRef={pathsGroupRef} /> {activeModule === "simulation" && (
<>
<PathCreation pathsGroupRef={pathsGroupRef} />
<PathConnector pathsGroupRef={pathsGroupRef} /> <PathConnector pathsGroupRef={pathsGroupRef} />
<ProcessContainer <ProcessContainer
processes={processes} processes={processes}
setProcesses={setProcesses} setProcesses={setProcesses}
agvRef={agvRef} agvRef={agvRef}
MaterialRef={MaterialRef} MaterialRef={MaterialRef}
/> armBots={armBots}
setArmBots={setArmBots}
/>
<Agv <Agv
processes={processes} processes={processes}
agvRef={agvRef} agvRef={agvRef}
MaterialRef={MaterialRef} MaterialRef={MaterialRef}
/> />
</>
)}
<StaticMachine setArmBots={setArmBots} staticMachines={staticMachines} setStaticMachines={setStaticMachines} />
<ArmBot armBots={armBots} setArmBots={setArmBots} setStaticMachines={setStaticMachines} />
</> </>
)} );
<ArmBot />
</>
);
} }
export default Simulation; export default Simulation;

View File

@ -0,0 +1,81 @@
import React, { useEffect } from 'react'
import * as SimulationTypes from '../../../types/simulationTypes';
import { useSimulationStates } from '../../../store/store';
import StaticMachineInstances from './staticMachineInstances';
interface ArmBotState {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
status: string;
material: string;
triggerId: string;
connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
isActive?: boolean;
}
interface StaticMachineState {
uuid: string;
status: string;
actions: { uuid: string; name: string; buffer: number; material: string; };
machineTriggerId: string;
connectedArmBot: string;
}
type StaticMachineProps = {
setArmBots: React.Dispatch<React.SetStateAction<ArmBotState[]>>;
staticMachines: StaticMachineState[];
setStaticMachines: React.Dispatch<React.SetStateAction<StaticMachineState[]>>;
}
function StaticMachine({ setArmBots, staticMachines, setStaticMachines }: StaticMachineProps) {
const { simulationStates } = useSimulationStates();
useEffect(() => {
const filtered = simulationStates.filter((s): s is SimulationTypes.StaticMachineEventsSchema => s.type === "StaticMachine");
const initialStates: StaticMachineState[] = filtered
.filter(machine => machine.points.connections.targets.length > 0)
.map(machine => ({
uuid: machine.modeluuid,
status: "idle",
actions: machine.points.actions,
machineTriggerId: machine.points.triggers.uuid,
connectedArmBot: machine.points.connections.targets[0].modelUUID
}));
setStaticMachines(initialStates);
}, [simulationStates]);
const updateArmBotTriggerAndMachineStatus = (armBotUuid: string, triggerId: string, machineId: string) => {
setArmBots((prevArmBots) => {
return prevArmBots.map(bot => {
if (bot.uuid === armBotUuid) {
return { ...bot, triggerId: triggerId };
}
return bot;
});
});
setStaticMachines((prevStaticMachines) => {
return prevStaticMachines.map(machine => {
if (machine.uuid === machineId) {
return { ...machine, status: "idle" };
} else {
return machine;
}
});
});
}
return (
<>
{staticMachines.map((machine, index) => (
<StaticMachineInstances key={index} machine={machine} updateArmBotTriggerAndMachineStatus={updateArmBotTriggerAndMachineStatus} />
))}
</>
)
}
export default StaticMachine;

View File

@ -0,0 +1,33 @@
import React, { useEffect } from 'react'
import { useAnimationPlaySpeed } from '../../../store/usePlayButtonStore';
interface StaticMachineState {
uuid: string;
status: string;
actions: { uuid: string; name: string; buffer: number; material: string; };
machineTriggerId: string;
connectedArmBot: string;
}
type StaticMachineInstancesProps = {
machine: StaticMachineState,
updateArmBotTriggerAndMachineStatus: (armBotUuid: string, triggerId: string, machineId: string) => void;
}
function StaticMachineInstances({ machine, updateArmBotTriggerAndMachineStatus }: StaticMachineInstancesProps) {
const { speed } = useAnimationPlaySpeed();
useEffect(() => {
if (machine.status === 'running') {
setTimeout(() => {
updateArmBotTriggerAndMachineStatus(machine.connectedArmBot, machine.machineTriggerId, machine.uuid);
}, machine.actions.buffer * 1000 * speed);
}
}, [machine])
return (
<></>
)
}
export default StaticMachineInstances

View File

@ -100,6 +100,8 @@ export const DraggableWidget = ({
const deleteSelectedChart = async () => { const deleteSelectedChart = async () => {
try { try {
console.log("delete");
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]; const organization = email?.split("@")[1]?.split(".")[0];
let deleteWidget = { let deleteWidget = {
@ -109,7 +111,9 @@ export const DraggableWidget = ({
}; };
if (visualizationSocket) { if (visualizationSocket) {
setSelectedChartId(null)
visualizationSocket.emit("v2:viz-widget:delete", deleteWidget); visualizationSocket.emit("v2:viz-widget:delete", deleteWidget);
console.log("delete widget",selectedChartId);
} }
const updatedWidgets = selectedZone.widgets.filter( const updatedWidgets = selectedZone.widgets.filter(
(w: Widget) => w.id !== widget.id (w: Widget) => w.id !== widget.id
@ -120,7 +124,6 @@ export const DraggableWidget = ({
widgets: updatedWidgets, widgets: updatedWidgets,
})); }));
setOpenKebabId(null); setOpenKebabId(null);
// const response = await deleteWidgetApi(widget.id, organization); // const response = await deleteWidgetApi(widget.id, organization);
// if (response?.message === "Widget deleted successfully") { // if (response?.message === "Widget deleted successfully") {
// const updatedWidgets = selectedZone.widgets.filter( // const updatedWidgets = selectedZone.widgets.filter(
@ -175,6 +178,7 @@ export const DraggableWidget = ({
const duplicatedWidget: Widget = { const duplicatedWidget: Widget = {
...widget, ...widget,
title: name === '' ? widget.title : name,
Data: { Data: {
duration: duration, duration: duration,
measurements: { ...measurements }, measurements: { ...measurements },
@ -187,6 +191,7 @@ export const DraggableWidget = ({
zoneId: selectedZone.zoneId, zoneId: selectedZone.zoneId,
widget: duplicatedWidget, widget: duplicatedWidget,
}; };
if (visualizationSocket) { if (visualizationSocket) {
visualizationSocket.emit("v2:viz-widget:add", duplicateWidget); visualizationSocket.emit("v2:viz-widget:add", duplicateWidget);
} }
@ -306,7 +311,10 @@ export const DraggableWidget = ({
: undefined, : undefined,
}} }}
ref={chartWidget} ref={chartWidget}
onClick={() => setSelectedChartId(widget)} onClick={() => {setSelectedChartId(widget)
console.log('click');
}}
> >
{/* Kebab Icon */} {/* Kebab Icon */}
<div className="icon kebab" onClick={handleKebabClick}> <div className="icon kebab" onClick={handleKebabClick}>
@ -327,7 +335,10 @@ export const DraggableWidget = ({
</div> </div>
<div className="label">Duplicate</div> <div className="label">Duplicate</div>
</div> </div>
<div className="edit btn" onClick={deleteSelectedChart}> <div className="edit btn" onClick={(e)=>{
e.stopPropagation()
deleteSelectedChart();
}}>
<div className="icon"> <div className="icon">
<DeleteIcon /> <DeleteIcon />
</div> </div>

View File

@ -1,6 +1,6 @@
import * as THREE from "three"; import * as THREE from "three";
import * as Types from "../types/world/worldTypes"; import * as Types from "../types/world/worldTypes";
import * as SimulationTypes from "../types/simulation"; import * as SimulationTypes from "../types/simulationTypes";
import { create } from "zustand"; import { create } from "zustand";
import { io } from "socket.io-client"; import { io } from "socket.io-client";

View File

@ -101,6 +101,7 @@ export const useDroppedObjectsStore = create<DroppedObjectsState>((set) => ({
let visualizationSocket = socketState.visualizationSocket; let visualizationSocket = socketState.visualizationSocket;
let iotMeasurements = iotData.flotingMeasurements; let iotMeasurements = iotData.flotingMeasurements;
let iotDuration = iotData.flotingDuration; let iotDuration = iotData.flotingDuration;
let iotHeader = iotData.header
if (!zone) return; if (!zone) return;
@ -117,6 +118,7 @@ export const useDroppedObjectsStore = create<DroppedObjectsState>((set) => ({
measurements: iotMeasurements, measurements: iotMeasurements,
duration: iotDuration, duration: iotDuration,
}, },
header: iotHeader,
id: `${originalObject.id}-copy-${Date.now()}`, // Unique ID id: `${originalObject.id}-copy-${Date.now()}`, // Unique ID
position: { position: {
...originalObject.position, ...originalObject.position,

View File

@ -64,7 +64,7 @@ interface StaticMachineEventsSchema {
uuid: string; uuid: string;
position: [number, number, number]; position: [number, number, number];
rotation: [number, number, number]; rotation: [number, number, number];
actions: { uuid: string; name: string; buffer: number | string; material: string; isUsed: boolean; }; actions: { uuid: string; name: string; buffer: number; material: string; };
triggers: { uuid: string; name: string; type: string }; triggers: { uuid: string; name: string; type: string };
connections: { connections: {
source: { modelUUID: string; pointUUID: string }; source: { modelUUID: string; pointUUID: string };
@ -103,7 +103,7 @@ export type EventData = {
isLocked: boolean; isLocked: boolean;
isVisible: boolean; isVisible: boolean;
eventData?: eventData?:
| { {
type: "Conveyor"; type: "Conveyor";
points: { points: {
uuid: string; uuid: string;