| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914 |
- const {
- getDataType,
- isStorageStructGroup,
- normalizeGroup
- } = require('../../domain/parameter-groups/model.js')
- const {
- parseStructCatalog,
- parseStructDefinition: parseStructDefinitionSource
- } = require('../../domain/parameter-groups/struct-parser.js')
- function getRegisterByteLengthFromConfig(register) {
- const dataType = getDataType(register.dataType).key
- if (dataType === 'raw') {
- return Math.max(1, Number(register.sourceByteLength || register.byteLength || register.rawByteLength) || 1)
- }
- if (dataType === 'text') return Math.max(1, Number(register.textByteLength) || 1)
- if (dataType === 'float' || dataType === 'int32_t' || dataType === 'uint32_t') return 4
- if (dataType === 'double' || dataType === 'int64_t' || dataType === 'uint64_t') return 8
- if (dataType === 'int128_t' || dataType === 'uint128_t') return 16
- if (dataType === 'int256_t' || dataType === 'uint256_t') return 32
- if (dataType === 'int8_t' || dataType === 'uint8_t') return 1
- return 2
- }
- function getRegistersByteLength(registers = []) {
- const explicitByteEnds = registers.map((register) => {
- const byteStart = Number(register && register.byteStart)
- if (!Number.isFinite(byteStart)) return null
- if (register.isBitField) {
- const bitOffset = Math.min(Math.max(Math.floor(Number(register.bitOffset) || 0), 0), 7)
- const bitWidth = Math.max(1, Math.round(Number(register.bitWidth) || 1))
- return Math.max(0, Math.floor(byteStart)) + Math.max(1, Math.ceil((bitOffset + bitWidth) / 8))
- }
- return Math.max(0, Math.floor(byteStart)) + getRegisterByteLengthFromConfig(register)
- }).filter((value) => Number.isFinite(value))
- if (explicitByteEnds.length) return Math.max.apply(null, explicitByteEnds)
- return registers.reduce((total, register) => total + getRegisterByteLengthFromConfig(register), 0)
- }
- function normalizeSymbolText(value) {
- return String(value || '')
- .replace(/^(?:IDATA|XDATA|DATA|CODE)[\s:_-]+/i, '')
- .replace(/^_+/, '')
- .replace(/[^A-Za-z0-9]/g, '')
- .toLowerCase()
- }
- function getStorageStructTypeName(group = {}) {
- return String(group.sourceDefinitionName || group.sourceSymbolType || group.sourceSymbolName || group.name || '')
- }
- function isStructCodeInfoEntry(group = {}) {
- const entryKind = String(group.sourceEntryKind || '').trim().toLowerCase()
- return !entryKind || entryKind === 'struct'
- }
- function isEnumCodeInfoEntry(group = {}) {
- return String(group.sourceEntryKind || '').trim().toLowerCase() === 'enum'
- }
- function isArrayCodeInfoEntry(group = {}) {
- return String(group.sourceEntryKind || '').trim().toLowerCase() === 'array'
- }
- function isVariableCodeInfoEntry(group = {}) {
- return String(group.sourceEntryKind || '').trim().toLowerCase() === 'variable'
- }
- function isStructArrayCodeInfoEntry(group = {}) {
- return isArrayCodeInfoEntry(group)
- && String(group.sourceElementType || group.sourceValueType || '').trim().toLowerCase() === 'struct'
- }
- function isEnumArrayCodeInfoEntry(group = {}) {
- return isArrayCodeInfoEntry(group)
- && String(group.sourceElementType || group.sourceValueType || '').trim().toLowerCase() === 'enum'
- }
- function structDefinitionNameMatches(group = {}, structInfo = {}) {
- const expectedName = normalizeSymbolText(getStorageStructTypeName(group))
- const structNames = [
- structInfo.name,
- structInfo.tagName,
- structInfo.tagName ? `struct ${structInfo.tagName}` : ''
- ].map(normalizeSymbolText).filter(Boolean)
- return !!expectedName && structNames.indexOf(expectedName) >= 0
- }
- function findStructCompletion(group, catalog) {
- if (isStorageStructGroup(group) && isStructCodeInfoEntry(group)) {
- const matchedStruct = catalog.structs.find((structInfo) => structDefinitionNameMatches(group, structInfo))
- return matchedStruct
- ? {
- name: group.sourceSymbolName || group.name,
- registers: matchedStruct.registers,
- structName: matchedStruct.name
- }
- : null
- }
- const symbolName = group.sourceSymbolName || group.name
- const direct = catalog.variablesByName[normalizeSymbolText(symbolName)]
- || catalog.variablesByName[symbolName]
- if (direct) return direct
- const normalizedSymbol = normalizeSymbolText(symbolName)
- const normalizedType = normalizeSymbolText(group.sourceSymbolType)
- const expectedBytes = Number(group.sourceByteLength || group.byteLength || 0)
- const matchedStruct = catalog.structs.find((structInfo) => {
- const normalizedStructName = normalizeSymbolText(structInfo.name)
- if (normalizedType && normalizedType === normalizedStructName) {
- return true
- }
- if (normalizedSymbol && (
- normalizedSymbol === normalizedStructName
- || normalizedSymbol.indexOf(normalizedStructName) >= 0
- || normalizedStructName.indexOf(normalizedSymbol) >= 0
- )) {
- return true
- }
- return expectedBytes > 0 && getRegistersByteLength(structInfo.registers) === expectedBytes
- })
- return matchedStruct
- ? {
- name: symbolName,
- registers: matchedStruct.registers,
- structName: matchedStruct.name
- }
- : null
- }
- function getEnumLookupNames(group = {}) {
- const registers = Array.isArray(group.registers) ? group.registers : []
- const names = [
- group.sourceDefinitionName,
- group.sourceSymbolType,
- group.sourceSymbolName,
- group.sourceInstanceName,
- group.name
- ]
- registers.forEach((register) => {
- names.push(
- register.sourceDefinitionName,
- register.sourceSymbolType,
- register.sourceSymbolName,
- register.sourceInstanceName,
- register.name
- )
- })
- return names.map(normalizeSymbolText).filter(Boolean)
- }
- function findEnumCompletion(group, catalog = {}) {
- const enums = Array.isArray(catalog.enums) ? catalog.enums : []
- if ((!isEnumCodeInfoEntry(group) && !isVariableCodeInfoEntry(group) && !isEnumArrayCodeInfoEntry(group)) || !enums.length) return null
- const names = getEnumLookupNames(group)
- if (!names.length) return null
- if (isEnumCodeInfoEntry(group) || isEnumArrayCodeInfoEntry(group)) {
- const matchedEnum = enums.find((enumInfo) => (
- [enumInfo.name, enumInfo.typedefName, enumInfo.tagName]
- .concat(enumInfo.typeNames || [])
- .map(normalizeSymbolText)
- .filter(Boolean)
- .some((name) => names.indexOf(name) >= 0)
- ))
- if (matchedEnum) return matchedEnum
- }
- const enumVariablesByName = catalog.enumVariablesByName || {}
- for (const name of names) {
- const variableEnum = enumVariablesByName[name]
- if (variableEnum) return variableEnum
- }
- return enums.find((enumInfo) => (
- [enumInfo.name, enumInfo.typedefName, enumInfo.tagName]
- .concat(enumInfo.typeNames || [])
- .map(normalizeSymbolText)
- .filter(Boolean)
- .some((name) => names.indexOf(name) >= 0)
- )) || null
- }
- function getIntegerDataTypeForByteLength(byteLength) {
- const length = Number(byteLength)
- if (length === 1) return 'uint8_t'
- if (length === 2) return 'uint16_t'
- if (length === 4) return 'uint32_t'
- if (length === 8) return 'uint64_t'
- if (length === 16) return 'uint128_t'
- if (length === 32) return 'uint256_t'
- return ''
- }
- function getDataTypeByteLength(dataType) {
- const key = getDataType(dataType).key
- if (key === 'int8_t' || key === 'uint8_t') return 1
- if (key === 'int16_t' || key === 'uint16_t' || key === 'hex') return 2
- if (key === 'int32_t' || key === 'uint32_t' || key === 'float') return 4
- if (key === 'double' || key === 'int64_t' || key === 'uint64_t') return 8
- if (key === 'int128_t' || key === 'uint128_t') return 16
- if (key === 'int256_t' || key === 'uint256_t') return 32
- return 2
- }
- function getEnumDataTypeForByteLength(enumInfo, byteLength) {
- const inferredType = getDataType(enumInfo && enumInfo.dataType).key
- if (getDataTypeByteLength(inferredType) === Number(byteLength)) return inferredType
- return getIntegerDataTypeForByteLength(byteLength)
- }
- function cloneEnumOptions(enumInfo) {
- return (Array.isArray(enumInfo && enumInfo.options) ? enumInfo.options : []).map((option) => ({
- label: option.label || option.name,
- name: option.name || option.label,
- value: Number(option.value) || 0
- }))
- }
- function completeEnumVariableGroup(group, enumInfo) {
- if (!enumInfo) return group
- const enumOptions = cloneEnumOptions(enumInfo)
- if (!enumOptions.length) return group
- const sourceRegisters = Array.isArray(group.registers) ? group.registers : []
- const registers = sourceRegisters.map((register) => {
- const byteLength = register.sourceByteLength || register.byteLength || group.sourceByteLength || group.byteLength
- const dataType = getEnumDataTypeForByteLength(enumInfo, byteLength)
- return dataType
- ? {
- ...register,
- dataType,
- enumName: enumInfo.name,
- enumOptions,
- sourceDefinitionName: enumInfo.name || register.sourceDefinitionName,
- sourceSymbolType: enumInfo.name || register.sourceSymbolType
- }
- : register
- })
- if (!registers.some((register, index) => register !== sourceRegisters[index])) return group
- return normalizeGroup({
- ...group,
- registers
- })
- }
- function createCompletedRegisters(group, completion) {
- const existingRemarksByByteStart = (Array.isArray(group.registers) ? group.registers : []).reduce((remarks, register) => {
- const byteStart = Number(register && register.byteStart)
- const remark = String(register && register.remark ? register.remark : '').trim()
- if (Number.isFinite(byteStart) && remark) remarks[Math.floor(byteStart)] = remark
- return remarks
- }, {})
- return completion.registers.map((register) => {
- const sourceAddress = (Number(group.sourceAddress) || Number(group.startAddress) || 0) + getRegisterByteStart(register)
- return {
- ...register,
- isStructField: true,
- remark: register.remark || existingRemarksByByteStart[Math.floor(Number(register.byteStart) || 0)] || '',
- sourceAddress,
- sourceAddressByteLength: group.sourceAddressByteLength,
- sourceAddressText: formatAddress(sourceAddress, group.sourceAddressWidth),
- sourceAddressWidth: group.sourceAddressWidth,
- sourceDefinitionName: group.sourceDefinitionName,
- sourceEntryKind: group.sourceEntryKind,
- sourceInstanceName: group.sourceInstanceName,
- sourceMemoryArea: group.sourceMemoryArea,
- sourceMemoryClass: group.sourceMemoryClass,
- sourceSymbolName: group.sourceSymbolName,
- sourceSymbolType: completion.structName || register.sourceSymbolType
- }
- })
- }
- function getArrayDimensions(group = {}) {
- return (Array.isArray(group.sourceArrayDimensions) ? group.sourceArrayDimensions : [])
- .map((value) => Math.max(1, Math.floor(Number(value) || 1)))
- .filter((value) => value > 0)
- }
- function getArrayIndexPath(linearIndex, dimensions = []) {
- const safeDimensions = getArrayDimensions({ sourceArrayDimensions: dimensions })
- if (!safeDimensions.length) return [Math.max(0, Math.floor(Number(linearIndex) || 0))]
- const indexes = Array.from({ length: safeDimensions.length }, () => 0)
- let remaining = Math.max(0, Math.floor(Number(linearIndex) || 0))
- for (let index = safeDimensions.length - 1; index >= 0; index -= 1) {
- indexes[index] = remaining % safeDimensions[index]
- remaining = Math.floor(remaining / safeDimensions[index])
- }
- return indexes
- }
- function formatArrayIndexPath(indexPath = []) {
- return (Array.isArray(indexPath) ? indexPath : [indexPath])
- .map((index) => `[${Math.max(0, Math.floor(Number(index) || 0))}]`)
- .join('')
- }
- function getStructArrayElementCount(group = {}) {
- const explicitCount = Number(group.sourceElementCount)
- if (Number.isFinite(explicitCount) && explicitCount > 0) return Math.floor(explicitCount)
- const dimensions = getArrayDimensions(group)
- if (dimensions.length) return dimensions.reduce((total, value) => total * value, 1)
- return 1
- }
- function createCompletedStructArrayRegisters(group, completion) {
- const existingRemarksByByteStart = (Array.isArray(group.registers) ? group.registers : []).reduce((remarks, register) => {
- const byteStart = Number(register && register.byteStart)
- const remark = String(register && register.remark ? register.remark : '').trim()
- if (Number.isFinite(byteStart) && remark) remarks[Math.floor(byteStart)] = remark
- return remarks
- }, {})
- const elementCount = getStructArrayElementCount(group)
- const elementByteLength = Math.max(1, Math.floor(Number(group.sourceElementByteLength) || getRegistersByteLength(completion.registers) || 1))
- const dimensions = getArrayDimensions(group)
- const baseAddress = Number(group.sourceAddress) || Number(group.startAddress) || 0
- const baseName = group.sourceSymbolName || group.name || completion.name
- const registers = []
- for (let elementIndex = 0; elementIndex < elementCount; elementIndex += 1) {
- const elementByteStart = elementIndex * elementByteLength
- const indexPath = getArrayIndexPath(elementIndex, dimensions)
- const indexText = formatArrayIndexPath(indexPath)
- completion.registers.forEach((register) => {
- const fieldByteStart = getRegisterByteStart(register)
- const byteStart = elementByteStart + fieldByteStart
- const sourceAddress = baseAddress + byteStart
- const fieldName = register.name || `field_${fieldByteStart}`
- registers.push({
- ...register,
- byteStart,
- isStructField: true,
- name: `${baseName}${indexText}.${fieldName}`,
- remark: register.remark || existingRemarksByByteStart[Math.floor(byteStart)] || '',
- sourceAddress,
- sourceAddressByteLength: group.sourceAddressByteLength,
- sourceAddressText: formatAddress(sourceAddress, group.sourceAddressWidth),
- sourceAddressWidth: group.sourceAddressWidth,
- sourceArrayDimensions: dimensions,
- sourceArrayIndex: elementIndex,
- sourceArrayIndexPath: indexPath,
- sourceByteLength: register.sourceByteLength,
- sourceDefinitionName: group.sourceDefinitionName,
- sourceElementByteLength: elementByteLength,
- sourceElementCount: elementCount,
- sourceElementType: group.sourceElementType,
- sourceEntryKind: group.sourceEntryKind,
- sourceInstanceName: group.sourceInstanceName,
- sourceMemoryArea: group.sourceMemoryArea,
- sourceMemoryClass: group.sourceMemoryClass,
- sourceSymbolName: `${baseName}${indexText}.${fieldName}`,
- sourceSymbolType: completion.structName || register.sourceSymbolType,
- sourceValueType: group.sourceValueType
- })
- })
- }
- return registers
- }
- function completeStructInstanceGroups(groups, sourceText, options = {}) {
- const catalog = parseStructCatalog(sourceText)
- let completedCount = 0
- let skippedCount = 0
- const nextGroups = groups.map((group) => {
- if (!group.sourceSymbolName || !group.sourceMemoryArea) return group
- if (isEnumCodeInfoEntry(group) || isVariableCodeInfoEntry(group) || isEnumArrayCodeInfoEntry(group)) {
- const enumInfo = findEnumCompletion(group, catalog)
- if (!enumInfo) return group
- completedCount += 1
- return completeEnumVariableGroup(group, enumInfo)
- }
- if (!isStructCodeInfoEntry(group) && !isStructArrayCodeInfoEntry(group)) return group
- const completion = findStructCompletion(group, catalog)
- if (!completion || !completion.registers || !completion.registers.length) {
- skippedCount += 1
- return group
- }
- const expectedBytes = Number(group.sourceByteLength || group.byteLength || 0)
- const actualBytes = getRegistersByteLength(completion.registers)
- const expectedElementBytes = Number(group.sourceElementByteLength || 0)
- const expectedStructBytes = isStructArrayCodeInfoEntry(group) && expectedElementBytes > 0
- ? expectedElementBytes
- : expectedBytes
- if (expectedStructBytes > 0 && actualBytes !== expectedStructBytes && options.strictLength !== false) {
- skippedCount += 1
- return group
- }
- completedCount += 1
- if (isStructArrayCodeInfoEntry(group)) {
- const registers = createCompletedStructArrayRegisters(group, completion)
- return normalizeGroup({
- ...group,
- layout: 'struct',
- quantity: registers.length,
- registers
- })
- }
- return normalizeGroup({
- ...group,
- layout: 'struct',
- quantity: completion.registers.length,
- registers: createCompletedRegisters(group, completion)
- })
- })
- return {
- completedCount,
- groups: nextGroups,
- skippedCount,
- structCount: catalog.structs.length,
- enumCount: Array.isArray(catalog.enums) ? catalog.enums.length : 0,
- variableCount: Object.keys(catalog.variablesByName).length
- }
- }
- function formatAddress(address, addressWidth) {
- const numberValue = Math.max(0, Math.floor(Number(address) || 0))
- const length = Number(addressWidth) === 32 || numberValue > 0xFFFF ? 8 : 4
- return `0x${numberValue.toString(16).toUpperCase().padStart(length, '0')}`
- }
- function normalizeDuplicateText(value) {
- return String(value === undefined || value === null ? '' : value)
- .trim()
- .toLowerCase()
- }
- function normalizeStructMatchText(value) {
- return String(value === undefined || value === null ? '' : value)
- .trim()
- .replace(/^(?:IDATA|XDATA|DATA|CODE)[\s:_-]+/i, '')
- .replace(/^struct\s+/i, '')
- .replace(/\s+#\d+$/i, '')
- .replace(/^_+/, '')
- .replace(/[^A-Za-z0-9]/g, '')
- .toLowerCase()
- }
- function normalizeAddressKey(value, textValue) {
- const numberValue = Number(value)
- if (Number.isFinite(numberValue)) return String(Math.floor(numberValue))
- return String(textValue === undefined || textValue === null ? '' : textValue)
- .trim()
- .toUpperCase()
- }
- function normalizeBitKey(source = {}) {
- const value = source.sourceBitOffset !== undefined && source.sourceBitOffset !== null && source.sourceBitOffset !== ''
- ? source.sourceBitOffset
- : source.bitOffset
- const numberValue = Number(value)
- return Number.isFinite(numberValue) ? String(Math.floor(numberValue)) : ''
- }
- function getRegisterDuplicateKey(register = {}, group = {}) {
- const area = normalizeDuplicateText(register.sourceMemoryArea || group.sourceMemoryArea || register.memoryArea || '')
- const symbolName = normalizeDuplicateText(register.sourceSymbolName || register.name || '')
- if (area && symbolName) return ['register', area, symbolName].join('|')
- const addressKey = normalizeAddressKey(
- register.sourceAddress !== undefined ? register.sourceAddress : register.address,
- register.sourceAddressText || register.addressText
- )
- const bitKey = normalizeBitKey(register)
- if (!area && !symbolName && !addressKey) return ''
- return ['register', area, symbolName, addressKey, bitKey].join('|')
- }
- function isSingleRegisterAggregateGroup(group = {}) {
- if (isArrayCodeInfoEntry(group)) return false
- const groupSymbolName = normalizeDuplicateText(group.sourceSymbolName || group.name || '')
- const registers = Array.isArray(group.registers) ? group.registers : []
- return registers.some((register) => {
- const registerSymbolName = normalizeDuplicateText(register.sourceSymbolName || register.name || '')
- return registerSymbolName && groupSymbolName && registerSymbolName !== groupSymbolName
- })
- }
- function getGroupDuplicateKey(group = {}) {
- const area = normalizeDuplicateText(group.sourceMemoryArea || '')
- const symbolName = normalizeDuplicateText(group.sourceSymbolName || group.name || '')
- const addressKey = normalizeAddressKey(
- group.sourceAddress !== undefined ? group.sourceAddress : group.startAddress,
- group.sourceAddressText || group.startAddressText
- )
- if (area && symbolName && addressKey) return ['group', area, symbolName, addressKey].join('|')
- if (area && symbolName) return ['group', area, symbolName].join('|')
- if (!area && !symbolName && !addressKey) return ''
- return ['group', area, symbolName, addressKey].join('|')
- }
- function getAggregateGroupDuplicateKey(source = {}) {
- const area = normalizeDuplicateText(source.sourceMemoryArea || source.memoryArea || '')
- const registerType = normalizeDuplicateText(source.registerType || '')
- const segment = normalizeDuplicateText(source.sourceSegment || '')
- return ['aggregate', area, registerType, segment].join('|')
- }
- function collectImportedVariableIndexes(groups = []) {
- return groups.reduce((indexes, group, groupIndex) => {
- if (!isSingleRegisterAggregateGroup(group)) {
- const groupKey = getGroupDuplicateKey(group)
- if (groupKey) indexes.groupIndexes[groupKey] = groupIndex
- } else {
- const aggregateKey = getAggregateGroupDuplicateKey(group)
- if (aggregateKey) indexes.aggregateGroupIndexes[aggregateKey] = groupIndex
- }
- ;(Array.isArray(group.registers) ? group.registers : []).forEach((register) => {
- const registerKey = getRegisterDuplicateKey(register, group)
- if (registerKey) {
- indexes.registerIndexes[registerKey] = {
- groupIndex,
- registerIndex: group.registers.indexOf(register)
- }
- }
- })
- return indexes
- }, {
- aggregateGroupIndexes: {},
- groupIndexes: {},
- registerIndexes: {}
- })
- }
- function getStructMatchNames(group = {}) {
- const registers = Array.isArray(group.registers) ? group.registers : []
- const names = [
- group.sourceSymbolName,
- group.sourceSymbolType,
- group.name,
- group.displayName
- ]
- registers.forEach((register) => {
- names.push(register.sourceSymbolType, register.sourceSymbolName)
- })
- return names
- .map(normalizeStructMatchText)
- .filter(Boolean)
- .filter((name, index, list) => list.indexOf(name) === index)
- }
- function structsMatchByName(existingGroup = {}, incomingGroup = {}) {
- const existingNames = getStructMatchNames(existingGroup)
- const incomingNames = getStructMatchNames(incomingGroup)
- return existingNames.some((name) => incomingNames.indexOf(name) >= 0)
- }
- function getGroupByteLengthCandidates(group = {}) {
- const registers = Array.isArray(group.registers) ? group.registers : []
- const candidates = [
- group.sourceByteLength,
- group.byteLength,
- group.structByteLength,
- getRegistersByteLength(registers)
- ]
- registers.forEach((register) => {
- candidates.push(register.structByteLength)
- })
- return candidates
- .map((value) => Number(value))
- .filter((value) => Number.isFinite(value) && value > 0)
- .map((value) => Math.floor(value))
- .filter((value, index, list) => list.indexOf(value) === index)
- }
- function structsMatchByByteLength(existingGroup = {}, incomingGroup = {}) {
- const existingLengths = getGroupByteLengthCandidates(existingGroup)
- const incomingLengths = getGroupByteLengthCandidates(incomingGroup)
- return existingLengths.some((length) => incomingLengths.indexOf(length) >= 0)
- }
- function structsMatchByLocation(existingGroup = {}, incomingGroup = {}) {
- const existingArea = normalizeDuplicateText(existingGroup.sourceMemoryArea || '')
- const incomingArea = normalizeDuplicateText(incomingGroup.sourceMemoryArea || '')
- const existingAddress = normalizeAddressKey(
- existingGroup.sourceAddress !== undefined ? existingGroup.sourceAddress : existingGroup.startAddress,
- existingGroup.sourceAddressText || existingGroup.startAddressText
- )
- const incomingAddress = normalizeAddressKey(
- incomingGroup.sourceAddress !== undefined ? incomingGroup.sourceAddress : incomingGroup.startAddress,
- incomingGroup.sourceAddressText || incomingGroup.startAddressText
- )
- if (existingArea && incomingArea && existingArea !== incomingArea) return false
- if (existingAddress && incomingAddress && existingAddress !== incomingAddress) return false
- return true
- }
- function isIncomingPlaceholderStructGroup(group = {}) {
- const registers = Array.isArray(group.registers) ? group.registers : []
- return group.layout === 'struct'
- && registers.length > 0
- && registers.every((register) => !!register.isPlaceholderByteField)
- }
- function hasImportedStructRegisters(group = {}) {
- const registers = Array.isArray(group.registers) ? group.registers : []
- return group.layout === 'struct'
- && registers.length > 0
- && registers.some((register) => !register.isPlaceholderByteField)
- }
- function canPreserveExistingStructLayout(existingGroup, incomingGroup, options = {}) {
- return options.preserveExistingStructLayout
- && hasImportedStructRegisters(existingGroup)
- && isIncomingPlaceholderStructGroup(incomingGroup)
- && structsMatchByName(existingGroup, incomingGroup)
- && structsMatchByByteLength(existingGroup, incomingGroup)
- && structsMatchByLocation(existingGroup, incomingGroup)
- }
- function getRegisterByteStart(register = {}) {
- const byteStart = Number(register.byteStart)
- return Number.isFinite(byteStart) ? Math.max(0, Math.floor(byteStart)) : 0
- }
- function mergePreservedStructRegister(register = {}, incomingGroup = {}) {
- const byteStart = getRegisterByteStart(register)
- const sourceAddress = (Number(incomingGroup.sourceAddress) || Number(incomingGroup.startAddress) || 0) + byteStart
- const sourceSymbolName = incomingGroup.sourceSymbolName || register.sourceSymbolName
- const sourceSymbolType = incomingGroup.sourceSymbolType || register.sourceSymbolType || sourceSymbolName
- return {
- ...register,
- rawBytes: [],
- rawValue: null,
- rawWords: [],
- sourceAddress,
- sourceAddressByteLength: incomingGroup.sourceAddressByteLength || register.sourceAddressByteLength,
- sourceAddressText: formatAddress(sourceAddress, incomingGroup.sourceAddressWidth || register.sourceAddressWidth),
- sourceAddressWidth: incomingGroup.sourceAddressWidth || register.sourceAddressWidth,
- sourceDefinitionName: incomingGroup.sourceDefinitionName || register.sourceDefinitionName,
- sourceEntryKind: incomingGroup.sourceEntryKind,
- sourceInstanceName: incomingGroup.sourceInstanceName || register.sourceInstanceName,
- sourceMemoryArea: incomingGroup.sourceMemoryArea,
- sourceMemoryClass: incomingGroup.sourceMemoryClass,
- sourceSymbolName,
- sourceSymbolType
- }
- }
- function resolveMergedPollEnabled(existingGroup = {}, incomingGroup = {}, options = {}) {
- if (options.preserveExistingPollEnabled && existingGroup.pollEnabled === false) return false
- return incomingGroup.pollEnabled === false ? false : true
- }
- function mergePreservedStructGroupState(existingGroup, incomingGroup, options = {}) {
- const preservedRegisters = (Array.isArray(existingGroup.registers) ? existingGroup.registers : [])
- .map((register) => mergePreservedStructRegister(register, incomingGroup))
- return {
- ...incomingGroup,
- deleteVisible: false,
- expanded: existingGroup.expanded === true,
- id: existingGroup.id,
- pollEnabled: resolveMergedPollEnabled(existingGroup, incomingGroup, options),
- quantity: preservedRegisters.length,
- registers: preservedRegisters
- }
- }
- function findPreservableStructGroupIndex(groups = [], incomingGroup = {}, preferredIndex, options = {}) {
- if (preferredIndex !== undefined && canPreserveExistingStructLayout(groups[preferredIndex], incomingGroup, options)) {
- return preferredIndex
- }
- if (!options.preserveExistingStructLayout || !isIncomingPlaceholderStructGroup(incomingGroup)) return undefined
- return groups.findIndex((group, index) => (
- index !== preferredIndex && canPreserveExistingStructLayout(group, incomingGroup, options)
- ))
- }
- function mergeImportedRegisterState(existingRegister, incomingRegister, options = {}) {
- if (!existingRegister) return incomingRegister
- const incomingRemark = incomingRegister.remark
- const shouldPreserveRemark = options.preserveExistingRemarks
- && !String(incomingRemark === undefined || incomingRemark === null ? '' : incomingRemark).trim()
- return {
- ...incomingRegister,
- id: existingRegister.id,
- inputValue: incomingRegister.inputValue !== undefined && incomingRegister.inputValue !== null
- ? incomingRegister.inputValue
- : existingRegister.inputValue,
- remark: shouldPreserveRemark
- ? existingRegister.remark
- : (incomingRemark !== undefined && incomingRemark !== null ? incomingRemark : existingRegister.remark),
- rawBytes: [],
- rawValue: null,
- rawWords: []
- }
- }
- function mergeImportedGroupState(existingGroup, incomingGroup, options = {}) {
- if (!existingGroup) return incomingGroup
- if (canPreserveExistingStructLayout(existingGroup, incomingGroup, options)) {
- return mergePreservedStructGroupState(existingGroup, incomingGroup, options)
- }
- const existingRegisters = Array.isArray(existingGroup.registers) ? existingGroup.registers : []
- const incomingRegisters = Array.isArray(incomingGroup.registers) ? incomingGroup.registers : []
- return {
- ...incomingGroup,
- deleteVisible: false,
- expanded: existingGroup.expanded === true,
- id: existingGroup.id,
- pollEnabled: resolveMergedPollEnabled(existingGroup, incomingGroup, options),
- registers: incomingRegisters.map((incomingRegister, index) => mergeImportedRegisterState(
- existingRegisters[index],
- incomingRegister,
- options
- ))
- }
- }
- function mergeAggregateImportedGroup(nextGroups, incomingGroup, indexes, options = {}) {
- const aggregateKey = getAggregateGroupDuplicateKey(incomingGroup)
- const aggregateGroupIndex = indexes.aggregateGroupIndexes[aggregateKey]
- let targetGroup = aggregateGroupIndex === undefined ? null : nextGroups[aggregateGroupIndex]
- let targetGroupIndex = aggregateGroupIndex
- let targetRegisters = targetGroup && Array.isArray(targetGroup.registers)
- ? targetGroup.registers.slice()
- : []
- let addedRegisterCount = 0
- let updatedRegisterCount = 0
- ;(Array.isArray(incomingGroup.registers) ? incomingGroup.registers : []).forEach((incomingRegister) => {
- const registerKey = getRegisterDuplicateKey(incomingRegister, incomingGroup)
- const existingRef = registerKey ? indexes.registerIndexes[registerKey] : null
- if (existingRef) {
- const existingGroup = nextGroups[existingRef.groupIndex]
- const existingRegister = existingGroup && existingGroup.registers
- ? existingGroup.registers[existingRef.registerIndex]
- : null
- const mergedRegister = mergeImportedRegisterState(existingRegister, incomingRegister, options)
- if (targetGroupIndex !== undefined && existingRef.groupIndex === targetGroupIndex) {
- targetRegisters[existingRef.registerIndex] = mergedRegister
- } else if (existingGroup) {
- const registers = existingGroup.registers.slice()
- registers[existingRef.registerIndex] = mergedRegister
- nextGroups[existingRef.groupIndex] = normalizeGroup({
- ...existingGroup,
- registers
- })
- }
- updatedRegisterCount += 1
- return
- }
- targetRegisters.push(incomingRegister)
- addedRegisterCount += 1
- })
- if (targetGroupIndex !== undefined && targetGroup) {
- nextGroups[targetGroupIndex] = normalizeGroup(mergeImportedGroupState(targetGroup, {
- ...incomingGroup,
- quantity: targetRegisters.length,
- registers: targetRegisters
- }, options))
- } else if (targetRegisters.length) {
- targetGroup = normalizeGroup({
- ...incomingGroup,
- quantity: targetRegisters.length,
- registers: targetRegisters
- })
- nextGroups.push(targetGroup)
- targetGroupIndex = nextGroups.length - 1
- }
- return {
- addedGroupCount: targetGroupIndex === aggregateGroupIndex ? 0 : (targetRegisters.length ? 1 : 0),
- addedRegisterCount,
- updatedGroupCount: targetGroupIndex === aggregateGroupIndex && (addedRegisterCount || updatedRegisterCount) ? 1 : 0,
- updatedRegisterCount
- }
- }
- function mergeImportedGroups(existingGroups = [], incomingGroups = [], options = {}) {
- const nextGroups = existingGroups.slice()
- let indexes = collectImportedVariableIndexes(nextGroups)
- const result = {
- addedGroupCount: 0,
- addedRegisterCount: 0,
- groups: nextGroups,
- updatedGroupCount: 0,
- updatedRegisterCount: 0
- }
- incomingGroups.forEach((incomingGroup) => {
- if (isSingleRegisterAggregateGroup(incomingGroup)) {
- const aggregateResult = mergeAggregateImportedGroup(nextGroups, incomingGroup, indexes, options)
- result.addedGroupCount += aggregateResult.addedGroupCount
- result.addedRegisterCount += aggregateResult.addedRegisterCount
- result.updatedGroupCount += aggregateResult.updatedGroupCount
- result.updatedRegisterCount += aggregateResult.updatedRegisterCount
- indexes = collectImportedVariableIndexes(nextGroups)
- return
- }
- const groupKey = getGroupDuplicateKey(incomingGroup)
- const existingGroupIndex = groupKey ? indexes.groupIndexes[groupKey] : undefined
- const preservableStructGroupIndex = findPreservableStructGroupIndex(
- nextGroups,
- incomingGroup,
- existingGroupIndex,
- options
- )
- const targetGroupIndex = preservableStructGroupIndex >= 0
- ? preservableStructGroupIndex
- : existingGroupIndex
- if (targetGroupIndex !== undefined) {
- const existingGroup = nextGroups[targetGroupIndex]
- nextGroups[targetGroupIndex] = normalizeGroup(mergeImportedGroupState(existingGroup, incomingGroup, options))
- result.updatedGroupCount += 1
- } else {
- nextGroups.push(incomingGroup)
- result.addedGroupCount += 1
- }
- indexes = collectImportedVariableIndexes(nextGroups)
- })
- result.changedCount = result.addedGroupCount
- + result.updatedGroupCount
- + result.addedRegisterCount
- + result.updatedRegisterCount
- return result
- }
- function parseStructDefinition(sourceText) {
- return parseStructDefinitionSource(sourceText)
- }
- module.exports = {
- completeStructInstanceGroups,
- getRegistersByteLength,
- mergeImportedGroups,
- parseStructDefinition
- }
|