value-number.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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 dataTypeIsFloat(dataType) {
  12. const type = getDataType(dataType).key
  13. return type === 'float' || type === 'double'
  14. }
  15. function dataTypeIsSignedInteger(dataType) {
  16. return /^int(?:8|16|32|64|128|256)_t$/.test(getDataType(dataType).key)
  17. }
  18. function dataTypeIsWideInteger(dataType) {
  19. return /^(?:u?int)(?:64|128|256)_t$/.test(getDataType(dataType).key)
  20. }
  21. function getIntegerBitWidth(dataType) {
  22. const match = getDataType(dataType).key.match(/^(?:u?int)(\d+)_t$/)
  23. return match ? Number(match[1]) : 0
  24. }
  25. function normalizeBigIntValue(value) {
  26. if (typeof value === 'bigint') return value
  27. const text = String(value === undefined || value === null ? '' : value).trim()
  28. if (!text) return null
  29. try {
  30. if (/^[-+]0x/i.test(text)) {
  31. const sign = text[0] === '-' ? -1n : 1n
  32. return sign * BigInt(`0x${text.slice(3)}`)
  33. }
  34. return BigInt(text)
  35. } catch (error) {
  36. return null
  37. }
  38. }
  39. function formatIntegerValue(value, dataType) {
  40. const type = getDataType(dataType).key
  41. const bigValue = normalizeBigIntValue(value)
  42. if (dataTypeIsWideInteger(type)) return bigValue === null ? '--' : bigValue.toString()
  43. const numberValue = Number(value)
  44. if (!Number.isFinite(numberValue)) return '--'
  45. if (type === 'int8_t') return String(((Math.round(numberValue) << 24) >> 24))
  46. if (type === 'uint8_t') return String(Math.round(numberValue) & 0xFF)
  47. if (type === 'int16_t') return String(((Math.round(numberValue) << 16) >> 16))
  48. if (type === 'uint16_t') return String(Math.round(numberValue) & 0xFFFF)
  49. if (type === 'int32_t') return String((Math.round(numberValue) | 0))
  50. if (type === 'uint32_t') return String(Math.round(numberValue) >>> 0)
  51. return String(Math.round(numberValue))
  52. }
  53. function formatHexValue(value) {
  54. const numberValue = Number(value)
  55. if (!Number.isFinite(numberValue)) return '--'
  56. return `0x${padWordHex(Math.round(numberValue) & 0xFFFF)}`
  57. }
  58. function formatFloatValue(value) {
  59. return formatFixedValue(value, 6).replace(/(\.\d*?)0+$/, '$1').replace(/\.$/, '')
  60. }
  61. function isLittleEndian(byteOrder) {
  62. const text = String(byteOrder || '').trim().toLowerCase()
  63. return text === 'little' || text === 'le' || text === '1'
  64. }
  65. function applyByteOrder(bytes, byteOrder) {
  66. const source = Array.isArray(bytes) ? bytes.slice() : []
  67. return isLittleEndian(byteOrder) ? source.reverse() : source
  68. }
  69. function parseIntegerText(value) {
  70. const text = String(value === undefined || value === null ? '' : value).trim()
  71. if (!text) return null
  72. const isHex = /^[-+]?0x[0-9a-f]+$/i.test(text) || /^0x[0-9a-f]+$/i.test(text)
  73. const parsed = isHex ? parseInt(text, 16) : Number(text)
  74. return Number.isFinite(parsed) ? parsed : null
  75. }
  76. function parseHexText(value) {
  77. const text = String(value === undefined || value === null ? '' : value).trim()
  78. if (!text) return null
  79. const hexText = text.toUpperCase().startsWith('0X') ? text.slice(2) : text
  80. if (/^[0-9A-F]{1,4}$/i.test(hexText)) {
  81. const parsedHex = parseInt(hexText, 16)
  82. return Number.isFinite(parsedHex) ? parsedHex : null
  83. }
  84. return null
  85. }
  86. function getNumericRange(dataType) {
  87. const type = getDataType(dataType).key
  88. if (type === 'int8_t') return { max: 127, min: -128 }
  89. if (type === 'uint8_t') return { max: 0xFF, min: 0 }
  90. if (type === 'int16_t') return { max: 32767, min: -32768 }
  91. if (type === 'uint16_t') return { max: 0xFFFF, min: 0 }
  92. if (type === 'int32_t') return { max: 2147483647, min: -2147483648 }
  93. if (type === 'uint32_t') return { max: 0xFFFFFFFF, min: 0 }
  94. if (dataTypeIsWideInteger(type)) {
  95. const width = getIntegerBitWidth(type)
  96. const max = dataTypeIsSignedInteger(type)
  97. ? (1n << BigInt(width - 1)) - 1n
  98. : (1n << BigInt(width)) - 1n
  99. const min = dataTypeIsSignedInteger(type) ? -(1n << BigInt(width - 1)) : 0n
  100. return { max, min }
  101. }
  102. if (type === 'hex') return { max: 0xFFFF, min: 0 }
  103. return { max: Number.POSITIVE_INFINITY, min: Number.NEGATIVE_INFINITY }
  104. }
  105. function parseNumberText(value, dataType) {
  106. const text = String(value === undefined || value === null ? '' : value).trim()
  107. if (!text || text === '--') return null
  108. if (dataTypeIsFloat(dataType)) {
  109. const parsed = Number(text)
  110. return Number.isFinite(parsed) ? parsed : null
  111. }
  112. if (isHexRegister(dataType)) return parseHexText(text)
  113. if (dataTypeIsWideInteger(dataType)) return normalizeBigIntValue(text)
  114. return parseIntegerText(text)
  115. }
  116. function parseRangeBoundary(value, dataType, label) {
  117. const text = String(value === undefined || value === null ? '' : value).trim()
  118. if (!text) return null
  119. const parsed = parseNumberText(text, dataType)
  120. if (parsed === null) {
  121. throw new Error(`${label}无效`)
  122. }
  123. return parsed
  124. }
  125. function validateNumericValue(register, value) {
  126. const dataType = getDataType(register.dataType).key
  127. const range = getNumericRange(dataType)
  128. const isWideInteger = dataTypeIsWideInteger(dataType)
  129. const numberValue = isWideInteger ? normalizeBigIntValue(value) : Number(value)
  130. if (isWideInteger && numberValue === null) return false
  131. if (!isWideInteger && !Number.isFinite(numberValue)) return false
  132. if (!dataTypeIsFloat(dataType) && !isWideInteger && Math.round(numberValue) !== numberValue) {
  133. throw new Error(`${register.name || '寄存器'} 需要整数`)
  134. }
  135. if (numberValue < range.min || numberValue > range.max) {
  136. throw new Error(`${register.name || '寄存器'} 超出 ${dataType} 范围`)
  137. }
  138. const minValue = parseRangeBoundary(register.minValue, dataType, `${register.name || '寄存器'} 最小值`)
  139. const maxValue = parseRangeBoundary(register.maxValue, dataType, `${register.name || '寄存器'} 最大值`)
  140. if (minValue !== null && numberValue < minValue) {
  141. throw new Error(`${register.name || '寄存器'} 小于限制最小值`)
  142. }
  143. if (maxValue !== null && numberValue > maxValue) {
  144. throw new Error(`${register.name || '寄存器'} 大于限制最大值`)
  145. }
  146. return true
  147. }
  148. function floatToBytes(value, byteOrder = 'big', byteLength = 4) {
  149. const length = byteLength === 8 ? 8 : 4
  150. const buffer = new ArrayBuffer(length)
  151. const view = new DataView(buffer)
  152. if (length === 8) {
  153. view.setFloat64(0, Number(value), false)
  154. } else {
  155. view.setFloat32(0, Number(value), false)
  156. }
  157. return applyByteOrder(Array.from({ length }, (_, index) => view.getUint8(index)), byteOrder)
  158. }
  159. function bytesToFloatValue(bytes, byteOrder = 'big', byteLength = 4) {
  160. const length = byteLength === 8 ? 8 : 4
  161. if (!Array.isArray(bytes) || bytes.length < length) return null
  162. const buffer = new ArrayBuffer(length)
  163. const view = new DataView(buffer)
  164. const source = applyByteOrder(bytes.slice(0, length), byteOrder)
  165. for (let index = 0; index < length; index += 1) {
  166. view.setUint8(index, Number(source[index]) & 0xFF)
  167. }
  168. return length === 8 ? view.getFloat64(0, false) : view.getFloat32(0, false)
  169. }
  170. function unsignedIntegerToBytes(value, byteLength, byteOrder = 'big') {
  171. let numberValue = normalizeBigIntValue(value)
  172. if (numberValue === null) numberValue = 0n
  173. const bytes = []
  174. if (numberValue < 0) {
  175. numberValue += 1n << BigInt(byteLength * 8)
  176. }
  177. for (let index = byteLength - 1; index >= 0; index -= 1) {
  178. bytes[index] = Number(numberValue & 0xFFn)
  179. numberValue >>= 8n
  180. }
  181. return applyByteOrder(bytes, byteOrder)
  182. }
  183. function bytesToUnsignedInteger(bytes, byteOrder = 'big') {
  184. const source = applyByteOrder(bytes, byteOrder)
  185. const bigValue = source.reduce((value, byte) => ((value << 8n) + BigInt(Number(byte) & 0xFF)), 0n)
  186. return source.length > 4 ? bigValue.toString() : Number(bigValue)
  187. }
  188. function bytesToSignedInteger(bytes, byteOrder = 'big') {
  189. const source = applyByteOrder(bytes, byteOrder)
  190. const unsignedValue = source.reduce((value, byte) => ((value << 8n) + BigInt(Number(byte) & 0xFF)), 0n)
  191. const signLimit = 1n << BigInt(source.length * 8 - 1)
  192. const fullRange = 1n << BigInt(source.length * 8)
  193. const signedValue = unsignedValue >= signLimit ? unsignedValue - fullRange : unsignedValue
  194. return source.length > 4 ? signedValue.toString() : Number(signedValue)
  195. }
  196. module.exports = {
  197. bytesToFloatValue,
  198. bytesToSignedInteger,
  199. bytesToUnsignedInteger,
  200. dataTypeIsFloat,
  201. dataTypeIsSignedInteger,
  202. dataTypeIsWideInteger,
  203. floatToBytes,
  204. formatFloatValue,
  205. formatHexValue,
  206. formatIntegerValue,
  207. getNumericRange,
  208. parseHexText,
  209. parseIntegerText,
  210. parseNumberText,
  211. parseRangeBoundary,
  212. unsignedIntegerToBytes,
  213. validateNumericValue
  214. }