1
0

index.js 28 KB

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