Files
Ayay/SHH.CameraDashboard/App.xaml.cs
2026-01-15 09:31:57 +08:00

209 lines
8.2 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using MessagePack;
using SHH.Contracts;
using SHH.ProcessLaunchers;
using System.Collections.ObjectModel;
using System.Windows;
namespace SHH.CameraDashboard
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
#region
/// <summary>
/// 进程管理器
/// </summary>
public static ProcessManager ProcManager { get; private set; }
= new ProcessManager(new ProcessDashboardLogger());
#endregion
#region OnExit
/// <summary>
/// 退出时执行
/// </summary>
/// <param name="e"></param>
protected override void OnExit(ExitEventArgs e)
{
// 1. 显式停止通讯总线
CommandBusClient.Instance.Stop();
// 2. 如果你有其他的单例服务需要清理(比如视频解码库),也放在这里
base.OnExit(e);
// 3. 终极保底:如果程序在清理逻辑执行后 3 秒还没消失,强制杀掉进程
// 防止某些第三方 DLL如海康 SDK的线程卡死
Task.Delay(3000).ContinueWith(_ =>
{
System.Diagnostics.Process.GetCurrentProcess().Kill();
});
}
#endregion
protected override async void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// 启动视频流接收 (Port 6002)
StreamReceiverService.Instance.Start(6002);
// 启动指令服务 (Port 6001)
CommandBusClient.Instance.Start(6001);
CommandBusClient.Instance.OnServerRegistered += SetupAutomaticConfiguration;
//CommandServer.Instance.Start(6001);
//CommandServer.Instance.OnClientRegistered += SetupAutomaticConfiguration;
// 现在我们来配置启动
// 1. 【核心代码】程序启动时,异步读取配置文件
var savedNodes = await LocalStorageService.LoadAsync<ObservableCollection<ServiceNodeModel>>(AppPaths.ServiceNodesConfig);
if (savedNodes != null)
{
foreach (var node in savedNodes)
AppGlobal.ServiceNodes.Add(node);
}
// =========================================================
// 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,;\" " +
$"--mode 1 " +
$"--ports \"5000,100\"";
// C. 注册进程配置 (复用 ProcManager)
ProcManager.Register(new ProcessConfig
{
Id = "CameraService", // 内部标识
DisplayName = "视频接入服务", // UI显示名称
// 请确保路径正确,建议用相对路径 AppDomain.CurrentDomain.BaseDirectory + "SHH.CameraService.exe"
ExePath = @"D:\Codes\Codes2026\Ayay\SHH.CameraService\bin\Debug\net8.0\SHH.CameraService.exe",
Arguments = serviceArgs, // ★★★ 核心:注入参数 ★★★
StartupOrder = 1, // 优先级
RestartDelayMs = 2000, // 崩溃后2秒重启
Visible = true, // 不显示黑框
EnableLogRedirect = false,
});
// =========================================================
// 4. 发射!启动所有注册的进程
// =========================================================
_ = ProcManager.StartAllAsync();
// 3. 启动主窗口
// 注意:如果 LoadAsync 耗时较长,这里可能会导致启动画面停留,
// 实际项目中可以搞一个 Splash Screen (启动屏) 来做这件事。
var mainWin = new MainWindow();
mainWin.Show();
}
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>
public static void ShutdownApp()
{
// 1. 这里可以处理统一的资源清理逻辑 (如停止摄像头推流、关闭数据库连接)
// 2. 保存用户配置
// 3. 彻底退出
StreamReceiverService.Instance.Dispose();
CommandServer.Instance.Dispose();
// 停止所有子进程
ProcManager.StopAll();
Current.Shutdown();
}
}
}