Files
Ayay/SHH.CameraDashboard/App.xaml.cs

257 lines
10 KiB
C#
Raw Normal View History

using MessagePack;
using SHH.Contracts;
2026-01-05 14:54:06 +08:00
using SHH.ProcessLaunchers;
using System.Collections.ObjectModel;
2025-12-30 10:53:02 +08:00
using System.Windows;
namespace SHH.CameraDashboard
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
2026-01-05 14:54:06 +08:00
#region
/// <summary>
/// 进程管理器
/// </summary>
public static ProcessManager ProcManager { get; private set; }
= new ProcessManager(new ProcessDashboardLogger());
#endregion
2026-01-01 22:40:32 +08:00
protected override async void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
2026-01-05 14:54:06 +08:00
// 启动视频流接收 (Port 6002)
StreamReceiverService.Instance.Start(6002);
// 启动指令服务 (Port 6001)
CommandBusClient.Instance.Start(6001);
CommandBusClient.Instance.OnServerRegistered += SetupAutomaticConfiguration;
//CommandServer.Instance.Start(6001);
//CommandServer.Instance.OnClientRegistered += SetupAutomaticConfiguration;
2026-01-05 14:54:06 +08:00
// 现在我们来配置启动
2026-01-01 22:40:32 +08:00
// 1. 【核心代码】程序启动时,异步读取配置文件
var savedNodes = await LocalStorageService.LoadAsync<ObservableCollection<ServiceNodeModel>>(AppPaths.ServiceNodesConfig);
if (savedNodes != null)
{
foreach (var node in savedNodes)
AppGlobal.ServiceNodes.Add(node);
}
2026-01-05 14:54:06 +08:00
// =========================================================
// 3. 构建启动参数 & 注册进程
// =========================================================
// A. 获取当前 Dashboard 的 PID (用于父进程守护)
int myPid = System.Environment.ProcessId;
// B. 构建参数字符串
// --pid: 让 Service 知道谁是父进程
// --uris: 告诉 Service 反向连接的目标 (注意顺序:视频端口, 指令端口)
// --mode: 1 (主动连接模式)
// --ports: Service 自身的 WebAPI 端口 (5005)
string serviceArgs = $"" +
$"--pid {myPid} " +
$"--appid \"CameraApp_01\" " +
$"--uris \"127.0.0.1,6003,video,PC;\" " +
$"--uris \"127.0.0.1,6001,command,PC;\" " +
$"--uris \"127.0.0.1,6004,command,PC;\" " +
$"--uris \"127.0.0.1,6002,video,;\" " +
2026-01-05 14:54:06 +08:00
$"--mode 1 " +
$"--ports \"5000,100\"";
// C. 注册进程配置 (复用 ProcManager)
ProcManager.Register(new ProcessConfig
{
Id = "CameraService", // 内部标识
DisplayName = "视频接入服务", // UI显示名称
// 请确保路径正确,建议用相对路径 AppDomain.CurrentDomain.BaseDirectory + "SHH.CameraService.exe"
ExePath = @"E:\Codes2026\Ayay\SHH.CameraService\bin\Debug\net8.0\SHH.CameraService.exe",
2026-01-05 14:54:06 +08:00
Arguments = serviceArgs, // ★★★ 核心:注入参数 ★★★
StartupOrder = 1, // 优先级
RestartDelayMs = 2000, // 崩溃后2秒重启
Visible = false // 不显示黑框
});
// =========================================================
// 4. 发射!启动所有注册的进程
// =========================================================
_ = ProcManager.StartAllAsync();
2026-01-01 22:40:32 +08:00
// 3. 启动主窗口
// 注意:如果 LoadAsync 耗时较长,这里可能会导致启动画面停留,
// 实际项目中可以搞一个 Splash Screen (启动屏) 来做这件事。
var mainWin = new MainWindow();
mainWin.Show();
}
2025-12-30 10:53:02 +08:00
private void SetupAutomaticConfiguration(RegisterPayload client)
{
Console.WriteLine($"[自动化] 新服务上线: {client.InstanceId}");
Task.Run(async () =>
{
await Task.Delay(500);
// 1. 构建业务配置对象
var cameraConfig = new CameraConfigDto
{
Id = 17798,
Name = "206摄像头",
Location = "404办公室",
IpAddress = "172.16.41.88",
Username = "admin",
Password = "abcd1234",
Port = 8000,
ChannelIndex = 1,
StreamType = 0,
Brand = DeviceBrand.HikVision.GetHashCode(), // 对应 DeviceBrand 枚举
RenderHandle = 0, // 初始化为0
MainboardIp = "", // 留空
MainboardPort = 0,
RtspPath = ""
};
// ★ 新增:一并带上订阅要求 ★
cameraConfig.AutoSubscriptions = new List<CameraConfigSubscribeDto>
{
// 第一条:显示帧,要求 8 帧
new CameraConfigSubscribeDto {
AppId = "UI_Display",
Type = 0,
TargetFps = 8,
Memo = "显示帧"
},
// 第二条:分析帧,要求 1 帧
new CameraConfigSubscribeDto {
AppId = "AI_Analysis",
Type = 0,
Memo = "分析帧",
TargetFps = 1
}
};
// 2. 构造指令包
var command = new CommandPayload
{
Protocol = ProtocolHeaders.Command,
CmdCode = ProtocolHeaders.SyncCamera,
TargetId = client.InstanceId,
RequestId = Guid.NewGuid().ToString("N"),
// ★ 修正 1: 使用 JsonParams 属性名,并将对象序列化为 JSON 字符串 ★
// 因为你的 DTO 定义 JsonParams 是 string 类型
JsonParams = JsonHelper.Serialize(cameraConfig),
// ★ 修正 2: Timestamp 直接赋值 DateTime 对象 ★
// 因为你的 DTO 定义 Timestamp 是 DateTime 类型
Timestamp = DateTime.Now,
RequireAck = true
};
// 3. 发送
await CommandBusClient.Instance.SendInternalAsync(client.InstanceId, command);
});
}
/// <summary>
/// 在程序启动时订阅事件
/// </summary>
/// <param name="obj"></param>
private void SetupAutomaticConfiguration(ConnectedClient obj)
{
// 监听注册事件:每当有 Service (CommandClientWorker) 连上来注册成功
CommandServer.Instance.OnClientRegistered += (client) =>
{
Console.WriteLine($"[自动化] 检测到新服务上线: {client.ServiceId} ({client.Ip})");
// 放到线程池去执行,避免阻塞 UI 或网络接收线程
Task.Run(async () =>
{
// 1. 稍微延时一点点 (500ms),给 Service 一点喘息时间准备接收指令
await Task.Delay(500);
// 2. 构造您指定的“206摄像头”配置
var cameraConfig = new CameraConfigDto
{
Id = 17798,
Name = "206摄像头",
Location = "404办公室",
IpAddress = "172.16.41.88",
Username = "admin",
Password = "abcd1234",
Port = 8000,
ChannelIndex = 1,
StreamType = 0,
Brand = DeviceBrand.HikVision.GetHashCode(), // 对应 DeviceBrand 枚举
RenderHandle = 0, // 初始化为0
MainboardIp = "", // 留空
MainboardPort = 0,
RtspPath = ""
};
// ★ 新增:一并带上订阅要求 ★
cameraConfig.AutoSubscriptions = new List<CameraConfigSubscribeDto>
{
// 第一条:显示帧,要求 8 帧
new CameraConfigSubscribeDto {
AppId = "UI_Display",
Type = 0,
TargetFps = 8,
Memo = "显示帧"
},
// 第二条:分析帧,要求 1 帧
new CameraConfigSubscribeDto {
AppId = "AI_Analysis",
Type = 0,
Memo = "分析帧",
TargetFps = 1
}
};
// 3. 封装协议包
var commandPacket = new
{
Action = "SyncCamera", // 告诉 Service 执行什么动作
Payload = cameraConfig, // 数据载荷
Time = DateTime.Now
};
// 4. 定向发送
// client.ServiceId 就是那个 "CameraApp_01"
CommandServer.Instance.SendCommand(client.ServiceId, commandPacket);
Console.WriteLine($"[自动化] 已向 {client.ServiceId} 下发配置: 206摄像头");
});
};
}
2026-01-01 22:40:32 +08:00
/// <summary>
/// 全局统一退出入口
/// </summary>
public static void ShutdownApp()
{
// 1. 这里可以处理统一的资源清理逻辑 (如停止摄像头推流、关闭数据库连接)
// 2. 保存用户配置
// 3. 彻底退出
2026-01-05 14:54:06 +08:00
StreamReceiverService.Instance.Dispose();
CommandServer.Instance.Dispose();
// 停止所有子进程
ProcManager.StopAll();
2026-01-01 22:40:32 +08:00
Current.Shutdown();
}
2026-01-05 14:54:06 +08:00
2026-01-01 22:40:32 +08:00
}
}