1
0

index.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
  1. const {
  2. padHex
  3. } = require('../../utils/base-utils.js')
  4. const {
  5. bytesToWords,
  6. toByteArray
  7. } = require('../../utils/binary-utils.js')
  8. const {
  9. BYTE_ORDER_HIGH,
  10. appendCrc16Ccitt,
  11. crc16Ccitt,
  12. hasValidCrc16Ccitt
  13. } = require('../../utils/crc.js')
  14. const PROTOCOL_NAME = 'storage-access'
  15. const CMD_ERR_MASK = 0x80
  16. const CMD_CONTROL_FLAG = 0x40
  17. const CMD_WRITE_MASK = 0x08
  18. const CMD_ADDRESS_MODE_MASK = 0x07
  19. const CMD_RESERVED_MASK = 0x30
  20. const CMD_CONTROL = 0x4F
  21. const CONTROL_RESPONSE_HEADER_LENGTH = 3
  22. const ADDRESS16_BYTE_LENGTH = 2
  23. const ADDRESS32_BYTE_LENGTH = 4
  24. const AREA = {
  25. ADDR32: 0x07,
  26. DATA: 0x01,
  27. IDATA: 0x02,
  28. XDATA: 0x03,
  29. CODE: 0x04
  30. }
  31. const CONTROL_OP = {
  32. RESET: 0x01,
  33. START: 0x02,
  34. STOP: 0x03,
  35. SET_CONTROL_REF: 0x04,
  36. READ_CODE_INFO_DESCRIPTOR: 0x05
  37. }
  38. const CONTROL_STATUS_MESSAGES = {
  39. 0x00: '成功',
  40. 0x01: '不支持的指令',
  41. 0x02: '参数无效',
  42. 0x03: '设备忙',
  43. 0x04: '执行失败'
  44. }
  45. const AREA_NAMES = {
  46. [AREA.ADDR32]: 'ADDR32',
  47. [AREA.DATA]: 'DATA',
  48. [AREA.IDATA]: 'IDATA',
  49. [AREA.XDATA]: 'XDATA',
  50. [AREA.CODE]: 'CODE'
  51. }
  52. const AREA_BY_NAME = {
  53. ADDR32: AREA.ADDR32,
  54. ADDRESS32: AREA.ADDR32,
  55. DATA: AREA.DATA,
  56. IDATA: AREA.IDATA,
  57. XDATA: AREA.XDATA,
  58. CODE: AREA.CODE
  59. }
  60. const EXCEPTION_MESSAGES = {
  61. 0x01: '非法命令',
  62. 0x02: '非法区域',
  63. 0x03: '非法地址',
  64. 0x04: '非法长度',
  65. 0x05: '写保护',
  66. 0x06: '设备忙',
  67. 0x07: '格式错误',
  68. 0x08: '访问被拒绝',
  69. 0x09: '内部错误',
  70. 0x0A: '对齐错误',
  71. 0x0B: '范围溢出',
  72. 0x0C: '不支持的操作'
  73. }
  74. const DEFAULT_MAX_FRAME_BYTES = 64
  75. const UNLIMITED_FRAME_BYTES = 0
  76. const MAX_UINT16 = 0xFFFF
  77. const MAX_UINT32 = 0xFFFFFFFF
  78. const MAX_PAYLOAD_BYTES = MAX_UINT16
  79. const READ_REQUEST_LENGTH_16 = 1 + ADDRESS16_BYTE_LENGTH + 2 + 2
  80. const READ_REQUEST_LENGTH_32 = 1 + ADDRESS32_BYTE_LENGTH + 2 + 2
  81. const WRITE_REQUEST_OVERHEAD_16 = 1 + ADDRESS16_BYTE_LENGTH + 2 + 2
  82. const WRITE_REQUEST_OVERHEAD_32 = 1 + ADDRESS32_BYTE_LENGTH + 2 + 2
  83. const READ_RESPONSE_OVERHEAD_16 = 1 + ADDRESS16_BYTE_LENGTH + 2 + 2
  84. const READ_RESPONSE_OVERHEAD_32 = 1 + ADDRESS32_BYTE_LENGTH + 2 + 2
  85. const WRITE_RESPONSE_LENGTH_16 = 1 + ADDRESS16_BYTE_LENGTH + 2 + 2
  86. const WRITE_RESPONSE_LENGTH_32 = 1 + ADDRESS32_BYTE_LENGTH + 2 + 2
  87. const EXCEPTION_RESPONSE_LENGTH = 4
  88. const CODE_INFO_DESCRIPTOR_BYTE_LENGTH = 11
  89. const MEMORY_ENDIAN_MARK_BIG = 0x55AA
  90. const MEMORY_ENDIAN_MARK_LITTLE = 0xAA55
  91. const MEMORY_ENDIAN = {
  92. BIG: 'big',
  93. LITTLE: 'little'
  94. }
  95. const STORAGE_CRC_OPTIONS = {
  96. byteOrder: BYTE_ORDER_HIGH
  97. }
  98. const VALID_AREAS = [AREA.ADDR32, AREA.DATA, AREA.IDATA, AREA.XDATA, AREA.CODE]
  99. const MEMORY_AREAS = [AREA.ADDR32, AREA.DATA, AREA.IDATA, AREA.XDATA, AREA.CODE]
  100. const RESERVED_AREAS = [0x00, 0x05, 0x06]
  101. function toByte(value, label) {
  102. if (!Number.isInteger(value) || value < 0 || value > 0xFF) {
  103. throw new Error(`${label}必须在 0x00 至 0xFF 之间`)
  104. }
  105. return value
  106. }
  107. function toWord(value, label) {
  108. if (!Number.isInteger(value) || value < 0 || value > 0xFFFF) {
  109. throw new Error(`${label}必须在 0x0000 至 0xFFFF 之间`)
  110. }
  111. return value
  112. }
  113. function toInt16Word(value, label) {
  114. if (!Number.isInteger(value) || value < -0x8000 || value > 0x7FFF) {
  115. throw new Error(`${label}必须在 -32768 至 32767 之间`)
  116. }
  117. return value & 0xFFFF
  118. }
  119. function toUint32(value, label) {
  120. if (!Number.isInteger(value) || value < 0 || value > MAX_UINT32) {
  121. throw new Error(`${label}必须在 0x00000000 至 0xFFFFFFFF 之间`)
  122. }
  123. return value
  124. }
  125. function normalizeArea(value) {
  126. if (typeof value === 'string') {
  127. const area = AREA_BY_NAME[value.trim().toUpperCase()]
  128. if (area !== undefined) return area
  129. }
  130. const area = toByte(Number(value), '存储区域')
  131. if (VALID_AREAS.indexOf(area) < 0) {
  132. throw new Error('存储区域必须为 addr32/data/idata/xdata/code')
  133. }
  134. return area
  135. }
  136. function isAddress32Area(area) {
  137. return Number(area) === AREA.ADDR32
  138. }
  139. function getAddressFieldByteLength(area) {
  140. return isAddress32Area(area) ? ADDRESS32_BYTE_LENGTH : ADDRESS16_BYTE_LENGTH
  141. }
  142. function getMemoryHeaderLength(area) {
  143. return 1 + getAddressFieldByteLength(area) + 2
  144. }
  145. function getReadRequestLength(area) {
  146. return getMemoryHeaderLength(area) + 2
  147. }
  148. function getWriteRequestOverhead(area) {
  149. return getMemoryHeaderLength(area) + 2
  150. }
  151. function getReadResponseOverhead(area) {
  152. return getMemoryHeaderLength(area) + 2
  153. }
  154. function getWriteResponseLength(area) {
  155. return getMemoryHeaderLength(area) + 2
  156. }
  157. function normalizeMemoryArea(value) {
  158. const area = normalizeArea(value)
  159. if (MEMORY_AREAS.indexOf(area) < 0) {
  160. throw new Error('存储读写区域必须为 addr32/data/idata/xdata/code')
  161. }
  162. return area
  163. }
  164. function toByteLength(value, label = '字节长度', maxPayload = MAX_PAYLOAD_BYTES) {
  165. const byteLength = toWord(Number(value), label)
  166. if (byteLength === 0) {
  167. throw new Error(`${label}必须大于 0`)
  168. }
  169. if (maxPayload > 0 && byteLength > maxPayload) {
  170. throw new Error(`单帧最多访问 ${maxPayload} 字节`)
  171. }
  172. return byteLength
  173. }
  174. function splitWord(value) {
  175. return [(value >> 8) & 0xFF, value & 0xFF]
  176. }
  177. function splitDword(value) {
  178. const normalizedValue = Number(value) >>> 0
  179. return [
  180. (normalizedValue >>> 24) & 0xFF,
  181. (normalizedValue >>> 16) & 0xFF,
  182. (normalizedValue >>> 8) & 0xFF,
  183. normalizedValue & 0xFF
  184. ]
  185. }
  186. function readWord(bytes, offset) {
  187. return (((bytes[offset] || 0) << 8) | (bytes[offset + 1] || 0)) & 0xFFFF
  188. }
  189. function readDword(bytes, offset) {
  190. return (
  191. ((bytes[offset] || 0) * 0x1000000)
  192. + (((bytes[offset + 1] || 0) << 16) >>> 0)
  193. + (((bytes[offset + 2] || 0) << 8) >>> 0)
  194. + (bytes[offset + 3] || 0)
  195. ) >>> 0
  196. }
  197. function normalizeDescriptorAddressWidth(value) {
  198. const numberValue = Number(value)
  199. if (numberValue === 16 || numberValue === 32) return numberValue
  200. throw new Error('CodeInfo 描述符地址长度必须为 16 或 32')
  201. }
  202. function parseMemoryEndianMark(bytes, offset) {
  203. const marker = readWord(bytes, offset)
  204. if (marker === MEMORY_ENDIAN_MARK_BIG) {
  205. return {
  206. marker,
  207. memoryEndian: MEMORY_ENDIAN.BIG
  208. }
  209. }
  210. if (marker === MEMORY_ENDIAN_MARK_LITTLE) {
  211. return {
  212. marker,
  213. memoryEndian: MEMORY_ENDIAN.LITTLE
  214. }
  215. }
  216. return {
  217. marker,
  218. memoryEndian: ''
  219. }
  220. }
  221. function normalizeMaxFrameBytes(maxFrameBytes = DEFAULT_MAX_FRAME_BYTES) {
  222. const numberValue = Number(maxFrameBytes)
  223. if (Number.isFinite(numberValue) && Math.round(numberValue) === UNLIMITED_FRAME_BYTES) return UNLIMITED_FRAME_BYTES
  224. if (Number.isFinite(numberValue) && numberValue > 0) return Math.round(numberValue)
  225. return DEFAULT_MAX_FRAME_BYTES
  226. }
  227. function resolveDescriptorMaxFrameBytes(configuredMaxFrameBytes, descriptorMaxFrameBytes) {
  228. const configured = normalizeMaxFrameBytes(configuredMaxFrameBytes)
  229. const descriptor = normalizeMaxFrameBytes(descriptorMaxFrameBytes)
  230. if (descriptor === UNLIMITED_FRAME_BYTES) return configured
  231. if (configured === UNLIMITED_FRAME_BYTES) return descriptor
  232. return Math.min(configured, descriptor)
  233. }
  234. function getPayloadLimitFromFrame(maxFrameBytes, overhead) {
  235. const frameBytes = normalizeMaxFrameBytes(maxFrameBytes)
  236. if (frameBytes === UNLIMITED_FRAME_BYTES) return MAX_PAYLOAD_BYTES
  237. return Math.max(0, Math.min(MAX_PAYLOAD_BYTES, frameBytes - overhead))
  238. }
  239. function getMaxReadByteLength(maxFrameBytes = DEFAULT_MAX_FRAME_BYTES, area = AREA.ADDR32) {
  240. return getPayloadLimitFromFrame(maxFrameBytes, getReadResponseOverhead(area))
  241. }
  242. function getMaxWriteByteLength(maxFrameBytes = DEFAULT_MAX_FRAME_BYTES, area = AREA.ADDR32) {
  243. return getPayloadLimitFromFrame(maxFrameBytes, getWriteRequestOverhead(area))
  244. }
  245. function buildCommand(area, isWrite = false) {
  246. const normalizedArea = isWrite ? normalizeMemoryArea(area) : normalizeArea(area)
  247. if (RESERVED_AREAS.indexOf(normalizedArea) >= 0) {
  248. throw new Error('存储访问区域号 0x00/0x05/0x06 暂时保留')
  249. }
  250. return (isWrite ? CMD_WRITE_MASK : 0x00) | normalizedArea
  251. }
  252. function decodeCommand(command) {
  253. const cmd = toByte(Number(command), '命令字')
  254. const sourceCommand = cmd & ~CMD_ERR_MASK
  255. const isControl = sourceCommand === CMD_CONTROL
  256. const area = isControl ? CMD_CONTROL : (sourceCommand & CMD_ADDRESS_MODE_MASK)
  257. const reservedBits = sourceCommand & CMD_RESERVED_MASK
  258. return {
  259. addressBytes: isControl ? 0 : getAddressFieldByteLength(area),
  260. area,
  261. command: cmd,
  262. hasError: !!(cmd & CMD_ERR_MASK),
  263. hasReservedBits: !isControl && reservedBits !== 0,
  264. isAddress32: !isControl && isAddress32Area(area),
  265. isControl,
  266. isWrite: !isControl && !!(sourceCommand & CMD_WRITE_MASK),
  267. sourceCommand
  268. }
  269. }
  270. function hasValidStorageCrc(bytes) {
  271. const frame = toByteArray(bytes)
  272. if (frame.length < 3) return false
  273. if (frame.length >= 4) return hasValidCrc16Ccitt(frame, STORAGE_CRC_OPTIONS)
  274. const expected = crc16Ccitt(frame.slice(0, -2), STORAGE_CRC_OPTIONS)
  275. const received = (((frame[frame.length - 2] || 0) << 8) | (frame[frame.length - 1] || 0)) & 0xFFFF
  276. return expected === received
  277. }
  278. function appendStorageCrc(bytes) {
  279. return appendCrc16Ccitt(bytes, STORAGE_CRC_OPTIONS)
  280. }
  281. function buildReadFrame(area, address, byteLength, options = {}) {
  282. const normalizedArea = normalizeMemoryArea(area)
  283. const addressBytes = getAddressFieldByteLength(normalizedArea)
  284. const startAddress = addressBytes === ADDRESS32_BYTE_LENGTH
  285. ? toUint32(Number(address), '内存地址')
  286. : toWord(Number(address), '内存地址')
  287. const maxByteLength = getMaxReadByteLength(options.maxFrameBytes, normalizedArea)
  288. const length = toByteLength(Number(byteLength), '读取字节长度', maxByteLength || MAX_PAYLOAD_BYTES)
  289. const command = buildCommand(normalizedArea, false)
  290. const addressParts = addressBytes === ADDRESS32_BYTE_LENGTH ? splitDword(startAddress) : splitWord(startAddress)
  291. const lengthParts = splitWord(toWord(length, '读取字节长度'))
  292. return appendStorageCrc([command].concat(addressParts, lengthParts))
  293. }
  294. function buildWriteFrame(area, address, bytes, options = {}) {
  295. const normalizedArea = normalizeMemoryArea(area)
  296. if (normalizedArea === AREA.CODE) {
  297. throw new Error('code 区暂不支持写入')
  298. }
  299. const addressBytes = getAddressFieldByteLength(normalizedArea)
  300. const startAddress = addressBytes === ADDRESS32_BYTE_LENGTH
  301. ? toUint32(Number(address), '内存地址')
  302. : toWord(Number(address), '内存地址')
  303. const dataBytes = toByteArray(bytes).map((byte) => toByte(Number(byte), '写入字节'))
  304. const maxByteLength = getMaxWriteByteLength(options.maxFrameBytes, normalizedArea)
  305. const length = toByteLength(dataBytes.length, '写入字节长度', maxByteLength || MAX_PAYLOAD_BYTES)
  306. const command = buildCommand(normalizedArea, true)
  307. const addressParts = addressBytes === ADDRESS32_BYTE_LENGTH ? splitDword(startAddress) : splitWord(startAddress)
  308. const lengthParts = splitWord(toWord(length, '写入字节长度'))
  309. return appendStorageCrc([command].concat(addressParts, lengthParts, dataBytes))
  310. }
  311. function buildControlFrame(operation, dataBytes = []) {
  312. const op = toByte(Number(operation), '特殊指令')
  313. const payload = toByteArray(dataBytes).map((byte) => toByte(Number(byte), '指令数据'))
  314. return appendStorageCrc([CMD_CONTROL, op].concat(payload))
  315. }
  316. function buildControlReferenceFrame(referenceValue) {
  317. return buildControlFrame(CONTROL_OP.SET_CONTROL_REF, splitWord(toInt16Word(Number(referenceValue), '控制参考值')))
  318. }
  319. function buildCodeInfoDescriptorFrame() {
  320. return buildControlFrame(CONTROL_OP.READ_CODE_INFO_DESCRIPTOR)
  321. }
  322. function formatHex(bytes) {
  323. return toByteArray(bytes).map((byte) => byte.toString(16).padStart(2, '0').toUpperCase()).join(' ')
  324. }
  325. function parseStorageAccessResponse(bytes) {
  326. const frame = toByteArray(bytes)
  327. if (frame.length < EXCEPTION_RESPONSE_LENGTH || !hasValidStorageCrc(frame)) return null
  328. const command = frame[0] & 0xFF
  329. const decoded = decodeCommand(command)
  330. if (decoded.isControl) return parseStorageControlResponse(frame, decoded)
  331. if (decoded.hasError) {
  332. if (frame.length !== EXCEPTION_RESPONSE_LENGTH) return null
  333. return {
  334. area: decoded.area,
  335. areaName: AREA_NAMES[decoded.area] || 'UNKNOWN',
  336. command,
  337. exceptionCode: frame[1] & 0xFF,
  338. isException: true,
  339. isWrite: decoded.isWrite,
  340. protocol: PROTOCOL_NAME,
  341. sourceCommand: decoded.sourceCommand
  342. }
  343. }
  344. if (decoded.hasReservedBits) return null
  345. if (!AREA_NAMES[decoded.area]) return null
  346. const addressBytes = decoded.addressBytes
  347. const headerLength = getMemoryHeaderLength(decoded.area)
  348. if (decoded.isWrite) {
  349. if (frame.length !== getWriteResponseLength(decoded.area)) return null
  350. return {
  351. address: decoded.isAddress32 ? readDword(frame, 1) : readWord(frame, 1),
  352. addressWidth: decoded.isAddress32 ? 32 : 16,
  353. area: decoded.area,
  354. areaName: AREA_NAMES[decoded.area] || 'UNKNOWN',
  355. byteLength: readWord(frame, 1 + addressBytes),
  356. command,
  357. dataBytes: [],
  358. isException: false,
  359. isAddress32: decoded.isAddress32,
  360. isWrite: true,
  361. protocol: PROTOCOL_NAME
  362. }
  363. }
  364. if (frame.length < getReadResponseOverhead(decoded.area)) return null
  365. const address = decoded.isAddress32 ? readDword(frame, 1) : readWord(frame, 1)
  366. const byteLength = readWord(frame, 1 + addressBytes)
  367. const dataStart = headerLength
  368. const dataEnd = dataStart + byteLength
  369. if (frame.length !== dataEnd + 2) return null
  370. const dataBytes = frame.slice(dataStart, dataEnd)
  371. return {
  372. address,
  373. addressWidth: decoded.isAddress32 ? 32 : 16,
  374. area: decoded.area,
  375. areaName: AREA_NAMES[decoded.area] || 'UNKNOWN',
  376. byteLength,
  377. command,
  378. dataBytes,
  379. isException: false,
  380. isAddress32: decoded.isAddress32,
  381. isWrite: false,
  382. protocol: PROTOCOL_NAME,
  383. words: bytesToWords(dataBytes.length % 2 === 0 ? dataBytes : dataBytes.concat(0))
  384. }
  385. }
  386. function parseStorageControlResponse(frame, decoded) {
  387. if (decoded.hasError) {
  388. if (frame.length !== EXCEPTION_RESPONSE_LENGTH) return null
  389. return {
  390. area: CMD_CONTROL,
  391. areaName: 'CONTROL',
  392. command: frame[0] & 0xFF,
  393. exceptionCode: frame[1] & 0xFF,
  394. isControl: true,
  395. isException: true,
  396. isWrite: false,
  397. protocol: PROTOCOL_NAME,
  398. sourceCommand: decoded.sourceCommand
  399. }
  400. }
  401. if (frame.length < CONTROL_RESPONSE_HEADER_LENGTH + 2) return null
  402. const operation = frame[1] & 0xFF
  403. const status = frame[2] & 0xFF
  404. const dataStart = CONTROL_RESPONSE_HEADER_LENGTH
  405. const dataEnd = frame.length - 2
  406. const byteLength = Math.max(0, dataEnd - dataStart)
  407. const dataBytes = frame.slice(dataStart, dataEnd)
  408. return {
  409. area: CMD_CONTROL,
  410. areaName: 'CONTROL',
  411. byteLength,
  412. command: frame[0] & 0xFF,
  413. controlStatus: status,
  414. controlStatusText: CONTROL_STATUS_MESSAGES[status] || '未知状态',
  415. dataBytes,
  416. isControl: true,
  417. isException: false,
  418. isWrite: false,
  419. operation,
  420. protocol: PROTOCOL_NAME,
  421. ...(operation === CONTROL_OP.READ_CODE_INFO_DESCRIPTOR && dataBytes.length >= CODE_INFO_DESCRIPTOR_BYTE_LENGTH
  422. ? (() => {
  423. const endian = parseMemoryEndianMark(dataBytes, 7)
  424. return {
  425. codeInfoAddress: readDword(dataBytes, 0),
  426. codeInfoByteLength: readWord(dataBytes, 4),
  427. codeInfoAddressWidth: dataBytes[6] & 0xFF,
  428. codeInfoDescriptorBytes: dataBytes.slice(0, CODE_INFO_DESCRIPTOR_BYTE_LENGTH),
  429. codeInfoMaxPacketLength: readWord(dataBytes, 9),
  430. codeInfoMemoryEndian: endian.memoryEndian,
  431. codeInfoMemoryEndianMark: endian.marker
  432. }
  433. })()
  434. : {})
  435. }
  436. }
  437. function parseStorageAccessRequest(bytes) {
  438. const frame = toByteArray(bytes)
  439. if (frame.length < 4 || !hasValidStorageCrc(frame)) return null
  440. const command = frame[0] & 0xFF
  441. const decoded = decodeCommand(command)
  442. if (decoded.isControl) return parseStorageControlRequest(frame)
  443. if (frame.length < READ_REQUEST_LENGTH_16) return null
  444. if (decoded.hasError) return null
  445. if (decoded.hasReservedBits) return null
  446. if (!AREA_NAMES[decoded.area]) return null
  447. const addressBytes = decoded.addressBytes
  448. const headerLength = getMemoryHeaderLength(decoded.area)
  449. if (frame.length < headerLength + 2) return null
  450. const address = decoded.isAddress32 ? readDword(frame, 1) : readWord(frame, 1)
  451. const byteLength = readWord(frame, 1 + addressBytes)
  452. const expectedLength = decoded.isWrite
  453. ? headerLength + byteLength + 2
  454. : headerLength + 2
  455. if (byteLength <= 0 || frame.length !== expectedLength) return null
  456. return {
  457. address,
  458. addressWidth: decoded.isAddress32 ? 32 : 16,
  459. area: decoded.area,
  460. areaName: AREA_NAMES[decoded.area] || 'UNKNOWN',
  461. byteLength,
  462. command,
  463. dataBytes: decoded.isWrite ? frame.slice(headerLength, headerLength + byteLength) : [],
  464. isAddress32: decoded.isAddress32,
  465. isWrite: decoded.isWrite,
  466. kind: 'raw-hex',
  467. operation: decoded.isWrite ? 'write' : 'read',
  468. protocol: PROTOCOL_NAME,
  469. quantity: byteLength
  470. }
  471. }
  472. function parseStorageControlRequest(frame) {
  473. if (frame.length < 4) return null
  474. const operation = frame[1] & 0xFF
  475. const dataStart = 2
  476. const dataEnd = frame.length - 2
  477. const byteLength = Math.max(0, dataEnd - dataStart)
  478. return {
  479. area: CMD_CONTROL,
  480. areaName: 'CONTROL',
  481. byteLength,
  482. command: CMD_CONTROL,
  483. dataBytes: frame.slice(dataStart, dataEnd),
  484. isControl: true,
  485. isWrite: false,
  486. kind: 'storage-control',
  487. operation,
  488. protocol: PROTOCOL_NAME
  489. }
  490. }
  491. function getExpectedResponseLength(expected, responseCommand, responseBytes = []) {
  492. if (!expected) return 0
  493. const command = Number(responseCommand) & 0xFF
  494. if (command === (expected.command | CMD_ERR_MASK)) return EXCEPTION_RESPONSE_LENGTH
  495. if (command !== expected.command) return 0
  496. if (expected.isControl) {
  497. if (responseBytes.length < CONTROL_RESPONSE_HEADER_LENGTH) return 0
  498. const status = responseBytes[2] & 0xFF
  499. if (status !== 0) return CONTROL_RESPONSE_HEADER_LENGTH + 2
  500. return CONTROL_RESPONSE_HEADER_LENGTH + Number(expected.expectedByteLength || 0) + 2
  501. }
  502. if (expected.operation === 'write' || expected.isWrite) {
  503. return getWriteResponseLength(expected.area)
  504. }
  505. const headerLength = getMemoryHeaderLength(expected.area)
  506. if (responseBytes.length < headerLength) return 0
  507. return headerLength + readWord(responseBytes, 1 + getAddressFieldByteLength(expected.area)) + 2
  508. }
  509. function isExpectedResponse(response, expected) {
  510. if (!response || !expected) return false
  511. const sourceCommand = response.isException ? response.sourceCommand : response.command
  512. if (sourceCommand !== expected.command) return false
  513. if (expected.isControl) {
  514. if (!response.isControl) return false
  515. if (response.isException) return true
  516. if (response.operation !== expected.operation) return false
  517. return true
  518. }
  519. if (!response.isException && response.area !== expected.area) return false
  520. if (response.isException) return true
  521. if (response.address !== expected.address) return false
  522. if (response.byteLength !== expected.byteLength) return false
  523. if (!response.isWrite && (!Array.isArray(response.dataBytes) || response.dataBytes.length !== expected.byteLength)) return false
  524. return true
  525. }
  526. function getExceptionText(code) {
  527. return EXCEPTION_MESSAGES[code] || '未知异常'
  528. }
  529. function formatExceptionMessage(response) {
  530. const sourceCommand = response && response.sourceCommand
  531. const exceptionCode = response && response.exceptionCode
  532. const exceptionText = getExceptionText(exceptionCode)
  533. return `设备返回异常帧:命令 0x${padHex(sourceCommand, 2)},异常码 0x${padHex(exceptionCode, 2)}(${exceptionText})`
  534. }
  535. function getReadBufferHint(expected) {
  536. if (!expected) return 0
  537. if (expected.isControl) return expected.responseBufferHint || CONTROL_RESPONSE_HEADER_LENGTH + Number(expected.expectedByteLength || 0) + 2
  538. if (expected.operation === 'write' || expected.isWrite) {
  539. return getWriteResponseLength(expected.area)
  540. }
  541. return getReadResponseOverhead(expected.area) + Number(expected.byteLength || expected.quantity || 0)
  542. }
  543. function alignResponseBuffer(buffer, expected) {
  544. if (!Array.isArray(buffer) || !buffer.length || !expected) return
  545. const expectedCommands = [expected.command, expected.command | CMD_ERR_MASK]
  546. let matchIndex = -1
  547. for (let index = 0; index < buffer.length; index += 1) {
  548. if (expectedCommands.indexOf(buffer[index]) < 0) continue
  549. matchIndex = index
  550. break
  551. }
  552. if (matchIndex > 0) {
  553. buffer.splice(0, matchIndex)
  554. } else if (matchIndex < 0 && buffer.length > 1) {
  555. buffer.splice(0, buffer.length - 1)
  556. }
  557. }
  558. function readResponseFromBuffer(buffer, expected, options = {}) {
  559. if (!Array.isArray(buffer) || !buffer.length || !expected) {
  560. return {
  561. status: 'pending'
  562. }
  563. }
  564. alignResponseBuffer(buffer, expected)
  565. while (buffer.length >= 1) {
  566. const responseCommand = buffer[0]
  567. const responseLength = getExpectedResponseLength(expected, responseCommand, buffer)
  568. if (!responseLength) {
  569. return {
  570. status: 'pending'
  571. }
  572. }
  573. const frameLimit = normalizeMaxFrameBytes(
  574. options.maxFrameBytes === undefined ? expected.maxFrameBytes : options.maxFrameBytes
  575. )
  576. if (frameLimit > 0 && responseLength > frameLimit) {
  577. return {
  578. frameLimit,
  579. responseLength,
  580. status: 'frame-too-long'
  581. }
  582. }
  583. if (buffer.length < responseLength) {
  584. return {
  585. status: 'pending'
  586. }
  587. }
  588. const frameBytes = buffer.slice(0, responseLength)
  589. const response = parseStorageAccessResponse(frameBytes)
  590. if (!response) {
  591. return {
  592. frameBytes,
  593. status: 'invalid'
  594. }
  595. }
  596. if (!isExpectedResponse(response, expected)) {
  597. buffer.shift()
  598. alignResponseBuffer(buffer, expected)
  599. continue
  600. }
  601. if (response.isException) {
  602. return {
  603. message: formatExceptionMessage(response),
  604. response,
  605. status: 'exception'
  606. }
  607. }
  608. buffer.splice(0, responseLength)
  609. return {
  610. response,
  611. status: 'complete'
  612. }
  613. }
  614. return {
  615. status: 'pending'
  616. }
  617. }
  618. function createExpected(area, address, byteLength, isWrite, kind, options = {}) {
  619. const normalizedArea = normalizeMemoryArea(area)
  620. const command = buildCommand(normalizedArea, isWrite)
  621. return {
  622. address,
  623. addressWidth: isAddress32Area(normalizedArea) ? 32 : 16,
  624. area: normalizedArea,
  625. byteLength,
  626. command,
  627. isAddress32: isAddress32Area(normalizedArea),
  628. isWrite,
  629. kind,
  630. operation: isWrite ? 'write' : 'read',
  631. protocol: PROTOCOL_NAME,
  632. quantity: byteLength
  633. }
  634. }
  635. function createControlExpected(operation, kind = 'storage-control', options = {}) {
  636. const op = toByte(Number(operation), '特殊指令')
  637. return {
  638. area: CMD_CONTROL,
  639. byteLength: 0,
  640. command: CMD_CONTROL,
  641. expectedByteLength: Number(options.expectedByteLength) || 0,
  642. isControl: true,
  643. isWrite: false,
  644. kind,
  645. operation: op,
  646. protocol: PROTOCOL_NAME,
  647. responseBufferHint: CONTROL_RESPONSE_HEADER_LENGTH + (Number(options.expectedByteLength) || 0) + 2
  648. }
  649. }
  650. function formatAddress(value) {
  651. return Number(value || 0).toString(16).toUpperCase()
  652. }
  653. function getChunkLabel(label, chunks, chunk) {
  654. if (!label || chunks.length <= 1) return label
  655. return `${label} ${formatAddress(chunk.address)}-${formatAddress(chunk.address + chunk.quantity - 1)}`
  656. }
  657. function splitQuantity(startAddress, quantity, maxQuantity) {
  658. const chunks = []
  659. let address = Number(startAddress) || 0
  660. let remaining = Math.max(0, Math.floor(Number(quantity) || 0))
  661. const chunkLimit = Math.max(1, Math.floor(Number(maxQuantity) || remaining || 1))
  662. while (remaining > 0) {
  663. const chunkQuantity = Math.min(remaining, chunkLimit)
  664. chunks.push({
  665. address,
  666. quantity: chunkQuantity
  667. })
  668. address += chunkQuantity
  669. remaining -= chunkQuantity
  670. }
  671. return chunks
  672. }
  673. function getReadChunks(startAddress, byteLength, options = {}) {
  674. const maxByteLength = getMaxReadByteLength(options.maxFrameBytes, options.area)
  675. return splitQuantity(startAddress, byteLength, maxByteLength || byteLength)
  676. }
  677. function getWriteChunks(startAddress, bytes, options = {}) {
  678. const sourceBytes = Array.prototype.slice.call(bytes || []).map((byte) => Number(byte) & 0xFF)
  679. const maxByteLength = getMaxWriteByteLength(options.maxFrameBytes, options.area)
  680. const chunks = splitQuantity(startAddress, sourceBytes.length, maxByteLength || sourceBytes.length)
  681. let offset = 0
  682. return chunks.map((chunk) => {
  683. const dataBytes = sourceBytes.slice(offset, offset + chunk.quantity)
  684. offset += chunk.quantity
  685. return {
  686. ...chunk,
  687. dataBytes
  688. }
  689. })
  690. }
  691. const response = {
  692. createControlExpected,
  693. createExpected,
  694. formatExceptionMessage,
  695. getExceptionText,
  696. getExpectedResponseLength,
  697. getReadBufferHint,
  698. isExpectedResponse,
  699. parseStorageAccessRequest,
  700. parseStorageAccessResponse,
  701. readResponseFromBuffer
  702. }
  703. module.exports = {
  704. AREA,
  705. AREA_BY_NAME,
  706. AREA_NAMES,
  707. ADDRESS16_BYTE_LENGTH,
  708. ADDRESS32_BYTE_LENGTH,
  709. CMD_CONTROL,
  710. CMD_CONTROL_FLAG,
  711. CMD_ADDRESS_MODE_MASK,
  712. CMD_ERR_MASK,
  713. CMD_RESERVED_MASK,
  714. CMD_WRITE_MASK,
  715. CONTROL_OP,
  716. CONTROL_RESPONSE_HEADER_LENGTH,
  717. CONTROL_STATUS_MESSAGES,
  718. DEFAULT_MAX_FRAME_BYTES,
  719. EXCEPTION_MESSAGES,
  720. EXCEPTION_RESPONSE_LENGTH,
  721. CODE_INFO_DESCRIPTOR_BYTE_LENGTH,
  722. MAX_PAYLOAD_BYTES,
  723. MEMORY_ENDIAN,
  724. MEMORY_ENDIAN_MARK_BIG,
  725. MEMORY_ENDIAN_MARK_LITTLE,
  726. PROTOCOL_NAME,
  727. READ_REQUEST_LENGTH_16,
  728. READ_REQUEST_LENGTH_32,
  729. READ_RESPONSE_OVERHEAD_16,
  730. READ_RESPONSE_OVERHEAD_32,
  731. STORAGE_CRC_OPTIONS,
  732. UNLIMITED_FRAME_BYTES,
  733. WRITE_REQUEST_OVERHEAD_16,
  734. WRITE_REQUEST_OVERHEAD_32,
  735. WRITE_RESPONSE_LENGTH_16,
  736. WRITE_RESPONSE_LENGTH_32,
  737. appendStorageCrc,
  738. buildCommand,
  739. buildCodeInfoDescriptorFrame,
  740. buildControlReferenceFrame,
  741. buildControlFrame,
  742. buildReadFrame,
  743. buildWriteFrame,
  744. createControlExpected,
  745. createExpected,
  746. decodeCommand,
  747. formatHex,
  748. getChunkLabel,
  749. getReadChunks,
  750. getWriteChunks,
  751. getMaxReadByteLength,
  752. getMaxWriteByteLength,
  753. hasValidStorageCrc,
  754. normalizeDescriptorAddressWidth,
  755. normalizeArea,
  756. normalizeMaxFrameBytes,
  757. normalizeMemoryArea,
  758. resolveDescriptorMaxFrameBytes,
  759. response,
  760. splitDword,
  761. splitQuantity,
  762. splitWord,
  763. toByte,
  764. toByteLength,
  765. toUint32,
  766. toWord
  767. }