|
@@ -3,12 +3,17 @@ const {
|
|
|
} = require('../../utils/base-utils.js')
|
|
} = require('../../utils/base-utils.js')
|
|
|
const {
|
|
const {
|
|
|
bytesToHex,
|
|
bytesToHex,
|
|
|
|
|
+ bytesToWords,
|
|
|
bytesToWordsLE,
|
|
bytesToWordsLE,
|
|
|
formatHexNumber,
|
|
formatHexNumber,
|
|
|
readByte,
|
|
readByte,
|
|
|
|
|
+ readUint16BE,
|
|
|
readUint16LE,
|
|
readUint16LE,
|
|
|
|
|
+ readUint32BE,
|
|
|
readUint32LE,
|
|
readUint32LE,
|
|
|
|
|
+ splitUint16BE,
|
|
|
splitUint16LE,
|
|
splitUint16LE,
|
|
|
|
|
+ splitUint32BE,
|
|
|
splitUint32LE,
|
|
splitUint32LE,
|
|
|
toByteArray
|
|
toByteArray
|
|
|
} = require('../../utils/binary-utils.js')
|
|
} = require('../../utils/binary-utils.js')
|
|
@@ -112,10 +117,47 @@ const VALID_AREAS = [AREA.CODEINFO, AREA.ADDR32, AREA.DATA, AREA.IDATA, AREA.XDA
|
|
|
const MEMORY_AREAS = [AREA.CODEINFO, AREA.ADDR32, AREA.DATA, AREA.IDATA, AREA.XDATA, AREA.CODE]
|
|
const MEMORY_AREAS = [AREA.CODEINFO, AREA.ADDR32, AREA.DATA, AREA.IDATA, AREA.XDATA, AREA.CODE]
|
|
|
const WRITABLE_AREAS = [AREA.ADDR32, AREA.DATA, AREA.IDATA, AREA.XDATA]
|
|
const WRITABLE_AREAS = [AREA.ADDR32, AREA.DATA, AREA.IDATA, AREA.XDATA]
|
|
|
const RESERVED_AREAS = [0x05, 0x06]
|
|
const RESERVED_AREAS = [0x05, 0x06]
|
|
|
-const readWord = readUint16LE
|
|
|
|
|
-const readDword = readUint32LE
|
|
|
|
|
-const splitWord = splitUint16LE
|
|
|
|
|
-const splitDword = splitUint32LE
|
|
|
|
|
|
|
+const readCrcWord = readUint16LE
|
|
|
|
|
+
|
|
|
|
|
+function normalizeMemoryEndian(value, fallback = MEMORY_ENDIAN.LITTLE) {
|
|
|
|
|
+ const text = String(value || '').trim().toLowerCase()
|
|
|
|
|
+ if (text === MEMORY_ENDIAN.LITTLE || text === 'le' || text === '1') return MEMORY_ENDIAN.LITTLE
|
|
|
|
|
+ if (text === MEMORY_ENDIAN.BIG || text === 'be' || text === '0') return MEMORY_ENDIAN.BIG
|
|
|
|
|
+
|
|
|
|
|
+ return fallback
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function isLittleMemoryEndian(memoryEndian) {
|
|
|
|
|
+ return normalizeMemoryEndian(memoryEndian) === MEMORY_ENDIAN.LITTLE
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function readWord(bytes, offset, memoryEndian = MEMORY_ENDIAN.LITTLE) {
|
|
|
|
|
+ return isLittleMemoryEndian(memoryEndian)
|
|
|
|
|
+ ? readUint16LE(bytes, offset)
|
|
|
|
|
+ : readUint16BE(bytes, offset)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function readDword(bytes, offset, memoryEndian = MEMORY_ENDIAN.LITTLE) {
|
|
|
|
|
+ return isLittleMemoryEndian(memoryEndian)
|
|
|
|
|
+ ? readUint32LE(bytes, offset)
|
|
|
|
|
+ : readUint32BE(bytes, offset)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function splitWord(value, memoryEndian = MEMORY_ENDIAN.LITTLE) {
|
|
|
|
|
+ return isLittleMemoryEndian(memoryEndian)
|
|
|
|
|
+ ? splitUint16LE(value)
|
|
|
|
|
+ : splitUint16BE(value)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function splitDword(value, memoryEndian = MEMORY_ENDIAN.LITTLE) {
|
|
|
|
|
+ return isLittleMemoryEndian(memoryEndian)
|
|
|
|
|
+ ? splitUint32LE(value)
|
|
|
|
|
+ : splitUint32BE(value)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function bytesToProtocolWords(bytes, memoryEndian = MEMORY_ENDIAN.LITTLE) {
|
|
|
|
|
+ return isLittleMemoryEndian(memoryEndian) ? bytesToWordsLE(bytes) : bytesToWords(bytes)
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
function toByte(value, label) {
|
|
function toByte(value, label) {
|
|
|
if (!Number.isInteger(value) || value < 0 || value > 0xFF) {
|
|
if (!Number.isInteger(value) || value < 0 || value > 0xFF) {
|
|
@@ -266,6 +308,16 @@ function getPayloadLimitFromFrame(maxFrameBytes, overhead) {
|
|
|
return Math.max(0, Math.min(MAX_PAYLOAD_BYTES, frameBytes - overhead))
|
|
return Math.max(0, Math.min(MAX_PAYLOAD_BYTES, frameBytes - overhead))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function getRequiredPayloadLimit(maxFrameBytes, overhead, actionText) {
|
|
|
|
|
+ const frameBytes = normalizeMaxFrameBytes(maxFrameBytes)
|
|
|
|
|
+ const payloadLimit = getPayloadLimitFromFrame(frameBytes, overhead)
|
|
|
|
|
+ if (frameBytes !== UNLIMITED_FRAME_BYTES && payloadLimit <= 0) {
|
|
|
|
|
+ throw new Error(`最大包长至少需要 ${overhead + 1} 字节才能${actionText}`)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return payloadLimit
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function getMaxReadByteLength(maxFrameBytes = DEFAULT_MAX_FRAME_BYTES, area = AREA.ADDR32) {
|
|
function getMaxReadByteLength(maxFrameBytes = DEFAULT_MAX_FRAME_BYTES, area = AREA.ADDR32) {
|
|
|
return getPayloadLimitFromFrame(maxFrameBytes, getReadResponseOverhead(area))
|
|
return getPayloadLimitFromFrame(maxFrameBytes, getReadResponseOverhead(area))
|
|
|
}
|
|
}
|
|
@@ -325,7 +377,7 @@ function hasValidStorageCrc(bytes) {
|
|
|
if (frame.length >= 4) return hasValidCrc16Ccitt(frame, STORAGE_CRC_OPTIONS)
|
|
if (frame.length >= 4) return hasValidCrc16Ccitt(frame, STORAGE_CRC_OPTIONS)
|
|
|
|
|
|
|
|
const expected = crc16Ccitt(frame.slice(0, -2), STORAGE_CRC_OPTIONS)
|
|
const expected = crc16Ccitt(frame.slice(0, -2), STORAGE_CRC_OPTIONS)
|
|
|
- const received = readWord(frame, frame.length - 2)
|
|
|
|
|
|
|
+ const received = readCrcWord(frame, frame.length - 2)
|
|
|
return expected === received
|
|
return expected === received
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -339,15 +391,22 @@ function buildReadFrame(area, address, byteLength, options = {}) {
|
|
|
return buildCodeInfoDescriptorFrame()
|
|
return buildCodeInfoDescriptorFrame()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ const memoryEndian = normalizeMemoryEndian(options.memoryEndian)
|
|
|
const addressBytes = getAddressFieldByteLength(normalizedArea)
|
|
const addressBytes = getAddressFieldByteLength(normalizedArea)
|
|
|
const startAddress = addressBytes === ADDRESS32_BYTE_LENGTH
|
|
const startAddress = addressBytes === ADDRESS32_BYTE_LENGTH
|
|
|
? toUint32(Number(address), '内存地址')
|
|
? toUint32(Number(address), '内存地址')
|
|
|
: toWord(Number(address), '内存地址')
|
|
: toWord(Number(address), '内存地址')
|
|
|
- const maxByteLength = getMaxReadByteLength(options.maxFrameBytes, normalizedArea)
|
|
|
|
|
|
|
+ const maxByteLength = getRequiredPayloadLimit(
|
|
|
|
|
+ options.maxFrameBytes,
|
|
|
|
|
+ getReadResponseOverhead(normalizedArea),
|
|
|
|
|
+ '读取 1 字节'
|
|
|
|
|
+ )
|
|
|
const length = toByteLength(Number(byteLength), '读取字节长度', maxByteLength || MAX_PAYLOAD_BYTES)
|
|
const length = toByteLength(Number(byteLength), '读取字节长度', maxByteLength || MAX_PAYLOAD_BYTES)
|
|
|
const command = buildCommand(normalizedArea, false)
|
|
const command = buildCommand(normalizedArea, false)
|
|
|
- const addressParts = addressBytes === ADDRESS32_BYTE_LENGTH ? splitDword(startAddress) : splitWord(startAddress)
|
|
|
|
|
- const lengthParts = splitWord(toWord(length, '读取字节长度'))
|
|
|
|
|
|
|
+ const addressParts = addressBytes === ADDRESS32_BYTE_LENGTH
|
|
|
|
|
+ ? splitDword(startAddress, memoryEndian)
|
|
|
|
|
+ : splitWord(startAddress, memoryEndian)
|
|
|
|
|
+ const lengthParts = splitWord(toWord(length, '读取字节长度'), memoryEndian)
|
|
|
|
|
|
|
|
return appendStorageCrc([command].concat(addressParts, lengthParts))
|
|
return appendStorageCrc([command].concat(addressParts, lengthParts))
|
|
|
}
|
|
}
|
|
@@ -358,16 +417,23 @@ function buildWriteFrame(area, address, bytes, options = {}) {
|
|
|
throw new Error(`${AREA_NAMES[normalizedArea] || '该'} 区不可写`)
|
|
throw new Error(`${AREA_NAMES[normalizedArea] || '该'} 区不可写`)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ const memoryEndian = normalizeMemoryEndian(options.memoryEndian)
|
|
|
const addressBytes = getAddressFieldByteLength(normalizedArea)
|
|
const addressBytes = getAddressFieldByteLength(normalizedArea)
|
|
|
const startAddress = addressBytes === ADDRESS32_BYTE_LENGTH
|
|
const startAddress = addressBytes === ADDRESS32_BYTE_LENGTH
|
|
|
? toUint32(Number(address), '内存地址')
|
|
? toUint32(Number(address), '内存地址')
|
|
|
: toWord(Number(address), '内存地址')
|
|
: toWord(Number(address), '内存地址')
|
|
|
const dataBytes = toByteArray(bytes).map((byte) => toByte(Number(byte), '写入字节'))
|
|
const dataBytes = toByteArray(bytes).map((byte) => toByte(Number(byte), '写入字节'))
|
|
|
- const maxByteLength = getMaxWriteByteLength(options.maxFrameBytes, normalizedArea)
|
|
|
|
|
|
|
+ const maxByteLength = getRequiredPayloadLimit(
|
|
|
|
|
+ options.maxFrameBytes,
|
|
|
|
|
+ getWriteRequestOverhead(normalizedArea),
|
|
|
|
|
+ '写入 1 字节'
|
|
|
|
|
+ )
|
|
|
const length = toByteLength(dataBytes.length, '写入字节长度', maxByteLength || MAX_PAYLOAD_BYTES)
|
|
const length = toByteLength(dataBytes.length, '写入字节长度', maxByteLength || MAX_PAYLOAD_BYTES)
|
|
|
const command = buildCommand(normalizedArea, true)
|
|
const command = buildCommand(normalizedArea, true)
|
|
|
- const addressParts = addressBytes === ADDRESS32_BYTE_LENGTH ? splitDword(startAddress) : splitWord(startAddress)
|
|
|
|
|
- const lengthParts = splitWord(toWord(length, '写入字节长度'))
|
|
|
|
|
|
|
+ const addressParts = addressBytes === ADDRESS32_BYTE_LENGTH
|
|
|
|
|
+ ? splitDword(startAddress, memoryEndian)
|
|
|
|
|
+ : splitWord(startAddress, memoryEndian)
|
|
|
|
|
+ const lengthParts = splitWord(toWord(length, '写入字节长度'), memoryEndian)
|
|
|
|
|
|
|
|
return appendStorageCrc([command].concat(addressParts, lengthParts, dataBytes))
|
|
return appendStorageCrc([command].concat(addressParts, lengthParts, dataBytes))
|
|
|
}
|
|
}
|
|
@@ -392,13 +458,14 @@ function parseCodeInfoDescriptorBytes(bytes) {
|
|
|
if (!endianMark.memoryEndian) {
|
|
if (!endianMark.memoryEndian) {
|
|
|
throw new Error('CodeInfo 描述符字节序标记无效')
|
|
throw new Error('CodeInfo 描述符字节序标记无效')
|
|
|
}
|
|
}
|
|
|
|
|
+ const memoryEndian = endianMark.memoryEndian
|
|
|
|
|
|
|
|
return {
|
|
return {
|
|
|
- codeInfoAddress: readDword(dataBytes, 0),
|
|
|
|
|
|
|
+ codeInfoAddress: readDword(dataBytes, 0, memoryEndian),
|
|
|
codeInfoAddressWidth: dataBytes[6] & 0xFF,
|
|
codeInfoAddressWidth: dataBytes[6] & 0xFF,
|
|
|
- codeInfoByteLength: readWord(dataBytes, 4),
|
|
|
|
|
|
|
+ codeInfoByteLength: readWord(dataBytes, 4, memoryEndian),
|
|
|
codeInfoDescriptorBytes: dataBytes.slice(0, CODE_INFO_DESCRIPTOR_BYTE_LENGTH),
|
|
codeInfoDescriptorBytes: dataBytes.slice(0, CODE_INFO_DESCRIPTOR_BYTE_LENGTH),
|
|
|
- codeInfoMaxPacketLength: readWord(dataBytes, 7),
|
|
|
|
|
|
|
+ codeInfoMaxPacketLength: readWord(dataBytes, 7, memoryEndian),
|
|
|
codeInfoMemoryEndian: endianMark.memoryEndian,
|
|
codeInfoMemoryEndian: endianMark.memoryEndian,
|
|
|
codeInfoMemoryEndianMark: endianMark.marker
|
|
codeInfoMemoryEndianMark: endianMark.marker
|
|
|
}
|
|
}
|
|
@@ -408,13 +475,14 @@ function formatHex(bytes) {
|
|
|
return bytesToHex(bytes, ' ')
|
|
return bytesToHex(bytes, ' ')
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function parseStorageAccessResponse(bytes) {
|
|
|
|
|
|
|
+function parseStorageAccessResponse(bytes, options = {}) {
|
|
|
const frame = toByteArray(bytes)
|
|
const frame = toByteArray(bytes)
|
|
|
if (frame.length < 3 || !hasValidStorageCrc(frame)) return null
|
|
if (frame.length < 3 || !hasValidStorageCrc(frame)) return null
|
|
|
|
|
|
|
|
const command = frame[0] & 0xFF
|
|
const command = frame[0] & 0xFF
|
|
|
const decoded = decodeCommand(command)
|
|
const decoded = decodeCommand(command)
|
|
|
if (decoded.isControl) return parseStorageControlResponse(frame, decoded)
|
|
if (decoded.isControl) return parseStorageControlResponse(frame, decoded)
|
|
|
|
|
+ const memoryEndian = normalizeMemoryEndian(options.memoryEndian)
|
|
|
|
|
|
|
|
if (decoded.hasError) {
|
|
if (decoded.hasError) {
|
|
|
if (frame.length !== EXCEPTION_RESPONSE_LENGTH) return null
|
|
if (frame.length !== EXCEPTION_RESPONSE_LENGTH) return null
|
|
@@ -438,6 +506,7 @@ function parseStorageAccessResponse(bytes) {
|
|
|
if (decoded.isWrite || frame.length !== CODE_INFO_DESCRIPTOR_RESPONSE_LENGTH) return null
|
|
if (decoded.isWrite || frame.length !== CODE_INFO_DESCRIPTOR_RESPONSE_LENGTH) return null
|
|
|
|
|
|
|
|
const dataBytes = frame.slice(1, 1 + CODE_INFO_DESCRIPTOR_BYTE_LENGTH)
|
|
const dataBytes = frame.slice(1, 1 + CODE_INFO_DESCRIPTOR_BYTE_LENGTH)
|
|
|
|
|
+ const descriptor = parseCodeInfoDescriptorBytes(dataBytes)
|
|
|
const response = {
|
|
const response = {
|
|
|
address: CODE_INFO_DESCRIPTOR_ADDRESS,
|
|
address: CODE_INFO_DESCRIPTOR_ADDRESS,
|
|
|
addressWidth: 0,
|
|
addressWidth: 0,
|
|
@@ -450,12 +519,15 @@ function parseStorageAccessResponse(bytes) {
|
|
|
isAddress32: false,
|
|
isAddress32: false,
|
|
|
isWrite: false,
|
|
isWrite: false,
|
|
|
protocol: PROTOCOL_NAME,
|
|
protocol: PROTOCOL_NAME,
|
|
|
- words: bytesToWordsLE(dataBytes.length % 2 === 0 ? dataBytes : dataBytes.concat(0))
|
|
|
|
|
|
|
+ words: bytesToProtocolWords(
|
|
|
|
|
+ dataBytes.length % 2 === 0 ? dataBytes : dataBytes.concat(0),
|
|
|
|
|
+ descriptor.codeInfoMemoryEndian
|
|
|
|
|
+ )
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
return {
|
|
|
...response,
|
|
...response,
|
|
|
- ...parseCodeInfoDescriptorBytes(dataBytes)
|
|
|
|
|
|
|
+ ...descriptor
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -466,11 +538,11 @@ function parseStorageAccessResponse(bytes) {
|
|
|
if (frame.length !== getWriteResponseLength(decoded.area)) return null
|
|
if (frame.length !== getWriteResponseLength(decoded.area)) return null
|
|
|
|
|
|
|
|
return {
|
|
return {
|
|
|
- address: decoded.isAddress32 ? readDword(frame, 1) : readWord(frame, 1),
|
|
|
|
|
|
|
+ address: decoded.isAddress32 ? readDword(frame, 1, memoryEndian) : readWord(frame, 1, memoryEndian),
|
|
|
addressWidth: decoded.isAddress32 ? 32 : 16,
|
|
addressWidth: decoded.isAddress32 ? 32 : 16,
|
|
|
area: decoded.area,
|
|
area: decoded.area,
|
|
|
areaName: AREA_NAMES[decoded.area] || 'UNKNOWN',
|
|
areaName: AREA_NAMES[decoded.area] || 'UNKNOWN',
|
|
|
- byteLength: readWord(frame, 1 + addressBytes),
|
|
|
|
|
|
|
+ byteLength: readWord(frame, 1 + addressBytes, memoryEndian),
|
|
|
command,
|
|
command,
|
|
|
dataBytes: [],
|
|
dataBytes: [],
|
|
|
isException: false,
|
|
isException: false,
|
|
@@ -482,8 +554,8 @@ function parseStorageAccessResponse(bytes) {
|
|
|
|
|
|
|
|
if (frame.length < getReadResponseOverhead(decoded.area)) return null
|
|
if (frame.length < getReadResponseOverhead(decoded.area)) return null
|
|
|
|
|
|
|
|
- const address = decoded.isAddress32 ? readDword(frame, 1) : readWord(frame, 1)
|
|
|
|
|
- const byteLength = readWord(frame, 1 + addressBytes)
|
|
|
|
|
|
|
+ const address = decoded.isAddress32 ? readDword(frame, 1, memoryEndian) : readWord(frame, 1, memoryEndian)
|
|
|
|
|
+ const byteLength = readWord(frame, 1 + addressBytes, memoryEndian)
|
|
|
const dataStart = headerLength
|
|
const dataStart = headerLength
|
|
|
const dataEnd = dataStart + byteLength
|
|
const dataEnd = dataStart + byteLength
|
|
|
if (frame.length !== dataEnd + 2) return null
|
|
if (frame.length !== dataEnd + 2) return null
|
|
@@ -502,7 +574,7 @@ function parseStorageAccessResponse(bytes) {
|
|
|
isAddress32: decoded.isAddress32,
|
|
isAddress32: decoded.isAddress32,
|
|
|
isWrite: false,
|
|
isWrite: false,
|
|
|
protocol: PROTOCOL_NAME,
|
|
protocol: PROTOCOL_NAME,
|
|
|
- words: bytesToWordsLE(dataBytes.length % 2 === 0 ? dataBytes : dataBytes.concat(0))
|
|
|
|
|
|
|
+ words: bytesToProtocolWords(dataBytes.length % 2 === 0 ? dataBytes : dataBytes.concat(0), memoryEndian)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return response
|
|
return response
|
|
@@ -550,7 +622,7 @@ function parseStorageControlResponse(frame, decoded) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function parseStorageAccessRequest(bytes) {
|
|
|
|
|
|
|
+function parseStorageAccessRequest(bytes, options = {}) {
|
|
|
const frame = toByteArray(bytes)
|
|
const frame = toByteArray(bytes)
|
|
|
if (frame.length < 3 || !hasValidStorageCrc(frame)) return null
|
|
if (frame.length < 3 || !hasValidStorageCrc(frame)) return null
|
|
|
|
|
|
|
@@ -582,12 +654,13 @@ function parseStorageAccessRequest(bytes) {
|
|
|
}
|
|
}
|
|
|
if (frame.length < READ_REQUEST_LENGTH_16) return null
|
|
if (frame.length < READ_REQUEST_LENGTH_16) return null
|
|
|
|
|
|
|
|
|
|
+ const memoryEndian = normalizeMemoryEndian(options.memoryEndian)
|
|
|
const addressBytes = decoded.addressBytes
|
|
const addressBytes = decoded.addressBytes
|
|
|
const headerLength = getMemoryHeaderLength(decoded.area)
|
|
const headerLength = getMemoryHeaderLength(decoded.area)
|
|
|
if (frame.length < headerLength + 2) return null
|
|
if (frame.length < headerLength + 2) return null
|
|
|
|
|
|
|
|
- const address = decoded.isAddress32 ? readDword(frame, 1) : readWord(frame, 1)
|
|
|
|
|
- const byteLength = readWord(frame, 1 + addressBytes)
|
|
|
|
|
|
|
+ const address = decoded.isAddress32 ? readDword(frame, 1, memoryEndian) : readWord(frame, 1, memoryEndian)
|
|
|
|
|
+ const byteLength = readWord(frame, 1 + addressBytes, memoryEndian)
|
|
|
const expectedLength = decoded.isWrite
|
|
const expectedLength = decoded.isWrite
|
|
|
? headerLength + byteLength + 2
|
|
? headerLength + byteLength + 2
|
|
|
: headerLength + 2
|
|
: headerLength + 2
|
|
@@ -655,7 +728,11 @@ function getExpectedResponseLength(expected, responseCommand, responseBytes = []
|
|
|
const headerLength = getMemoryHeaderLength(expected.area)
|
|
const headerLength = getMemoryHeaderLength(expected.area)
|
|
|
if (responseBytes.length < headerLength) return 0
|
|
if (responseBytes.length < headerLength) return 0
|
|
|
|
|
|
|
|
- return headerLength + readWord(responseBytes, 1 + getAddressFieldByteLength(expected.area)) + 2
|
|
|
|
|
|
|
+ return headerLength + readWord(
|
|
|
|
|
+ responseBytes,
|
|
|
|
|
+ 1 + getAddressFieldByteLength(expected.area),
|
|
|
|
|
+ expected.memoryEndian
|
|
|
|
|
+ ) + 2
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function isExpectedResponse(response, expected) {
|
|
function isExpectedResponse(response, expected) {
|
|
@@ -760,7 +837,7 @@ function readResponseFromBuffer(buffer, expected, options = {}) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const frameBytes = buffer.slice(0, responseLength)
|
|
const frameBytes = buffer.slice(0, responseLength)
|
|
|
- const response = parseStorageAccessResponse(frameBytes)
|
|
|
|
|
|
|
+ const response = parseStorageAccessResponse(frameBytes, expected)
|
|
|
if (!response) {
|
|
if (!response) {
|
|
|
return {
|
|
return {
|
|
|
frameBytes,
|
|
frameBytes,
|
|
@@ -807,6 +884,7 @@ function createExpected(area, address, byteLength, isWrite, kind, options = {})
|
|
|
isAddress32: isAddress32Area(normalizedArea),
|
|
isAddress32: isAddress32Area(normalizedArea),
|
|
|
isWrite,
|
|
isWrite,
|
|
|
kind,
|
|
kind,
|
|
|
|
|
+ memoryEndian: normalizeMemoryEndian(options.memoryEndian),
|
|
|
operation: isWrite ? 'write' : 'read',
|
|
operation: isWrite ? 'write' : 'read',
|
|
|
protocol: PROTOCOL_NAME,
|
|
protocol: PROTOCOL_NAME,
|
|
|
quantity: byteLength
|
|
quantity: byteLength
|
|
@@ -861,15 +939,36 @@ function splitQuantity(startAddress, quantity, maxQuantity) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function getReadChunks(startAddress, byteLength, options = {}) {
|
|
function getReadChunks(startAddress, byteLength, options = {}) {
|
|
|
- const maxByteLength = getMaxReadByteLength(options.maxFrameBytes, options.area)
|
|
|
|
|
|
|
+ const normalizedArea = normalizeMemoryArea(options.area === undefined ? AREA.ADDR32 : options.area)
|
|
|
|
|
+ if (isCodeInfoDescriptorArea(normalizedArea)) {
|
|
|
|
|
+ const frameLimit = normalizeMaxFrameBytes(options.maxFrameBytes)
|
|
|
|
|
+ if (frameLimit !== UNLIMITED_FRAME_BYTES && frameLimit < CODE_INFO_DESCRIPTOR_RESPONSE_LENGTH) {
|
|
|
|
|
+ throw new Error(`最大包长至少需要 ${CODE_INFO_DESCRIPTOR_RESPONSE_LENGTH} 字节才能读取 CodeInfo 描述符`)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return [{
|
|
|
|
|
+ address: CODE_INFO_DESCRIPTOR_ADDRESS,
|
|
|
|
|
+ quantity: CODE_INFO_DESCRIPTOR_BYTE_LENGTH
|
|
|
|
|
+ }]
|
|
|
|
|
+ }
|
|
|
|
|
+ const maxByteLength = getRequiredPayloadLimit(
|
|
|
|
|
+ options.maxFrameBytes,
|
|
|
|
|
+ getReadResponseOverhead(normalizedArea),
|
|
|
|
|
+ '读取 1 字节'
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
- return splitQuantity(startAddress, byteLength, maxByteLength || byteLength)
|
|
|
|
|
|
|
+ return splitQuantity(startAddress, byteLength, maxByteLength)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function getWriteChunks(startAddress, bytes, options = {}) {
|
|
function getWriteChunks(startAddress, bytes, options = {}) {
|
|
|
const sourceBytes = Array.prototype.slice.call(bytes || []).map((byte) => Number(byte) & 0xFF)
|
|
const sourceBytes = Array.prototype.slice.call(bytes || []).map((byte) => Number(byte) & 0xFF)
|
|
|
- const maxByteLength = getMaxWriteByteLength(options.maxFrameBytes, options.area)
|
|
|
|
|
- const chunks = splitQuantity(startAddress, sourceBytes.length, maxByteLength || sourceBytes.length)
|
|
|
|
|
|
|
+ const normalizedArea = normalizeMemoryArea(options.area === undefined ? AREA.ADDR32 : options.area)
|
|
|
|
|
+ const maxByteLength = getRequiredPayloadLimit(
|
|
|
|
|
+ options.maxFrameBytes,
|
|
|
|
|
+ getWriteRequestOverhead(normalizedArea),
|
|
|
|
|
+ '写入 1 字节'
|
|
|
|
|
+ )
|
|
|
|
|
+ const chunks = splitQuantity(startAddress, sourceBytes.length, maxByteLength)
|
|
|
let offset = 0
|
|
let offset = 0
|
|
|
|
|
|
|
|
return chunks.map((chunk) => {
|
|
return chunks.map((chunk) => {
|
|
@@ -951,6 +1050,7 @@ module.exports = {
|
|
|
normalizeDescriptorAddressWidth,
|
|
normalizeDescriptorAddressWidth,
|
|
|
normalizeArea,
|
|
normalizeArea,
|
|
|
normalizeMaxFrameBytes,
|
|
normalizeMaxFrameBytes,
|
|
|
|
|
+ normalizeMemoryEndian,
|
|
|
normalizeMemoryArea,
|
|
normalizeMemoryArea,
|
|
|
parseCodeInfoDescriptorBytes,
|
|
parseCodeInfoDescriptorBytes,
|
|
|
resolveDescriptorMaxFrameBytes,
|
|
resolveDescriptorMaxFrameBytes,
|