Refactor conveyor mechanics and action handlers: update spawn count, interval, and delay handling; enhance material management in useMaterialStore; implement spawn action handler; unify action handling across various components.
This commit is contained in:
parent
aefa9ec2b2
commit
13af6c2c67
|
@ -103,7 +103,7 @@ function ConveyorMechanics() {
|
|||
const handleSpawnCountChange = (value: string) => {
|
||||
if (!selectedEventData || !selectedPointData) return;
|
||||
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, {
|
||||
spawnCount: value === "inherit" ? "inherit" : parseFloat(value),
|
||||
spawnCount: parseFloat(value),
|
||||
});
|
||||
|
||||
if (event) {
|
||||
|
@ -119,7 +119,7 @@ function ConveyorMechanics() {
|
|||
const handleSpawnIntervalChange = (value: string) => {
|
||||
if (!selectedEventData || !selectedPointData) return;
|
||||
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, {
|
||||
spawnInterval: value === "inherit" ? "inherit" : parseFloat(value),
|
||||
spawnInterval: parseFloat(value),
|
||||
});
|
||||
|
||||
if (event) {
|
||||
|
@ -149,7 +149,7 @@ function ConveyorMechanics() {
|
|||
const handleDelayChange = (value: string) => {
|
||||
if (!selectedEventData || !selectedPointData) return;
|
||||
const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, {
|
||||
delay: value === "inherit" ? "inherit" : parseFloat(value),
|
||||
delay: parseFloat(value),
|
||||
});
|
||||
|
||||
if (event) {
|
||||
|
@ -271,7 +271,7 @@ function ConveyorMechanics() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="tirgger">
|
||||
<Trigger selectedPointData={selectedPointData as any} type= {'Conveyor'}/>
|
||||
<Trigger selectedPointData={selectedPointData as any} type={'Conveyor'} />
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
import { useCallback, useEffect, useRef } from "react";
|
||||
import * as THREE from 'three';
|
||||
import { useFrame } from "@react-three/fiber";
|
||||
import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore";
|
||||
import { useProductStore } from "../../../../../store/simulation/useProductStore";
|
||||
import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore";
|
||||
|
||||
export function useSpawnHandler() {
|
||||
const { addMaterial } = useMaterialStore();
|
||||
const { getModelUuidByActionUuid } = useProductStore();
|
||||
const { selectedProduct } = useSelectedProduct();
|
||||
const lastSpawnTime = useRef<number | null>(null);
|
||||
const startTime = useRef<number | null>(null);
|
||||
const spawnCountRef = useRef<number>(0);
|
||||
const spawnParams = useRef<{
|
||||
material: string;
|
||||
intervalMs: number;
|
||||
totalCount: number;
|
||||
point: ConveyorPointSchema;
|
||||
} | null>(null);
|
||||
|
||||
const clearCurrentSpawn = useCallback(() => {
|
||||
lastSpawnTime.current = null;
|
||||
startTime.current = null;
|
||||
spawnCountRef.current = 0;
|
||||
spawnParams.current = null;
|
||||
}, []);
|
||||
|
||||
const spawnLogStatus = (materialUuid: string, status: string) => {
|
||||
// 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 newMaterial: MaterialSchema = {
|
||||
materialId: THREE.MathUtils.generateUUID(),
|
||||
materialName: `${materialType}-${Date.now()}`,
|
||||
materialType: materialType,
|
||||
isActive: false,
|
||||
isVisible: true,
|
||||
isRendered: true,
|
||||
current: {
|
||||
modelUuid: modelUuid,
|
||||
pointUuid: point.uuid,
|
||||
actionUuid: point.action?.actionUuid || ''
|
||||
},
|
||||
weight: 1,
|
||||
cost: 1
|
||||
};
|
||||
|
||||
addMaterial(newMaterial);
|
||||
return newMaterial;
|
||||
}, [addMaterial]);
|
||||
|
||||
useFrame(() => {
|
||||
if (!spawnParams.current || !startTime.current) return;
|
||||
|
||||
const currentTime = performance.now();
|
||||
const { material, intervalMs, totalCount, point } = spawnParams.current;
|
||||
const isFirstSpawn = lastSpawnTime.current === null;
|
||||
const elapsed = currentTime - startTime.current;
|
||||
|
||||
// First spawn
|
||||
if (isFirstSpawn) {
|
||||
if (elapsed >= intervalMs) {
|
||||
const createdMaterial = createNewMaterial(material, point);
|
||||
if (createdMaterial) {
|
||||
spawnLogStatus(createdMaterial.materialId, `[${elapsed.toFixed(2)}ms] Spawned ${material} (1/${totalCount})`);
|
||||
}
|
||||
lastSpawnTime.current = currentTime;
|
||||
spawnCountRef.current = 1;
|
||||
|
||||
if (totalCount <= 1) {
|
||||
clearCurrentSpawn();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Subsequent spawns
|
||||
if (lastSpawnTime.current !== null) {
|
||||
const timeSinceLast = currentTime - lastSpawnTime.current;
|
||||
if (timeSinceLast >= intervalMs) {
|
||||
const count = spawnCountRef.current + 1;
|
||||
const createdMaterial = createNewMaterial(material, point);
|
||||
if (createdMaterial) {
|
||||
spawnLogStatus(createdMaterial.materialId, `[${elapsed.toFixed(2)}ms] Spawned ${material} (1/${totalCount})`);
|
||||
}
|
||||
lastSpawnTime.current = currentTime;
|
||||
spawnCountRef.current = count;
|
||||
|
||||
if (count >= totalCount) {
|
||||
clearCurrentSpawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const handleSpawn = useCallback((point: ConveyorPointSchema) => {
|
||||
if (!point.action || point.action.actionType !== 'spawn') return;
|
||||
|
||||
const { material, spawnInterval = 0, spawnCount = 1 } = point.action;
|
||||
const intervalMs = spawnInterval * 1000;
|
||||
|
||||
clearCurrentSpawn();
|
||||
|
||||
spawnParams.current = {
|
||||
material,
|
||||
intervalMs,
|
||||
totalCount: spawnCount,
|
||||
point: point
|
||||
};
|
||||
|
||||
startTime.current = performance.now();
|
||||
}, [clearCurrentSpawn]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
clearCurrentSpawn();
|
||||
};
|
||||
}, [clearCurrentSpawn]);
|
||||
|
||||
return {
|
||||
handleSpawn,
|
||||
clearCurrentSpawn
|
||||
};
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
import { useEffect } from "react";
|
||||
import { useSpawnHandler } from "./actionHandler/spawnActionHandler";
|
||||
|
||||
// Conveyor Actions
|
||||
export function useConveyorActions(point: ConveyorPointSchema) {
|
||||
const { handleSpawn } = useSpawnHandler();
|
||||
|
||||
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
|
||||
};
|
||||
}, [point]);
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import { useEffect } from 'react';
|
||||
|
||||
// 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
|
||||
};
|
||||
}, [point]);
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import { useEffect } from 'react';
|
||||
|
||||
// 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
|
||||
};
|
||||
}, [point]);
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import { useEffect } from 'react';
|
||||
|
||||
// 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
|
||||
};
|
||||
}, [point]);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import { useConveyorActions } from "./conveyor/useConveyorActions";
|
||||
import { useMachineActions } from "./machine/useMachineActions";
|
||||
import { useRoboticArmActions } from "./roboticArm/useRoboticArmActions";
|
||||
import { useStorageActions } from "./storageUnit/useStorageUnitActions";
|
||||
import { useVehicleActions } from "./vehicle/useVehicleActions";
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import { useEffect } from 'react';
|
||||
|
||||
// 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
|
||||
};
|
||||
}, [point]);
|
||||
}
|
|
@ -6,8 +6,8 @@ function MachineInstances() {
|
|||
const { machines } = useMachineStore();
|
||||
return (
|
||||
<>
|
||||
{machines.map((val: MachineStatus) => (
|
||||
<MachineInstance key={val.modelUuid} machineDetail={val} />
|
||||
{machines.map((machine: MachineStatus) => (
|
||||
<MachineInstance key={machine.modelUuid} machineDetail={machine} />
|
||||
))}
|
||||
|
||||
</>
|
||||
|
|
|
@ -1,9 +1,22 @@
|
|||
import React from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import * as THREE from 'three';
|
||||
import MaterialAnimator from '../animator/materialAnimator'
|
||||
|
||||
function MaterialInstance() {
|
||||
return (
|
||||
<></>
|
||||
)
|
||||
function MaterialInstance({ material }: { material: MaterialSchema }) {
|
||||
const [position, setPosition] = useState<THREE.Vector3>();
|
||||
const [rotation, setRotation] = useState<THREE.Vector3>();
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('material: ', material);
|
||||
}, [material])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<MaterialAnimator />
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default MaterialInstance
|
|
@ -1,14 +1,20 @@
|
|||
import React from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import MaterialInstance from './instance/materialInstance'
|
||||
import MaterialAnimator from './animator/materialAnimator'
|
||||
import { useMaterialStore } from '../../../../store/simulation/useMaterialStore';
|
||||
|
||||
function MaterialInstances() {
|
||||
const { materials } = useMaterialStore();
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('materials: ', materials);
|
||||
}, [materials])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<MaterialInstance />
|
||||
|
||||
<MaterialAnimator />
|
||||
{materials.map((material: MaterialSchema) =>
|
||||
<MaterialInstance key={material.materialId} material={material} />
|
||||
)}
|
||||
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -23,7 +23,7 @@ function Simulation() {
|
|||
}, [events])
|
||||
|
||||
useEffect(() => {
|
||||
console.log('products: ', products);
|
||||
// console.log('products: ', products);
|
||||
}, [products])
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,18 +1,238 @@
|
|||
import { useEffect } from 'react'
|
||||
import { useProductStore } from '../../../store/simulation/useProductStore'
|
||||
import { useProductStore } from '../../../store/simulation/useProductStore';
|
||||
import { useActionHandler } from '../actions/useActionHandler';
|
||||
|
||||
function Simulator() {
|
||||
const { products } = useProductStore();
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('products: ', products);
|
||||
}, [products])
|
||||
const executionOrder = determineExecutionOrder(products);
|
||||
|
||||
return (
|
||||
<>
|
||||
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));
|
||||
}
|
||||
});
|
||||
|
||||
return executionSequences;
|
||||
}
|
||||
|
||||
function determineExecutionOrder(products: productsSchema): PointsScheme[] {
|
||||
// Create maps for all events and points
|
||||
const eventMap = new Map<string, EventsSchema>();
|
||||
const pointMap = new Map<string, PointsScheme>();
|
||||
const allPoints: PointsScheme[] = [];
|
||||
|
||||
// First pass: collect all points
|
||||
products.forEach(product => {
|
||||
product.eventDatas.forEach(event => {
|
||||
eventMap.set(event.modelUuid, 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') {
|
||||
pointMap.set(event.point.uuid, event.point);
|
||||
allPoints.push(event.point);
|
||||
} else if (event.type === 'roboticArm') {
|
||||
pointMap.set(event.point.uuid, event.point);
|
||||
allPoints.push(event.point);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Build dependency graphs
|
||||
const graph = new Map<string, string[]>();
|
||||
const reverseGraph = new Map<string, string[]>();
|
||||
const allTriggeredPoints = 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);
|
||||
allTriggeredPoints.add(targetUuid);
|
||||
|
||||
if (!reverseGraph.has(targetUuid)) {
|
||||
reverseGraph.set(targetUuid, []);
|
||||
}
|
||||
reverseGraph.get(targetUuid)!.push(point.uuid);
|
||||
}
|
||||
});
|
||||
|
||||
graph.set(point.uuid, dependencies);
|
||||
});
|
||||
|
||||
// Identify root points (points that trigger others but aren't triggered themselves)
|
||||
const rootPoints = allPoints
|
||||
.filter(point => !allTriggeredPoints.has(point.uuid))
|
||||
.filter(point => {
|
||||
// Only include roots that actually have triggers pointing FROM them
|
||||
const triggers = extractTriggersFromPoint(point);
|
||||
return triggers.some(t => t.triggeredAsset?.triggeredPoint?.pointUuid);
|
||||
});
|
||||
|
||||
// If no root points found but we have triggered points, find the earliest triggers
|
||||
if (rootPoints.length === 0 && allTriggeredPoints.size > 0) {
|
||||
// This handles cases where we have circular dependencies
|
||||
// but still want to include the triggered points
|
||||
const minTriggerCount = Math.min(
|
||||
...Array.from(allTriggeredPoints)
|
||||
.map(uuid => (graph.get(uuid) || []).length)
|
||||
);
|
||||
const potentialRoots = Array.from(allTriggeredPoints)
|
||||
.filter(uuid => (graph.get(uuid) || []).length === minTriggerCount);
|
||||
|
||||
rootPoints.push(...potentialRoots.map(uuid => pointMap.get(uuid)!));
|
||||
}
|
||||
|
||||
// Topological sort only for triggered points
|
||||
const visited = new Set<string>();
|
||||
const temp = new Set<string>();
|
||||
const order: string[] = [];
|
||||
let hasCycle = false;
|
||||
|
||||
function visit(node: string) {
|
||||
if (temp.has(node)) {
|
||||
hasCycle = true;
|
||||
return;
|
||||
}
|
||||
if (visited.has(node)) return;
|
||||
|
||||
temp.add(node);
|
||||
|
||||
const dependencies = reverseGraph.get(node) || [];
|
||||
for (const dep of dependencies) {
|
||||
visit(dep);
|
||||
}
|
||||
|
||||
temp.delete(node);
|
||||
visited.add(node);
|
||||
order.push(node);
|
||||
}
|
||||
|
||||
// Start processing from root points
|
||||
rootPoints.forEach(root => visit(root.uuid));
|
||||
|
||||
// Convert UUIDs back to points and filter out untriggered points
|
||||
const triggeredPoints = order
|
||||
.map(uuid => pointMap.get(uuid)!)
|
||||
.filter(point => allTriggeredPoints.has(point.uuid) ||
|
||||
rootPoints.some(root => root.uuid === point.uuid));
|
||||
|
||||
return triggeredPoints;
|
||||
}
|
||||
|
||||
function extractTriggersFromPoint(point: PointsScheme): TriggerSchema[] {
|
||||
if ('actions' in point) {
|
||||
return point.actions.flatMap(action => action.triggers);
|
||||
} else if ('action' in point) {
|
||||
return point.action.triggers;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
return <></>;
|
||||
}
|
||||
|
||||
export default Simulator
|
||||
export default Simulator;
|
|
@ -9,8 +9,8 @@ function VehicleInstances() {
|
|||
return (
|
||||
<>
|
||||
|
||||
{vehicles.map((val: VehicleStatus) =>
|
||||
<VehicleInstance agvDetail={val} key={val.modelUuid} />
|
||||
{vehicles.map((vehicle: VehicleStatus) =>
|
||||
<VehicleInstance agvDetail={vehicle} key={vehicle.modelUuid} />
|
||||
)}
|
||||
|
||||
</>
|
||||
|
|
|
@ -4,16 +4,39 @@ import { immer } from 'zustand/middleware/immer';
|
|||
type MaterialsStore = {
|
||||
materials: MaterialsSchema;
|
||||
|
||||
addMaterial: (material: MaterialSchema) => void;
|
||||
removeMaterial: (materialId: string) => void;
|
||||
updateMaterial: (materialId: string, updates: Partial<MaterialSchema>) => void;
|
||||
addMaterial: (material: MaterialSchema) => MaterialSchema | undefined;
|
||||
removeMaterial: (materialId: string) => MaterialSchema | undefined;
|
||||
updateMaterial: (materialId: string, updates: Partial<MaterialSchema>) => MaterialSchema | undefined;
|
||||
|
||||
setStartTime: (materialId: string, startTime: string) => void;
|
||||
setEndTime: (materialId: string, endTime: string) => void;
|
||||
setCost: (materialId: string, cost: number) => void;
|
||||
setWeight: (materialId: string, weight: number) => void;
|
||||
setCurrentLocation: (
|
||||
materialId: string,
|
||||
location: {
|
||||
modelUuid: string;
|
||||
pointUuid: string;
|
||||
actionUuid: string;
|
||||
}
|
||||
) => MaterialSchema | undefined;
|
||||
|
||||
setNextLocation: (
|
||||
materialId: string,
|
||||
location?: {
|
||||
modelUuid: string;
|
||||
pointUuid: string;
|
||||
actionUuid: string;
|
||||
} | null
|
||||
) => MaterialSchema | undefined;
|
||||
|
||||
setStartTime: (materialId: string, startTime: string) => MaterialSchema | undefined;
|
||||
setEndTime: (materialId: string, endTime: string) => MaterialSchema | undefined;
|
||||
setCost: (materialId: string, cost: number) => MaterialSchema | undefined;
|
||||
setWeight: (materialId: string, weight: number) => MaterialSchema | undefined;
|
||||
setIsActive: (materialId: string, isActive: boolean) => MaterialSchema | undefined;
|
||||
setIsVisible: (materialId: string, isVisible: boolean) => MaterialSchema | undefined;
|
||||
setIsRendered: (materialId: string, isRendered: boolean) => MaterialSchema | undefined;
|
||||
|
||||
getMaterialById: (materialId: string) => MaterialSchema | undefined;
|
||||
getMaterialsByPoint: (pointUuid: string) => MaterialSchema[];
|
||||
getMaterialsByModel: (modelUuid: string) => MaterialSchema[];
|
||||
};
|
||||
|
||||
export const useMaterialStore = create<MaterialsStore>()(
|
||||
|
@ -21,56 +44,161 @@ export const useMaterialStore = create<MaterialsStore>()(
|
|||
materials: [],
|
||||
|
||||
addMaterial: (material) => {
|
||||
let updatedMaterial: MaterialSchema | undefined;
|
||||
set((state) => {
|
||||
state.materials.push(material);
|
||||
});
|
||||
return updatedMaterial;
|
||||
},
|
||||
|
||||
removeMaterial: (materialId) => {
|
||||
let updatedMaterial: MaterialSchema | undefined;
|
||||
set((state) => {
|
||||
state.materials = state.materials.filter(m => m.materialId !== materialId);
|
||||
const material = state.materials.find(m => m.materialId === materialId);
|
||||
if (material) {
|
||||
state.materials.filter(m => m.materialId !== material.materialId);
|
||||
updatedMaterial = JSON.parse(JSON.stringify(material));
|
||||
}
|
||||
});
|
||||
return updatedMaterial;
|
||||
},
|
||||
|
||||
updateMaterial: (materialId, updates) => {
|
||||
let updatedMaterial: MaterialSchema | undefined;
|
||||
set((state) => {
|
||||
const material = state.materials.find(m => m.materialId === materialId);
|
||||
if (material) {
|
||||
Object.assign(material, updates);
|
||||
updatedMaterial = JSON.parse(JSON.stringify(material));
|
||||
}
|
||||
});
|
||||
return updatedMaterial;
|
||||
},
|
||||
|
||||
setCurrentLocation: (materialId, location) => {
|
||||
let updatedMaterial: MaterialSchema | undefined;
|
||||
set((state) => {
|
||||
const material = state.materials.find(m => m.materialId === materialId);
|
||||
if (material) {
|
||||
material.current = location;
|
||||
updatedMaterial = JSON.parse(JSON.stringify(material));
|
||||
}
|
||||
});
|
||||
return updatedMaterial;
|
||||
},
|
||||
|
||||
setNextLocation: (materialId, location) => {
|
||||
let updatedMaterial: MaterialSchema | undefined;
|
||||
set((state) => {
|
||||
const material = state.materials.find(m => m.materialId === materialId);
|
||||
if (material) {
|
||||
material.next = location || undefined;
|
||||
updatedMaterial = JSON.parse(JSON.stringify(material));
|
||||
}
|
||||
});
|
||||
return updatedMaterial;
|
||||
},
|
||||
|
||||
setStartTime: (materialId, startTime) => {
|
||||
let updatedMaterial: MaterialSchema | undefined;
|
||||
set((state) => {
|
||||
const material = state.materials.find(m => m.materialId === materialId);
|
||||
if (material) material.startTime = startTime;
|
||||
if (material) {
|
||||
material.startTime = startTime
|
||||
updatedMaterial = JSON.parse(JSON.stringify(material));
|
||||
};
|
||||
});
|
||||
return updatedMaterial;
|
||||
},
|
||||
|
||||
setEndTime: (materialId, endTime) => {
|
||||
let updatedMaterial: MaterialSchema | undefined;
|
||||
set((state) => {
|
||||
const material = state.materials.find(m => m.materialId === materialId);
|
||||
if (material) material.endTime = endTime;
|
||||
if (material) {
|
||||
material.endTime = endTime;
|
||||
updatedMaterial = JSON.parse(JSON.stringify(material));
|
||||
};
|
||||
});
|
||||
return updatedMaterial;
|
||||
},
|
||||
|
||||
setCost: (materialId, cost) => {
|
||||
let updatedMaterial: MaterialSchema | undefined;
|
||||
set((state) => {
|
||||
const material = state.materials.find(m => m.materialId === materialId);
|
||||
if (material) material.cost = cost;
|
||||
if (material) {
|
||||
material.cost = cost;
|
||||
updatedMaterial = JSON.parse(JSON.stringify(material));
|
||||
};
|
||||
});
|
||||
return updatedMaterial;
|
||||
},
|
||||
|
||||
setWeight: (materialId, weight) => {
|
||||
let updatedMaterial: MaterialSchema | undefined;
|
||||
set((state) => {
|
||||
const material = state.materials.find(m => m.materialId === materialId);
|
||||
if (material) material.weight = weight;
|
||||
if (material) {
|
||||
material.weight = weight;
|
||||
updatedMaterial = JSON.parse(JSON.stringify(material));
|
||||
};
|
||||
});
|
||||
return updatedMaterial;
|
||||
},
|
||||
|
||||
setIsActive: (materialId, isActive) => {
|
||||
let updatedMaterial: MaterialSchema | undefined;
|
||||
set((state) => {
|
||||
const material = state.materials.find(m => m.materialId === materialId);
|
||||
if (material) {
|
||||
material.isActive = isActive;
|
||||
updatedMaterial = JSON.parse(JSON.stringify(material));
|
||||
};
|
||||
});
|
||||
return updatedMaterial;
|
||||
},
|
||||
|
||||
setIsVisible: (materialId, isVisible) => {
|
||||
let updatedMaterial: MaterialSchema | undefined;
|
||||
set((state) => {
|
||||
const material = state.materials.find(m => m.materialId === materialId);
|
||||
if (material) {
|
||||
material.isVisible = isVisible;
|
||||
updatedMaterial = JSON.parse(JSON.stringify(material));
|
||||
};
|
||||
});
|
||||
return updatedMaterial;
|
||||
},
|
||||
|
||||
setIsRendered: (materialId, isRendered) => {
|
||||
let updatedMaterial: MaterialSchema | undefined;
|
||||
set((state) => {
|
||||
const material = state.materials.find(m => m.materialId === materialId);
|
||||
if (material) {
|
||||
material.isRendered = isRendered;
|
||||
updatedMaterial = JSON.parse(JSON.stringify(material));
|
||||
};
|
||||
});
|
||||
return updatedMaterial;
|
||||
},
|
||||
|
||||
getMaterialById: (materialId) => {
|
||||
return get().materials.find(m => m.materialId === materialId);
|
||||
},
|
||||
|
||||
getMaterialsByPoint: (pointUuid) => {
|
||||
return get().materials.filter(m =>
|
||||
m.current?.pointUuid === pointUuid ||
|
||||
m.next?.pointUuid === pointUuid
|
||||
);
|
||||
},
|
||||
|
||||
getMaterialsByModel: (modelUuid) => {
|
||||
return get().materials.filter(m =>
|
||||
m.current?.modelUuid === modelUuid ||
|
||||
m.next?.modelUuid === modelUuid
|
||||
);
|
||||
},
|
||||
}))
|
||||
);
|
||||
);
|
|
@ -63,6 +63,7 @@ type ProductsStore = {
|
|||
getEventByModelUuid: (productId: string, modelUuid: string) => EventsSchema | undefined;
|
||||
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;
|
||||
getTriggerByUuid: (productId: string, triggerUuid: string) => TriggerSchema | undefined;
|
||||
getIsEventInProduct: (productId: string, modelUuid: string) => boolean;
|
||||
};
|
||||
|
@ -573,6 +574,30 @@ export const useProductStore = create<ProductsStore>()(
|
|||
return undefined;
|
||||
},
|
||||
|
||||
getModelUuidByActionUuid: (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 event.modelUuid;
|
||||
}
|
||||
}
|
||||
} else if ('point' in event) {
|
||||
const point = (event as any).point;
|
||||
if ('action' in point && point.action?.actionUuid === actionUuid) {
|
||||
return event.modelUuid;
|
||||
} else if ('actions' in point) {
|
||||
const action = point.actions.find((a: any) => a.actionUuid === actionUuid);
|
||||
if (action) return event.modelUuid;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
getTriggerByUuid: (productId, triggerUuid) => {
|
||||
const product = get().products.find(p => p.productId === productId);
|
||||
if (!product) return undefined;
|
||||
|
|
|
@ -27,9 +27,9 @@ interface ConveyorPointSchema {
|
|||
actionName: string;
|
||||
actionType: "default" | "spawn" | "swap" | "delay" | "despawn";
|
||||
material: string;
|
||||
delay: number | "inherit";
|
||||
spawnInterval: number | "inherit";
|
||||
spawnCount: number | "inherit";
|
||||
delay: number;
|
||||
spawnInterval: number;
|
||||
spawnCount: number;
|
||||
triggers: TriggerSchema[];
|
||||
};
|
||||
}
|
||||
|
@ -180,13 +180,27 @@ interface StorageUnitStatus extends StorageEventSchema {
|
|||
|
||||
interface MaterialSchema {
|
||||
materialId: string;
|
||||
materialName: string;
|
||||
materialName: stri9ng;
|
||||
materialType: string;
|
||||
isActive: boolean;
|
||||
isVisible: boolean;
|
||||
isRendered: boolean;
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
cost?: number;
|
||||
weight?: number;
|
||||
|
||||
current: {
|
||||
modelUuid: string;
|
||||
pointUuid: string;
|
||||
actionUuid: string;
|
||||
};
|
||||
|
||||
next?: {
|
||||
modelUuid: string;
|
||||
pointUuid: string;
|
||||
actionUuid: string;
|
||||
};
|
||||
}
|
||||
|
||||
type MaterialsSchema = MaterialSchema[];
|
Loading…
Reference in New Issue