Ir al contenido

← Registro CSV con tabla de histórico en vivo en Node-RED

Mezclado industrial (HMI táctil)TouchBerry PiFicherosDatalogging

Registro CSV con tabla de histórico en vivo en Node-RED — ejemplo completo

Registra las activaciones de máquina en un fichero CSV y muéstralas en una tabla en vivo de Node-RED con un nodo watch: datalogging simple y auditable.

Programa completo y ejecutable para el TouchBerry Pi (activation-log-csv-table.js): incluye cabecera de conexionado, requisitos y notas de integración.

Descarga el pack completo del proyecto — gratisEste ejemplo + los relacionados + lista de materiales

Vista de solo lectura.

/*
 * COMPLETE EXAMPLE — Activation logging to CSV with a live history table
 *
 * Hardware:  TouchBerry Pi (Raspberry Pi + Industrial Shields PLC, touch screen)
 * Based on:  industrial mixing project (Node-RED touch HMI)
 *
 * Requirements:
 *   - Node-RED with @flowfuse/node-red-dashboard (UI v2) and the ui-table node
 *   - Directory /home/pi/logs existing and writable by Node-RED
 *
 * Architecture:
 *   Every time the operator activates the machine from the HMI, a line
 *   (date, user, reason, comment) is appended to the CSV. A watch node
 *   monitors the file: as soon as it changes, it is re-read, parsed and
 *   the table on the /history page is refreshed. This way the table always
 *   reflects the real file, which is the same one exported to USB.
 *
 * Flow wiring:
 *
 *   -- Write branch --
 *   [activation form/button] ──> [function 1: formatCsvLine] ──> [file (append, no extra newline)]
 *
 *   -- Read branch (live table) --
 *   [watch /home/pi/logs/log_activaciones.csv] ──> [read file (utf8, whole file)]
 *        ──> [function 2: csvToTable] ──> [ui-table "Activation history"]
 *
 *   -- Initial load at startup --
 *   [inject once=true] ──> [read file] ──> [function 2] ──> [ui-table]
 *
 * Node configuration:
 *   - file: "append to file" mode, "add newline" DISABLED (the line already
 *     carries its own \n), filename set by msg.filename.
 *   - watch: path of the CSV; emits one msg per modification.
 *   - ui-table: columns Date / User / Reason / Comment.
 */

// ============================================================
// function 1: formatCsvLine
// ------------------------------------------------------------
// Input (from the dashboard activation form):
//   msg.topic            active user on the HMI
//   msg.payload.reason   reason selected in the dropdown
//   msg.payload.comment  free-text comment (optional)
// ============================================================
const d = new Date();
const pad = n => String(n).padStart(2, "0");
const formattedDate =
    `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ` +
    `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;

// Sanitize: the separator is the comma, so remove it from free-text fields
const sanitize = t => String(t || "").replace(/,/g, ";").replace(/\r?\n/g, " ");
const reason  = sanitize(msg.payload.reason);
const comment = sanitize(msg.payload.comment);

msg.payload = formattedDate + "," + msg.topic + "," + reason + "," + comment + "\n";
msg.filename = "/home/pi/logs/log_activaciones.csv";  // file node in append mode
return msg;


// ============================================================
// function 2: csvToTable
// ------------------------------------------------------------
// Receives the full CSV (string) read by the read-file node and
// converts it into the array of objects expected by ui-table.
// The most recent activations are shown first.
// ============================================================
const MAX_ROWS = 200;                  // do not overload the HMI table

const lines = (msg.payload || "")
    .split("\n")
    .map(l => l.trim())
    .filter(l => l.length > 0);

const rows = lines.map(line => {
    const [date, user, reason, ...rest] = line.split(",");
    return {
        Date:    date   || "",
        User:    user   || "",
        Reason:  reason || "",
        Comment: rest.join(",")        // the comment may contain ';'
    };
});

msg.payload = rows.reverse().slice(0, MAX_ROWS);
return msg;
Descarga el pack completo del proyecto — gratisEste ejemplo + los relacionados + lista de materiales