221 lines
9.5 KiB
TypeScript
221 lines
9.5 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { useSelectedAction, useSelectedEventSphere, useSelectedProduct } from '../../../../store/simulation/useSimulationStore';
|
|
import { useGLTF } from '@react-three/drei';
|
|
import { useThree } from '@react-three/fiber';
|
|
import { useProductStore } from '../../../../store/simulation/useProductStore';
|
|
import { useArmBotStore } from '../../../../store/simulation/useArmBotStore';
|
|
import PickDropPoints from './PickDropPoints';
|
|
import useDraggableGLTF from './useDraggableGLTF';
|
|
import * as THREE from 'three';
|
|
|
|
import armPick from "../../../../assets/gltf-glb/ui/arm_ui_pick.glb";
|
|
import armDrop from "../../../../assets/gltf-glb/ui/arm_ui_drop.glb";
|
|
import { upsertProductOrEventApi } from '../../../../services/simulation/UpsertProductOrEventApi';
|
|
|
|
type Positions = {
|
|
pick: [number, number, number];
|
|
drop: [number, number, number];
|
|
default: [number, number, number];
|
|
};
|
|
|
|
const ArmBotUI = () => {
|
|
const { getEventByModelUuid, updateAction, getActionByUuid } = useProductStore();
|
|
const { selectedEventSphere } = useSelectedEventSphere();
|
|
const { selectedProduct } = useSelectedProduct();
|
|
const { scene } = useThree();
|
|
const { selectedAction } = useSelectedAction();
|
|
const { armBots } = useArmBotStore();
|
|
|
|
const armUiPick = useGLTF(armPick) as any;
|
|
const armUiDrop = useGLTF(armDrop) as any;
|
|
|
|
const [startPosition, setStartPosition] = useState<[number, number, number] | null>([0, 0, 0]);
|
|
const [endPosition, setEndPosition] = useState<[number, number, number] | null>([0, 0, 0]);
|
|
const [selectedArmBotData, setSelectedArmBotData] = useState<any>(null);
|
|
|
|
const email = localStorage.getItem('email')
|
|
const organization = (email!.split("@")[1]).split(".")[0];
|
|
|
|
const updateBackend = (
|
|
productName: string,
|
|
productId: string,
|
|
organization: string,
|
|
eventData: EventsSchema
|
|
) => {
|
|
upsertProductOrEventApi({
|
|
productName: productName,
|
|
productId: productId,
|
|
organization: organization,
|
|
eventDatas: eventData
|
|
})
|
|
}
|
|
|
|
// Fetch and setup selected ArmBot data
|
|
useEffect(() => {
|
|
if (selectedEventSphere) {
|
|
const selectedArmBot = getEventByModelUuid(selectedProduct.productId, selectedEventSphere.userData.modelUuid);
|
|
|
|
if (selectedArmBot?.type === "roboticArm" && selectedAction.actionId) {
|
|
setSelectedArmBotData(selectedArmBot);
|
|
const defaultPositions = getDefaultPositions(selectedArmBot.modelUuid);
|
|
const matchingAction = getActionByUuid(selectedProduct.productId, selectedAction.actionId);
|
|
if (matchingAction && (matchingAction as RoboticArmPointSchema["actions"][0]).process) {
|
|
const startPoint = (matchingAction as RoboticArmPointSchema["actions"][0]).process.startPoint;
|
|
const pickPosition = (!startPoint || (Array.isArray(startPoint) && startPoint.every(v => v === 0)))
|
|
? defaultPositions.pick
|
|
: startPoint;
|
|
|
|
const endPoint = (matchingAction as RoboticArmPointSchema["actions"][0]).process.endPoint;
|
|
const dropPosition = (!endPoint || (Array.isArray(endPoint) && endPoint.every(v => v === 0)))
|
|
? defaultPositions.drop
|
|
: endPoint;
|
|
|
|
setStartPosition(pickPosition);
|
|
setEndPosition(dropPosition);
|
|
}
|
|
}
|
|
}
|
|
}, [armBots, selectedEventSphere, selectedProduct, getEventByModelUuid, selectedAction]);
|
|
|
|
function getDefaultPositions(modelUuid: string): Positions {
|
|
const modelData = getEventByModelUuid(selectedProduct.productId, modelUuid);
|
|
|
|
if (modelData?.type === "roboticArm") {
|
|
const baseX = modelData.point.position?.[0] || 0;
|
|
const baseY = modelData.point.position?.[1] || 0;;
|
|
const baseZ = modelData.point.position?.[2] || 0;
|
|
return {
|
|
pick: [baseX, baseY, baseZ + 0.5],
|
|
drop: [baseX, baseY, baseZ - 0.5],
|
|
default: [baseX, baseY, baseZ],
|
|
};
|
|
}
|
|
|
|
return {
|
|
pick: [0.5, 1.5, 0],
|
|
drop: [-0.5, 1.5, 0],
|
|
default: [0, 1.5, 0],
|
|
};
|
|
}
|
|
|
|
function getLocalPosition(parentUuid: string, worldPosArray: [number, number, number] | null): [number, number, number] | null {
|
|
if (worldPosArray) {
|
|
const worldPos = new THREE.Vector3(...worldPosArray);
|
|
const parentObject = scene.getObjectByProperty('uuid', parentUuid);
|
|
|
|
if (parentObject) {
|
|
const localPos = worldPos.clone();
|
|
parentObject.worldToLocal(localPos);
|
|
return [localPos.x, localPos.y, localPos.z];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
const updatePointToState = (obj: THREE.Object3D) => {
|
|
const { modelUuid, actionType, actionUuid } = obj.userData;
|
|
const newPosition = new THREE.Vector3();
|
|
obj.getWorldPosition(newPosition);
|
|
const worldPositionArray = newPosition.toArray() as [number, number, number];
|
|
|
|
if (selectedEventSphere) {
|
|
const selectedArmBot = getEventByModelUuid(selectedProduct.productId, selectedEventSphere.userData.modelUuid);
|
|
|
|
const armBot = selectedArmBot?.modelUuid === modelUuid ? selectedArmBot : null;
|
|
if (!armBot) return;
|
|
|
|
if (armBot.type === "roboticArm") {
|
|
armBot?.point?.actions?.map((action) => {
|
|
if (action.actionUuid === actionUuid) {
|
|
const updatedProcess = { ...action.process };
|
|
|
|
if (actionType === "pick") {
|
|
updatedProcess.startPoint = getLocalPosition(modelUuid, worldPositionArray);
|
|
setStartPosition(updatedProcess.startPoint)
|
|
|
|
} else if (actionType === "drop") {
|
|
updatedProcess.endPoint = getLocalPosition(modelUuid, worldPositionArray);
|
|
setEndPosition(updatedProcess.endPoint)
|
|
}
|
|
|
|
const event = updateAction(selectedProduct.productId,
|
|
actionUuid,
|
|
{
|
|
actionUuid: action.actionUuid,
|
|
process: updatedProcess,
|
|
}
|
|
)
|
|
|
|
if (event) {
|
|
updateBackend(
|
|
selectedProduct.productName,
|
|
selectedProduct.productId,
|
|
organization,
|
|
event
|
|
);
|
|
}
|
|
return {
|
|
...action,
|
|
process: updatedProcess,
|
|
};
|
|
}
|
|
return action;
|
|
});
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
const { handlePointerDown } = useDraggableGLTF(updatePointToState);
|
|
|
|
if (!selectedArmBotData || !Array.isArray(selectedArmBotData.point?.actions)) {
|
|
return null; // avoid rendering if no data yet
|
|
}
|
|
return (
|
|
<>
|
|
{selectedArmBotData.point.actions.map((action: any) => {
|
|
if (action.actionUuid === selectedAction.actionId) {
|
|
return (
|
|
<React.Fragment key={action.actionUuid}>
|
|
<group
|
|
position={new THREE.Vector3(...selectedArmBotData.position)}
|
|
rotation={new THREE.Euler(...selectedArmBotData.rotation)}
|
|
>
|
|
{startPosition && endPosition && (
|
|
<>
|
|
<PickDropPoints
|
|
position={startPosition}
|
|
modelUuid={selectedArmBotData.modelUuid}
|
|
pointUuid={selectedArmBotData.point.uuid}
|
|
actionType="pick"
|
|
actionUuid={action.actionUuid}
|
|
gltfScene={armUiPick.scene}
|
|
handlePointerDown={handlePointerDown}
|
|
isSelected={true}
|
|
/>
|
|
<PickDropPoints
|
|
position={endPosition}
|
|
modelUuid={selectedArmBotData.modelUuid}
|
|
pointUuid={selectedArmBotData.point.uuid}
|
|
actionType="drop"
|
|
actionUuid={action.actionUuid}
|
|
gltfScene={armUiDrop.scene}
|
|
handlePointerDown={handlePointerDown}
|
|
isSelected={true}
|
|
/>
|
|
</>
|
|
)}
|
|
</group>
|
|
</React.Fragment>
|
|
);
|
|
} else {
|
|
return null; // important! must return something
|
|
}
|
|
})}
|
|
</>
|
|
);
|
|
|
|
};
|
|
|
|
export default ArmBotUI;
|