From 0cc5f24d2f2cfc859ac97b19555bfd25741e4579 Mon Sep 17 00:00:00 2001 From: Duc Le Date: Tue, 19 Nov 2024 17:25:00 +0000 Subject: [PATCH 1/5] Add formatting of PV values --- app/components/Block.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/components/Block.tsx b/app/components/Block.tsx index e667b61..b37feb1 100644 --- a/app/components/Block.tsx +++ b/app/components/Block.tsx @@ -36,6 +36,19 @@ export default function Block({ pvChangedIndicator.classList.add("text-transparent"); }, 2000); } + var formattedValue = currentValue + if (typeof currentValue === 'string') { + var formattedValue: number = +currentValue + } + if (isNaN(formattedValue)) { + var formattedValue = currentValue + } else { + if (pv.units != null && formattedValue != 0 && (formattedValue < 0.001 || formattedValue > 10000)) { + var formattedValue = formattedValue.toExponential() + } else { + var formattedValue = formattedValue.toPrecision() + } + } const minimum_date_to_be_shown = 631152000; // This is what PVWS thinks epoch time is for some reason. don't bother showing it as the instrument wasn't running EPICS on 01/01/1990 return ( @@ -69,7 +82,7 @@ export default function Block({ className={pv.severity != "NONE" ? "text-red-400" : ""} > {showAdvanced && "Readback: "} - {pv.value} {pv.units != null && pv.units} + {formattedValue} {pv.units != null && pv.units} Date: Tue, 19 Nov 2024 19:01:08 +0000 Subject: [PATCH 2/5] CI bugfixes Fix linting error Move code to separate function Scientific output only on small/large _absolute_ value --- app/components/Block.tsx | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/app/components/Block.tsx b/app/components/Block.tsx index b37feb1..e60ebc7 100644 --- a/app/components/Block.tsx +++ b/app/components/Block.tsx @@ -5,6 +5,19 @@ import React, { useState } from "react"; const grafana_stub = "https://shadow.nd.rl.ac.uk/grafana/d/wMlwwaHMk/block-history?viewPanel=2&orgId=1&var-block="; +function numberFormatter(value: string | number | undefined) { + var nValue: number = value == undefined ? NaN : +value + if (isNaN(nValue)) { + return value + } else { + if (nValue != 0 && (Math.abs(nValue) < 0.001 || Math.abs(nValue) > 10000)) { + return nValue.toExponential() + } else { + return nValue.toPrecision() + } + } +} + export default function Block({ pv, instName, @@ -36,19 +49,6 @@ export default function Block({ pvChangedIndicator.classList.add("text-transparent"); }, 2000); } - var formattedValue = currentValue - if (typeof currentValue === 'string') { - var formattedValue: number = +currentValue - } - if (isNaN(formattedValue)) { - var formattedValue = currentValue - } else { - if (pv.units != null && formattedValue != 0 && (formattedValue < 0.001 || formattedValue > 10000)) { - var formattedValue = formattedValue.toExponential() - } else { - var formattedValue = formattedValue.toPrecision() - } - } const minimum_date_to_be_shown = 631152000; // This is what PVWS thinks epoch time is for some reason. don't bother showing it as the instrument wasn't running EPICS on 01/01/1990 return ( @@ -82,7 +82,7 @@ export default function Block({ className={pv.severity != "NONE" ? "text-red-400" : ""} > {showAdvanced && "Readback: "} - {formattedValue} {pv.units != null && pv.units} + {numberFormatter(pv.value)} {pv.units != null && pv.units} Date: Tue, 19 Nov 2024 19:08:46 +0000 Subject: [PATCH 3/5] Prettier code --- app/components/Block.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/components/Block.tsx b/app/components/Block.tsx index e60ebc7..6adcbeb 100644 --- a/app/components/Block.tsx +++ b/app/components/Block.tsx @@ -6,14 +6,14 @@ const grafana_stub = "https://shadow.nd.rl.ac.uk/grafana/d/wMlwwaHMk/block-history?viewPanel=2&orgId=1&var-block="; function numberFormatter(value: string | number | undefined) { - var nValue: number = value == undefined ? NaN : +value + var nValue: number = value == undefined ? NaN : +value; if (isNaN(nValue)) { - return value + return value; } else { if (nValue != 0 && (Math.abs(nValue) < 0.001 || Math.abs(nValue) > 10000)) { - return nValue.toExponential() + return nValue.toExponential(); } else { - return nValue.toPrecision() + return nValue.toPrecision(); } } } From 09ce0f074f8ca00b0e86b2a4cfb913eba13a9822 Mon Sep 17 00:00:00 2001 From: Duc Le Date: Wed, 20 Nov 2024 20:18:27 +0000 Subject: [PATCH 4/5] Refactor to agree with ibex_gui Move function from Block to PVutils Renamed function to match name in ibex_gui Change to using toPrecision in InstrumentPage (when PV values are updated, not in Block) --- app/components/Block.tsx | 15 +--- app/components/InstrumentPage.tsx | 9 ++- app/components/PVutils.test.ts | 121 ++++++++++++++++++++++++++++++ app/components/PVutils.ts | 27 +++++++ 4 files changed, 155 insertions(+), 17 deletions(-) diff --git a/app/components/Block.tsx b/app/components/Block.tsx index 6adcbeb..e667b61 100644 --- a/app/components/Block.tsx +++ b/app/components/Block.tsx @@ -5,19 +5,6 @@ import React, { useState } from "react"; const grafana_stub = "https://shadow.nd.rl.ac.uk/grafana/d/wMlwwaHMk/block-history?viewPanel=2&orgId=1&var-block="; -function numberFormatter(value: string | number | undefined) { - var nValue: number = value == undefined ? NaN : +value; - if (isNaN(nValue)) { - return value; - } else { - if (nValue != 0 && (Math.abs(nValue) < 0.001 || Math.abs(nValue) > 10000)) { - return nValue.toExponential(); - } else { - return nValue.toPrecision(); - } - } -} - export default function Block({ pv, instName, @@ -82,7 +69,7 @@ export default function Block({ className={pv.severity != "NONE" ? "text-red-400" : ""} > {showAdvanced && "Readback: "} - {numberFormatter(pv.value)} {pv.units != null && pv.units} + {pv.value} {pv.units != null && pv.units} { @@ -46,3 +47,123 @@ test("findPVByHumanReadableName does not find a nonexistant PV and returns undef expect(result).toBe(undefined); }); + +// Test of ExponentialOnThresholdFormat ported from ibex_gui Java code +test("GIVEN value 0.1 which is above lower threshold WHEN formatting with precision 3 THEN no exponential notation used", () => { + expect(ExponentialOnThresholdFormat(0.1)).toBe("0.100"); +}); + +test("GIVEN negative value 0.1 which is above lower threshold WHEN formatting with precision 3 THEN no exponential notation used", () => { + expect(ExponentialOnThresholdFormat(-0.1)).toBe("-0.100"); +}); + +test("GIVEN value 0.01 which is above lower threshold WHEN formatting with precision 3 THEN no exponential notation used", () => { + expect(ExponentialOnThresholdFormat(0.01)).toBe("0.010"); +}); + +test("GIVEN value equal to lower threshold WHEN formatting with precision 3 THEN no exponential notation used", () => { + expect(ExponentialOnThresholdFormat(0.001)).toBe("0.001"); +}); + +test("GIVEN negative value equal to lower threshold WHEN formatting with precision 3 THEN no exponential notation used", () => { + expect(ExponentialOnThresholdFormat(-0.001)).toBe("-0.001"); +}); + +test("GIVEN value above lower threshold with extra fractioanl digits WHEN formatting with precision 3 THEN no exponential notation used and some digits are lost", () => { + expect(ExponentialOnThresholdFormat(0.01234)).toBe("0.012"); +}); + +test("GIVEN value above lower threshold with integer part WHEN formatting with precision 5 THEN no exponential notation used", () => { + expect(ExponentialOnThresholdFormat(1234.56, 5)).toBe("1234.56000"); +}); + +test("GIVEN value above lower threshold with integer part and extra fractional digits WHEN formatting with precision 3 THEN no exponential notation used", () => { + expect(ExponentialOnThresholdFormat(12.5678)).toBe("12.568"); +}); + +test("GIVEN value below lower threshold WHEN formatting with precision 3 THEN exponential notation used", () => { + expect(ExponentialOnThresholdFormat(0.0009)).toBe("9.000E-4"); +}); + +test("GIVEN very small value WHEN formatting with precision 5 THEN exponential notation used", () => { + expect(ExponentialOnThresholdFormat(0.000000001234567, 5)).toBe("1.23457E-9"); +}); + +test("GIVEN very small negative value WHEN formatting with precision 5 THEN exponential notation used", () => { + expect(ExponentialOnThresholdFormat(-0.000000001234567, 5)).toBe( + "-1.23457E-9", + ); +}); + +test("GIVEN value just below higher threshold WHEN formatting with precision 3 THEN no exponential notation used", () => { + expect(ExponentialOnThresholdFormat(999999.1234)).toBe("999999.123"); +}); + +test("GIVEN negative value just below higher threshold WHEN formatting with precision 3 THEN no exponential notation used", () => { + expect(ExponentialOnThresholdFormat(-999999.1234)).toBe("-999999.123"); +}); + +// TypeScript does not have separate integer/float types - for integers never use exponential so change test a bit +test("GIVEN value equal to higher threshold WHEN formatting with precision 3 THEN exponential notation used", () => { + //expect(ExponentialOnThresholdFormat(1000000)).toBe("1.000E6"); + expect(ExponentialOnThresholdFormat(1000000.1)).toBe("1.000E6"); +}); + +test("GIVEN negative value equal to higher threshold WHEN formatting with precision 3 THEN exponential notation used", () => { + expect(ExponentialOnThresholdFormat(-1000000.1)).toBe("-1.000E6"); +}); + +test("GIVEN value above higher threshold WHEN formatting with precision 5 THEN exponential notation used", () => { + //expect(ExponentialOnThresholdFormat(123456789, 5)).toBe("1.23457E8"); + expect(ExponentialOnThresholdFormat(123456789.1, 5)).toBe("1.23457E8"); +}); + +test("GIVEN negative value above higher threshold WHEN formatting with precision 5 THEN exponential notation used", () => { + //expect(ExponentialOnThresholdFormat(-123456789, 5)).toBe("-1.23457E8"); + expect(ExponentialOnThresholdFormat(-123456789.1, 5)).toBe("-1.23457E8"); +}); + +// TypeScript does not have separate integer/float types - so ignore next two tests. +//it("GIVEN value 0 WHEN formatting THEN no exponential notation used", () => { +// expect(ExponentialOnThresholdFormat(0)).toBe("0.000"); +//}); +// +//it("GIVEN negative value 0 WHEN formatting THEN no exponential notation used", () => { +// expect(ExponentialOnThresholdFormat(-0)).toBe("0.000"); +//}); + +test("GIVEN integer below higher threshold WHEN formatting THEN no exponential notation used", () => { + expect(ExponentialOnThresholdFormat(13)).toBe("13"); +}); + +test("GIVEN negative integer below higher threshold WHEN formatting THEN no exponential notation used", () => { + expect(ExponentialOnThresholdFormat(-13)).toBe("-13"); +}); + +test("GIVEN integer just below higher threshold WHEN formatting THEN no exponential notation used", () => { + expect(ExponentialOnThresholdFormat(999999)).toBe("999999"); +}); + +test("GIVEN integer equal to higher threshold WHEN formatting THEN no exponential notation used", () => { + expect(ExponentialOnThresholdFormat(1000000)).toBe("1000000"); +}); + +test("GIVEN negative integer equal to higher threshold WHEN formatting THEN no exponential notation used", () => { + expect(ExponentialOnThresholdFormat(-1000000)).toBe("-1000000"); +}); + +test("GIVEN integer above higher threshold WHEN formatting THEN no exponential notation used", () => { + expect(ExponentialOnThresholdFormat(123456789)).toBe("123456789"); +}); + +test("GIVEN negative integer above higher threshold WHEN formatting THEN no exponential notation used", () => { + expect(ExponentialOnThresholdFormat(-123456789)).toBe("-123456789"); +}); + +test("GIVEN integer equal to 0 WHEN formatting THEN no exponential notation used", () => { + expect(ExponentialOnThresholdFormat(0)).toBe("0"); +}); + +test("GIVEN negative integer equal to 0 WHEN formatting THEN no exponential notation used", () => { + expect(ExponentialOnThresholdFormat(-0)).toBe("0"); +}); diff --git a/app/components/PVutils.ts b/app/components/PVutils.ts index d231121..7600649 100644 --- a/app/components/PVutils.ts +++ b/app/components/PVutils.ts @@ -19,3 +19,30 @@ export function findPVByAddress( ): IfcPV | undefined { return arr.find((b: IfcPV) => b.pvaddress == address); } + +/** + * Formats a given PV value input such that above or below a threshold value, + * it uses scientific (exponential) notation, matching behaviour of ibex_gui + */ +export function ExponentialOnThresholdFormat( + value: string | number, + precision: number = 3, +) { + var nValue: number = value == undefined ? NaN : +value; + if (isNaN(nValue)) { + return value; + } else { + if (nValue == 0) { + return "0"; + } else if (Number.isInteger(nValue)) { + return nValue.toString(); + } else if (Math.abs(nValue) < 0.001 || Math.abs(nValue) >= 1000000) { + return nValue + .toExponential(precision) + .replace("e+", "E") + .replace("e", "E"); + } else { + return nValue.toFixed(precision); + } + } +} From 63fe2643df4a8309f4350c67f0c899cfbe813355 Mon Sep 17 00:00:00 2001 From: Duc Le Date: Wed, 20 Nov 2024 20:24:02 +0000 Subject: [PATCH 5/5] Changed toPrecision test to use toFixed Despite the bad name, this is to agree with the Java ibex_gui code which uses fixed precision (N decimal places, rather than N significant figures). --- app/components/InstrumentPage.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/InstrumentPage.test.ts b/app/components/InstrumentPage.test.ts index 90c200d..e7630ee 100644 --- a/app/components/InstrumentPage.test.ts +++ b/app/components/InstrumentPage.test.ts @@ -49,7 +49,7 @@ test("toPrecision truncates block if it has precision", () => { precision: precision, }; expect(toPrecision(aBlock, originalValue)).toBe( - originalValue.toPrecision(precision), + originalValue.toFixed(precision), ); });