const { parseHexInteger } = require('../../utils/base-utils.js') const transport = require('../../transport/ble-core.js') const modbusProtocol = require('../../protocols/modbus-rtu/index.js') const { decodeRegisterValue, formatCoilDisplayValue, formatRegisterValue, getDataType, getGroupEncodedWords, getRegisterEncodedWords, getRegisterWordsFromWordCache, getRegisterWriteValueText, isBitRegisterType, isByteRegister, parseCoilValue, splitWordSpans } = require('../../domain/parameter-groups/model.js') function getWriteSpanMaxQuantity(totalQuantity, maxPacketLength) { if (maxPacketLength === 0) return Math.max(1, totalQuantity) return Math.max(1, modbusProtocol.client.getMaxWriteMultipleRegisterQuantity(maxPacketLength)) } async function readGroup(group, options = {}) { const totalQuantity = Math.max(1, group.wordQuantity || group.quantity || 0) const wordCache = {} const slaveAddress = modbusProtocol.client.getSharedSlaveAddress() if (slaveAddress === null) return null const values = await modbusProtocol.client.readSpans( slaveAddress, group.functionCode, [{ address: group.startAddress, quantity: totalQuantity }], group.name || '参数组读取', 'parameter-group-read', { maxFrameBytes: options.maxPacketLength, showModal: options.showModal !== false } ) if (!values) return null if (isBitRegisterType(group.registerType)) { Object.keys(values.coils || {}).forEach((addressText) => { wordCache[parseHexInteger(addressText)] = Number(values.coils[addressText]) ? 1 : 0 }) } else { Object.keys(values.words || {}).forEach((addressText) => { wordCache[parseHexInteger(addressText)] = Number(values.words[addressText]) & 0xFFFF }) } return wordCache } function getRegisterWordsForWrite(group) { const words = group.isStructLayout ? getGroupEncodedWords(group) : Array.from({ length: Math.max(1, group.wordQuantity || 1) }, () => 0) if (group.isStructLayout) return words for (let index = 0; index < group.registers.length; index += 1) { const register = group.registers[index] const registerWords = getRegisterEncodedWords(register) if (!Array.isArray(registerWords) || !registerWords.length) { throw new Error(`${register.name || `寄存器 ${index + 1}`} 没有有效写入值`) } const dataType = getDataType(register.dataType).key const relativeAddress = Math.max(0, register.address - group.startAddress) if (isByteRegister(dataType)) { const byteValue = Number(registerWords[0]) & 0xFF const currentWord = words[relativeAddress] || 0 words[relativeAddress] = register.byteOffset === 0 ? (((byteValue << 8) | (currentWord & 0x00FF)) & 0xFFFF) : (((currentWord & 0xFF00) | byteValue) & 0xFFFF) } else { for (let offset = 0; offset < register.registerCount; offset += 1) { words[relativeAddress + offset] = Number(registerWords[offset]) & 0xFFFF } } } return words } function createWrittenRegisterSnapshots(group, words = []) { const writtenWordCache = words.reduce((cache, word, offset) => { cache[group.startAddress + offset] = word return cache }, {}) return group.registers.map((register) => { const rawWords = getRegisterWordsFromWordCache(register, writtenWordCache) || [] const rawValue = decodeRegisterValue(register, rawWords) const displayValue = formatRegisterValue(register, rawValue) return { rawWords, rawValue, displayValue } }) } async function writeCoilGroup(group, options = {}) { const slaveAddress = modbusProtocol.client.getSharedSlaveAddress() if (slaveAddress === null) return null const writtenRegisters = [] for (let index = 0; index < group.registers.length; index += 1) { const register = group.registers[index] const coilValue = parseCoilValue(getRegisterWriteValueText(register)) if (coilValue === null) { transport.showCommandAlert('参数组写入', `${register.name || `寄存器 ${index + 1}`} 没有有效写入值`) return null } const response = await modbusProtocol.client.writeSingleCoil( slaveAddress, group.startAddress + index, !!coilValue, register.name || group.name || '参数组写入', 'parameter-group-coil-write', { maxFrameBytes: options.maxPacketLength } ) if (!response) return null writtenRegisters.push({ rawBytes: [], rawValue: coilValue, rawWords: [], displayValue: formatCoilDisplayValue(coilValue) }) } return writtenRegisters } async function writeRegisterGroup(group, options = {}) { const slaveAddress = modbusProtocol.client.getSharedSlaveAddress() if (slaveAddress === null) return null let words try { words = getRegisterWordsForWrite(group) } catch (error) { transport.showCommandAlert('参数组写入', error.message || '寄存器组没有有效写入值') return null } const writtenRegisters = createWrittenRegisterSnapshots(group, words) const maxWriteQuantity = getWriteSpanMaxQuantity(words.length, options.maxPacketLength) const spans = splitWordSpans(group.startAddress, words.length, maxWriteQuantity) let cursor = 0 for (const span of spans) { const spanWords = words.slice(cursor, cursor + span.quantity) cursor += span.quantity const response = await modbusProtocol.client.writeMultipleRegisters( slaveAddress, span.address, spanWords, group.name || '参数组写入', 'parameter-group-write', { maxFrameBytes: options.maxPacketLength } ) if (!response) return null } return writtenRegisters } async function writeGroup(group, options = {}) { return group.registerType === 'coil' ? writeCoilGroup(group, options) : writeRegisterGroup(group, options) } module.exports = { readGroup, writeGroup }