service.js 20 KB

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