const storageAccessProtocol = require('../../protocols/storage-access/index.js') const { bytesToHex, formatHexNumber } = require('../../utils/binary-utils.js') const { normalizeHexDwordText, normalizeHexText, normalizeHexWordText, parseHexBytes, parseHexDword, parseHexNumber, validateHexDwordText, validateHexText, validateHexWordText } = require('../../utils/validation.js') const { getManualMemoryAreaOptions } = require('./memory-areas.js') const protocolIo = require('./protocol-io.js') const MEMORY_COMMANDS = [ { key: 'read', label: '读取', description: '按字节读取内存' }, { key: 'write', label: '写入', description: '按字节写入内存' } ] const CONTROL_COMMANDS = [ { key: 'reset', label: '复位', op: storageAccessProtocol.CONTROL_OP.RESET } ] function getMemoryAreaOptions() { return getManualMemoryAreaOptions() } function resolveMemoryCommand(index) { const commandIndex = Number(index) === 1 ? 1 : 0 return { command: MEMORY_COMMANDS[commandIndex], index: commandIndex } } function resolveMemoryArea(index, commandKey = 'read') { const memoryAreas = getMemoryAreaOptions() let areaIndex = Number(index) || 0 if (commandKey === 'write' && memoryAreas[areaIndex] && memoryAreas[areaIndex].readOnly) { areaIndex = 0 } if (!memoryAreas[areaIndex]) areaIndex = 0 return { area: memoryAreas[areaIndex] || memoryAreas[0], index: areaIndex, options: memoryAreas } } function normalizeDisplayHexText(value, addressWidth = 16) { const text = addressWidth === 32 ? normalizeHexDwordText(value) : normalizeHexWordText(value) return text.toUpperCase() } function buildMemoryPreview(commandKey, area, address, length, dataBytes) { try { const frameOptions = protocolIo.normalizeProtocolIoOptions({ maxFrameBytes: 0, useDeviceCaps: false, showModal: false }) if (commandKey === 'write') { return bytesToHex(storageAccessProtocol.buildWriteFrame(area, address, dataBytes, frameOptions), ' ') } return bytesToHex(storageAccessProtocol.buildReadFrame(area, address, length, frameOptions), ' ') } catch (error) { return '' } } function normalizeMemoryCommandState(current = {}, changed = {}) { const next = { storageAccessAddress: current.storageAccessAddress || '', storageAccessAreaIndex: current.storageAccessAreaIndex || 0, storageAccessCommandIndex: current.storageAccessCommandIndex || 0, storageAccessDataText: current.storageAccessDataText || '', storageAccessLength: current.storageAccessLength || '', ...changed } const resolvedCommand = resolveMemoryCommand(next.storageAccessCommandIndex) const command = resolvedCommand.command const resolvedArea = resolveMemoryArea(next.storageAccessAreaIndex, command.key) const area = resolvedArea.area const addressWidth = Number(area.addressWidth) === 32 ? 32 : 16 const addressText = normalizeDisplayHexText(next.storageAccessAddress, addressWidth) const lengthText = normalizeDisplayHexText(next.storageAccessLength, 16) const dataText = normalizeHexText(next.storageAccessDataText) const dataBytes = dataText ? parseHexBytes(dataText) : [] const address = parseInt(addressText || '0', 16) const byteLength = parseInt(lengthText || '0', 16) const hasAddressText = !!addressText.trim() const hasLengthText = !!lengthText.trim() const addressError = hasAddressText ? (addressWidth === 32 ? validateHexDwordText(addressText, '地址') : validateHexWordText(addressText, '地址')) : '' const lengthError = hasLengthText ? validateHexWordText(lengthText, '长度') : '' let errorText = addressError || lengthError if (!errorText && hasLengthText && byteLength <= 0) { errorText = '长度必须大于 0' } else if (!errorText && command.key === 'write') { const hexError = validateHexText(next.storageAccessDataText) if (hexError) { errorText = hexError } else if (area.readOnly) { errorText = `${area.label} 区不可写` } else if (dataBytes.length !== byteLength) { errorText = `写入长度为 ${byteLength} 字节,当前数据为 ${dataBytes.length} 字节` } } const canPreview = !errorText && hasAddressText && hasLengthText && byteLength > 0 return { storageAccessAddress: addressText, storageAccessAddressMaxLength: addressWidth === 32 ? 8 : 4, storageAccessAddressWidthText: `${addressWidth}bit`, storageAccessAreaIndex: resolvedArea.index, storageAccessAreaLabel: area.label, storageAccessAreaLocked: false, storageAccessAreaOptions: resolvedArea.options, storageAccessCommandIndex: resolvedCommand.index, storageAccessCommandLabel: command.label, storageAccessDataText: dataText, storageAccessErrorText: errorText, storageAccessLength: lengthText, storageAccessPreviewHexText: canPreview ? buildMemoryPreview(command.key, area.key, address, byteLength, dataBytes) : '', storageAccessPreviewText: canPreview ? `${area.label} 0x${formatHexNumber(address, addressWidth === 32 ? 8 : 4)} / ${byteLength} bytes` : '', storageAccessShowWriteData: command.key === 'write', storageAccessTitleText: `${command.label}命令` } } function parseMemoryCommandInput(data = {}) { const state = normalizeMemoryCommandState(data) const command = resolveMemoryCommand(state.storageAccessCommandIndex).command const area = resolveMemoryArea(state.storageAccessAreaIndex, command.key).area if (state.storageAccessErrorText) { throw new Error(state.storageAccessErrorText) } if (!String(state.storageAccessAddress || '').trim()) { throw new Error('地址请输入十六进制') } if (!String(state.storageAccessLength || '').trim()) { throw new Error('长度请输入十六进制') } return { area, areaValue: area.key, byteLength: parseHexNumber(state.storageAccessLength, '长度', 0xFFFF), command, commandKey: command.key, dataBytes: command.key === 'write' ? parseHexBytes(state.storageAccessDataText || '') : [], startAddress: Number(area.addressWidth) === 32 ? parseHexDword(state.storageAccessAddress, '地址') : parseHexNumber(state.storageAccessAddress, '地址', 0xFFFF), state } } async function executeMemoryCommand(data = {}, options = {}) { const command = parseMemoryCommandInput(data) if (command.commandKey === 'read') { const bytes = await protocolIo.readMemory( command.areaValue, command.startAddress, command.byteLength, options.label || '存储访问协议读取', options.kind || 'communication-storage-read', options ) return { bytes, command, ok: !!bytes, previewHex: bytes ? bytesToHex(bytes, ' ') : '', state: command.state } } const ok = await protocolIo.writeMemory( command.areaValue, command.startAddress, command.dataBytes, options.label || '存储访问协议写入', options.kind || 'communication-storage-write', options ) return { command, ok, state: command.state } } function getControlCommand(commandKey) { return CONTROL_COMMANDS.find((command) => command.key === commandKey) || null } async function executeControlCommand(commandKey, data = {}, options = {}) { const command = getControlCommand(commandKey) if (!command) { return { errorText: '特殊指令无效', ok: false } } const response = await protocolIo.executeControl( command.op, [], command.label || '特殊指令', `storage-control-${command.key}`, { maxPacketLength: options.maxPacketLength, showModal: options.showModal !== false } ) if (!response) { return { errorText: '指令执行失败或超时', ok: false } } return { command, ok: true, response } } module.exports = { executeControlCommand, executeMemoryCommand, getControlCommand, getMemoryAreaOptions, normalizeMemoryCommandState }