1
0

modbus-io.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. const {
  2. parseHexInteger
  3. } = require('../../utils/base-utils.js')
  4. const transport = require('../../transport/ble-core.js')
  5. const modbusProtocol = require('../../protocols/modbus-rtu/index.js')
  6. const {
  7. decodeRegisterValue,
  8. formatCoilDisplayValue,
  9. formatRegisterValue,
  10. getDataType,
  11. getGroupEncodedWords,
  12. getRegisterEncodedWords,
  13. getRegisterWordsFromWordCache,
  14. getRegisterWriteValueText,
  15. isBitRegisterType,
  16. isByteRegister,
  17. parseCoilValue,
  18. splitWordSpans
  19. } = require('../../domain/parameter-groups/model.js')
  20. function getWriteSpanMaxQuantity(totalQuantity, maxPacketLength) {
  21. if (maxPacketLength === 0) return Math.max(1, totalQuantity)
  22. return Math.max(1, modbusProtocol.client.getMaxWriteMultipleRegisterQuantity(maxPacketLength))
  23. }
  24. async function readGroup(group, options = {}) {
  25. const totalQuantity = Math.max(1, group.wordQuantity || group.quantity || 0)
  26. const wordCache = {}
  27. const slaveAddress = modbusProtocol.client.getSharedSlaveAddress()
  28. if (slaveAddress === null) return null
  29. const values = await modbusProtocol.client.readSpans(
  30. slaveAddress,
  31. group.functionCode,
  32. [{
  33. address: group.startAddress,
  34. quantity: totalQuantity
  35. }],
  36. group.name || '参数组读取',
  37. 'parameter-group-read',
  38. {
  39. maxFrameBytes: options.maxPacketLength,
  40. showModal: options.showModal !== false
  41. }
  42. )
  43. if (!values) return null
  44. if (isBitRegisterType(group.registerType)) {
  45. Object.keys(values.coils || {}).forEach((addressText) => {
  46. wordCache[parseHexInteger(addressText)] = Number(values.coils[addressText]) ? 1 : 0
  47. })
  48. } else {
  49. Object.keys(values.words || {}).forEach((addressText) => {
  50. wordCache[parseHexInteger(addressText)] = Number(values.words[addressText]) & 0xFFFF
  51. })
  52. }
  53. return wordCache
  54. }
  55. function getRegisterWordsForWrite(group) {
  56. const words = group.isStructLayout
  57. ? getGroupEncodedWords(group)
  58. : Array.from({ length: Math.max(1, group.wordQuantity || 1) }, () => 0)
  59. if (group.isStructLayout) return words
  60. for (let index = 0; index < group.registers.length; index += 1) {
  61. const register = group.registers[index]
  62. const registerWords = getRegisterEncodedWords(register)
  63. if (!Array.isArray(registerWords) || !registerWords.length) {
  64. throw new Error(`${register.name || `寄存器 ${index + 1}`} 没有有效写入值`)
  65. }
  66. const dataType = getDataType(register.dataType).key
  67. const relativeAddress = Math.max(0, register.address - group.startAddress)
  68. if (isByteRegister(dataType)) {
  69. const byteValue = Number(registerWords[0]) & 0xFF
  70. const currentWord = words[relativeAddress] || 0
  71. words[relativeAddress] = register.byteOffset === 0
  72. ? (((byteValue << 8) | (currentWord & 0x00FF)) & 0xFFFF)
  73. : (((currentWord & 0xFF00) | byteValue) & 0xFFFF)
  74. } else {
  75. for (let offset = 0; offset < register.registerCount; offset += 1) {
  76. words[relativeAddress + offset] = Number(registerWords[offset]) & 0xFFFF
  77. }
  78. }
  79. }
  80. return words
  81. }
  82. function createWrittenRegisterSnapshots(group, words = []) {
  83. const writtenWordCache = words.reduce((cache, word, offset) => {
  84. cache[group.startAddress + offset] = word
  85. return cache
  86. }, {})
  87. return group.registers.map((register) => {
  88. const rawWords = getRegisterWordsFromWordCache(register, writtenWordCache) || []
  89. const rawValue = decodeRegisterValue(register, rawWords)
  90. const displayValue = formatRegisterValue(register, rawValue)
  91. return {
  92. rawWords,
  93. rawValue,
  94. displayValue
  95. }
  96. })
  97. }
  98. async function writeCoilGroup(group, options = {}) {
  99. const slaveAddress = modbusProtocol.client.getSharedSlaveAddress()
  100. if (slaveAddress === null) return null
  101. const writtenRegisters = []
  102. for (let index = 0; index < group.registers.length; index += 1) {
  103. const register = group.registers[index]
  104. const coilValue = parseCoilValue(getRegisterWriteValueText(register))
  105. if (coilValue === null) {
  106. transport.showCommandAlert('参数组写入', `${register.name || `寄存器 ${index + 1}`} 没有有效写入值`)
  107. return null
  108. }
  109. const response = await modbusProtocol.client.writeSingleCoil(
  110. slaveAddress,
  111. group.startAddress + index,
  112. !!coilValue,
  113. register.name || group.name || '参数组写入',
  114. 'parameter-group-coil-write',
  115. {
  116. maxFrameBytes: options.maxPacketLength
  117. }
  118. )
  119. if (!response) return null
  120. writtenRegisters.push({
  121. rawBytes: [],
  122. rawValue: coilValue,
  123. rawWords: [],
  124. displayValue: formatCoilDisplayValue(coilValue)
  125. })
  126. }
  127. return writtenRegisters
  128. }
  129. async function writeRegisterGroup(group, options = {}) {
  130. const slaveAddress = modbusProtocol.client.getSharedSlaveAddress()
  131. if (slaveAddress === null) return null
  132. let words
  133. try {
  134. words = getRegisterWordsForWrite(group)
  135. } catch (error) {
  136. transport.showCommandAlert('参数组写入', error.message || '寄存器组没有有效写入值')
  137. return null
  138. }
  139. const writtenRegisters = createWrittenRegisterSnapshots(group, words)
  140. const maxWriteQuantity = getWriteSpanMaxQuantity(words.length, options.maxPacketLength)
  141. const spans = splitWordSpans(group.startAddress, words.length, maxWriteQuantity)
  142. let cursor = 0
  143. for (const span of spans) {
  144. const spanWords = words.slice(cursor, cursor + span.quantity)
  145. cursor += span.quantity
  146. const response = await modbusProtocol.client.writeMultipleRegisters(
  147. slaveAddress,
  148. span.address,
  149. spanWords,
  150. group.name || '参数组写入',
  151. 'parameter-group-write',
  152. {
  153. maxFrameBytes: options.maxPacketLength
  154. }
  155. )
  156. if (!response) return null
  157. }
  158. return writtenRegisters
  159. }
  160. async function writeGroup(group, options = {}) {
  161. return group.registerType === 'coil'
  162. ? writeCoilGroup(group, options)
  163. : writeRegisterGroup(group, options)
  164. }
  165. module.exports = {
  166. readGroup,
  167. writeGroup
  168. }