1
0

code-info-parser.js 15 KB

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