1
0

service.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. const storageAccessProtocol = require('../../protocols/storage-access/index.js')
  2. const settingsService = require('../../store/settings-store.js')
  3. const transport = require('../../transport/ble-core.js')
  4. const {
  5. createGroupsFromCodeInfo,
  6. parseCodeInfo
  7. } = require('../../domain/storage-access/code-info-parser.js')
  8. const {
  9. cloneImportedGroup,
  10. normalizeGroup
  11. } = require('../../domain/parameter-groups/model.js')
  12. const {
  13. bytesToHex
  14. } = require('../../utils/binary-utils.js')
  15. const {
  16. normalizeHexDwordText,
  17. normalizeHexText,
  18. normalizeHexWordText,
  19. parseHexBytes,
  20. parseHexDword,
  21. parseHexNumber,
  22. validateHexDwordText,
  23. validateHexText,
  24. validateHexWordText
  25. } = require('../../utils/validation.js')
  26. const MEMORY_COMMANDS = [
  27. { key: 'read', label: '读取', description: '按字节读取内存' },
  28. { key: 'write', label: '写入', description: '按字节写入内存' }
  29. ]
  30. const MEMORY_AREAS = [
  31. { key: storageAccessProtocol.AREA.ADDR32, label: 'addr32', name: 'ADDR32', addressWidth: 32 },
  32. { key: storageAccessProtocol.AREA.DATA, label: 'data', name: 'DATA' },
  33. { key: storageAccessProtocol.AREA.IDATA, label: 'idata', name: 'IDATA' },
  34. { key: storageAccessProtocol.AREA.XDATA, label: 'xdata', name: 'XDATA' },
  35. { key: storageAccessProtocol.AREA.CODE, label: 'code', name: 'CODE' }
  36. ]
  37. const MEMORY_READ_INDEX = 0
  38. const MEMORY_WRITE_INDEX = 1
  39. const CONTROL_COMMANDS = [
  40. { key: 'reset', label: '复位', op: storageAccessProtocol.CONTROL_OP.RESET },
  41. { key: 'start', label: '启动', op: storageAccessProtocol.CONTROL_OP.START },
  42. { key: 'stop', label: '停止', op: storageAccessProtocol.CONTROL_OP.STOP },
  43. { key: 'controlRef', label: '控制参考值', op: storageAccessProtocol.CONTROL_OP.SET_CONTROL_REF, hidden: true }
  44. ]
  45. let syncedDeviceCaps = {
  46. addressWidth: 0,
  47. maxPacketLength: 0,
  48. memoryEndian: ''
  49. }
  50. function resolveMaxPacketLength(value) {
  51. const settings = settingsService.getState()
  52. const numberValue = Number(value === undefined ? settings.parameterMaxPacketLength : value)
  53. const deviceMaxPacketLength = Number(syncedDeviceCaps.maxPacketLength || 0)
  54. const configuredMaxPacketLength = Number.isFinite(numberValue) && Math.round(numberValue) === 0
  55. ? 0
  56. : (Number.isFinite(numberValue) && numberValue > 0 ? Math.round(numberValue) : 64)
  57. if (!Number.isFinite(deviceMaxPacketLength) || deviceMaxPacketLength <= 0) return configuredMaxPacketLength
  58. if (configuredMaxPacketLength === 0) return Math.round(deviceMaxPacketLength)
  59. return Math.min(configuredMaxPacketLength, Math.round(deviceMaxPacketLength))
  60. }
  61. function resolveConfiguredMaxPacketLength(value) {
  62. const settings = settingsService.getState()
  63. const numberValue = Number(value === undefined ? settings.parameterMaxPacketLength : value)
  64. if (Number.isFinite(numberValue) && Math.round(numberValue) === 0) return 0
  65. if (Number.isFinite(numberValue) && numberValue > 0) return Math.round(numberValue)
  66. return 64
  67. }
  68. function normalizeTransferOptions(options = {}) {
  69. const maxPacketLength = options.maxPacketLength === undefined ? options.maxFrameBytes : options.maxPacketLength
  70. return {
  71. expectedByteLength: options.expectedByteLength,
  72. maxFrameBytes: options.useDeviceCaps === false
  73. ? resolveConfiguredMaxPacketLength(maxPacketLength)
  74. : resolveMaxPacketLength(maxPacketLength),
  75. onChunk: options.onChunk,
  76. showModal: options.showModal !== false
  77. }
  78. }
  79. function updateSyncedDeviceCaps(caps = {}) {
  80. const addressWidth = Number(caps.addressWidth)
  81. const maxPacketLength = Number(caps.maxPacketLength)
  82. const memoryEndian = String(caps.memoryEndian || '').trim().toLowerCase()
  83. syncedDeviceCaps = {
  84. addressWidth: addressWidth === 16 || addressWidth === 32 ? addressWidth : 0,
  85. maxPacketLength: Number.isFinite(maxPacketLength) && maxPacketLength > 0
  86. ? Math.round(maxPacketLength)
  87. : 0,
  88. memoryEndian: memoryEndian === 'little' ? 'little' : (memoryEndian === 'big' ? 'big' : '')
  89. }
  90. }
  91. async function readMemory(area, startAddress, byteLength, label, kind, options = {}) {
  92. const transferOptions = normalizeTransferOptions(options)
  93. const normalizedArea = storageAccessProtocol.normalizeMemoryArea(area)
  94. const bytes = []
  95. const chunks = storageAccessProtocol.getReadChunks(startAddress, byteLength, {
  96. ...transferOptions,
  97. area: normalizedArea
  98. })
  99. for (const chunk of chunks) {
  100. const response = await transport.sendManagedFrame(
  101. storageAccessProtocol.buildReadFrame(normalizedArea, chunk.address, chunk.quantity, {
  102. maxFrameBytes: transferOptions.maxFrameBytes
  103. }),
  104. storageAccessProtocol.getChunkLabel(label, chunks, chunk),
  105. storageAccessProtocol.createExpected(normalizedArea, chunk.address, chunk.quantity, false, kind),
  106. {
  107. maxFrameBytes: transferOptions.maxFrameBytes,
  108. showModal: transferOptions.showModal
  109. }
  110. )
  111. if (!response) return null
  112. const dataBytes = Array.isArray(response.dataBytes) ? response.dataBytes : []
  113. dataBytes.forEach((byte, index) => {
  114. bytes[chunk.address - startAddress + index] = Number(byte) & 0xFF
  115. })
  116. if (typeof transferOptions.onChunk === 'function') {
  117. transferOptions.onChunk(response, chunk)
  118. }
  119. }
  120. return bytes
  121. }
  122. async function writeMemory(area, startAddress, bytes, label, kind, options = {}) {
  123. const transferOptions = normalizeTransferOptions(options)
  124. const normalizedArea = storageAccessProtocol.normalizeMemoryArea(area)
  125. const chunks = storageAccessProtocol.getWriteChunks(startAddress, bytes, {
  126. ...transferOptions,
  127. area: normalizedArea
  128. })
  129. for (const chunk of chunks) {
  130. const response = await transport.sendManagedFrame(
  131. storageAccessProtocol.buildWriteFrame(normalizedArea, chunk.address, chunk.dataBytes, {
  132. maxFrameBytes: transferOptions.maxFrameBytes
  133. }),
  134. storageAccessProtocol.getChunkLabel(label, chunks, chunk),
  135. storageAccessProtocol.createExpected(normalizedArea, chunk.address, chunk.quantity, true, kind),
  136. {
  137. maxFrameBytes: transferOptions.maxFrameBytes,
  138. showModal: transferOptions.showModal
  139. }
  140. )
  141. if (!response) return false
  142. if (typeof transferOptions.onChunk === 'function') {
  143. transferOptions.onChunk(response, chunk)
  144. }
  145. }
  146. return true
  147. }
  148. async function readCodeInfoBlock(label, kind, options = {}) {
  149. const transferOptions = normalizeTransferOptions({
  150. ...options,
  151. useDeviceCaps: false
  152. })
  153. const descriptorResponse = await executeControl(
  154. storageAccessProtocol.CONTROL_OP.READ_CODE_INFO_DESCRIPTOR,
  155. [],
  156. label,
  157. `${kind}-descriptor`,
  158. {
  159. ...transferOptions,
  160. expectedByteLength: storageAccessProtocol.CODE_INFO_DESCRIPTOR_BYTE_LENGTH,
  161. useDeviceCaps: false,
  162. showModal: false
  163. }
  164. )
  165. if (!descriptorResponse) {
  166. if (transferOptions.showModal !== false) {
  167. transport.showCommandAlert(label, 'CodeInfo 描述符读取失败或超时')
  168. }
  169. return null
  170. }
  171. const codeInfoAddress = Number(descriptorResponse.codeInfoAddress || 0)
  172. const codeInfoByteLength = Number(descriptorResponse.codeInfoByteLength || 0)
  173. let codeInfoAddressWidth
  174. try {
  175. codeInfoAddressWidth = storageAccessProtocol.normalizeDescriptorAddressWidth(
  176. descriptorResponse.codeInfoAddressWidth
  177. )
  178. } catch (error) {
  179. if (transferOptions.showModal !== false) {
  180. transport.showCommandAlert(label, error.message || 'CodeInfo 描述符地址长度无效')
  181. }
  182. return null
  183. }
  184. const codeInfoMemoryEndian = String(descriptorResponse.codeInfoMemoryEndian || '').trim()
  185. if (!codeInfoMemoryEndian) {
  186. if (transferOptions.showModal !== false) {
  187. transport.showCommandAlert(label, 'CodeInfo 描述符内存字节序标记无效')
  188. }
  189. return null
  190. }
  191. const codeInfoMaxPacketLength = Number(descriptorResponse.codeInfoMaxPacketLength || 0) & 0xFFFF
  192. const codeInfoArea = codeInfoAddressWidth === 32 ? storageAccessProtocol.AREA.ADDR32 : storageAccessProtocol.AREA.CODE
  193. const codeInfoReadAddress = codeInfoAddressWidth === 32 ? codeInfoAddress : (codeInfoAddress & 0xFFFF)
  194. const codeInfoMaxFrameBytes = storageAccessProtocol.resolveDescriptorMaxFrameBytes(
  195. transferOptions.maxFrameBytes,
  196. codeInfoMaxPacketLength
  197. )
  198. if (!codeInfoByteLength || codeInfoByteLength > 0xFFFF) {
  199. if (transferOptions.showModal !== false) {
  200. transport.showCommandAlert(label, 'CodeInfo 信息块长度无效')
  201. }
  202. return null
  203. }
  204. const codeInfoBytes = await readMemory(
  205. codeInfoArea,
  206. codeInfoReadAddress,
  207. codeInfoByteLength,
  208. label,
  209. kind,
  210. {
  211. ...transferOptions,
  212. maxFrameBytes: codeInfoMaxFrameBytes,
  213. useDeviceCaps: false
  214. }
  215. )
  216. if (!codeInfoBytes) return null
  217. return {
  218. codeInfoAddress,
  219. codeInfoAddressWidth,
  220. codeInfoByteLength,
  221. codeInfoBytes,
  222. codeInfoDescriptorBytes: Array.isArray(descriptorResponse.codeInfoDescriptorBytes)
  223. ? descriptorResponse.codeInfoDescriptorBytes
  224. : [],
  225. codeInfoMaxPacketLength,
  226. codeInfoMemoryEndian,
  227. codeInfoMemoryEndianMark: Number(descriptorResponse.codeInfoMemoryEndianMark || 0) & 0xFFFF,
  228. codeInfoMemoryType: codeInfoArea
  229. }
  230. }
  231. async function executeControl(operation, dataBytes, label, kind, options = {}) {
  232. const transferOptions = normalizeTransferOptions(options)
  233. const response = await transport.sendManagedFrame(
  234. storageAccessProtocol.buildControlFrame(operation, dataBytes),
  235. label,
  236. storageAccessProtocol.createControlExpected(operation, kind, {
  237. expectedByteLength: transferOptions.expectedByteLength
  238. }),
  239. {
  240. maxFrameBytes: transferOptions.maxFrameBytes,
  241. showModal: transferOptions.showModal
  242. }
  243. )
  244. return response || null
  245. }
  246. function formatDwordHex(value) {
  247. const numberValue = Math.max(0, Math.floor(Number(value) || 0))
  248. return numberValue.toString(16).toUpperCase().padStart(8, '0')
  249. }
  250. function getAreaAddressWidth(area) {
  251. return Number(area && area.addressWidth) === 32 || Number(area && area.key) === storageAccessProtocol.AREA.ADDR32
  252. ? 32
  253. : 16
  254. }
  255. function getMemoryAreaOptions() {
  256. return MEMORY_AREAS.map((area) => ({ ...area }))
  257. }
  258. function resolveMemoryCommand(index) {
  259. const commandIndex = Number(index) === 2 ? MEMORY_WRITE_INDEX : Number(index)
  260. return {
  261. command: MEMORY_COMMANDS[commandIndex] || MEMORY_COMMANDS[MEMORY_READ_INDEX],
  262. index: commandIndex === MEMORY_WRITE_INDEX ? MEMORY_WRITE_INDEX : MEMORY_READ_INDEX
  263. }
  264. }
  265. function resolveMemoryArea(index, commandKey = 'read') {
  266. const memoryAreas = getMemoryAreaOptions()
  267. let areaIndex = Number(index) || 0
  268. if (commandKey === 'write' && memoryAreas[areaIndex] && memoryAreas[areaIndex].key === storageAccessProtocol.AREA.CODE) {
  269. areaIndex = 0
  270. }
  271. if (!memoryAreas[areaIndex]) areaIndex = 0
  272. return {
  273. area: memoryAreas[areaIndex] || memoryAreas[0],
  274. index: areaIndex,
  275. options: memoryAreas
  276. }
  277. }
  278. function normalizeDisplayHexText(value, addressWidth = 16) {
  279. const text = addressWidth === 32
  280. ? normalizeHexDwordText(value)
  281. : normalizeHexWordText(value)
  282. return text.toUpperCase()
  283. }
  284. function buildMemoryPreview(commandKey, area, address, length, dataBytes) {
  285. try {
  286. const frameOptions = {
  287. maxFrameBytes: 0
  288. }
  289. if (commandKey === 'write') {
  290. return bytesToHex(storageAccessProtocol.buildWriteFrame(area, address, dataBytes, frameOptions), ' ')
  291. }
  292. return bytesToHex(storageAccessProtocol.buildReadFrame(area, address, length, frameOptions), ' ')
  293. } catch (error) {
  294. return ''
  295. }
  296. }
  297. function normalizeMemoryCommandState(current = {}, changed = {}) {
  298. const next = {
  299. storageAccessAreaIndex: current.storageAccessAreaIndex || 0,
  300. storageAccessAddress: current.storageAccessAddress || '',
  301. storageAccessCommandIndex: current.storageAccessCommandIndex || 0,
  302. storageAccessDataText: current.storageAccessDataText || '',
  303. storageAccessLength: current.storageAccessLength || '',
  304. ...changed
  305. }
  306. const resolvedCommand = resolveMemoryCommand(next.storageAccessCommandIndex)
  307. const commandIndex = resolvedCommand.index
  308. const command = resolvedCommand.command
  309. const resolvedArea = resolveMemoryArea(next.storageAccessAreaIndex, command.key)
  310. const areaIndex = resolvedArea.index
  311. const area = resolvedArea.area
  312. const addressWidth = getAreaAddressWidth(area)
  313. const addressText = normalizeDisplayHexText(next.storageAccessAddress, addressWidth)
  314. const lengthText = normalizeDisplayHexText(next.storageAccessLength, 16)
  315. const dataText = normalizeHexText(next.storageAccessDataText)
  316. const dataBytes = dataText ? parseHexBytes(dataText) : []
  317. const address = parseInt(addressText || '0', 16)
  318. const byteLength = parseInt(lengthText || '0', 16)
  319. const hasAddressText = !!addressText.trim()
  320. const hasLengthText = !!lengthText.trim()
  321. let errorText = ''
  322. const addressError = hasAddressText
  323. ? (addressWidth === 32
  324. ? validateHexDwordText(addressText, '地址')
  325. : validateHexWordText(addressText, '地址'))
  326. : ''
  327. const lengthError = hasLengthText ? validateHexWordText(lengthText, '长度') : ''
  328. if (addressError) {
  329. errorText = addressError
  330. } else if (lengthError) {
  331. errorText = lengthError
  332. } else if (hasLengthText && byteLength <= 0) {
  333. errorText = '长度必须大于 0'
  334. } else if (command.key === 'write') {
  335. const hexError = validateHexText(next.storageAccessDataText)
  336. if (hexError) {
  337. errorText = hexError
  338. } else if (area.key === storageAccessProtocol.AREA.CODE) {
  339. errorText = 'code 区暂不支持写入'
  340. } else if (dataBytes.length !== byteLength) {
  341. errorText = `写入长度为 ${byteLength} 字节,当前数据为 ${dataBytes.length} 字节`
  342. }
  343. }
  344. const canPreview = !errorText && hasAddressText && hasLengthText && byteLength > 0
  345. const previewHex = canPreview
  346. ? buildMemoryPreview(command.key, area.key, address, byteLength, dataBytes)
  347. : ''
  348. return {
  349. storageAccessAddress: addressText,
  350. storageAccessAddressMaxLength: addressWidth === 32 ? 8 : 4,
  351. storageAccessAddressWidthText: `${addressWidth}bit`,
  352. storageAccessAreaLocked: false,
  353. storageAccessAreaOptions: resolvedArea.options,
  354. storageAccessAreaIndex: areaIndex,
  355. storageAccessAreaLabel: area.label,
  356. storageAccessCommandIndex: commandIndex,
  357. storageAccessCommandLabel: command.label,
  358. storageAccessDataText: dataText,
  359. storageAccessErrorText: errorText,
  360. storageAccessLength: lengthText,
  361. storageAccessPreviewHexText: previewHex,
  362. storageAccessPreviewText: canPreview
  363. ? `${area.label} 0x${addressWidth === 32 ? formatDwordHex(address) : address.toString(16).toUpperCase().padStart(4, '0')} / ${byteLength} bytes`
  364. : '',
  365. storageAccessShowWriteData: command.key === 'write',
  366. storageAccessTitleText: `${command.label}命令`
  367. }
  368. }
  369. function getSyncedDeviceCaps() {
  370. return {
  371. ...syncedDeviceCaps
  372. }
  373. }
  374. function parseMemoryCommandInput(data = {}) {
  375. const state = normalizeMemoryCommandState(data)
  376. const command = resolveMemoryCommand(state.storageAccessCommandIndex).command
  377. const area = resolveMemoryArea(state.storageAccessAreaIndex, command.key).area
  378. const addressWidth = getAreaAddressWidth(area)
  379. if (state.storageAccessErrorText) {
  380. throw new Error(state.storageAccessErrorText)
  381. }
  382. if (!String(state.storageAccessAddress || '').trim()) {
  383. throw new Error('地址请输入十六进制')
  384. }
  385. if (!String(state.storageAccessLength || '').trim()) {
  386. throw new Error('长度请输入十六进制')
  387. }
  388. return {
  389. area,
  390. areaValue: area.key,
  391. byteLength: parseHexNumber(state.storageAccessLength, '长度', 0xFFFF),
  392. command,
  393. commandKey: command.key,
  394. dataBytes: command.key === 'write' ? parseHexBytes(state.storageAccessDataText || '') : [],
  395. startAddress: addressWidth === 32
  396. ? parseHexDword(state.storageAccessAddress, '地址')
  397. : parseHexNumber(state.storageAccessAddress, '地址', 0xFFFF),
  398. state
  399. }
  400. }
  401. async function executeMemoryCommand(data = {}, options = {}) {
  402. const command = parseMemoryCommandInput(data)
  403. if (command.commandKey === 'read') {
  404. const bytes = await readMemory(
  405. command.areaValue,
  406. command.startAddress,
  407. command.byteLength,
  408. options.label || '存储访问协议读取',
  409. options.kind || 'communication-storage-read',
  410. options
  411. )
  412. return {
  413. bytes,
  414. command,
  415. ok: !!bytes,
  416. previewHex: bytes ? bytesToHex(bytes, ' ') : '',
  417. state: command.state
  418. }
  419. }
  420. const ok = await writeMemory(
  421. command.areaValue,
  422. command.startAddress,
  423. command.dataBytes,
  424. options.label || '存储访问协议写入',
  425. options.kind || 'communication-storage-write',
  426. options
  427. )
  428. return {
  429. command,
  430. ok,
  431. state: command.state
  432. }
  433. }
  434. function normalizeReferenceText(value, fallback = '0') {
  435. const source = String(value === undefined || value === null ? '' : value).trim()
  436. if (!source) return fallback
  437. const hexText = source.replace(/^0x/i, '')
  438. if (/^[0-9A-F]{1,4}$/i.test(hexText) && /[A-F]/i.test(hexText)) {
  439. const rawValue = parseInt(hexText, 16) || 0
  440. return String(rawValue & 0x8000 ? rawValue - 0x10000 : rawValue)
  441. }
  442. if (/^-?\d+$/.test(source)) {
  443. return String(Number(source))
  444. }
  445. return source
  446. }
  447. function normalizeControlReference(value) {
  448. const text = String(value === undefined || value === null ? '' : value).trim()
  449. if (!text) {
  450. return {
  451. bytes: [],
  452. errorText: '控制参考值请输入十进制',
  453. text,
  454. value: 0
  455. }
  456. }
  457. if (!/^-?\d+$/.test(text)) {
  458. return {
  459. bytes: [],
  460. errorText: '控制参考值只支持十进制',
  461. text,
  462. value: 0
  463. }
  464. }
  465. const valueNumber = Number(text)
  466. if (valueNumber < -0x8000 || valueNumber > 0x7FFF) {
  467. return {
  468. bytes: [],
  469. errorText: '控制参考值范围为 -32768 - 32767',
  470. text,
  471. value: 0
  472. }
  473. }
  474. const wordValue = valueNumber & 0xFFFF
  475. return {
  476. bytes: [
  477. (wordValue >>> 8) & 0xFF,
  478. wordValue & 0xFF
  479. ],
  480. errorText: '',
  481. text,
  482. value: valueNumber
  483. }
  484. }
  485. function getControlCommand(commandKey) {
  486. return CONTROL_COMMANDS.find((command) => command.key === commandKey) || null
  487. }
  488. function normalizeControlState(current = {}, changed = {}) {
  489. const next = {
  490. storageAccessControlRefText: current.storageAccessControlRefText || '0',
  491. ...changed
  492. }
  493. const controlRefText = normalizeReferenceText(next.storageAccessControlRefText, '0')
  494. const controlRef = normalizeControlReference(controlRefText)
  495. return {
  496. storageAccessControlRefErrorText: controlRef.errorText,
  497. storageAccessControlRefPreviewHexText: controlRef.errorText
  498. ? ''
  499. : bytesToHex(storageAccessProtocol.buildControlReferenceFrame(controlRef.value), ' '),
  500. storageAccessControlRefText: controlRefText
  501. }
  502. }
  503. function getControlCommands() {
  504. return CONTROL_COMMANDS.map((command) => ({
  505. ...command,
  506. previewHexText: command.key === 'controlRef'
  507. ? ''
  508. : bytesToHex(storageAccessProtocol.buildControlFrame(command.op), ' ')
  509. }))
  510. }
  511. async function syncCodeInfo(options = {}) {
  512. const result = await readCodeInfoBlock(
  513. options.label || '同步CodeInfo',
  514. options.kind || 'storage-code-info-read',
  515. {
  516. maxPacketLength: options.maxPacketLength,
  517. showModal: options.showModal !== false
  518. }
  519. )
  520. if (!result) {
  521. return {
  522. ok: false
  523. }
  524. }
  525. const descriptorCaps = {
  526. addressWidth: result.codeInfoAddressWidth,
  527. codeInfoByteLength: result.codeInfoByteLength,
  528. maxPacketLength: result.codeInfoMaxPacketLength,
  529. memoryEndian: result.codeInfoMemoryEndian,
  530. memoryEndianMark: result.codeInfoMemoryEndianMark
  531. }
  532. const codeInfo = parseCodeInfo(result.codeInfoBytes, descriptorCaps)
  533. const importedGroups = createGroupsFromCodeInfo(codeInfo, options)
  534. .map(cloneImportedGroup)
  535. .map(normalizeGroup)
  536. updateSyncedDeviceCaps(descriptorCaps)
  537. return {
  538. codeInfoAddress: result.codeInfoAddress,
  539. codeInfoAddressWidth: result.codeInfoAddressWidth,
  540. codeInfoAddressText: formatDwordHex(result.codeInfoAddress),
  541. codeInfoByteLength: result.codeInfoByteLength,
  542. codeInfoByteLengthText: formatDwordHex(result.codeInfoByteLength),
  543. codeInfoBytes: result.codeInfoBytes,
  544. codeInfo,
  545. codeInfoDescriptorBytes: result.codeInfoDescriptorBytes,
  546. codeInfoMaxPacketLength: result.codeInfoMaxPacketLength,
  547. codeInfoMemoryEndian: result.codeInfoMemoryEndian,
  548. codeInfoMemoryEndianMark: result.codeInfoMemoryEndianMark,
  549. groupCount: importedGroups.length,
  550. importedGroups,
  551. codeInfoMemoryType: result.codeInfoMemoryType,
  552. ok: true,
  553. structCount: codeInfo.structCount
  554. }
  555. }
  556. async function executeControlCommand(commandKey, data = {}, options = {}) {
  557. const command = getControlCommand(commandKey)
  558. if (!command) {
  559. return {
  560. errorText: '特殊指令无效',
  561. ok: false
  562. }
  563. }
  564. const controlState = normalizeControlState(data)
  565. if (command.key === 'controlRef' && controlState.storageAccessControlRefErrorText) {
  566. return {
  567. errorText: controlState.storageAccessControlRefErrorText,
  568. ok: false
  569. }
  570. }
  571. const response = await executeControl(
  572. command.op,
  573. command.key === 'controlRef'
  574. ? normalizeControlReference(controlState.storageAccessControlRefText).bytes
  575. : [],
  576. command.label || '特殊指令',
  577. `storage-control-${command.key}`,
  578. {
  579. maxPacketLength: options.maxPacketLength,
  580. showModal: options.showModal !== false
  581. }
  582. )
  583. if (!response) {
  584. return {
  585. errorText: '指令执行失败或超时',
  586. ok: false
  587. }
  588. }
  589. if (response.controlStatus !== 0) {
  590. return {
  591. errorText: response.controlStatusText || '设备拒绝执行',
  592. ok: false,
  593. response
  594. }
  595. }
  596. return {
  597. command,
  598. ok: true,
  599. response
  600. }
  601. }
  602. module.exports = {
  603. AREA: storageAccessProtocol.AREA,
  604. CONTROL_OP: storageAccessProtocol.CONTROL_OP,
  605. executeMemoryCommand,
  606. executeControl,
  607. executeControlCommand,
  608. formatDwordHex,
  609. getControlCommand,
  610. getControlCommands,
  611. getMemoryAreaOptions,
  612. getSyncedDeviceCaps,
  613. normalizeMemoryCommandState,
  614. normalizeControlState,
  615. readCodeInfoBlock,
  616. readMemory,
  617. resolveMaxPacketLength,
  618. syncCodeInfo,
  619. updateSyncedDeviceCaps,
  620. writeMemory
  621. }