UNPKG

n8n-nodes-modbus-fccomplete

Version:

n8n nodes for industrial automation with complete Modbus support (FC1-FC4) and intuitive data conversion with scaling options

232 lines 11.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ModbusTrigger = void 0; const n8n_workflow_1 = require("n8n-workflow"); const GenericFunctions_1 = require("./GenericFunctions"); class ModbusTrigger { constructor() { this.description = { displayName: 'MODBUS Trigger', name: 'modbusTrigger', icon: 'file:modbus.svg', group: ['trigger'], version: 1, description: 'Listens to MODBUS TCP events', eventTriggerDescription: '', defaults: { name: 'MODBUS Trigger', }, triggerPanel: { header: '', executionsHelp: { inactive: "<b>While building your workflow</b>, click the 'listen' button, then trigger an MODBUS event. This will trigger an execution, which will show up in this editor.<br /> <br /><b>Once you're happy with your workflow</b>, <a data-key='activate'>activate</a> it. Then every time a change is detected, the workflow will execute. These executions will show up in the <a data-key='executions'>executions list</a>, but not in the editor.", active: "<b>While building your workflow</b>, click the 'listen' button, then trigger an MODBUS event. This will trigger an execution, which will show up in this editor.<br /> <br /><b>Your workflow will also execute automatically</b>, since it's activated. Every time a change is detected, this node will trigger an execution. These executions will show up in the <a data-key='executions'>executions list</a>, but not in the editor.", }, activationHint: "Once you’ve finished building your workflow, <a data-key='activate'>activate</a> it to have it also listen continuously (you just won’t see those executions here).", }, inputs: [], outputs: ['main'], credentials: [ { name: 'modbusApi', required: true, }, ], properties: [ { displayName: 'Function Code', name: 'functionCode', type: 'options', options: [ { name: 'FC1 - Read Coils', value: 'FC1', description: 'Read discrete output coils (1-bit values)', }, { name: 'FC2 - Read Discrete Inputs', value: 'FC2', description: 'Read discrete input contacts (1-bit values)', }, { name: 'FC3 - Read Holding Registers', value: 'FC3', description: 'Read holding registers (16-bit values)', }, { name: 'FC4 - Read Input Registers', value: 'FC4', description: 'Read input registers (16-bit values)', }, ], default: 'FC3', noDataExpression: true, }, { displayName: 'Memory Address', name: 'memoryAddress', type: 'number', default: 1, description: 'The memory address to read from', }, { displayName: 'Quantity', name: 'quantity', type: 'number', default: 1, description: 'The number of coils/inputs/registers to read', typeOptions: { maxValue: 2000, minValue: 1, }, }, { displayName: 'Polling', name: 'polling', type: 'number', default: 1000, description: 'The polling interval in milliseconds', }, { displayName: 'Unit ID', name: 'unitId', type: 'number', default: 1, description: 'The Modbus unit/slave ID (0-255). Use 0 for devices that require it.', typeOptions: { maxValue: 255, minValue: 0, }, }, { displayName: 'Options', name: 'options', type: 'collection', placeholder: 'Add option', default: {}, options: [], }, ], }; } async trigger() { let poller; let client; try { const credentials = await this.getCredentials('modbusApi'); const functionCode = this.getNodeParameter('functionCode'); const memoryAddress = this.getNodeParameter('memoryAddress'); const quantity = this.getNodeParameter('quantity'); const polling = this.getNodeParameter('polling'); const unitId = this.getNodeParameter('unitId'); const options = this.getNodeParameter('options'); if (isNaN(memoryAddress)) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Memory address must be a valid number.'); } client = await (0, GenericFunctions_1.createClient)(credentials); const compareData = (data1, data2) => { if (!data1 || !data2 || data1.length !== data2.length) return false; return data1.every((val, i) => val === data2[i]); }; const readModbusData = (callback) => { switch (functionCode) { case 'FC1': client.readCoils({ address: memoryAddress, quantity, extra: { unitId } }, callback); break; case 'FC2': client.readDiscreteInputs({ address: memoryAddress, quantity, extra: { unitId } }, callback); break; case 'FC3': client.readHoldingRegisters({ address: memoryAddress, quantity, extra: { unitId } }, callback); break; case 'FC4': client.readInputRegisters({ address: memoryAddress, quantity, extra: { unitId } }, callback); break; default: callback(new Error('Invalid function code: ' + functionCode), null); } }; const processData = (data) => { var _a; if (functionCode === 'FC1' || functionCode === 'FC2') { return (data === null || data === void 0 ? void 0 : data.response.data) || []; } else { return ((_a = data === null || data === void 0 ? void 0 : data.response.data) === null || _a === void 0 ? void 0 : _a.map((value) => value.readInt16BE(0))) || []; } }; if (this.getMode() === 'trigger') { const donePromise = !options.parallelProcessing ? this.helpers.createDeferredPromise() : undefined; let previousData; poller = setInterval(() => { readModbusData((err, data) => { if (err) { clearInterval(poller); throw new n8n_workflow_1.NodeOperationError(this.getNode(), `MODBUS ${functionCode} Error: ` + err.message); } const currentData = processData(data); if (!compareData(previousData, currentData)) { previousData = currentData; const returnData = { functionCode: functionCode, address: memoryAddress, quantity: quantity, data: currentData, }; this.emit([this.helpers.returnJsonArray([returnData])]); if (donePromise) { donePromise.promise; } } }); }, polling); } const manualTriggerFunction = async () => { return new Promise((resolve, reject) => { let cycle = 0; let previousData; poller = setInterval(() => { readModbusData((err, data) => { if (err) { clearInterval(poller); reject(new n8n_workflow_1.NodeOperationError(this.getNode(), `MODBUS ${functionCode} Error: ` + err.message)); return; } const currentData = processData(data); if (!compareData(previousData, currentData) || cycle === 0) { previousData = currentData; if (cycle > 0) { const returnData = { functionCode: functionCode, address: memoryAddress, quantity: quantity, data: currentData, }; this.emit([this.helpers.returnJsonArray([returnData])]); clearInterval(poller); resolve(); } cycle++; } }); }, polling); }); }; const closeFunction = async () => { clearInterval(poller); }; return { closeFunction, manualTriggerFunction, }; } catch (error) { throw error; } } } exports.ModbusTrigger = ModbusTrigger; //# sourceMappingURL=ModbusTrigger.node.js.map