1
0

code-info-parser.js 16 KB

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