[Plots] Gracefully handle Float32Array breaking values (#7138)

* WIP

* guaranteeing float32breaking values for swgs when option is set

* cleaning up and clarity

* more clarity

* removing randomization of float breaking number, as it is not necessary

* logging the values that could not be plotted for awareness

* remving auto-added imports

---------

Co-authored-by: Jesse Mazzella <ozyx@users.noreply.github.com>
This commit is contained in:
Jamie V 2023-10-24 16:07:43 -07:00 committed by GitHub
parent 7bf983210c
commit d94fe8806b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 99 additions and 17 deletions

View File

@ -29,7 +29,8 @@ define(['./WorkerInterface'], function (WorkerInterface) {
randomness: 0, randomness: 0,
phase: 0, phase: 0,
loadDelay: 0, loadDelay: 0,
infinityValues: false infinityValues: false,
exceedFloat32: false
}; };
function GeneratorProvider(openmct, StalenessProvider) { function GeneratorProvider(openmct, StalenessProvider) {
@ -53,7 +54,8 @@ define(['./WorkerInterface'], function (WorkerInterface) {
'randomness', 'randomness',
'phase', 'phase',
'loadDelay', 'loadDelay',
'infinityValues' 'infinityValues',
'exceedFloat32'
]; ];
request = request || {}; request = request || {};

View File

@ -85,7 +85,8 @@
data.offset, data.offset,
data.phase, data.phase,
data.randomness, data.randomness,
data.infinityValues data.infinityValues,
data.exceedFloat32
), ),
wavelengths: wavelengths(), wavelengths: wavelengths(),
intensities: intensities(), intensities: intensities(),
@ -96,7 +97,8 @@
data.offset, data.offset,
data.phase, data.phase,
data.randomness, data.randomness,
data.infinityValues data.infinityValues,
data.exceedFloat32
) )
} }
}); });
@ -136,6 +138,7 @@
var randomness = request.randomness; var randomness = request.randomness;
var loadDelay = Math.max(request.loadDelay, 0); var loadDelay = Math.max(request.loadDelay, 0);
var infinityValues = request.infinityValues; var infinityValues = request.infinityValues;
var exceedFloat32 = request.exceedFloat32;
var step = 1000 / dataRateInHz; var step = 1000 / dataRateInHz;
var nextStep = start - (start % step) + step; var nextStep = start - (start % step) + step;
@ -146,10 +149,28 @@
data.push({ data.push({
utc: nextStep, utc: nextStep,
yesterday: nextStep - 60 * 60 * 24 * 1000, yesterday: nextStep - 60 * 60 * 24 * 1000,
sin: sin(nextStep, period, amplitude, offset, phase, randomness, infinityValues), sin: sin(
nextStep,
period,
amplitude,
offset,
phase,
randomness,
infinityValues,
exceedFloat32
),
wavelengths: wavelengths(), wavelengths: wavelengths(),
intensities: intensities(), intensities: intensities(),
cos: cos(nextStep, period, amplitude, offset, phase, randomness, infinityValues) cos: cos(
nextStep,
period,
amplitude,
offset,
phase,
randomness,
infinityValues,
exceedFloat32
)
}); });
} }
@ -176,9 +197,26 @@
}); });
} }
function cos(timestamp, period, amplitude, offset, phase, randomness, infinityValues) { function cos(
if (infinityValues && Math.random() > 0.5) { timestamp,
period,
amplitude,
offset,
phase,
randomness,
infinityValues,
exceedFloat32
) {
if (infinityValues && exceedFloat32) {
if (Math.random() > 0.5) {
return Number.POSITIVE_INFINITY; return Number.POSITIVE_INFINITY;
} else if (Math.random() < 0.01) {
return getRandomFloat32OverflowValue();
}
} else if (infinityValues && Math.random() > 0.5) {
return Number.POSITIVE_INFINITY;
} else if (exceedFloat32 && Math.random() < 0.01) {
return getRandomFloat32OverflowValue();
} }
return ( return (
@ -188,9 +226,26 @@
); );
} }
function sin(timestamp, period, amplitude, offset, phase, randomness, infinityValues) { function sin(
if (infinityValues && Math.random() > 0.5) { timestamp,
period,
amplitude,
offset,
phase,
randomness,
infinityValues,
exceedFloat32
) {
if (infinityValues && exceedFloat32) {
if (Math.random() > 0.5) {
return Number.POSITIVE_INFINITY; return Number.POSITIVE_INFINITY;
} else if (Math.random() < 0.01) {
return getRandomFloat32OverflowValue();
}
} else if (infinityValues && Math.random() > 0.5) {
return Number.POSITIVE_INFINITY;
} else if (exceedFloat32 && Math.random() < 0.01) {
return getRandomFloat32OverflowValue();
} }
return ( return (
@ -200,6 +255,13 @@
); );
} }
// Values exceeding float32 range (Positive: 3.4+38, Negative: -3.4+38)
function getRandomFloat32OverflowValue() {
const sign = Math.random() > 0.5 ? 1 : -1;
return sign * 3.4e39;
}
function wavelengths() { function wavelengths() {
let values = []; let values = [];
while (values.length < 5) { while (values.length < 5) {

View File

@ -122,6 +122,13 @@ export default function (openmct) {
key: 'infinityValues', key: 'infinityValues',
property: ['telemetry', 'infinityValues'] property: ['telemetry', 'infinityValues']
}, },
{
name: 'Exceed Float32 Limits',
control: 'toggleSwitch',
cssClass: 'l-input',
key: 'exceedFloat32',
property: ['telemetry', 'exceedFloat32']
},
{ {
name: 'Provide Staleness Updates', name: 'Provide Staleness Updates',
control: 'toggleSwitch', control: 'toggleSwitch',
@ -140,6 +147,7 @@ export default function (openmct) {
randomness: 0, randomness: 0,
loadDelay: 0, loadDelay: 0,
infinityValues: false, infinityValues: false,
exceedFloat32: false,
staleness: false staleness: false
}; };
} }

View File

@ -19,8 +19,6 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
import _ from 'lodash';
import configStore from '../configuration/ConfigStore'; import configStore from '../configuration/ConfigStore';
import { MARKER_SHAPES } from '../draw/MarkerShapes'; import { MARKER_SHAPES } from '../draw/MarkerShapes';
import { symlog } from '../mathUtils'; import { symlog } from '../mathUtils';
@ -64,6 +62,10 @@ import Model from './Model';
* *
* @extends {Model<PlotSeriesModelType, PlotSeriesModelOptions>} * @extends {Model<PlotSeriesModelType, PlotSeriesModelOptions>}
*/ */
const FLOAT32_MAX = 3.4e38;
const FLOAT32_MIN = -3.4e38;
export default class PlotSeries extends Model { export default class PlotSeries extends Model {
logMode = false; logMode = false;
@ -371,7 +373,7 @@ export default class PlotSeries extends Model {
let stats = this.get('stats'); let stats = this.get('stats');
let changed = false; let changed = false;
if (!stats) { if (!stats) {
if ([Infinity, -Infinity].includes(value)) { if ([Infinity, -Infinity].includes(value) || !this.isValidFloat32(value)) {
return; return;
} }
@ -383,13 +385,13 @@ export default class PlotSeries extends Model {
}; };
changed = true; changed = true;
} else { } else {
if (stats.maxValue < value && value !== Infinity) { if (stats.maxValue < value && value !== Infinity && this.isValidFloat32(value)) {
stats.maxValue = value; stats.maxValue = value;
stats.maxPoint = point; stats.maxPoint = point;
changed = true; changed = true;
} }
if (stats.minValue > value && value !== -Infinity) { if (stats.minValue > value && value !== -Infinity && this.isValidFloat32(value)) {
stats.minValue = value; stats.minValue = value;
stats.minPoint = point; stats.minPoint = point;
changed = true; changed = true;
@ -425,7 +427,7 @@ export default class PlotSeries extends Model {
const lastYVal = this.getYVal(data[insertIndex - 1]); const lastYVal = this.getYVal(data[insertIndex - 1]);
if (this.isValueInvalid(currentYVal) && this.isValueInvalid(lastYVal)) { if (this.isValueInvalid(currentYVal) && this.isValueInvalid(lastYVal)) {
console.warn('[Plot] Invalid Y Values detected'); console.warn(`[Plot] Invalid Y Values detected: ${currentYVal} ${lastYVal}`);
return; return;
} }
@ -453,7 +455,15 @@ export default class PlotSeries extends Model {
* @private * @private
*/ */
isValueInvalid(val) { isValueInvalid(val) {
return Number.isNaN(val) || this.unPlottableValues.includes(val); return Number.isNaN(val) || this.unPlottableValues.includes(val) || !this.isValidFloat32(val);
}
/**
*
* @private
*/
isValidFloat32(val) {
return val < FLOAT32_MAX && val > FLOAT32_MIN;
} }
/** /**