using FATrace.Com;
using FATrace.HKNetLib.Hardware;
using FATrace.HKNetLib.Wrapper;
using FATrace.Model;
using LibVLCSharp.Shared;
using ReaLTaiizor.Forms;
using System.ComponentModel;
using FATrace.OEMApp.Services;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Linq;
using TaskStatus = FATrace.Model.TaskStatus;
using System.Windows.Forms;
using System.Drawing;
namespace FATrace.OEMApp
{
public partial class MainApp : MaterialForm
{
///
/// 海康Camera 客户端
///
public HkCamera HkCameraClient { get; set; }
// 历史表列头中文映射
private readonly Dictionary _historyHeaderMap = new Dictionary
{
{ nameof(VideoAction.Id), "编号" },
{ nameof(VideoAction.Code), "条码" },
{ nameof(VideoAction.User), "用户" },
{ nameof(VideoAction.VideoFilePath), "视频路径" },
{ nameof(VideoAction.VideoName), "视频名称" },
{ nameof(VideoAction.StartTime), "开始时间" },
{ nameof(VideoAction.EndTime), "结束时间" },
{ nameof(VideoAction.CreateTime), "创建时间" }
};
public MainApp()
{
InitializeComponent();
}
///
/// 主窗体加载:
/// - 初始化海康客户端与基础配置(日志目录与保存路径)
/// - 初始化播放器与历史记录网格绑定
/// - 启动后台任务服务(顺序下载队列 + Jellyfin 批量监听)
/// - 初始化并启动 gridRULog(DataGridView)定时刷新,实时展示下载/监听任务状态
///
private void MainApp_Load(object sender, EventArgs e)
{
HkCameraClient = new HkCamera();
//保存SDK日志
CHCNetSDK.NET_DVR_SetLogToFile(3, "C:\\SdkLog\\", true);
//读取配置
//HkCameraClient.NVR_IP = ConfigHelper.GetValue("NVRIP");
//HkCameraClient.NVR_Port = ConfigHelper.GetValue("NVRPort");
//HkCameraClient.NVR_UserName = ConfigHelper.GetValue("NVRUserName");
HkCameraClient.NVRVideoSavePath = ConfigHelper.GetValue("NVRVideoSavePath");
//NVR登录
NVRLogin();
InitMediaPlayer();
InitHistoryGridBinding();
// 启动后台任务服务:下载队列(单线程顺序)与 Jellyfin 批量监控(并行匹配)
try
{
DownloadTaskWorker.Instance.Start(HkCameraClient);
JellyfinMonitorQueueService.Instance.Start();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[TaskServices] 启动失败: {ex.Message}");
}
// 初始化 gridRULog 并启动 UI 定时刷新
InitRuLogGrid();
// 立即刷新一次,避免首次 1s 空白
RefreshRuLogGrid();
StartTaskUiTimer();
//materialListView1.DataBindings
try
{
var systemName = Program.SystemName;
var authText = Program.IsActive ? "(已授权)" : "(未授权)";
this.Text = string.IsNullOrWhiteSpace(systemName)
? $"添加剂追溯系统 {authText}"
: $"{systemName} {authText}";
}
catch (Exception)
{
// 安静失败,不影响主界面显示
}
try
{
}
catch (Exception ex)
{
MessageBox.Show($"初始化界面失败: {ex.Message}");
}
}
private void NVRLogin()
{
HkCameraClient.NVR_IP = ConfigHelper.GetValue("NVRIP");
HkCameraClient.NVR_Port = ushort.Parse(ConfigHelper.GetValue("NVRPort"));
HkCameraClient.NVR_UserName = ConfigHelper.GetValue("NVRUserName");
HkCameraClient.NVR_Pw = ConfigHelper.GetValue("NVRPw");
var result = HkCameraClient.Sdk_NET_DVR_Login_V30(HkCameraClient.NVR_IP, HkCameraClient.NVR_Port, HkCameraClient.NVR_UserName, HkCameraClient.NVR_Pw);
if (result)
{
MessageBox.Show("登录成功");
return;
}
MessageBox.Show($"登录失败:{HkCameraClient.LastMsgErr}");
return;
}
///
/// 当前登录用户
///
public string CurUserName { get; set; } = "Admin";
#region 视频处理变量
///
/// 当前播放的视频
///
public string CurrentVideoPath { get; set; }
///
/// 按文件名/路径去重,避免同一下载完成事件被重复处理
/// key: 文件名或路径(小写) value: 对应插入的 OEMRawUse.Id(0 表示尚未完成插入)
///
private readonly ConcurrentDictionary _downloadProcessingKeys = new();
#endregion
#region 内包信息
///
/// 内包条码
///
public string CurInBagCode { get; set; } = "AAAAAA";
///
/// 内包条码 原料名称
///
public string CurInBagRawName { get; set; } = "添加剂Test";
#endregion
private void btnNVRLogin_Click(object sender, EventArgs e)
{
HkCameraClient.NVR_IP = ConfigHelper.GetValue("NVRIP");
HkCameraClient.NVR_Port = ushort.Parse(ConfigHelper.GetValue("NVRPort"));
HkCameraClient.NVR_UserName = ConfigHelper.GetValue("NVRUserName");
HkCameraClient.NVR_Pw = ConfigHelper.GetValue("NVRPw");
var result = HkCameraClient.Sdk_NET_DVR_Login_V30(HkCameraClient.NVR_IP, HkCameraClient.NVR_Port, HkCameraClient.NVR_UserName, HkCameraClient.NVR_Pw);
if (result)
{
MessageBox.Show("登录成功");
return;
}
MessageBox.Show($"登录失败:{HkCameraClient.LastMsgErr}");
return;
}
///
/// 下载按钮:仅将请求入队为 DownloadTask,由后台 DownloadTaskWorker 顺序处理。
/// 注意:不会在此处直接调用 SDK 下载,也不会订阅 SDK 事件,避免并发与重复。
///
private void btnLoadVideo_Click(object sender, EventArgs e)
{
// 仅入队下载任务,由后台 DownloadTaskWorker 顺序处理
var taskId = DownloadTaskWorker.Instance.Enqueue(
code: CurInBagCode,
rawName: CurInBagRawName,
user: CurUserName,
start: DateTime.Now.AddMinutes(-5),
end: DateTime.Now
);
MessageBox.Show($"已入队下载任务,Id={taskId}");
}
private void HkCameraClient_NVRLoadVideoProcessEventHandler(object? sender, short value)
{
this.BeginInvoke(new Action(() =>
{
DownloadProgressBarMain.Value = value;
}));
}
private void btnDownloadName_Click(object sender, EventArgs e)
{
//if (m_lDownHandle >= 0)
//{
// MessageBox.Show("Downloading, please stop firstly!");//正在下载,请先停止下载
// return;
//}
//string sVideoFileName; //录像文件保存路径和文件名 the path and file name to save
//sVideoFileName = "D:\\Downtest1111_" + sPlayBackFileName + ".mp4";
////按文件名下载 Download by file name
//m_lDownHandle = CHCNetSDK.NET_DVR_GetFileByName(m_lUserID, sPlayBackFileName, sVideoFileName);
//if (m_lDownHandle < 0)
//{
// iLastErr = CHCNetSDK.NET_DVR_GetLastError();
// str = "NET_DVR_GetFileByName failed, error code= " + iLastErr;
// MessageBox.Show(str);
// return;
//}
//uint iOutValue = 0;
////设置转封装格式
////UInt32 iInValue = 5;
////IntPtr lpInValue = Marshal.AllocHGlobal(4);
////Marshal.StructureToPtr(iInValue, lpInValue, false);
////if (!CHCNetSDK.NET_DVR_PlayBackControl_V40(m_lDownHandle, CHCNetSDK.NET_DVR_SET_TRANS_TYPE, lpInValue, 4, IntPtr.Zero, ref iOutValue))
////{
//// iLastErr = CHCNetSDK.NET_DVR_GetLastError();
//// str = "NET_DVR_PLAYSTART failed, error code= " + iLastErr; //下载控制失败,输出错误号
//// MessageBox.Show(str);
//// return;
////}
//if (!CHCNetSDK.NET_DVR_PlayBackControl_V40(m_lDownHandle, CHCNetSDK.NET_DVR_PLAYSTART, IntPtr.Zero, 0, IntPtr.Zero, ref iOutValue))
//{
// iLastErr = CHCNetSDK.NET_DVR_GetLastError();
// str = "NET_DVR_PLAYSTART failed, error code= " + iLastErr; //下载控制失败,输出错误号
// MessageBox.Show(str);
// return;
//}
//timerDownload.Interval = 1000;
//timerDownload.Enabled = true;
//btnStopDownload.Enabled = true;
}
private void btnStopLoadVideo_Click(object sender, EventArgs e)
{
var Result = HkCameraClient.Sdk_NET_DVR_StopGetFile();
if (Result.Result)
{
MessageBox.Show($"[暂停成功] :{Result.Msg}");
}
else
{
MessageBox.Show($"[暂停失败] :{Result.Msg}");
}
}
#region 视频播放
private LibVLC _libVLC;
private MediaPlayer _mediaPlayer;
///
/// 初始化MediaPlay
///
private void InitMediaPlayer()
{
_libVLC = new LibVLC();
_mediaPlayer = new MediaPlayer(_libVLC);
videoView1.MediaPlayer = _mediaPlayer;
}
#endregion
///
/// 测试手动播放
///
///
///
private void btnPlayHistoryVideo_Click(object sender, EventArgs e)
{
string path = "D:\\Downtest_Channel2025-09-12 102454.mp4";
_mediaPlayer.Play(new Media(_libVLC, path));
}
///
/// 停止播放当前的视频文件
///
///
///
private void btnStopHistoryPlay_Click(object sender, EventArgs e)
{
_mediaPlayer.Stop();
}
///
/// 清理播放资源
///
private void CleanMediaPlay()
{
_mediaPlayer.Stop();
_mediaPlayer.Dispose();
_libVLC.Dispose();
}
#region 历史数据查询
///
/// 采用 BindingList + BindingSource,使数据变化自动触发 UI 刷新
///
private BindingSource historyBindingSource { get; set; }
private BindingList historyVideoBindingList { get; set; }
///
/// 初始化历史记录表格的数据绑定
///
private void InitHistoryGridBinding()
{
historyBindingSource = new BindingSource();
historyVideoBindingList = new BindingList();
historyBindingSource.DataSource = historyVideoBindingList;
// 绑定到 WinForms 原生 DataGridView
dataGridView1.AutoGenerateColumns = true;
dataGridView1.DataSource = historyBindingSource;
// 绑定完成后统一调整列头与可见性
dataGridView1.DataBindingComplete -= DataGridView1_DataBindingComplete;
dataGridView1.DataBindingComplete += DataGridView1_DataBindingComplete;
// 双击行播放对应视频
dataGridView1.CellDoubleClick -= DataGridView1_CellDoubleClick;
dataGridView1.CellDoubleClick += DataGridView1_CellDoubleClick;
// 若此时已存在列,立即调整一次
ConfigureHistoryGridColumns();
}
private void btnHistoryVideoSearch_Click(object sender, EventArgs e)
{
var query = FSqlContext.FDb.Select();
if (!string.IsNullOrEmpty(txtSearchCode.Text.Trim()))
{
query = query.Where(a => a.Code!.Contains(txtSearchCode.Text.Trim()));
}
query.Where(a => a.CreateTime >= PdtHistorySearchStart.Value && a.CreateTime <= PdtHistorySearchEnd.Value);
// 拉取结果并刷新绑定列表
var resultList = query.ToList();
// 暂停变更通知,批量更新提高效率
historyVideoBindingList.RaiseListChangedEvents = false;
historyVideoBindingList.Clear();
foreach (var item in resultList)
{
historyVideoBindingList.Add(item);
}
historyVideoBindingList.RaiseListChangedEvents = true;
// 通知 UI 刷新
historyVideoBindingList.ResetBindings();
}
// 与历史记录表格列配置相关的成员(WinForms DataGridView)
private void DataGridView1_DataBindingComplete(object? sender, DataGridViewBindingCompleteEventArgs e)
{
try
{
ConfigureHistoryGridColumns();
}
catch (Exception ex)
{
// 仅记录,不中断界面
System.Diagnostics.Debug.WriteLine($"[DataBindingComplete] 列配置失败: {ex.Message}");
}
}
///
/// 双击行,播放该行对应的视频文件
///
private void DataGridView1_CellDoubleClick(object? sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex < 0) return; // 双击列头等
try
{
var row = dataGridView1.Rows[e.RowIndex];
if (row?.DataBoundItem is VideoAction va)
{
var path = va.VideoFilePath;
if (string.IsNullOrWhiteSpace(path))
{
MessageBox.Show("该记录没有可播放的视频路径。", "播放提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
if (!File.Exists(path))
{
MessageBox.Show($"未找到视频文件:\n{path}", "播放失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
_mediaPlayer?.Stop();
_mediaPlayer?.Play(new LibVLCSharp.Shared.Media(_libVLC, path));
}
}
catch (Exception ex)
{
MessageBox.Show($"播放失败: {ex.Message}", "播放失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
///
/// 按照 _historyHeaderMap 配置列头、隐藏不需要的列,并做常用格式化
///
private void ConfigureHistoryGridColumns()
{
if (dataGridView1 == null || dataGridView1.Columns.Count == 0) return;
foreach (DataGridViewColumn col in dataGridView1.Columns)
{
var propName = col.DataPropertyName;
// 设置列头中文
if (!string.IsNullOrWhiteSpace(propName) && _historyHeaderMap.TryGetValue(propName, out var headerText))
{
col.HeaderText = headerText;
}
// 隐藏 Id 列
if (propName == nameof(VideoAction.Id))
{
col.Visible = false;
continue;
}
//// 常用列宽与格式
//if (propName is nameof(VideoAction.Code))
//{
// col.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
//}
//else if (propName is nameof(VideoAction.VideoFilePath))
//{
// col.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
//}
// 时间列格式化
if (propName is nameof(VideoAction.StartTime) or nameof(VideoAction.EndTime) or nameof(VideoAction.CreateTime))
{
col.DefaultCellStyle.Format = "yyyy-MM-dd HH:mm:ss";
col.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
}
}
// 选中整行,易于操作
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridView1.MultiSelect = false;
dataGridView1.ReadOnly = true;
dataGridView1.AllowUserToAddRows = false;
}
#endregion
///
/// 测试/结束操作按钮:同样仅入队一条下载任务,方便快速验证下载->监听全流程。
///
///
///
private void btnTestAction_Click(object sender, EventArgs e)
{
var taskId = DownloadTaskWorker.Instance.Enqueue(
code: CurInBagCode,
rawName: CurInBagRawName,
user: CurUserName,
start: DateTime.Now.AddMinutes(-5),
end: DateTime.Now
);
MessageBox.Show($"[Test] 已入队下载任务,Id={taskId}");
}
///
/// 下载完成事件:触发后入库并启动 Jellyfin 轮询匹配
///
///
///
private void HkCameraClient_NVRLoadVideoCompleteEventHandler(object? sender, string e)
{
// 计算用于去重的 key(文件路径或文件名,小写)
var localNameOrPath = (!string.IsNullOrWhiteSpace(e) && (e.Contains("\\") || e.Contains("/") || e.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase)))
? e
: this.CurrentVideoPath;
var key = (System.IO.Path.GetFileName(localNameOrPath) ?? localNameOrPath).ToLowerInvariant();
// 若同一 key 已在处理中,则忽略本次回调
if (!_downloadProcessingKeys.TryAdd(key, 0))
{
System.Diagnostics.Debug.WriteLine($"[NVRLoadVideoComplete] 正在处理相同文件,忽略: {key}");
return;
}
// 先保存当前的信息,记录主键 Id
var rawUse = new OEMRawUse()
{
InBagCode = CurInBagCode,
RawName = CurInBagRawName,
User = CurUserName,
UrlState = false,
VideoUrl = string.Empty
};
long rawUseId = 0;
try
{
rawUseId = FSqlContext.FDb.Insert(rawUse).ExecuteIdentity();
_downloadProcessingKeys[key] = rawUseId;
}
catch (Exception ex)
{
_downloadProcessingKeys.TryRemove(key, out _);
System.Diagnostics.Debug.WriteLine($"[NVRLoadVideoComplete] 插入 OEMRawUse 失败: {ex.Message}");
return;
}
// 后台执行,避免阻塞 UI 线程
Task.Run(async () =>
{
try
{
var monitor = new JellyfinMonitorService();
await monitor.MonitorAndUpdateAfterDownloadAsync(
oemRawUseId: rawUseId,
videoLocalPathOrName: localNameOrPath,
code: CurInBagCode,
rawName: CurInBagRawName,
userName: CurUserName,
cancellationToken: CancellationToken.None
).ConfigureAwait(false);
}
catch (Exception ex1)
{
System.Diagnostics.Debug.WriteLine($"[JellyfinMonitor] 异常: {ex1.Message}");
}
finally
{
// 移除去重 key,允许后续相同文件再次处理(如需重试)
_downloadProcessingKeys.TryRemove(key, out _);
}
});
}
#region 任务监控(gridRULog / DataGridView)
private System.Windows.Forms.Timer _taskUiTimer;
private BindingSource ruLogBindingSource;
private BindingList ruLogBindingList;
///
/// DataGridView 行模型
///
private sealed class RuLogRow
{
public string TimeText { get; set; } = string.Empty;
public string Type { get; set; } = string.Empty; // 下载/监听
public long TaskId { get; set; }
public string StatusText { get; set; } = string.Empty;
public string? ProgressText { get; set; }
public string Remark { get; set; } = string.Empty; // 错误优先,否则文件路径/ItemId
}
///
/// 初始化 gridRULog:列定义/数据绑定/低闪烁
///
private void InitRuLogGrid()
{
ruLogBindingSource = new BindingSource();
ruLogBindingList = new BindingList();
ruLogBindingSource.DataSource = ruLogBindingList;
gridRULog.AutoGenerateColumns = false;
gridRULog.Columns.Clear();
// 列:时间、类型、任务Id、状态、进度、备注(填充)
gridRULog.Columns.Add(new DataGridViewTextBoxColumn
{
Name = "colTime",
HeaderText = "时间",
DataPropertyName = nameof(RuLogRow.TimeText),
Width = 100,
AutoSizeMode = DataGridViewAutoSizeColumnMode.None
});
gridRULog.Columns.Add(new DataGridViewTextBoxColumn
{
Name = "colType",
HeaderText = "类型",
DataPropertyName = nameof(RuLogRow.Type),
Width = 100,
AutoSizeMode = DataGridViewAutoSizeColumnMode.None
});
gridRULog.Columns.Add(new DataGridViewTextBoxColumn
{
Name = "colTaskId",
HeaderText = "任务Id",
DataPropertyName = nameof(RuLogRow.TaskId),
Width = 120,
AutoSizeMode = DataGridViewAutoSizeColumnMode.None
});
gridRULog.Columns.Add(new DataGridViewTextBoxColumn
{
Name = "colStatus",
HeaderText = "状态",
DataPropertyName = nameof(RuLogRow.StatusText),
Width = 120,
AutoSizeMode = DataGridViewAutoSizeColumnMode.None
});
gridRULog.Columns.Add(new DataGridViewTextBoxColumn
{
Name = "colProgress",
HeaderText = "进度",
DataPropertyName = nameof(RuLogRow.ProgressText),
Width = 80,
AutoSizeMode = DataGridViewAutoSizeColumnMode.None
});
gridRULog.Columns.Add(new DataGridViewTextBoxColumn
{
Name = "colRemark",
HeaderText = "备注",
DataPropertyName = nameof(RuLogRow.Remark),
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill
});
// 双缓存减少闪烁
SetDataGridViewDoubleBuffered(gridRULog);
gridRULog.DataSource = ruLogBindingSource;
gridRULog.ColumnHeadersVisible = true;
gridRULog.EnableHeadersVisualStyles = false;
gridRULog.AllowUserToResizeRows = false;
gridRULog.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells;
gridRULog.BackgroundColor = Color.White;
gridRULog.BorderStyle = BorderStyle.FixedSingle;
gridRULog.ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single;
gridRULog.ColumnHeadersDefaultCellStyle.BackColor = Color.Gainsboro;
gridRULog.ColumnHeadersDefaultCellStyle.ForeColor = Color.Black;
gridRULog.RowHeadersVisible = false;
// 使用 Designer 中的 Location/Size 与 Anchor,不额外 Dock
gridRULog.BringToFront();
// 跟随父容器尺寸变化进行定位与大小调整
materialCard2.Resize -= MaterialCard2_Resize;
materialCard2.Resize += MaterialCard2_Resize;
LayoutRuLogGrid();
}
///
/// 根据左侧控件宽度动态计算 gridRULog 的 Left/Size,确保位置与尺寸正确
///
private void LayoutRuLogGrid()
{
if (gridRULog == null || materialCard2 == null) return;
try
{
int leftReserved = 540; // 默认左侧操作区宽度
try
{
var candidates = new Control[] { txtRUInBagCode, txtRURawName, btnTestAction, btnRawStopLoadVideo, DownloadProgressBarMain };
foreach (var c in candidates)
{
if (c?.Parent == materialCard2)
{
leftReserved = Math.Max(leftReserved, c.Right);
}
}
leftReserved += 20; // 预留间距
}
catch { }
var client = materialCard2.ClientSize;
var left = leftReserved;
var top = 9;
var width = Math.Max(200, client.Width - left - 14);
var height = Math.Max(120, client.Height - top - 9);
gridRULog.Location = new Point(left, top);
gridRULog.Size = new Size(width, height);
gridRULog.BringToFront();
}
catch { }
}
private void MaterialCard2_Resize(object? sender, EventArgs e)
{
LayoutRuLogGrid();
}
///
/// 启动 UI 定时器,定时刷新下载与 Jellyfin 监听任务状态
///
private void StartTaskUiTimer()
{
_taskUiTimer = new System.Windows.Forms.Timer();
_taskUiTimer.Interval = 1000; // 1s 刷新
_taskUiTimer.Tick += TaskUiTimer_Tick;
_taskUiTimer.Start();
}
private void TaskUiTimer_Tick(object? sender, EventArgs e)
{
RefreshRuLogGrid();
}
///
/// 刷新 gridRULog(下载 + 监听)
///
private void RefreshRuLogGrid()
{
try
{
var db = FSqlContext.FDb;
var downloads = db.Select()
.OrderByDescending(a => a.UpdateTime)
.Limit(50)
.ToList();
var monitors = db.Select()
.OrderByDescending(a => a.UpdateTime)
.Limit(50)
.ToList();
var rows = new List(downloads.Count + monitors.Count);
// 运行中优先
foreach (var t in downloads.Where(x => x.Status == TaskStatus.Running))
{
rows.Add(new RuLogRow
{
TimeText = t.UpdateTime.ToString("HH:mm:ss"),
Type = "下载",
TaskId = t.Id,
StatusText = t.Status.ToString(),
ProgressText = t.Progress.ToString(),
Remark = string.IsNullOrWhiteSpace(t.Error) ? (t.VideoFilePath ?? string.Empty) : t.Error!
});
}
foreach (var t in monitors.Where(x => x.Status == TaskStatus.Running))
{
rows.Add(new RuLogRow
{
TimeText = t.UpdateTime.ToString("HH:mm:ss"),
Type = "监听",
TaskId = t.Id,
StatusText = t.Status.ToString(),
ProgressText = string.Empty,
Remark = string.IsNullOrWhiteSpace(t.Error) ? (t.FoundItemId ?? t.LocalFileNameOrPath ?? string.Empty) : t.Error!
});
}
// 其余
foreach (var t in downloads.Where(x => x.Status != TaskStatus.Running))
{
rows.Add(new RuLogRow
{
TimeText = t.UpdateTime.ToString("HH:mm:ss"),
Type = "下载",
TaskId = t.Id,
StatusText = t.Status.ToString(),
ProgressText = t.Progress.ToString(),
Remark = string.IsNullOrWhiteSpace(t.Error) ? (t.VideoFilePath ?? string.Empty) : t.Error!
});
}
foreach (var t in monitors.Where(x => x.Status != TaskStatus.Running))
{
rows.Add(new RuLogRow
{
TimeText = t.UpdateTime.ToString("HH:mm:ss"),
Type = "监听",
TaskId = t.Id,
StatusText = t.Status.ToString(),
ProgressText = string.Empty,
Remark = string.IsNullOrWhiteSpace(t.Error) ? (t.FoundItemId ?? t.LocalFileNameOrPath ?? string.Empty) : t.Error!
});
}
// 批量更新绑定列表,尽量减少闪烁
ruLogBindingList.RaiseListChangedEvents = false;
ruLogBindingList.Clear();
foreach (var r in rows)
{
ruLogBindingList.Add(r);
}
ruLogBindingList.RaiseListChangedEvents = true;
ruLogBindingList.ResetBindings();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[gridRULog] 刷新失败: {ex.Message}");
}
}
private static void SetDataGridViewDoubleBuffered(DataGridView dgv)
{
try
{
typeof(DataGridView).InvokeMember(
"DoubleBuffered",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.SetProperty,
null,
dgv,
new object[] { true }
);
}
catch { }
}
#endregion
private void btnRawStopLoadVideo_Click(object sender, EventArgs e)
{
HkCameraClient.Sdk_NET_DVR_StopGetFile();
}
}
}