package com.battery.device.api.registry; import java.time.ZoneId; import java.util.Locale; import java.util.Map; import java.util.Objects; import com.battery.device.api.model.AdapterCapabilities; /** 设备适配器实例的运行时配置。 */ public record AdapterInstanceConfig( String adapterCode, String adapterType, String host, int port, int pollIntervalMs, String timezoneId, boolean enabled, AdapterCapabilities capabilities, Map sourceConfig) { /** * 来源形态配置按存储形态互不相交,只承载非敏感项;连接口令由环境变量或启动参数提供,不下沉到数据库。 */ public AdapterInstanceConfig { adapterCode = requireText(adapterCode, "adapterCode"); adapterType = requireText(adapterType, "adapterType").toUpperCase(Locale.ROOT); host = requireText(host, "host"); timezoneId = requireText(timezoneId, "timezoneId"); capabilities = Objects.requireNonNull(capabilities, "capabilities"); sourceConfig = sourceConfig == null ? Map.of() : Map.copyOf(sourceConfig); if (port < 1 || port > 65535) throw new IllegalArgumentException("port must be between 1 and 65535"); if (pollIntervalMs < 1) throw new IllegalArgumentException("pollIntervalMs must be positive"); ZoneId.of(timezoneId); if (!capabilities.discovery() || capabilities.control()) throw new IllegalArgumentException("MVP adapter must support discovery and remain read-only"); } /** 兼容不含来源形态配置的既有装配。 */ public AdapterInstanceConfig(String adapterCode, String adapterType, String host, int port, int pollIntervalMs, String timezoneId, boolean enabled, AdapterCapabilities capabilities) { this(adapterCode, adapterType, host, port, pollIntervalMs, timezoneId, enabled, capabilities, Map.of()); } /** 读取来源形态的非敏感配置项;缺少该项时返回空值,是否必需由适配器决定。 */ public String sourceOption(String key) { return sourceConfig.get(key); } private static String requireText(String value, String field) { String normalized = Objects.requireNonNull(value, field).trim(); if (normalized.isEmpty()) throw new IllegalArgumentException(field + " must not be blank"); return normalized; } }