1
0

code-info-parser.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. const {
  2. bytesToHex,
  3. bytesToUtf8Text,
  4. trimTrailingNullBytes
  5. } = require('../../utils/binary-utils.js')
  6. const CODE_INFO_TLV_HEADER_BYTE_LENGTH = 2
  7. const CODE_INFO_ENTRY_NAME_BYTE_LENGTH = 32
  8. const CODE_INFO_TLV = {
  9. DATA_STRUCT: 0x01,
  10. DATA_VARIABLE: 0x02,
  11. IDATA_STRUCT: 0x03,
  12. IDATA_VARIABLE: 0x04,
  13. XDATA_STRUCT: 0x05,
  14. XDATA_VARIABLE: 0x06,
  15. ADDR32_STRUCT: 0x07,
  16. ADDR32_VARIABLE: 0x08,
  17. CAVE_FREQ: 0x40,
  18. REF_VOLT: 0x41,
  19. AMP_GAIN: 0x42,
  20. RS_SHUNT: 0x43,
  21. BUS_DIV: 0x44,
  22. ALONG_DIV: 0x45,
  23. CHIP_MODEL: 0x46,
  24. MODEL: 0x47
  25. }
  26. const CODE_INFO_TLV_NAMES = {
  27. [CODE_INFO_TLV.DATA_STRUCT]: 'data_struct',
  28. [CODE_INFO_TLV.DATA_VARIABLE]: 'data_var',
  29. [CODE_INFO_TLV.IDATA_STRUCT]: 'idata_struct',
  30. [CODE_INFO_TLV.IDATA_VARIABLE]: 'idata_var',
  31. [CODE_INFO_TLV.XDATA_STRUCT]: 'xdata_struct',
  32. [CODE_INFO_TLV.XDATA_VARIABLE]: 'xdata_var',
  33. [CODE_INFO_TLV.ADDR32_STRUCT]: 'addr32_struct',
  34. [CODE_INFO_TLV.ADDR32_VARIABLE]: 'addr32_var',
  35. [CODE_INFO_TLV.CAVE_FREQ]: 'cave_freq',
  36. [CODE_INFO_TLV.REF_VOLT]: 'ref_volt',
  37. [CODE_INFO_TLV.AMP_GAIN]: 'amp_gain',
  38. [CODE_INFO_TLV.RS_SHUNT]: 'rs_shunt',
  39. [CODE_INFO_TLV.BUS_DIV]: 'bus_div',
  40. [CODE_INFO_TLV.ALONG_DIV]: 'along_div',
  41. [CODE_INFO_TLV.CHIP_MODEL]: 'chip_model',
  42. [CODE_INFO_TLV.MODEL]: 'model'
  43. }
  44. const CODE_INFO_ENTRY_KIND = {
  45. STRUCT: 0x00,
  46. VARIABLE: 0x01
  47. }
  48. const CODE_INFO_ENTRY_KIND_TEXT = {
  49. [CODE_INFO_ENTRY_KIND.STRUCT]: 'struct',
  50. [CODE_INFO_ENTRY_KIND.VARIABLE]: 'variable'
  51. }
  52. const CODE_INFO_ENTRY_LAYOUTS = {
  53. [CODE_INFO_TLV.DATA_STRUCT]: {
  54. addressWidth: 16,
  55. entryKind: CODE_INFO_ENTRY_KIND.STRUCT,
  56. memoryArea: 'DATA'
  57. },
  58. [CODE_INFO_TLV.DATA_VARIABLE]: {
  59. addressWidth: 16,
  60. entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
  61. memoryArea: 'DATA'
  62. },
  63. [CODE_INFO_TLV.IDATA_STRUCT]: {
  64. addressWidth: 16,
  65. entryKind: CODE_INFO_ENTRY_KIND.STRUCT,
  66. memoryArea: 'IDATA'
  67. },
  68. [CODE_INFO_TLV.IDATA_VARIABLE]: {
  69. addressWidth: 16,
  70. entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
  71. memoryArea: 'IDATA'
  72. },
  73. [CODE_INFO_TLV.XDATA_STRUCT]: {
  74. addressWidth: 16,
  75. entryKind: CODE_INFO_ENTRY_KIND.STRUCT,
  76. memoryArea: 'XDATA'
  77. },
  78. [CODE_INFO_TLV.XDATA_VARIABLE]: {
  79. addressWidth: 16,
  80. entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
  81. memoryArea: 'XDATA'
  82. },
  83. [CODE_INFO_TLV.ADDR32_STRUCT]: {
  84. addressWidth: 32,
  85. entryKind: CODE_INFO_ENTRY_KIND.STRUCT,
  86. memoryArea: 'ADDR32'
  87. },
  88. [CODE_INFO_TLV.ADDR32_VARIABLE]: {
  89. addressWidth: 32,
  90. entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
  91. memoryArea: 'ADDR32'
  92. }
  93. }
  94. const MEMORY_ENDIAN = {
  95. BIG: 'big',
  96. LITTLE: 'little'
  97. }
  98. function toBytes(bytes) {
  99. return Array.prototype.slice.call(bytes || []).map((byte) => Number(byte) & 0xFF)
  100. }
  101. function readUint16(bytes, offset) {
  102. if (offset + 1 >= bytes.length) return 0
  103. return (((bytes[offset] || 0) << 8) | (bytes[offset + 1] || 0)) & 0xFFFF
  104. }
  105. function readUint32(bytes, offset) {
  106. if (offset + 3 >= bytes.length) return 0
  107. return (
  108. ((bytes[offset] || 0) * 0x1000000)
  109. + (((bytes[offset + 1] || 0) << 16) >>> 0)
  110. + (((bytes[offset + 2] || 0) << 8) >>> 0)
  111. + (bytes[offset + 3] || 0)
  112. ) >>> 0
  113. }
  114. function readFloat(bytes, offset) {
  115. if (offset + 3 >= bytes.length) return 0
  116. const buffer = new ArrayBuffer(4)
  117. const view = new DataView(buffer)
  118. for (let index = 0; index < 4; index += 1) {
  119. view.setUint8(index, bytes[offset + index] || 0)
  120. }
  121. return view.getFloat32(0, false)
  122. }
  123. function readTlvText(bytes) {
  124. return bytesToUtf8Text(trimTrailingNullBytes(bytes)).trim()
  125. }
  126. function normalizeCodeInfoAddressWidth(value) {
  127. const numberValue = Number(value)
  128. if (numberValue === 16) return 16
  129. if (numberValue === 32) return 32
  130. throw new Error('CodeInfo 描述符地址长度必须为 16 或 32')
  131. }
  132. function getEntryLayout(type) {
  133. const entryType = Number(type) & 0xFF
  134. const layout = CODE_INFO_ENTRY_LAYOUTS[entryType]
  135. if (!layout) return null
  136. const addressWidth = layout.addressWidth
  137. const addressByteLength = addressWidth === 16 ? 2 : 4
  138. const valueByteLength = addressByteLength + 2 + CODE_INFO_ENTRY_NAME_BYTE_LENGTH
  139. return {
  140. addressByteLength,
  141. addressWidth,
  142. entryKind: layout.entryKind,
  143. hasMemoryType: false,
  144. memoryArea: layout.memoryArea,
  145. nameByteLength: CODE_INFO_ENTRY_NAME_BYTE_LENGTH,
  146. nameOffset: addressByteLength + 2,
  147. valueByteLength
  148. }
  149. }
  150. function formatAddress(address, addressWidth = 32) {
  151. const numberValue = Math.max(0, Math.floor(Number(address) || 0))
  152. const hexWidth = normalizeCodeInfoAddressWidth(addressWidth) === 16 ? 4 : 8
  153. return `0x${numberValue.toString(16).toUpperCase().padStart(hexWidth, '0')}`
  154. }
  155. function normalizeTypeName(value, fallback) {
  156. const text = String(value || '').trim()
  157. return text || fallback
  158. }
  159. function getEntryKindText(entryKind) {
  160. return CODE_INFO_ENTRY_KIND_TEXT[entryKind] || 'unknown'
  161. }
  162. function normalizeMemoryEndian(value) {
  163. const text = String(value || '').trim().toLowerCase()
  164. if (text === 'little' || text === 'le' || text === '1') return MEMORY_ENDIAN.LITTLE
  165. return MEMORY_ENDIAN.BIG
  166. }
  167. function resolveCodeInfoByteLength(sourceLength, options = {}) {
  168. const expectedLength = Number(
  169. options.codeInfoByteLength === undefined ? options.byteLength : options.codeInfoByteLength
  170. )
  171. if (!Number.isFinite(expectedLength) || expectedLength <= 0) return sourceLength
  172. if (sourceLength < expectedLength) {
  173. throw new Error('CodeInfo 数据长度小于描述符声明长度')
  174. }
  175. return Math.floor(expectedLength)
  176. }
  177. function parseMemoryEntry(valueBytes, index, type) {
  178. const layout = getEntryLayout(type)
  179. if (!layout) return null
  180. if (valueBytes.length !== layout.valueByteLength) {
  181. throw new Error(`CodeInfo 内存入口 TLV 长度无效,期望 ${layout.valueByteLength} 字节`)
  182. }
  183. const memType = 0
  184. const addressOffset = 0
  185. const byteAddr = layout.addressByteLength === 2
  186. ? readUint16(valueBytes, addressOffset)
  187. : readUint32(valueBytes, addressOffset)
  188. const byteLength = readUint16(valueBytes, addressOffset + layout.addressByteLength)
  189. const nameOffset = layout.nameOffset
  190. const entryKind = layout.entryKind
  191. const entryKindText = getEntryKindText(entryKind)
  192. const typeName = normalizeTypeName(
  193. readTlvText(valueBytes.slice(nameOffset, nameOffset + layout.nameByteLength)),
  194. `${entryKindText === 'variable' ? 'var' : 'struct'}_${index + 1}`
  195. )
  196. const memoryArea = layout.memoryArea || 'UNKNOWN'
  197. return {
  198. addressByteLength: layout.addressByteLength,
  199. addressWidth: layout.addressWidth,
  200. byteAddr,
  201. byteLength,
  202. entryKind,
  203. entryKindText,
  204. index,
  205. isStructEntry: entryKind === CODE_INFO_ENTRY_KIND.STRUCT,
  206. isVariableEntry: entryKind === CODE_INFO_ENTRY_KIND.VARIABLE,
  207. memType,
  208. memoryArea,
  209. rawByteLength: valueBytes.length,
  210. sourceAddressText: formatAddress(byteAddr, layout.addressWidth),
  211. tlvType: Number(type) & 0xFF,
  212. typeName
  213. }
  214. }
  215. function applyCodeInfoTlvValue(target, type, valueBytes) {
  216. switch (type) {
  217. case CODE_INFO_TLV.CAVE_FREQ:
  218. if (valueBytes.length < 1) throw new Error('CodeInfo cave_freq TLV 长度无效')
  219. target.caveFreq = valueBytes[0] & 0xFF
  220. break
  221. case CODE_INFO_TLV.REF_VOLT:
  222. if (valueBytes.length < 1) throw new Error('CodeInfo ref_volt TLV 长度无效')
  223. target.refVoltRaw = valueBytes[0] & 0xFF
  224. target.refVolt = target.refVoltRaw / 10
  225. break
  226. case CODE_INFO_TLV.AMP_GAIN:
  227. if (valueBytes.length < 1) throw new Error('CodeInfo amp_gain TLV 长度无效')
  228. target.ampGain = valueBytes[0] & 0xFF
  229. break
  230. case CODE_INFO_TLV.RS_SHUNT:
  231. if (valueBytes.length < 2) throw new Error('CodeInfo rs_shunt TLV 长度无效')
  232. target.rsShunt = readUint16(valueBytes, 0)
  233. break
  234. case CODE_INFO_TLV.BUS_DIV:
  235. if (valueBytes.length < 4) throw new Error('CodeInfo bus_div TLV 长度无效')
  236. target.busDiv = readFloat(valueBytes, 0)
  237. break
  238. case CODE_INFO_TLV.ALONG_DIV:
  239. if (valueBytes.length < 4) throw new Error('CodeInfo along_div TLV 长度无效')
  240. target.alongDiv = readFloat(valueBytes, 0)
  241. break
  242. case CODE_INFO_TLV.CHIP_MODEL:
  243. target.chipModel = readTlvText(valueBytes)
  244. break
  245. case CODE_INFO_TLV.MODEL:
  246. target.model = readTlvText(valueBytes)
  247. break
  248. default:
  249. if (type >= 0x20 && type < 0x40) {
  250. target.customTlvCount = (Number(target.customTlvCount) || 0) + 1
  251. }
  252. break
  253. }
  254. }
  255. function parseCodeInfoTlvs(bytes) {
  256. const info = {
  257. entries: [],
  258. tlvItems: []
  259. }
  260. let offset = 0
  261. while (offset < bytes.length) {
  262. if (offset + CODE_INFO_TLV_HEADER_BYTE_LENGTH > bytes.length) {
  263. throw new Error('CodeInfo TLV 项头长度无效')
  264. }
  265. const type = bytes[offset] & 0xFF
  266. const byteLength = bytes[offset + 1] & 0xFF
  267. const valueOffset = offset + CODE_INFO_TLV_HEADER_BYTE_LENGTH
  268. const nextOffset = valueOffset + byteLength
  269. if (nextOffset > bytes.length) {
  270. throw new Error('CodeInfo TLV 项长度超出声明范围')
  271. }
  272. const valueBytes = bytes.slice(valueOffset, nextOffset)
  273. const item = {
  274. byteLength,
  275. name: CODE_INFO_TLV_NAMES[type] || `tlv_0x${type.toString(16).toUpperCase().padStart(2, '0')}`,
  276. rawHex: bytesToHex(valueBytes, ' '),
  277. type
  278. }
  279. info.tlvItems.push(item)
  280. const entry = parseMemoryEntry(valueBytes, info.entries.length, type)
  281. if (entry) {
  282. info.entries.push(entry)
  283. } else {
  284. applyCodeInfoTlvValue(info, type, valueBytes)
  285. }
  286. offset = nextOffset
  287. }
  288. return info
  289. }
  290. function parseCodeInfo(bytes, options = {}) {
  291. const inputBytes = toBytes(bytes)
  292. const codeInfoByteLength = resolveCodeInfoByteLength(inputBytes.length, options)
  293. const source = inputBytes.slice(0, codeInfoByteLength)
  294. const addressWidth = normalizeCodeInfoAddressWidth(options.addressWidth)
  295. const maxPacketLength = Number(options.maxPacketLength || 0) & 0xFFFF
  296. const tlvInfo = parseCodeInfoTlvs(source)
  297. const entries = tlvInfo.entries
  298. const entryCount = entries.length
  299. const entryAddressByteLengths = entries
  300. .map((entry) => Number(entry.addressByteLength) || 0)
  301. .filter((value, index, list) => value > 0 && list.indexOf(value) === index)
  302. return {
  303. ...tlvInfo,
  304. addressWidth,
  305. addressWidthRaw: addressWidth,
  306. byteLength: codeInfoByteLength,
  307. entryCount,
  308. maxPacketLength,
  309. memoryEndian: normalizeMemoryEndian(options.memoryEndian),
  310. memoryEndianRaw: Number(options.memoryEndianRaw || options.memoryEndianMark || 0) & 0xFFFF,
  311. rawHex: bytesToHex(source, ' '),
  312. structEntryAddressByteLength: entryAddressByteLengths.length === 1 ? entryAddressByteLengths[0] : 0,
  313. structCount: entryCount,
  314. structTable: entries,
  315. tableByteLength: entries.reduce((total, entry) => total + Number(entry.rawByteLength || 0), 0),
  316. tableCapacity: entryCount,
  317. tableRemainderByteLength: 0,
  318. tlvByteLength: source.length,
  319. truncated: false
  320. }
  321. }
  322. function createRegistersForByteSpan(entry) {
  323. const count = Math.max(1, Number(entry.byteLength) || 1)
  324. const registers = []
  325. for (let offset = 0; offset < count; offset += 1) {
  326. registers.push({
  327. byteStart: offset,
  328. dataType: 'uint8_t',
  329. isPlaceholderByteField: true,
  330. name: offset.toString(16).toUpperCase().padStart(2, '0'),
  331. sourceAddress: entry.byteAddr + offset,
  332. sourceAddressByteLength: entry.addressByteLength,
  333. sourceAddressText: formatAddress(entry.byteAddr + offset, entry.addressWidth),
  334. sourceAddressWidth: entry.addressWidth,
  335. sourceByteLength: 1,
  336. sourceEntryKind: entry.entryKindText,
  337. sourceMemoryArea: entry.memoryArea,
  338. sourceMemoryClass: entry.memoryArea,
  339. sourceSymbolName: entry.typeName,
  340. sourceSymbolType: entry.typeName
  341. })
  342. }
  343. return registers
  344. }
  345. function createVariableRegisters(entry) {
  346. return [{
  347. byteStart: 0,
  348. dataType: 'raw',
  349. name: entry.typeName,
  350. sourceAddress: entry.byteAddr,
  351. sourceAddressByteLength: entry.addressByteLength,
  352. sourceAddressText: formatAddress(entry.byteAddr, entry.addressWidth),
  353. sourceAddressWidth: entry.addressWidth,
  354. sourceByteLength: entry.byteLength,
  355. sourceEntryKind: entry.entryKindText,
  356. sourceMemoryArea: entry.memoryArea,
  357. sourceMemoryClass: entry.memoryArea,
  358. sourceSymbolName: entry.typeName,
  359. sourceSymbolType: entry.typeName
  360. }]
  361. }
  362. function createCodeInfoContext(codeInfo = {}) {
  363. return {
  364. addressWidth: codeInfo.addressWidth,
  365. codeInfoAddressWidth: codeInfo.addressWidth,
  366. maxPacketLength: codeInfo.maxPacketLength,
  367. memoryEndian: codeInfo.memoryEndian,
  368. storageAddressWidth: codeInfo.addressWidth
  369. }
  370. }
  371. function createSourceMeta(entry, byteLength) {
  372. return {
  373. sourceAddress: entry.byteAddr,
  374. sourceAddressByteLength: entry.addressByteLength,
  375. sourceAddressText: formatAddress(entry.byteAddr, entry.addressWidth),
  376. sourceAddressWidth: entry.addressWidth,
  377. sourceByteLength: byteLength,
  378. sourceEntryKind: entry.entryKindText,
  379. sourceMemoryArea: entry.memoryArea,
  380. sourceMemoryClass: entry.memoryArea,
  381. sourceSymbolName: entry.typeName,
  382. sourceSymbolType: entry.typeName
  383. }
  384. }
  385. function createGroupFromEntry(entry, chunkLength, suffix = '', codeInfo = {}) {
  386. const isStructEntry = entry.entryKind === CODE_INFO_ENTRY_KIND.STRUCT
  387. const registers = isStructEntry
  388. ? createRegistersForByteSpan(entry)
  389. : createVariableRegisters(entry)
  390. return {
  391. addressUnit: 'byte',
  392. codeInfoContext: createCodeInfoContext(codeInfo),
  393. layout: isStructEntry ? 'struct' : 'register',
  394. name: `${entry.typeName}${suffix}`,
  395. quantity: registers.length,
  396. registerType: entry.memoryArea === 'CODE' ? 'input' : 'holding',
  397. registers,
  398. ...createSourceMeta(entry, chunkLength),
  399. sourceSegment: 'CodeInfo TLV',
  400. sourceSegmentModule: '',
  401. startAddress: formatAddress(entry.byteAddr, entry.addressWidth)
  402. }
  403. }
  404. function createGroupsFromCodeInfo(codeInfo, options = {}) {
  405. const maxRegisters = Math.max(1, Number(options.maxRegistersPerGroup) || 256)
  406. const groups = []
  407. const entries = Array.isArray(codeInfo.entries) ? codeInfo.entries : codeInfo.structTable
  408. ;(Array.isArray(entries) ? entries : []).forEach((entry) => {
  409. if (!entry.byteLength || entry.memoryArea === 'UNKNOWN') return
  410. if (!entry.isStructEntry && !entry.isVariableEntry) return
  411. for (let offset = 0; offset < entry.byteLength; offset += maxRegisters) {
  412. const chunkLength = Math.min(maxRegisters, entry.byteLength - offset)
  413. const chunkEntry = {
  414. ...entry,
  415. byteAddr: entry.byteAddr + offset,
  416. byteLength: chunkLength
  417. }
  418. const suffix = entry.byteLength > maxRegisters ? ` #${Math.floor(offset / maxRegisters) + 1}` : ''
  419. groups.push(createGroupFromEntry(chunkEntry, chunkLength, suffix, codeInfo))
  420. }
  421. })
  422. return groups
  423. }
  424. module.exports = {
  425. CODE_INFO_ENTRY_KIND,
  426. CODE_INFO_ENTRY_NAME_BYTE_LENGTH,
  427. CODE_INFO_TLV_HEADER_BYTE_LENGTH,
  428. CODE_INFO_TLV,
  429. MEMORY_ENDIAN,
  430. createGroupsFromCodeInfo,
  431. normalizeCodeInfoAddressWidth,
  432. parseCodeInfo
  433. }