v2 #78

Merged
Vishnu merged 5 commits from v2 into main 2025-05-05 09:21:34 +00:00
12 changed files with 366 additions and 251 deletions
Showing only changes of commit 8ba2bae810 - Show all commits

View File

@ -28,6 +28,4 @@ export default async function setCameraView({
controls?.setLookAt(...newPosition.toArray(), newPosition.x, 0, newPosition.z, true);
}
// Optionally you can log
console.log(`Camera view updated by ${username ?? 'unknown user'}`);
}

View File

@ -7,7 +7,7 @@ import { useSelectedProduct } from "../../../../../store/simulation/useSimulatio
export function useSpawnHandler() {
const { addMaterial } = useMaterialStore();
const { getModelUuidByActionUuid } = useProductStore();
const { getModelUuidByActionUuid, getPointUuidByActionUuid } = useProductStore();
const { selectedProduct } = useSelectedProduct();
const lastSpawnTime = useRef<number | null>(null);
const startTime = useRef<number | null>(null);
@ -16,7 +16,7 @@ export function useSpawnHandler() {
material: string;
intervalMs: number;
totalCount: number;
point: ConveyorPointSchema;
action: ConveyorAction;
} | null>(null);
const clearCurrentSpawn = useCallback(() => {
@ -27,12 +27,13 @@ export function useSpawnHandler() {
}, []);
const spawnLogStatus = (materialUuid: string, status: string) => {
// console.log(`${materialUuid}, ${status}`);
console.log(`${materialUuid}, ${status}`);
}
const createNewMaterial = useCallback((materialType: string, point: ConveyorPointSchema) => {
const modelUuid = getModelUuidByActionUuid(selectedProduct.productId, point.action.actionUuid);
if (!modelUuid || !point.uuid) return;
const createNewMaterial = useCallback((materialType: string, action: ConveyorAction) => {
const modelUuid = getModelUuidByActionUuid(selectedProduct.productId, action.actionUuid);
const pointUuid = getPointUuidByActionUuid(selectedProduct.productId, action.actionUuid);
if (!modelUuid || !pointUuid) return;
const newMaterial: MaterialSchema = {
materialId: THREE.MathUtils.generateUUID(),
@ -43,8 +44,8 @@ export function useSpawnHandler() {
isRendered: true,
current: {
modelUuid: modelUuid,
pointUuid: point.uuid,
actionUuid: point.action?.actionUuid || ''
pointUuid: pointUuid,
actionUuid: action?.actionUuid || ''
},
weight: 1,
cost: 1
@ -58,14 +59,14 @@ export function useSpawnHandler() {
if (!spawnParams.current || !startTime.current) return;
const currentTime = performance.now();
const { material, intervalMs, totalCount, point } = spawnParams.current;
const { material, intervalMs, totalCount, action } = spawnParams.current;
const isFirstSpawn = lastSpawnTime.current === null;
const elapsed = currentTime - startTime.current;
// First spawn
if (isFirstSpawn) {
if (elapsed >= intervalMs) {
const createdMaterial = createNewMaterial(material, point);
const createdMaterial = createNewMaterial(material, action);
if (createdMaterial) {
spawnLogStatus(createdMaterial.materialId, `[${elapsed.toFixed(2)}ms] Spawned ${material} (1/${totalCount})`);
}
@ -84,9 +85,9 @@ export function useSpawnHandler() {
const timeSinceLast = currentTime - lastSpawnTime.current;
if (timeSinceLast >= intervalMs) {
const count = spawnCountRef.current + 1;
const createdMaterial = createNewMaterial(material, point);
const createdMaterial = createNewMaterial(material, action);
if (createdMaterial) {
spawnLogStatus(createdMaterial.materialId, `[${elapsed.toFixed(2)}ms] Spawned ${material} (1/${totalCount})`);
spawnLogStatus(createdMaterial.materialId, `[${elapsed.toFixed(2)}ms] Spawned ${material} (${count}/${totalCount})`);
}
lastSpawnTime.current = currentTime;
spawnCountRef.current = count;
@ -98,10 +99,10 @@ export function useSpawnHandler() {
}
});
const handleSpawn = useCallback((point: ConveyorPointSchema) => {
if (!point.action || point.action.actionType !== 'spawn') return;
const handleSpawn = useCallback((action: ConveyorAction) => {
if (!action || action.actionType !== 'spawn') return;
const { material, spawnInterval = 0, spawnCount = 1 } = point.action;
const { material, spawnInterval = 0, spawnCount = 1 } = action;
const intervalMs = spawnInterval * 1000;
clearCurrentSpawn();
@ -110,7 +111,7 @@ export function useSpawnHandler() {
material,
intervalMs,
totalCount: spawnCount,
point: point
action: action
};
startTime.current = performance.now();

View File

@ -1,40 +1,66 @@
import { useEffect } from "react";
import { useSpawnHandler } from "./actionHandler/spawnActionHandler";
import { useEffect, useCallback, useRef } from "react";
import { useSpawnHandler } from "./actionHandler/useSpawnHandler";
// Conveyor Actions
export function useConveyorActions(point: ConveyorPointSchema) {
const { handleSpawn } = useSpawnHandler();
export function useConveyorActions() {
const { handleSpawn, clearCurrentSpawn } = useSpawnHandler();
const handleDefaultAction = useCallback((action: ConveyorAction) => {
console.log(`Default conveyor action ${action.actionUuid}`);
}, []);
const handleSpawnAction = useCallback((action: ConveyorAction) => {
handleSpawn(action);
}, [handleSpawn]);
const handleSwapAction = useCallback((action: ConveyorAction) => {
console.log(`Swapping to material ${action.material}`);
}, []);
const handleDelayAction = useCallback((action: ConveyorAction) => {
const delayMs = (action.delay || 0) * 1000;
console.log(`Delaying for ${delayMs}ms`);
}, []);
const handleDespawnAction = useCallback((action: ConveyorAction) => {
console.log(`Despawning material`);
}, []);
const handleConveyorAction = useCallback((action: ConveyorAction) => {
if (!action) return;
switch (action.actionType) {
case 'default':
handleDefaultAction(action);
break;
case 'spawn':
handleSpawnAction(action);
break;
case 'swap':
handleSwapAction(action);
break;
case 'delay':
handleDelayAction(action);
break;
case 'despawn':
handleDespawnAction(action);
break;
default:
console.warn(`Unknown conveyor action type: ${action.actionType}`);
}
}, [handleDefaultAction, handleSpawnAction, handleSwapAction, handleDelayAction, handleDespawnAction]);
const cleanup = useCallback(() => {
clearCurrentSpawn();
}, [clearCurrentSpawn]);
useEffect(() => {
if (!point.action) return;
const { actionType, material, delay, spawnInterval, spawnCount } = point.action;
const handleAction = () => {
switch (actionType) {
case 'default':
console.log(`Default conveyor action at point ${point.uuid}`);
break;
case 'spawn':
console.log(`Spawning material ${material} at point ${point.uuid}`);
handleSpawn(point);
break;
case 'swap':
console.log(`Swapping to material ${material} at point ${point.uuid}`);
break;
case 'delay':
console.log(`Delaying for ${delay}ms at point ${point.uuid}`);
break;
case 'despawn':
console.log(`Despawning material at point ${point.uuid}`);
break;
}
};
handleAction();
return () => {
// Cleanup if needed
cleanup();
};
}, [point]);
}, [cleanup]);
return {
handleConveyorAction,
cleanup
};
}

View File

@ -1,23 +1,35 @@
import { useEffect } from 'react';
import { useEffect, useCallback, useRef, useState } from 'react';
export function useMachineActions() {
const handleProcessAction = useCallback((action: MachineAction) => {
if (!action || action.actionType !== 'process') return;
console.log(`Machine processing for ${action.processTime}ms`);
}, []);
const handleMachineAction = useCallback((action: MachineAction) => {
if (!action) return;
switch (action.actionType) {
case 'process':
handleProcessAction(action);
break;
default:
console.warn(`Unknown machine action type: ${action.actionType}`);
}
}, [handleProcessAction]);
const cleanup = useCallback(() => {
}, []);
// Machine Actions
export function useMachineActions(point: MachinePointSchema) {
useEffect(() => {
if (!point.action) return;
const { actionType, processTime, swapMaterial } = point.action;
const handleAction = () => {
if (actionType === 'process') {
console.log(`Machine processing for ${processTime}ms at point ${point.uuid}`);
if (swapMaterial) {
console.log(`Swapping material to ${swapMaterial}`);
}
}
};
return () => {
// Cleanup if needed
cleanup();
};
}, [point]);
}, [cleanup]);
return {
handleMachineAction,
cleanup,
};
}

View File

@ -1,26 +1,32 @@
import { useEffect } from 'react';
import { useEffect, useCallback } from 'react';
export function useRoboticArmActions() {
const handlePickAndPlace = useCallback((action: RoboticArmAction) => {
console.log(`Robotic arm pick and place`);
}, []);
const handleRoboticArmAction = useCallback((action: RoboticArmAction) => {
switch (action.actionType) {
case 'pickAndPlace':
handlePickAndPlace(action);
break;
default:
console.warn(`Unknown robotic arm action type: ${action.actionType}`);
}
}, [handlePickAndPlace]);
const cleanup = useCallback(() => {
}, []);
// Robotic Arm Actions
export function useRoboticArmActions(point: RoboticArmPointSchema) {
useEffect(() => {
point.actions.forEach(action => {
const { actionType, process } = action;
const handleAction = () => {
if (actionType === 'pickAndPlace') {
console.log(`Robotic arm pick and place at point ${point.uuid}`);
if (process.startPoint) {
console.log(`Start point: ${process.startPoint}`);
}
if (process.endPoint) {
console.log(`End point: ${process.endPoint}`);
}
}
};
});
return () => {
// Cleanup if needed
cleanup();
};
}, [point]);
}, [cleanup]);
return {
handleRoboticArmAction,
cleanup
};
}

View File

@ -1,22 +1,33 @@
import { useEffect } from 'react';
import { useEffect, useCallback, useRef, useState } from 'react';
export function useStorageActions() {
const handleStoreAction = useCallback((action: StorageAction) => {
if (!action || action.actionType !== 'store') return;
}, []);
const handleStorageAction = useCallback((action: StorageAction) => {
if (!action) return;
switch (action.actionType) {
case 'store':
handleStoreAction(action);
break;
default:
console.warn(`Unknown storage action type: ${action.actionType}`);
}
}, [handleStoreAction]);
const cleanup = useCallback(() => {
}, []);
// Storage Actions
export function useStorageActions(point: StoragePointSchema) {
useEffect(() => {
if (!point.action) return;
const { actionType, materials, storageCapacity } = point.action;
const handleAction = () => {
if (actionType === 'store') {
console.log(`Storage action at point ${point.uuid}`);
console.log(`Materials: ${materials.map(m => m.materialName).join(', ')}`);
console.log(`Capacity: ${storageCapacity}`);
}
};
return () => {
// Cleanup if needed
cleanup();
};
}, [point]);
}, [cleanup]);
return {
handleStorageAction,
cleanup
};
}

View File

@ -3,23 +3,76 @@ import { useMachineActions } from "./machine/useMachineActions";
import { useRoboticArmActions } from "./roboticArm/useRoboticArmActions";
import { useStorageActions } from "./storageUnit/useStorageUnitActions";
import { useVehicleActions } from "./vehicle/useVehicleActions";
import { useCallback, useEffect } from "react";
// Master hook that selects the appropriate action handler
export function useActionHandler(point: PointsScheme) {
if ('actions' in point) {
// Robotic Arm
useRoboticArmActions(point);
} else if (point.action.actionType === 'travel') {
// Vehicle
useVehicleActions(point as VehiclePointSchema);
} else if (point.action.actionType === 'process') {
// Machine
useMachineActions(point as MachinePointSchema);
} else if (point.action.actionType === 'store') {
// Storage
useStorageActions(point as StoragePointSchema);
} else {
// Conveyor
useConveyorActions(point as ConveyorPointSchema);
}
export function useActionHandler() {
// Initialize all action handlers
const { handleConveyorAction, cleanup: cleanupConveyor } = useConveyorActions();
const { handleVehicleAction, cleanup: cleanupVehicle } = useVehicleActions();
const { handleRoboticArmAction, cleanup: cleanupRoboticArm } = useRoboticArmActions();
const { handleMachineAction, cleanup: cleanupMachine } = useMachineActions();
const { handleStorageAction, cleanup: cleanupStorage } = useStorageActions();
// Main handler function
const handleAction = useCallback((action: Action) => {
if (!action) return;
try {
switch (action.actionType) {
case 'default': case 'spawn': case 'swap': case 'delay': case 'despawn':
handleConveyorAction(action as ConveyorAction);
break;
case 'travel':
handleVehicleAction(action as VehicleAction);
break;
case 'pickAndPlace':
handleRoboticArmAction(action as RoboticArmAction);
break;
case 'process':
handleMachineAction(action as MachineAction);
break;
case 'store':
handleStorageAction(action as StorageAction);
break;
default:
console.warn(`Unknown action type: ${(action as Action).actionType}`);
}
} catch (error) {
console.error('Error handling action:', error);
// Consider adding error recovery or notification here
}
}, [
handleConveyorAction,
handleVehicleAction,
handleRoboticArmAction,
handleMachineAction,
handleStorageAction
]);
// Cleanup all actions
const cleanup = useCallback(() => {
cleanupConveyor();
cleanupVehicle();
cleanupRoboticArm();
cleanupMachine();
cleanupStorage();
}, [
cleanupConveyor,
cleanupVehicle,
cleanupRoboticArm,
cleanupMachine,
cleanupStorage
]);
// Auto cleanup on unmount
useEffect(() => {
return () => {
cleanup();
};
}, [cleanup]);
return {
handleAction,
cleanup
};
}

View File

@ -1,26 +1,37 @@
import { useEffect } from 'react';
import { useEffect, useCallback } from 'react';
export function useVehicleActions() {
const handleTravelAction = useCallback((action: VehicleAction) => {
if (!action || action.actionType !== 'travel') return;
console.log(`Vehicle travel action ${action.actionUuid}`);
}, []);
const handleVehicleAction = useCallback((action: VehicleAction) => {
if (!action) return;
switch (action.actionType) {
case 'travel':
handleTravelAction(action);
break;
default:
console.warn(`Unknown vehicle action type: ${action.actionType}`);
}
}, [handleTravelAction]);
const cleanup = useCallback(() => {
}, []);
// Vehicle Actions
export function useVehicleActions(point: VehiclePointSchema) {
useEffect(() => {
if (!point.action) return;
const { actionType, unLoadDuration, loadCapacity, steeringAngle } = point.action;
const handleAction = () => {
if (actionType === 'travel') {
console.log(`Vehicle travel action at point ${point.uuid}`);
if (point.action.pickUpPoint) {
console.log(`Pick up at: ${JSON.stringify(point.action.pickUpPoint)}`);
}
if (point.action.unLoadPoint) {
console.log(`Unload at: ${JSON.stringify(point.action.unLoadPoint)}`);
}
}
};
return () => {
// Cleanup if needed
cleanup();
};
}, [point]);
}, [cleanup]);
return {
handleVehicleAction,
cleanup
};
}

View File

@ -1,114 +1,21 @@
import { useEffect } from 'react';
import { useProductStore } from '../../../store/simulation/useProductStore';
import { useActionHandler } from '../actions/useActionHandler';
function Simulator() {
const { products } = useProductStore();
const { handleAction } = useActionHandler();
const executionOrder = determineExecutionOrder(products);
executionOrder.forEach(point => {
useActionHandler(point);
});
function determineExecutionSequences(products: productsSchema): PointsScheme[][] {
// Create maps for all points
const pointMap = new Map<string, PointsScheme>();
const allPoints: PointsScheme[] = [];
// First pass: collect all points
products.forEach(product => {
product.eventDatas.forEach(event => {
if (event.type === 'transfer') {
event.points.forEach(point => {
pointMap.set(point.uuid, point);
allPoints.push(point);
});
} else if (event.type === 'vehicle' ||
event.type === 'machine' ||
event.type === 'storageUnit' ||
event.type === 'roboticArm') {
pointMap.set(event.point.uuid, event.point);
allPoints.push(event.point);
}
});
});
// Build complete dependency graph
const dependencyGraph = new Map<string, string[]>();
const reverseDependencyGraph = new Map<string, string[]>();
const triggeredPoints = new Set<string>();
allPoints.forEach(point => {
const triggers = extractTriggersFromPoint(point);
const dependencies: string[] = [];
triggers.forEach(trigger => {
const targetUuid = trigger.triggeredAsset?.triggeredPoint?.pointUuid;
if (targetUuid && pointMap.has(targetUuid)) {
dependencies.push(targetUuid);
triggeredPoints.add(targetUuid);
if (!reverseDependencyGraph.has(targetUuid)) {
reverseDependencyGraph.set(targetUuid, []);
}
reverseDependencyGraph.get(targetUuid)!.push(point.uuid);
}
});
dependencyGraph.set(point.uuid, dependencies);
});
// Identify independent root points (points that trigger others but aren't triggered themselves)
const rootPoints = allPoints.filter(point => {
const hasOutgoingTriggers = extractTriggersFromPoint(point).some(
t => t.triggeredAsset?.triggeredPoint?.pointUuid
);
return hasOutgoingTriggers && !triggeredPoints.has(point.uuid);
});
// For each root point, build its complete trigger chain
const executionSequences: PointsScheme[][] = [];
function buildSequence(startUuid: string): PointsScheme[] {
const sequence: PointsScheme[] = [];
const visited = new Set<string>();
function traverse(uuid: string) {
if (visited.has(uuid)) return;
visited.add(uuid);
const point = pointMap.get(uuid);
if (point) {
sequence.push(point);
}
// Follow forward dependencies
const nextPoints = dependencyGraph.get(uuid) || [];
nextPoints.forEach(nextUuid => traverse(nextUuid));
}
traverse(startUuid);
return sequence;
}
// Build sequences for all root points
rootPoints.forEach(root => {
executionSequences.push(buildSequence(root.uuid));
});
// Handle any triggered points not reachable from roots (isolated chains)
const processedPoints = new Set(
executionSequences.flat().map(p => p.uuid)
);
allPoints.forEach(point => {
if (triggeredPoints.has(point.uuid) && !processedPoints.has(point.uuid)) {
executionSequences.push(buildSequence(point.uuid));
useEffect(() => {
const executionOrder = determineExecutionOrder(products);
executionOrder.forEach(point => {
if ('actions' in point) {
handleAction(point.actions[0]);
}else{
handleAction(point.action);
}
});
return executionSequences;
}
}, [products, handleAction]);
function determineExecutionOrder(products: productsSchema): PointsScheme[] {
// Create maps for all events and points

View File

@ -0,0 +1,13 @@
import { useCallback } from 'react';
import { useActionHandler } from '../../actions/useActionHandler';
export function useTriggerHandler() {
const triggerPointActions = useCallback((point: PointsScheme) => {
if (!point) return;
}, []);
return {
triggerPointActions
};
}

View File

@ -64,6 +64,7 @@ type ProductsStore = {
getPointByUuid: (productId: string, modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined;
getActionByUuid: (productId: string, actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined;
getModelUuidByActionUuid: (productId: string, actionUuid: string) => (string) | undefined;
getPointUuidByActionUuid: (productId: string, actionUuid: string) => (string) | undefined;
getTriggerByUuid: (productId: string, triggerUuid: string) => TriggerSchema | undefined;
getIsEventInProduct: (productId: string, modelUuid: string) => boolean;
};
@ -598,6 +599,30 @@ export const useProductStore = create<ProductsStore>()(
return undefined;
},
getPointUuidByActionUuid: (productId, actionUuid) => {
const product = get().products.find(p => p.productId === productId);
if (!product) return undefined;
for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
if (point.action?.actionUuid === actionUuid) {
return point.uuid;
}
}
} else if ('point' in event) {
const point = (event as any).point;
if ('action' in point && point.action?.actionUuid === actionUuid) {
return point.uuid;
} else if ('actions' in point) {
const action = point.actions.find((a: any) => a.actionUuid === actionUuid);
if (action) return point.uuid;
}
}
}
return undefined;
},
getTriggerByUuid: (productId, triggerUuid) => {
const product = get().products.find(p => p.productId === productId);
if (!product) return undefined;

View File

@ -203,4 +203,56 @@ interface MaterialSchema {
};
}
type MaterialsSchema = MaterialSchema[];
type MaterialsSchema = MaterialSchema[];
interface ConveyorAction {
actionUuid: string;
actionName: string;
actionType: "default" | "spawn" | "swap" | "delay" | "despawn";
material: string;
delay: number;
spawnInterval: number;
spawnCount: number;
triggers: TriggerSchema[];
}
interface VehicleAction {
actionUuid: string;
actionName: string;
actionType: "travel";
unLoadDuration: number;
loadCapacity: number;
steeringAngle: number;
pickUpPoint: { position: { x: number; y: number, z: number }, rotation: { x: number; y: number, z: number } } | null;
unLoadPoint: { position: { x: number; y: number, z: number }, rotation: { x: number; y: number, z: number } } | null;
triggers: TriggerSchema[];
}
interface RoboticArmAction {
actionUuid: string;
actionName: string;
actionType: "pickAndPlace";
process: { startPoint: [number, number, number] | null; endPoint: [number, number, number] | null; };
triggers: TriggerSchema[];
}
interface MachineAction {
actionUuid: string;
actionName: string;
actionType: "process";
processTime: number;
swapMaterial: string;
triggers: TriggerSchema[];
}
interface StorageAction {
actionUuid: string;
actionName: string;
actionType: "store";
materials: { materialName: string; materialId: string; }[];
storageCapacity: number;
triggers: TriggerSchema[];
}
type Action = ConveyorAction | VehicleAction | RoboticArmAction | MachineAction | StorageAction;