const { bytesToWords } = require('../../utils/binary-utils.js') const transport = require('../../transport/ble-core.js') const storageAccessMemory = require('../storage-access/memory-service.js') const { decodeRegisterFromByteCache, decodeRegisterValue, formatRegisterValue, getGroupEncodedBytes, getRegisterBytesFromByteCache, getRegisterEncodedBytes, getRegisterWordsFromByteCache, getRegisterWordsFromWordCache } = require('../../domain/parameter-groups/model.js') const STORAGE_ACCESS_MEMORY_TYPES = { DATA: 0x01, BIT: 0x01, IDATA: 0x02, XDATA: 0x03, CODE: 0x04 } const STORAGE_ACCESS_CODE_AREA = 0x04 function getMemoryType(group = {}) { const memoryArea = String(group.sourceMemoryArea || '').trim().toUpperCase() return Object.prototype.hasOwnProperty.call(STORAGE_ACCESS_MEMORY_TYPES, memoryArea) ? STORAGE_ACCESS_MEMORY_TYPES[memoryArea] : null } function isMemoryGroup(group = {}) { return getMemoryType(group) !== null } function isByteAddressedGroup(group = {}) { return group.addressUnit === 'byte' || group.addressUnit === 'bytes' || isMemoryGroup(group) } function getMemoryAddress(group = {}) { const sourceAddress = Number(group.sourceAddress) if (Number.isFinite(sourceAddress)) return Math.max(0, Math.floor(sourceAddress)) & 0xFFFF return Math.max(0, Number(group.startAddress) || 0) & 0xFFFF } function getMemoryByteLength(group = {}) { const sourceByteLength = Number(group.sourceByteLength) if (Number.isFinite(sourceByteLength) && sourceByteLength > 0) return Math.min(0xFFFF, Math.ceil(sourceByteLength)) const byteLength = Number(group.byteLength) if (Number.isFinite(byteLength) && byteLength > 0) return Math.min(0xFFFF, Math.ceil(byteLength)) return Math.max(1, Math.ceil((Number(group.wordQuantity) || 1) * 2)) } function bytesToPaddedWords(bytes = []) { return bytesToWords(bytes.length % 2 === 0 ? bytes : bytes.concat(0)) } function fillByteCacheFromBytes(byteCache, startAddress, bytes = []) { bytes.forEach((byte, offset) => { byteCache[startAddress + offset] = Number(byte) & 0xFF }) } function fillWordCacheFromBytes(wordCache, startAddress, bytes = []) { const words = bytesToPaddedWords(bytes) words.forEach((word, offset) => { wordCache[startAddress + offset] = Number(word) & 0xFFFF }) } function createWrittenRegisterSnapshots(group, wordCache) { return group.registers.map((register) => { const rawBytes = isByteAddressedGroup(group) ? (getRegisterBytesFromByteCache(register, wordCache) || []) : [] const rawWords = isByteAddressedGroup(group) ? (getRegisterWordsFromByteCache(register, wordCache) || []) : (getRegisterWordsFromWordCache(register, wordCache) || []) const rawValue = isByteAddressedGroup(group) ? decodeRegisterFromByteCache(register, wordCache) : decodeRegisterValue(register, rawWords) return { rawBytes, rawWords, rawValue, displayValue: formatRegisterValue(register, rawValue) } }) } function createWrittenRegisterSnapshot(group, register, byteCache) { const snapshots = createWrittenRegisterSnapshots({ ...group, registers: [register] }, byteCache) return snapshots[0] || null } function groupHasBitFields(group = {}) { return (Array.isArray(group.registers) ? group.registers : []).some((register) => !!register.isBitField) } async function writeMemoryRegister(group, register, options = {}) { const memoryType = getMemoryType(group) const maxPacketLength = storageAccessMemory.resolveMaxPacketLength(options.maxPacketLength) const byteLength = Math.max(1, Number(register.byteLength) || 1) const address = Math.max(0, Math.floor(Number(register.address) || getMemoryAddress(group))) & 0xFFFF let bytes if (memoryType === null) { transport.showCommandAlert('内存写入', `暂不支持 ${group.sourceMemoryArea || '未知'} 内存区域`) return null } if (memoryType === STORAGE_ACCESS_CODE_AREA) { transport.showCommandAlert('内存写入', 'code 区暂不支持写入') return null } try { if (register.isBitField) { const baseBytes = await storageAccessMemory.readMemory( memoryType, address, byteLength, register.name ? `${register.name} 读改写` : '变量读改写', 'parameter-group-memory-register-rmw-read', { maxFrameBytes: maxPacketLength } ) if (!baseBytes) return null bytes = getGroupEncodedBytes({ ...group, paddedByteLength: byteLength, registers: [{ ...register, address, byteStart: 0 }] }, baseBytes).slice(0, byteLength) } else { bytes = getRegisterEncodedBytes(register) } } catch (error) { transport.showCommandAlert('内存写入', error.message || '变量没有有效写入值') return null } if (!Array.isArray(bytes) || !bytes.length) { transport.showCommandAlert('内存写入', `${register.name || '变量'} 没有有效写入值`) return null } bytes = bytes.slice(0, byteLength) while (bytes.length < byteLength) bytes.push(0) const ok = await storageAccessMemory.writeMemory( memoryType, address, bytes, register.name || group.name || '变量写入', 'parameter-group-memory-register-write', { maxFrameBytes: maxPacketLength } ) if (!ok) return null const byteCache = {} fillByteCacheFromBytes(byteCache, address, bytes) return createWrittenRegisterSnapshot(group, register, byteCache) } async function readMemoryGroup(group, options = {}) { const memoryType = getMemoryType(group) const address = getMemoryAddress(group) const byteLength = getMemoryByteLength(group) const maxPacketLength = storageAccessMemory.resolveMaxPacketLength(options.maxPacketLength) if (memoryType === null) { transport.showCommandAlert('内存读取', `暂不支持 ${group.sourceMemoryArea || '未知'} 内存区域`) return null } const bytes = await storageAccessMemory.readMemory( memoryType, address, byteLength, group.name || '内存读取', 'parameter-group-memory-read', { maxFrameBytes: maxPacketLength, showModal: options.showModal !== false } ) if (!bytes) return null const wordCache = {} if (isByteAddressedGroup(group)) { fillByteCacheFromBytes(wordCache, address, bytes) } else { fillWordCacheFromBytes(wordCache, address, bytes) } return wordCache } async function writeMemoryGroup(group, options = {}) { const memoryType = getMemoryType(group) const address = getMemoryAddress(group) const byteLength = getMemoryByteLength(group) const maxPacketLength = storageAccessMemory.resolveMaxPacketLength(options.maxPacketLength) let bytes if (memoryType === null) { transport.showCommandAlert('内存写入', `暂不支持 ${group.sourceMemoryArea || '未知'} 内存区域`) return null } if (memoryType === STORAGE_ACCESS_CODE_AREA) { transport.showCommandAlert('内存写入', 'code 区暂不支持写入') return null } try { let baseBytes = null if (groupHasBitFields(group)) { baseBytes = await storageAccessMemory.readMemory( memoryType, address, byteLength, group.name ? `${group.name} 读改写` : '内存读改写', 'parameter-group-memory-rmw-read', { maxFrameBytes: maxPacketLength } ) if (!baseBytes) return null } bytes = getGroupEncodedBytes(group, baseBytes) } catch (error) { transport.showCommandAlert('内存写入', error.message || '寄存器组没有有效写入值') return null } bytes = bytes.slice(0, byteLength) const ok = await storageAccessMemory.writeMemory( memoryType, address, bytes, group.name || '内存写入', 'parameter-group-memory-write', { maxFrameBytes: maxPacketLength } ) if (!ok) return null const wordCache = {} if (isByteAddressedGroup(group)) { fillByteCacheFromBytes(wordCache, address, bytes) } else { fillWordCacheFromBytes(wordCache, address, bytes) } return createWrittenRegisterSnapshots(group, wordCache) } module.exports = { getMemoryAddress, getMemoryByteLength, getMemoryType, isByteAddressedGroup, isMemoryGroup, readMemoryGroup, writeMemoryGroup, writeMemoryRegister }