value-number.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. const {
  2. formatFixedValue
  3. } = require('../../utils/base-utils.js')
  4. const {
  5. getDataType,
  6. isHexRegister
  7. } = require('./value-types.js')
  8. function padWordHex(value) {
  9. return Number(value || 0).toString(16).toUpperCase().padStart(4, '0')
  10. }
  11. function formatIntegerValue(value, dataType) {
  12. const type = getDataType(dataType).key
  13. const numberValue = Number(value)
  14. if (!Number.isFinite(numberValue)) return '--'
  15. if (type === 'int8_t') return String(((Math.round(numberValue) << 24) >> 24))
  16. if (type === 'uint8_t') return String(Math.round(numberValue) & 0xFF)
  17. if (type === 'int16_t') return String(((Math.round(numberValue) << 16) >> 16))
  18. if (type === 'uint16_t') return String(Math.round(numberValue) & 0xFFFF)
  19. if (type === 'int32_t') return String((Math.round(numberValue) | 0))
  20. if (type === 'uint32_t') return String(Math.round(numberValue) >>> 0)
  21. return String(Math.round(numberValue))
  22. }
  23. function formatHexValue(value) {
  24. const numberValue = Number(value)
  25. if (!Number.isFinite(numberValue)) return '--'
  26. return `0x${padWordHex(Math.round(numberValue) & 0xFFFF)}`
  27. }
  28. function formatFloatValue(value) {
  29. return formatFixedValue(value, 6).replace(/(\.\d*?)0+$/, '$1').replace(/\.$/, '')
  30. }
  31. function isLittleEndian(byteOrder) {
  32. const text = String(byteOrder || '').trim().toLowerCase()
  33. return text === 'little' || text === 'le' || text === '1'
  34. }
  35. function applyByteOrder(bytes, byteOrder) {
  36. const source = Array.isArray(bytes) ? bytes.slice() : []
  37. return isLittleEndian(byteOrder) ? source.reverse() : source
  38. }
  39. function parseIntegerText(value) {
  40. const text = String(value === undefined || value === null ? '' : value).trim()
  41. if (!text) return null
  42. const isHex = /^[-+]?0x[0-9a-f]+$/i.test(text) || /^0x[0-9a-f]+$/i.test(text)
  43. const parsed = isHex ? parseInt(text, 16) : Number(text)
  44. return Number.isFinite(parsed) ? parsed : null
  45. }
  46. function parseHexText(value) {
  47. const text = String(value === undefined || value === null ? '' : value).trim()
  48. if (!text) return null
  49. const hexText = text.toUpperCase().startsWith('0X') ? text.slice(2) : text
  50. if (/^[0-9A-F]{1,4}$/i.test(hexText)) {
  51. const parsedHex = parseInt(hexText, 16)
  52. return Number.isFinite(parsedHex) ? parsedHex : null
  53. }
  54. return null
  55. }
  56. function getNumericRange(dataType) {
  57. const type = getDataType(dataType).key
  58. if (type === 'int8_t') return { max: 127, min: -128 }
  59. if (type === 'uint8_t') return { max: 0xFF, min: 0 }
  60. if (type === 'int16_t') return { max: 32767, min: -32768 }
  61. if (type === 'uint16_t') return { max: 0xFFFF, min: 0 }
  62. if (type === 'int32_t') return { max: 2147483647, min: -2147483648 }
  63. if (type === 'uint32_t') return { max: 0xFFFFFFFF, min: 0 }
  64. if (type === 'hex') return { max: 0xFFFF, min: 0 }
  65. return { max: Number.POSITIVE_INFINITY, min: Number.NEGATIVE_INFINITY }
  66. }
  67. function parseNumberText(value, dataType) {
  68. const text = String(value === undefined || value === null ? '' : value).trim()
  69. if (!text || text === '--') return null
  70. if (getDataType(dataType).key === 'float') {
  71. const parsed = Number(text)
  72. return Number.isFinite(parsed) ? parsed : null
  73. }
  74. if (isHexRegister(dataType)) return parseHexText(text)
  75. return parseIntegerText(text)
  76. }
  77. function parseRangeBoundary(value, dataType, label) {
  78. const text = String(value === undefined || value === null ? '' : value).trim()
  79. if (!text) return null
  80. const parsed = parseNumberText(text, dataType)
  81. if (parsed === null) {
  82. throw new Error(`${label}无效`)
  83. }
  84. return parsed
  85. }
  86. function validateNumericValue(register, value) {
  87. const dataType = getDataType(register.dataType).key
  88. const range = getNumericRange(dataType)
  89. const numberValue = Number(value)
  90. if (!Number.isFinite(numberValue)) return false
  91. if (dataType !== 'float' && Math.round(numberValue) !== numberValue) {
  92. throw new Error(`${register.name || '寄存器'} 需要整数`)
  93. }
  94. if (numberValue < range.min || numberValue > range.max) {
  95. throw new Error(`${register.name || '寄存器'} 超出 ${dataType} 范围`)
  96. }
  97. const minValue = parseRangeBoundary(register.minValue, dataType, `${register.name || '寄存器'} 最小值`)
  98. const maxValue = parseRangeBoundary(register.maxValue, dataType, `${register.name || '寄存器'} 最大值`)
  99. if (minValue !== null && numberValue < minValue) {
  100. throw new Error(`${register.name || '寄存器'} 小于限制最小值`)
  101. }
  102. if (maxValue !== null && numberValue > maxValue) {
  103. throw new Error(`${register.name || '寄存器'} 大于限制最大值`)
  104. }
  105. return true
  106. }
  107. function floatToBytes(value, byteOrder = 'big') {
  108. const buffer = new ArrayBuffer(4)
  109. const view = new DataView(buffer)
  110. view.setFloat32(0, Number(value), false)
  111. return applyByteOrder([
  112. view.getUint8(0),
  113. view.getUint8(1),
  114. view.getUint8(2),
  115. view.getUint8(3)
  116. ], byteOrder)
  117. }
  118. function bytesToFloatValue(bytes, byteOrder = 'big') {
  119. if (!Array.isArray(bytes) || bytes.length < 4) return null
  120. const buffer = new ArrayBuffer(4)
  121. const view = new DataView(buffer)
  122. const source = applyByteOrder(bytes.slice(0, 4), byteOrder)
  123. for (let index = 0; index < 4; index += 1) {
  124. view.setUint8(index, Number(source[index]) & 0xFF)
  125. }
  126. return view.getFloat32(0, false)
  127. }
  128. function unsignedIntegerToBytes(value, byteLength, byteOrder = 'big') {
  129. let numberValue = Math.round(Number(value) || 0)
  130. const bytes = []
  131. if (numberValue < 0) {
  132. numberValue += Math.pow(2, byteLength * 8)
  133. }
  134. for (let index = byteLength - 1; index >= 0; index -= 1) {
  135. bytes[index] = numberValue & 0xFF
  136. numberValue = Math.floor(numberValue / 0x100)
  137. }
  138. return applyByteOrder(bytes, byteOrder)
  139. }
  140. function bytesToUnsignedInteger(bytes, byteOrder = 'big') {
  141. return applyByteOrder(bytes, byteOrder).reduce((value, byte) => ((value * 0x100) + (Number(byte) & 0xFF)), 0)
  142. }
  143. function bytesToSignedInteger(bytes, byteOrder = 'big') {
  144. const unsignedValue = bytesToUnsignedInteger(bytes, byteOrder)
  145. const signLimit = Math.pow(2, bytes.length * 8 - 1)
  146. const fullRange = Math.pow(2, bytes.length * 8)
  147. return unsignedValue >= signLimit ? unsignedValue - fullRange : unsignedValue
  148. }
  149. module.exports = {
  150. bytesToFloatValue,
  151. bytesToSignedInteger,
  152. bytesToUnsignedInteger,
  153. floatToBytes,
  154. formatFloatValue,
  155. formatHexValue,
  156. formatIntegerValue,
  157. getNumericRange,
  158. parseHexText,
  159. parseIntegerText,
  160. parseNumberText,
  161. parseRangeBoundary,
  162. unsignedIntegerToBytes,
  163. validateNumericValue
  164. }