| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- const {
- normalizeGroup
- } = require('../../domain/parameter-groups/model.js')
- const {
- getRegistersByteLength
- } = require('./struct-completion.js')
- function formatAddress(address) {
- return `0x${Number(address || 0).toString(16).toUpperCase().padStart(4, '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 = {}) {
- 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 || '')
- if (area && symbolName) return ['group', area, symbolName].join('|')
- const addressKey = normalizeAddressKey(
- group.sourceAddress !== undefined ? group.sourceAddress : group.startAddress,
- group.sourceAddressText || group.startAddressText
- )
- 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 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)
- }
- 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) & 0xFFFF
- const sourceSymbolName = incomingGroup.sourceSymbolName || register.sourceSymbolName
- const sourceSymbolType = incomingGroup.sourceSymbolType || register.sourceSymbolType || sourceSymbolName
- return {
- ...register,
- rawBytes: [],
- rawValue: null,
- rawWords: [],
- sourceAddress,
- sourceAddressText: formatAddress(sourceAddress),
- sourceMemoryArea: incomingGroup.sourceMemoryArea,
- sourceMemoryClass: incomingGroup.sourceMemoryClass,
- sourceSymbolName,
- sourceSymbolType
- }
- }
- function mergePreservedStructGroupState(existingGroup, incomingGroup) {
- const preservedRegisters = (Array.isArray(existingGroup.registers) ? existingGroup.registers : [])
- .map((register) => mergePreservedStructRegister(register, incomingGroup))
- return {
- ...incomingGroup,
- deleteVisible: false,
- expanded: existingGroup.expanded === true,
- id: existingGroup.id,
- 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)
- }
- 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,
- 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
- }))
- } 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
- }
- module.exports = {
- mergeImportedGroups
- }
|