feat: Implement human simulation features
- Added human event handling in the simulation, including the ability to add, update, and remove human instances. - Introduced a new Human store to manage human states and actions. - Updated the simulation context to include human management. - Enhanced the Points and TriggerConnector components to support human interactions. - Refactored existing components to integrate human-related functionalities. - Added HumanInstance and HumanInstances components for rendering human entities in the simulation. - Updated TypeScript definitions to include human-related types and actions.
This commit is contained in:
@@ -51,9 +51,8 @@ const AssetProperties: React.FC = () => {
|
||||
};
|
||||
|
||||
const handleAnimationClick = (animation: string) => {
|
||||
if (selectedFloorItem && selectedFloorItem.animationState) {
|
||||
const isPlaying = selectedFloorItem.animationState?.playing || false;
|
||||
setCurrentAnimation(selectedFloorItem.uuid, animation, !isPlaying);
|
||||
if (selectedFloorItem) {
|
||||
setCurrentAnimation(selectedFloorItem.uuid, animation, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ const VersionHistory = () => {
|
||||
const handleSelectVersion = (version: Version) => {
|
||||
if (!projectId) return;
|
||||
|
||||
getVersionDataApi(projectId, version.versionId).then((verdionData) => {
|
||||
getVersionDataApi(projectId, version.versionId).then((versionData) => {
|
||||
setSelectedVersion(version);
|
||||
// console.log(verdionData);
|
||||
// console.log(versionData);
|
||||
}).catch((err) => {
|
||||
// console.log(err);
|
||||
})
|
||||
|
||||
@@ -3,12 +3,14 @@ import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import { upsertAisleApi } from '../../../../services/factoryBuilder/aisle/upsertAisleApi';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useVersionContext } from '../../version/versionContext';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
import ReferenceAisle from './referenceAisle';
|
||||
import ReferencePoint from '../../point/reference/referencePoint';
|
||||
import { getUserData } from '../../../../functions/getUserData';
|
||||
|
||||
// import { upsertAisleApi } from '../../../../services/factoryBuilder/aisle/upsertAisleApi';
|
||||
|
||||
function AisleCreator() {
|
||||
const { scene, camera, raycaster, gl, pointer } = useThree();
|
||||
@@ -23,6 +25,7 @@ function AisleCreator() {
|
||||
const isLeftMouseDown = useRef(false);
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { projectId } = useParams();
|
||||
|
||||
const [tempPoints, setTempPoints] = useState<Point[]>([]);
|
||||
@@ -106,7 +109,22 @@ function AisleCreator() {
|
||||
};
|
||||
addAisle(aisle);
|
||||
if (projectId) {
|
||||
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// API
|
||||
|
||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// SOCKET
|
||||
|
||||
socket.emit('v1:model-aisle:add', {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: aisle.aisleUuid,
|
||||
points: aisle.points,
|
||||
type: aisle.type
|
||||
})
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
@@ -129,7 +147,22 @@ function AisleCreator() {
|
||||
};
|
||||
addAisle(aisle);
|
||||
if (projectId) {
|
||||
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// API
|
||||
|
||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// SOCKET
|
||||
|
||||
socket.emit('v1:model-aisle:add', {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: aisle.aisleUuid,
|
||||
points: aisle.points,
|
||||
type: aisle.type
|
||||
})
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
@@ -151,7 +184,22 @@ function AisleCreator() {
|
||||
};
|
||||
addAisle(aisle);
|
||||
if (projectId) {
|
||||
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// API
|
||||
|
||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// SOCKET
|
||||
|
||||
socket.emit('v1:model-aisle:add', {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: aisle.aisleUuid,
|
||||
points: aisle.points,
|
||||
type: aisle.type
|
||||
})
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
@@ -172,7 +220,22 @@ function AisleCreator() {
|
||||
};
|
||||
addAisle(aisle);
|
||||
if (projectId) {
|
||||
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// API
|
||||
|
||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// SOCKET
|
||||
|
||||
socket.emit('v1:model-aisle:add', {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: aisle.aisleUuid,
|
||||
points: aisle.points,
|
||||
type: aisle.type
|
||||
})
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
@@ -195,7 +258,22 @@ function AisleCreator() {
|
||||
};
|
||||
addAisle(aisle);
|
||||
if (projectId) {
|
||||
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// API
|
||||
|
||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// SOCKET
|
||||
|
||||
socket.emit('v1:model-aisle:add', {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: aisle.aisleUuid,
|
||||
points: aisle.points,
|
||||
type: aisle.type
|
||||
})
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
@@ -217,7 +295,22 @@ function AisleCreator() {
|
||||
};
|
||||
addAisle(aisle);
|
||||
if (projectId) {
|
||||
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// API
|
||||
|
||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// SOCKET
|
||||
|
||||
socket.emit('v1:model-aisle:add', {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: aisle.aisleUuid,
|
||||
points: aisle.points,
|
||||
type: aisle.type
|
||||
})
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
@@ -238,7 +331,22 @@ function AisleCreator() {
|
||||
};
|
||||
addAisle(aisle);
|
||||
if (projectId) {
|
||||
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// API
|
||||
|
||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// SOCKET
|
||||
|
||||
socket.emit('v1:model-aisle:add', {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: aisle.aisleUuid,
|
||||
points: aisle.points,
|
||||
type: aisle.type
|
||||
})
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
@@ -260,7 +368,22 @@ function AisleCreator() {
|
||||
};
|
||||
addAisle(aisle);
|
||||
if (projectId) {
|
||||
upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// API
|
||||
|
||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// SOCKET
|
||||
|
||||
socket.emit('v1:model-aisle:add', {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: aisle.aisleUuid,
|
||||
points: aisle.points,
|
||||
type: aisle.type
|
||||
})
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
|
||||
@@ -360,6 +360,40 @@ async function handleModelLoad(
|
||||
position: storageEvent.point.position,
|
||||
rotation: storageEvent.point.rotation,
|
||||
};
|
||||
} else if (selectedItem.type === "Human") {
|
||||
const humanEvent: HumanEventSchema = {
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
position: newFloorItem.position,
|
||||
rotation: newFloorItem.rotation,
|
||||
state: "idle",
|
||||
type: "human",
|
||||
point: {
|
||||
uuid: THREE.MathUtils.generateUUID(),
|
||||
position: [data.points[0].x, data.points[0].y, data.points[0].z],
|
||||
rotation: [0, 0, 0],
|
||||
actions: [
|
||||
{
|
||||
actionUuid: THREE.MathUtils.generateUUID(),
|
||||
actionName: "Action 1",
|
||||
actionType: "animation",
|
||||
animation: null,
|
||||
loadCapacity: 1,
|
||||
travelPoints: {
|
||||
startPoint: null,
|
||||
endPoint: null,
|
||||
},
|
||||
triggers: []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
addEvent(humanEvent);
|
||||
eventData.point = {
|
||||
uuid: humanEvent.point.uuid,
|
||||
position: humanEvent.point.position,
|
||||
rotation: humanEvent.point.rotation,
|
||||
}
|
||||
}
|
||||
|
||||
const completeData = {
|
||||
|
||||
@@ -301,20 +301,21 @@ function Model({ asset }: { readonly asset: Asset }) {
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const handlePlay = (clipName: string) => {
|
||||
|
||||
if (asset.animationState && asset.animationState.playing) {
|
||||
if (!mixerRef.current) return;
|
||||
|
||||
Object.values(actions.current).forEach((action) => action.stop());
|
||||
|
||||
const action = actions.current[clipName];
|
||||
const action = actions.current[asset.animationState.current];
|
||||
if (action && asset.animationState?.playing) {
|
||||
action.reset().setLoop(THREE.LoopOnce, 1).play();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
Object.values(actions.current).forEach((action) => action.stop());
|
||||
}
|
||||
|
||||
handlePlay(asset.animationState?.current || '');
|
||||
|
||||
}, [asset])
|
||||
}, [asset.animationState])
|
||||
|
||||
return (
|
||||
<group
|
||||
@@ -362,17 +363,6 @@ function Model({ asset }: { readonly asset: Asset }) {
|
||||
<AssetBoundingBox boundingBox={boundingBox} />
|
||||
)
|
||||
)}
|
||||
{/* <group >
|
||||
<Html>
|
||||
<div style={{ position: 'absolute', }}>
|
||||
{animationNames.map((name) => (
|
||||
<button key={name} onClick={() => handlePlay(name)} style={{ margin: 4 }}>
|
||||
{name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</Html>
|
||||
</group> */}
|
||||
</group >
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ import { useParams } from 'react-router-dom';
|
||||
import { useVersionContext } from '../version/versionContext';
|
||||
import { useSceneContext } from '../../scene/sceneContext';
|
||||
|
||||
import { upsertAisleApi } from '../../../services/factoryBuilder/aisle/upsertAisleApi';
|
||||
import { deleteAisleApi } from '../../../services/factoryBuilder/aisle/deleteAisleApi';
|
||||
// import { upsertAisleApi } from '../../../services/factoryBuilder/aisle/upsertAisleApi';
|
||||
// import { deleteAisleApi } from '../../../services/factoryBuilder/aisle/deleteAisleApi';
|
||||
// import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi';
|
||||
// import { deleteWallApi } from '../../../services/factoryBuilder/wall/deleteWallApi';
|
||||
// import { upsertFloorApi } from '../../../services/factoryBuilder/floor/upsertFloorApi';
|
||||
@@ -159,7 +159,22 @@ function Point({ point }: { readonly point: Point }) {
|
||||
const updatedAisles = getAislesByPointId(point.pointUuid);
|
||||
if (updatedAisles.length > 0 && projectId) {
|
||||
updatedAisles.forEach((updatedAisle) => {
|
||||
upsertAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// API
|
||||
|
||||
// upsertAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || '');
|
||||
|
||||
// SOCKET
|
||||
|
||||
socket.emit('v1:model-aisle:add', {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: updatedAisle.aisleUuid,
|
||||
points: updatedAisle.points,
|
||||
type: updatedAisle.type
|
||||
})
|
||||
})
|
||||
}
|
||||
} else if (point.pointType === 'Wall') {
|
||||
@@ -238,7 +253,22 @@ function Point({ point }: { readonly point: Point }) {
|
||||
if (removedAisles.length > 0) {
|
||||
removedAisles.forEach(aisle => {
|
||||
if (projectId) {
|
||||
deleteAisleApi(aisle.aisleUuid, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// API
|
||||
|
||||
// deleteAisleApi(aisle.aisleUuid, projectId, selectedVersion?.versionId || '');
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: aisle.aisleUuid
|
||||
}
|
||||
|
||||
socket.emit('v1:model-aisle:delete', data);
|
||||
}
|
||||
});
|
||||
setHoveredPoint(null);
|
||||
|
||||
@@ -6,7 +6,7 @@ export default function SocketResponses() {
|
||||
|
||||
useEffect(() => {
|
||||
socket.on("v1:model-asset:response:add", (data: any) => {
|
||||
console.log('data: ', data);
|
||||
// console.log('data: ', data);
|
||||
});
|
||||
|
||||
return () => {
|
||||
|
||||
@@ -16,6 +16,7 @@ import { createMachineStore, MachineStoreType } from '../../store/simulation/use
|
||||
import { createConveyorStore, ConveyorStoreType } from '../../store/simulation/useConveyorStore';
|
||||
import { createVehicleStore, VehicleStoreType } from '../../store/simulation/useVehicleStore';
|
||||
import { createStorageUnitStore, StorageUnitStoreType } from '../../store/simulation/useStorageUnitStore';
|
||||
import { createHumanStore, HumanStoreType } from '../../store/simulation/useHumanStore';
|
||||
|
||||
type SceneContextValue = {
|
||||
|
||||
@@ -35,6 +36,7 @@ type SceneContextValue = {
|
||||
conveyorStore: ConveyorStoreType;
|
||||
vehicleStore: VehicleStoreType;
|
||||
storageUnitStore: StorageUnitStoreType;
|
||||
humanStore: HumanStoreType;
|
||||
|
||||
clearStores: () => void;
|
||||
|
||||
@@ -67,6 +69,7 @@ export function SceneProvider({
|
||||
const conveyorStore = useMemo(() => createConveyorStore(), []);
|
||||
const vehicleStore = useMemo(() => createVehicleStore(), []);
|
||||
const storageUnitStore = useMemo(() => createStorageUnitStore(), []);
|
||||
const humanStore = useMemo(() => createHumanStore(), []);
|
||||
|
||||
const clearStores = useMemo(() => () => {
|
||||
assetStore.getState().clearAssets();
|
||||
@@ -83,7 +86,8 @@ export function SceneProvider({
|
||||
conveyorStore.getState().clearConveyors();
|
||||
vehicleStore.getState().clearVehicles();
|
||||
storageUnitStore.getState().clearStorageUnits();
|
||||
}, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore]);
|
||||
humanStore.getState().clearHumans();
|
||||
}, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore]);
|
||||
|
||||
const contextValue = useMemo(() => (
|
||||
{
|
||||
@@ -101,10 +105,11 @@ export function SceneProvider({
|
||||
conveyorStore,
|
||||
vehicleStore,
|
||||
storageUnitStore,
|
||||
humanStore,
|
||||
clearStores,
|
||||
layout
|
||||
}
|
||||
), [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, clearStores, layout]);
|
||||
), [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, clearStores, layout]);
|
||||
|
||||
return (
|
||||
<SceneContext.Provider value={contextValue}>
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import { useInputValues, useProductionCapacityData, useThroughPutData } from '../../../../store/builder/store'
|
||||
import { usePlayButtonStore } from '../../../../store/usePlayButtonStore';
|
||||
import { useProductContext } from '../../products/productContext';
|
||||
|
||||
export default function ProductionCapacityData() {
|
||||
const { throughputData } = useThroughPutData()
|
||||
const { productionCapacityData, setProductionCapacityData } = useProductionCapacityData()
|
||||
const { setProductionCapacityData } = useProductionCapacityData()
|
||||
const { inputValues } = useInputValues();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (!isPlaying) {
|
||||
|
||||
setProductionCapacityData(0);
|
||||
}
|
||||
}, [isPlaying]);
|
||||
@@ -26,8 +22,6 @@ export default function ProductionCapacityData() {
|
||||
const Monthly_working_days = workingDaysPerYear / 12;
|
||||
const Production_capacity_per_month = throughputData * Monthly_working_days;
|
||||
|
||||
|
||||
|
||||
setProductionCapacityData(Number(Production_capacity_per_month.toFixed(2)));
|
||||
}
|
||||
}, [throughputData, inputValues, isPlaying]);
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import { usePlayButtonStore } from '../../../store/usePlayButtonStore'
|
||||
import ProductionCapacityData from './productionCapacity/productionCapacityData'
|
||||
import ThroughPutData from './throughPut/throughPutData'
|
||||
import ROIData from './ROI/roiData'
|
||||
|
||||
function SimulationAnalysis() {
|
||||
const { isPlaying } = usePlayButtonStore()
|
||||
// useEffect(()=>{
|
||||
// if (isPlaying) {
|
||||
//
|
||||
// } else {
|
||||
//
|
||||
// }
|
||||
// },[isPlaying])
|
||||
|
||||
return (
|
||||
<>
|
||||
<ThroughPutData />
|
||||
|
||||
@@ -352,6 +352,37 @@ function PointsCreator() {
|
||||
</mesh>
|
||||
</group>
|
||||
);
|
||||
} else if (usedEvent.type === "human") {
|
||||
const point = usedEvent.point;
|
||||
return (
|
||||
<group
|
||||
key={`${index}-${usedEvent.modelUuid}`}
|
||||
position={usedEvent.position}
|
||||
rotation={usedEvent.rotation}
|
||||
>
|
||||
<mesh
|
||||
name="Event-Sphere"
|
||||
uuid={point.uuid}
|
||||
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (toolMode === 'cursor') {
|
||||
setSelectedEventSphere(
|
||||
sphereRefs.current[point.uuid]
|
||||
);
|
||||
}
|
||||
}}
|
||||
position={new THREE.Vector3(...point.position)}
|
||||
userData={{
|
||||
modelUuid: usedEvent.modelUuid,
|
||||
pointUuid: point.uuid,
|
||||
}}
|
||||
>
|
||||
<sphereGeometry args={[0.1, 16, 16]} />
|
||||
<meshStandardMaterial color="white" />
|
||||
</mesh>
|
||||
</group>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import React from 'react'
|
||||
import PointsCreator from './creator/pointsCreator'
|
||||
|
||||
function Points() {
|
||||
|
||||
@@ -152,6 +152,22 @@ function TriggerConnector() {
|
||||
});
|
||||
}
|
||||
}
|
||||
// Handle Human point
|
||||
else if (event.type === "human" && 'point' in event) {
|
||||
const point = event.point;
|
||||
point.actions?.forEach(action => {
|
||||
action.triggers?.forEach(trigger => {
|
||||
if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) {
|
||||
newConnections.push({
|
||||
id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`,
|
||||
startPointUuid: point.uuid,
|
||||
endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
|
||||
trigger
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
setConnections(newConnections);
|
||||
|
||||
13
app/src/modules/simulation/human/human.tsx
Normal file
13
app/src/modules/simulation/human/human.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import HumanInstances from './instances/humanInstances'
|
||||
|
||||
function Human() {
|
||||
return (
|
||||
<>
|
||||
|
||||
<HumanInstances />
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Human
|
||||
@@ -0,0 +1,20 @@
|
||||
import React from 'react'
|
||||
import HumanInstance from './instance/humanInstance';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
|
||||
function HumanInstances() {
|
||||
const { humanStore } = useSceneContext();
|
||||
const { humans } = humanStore();
|
||||
|
||||
return (
|
||||
<>
|
||||
{humans.map((human: HumanStatus) => (
|
||||
<React.Fragment key={human.modelUuid}>
|
||||
<HumanInstance human={human} />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default HumanInstances
|
||||
@@ -0,0 +1,15 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
function HumanInstance({ human }: { human: HumanStatus }) {
|
||||
|
||||
useEffect(() => {
|
||||
console.log('human: ', human);
|
||||
}, [human])
|
||||
|
||||
return (
|
||||
<>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default HumanInstance
|
||||
@@ -10,7 +10,7 @@ import { useParams } from 'react-router-dom';
|
||||
import { useVersionContext } from '../../builder/version/versionContext';
|
||||
|
||||
function Products() {
|
||||
const { armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, layout, productStore } = useSceneContext();
|
||||
const { armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, layout, productStore } = useSceneContext();
|
||||
const { products, getProductById, addProduct, setProducts } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { setMainProduct } = useMainProduct();
|
||||
@@ -20,6 +20,7 @@ function Products() {
|
||||
const { addMachine, clearMachines } = machineStore();
|
||||
const { addConveyor, clearConveyors } = conveyorStore();
|
||||
const { setCurrentMaterials, clearStorageUnits, updateCurrentLoad, addStorageUnit } = storageUnitStore();
|
||||
const { addHuman, clearHumans } = humanStore();
|
||||
const { isReset } = useResetButtonStore();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { mainProduct } = useMainProduct();
|
||||
@@ -153,6 +154,20 @@ function Products() {
|
||||
}
|
||||
}, [selectedProduct, products, isReset, isPlaying]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedProduct.productUuid) {
|
||||
const product = getProductById(selectedProduct.productUuid);
|
||||
if (product) {
|
||||
clearHumans();
|
||||
product.eventDatas.forEach(events => {
|
||||
if (events.type === 'human') {
|
||||
addHuman(selectedProduct.productUuid, events);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [selectedProduct, products, isReset, isPlaying]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import Vehicles from './vehicle/vehicles';
|
||||
import Points from './events/points/points';
|
||||
import Conveyor from './conveyor/conveyor';
|
||||
@@ -6,6 +6,7 @@ import RoboticArm from './roboticArm/roboticArm';
|
||||
import Materials from './materials/materials';
|
||||
import Machine from './machine/machine';
|
||||
import StorageUnit from './storageUnit/storageUnit';
|
||||
import Human from './human/human';
|
||||
import Simulator from './simulator/simulator';
|
||||
import Products from './products/products';
|
||||
import Trigger from './triggers/trigger';
|
||||
@@ -52,6 +53,8 @@ function Simulation() {
|
||||
|
||||
<StorageUnit />
|
||||
|
||||
<Human />
|
||||
|
||||
<Simulator />
|
||||
|
||||
<SimulationAnalysis />
|
||||
|
||||
@@ -4,7 +4,6 @@ import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayB
|
||||
import { determineExecutionOrder } from './functions/determineExecutionOrder';
|
||||
import { useProductContext } from '../products/productContext';
|
||||
import { useSceneContext } from '../../scene/sceneContext';
|
||||
import { useCompareProductDataStore } from '../../../store/builder/store';
|
||||
|
||||
function Simulator() {
|
||||
const { selectedProductStore } = useProductContext();
|
||||
|
||||
@@ -5,20 +5,20 @@ import { useSceneContext } from "../../../scene/sceneContext";
|
||||
import { useViewSceneStore } from "../../../../store/builder/store";
|
||||
|
||||
function VehicleInstances() {
|
||||
const { vehicleStore } = useSceneContext();
|
||||
const { vehicles } = vehicleStore();
|
||||
const { viewSceneLabels } = useViewSceneStore();
|
||||
const { vehicleStore } = useSceneContext();
|
||||
const { vehicles } = vehicleStore();
|
||||
const { viewSceneLabels } = useViewSceneStore();
|
||||
|
||||
return (
|
||||
<>
|
||||
{vehicles.map((vehicle: VehicleStatus) => (
|
||||
<React.Fragment key={vehicle.modelUuid}>
|
||||
<VehicleInstance agvDetail={vehicle} />
|
||||
{viewSceneLabels && <VehicleContentUi vehicle={vehicle} />}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
{vehicles.map((vehicle: VehicleStatus) => (
|
||||
<React.Fragment key={vehicle.modelUuid}>
|
||||
<VehicleInstance agvDetail={vehicle} />
|
||||
{viewSceneLabels && <VehicleContentUi vehicle={vehicle} />}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default VehicleInstances;
|
||||
|
||||
239
app/src/store/simulation/useHumanStore.ts
Normal file
239
app/src/store/simulation/useHumanStore.ts
Normal file
@@ -0,0 +1,239 @@
|
||||
import { create } from "zustand";
|
||||
import { immer } from "zustand/middleware/immer";
|
||||
|
||||
interface HumansStore {
|
||||
humans: HumanStatus[];
|
||||
|
||||
addHuman: (productUuid: string, event: HumanEventSchema) => void;
|
||||
removeHuman: (modelUuid: string) => void;
|
||||
updateHuman: (
|
||||
modelUuid: string,
|
||||
updates: Partial<Omit<HumanStatus, "modelUuid" | "productUuid">>
|
||||
) => void;
|
||||
clearHumans: () => void;
|
||||
|
||||
setHumanActive: (modelUuid: string, isActive: boolean) => void;
|
||||
setHumanPicking: (modelUuid: string, isPicking: boolean) => void;
|
||||
setHumanLoad: (modelUuid: string, load: number) => void;
|
||||
incrementHumanLoad: (modelUuid: string, incrementBy: number) => void;
|
||||
decrementHumanLoad: (modelUuid: string, decrementBy: number) => void;
|
||||
|
||||
addCurrentMaterial: (modelUuid: string, materialType: string, materialId: string) => void;
|
||||
setCurrentMaterials: (modelUuid: string, materials: { materialType: string; materialId: string }[]) => void;
|
||||
removeLastMaterial: (modelUuid: string) => { materialType: string; materialId: string } | undefined;
|
||||
getLastMaterial: (modelUuid: string) => { materialType: string; materialId: string } | undefined;
|
||||
clearCurrentMaterials: (modelUuid: string) => void;
|
||||
|
||||
setCurrentAction: (
|
||||
modelUuid: string,
|
||||
action: HumanStatus["currentAction"]
|
||||
) => void;
|
||||
|
||||
incrementActiveTime: (modelUuid: string, incrementBy: number) => void;
|
||||
incrementIdleTime: (modelUuid: string, incrementBy: number) => void;
|
||||
incrementDistanceTraveled: (modelUuid: string, incrementBy: number) => void;
|
||||
resetTime: (modelUuid: string) => void;
|
||||
|
||||
getHumanById: (modelUuid: string) => HumanStatus | undefined;
|
||||
getHumansByProduct: (productUuid: string) => HumanStatus[];
|
||||
getActiveHumans: () => HumanStatus[];
|
||||
}
|
||||
|
||||
export const createHumanStore = () => {
|
||||
return create<HumansStore>()(
|
||||
immer((set, get) => ({
|
||||
humans: [],
|
||||
|
||||
addHuman: (productUuid, event) => {
|
||||
set((state) => {
|
||||
const exists = state.humans.some(h => h.modelUuid === event.modelUuid);
|
||||
if (!exists) {
|
||||
state.humans.push({
|
||||
...event,
|
||||
productUuid,
|
||||
isActive: false,
|
||||
isPicking: false,
|
||||
idleTime: 0,
|
||||
activeTime: 0,
|
||||
currentLoad: 0,
|
||||
currentMaterials: [],
|
||||
distanceTraveled: 0
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
removeHuman: (modelUuid) => {
|
||||
set((state) => {
|
||||
state.humans = state.humans.filter(h => h.modelUuid !== modelUuid);
|
||||
});
|
||||
},
|
||||
|
||||
updateHuman: (modelUuid, updates) => {
|
||||
set((state) => {
|
||||
const human = state.humans.find(h => h.modelUuid === modelUuid);
|
||||
if (human) {
|
||||
Object.assign(human, updates);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
clearHumans: () => {
|
||||
set((state) => {
|
||||
state.humans = [];
|
||||
});
|
||||
},
|
||||
|
||||
setHumanActive: (modelUuid, isActive) => {
|
||||
set((state) => {
|
||||
const human = state.humans.find(h => h.modelUuid === modelUuid);
|
||||
if (human) {
|
||||
human.isActive = isActive;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setHumanPicking: (modelUuid, isPicking) => {
|
||||
set((state) => {
|
||||
const human = state.humans.find(h => h.modelUuid === modelUuid);
|
||||
if (human) {
|
||||
human.isPicking = isPicking;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setHumanLoad: (modelUuid, load) => {
|
||||
set((state) => {
|
||||
const human = state.humans.find(h => h.modelUuid === modelUuid);
|
||||
if (human) {
|
||||
human.currentLoad = load;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
incrementHumanLoad: (modelUuid, incrementBy) => {
|
||||
set((state) => {
|
||||
const human = state.humans.find(h => h.modelUuid === modelUuid);
|
||||
if (human) {
|
||||
human.currentLoad += incrementBy;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
decrementHumanLoad: (modelUuid, decrementBy) => {
|
||||
set((state) => {
|
||||
const human = state.humans.find(h => h.modelUuid === modelUuid);
|
||||
if (human) {
|
||||
human.currentLoad -= decrementBy;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
addCurrentMaterial: (modelUuid, materialType, materialId) => {
|
||||
set((state) => {
|
||||
const human = state.humans.find(h => h.modelUuid === modelUuid);
|
||||
if (human) {
|
||||
human.currentMaterials.push({ materialType, materialId });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setCurrentMaterials: (modelUuid, materials) => {
|
||||
set((state) => {
|
||||
const human = state.humans.find(h => h.modelUuid === modelUuid);
|
||||
if (human) {
|
||||
human.currentMaterials = materials;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
removeLastMaterial: (modelUuid) => {
|
||||
let removed;
|
||||
set((state) => {
|
||||
const human = state.humans.find(h => h.modelUuid === modelUuid);
|
||||
if (human && human.currentMaterials.length > 0) {
|
||||
removed = human.currentMaterials.pop();
|
||||
}
|
||||
});
|
||||
return removed;
|
||||
},
|
||||
|
||||
getLastMaterial: (modelUuid) => {
|
||||
const human = get().humans.find(h => h.modelUuid === modelUuid);
|
||||
if (human && human.currentMaterials.length > 0) {
|
||||
return human.currentMaterials[human.currentMaterials.length - 1];
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
clearCurrentMaterials: (modelUuid) => {
|
||||
set((state) => {
|
||||
const human = state.humans.find(h => h.modelUuid === modelUuid);
|
||||
if (human) {
|
||||
human.currentMaterials = [];
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setCurrentAction: (modelUuid, action) => {
|
||||
set((state) => {
|
||||
const human = state.humans.find(h => h.modelUuid === modelUuid);
|
||||
if (human) {
|
||||
human.currentAction = action;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
incrementActiveTime: (modelUuid, incrementBy) => {
|
||||
set((state) => {
|
||||
const human = state.humans.find(h => h.modelUuid === modelUuid);
|
||||
if (human) {
|
||||
human.activeTime += incrementBy;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
incrementIdleTime: (modelUuid, incrementBy) => {
|
||||
set((state) => {
|
||||
const human = state.humans.find(h => h.modelUuid === modelUuid);
|
||||
if (human) {
|
||||
human.idleTime += incrementBy;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
incrementDistanceTraveled: (modelUuid, incrementBy) => {
|
||||
set((state) => {
|
||||
const human = state.humans.find(h => h.modelUuid === modelUuid);
|
||||
if (human) {
|
||||
human.distanceTraveled += incrementBy;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
resetTime: (modelUuid) => {
|
||||
set((state) => {
|
||||
const human = state.humans.find(h => h.modelUuid === modelUuid);
|
||||
if (human) {
|
||||
human.activeTime = 0;
|
||||
human.idleTime = 0;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getHumanById: (modelUuid) => {
|
||||
return get().humans.find(h => h.modelUuid === modelUuid);
|
||||
},
|
||||
|
||||
getHumansByProduct: (productUuid) => {
|
||||
return get().humans.filter(h => h.productUuid === productUuid);
|
||||
},
|
||||
|
||||
getActiveHumans: () => {
|
||||
return get().humans.filter(h => h.isActive);
|
||||
}
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
export type HumanStoreType = ReturnType<typeof createHumanStore>;
|
||||
188
app/src/types/simulationTypes.d.ts
vendored
188
app/src/types/simulationTypes.d.ts
vendored
@@ -1,3 +1,4 @@
|
||||
// Base Types
|
||||
interface AssetEventSchema {
|
||||
modelUuid: string;
|
||||
modelName: string;
|
||||
@@ -18,69 +19,7 @@ interface TriggerSchema {
|
||||
} | null;
|
||||
}
|
||||
|
||||
interface ConveyorPointSchema {
|
||||
uuid: string;
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
action: ConveyorAction;
|
||||
}
|
||||
|
||||
interface VehiclePointSchema {
|
||||
uuid: string;
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
action: VehicleAction;
|
||||
}
|
||||
|
||||
interface RoboticArmPointSchema {
|
||||
uuid: string;
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
actions: RoboticArmAction[];
|
||||
}
|
||||
|
||||
interface MachinePointSchema {
|
||||
uuid: string;
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
action: MachineAction;
|
||||
}
|
||||
|
||||
interface StoragePointSchema {
|
||||
uuid: string;
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
action: StorageAction;
|
||||
}
|
||||
|
||||
interface ConveyorEventSchema extends AssetEventSchema {
|
||||
type: "transfer";
|
||||
speed: number;
|
||||
points: ConveyorPointSchema[];
|
||||
}
|
||||
|
||||
interface VehicleEventSchema extends AssetEventSchema {
|
||||
type: "vehicle";
|
||||
speed: number;
|
||||
point: VehiclePointSchema;
|
||||
}
|
||||
|
||||
interface RoboticArmEventSchema extends AssetEventSchema {
|
||||
type: "roboticArm";
|
||||
speed: number;
|
||||
point: RoboticArmPointSchema;
|
||||
}
|
||||
|
||||
interface MachineEventSchema extends AssetEventSchema {
|
||||
type: "machine";
|
||||
point: MachinePointSchema;
|
||||
}
|
||||
|
||||
interface StorageEventSchema extends AssetEventSchema {
|
||||
type: "storageUnit";
|
||||
point: StoragePointSchema;
|
||||
}
|
||||
|
||||
// Actions
|
||||
interface ConveyorAction {
|
||||
actionUuid: string;
|
||||
actionName: string;
|
||||
@@ -130,19 +69,100 @@ interface StorageAction {
|
||||
triggers: TriggerSchema[];
|
||||
}
|
||||
|
||||
type Action = ConveyorAction | VehicleAction | RoboticArmAction | MachineAction | StorageAction;
|
||||
interface HumanAction {
|
||||
actionUuid: string;
|
||||
actionName: string;
|
||||
actionType: "animation" | "animatedTravel";
|
||||
animation: string | null;
|
||||
loadCapacity: number;
|
||||
travelPoints?: { startPoint: [number, number, number] | null; endPoint: [number, number, number] | null; }
|
||||
triggers: TriggerSchema[];
|
||||
}
|
||||
|
||||
type PointsScheme = ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema;
|
||||
type Action = ConveyorAction | VehicleAction | RoboticArmAction | MachineAction | StorageAction | HumanAction;
|
||||
|
||||
type EventsSchema = ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema;
|
||||
// Points
|
||||
interface ConveyorPointSchema {
|
||||
uuid: string;
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
action: ConveyorAction;
|
||||
}
|
||||
|
||||
type productsSchema = {
|
||||
productName: string;
|
||||
productUuid: string;
|
||||
eventDatas: EventsSchema[];
|
||||
}[]
|
||||
interface VehiclePointSchema {
|
||||
uuid: string;
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
action: VehicleAction;
|
||||
}
|
||||
|
||||
interface RoboticArmPointSchema {
|
||||
uuid: string;
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
actions: RoboticArmAction[];
|
||||
}
|
||||
|
||||
interface MachinePointSchema {
|
||||
uuid: string;
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
action: MachineAction;
|
||||
}
|
||||
|
||||
interface StoragePointSchema {
|
||||
uuid: string;
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
action: StorageAction;
|
||||
}
|
||||
|
||||
interface HumanPointSchema {
|
||||
uuid: string;
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
actions: HumanAction[];
|
||||
}
|
||||
|
||||
type PointsScheme = | ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | HumanPointSchema;
|
||||
|
||||
// Events
|
||||
interface ConveyorEventSchema extends AssetEventSchema {
|
||||
type: "transfer";
|
||||
speed: number;
|
||||
points: ConveyorPointSchema[];
|
||||
}
|
||||
|
||||
interface VehicleEventSchema extends AssetEventSchema {
|
||||
type: "vehicle";
|
||||
speed: number;
|
||||
point: VehiclePointSchema;
|
||||
}
|
||||
|
||||
interface RoboticArmEventSchema extends AssetEventSchema {
|
||||
type: "roboticArm";
|
||||
speed: number;
|
||||
point: RoboticArmPointSchema;
|
||||
}
|
||||
|
||||
interface MachineEventSchema extends AssetEventSchema {
|
||||
type: "machine";
|
||||
point: MachinePointSchema;
|
||||
}
|
||||
|
||||
interface StorageEventSchema extends AssetEventSchema {
|
||||
type: "storageUnit";
|
||||
point: StoragePointSchema;
|
||||
}
|
||||
|
||||
interface HumanEventSchema extends AssetEventSchema {
|
||||
type: "human";
|
||||
point: HumanPointSchema;
|
||||
}
|
||||
|
||||
type EventsSchema = | ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema | HumanEventSchema;
|
||||
|
||||
// Statuses
|
||||
interface ConveyorStatus extends ConveyorEventSchema {
|
||||
productUuid: string;
|
||||
isActive: boolean;
|
||||
@@ -197,6 +217,24 @@ interface StorageUnitStatus extends StorageEventSchema {
|
||||
currentMaterials: { materialType: string; materialId: string; }[];
|
||||
}
|
||||
|
||||
interface HumanStatus extends HumanEventSchema {
|
||||
productUuid: string;
|
||||
isActive: boolean;
|
||||
isPicking: boolean;
|
||||
idleTime: number;
|
||||
activeTime: number;
|
||||
currentLoad: number;
|
||||
currentMaterials: { materialType: string; materialId: string; }[];
|
||||
distanceTraveled: number;
|
||||
currentAction?: {
|
||||
actionUuid: string;
|
||||
actionName: string;
|
||||
materialType?: string | null;
|
||||
materialId?: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
// Materials
|
||||
interface MaterialSchema {
|
||||
materialId: string;
|
||||
materialName: string;
|
||||
@@ -230,6 +268,14 @@ interface MaterialSchema {
|
||||
|
||||
type MaterialsSchema = MaterialSchema[];
|
||||
|
||||
// Products
|
||||
type productsSchema = {
|
||||
productName: string;
|
||||
productUuid: string;
|
||||
eventDatas: EventsSchema[];
|
||||
}[];
|
||||
|
||||
// Material History
|
||||
interface MaterialHistoryEntry {
|
||||
material: MaterialSchema;
|
||||
removedAt: string;
|
||||
|
||||
Reference in New Issue
Block a user