index.js 28 KB

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