|
|
@@ -183,6 +183,20 @@ function toUint32(value, label) {
|
|
|
return value
|
|
|
}
|
|
|
|
|
|
+function toAccessLength(value, label = '访问字节长度') {
|
|
|
+ const numberValue = Number(value)
|
|
|
+ if (!Number.isFinite(numberValue)) {
|
|
|
+ throw new Error(`${label}必须大于 0`)
|
|
|
+ }
|
|
|
+
|
|
|
+ const byteLength = Math.floor(numberValue)
|
|
|
+ if (byteLength <= 0) {
|
|
|
+ throw new Error(`${label}必须大于 0`)
|
|
|
+ }
|
|
|
+
|
|
|
+ return byteLength
|
|
|
+}
|
|
|
+
|
|
|
function normalizeArea(value) {
|
|
|
if (typeof value === 'string') {
|
|
|
const area = AREA_BY_NAME[value.trim().toUpperCase()]
|
|
|
@@ -205,6 +219,30 @@ function getAddressFieldByteLength(area) {
|
|
|
return isAddress32Area(area) ? ADDRESS32_BYTE_LENGTH : ADDRESS16_BYTE_LENGTH
|
|
|
}
|
|
|
|
|
|
+function getAddressMaxValue(area) {
|
|
|
+ return getAddressFieldByteLength(area) === ADDRESS32_BYTE_LENGTH ? MAX_UINT32 : MAX_UINT16
|
|
|
+}
|
|
|
+
|
|
|
+function validateMemoryAccessRange(area, address, byteLength, label = '访问') {
|
|
|
+ const normalizedArea = normalizeMemoryArea(area)
|
|
|
+ const addressBytes = getAddressFieldByteLength(normalizedArea)
|
|
|
+ const startAddress = addressBytes === ADDRESS32_BYTE_LENGTH
|
|
|
+ ? toUint32(Number(address), '内存地址')
|
|
|
+ : toWord(Number(address), '内存地址')
|
|
|
+ const length = toAccessLength(byteLength, `${label}字节长度`)
|
|
|
+ const maxAddress = getAddressMaxValue(normalizedArea)
|
|
|
+ const endAddress = startAddress + length - 1
|
|
|
+
|
|
|
+ if (endAddress > maxAddress) {
|
|
|
+ throw new Error(`${label}范围超过 0x${formatHexNumber(maxAddress, addressBytes * 2)} 地址上限`)
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ length,
|
|
|
+ startAddress
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
function getMemoryHeaderLength(area) {
|
|
|
return 1 + getAddressFieldByteLength(area) + 2
|
|
|
}
|
|
|
@@ -392,16 +430,15 @@ function buildReadFrame(area, address, byteLength, options = {}) {
|
|
|
}
|
|
|
|
|
|
const memoryEndian = normalizeMemoryEndian(options.memoryEndian)
|
|
|
- const addressBytes = getAddressFieldByteLength(normalizedArea)
|
|
|
- const startAddress = addressBytes === ADDRESS32_BYTE_LENGTH
|
|
|
- ? toUint32(Number(address), '内存地址')
|
|
|
- : toWord(Number(address), '内存地址')
|
|
|
const maxByteLength = getRequiredPayloadLimit(
|
|
|
options.maxFrameBytes,
|
|
|
getReadResponseOverhead(normalizedArea),
|
|
|
'读取 1 字节'
|
|
|
)
|
|
|
const length = toByteLength(Number(byteLength), '读取字节长度', maxByteLength || MAX_PAYLOAD_BYTES)
|
|
|
+ const addressBytes = getAddressFieldByteLength(normalizedArea)
|
|
|
+ const range = validateMemoryAccessRange(normalizedArea, address, length, '读取')
|
|
|
+ const startAddress = range.startAddress
|
|
|
const command = buildCommand(normalizedArea, false)
|
|
|
const addressParts = addressBytes === ADDRESS32_BYTE_LENGTH
|
|
|
? splitDword(startAddress, memoryEndian)
|
|
|
@@ -418,10 +455,6 @@ function buildWriteFrame(area, address, bytes, options = {}) {
|
|
|
}
|
|
|
|
|
|
const memoryEndian = normalizeMemoryEndian(options.memoryEndian)
|
|
|
- const addressBytes = getAddressFieldByteLength(normalizedArea)
|
|
|
- const startAddress = addressBytes === ADDRESS32_BYTE_LENGTH
|
|
|
- ? toUint32(Number(address), '内存地址')
|
|
|
- : toWord(Number(address), '内存地址')
|
|
|
const dataBytes = toByteArray(bytes).map((byte) => toByte(Number(byte), '写入字节'))
|
|
|
const maxByteLength = getRequiredPayloadLimit(
|
|
|
options.maxFrameBytes,
|
|
|
@@ -429,6 +462,9 @@ function buildWriteFrame(area, address, bytes, options = {}) {
|
|
|
'写入 1 字节'
|
|
|
)
|
|
|
const length = toByteLength(dataBytes.length, '写入字节长度', maxByteLength || MAX_PAYLOAD_BYTES)
|
|
|
+ const addressBytes = getAddressFieldByteLength(normalizedArea)
|
|
|
+ const range = validateMemoryAccessRange(normalizedArea, address, length, '写入')
|
|
|
+ const startAddress = range.startAddress
|
|
|
const command = buildCommand(normalizedArea, true)
|
|
|
const addressParts = addressBytes === ADDRESS32_BYTE_LENGTH
|
|
|
? splitDword(startAddress, memoryEndian)
|
|
|
@@ -837,7 +873,17 @@ function readResponseFromBuffer(buffer, expected, options = {}) {
|
|
|
}
|
|
|
|
|
|
const frameBytes = buffer.slice(0, responseLength)
|
|
|
- const response = parseStorageAccessResponse(frameBytes, expected)
|
|
|
+ let response
|
|
|
+ try {
|
|
|
+ response = parseStorageAccessResponse(frameBytes, expected)
|
|
|
+ } catch (error) {
|
|
|
+ return {
|
|
|
+ frameBytes,
|
|
|
+ message: error && error.message ? error.message : '存储访问响应帧解析失败',
|
|
|
+ status: 'invalid'
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (!response) {
|
|
|
return {
|
|
|
frameBytes,
|
|
|
@@ -951,24 +997,26 @@ function getReadChunks(startAddress, byteLength, options = {}) {
|
|
|
quantity: CODE_INFO_DESCRIPTOR_BYTE_LENGTH
|
|
|
}]
|
|
|
}
|
|
|
+ const range = validateMemoryAccessRange(normalizedArea, startAddress, byteLength, '读取')
|
|
|
const maxByteLength = getRequiredPayloadLimit(
|
|
|
options.maxFrameBytes,
|
|
|
getReadResponseOverhead(normalizedArea),
|
|
|
'读取 1 字节'
|
|
|
)
|
|
|
|
|
|
- return splitQuantity(startAddress, byteLength, maxByteLength)
|
|
|
+ return splitQuantity(range.startAddress, range.length, maxByteLength)
|
|
|
}
|
|
|
|
|
|
function getWriteChunks(startAddress, bytes, options = {}) {
|
|
|
const sourceBytes = Array.prototype.slice.call(bytes || []).map((byte) => Number(byte) & 0xFF)
|
|
|
const normalizedArea = normalizeMemoryArea(options.area === undefined ? AREA.ADDR32 : options.area)
|
|
|
+ const range = validateMemoryAccessRange(normalizedArea, startAddress, sourceBytes.length, '写入')
|
|
|
const maxByteLength = getRequiredPayloadLimit(
|
|
|
options.maxFrameBytes,
|
|
|
getWriteRequestOverhead(normalizedArea),
|
|
|
'写入 1 字节'
|
|
|
)
|
|
|
- const chunks = splitQuantity(startAddress, sourceBytes.length, maxByteLength)
|
|
|
+ const chunks = splitQuantity(range.startAddress, range.length, maxByteLength)
|
|
|
let offset = 0
|
|
|
|
|
|
return chunks.map((chunk) => {
|