const { formatFixedValue } = require('../../utils/base-utils.js') const { getDataType, isHexRegister } = require('./value-types.js') function padWordHex(value) { return Number(value || 0).toString(16).toUpperCase().padStart(4, '0') } function dataTypeIsFloat(dataType) { const type = getDataType(dataType).key return type === 'float' || type === 'double' } function dataTypeIsSignedInteger(dataType) { return /^int(?:8|16|32|64|128|256)_t$/.test(getDataType(dataType).key) } function dataTypeIsWideInteger(dataType) { return /^(?:u?int)(?:64|128|256)_t$/.test(getDataType(dataType).key) } function getIntegerBitWidth(dataType) { const match = getDataType(dataType).key.match(/^(?:u?int)(\d+)_t$/) return match ? Number(match[1]) : 0 } function normalizeBigIntValue(value) { if (typeof value === 'bigint') return value const text = String(value === undefined || value === null ? '' : value).trim() if (!text) return null try { if (/^[-+]0x/i.test(text)) { const sign = text[0] === '-' ? -1n : 1n return sign * BigInt(`0x${text.slice(3)}`) } return BigInt(text) } catch (error) { return null } } function formatIntegerValue(value, dataType) { const type = getDataType(dataType).key const bigValue = normalizeBigIntValue(value) if (dataTypeIsWideInteger(type)) return bigValue === null ? '--' : bigValue.toString() const numberValue = Number(value) if (!Number.isFinite(numberValue)) return '--' if (type === 'int8_t') return String(((Math.round(numberValue) << 24) >> 24)) if (type === 'uint8_t') return String(Math.round(numberValue) & 0xFF) if (type === 'int16_t') return String(((Math.round(numberValue) << 16) >> 16)) if (type === 'uint16_t') return String(Math.round(numberValue) & 0xFFFF) if (type === 'int32_t') return String((Math.round(numberValue) | 0)) if (type === 'uint32_t') return String(Math.round(numberValue) >>> 0) return String(Math.round(numberValue)) } function formatHexValue(value) { const numberValue = Number(value) if (!Number.isFinite(numberValue)) return '--' return `0x${padWordHex(Math.round(numberValue) & 0xFFFF)}` } function formatFloatValue(value) { return formatFixedValue(value, 6).replace(/(\.\d*?)0+$/, '$1').replace(/\.$/, '') } function isLittleEndian(byteOrder) { const text = String(byteOrder || '').trim().toLowerCase() return text === 'little' || text === 'le' || text === '1' } function applyByteOrder(bytes, byteOrder) { const source = Array.isArray(bytes) ? bytes.slice() : [] return isLittleEndian(byteOrder) ? source.reverse() : source } function parseIntegerText(value) { const text = String(value === undefined || value === null ? '' : value).trim() if (!text) return null const isHex = /^[-+]?0x[0-9a-f]+$/i.test(text) || /^0x[0-9a-f]+$/i.test(text) const parsed = isHex ? parseInt(text, 16) : Number(text) return Number.isFinite(parsed) ? parsed : null } function parseHexText(value) { const text = String(value === undefined || value === null ? '' : value).trim() if (!text) return null const hexText = text.toUpperCase().startsWith('0X') ? text.slice(2) : text if (/^[0-9A-F]{1,4}$/i.test(hexText)) { const parsedHex = parseInt(hexText, 16) return Number.isFinite(parsedHex) ? parsedHex : null } return null } function getNumericRange(dataType) { const type = getDataType(dataType).key if (type === 'int8_t') return { max: 127, min: -128 } if (type === 'uint8_t') return { max: 0xFF, min: 0 } if (type === 'int16_t') return { max: 32767, min: -32768 } if (type === 'uint16_t') return { max: 0xFFFF, min: 0 } if (type === 'int32_t') return { max: 2147483647, min: -2147483648 } if (type === 'uint32_t') return { max: 0xFFFFFFFF, min: 0 } if (dataTypeIsWideInteger(type)) { const width = getIntegerBitWidth(type) const max = dataTypeIsSignedInteger(type) ? (1n << BigInt(width - 1)) - 1n : (1n << BigInt(width)) - 1n const min = dataTypeIsSignedInteger(type) ? -(1n << BigInt(width - 1)) : 0n return { max, min } } if (type === 'hex') return { max: 0xFFFF, min: 0 } return { max: Number.POSITIVE_INFINITY, min: Number.NEGATIVE_INFINITY } } function parseNumberText(value, dataType) { const text = String(value === undefined || value === null ? '' : value).trim() if (!text || text === '--') return null if (dataTypeIsFloat(dataType)) { const parsed = Number(text) return Number.isFinite(parsed) ? parsed : null } if (isHexRegister(dataType)) return parseHexText(text) if (dataTypeIsWideInteger(dataType)) return normalizeBigIntValue(text) return parseIntegerText(text) } function parseRangeBoundary(value, dataType, label) { const text = String(value === undefined || value === null ? '' : value).trim() if (!text) return null const parsed = parseNumberText(text, dataType) if (parsed === null) { throw new Error(`${label}无效`) } return parsed } function validateNumericValue(register, value) { const dataType = getDataType(register.dataType).key const range = getNumericRange(dataType) const isWideInteger = dataTypeIsWideInteger(dataType) const numberValue = isWideInteger ? normalizeBigIntValue(value) : Number(value) if (isWideInteger && numberValue === null) return false if (!isWideInteger && !Number.isFinite(numberValue)) return false if (!dataTypeIsFloat(dataType) && !isWideInteger && Math.round(numberValue) !== numberValue) { throw new Error(`${register.name || '寄存器'} 需要整数`) } if (numberValue < range.min || numberValue > range.max) { throw new Error(`${register.name || '寄存器'} 超出 ${dataType} 范围`) } const minValue = parseRangeBoundary(register.minValue, dataType, `${register.name || '寄存器'} 最小值`) const maxValue = parseRangeBoundary(register.maxValue, dataType, `${register.name || '寄存器'} 最大值`) if (minValue !== null && numberValue < minValue) { throw new Error(`${register.name || '寄存器'} 小于限制最小值`) } if (maxValue !== null && numberValue > maxValue) { throw new Error(`${register.name || '寄存器'} 大于限制最大值`) } return true } function floatToBytes(value, byteOrder = 'big', byteLength = 4) { const length = byteLength === 8 ? 8 : 4 const buffer = new ArrayBuffer(length) const view = new DataView(buffer) if (length === 8) { view.setFloat64(0, Number(value), false) } else { view.setFloat32(0, Number(value), false) } return applyByteOrder(Array.from({ length }, (_, index) => view.getUint8(index)), byteOrder) } function bytesToFloatValue(bytes, byteOrder = 'big', byteLength = 4) { const length = byteLength === 8 ? 8 : 4 if (!Array.isArray(bytes) || bytes.length < length) return null const buffer = new ArrayBuffer(length) const view = new DataView(buffer) const source = applyByteOrder(bytes.slice(0, length), byteOrder) for (let index = 0; index < length; index += 1) { view.setUint8(index, Number(source[index]) & 0xFF) } return length === 8 ? view.getFloat64(0, false) : view.getFloat32(0, false) } function unsignedIntegerToBytes(value, byteLength, byteOrder = 'big') { let numberValue = normalizeBigIntValue(value) if (numberValue === null) numberValue = 0n const bytes = [] if (numberValue < 0) { numberValue += 1n << BigInt(byteLength * 8) } for (let index = byteLength - 1; index >= 0; index -= 1) { bytes[index] = Number(numberValue & 0xFFn) numberValue >>= 8n } return applyByteOrder(bytes, byteOrder) } function bytesToUnsignedInteger(bytes, byteOrder = 'big') { const source = applyByteOrder(bytes, byteOrder) const bigValue = source.reduce((value, byte) => ((value << 8n) + BigInt(Number(byte) & 0xFF)), 0n) return source.length > 4 ? bigValue.toString() : Number(bigValue) } function bytesToSignedInteger(bytes, byteOrder = 'big') { const source = applyByteOrder(bytes, byteOrder) const unsignedValue = source.reduce((value, byte) => ((value << 8n) + BigInt(Number(byte) & 0xFF)), 0n) const signLimit = 1n << BigInt(source.length * 8 - 1) const fullRange = 1n << BigInt(source.length * 8) const signedValue = unsignedValue >= signLimit ? unsignedValue - fullRange : unsignedValue return source.length > 4 ? signedValue.toString() : Number(signedValue) } module.exports = { bytesToFloatValue, bytesToSignedInteger, bytesToUnsignedInteger, dataTypeIsFloat, dataTypeIsSignedInteger, dataTypeIsWideInteger, floatToBytes, formatFloatValue, formatHexValue, formatIntegerValue, getNumericRange, parseHexText, parseIntegerText, parseNumberText, parseRangeBoundary, unsignedIntegerToBytes, validateNumericValue }