manual-command.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. const storageAccessProtocol = require('../../protocols/storage-access/index.js')
  2. const {
  3. bytesToHex
  4. } = require('../../utils/binary-utils.js')
  5. const {
  6. normalizeHexDwordText,
  7. normalizeHexText,
  8. normalizeHexWordText,
  9. parseHexBytes,
  10. parseHexDword,
  11. parseHexNumber,
  12. validateHexDwordText,
  13. validateHexText,
  14. validateHexWordText
  15. } = require('../../utils/validation.js')
  16. const protocolIo = require('./protocol-io.js')
  17. const MEMORY_COMMANDS = [
  18. { key: 'read', label: '读取', description: '按字节读取内存' },
  19. { key: 'write', label: '写入', description: '按字节写入内存' }
  20. ]
  21. const MEMORY_AREAS = [
  22. { key: storageAccessProtocol.AREA.ADDR32, label: 'addr32', name: 'ADDR32', addressWidth: 32 },
  23. { key: storageAccessProtocol.AREA.DATA, label: 'data', name: 'DATA' },
  24. { key: storageAccessProtocol.AREA.IDATA, label: 'idata', name: 'IDATA' },
  25. { key: storageAccessProtocol.AREA.XDATA, label: 'xdata', name: 'XDATA' },
  26. { key: storageAccessProtocol.AREA.CODE, label: 'code', name: 'CODE', readOnly: true }
  27. ]
  28. const CONTROL_COMMANDS = [
  29. { key: 'reset', label: '复位', op: storageAccessProtocol.CONTROL_OP.RESET }
  30. ]
  31. function formatDwordHex(value) {
  32. const numberValue = Math.max(0, Math.floor(Number(value) || 0))
  33. return numberValue.toString(16).toUpperCase().padStart(8, '0')
  34. }
  35. function getMemoryAreaOptions() {
  36. return MEMORY_AREAS.map((area) => ({ ...area }))
  37. }
  38. function resolveMemoryCommand(index) {
  39. const commandIndex = Number(index) === 1 ? 1 : 0
  40. return {
  41. command: MEMORY_COMMANDS[commandIndex],
  42. index: commandIndex
  43. }
  44. }
  45. function resolveMemoryArea(index, commandKey = 'read') {
  46. const memoryAreas = getMemoryAreaOptions()
  47. let areaIndex = Number(index) || 0
  48. if (commandKey === 'write' && memoryAreas[areaIndex] && memoryAreas[areaIndex].readOnly) {
  49. areaIndex = 0
  50. }
  51. if (!memoryAreas[areaIndex]) areaIndex = 0
  52. return {
  53. area: memoryAreas[areaIndex] || memoryAreas[0],
  54. index: areaIndex,
  55. options: memoryAreas
  56. }
  57. }
  58. function normalizeDisplayHexText(value, addressWidth = 16) {
  59. const text = addressWidth === 32
  60. ? normalizeHexDwordText(value)
  61. : normalizeHexWordText(value)
  62. return text.toUpperCase()
  63. }
  64. function buildMemoryPreview(commandKey, area, address, length, dataBytes) {
  65. try {
  66. const frameOptions = {
  67. maxFrameBytes: 0
  68. }
  69. if (commandKey === 'write') {
  70. return bytesToHex(storageAccessProtocol.buildWriteFrame(area, address, dataBytes, frameOptions), ' ')
  71. }
  72. return bytesToHex(storageAccessProtocol.buildReadFrame(area, address, length, frameOptions), ' ')
  73. } catch (error) {
  74. return ''
  75. }
  76. }
  77. function normalizeMemoryCommandState(current = {}, changed = {}) {
  78. const next = {
  79. storageAccessAddress: current.storageAccessAddress || '',
  80. storageAccessAreaIndex: current.storageAccessAreaIndex || 0,
  81. storageAccessCommandIndex: current.storageAccessCommandIndex || 0,
  82. storageAccessDataText: current.storageAccessDataText || '',
  83. storageAccessLength: current.storageAccessLength || '',
  84. ...changed
  85. }
  86. const resolvedCommand = resolveMemoryCommand(next.storageAccessCommandIndex)
  87. const command = resolvedCommand.command
  88. const resolvedArea = resolveMemoryArea(next.storageAccessAreaIndex, command.key)
  89. const area = resolvedArea.area
  90. const addressWidth = Number(area.addressWidth) === 32 ? 32 : 16
  91. const addressText = normalizeDisplayHexText(next.storageAccessAddress, addressWidth)
  92. const lengthText = normalizeDisplayHexText(next.storageAccessLength, 16)
  93. const dataText = normalizeHexText(next.storageAccessDataText)
  94. const dataBytes = dataText ? parseHexBytes(dataText) : []
  95. const address = parseInt(addressText || '0', 16)
  96. const byteLength = parseInt(lengthText || '0', 16)
  97. const hasAddressText = !!addressText.trim()
  98. const hasLengthText = !!lengthText.trim()
  99. const addressError = hasAddressText
  100. ? (addressWidth === 32
  101. ? validateHexDwordText(addressText, '地址')
  102. : validateHexWordText(addressText, '地址'))
  103. : ''
  104. const lengthError = hasLengthText ? validateHexWordText(lengthText, '长度') : ''
  105. let errorText = addressError || lengthError
  106. if (!errorText && hasLengthText && byteLength <= 0) {
  107. errorText = '长度必须大于 0'
  108. } else if (!errorText && command.key === 'write') {
  109. const hexError = validateHexText(next.storageAccessDataText)
  110. if (hexError) {
  111. errorText = hexError
  112. } else if (area.readOnly) {
  113. errorText = `${area.label} 区不可写`
  114. } else if (dataBytes.length !== byteLength) {
  115. errorText = `写入长度为 ${byteLength} 字节,当前数据为 ${dataBytes.length} 字节`
  116. }
  117. }
  118. const canPreview = !errorText && hasAddressText && hasLengthText && byteLength > 0
  119. return {
  120. storageAccessAddress: addressText,
  121. storageAccessAddressMaxLength: addressWidth === 32 ? 8 : 4,
  122. storageAccessAddressWidthText: `${addressWidth}bit`,
  123. storageAccessAreaIndex: resolvedArea.index,
  124. storageAccessAreaLabel: area.label,
  125. storageAccessAreaLocked: false,
  126. storageAccessAreaOptions: resolvedArea.options,
  127. storageAccessCommandIndex: resolvedCommand.index,
  128. storageAccessCommandLabel: command.label,
  129. storageAccessDataText: dataText,
  130. storageAccessErrorText: errorText,
  131. storageAccessLength: lengthText,
  132. storageAccessPreviewHexText: canPreview
  133. ? buildMemoryPreview(command.key, area.key, address, byteLength, dataBytes)
  134. : '',
  135. storageAccessPreviewText: canPreview
  136. ? `${area.label} 0x${addressWidth === 32 ? formatDwordHex(address) : address.toString(16).toUpperCase().padStart(4, '0')} / ${byteLength} bytes`
  137. : '',
  138. storageAccessShowWriteData: command.key === 'write',
  139. storageAccessTitleText: `${command.label}命令`
  140. }
  141. }
  142. function parseMemoryCommandInput(data = {}) {
  143. const state = normalizeMemoryCommandState(data)
  144. const command = resolveMemoryCommand(state.storageAccessCommandIndex).command
  145. const area = resolveMemoryArea(state.storageAccessAreaIndex, command.key).area
  146. if (state.storageAccessErrorText) {
  147. throw new Error(state.storageAccessErrorText)
  148. }
  149. if (!String(state.storageAccessAddress || '').trim()) {
  150. throw new Error('地址请输入十六进制')
  151. }
  152. if (!String(state.storageAccessLength || '').trim()) {
  153. throw new Error('长度请输入十六进制')
  154. }
  155. return {
  156. area,
  157. areaValue: area.key,
  158. byteLength: parseHexNumber(state.storageAccessLength, '长度', 0xFFFF),
  159. command,
  160. commandKey: command.key,
  161. dataBytes: command.key === 'write' ? parseHexBytes(state.storageAccessDataText || '') : [],
  162. startAddress: Number(area.addressWidth) === 32
  163. ? parseHexDword(state.storageAccessAddress, '地址')
  164. : parseHexNumber(state.storageAccessAddress, '地址', 0xFFFF),
  165. state
  166. }
  167. }
  168. async function executeMemoryCommand(data = {}, options = {}) {
  169. const command = parseMemoryCommandInput(data)
  170. if (command.commandKey === 'read') {
  171. const bytes = await protocolIo.readMemory(
  172. command.areaValue,
  173. command.startAddress,
  174. command.byteLength,
  175. options.label || '存储访问协议读取',
  176. options.kind || 'communication-storage-read',
  177. options
  178. )
  179. return {
  180. bytes,
  181. command,
  182. ok: !!bytes,
  183. previewHex: bytes ? bytesToHex(bytes, ' ') : '',
  184. state: command.state
  185. }
  186. }
  187. const ok = await protocolIo.writeMemory(
  188. command.areaValue,
  189. command.startAddress,
  190. command.dataBytes,
  191. options.label || '存储访问协议写入',
  192. options.kind || 'communication-storage-write',
  193. options
  194. )
  195. return {
  196. command,
  197. ok,
  198. state: command.state
  199. }
  200. }
  201. function getControlCommand(commandKey) {
  202. return CONTROL_COMMANDS.find((command) => command.key === commandKey) || null
  203. }
  204. async function executeControlCommand(commandKey, data = {}, options = {}) {
  205. const command = getControlCommand(commandKey)
  206. if (!command) {
  207. return {
  208. errorText: '特殊指令无效',
  209. ok: false
  210. }
  211. }
  212. const response = await protocolIo.executeControl(
  213. command.op,
  214. [],
  215. command.label || '特殊指令',
  216. `storage-control-${command.key}`,
  217. {
  218. maxPacketLength: options.maxPacketLength,
  219. showModal: options.showModal !== false
  220. }
  221. )
  222. if (!response) {
  223. return {
  224. errorText: '指令执行失败或超时',
  225. ok: false
  226. }
  227. }
  228. return {
  229. command,
  230. ok: true,
  231. response
  232. }
  233. }
  234. module.exports = {
  235. executeControlCommand,
  236. executeMemoryCommand,
  237. getControlCommand,
  238. getMemoryAreaOptions,
  239. normalizeMemoryCommandState
  240. }