-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathscaleandfilterdata.js
More file actions
140 lines (114 loc) · 5.17 KB
/
Copy pathscaleandfilterdata.js
File metadata and controls
140 lines (114 loc) · 5.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// --- Configuration Section (Easily Editable) ---
const deviceIdPrefix = "pico_"; // Prefix for device IDs
// Calibration/Scaling factors. These are now *PER DEVICE* and *PER SENSOR*.
const scalingFactors = {
"pico_1": {
Temperature: { sensorType: "mA_INPUT", rangeMin: -10, rangeMax: 150 }, // Example: 0-100 degrees
pH: { sensorType: "mA_INPUT", rangeMin: -1, rangeMax: 15 }, // Example: 0-14 pH
ORP: { sensorType: "mA_INPUT", rangeMin: -1000, rangeMax: +0 },// Example: -500 to +500 mV
motor_mA: { multiplier: 0.1, offset: 0.0, sensorType: "INA226" },
RPM: { multiplier: 1, offset: 0, sensorType: "PULSE" }
},
"pico_2": {
Temperature: { sensorType: "mA_INPUT", rangeMin: -10, rangeMax: 150 },
pH: { sensorType: "mA_INPUT", rangeMin: -1, rangeMax: 15 },
ORP: { sensorType: "mA_INPUT", rangeMin: -1000, rangeMax: +0 },
motor_mA: { multiplier: 0.1, offset: 0.0, sensorType: "INA226" },
RPM: { multiplier: 1, offset: 0, sensorType: "PULSE" }
},
"pico_3": {
Temperature: { sensorType: "mA_INPUT", rangeMin: 0, rangeMax: 100 },
pH: { sensorType: "mA_INPUT", rangeMin: 0, rangeMax: 14 },
ORP: { sensorType: "mA_INPUT", rangeMin: -1000, rangeMax: +0 },
motor_mA: { multiplier: 0.1, offset: 0.0, sensorType: "INA226" },
RPM: { multiplier: 1, offset: 0, sensorType: "PULSE" }
},
"pico_4": {
Temperature: { sensorType: "mA_INPUT", rangeMin: 0, rangeMax: 100 },
pH: { sensorType: "mA_INPUT", rangeMin: 0, rangeMax: 14 },
ORP: { sensorType: "mA_INPUT", rangeMin: -1000, rangeMax: +0 },
motor_mA: { multiplier: 0.1, offset: 0.0, sensorType: "INA226" },
RPM: { multiplier: 1, offset: 0, sensorType: "PULSE" }
},
};
// Define valid ranges for each measurement, *after* final scaling. These are your REAL-WORLD units.
const validRanges = {
Temperature: { min: -1, max: 110 }, // Allow slight negative/overrange
pH: { min: -0.5, max: 14.5 },
ORP: { min: -2100, max: 2100 },
motor_mA: { min: -5, max: 2000 },
RPM: { min: 0, max: 250 } // Slightly above expected max, for margin.
};
// Noise floor for 4-20mA signals.
const noiseFloor_mA = 0.5;
// Measurement name for InfluxDB
const influxMeasurement = 'pico_measurements';
// --- Additional Configuration for RPM Handling ---
const maxReasonableRPM = 250; // Maximum RPM, slightly above expected max.
// --- End Configuration Section ---
// --- Helper Functions ---
function scale_mA_Input(value, rangeMin, rangeMax) {
// 1. Apply noise floor (in mA).
if (value < noiseFloor_mA) {
value = 0;
}
// 2. Convert 4-20mA to 0-100%.
const percent = (value - 4) / 16;
// 3. Clamp the percentage.
const clampedPercent = Math.max(0, Math.min(1, percent));
// 4. Scale to engineering units.
const scaledValue = rangeMin + clampedPercent * (rangeMax - rangeMin);
return scaledValue;
}
function validateAndScale(value, fieldName, deviceId) {
if (typeof value !== 'number' || isNaN(value) || !isFinite(value)) {
node.warn(`Invalid ${fieldName} for ${deviceId}: ${value} (Not a valid number)`);
return null;
}
const deviceScaling = scalingFactors[deviceId] || {};
const scaling = deviceScaling[fieldName] || { sensorType: "UNKNOWN", rangeMin: 0, rangeMax: 1 };
let scaledValue = value;
// Apply scaling based on sensor type
if (scaling.sensorType === "mA_INPUT") {
// Scale the mA value to engineering units.
scaledValue = scale_mA_Input(scaledValue, scaling.rangeMin, scaling.rangeMax);
} else if (scaling.sensorType === "INA226") {
// Apply offset and multiplier (for motor current).
scaledValue = value + scaling.offset;
scaledValue = scaledValue * scaling.multiplier;
} else if (scaling.sensorType === "PULSE") {
// RPM-Specific Handling:
if (scaledValue > maxReasonableRPM) {
node.warn(`Invalid ${fieldName} for ${deviceId}: ${scaledValue} (Exceeds max reasonable RPM)`);
return null; // Reject unreasonably large RPM values
}
}
// Range validation *after* scaling
const range = validRanges[fieldName];
if (range && (scaledValue < range.min || scaledValue > range.max)) {
node.warn(`Invalid ${fieldName} for ${deviceId}: ${scaledValue} (Out of range [${range.min}, ${range.max}])`);
return null;
}
return scaledValue;
}
// --- Main Function Logic ---
const data = msg.payload;
const deviceId = msg.unitid ? `${deviceIdPrefix}${msg.unitid}` : `${deviceIdPrefix}1`;
const timestamp = Date.now();
const influxData = {
measurement: influxMeasurement,
tags: {
device: deviceId
},
timestamp: timestamp,
fields: {}
};
const fieldsToProcess = ["Temperature", "pH", "ORP", "motor_mA", "RPM"];
for (const field of fieldsToProcess) {
const scaledValue = validateAndScale(data[field], field, deviceId);
if (scaledValue !== null) {
influxData.fields[field] = scaledValue;
}
}
msg.payload = [influxData];
return msg;