1
0

service.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. const {
  2. isCancelError,
  3. loadGroupsFromMessageFile,
  4. saveGroupsToChat
  5. } = require('./persistence.js')
  6. const {
  7. completeStructInstanceGroups,
  8. mergeImportedGroups,
  9. parseStructDefinition
  10. } = require('./imports.js')
  11. const storageAccessService = require('../storage-access/service.js')
  12. const parameterGroupIo = require('./io.js')
  13. const settingsService = require('../../store/settings-store.js')
  14. const store = require('./store.js')
  15. const {
  16. loadSelectedFile
  17. } = require('../../repositories/file.js')
  18. const transport = require('../../transport/ble-core.js')
  19. const {
  20. DATA_TYPE_OPTIONS,
  21. MAX_STORAGE_ADDRESS,
  22. REGISTER_TYPE_OPTIONS,
  23. cloneImportedGroup,
  24. getDataType,
  25. isAddressRangeOverflow,
  26. normalizeGroup,
  27. normalizeGroupConfig,
  28. validateRegisterValue
  29. } = require('../../domain/parameter-groups/model.js')
  30. const {
  31. findGroup,
  32. getGroups,
  33. getState,
  34. init,
  35. setGroups,
  36. setStorageCodeInfo,
  37. switchProtocolMode,
  38. subscribe,
  39. updateGroups
  40. } = store
  41. let initialized = false
  42. function getActiveProtocolMode() {
  43. return settingsService.getState().protocolMode
  44. }
  45. function isStorageAccessProtocolMode(protocolMode) {
  46. return settingsService.isStorageAccessProtocol(protocolMode)
  47. }
  48. function isGroupAddressRangeOverflow(group = {}, protocolMode = getActiveProtocolMode()) {
  49. if (!isStorageAccessProtocolMode(protocolMode)) {
  50. return isAddressRangeOverflow(group.startAddress, group.quantity)
  51. }
  52. const startAddress = Math.max(0, Math.floor(Number(group.startAddress) || 0))
  53. const addressSpan = Math.max(1, Math.floor(Number(group.byteLength || group.sourceByteLength || group.quantity) || 1))
  54. return startAddress + addressSpan - 1 > MAX_STORAGE_ADDRESS
  55. }
  56. function getAddressOverflowText(protocolMode = getActiveProtocolMode()) {
  57. return isStorageAccessProtocolMode(protocolMode)
  58. ? '地址范围超出 0xFFFFFFFF'
  59. : '地址范围超出 0xFFFF'
  60. }
  61. function isUnconfiguredRegister(register = {}) {
  62. return getDataType(register.dataType).key === 'raw'
  63. }
  64. function getUnconfiguredRegisterName(register = {}, index = 0) {
  65. return register.name || `变量 ${index + 1}`
  66. }
  67. function findUnconfiguredRegister(group = {}) {
  68. const registers = Array.isArray(group.registers) ? group.registers : []
  69. const index = registers.findIndex(isUnconfiguredRegister)
  70. return index >= 0
  71. ? {
  72. index,
  73. register: registers[index]
  74. }
  75. : null
  76. }
  77. function initParameterGroups() {
  78. settingsService.init()
  79. init(getActiveProtocolMode())
  80. switchProtocolMode(getActiveProtocolMode(), {
  81. notify: false
  82. })
  83. if (initialized) return
  84. settingsService.subscribe((settingsState) => {
  85. switchProtocolMode(settingsState.protocolMode)
  86. })
  87. initialized = true
  88. }
  89. async function importJsonFromMessageFile() {
  90. try {
  91. const importedGroups = (await loadGroupsFromMessageFile()).map(normalizeGroup)
  92. if (!importedGroups.length) throw new Error('JSON 中没有可导入的寄存器组')
  93. const merged = mergeImportedGroups(getGroups(), importedGroups)
  94. setGroups(merged.groups)
  95. return merged.changedCount || 0
  96. } catch (error) {
  97. const message = error && error.message ? error.message : '导入参数组配置失败'
  98. transport.showCommandAlert('参数组导入', message)
  99. return 0
  100. }
  101. }
  102. function completeStructInstanceGroupsWithStructSource(sourceText, options = {}) {
  103. const completed = completeStructInstanceGroups(getGroups(), sourceText, options)
  104. if (!completed.completedCount) {
  105. throw new Error('没有找到可匹配的结构体实例')
  106. }
  107. setGroups(completed.groups)
  108. return completed
  109. }
  110. function mergeImportedGroupsIntoState(importedGroups = [], options = {}) {
  111. const normalizedGroups = importedGroups.map(cloneImportedGroup).map(normalizeGroup)
  112. const merged = mergeImportedGroups(getGroups(), normalizedGroups, options)
  113. if (normalizedGroups.length) {
  114. setGroups(merged.groups)
  115. }
  116. return merged
  117. }
  118. function clearStorageAccessGroups() {
  119. setGroups([], {
  120. protocolMode: settingsService.PROTOCOL_MODE.STORAGE_ACCESS
  121. })
  122. setStorageCodeInfo(null)
  123. return true
  124. }
  125. async function completeStructInstanceGroupsWithStructFile(options = {}) {
  126. try {
  127. const file = await loadSelectedFile('auto', {
  128. encoding: 'utf8',
  129. extensionMessage: '请选择 .h 或 .c 结构体/枚举定义文件',
  130. extensions: ['h', 'c', 'txt'],
  131. fallbackName: 'structs.h'
  132. })
  133. return completeStructInstanceGroupsWithStructSource(file.text, options)
  134. } catch (error) {
  135. const message = error && error.message ? error.message : '结构体/枚举补全失败'
  136. transport.showCommandAlert('结构体/枚举补全', message)
  137. return {
  138. completedCount: 0,
  139. skippedCount: 0,
  140. structCount: 0,
  141. variableCount: 0
  142. }
  143. }
  144. }
  145. async function saveJsonToChat() {
  146. try {
  147. const result = await saveGroupsToChat(getGroups())
  148. return result && result.count
  149. ? result
  150. : {
  151. count: 0
  152. }
  153. } catch (error) {
  154. const message = error && error.message ? error.message : '保存参数组配置失败'
  155. if (!isCancelError(error)) {
  156. transport.showCommandAlert('参数组保存', message)
  157. }
  158. return {
  159. count: 0
  160. }
  161. }
  162. }
  163. async function syncFromStorageAccessCodeInfo(options = {}) {
  164. const result = await storageAccessService.syncCodeInfo(options)
  165. if (!result || !result.ok) return result
  166. setStorageCodeInfo(result.codeInfo)
  167. const merged = mergeImportedGroupsIntoState(result.importedGroups || [], {
  168. preserveExistingRemarks: true,
  169. preserveExistingPollEnabled: true,
  170. preserveExistingStructLayout: true
  171. })
  172. return {
  173. ...result,
  174. addedGroups: merged.addedGroupCount,
  175. addedRegisters: merged.addedRegisterCount,
  176. updatedGroups: merged.updatedGroupCount,
  177. updatedRegisters: merged.updatedRegisterCount
  178. }
  179. }
  180. function addGroupFromConfig(config = {}) {
  181. const protocolMode = getActiveProtocolMode()
  182. let groupConfig
  183. try {
  184. groupConfig = normalizeGroupConfig({
  185. ...(isStorageAccessProtocolMode(protocolMode) ? { addressUnit: 'byte', sourceMemoryArea: config.sourceMemoryArea || 'XDATA' } : {}),
  186. ...config
  187. })
  188. } catch (error) {
  189. transport.showCommandAlert('参数组添加', error.message || '寄存器组配置无效')
  190. return null
  191. }
  192. if (isGroupAddressRangeOverflow(groupConfig, protocolMode)) {
  193. transport.showCommandAlert('参数组添加', getAddressOverflowText(protocolMode))
  194. return null
  195. }
  196. const registers = Array.isArray(config.registers) ? config.registers : []
  197. const group = normalizeGroup({
  198. ...groupConfig,
  199. layout: config.layout,
  200. ...(registers.length ? { registers } : {}),
  201. expanded: false
  202. })
  203. if (group.addressOverflow) {
  204. transport.showCommandAlert('参数组添加', getAddressOverflowText(protocolMode))
  205. return null
  206. }
  207. setGroups(getGroups().concat(group))
  208. return group
  209. }
  210. function updateGroupConfig(groupId, config = {}) {
  211. const protocolMode = getActiveProtocolMode()
  212. const group = findGroup(groupId)
  213. if (!group) return null
  214. let nextConfig
  215. try {
  216. nextConfig = normalizeGroupConfig({
  217. ...group,
  218. ...config
  219. })
  220. } catch (error) {
  221. transport.showCommandAlert('参数组更新', error.message || '寄存器组配置无效')
  222. return null
  223. }
  224. if (isGroupAddressRangeOverflow(nextConfig, protocolMode)) {
  225. transport.showCommandAlert('参数组更新', getAddressOverflowText(protocolMode))
  226. return null
  227. }
  228. const registers = Array.isArray(config.registers) ? config.registers : group.registers
  229. const updatedGroup = normalizeGroup({
  230. ...group,
  231. ...nextConfig,
  232. registers
  233. })
  234. if (updatedGroup.addressOverflow) {
  235. transport.showCommandAlert('参数组更新', getAddressOverflowText(protocolMode))
  236. return null
  237. }
  238. setGroups(getGroups().map((item) => (
  239. item.id === groupId ? updatedGroup : item
  240. )))
  241. return updatedGroup
  242. }
  243. function setGroupExpanded(groupId, expanded) {
  244. updateGroups((group) => group.id === groupId
  245. ? {
  246. ...group,
  247. deleteVisible: false,
  248. expanded
  249. }
  250. : group)
  251. }
  252. function setGroupDeleteVisible(groupId, deleteVisible) {
  253. updateGroups((group) => group.id === groupId
  254. ? {
  255. ...group,
  256. deleteVisible
  257. }
  258. : group)
  259. }
  260. function removeGroup(groupId) {
  261. setGroups(getGroups().filter((group) => group.id !== groupId))
  262. }
  263. function reorderRegister(groupId, fromIndex, toIndex) {
  264. const group = findGroup(groupId)
  265. if (!group) return null
  266. if (group.isStructLayout) return group
  267. const registers = group.registers.slice()
  268. const sourceIndex = Number(fromIndex)
  269. const targetIndex = Number(toIndex)
  270. if (!Number.isInteger(sourceIndex) || !Number.isInteger(targetIndex)) return null
  271. if (sourceIndex < 0 || sourceIndex >= registers.length) return null
  272. const safeTargetIndex = Math.min(Math.max(targetIndex, 0), registers.length - 1)
  273. if (safeTargetIndex === sourceIndex) return group
  274. const moved = registers.splice(sourceIndex, 1)[0]
  275. registers.splice(safeTargetIndex, 0, moved)
  276. const updatedGroup = normalizeGroup({
  277. ...group,
  278. quantity: registers.length,
  279. registers
  280. })
  281. setGroups(getGroups().map((item) => (
  282. item.id === groupId ? updatedGroup : item
  283. )))
  284. return updatedGroup
  285. }
  286. function updateRegister(groupId, registerIndex, changedData) {
  287. updateGroups((group) => {
  288. if (group.id !== groupId) return group
  289. const shouldResetReadState = Object.prototype.hasOwnProperty.call(changedData, 'dataType')
  290. || Object.prototype.hasOwnProperty.call(changedData, 'textByteLength')
  291. return {
  292. ...group,
  293. registers: group.registers.map((register, currentIndex) => (
  294. currentIndex === registerIndex
  295. ? {
  296. ...register,
  297. ...(shouldResetReadState ? { rawBytes: [], rawValue: null, rawWords: [] } : {}),
  298. ...changedData
  299. }
  300. : register
  301. ))
  302. }
  303. })
  304. }
  305. function updateRegisterValue(groupId, registerIndex, value) {
  306. updateRegister(groupId, registerIndex, {
  307. inputValue: value,
  308. isDirty: true
  309. })
  310. }
  311. function validateRegisterInputValue(groupId, registerIndex, value) {
  312. const group = findGroup(groupId)
  313. if (!group) return false
  314. const register = group.registers[registerIndex]
  315. if (!register) return false
  316. return validateRegisterValue(register, value)
  317. }
  318. async function readGroup(groupId, options = {}) {
  319. const protocolMode = options.protocolMode || getActiveProtocolMode()
  320. const group = findGroup(groupId, protocolMode)
  321. if (!group) return false
  322. if (group.addressOverflow) {
  323. transport.showCommandAlert('参数组读取', getAddressOverflowText(protocolMode))
  324. return false
  325. }
  326. const readResult = await parameterGroupIo.readGroup(group, {
  327. ...options,
  328. useStorageAccess: isStorageAccessProtocolMode(protocolMode)
  329. })
  330. if (!readResult) return false
  331. updateGroups((item) => {
  332. if (item.id !== groupId) return item
  333. return readResult.applyToGroup(item)
  334. }, {
  335. protocolMode
  336. })
  337. return true
  338. }
  339. async function writeRegister(groupId, registerIndex) {
  340. const protocolMode = getActiveProtocolMode()
  341. const group = findGroup(groupId, protocolMode)
  342. const register = group && group.registers ? group.registers[registerIndex] : null
  343. if (!group || !register) return false
  344. if (!group.writable) {
  345. transport.showCommandAlert('参数组写入', '当前变量为只读')
  346. return false
  347. }
  348. if (group.addressOverflow) {
  349. transport.showCommandAlert('参数组写入', getAddressOverflowText(protocolMode))
  350. return false
  351. }
  352. if (isUnconfiguredRegister(register)) {
  353. transport.showCommandAlert('参数组写入', `${getUnconfiguredRegisterName(register, registerIndex)} 需要先配置数据类型`)
  354. return false
  355. }
  356. const writeResult = await parameterGroupIo.writeRegister(group, registerIndex, {
  357. useStorageAccess: isStorageAccessProtocolMode(protocolMode)
  358. })
  359. if (!writeResult) return false
  360. updateGroups((item) => {
  361. if (item.id !== groupId) return item
  362. return writeResult.applyToGroup(item)
  363. }, {
  364. protocolMode
  365. })
  366. return true
  367. }
  368. async function writeGroup(groupId, options = {}) {
  369. const protocolMode = options.protocolMode || getActiveProtocolMode()
  370. const group = findGroup(groupId, protocolMode)
  371. if (!group) return false
  372. if (!group.writable) {
  373. transport.showCommandAlert('参数组写入', '当前寄存器组为只读')
  374. return false
  375. }
  376. if (group.addressOverflow) {
  377. transport.showCommandAlert('参数组写入', getAddressOverflowText(protocolMode))
  378. return false
  379. }
  380. const unconfigured = findUnconfiguredRegister(group)
  381. if (unconfigured) {
  382. transport.showCommandAlert('参数组写入', `${getUnconfiguredRegisterName(unconfigured.register, unconfigured.index)} 需要先配置数据类型`)
  383. return false
  384. }
  385. const writeResult = await parameterGroupIo.writeGroup(group, {
  386. ...options,
  387. useStorageAccess: isStorageAccessProtocolMode(protocolMode)
  388. })
  389. if (!writeResult) return false
  390. updateGroups((item) => {
  391. if (item.id !== groupId) return item
  392. return writeResult.applyToGroup(item)
  393. }, {
  394. protocolMode
  395. })
  396. return true
  397. }
  398. module.exports = {
  399. DATA_TYPE_OPTIONS,
  400. REGISTER_TYPE_OPTIONS,
  401. addGroupFromConfig,
  402. clearStorageAccessGroups,
  403. completeStructInstanceGroups,
  404. completeStructInstanceGroupsWithStructFile,
  405. completeStructInstanceGroupsWithStructSource,
  406. getState,
  407. importJsonFromMessageFile,
  408. init: initParameterGroups,
  409. mergeImportedGroups: mergeImportedGroupsIntoState,
  410. parseStructDefinition,
  411. readGroup,
  412. removeGroup,
  413. reorderRegister,
  414. saveJsonToChat,
  415. setGroupDeleteVisible,
  416. setGroupExpanded,
  417. subscribe,
  418. syncFromStorageAccessCodeInfo,
  419. updateGroupConfig,
  420. updateRegister,
  421. updateRegisterValue,
  422. validateRegisterInputValue,
  423. writeRegister,
  424. writeGroup
  425. }