const { buildReadFrame, buildWriteMultipleRegistersFrame, buildWriteSingleCoilFrame, buildWriteSingleRegisterFrame, formatHex } = require('../../protocols/modbus-rtu/index.js') const MODBUS_COMMANDS = [ { key: 'readCoils', label: '01 读取线圈', functionCode: 0x01, inputMode: 'quantity' }, { key: 'readDiscreteInputs', label: '02 读取离散输入', functionCode: 0x02, inputMode: 'quantity' }, { key: 'readHolding', label: '03 读取保持寄存器', functionCode: 0x03, inputMode: 'quantity' }, { key: 'readInput', label: '04 读取输入寄存器', functionCode: 0x04, inputMode: 'quantity' }, { key: 'writeCoil', label: '05 写单线圈', functionCode: 0x05, inputMode: 'coil' }, { key: 'writeRegister', label: '06 写单寄存器', functionCode: 0x06, inputMode: 'single' }, { key: 'writeRegisters', label: '10 写多寄存器', functionCode: 0x10, inputMode: 'multiple' } ] function parseHexNumber(value, label, maxValue) { const text = String(value || '').trim().replace(/^0x/i, '') if (!text || !/^[0-9a-fA-F]+$/.test(text)) { throw new Error(`${label}请输入十六进制数值`) } const parsedValue = parseInt(text, 16) if (parsedValue > maxValue) { throw new Error(`${label}超出范围`) } return parsedValue } function parseRegisterValues(value) { const text = String(value || '').trim() if (!text) throw new Error('请输入寄存器写入值') return text.split(/[\s,;]+/) .filter(Boolean) .map((item) => parseHexNumber(item, '写入值', 0xFFFF)) } function getCommand(index) { return MODBUS_COMMANDS[index] || MODBUS_COMMANDS[0] } function getDefaultCommandValue(command) { if (command.inputMode === 'quantity') return '0001' if (command.inputMode === 'coil') return 'ON' if (command.inputMode === 'multiple') return '0000' return '0000' } function generateModbusFrame(command, slaveAddress, registerAddress, commandValue, coilEnabled, commandRegisterQuantity) { const slave = parseHexNumber(slaveAddress, '从站地址', 0xFF) const address = parseHexNumber(registerAddress, '起始地址', 0xFFFF) if (command.inputMode === 'quantity') { const quantity = parseHexNumber(commandValue, '读取数量', 0xFFFF) return buildReadFrame(slave, command.functionCode, address, quantity) } if (command.inputMode === 'coil') { return buildWriteSingleCoilFrame(slave, address, coilEnabled) } if (command.inputMode === 'single') { return buildWriteSingleRegisterFrame(slave, address, parseHexNumber(commandValue, '写入值', 0xFFFF)) } const words = parseRegisterValues(commandValue) const quantity = parseHexNumber(commandRegisterQuantity, '寄存器个数', 0xFFFF) if (quantity !== words.length) { throw new Error(`写入值数量应为 ${quantity} 个寄存器`) } return buildWriteMultipleRegistersFrame(slave, address, words) } function createProtocolState(commandIndex, slaveAddress, registerAddress, commandValue, coilEnabled, commandRegisterQuantity) { const command = getCommand(commandIndex) const commandValueLabel = command.inputMode === 'quantity' ? '读取数量' : '写入值' try { return { commandValueLabel, generatedHex: formatHex(generateModbusFrame(command, slaveAddress, registerAddress, commandValue, coilEnabled, commandRegisterQuantity)), protocolErrorText: '', showCoilValue: command.inputMode === 'coil', showRegisterQuantity: command.inputMode === 'multiple', showCommandValue: command.inputMode !== 'coil' } } catch (error) { return { commandValueLabel, generatedHex: '', protocolErrorText: error.message, showCoilValue: command.inputMode === 'coil', showRegisterQuantity: command.inputMode === 'multiple', showCommandValue: command.inputMode !== 'coil' } } } function buildGeneratedExpectedResponse(state = {}) { try { const command = getCommand(state.commandIndex) const address = parseHexNumber(state.registerAddress, '起始地址', 0xFFFF) const slaveAddress = parseHexNumber(state.slaveAddress, '从站地址', 0xFF) const quantity = command.inputMode === 'quantity' ? parseHexNumber(state.commandValue, '读取数量', 0xFFFF) : (command.inputMode === 'multiple' ? parseHexNumber(state.commandRegisterQuantity, '寄存器个数', 0xFFFF) : 1) const value = command.inputMode === 'coil' ? (state.coilEnabled ? 0xFF00 : 0x0000) : (command.inputMode === 'single' ? parseHexNumber(state.commandValue, '写入值', 0xFFFF) : undefined) return { address, functionCode: command.functionCode, kind: 'manual-rtu', protocol: 'modbus-rtu', quantity, value, slaveAddress } } catch (error) { return null } } function formatResponseHex(value, width) { return Number(value || 0).toString(16).toUpperCase().padStart(width, '0') } function formatGeneratedResponse(response) { if (!response) return '' const slave = formatResponseHex(response.slaveAddress, 2) const functionCode = formatResponseHex(response.functionCode, 2) if (Array.isArray(response.words) && response.words.length) { return `从机 0x${slave} / 功能码 0x${functionCode} / 字 ${response.words.map((word) => formatResponseHex(word, 4)).join(' ')}` } if (Array.isArray(response.dataBytes) && response.dataBytes.length) { return `从机 0x${slave} / 功能码 0x${functionCode} / 数据 ${formatHex(response.dataBytes)}` } if (Number.isInteger(response.address)) { return `从机 0x${slave} / 功能码 0x${functionCode} / 地址 0x${formatResponseHex(response.address, 4)} / 值 0x${formatResponseHex(response.quantityOrValue, 4)}` } return `从机 0x${slave} / 功能码 0x${functionCode}` } module.exports = { MODBUS_COMMANDS, buildGeneratedExpectedResponse, createProtocolState, formatGeneratedResponse, generateModbusFrame, getCommand, getDefaultCommandValue, parseHexNumber, parseRegisterValues }