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 • 9.47 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DataConversionUtils = void 0;
class DataConversionUtils {
static convertData(registers, rule) {
var _a;
try {
const result = {
name: rule.name,
value: null,
originalValue: null,
dataType: rule.dataType,
valid: false,
metadata: {
byteOrder: rule.byteOrder,
scaleFactor: rule.scaleFactor,
offset: rule.offset,
unitConversion: rule.unitConversion ? `${rule.unitConversion.from} to ${rule.unitConversion.to}` : undefined,
},
};
const requiredRegisters = this.getRequiredRegisters(rule.dataType);
if (registers.length < rule.startRegister + requiredRegisters) {
result.error = `Not enough registers available. Required: ${requiredRegisters}, Available: ${registers.length - rule.startRegister}`;
return result;
}
const dataRegisters = registers.slice(rule.startRegister, rule.startRegister + requiredRegisters);
result.originalValue = dataRegisters.length === 1 ? dataRegisters[0] : dataRegisters;
switch (rule.dataType) {
case 'int16':
result.value = this.convertInt16(dataRegisters[0], rule.byteOrder);
break;
case 'uint16':
result.value = this.convertUint16(dataRegisters[0], rule.byteOrder);
break;
case 'int32':
result.value = this.convertInt32(dataRegisters, rule.byteOrder, rule.wordSwap);
break;
case 'uint32':
result.value = this.convertUint32(dataRegisters, rule.byteOrder, rule.wordSwap);
break;
case 'float32':
result.value = this.convertFloat32(dataRegisters, rule.byteOrder, rule.wordSwap);
break;
case 'scaled':
result.value = this.convertScaled(dataRegisters[0], rule);
break;
case 'bitfield':
result.value = this.convertBitfield(dataRegisters[0], rule);
break;
case 'bcd':
result.value = this.convertBCD(dataRegisters[0], rule.byteOrder);
break;
default:
result.error = `Unsupported data type: ${rule.dataType}`;
return result;
}
if (rule.unitConversion && typeof result.value === 'number') {
result.value = this.applyUnitConversion(result.value, rule.unitConversion);
}
if (rule.decimalPlaces !== undefined && typeof result.value === 'number') {
result.value = Number(result.value.toFixed(rule.decimalPlaces));
}
if ((_a = rule.validation) === null || _a === void 0 ? void 0 : _a.enabled) {
const validation = this.validateValue(result.value, rule.validation);
result.valid = validation.valid;
if (!validation.valid) {
result.error = validation.error;
}
}
else {
result.valid = true;
}
return result;
}
catch (error) {
return {
name: rule.name,
value: null,
originalValue: null,
dataType: rule.dataType,
valid: false,
error: error instanceof Error ? error.message : 'Unknown conversion error',
metadata: {
byteOrder: rule.byteOrder,
},
};
}
}
static getRequiredRegisters(dataType) {
switch (dataType) {
case 'int16':
case 'uint16':
case 'scaled':
case 'bitfield':
case 'bcd':
return 1;
case 'int32':
case 'uint32':
case 'float32':
return 2;
default:
return 1;
}
}
static convertInt16(register, byteOrder) {
if (register > 32767) {
return register - 65536;
}
return register;
}
static convertUint16(register, byteOrder) {
return register & 0xFFFF;
}
static convertInt32(registers, byteOrder, wordSwap = false) {
const reg0 = wordSwap ? registers[1] : registers[0];
const reg1 = wordSwap ? registers[0] : registers[1];
let value;
if (byteOrder === 'big_endian') {
value = (reg0 << 16) | (reg1 & 0xFFFF);
}
else {
value = (reg1 << 16) | (reg0 & 0xFFFF);
}
if (value > 2147483647) {
return value - 4294967296;
}
return value;
}
static convertUint32(registers, byteOrder, wordSwap = false) {
const reg0 = wordSwap ? registers[1] : registers[0];
const reg1 = wordSwap ? registers[0] : registers[1];
if (byteOrder === 'big_endian') {
return ((reg0 << 16) | (reg1 & 0xFFFF)) >>> 0;
}
else {
return ((reg1 << 16) | (reg0 & 0xFFFF)) >>> 0;
}
}
static convertFloat32(registers, byteOrder, wordSwap = false) {
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);
const reg0 = wordSwap ? registers[1] : registers[0];
const reg1 = wordSwap ? registers[0] : registers[1];
if (byteOrder === 'big_endian') {
view.setUint16(0, reg0, false);
view.setUint16(2, reg1, false);
}
else {
view.setUint16(0, reg1, false);
view.setUint16(2, reg0, false);
}
return view.getFloat32(0, false);
}
static convertScaled(register, rule) {
let value = register;
if (rule.scaleFactor !== undefined) {
value *= rule.scaleFactor;
}
if (rule.offset !== undefined) {
value += rule.offset;
}
return value;
}
static convertBitfield(register, rule) {
if (rule.bitMask !== undefined) {
const result = register & rule.bitMask;
return result;
}
else if (rule.bitPosition !== undefined) {
const bitLength = rule.bitLength || 1;
const mask = (1 << bitLength) - 1;
const result = (register >> rule.bitPosition) & mask;
if (bitLength === 1) {
return Boolean(result);
}
return result;
}
return register;
}
static convertBCD(register, byteOrder) {
const high = (register >> 8) & 0xFF;
const low = register & 0xFF;
const highDecimal = ((high >> 4) & 0x0F) * 10 + (high & 0x0F);
const lowDecimal = ((low >> 4) & 0x0F) * 10 + (low & 0x0F);
return highDecimal * 100 + lowDecimal;
}
static applyUnitConversion(value, conversion) {
const conversionMap = {
'celsius_to_fahrenheit': (val) => (val * 9 / 5) + 32,
'fahrenheit_to_celsius': (val) => (val - 32) * 5 / 9,
'celsius_to_kelvin': (val) => val + 273.15,
'kelvin_to_celsius': (val) => val - 273.15,
'psi_to_bar': (val) => val * 0.0689476,
'bar_to_psi': (val) => val * 14.5038,
'pascal_to_bar': (val) => val * 0.00001,
'bar_to_pascal': (val) => val * 100000,
'gpm_to_lpm': (val) => val * 3.78541,
'lpm_to_gpm': (val) => val * 0.264172,
'cfm_to_cmh': (val) => val * 1.69901,
'cmh_to_cfm': (val) => val * 0.588578,
'hp_to_kw': (val) => val * 0.745699,
'kw_to_hp': (val) => val * 1.34102,
'btu_to_kw': (val) => val * 0.000293071,
'kw_to_btu': (val) => val * 3412.14,
};
const conversionKey = `${conversion.from}_to_${conversion.to}`;
const converter = conversionMap[conversionKey];
if (converter) {
return converter(value);
}
return value;
}
static validateValue(value, validation) {
if (typeof value === 'number') {
if (isNaN(value) && !validation.allowNaN) {
return { valid: false, error: 'Value is NaN' };
}
if (validation.min !== undefined && value < validation.min) {
return { valid: false, error: `Value ${value} is below minimum ${validation.min}` };
}
if (validation.max !== undefined && value > validation.max) {
return { valid: false, error: `Value ${value} is above maximum ${validation.max}` };
}
}
return { valid: true };
}
static convertMultiple(registers, rules) {
return rules.map(rule => this.convertData(registers, rule));
}
}
exports.DataConversionUtils = DataConversionUtils;
//# sourceMappingURL=DataConversionUtils.js.map