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 MODBUS_PROTOCOL_OPTIONS = [ { key: 'motor-control', label: '电机控制协议' }, { key: 'generic', label: '通用协议' } ] const DEFAULT_SETTINGS = { genericModbusAutoPollEnabled: false, genericModbusMaxPacketLength: 64, genericModbusPollInterval: 100, modbusSlaveAddress: 'F0', modbusProtocolFilter: MODBUS_PROTOCOL_OPTIONS[0].key, nightModeEnabled: false, nightModeFollowSystem: true, statusPollInterval: 100, userStatusCount: 0 } const STATUS_POLL_MIN_INTERVAL = 100 const STATUS_POLL_MAX_INTERVAL = 3000 const GENERIC_MODBUS_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 normalizeGenericPacketLength(value, fallback = DEFAULT_SETTINGS.genericModbusMaxPacketLength) { 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, GENERIC_MODBUS_MIN_PACKET_LENGTH) } function normalizeModbusProtocolFilter(value) { const key = String(value || '').trim() const matchedOption = MODBUS_PROTOCOL_OPTIONS.find((option) => option.key === key) return matchedOption ? matchedOption.key : DEFAULT_SETTINGS.modbusProtocolFilter } 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 normalizeSettings(settings = {}) { return { genericModbusAutoPollEnabled: !!settings.genericModbusAutoPollEnabled, genericModbusMaxPacketLength: normalizeGenericPacketLength( settings.genericModbusMaxPacketLength, DEFAULT_SETTINGS.genericModbusMaxPacketLength ), genericModbusPollInterval: clampInteger( settings.genericModbusPollInterval, STATUS_POLL_MIN_INTERVAL, STATUS_POLL_MAX_INTERVAL, DEFAULT_SETTINGS.genericModbusPollInterval ), modbusSlaveAddress: normalizeHexByte(settings.modbusSlaveAddress), modbusProtocolFilter: normalizeModbusProtocolFilter(settings.modbusProtocolFilter), nightModeEnabled: !!settings.nightModeEnabled, nightModeFollowSystem: settings.nightModeFollowSystem !== false, statusPollInterval: clampInteger( settings.statusPollInterval, STATUS_POLL_MIN_INTERVAL, STATUS_POLL_MAX_INTERVAL, DEFAULT_SETTINGS.statusPollInterval ), userStatusCount: clampInteger(settings.userStatusCount, 0, 999, DEFAULT_SETTINGS.userStatusCount) } } 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({ ...DEFAULT_SETTINGS, ...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 getModbusSlaveAddress() { init() return parseHexByte(state.modbusSlaveAddress, 'Modbus从机地址') } function setModbusProtocolFilter(value) { init() setState({ modbusProtocolFilter: normalizeModbusProtocolFilter(value) }) } function setGenericModbusAutoPollEnabled(value) { init() setState({ genericModbusAutoPollEnabled: !!value }) } function setGenericModbusMaxPacketLength(value) { init() setState({ genericModbusMaxPacketLength: normalizeGenericPacketLength(value, state.genericModbusMaxPacketLength) }) } function setGenericModbusPollInterval(value) { init() setState({ genericModbusPollInterval: value }) } function getModbusProtocolFilter() { init() return state.modbusProtocolFilter } function setStatusPollInterval(value) { init() setState({ statusPollInterval: value }) } function setUserStatusCount(value, maxValue = 999) { init() setState({ userStatusCount: clampInteger(value, 0, maxValue, state.userStatusCount) }) } module.exports = { GENERIC_MODBUS_MIN_PACKET_LENGTH, MODBUS_PROTOCOL_OPTIONS, STATUS_POLL_MAX_INTERVAL, STATUS_POLL_MIN_INTERVAL, getModbusProtocolFilter, getModbusSlaveAddress, getState, init, setGenericModbusAutoPollEnabled, setGenericModbusMaxPacketLength, setGenericModbusPollInterval, setModbusSlaveAddress, setModbusProtocolFilter, setNightModeEnabled, setNightModeFollowSystem, setStatusPollInterval, setUserStatusCount, subscribe }