1
0

index.js 31 KB

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