|
|
@@ -1,614 +0,0 @@
|
|
|
-const storageAccessProtocol = require('../../protocols/storage-access/index.js')
|
|
|
-const settingsService = require('../../store/settings-store.js')
|
|
|
-const transport = require('../../transport/ble-core.js')
|
|
|
-const {
|
|
|
- createGroupsFromCodeInfo,
|
|
|
- parseCodeInfo
|
|
|
-} = require('../../domain/storage-access/code-info-parser.js')
|
|
|
-const {
|
|
|
- cloneImportedGroup,
|
|
|
- normalizeGroup
|
|
|
-} = require('../../domain/parameter-groups/model.js')
|
|
|
-const {
|
|
|
- bytesToHex
|
|
|
-} = require('../../utils/binary-utils.js')
|
|
|
-const {
|
|
|
- normalizeHexDwordText,
|
|
|
- normalizeHexText,
|
|
|
- normalizeHexWordText,
|
|
|
- parseHexBytes,
|
|
|
- parseHexDword,
|
|
|
- parseHexNumber,
|
|
|
- validateHexDwordText,
|
|
|
- validateHexText,
|
|
|
- validateHexWordText
|
|
|
-} = require('../../utils/validation.js')
|
|
|
-
|
|
|
-const MEMORY_COMMANDS = [
|
|
|
- { key: 'read', label: '读取', description: '按字节读取内存' },
|
|
|
- { key: 'write', label: '写入', description: '按字节写入内存' }
|
|
|
-]
|
|
|
-
|
|
|
-const MEMORY_AREAS = [
|
|
|
- { key: storageAccessProtocol.AREA.ADDR32, label: 'addr32', name: 'ADDR32', addressWidth: 32 },
|
|
|
- { key: storageAccessProtocol.AREA.CODEINFO, label: 'codeinfo', name: 'CODEINFO', readOnly: true },
|
|
|
- { key: storageAccessProtocol.AREA.DATA, label: 'data', name: 'DATA' },
|
|
|
- { key: storageAccessProtocol.AREA.IDATA, label: 'idata', name: 'IDATA' },
|
|
|
- { key: storageAccessProtocol.AREA.XDATA, label: 'xdata', name: 'XDATA' },
|
|
|
- { key: storageAccessProtocol.AREA.CODE, label: 'code', name: 'CODE', readOnly: true }
|
|
|
-]
|
|
|
-const MEMORY_READ_INDEX = 0
|
|
|
-const MEMORY_WRITE_INDEX = 1
|
|
|
-
|
|
|
-const CONTROL_COMMANDS = [
|
|
|
- { key: 'reset', label: '复位', op: storageAccessProtocol.CONTROL_OP.RESET }
|
|
|
-]
|
|
|
-
|
|
|
-let syncedDeviceCaps = {
|
|
|
- addressWidth: 0,
|
|
|
- maxPacketLength: 0,
|
|
|
- memoryEndian: ''
|
|
|
-}
|
|
|
-
|
|
|
-function resolveMaxPacketLength(value) {
|
|
|
- const settings = settingsService.getState()
|
|
|
- const numberValue = Number(value === undefined ? settings.parameterMaxPacketLength : value)
|
|
|
- const deviceMaxPacketLength = Number(syncedDeviceCaps.maxPacketLength || 0)
|
|
|
- const configuredMaxPacketLength = Number.isFinite(numberValue) && Math.round(numberValue) === 0
|
|
|
- ? 0
|
|
|
- : (Number.isFinite(numberValue) && numberValue > 0 ? Math.round(numberValue) : 64)
|
|
|
-
|
|
|
- if (!Number.isFinite(deviceMaxPacketLength) || deviceMaxPacketLength <= 0) return configuredMaxPacketLength
|
|
|
- if (configuredMaxPacketLength === 0) return Math.round(deviceMaxPacketLength)
|
|
|
-
|
|
|
- return Math.min(configuredMaxPacketLength, Math.round(deviceMaxPacketLength))
|
|
|
-}
|
|
|
-
|
|
|
-function resolveConfiguredMaxPacketLength(value) {
|
|
|
- const settings = settingsService.getState()
|
|
|
- const numberValue = Number(value === undefined ? settings.parameterMaxPacketLength : value)
|
|
|
- if (Number.isFinite(numberValue) && Math.round(numberValue) === 0) return 0
|
|
|
- if (Number.isFinite(numberValue) && numberValue > 0) return Math.round(numberValue)
|
|
|
-
|
|
|
- return 64
|
|
|
-}
|
|
|
-
|
|
|
-function normalizeTransferOptions(options = {}) {
|
|
|
- const maxPacketLength = options.maxPacketLength === undefined ? options.maxFrameBytes : options.maxPacketLength
|
|
|
-
|
|
|
- return {
|
|
|
- expectedByteLength: options.expectedByteLength,
|
|
|
- maxFrameBytes: options.useDeviceCaps === false
|
|
|
- ? resolveConfiguredMaxPacketLength(maxPacketLength)
|
|
|
- : resolveMaxPacketLength(maxPacketLength),
|
|
|
- onChunk: options.onChunk,
|
|
|
- showModal: options.showModal !== false
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-function updateSyncedDeviceCaps(caps = {}) {
|
|
|
- const addressWidth = Number(caps.addressWidth)
|
|
|
- const maxPacketLength = Number(caps.maxPacketLength)
|
|
|
- const memoryEndian = String(caps.memoryEndian || '').trim().toLowerCase()
|
|
|
-
|
|
|
- syncedDeviceCaps = {
|
|
|
- addressWidth: addressWidth === 16 || addressWidth === 32 ? addressWidth : 0,
|
|
|
- maxPacketLength: Number.isFinite(maxPacketLength) && maxPacketLength > 0
|
|
|
- ? Math.round(maxPacketLength)
|
|
|
- : 0,
|
|
|
- memoryEndian: memoryEndian === 'little' ? 'little' : (memoryEndian === 'big' ? 'big' : '')
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-async function readMemory(area, startAddress, byteLength, label, kind, options = {}) {
|
|
|
- const transferOptions = normalizeTransferOptions(options)
|
|
|
- const normalizedArea = storageAccessProtocol.normalizeMemoryArea(area)
|
|
|
- const bytes = []
|
|
|
- const chunks = storageAccessProtocol.getReadChunks(startAddress, byteLength, {
|
|
|
- ...transferOptions,
|
|
|
- area: normalizedArea
|
|
|
- })
|
|
|
-
|
|
|
- for (const chunk of chunks) {
|
|
|
- const response = await transport.sendManagedFrame(
|
|
|
- storageAccessProtocol.buildReadFrame(normalizedArea, chunk.address, chunk.quantity, {
|
|
|
- maxFrameBytes: transferOptions.maxFrameBytes
|
|
|
- }),
|
|
|
- storageAccessProtocol.getChunkLabel(label, chunks, chunk),
|
|
|
- storageAccessProtocol.createExpected(normalizedArea, chunk.address, chunk.quantity, false, kind),
|
|
|
- {
|
|
|
- maxFrameBytes: transferOptions.maxFrameBytes,
|
|
|
- showModal: transferOptions.showModal
|
|
|
- }
|
|
|
- )
|
|
|
- if (!response) return null
|
|
|
-
|
|
|
- const dataBytes = Array.isArray(response.dataBytes) ? response.dataBytes : []
|
|
|
- dataBytes.forEach((byte, index) => {
|
|
|
- bytes[chunk.address - startAddress + index] = Number(byte) & 0xFF
|
|
|
- })
|
|
|
-
|
|
|
- if (typeof transferOptions.onChunk === 'function') {
|
|
|
- transferOptions.onChunk(response, chunk)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return bytes
|
|
|
-}
|
|
|
-
|
|
|
-async function writeMemory(area, startAddress, bytes, label, kind, options = {}) {
|
|
|
- const transferOptions = normalizeTransferOptions(options)
|
|
|
- const normalizedArea = storageAccessProtocol.normalizeMemoryArea(area)
|
|
|
- const chunks = storageAccessProtocol.getWriteChunks(startAddress, bytes, {
|
|
|
- ...transferOptions,
|
|
|
- area: normalizedArea
|
|
|
- })
|
|
|
-
|
|
|
- for (const chunk of chunks) {
|
|
|
- const response = await transport.sendManagedFrame(
|
|
|
- storageAccessProtocol.buildWriteFrame(normalizedArea, chunk.address, chunk.dataBytes, {
|
|
|
- maxFrameBytes: transferOptions.maxFrameBytes
|
|
|
- }),
|
|
|
- storageAccessProtocol.getChunkLabel(label, chunks, chunk),
|
|
|
- storageAccessProtocol.createExpected(normalizedArea, chunk.address, chunk.quantity, true, kind),
|
|
|
- {
|
|
|
- maxFrameBytes: transferOptions.maxFrameBytes,
|
|
|
- showModal: transferOptions.showModal
|
|
|
- }
|
|
|
- )
|
|
|
- if (!response) return false
|
|
|
-
|
|
|
- if (typeof transferOptions.onChunk === 'function') {
|
|
|
- transferOptions.onChunk(response, chunk)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return true
|
|
|
-}
|
|
|
-
|
|
|
-async function readCodeInfoBlock(label, kind, options = {}) {
|
|
|
- const transferOptions = normalizeTransferOptions({
|
|
|
- ...options,
|
|
|
- useDeviceCaps: false
|
|
|
- })
|
|
|
- const descriptorBytes = await readMemory(
|
|
|
- storageAccessProtocol.AREA.CODEINFO,
|
|
|
- storageAccessProtocol.CODE_INFO_DESCRIPTOR_ADDRESS,
|
|
|
- storageAccessProtocol.CODE_INFO_DESCRIPTOR_BYTE_LENGTH,
|
|
|
- label,
|
|
|
- `${kind}-descriptor`,
|
|
|
- {
|
|
|
- ...transferOptions,
|
|
|
- useDeviceCaps: false,
|
|
|
- showModal: false
|
|
|
- }
|
|
|
- )
|
|
|
- if (!descriptorBytes) {
|
|
|
- if (transferOptions.showModal !== false) {
|
|
|
- transport.showCommandAlert(label, 'CodeInfo 描述符读取失败或超时')
|
|
|
- }
|
|
|
- return null
|
|
|
- }
|
|
|
-
|
|
|
- let descriptorResponse
|
|
|
- try {
|
|
|
- descriptorResponse = storageAccessProtocol.parseCodeInfoDescriptorBytes(descriptorBytes)
|
|
|
- } catch (error) {
|
|
|
- if (transferOptions.showModal !== false) {
|
|
|
- transport.showCommandAlert(label, error.message || 'CodeInfo 描述符长度无效')
|
|
|
- }
|
|
|
- return null
|
|
|
- }
|
|
|
-
|
|
|
- const codeInfoAddress = Number(descriptorResponse.codeInfoAddress || 0)
|
|
|
- const codeInfoByteLength = Number(descriptorResponse.codeInfoByteLength || 0)
|
|
|
- let codeInfoAddressWidth
|
|
|
- try {
|
|
|
- codeInfoAddressWidth = storageAccessProtocol.normalizeDescriptorAddressWidth(
|
|
|
- descriptorResponse.codeInfoAddressWidth
|
|
|
- )
|
|
|
- } catch (error) {
|
|
|
- if (transferOptions.showModal !== false) {
|
|
|
- transport.showCommandAlert(label, error.message || 'CodeInfo 描述符地址长度无效')
|
|
|
- }
|
|
|
- return null
|
|
|
- }
|
|
|
- const codeInfoMemoryEndian = String(descriptorResponse.codeInfoMemoryEndian || 'big').trim()
|
|
|
- const codeInfoMaxPacketLength = Number(descriptorResponse.codeInfoMaxPacketLength || 0) & 0xFFFF
|
|
|
- const codeInfoArea = codeInfoAddressWidth === 32 ? storageAccessProtocol.AREA.ADDR32 : storageAccessProtocol.AREA.CODE
|
|
|
- const codeInfoReadAddress = codeInfoAddressWidth === 32 ? codeInfoAddress : (codeInfoAddress & 0xFFFF)
|
|
|
- const codeInfoMaxFrameBytes = storageAccessProtocol.resolveDescriptorMaxFrameBytes(
|
|
|
- transferOptions.maxFrameBytes,
|
|
|
- codeInfoMaxPacketLength
|
|
|
- )
|
|
|
- if (!codeInfoByteLength || codeInfoByteLength > 0xFFFF) {
|
|
|
- if (transferOptions.showModal !== false) {
|
|
|
- transport.showCommandAlert(label, 'CodeInfo 信息块长度无效')
|
|
|
- }
|
|
|
- return null
|
|
|
- }
|
|
|
-
|
|
|
- const codeInfoBytes = await readMemory(
|
|
|
- codeInfoArea,
|
|
|
- codeInfoReadAddress,
|
|
|
- codeInfoByteLength,
|
|
|
- label,
|
|
|
- kind,
|
|
|
- {
|
|
|
- ...transferOptions,
|
|
|
- maxFrameBytes: codeInfoMaxFrameBytes,
|
|
|
- useDeviceCaps: false
|
|
|
- }
|
|
|
- )
|
|
|
- if (!codeInfoBytes) return null
|
|
|
-
|
|
|
- return {
|
|
|
- codeInfoAddress,
|
|
|
- codeInfoAddressWidth,
|
|
|
- codeInfoByteLength,
|
|
|
- codeInfoBytes,
|
|
|
- codeInfoDescriptorBytes: descriptorBytes,
|
|
|
- codeInfoMaxPacketLength,
|
|
|
- codeInfoMemoryEndian,
|
|
|
- codeInfoMemoryEndianMark: Number(descriptorResponse.codeInfoMemoryEndianMark || 0) & 0xFFFF,
|
|
|
- codeInfoMemoryType: codeInfoArea
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-async function executeControl(operation, dataBytes, label, kind, options = {}) {
|
|
|
- const transferOptions = normalizeTransferOptions(options)
|
|
|
- const response = await transport.sendManagedFrame(
|
|
|
- storageAccessProtocol.buildControlFrame(operation, dataBytes),
|
|
|
- label,
|
|
|
- storageAccessProtocol.createControlExpected(operation, kind, {
|
|
|
- expectedByteLength: transferOptions.expectedByteLength
|
|
|
- }),
|
|
|
- {
|
|
|
- maxFrameBytes: transferOptions.maxFrameBytes,
|
|
|
- showModal: transferOptions.showModal
|
|
|
- }
|
|
|
- )
|
|
|
-
|
|
|
- return response || null
|
|
|
-}
|
|
|
-
|
|
|
-function formatDwordHex(value) {
|
|
|
- const numberValue = Math.max(0, Math.floor(Number(value) || 0))
|
|
|
-
|
|
|
- return numberValue.toString(16).toUpperCase().padStart(8, '0')
|
|
|
-}
|
|
|
-
|
|
|
-function getAreaAddressWidth(area) {
|
|
|
- return Number(area && area.addressWidth) === 32 || Number(area && area.key) === storageAccessProtocol.AREA.ADDR32
|
|
|
- ? 32
|
|
|
- : 16
|
|
|
-}
|
|
|
-
|
|
|
-function getMemoryAreaOptions() {
|
|
|
- return MEMORY_AREAS.map((area) => ({ ...area }))
|
|
|
-}
|
|
|
-
|
|
|
-function resolveMemoryCommand(index) {
|
|
|
- const commandIndex = Number(index) === 2 ? MEMORY_WRITE_INDEX : Number(index)
|
|
|
-
|
|
|
- return {
|
|
|
- command: MEMORY_COMMANDS[commandIndex] || MEMORY_COMMANDS[MEMORY_READ_INDEX],
|
|
|
- index: commandIndex === MEMORY_WRITE_INDEX ? MEMORY_WRITE_INDEX : MEMORY_READ_INDEX
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-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 = {
|
|
|
- maxFrameBytes: 0
|
|
|
- }
|
|
|
-
|
|
|
- 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 = {
|
|
|
- storageAccessAreaIndex: current.storageAccessAreaIndex || 0,
|
|
|
- storageAccessAddress: current.storageAccessAddress || '',
|
|
|
- storageAccessCommandIndex: current.storageAccessCommandIndex || 0,
|
|
|
- storageAccessDataText: current.storageAccessDataText || '',
|
|
|
- storageAccessLength: current.storageAccessLength || '',
|
|
|
- ...changed
|
|
|
- }
|
|
|
-
|
|
|
- const resolvedCommand = resolveMemoryCommand(next.storageAccessCommandIndex)
|
|
|
- const commandIndex = resolvedCommand.index
|
|
|
- const command = resolvedCommand.command
|
|
|
- const resolvedArea = resolveMemoryArea(next.storageAccessAreaIndex, command.key)
|
|
|
- const areaIndex = resolvedArea.index
|
|
|
- const area = resolvedArea.area
|
|
|
- const isCodeInfoDescriptor = command.key === 'read' && area.key === storageAccessProtocol.AREA.CODEINFO
|
|
|
- const addressWidth = getAreaAddressWidth(area)
|
|
|
- const addressText = isCodeInfoDescriptor
|
|
|
- ? '0000'
|
|
|
- : normalizeDisplayHexText(next.storageAccessAddress, addressWidth)
|
|
|
- const lengthText = isCodeInfoDescriptor
|
|
|
- ? storageAccessProtocol.CODE_INFO_DESCRIPTOR_BYTE_LENGTH.toString(16).toUpperCase().padStart(4, '0')
|
|
|
- : 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()
|
|
|
-
|
|
|
- let errorText = ''
|
|
|
- const addressError = hasAddressText
|
|
|
- ? (addressWidth === 32
|
|
|
- ? validateHexDwordText(addressText, '地址')
|
|
|
- : validateHexWordText(addressText, '地址'))
|
|
|
- : ''
|
|
|
- const lengthError = hasLengthText ? validateHexWordText(lengthText, '长度') : ''
|
|
|
- if (addressError) {
|
|
|
- errorText = addressError
|
|
|
- } else if (lengthError) {
|
|
|
- errorText = lengthError
|
|
|
- } else if (hasLengthText && byteLength <= 0) {
|
|
|
- errorText = '长度必须大于 0'
|
|
|
- } else if (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
|
|
|
- const previewHex = canPreview
|
|
|
- ? buildMemoryPreview(command.key, area.key, address, byteLength, dataBytes)
|
|
|
- : ''
|
|
|
-
|
|
|
- return {
|
|
|
- storageAccessAddress: addressText,
|
|
|
- storageAccessAddressMaxLength: addressWidth === 32 ? 8 : 4,
|
|
|
- storageAccessAddressWidthText: `${addressWidth}bit`,
|
|
|
- storageAccessAreaLocked: false,
|
|
|
- storageAccessAreaOptions: resolvedArea.options,
|
|
|
- storageAccessAreaIndex: areaIndex,
|
|
|
- storageAccessAreaLabel: area.label,
|
|
|
- storageAccessCommandIndex: commandIndex,
|
|
|
- storageAccessCommandLabel: command.label,
|
|
|
- storageAccessDataText: dataText,
|
|
|
- storageAccessErrorText: errorText,
|
|
|
- storageAccessLength: lengthText,
|
|
|
- storageAccessPreviewHexText: previewHex,
|
|
|
- storageAccessPreviewText: canPreview
|
|
|
- ? (isCodeInfoDescriptor
|
|
|
- ? 'codeinfo descriptor'
|
|
|
- : `${area.label} 0x${addressWidth === 32 ? formatDwordHex(address) : address.toString(16).toUpperCase().padStart(4, '0')} / ${byteLength} bytes`)
|
|
|
- : '',
|
|
|
- storageAccessShowWriteData: command.key === 'write',
|
|
|
- storageAccessTitleText: `${command.label}命令`
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-function getSyncedDeviceCaps() {
|
|
|
- return {
|
|
|
- ...syncedDeviceCaps
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-function parseMemoryCommandInput(data = {}) {
|
|
|
- const state = normalizeMemoryCommandState(data)
|
|
|
- const command = resolveMemoryCommand(state.storageAccessCommandIndex).command
|
|
|
- const area = resolveMemoryArea(state.storageAccessAreaIndex, command.key).area
|
|
|
- const addressWidth = getAreaAddressWidth(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: 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 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 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
|
|
|
-}
|
|
|
-
|
|
|
-function normalizeControlState() {
|
|
|
- return {}
|
|
|
-}
|
|
|
-
|
|
|
-function getControlCommands() {
|
|
|
- return CONTROL_COMMANDS.map((command) => ({
|
|
|
- ...command,
|
|
|
- previewHexText: bytesToHex(storageAccessProtocol.buildControlFrame(command.op), ' ')
|
|
|
- }))
|
|
|
-}
|
|
|
-
|
|
|
-async function syncCodeInfo(options = {}) {
|
|
|
- const result = await readCodeInfoBlock(
|
|
|
- options.label || '同步CodeInfo',
|
|
|
- options.kind || 'storage-code-info-read',
|
|
|
- {
|
|
|
- maxPacketLength: options.maxPacketLength,
|
|
|
- showModal: options.showModal !== false
|
|
|
- }
|
|
|
- )
|
|
|
- if (!result) {
|
|
|
- return {
|
|
|
- ok: false
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- const descriptorCaps = {
|
|
|
- addressWidth: result.codeInfoAddressWidth,
|
|
|
- codeInfoByteLength: result.codeInfoByteLength,
|
|
|
- maxPacketLength: result.codeInfoMaxPacketLength,
|
|
|
- memoryEndian: result.codeInfoMemoryEndian,
|
|
|
- memoryEndianMark: result.codeInfoMemoryEndianMark
|
|
|
- }
|
|
|
- const codeInfo = parseCodeInfo(result.codeInfoBytes, descriptorCaps)
|
|
|
- const importedGroups = createGroupsFromCodeInfo(codeInfo, options)
|
|
|
- .map(cloneImportedGroup)
|
|
|
- .map(normalizeGroup)
|
|
|
- updateSyncedDeviceCaps(descriptorCaps)
|
|
|
-
|
|
|
- return {
|
|
|
- codeInfoAddress: result.codeInfoAddress,
|
|
|
- codeInfoAddressWidth: result.codeInfoAddressWidth,
|
|
|
- codeInfoAddressText: formatDwordHex(result.codeInfoAddress),
|
|
|
- codeInfoByteLength: result.codeInfoByteLength,
|
|
|
- codeInfoByteLengthText: formatDwordHex(result.codeInfoByteLength),
|
|
|
- codeInfoBytes: result.codeInfoBytes,
|
|
|
- codeInfo,
|
|
|
- codeInfoDescriptorBytes: result.codeInfoDescriptorBytes,
|
|
|
- codeInfoMaxPacketLength: result.codeInfoMaxPacketLength,
|
|
|
- codeInfoMemoryEndian: result.codeInfoMemoryEndian,
|
|
|
- codeInfoMemoryEndianMark: result.codeInfoMemoryEndianMark,
|
|
|
- groupCount: importedGroups.length,
|
|
|
- importedGroups,
|
|
|
- codeInfoMemoryType: result.codeInfoMemoryType,
|
|
|
- ok: true,
|
|
|
- structCount: codeInfo.structCount
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-async function executeControlCommand(commandKey, data = {}, options = {}) {
|
|
|
- const command = getControlCommand(commandKey)
|
|
|
- if (!command) {
|
|
|
- return {
|
|
|
- errorText: '特殊指令无效',
|
|
|
- ok: false
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- const response = await 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 = {
|
|
|
- AREA: storageAccessProtocol.AREA,
|
|
|
- CONTROL_OP: storageAccessProtocol.CONTROL_OP,
|
|
|
- executeMemoryCommand,
|
|
|
- executeControl,
|
|
|
- executeControlCommand,
|
|
|
- formatDwordHex,
|
|
|
- getControlCommand,
|
|
|
- getControlCommands,
|
|
|
- getMemoryAreaOptions,
|
|
|
- getSyncedDeviceCaps,
|
|
|
- normalizeMemoryCommandState,
|
|
|
- normalizeControlState,
|
|
|
- readCodeInfoBlock,
|
|
|
- readMemory,
|
|
|
- resolveMaxPacketLength,
|
|
|
- syncCodeInfo,
|
|
|
- updateSyncedDeviceCaps,
|
|
|
- writeMemory
|
|
|
-}
|