feat: Add data type, common value, data value, and data source updates in Dashboard and Element Editors

This commit is contained in:
2025-12-17 13:04:09 +05:30
parent e64ed91784
commit 62277e22c1
3 changed files with 307 additions and 34 deletions

View File

@@ -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);

View File

@@ -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<ElementEditorProps> = ({
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<ElementEditorProps> = ({
{ 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: <DeviceIcon />,
}));
}, [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: <ParametersIcon /> },
{ id: "global.systemPerformance.systemThroughput", label: "System Throughput", icon: <ParametersIcon /> },
{ id: "global.systemPerformance.systemUtilization", label: "System Utilization", icon: <ParametersIcon /> },
{ id: "global.materialFlow.totalMaterialsInSystem", label: "Total Materials in System", icon: <ParametersIcon /> },
{ id: "global.materialFlow.materialsCompleted", label: "Materials Completed", icon: <ParametersIcon /> },
{ id: "global.materialFlow.averageResidenceTime", label: "Average Residence Time", icon: <ParametersIcon /> },
],
},
{
title: "Critical System Metrics",
items: [
{ id: "global.systemPerformance.criticalMetrics.activeAssets", label: "Active Assets", icon: <ParametersIcon /> },
{ id: "global.systemPerformance.criticalMetrics.totalAssets", label: "Total Assets", icon: <ParametersIcon /> },
{ id: "global.systemPerformance.criticalMetrics.assetsInError", label: "Assets in Error", icon: <ParametersIcon /> },
{ id: "global.systemPerformance.criticalMetrics.assetsIdle", label: "Assets Idle", icon: <ParametersIcon /> },
{ id: "global.systemPerformance.criticalMetrics.totalDowntime", label: "Total Downtime", icon: <ParametersIcon /> },
{ id: "global.systemPerformance.criticalMetrics.systemUptime", label: "System Uptime", icon: <ParametersIcon /> },
],
},
{
title: "Predictive Insights",
items: [
{ id: "global.predictiveInsights.maintenanceAlerts", label: "Maintenance Alerts", icon: <ParametersIcon /> },
{ id: "global.predictiveInsights.optimizationOpportunities", label: "Optimization Opportunities", icon: <ParametersIcon /> },
],
},
];
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: <ParametersIcon /> },
{ id: "currentStatus.isPaused", label: "Is Paused", icon: <ParametersIcon /> },
{ id: "currentStatus.speed", label: "Speed", icon: <ParametersIcon /> },
{ id: "currentStatus.currentMaterials", label: "Current Materials", icon: <ParametersIcon /> },
{ id: "timeMetrics.uptime", label: "Uptime", icon: <ParametersIcon /> },
{ id: "timeMetrics.utilizationRate", label: "Utilization Rate", icon: <ParametersIcon /> },
{ id: "throughput.itemsPerHour", label: "Items Per Hour", icon: <ParametersIcon /> },
{ id: "efficiency.overallEffectiveness", label: "Overall Effectiveness (OEE)", icon: <ParametersIcon /> },
{ id: "materialFlow.wip", label: "Work In Progress", icon: <ParametersIcon /> },
{ id: "quality.successRate", label: "Success Rate", icon: <ParametersIcon /> },
{ id: "costMetrics.operatingCost", label: "Operating Cost", icon: <ParametersIcon /> },
{ id: "energyMetrics.energyConsumed", label: "Energy Consumed", icon: <ParametersIcon /> },
],
});
break;
case "vehicle":
assetSpecificItems.push({
title: "Vehicle Metrics",
items: [
{ id: "currentStatus.isActive", label: "Is Active", icon: <ParametersIcon /> },
{ id: "currentStatus.currentPhase", label: "Current Phase", icon: <ParametersIcon /> },
{ id: "currentStatus.speed", label: "Speed", icon: <ParametersIcon /> },
{ id: "currentStatus.currentLoad", label: "Current Load", icon: <ParametersIcon /> },
{ id: "currentStatus.distanceTraveled", label: "Distance Traveled", icon: <ParametersIcon /> },
{ id: "movementMetrics.distanceTraveled", label: "Total Distance", icon: <ParametersIcon /> },
{ id: "movementMetrics.averageSpeedActual", label: "Average Speed", icon: <ParametersIcon /> },
{ id: "throughput.tripsCompleted", label: "Trips Completed", icon: <ParametersIcon /> },
{ id: "efficiency.loadUtilization", label: "Load Utilization", icon: <ParametersIcon /> },
{ id: "quality.onTimeDelivery", label: "On-Time Delivery", icon: <ParametersIcon /> },
{ id: "costMetrics.costPerMile", label: "Cost Per Mile", icon: <ParametersIcon /> },
],
});
break;
case "roboticArm":
assetSpecificItems.push({
title: "Robotic Arm Metrics",
items: [
{ id: "currentStatus.isActive", label: "Is Active", icon: <ParametersIcon /> },
{ id: "currentStatus.speed", label: "Speed", icon: <ParametersIcon /> },
{ id: "timeMetrics.uptime", label: "Uptime", icon: <ParametersIcon /> },
{ id: "timeMetrics.utilizationRate", label: "Utilization Rate", icon: <ParametersIcon /> },
{ id: "throughput.cyclesCompleted", label: "Cycles Completed", icon: <ParametersIcon /> },
{ id: "throughput.pickAndPlaceCount", label: "Pick & Place Count", icon: <ParametersIcon /> },
{ id: "efficiency.overallEffectiveness", label: "Overall Effectiveness", icon: <ParametersIcon /> },
{ id: "quality.pickSuccessRate", label: "Pick Success Rate", icon: <ParametersIcon /> },
{ id: "quality.placeAccuracy", label: "Place Accuracy", icon: <ParametersIcon /> },
{ id: "costMetrics.totalCost", label: "Total Cost", icon: <ParametersIcon /> },
],
});
break;
case "machine":
assetSpecificItems.push({
title: "Machine Metrics",
items: [
{ id: "currentStatus.isActive", label: "Is Active", icon: <ParametersIcon /> },
{ id: "timeMetrics.uptime", label: "Uptime", icon: <ParametersIcon /> },
{ id: "timeMetrics.utilizationRate", label: "Utilization Rate", icon: <ParametersIcon /> },
{ id: "throughput.cyclesCompleted", label: "Cycles Completed", icon: <ParametersIcon /> },
{ id: "throughput.partsProcessed", label: "Parts Processed", icon: <ParametersIcon /> },
{ id: "efficiency.overallEffectiveness", label: "Overall Effectiveness", icon: <ParametersIcon /> },
{ id: "quality.defectRate", label: "Defect Rate", icon: <ParametersIcon /> },
{ id: "quality.successRate", label: "Success Rate", icon: <ParametersIcon /> },
{ id: "quality.reworkRate", label: "Rework Rate", icon: <ParametersIcon /> },
{ id: "costMetrics.costPerUnit", label: "Cost Per Unit", icon: <ParametersIcon /> },
{ id: "energyMetrics.energyPerUnit", label: "Energy Per Unit", icon: <ParametersIcon /> },
],
});
break;
case "storageUnit":
assetSpecificItems.push({
title: "Storage Metrics",
items: [
{ id: "currentStatus.isActive", label: "Is Active", icon: <ParametersIcon /> },
{ id: "currentStatus.currentLoad", label: "Current Load", icon: <ParametersIcon /> },
{ id: "currentStatus.storageCapacity", label: "Storage Capacity", icon: <ParametersIcon /> },
{ id: "capacityMetrics.utilizationRate", label: "Utilization Rate", icon: <ParametersIcon /> },
{ id: "capacityMetrics.averageOccupancy", label: "Average Occupancy", icon: <ParametersIcon /> },
{ id: "throughput.storeOperations", label: "Store Operations", icon: <ParametersIcon /> },
{ id: "throughput.retrieveOperations", label: "Retrieve Operations", icon: <ParametersIcon /> },
{ id: "throughput.totalOperations", label: "Total Operations", icon: <ParametersIcon /> },
{ id: "efficiency.spaceUtilization", label: "Space Utilization", icon: <ParametersIcon /> },
{ id: "costMetrics.costPerStorageHour", label: "Cost Per Storage Hour", icon: <ParametersIcon /> },
],
});
break;
case "human":
assetSpecificItems.push({
title: "Human Metrics",
items: [
{ id: "currentStatus.isActive", label: "Is Active", icon: <ParametersIcon /> },
{ id: "currentStatus.currentPhase", label: "Current Phase", icon: <ParametersIcon /> },
{ id: "currentStatus.speed", label: "Speed", icon: <ParametersIcon /> },
{ id: "currentStatus.currentLoad", label: "Current Load", icon: <ParametersIcon /> },
{ id: "productivityMetrics.actionsCompleted", label: "Actions Completed", icon: <ParametersIcon /> },
{ id: "productivityMetrics.actionsPerHour", label: "Actions Per Hour", icon: <ParametersIcon /> },
{ id: "efficiency.laborProductivity", label: "Labor Productivity", icon: <ParametersIcon /> },
{ id: "timeMetrics.utilizationRate", label: "Utilization Rate", icon: <ParametersIcon /> },
{ id: "workloadDistribution", label: "Workload Distribution", icon: <ParametersIcon /> },
{ id: "costMetrics.costPerHour", label: "Cost Per Hour", icon: <ParametersIcon /> },
{ id: "productivityMetrics.distanceTraveled", label: "Distance Traveled", icon: <ParametersIcon /> },
],
});
break;
case "crane":
assetSpecificItems.push({
title: "Crane Metrics",
items: [
{ id: "currentStatus.isActive", label: "Is Active", icon: <ParametersIcon /> },
{ id: "currentStatus.isCarrying", label: "Is Carrying", icon: <ParametersIcon /> },
{ id: "currentStatus.currentLoad", label: "Current Load", icon: <ParametersIcon /> },
{ id: "throughput.cyclesCompleted", label: "Cycles Completed", icon: <ParametersIcon /> },
{ id: "throughput.loadsHandled", label: "Loads Handled", icon: <ParametersIcon /> },
{ id: "movementMetrics.totalLifts", label: "Total Lifts", icon: <ParametersIcon /> },
{ id: "efficiency.loadUtilization", label: "Load Utilization", icon: <ParametersIcon /> },
{ id: "quality.liftSuccessRate", label: "Lift Success Rate", icon: <ParametersIcon /> },
{ id: "quality.positioningAccuracy", label: "Positioning Accuracy", icon: <ParametersIcon /> },
{ id: "costMetrics.costPerLift", label: "Cost Per Lift", icon: <ParametersIcon /> },
{ id: "energyMetrics.energyPerLift", label: "Energy Per Lift", icon: <ParametersIcon /> },
],
});
break;
}
return assetSpecificItems;
},
[product]
);
const addField = () => {
if (selectDataMapping === "singleMachine") {
setSingleFields((prev) => [
@@ -233,13 +433,13 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
onChange={(newValue: string) => updateElementZIndex(selectedBlock, selectedElement, Number(newValue))}
/>
{/* <InputRange
label={"Blur"}
min={0}
max={40}
value={parseInt(getCurrentElementStyleValue(currentElement, "backdropFilter")?.match(/\d+/)?.[0] || "0")}
onChange={(value: number) => updateElementStyle(selectedBlock, selectedElement, { backdropFilter: `blur(${Number(value)}px)` })}
/> */}
<InputRange
label={"Blur"}
min={0}
max={40}
value={parseInt(getCurrentElementStyleValue(currentElement, "backdropFilter")?.match(/\d+/)?.[0] || "0")}
onChange={(value: number) => updateElementStyle(selectedBlock, selectedElement, { backdropFilter: `blur(${Number(value)}px)` })}
/>
<InputRange
label={"Radius"}
@@ -344,16 +544,17 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
},
{
title: "Assets",
items: [
{ id: "cmm-001", label: "CMM-001", icon: <DeviceIcon /> },
{ id: "cnc-1", label: "CNC-Lathe-0001", icon: <DeviceIcon /> },
{ id: "cnc-2", label: "CNC-drilling-tapping-3Axis", icon: <DeviceIcon /> },
{ id: "cnc-3", label: "CNC_0001", icon: <DeviceIcon /> },
],
items: getAssetDropdownItems(),
},
]}
value={null}
onChange={() => {}}
value={
element.dataSource
? { id: element.dataSource, label: getEventByModelUuid(selectedProduct.productUuid, element.dataSource)?.modelName ?? "", icon: <DeviceIcon /> }
: null
}
onChange={(value) => {
updateDataSource(selectedBlock, selectedElement, value.id);
}}
dropDownHeader={"RT-Data"}
eyedroper={true}
/>
@@ -361,29 +562,17 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
<div className="data">
<DataDetailedDropdown
title="Value"
title="Data Value"
placeholder="Select Value"
sections={[
{
title: "Selected Assets name",
items: [
{ id: "ambientTemp", label: "ambientTemp", icon: <ParametersIcon /> },
{ id: "measurementDeviation", label: "measurementDeviation", icon: <ParametersIcon /> },
{ id: "powerConsumption", label: "powerConsumption", icon: <ParametersIcon /> },
{ id: "probeX", label: "probePositionX", icon: <ParametersIcon /> },
{ id: "probeY", label: "probePositionY", icon: <ParametersIcon /> },
{ id: "probeZ", label: "probePositionZ", icon: <ParametersIcon /> },
],
},
]}
value={null}
sections={getLableValueDropdownItems(element.dataSource as string | undefined)}
value={element.dataValue ? { id: element.dataValue, label: element.dataValue, icon: <ParametersIcon /> } : null}
onChange={() => {}}
dropDownHeader={"RT-Data-Value"}
/>
</div>
</div>
)}
<>{console.log(element)}</>
{/* Data Mapping */}
{element?.type === "graph" && (
<div className="data-mapping">

View File

@@ -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);