增加了通过网络主动上报图像的支持

增加了指令维护通道的支持
This commit is contained in:
2026-01-07 10:59:03 +08:00
parent a697aab3e0
commit 3d47c8f009
47 changed files with 1613 additions and 1734 deletions

View File

@@ -1,227 +1,141 @@
namespace SHH.CameraSdk;
/// <summary>
/// 全局服务配置模型 (V3 最终版)
/// <para>负责解析命令行参数,构建网络拓扑和身份标识</para>
/// </summary>
public class ServiceConfig
{
// ==========================================
// 1. 身份与进程属性
// 1. 基础属性
// ==========================================
/// <summary>
/// 父进程 PID (用于哨兵守护,--pid)
/// </summary>
public int ParentPid { get; private set; }
/// <summary>
/// 应用完整标识 (例如 "CameraApp_01", --appid)
/// </summary>
public string AppId { get; private set; } = "Unknown_01";
/// <summary>
/// 【核心】从 AppId 自动提取的数字编号
/// <para>规则:取最后一个下划线后的数字</para>
/// <para>示例:"CameraApp_05" -> 5</para>
/// </summary>
public int NumericId { get; private set; } = 1;
// ==========================================
// 2. 网络连接属性 (分流)
// ==========================================
/// <summary>
/// 视频流目标地址列表 (对应 & 符号左侧)
/// <para>ZeroMQBridgeWorker 使用此列表</para>
/// </summary>
public List<string> VideoEndpoints { get; private set; } = new List<string>();
/// <summary>
/// 指令控制目标地址列表 (对应 & 符号右侧)
/// <para>CommandClientWorker 使用此列表</para>
/// </summary>
public List<string> CommandEndpoints { get; private set; } = new List<string>();
/// <summary>
/// WebAPI 基础端口 (--ports 的第一个值)
/// </summary>
public int BasePort { get; private set; } = 5000;
/// <summary>
/// 端口扫描范围 (--ports 的第二个值)
/// </summary>
public int MaxPortRange { get; private set; } = 100;
/// <summary>
/// 网络模式 (--mode)
/// </summary>
public NetworkMode Mode { get; private set; } = NetworkMode.Passive;
// ==========================================
// 3. 辅助属性
// ==========================================
/// <summary>
/// 是否需要执行 Connect 操作
/// </summary>
public bool ShouldConnect => Mode == NetworkMode.Active || Mode == NetworkMode.Hybrid;
// ==========================================
// 4. 解析入口 (Factory Method)
// 2. 目标地址列表 (类型变了!)
// ==========================================
// ★★★ 修改点:从 List<string> 变为 List<ServiceEndpoint> ★★★
public List<ServiceEndpoint> VideoEndpoints { get; private set; } = new List<ServiceEndpoint>();
public List<ServiceEndpoint> CommandEndpoints { get; private set; } = new List<ServiceEndpoint>();
// ==========================================
// 3. 工厂方法 (保持不变)
// ==========================================
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();
var value = (i + 1 < args.Length && !args[i + 1].StartsWith("--")) ? args[i + 1] : string.Empty;
bool consumed = !string.IsNullOrEmpty(value);
// 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 "--pid": if (int.TryParse(value, out int pid)) config.ParentPid = pid; 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;
if (!string.IsNullOrWhiteSpace(value)) ParseSingleUriConfig(config, value);
break;
case "--mode":
if (int.TryParse(value, out int m) && Enum.IsDefined(typeof(NetworkMode), m))
{
config.Mode = (NetworkMode)m;
}
consumed = true;
break;
case "--mode": if (int.TryParse(value, out int m)) config.Mode = (NetworkMode)m; 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;
}
if (parts.Length >= 1 && int.TryParse(parts[0], out int p)) config.BasePort = p;
if (parts.Length >= 2 && int.TryParse(parts[1], out int r)) config.MaxPortRange = r;
}
consumed = true;
break;
}
// 4. 如果消耗了 Value跳过下一个索引
if (consumed) i++;
}
return config;
}
// ==========================================
// 5. 核心解析算法实现
// ==========================================
/// <summary>
/// 算法:提取下划线后的数字
/// </summary>
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;
}
if (int.TryParse(appId.Substring(lastIdx + 1), out int id)) return id;
}
// 解析失败默认返回 1
return 1;
}
/// <summary>
/// 算法:解析 URI 列表并分流
/// <para>格式: IP,VideoPort&CommandPort</para>
/// <para>空缺处理: "&6001" (仅指令), "6002&" (仅视频)</para>
/// </summary>
private static void ParseUris(ServiceConfig config, string rawValue)
// ==========================================
// 4. 解析算法实现 (核心修改)
// ==========================================
private static void ParseSingleUriConfig(ServiceConfig config, string rawValue)
{
// 1. 按分号拆分不同主机配置
// "127.0.0.1,6002&6001; 192.168.1.5,&6001"
var groups = rawValue.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
var segments = rawValue.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var group in groups)
foreach (var segment in segments)
{
// 2. 按逗号拆分 IP 和 端口段
var hostParts = group.Split(',');
if (hostParts.Length < 2) continue; // 格式非法
var parts = segment.Split(',');
if (parts.Length < 3) continue;
string ip = hostParts[0].Trim();
string portSection = hostParts[1].Trim(); // "6002&6001"
string ip = parts[0].Trim();
string portStr = parts[1].Trim();
string type = parts[2].Trim().ToLower();
// 3. 按 & 拆分端口 (注意:不要 RemoveEmptyEntries位置很重要)
var ports = portSection.Split('&');
// ★★★ 提取第四个字段作为备注 ★★★
string desc = parts.Length >= 4 ? parts[3].Trim() : "未命名终端";
// --- 索引 0: 视频端口 ---
if (ports.Length > 0)
if (int.TryParse(portStr, out int port))
{
string p = ports[0].Trim();
if (!string.IsNullOrWhiteSpace(p) && int.TryParse(p, out int port))
string zmqUri = $"tcp://{ip}:{port}";
// 构建对象
var endpoint = new ServiceEndpoint
{
string uri = $"tcp://{ip}:{port}";
if (!config.VideoEndpoints.Contains(uri))
config.VideoEndpoints.Add(uri);
Uri = zmqUri,
Description = desc
};
// 添加前检查 Uri 是否重复 (备注不参与排重)
if (type == "video")
{
if (!config.VideoEndpoints.Any(e => e.Uri == zmqUri))
config.VideoEndpoints.Add(endpoint);
}
}
// --- 索引 1: 指令端口 ---
if (ports.Length > 1)
{
string p = ports[1].Trim();
if (!string.IsNullOrWhiteSpace(p) && int.TryParse(p, out int port))
else if (type == "command" || type == "text")
{
string uri = $"tcp://{ip}:{port}";
if (!config.CommandEndpoints.Contains(uri))
config.CommandEndpoints.Add(uri);
if (!config.CommandEndpoints.Any(e => e.Uri == zmqUri))
config.CommandEndpoints.Add(endpoint);
}
}
}
}
}
/// <summary>
/// [新增] 端点配置对象,包含地址和备注
/// </summary>
public class ServiceEndpoint
{
/// <summary>
/// ZeroMQ 连接地址 (e.g. "tcp://127.0.0.1:6001")
/// </summary>
public string Uri { get; set; } = string.Empty;
/// <summary>
/// 备注信息 (e.g. "调试机", "大屏")
/// </summary>
public string Description { get; set; } = string.Empty;
/// <summary>
/// ToString
/// </summary>
/// <returns></returns>
public override string ToString() => $"{Uri} ({Description})";
}