value-codec.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. const {
  2. normalizeTextValue
  3. } = require('../../utils/base-utils.js')
  4. const {
  5. bytesToWords,
  6. wordsToBytes
  7. } = require('../../utils/binary-utils.js')
  8. const {
  9. alignEvenByteLength,
  10. getBitFieldByteLength,
  11. getBitFieldMaxValue,
  12. getDataType,
  13. getDataTypeIndex,
  14. getEncodeByteLimit,
  15. getRegisterByteLength,
  16. getRegisterTextByteLength,
  17. getRegisterValueTypeLabel,
  18. getRegisterWordCount,
  19. getRegisterWordCountAtOffset,
  20. isBitFieldRegister,
  21. isBitRegisterType,
  22. isByteRegister,
  23. isHexRegister,
  24. isNumericRegister,
  25. isTextRegister,
  26. normalizeBitOffset,
  27. normalizeBitWidth,
  28. normalizeTextByteLength,
  29. supportsRange,
  30. supportsUnit
  31. } = require('./value-types.js')
  32. const {
  33. decodeTextBytes,
  34. encodeTextBytes
  35. } = require('./value-text.js')
  36. const {
  37. bytesToFloatValue,
  38. bytesToSignedInteger,
  39. bytesToUnsignedInteger,
  40. floatToBytes,
  41. formatFloatValue,
  42. formatHexValue,
  43. formatIntegerValue,
  44. parseNumberText,
  45. unsignedIntegerToBytes,
  46. validateNumericValue
  47. } = require('./value-number.js')
  48. function padWordHex(value) {
  49. return Number(value || 0).toString(16).toUpperCase().padStart(4, '0')
  50. }
  51. function formatRawWordText(words = []) {
  52. if (!Array.isArray(words) || !words.length) return '--'
  53. return words.map((word) => `0x${padWordHex(word)}`).join(' ')
  54. }
  55. function formatRawByteText(bytes = []) {
  56. if (!Array.isArray(bytes) || !bytes.length) return '--'
  57. return bytes.map((byte) => `0x${(Number(byte) & 0xFF).toString(16).toUpperCase().padStart(2, '0')}`).join(' ')
  58. }
  59. function formatRawByteTextWithDefault(bytes = [], byteLength = 1) {
  60. const safeLength = Math.max(1, Math.floor(Number(byteLength) || 1))
  61. const source = Array.isArray(bytes) ? bytes : []
  62. return Array.from({ length: safeLength }, (_, index) => (
  63. `0x${(Number(source[index] || 0) & 0xFF).toString(16).toUpperCase().padStart(2, '0')}`
  64. )).join(' ')
  65. }
  66. function parseCoilValue(value) {
  67. const text = String(value === undefined || value === null ? '' : value).trim()
  68. if (!text || text === '--') return null
  69. if (['1', 'true', 'TRUE', 'on', 'ON', '开'].includes(text)) return 1
  70. if (['0', 'false', 'FALSE', 'off', 'OFF', '关'].includes(text)) return 0
  71. const coilValue = Number(text)
  72. return Number.isFinite(coilValue) ? (coilValue ? 1 : 0) : null
  73. }
  74. function parseBitFieldValue(register, valueText) {
  75. const text = normalizeTextValue(valueText).trim()
  76. const bitWidth = normalizeBitWidth(register.bitWidth)
  77. let parsed = parseNumberText(text, 'uint32_t')
  78. const maxValue = getBitFieldMaxValue(register)
  79. if (parsed === null && bitWidth === 1) parsed = parseCoilValue(text)
  80. if (parsed === null) return null
  81. if (Math.round(parsed) !== parsed || parsed < 0 || parsed > maxValue) {
  82. throw new Error(`${register.name || '位域'} 超出 0 - ${maxValue} 范围`)
  83. }
  84. return Math.round(parsed)
  85. }
  86. function decodeBitFieldBytes(register, bytes = []) {
  87. const bitOffset = normalizeBitOffset(register.bitOffset)
  88. const bitWidth = normalizeBitWidth(register.bitWidth)
  89. let byteIndex = 0
  90. let currentBitOffset = bitOffset
  91. let multiplier = 1
  92. let remaining = bitWidth
  93. let value = 0
  94. while (remaining > 0 && byteIndex < bytes.length) {
  95. const take = Math.min(8 - currentBitOffset, remaining)
  96. const mask = (1 << take) - 1
  97. const part = ((Number(bytes[byteIndex]) & 0xFF) >> currentBitOffset) & mask
  98. value += part * multiplier
  99. multiplier *= Math.pow(2, take)
  100. remaining -= take
  101. byteIndex += 1
  102. currentBitOffset = 0
  103. }
  104. return remaining > 0 ? null : value
  105. }
  106. function encodeBitFieldIntoBytes(register, bytes, byteStart = 0) {
  107. const valueText = normalizeTextValue(register.inputValue)
  108. let value = parseBitFieldValue(register, valueText)
  109. const bitOffset = normalizeBitOffset(register.bitOffset)
  110. const bitWidth = normalizeBitWidth(register.bitWidth)
  111. let byteIndex = Math.max(0, Math.floor(Number(byteStart) || 0))
  112. let currentBitOffset = bitOffset
  113. let remaining = bitWidth
  114. if (value === null) return null
  115. while (remaining > 0) {
  116. const take = Math.min(8 - currentBitOffset, remaining)
  117. const mask = (1 << take) - 1
  118. const shiftedMask = (mask << currentBitOffset) & 0xFF
  119. const part = value & mask
  120. if (byteIndex >= bytes.length) return null
  121. bytes[byteIndex] = ((Number(bytes[byteIndex]) & 0xFF) & (~shiftedMask & 0xFF))
  122. | ((part << currentBitOffset) & shiftedMask)
  123. value = Math.floor(value / Math.pow(2, take))
  124. remaining -= take
  125. byteIndex += 1
  126. currentBitOffset = 0
  127. }
  128. return bytes
  129. }
  130. function encodeBitFieldBytes(register) {
  131. const bytes = Array.from({ length: getBitFieldByteLength(register) }, () => 0)
  132. return encodeBitFieldIntoBytes(register, bytes, 0)
  133. }
  134. function getRegisterDataBytes(register, words) {
  135. const dataType = getDataType(register.dataType).key
  136. const byteLength = getRegisterByteLength(dataType, register)
  137. const byteOffset = Math.max(0, Math.floor(Number(register.byteOffset) || 0))
  138. const sourceBytes = wordsToBytes(words, Math.max(0, (Array.isArray(words) ? words.length : 0) * 2))
  139. return sourceBytes.slice(byteOffset, byteOffset + byteLength)
  140. }
  141. function encodeRegisterBytes(register) {
  142. const dataType = getDataType(register.dataType).key
  143. const valueText = normalizeTextValue(register.inputValue)
  144. const byteLength = getRegisterByteLength(dataType, register)
  145. if (isBitFieldRegister(register)) {
  146. return encodeBitFieldBytes(register)
  147. }
  148. if (isTextRegister(dataType)) {
  149. const byteLimit = getEncodeByteLimit(register)
  150. const bytes = encodeTextBytes(valueText, dataType, byteLimit)
  151. const paddedBytes = bytes.slice()
  152. while (paddedBytes.length < byteLength) {
  153. paddedBytes.push(0)
  154. }
  155. return paddedBytes.slice(0, byteLength)
  156. }
  157. const numberValue = parseNumberText(valueText, dataType)
  158. if (numberValue === null) return null
  159. validateNumericValue(register, numberValue)
  160. if (dataType === 'float') return floatToBytes(numberValue)
  161. const rounded = Math.round(numberValue)
  162. if (dataType === 'int8_t' || dataType === 'uint8_t') return [rounded & 0xFF]
  163. if (dataType === 'int16_t' || dataType === 'uint16_t' || dataType === 'hex') {
  164. return unsignedIntegerToBytes(rounded, 2)
  165. }
  166. if (dataType === 'int32_t' || dataType === 'uint32_t') {
  167. return unsignedIntegerToBytes(rounded, 4)
  168. }
  169. return unsignedIntegerToBytes(rounded, byteLength)
  170. }
  171. function encodeRegisterWords(register) {
  172. const dataType = getDataType(register.dataType).key
  173. const bytes = encodeRegisterBytes(register)
  174. if (!Array.isArray(bytes)) return null
  175. if (isByteRegister(dataType)) return [bytes[0] & 0xFF]
  176. return bytesToWords(bytes.length % 2 === 0 ? bytes : bytes.concat(0))
  177. }
  178. function decodeRegisterValue(register, words) {
  179. const dataType = getDataType(register.dataType).key
  180. if (!Array.isArray(words) || words.length < getRegisterWordCountAtOffset(dataType, register.byteOffset || 0, register)) return null
  181. const bytes = getRegisterDataBytes(register, words)
  182. const byteLength = getRegisterByteLength(dataType, register)
  183. if (bytes.length < byteLength) return null
  184. if (isBitFieldRegister(register)) {
  185. return decodeBitFieldBytes(register, bytes)
  186. }
  187. if (isTextRegister(dataType)) {
  188. return decodeTextBytes(bytes.slice(0, getEncodeByteLimit(register)), dataType)
  189. }
  190. if (dataType === 'float') {
  191. return bytesToFloatValue(bytes)
  192. }
  193. if (dataType === 'int8_t') {
  194. const byteValue = bytes[0] & 0xFF
  195. return byteValue & 0x80 ? byteValue - 0x100 : byteValue
  196. }
  197. if (dataType === 'uint8_t') {
  198. return bytes[0] & 0xFF
  199. }
  200. if (dataType === 'int16_t') {
  201. return bytesToSignedInteger(bytes.slice(0, 2))
  202. }
  203. if (dataType === 'uint16_t') {
  204. return bytesToUnsignedInteger(bytes.slice(0, 2))
  205. }
  206. if (dataType === 'hex') {
  207. return bytesToUnsignedInteger(bytes.slice(0, 2))
  208. }
  209. if (dataType === 'int32_t') {
  210. return bytesToSignedInteger(bytes.slice(0, 4))
  211. }
  212. return bytesToUnsignedInteger(bytes.slice(0, 4))
  213. }
  214. function formatRegisterValue(register, rawValue) {
  215. if (rawValue === null || rawValue === undefined) return '--'
  216. const dataType = getDataType(register.dataType).key
  217. if (isTextRegister(dataType)) return normalizeTextValue(rawValue)
  218. if (dataType === 'hex') return formatHexValue(rawValue)
  219. if (dataType === 'float') return formatFloatValue(rawValue)
  220. return formatIntegerValue(rawValue, dataType)
  221. }
  222. function formatCoilDisplayValue(value) {
  223. return Number(value) ? '1' : '0'
  224. }
  225. function registerTypeIsBit(register) {
  226. return !!register && isBitRegisterType(register.registerType)
  227. }
  228. function validateRegisterValue(register, value) {
  229. const valueText = normalizeTextValue(
  230. value === undefined || value === null ? '' : value
  231. ).trim()
  232. if (!valueText || valueText === '--') return true
  233. if (registerTypeIsBit(register)) {
  234. if (parseCoilValue(valueText) === null) {
  235. throw new Error(`${register.name || '线圈'} 只能填写 0 或 1`)
  236. }
  237. return true
  238. }
  239. if (isBitFieldRegister(register)) {
  240. if (parseBitFieldValue(register, valueText) === null) {
  241. throw new Error(`${register.name || '位域'} 输入值无效`)
  242. }
  243. return true
  244. }
  245. const dataType = getDataType(register.dataType).key
  246. if (isTextRegister(dataType)) {
  247. encodeTextBytes(valueText, dataType, getEncodeByteLimit(register))
  248. return true
  249. }
  250. const numberValue = parseNumberText(valueText, dataType)
  251. if (numberValue === null) {
  252. throw new Error(`${register.name || '寄存器'} 输入值无效`)
  253. }
  254. return validateNumericValue(register, numberValue)
  255. }
  256. module.exports = {
  257. alignEvenByteLength,
  258. decodeRegisterValue,
  259. encodeBitFieldIntoBytes,
  260. encodeRegisterBytes,
  261. encodeRegisterWords,
  262. encodeTextBytes,
  263. formatCoilDisplayValue,
  264. formatRawByteText,
  265. formatRawByteTextWithDefault,
  266. formatRawWordText,
  267. formatRegisterValue,
  268. getBitFieldByteLength,
  269. getBitFieldMaxValue,
  270. getDataType,
  271. getDataTypeIndex,
  272. getEncodeByteLimit,
  273. getRegisterByteLength,
  274. getRegisterTextByteLength,
  275. getRegisterValueTypeLabel,
  276. getRegisterWordCount,
  277. getRegisterWordCountAtOffset,
  278. isBitFieldRegister,
  279. isBitRegisterType,
  280. isByteRegister,
  281. isHexRegister,
  282. isNumericRegister,
  283. isTextRegister,
  284. normalizeBitOffset,
  285. normalizeBitWidth,
  286. normalizeTextByteLength,
  287. parseCoilValue,
  288. parseNumberText,
  289. registerTypeIsBit,
  290. supportsRange,
  291. supportsUnit,
  292. validateRegisterValue
  293. }