const settingsService = require('../../store/settings-store.js') const themeService = require('../../store/theme-store.js') const transport = require('../../transport/ble-core.js') const { createPageToast } = require('../../utils/page-toast.js') const { parameterGroupService } = require('../../features/parameter-groups/index.js') const { LOG_MODE_OPTIONS, decorateLogs, executeStorageAccessProtocol, executeStorageAccessSpecialCommand, getLogModeToggleText, getManualStatePayload, getNextLogMode, getNextSerialMode, getProtocolModeState, getStorageAccessAreaOptions, manualRtuService, sendSerialFrame, normalizeStorageAccessSpecialState, normalizeStorageAccessState, normalizeSerialState } = require('../../features/communication/index.js') const INITIAL_SERIAL_STATE = normalizeSerialState({ serialInputText: '', serialMode: 'hex' }) Page({ data: { ...themeService.getState(), ...settingsService.getState(), ...getProtocolModeState(settingsService.getState()), ...transport.getState(), ...parameterGroupService.getState(), ...manualRtuService.getState(), logs: decorateLogs(transport.getState().logs, 'hex'), logMode: 'hex', logModeToggleText: getLogModeToggleText('hex'), logModeOptions: LOG_MODE_OPTIONS, storageAccessAreaOptions: getStorageAccessAreaOptions(), ...normalizeStorageAccessState({ storageAccessAreaIndex: 0, storageAccessAddress: '', storageAccessCommandIndex: 0, storageAccessDataText: '', storageAccessLength: '' }), ...normalizeStorageAccessSpecialState(), serialTitleText: '串口发送', ...INITIAL_SERIAL_STATE, toastText: '', toastType: '' }, onLoad() { this.pageToast = createPageToast(this, this.data) this.lastSyncedSlaveAddress = '' transport.init() parameterGroupService.init() settingsService.init() themeService.init() const settingsState = settingsService.getState() manualRtuService.setProtocolInput({ slaveAddress: settingsState.modbusSlaveAddress }) this.lastSyncedSlaveAddress = settingsState.modbusSlaveAddress this.unsubscribeTheme = themeService.subscribe((themeState) => { this.setData(themeState) }) this.unsubscribeSettings = settingsService.subscribe((settingsState) => { this.setData({ ...settingsState, ...getProtocolModeState(settingsState) }) if (settingsState.modbusSlaveAddress !== this.lastSyncedSlaveAddress) { this.lastSyncedSlaveAddress = settingsState.modbusSlaveAddress manualRtuService.setProtocolInput({ slaveAddress: settingsState.modbusSlaveAddress }) } }) this.unsubscribeTransport = transport.subscribe((transportState) => { this.setData({ ...transportState, logs: decorateLogs(transportState.logs, this.data.logMode) }) }) this.unsubscribeParameterGroups = parameterGroupService.subscribe((parameterState) => { this.setData({ ...parameterState, ...normalizeStorageAccessState(this.data, { storageAccessCommandIndex: this.data.storageAccessCommandIndex, storageAccessAreaIndex: this.data.storageAccessAreaIndex }) }) }) this.unsubscribeManualRtu = manualRtuService.subscribe((manualState) => { this.setData(getManualStatePayload(manualState)) }) }, onShow() { if (this.pageToast) { this.pageToast.setActive(true) } }, onHide() { if (this.pageToast) { this.pageToast.setActive(false) } }, onUnload() { if (this.pageToast) { this.pageToast.destroy() this.pageToast = null } if (this.unsubscribeTheme) { this.unsubscribeTheme() this.unsubscribeTheme = null } if (this.unsubscribeSettings) { this.unsubscribeSettings() this.unsubscribeSettings = null } if (this.unsubscribeTransport) { this.unsubscribeTransport() this.unsubscribeTransport = null } if (this.unsubscribeParameterGroups) { this.unsubscribeParameterGroups() this.unsubscribeParameterGroups = null } if (this.unsubscribeManualRtu) { this.unsubscribeManualRtu() this.unsubscribeManualRtu = null } }, noop() {}, switchSerialMode() { this.setData(normalizeSerialState(this.data, { serialMode: getNextSerialMode(this.data.serialMode) })) }, switchLogMode(event) { const requestedMode = event && event.currentTarget && event.currentTarget.dataset ? event.currentTarget.dataset.mode : '' const mode = requestedMode || getNextLogMode(this.data.logMode) this.setData({ logMode: mode, logModeToggleText: getLogModeToggleText(mode), logs: decorateLogs(transport.getState().logs, mode) }) }, clearCommunicationLogs() { transport.clearLogs() }, onSerialInput(event) { this.setData(normalizeSerialState(this.data, { serialInputText: event.detail.value })) }, clearSerialInput() { this.setData(normalizeSerialState(this.data, { serialInputText: '' })) }, async sendSerialFrame() { try { const result = await sendSerialFrame(this.data) if (!result.ok) { if (this.pageToast) this.pageToast.show(result.errorText || '发送失败', 'error') return } if (this.pageToast) this.pageToast.show('已发送') this.setData({ ...(result.serialState || {}), serialPreviewHex: result.previewHex || '', serialPreviewLengthText: `${(result.bytes || []).length} bytes` }) } catch (error) { if (this.pageToast) this.pageToast.show(error.message || '发送失败', 'error') } }, onProtocolCommandChange(event) { manualRtuService.setCommandIndex(Number(event.detail.value)) }, onProtocolInput(event) { const field = event.currentTarget.dataset.field if (!field) return manualRtuService.setProtocolInput({ [field]: event.detail.value }) }, onProtocolCoilChange(event) { manualRtuService.setProtocolInput({ coilEnabled: !!event.detail.value }) }, openProtocolMultipleDialog() { manualRtuService.openProtocolMultipleDialog() }, closeProtocolMultipleDialog() { manualRtuService.closeProtocolMultipleDialog() }, toggleProtocolMultipleExpanded() { manualRtuService.toggleProtocolMultipleExpanded() }, onProtocolMultipleQuantityChange(event) { manualRtuService.setProtocolMultipleQuantity(event.detail.value) }, onProtocolMultipleValueInput(event) { const index = Number(event.currentTarget.dataset.index) manualRtuService.setProtocolMultipleValue(index, event.detail.value) }, onProtocolMultipleTypeChange(event) { const index = Number(event.currentTarget.dataset.index) manualRtuService.setProtocolMultipleType(index, Number(event.detail.value)) }, onProtocolMultipleTextLengthInput(event) { const index = Number(event.currentTarget.dataset.index) manualRtuService.setProtocolMultipleTextLength(index, event.detail.value) }, async sendProtocolFrame(event) { const source = event && event.currentTarget && event.currentTarget.dataset ? event.currentTarget.dataset.source : '' if (this.data.protocolMultipleDialog && this.data.protocolMultipleDialog.visible && source !== 'dialog') { if (this.pageToast) this.pageToast.show('请在多值窗口内发送', 'error') return } if (!this.data.connectedDevice) { if (this.pageToast) this.pageToast.show('请先连接蓝牙设备', 'error') return } const response = await manualRtuService.sendGeneratedFrame() if (response && this.pageToast) { this.pageToast.show(response === true ? '已发送' : '已收到回复') } else if (this.pageToast) { const manualState = manualRtuService.getState() this.pageToast.show(manualState.protocolStatusText || '发送失败或超时未回复', 'error') } }, switchStorageAccessCommandMode() { const currentIndex = Number(this.data.storageAccessCommandIndex) || 0 const nextState = normalizeStorageAccessState(this.data, { storageAccessCommandIndex: currentIndex === 0 ? 1 : 0 }) this.setData(nextState) }, onStorageAccessInput(event) { const field = event.currentTarget.dataset.field if (!field) return const changed = { storageAccessCommandIndex: this.data.storageAccessCommandIndex, [field]: event.detail.value } const nextState = normalizeStorageAccessState(this.data, changed) this.setData(nextState) }, onStorageAccessControlRefInput(event) { this.setData(normalizeStorageAccessSpecialState(this.data, { storageAccessControlRefText: event.detail.value })) }, async onStorageAccessControlRefConfirm() { const controlRefText = String(this.data.storageAccessControlRefText || '').trim() const now = Date.now() if (this.lastStorageAccessControlRefSent && this.lastStorageAccessControlRefSent.text === controlRefText && now - this.lastStorageAccessControlRefSent.time < 600) { return } this.lastStorageAccessControlRefSent = { text: controlRefText, time: now } await this.sendStorageAccessSpecialCommand({ currentTarget: { dataset: { command: 'controlRef' } } }) }, onStorageAccessAreaChange(event) { const storageAccessAreaIndex = Number(event.detail.value) const nextState = normalizeStorageAccessState(this.data, { storageAccessCommandIndex: this.data.storageAccessCommandIndex, storageAccessAreaIndex }) this.setData(nextState) }, async sendStorageAccessProtocolFrame() { try { if (!this.data.connectedDevice) { if (this.pageToast) this.pageToast.show('请先连接蓝牙设备', 'error') return } const result = await executeStorageAccessProtocol(this.data) if (!result.ok) { if (this.pageToast) this.pageToast.show(result.errorText || '操作失败', 'error') return } if (!this.data.storageAccessShowWriteData) { this.setData({ storageAccessPreviewHexText: result.previewHex || '' }) if (this.pageToast) this.pageToast.show(`读取完成 ${(result.bytes || []).length} bytes`) return } if (this.pageToast) this.pageToast.show('写入完成') } catch (error) { if (this.pageToast) this.pageToast.show(error.message || '操作失败', 'error') } }, async syncStorageAccessCodeInfo() { if (!this.data.isStorageAccessProtocol) return if (!this.data.connectedDevice) { if (this.pageToast) this.pageToast.show('请先连接蓝牙设备', 'error') return } const result = await parameterGroupService.syncFromStorageAccessCodeInfo({ maxPacketLength: this.data.parameterMaxPacketLength }) if (result && result.ok && this.pageToast) { this.setData(normalizeStorageAccessState(this.data, { storageAccessCommandIndex: this.data.storageAccessCommandIndex, storageAccessAreaIndex: 0 })) const codeInfoAddressText = result.codeInfoAddressText || Number(result.codeInfoAddress || 0).toString(16).toUpperCase() const codeInfoByteLengthText = result.codeInfoByteLengthText || Number(result.codeInfoByteLength || 0).toString(16).toUpperCase() const addedCount = Number(result.addedGroups || 0) + Number(result.addedRegisters || 0) const updatedCount = Number(result.updatedGroups || 0) + Number(result.updatedRegisters || 0) const changedText = [ addedCount ? `新增 ${addedCount}` : '', updatedCount ? `更新 ${updatedCount}` : '' ].filter(Boolean).join(',') this.pageToast.show(`同步完成 codeInfo 0x${codeInfoAddressText} / 0x${codeInfoByteLengthText},${result.structCount} 项${changedText ? `,${changedText}` : ''}`) } }, async sendStorageAccessSpecialCommand(event) { const commandKey = event && event.currentTarget && event.currentTarget.dataset ? event.currentTarget.dataset.command : '' const command = (this.data.storageAccessSpecialCommands || []).find((item) => item.key === commandKey) try { const result = await executeStorageAccessSpecialCommand(command, this.data) if (!result.ok) { if (this.pageToast) this.pageToast.show(result.errorText || '指令执行失败', 'error') return } if (this.pageToast) this.pageToast.show(`${command.label || '特殊指令'}已执行`) } catch (error) { if (this.pageToast) this.pageToast.show(error.message || '指令执行失败', 'error') } } })