import base64ToArrayBuffer from 'base64-arraybuffer'; import moment from 'moment'; import { decimalToBitfield, obisCodeValueToNumeric, numericObisFormat, unitConversion } from './binary'; import { ParserError } from './errors'; import { HexingParserBase } from './HexingParserBase'; import { ObisParser } from './ObisParser'; import { ParserResult } from './ParserResult'; import { RegisterCode } from './RegisterCode'; import { CurrentDenominatorModel, CurrentNumeratorModel, DateNowModel, DemandResetCounterModel, RegisterModel, SerialNumberModel, TotalActiveEnergyAbsoluteModel, TotalCumulativeMaximumDemandImportModel, TotalInductiveReactiveEnergyImportModel, TotalMaximumDemandImportModel, WeekdayNowModel, Tariff1ActiveEnergyImportModel, Tariff2ActiveEnergyImportModel, Tariff3ActiveEnergyImportModel, Tariff1MaximumDemandExportModel, Tariff2MaximumDemandExportModel, Tariff3MaximumDemandExportModel, Tariff1ActiveEnergyExportModel, Tariff2ActiveEnergyExportModel, Tariff3ActiveEnergyExportModel, Tariff1CumulativeMaximumDemandExportModel, Tariff2CumulativeMaximumDemandExportModel, Tariff3CumulativeMaximumDemandExportModel, Tariff1CumulativeMaximumDemandImportModel, Tariff2CumulativeMaximumDemandImportModel, Tariff3CumulativeMaximumDemandImportModel, Tariff1MaximumDemandImportModel, Tariff2MaximumDemandImportModel, Tariff3MaximumDemandImportModel, Tariff1InductiveReactiveEnergyImportModel, Tariff2InductiveReactiveEnergyImportModel, Tariff3InductiveReactiveEnergyImportModel, TimeNowModel, TotalActiveEnergyExportModel, TotalCumulativeMaximumDemandExportModel, TotalMaximumDemandExportModel, VoltageDenominatorModel, VoltageNumeratorModel, } from './registers'; export class HXE310AndHXF300HexingParser extends HexingParserBase { hexingHXE310AndHXF300CodeToObisMap: [RegisterModel, string][] = [ [CurrentNumeratorModel, '0.4.2'], [CurrentDenominatorModel, '0.4.3'], [VoltageNumeratorModel, '0.4.5'], [VoltageDenominatorModel, '0.4.6'], [SerialNumberModel, 'C.1.0'], [TimeNowModel, '0.9.1'], [DateNowModel, '0.9.2'], [TotalCumulativeMaximumDemandImportModel, '1.2.0'], [Tariff1CumulativeMaximumDemandImportModel, '1.2.1'], [Tariff2CumulativeMaximumDemandImportModel, '1.2.2'], [Tariff3CumulativeMaximumDemandImportModel, '1.2.3'], [TotalMaximumDemandImportModel, '1.6.0'], [Tariff1MaximumDemandImportModel, '1.6.1'], [Tariff2MaximumDemandImportModel, '1.6.2'], [Tariff3MaximumDemandImportModel, '1.6.3'], [TotalActiveEnergyAbsoluteModel, '15.8.0'], [Tariff1ActiveEnergyImportModel, '1.8.1'], [Tariff2ActiveEnergyImportModel, '1.8.2'], [Tariff3ActiveEnergyImportModel, '1.8.3'], [TotalCumulativeMaximumDemandExportModel, '2.2.0'], [Tariff1CumulativeMaximumDemandExportModel, '2.2.1'], [Tariff2CumulativeMaximumDemandExportModel, '2.2.2'], [Tariff3CumulativeMaximumDemandExportModel, '2.2.3'], [TotalMaximumDemandExportModel, '2.6.0'], [Tariff1MaximumDemandExportModel, '2.6.1'], [Tariff2MaximumDemandExportModel, '2.6.2'], [Tariff3MaximumDemandExportModel, '2.6.3'], [TotalActiveEnergyExportModel, '2.8.0'], [Tariff1ActiveEnergyExportModel, '2.8.1'], [Tariff2ActiveEnergyExportModel, '2.8.2'], [Tariff3ActiveEnergyExportModel, '2.8.3'], [TotalInductiveReactiveEnergyImportModel, '5.8.0'], [Tariff1InductiveReactiveEnergyImportModel, '5.8.1'], [Tariff2InductiveReactiveEnergyImportModel, '5.8.2'], [Tariff3InductiveReactiveEnergyImportModel, '5.8.3'], [DemandResetCounterModel, '96.50.0'], ]; needUnitTransformationAndLinearConversion: Array = [1]; obisNeedsUnitTransformationOrValidationAndMustDoLinearConversion(aObisCode) { return { obises: [ '1.2.0', '1.2.1', '1.2.2', '1.2.3', '1.6.0', '1.6.1', '1.6.2', '1.6.3', '1.8.0', '1.8.1', '1.8.2', '1.8.3', '2.2.0', '2.2.1', '2.2.2', '2.2.3', '2.6.0', '2.6.1', '2.6.2', '2.6.3', '2.8.0', '2.8.1', '2.8.2', '2.8.3', '5.8.0', '5.8.1', '5.8.2', '5.8.3', '15.8.0', ].includes(aObisCode), conversion: 1000, }; } linearConversionFactor(result: ParserResult) { const NTC = result.readRegisters.find(register => register.record_code === 'current_numerator'); const DTC = result.readRegisters.find(register => register.record_code === 'current_denominator'); const NTT = result.readRegisters.find(register => register.record_code === 'voltage_numerator'); const DTT = result.readRegisters.find(register => register.record_code === 'voltage_denominator'); const RTC = parseFloat(NTC?.value_read ?? '1') / parseFloat(DTC?.value_read ?? '1'); const RTT = parseFloat(NTT?.value_read ?? '1') / parseFloat(DTT?.value_read ?? '1'); return RTC * RTT; } getCodeMapper() { switch (this.interpretationCode) { case 1: return this.hexingHXE310AndHXF300CodeToObisMap; default: throw new ParserError('Invalid interpretation code'); } } parse(aBase64String: string, recoverMeterNumber?: Function): ParserResult { const arrayBuffer = new Uint8Array(base64ToArrayBuffer.decode(aBase64String)); const arraySlice = arrayBuffer; const obisParser = new ObisParser(); const obisRegisters = obisParser.parse(arraySlice); if (recoverMeterNumber && obisRegisters.getMeterNumber()) { return recoverMeterNumber(numericObisFormat(obisRegisters.getMeterNumber())); } const result = new ParserResult(); const addValue = (entry: [RegisterModel, string]) => { const [recordModel, targetObis] = entry; let rawValue = obisRegisters.getValueOfObis(targetObis); if (rawValue == null) { const anotherPossibleValue = obisRegisters.getValueOfObis(this.anotherPossibleObis[targetObis]); if (anotherPossibleValue) { rawValue = anotherPossibleValue; } else { result.add(recordModel, null); return; } } let finalValue: any = rawValue; if (recordModel.format === 'numeric') { finalValue = obisCodeValueToNumeric(rawValue); if (this.obisNeedsUnitTransformationOrValidationAndMustDoLinearConversion(targetObis).obises) { const decimals = parseFloat(finalValue) === 0 ? 0 : 4; finalValue = unitConversion( finalValue * this.linearConversionFactor(result), this.obisNeedsUnitTransformationOrValidationAndMustDoLinearConversion(targetObis).conversion, decimals, ); } } if (recordModel.format === 'bitfield') { finalValue = decimalToBitfield(rawValue); } if (recordModel.format === 'text') { finalValue = rawValue.trim(); } if (recordModel.code === RegisterCode.DateNow) { const dateMoment = moment(rawValue.trim(), 'DD-MM-YYYY-W'); if (dateMoment.isValid()) { result.add(WeekdayNowModel, `${dateMoment.day()}`); finalValue = dateMoment.format('DDMMYY'); } else { finalValue = null; } } result.add(recordModel, finalValue); }; const mapperToUse = this.getCodeMapper(); for (const entry of mapperToUse) { addValue(entry); } return result; } }