register-io.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. const {
  2. normalizeTextValue
  3. } = require('../../utils/base-utils.js')
  4. const {
  5. bytesToWords
  6. } = require('../../utils/binary-utils.js')
  7. const {
  8. MAX_MODBUS_ADDRESS
  9. } = require('./constants.js')
  10. const {
  11. decodeRegisterValue,
  12. encodeBitFieldIntoBytes,
  13. encodeRegisterBytes,
  14. encodeRegisterWords,
  15. isBitFieldRegister,
  16. validateRegisterValue: validateCodecRegisterValue
  17. } = require('./value-codec.js')
  18. function normalizeAddress(value, fallback = 0) {
  19. if (typeof value === 'number') {
  20. return Number.isFinite(value) ? Math.max(0, Math.min(Math.round(value), MAX_MODBUS_ADDRESS)) : fallback
  21. }
  22. const text = String(value === undefined || value === null ? '' : value).trim()
  23. if (!text) return fallback
  24. const hexText = text.toUpperCase().startsWith('0X') ? text.slice(2) : text
  25. if (/^[0-9A-F]+$/i.test(hexText)) {
  26. const parsedHex = parseInt(hexText, 16)
  27. return Number.isFinite(parsedHex) ? Math.max(0, Math.min(parsedHex, MAX_MODBUS_ADDRESS)) : fallback
  28. }
  29. const numberValue = Number(text)
  30. return Number.isFinite(numberValue) ? Math.max(0, Math.min(Math.round(numberValue), MAX_MODBUS_ADDRESS)) : fallback
  31. }
  32. function splitWordSpans(startAddress, quantity, maxQuantity) {
  33. const spans = []
  34. let address = normalizeAddress(startAddress, 0)
  35. let remaining = Math.max(0, Math.floor(Number(quantity) || 0))
  36. while (remaining > 0) {
  37. const spanQuantity = Math.min(remaining, maxQuantity)
  38. spans.push({
  39. address,
  40. quantity: spanQuantity
  41. })
  42. address += spanQuantity
  43. remaining -= spanQuantity
  44. }
  45. return spans
  46. }
  47. function getRegisterWriteValueText(register) {
  48. if (register.inputValue !== undefined && register.inputValue !== null) return normalizeTextValue(register.inputValue)
  49. if (register.defaultValue !== undefined && register.defaultValue !== null) return normalizeTextValue(register.defaultValue)
  50. return ''
  51. }
  52. function getRegisterWordsFromWordCache(register, wordCache) {
  53. const words = []
  54. for (let offset = 0; offset < register.registerCount; offset += 1) {
  55. const word = wordCache[register.address + offset]
  56. if (word === undefined) return null
  57. words.push(word)
  58. }
  59. return words
  60. }
  61. function getRegisterBytesFromByteCache(register, byteCache) {
  62. const bytes = []
  63. for (let offset = 0; offset < register.byteLength; offset += 1) {
  64. const byte = byteCache[register.address + offset]
  65. if (byte === undefined) return null
  66. bytes.push(Number(byte) & 0xFF)
  67. }
  68. return bytes
  69. }
  70. function getRegisterWordsFromByteCache(register, byteCache) {
  71. const bytes = getRegisterBytesFromByteCache(register, byteCache)
  72. if (!bytes) return null
  73. return bytesToWords(bytes.length % 2 === 0 ? bytes : bytes.concat(0))
  74. }
  75. function decodeRegisterFromBytes(register, bytes) {
  76. if (!Array.isArray(bytes) || bytes.length < register.byteLength) return null
  77. return decodeRegisterValue(
  78. {
  79. ...register,
  80. byteOffset: 0
  81. },
  82. bytesToWords(bytes.length % 2 === 0 ? bytes : bytes.concat(0))
  83. )
  84. }
  85. function decodeRegisterFromWordCache(register, wordCache) {
  86. const words = getRegisterWordsFromWordCache(register, wordCache)
  87. if (!words) return null
  88. return decodeRegisterValue(register, words)
  89. }
  90. function decodeRegisterFromByteCache(register, byteCache) {
  91. const bytes = getRegisterBytesFromByteCache(register, byteCache)
  92. if (!bytes) return null
  93. return decodeRegisterFromBytes(register, bytes)
  94. }
  95. function getRegisterEncodedWords(register) {
  96. return encodeRegisterWords({
  97. ...register,
  98. inputValue: getRegisterWriteValueText(register)
  99. })
  100. }
  101. function getRegisterEncodedBytes(register) {
  102. return encodeRegisterBytes({
  103. ...register,
  104. inputValue: getRegisterWriteValueText(register)
  105. })
  106. }
  107. function getRegisterByteStart(register, groupStartAddress = 0) {
  108. if (Number.isFinite(Number(register.byteStart))) {
  109. return Math.max(0, Math.floor(Number(register.byteStart)))
  110. }
  111. return Math.max(0, ((Number(register.address) || 0) - (Number(groupStartAddress) || 0)) * 2 + (Number(register.byteOffset) || 0))
  112. }
  113. function getGroupEncodedBytes(group, baseBytes = null) {
  114. const byteLength = Math.max(2, Number(group && group.paddedByteLength) || ((Number(group && group.wordQuantity) || 1) * 2))
  115. const bytes = Array.isArray(baseBytes)
  116. ? baseBytes.slice(0, byteLength).map((byte) => Number(byte) & 0xFF)
  117. : []
  118. const registers = Array.isArray(group && group.registers) ? group.registers : []
  119. while (bytes.length < byteLength) {
  120. bytes.push(0)
  121. }
  122. registers.forEach((register) => {
  123. const byteStart = getRegisterByteStart(register, group.startAddress)
  124. if (isBitFieldRegister(register)) {
  125. const encodedBytes = encodeBitFieldIntoBytes(
  126. {
  127. ...register,
  128. inputValue: getRegisterWriteValueText(register)
  129. },
  130. bytes,
  131. byteStart
  132. )
  133. if (!Array.isArray(encodedBytes)) {
  134. throw new Error(`${register.name || '位域'} 没有有效写入值`)
  135. }
  136. return
  137. }
  138. const registerBytes = encodeRegisterBytes(register)
  139. if (!Array.isArray(registerBytes) || !registerBytes.length) {
  140. throw new Error(`${register.name || '寄存器'} 没有有效写入值`)
  141. }
  142. for (let offset = 0; offset < register.byteLength; offset += 1) {
  143. if (byteStart + offset < bytes.length) {
  144. bytes[byteStart + offset] = Number(registerBytes[offset] || 0) & 0xFF
  145. }
  146. }
  147. })
  148. return bytes
  149. }
  150. function getGroupEncodedWords(group, baseBytes = null) {
  151. return bytesToWords(getGroupEncodedBytes(group, baseBytes))
  152. }
  153. function validateRegisterValue(register, value) {
  154. return validateCodecRegisterValue(
  155. register,
  156. value === undefined || value === null ? getRegisterWriteValueText(register) : value
  157. )
  158. }
  159. module.exports = {
  160. decodeRegisterFromByteCache,
  161. decodeRegisterFromBytes,
  162. decodeRegisterFromWordCache,
  163. getGroupEncodedBytes,
  164. getGroupEncodedWords,
  165. getRegisterBytesFromByteCache,
  166. getRegisterEncodedBytes,
  167. getRegisterEncodedWords,
  168. getRegisterWordsFromByteCache,
  169. getRegisterWordsFromWordCache,
  170. getRegisterWriteValueText,
  171. splitWordSpans,
  172. validateRegisterValue
  173. }