const { toFiniteNumber } = require('../utils/number-format.js') const { clampInteger } = require('../utils/base-utils.js') const { getWxApi } = require('../utils/platform-utils.js') const STORAGE_KEY = 'app-settings' const PROTOCOL_MODE = { MODBUS_RTU: 'modbus-rtu', STORAGE_ACCESS: 'storage-access' } const LEGACY_PROTOCOL_MODE_ALIASES = { generic: PROTOCOL_MODE.MODBUS_RTU, private: PROTOCOL_MODE.STORAGE_ACCESS } const PROTOCOL_OPTIONS = [ { key: PROTOCOL_MODE.STORAGE_ACCESS, label: '存储访问' }, { key: PROTOCOL_MODE.MODBUS_RTU, label: '标准Modbus' } ] const DEFAULT_SETTINGS = { modbusSlaveAddress: 'F0', nightModeEnabled: false, nightModeFollowSystem: true, parameterAutoPollEnabled: false, parameterMaxPacketLength: 64, parameterPollInterval: 100, protocolMode: PROTOCOL_MODE.STORAGE_ACCESS } const STATUS_POLL_MIN_INTERVAL = 100 const STATUS_POLL_MAX_INTERVAL = 3000 const PARAMETER_MIN_PACKET_LENGTH = 32 const state = { ...DEFAULT_SETTINGS } let initialized = false const subscribers = [] function normalizeHexByte(value, fallback = DEFAULT_SETTINGS.modbusSlaveAddress) { const fallbackText = String(fallback || DEFAULT_SETTINGS.modbusSlaveAddress).toUpperCase() const text = String(value === undefined || value === null ? '' : value).trim() const hexText = text.toUpperCase().startsWith('0X') ? text.slice(2) : text if (!/^[0-9A-F]{1,2}$/i.test(hexText)) return fallbackText return parseInt(hexText, 16).toString(16).toUpperCase().padStart(2, '0') } function normalizeParameterPacketLength(value, fallback = DEFAULT_SETTINGS.parameterMaxPacketLength) { const numberValue = toFiniteNumber(value, NaN) if (!Number.isFinite(numberValue)) return fallback const rounded = Math.round(numberValue) if (rounded <= 0) return 0 return Math.max(rounded, PARAMETER_MIN_PACKET_LENGTH) } function normalizeProtocolMode(value) { const key = String(value || '').trim() const normalizedKey = LEGACY_PROTOCOL_MODE_ALIASES[key] || key const matched = PROTOCOL_OPTIONS.find((option) => option.key === key) if (matched) return matched.key const normalizedMatched = PROTOCOL_OPTIONS.find((option) => option.key === normalizedKey) return normalizedMatched ? normalizedMatched.key : DEFAULT_SETTINGS.protocolMode } function isModbusProtocol(value = state.protocolMode) { return normalizeProtocolMode(value) === PROTOCOL_MODE.MODBUS_RTU } function isStorageAccessProtocol(value = state.protocolMode) { return normalizeProtocolMode(value) === PROTOCOL_MODE.STORAGE_ACCESS } function parseHexByte(value, label = '从机地址') { const text = String(value === undefined || value === null ? '' : value).trim() const hexText = text.toUpperCase().startsWith('0X') ? text.slice(2) : text if (!/^[0-9A-F]{1,2}$/i.test(hexText)) { throw new Error(`${label}需为 00 - FF`) } return parseInt(hexText, 16) } function migrateLegacySettings(settings = {}) { const hasProtocolMode = settings.protocolMode !== undefined && settings.protocolMode !== null && settings.protocolMode !== '' const hasParameterAutoPollEnabled = settings.parameterAutoPollEnabled !== undefined const hasParameterMaxPacketLength = settings.parameterMaxPacketLength !== undefined const hasParameterPollInterval = settings.parameterPollInterval !== undefined return { ...settings, protocolMode: hasProtocolMode ? settings.protocolMode : settings.modbusProtocolMode, parameterAutoPollEnabled: hasParameterAutoPollEnabled ? settings.parameterAutoPollEnabled : settings.genericModbusAutoPollEnabled, parameterMaxPacketLength: hasParameterMaxPacketLength ? settings.parameterMaxPacketLength : settings.genericModbusMaxPacketLength, parameterPollInterval: hasParameterPollInterval ? settings.parameterPollInterval : settings.genericModbusPollInterval } } function normalizeSettings(settings = {}) { const migratedSettings = migrateLegacySettings(settings) const protocolMode = normalizeProtocolMode(migratedSettings.protocolMode) const parameterAutoPollEnabled = !!migratedSettings.parameterAutoPollEnabled const parameterMaxPacketLength = normalizeParameterPacketLength( migratedSettings.parameterMaxPacketLength, DEFAULT_SETTINGS.parameterMaxPacketLength ) const parameterPollInterval = clampInteger( migratedSettings.parameterPollInterval, STATUS_POLL_MIN_INTERVAL, STATUS_POLL_MAX_INTERVAL, DEFAULT_SETTINGS.parameterPollInterval ) return { modbusSlaveAddress: normalizeHexByte(migratedSettings.modbusSlaveAddress), nightModeEnabled: !!migratedSettings.nightModeEnabled, nightModeFollowSystem: migratedSettings.nightModeFollowSystem !== false, parameterAutoPollEnabled, parameterMaxPacketLength, parameterPollInterval, protocolMode } } function readStoredSettings() { const wxApi = getWxApi() if (typeof wxApi.getStorageSync !== 'function') return {} try { return wxApi.getStorageSync(STORAGE_KEY) || {} } catch (error) { return {} } } function persistSettings() { const wxApi = getWxApi() if (typeof wxApi.setStorageSync !== 'function') return try { wxApi.setStorageSync(STORAGE_KEY, getState()) } catch (error) {} } function notify() { const nextState = getState() subscribers.slice().forEach((subscriber) => { subscriber(nextState) }) } function setState(changedData, options = {}) { Object.assign(state, normalizeSettings({ ...state, ...changedData })) if (options.persist !== false) { persistSettings() } notify() } function init() { if (initialized) return Object.assign(state, normalizeSettings(readStoredSettings())) initialized = true } function getState() { return { ...state } } function subscribe(subscriber) { if (typeof subscriber !== 'function') return () => {} init() subscribers.push(subscriber) subscriber(getState()) return () => { const index = subscribers.indexOf(subscriber) if (index >= 0) subscribers.splice(index, 1) } } function setNightModeEnabled(value) { init() setState({ nightModeEnabled: !!value }) } function setNightModeFollowSystem(value) { init() setState({ nightModeFollowSystem: !!value }) } function setModbusSlaveAddress(value) { init() setState({ modbusSlaveAddress: normalizeHexByte(value, state.modbusSlaveAddress) }) } function setProtocolMode(value) { init() setState({ protocolMode: normalizeProtocolMode(value) }) } function getModbusSlaveAddress() { init() return parseHexByte(state.modbusSlaveAddress, 'Modbus从机地址') } function setParameterAutoPollEnabled(value) { init() setState({ parameterAutoPollEnabled: !!value }) } function setParameterMaxPacketLength(value) { init() setState({ parameterMaxPacketLength: normalizeParameterPacketLength(value, state.parameterMaxPacketLength) }) } function setParameterPollInterval(value) { init() setState({ parameterPollInterval: value }) } module.exports = { PARAMETER_MIN_PACKET_LENGTH, PROTOCOL_MODE, PROTOCOL_OPTIONS, STATUS_POLL_MAX_INTERVAL, STATUS_POLL_MIN_INTERVAL, getModbusSlaveAddress, getState, init, isModbusProtocol, isStorageAccessProtocol, setModbusSlaveAddress, setNightModeEnabled, setNightModeFollowSystem, setParameterAutoPollEnabled, setParameterMaxPacketLength, setParameterPollInterval, setProtocolMode, subscribe }