package com.battery.device.bts; import java.nio.file.Path; import java.time.Duration; import java.time.ZoneId; import java.util.Map; import com.battery.device.api.DeviceAdapter; import com.battery.device.api.DeviceAdapterException; import com.battery.device.api.credentials.LocalCredentials; import com.battery.device.api.registry.AdapterInstanceConfig; import com.battery.device.api.registry.DeviceAdapterFactory; /** * 按存储形态 {@code NEWARE_BTS} 构造**直连新威对外接口**的只读适配器实例(ADR-0068)。 * *

与 {@code NEWARE_MYSQL}(直连厂商库)并列:这台机器能不能远程取数,取决于它的上位机形态, * 而不是厂商。8.0 形态的库内没有逐点数据,所以它的逐点只能从接口的 {@code download} 取。 * *

为什么口令不进数据库:这里要的是那台机器的**本地管理员口令**——它和厂商库口令一样是来源凭据, * 因此共用同一套取值顺序(本地凭据文件 → 同名环境变量 → 同名 JVM 启动参数,见 * {@link LocalCredentials}),{@code source_config} 里只放变量名。三者都没有时装配失败, * 错误信息同时点名文件路径与变量名,使"忘了放口令"是可见的失败而不是沉默的缺失。 * *

本类不缓存实例:实例由 {@code DeviceAdapterRegistry} 持有并按实例串行执行。 */ public final class NewareBtsAdapterFactory implements DeviceAdapterFactory { /** 实例配置里承载"本机管理员账号变量名"的键。 */ public static final String USER_ENV_OPTION = "userEnv"; /** 实例配置里承载"本机管理员口令变量名"的键。 */ public static final String PASSWORD_ENV_OPTION = "passwordEnv"; public static final String DEFAULT_USER_ENV = "NEWARE_WIN_USER"; /** 建 SMB 会话是本地进程操作,超时给短一些:卡住也没有数据可取。 */ private static final Duration SESSION_TIMEOUT = Duration.ofSeconds(10); /** 读应答超时;{@code download} 一页上千行在 SMB 上会慢一些,但远不到这个量级。 */ private static final Duration RESPONSE_TIMEOUT = Duration.ofSeconds(60); /** 凭据文件相对进程工作目录定位,使开发(仓库根)与交付(应用目录)用同一个位置。 */ static final Path CREDENTIALS_FILE = LocalCredentials.DEFAULT_FILE; /** 返回本形态的适配器类型标识 {@code NEWARE_BTS}。 */ @Override public String adapterType() { return NewareBtsAdapter.ADAPTER_TYPE; } /** 从实例配置取账号与口令来源,装配远程命名管道传输与报文编解码器。 */ @Override public DeviceAdapter create(AdapterInstanceConfig config) { Map sourceConfig = config.sourceConfig(); String user = LocalCredentials.resolve(option(sourceConfig, USER_ENV_OPTION, DEFAULT_USER_ENV), CREDENTIALS_FILE.toAbsolutePath(), System.getenv(), System::getProperty); String password = LocalCredentials.resolve(required(sourceConfig, PASSWORD_ENV_OPTION), CREDENTIALS_FILE.toAbsolutePath(), System.getenv(), System::getProperty); NewareBtsPipeTransport transport = new NewareBtsPipeTransport(config.host(), user, password, SESSION_TIMEOUT, RESPONSE_TIMEOUT); return new NewareBtsAdapter(config.adapterCode(), transport, new BtsXmlCodec(config.adapterCode(), ZoneId.of(config.timezoneId())), config.capabilities()); } private static String option(Map sourceConfig, String key, String fallback) { String value = sourceConfig.get(key); return value == null || value.isBlank() ? fallback : value.trim(); } /** 口令变量名没有缺省值:五台机器的口令实测有两种,猜一个就会连错机器。 */ private static String required(Map sourceConfig, String key) { String value = option(sourceConfig, key, null); if (value == null) throw new DeviceAdapterException("新威 BTS 实例缺少配置项 " + key + "(本机管理员口令的变量名):口令有两种取值,不能有缺省"); return value; } }