1
0

params.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807
  1. const {
  2. createGenericGroupConfig,
  3. createGenericGroupDialogState,
  4. createGenericModbusDialogState,
  5. createGenericRegisterChangedData,
  6. createGenericRegisterDialogState,
  7. findGenericGroup,
  8. findGenericRegister,
  9. getActiveGenericGroup,
  10. getGenericDialogDataTypeState,
  11. getGenericOption,
  12. getPageState,
  13. getSettingsPageState,
  14. getTransportPageState,
  15. getVisiblePageState,
  16. resolveActiveParamView
  17. } = require('../../features/private-protocol/params-view-model.js')
  18. const {
  19. createGenericModbusPoller,
  20. genericModbusService
  21. } = require('../../features/generic-modbus/index.js')
  22. const settingsService = require('../../store/settings-store.js')
  23. const themeService = require('../../store/theme-store.js')
  24. const transport = require('../../transport/ble-core.js')
  25. const {
  26. createPageToast
  27. } = require('../../utils/page-toast.js')
  28. const GENERIC_REGISTER_DRAG_THRESHOLD_PX = 12
  29. const GENERIC_REGISTER_ROW_HEIGHT_RPX = 112
  30. function clampIndex(value, min, max) {
  31. return Math.min(Math.max(value, min), max)
  32. }
  33. function getWindowWidth() {
  34. try {
  35. if (typeof wx !== 'undefined' && wx && typeof wx.getWindowInfo === 'function') {
  36. const info = wx.getWindowInfo()
  37. if (info && Number.isFinite(info.windowWidth)) return info.windowWidth
  38. }
  39. } catch (error) {}
  40. try {
  41. if (typeof wx !== 'undefined' && wx && typeof wx.getSystemInfoSync === 'function') {
  42. const info = wx.getSystemInfoSync()
  43. if (info && Number.isFinite(info.windowWidth)) return info.windowWidth
  44. }
  45. } catch (error) {}
  46. return 375
  47. }
  48. function rpxToPx(rpx, windowWidth) {
  49. return Math.round(Number(rpx || 0) * Number(windowWidth || 375) / 750)
  50. }
  51. function getFallbackDragRowOffsetPx(windowWidth) {
  52. return Math.max(44, rpxToPx(GENERIC_REGISTER_ROW_HEIGHT_RPX, windowWidth))
  53. }
  54. function resolveDragTargetIndex(drag, currentY, totalCount) {
  55. if (!drag || !Number.isInteger(totalCount) || totalCount <= 0) return 0
  56. const sourceIndex = clampIndex(Number(drag.index) || 0, 0, totalCount - 1)
  57. const rowCenters = Array.isArray(drag.rowCenters) ? drag.rowCenters : []
  58. const sourceCenter = Number(rowCenters[sourceIndex])
  59. if (rowCenters.length === totalCount && Number.isFinite(sourceCenter)) {
  60. const currentCenter = sourceCenter + (Number(currentY) - Number(drag.startY || currentY))
  61. let targetIndex = sourceIndex
  62. if (currentCenter >= sourceCenter) {
  63. for (let index = sourceIndex + 1; index < totalCount; index += 1) {
  64. if (currentCenter > Number(rowCenters[index])) targetIndex = index
  65. }
  66. } else {
  67. for (let index = sourceIndex - 1; index >= 0; index -= 1) {
  68. if (currentCenter < Number(rowCenters[index])) targetIndex = index
  69. }
  70. }
  71. return clampIndex(targetIndex, 0, totalCount - 1)
  72. }
  73. const rowOffset = Math.max(1, Number(drag.rowOffset) || 1)
  74. const step = Math.round((Number(currentY) - Number(drag.startY || currentY)) / rowOffset)
  75. return clampIndex(sourceIndex + step, 0, totalCount - 1)
  76. }
  77. function buildActiveGenericRegisterRows(group, dragState) {
  78. if (!group || !Array.isArray(group.registers)) return []
  79. const drag = dragState && dragState.groupId === group.id ? dragState : null
  80. const activeIndex = drag ? clampIndex(Number(drag.index) || 0, 0, group.registers.length - 1) : -1
  81. const targetIndex = drag ? clampIndex(Number(drag.targetIndex) || activeIndex, 0, group.registers.length - 1) : -1
  82. const rowOffset = drag ? Math.max(1, Math.round(Number(drag.rowOffset) || 0)) : 0
  83. const translateY = drag ? Math.round(Number(drag.translateY) || 0) : 0
  84. return group.registers.map((register, index) => {
  85. const row = {
  86. ...register,
  87. sourceIndex: index,
  88. dragClass: '',
  89. dragHandleClass: '',
  90. dragStyle: ''
  91. }
  92. if (!drag) return row
  93. const isActive = index === activeIndex
  94. let shiftY = 0
  95. if (drag.moved && rowOffset) {
  96. if (activeIndex < targetIndex && index > activeIndex && index <= targetIndex) {
  97. shiftY = -rowOffset
  98. } else if (activeIndex > targetIndex && index >= targetIndex && index < activeIndex) {
  99. shiftY = rowOffset
  100. }
  101. }
  102. if (isActive) {
  103. row.dragClass = drag.moved ? 'is-dragging' : 'is-drag-armed'
  104. row.dragHandleClass = drag.moved ? 'is-dragging' : 'is-drag-armed'
  105. row.dragStyle = drag.moved
  106. ? `transform: translate3d(0, ${translateY}px, 0) scale(1.02); z-index: 8;`
  107. : 'z-index: 3;'
  108. return row
  109. }
  110. if (shiftY) {
  111. row.dragClass = shiftY > 0 ? 'is-shift-down' : 'is-shift-up'
  112. row.dragStyle = `transform: translate3d(0, ${shiftY}px, 0);`
  113. }
  114. return row
  115. })
  116. }
  117. Page({
  118. data: {
  119. ...getPageState(),
  120. activeParamView: 'genericModbus',
  121. activeGenericGroupId: '',
  122. activeGenericRegisterRows: [],
  123. genericModbusDialog: createGenericModbusDialogState()
  124. },
  125. onTabItemTap() {
  126. this.backToParamsHome()
  127. },
  128. onLoad() {
  129. this.pageToast = createPageToast(this, this.data)
  130. this.genericModbusPoller = createGenericModbusPoller(() => this.data)
  131. this.genericModbusTouchStarts = {}
  132. this.genericWindowWidth = getWindowWidth()
  133. genericModbusService.init()
  134. themeService.init()
  135. settingsService.init()
  136. this.unsubscribeTheme = themeService.subscribe((themeState) => {
  137. this.setData(themeState)
  138. })
  139. this.unsubscribeTransport = transport.subscribe((transportState) => {
  140. this.setData(getTransportPageState(transportState))
  141. if (transportState.connectedDevice) {
  142. setTimeout(() => this.scheduleVisibleGenericAutoReads(), 0)
  143. } else {
  144. this.clearGenericAutoTimers()
  145. }
  146. })
  147. this.unsubscribeGenericModbus = genericModbusService.subscribe((genericState) => {
  148. const activeGenericGroup = getActiveGenericGroup(genericState.genericModbusGroups, this.data.activeGenericGroupId)
  149. this.setData({
  150. ...genericState,
  151. activeGenericGroup,
  152. activeGenericRegisterRows: buildActiveGenericRegisterRows(activeGenericGroup, this.genericModbusRegisterDrag),
  153. activeParamView: this.data.activeParamView === 'genericModbusGroup' && !activeGenericGroup
  154. ? 'genericModbus'
  155. : this.data.activeParamView
  156. })
  157. })
  158. this.unsubscribeSettings = settingsService.subscribe((settingsState) => {
  159. const nextState = getSettingsPageState(this.data, settingsState)
  160. const activeParamView = nextState.activeParamView
  161. const activeGenericGroup = getActiveGenericGroup(this.data.genericModbusGroups, this.data.activeGenericGroupId)
  162. const safeActiveView = activeParamView === 'genericModbusGroup' && !activeGenericGroup
  163. ? 'genericModbus'
  164. : activeParamView
  165. this.setData({
  166. ...nextState,
  167. activeParamView: safeActiveView,
  168. activeGenericGroup,
  169. activeGenericRegisterRows: buildActiveGenericRegisterRows(activeGenericGroup, this.genericModbusRegisterDrag)
  170. })
  171. if (safeActiveView === 'genericModbus' || safeActiveView === 'genericModbusGroup') {
  172. setTimeout(() => this.scheduleVisibleGenericAutoReads(), 0)
  173. } else {
  174. this.clearGenericAutoTimers()
  175. }
  176. })
  177. },
  178. onShow() {
  179. if (this.pageToast) {
  180. this.pageToast.setActive(true)
  181. }
  182. const pageState = getVisiblePageState(this.data)
  183. this.setData({
  184. ...pageState,
  185. activeGenericGroup: getActiveGenericGroup(pageState.genericModbusGroups, this.data.activeGenericGroupId),
  186. activeGenericRegisterRows: buildActiveGenericRegisterRows(
  187. getActiveGenericGroup(pageState.genericModbusGroups, this.data.activeGenericGroupId),
  188. this.genericModbusRegisterDrag
  189. )
  190. })
  191. this.pageToast.showFromState(pageState)
  192. this.scheduleVisibleGenericAutoReads()
  193. },
  194. onHide() {
  195. if (this.pageToast) {
  196. this.pageToast.setActive(false)
  197. }
  198. this.clearGenericRegisterDrag()
  199. this.clearGenericAutoTimers()
  200. },
  201. onUnload() {
  202. if (this.pageToast) {
  203. this.pageToast.destroy()
  204. this.pageToast = null
  205. }
  206. if (this.unsubscribeTheme) {
  207. this.unsubscribeTheme()
  208. this.unsubscribeTheme = null
  209. }
  210. if (this.unsubscribeTransport) {
  211. this.unsubscribeTransport()
  212. this.unsubscribeTransport = null
  213. }
  214. if (this.unsubscribeGenericModbus) {
  215. this.unsubscribeGenericModbus()
  216. this.unsubscribeGenericModbus = null
  217. }
  218. if (this.unsubscribeSettings) {
  219. this.unsubscribeSettings()
  220. this.unsubscribeSettings = null
  221. }
  222. this.clearGenericAutoTimers()
  223. },
  224. openParamView(event) {
  225. if (this.pageToast) this.pageToast.clear()
  226. this.closeGenericModbusDraft()
  227. this.clearGenericRegisterDrag()
  228. const activeParamView = event.currentTarget.dataset.view
  229. if (!activeParamView) return
  230. this.setData({
  231. activeParamView
  232. })
  233. if (activeParamView === 'genericModbus') {
  234. setTimeout(() => this.scheduleVisibleGenericAutoReads(), 0)
  235. }
  236. },
  237. openGenericModbusGroup(event) {
  238. const groupId = event.currentTarget.dataset.groupId
  239. const group = findGenericGroup(this.data.genericModbusGroups, groupId)
  240. if (!group) return
  241. if (this.pageToast) this.pageToast.clear()
  242. this.closeGenericModbusDraft()
  243. this.setData({
  244. activeGenericGroup: group,
  245. activeGenericGroupId: groupId,
  246. activeParamView: 'genericModbusGroup',
  247. activeGenericRegisterRows: buildActiveGenericRegisterRows(group, this.genericModbusRegisterDrag)
  248. })
  249. },
  250. backToParamsHome() {
  251. if (this.pageToast) this.pageToast.clear()
  252. this.closeGenericModbusDraft()
  253. this.clearGenericRegisterDrag()
  254. this.clearGenericAutoTimers()
  255. const activeParamView = resolveActiveParamView('', this.data)
  256. this.setData({
  257. activeGenericGroup: null,
  258. activeGenericGroupId: '',
  259. activeParamView,
  260. activeGenericRegisterRows: []
  261. })
  262. if (activeParamView === 'genericModbus') {
  263. setTimeout(() => this.scheduleVisibleGenericAutoReads(), 0)
  264. }
  265. },
  266. noop() {},
  267. updateGenericModbusDialog(changedData) {
  268. this.setData({
  269. genericModbusDialog: {
  270. ...this.data.genericModbusDialog,
  271. ...changedData
  272. }
  273. })
  274. },
  275. openGenericModbusDraft(event) {
  276. const groupId = event && event.currentTarget && event.currentTarget.dataset
  277. ? event.currentTarget.dataset.groupId
  278. : ''
  279. const group = groupId ? findGenericGroup(this.data.genericModbusGroups, groupId) : null
  280. this.updateGenericModbusDialog(createGenericGroupDialogState(group))
  281. },
  282. closeGenericModbusDraft() {
  283. this.genericModbusGroupLongPressGuard = ''
  284. this.genericModbusRegisterLongPressGuard = ''
  285. this.updateGenericModbusDialog(createGenericModbusDialogState())
  286. },
  287. onGenericDraftInput(event) {
  288. const field = event.currentTarget.dataset.field
  289. if (!field) return
  290. const value = event.detail.value
  291. this.updateGenericModbusDialog({
  292. [field]: value,
  293. ...(field === 'structDefinition' ? {
  294. parsedStructRegisters: [],
  295. structParsedSummary: ''
  296. } : {})
  297. })
  298. },
  299. parseGenericStructDefinition() {
  300. const dialog = this.data.genericModbusDialog || createGenericModbusDialogState()
  301. const sourceText = dialog.structDefinition || ''
  302. if (!sourceText.trim()) {
  303. if (this.pageToast) this.pageToast.show('请先粘贴结构体定义', 'error')
  304. return
  305. }
  306. const registerType = getGenericOption(this.data.genericModbusRegisterTypeOptions, dialog.registerTypeIndex)
  307. if (registerType.key === 'coil' || registerType.key === 'discrete') {
  308. if (this.pageToast) this.pageToast.show('结构体解析仅支持寄存器类型', 'error')
  309. return
  310. }
  311. try {
  312. const parsed = genericModbusService.parseStructDefinition(sourceText)
  313. const inputRegisterIndex = Math.max(
  314. 0,
  315. this.data.genericModbusRegisterTypeOptions.findIndex((item) => item.key === 'input')
  316. )
  317. const inputRegisterType = getGenericOption(this.data.genericModbusRegisterTypeOptions, inputRegisterIndex)
  318. this.updateGenericModbusDialog({
  319. groupName: parsed.name || dialog.groupName,
  320. parsedStructRegisters: parsed.registers,
  321. quantity: String(parsed.registers.length),
  322. registerTypeIndex: inputRegisterIndex,
  323. registerTypeText: inputRegisterType.label || '',
  324. structParsedSummary: `${parsed.structName} · ${parsed.registers.length} 个字段`
  325. })
  326. if (this.pageToast) this.pageToast.show('结构体解析完成')
  327. } catch (error) {
  328. if (this.pageToast) this.pageToast.show(error.message || '结构体解析失败', 'error')
  329. }
  330. },
  331. onGenericDraftTypeChange(event) {
  332. const registerTypeIndex = Number(event.detail.value)
  333. const registerType = getGenericOption(this.data.genericModbusRegisterTypeOptions, registerTypeIndex)
  334. const clearParsedStruct = registerType.key === 'coil' || registerType.key === 'discrete'
  335. this.updateGenericModbusDialog({
  336. ...(clearParsedStruct ? {
  337. parsedStructRegisters: [],
  338. structParsedSummary: ''
  339. } : {}),
  340. registerTypeIndex,
  341. registerTypeText: registerType.label || ''
  342. })
  343. },
  344. onGenericDialogDataTypeChange(event) {
  345. const dataTypeIndex = Number(event.detail.value)
  346. this.updateGenericModbusDialog(getGenericDialogDataTypeState(
  347. this.data.genericModbusDialog,
  348. this.data.genericModbusDataTypeOptions,
  349. dataTypeIndex
  350. ))
  351. },
  352. openGenericGroupEdit(event) {
  353. const groupId = event.currentTarget.dataset.groupId
  354. const group = findGenericGroup(this.data.genericModbusGroups, groupId)
  355. if (!group) return
  356. this.genericModbusGroupLongPressGuard = groupId
  357. this.updateGenericModbusDialog(createGenericGroupDialogState(group))
  358. },
  359. openGenericRegisterInfo(event) {
  360. const groupId = event.currentTarget.dataset.groupId
  361. const registerIndex = Number(event.currentTarget.dataset.index)
  362. const registerKey = `${groupId}:${registerIndex}`
  363. if (this.genericModbusRegisterLongPressGuard === registerKey) {
  364. this.genericModbusRegisterLongPressGuard = ''
  365. return
  366. }
  367. const {
  368. group,
  369. register
  370. } = findGenericRegister(this.data.genericModbusGroups, groupId, registerIndex)
  371. if (!register) return
  372. this.updateGenericModbusDialog(createGenericRegisterDialogState('viewRegister', group, register, registerIndex))
  373. },
  374. openGenericRegisterEdit(event) {
  375. const groupId = event.currentTarget.dataset.groupId
  376. const registerIndex = Number(event.currentTarget.dataset.index)
  377. const {
  378. group,
  379. register
  380. } = findGenericRegister(this.data.genericModbusGroups, groupId, registerIndex)
  381. if (!register) return
  382. this.genericModbusRegisterLongPressGuard = `${groupId}:${registerIndex}`
  383. this.updateGenericModbusDialog(createGenericRegisterDialogState('editRegister', group, register, registerIndex))
  384. },
  385. async confirmGenericModbusDialog() {
  386. const dialog = this.data.genericModbusDialog || createGenericModbusDialogState()
  387. const mode = dialog.mode
  388. if (mode === 'createGroup') {
  389. const group = genericModbusService.addGroupFromConfig(createGenericGroupConfig(dialog))
  390. if (group) {
  391. if (this.pageToast) this.pageToast.show(`${group.name}已添加`)
  392. this.closeGenericModbusDraft()
  393. this.setData({
  394. activeGenericGroup: group,
  395. activeGenericGroupId: group.id,
  396. activeParamView: 'genericModbusGroup',
  397. activeGenericRegisterRows: buildActiveGenericRegisterRows(group, this.genericModbusRegisterDrag)
  398. })
  399. }
  400. return
  401. }
  402. if (mode === 'editGroup') {
  403. const group = genericModbusService.updateGroupConfig(dialog.groupId, createGenericGroupConfig(dialog))
  404. if (group) {
  405. if (this.pageToast) this.pageToast.show(`${group.name}已更新`)
  406. this.closeGenericModbusDraft()
  407. if (this.data.activeGenericGroupId === group.id) {
  408. this.setData({
  409. activeGenericGroup: group,
  410. activeGenericRegisterRows: buildActiveGenericRegisterRows(group, this.genericModbusRegisterDrag)
  411. })
  412. }
  413. }
  414. return
  415. }
  416. if (mode === 'editRegister') {
  417. const changedData = createGenericRegisterChangedData(dialog, this.data.genericModbusDataTypeOptions)
  418. genericModbusService.updateRegister(dialog.groupId, dialog.registerIndex, changedData)
  419. if (this.pageToast) this.pageToast.show(`${dialog.name || '寄存器'}已更新`)
  420. this.closeGenericModbusDraft()
  421. }
  422. },
  423. async importGenericModbusJson() {
  424. const count = await genericModbusService.importJsonFromMessageFile()
  425. if (count && this.pageToast) this.pageToast.show(`已导入 ${count} 个寄存器组`)
  426. },
  427. async syncGenericModbusGroups() {
  428. if (this.data.isGenericProtocol) return
  429. if (!this.data.connectedDevice) return
  430. const result = await genericModbusService.queryCodeInfoBlock({
  431. maxPacketLength: this.data.genericModbusMaxPacketLength
  432. })
  433. if (result && result.ok && this.pageToast) {
  434. const addressText = Number(result.address || 0).toString(16).toUpperCase().padStart(4, '0')
  435. const addedCount = Number(result.addedGroups || 0) + Number(result.addedRegisters || 0)
  436. const updatedCount = Number(result.updatedGroups || 0) + Number(result.updatedRegisters || 0)
  437. const changedText = [
  438. addedCount ? `新增 ${addedCount}` : '',
  439. updatedCount ? `更新 ${updatedCount}` : ''
  440. ].filter(Boolean).join(',')
  441. this.pageToast.show(`同步完成 0x${addressText},${result.structCount} 项${changedText ? `,${changedText}` : ''}`)
  442. }
  443. },
  444. async completeGenericModbusStructs() {
  445. const result = await genericModbusService.completeStructInstanceGroupsWithStructFile()
  446. if (result && result.completedCount && this.pageToast) {
  447. this.pageToast.show(`已补全 ${result.completedCount} 个寄存器组`)
  448. }
  449. },
  450. toggleGenericModbusPolling() {
  451. if (this.data.isPrivateProtocol && !this.data.connectedDevice) return
  452. const enabled = !this.data.genericModbusAutoPollEnabled
  453. settingsService.setGenericModbusAutoPollEnabled(enabled)
  454. if (enabled) {
  455. this.scheduleGenericAutoPoll(0)
  456. } else {
  457. this.clearGenericAutoTimers()
  458. }
  459. },
  460. async saveGenericModbusJson() {
  461. const count = await genericModbusService.saveJsonToChat()
  462. if (count && this.pageToast) this.pageToast.show(`已保存 ${count} 个寄存器组`)
  463. },
  464. toggleGenericModbusGroup(event) {
  465. const groupId = event.currentTarget.dataset.groupId
  466. if (this.genericModbusGroupLongPressGuard === groupId) {
  467. this.genericModbusGroupLongPressGuard = ''
  468. return
  469. }
  470. const group = findGenericGroup(this.data.genericModbusGroups, groupId)
  471. if (!group) return
  472. genericModbusService.setGroupExpanded(groupId, !group.expanded)
  473. },
  474. onGenericRegisterValueInput(event) {
  475. genericModbusService.updateRegisterValue(
  476. event.currentTarget.dataset.groupId,
  477. Number(event.currentTarget.dataset.index),
  478. event.detail.value
  479. )
  480. },
  481. async onGenericRegisterValueBlur(event) {
  482. const groupId = event.currentTarget.dataset.groupId
  483. const registerIndex = Number(event.currentTarget.dataset.index)
  484. try {
  485. genericModbusService.validateRegisterInputValue(groupId, registerIndex, event.detail.value)
  486. } catch (error) {
  487. if (this.pageToast) this.pageToast.show(error.message || '输入值无效', 'error')
  488. return
  489. }
  490. if (!this.data.isPrivateProtocol || !this.data.connectedDevice) return
  491. const group = findGenericGroup(this.data.genericModbusGroups, groupId)
  492. const register = group && group.registers ? group.registers[registerIndex] : null
  493. if (!group || !register || !register.isDirty || !group.writable || group.addressOverflow) return
  494. this.clearGenericAutoTimers()
  495. const ok = await genericModbusService.writeRegister(groupId, registerIndex)
  496. if (this.data.genericModbusAutoPollEnabled) {
  497. this.scheduleGenericAutoPoll(this.data.genericModbusPollInterval || 100)
  498. }
  499. if (ok && this.pageToast) {
  500. this.pageToast.show(`${register.name || '变量'}已写入`)
  501. }
  502. },
  503. async readGenericModbusGroup(event) {
  504. if (!this.data.connectedDevice) return
  505. const groupId = event.currentTarget.dataset.groupId
  506. const ok = await genericModbusService.readGroup(groupId, {
  507. maxPacketLength: this.data.genericModbusMaxPacketLength
  508. })
  509. if (ok && this.pageToast) this.pageToast.show('通用Modbus读取完成')
  510. },
  511. async writeGenericModbusGroup(event) {
  512. if (!this.data.connectedDevice) return
  513. const groupId = event.currentTarget.dataset.groupId
  514. const ok = await genericModbusService.writeGroup(groupId)
  515. if (ok && this.pageToast) this.pageToast.show('通用Modbus写入完成')
  516. },
  517. onGenericGroupTouchStart(event) {
  518. const groupId = event.currentTarget.dataset.groupId
  519. const touch = (event.changedTouches || [])[0]
  520. if (!groupId || !touch) return
  521. this.genericModbusTouchStarts[groupId] = touch.clientX
  522. },
  523. onGenericGroupTouchEnd(event) {
  524. const groupId = event.currentTarget.dataset.groupId
  525. const group = findGenericGroup(this.data.genericModbusGroups, groupId)
  526. const touch = (event.changedTouches || [])[0]
  527. const startX = this.genericModbusTouchStarts[groupId]
  528. if (!groupId || !group || group.expanded || !touch || !Number.isFinite(startX)) return
  529. const deltaX = touch.clientX - startX
  530. if (deltaX > 42) {
  531. genericModbusService.setGroupDeleteVisible(groupId, true)
  532. } else if (deltaX < -24) {
  533. genericModbusService.setGroupDeleteVisible(groupId, false)
  534. }
  535. },
  536. onGenericRegisterDragStart(event) {
  537. const touch = (event.changedTouches || [])[0]
  538. if (!touch) return
  539. const groupId = event.currentTarget.dataset.groupId
  540. const index = Number(event.currentTarget.dataset.index)
  541. const activeGenericGroup = findGenericGroup(this.data.genericModbusGroups, groupId)
  542. if (!groupId || !activeGenericGroup || !Number.isInteger(index)) return
  543. this.genericModbusRegisterDrag = {
  544. groupId,
  545. index,
  546. moved: false,
  547. rowCenters: [],
  548. rowOffset: getFallbackDragRowOffsetPx(this.genericWindowWidth),
  549. startY: touch.clientY,
  550. targetIndex: index,
  551. translateY: 0
  552. }
  553. if (this.data.activeGenericGroupId === groupId) {
  554. this.setData({
  555. activeGenericRegisterRows: buildActiveGenericRegisterRows(activeGenericGroup, this.genericModbusRegisterDrag)
  556. })
  557. }
  558. this.measureGenericRegisterRows(this.genericModbusRegisterDrag)
  559. },
  560. onGenericRegisterDragMove(event) {
  561. const touch = (event.changedTouches || [])[0]
  562. if (!touch || !this.genericModbusRegisterDrag) return
  563. const drag = this.genericModbusRegisterDrag
  564. const group = findGenericGroup(this.data.genericModbusGroups, drag.groupId)
  565. if (!group) return
  566. const translateY = Math.round(touch.clientY - drag.startY)
  567. const moved = Math.abs(translateY) > GENERIC_REGISTER_DRAG_THRESHOLD_PX
  568. const targetIndex = moved
  569. ? resolveDragTargetIndex(drag, touch.clientY, group.registers.length)
  570. : drag.index
  571. if (
  572. drag.translateY === translateY
  573. && drag.moved === moved
  574. && drag.targetIndex === targetIndex
  575. ) {
  576. return
  577. }
  578. drag.translateY = translateY
  579. drag.moved = moved
  580. drag.targetIndex = targetIndex
  581. if (this.data.activeGenericGroupId === group.id) {
  582. this.setData({
  583. activeGenericRegisterRows: buildActiveGenericRegisterRows(group, drag)
  584. })
  585. }
  586. },
  587. onGenericRegisterDragEnd(event) {
  588. const drag = this.genericModbusRegisterDrag
  589. this.genericModbusRegisterDrag = null
  590. if (!drag || !drag.groupId) return
  591. const group = findGenericGroup(this.data.genericModbusGroups, drag.groupId)
  592. if (!group) return
  593. if (this.data.activeGenericGroupId === group.id) {
  594. this.setData({
  595. activeGenericRegisterRows: buildActiveGenericRegisterRows(group, null)
  596. })
  597. }
  598. if (!drag.moved) return
  599. const targetIndex = clampIndex(
  600. Number(drag.targetIndex) || drag.index,
  601. 0,
  602. group.registers.length - 1
  603. )
  604. if (targetIndex === drag.index) return
  605. const updatedGroup = genericModbusService.reorderRegister(drag.groupId, drag.index, targetIndex)
  606. if (!updatedGroup) return
  607. this.genericModbusRegisterLongPressGuard = `${drag.groupId}:${targetIndex}`
  608. setTimeout(() => {
  609. if (this.genericModbusRegisterLongPressGuard === `${drag.groupId}:${targetIndex}`) {
  610. this.genericModbusRegisterLongPressGuard = ''
  611. }
  612. }, 260)
  613. if (this.data.activeGenericGroupId === updatedGroup.id) {
  614. this.setData({
  615. activeGenericGroup: updatedGroup,
  616. activeGenericRegisterRows: buildActiveGenericRegisterRows(updatedGroup, null)
  617. })
  618. }
  619. },
  620. onGenericRegisterDragCancel() {
  621. const drag = this.genericModbusRegisterDrag
  622. this.genericModbusRegisterDrag = null
  623. if (!drag || !drag.groupId) return
  624. const group = findGenericGroup(this.data.genericModbusGroups, drag.groupId)
  625. if (!group || this.data.activeGenericGroupId !== group.id) return
  626. this.setData({
  627. activeGenericRegisterRows: buildActiveGenericRegisterRows(group, null)
  628. })
  629. },
  630. deleteGenericModbusGroup(event) {
  631. const groupId = event.currentTarget.dataset.groupId
  632. this.clearGenericAutoTimer(groupId)
  633. genericModbusService.removeGroup(groupId)
  634. if (this.data.activeGenericGroupId === groupId) {
  635. this.setData({
  636. activeGenericGroup: null,
  637. activeGenericGroupId: '',
  638. activeParamView: 'genericModbus'
  639. })
  640. }
  641. if (this.pageToast) this.pageToast.show('寄存器组已删除')
  642. },
  643. clearGenericAutoTimer(groupId) {
  644. if (this.genericModbusPoller) this.genericModbusPoller.clearTimer(groupId)
  645. },
  646. clearGenericAutoTimers() {
  647. if (this.genericModbusPoller) this.genericModbusPoller.clearAll()
  648. },
  649. scheduleVisibleGenericAutoReads() {
  650. if (this.genericModbusPoller) this.genericModbusPoller.scheduleVisible()
  651. },
  652. scheduleGenericAutoPoll(delay) {
  653. if (this.genericModbusPoller) this.genericModbusPoller.schedule(delay)
  654. },
  655. clearGenericRegisterDrag() {
  656. if (!this.genericModbusRegisterDrag) return
  657. const drag = this.genericModbusRegisterDrag
  658. this.genericModbusRegisterDrag = null
  659. const group = findGenericGroup(this.data.genericModbusGroups, drag.groupId)
  660. this.setData({
  661. activeGenericRegisterRows: buildActiveGenericRegisterRows(group, null)
  662. })
  663. },
  664. measureGenericRegisterRows(dragReference) {
  665. const query = this.createSelectorQuery()
  666. query.selectAll('.generic-register-row').boundingClientRect((rects) => {
  667. if (!this.genericModbusRegisterDrag || this.genericModbusRegisterDrag !== dragReference) return
  668. if (!Array.isArray(rects) || !rects.length) return
  669. dragReference.rowCenters = rects.map((rect) => Number(rect.top || 0) + Number(rect.height || 0) / 2)
  670. dragReference.rowOffset = Math.max(
  671. 1,
  672. Math.round(Number((rects[dragReference.index] || {}).height) || dragReference.rowOffset || 0)
  673. )
  674. const group = findGenericGroup(this.data.genericModbusGroups, dragReference.groupId)
  675. if (!group || this.data.activeGenericGroupId !== group.id) return
  676. this.setData({
  677. activeGenericRegisterRows: buildActiveGenericRegisterRows(group, dragReference)
  678. })
  679. }).exec()
  680. }
  681. })