The server side: a multi-plant Node-RED concentrator
Validate and deduplicate at the door
Everything lands on disk, twice
Knowing who went quiet
A snippet from the implementation
Straight from the example as deployed on the Server (Node-RED): copy it freely:
const PLANT = env.get("PLANT") || "plant1"; // same logic in both plants
let data;
try {
data = (typeof msg.payload === "string") ? JSON.parse(msg.payload) : msg.payload;
} catch (e) {
msg.error = "invalid JSON: " + e.message;
return [null, msg]; // output 2: discards
}
// Minimum fields sent by the firmware
if (data.id === undefined) {
msg.error = "missing PLC id";
return [null, msg];
}
// Deduplication by id_msg (the PLC re-sends after reconnection / SD replay)
if (data.id_msg) {
const seen = context.get("seen") || {};
if (seen[data.id_msg]) {
msg.error = "duplicate: " + data.id_msg;
return [null, msg];
}
seen[data.id_msg] = Date.now();
// Purge: keep only the last hour of IDs so the map does not grow unbounded
const oneHourAgo = Date.now() - 3600 * 1000;
for (const k in seen) if (seen[k] < oneHourAgo) delete seen[k];
context.set("seen", seen);
}The full example is a complete program, wiring header, setup and main loop, ready to adapt to your application.
Frequently asked questions
Why deduplicate on the server instead of the PLC?
The PLC deliberately favours at-least-once delivery: it would rather send twice than lose data after a reconnection. The concentrator holds the recent id_msg set centrally, which is cheap in Node-RED context and impossible to do across reboots on the device.
Should each plant run its own concentrator?
Yes in this deployment: one broker plus Node-RED per plant keeps data flowing during inter-site link failures. The flows are identical; an environment variable sets the plant name, and a central system can consume both.
How does data get into a database from here?
Add a database node after the validation output: the messages are already clean, deduplicated and enriched JSON. The file backups stay as the audit trail and replay source if the database ever needs rebuilding.