manual-command.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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 = {
  60. maxFrameBytes: 0
  61. }
  62. if (commandKey === 'write') {
  63. return bytesToHex(storageAccessProtocol.buildWriteFrame(area, address, dataBytes, frameOptions), ' ')
  64. }
  65. return bytesToHex(storageAccessProtocol.buildReadFrame(area, address, length, frameOptions), ' ')
  66. } catch (error) {
  67. return ''
  68. }
  69. }
  70. function normalizeMemoryCommandState(current = {}, changed = {}) {
  71. const next = {
  72. storageAccessAddress: current.storageAccessAddress || '',
  73. storageAccessAreaIndex: current.storageAccessAreaIndex || 0,
  74. storageAccessCommandIndex: current.storageAccessCommandIndex || 0,
  75. storageAccessDataText: current.storageAccessDataText || '',
  76. storageAccessLength: current.storageAccessLength || '',
  77. ...changed
  78. }
  79. const resolvedCommand = resolveMemoryCommand(next.storageAccessCommandIndex)
  80. const command = resolvedCommand.command
  81. const resolvedArea = resolveMemoryArea(next.storageAccessAreaIndex, command.key)
  82. const area = resolvedArea.area
  83. const addressWidth = Number(area.addressWidth) === 32 ? 32 : 16
  84. const addressText = normalizeDisplayHexText(next.storageAccessAddress, addressWidth)
  85. const lengthText = normalizeDisplayHexText(next.storageAccessLength, 16)
  86. const dataText = normalizeHexText(next.storageAccessDataText)
  87. const dataBytes = dataText ? parseHexBytes(dataText) : []
  88. const address = parseInt(addressText || '0', 16)
  89. const byteLength = parseInt(lengthText || '0', 16)
  90. const hasAddressText = !!addressText.trim()
  91. const hasLengthText = !!lengthText.trim()
  92. const addressError = hasAddressText
  93. ? (addressWidth === 32
  94. ? validateHexDwordText(addressText, '地址')
  95. : validateHexWordText(addressText, '地址'))
  96. : ''
  97. const lengthError = hasLengthText ? validateHexWordText(lengthText, '长度') : ''
  98. let errorText = addressError || lengthError
  99. if (!errorText && hasLengthText && byteLength <= 0) {
  100. errorText = '长度必须大于 0'
  101. } else if (!errorText && command.key === 'write') {
  102. const hexError = validateHexText(next.storageAccessDataText)
  103. if (hexError) {
  104. errorText = hexError
  105. } else if (area.readOnly) {
  106. errorText = `${area.label} 区不可写`
  107. } else if (dataBytes.length !== byteLength) {
  108. errorText = `写入长度为 ${byteLength} 字节,当前数据为 ${dataBytes.length} 字节`
  109. }
  110. }
  111. const canPreview = !errorText && hasAddressText && hasLengthText && byteLength > 0
  112. return {
  113. storageAccessAddress: addressText,
  114. storageAccessAddressMaxLength: addressWidth === 32 ? 8 : 4,
  115. storageAccessAddressWidthText: `${addressWidth}bit`,
  116. storageAccessAreaIndex: resolvedArea.index,
  117. storageAccessAreaLabel: area.label,
  118. storageAccessAreaLocked: false,
  119. storageAccessAreaOptions: resolvedArea.options,
  120. storageAccessCommandIndex: resolvedCommand.index,
  121. storageAccessCommandLabel: command.label,
  122. storageAccessDataText: dataText,
  123. storageAccessErrorText: errorText,
  124. storageAccessLength: lengthText,
  125. storageAccessPreviewHexText: canPreview
  126. ? buildMemoryPreview(command.key, area.key, address, byteLength, dataBytes)
  127. : '',
  128. storageAccessPreviewText: canPreview
  129. ? `${area.label} 0x${formatHexNumber(address, addressWidth === 32 ? 8 : 4)} / ${byteLength} bytes`
  130. : '',
  131. storageAccessShowWriteData: command.key === 'write',
  132. storageAccessTitleText: `${command.label}命令`
  133. }
  134. }
  135. function parseMemoryCommandInput(data = {}) {
  136. const state = normalizeMemoryCommandState(data)
  137. const command = resolveMemoryCommand(state.storageAccessCommandIndex).command
  138. const area = resolveMemoryArea(state.storageAccessAreaIndex, command.key).area
  139. if (state.storageAccessErrorText) {
  140. throw new Error(state.storageAccessErrorText)
  141. }
  142. if (!String(state.storageAccessAddress || '').trim()) {
  143. throw new Error('地址请输入十六进制')
  144. }
  145. if (!String(state.storageAccessLength || '').trim()) {
  146. throw new Error('长度请输入十六进制')
  147. }
  148. return {
  149. area,
  150. areaValue: area.key,
  151. byteLength: parseHexNumber(state.storageAccessLength, '长度', 0xFFFF),
  152. command,
  153. commandKey: command.key,
  154. dataBytes: command.key === 'write' ? parseHexBytes(state.storageAccessDataText || '') : [],
  155. startAddress: Number(area.addressWidth) === 32
  156. ? parseHexDword(state.storageAccessAddress, '地址')
  157. : parseHexNumber(state.storageAccessAddress, '地址', 0xFFFF),
  158. state
  159. }
  160. }
  161. async function executeMemoryCommand(data = {}, options = {}) {
  162. const command = parseMemoryCommandInput(data)
  163. if (command.commandKey === 'read') {
  164. const bytes = await protocolIo.readMemory(
  165. command.areaValue,
  166. command.startAddress,
  167. command.byteLength,
  168. options.label || '存储访问协议读取',
  169. options.kind || 'communication-storage-read',
  170. options
  171. )
  172. return {
  173. bytes,
  174. command,
  175. ok: !!bytes,
  176. previewHex: bytes ? bytesToHex(bytes, ' ') : '',
  177. state: command.state
  178. }
  179. }
  180. const ok = await protocolIo.writeMemory(
  181. command.areaValue,
  182. command.startAddress,
  183. command.dataBytes,
  184. options.label || '存储访问协议写入',
  185. options.kind || 'communication-storage-write',
  186. options
  187. )
  188. return {
  189. command,
  190. ok,
  191. state: command.state
  192. }
  193. }
  194. function getControlCommand(commandKey) {
  195. return CONTROL_COMMANDS.find((command) => command.key === commandKey) || null
  196. }
  197. async function executeControlCommand(commandKey, data = {}, options = {}) {
  198. const command = getControlCommand(commandKey)
  199. if (!command) {
  200. return {
  201. errorText: '特殊指令无效',
  202. ok: false
  203. }
  204. }
  205. const response = await protocolIo.executeControl(
  206. command.op,
  207. [],
  208. command.label || '特殊指令',
  209. `storage-control-${command.key}`,
  210. {
  211. maxPacketLength: options.maxPacketLength,
  212. showModal: options.showModal !== false
  213. }
  214. )
  215. if (!response) {
  216. return {
  217. errorText: '指令执行失败或超时',
  218. ok: false
  219. }
  220. }
  221. return {
  222. command,
  223. ok: true,
  224. response
  225. }
  226. }
  227. module.exports = {
  228. executeControlCommand,
  229. executeMemoryCommand,
  230. getControlCommand,
  231. getMemoryAreaOptions,
  232. normalizeMemoryCommandState
  233. }