具备界面基础功能

This commit is contained in:
2026-01-01 22:40:32 +08:00
parent 0c86b4dad3
commit d039559402
81 changed files with 8333 additions and 1905 deletions

View File

@@ -0,0 +1,232 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
namespace SHH.CameraDashboard
{
public partial class CameraRepository
{
// 文件: Services\WebApis\CameraReps\CameraRepository.cs
public async Task<bool> UpdateSubscriptionAsync(long cameraId, SubscriptionDto dto)
{
var serviceNode = AppGlobal.UseServiceNode;
if (serviceNode == null) return false;
// URL: POST /api/Cameras/{id}/subscriptions
string requestUrl = $"http://{serviceNode.ServiceNodeIp}:{serviceNode.ServiceNodePort}{WebApiRoutes.Cameras.Root}/{cameraId}/subscriptions";
try
{
string jsonBody = JsonHelper.Serialize(dto);
// 发送 POST 请求
await WebApiService.Instance.PostAsync(requestUrl, jsonBody, "更新订阅配置");
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"更新订阅失败: {ex.Message}");
return false;
}
}
/// <summary>
/// [新增] 获取订阅列表
/// </summary>
public async Task<List<SubscriptionDto>> GetSubscriptionsAsync(long cameraId)
{
var serviceNode = AppGlobal.UseServiceNode;
if (serviceNode == null) return new List<SubscriptionDto>();
// URL: GET /api/Cameras/{id}/subscriptions
string requestUrl = $"http://{serviceNode.ServiceNodeIp}:{serviceNode.ServiceNodePort}{WebApiRoutes.Cameras.Root}/{cameraId}/subscriptions";
try
{
string json = await WebApiService.Instance.GetAsync(requestUrl, "获取订阅列表");
// 如果返回空或null返回空列表
if (string.IsNullOrEmpty(json)) return new List<SubscriptionDto>();
var list = JsonHelper.Deserialize<List<SubscriptionDto>>(json);
return list ?? new List<SubscriptionDto>();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"获取订阅列表失败: {ex.Message}");
return new List<SubscriptionDto>();
}
}
/// <summary>
/// [修改] 删除/注销订阅
/// 改为标准的 DELETE 请求
/// </summary>
public async Task<bool> DeleteSubscriptionAsync(long cameraId, string appId)
{
if (string.IsNullOrWhiteSpace(appId)) return false;
var serviceNode = AppGlobal.UseServiceNode;
if (serviceNode == null) return false;
// 拼接 URL: DELETE /api/Cameras/1001/subscriptions/Client_01
// 注意AppId 如果包含特殊字符,建议 UrlEncode但一般 ID 都是字母数字
string requestUrl = $"http://{serviceNode.ServiceNodeIp}:{serviceNode.ServiceNodePort}{WebApiRoutes.Cameras.Root}/{cameraId}/subscriptions/{appId}";
try
{
// 调用刚刚在 WebApiService 里加的 DeleteAsync
await WebApiService.Instance.DeleteAsync(requestUrl, "注销订阅");
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"注销订阅失败: {ex.Message}");
return false;
}
}
}
/// <summary>
/// 视频流订阅配置请求对象
/// 用于定义第三方应用或内部模块对指定相机流的消费需求
/// </summary>
public class SubscriptionDto
{
/// <summary>
/// 进程唯一标识 (如 "AI_Process_01"、"Main_Display_02")
/// </summary>
public string AppId { get; set; } = string.Empty;
/// <summary>
/// 订阅业务类型。
/// 决定了后端流控引擎后续的资源分配(如是否开启录像机或渲染器)。
/// </summary>
public int Type { get; set; }
/// <summary>
/// 显示帧率需求 (单位: fps)
/// <para>不需要显示则设为 0控制器会自动注销该类型需求</para>
/// </summary>
public int DisplayFps
{
get => TargetFps;
set => TargetFps = value;
}
public int TargetFps { get; set; }
/// <summary>
/// [新增] 实际显示/分发帧率
/// JSON: "realFps"
/// </summary>
[JsonPropertyName("realFps")]
public double RealFps { get; set; }
/// <summary>
/// 备注信息。
/// 用于记录订阅的用途、申请人或关联业务系统。
/// </summary>
public string Memo { get; set; }
= string.Empty;
/// <summary>
/// 窗口句柄HWND
/// 仅在 Type 为 HandleDisplay 时必填。格式通常为十六进制或十进制字符串。
/// </summary>
public string Handle { get; set; }
= string.Empty;
/// <summary>
/// 录像持续时长(分钟,范围 1-60
/// 仅在 Type 为 LocalRecord 时有效。
/// </summary>
public int RecordDuration { get; set; }
/// <summary>
/// 录像文件存放绝对路径。
/// 仅在 Type 为 LocalRecord 时有效例如C:\Recordings\Room01。
/// </summary>
public string SavePath { get; set; }
= string.Empty;
/// <summary>
/// 通讯方式协议。
/// 仅在 Type 为 NetworkTrans 或 WebPush 时有效,默认为 Network。
/// </summary>
public int Protocol { get; set; }
/// <summary>
/// 目标接收端 IP 地址。
/// 仅在 Type 为 NetworkTrans 或 WebPush 且 Protocol 为 Network 时必填。
/// </summary>
public string TargetIp { get; set; }
= string.Empty;
/// <summary>
/// 目标接收端端口号。
/// 仅在 Type 为 NetworkTrans 或 WebPush 时必填。
/// </summary>
public int TargetPort { get; set; }
}
/// <summary>
/// 订阅业务类型枚举
/// 描述视频流的最终去向和业务用途,用于帧分发策略的路由决策
/// </summary>
public enum SubscriptionType
{
/// <summary>
/// 本地窗口渲染
/// <para>直接在服务器端显示器绘制(如 OpenCV Window、WinForm 控件)</para>
/// </summary>
[Description("本地窗口显示")]
LocalWindow = 0,
/// <summary>
/// 本地录像存储
/// <para>写入磁盘文件(如 MP4/AVI 格式,支持定时切割、循环覆盖)</para>
/// </summary>
[Description("本地录像存储")]
LocalRecord = 1,
/// <summary>
/// 句柄绑定显示
/// <para>渲染到指定 HWND 窗口句柄(如 SDK 硬件解码渲染到客户端控件)</para>
/// </summary>
[Description("句柄绑定显示")]
HandleDisplay = 2,
/// <summary>
/// 自定义网络传输
/// <para>通过私有协议转发给第三方系统(如工控机、告警服务器)</para>
/// </summary>
[Description("自定义网络传输")]
NetworkTrans = 3,
/// <summary>
/// 网页端推流
/// <para>转码为 Web 标准协议(如 WebRTC、HLS、RTMP供浏览器播放</para>
/// </summary>
[Description("网页端推流")]
WebPush = 4
}
/// <summary>
/// 网络传输协议类型
/// </summary>
public enum TransportProtocol
{
/// <summary> 可靠传输 (默认) </summary>
Tcp = 0,
/// <summary> 快速传输 (可能丢包/花屏) </summary>
Udp = 1,
/// <summary> 组播 (节省带宽) </summary>
Multicast = 2,
/// <summary> 内存交互 </summary>
Memory = 99,
}
}