From 6cad8c8a24629b0ebee0b94b27247beec8a8f962 Mon Sep 17 00:00:00 2001 From: Abhinav Mishra Date: Sat, 21 Mar 2026 19:52:05 +0530 Subject: [PATCH] fix(frontend): implement dynamic Y-axis scaling for power charts (#723) --- frontend/src/charts/UniversalChart.jsx | 35 +++++++++++++-- .../dashboard/components/UnifiedChart.jsx | 5 ++- .../dashboard/components/unifiedChartUtils.js | 45 +++++++++++++++++++ 3 files changed, 80 insertions(+), 5 deletions(-) diff --git a/frontend/src/charts/UniversalChart.jsx b/frontend/src/charts/UniversalChart.jsx index 809d51be..e9bec7c3 100644 --- a/frontend/src/charts/UniversalChart.jsx +++ b/frontend/src/charts/UniversalChart.jsx @@ -7,8 +7,8 @@ import { getNonStreamTimeDomain } from './timeDomain'; import ChartWrapper from './ChartWrapper'; import { chartPlugins } from './plugins'; import { getVwcAxisBounds } from './VwcChart/vwcAxis'; - -export default function UniversalChart({ data, stream, chartId, measurements, units, axisIds, axisPolicy, startDate, endDate, onResampleChange }) { +import { formatElectricalUnit, formatSensorValue } from "../pages/dashboard/components/unifiedChartUtils"; +export default function UniversalChart({ data, stream, chartId, sensorName, measurements, units, axisIds, axisPolicy, startDate, endDate, onResampleChange }) { // Build chart options dynamically based on measurements const buildChartOptions = () => { const nonStreamXDomain = getNonStreamTimeDomain(stream, startDate, endDate); @@ -86,6 +86,13 @@ export default function UniversalChart({ data, stream, chartId, measurements, un max: leftYMax, }), }; + scales.y.ticks = scales.y.ticks || {}; + scales.y.ticks.callback = function(value) { + if (sensorName === 'POWER_VOLTAGE' || sensorName === 'POWER_CURRENT') { + return formatElectricalUnit(value, sensorName); + } + return value; + }; } // Handle dual measurements (left and right axes) else if (measurements.length === 2) { @@ -136,12 +143,33 @@ export default function UniversalChart({ data, stream, chartId, measurements, un }; } + const activePlugins = measurements.length > 1 ? structuredClone(chartPlugins) : {}; + + activePlugins.tooltip = { + callbacks: { + label: function(context) { + let label = context.dataset.label || ''; + if (label) { + label += ': '; + } + if (context.parsed.y !== null) { + if (sensorName === 'POWER_VOLTAGE' || sensorName === 'POWER_CURRENT') { + label += formatElectricalUnit(context.parsed.y, sensorName); + } else { + label += context.parsed.y; // Default behavior for other sensors + } + } + return label; + } + } + }; + return { maintainAspectRatio: false, responsive: true, parsing: false, scales, - ...(measurements.length > 1 && { plugins: structuredClone(chartPlugins) }), + plugins: activePlugins, }; }; @@ -160,6 +188,7 @@ UniversalChart.propTypes = { units: PropTypes.arrayOf(PropTypes.string).isRequired, axisIds: PropTypes.arrayOf(PropTypes.string).isRequired, axisPolicy: PropTypes.string, + sensorName: PropTypes.string, startDate: PropTypes.object, endDate: PropTypes.object, onResampleChange: PropTypes.func, diff --git a/frontend/src/pages/dashboard/components/UnifiedChart.jsx b/frontend/src/pages/dashboard/components/UnifiedChart.jsx index f4fe009b..32809ca4 100644 --- a/frontend/src/pages/dashboard/components/UnifiedChart.jsx +++ b/frontend/src/pages/dashboard/components/UnifiedChart.jsx @@ -14,14 +14,14 @@ const CHART_CONFIGS = { power_voltage: { sensor_name: 'POWER_VOLTAGE', measurements: ['Voltage'], - units: ['mV'], + units: ['V'], axisIds: ['y'], chartId: 'powerVoltage', }, power_current: { sensor_name: 'POWER_CURRENT', measurements: ['Current'], - units: ['uA'], + units: ['A'], axisIds: ['y'], chartId: 'powerCurrent', }, @@ -443,6 +443,7 @@ function UnifiedChart({ type, cells, startDate, endDate, stream, liveData, proce data={sensorChartData} stream={stream} chartId={chartId} + sensorName={sensor_name} measurements={measurements} units={units} axisIds={axisIds} diff --git a/frontend/src/pages/dashboard/components/unifiedChartUtils.js b/frontend/src/pages/dashboard/components/unifiedChartUtils.js index 9b746182..2557f0f2 100644 --- a/frontend/src/pages/dashboard/components/unifiedChartUtils.js +++ b/frontend/src/pages/dashboard/components/unifiedChartUtils.js @@ -58,3 +58,48 @@ export function normalizeUnifiedStreamValue(sensorName, measurementLabel, value) } return value; } + +export const formatElectricalUnit = (value, type) => { + if (value === null || value === undefined) return 'N/A'; + + const absVal = Math.abs(value); + + if (type === 'POWER_CURRENT') { + if (absVal < 0.001) return `${(value * 1000000).toFixed(3)} µA`; + if (absVal < 1) return `${(value * 1000).toFixed(3)} mA`; + return `${value.toFixed(3)} A`; + } + + if (type === 'POWER_VOLTAGE') { + if (absVal < 1) return `${(value * 1000).toFixed(3)} mV`; + return `${value.toFixed(3)} V`; + } + + return typeof value === 'number' ? value.toFixed(3) : value; +}; + +/** + * Dynamically scales and formats sensor values based on their magnitude. + * @param {number} value - The raw decimal value from the backend. + * @param {string} type - The sensor type (e.g., 'POWER_VOLTAGE', 'POWER_CURRENT'). + * @returns {string} The formatted value with appropriate units. + */ +export const formatSensorValue = (value, type) => { + if (value === null || value === undefined) return '-'; + + const absValue = Math.abs(value); + + if (type === 'POWER_CURRENT') { + if (absValue < 0.001) return `${(value * 1000000).toFixed(2)} µA`; + if (absValue < 1) return `${(value * 1000).toFixed(2)} mA`; + return `${value.toFixed(2)} A`; + } + + if (type === 'POWER_VOLTAGE') { + if (absValue < 1) return `${(value * 1000).toFixed(2)} mV`; + return `${value.toFixed(2)} V`; + } + + // Fallback for other sensor types (just limit decimals) + return typeof value === 'number' ? value.toFixed(2) : value; +}; \ No newline at end of file