namespace SHH.CameraSdk; /// /// 全局服务配置模型 (V3 最终版) /// 负责解析命令行参数,构建网络拓扑和身份标识 /// public class ServiceConfig { // ========================================== // 1. 身份与进程属性 // ========================================== /// /// 父进程 PID (用于哨兵守护,--pid) /// public int ParentPid { get; private set; } /// /// 应用完整标识 (例如 "CameraApp_01", --appid) /// public string AppId { get; private set; } = "Unknown_01"; /// /// 【核心】从 AppId 自动提取的数字编号 /// 规则:取最后一个下划线后的数字 /// 示例:"CameraApp_05" -> 5 /// public int NumericId { get; private set; } = 1; // ========================================== // 2. 网络连接属性 (分流) // ========================================== /// /// 视频流目标地址列表 (对应 & 符号左侧) /// ZeroMQBridgeWorker 使用此列表 /// public List VideoEndpoints { get; private set; } = new List(); /// /// 指令控制目标地址列表 (对应 & 符号右侧) /// CommandClientWorker 使用此列表 /// public List CommandEndpoints { get; private set; } = new List(); /// /// WebAPI 基础端口 (--ports 的第一个值) /// public int BasePort { get; private set; } = 5000; /// /// 端口扫描范围 (--ports 的第二个值) /// public int MaxPortRange { get; private set; } = 100; /// /// 网络模式 (--mode) /// public NetworkMode Mode { get; private set; } = NetworkMode.Passive; // ========================================== // 3. 辅助属性 // ========================================== /// /// 是否需要执行 Connect 操作 /// public bool ShouldConnect => Mode == NetworkMode.Active || Mode == NetworkMode.Hybrid; // ========================================== // 4. 解析入口 (Factory Method) // ========================================== public static ServiceConfig BuildFromArgs(string[] args) { var config = new ServiceConfig(); for (int i = 0; i < args.Length; i++) { // 1. 预处理 Key var key = args[i].ToLower().Trim(); // 2. 预取 Value (如果存在且不是下一个 flag) var value = (i + 1 < args.Length) ? args[i + 1] : string.Empty; // 简单判断:如果 value 以 -- 开头,说明当前 key 是开关,或者参数值缺失 if (value.StartsWith("--")) value = string.Empty; bool consumed = false; // 标记是否消耗了下一个参数 // 3. 匹配参数 switch (key) { case "--pid": if (int.TryParse(value, out int pid)) config.ParentPid = pid; consumed = true; break; case "--appid": if (!string.IsNullOrWhiteSpace(value)) { config.AppId = value; // ★★★ 立即解析数字编号 ★★★ config.NumericId = ParseIdFromAppId(value); } consumed = true; break; case "--uris": if (!string.IsNullOrWhiteSpace(value)) { // ★★★ 解析复杂 URI 字符串 ★★★ ParseUris(config, value); } consumed = true; break; case "--mode": if (int.TryParse(value, out int m) && Enum.IsDefined(typeof(NetworkMode), m)) { config.Mode = (NetworkMode)m; } consumed = true; break; case "--ports": // 格式: "BasePort,Range" -> "6003,100" if (!string.IsNullOrWhiteSpace(value) && value.Contains(",")) { var parts = value.Split(','); if (parts.Length >= 1) { if (int.TryParse(parts[0], out int baseP)) config.BasePort = baseP; } if (parts.Length >= 2) { if (int.TryParse(parts[1], out int range)) config.MaxPortRange = range; } } consumed = true; break; } // 4. 如果消耗了 Value,跳过下一个索引 if (consumed) i++; } return config; } // ========================================== // 5. 核心解析算法实现 // ========================================== /// /// 算法:提取下划线后的数字 /// private static int ParseIdFromAppId(string appId) { if (string.IsNullOrWhiteSpace(appId)) return 1; // 查找最后一个下划线 int lastIdx = appId.LastIndexOf('_'); // 确保下划线存在,且后面还有字符 if (lastIdx >= 0 && lastIdx < appId.Length - 1) { string numPart = appId.Substring(lastIdx + 1); if (int.TryParse(numPart, out int id)) { return id; } } // 解析失败默认返回 1 return 1; } /// /// 算法:解析 URI 列表并分流 /// 格式: IP,VideoPort&CommandPort /// 空缺处理: "&6001" (仅指令), "6002&" (仅视频) /// private static void ParseUris(ServiceConfig config, string rawValue) { // 1. 按分号拆分不同主机配置 // "127.0.0.1,6002&6001; 192.168.1.5,&6001" var groups = rawValue.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (var group in groups) { // 2. 按逗号拆分 IP 和 端口段 var hostParts = group.Split(','); if (hostParts.Length < 2) continue; // 格式非法 string ip = hostParts[0].Trim(); string portSection = hostParts[1].Trim(); // "6002&6001" // 3. 按 & 拆分端口 (注意:不要 RemoveEmptyEntries,位置很重要) var ports = portSection.Split('&'); // --- 索引 0: 视频端口 --- if (ports.Length > 0) { string p = ports[0].Trim(); if (!string.IsNullOrWhiteSpace(p) && int.TryParse(p, out int port)) { string uri = $"tcp://{ip}:{port}"; if (!config.VideoEndpoints.Contains(uri)) config.VideoEndpoints.Add(uri); } } // --- 索引 1: 指令端口 --- if (ports.Length > 1) { string p = ports[1].Trim(); if (!string.IsNullOrWhiteSpace(p) && int.TryParse(p, out int port)) { string uri = $"tcp://{ip}:{port}"; if (!config.CommandEndpoints.Contains(uri)) config.CommandEndpoints.Add(uri); } } } } }