4. ThingWorx 服务脚本实现数据清洗
为什么清洗要放在服务脚本里
设备侧上报的原始值往往同时存在三类问题:抖动噪声(如温度在 25.1/25.3/25.0 之间跳变)、单位不统一(有的设备上报 ℉,有的上报 ℃)、越界异常(如 -999 表示传感器故障、或 200℃ 的明显错误值)。把清洗逻辑写成 ThingWorx 服务脚本,可以让所有订阅该属性的下游逻辑(告警、存储、看板)拿到的是同一份"干净值",避免每个消费方各写一套判断。
清洗服务的输入与输出约定
本案例定义一个名为 CleanDeviceData 的服务,输入参数如下:
rawValue(NUMBER):设备上报的原始数值rawUnit(STRING):原始单位,取值"C"或"F"minValid(NUMBER):有效下限,示例取-40maxValid(NUMBER):有效上限,示例取125
输出为 InfoTable,包含 cleanValue(清洗后摄氏度值)、status(OK / FILTERED / CONVERTED)、reason(说明文本)。
服务脚本实现
在 ThingWorx Composer 中新建服务,脚本类型选择 JavaScript,粘贴以下代码。脚本使用 ThingWorx 内置的 InfoTable 与 logger 对象,无需额外引入库。
// 服务名: CleanDeviceData
// 输入: rawValue(NUMBER), rawUnit(STRING), minValid(NUMBER), maxValid(NUMBER)
// 输出: InfoTable(cleanValue, status, reason)
var params = {
infoTableName: "CleanResult",
dataShapeName: "CleanResultShape"
};
// 1. 单位换算
var celsius = rawValue;
var converted = false;
if (rawUnit === "F") {
celsius = (rawValue - 32) * 5 / 9;
converted = true;
} else if (rawUnit !== "C") {
// 非法单位,直接拒绝
var t = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape(params);
t.AddRow({ cleanValue: 0, status: "FILTERED", reason: "invalid unit: " + rawUnit });
return t;
}
// 2. 异常值过滤
if (celsius < minValid || celsius > maxValid) {
logger.warn("CleanDeviceData filtered out-of-range value: " + celsius);
var t2 = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape(params);
t2.AddRow({ cleanValue: 0, status: "FILTERED", reason: "out of range [" + minValid + "," + maxValid + "]" });
return t2;
}
// 3. 去噪:使用 Thing 属性保存最近3次值做滑动均值
var history = me.lastThreeValues; // INFOTABLE 或 STRING,示例用逗号分隔字符串
var arr = [];
if (history && history.length > 0) {
arr = history.split(",").map(Number);
}
arr.push(celsius);
if (arr.length > 3) { arr.shift(); }
me.lastThreeValues = arr.join(",");
var sum = 0;
for (var i = 0; i < arr.length; i++) { sum += arr[i]; }
var smoothed = sum / arr.length;
// 4. 组装输出
var result = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape(params);
result.AddRow({
cleanValue: smoothed,
status: converted ? "CONVERTED" : "OK",
reason: "window=" + arr.length + ", raw=" + rawValue + rawUnit
});
return result;
去噪部分用 Thing 属性
lastThreeValues 保存历史值,避免引入外部存储。首次调用时该属性为空,脚本会自动跳过历史拼接,只对当前值做均值。验证与结果判读
在 Composer 的服务测试面板中依次输入以下三组参数,观察返回 InfoTable 的 status 与 cleanValue:
| rawValue | rawUnit | 预期 status | 预期 cleanValue | 判读依据 |
|---|---|---|---|---|
| 77.0 | F | CONVERTED | 25.0 | 单位换算后落在有效区间 |
| -999 | C | FILTERED | 0 | 低于 minValid=-40 |
| 25.1 / 25.3 / 25.0 | C | OK | ≈25.13 | 三次滑动均值 |
若返回的
status 始终为 FILTERED,先检查 minValid/maxValid 是否被误传为字符串;ThingWorx 服务参数类型不匹配时不会报错,而是按字符串比较,导致所有值都被判为越界。接入订阅链路的建议
清洗服务本身不绑定订阅。若要让设备属性变化时自动清洗,可在 Thing 的 Temperature 属性上添加订阅,事件触发时调用 CleanDeviceData,并把返回的 cleanValue 写回另一个属性 TemperatureClean。这样告警与存储逻辑只需读取 TemperatureClean,无需重复实现换算与过滤。