From 62277e22c1f5d5141b0c7502a3ef6d93aa93eaf9 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Wed, 17 Dec 2025 13:04:09 +0530 Subject: [PATCH] feat: Add data type, common value, data value, and data source updates in Dashboard and Element Editors --- .../SimulationDashboard/DashboardEditor.tsx | 32 +++ .../components/element/ElementEditor.tsx | 257 +++++++++++++++--- .../simulation/useSimulationDashBoardStore.ts | 52 ++++ 3 files changed, 307 insertions(+), 34 deletions(-) diff --git a/app/src/components/SimulationDashboard/DashboardEditor.tsx b/app/src/components/SimulationDashboard/DashboardEditor.tsx index 582bece..f0e79f1 100644 --- a/app/src/components/SimulationDashboard/DashboardEditor.tsx +++ b/app/src/components/SimulationDashboard/DashboardEditor.tsx @@ -56,6 +56,10 @@ const DashboardEditor: React.FC = () => { peekUpdateGraphData, peekUpdateGraphTitle, peekUpdateGraphType, + peekUpdateDataType, + peekUpdateCommonValue, + peekUpdateDataValue, + peekUpdateDataSource, peekSwapElements, } = simulationDashBoardStore(); @@ -584,6 +588,34 @@ const DashboardEditor: React.FC = () => { await updateBackend(updatedBlock); } }} + updateDataType={async (blockId, elementId, dataType) => { + const updatedBlocks = peekUpdateDataType(blockId, elementId, dataType); + const updatedBlock = getBlockFromPeekedBlocks(updatedBlocks, blockId); + if (updatedBlock) { + await updateBackend(updatedBlock); + } + }} + updateCommonValue={async (blockId, elementId, commonValue) => { + const updatedBlocks = peekUpdateCommonValue(blockId, elementId, commonValue); + const updatedBlock = getBlockFromPeekedBlocks(updatedBlocks, blockId); + if (updatedBlock) { + await updateBackend(updatedBlock); + } + }} + updateDataValue={async (blockId, elementId, dataValue) => { + const updatedBlocks = peekUpdateDataValue(blockId, elementId, dataValue); + const updatedBlock = getBlockFromPeekedBlocks(updatedBlocks, blockId); + if (updatedBlock) { + await updateBackend(updatedBlock); + } + }} + updateDataSource={async (blockId, elementId, dataSource) => { + const updatedBlocks = peekUpdateDataSource(blockId, elementId, dataSource); + const updatedBlock = getBlockFromPeekedBlocks(updatedBlocks, blockId); + if (updatedBlock) { + await updateBackend(updatedBlock); + } + }} handleRemoveElement={async (blockId, elementId) => { const updatedBlocks = peekRemoveElement(blockId, elementId); const updatedBlock = getBlockFromPeekedBlocks(updatedBlocks, blockId); diff --git a/app/src/components/SimulationDashboard/components/element/ElementEditor.tsx b/app/src/components/SimulationDashboard/components/element/ElementEditor.tsx index 946d5f1..517b614 100644 --- a/app/src/components/SimulationDashboard/components/element/ElementEditor.tsx +++ b/app/src/components/SimulationDashboard/components/element/ElementEditor.tsx @@ -1,4 +1,4 @@ -import { useState, type RefObject } from "react"; +import { useCallback, useState, type RefObject } from "react"; import { ExtendedCSSProperties, UIElement } from "../../../../types/exportedTypes"; import { getCurrentElementStyleValue } from "../../functions/helpers/getCurrentElementStyleValue"; import type { DataModelManager } from "../../data/dataModel"; @@ -26,6 +26,10 @@ interface ElementEditorProps { updateGraphData: (blockId: string, elementId: string, newData: GraphDataPoint[]) => void; updateGraphTitle: (blockId: string, elementId: string, title: string) => void; updateGraphType: (blockId: string, elementId: string, type: GraphTypes) => void; + updateDataType: (blockId: string, elementId: string, dataType: "single-machine" | "multiple-machine") => void; + updateCommonValue: (blockId: string, elementId: string, commonValue: string) => void; + updateDataValue: (blockId: string, elementId: string, dataValue: string | string[]) => void; + updateDataSource: (blockId: string, elementId: string, dataSource: string | string[]) => void; handleRemoveElement: (blockId: string, elementId: string) => void; setSwapSource: (source: string | null) => void; setShowSwapUI: (show: boolean) => void; @@ -46,15 +50,19 @@ const ElementEditor: React.FC = ({ updateGraphData, updateGraphTitle, updateGraphType, + updateDataType, + updateCommonValue, + updateDataValue, + updateDataSource, handleRemoveElement, setSwapSource, setShowSwapUI, dataModelManager, }) => { const { simulationDashBoardStore, productStore, analysisStore } = useSceneContext(); - const { products, selectedProduct, getProductById } = productStore(); + const { products, selectedProduct, getProductById, getEventByModelUuid } = productStore(); const { analysis } = analysisStore(); - const product = getProductById(selectedProduct.productName); + const product = getProductById(selectedProduct.productUuid); const { getElementById } = simulationDashBoardStore(); const element = getElementById(selectedBlock, selectedElement); const [selectType, setSelectType] = useState("design"); @@ -72,6 +80,198 @@ const ElementEditor: React.FC = ({ { id: "data-source-2", label: "Data Source" }, ]); + // Get asset dropdown items from product + const getAssetDropdownItems = useCallback(() => { + if (!product?.eventDatas) return []; + + return product.eventDatas.map((asset) => ({ + id: asset.modelUuid, + label: asset.modelName, + icon: , + })); + }, [product?.eventDatas]); + + const getLableValueDropdownItems = useCallback( + (assetId: string | undefined) => { + if (!product || !assetId) return []; + + // Global system metrics (available for all assets) + const globalItems = [ + { + title: "System Performance", + items: [ + { id: "global.systemPerformance.overallOEE", label: "Overall System OEE", icon: }, + { id: "global.systemPerformance.systemThroughput", label: "System Throughput", icon: }, + { id: "global.systemPerformance.systemUtilization", label: "System Utilization", icon: }, + { id: "global.materialFlow.totalMaterialsInSystem", label: "Total Materials in System", icon: }, + { id: "global.materialFlow.materialsCompleted", label: "Materials Completed", icon: }, + { id: "global.materialFlow.averageResidenceTime", label: "Average Residence Time", icon: }, + ], + }, + { + title: "Critical System Metrics", + items: [ + { id: "global.systemPerformance.criticalMetrics.activeAssets", label: "Active Assets", icon: }, + { id: "global.systemPerformance.criticalMetrics.totalAssets", label: "Total Assets", icon: }, + { id: "global.systemPerformance.criticalMetrics.assetsInError", label: "Assets in Error", icon: }, + { id: "global.systemPerformance.criticalMetrics.assetsIdle", label: "Assets Idle", icon: }, + { id: "global.systemPerformance.criticalMetrics.totalDowntime", label: "Total Downtime", icon: }, + { id: "global.systemPerformance.criticalMetrics.systemUptime", label: "System Uptime", icon: }, + ], + }, + { + title: "Predictive Insights", + items: [ + { id: "global.predictiveInsights.maintenanceAlerts", label: "Maintenance Alerts", icon: }, + { id: "global.predictiveInsights.optimizationOpportunities", label: "Optimization Opportunities", icon: }, + ], + }, + ]; + + if (assetId === "global") { + return [...globalItems]; + } + + const assetAnalysis = product?.eventDatas.find((e) => e.modelUuid === assetId); + if (!assetAnalysis) { + return [...globalItems]; + } + + const assetSpecificItems = []; + + switch (assetAnalysis.type) { + case "transfer": + assetSpecificItems.push({ + title: "Conveyor Metrics", + items: [ + { id: "currentStatus.isActive", label: "Is Active", icon: }, + { id: "currentStatus.isPaused", label: "Is Paused", icon: }, + { id: "currentStatus.speed", label: "Speed", icon: }, + { id: "currentStatus.currentMaterials", label: "Current Materials", icon: }, + { id: "timeMetrics.uptime", label: "Uptime", icon: }, + { id: "timeMetrics.utilizationRate", label: "Utilization Rate", icon: }, + { id: "throughput.itemsPerHour", label: "Items Per Hour", icon: }, + { id: "efficiency.overallEffectiveness", label: "Overall Effectiveness (OEE)", icon: }, + { id: "materialFlow.wip", label: "Work In Progress", icon: }, + { id: "quality.successRate", label: "Success Rate", icon: }, + { id: "costMetrics.operatingCost", label: "Operating Cost", icon: }, + { id: "energyMetrics.energyConsumed", label: "Energy Consumed", icon: }, + ], + }); + break; + case "vehicle": + assetSpecificItems.push({ + title: "Vehicle Metrics", + items: [ + { id: "currentStatus.isActive", label: "Is Active", icon: }, + { id: "currentStatus.currentPhase", label: "Current Phase", icon: }, + { id: "currentStatus.speed", label: "Speed", icon: }, + { id: "currentStatus.currentLoad", label: "Current Load", icon: }, + { id: "currentStatus.distanceTraveled", label: "Distance Traveled", icon: }, + { id: "movementMetrics.distanceTraveled", label: "Total Distance", icon: }, + { id: "movementMetrics.averageSpeedActual", label: "Average Speed", icon: }, + { id: "throughput.tripsCompleted", label: "Trips Completed", icon: }, + { id: "efficiency.loadUtilization", label: "Load Utilization", icon: }, + { id: "quality.onTimeDelivery", label: "On-Time Delivery", icon: }, + { id: "costMetrics.costPerMile", label: "Cost Per Mile", icon: }, + ], + }); + break; + case "roboticArm": + assetSpecificItems.push({ + title: "Robotic Arm Metrics", + items: [ + { id: "currentStatus.isActive", label: "Is Active", icon: }, + { id: "currentStatus.speed", label: "Speed", icon: }, + { id: "timeMetrics.uptime", label: "Uptime", icon: }, + { id: "timeMetrics.utilizationRate", label: "Utilization Rate", icon: }, + { id: "throughput.cyclesCompleted", label: "Cycles Completed", icon: }, + { id: "throughput.pickAndPlaceCount", label: "Pick & Place Count", icon: }, + { id: "efficiency.overallEffectiveness", label: "Overall Effectiveness", icon: }, + { id: "quality.pickSuccessRate", label: "Pick Success Rate", icon: }, + { id: "quality.placeAccuracy", label: "Place Accuracy", icon: }, + { id: "costMetrics.totalCost", label: "Total Cost", icon: }, + ], + }); + break; + case "machine": + assetSpecificItems.push({ + title: "Machine Metrics", + items: [ + { id: "currentStatus.isActive", label: "Is Active", icon: }, + { id: "timeMetrics.uptime", label: "Uptime", icon: }, + { id: "timeMetrics.utilizationRate", label: "Utilization Rate", icon: }, + { id: "throughput.cyclesCompleted", label: "Cycles Completed", icon: }, + { id: "throughput.partsProcessed", label: "Parts Processed", icon: }, + { id: "efficiency.overallEffectiveness", label: "Overall Effectiveness", icon: }, + { id: "quality.defectRate", label: "Defect Rate", icon: }, + { id: "quality.successRate", label: "Success Rate", icon: }, + { id: "quality.reworkRate", label: "Rework Rate", icon: }, + { id: "costMetrics.costPerUnit", label: "Cost Per Unit", icon: }, + { id: "energyMetrics.energyPerUnit", label: "Energy Per Unit", icon: }, + ], + }); + break; + case "storageUnit": + assetSpecificItems.push({ + title: "Storage Metrics", + items: [ + { id: "currentStatus.isActive", label: "Is Active", icon: }, + { id: "currentStatus.currentLoad", label: "Current Load", icon: }, + { id: "currentStatus.storageCapacity", label: "Storage Capacity", icon: }, + { id: "capacityMetrics.utilizationRate", label: "Utilization Rate", icon: }, + { id: "capacityMetrics.averageOccupancy", label: "Average Occupancy", icon: }, + { id: "throughput.storeOperations", label: "Store Operations", icon: }, + { id: "throughput.retrieveOperations", label: "Retrieve Operations", icon: }, + { id: "throughput.totalOperations", label: "Total Operations", icon: }, + { id: "efficiency.spaceUtilization", label: "Space Utilization", icon: }, + { id: "costMetrics.costPerStorageHour", label: "Cost Per Storage Hour", icon: }, + ], + }); + break; + case "human": + assetSpecificItems.push({ + title: "Human Metrics", + items: [ + { id: "currentStatus.isActive", label: "Is Active", icon: }, + { id: "currentStatus.currentPhase", label: "Current Phase", icon: }, + { id: "currentStatus.speed", label: "Speed", icon: }, + { id: "currentStatus.currentLoad", label: "Current Load", icon: }, + { id: "productivityMetrics.actionsCompleted", label: "Actions Completed", icon: }, + { id: "productivityMetrics.actionsPerHour", label: "Actions Per Hour", icon: }, + { id: "efficiency.laborProductivity", label: "Labor Productivity", icon: }, + { id: "timeMetrics.utilizationRate", label: "Utilization Rate", icon: }, + { id: "workloadDistribution", label: "Workload Distribution", icon: }, + { id: "costMetrics.costPerHour", label: "Cost Per Hour", icon: }, + { id: "productivityMetrics.distanceTraveled", label: "Distance Traveled", icon: }, + ], + }); + break; + case "crane": + assetSpecificItems.push({ + title: "Crane Metrics", + items: [ + { id: "currentStatus.isActive", label: "Is Active", icon: }, + { id: "currentStatus.isCarrying", label: "Is Carrying", icon: }, + { id: "currentStatus.currentLoad", label: "Current Load", icon: }, + { id: "throughput.cyclesCompleted", label: "Cycles Completed", icon: }, + { id: "throughput.loadsHandled", label: "Loads Handled", icon: }, + { id: "movementMetrics.totalLifts", label: "Total Lifts", icon: }, + { id: "efficiency.loadUtilization", label: "Load Utilization", icon: }, + { id: "quality.liftSuccessRate", label: "Lift Success Rate", icon: }, + { id: "quality.positioningAccuracy", label: "Positioning Accuracy", icon: }, + { id: "costMetrics.costPerLift", label: "Cost Per Lift", icon: }, + { id: "energyMetrics.energyPerLift", label: "Energy Per Lift", icon: }, + ], + }); + break; + } + + return assetSpecificItems; + }, + [product] + ); + const addField = () => { if (selectDataMapping === "singleMachine") { setSingleFields((prev) => [ @@ -233,13 +433,13 @@ const ElementEditor: React.FC = ({ onChange={(newValue: string) => updateElementZIndex(selectedBlock, selectedElement, Number(newValue))} /> - {/* updateElementStyle(selectedBlock, selectedElement, { backdropFilter: `blur(${Number(value)}px)` })} - /> */} + updateElementStyle(selectedBlock, selectedElement, { backdropFilter: `blur(${Number(value)}px)` })} + /> = ({ }, { title: "Assets", - items: [ - { id: "cmm-001", label: "CMM-001", icon: }, - { id: "cnc-1", label: "CNC-Lathe-0001", icon: }, - { id: "cnc-2", label: "CNC-drilling-tapping-3Axis", icon: }, - { id: "cnc-3", label: "CNC_0001", icon: }, - ], + items: getAssetDropdownItems(), }, ]} - value={null} - onChange={() => {}} + value={ + element.dataSource + ? { id: element.dataSource, label: getEventByModelUuid(selectedProduct.productUuid, element.dataSource)?.modelName ?? "", icon: } + : null + } + onChange={(value) => { + updateDataSource(selectedBlock, selectedElement, value.id); + }} dropDownHeader={"RT-Data"} eyedroper={true} /> @@ -361,29 +562,17 @@ const ElementEditor: React.FC = ({
}, - { id: "measurementDeviation", label: "measurementDeviation", icon: }, - { id: "powerConsumption", label: "powerConsumption", icon: }, - { id: "probeX", label: "probePositionX", icon: }, - { id: "probeY", label: "probePositionY", icon: }, - { id: "probeZ", label: "probePositionZ", icon: }, - ], - }, - ]} - value={null} + sections={getLableValueDropdownItems(element.dataSource as string | undefined)} + value={element.dataValue ? { id: element.dataValue, label: element.dataValue, icon: } : null} onChange={() => {}} dropDownHeader={"RT-Data-Value"} />
)} - <>{console.log(element)} + {/* Data Mapping */} {element?.type === "graph" && (
diff --git a/app/src/store/simulation/useSimulationDashBoardStore.ts b/app/src/store/simulation/useSimulationDashBoardStore.ts index b67eeda..f3e5d96 100644 --- a/app/src/store/simulation/useSimulationDashBoardStore.ts +++ b/app/src/store/simulation/useSimulationDashBoardStore.ts @@ -74,6 +74,10 @@ interface SimulationDashboardStore { peekUpdateGraphData: (blockId: string, elementId: string, newData: GraphDataPoint[]) => Block[]; peekUpdateGraphTitle: (blockId: string, elementId: string, title: string) => Block[]; peekUpdateGraphType: (blockId: string, elementId: string, graphType: GraphTypes) => Block[]; + peekUpdateDataType: (blockId: string, elementId: string, dataType: "single-machine" | "multiple-machine") => Block[]; + peekUpdateCommonValue: (blockId: string, elementId: string, commonValue: string) => Block[]; + peekUpdateDataValue: (blockId: string, elementId: string, dataValue: string | string[]) => Block[]; + peekUpdateDataSource: (blockId: string, elementId: string, dataSource: string | string[]) => Block[]; peekSwapElements: (blockId: string, elementId1: string, elementId2: string) => Block[]; // Helper functions @@ -847,6 +851,54 @@ export const createSimulationDashboardStore = () => { return blocks; }, + peekUpdateDataType: (blockId, elementId, dataType) => { + const blocks = cloneBlocks(get().blocks); + const block = blocks.find((b) => b.blockUuid === blockId); + if (block) { + const element = block.elements.find((el) => el.elementUuid === elementId); + if (element && element.type === "graph") { + element.dataType = dataType; + } + } + return blocks; + }, + + peekUpdateCommonValue: (blockId, elementId, commonValue) => { + const blocks = cloneBlocks(get().blocks); + const block = blocks.find((b) => b.blockUuid === blockId); + if (block) { + const element = block.elements.find((el) => el.elementUuid === elementId); + if (element && element.type === "graph" && element.dataType === "multiple-machine") { + element.commonValue = commonValue; + } + } + return blocks; + }, + + peekUpdateDataValue: (blockId, elementId, dataValue) => { + const blocks = cloneBlocks(get().blocks); + const block = blocks.find((b) => b.blockUuid === blockId); + if (block) { + const element = block.elements.find((el) => el.elementUuid === elementId); + if (element && element.type === "graph" && element.dataType === "single-machine") { + element.dataValue = dataValue as string[]; + } + } + return blocks; + }, + + peekUpdateDataSource: (blockId, elementId, dataSource) => { + const blocks = cloneBlocks(get().blocks); + const block = blocks.find((b) => b.blockUuid === blockId); + if (block) { + const element = block.elements.find((el) => el.elementUuid === elementId); + if (element) { + element.dataSource = dataSource; + } + } + return blocks; + }, + peekSwapElements: (blockId, elementId1, elementId2) => { const blocks = cloneBlocks(get().blocks); const block = blocks.find((b) => b.blockUuid === blockId);