/* eslint-disable no-bitwise */ import base64ToArrayBuffer from 'base64-arraybuffer'; import moment from 'moment'; export const decimalToHexWithPadding = aNumber => { return aNumber.toString(16).padStart(2, '0').toUpperCase(); }; export const base64ToHex = aBase64String => { const unit8Array = Array.from(new Uint8Array(base64ToArrayBuffer.decode(aBase64String))); const anArray = unit8Array.map(decimalToHexWithPadding); const string = anArray.join(' '); return string; }; export const charsPairs = anArray => { let nextArray: any[] = []; for (let i = 0; i < anArray.length - 1; i += 2) { nextArray.push(anArray[i] + anArray[i + 1]); } return nextArray; }; const isPrintableAscii = decimal => { return decimal >= 32 && decimal <= 126; }; export const hexToBase64 = anHexString => { const cleanString = anHexString.replace(/\s/g, ''); const decimalBytes = charsPairs(cleanString).map(anHexByte => parseInt(anHexByte, 16)); return base64ToArrayBuffer.encode(decimalBytes as unknown as ArrayBuffer); }; export const notPrintableToSpaces = decimalChar => { if (isPrintableAscii(decimalChar)) { return decimalChar; } else { return 32; } }; export const charCodesToAscii = charCodeArray => { const filteredArray = charCodeArray.map(notPrintableToSpaces); return String.fromCharCode(...filteredArray); }; export const base64ToAscii = aBase64String => { const uint8Array = Array.from(new Uint8Array(base64ToArrayBuffer.decode(aBase64String))); return charCodesToAscii(uint8Array); }; export const base64ToEscapedAscii = aBase64String => { const uint8Array = Array.from(new Uint8Array(base64ToArrayBuffer.decode(aBase64String))); let result = ''; for (const charCode of uint8Array) { if (isPrintableAscii(charCode)) { result += String.fromCharCode(charCode); } else { result += '\\x' + charCode.toString(16).padStart(2, '0'); } } return result; }; export const decimalArrayAsBigEndianNumber = (aDecimalArray: Uint8Array) => { const arrayCopy = Array.from(aDecimalArray); const anArray = arrayCopy.map(decimalToHexWithPadding); const string = anArray.join(''); return parseInt(string, 16).toString(); }; export const decimalArrayAsLittleEndianNumber = (aDecimalArray: Uint8Array) => { const arrayCopy = Array.from(aDecimalArray); const anArray = arrayCopy.map(decimalToHexWithPadding); anArray.reverse(); const string = anArray.join(''); return parseInt(string, 16).toString(); }; export const uint8ArrayToAsciiNumber = aUint8Array => { const aString = charCodesToAscii(aUint8Array); return aString.replace(/^\D*0*/, ''); }; export const asciiToBinaryCodedDecimal = (aString: Uint8Array, decimals = 0) => { const array: (string | number)[] = []; for (const byte of aString) { const semiByte1 = byte >>> 4; const semiByte0 = byte & 0xf; array.push(semiByte1); array.push(semiByte0); } if (decimals) { const index = array.length - decimals; array.splice(index, 0, '.'); } return array.join(''); }; export const removeLeadingZeroes = aString => { return aString.replace(/^-*#?0*/, ''); }; export const decimalToBitfield = decimal => { return '0b' + (decimal >>> 0).toString(2).padStart(8, '0'); }; export const parseBCD = (array: Uint8Array, decimals = 0) => { return formatNumericValue(asciiToBinaryCodedDecimal(array), decimals); }; export const formatNumericValue = (stringValue, decimals = 0) => { return `${parseInt(stringValue, 10) / 10 ** decimals}`; }; export const powerFactorDecoding = (aNumber: number): string | null => { if (aNumber === 255) { return null; } const principal = aNumber & 0b01111111; const sign = aNumber >> 7 === 1 ? -1 : 1; return ((sign * principal) / 100).toFixed(3); }; export const obisCodeValueToNumeric = (aNumericString: string): string | null => { const number = parseFloat(aNumericString); if (isNaN(number)) { return null; } return number.toString(); }; export const parseDateAscii = (array: Uint8Array) => { const dateString = charCodesToAscii(array); return moment(dateString, 'YYMMDD').format('YYYY-MM-DD'); }; export const parseTimeAscii = (array: Uint8Array) => { const timeString = charCodesToAscii(array); return moment(timeString, 'HHmmss').format('HH:mm'); }; export const uint8ToBCD = number => { const digit1 = Math.floor(number / 10); // 0000 0010 const digit0 = number % 10; // 0000 0011 return (digit1 << 4) | digit0; }; export const replaceLeadingZeros = aString => { return aString.replace(/^-*#?0*/, ''); }; export const formatCleanNumericValue = (stringValue, decimals) => { const cleanValue = replaceLeadingZeros(stringValue); if (!cleanValue) { return '0'; } return `${parseInt(cleanValue, 10) / 10 ** decimals}`; }; export const formatAlphaDate = anAlphaDate => { if (anAlphaDate === '000000') { return null; } return moment(anAlphaDate, 'YYMMDD').format('YYYY-MM-DD'); }; export const formatAlphaTime = (anAlphaTime, anAlphaDate) => { if (anAlphaDate === null) { return anAlphaDate; } return moment(anAlphaTime, 'HHmmss').format('HH:mm'); }; export const formatAlphaWeekday = weekday => { if (weekday === 0) { return null; } return `${weekday}`; }; const validateAlphaA3Date = uint8AlphaA3Date => { if (!uint8AlphaA3Date) { return null; } const year = uint8AlphaA3Date[0].toString().padStart(2, '0'); const month = uint8AlphaA3Date[1].toString().padStart(2, '0'); const day = uint8AlphaA3Date[2].toString().padStart(2, '0'); const dateString = `${year}${month}${day}`; if (dateString === '000000') { return null; } return dateString; }; export const formatAlphaA3Date = arrayAlphaA3Date => { const dateString = validateAlphaA3Date(arrayAlphaA3Date); if (!dateString) { return dateString; } return moment(dateString, 'YYMMDD').format('YYYY-MM-DD'); }; export const formatAlphaA3Time = (arrayAlphaTime, arrayAlphaA3Date) => { if (!validateAlphaA3Date(arrayAlphaA3Date)) { return null; } const hour = arrayAlphaTime[0].toString().padStart(2, '0'); const minutes = arrayAlphaTime[1].toString().padStart(2, '0'); const alphaTimeString = `${hour}${minutes}`; return moment(alphaTimeString, 'HHmm').format('HH:mm'); }; export const numericObisFormat = aNumericString => { const number = aNumericString .replace(/(^0+)(0(?:\.|$|\D)|[1-9]\d*)(.*)/, (_substr, _p1, p2, p3) => { return p2 + p3; }) .replace(/[^0-9.].*$/, ''); return number; }; export const unitConversion = (aStringNumber, unitConversionFactor, numberToFixed) => { if (unitConversionFactor == null) { return aStringNumber; } const number = parseInt(aStringNumber, 10) / unitConversionFactor; return number.toFixed(numberToFixed); }; export const extractBase64PayloadFromLog = (logLine: string): string => { // Split into lines const lines = logLine.split('\n'); // Filter for Read commands and extract hex values const hexValues: string[] = []; for (const line of lines) { // Check if line contains "Read" if (line.includes('Read ')) { // Extract everything after "Read " const readIndex = line.indexOf('Read '); if (readIndex !== -1) { const hexPart = line.substring(readIndex + 5).trim(); // +5 for "Read " if (hexPart) { hexValues.push(hexPart); } } } } // Concatenate all hex values const allHex = hexValues.join(' '); // Convert to base64 using existing function return hexToBase64(allHex); }; export const isValidChecksum = (data: Uint8Array, checksum_byte: number): boolean => { let xor = 0; for (let i = 0; i < data.length; i++) { xor ^= data[i] || 0; } return xor === checksum_byte; };