Overview
Bad data from sensors—spikes, dropouts, error codes, stuck values—can corrupt historical records and trigger false alarms. Data validation scripts catch these problems before values are stored, keeping time-series data clean and alarms reliable.
| Issue | Example | Impact |
|---|---|---|
| Spike | Temperature jumps from 72 to 9999 for one poll | Skews averages and triggers false alarms |
| Dropout | Value reads 0 or -1 when a sensor loses communication | Causes false low alarms and incorrect calculations |
| Stuck value | Temperature reads exactly 72.0 for 24 hours | Hides real changes and may indicate sensor failure |
| Error code | Device returns -999 or 65535 to indicate a fault | Pollutes data if logged as a real value |
| Out of range | Humidity reads 150% or pressure is negative | Indicates a physically impossible reading or sensor fault |
Prerequisites
- A running Mango by Radix IoT v4.x or v5.x instance
- Access to edit data points, create meta points, and configure global scripts
- Known valid ranges and fault codes for the sensors being monitored
- Enough historical data to choose realistic validation thresholds
Approach 1: Point Configuration Without Scripting
Mango’s built-in Discard extreme values setting handles simple range validation:
- Open the data point’s edit page.
- Enable Discard extreme values.
- Set the Discard low limit and Discard high limit.
- Save the point.
Values outside the configured range are silently discarded: they are not logged or displayed. This is the simplest approach and should be the first line of defense.
Note
Do not confuse Discard extreme values with Prevent extreme sets. They are separate settings. Discard extreme values silently drops out-of-range logged readings; Prevent extreme sets controls attempts to set a point to an extreme value.
Approach 2: Meta Point Validation
For logic more complex than range checks, create a meta point that reads the raw source point and produces a validated value.
Clamp Values to a Valid Range
// Context: raw = raw temperature point
var value = raw.value;
if (value < -40) value = -40;
if (value > 300) value = 300;
return value;
Filter Known Error Codes
// Context: raw = source point
var errorCodes = [-999, -1, 65535, 32767];
if (errorCodes.indexOf(raw.value) >= 0) {
return UNCHANGED; // Leave the meta point's current value in place
}
return raw.value;
Warning
Do not use return null to skip an update. For Numeric, Binary, Multistate, and Alphanumeric meta points, Mango can coerce null to a type default such as 0, false, or an empty string and log it as a real value. Use return UNCHANGED to leave the current value untouched.
Detect Spikes
// Reject values that change more than 50 units in one poll
// Context: raw = source point
var current = raw.value;
var previous = raw.ago(SECOND, 1);
if (previous !== null) {
var diff = Math.abs(current - previous);
if (diff > 50) {
LOG.warn(
"Spike detected on " +
raw.dataPointWrapper.name +
": " +
current
);
return UNCHANGED;
}
}
return current;
Warning
raw.past(periodType, count) returns one aggregate statistics object containing values such as average, minimum, maximum, and count. It does not return an array of samples. Use raw.ago(periodType, count) when you need one prior value; it returns the number directly.
Detect a Stuck Value
// Context: raw = source point
var to = raw.time;
var from = to - (2 * 60 * 60 * 1000);
var history = raw.pointValuesBetween(from, to);
if (history.size() > 10) {
var allSame = true;
var first = history.get(0).doubleValue;
for (var i = 1; i < history.size(); i++) {
if (history.get(i).doubleValue !== first) {
allSame = false;
break;
}
}
if (allSame) {
LOG.warn(
"Stuck value detected on " +
raw.dataPointWrapper.name +
": " +
raw.value
);
return -1; // Multistate code for "suspect"
}
}
return 0; // Multistate code for "normal"
Warning
pointValuesBetween() returns a Java list, not a JavaScript array. Use .size() and
.get(i), which is the idiom in Mango’s own scripting help. .length is engine-dependent:
it is undefined on Nashorn, so a loop written as i < history.length simply never runs
and reports nothing — no error, no detection. The same applies to the list returned by
DataPointQuery.query().
Note
This pattern returns 0 and -1 as status codes rather than returning the temperature. Configure the meta point as Multistate, not Numeric, so these values have a defined meaning on dashboards.
Approach 3: Global Script for Batch Validation
A scheduled global script can review recently logged values across many points and flag anomalies:
// Nightly validation: check all temperature points for spikes
var points = DataPointQuery.query("eq(tags.pointType,temperature)");
var to = new Date().getTime();
var from = to - (24 * 60 * 60 * 1000);
for (var i = 0; i < points.size(); i++) {
var pt = points.get(i);
if (pt.runtime === null) continue;
var values = pt.runtime.pointValuesBetween(from, to);
var min = Infinity;
var max = -Infinity;
for (var j = 0; j < values.size(); j++) {
var value = values.get(j).doubleValue;
if (value < min) min = value;
if (value > max) max = value;
}
if (max - min > 100) {
LOG.warn(
"Possible spike on " +
pt.name +
": range = " +
min +
" to " +
max
);
}
}
Use DataPointQuery.query(rql) with an RQL expression such as eq(tags.pointType,temperature). Each result is a data point wrapper. Its .runtime property is null when the point is not enabled; otherwise, it exposes pointValuesBetween(from, to) for retrieving history.
Building a Validation Strategy
- Enable Discard extreme values on numeric points as the first line of defense.
- Add meta point validation where you need spike detection, error-code filtering, or more complex logic.
- Schedule a global script for periodic batch validation.
- Log validation events so recurring data-quality problems can be traced to their source.
Verification
- Confirm an in-range source value reaches the validated point unchanged.
- Submit a known error code and verify the meta point retains its previous value.
- Submit an artificial spike and confirm the script logs a warning containing
raw.dataPointWrapper.name. - Confirm stuck-value detection uses samples returned by
raw.pointValuesBetween(from, to). - Review historical data to ensure rejected values were not stored as
0,false, or an empty string.
Troubleshooting
Rejected Values Become Zero or False
Replace return null with return UNCHANGED. A null result can be coerced to the point data type’s default value.
Spike Detection Fails or Reports Undefined
- Use
raw.ago(periodType, count)for one earlier value. - Use
raw.dataPointWrapper.namefor the point name. - Do not treat
raw.past()as an array.
Stuck-Value Detection Throws an Error
- Retrieve samples with
raw.pointValuesBetween(from, to). - Iterate with
.size()and.get(i)— the result is a Java list, and.lengthis
undefinedon Nashorn, which makes the loop silently skip. - Compare each sample’s
.doubleValue, not its boxed.value. - Confirm the time range contains enough logged samples.
Global Script Returns No Points
- Confirm the points have the expected tags.
- Use
DataPointQuery.query("eq(tags.pointType,temperature)"). - Iterate the result with
.size()and.get(i). It is a Java list, sopoints.length
can beundefinedand the loop will report zero points without raising an error. - Skip results whose
.runtimeproperty isnull.
Related Guides
- Guide to Scripting Locations in Mango
- How to Create Unit Conversions with Meta Points
- How to Use Global Scripts for Reusable Functions
- How to Configure Point Logging for Optimal Data Collection