| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- const {
- BYTE_ORDER_LOW,
- appendCrc16Modbus,
- hasValidCrc16Modbus
- } = require('../../utils/crc.js')
- const MODBUS_CRC_OPTIONS = {
- byteOrder: BYTE_ORDER_LOW
- }
- const MAX_MODBUS_DMA_BYTES = 64
- const MODBUS_READ_RESPONSE_OVERHEAD = 5
- const MODBUS_WRITE_MULTIPLE_REQUEST_OVERHEAD = 9
- const MODBUS_CODE_INFO_FUNCTION_CODE = 0x40
- const MODBUS_DEBUG_READ_FUNCTION_CODE = 0x41
- const MODBUS_DEBUG_WRITE_FUNCTION_CODE = 0x42
- const MODBUS_CODE_INFO_RESPONSE_LENGTH = 9
- const MODBUS_DEBUG_READ_RESPONSE_OVERHEAD = 6
- const MODBUS_DEBUG_WRITE_REQUEST_OVERHEAD = 9
- const MAX_READ_REGISTER_QUANTITY = Math.floor((MAX_MODBUS_DMA_BYTES - MODBUS_READ_RESPONSE_OVERHEAD) / 2)
- const MAX_READ_COIL_QUANTITY = (MAX_MODBUS_DMA_BYTES - MODBUS_READ_RESPONSE_OVERHEAD) * 8
- const MAX_WRITE_MULTIPLE_REGISTER_QUANTITY = Math.floor((MAX_MODBUS_DMA_BYTES - MODBUS_WRITE_MULTIPLE_REQUEST_OVERHEAD) / 2)
- const UNLIMITED_FRAME_BYTES = 0
- function toByte(value, label) {
- if (!Number.isInteger(value) || value < 0 || value > 0xFF) {
- throw new Error(`${label}必须在 0x00 至 0xFF 之间`)
- }
- return value
- }
- function toWord(value, label) {
- if (!Number.isInteger(value) || value < 0 || value > 0xFFFF) {
- throw new Error(`${label}必须在 0x0000 至 0xFFFF 之间`)
- }
- return value
- }
- function toMemoryType(value) {
- const memoryType = toByte(value, '内存区域')
- if (memoryType > 0x03) {
- throw new Error('内存区域必须为 data/idata/xdata/code')
- }
- return memoryType
- }
- function toDebugByteLength(value, label = '字节长度') {
- const byteLength = toWord(value, label)
- if (byteLength === 0) {
- throw new Error(`${label}必须大于 0`)
- }
- if (byteLength > 0xFF) {
- throw new Error('单帧字节长度不能超过 255')
- }
- return byteLength
- }
- function splitWord(value) {
- return [(value >> 8) & 0xFF, value & 0xFF]
- }
- function normalizeMaxFrameBytes(maxFrameBytes = MAX_MODBUS_DMA_BYTES) {
- const numberValue = Number(maxFrameBytes)
- if (Number.isFinite(numberValue) && Math.round(numberValue) === UNLIMITED_FRAME_BYTES) return UNLIMITED_FRAME_BYTES
- if (Number.isFinite(numberValue) && numberValue > 0) return Math.round(numberValue)
- return MAX_MODBUS_DMA_BYTES
- }
- function getMaxReadQuantity(functionCode, maxFrameBytes = MAX_MODBUS_DMA_BYTES) {
- const frameBytes = normalizeMaxFrameBytes(maxFrameBytes)
- if (frameBytes === UNLIMITED_FRAME_BYTES) return 0xFFFF
- const dataBytes = frameBytes - MODBUS_READ_RESPONSE_OVERHEAD
- if (dataBytes <= 0) return 0
- if (functionCode === 0x01 || functionCode === 0x02) return dataBytes * 8
- if (functionCode === 0x03 || functionCode === 0x04) return Math.floor(dataBytes / 2)
- return 0
- }
- function getMaxWriteMultipleRegisterQuantity(maxFrameBytes = MAX_MODBUS_DMA_BYTES) {
- const frameBytes = normalizeMaxFrameBytes(maxFrameBytes)
- if (frameBytes === UNLIMITED_FRAME_BYTES) return 0xFFFF
- return Math.max(0, Math.floor((frameBytes - MODBUS_WRITE_MULTIPLE_REQUEST_OVERHEAD) / 2))
- }
- function getMaxDebugReadByteLength(maxFrameBytes = MAX_MODBUS_DMA_BYTES) {
- const frameBytes = normalizeMaxFrameBytes(maxFrameBytes)
- if (frameBytes === UNLIMITED_FRAME_BYTES) return 0xFF
- return Math.max(0, Math.min(0xFF, frameBytes - MODBUS_DEBUG_READ_RESPONSE_OVERHEAD))
- }
- function getMaxDebugWriteByteLength(maxFrameBytes = MAX_MODBUS_DMA_BYTES) {
- const frameBytes = normalizeMaxFrameBytes(maxFrameBytes)
- if (frameBytes === UNLIMITED_FRAME_BYTES) return 0xFF
- return Math.max(0, Math.min(0xFF, frameBytes - MODBUS_DEBUG_WRITE_REQUEST_OVERHEAD))
- }
- function buildReadFrame(slaveAddress, functionCode, address, quantity, options = {}) {
- const slave = toByte(slaveAddress, '从站地址')
- const command = toByte(functionCode, '功能码')
- const startAddress = toWord(address, '寄存器地址')
- const registerQuantity = toWord(quantity, '读取数量')
- const maxQuantity = getMaxReadQuantity(command, options.maxFrameBytes)
- if (![0x01, 0x02, 0x03, 0x04].includes(command)) {
- throw new Error('当前功能码不是读取命令')
- }
- if (registerQuantity === 0) {
- throw new Error('读取数量必须大于 0')
- }
- if ([0x03, 0x04].includes(command) && maxQuantity > 0 && registerQuantity > maxQuantity) {
- throw new Error(`单帧最多读取 ${maxQuantity} 个寄存器`)
- }
- if ((command === 0x01 || command === 0x02) && maxQuantity > 0 && registerQuantity > maxQuantity) {
- throw new Error(`单帧最多读取 ${maxQuantity} 个位状态`)
- }
- return appendCrc16Modbus(
- [slave, command].concat(splitWord(startAddress), splitWord(registerQuantity)),
- MODBUS_CRC_OPTIONS
- )
- }
- function buildWriteSingleCoilFrame(slaveAddress, address, checked) {
- const slave = toByte(slaveAddress, '从站地址')
- const startAddress = toWord(address, '线圈地址')
- const outputValue = checked ? 0xFF00 : 0x0000
- return appendCrc16Modbus(
- [slave, 0x05].concat(splitWord(startAddress), splitWord(outputValue)),
- MODBUS_CRC_OPTIONS
- )
- }
- function buildWriteSingleRegisterFrame(slaveAddress, address, value) {
- const slave = toByte(slaveAddress, '从站地址')
- const startAddress = toWord(address, '寄存器地址')
- const registerValue = toWord(value, '写入值')
- return appendCrc16Modbus(
- [slave, 0x06].concat(splitWord(startAddress), splitWord(registerValue)),
- MODBUS_CRC_OPTIONS
- )
- }
- function buildWriteMultipleRegistersFrame(slaveAddress, address, values, options = {}) {
- const slave = toByte(slaveAddress, '从站地址')
- const startAddress = toWord(address, '寄存器地址')
- const maxQuantity = getMaxWriteMultipleRegisterQuantity(options.maxFrameBytes)
- if (!Array.isArray(values) || values.length === 0) {
- throw new Error('请输入至少一个寄存器写入值')
- }
- if (maxQuantity > 0 && values.length > maxQuantity) {
- throw new Error(`单帧最多写入 ${maxQuantity} 个寄存器`)
- }
- const registerBytes = values.reduce((result, value) => {
- return result.concat(splitWord(toWord(value, '写入值')))
- }, [])
- return appendCrc16Modbus(
- [slave, 0x10]
- .concat(splitWord(startAddress), splitWord(values.length), [registerBytes.length], registerBytes),
- MODBUS_CRC_OPTIONS
- )
- }
- function buildCodeInfoQueryFrame(slaveAddress) {
- const slave = toByte(slaveAddress, '从站地址')
- return appendCrc16Modbus(
- [slave, MODBUS_CODE_INFO_FUNCTION_CODE],
- MODBUS_CRC_OPTIONS
- )
- }
- function buildDebugReadMemoryFrame(slaveAddress, memoryType, address, byteLength, options = {}) {
- const slave = toByte(slaveAddress, '从站地址')
- const memType = toMemoryType(memoryType)
- const startAddress = toWord(address, '内存地址')
- const length = toDebugByteLength(byteLength)
- const maxByteLength = getMaxDebugReadByteLength(options.maxFrameBytes)
- if (maxByteLength > 0 && length > maxByteLength) {
- throw new Error(`单帧最多读取 ${maxByteLength} 字节`)
- }
- return appendCrc16Modbus(
- [slave, MODBUS_DEBUG_READ_FUNCTION_CODE, memType].concat(splitWord(startAddress), splitWord(length)),
- MODBUS_CRC_OPTIONS
- )
- }
- function buildDebugWriteMemoryFrame(slaveAddress, memoryType, address, bytes, options = {}) {
- const slave = toByte(slaveAddress, '从站地址')
- const memType = toMemoryType(memoryType)
- const startAddress = toWord(address, '内存地址')
- const dataBytes = Array.prototype.slice.call(bytes || []).map((byte) => toByte(Number(byte), '写入字节'))
- const length = toDebugByteLength(dataBytes.length)
- const maxByteLength = getMaxDebugWriteByteLength(options.maxFrameBytes)
- if (memType === 0x03) {
- throw new Error('code 区暂不支持写入')
- }
- if (maxByteLength > 0 && length > maxByteLength) {
- throw new Error(`单帧最多写入 ${maxByteLength} 字节`)
- }
- return appendCrc16Modbus(
- [slave, MODBUS_DEBUG_WRITE_FUNCTION_CODE, memType]
- .concat(splitWord(startAddress), splitWord(length), dataBytes),
- MODBUS_CRC_OPTIONS
- )
- }
- function formatHex(bytes) {
- return bytes.map((byte) => byte.toString(16).padStart(2, '0').toUpperCase()).join(' ')
- }
- function getReadResponseByteLength(functionCode, quantity) {
- if (functionCode === 0x01 || functionCode === 0x02) return MODBUS_READ_RESPONSE_OVERHEAD + Math.ceil(Number(quantity || 0) / 8)
- if (functionCode === 0x03 || functionCode === 0x04) return MODBUS_READ_RESPONSE_OVERHEAD + Number(quantity || 0) * 2
- if (functionCode === MODBUS_CODE_INFO_FUNCTION_CODE) return MODBUS_CODE_INFO_RESPONSE_LENGTH
- if (functionCode === MODBUS_DEBUG_READ_FUNCTION_CODE) return MODBUS_DEBUG_READ_RESPONSE_OVERHEAD + Number(quantity || 0)
- if (functionCode === MODBUS_DEBUG_WRITE_FUNCTION_CODE) return 9
- return 0
- }
- module.exports = {
- MAX_MODBUS_DMA_BYTES,
- MAX_READ_COIL_QUANTITY,
- MAX_READ_REGISTER_QUANTITY,
- MAX_WRITE_MULTIPLE_REGISTER_QUANTITY,
- MODBUS_CRC_OPTIONS,
- MODBUS_CODE_INFO_FUNCTION_CODE,
- MODBUS_CODE_INFO_RESPONSE_LENGTH,
- MODBUS_DEBUG_READ_FUNCTION_CODE,
- MODBUS_DEBUG_WRITE_FUNCTION_CODE,
- UNLIMITED_FRAME_BYTES,
- buildCodeInfoQueryFrame,
- buildDebugReadMemoryFrame,
- buildDebugWriteMemoryFrame,
- buildReadFrame,
- buildWriteMultipleRegistersFrame,
- buildWriteSingleCoilFrame,
- buildWriteSingleRegisterFrame,
- formatHex,
- getMaxDebugReadByteLength,
- getMaxDebugWriteByteLength,
- getMaxWriteMultipleRegisterQuantity,
- getMaxReadQuantity,
- getReadResponseByteLength,
- hasValidCrc16Modbus
- }
|