| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- const {
- normalizeTextValue
- } = require('../../utils/base-utils.js')
- const {
- bytesToWords,
- wordsToBytes
- } = require('../../utils/binary-utils.js')
- const {
- alignEvenByteLength,
- getBitFieldByteLength,
- getBitFieldMaxValue,
- getDataType,
- getDataTypeIndex,
- getEncodeByteLimit,
- getRegisterByteLength,
- getRegisterTextByteLength,
- getRegisterValueTypeLabel,
- getRegisterWordCount,
- getRegisterWordCountAtOffset,
- isBitFieldRegister,
- isBitRegisterType,
- isByteRegister,
- isHexRegister,
- isNumericRegister,
- isTextRegister,
- normalizeBitOffset,
- normalizeBitWidth,
- normalizeTextByteLength,
- supportsRange,
- supportsUnit
- } = require('./value-types.js')
- const {
- decodeTextBytes,
- encodeTextBytes
- } = require('./value-text.js')
- const {
- bytesToFloatValue,
- bytesToSignedInteger,
- bytesToUnsignedInteger,
- floatToBytes,
- formatFloatValue,
- formatHexValue,
- formatIntegerValue,
- parseNumberText,
- unsignedIntegerToBytes,
- validateNumericValue
- } = require('./value-number.js')
- function padWordHex(value) {
- return Number(value || 0).toString(16).toUpperCase().padStart(4, '0')
- }
- function formatRawWordText(words = []) {
- if (!Array.isArray(words) || !words.length) return '--'
- return words.map((word) => `0x${padWordHex(word)}`).join(' ')
- }
- function formatRawByteText(bytes = []) {
- if (!Array.isArray(bytes) || !bytes.length) return '--'
- return bytes.map((byte) => `0x${(Number(byte) & 0xFF).toString(16).toUpperCase().padStart(2, '0')}`).join(' ')
- }
- function formatRawByteTextWithDefault(bytes = [], byteLength = 1) {
- const safeLength = Math.max(1, Math.floor(Number(byteLength) || 1))
- const source = Array.isArray(bytes) ? bytes : []
- return Array.from({ length: safeLength }, (_, index) => (
- `0x${(Number(source[index] || 0) & 0xFF).toString(16).toUpperCase().padStart(2, '0')}`
- )).join(' ')
- }
- function parseCoilValue(value) {
- const text = String(value === undefined || value === null ? '' : value).trim()
- if (!text || text === '--') return null
- if (['1', 'true', 'TRUE', 'on', 'ON', '开'].includes(text)) return 1
- if (['0', 'false', 'FALSE', 'off', 'OFF', '关'].includes(text)) return 0
- const coilValue = Number(text)
- return Number.isFinite(coilValue) ? (coilValue ? 1 : 0) : null
- }
- function parseBitFieldValue(register, valueText) {
- const text = normalizeTextValue(valueText).trim()
- const bitWidth = normalizeBitWidth(register.bitWidth)
- let parsed = parseNumberText(text, 'uint32_t')
- const maxValue = getBitFieldMaxValue(register)
- if (parsed === null && bitWidth === 1) parsed = parseCoilValue(text)
- if (parsed === null) return null
- if (Math.round(parsed) !== parsed || parsed < 0 || parsed > maxValue) {
- throw new Error(`${register.name || '位域'} 超出 0 - ${maxValue} 范围`)
- }
- return Math.round(parsed)
- }
- function decodeBitFieldBytes(register, bytes = []) {
- const bitOffset = normalizeBitOffset(register.bitOffset)
- const bitWidth = normalizeBitWidth(register.bitWidth)
- let byteIndex = 0
- let currentBitOffset = bitOffset
- let multiplier = 1
- let remaining = bitWidth
- let value = 0
- while (remaining > 0 && byteIndex < bytes.length) {
- const take = Math.min(8 - currentBitOffset, remaining)
- const mask = (1 << take) - 1
- const part = ((Number(bytes[byteIndex]) & 0xFF) >> currentBitOffset) & mask
- value += part * multiplier
- multiplier *= Math.pow(2, take)
- remaining -= take
- byteIndex += 1
- currentBitOffset = 0
- }
- return remaining > 0 ? null : value
- }
- function encodeBitFieldIntoBytes(register, bytes, byteStart = 0) {
- const valueText = normalizeTextValue(register.inputValue)
- let value = parseBitFieldValue(register, valueText)
- const bitOffset = normalizeBitOffset(register.bitOffset)
- const bitWidth = normalizeBitWidth(register.bitWidth)
- let byteIndex = Math.max(0, Math.floor(Number(byteStart) || 0))
- let currentBitOffset = bitOffset
- let remaining = bitWidth
- if (value === null) return null
- while (remaining > 0) {
- const take = Math.min(8 - currentBitOffset, remaining)
- const mask = (1 << take) - 1
- const shiftedMask = (mask << currentBitOffset) & 0xFF
- const part = value & mask
- if (byteIndex >= bytes.length) return null
- bytes[byteIndex] = ((Number(bytes[byteIndex]) & 0xFF) & (~shiftedMask & 0xFF))
- | ((part << currentBitOffset) & shiftedMask)
- value = Math.floor(value / Math.pow(2, take))
- remaining -= take
- byteIndex += 1
- currentBitOffset = 0
- }
- return bytes
- }
- function encodeBitFieldBytes(register) {
- const bytes = Array.from({ length: getBitFieldByteLength(register) }, () => 0)
- return encodeBitFieldIntoBytes(register, bytes, 0)
- }
- function getRegisterDataBytes(register, words) {
- const dataType = getDataType(register.dataType).key
- const byteLength = getRegisterByteLength(dataType, register)
- const byteOffset = Math.max(0, Math.floor(Number(register.byteOffset) || 0))
- const sourceBytes = wordsToBytes(words, Math.max(0, (Array.isArray(words) ? words.length : 0) * 2))
- return sourceBytes.slice(byteOffset, byteOffset + byteLength)
- }
- function encodeRegisterBytes(register) {
- const dataType = getDataType(register.dataType).key
- const valueText = normalizeTextValue(register.inputValue)
- const byteLength = getRegisterByteLength(dataType, register)
- if (isBitFieldRegister(register)) {
- return encodeBitFieldBytes(register)
- }
- if (isTextRegister(dataType)) {
- const byteLimit = getEncodeByteLimit(register)
- const bytes = encodeTextBytes(valueText, dataType, byteLimit)
- const paddedBytes = bytes.slice()
- while (paddedBytes.length < byteLength) {
- paddedBytes.push(0)
- }
- return paddedBytes.slice(0, byteLength)
- }
- const numberValue = parseNumberText(valueText, dataType)
- if (numberValue === null) return null
- validateNumericValue(register, numberValue)
- if (dataType === 'float') return floatToBytes(numberValue)
- const rounded = Math.round(numberValue)
- if (dataType === 'int8_t' || dataType === 'uint8_t') return [rounded & 0xFF]
- if (dataType === 'int16_t' || dataType === 'uint16_t' || dataType === 'hex') {
- return unsignedIntegerToBytes(rounded, 2)
- }
- if (dataType === 'int32_t' || dataType === 'uint32_t') {
- return unsignedIntegerToBytes(rounded, 4)
- }
- return unsignedIntegerToBytes(rounded, byteLength)
- }
- function encodeRegisterWords(register) {
- const dataType = getDataType(register.dataType).key
- const bytes = encodeRegisterBytes(register)
- if (!Array.isArray(bytes)) return null
- if (isByteRegister(dataType)) return [bytes[0] & 0xFF]
- return bytesToWords(bytes.length % 2 === 0 ? bytes : bytes.concat(0))
- }
- function decodeRegisterValue(register, words) {
- const dataType = getDataType(register.dataType).key
- if (!Array.isArray(words) || words.length < getRegisterWordCountAtOffset(dataType, register.byteOffset || 0, register)) return null
- const bytes = getRegisterDataBytes(register, words)
- const byteLength = getRegisterByteLength(dataType, register)
- if (bytes.length < byteLength) return null
- if (isBitFieldRegister(register)) {
- return decodeBitFieldBytes(register, bytes)
- }
- if (isTextRegister(dataType)) {
- return decodeTextBytes(bytes.slice(0, getEncodeByteLimit(register)), dataType)
- }
- if (dataType === 'float') {
- return bytesToFloatValue(bytes)
- }
- if (dataType === 'int8_t') {
- const byteValue = bytes[0] & 0xFF
- return byteValue & 0x80 ? byteValue - 0x100 : byteValue
- }
- if (dataType === 'uint8_t') {
- return bytes[0] & 0xFF
- }
- if (dataType === 'int16_t') {
- return bytesToSignedInteger(bytes.slice(0, 2))
- }
- if (dataType === 'uint16_t') {
- return bytesToUnsignedInteger(bytes.slice(0, 2))
- }
- if (dataType === 'hex') {
- return bytesToUnsignedInteger(bytes.slice(0, 2))
- }
- if (dataType === 'int32_t') {
- return bytesToSignedInteger(bytes.slice(0, 4))
- }
- return bytesToUnsignedInteger(bytes.slice(0, 4))
- }
- function formatRegisterValue(register, rawValue) {
- if (rawValue === null || rawValue === undefined) return '--'
- const dataType = getDataType(register.dataType).key
- if (isTextRegister(dataType)) return normalizeTextValue(rawValue)
- if (dataType === 'hex') return formatHexValue(rawValue)
- if (dataType === 'float') return formatFloatValue(rawValue)
- return formatIntegerValue(rawValue, dataType)
- }
- function formatCoilDisplayValue(value) {
- return Number(value) ? '1' : '0'
- }
- function registerTypeIsBit(register) {
- return !!register && isBitRegisterType(register.registerType)
- }
- function validateRegisterValue(register, value) {
- const valueText = normalizeTextValue(
- value === undefined || value === null ? '' : value
- ).trim()
- if (!valueText || valueText === '--') return true
- if (registerTypeIsBit(register)) {
- if (parseCoilValue(valueText) === null) {
- throw new Error(`${register.name || '线圈'} 只能填写 0 或 1`)
- }
- return true
- }
- if (isBitFieldRegister(register)) {
- if (parseBitFieldValue(register, valueText) === null) {
- throw new Error(`${register.name || '位域'} 输入值无效`)
- }
- return true
- }
- const dataType = getDataType(register.dataType).key
- if (isTextRegister(dataType)) {
- encodeTextBytes(valueText, dataType, getEncodeByteLimit(register))
- return true
- }
- const numberValue = parseNumberText(valueText, dataType)
- if (numberValue === null) {
- throw new Error(`${register.name || '寄存器'} 输入值无效`)
- }
- return validateNumericValue(register, numberValue)
- }
- module.exports = {
- alignEvenByteLength,
- decodeRegisterValue,
- encodeBitFieldIntoBytes,
- encodeRegisterBytes,
- encodeRegisterWords,
- encodeTextBytes,
- formatCoilDisplayValue,
- formatRawByteText,
- formatRawByteTextWithDefault,
- formatRawWordText,
- formatRegisterValue,
- getBitFieldByteLength,
- getBitFieldMaxValue,
- getDataType,
- getDataTypeIndex,
- getEncodeByteLimit,
- getRegisterByteLength,
- getRegisterTextByteLength,
- getRegisterValueTypeLabel,
- getRegisterWordCount,
- getRegisterWordCountAtOffset,
- isBitFieldRegister,
- isBitRegisterType,
- isByteRegister,
- isHexRegister,
- isNumericRegister,
- isTextRegister,
- normalizeBitOffset,
- normalizeBitWidth,
- normalizeTextByteLength,
- parseCoilValue,
- parseNumberText,
- registerTypeIsBit,
- supportsRange,
- supportsUnit,
- validateRegisterValue
- }
|