params.js 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015
  1. const {
  2. controlService,
  3. createGenericGroupConfig,
  4. createGenericGroupDialogState,
  5. createGenericModbusDialogState,
  6. createGenericRegisterChangedData,
  7. createGenericRegisterDialogState,
  8. findGenericGroup,
  9. findGenericRegister,
  10. getActiveGenericGroup,
  11. getCombinedGroupKeys,
  12. getCombinedGroupLabel,
  13. getControlViewState,
  14. getGenericDialogDataTypeState,
  15. getGenericOption,
  16. getGroupLabel,
  17. getPageState,
  18. getSettingsPageState,
  19. getVisiblePageState,
  20. hasWritableGroupChanges,
  21. paramsPageState,
  22. paramsService,
  23. resolveActiveParamView,
  24. syncService
  25. } = require('../../features/motor-control/index.js')
  26. const {
  27. createGenericModbusPoller,
  28. genericModbusService
  29. } = require('../../features/generic-modbus/index.js')
  30. const settingsService = require('../../store/settings-store.js')
  31. const themeService = require('../../store/theme-store.js')
  32. const {
  33. createPageToast
  34. } = require('../../utils/page-toast.js')
  35. const GENERIC_REGISTER_DRAG_THRESHOLD_PX = 12
  36. const GENERIC_REGISTER_ROW_HEIGHT_RPX = 112
  37. function clampIndex(value, min, max) {
  38. return Math.min(Math.max(value, min), max)
  39. }
  40. function getWindowWidth() {
  41. try {
  42. if (typeof wx !== 'undefined' && wx && typeof wx.getWindowInfo === 'function') {
  43. const info = wx.getWindowInfo()
  44. if (info && Number.isFinite(info.windowWidth)) return info.windowWidth
  45. }
  46. } catch (error) {}
  47. try {
  48. if (typeof wx !== 'undefined' && wx && typeof wx.getSystemInfoSync === 'function') {
  49. const info = wx.getSystemInfoSync()
  50. if (info && Number.isFinite(info.windowWidth)) return info.windowWidth
  51. }
  52. } catch (error) {}
  53. return 375
  54. }
  55. function rpxToPx(rpx, windowWidth) {
  56. return Math.round(Number(rpx || 0) * Number(windowWidth || 375) / 750)
  57. }
  58. function getFallbackDragRowOffsetPx(windowWidth) {
  59. return Math.max(44, rpxToPx(GENERIC_REGISTER_ROW_HEIGHT_RPX, windowWidth))
  60. }
  61. function resolveDragTargetIndex(drag, currentY, totalCount) {
  62. if (!drag || !Number.isInteger(totalCount) || totalCount <= 0) return 0
  63. const sourceIndex = clampIndex(Number(drag.index) || 0, 0, totalCount - 1)
  64. const rowCenters = Array.isArray(drag.rowCenters) ? drag.rowCenters : []
  65. const sourceCenter = Number(rowCenters[sourceIndex])
  66. if (rowCenters.length === totalCount && Number.isFinite(sourceCenter)) {
  67. const currentCenter = sourceCenter + (Number(currentY) - Number(drag.startY || currentY))
  68. let targetIndex = sourceIndex
  69. if (currentCenter >= sourceCenter) {
  70. for (let index = sourceIndex + 1; index < totalCount; index += 1) {
  71. if (currentCenter > Number(rowCenters[index])) targetIndex = index
  72. }
  73. } else {
  74. for (let index = sourceIndex - 1; index >= 0; index -= 1) {
  75. if (currentCenter < Number(rowCenters[index])) targetIndex = index
  76. }
  77. }
  78. return clampIndex(targetIndex, 0, totalCount - 1)
  79. }
  80. const rowOffset = Math.max(1, Number(drag.rowOffset) || 1)
  81. const step = Math.round((Number(currentY) - Number(drag.startY || currentY)) / rowOffset)
  82. return clampIndex(sourceIndex + step, 0, totalCount - 1)
  83. }
  84. function buildActiveGenericRegisterRows(group, dragState) {
  85. if (!group || !Array.isArray(group.registers)) return []
  86. const drag = dragState && dragState.groupId === group.id ? dragState : null
  87. const activeIndex = drag ? clampIndex(Number(drag.index) || 0, 0, group.registers.length - 1) : -1
  88. const targetIndex = drag ? clampIndex(Number(drag.targetIndex) || activeIndex, 0, group.registers.length - 1) : -1
  89. const rowOffset = drag ? Math.max(1, Math.round(Number(drag.rowOffset) || 0)) : 0
  90. const translateY = drag ? Math.round(Number(drag.translateY) || 0) : 0
  91. return group.registers.map((register, index) => {
  92. const row = {
  93. ...register,
  94. sourceIndex: index,
  95. dragClass: '',
  96. dragHandleClass: '',
  97. dragStyle: ''
  98. }
  99. if (!drag) return row
  100. const isActive = index === activeIndex
  101. let shiftY = 0
  102. if (drag.moved && rowOffset) {
  103. if (activeIndex < targetIndex && index > activeIndex && index <= targetIndex) {
  104. shiftY = -rowOffset
  105. } else if (activeIndex > targetIndex && index >= targetIndex && index < activeIndex) {
  106. shiftY = rowOffset
  107. }
  108. }
  109. if (isActive) {
  110. row.dragClass = drag.moved ? 'is-dragging' : 'is-drag-armed'
  111. row.dragHandleClass = drag.moved ? 'is-dragging' : 'is-drag-armed'
  112. row.dragStyle = drag.moved
  113. ? `transform: translate3d(0, ${translateY}px, 0) scale(1.02); z-index: 8;`
  114. : 'z-index: 3;'
  115. return row
  116. }
  117. if (shiftY) {
  118. row.dragClass = shiftY > 0 ? 'is-shift-down' : 'is-shift-up'
  119. row.dragStyle = `transform: translate3d(0, ${shiftY}px, 0);`
  120. }
  121. return row
  122. })
  123. }
  124. Page({
  125. data: {
  126. ...getPageState(),
  127. activeParamView: '',
  128. activeGenericGroupId: '',
  129. activeGenericRegisterRows: [],
  130. genericModbusDialog: createGenericModbusDialogState()
  131. },
  132. onTabItemTap() {
  133. this.backToParamsHome()
  134. },
  135. onLoad() {
  136. this.pageToast = createPageToast(this, this.data)
  137. this.genericModbusPoller = createGenericModbusPoller(() => this.data)
  138. this.genericModbusTouchStarts = {}
  139. this.genericWindowWidth = getWindowWidth()
  140. controlService.init()
  141. genericModbusService.init()
  142. themeService.init()
  143. settingsService.init()
  144. this.unsubscribeSync = syncService.subscribe((syncState) => {
  145. if (!syncState.syncVersion || syncState.syncVersion === this.data.syncVersion) return
  146. const nextState = getPageState(
  147. syncService.getParamsSnapshot(),
  148. controlService.getState()
  149. )
  150. this.setData(nextState)
  151. this.pageToast.showFromState(nextState)
  152. })
  153. this.unsubscribeControl = controlService.subscribe((controlState) => {
  154. const nextState = getControlViewState(controlState)
  155. this.setData(nextState)
  156. this.pageToast.showFromState(nextState)
  157. if (nextState.connectedDevice) {
  158. this.scheduleVisibleGenericAutoReads()
  159. } else {
  160. this.clearGenericAutoTimers()
  161. }
  162. })
  163. this.unsubscribeTheme = themeService.subscribe((themeState) => {
  164. this.setData(themeState)
  165. })
  166. this.unsubscribeGenericModbus = genericModbusService.subscribe((genericState) => {
  167. const activeGenericGroup = getActiveGenericGroup(genericState.genericModbusGroups, this.data.activeGenericGroupId)
  168. this.setData({
  169. ...genericState,
  170. activeGenericGroup,
  171. activeGenericRegisterRows: buildActiveGenericRegisterRows(activeGenericGroup, this.genericModbusRegisterDrag),
  172. activeParamView: this.data.activeParamView === 'genericModbusGroup' && !activeGenericGroup
  173. ? 'genericModbus'
  174. : this.data.activeParamView
  175. })
  176. })
  177. this.unsubscribeSettings = settingsService.subscribe((settingsState) => {
  178. const nextState = getSettingsPageState(this.data, settingsState)
  179. const activeParamView = nextState.activeParamView
  180. const activeGenericGroup = getActiveGenericGroup(this.data.genericModbusGroups, this.data.activeGenericGroupId)
  181. const safeActiveView = activeParamView === 'genericModbusGroup' && !activeGenericGroup
  182. ? 'genericModbus'
  183. : activeParamView
  184. this.setData({
  185. ...nextState,
  186. activeParamView: safeActiveView,
  187. activeGenericGroup,
  188. activeGenericRegisterRows: buildActiveGenericRegisterRows(activeGenericGroup, this.genericModbusRegisterDrag)
  189. })
  190. if (safeActiveView === 'genericModbus' || safeActiveView === 'genericModbusGroup') {
  191. setTimeout(() => this.scheduleVisibleGenericAutoReads(), 0)
  192. } else {
  193. this.clearGenericAutoTimers()
  194. }
  195. })
  196. },
  197. onShow() {
  198. if (this.pageToast) {
  199. this.pageToast.setActive(true)
  200. }
  201. controlService.syncSharedInputs()
  202. const pageState = getVisiblePageState(this.data)
  203. this.setData({
  204. ...pageState,
  205. activeGenericGroup: getActiveGenericGroup(pageState.genericModbusGroups, this.data.activeGenericGroupId),
  206. activeGenericRegisterRows: buildActiveGenericRegisterRows(
  207. getActiveGenericGroup(pageState.genericModbusGroups, this.data.activeGenericGroupId),
  208. this.genericModbusRegisterDrag
  209. )
  210. })
  211. this.pageToast.showFromState(pageState)
  212. this.scheduleVisibleGenericAutoReads()
  213. },
  214. onHide() {
  215. if (this.pageToast) {
  216. this.pageToast.setActive(false)
  217. }
  218. this.clearGenericRegisterDrag()
  219. this.clearGenericAutoTimers()
  220. },
  221. onUnload() {
  222. if (this.pageToast) {
  223. this.pageToast.destroy()
  224. this.pageToast = null
  225. }
  226. if (this.unsubscribeSync) {
  227. this.unsubscribeSync()
  228. this.unsubscribeSync = null
  229. }
  230. if (this.unsubscribeControl) {
  231. this.unsubscribeControl()
  232. this.unsubscribeControl = null
  233. }
  234. if (this.unsubscribeTheme) {
  235. this.unsubscribeTheme()
  236. this.unsubscribeTheme = null
  237. }
  238. if (this.unsubscribeGenericModbus) {
  239. this.unsubscribeGenericModbus()
  240. this.unsubscribeGenericModbus = null
  241. }
  242. if (this.unsubscribeSettings) {
  243. this.unsubscribeSettings()
  244. this.unsubscribeSettings = null
  245. }
  246. this.clearGenericAutoTimers()
  247. },
  248. async onGroupRead(event) {
  249. if (!this.data.connectedDevice) return
  250. const groupKey = event.currentTarget.dataset.group
  251. const nextState = await paramsService.readGroup(this.data, groupKey)
  252. if (nextState) {
  253. this.setData(nextState)
  254. if (this.pageToast) this.pageToast.show(`${getGroupLabel(groupKey)}读取完成`)
  255. }
  256. },
  257. async onGroupWrite(event) {
  258. if (!this.data.connectedDevice) return
  259. const groupKey = event.currentTarget.dataset.group
  260. const written = await paramsService.writeGroup(this.data, groupKey)
  261. if (written) {
  262. this.setData(paramsPageState.clearGroupDirty(this.data, groupKey))
  263. if (this.pageToast) this.pageToast.show(`${getGroupLabel(groupKey)}写入完成`)
  264. }
  265. },
  266. async readCombinedGroups(viewKey) {
  267. if (!this.data.connectedDevice) return false
  268. const groupKeys = getCombinedGroupKeys(viewKey)
  269. let nextState = this.data
  270. for (const groupKey of groupKeys) {
  271. const updatedState = await paramsService.readGroup(nextState, groupKey)
  272. if (!updatedState) {
  273. if (nextState !== this.data) this.setData(nextState)
  274. return false
  275. }
  276. nextState = updatedState
  277. this.setData(nextState)
  278. }
  279. if (this.pageToast) this.pageToast.show(`${getCombinedGroupLabel(viewKey)}读取完成`)
  280. return true
  281. },
  282. async writeCombinedGroups(viewKey) {
  283. if (!this.data.connectedDevice) return false
  284. const groupKeys = getCombinedGroupKeys(viewKey)
  285. let nextState = this.data
  286. let writtenAny = false
  287. for (const groupKey of groupKeys) {
  288. if (!hasWritableGroupChanges(nextState, groupKey)) continue
  289. const written = await paramsService.writeGroup(nextState, groupKey)
  290. if (!written) {
  291. if (writtenAny) this.setData(nextState)
  292. return false
  293. }
  294. nextState = paramsPageState.clearGroupDirty(nextState, groupKey)
  295. writtenAny = true
  296. this.setData(nextState)
  297. }
  298. if (!writtenAny) {
  299. if (this.pageToast) this.pageToast.show('暂无需要写入的参数')
  300. return false
  301. }
  302. if (this.pageToast) this.pageToast.show(`${getCombinedGroupLabel(viewKey)}写入完成`)
  303. return true
  304. },
  305. readStartupManagement() {
  306. this.readCombinedGroups('startup')
  307. },
  308. writeStartupManagement() {
  309. this.writeCombinedGroups('startup')
  310. },
  311. readSpeedManagement() {
  312. this.readCombinedGroups('speed')
  313. },
  314. writeSpeedManagement() {
  315. this.writeCombinedGroups('speed')
  316. },
  317. onEstimatorUpdate() {
  318. this.setData(paramsPageState.refreshState(this.data))
  319. if (this.pageToast) this.pageToast.show('估算器参数更新完成')
  320. },
  321. openParamView(event) {
  322. if (this.pageToast) this.pageToast.clear()
  323. this.closeGenericModbusDraft()
  324. this.clearGenericRegisterDrag()
  325. const activeParamView = event.currentTarget.dataset.view
  326. if (!activeParamView) return
  327. this.setData({
  328. activeParamView
  329. })
  330. if (activeParamView === 'genericModbus') {
  331. setTimeout(() => this.scheduleVisibleGenericAutoReads(), 0)
  332. }
  333. },
  334. openGenericModbusGroup(event) {
  335. const groupId = event.currentTarget.dataset.groupId
  336. const group = findGenericGroup(this.data.genericModbusGroups, groupId)
  337. if (!group) return
  338. if (this.pageToast) this.pageToast.clear()
  339. this.closeGenericModbusDraft()
  340. this.setData({
  341. activeGenericGroup: group,
  342. activeGenericGroupId: groupId,
  343. activeParamView: 'genericModbusGroup',
  344. activeGenericRegisterRows: buildActiveGenericRegisterRows(group, this.genericModbusRegisterDrag)
  345. })
  346. },
  347. backToParamsHome() {
  348. if (this.pageToast) this.pageToast.clear()
  349. this.closeGenericModbusDraft()
  350. this.clearGenericRegisterDrag()
  351. this.clearGenericAutoTimers()
  352. const activeParamView = resolveActiveParamView('', this.data)
  353. this.setData({
  354. activeGenericGroup: null,
  355. activeGenericGroupId: '',
  356. activeParamView,
  357. activeGenericRegisterRows: []
  358. })
  359. if (activeParamView === 'genericModbus') {
  360. setTimeout(() => this.scheduleVisibleGenericAutoReads(), 0)
  361. }
  362. },
  363. onMotorParameterInput(event) {
  364. controlService.updateMotorParameterInput(
  365. Number(event.currentTarget.dataset.index),
  366. event.detail.value
  367. )
  368. },
  369. onMotorParameterBlur(event) {
  370. controlService.updateMotorParameterBlur(
  371. Number(event.currentTarget.dataset.index),
  372. event.detail.value
  373. )
  374. },
  375. writeMotorParameters() {
  376. if (!this.data.connectedDevice) return
  377. controlService.writeMotorParameters()
  378. },
  379. async readDriverPageParameters() {
  380. if (!this.data.connectedDevice) return
  381. await controlService.readDriverParameters()
  382. await controlService.readMotorParameters()
  383. },
  384. readStatus() {
  385. if (!this.data.canReadStatus) return
  386. controlService.readStatus()
  387. },
  388. onInputChange(event) {
  389. this.setData(paramsPageState.applyParameterInput(
  390. this.data,
  391. Number(event.currentTarget.dataset.index),
  392. event.detail.value
  393. ))
  394. },
  395. onAtoBandwidthInput(event) {
  396. this.setData(paramsPageState.applyAtoBandwidthInput(
  397. this.data,
  398. Number(event.currentTarget.dataset.index),
  399. event.detail.value
  400. ))
  401. },
  402. onDqGainInput(event) {
  403. this.setData(paramsPageState.applyDqGainInput(
  404. this.data,
  405. Number(event.currentTarget.dataset.index),
  406. event.detail.value
  407. ))
  408. },
  409. onSpeedLoopExtraInput(event) {
  410. this.setData(paramsPageState.applySpeedLoopExtraInput(
  411. this.data,
  412. Number(event.currentTarget.dataset.index),
  413. event.detail.value
  414. ))
  415. },
  416. onOilParameterInput(event) {
  417. this.setData(paramsPageState.applyOilParameterInput(
  418. this.data,
  419. Number(event.currentTarget.dataset.index),
  420. event.detail.value
  421. ))
  422. },
  423. onPrepositionParameterInput(event) {
  424. this.setData(paramsPageState.applyPrepositionParameterInput(
  425. this.data,
  426. Number(event.currentTarget.dataset.index),
  427. event.detail.value
  428. ))
  429. },
  430. onInputBlur(event) {
  431. this.setData(paramsPageState.applyInputBlur(
  432. this.data,
  433. event.currentTarget.dataset.inputGroup,
  434. Number(event.currentTarget.dataset.index),
  435. event.detail.value
  436. ))
  437. },
  438. onTailwindSwitchChange(event) {
  439. if (!this.data.connectedDevice) return
  440. const index = Number(event.currentTarget.dataset.index)
  441. const nextState = paramsPageState.applyTailwindSwitchChange(
  442. this.data,
  443. index,
  444. !!event.detail.value
  445. )
  446. this.setData(nextState)
  447. paramsService.writeSwitchRegister(nextState.tailwindSwitchRegisters[index]).then((written) => {
  448. if (written) {
  449. this.setData(paramsPageState.clearTailwindSwitchDirty(this.data, index))
  450. if (this.pageToast) this.pageToast.show(`${nextState.tailwindSwitchRegisters[index].name}写入完成`)
  451. }
  452. })
  453. },
  454. onProtectionSwitchChange(event) {
  455. if (!this.data.connectedDevice) return
  456. const index = Number(event.currentTarget.dataset.index)
  457. const nextState = paramsPageState.applyProtectionSwitchChange(
  458. this.data,
  459. index,
  460. !!event.detail.value
  461. )
  462. this.setData(nextState)
  463. paramsService.writeSwitchRegister(nextState.protectionSwitchRegisters[index]).then((written) => {
  464. if (written) {
  465. this.setData(paramsPageState.clearProtectionSwitchDirty(this.data, index))
  466. if (this.pageToast) this.pageToast.show(`${nextState.protectionSwitchRegisters[index].name}写入完成`)
  467. }
  468. })
  469. },
  470. onProtectionInputChange(event) {
  471. this.setData(paramsPageState.applyProtectionInput(
  472. this.data,
  473. Number(event.currentTarget.dataset.index),
  474. event.detail.value
  475. ))
  476. },
  477. noop() {},
  478. updateGenericModbusDialog(changedData) {
  479. this.setData({
  480. genericModbusDialog: {
  481. ...this.data.genericModbusDialog,
  482. ...changedData
  483. }
  484. })
  485. },
  486. openGenericModbusDraft(event) {
  487. const groupId = event && event.currentTarget && event.currentTarget.dataset
  488. ? event.currentTarget.dataset.groupId
  489. : ''
  490. const group = groupId ? findGenericGroup(this.data.genericModbusGroups, groupId) : null
  491. this.updateGenericModbusDialog(createGenericGroupDialogState(group))
  492. },
  493. closeGenericModbusDraft() {
  494. this.genericModbusGroupLongPressGuard = ''
  495. this.genericModbusRegisterLongPressGuard = ''
  496. this.updateGenericModbusDialog(createGenericModbusDialogState())
  497. },
  498. onGenericDraftInput(event) {
  499. const field = event.currentTarget.dataset.field
  500. if (!field) return
  501. const value = event.detail.value
  502. this.updateGenericModbusDialog({
  503. [field]: value,
  504. ...(field === 'structDefinition' ? {
  505. parsedStructRegisters: [],
  506. structParsedSummary: ''
  507. } : {})
  508. })
  509. },
  510. parseGenericStructDefinition() {
  511. const dialog = this.data.genericModbusDialog || createGenericModbusDialogState()
  512. const sourceText = dialog.structDefinition || ''
  513. if (!sourceText.trim()) {
  514. if (this.pageToast) this.pageToast.show('请先粘贴结构体定义', 'error')
  515. return
  516. }
  517. const registerType = getGenericOption(this.data.genericModbusRegisterTypeOptions, dialog.registerTypeIndex)
  518. if (registerType.key === 'coil' || registerType.key === 'discrete') {
  519. if (this.pageToast) this.pageToast.show('结构体解析仅支持寄存器类型', 'error')
  520. return
  521. }
  522. try {
  523. const parsed = genericModbusService.parseStructDefinition(sourceText)
  524. const inputRegisterIndex = Math.max(
  525. 0,
  526. this.data.genericModbusRegisterTypeOptions.findIndex((item) => item.key === 'input')
  527. )
  528. const inputRegisterType = getGenericOption(this.data.genericModbusRegisterTypeOptions, inputRegisterIndex)
  529. this.updateGenericModbusDialog({
  530. groupName: parsed.name || dialog.groupName,
  531. parsedStructRegisters: parsed.registers,
  532. quantity: String(parsed.registers.length),
  533. registerTypeIndex: inputRegisterIndex,
  534. registerTypeText: inputRegisterType.label || '',
  535. structParsedSummary: `${parsed.structName} · ${parsed.registers.length} 个字段`
  536. })
  537. if (this.pageToast) this.pageToast.show('结构体解析完成')
  538. } catch (error) {
  539. if (this.pageToast) this.pageToast.show(error.message || '结构体解析失败', 'error')
  540. }
  541. },
  542. onGenericDraftTypeChange(event) {
  543. const registerTypeIndex = Number(event.detail.value)
  544. const registerType = getGenericOption(this.data.genericModbusRegisterTypeOptions, registerTypeIndex)
  545. const clearParsedStruct = registerType.key === 'coil' || registerType.key === 'discrete'
  546. this.updateGenericModbusDialog({
  547. ...(clearParsedStruct ? {
  548. parsedStructRegisters: [],
  549. structParsedSummary: ''
  550. } : {}),
  551. registerTypeIndex,
  552. registerTypeText: registerType.label || ''
  553. })
  554. },
  555. onGenericDialogDataTypeChange(event) {
  556. const dataTypeIndex = Number(event.detail.value)
  557. this.updateGenericModbusDialog(getGenericDialogDataTypeState(
  558. this.data.genericModbusDialog,
  559. this.data.genericModbusDataTypeOptions,
  560. dataTypeIndex
  561. ))
  562. },
  563. openGenericGroupEdit(event) {
  564. const groupId = event.currentTarget.dataset.groupId
  565. const group = findGenericGroup(this.data.genericModbusGroups, groupId)
  566. if (!group) return
  567. this.genericModbusGroupLongPressGuard = groupId
  568. this.updateGenericModbusDialog(createGenericGroupDialogState(group))
  569. },
  570. openGenericRegisterInfo(event) {
  571. const groupId = event.currentTarget.dataset.groupId
  572. const registerIndex = Number(event.currentTarget.dataset.index)
  573. const registerKey = `${groupId}:${registerIndex}`
  574. if (this.genericModbusRegisterLongPressGuard === registerKey) {
  575. this.genericModbusRegisterLongPressGuard = ''
  576. return
  577. }
  578. const {
  579. group,
  580. register
  581. } = findGenericRegister(this.data.genericModbusGroups, groupId, registerIndex)
  582. if (!register) return
  583. this.updateGenericModbusDialog(createGenericRegisterDialogState('viewRegister', group, register, registerIndex))
  584. },
  585. openGenericRegisterEdit(event) {
  586. const groupId = event.currentTarget.dataset.groupId
  587. const registerIndex = Number(event.currentTarget.dataset.index)
  588. const {
  589. group,
  590. register
  591. } = findGenericRegister(this.data.genericModbusGroups, groupId, registerIndex)
  592. if (!register) return
  593. this.genericModbusRegisterLongPressGuard = `${groupId}:${registerIndex}`
  594. this.updateGenericModbusDialog(createGenericRegisterDialogState('editRegister', group, register, registerIndex))
  595. },
  596. async confirmGenericModbusDialog() {
  597. const dialog = this.data.genericModbusDialog || createGenericModbusDialogState()
  598. const mode = dialog.mode
  599. if (mode === 'createGroup') {
  600. const group = genericModbusService.addGroupFromConfig(createGenericGroupConfig(dialog))
  601. if (group) {
  602. if (this.pageToast) this.pageToast.show(`${group.name}已添加`)
  603. this.closeGenericModbusDraft()
  604. this.setData({
  605. activeGenericGroup: group,
  606. activeGenericGroupId: group.id,
  607. activeParamView: 'genericModbusGroup',
  608. activeGenericRegisterRows: buildActiveGenericRegisterRows(group, this.genericModbusRegisterDrag)
  609. })
  610. }
  611. return
  612. }
  613. if (mode === 'editGroup') {
  614. const group = genericModbusService.updateGroupConfig(dialog.groupId, createGenericGroupConfig(dialog))
  615. if (group) {
  616. if (this.pageToast) this.pageToast.show(`${group.name}已更新`)
  617. this.closeGenericModbusDraft()
  618. if (this.data.activeGenericGroupId === group.id) {
  619. this.setData({
  620. activeGenericGroup: group,
  621. activeGenericRegisterRows: buildActiveGenericRegisterRows(group, this.genericModbusRegisterDrag)
  622. })
  623. }
  624. }
  625. return
  626. }
  627. if (mode === 'editRegister') {
  628. const changedData = createGenericRegisterChangedData(dialog, this.data.genericModbusDataTypeOptions)
  629. genericModbusService.updateRegister(dialog.groupId, dialog.registerIndex, changedData)
  630. if (this.pageToast) this.pageToast.show(`${dialog.name || '寄存器'}已更新`)
  631. this.closeGenericModbusDraft()
  632. }
  633. },
  634. async importGenericModbusJson() {
  635. const count = await genericModbusService.importJsonFromMessageFile()
  636. if (count && this.pageToast) this.pageToast.show(`已导入 ${count} 个寄存器组`)
  637. },
  638. async saveGenericModbusJson() {
  639. const count = await genericModbusService.saveJsonToChat()
  640. if (count && this.pageToast) this.pageToast.show(`已保存 ${count} 个寄存器组`)
  641. },
  642. toggleGenericModbusGroup(event) {
  643. const groupId = event.currentTarget.dataset.groupId
  644. if (this.genericModbusGroupLongPressGuard === groupId) {
  645. this.genericModbusGroupLongPressGuard = ''
  646. return
  647. }
  648. const group = findGenericGroup(this.data.genericModbusGroups, groupId)
  649. if (!group) return
  650. genericModbusService.setGroupExpanded(groupId, !group.expanded)
  651. },
  652. onGenericRegisterValueInput(event) {
  653. genericModbusService.updateRegisterValue(
  654. event.currentTarget.dataset.groupId,
  655. Number(event.currentTarget.dataset.index),
  656. event.detail.value
  657. )
  658. },
  659. onGenericRegisterValueBlur(event) {
  660. const groupId = event.currentTarget.dataset.groupId
  661. const registerIndex = Number(event.currentTarget.dataset.index)
  662. try {
  663. genericModbusService.validateRegisterInputValue(groupId, registerIndex, event.detail.value)
  664. } catch (error) {
  665. if (this.pageToast) this.pageToast.show(error.message || '输入值无效', 'error')
  666. }
  667. },
  668. async readGenericModbusGroup(event) {
  669. if (!this.data.connectedDevice) return
  670. const groupId = event.currentTarget.dataset.groupId
  671. const ok = await genericModbusService.readGroup(groupId, {
  672. maxPacketLength: this.data.genericModbusMaxPacketLength
  673. })
  674. if (ok && this.pageToast) this.pageToast.show('通用Modbus读取完成')
  675. },
  676. async writeGenericModbusGroup(event) {
  677. if (!this.data.connectedDevice) return
  678. const groupId = event.currentTarget.dataset.groupId
  679. const ok = await genericModbusService.writeGroup(groupId)
  680. if (ok && this.pageToast) this.pageToast.show('通用Modbus写入完成')
  681. },
  682. onGenericGroupTouchStart(event) {
  683. const groupId = event.currentTarget.dataset.groupId
  684. const touch = (event.changedTouches || [])[0]
  685. if (!groupId || !touch) return
  686. this.genericModbusTouchStarts[groupId] = touch.clientX
  687. },
  688. onGenericGroupTouchEnd(event) {
  689. const groupId = event.currentTarget.dataset.groupId
  690. const group = findGenericGroup(this.data.genericModbusGroups, groupId)
  691. const touch = (event.changedTouches || [])[0]
  692. const startX = this.genericModbusTouchStarts[groupId]
  693. if (!groupId || !group || group.expanded || !touch || !Number.isFinite(startX)) return
  694. const deltaX = touch.clientX - startX
  695. if (deltaX > 42) {
  696. genericModbusService.setGroupDeleteVisible(groupId, true)
  697. } else if (deltaX < -24) {
  698. genericModbusService.setGroupDeleteVisible(groupId, false)
  699. }
  700. },
  701. onGenericRegisterDragStart(event) {
  702. const touch = (event.changedTouches || [])[0]
  703. if (!touch) return
  704. const groupId = event.currentTarget.dataset.groupId
  705. const index = Number(event.currentTarget.dataset.index)
  706. const activeGenericGroup = findGenericGroup(this.data.genericModbusGroups, groupId)
  707. if (!groupId || !activeGenericGroup || !Number.isInteger(index)) return
  708. this.genericModbusRegisterDrag = {
  709. groupId,
  710. index,
  711. moved: false,
  712. rowCenters: [],
  713. rowOffset: getFallbackDragRowOffsetPx(this.genericWindowWidth),
  714. startY: touch.clientY,
  715. targetIndex: index,
  716. translateY: 0
  717. }
  718. if (this.data.activeGenericGroupId === groupId) {
  719. this.setData({
  720. activeGenericRegisterRows: buildActiveGenericRegisterRows(activeGenericGroup, this.genericModbusRegisterDrag)
  721. })
  722. }
  723. this.measureGenericRegisterRows(this.genericModbusRegisterDrag)
  724. },
  725. onGenericRegisterDragMove(event) {
  726. const touch = (event.changedTouches || [])[0]
  727. if (!touch || !this.genericModbusRegisterDrag) return
  728. const drag = this.genericModbusRegisterDrag
  729. const group = findGenericGroup(this.data.genericModbusGroups, drag.groupId)
  730. if (!group) return
  731. const translateY = Math.round(touch.clientY - drag.startY)
  732. const moved = Math.abs(translateY) > GENERIC_REGISTER_DRAG_THRESHOLD_PX
  733. const targetIndex = moved
  734. ? resolveDragTargetIndex(drag, touch.clientY, group.registers.length)
  735. : drag.index
  736. if (
  737. drag.translateY === translateY
  738. && drag.moved === moved
  739. && drag.targetIndex === targetIndex
  740. ) {
  741. return
  742. }
  743. drag.translateY = translateY
  744. drag.moved = moved
  745. drag.targetIndex = targetIndex
  746. if (this.data.activeGenericGroupId === group.id) {
  747. this.setData({
  748. activeGenericRegisterRows: buildActiveGenericRegisterRows(group, drag)
  749. })
  750. }
  751. },
  752. onGenericRegisterDragEnd(event) {
  753. const drag = this.genericModbusRegisterDrag
  754. this.genericModbusRegisterDrag = null
  755. if (!drag || !drag.groupId) return
  756. const group = findGenericGroup(this.data.genericModbusGroups, drag.groupId)
  757. if (!group) return
  758. if (this.data.activeGenericGroupId === group.id) {
  759. this.setData({
  760. activeGenericRegisterRows: buildActiveGenericRegisterRows(group, null)
  761. })
  762. }
  763. if (!drag.moved) return
  764. const targetIndex = clampIndex(
  765. Number(drag.targetIndex) || drag.index,
  766. 0,
  767. group.registers.length - 1
  768. )
  769. if (targetIndex === drag.index) return
  770. const updatedGroup = genericModbusService.reorderRegister(drag.groupId, drag.index, targetIndex)
  771. if (!updatedGroup) return
  772. this.genericModbusRegisterLongPressGuard = `${drag.groupId}:${targetIndex}`
  773. setTimeout(() => {
  774. if (this.genericModbusRegisterLongPressGuard === `${drag.groupId}:${targetIndex}`) {
  775. this.genericModbusRegisterLongPressGuard = ''
  776. }
  777. }, 260)
  778. if (this.data.activeGenericGroupId === updatedGroup.id) {
  779. this.setData({
  780. activeGenericGroup: updatedGroup,
  781. activeGenericRegisterRows: buildActiveGenericRegisterRows(updatedGroup, null)
  782. })
  783. }
  784. },
  785. onGenericRegisterDragCancel() {
  786. const drag = this.genericModbusRegisterDrag
  787. this.genericModbusRegisterDrag = null
  788. if (!drag || !drag.groupId) return
  789. const group = findGenericGroup(this.data.genericModbusGroups, drag.groupId)
  790. if (!group || this.data.activeGenericGroupId !== group.id) return
  791. this.setData({
  792. activeGenericRegisterRows: buildActiveGenericRegisterRows(group, null)
  793. })
  794. },
  795. deleteGenericModbusGroup(event) {
  796. const groupId = event.currentTarget.dataset.groupId
  797. this.clearGenericAutoTimer(groupId)
  798. genericModbusService.removeGroup(groupId)
  799. if (this.data.activeGenericGroupId === groupId) {
  800. this.setData({
  801. activeGenericGroup: null,
  802. activeGenericGroupId: '',
  803. activeParamView: 'genericModbus'
  804. })
  805. }
  806. if (this.pageToast) this.pageToast.show('寄存器组已删除')
  807. },
  808. clearGenericAutoTimer(groupId) {
  809. if (this.genericModbusPoller) this.genericModbusPoller.clearTimer(groupId)
  810. },
  811. clearGenericAutoTimers() {
  812. if (this.genericModbusPoller) this.genericModbusPoller.clearAll()
  813. },
  814. scheduleVisibleGenericAutoReads() {
  815. if (this.genericModbusPoller) this.genericModbusPoller.scheduleVisible()
  816. },
  817. scheduleGenericAutoPoll(delay) {
  818. if (this.genericModbusPoller) this.genericModbusPoller.schedule(delay)
  819. },
  820. clearGenericRegisterDrag() {
  821. if (!this.genericModbusRegisterDrag) return
  822. const drag = this.genericModbusRegisterDrag
  823. this.genericModbusRegisterDrag = null
  824. const group = findGenericGroup(this.data.genericModbusGroups, drag.groupId)
  825. this.setData({
  826. activeGenericRegisterRows: buildActiveGenericRegisterRows(group, null)
  827. })
  828. },
  829. measureGenericRegisterRows(dragReference) {
  830. const query = this.createSelectorQuery()
  831. query.selectAll('.generic-register-row').boundingClientRect((rects) => {
  832. if (!this.genericModbusRegisterDrag || this.genericModbusRegisterDrag !== dragReference) return
  833. if (!Array.isArray(rects) || !rects.length) return
  834. dragReference.rowCenters = rects.map((rect) => Number(rect.top || 0) + Number(rect.height || 0) / 2)
  835. dragReference.rowOffset = Math.max(
  836. 1,
  837. Math.round(Number((rects[dragReference.index] || {}).height) || dragReference.rowOffset || 0)
  838. )
  839. const group = findGenericGroup(this.data.genericModbusGroups, dragReference.groupId)
  840. if (!group || this.data.activeGenericGroupId !== group.id) return
  841. this.setData({
  842. activeGenericRegisterRows: buildActiveGenericRegisterRows(group, dragReference)
  843. })
  844. }).exec()
  845. }
  846. })