| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- 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
- }
|