manual-command.js 7.9 KB

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