1
0

imports.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914
  1. const {
  2. getDataType,
  3. isStorageStructGroup,
  4. normalizeGroup
  5. } = require('../../domain/parameter-groups/model.js')
  6. const {
  7. parseStructCatalog,
  8. parseStructDefinition: parseStructDefinitionSource
  9. } = require('../../domain/parameter-groups/struct-parser.js')
  10. function getRegisterByteLengthFromConfig(register) {
  11. const dataType = getDataType(register.dataType).key
  12. if (dataType === 'raw') {
  13. return Math.max(1, Number(register.sourceByteLength || register.byteLength || register.rawByteLength) || 1)
  14. }
  15. if (dataType === 'text') return Math.max(1, Number(register.textByteLength) || 1)
  16. if (dataType === 'float' || dataType === 'int32_t' || dataType === 'uint32_t') return 4
  17. if (dataType === 'double' || dataType === 'int64_t' || dataType === 'uint64_t') return 8
  18. if (dataType === 'int128_t' || dataType === 'uint128_t') return 16
  19. if (dataType === 'int256_t' || dataType === 'uint256_t') return 32
  20. if (dataType === 'int8_t' || dataType === 'uint8_t') return 1
  21. return 2
  22. }
  23. function getRegistersByteLength(registers = []) {
  24. const explicitByteEnds = registers.map((register) => {
  25. const byteStart = Number(register && register.byteStart)
  26. if (!Number.isFinite(byteStart)) return null
  27. if (register.isBitField) {
  28. const bitOffset = Math.min(Math.max(Math.floor(Number(register.bitOffset) || 0), 0), 7)
  29. const bitWidth = Math.max(1, Math.round(Number(register.bitWidth) || 1))
  30. return Math.max(0, Math.floor(byteStart)) + Math.max(1, Math.ceil((bitOffset + bitWidth) / 8))
  31. }
  32. return Math.max(0, Math.floor(byteStart)) + getRegisterByteLengthFromConfig(register)
  33. }).filter((value) => Number.isFinite(value))
  34. if (explicitByteEnds.length) return Math.max.apply(null, explicitByteEnds)
  35. return registers.reduce((total, register) => total + getRegisterByteLengthFromConfig(register), 0)
  36. }
  37. function normalizeSymbolText(value) {
  38. return String(value || '')
  39. .replace(/^(?:IDATA|XDATA|DATA|CODE)[\s:_-]+/i, '')
  40. .replace(/^_+/, '')
  41. .replace(/[^A-Za-z0-9]/g, '')
  42. .toLowerCase()
  43. }
  44. function getStorageStructTypeName(group = {}) {
  45. return String(group.sourceDefinitionName || group.sourceSymbolType || group.sourceSymbolName || group.name || '')
  46. }
  47. function isStructCodeInfoEntry(group = {}) {
  48. const entryKind = String(group.sourceEntryKind || '').trim().toLowerCase()
  49. return !entryKind || entryKind === 'struct'
  50. }
  51. function isEnumCodeInfoEntry(group = {}) {
  52. return String(group.sourceEntryKind || '').trim().toLowerCase() === 'enum'
  53. }
  54. function isArrayCodeInfoEntry(group = {}) {
  55. return String(group.sourceEntryKind || '').trim().toLowerCase() === 'array'
  56. }
  57. function isVariableCodeInfoEntry(group = {}) {
  58. return String(group.sourceEntryKind || '').trim().toLowerCase() === 'variable'
  59. }
  60. function isStructArrayCodeInfoEntry(group = {}) {
  61. return isArrayCodeInfoEntry(group)
  62. && String(group.sourceElementType || group.sourceValueType || '').trim().toLowerCase() === 'struct'
  63. }
  64. function isEnumArrayCodeInfoEntry(group = {}) {
  65. return isArrayCodeInfoEntry(group)
  66. && String(group.sourceElementType || group.sourceValueType || '').trim().toLowerCase() === 'enum'
  67. }
  68. function structDefinitionNameMatches(group = {}, structInfo = {}) {
  69. const expectedName = normalizeSymbolText(getStorageStructTypeName(group))
  70. const structNames = [
  71. structInfo.name,
  72. structInfo.tagName,
  73. structInfo.tagName ? `struct ${structInfo.tagName}` : ''
  74. ].map(normalizeSymbolText).filter(Boolean)
  75. return !!expectedName && structNames.indexOf(expectedName) >= 0
  76. }
  77. function findStructCompletion(group, catalog) {
  78. if (isStorageStructGroup(group) && isStructCodeInfoEntry(group)) {
  79. const matchedStruct = catalog.structs.find((structInfo) => structDefinitionNameMatches(group, structInfo))
  80. return matchedStruct
  81. ? {
  82. name: group.sourceSymbolName || group.name,
  83. registers: matchedStruct.registers,
  84. structName: matchedStruct.name
  85. }
  86. : null
  87. }
  88. const symbolName = group.sourceSymbolName || group.name
  89. const direct = catalog.variablesByName[normalizeSymbolText(symbolName)]
  90. || catalog.variablesByName[symbolName]
  91. if (direct) return direct
  92. const normalizedSymbol = normalizeSymbolText(symbolName)
  93. const normalizedType = normalizeSymbolText(group.sourceSymbolType)
  94. const expectedBytes = Number(group.sourceByteLength || group.byteLength || 0)
  95. const matchedStruct = catalog.structs.find((structInfo) => {
  96. const normalizedStructName = normalizeSymbolText(structInfo.name)
  97. if (normalizedType && normalizedType === normalizedStructName) {
  98. return true
  99. }
  100. if (normalizedSymbol && (
  101. normalizedSymbol === normalizedStructName
  102. || normalizedSymbol.indexOf(normalizedStructName) >= 0
  103. || normalizedStructName.indexOf(normalizedSymbol) >= 0
  104. )) {
  105. return true
  106. }
  107. return expectedBytes > 0 && getRegistersByteLength(structInfo.registers) === expectedBytes
  108. })
  109. return matchedStruct
  110. ? {
  111. name: symbolName,
  112. registers: matchedStruct.registers,
  113. structName: matchedStruct.name
  114. }
  115. : null
  116. }
  117. function getEnumLookupNames(group = {}) {
  118. const registers = Array.isArray(group.registers) ? group.registers : []
  119. const names = [
  120. group.sourceDefinitionName,
  121. group.sourceSymbolType,
  122. group.sourceSymbolName,
  123. group.sourceInstanceName,
  124. group.name
  125. ]
  126. registers.forEach((register) => {
  127. names.push(
  128. register.sourceDefinitionName,
  129. register.sourceSymbolType,
  130. register.sourceSymbolName,
  131. register.sourceInstanceName,
  132. register.name
  133. )
  134. })
  135. return names.map(normalizeSymbolText).filter(Boolean)
  136. }
  137. function findEnumCompletion(group, catalog = {}) {
  138. const enums = Array.isArray(catalog.enums) ? catalog.enums : []
  139. if ((!isEnumCodeInfoEntry(group) && !isVariableCodeInfoEntry(group) && !isEnumArrayCodeInfoEntry(group)) || !enums.length) return null
  140. const names = getEnumLookupNames(group)
  141. if (!names.length) return null
  142. if (isEnumCodeInfoEntry(group) || isEnumArrayCodeInfoEntry(group)) {
  143. const matchedEnum = enums.find((enumInfo) => (
  144. [enumInfo.name, enumInfo.typedefName, enumInfo.tagName]
  145. .concat(enumInfo.typeNames || [])
  146. .map(normalizeSymbolText)
  147. .filter(Boolean)
  148. .some((name) => names.indexOf(name) >= 0)
  149. ))
  150. if (matchedEnum) return matchedEnum
  151. }
  152. const enumVariablesByName = catalog.enumVariablesByName || {}
  153. for (const name of names) {
  154. const variableEnum = enumVariablesByName[name]
  155. if (variableEnum) return variableEnum
  156. }
  157. return enums.find((enumInfo) => (
  158. [enumInfo.name, enumInfo.typedefName, enumInfo.tagName]
  159. .concat(enumInfo.typeNames || [])
  160. .map(normalizeSymbolText)
  161. .filter(Boolean)
  162. .some((name) => names.indexOf(name) >= 0)
  163. )) || null
  164. }
  165. function getIntegerDataTypeForByteLength(byteLength) {
  166. const length = Number(byteLength)
  167. if (length === 1) return 'uint8_t'
  168. if (length === 2) return 'uint16_t'
  169. if (length === 4) return 'uint32_t'
  170. if (length === 8) return 'uint64_t'
  171. if (length === 16) return 'uint128_t'
  172. if (length === 32) return 'uint256_t'
  173. return ''
  174. }
  175. function getDataTypeByteLength(dataType) {
  176. const key = getDataType(dataType).key
  177. if (key === 'int8_t' || key === 'uint8_t') return 1
  178. if (key === 'int16_t' || key === 'uint16_t' || key === 'hex') return 2
  179. if (key === 'int32_t' || key === 'uint32_t' || key === 'float') return 4
  180. if (key === 'double' || key === 'int64_t' || key === 'uint64_t') return 8
  181. if (key === 'int128_t' || key === 'uint128_t') return 16
  182. if (key === 'int256_t' || key === 'uint256_t') return 32
  183. return 2
  184. }
  185. function getEnumDataTypeForByteLength(enumInfo, byteLength) {
  186. const inferredType = getDataType(enumInfo && enumInfo.dataType).key
  187. if (getDataTypeByteLength(inferredType) === Number(byteLength)) return inferredType
  188. return getIntegerDataTypeForByteLength(byteLength)
  189. }
  190. function cloneEnumOptions(enumInfo) {
  191. return (Array.isArray(enumInfo && enumInfo.options) ? enumInfo.options : []).map((option) => ({
  192. label: option.label || option.name,
  193. name: option.name || option.label,
  194. value: Number(option.value) || 0
  195. }))
  196. }
  197. function completeEnumVariableGroup(group, enumInfo) {
  198. if (!enumInfo) return group
  199. const enumOptions = cloneEnumOptions(enumInfo)
  200. if (!enumOptions.length) return group
  201. const sourceRegisters = Array.isArray(group.registers) ? group.registers : []
  202. const registers = sourceRegisters.map((register) => {
  203. const byteLength = register.sourceByteLength || register.byteLength || group.sourceByteLength || group.byteLength
  204. const dataType = getEnumDataTypeForByteLength(enumInfo, byteLength)
  205. return dataType
  206. ? {
  207. ...register,
  208. dataType,
  209. enumName: enumInfo.name,
  210. enumOptions,
  211. sourceDefinitionName: enumInfo.name || register.sourceDefinitionName,
  212. sourceSymbolType: enumInfo.name || register.sourceSymbolType
  213. }
  214. : register
  215. })
  216. if (!registers.some((register, index) => register !== sourceRegisters[index])) return group
  217. return normalizeGroup({
  218. ...group,
  219. registers
  220. })
  221. }
  222. function createCompletedRegisters(group, completion) {
  223. const existingRemarksByByteStart = (Array.isArray(group.registers) ? group.registers : []).reduce((remarks, register) => {
  224. const byteStart = Number(register && register.byteStart)
  225. const remark = String(register && register.remark ? register.remark : '').trim()
  226. if (Number.isFinite(byteStart) && remark) remarks[Math.floor(byteStart)] = remark
  227. return remarks
  228. }, {})
  229. return completion.registers.map((register) => {
  230. const sourceAddress = (Number(group.sourceAddress) || Number(group.startAddress) || 0) + getRegisterByteStart(register)
  231. return {
  232. ...register,
  233. isStructField: true,
  234. remark: register.remark || existingRemarksByByteStart[Math.floor(Number(register.byteStart) || 0)] || '',
  235. sourceAddress,
  236. sourceAddressByteLength: group.sourceAddressByteLength,
  237. sourceAddressText: formatAddress(sourceAddress, group.sourceAddressWidth),
  238. sourceAddressWidth: group.sourceAddressWidth,
  239. sourceDefinitionName: group.sourceDefinitionName,
  240. sourceEntryKind: group.sourceEntryKind,
  241. sourceInstanceName: group.sourceInstanceName,
  242. sourceMemoryArea: group.sourceMemoryArea,
  243. sourceMemoryClass: group.sourceMemoryClass,
  244. sourceSymbolName: group.sourceSymbolName,
  245. sourceSymbolType: completion.structName || register.sourceSymbolType
  246. }
  247. })
  248. }
  249. function getArrayDimensions(group = {}) {
  250. return (Array.isArray(group.sourceArrayDimensions) ? group.sourceArrayDimensions : [])
  251. .map((value) => Math.max(1, Math.floor(Number(value) || 1)))
  252. .filter((value) => value > 0)
  253. }
  254. function getArrayIndexPath(linearIndex, dimensions = []) {
  255. const safeDimensions = getArrayDimensions({ sourceArrayDimensions: dimensions })
  256. if (!safeDimensions.length) return [Math.max(0, Math.floor(Number(linearIndex) || 0))]
  257. const indexes = Array.from({ length: safeDimensions.length }, () => 0)
  258. let remaining = Math.max(0, Math.floor(Number(linearIndex) || 0))
  259. for (let index = safeDimensions.length - 1; index >= 0; index -= 1) {
  260. indexes[index] = remaining % safeDimensions[index]
  261. remaining = Math.floor(remaining / safeDimensions[index])
  262. }
  263. return indexes
  264. }
  265. function formatArrayIndexPath(indexPath = []) {
  266. return (Array.isArray(indexPath) ? indexPath : [indexPath])
  267. .map((index) => `[${Math.max(0, Math.floor(Number(index) || 0))}]`)
  268. .join('')
  269. }
  270. function getStructArrayElementCount(group = {}) {
  271. const explicitCount = Number(group.sourceElementCount)
  272. if (Number.isFinite(explicitCount) && explicitCount > 0) return Math.floor(explicitCount)
  273. const dimensions = getArrayDimensions(group)
  274. if (dimensions.length) return dimensions.reduce((total, value) => total * value, 1)
  275. return 1
  276. }
  277. function createCompletedStructArrayRegisters(group, completion) {
  278. const existingRemarksByByteStart = (Array.isArray(group.registers) ? group.registers : []).reduce((remarks, register) => {
  279. const byteStart = Number(register && register.byteStart)
  280. const remark = String(register && register.remark ? register.remark : '').trim()
  281. if (Number.isFinite(byteStart) && remark) remarks[Math.floor(byteStart)] = remark
  282. return remarks
  283. }, {})
  284. const elementCount = getStructArrayElementCount(group)
  285. const elementByteLength = Math.max(1, Math.floor(Number(group.sourceElementByteLength) || getRegistersByteLength(completion.registers) || 1))
  286. const dimensions = getArrayDimensions(group)
  287. const baseAddress = Number(group.sourceAddress) || Number(group.startAddress) || 0
  288. const baseName = group.sourceSymbolName || group.name || completion.name
  289. const registers = []
  290. for (let elementIndex = 0; elementIndex < elementCount; elementIndex += 1) {
  291. const elementByteStart = elementIndex * elementByteLength
  292. const indexPath = getArrayIndexPath(elementIndex, dimensions)
  293. const indexText = formatArrayIndexPath(indexPath)
  294. completion.registers.forEach((register) => {
  295. const fieldByteStart = getRegisterByteStart(register)
  296. const byteStart = elementByteStart + fieldByteStart
  297. const sourceAddress = baseAddress + byteStart
  298. const fieldName = register.name || `field_${fieldByteStart}`
  299. registers.push({
  300. ...register,
  301. byteStart,
  302. isStructField: true,
  303. name: `${baseName}${indexText}.${fieldName}`,
  304. remark: register.remark || existingRemarksByByteStart[Math.floor(byteStart)] || '',
  305. sourceAddress,
  306. sourceAddressByteLength: group.sourceAddressByteLength,
  307. sourceAddressText: formatAddress(sourceAddress, group.sourceAddressWidth),
  308. sourceAddressWidth: group.sourceAddressWidth,
  309. sourceArrayDimensions: dimensions,
  310. sourceArrayIndex: elementIndex,
  311. sourceArrayIndexPath: indexPath,
  312. sourceByteLength: register.sourceByteLength,
  313. sourceDefinitionName: group.sourceDefinitionName,
  314. sourceElementByteLength: elementByteLength,
  315. sourceElementCount: elementCount,
  316. sourceElementType: group.sourceElementType,
  317. sourceEntryKind: group.sourceEntryKind,
  318. sourceInstanceName: group.sourceInstanceName,
  319. sourceMemoryArea: group.sourceMemoryArea,
  320. sourceMemoryClass: group.sourceMemoryClass,
  321. sourceSymbolName: `${baseName}${indexText}.${fieldName}`,
  322. sourceSymbolType: completion.structName || register.sourceSymbolType,
  323. sourceValueType: group.sourceValueType
  324. })
  325. })
  326. }
  327. return registers
  328. }
  329. function completeStructInstanceGroups(groups, sourceText, options = {}) {
  330. const catalog = parseStructCatalog(sourceText)
  331. let completedCount = 0
  332. let skippedCount = 0
  333. const nextGroups = groups.map((group) => {
  334. if (!group.sourceSymbolName || !group.sourceMemoryArea) return group
  335. if (isEnumCodeInfoEntry(group) || isVariableCodeInfoEntry(group) || isEnumArrayCodeInfoEntry(group)) {
  336. const enumInfo = findEnumCompletion(group, catalog)
  337. if (!enumInfo) return group
  338. completedCount += 1
  339. return completeEnumVariableGroup(group, enumInfo)
  340. }
  341. if (!isStructCodeInfoEntry(group) && !isStructArrayCodeInfoEntry(group)) return group
  342. const completion = findStructCompletion(group, catalog)
  343. if (!completion || !completion.registers || !completion.registers.length) {
  344. skippedCount += 1
  345. return group
  346. }
  347. const expectedBytes = Number(group.sourceByteLength || group.byteLength || 0)
  348. const actualBytes = getRegistersByteLength(completion.registers)
  349. const expectedElementBytes = Number(group.sourceElementByteLength || 0)
  350. const expectedStructBytes = isStructArrayCodeInfoEntry(group) && expectedElementBytes > 0
  351. ? expectedElementBytes
  352. : expectedBytes
  353. if (expectedStructBytes > 0 && actualBytes !== expectedStructBytes && options.strictLength !== false) {
  354. skippedCount += 1
  355. return group
  356. }
  357. completedCount += 1
  358. if (isStructArrayCodeInfoEntry(group)) {
  359. const registers = createCompletedStructArrayRegisters(group, completion)
  360. return normalizeGroup({
  361. ...group,
  362. layout: 'struct',
  363. quantity: registers.length,
  364. registers
  365. })
  366. }
  367. return normalizeGroup({
  368. ...group,
  369. layout: 'struct',
  370. quantity: completion.registers.length,
  371. registers: createCompletedRegisters(group, completion)
  372. })
  373. })
  374. return {
  375. completedCount,
  376. groups: nextGroups,
  377. skippedCount,
  378. structCount: catalog.structs.length,
  379. enumCount: Array.isArray(catalog.enums) ? catalog.enums.length : 0,
  380. variableCount: Object.keys(catalog.variablesByName).length
  381. }
  382. }
  383. function formatAddress(address, addressWidth) {
  384. const numberValue = Math.max(0, Math.floor(Number(address) || 0))
  385. const length = Number(addressWidth) === 32 || numberValue > 0xFFFF ? 8 : 4
  386. return `0x${numberValue.toString(16).toUpperCase().padStart(length, '0')}`
  387. }
  388. function normalizeDuplicateText(value) {
  389. return String(value === undefined || value === null ? '' : value)
  390. .trim()
  391. .toLowerCase()
  392. }
  393. function normalizeStructMatchText(value) {
  394. return String(value === undefined || value === null ? '' : value)
  395. .trim()
  396. .replace(/^(?:IDATA|XDATA|DATA|CODE)[\s:_-]+/i, '')
  397. .replace(/^struct\s+/i, '')
  398. .replace(/\s+#\d+$/i, '')
  399. .replace(/^_+/, '')
  400. .replace(/[^A-Za-z0-9]/g, '')
  401. .toLowerCase()
  402. }
  403. function normalizeAddressKey(value, textValue) {
  404. const numberValue = Number(value)
  405. if (Number.isFinite(numberValue)) return String(Math.floor(numberValue))
  406. return String(textValue === undefined || textValue === null ? '' : textValue)
  407. .trim()
  408. .toUpperCase()
  409. }
  410. function normalizeBitKey(source = {}) {
  411. const value = source.sourceBitOffset !== undefined && source.sourceBitOffset !== null && source.sourceBitOffset !== ''
  412. ? source.sourceBitOffset
  413. : source.bitOffset
  414. const numberValue = Number(value)
  415. return Number.isFinite(numberValue) ? String(Math.floor(numberValue)) : ''
  416. }
  417. function getRegisterDuplicateKey(register = {}, group = {}) {
  418. const area = normalizeDuplicateText(register.sourceMemoryArea || group.sourceMemoryArea || register.memoryArea || '')
  419. const symbolName = normalizeDuplicateText(register.sourceSymbolName || register.name || '')
  420. if (area && symbolName) return ['register', area, symbolName].join('|')
  421. const addressKey = normalizeAddressKey(
  422. register.sourceAddress !== undefined ? register.sourceAddress : register.address,
  423. register.sourceAddressText || register.addressText
  424. )
  425. const bitKey = normalizeBitKey(register)
  426. if (!area && !symbolName && !addressKey) return ''
  427. return ['register', area, symbolName, addressKey, bitKey].join('|')
  428. }
  429. function isSingleRegisterAggregateGroup(group = {}) {
  430. if (isArrayCodeInfoEntry(group)) return false
  431. const groupSymbolName = normalizeDuplicateText(group.sourceSymbolName || group.name || '')
  432. const registers = Array.isArray(group.registers) ? group.registers : []
  433. return registers.some((register) => {
  434. const registerSymbolName = normalizeDuplicateText(register.sourceSymbolName || register.name || '')
  435. return registerSymbolName && groupSymbolName && registerSymbolName !== groupSymbolName
  436. })
  437. }
  438. function getGroupDuplicateKey(group = {}) {
  439. const area = normalizeDuplicateText(group.sourceMemoryArea || '')
  440. const symbolName = normalizeDuplicateText(group.sourceSymbolName || group.name || '')
  441. const addressKey = normalizeAddressKey(
  442. group.sourceAddress !== undefined ? group.sourceAddress : group.startAddress,
  443. group.sourceAddressText || group.startAddressText
  444. )
  445. if (area && symbolName && addressKey) return ['group', area, symbolName, addressKey].join('|')
  446. if (area && symbolName) return ['group', area, symbolName].join('|')
  447. if (!area && !symbolName && !addressKey) return ''
  448. return ['group', area, symbolName, addressKey].join('|')
  449. }
  450. function getAggregateGroupDuplicateKey(source = {}) {
  451. const area = normalizeDuplicateText(source.sourceMemoryArea || source.memoryArea || '')
  452. const registerType = normalizeDuplicateText(source.registerType || '')
  453. const segment = normalizeDuplicateText(source.sourceSegment || '')
  454. return ['aggregate', area, registerType, segment].join('|')
  455. }
  456. function collectImportedVariableIndexes(groups = []) {
  457. return groups.reduce((indexes, group, groupIndex) => {
  458. if (!isSingleRegisterAggregateGroup(group)) {
  459. const groupKey = getGroupDuplicateKey(group)
  460. if (groupKey) indexes.groupIndexes[groupKey] = groupIndex
  461. } else {
  462. const aggregateKey = getAggregateGroupDuplicateKey(group)
  463. if (aggregateKey) indexes.aggregateGroupIndexes[aggregateKey] = groupIndex
  464. }
  465. ;(Array.isArray(group.registers) ? group.registers : []).forEach((register) => {
  466. const registerKey = getRegisterDuplicateKey(register, group)
  467. if (registerKey) {
  468. indexes.registerIndexes[registerKey] = {
  469. groupIndex,
  470. registerIndex: group.registers.indexOf(register)
  471. }
  472. }
  473. })
  474. return indexes
  475. }, {
  476. aggregateGroupIndexes: {},
  477. groupIndexes: {},
  478. registerIndexes: {}
  479. })
  480. }
  481. function getStructMatchNames(group = {}) {
  482. const registers = Array.isArray(group.registers) ? group.registers : []
  483. const names = [
  484. group.sourceSymbolName,
  485. group.sourceSymbolType,
  486. group.name,
  487. group.displayName
  488. ]
  489. registers.forEach((register) => {
  490. names.push(register.sourceSymbolType, register.sourceSymbolName)
  491. })
  492. return names
  493. .map(normalizeStructMatchText)
  494. .filter(Boolean)
  495. .filter((name, index, list) => list.indexOf(name) === index)
  496. }
  497. function structsMatchByName(existingGroup = {}, incomingGroup = {}) {
  498. const existingNames = getStructMatchNames(existingGroup)
  499. const incomingNames = getStructMatchNames(incomingGroup)
  500. return existingNames.some((name) => incomingNames.indexOf(name) >= 0)
  501. }
  502. function getGroupByteLengthCandidates(group = {}) {
  503. const registers = Array.isArray(group.registers) ? group.registers : []
  504. const candidates = [
  505. group.sourceByteLength,
  506. group.byteLength,
  507. group.structByteLength,
  508. getRegistersByteLength(registers)
  509. ]
  510. registers.forEach((register) => {
  511. candidates.push(register.structByteLength)
  512. })
  513. return candidates
  514. .map((value) => Number(value))
  515. .filter((value) => Number.isFinite(value) && value > 0)
  516. .map((value) => Math.floor(value))
  517. .filter((value, index, list) => list.indexOf(value) === index)
  518. }
  519. function structsMatchByByteLength(existingGroup = {}, incomingGroup = {}) {
  520. const existingLengths = getGroupByteLengthCandidates(existingGroup)
  521. const incomingLengths = getGroupByteLengthCandidates(incomingGroup)
  522. return existingLengths.some((length) => incomingLengths.indexOf(length) >= 0)
  523. }
  524. function structsMatchByLocation(existingGroup = {}, incomingGroup = {}) {
  525. const existingArea = normalizeDuplicateText(existingGroup.sourceMemoryArea || '')
  526. const incomingArea = normalizeDuplicateText(incomingGroup.sourceMemoryArea || '')
  527. const existingAddress = normalizeAddressKey(
  528. existingGroup.sourceAddress !== undefined ? existingGroup.sourceAddress : existingGroup.startAddress,
  529. existingGroup.sourceAddressText || existingGroup.startAddressText
  530. )
  531. const incomingAddress = normalizeAddressKey(
  532. incomingGroup.sourceAddress !== undefined ? incomingGroup.sourceAddress : incomingGroup.startAddress,
  533. incomingGroup.sourceAddressText || incomingGroup.startAddressText
  534. )
  535. if (existingArea && incomingArea && existingArea !== incomingArea) return false
  536. if (existingAddress && incomingAddress && existingAddress !== incomingAddress) return false
  537. return true
  538. }
  539. function isIncomingPlaceholderStructGroup(group = {}) {
  540. const registers = Array.isArray(group.registers) ? group.registers : []
  541. return group.layout === 'struct'
  542. && registers.length > 0
  543. && registers.every((register) => !!register.isPlaceholderByteField)
  544. }
  545. function hasImportedStructRegisters(group = {}) {
  546. const registers = Array.isArray(group.registers) ? group.registers : []
  547. return group.layout === 'struct'
  548. && registers.length > 0
  549. && registers.some((register) => !register.isPlaceholderByteField)
  550. }
  551. function canPreserveExistingStructLayout(existingGroup, incomingGroup, options = {}) {
  552. return options.preserveExistingStructLayout
  553. && hasImportedStructRegisters(existingGroup)
  554. && isIncomingPlaceholderStructGroup(incomingGroup)
  555. && structsMatchByName(existingGroup, incomingGroup)
  556. && structsMatchByByteLength(existingGroup, incomingGroup)
  557. && structsMatchByLocation(existingGroup, incomingGroup)
  558. }
  559. function getRegisterByteStart(register = {}) {
  560. const byteStart = Number(register.byteStart)
  561. return Number.isFinite(byteStart) ? Math.max(0, Math.floor(byteStart)) : 0
  562. }
  563. function mergePreservedStructRegister(register = {}, incomingGroup = {}) {
  564. const byteStart = getRegisterByteStart(register)
  565. const sourceAddress = (Number(incomingGroup.sourceAddress) || Number(incomingGroup.startAddress) || 0) + byteStart
  566. const sourceSymbolName = incomingGroup.sourceSymbolName || register.sourceSymbolName
  567. const sourceSymbolType = incomingGroup.sourceSymbolType || register.sourceSymbolType || sourceSymbolName
  568. return {
  569. ...register,
  570. rawBytes: [],
  571. rawValue: null,
  572. rawWords: [],
  573. sourceAddress,
  574. sourceAddressByteLength: incomingGroup.sourceAddressByteLength || register.sourceAddressByteLength,
  575. sourceAddressText: formatAddress(sourceAddress, incomingGroup.sourceAddressWidth || register.sourceAddressWidth),
  576. sourceAddressWidth: incomingGroup.sourceAddressWidth || register.sourceAddressWidth,
  577. sourceDefinitionName: incomingGroup.sourceDefinitionName || register.sourceDefinitionName,
  578. sourceEntryKind: incomingGroup.sourceEntryKind,
  579. sourceInstanceName: incomingGroup.sourceInstanceName || register.sourceInstanceName,
  580. sourceMemoryArea: incomingGroup.sourceMemoryArea,
  581. sourceMemoryClass: incomingGroup.sourceMemoryClass,
  582. sourceSymbolName,
  583. sourceSymbolType
  584. }
  585. }
  586. function resolveMergedPollEnabled(existingGroup = {}, incomingGroup = {}, options = {}) {
  587. if (options.preserveExistingPollEnabled && existingGroup.pollEnabled === false) return false
  588. return incomingGroup.pollEnabled === false ? false : true
  589. }
  590. function mergePreservedStructGroupState(existingGroup, incomingGroup, options = {}) {
  591. const preservedRegisters = (Array.isArray(existingGroup.registers) ? existingGroup.registers : [])
  592. .map((register) => mergePreservedStructRegister(register, incomingGroup))
  593. return {
  594. ...incomingGroup,
  595. deleteVisible: false,
  596. expanded: existingGroup.expanded === true,
  597. id: existingGroup.id,
  598. pollEnabled: resolveMergedPollEnabled(existingGroup, incomingGroup, options),
  599. quantity: preservedRegisters.length,
  600. registers: preservedRegisters
  601. }
  602. }
  603. function findPreservableStructGroupIndex(groups = [], incomingGroup = {}, preferredIndex, options = {}) {
  604. if (preferredIndex !== undefined && canPreserveExistingStructLayout(groups[preferredIndex], incomingGroup, options)) {
  605. return preferredIndex
  606. }
  607. if (!options.preserveExistingStructLayout || !isIncomingPlaceholderStructGroup(incomingGroup)) return undefined
  608. return groups.findIndex((group, index) => (
  609. index !== preferredIndex && canPreserveExistingStructLayout(group, incomingGroup, options)
  610. ))
  611. }
  612. function mergeImportedRegisterState(existingRegister, incomingRegister, options = {}) {
  613. if (!existingRegister) return incomingRegister
  614. const incomingRemark = incomingRegister.remark
  615. const shouldPreserveRemark = options.preserveExistingRemarks
  616. && !String(incomingRemark === undefined || incomingRemark === null ? '' : incomingRemark).trim()
  617. return {
  618. ...incomingRegister,
  619. id: existingRegister.id,
  620. inputValue: incomingRegister.inputValue !== undefined && incomingRegister.inputValue !== null
  621. ? incomingRegister.inputValue
  622. : existingRegister.inputValue,
  623. remark: shouldPreserveRemark
  624. ? existingRegister.remark
  625. : (incomingRemark !== undefined && incomingRemark !== null ? incomingRemark : existingRegister.remark),
  626. rawBytes: [],
  627. rawValue: null,
  628. rawWords: []
  629. }
  630. }
  631. function mergeImportedGroupState(existingGroup, incomingGroup, options = {}) {
  632. if (!existingGroup) return incomingGroup
  633. if (canPreserveExistingStructLayout(existingGroup, incomingGroup, options)) {
  634. return mergePreservedStructGroupState(existingGroup, incomingGroup, options)
  635. }
  636. const existingRegisters = Array.isArray(existingGroup.registers) ? existingGroup.registers : []
  637. const incomingRegisters = Array.isArray(incomingGroup.registers) ? incomingGroup.registers : []
  638. return {
  639. ...incomingGroup,
  640. deleteVisible: false,
  641. expanded: existingGroup.expanded === true,
  642. id: existingGroup.id,
  643. pollEnabled: resolveMergedPollEnabled(existingGroup, incomingGroup, options),
  644. registers: incomingRegisters.map((incomingRegister, index) => mergeImportedRegisterState(
  645. existingRegisters[index],
  646. incomingRegister,
  647. options
  648. ))
  649. }
  650. }
  651. function mergeAggregateImportedGroup(nextGroups, incomingGroup, indexes, options = {}) {
  652. const aggregateKey = getAggregateGroupDuplicateKey(incomingGroup)
  653. const aggregateGroupIndex = indexes.aggregateGroupIndexes[aggregateKey]
  654. let targetGroup = aggregateGroupIndex === undefined ? null : nextGroups[aggregateGroupIndex]
  655. let targetGroupIndex = aggregateGroupIndex
  656. let targetRegisters = targetGroup && Array.isArray(targetGroup.registers)
  657. ? targetGroup.registers.slice()
  658. : []
  659. let addedRegisterCount = 0
  660. let updatedRegisterCount = 0
  661. ;(Array.isArray(incomingGroup.registers) ? incomingGroup.registers : []).forEach((incomingRegister) => {
  662. const registerKey = getRegisterDuplicateKey(incomingRegister, incomingGroup)
  663. const existingRef = registerKey ? indexes.registerIndexes[registerKey] : null
  664. if (existingRef) {
  665. const existingGroup = nextGroups[existingRef.groupIndex]
  666. const existingRegister = existingGroup && existingGroup.registers
  667. ? existingGroup.registers[existingRef.registerIndex]
  668. : null
  669. const mergedRegister = mergeImportedRegisterState(existingRegister, incomingRegister, options)
  670. if (targetGroupIndex !== undefined && existingRef.groupIndex === targetGroupIndex) {
  671. targetRegisters[existingRef.registerIndex] = mergedRegister
  672. } else if (existingGroup) {
  673. const registers = existingGroup.registers.slice()
  674. registers[existingRef.registerIndex] = mergedRegister
  675. nextGroups[existingRef.groupIndex] = normalizeGroup({
  676. ...existingGroup,
  677. registers
  678. })
  679. }
  680. updatedRegisterCount += 1
  681. return
  682. }
  683. targetRegisters.push(incomingRegister)
  684. addedRegisterCount += 1
  685. })
  686. if (targetGroupIndex !== undefined && targetGroup) {
  687. nextGroups[targetGroupIndex] = normalizeGroup(mergeImportedGroupState(targetGroup, {
  688. ...incomingGroup,
  689. quantity: targetRegisters.length,
  690. registers: targetRegisters
  691. }, options))
  692. } else if (targetRegisters.length) {
  693. targetGroup = normalizeGroup({
  694. ...incomingGroup,
  695. quantity: targetRegisters.length,
  696. registers: targetRegisters
  697. })
  698. nextGroups.push(targetGroup)
  699. targetGroupIndex = nextGroups.length - 1
  700. }
  701. return {
  702. addedGroupCount: targetGroupIndex === aggregateGroupIndex ? 0 : (targetRegisters.length ? 1 : 0),
  703. addedRegisterCount,
  704. updatedGroupCount: targetGroupIndex === aggregateGroupIndex && (addedRegisterCount || updatedRegisterCount) ? 1 : 0,
  705. updatedRegisterCount
  706. }
  707. }
  708. function mergeImportedGroups(existingGroups = [], incomingGroups = [], options = {}) {
  709. const nextGroups = existingGroups.slice()
  710. let indexes = collectImportedVariableIndexes(nextGroups)
  711. const result = {
  712. addedGroupCount: 0,
  713. addedRegisterCount: 0,
  714. groups: nextGroups,
  715. updatedGroupCount: 0,
  716. updatedRegisterCount: 0
  717. }
  718. incomingGroups.forEach((incomingGroup) => {
  719. if (isSingleRegisterAggregateGroup(incomingGroup)) {
  720. const aggregateResult = mergeAggregateImportedGroup(nextGroups, incomingGroup, indexes, options)
  721. result.addedGroupCount += aggregateResult.addedGroupCount
  722. result.addedRegisterCount += aggregateResult.addedRegisterCount
  723. result.updatedGroupCount += aggregateResult.updatedGroupCount
  724. result.updatedRegisterCount += aggregateResult.updatedRegisterCount
  725. indexes = collectImportedVariableIndexes(nextGroups)
  726. return
  727. }
  728. const groupKey = getGroupDuplicateKey(incomingGroup)
  729. const existingGroupIndex = groupKey ? indexes.groupIndexes[groupKey] : undefined
  730. const preservableStructGroupIndex = findPreservableStructGroupIndex(
  731. nextGroups,
  732. incomingGroup,
  733. existingGroupIndex,
  734. options
  735. )
  736. const targetGroupIndex = preservableStructGroupIndex >= 0
  737. ? preservableStructGroupIndex
  738. : existingGroupIndex
  739. if (targetGroupIndex !== undefined) {
  740. const existingGroup = nextGroups[targetGroupIndex]
  741. nextGroups[targetGroupIndex] = normalizeGroup(mergeImportedGroupState(existingGroup, incomingGroup, options))
  742. result.updatedGroupCount += 1
  743. } else {
  744. nextGroups.push(incomingGroup)
  745. result.addedGroupCount += 1
  746. }
  747. indexes = collectImportedVariableIndexes(nextGroups)
  748. })
  749. result.changedCount = result.addedGroupCount
  750. + result.updatedGroupCount
  751. + result.addedRegisterCount
  752. + result.updatedRegisterCount
  753. return result
  754. }
  755. function parseStructDefinition(sourceText) {
  756. return parseStructDefinitionSource(sourceText)
  757. }
  758. module.exports = {
  759. completeStructInstanceGroups,
  760. getRegistersByteLength,
  761. mergeImportedGroups,
  762. parseStructDefinition
  763. }