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; 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(); } 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"); InitMediaPlayer(); InitHistoryGridBinding(); //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}"); } } /// /// 当前登录用户 /// 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; } private void btnLoadVideo_Click(object sender, EventArgs e) { CurrentVideoPath = NVRCom.GetVideoName(HkCameraClient.NVRVideoSavePath, "CODE"); var Result = HkCameraClient.Sdk_NET_DVR_GetFileByTime_V40(DateTime.Now.AddMinutes(-5), DateTime.Now, CurrentVideoPath); if (Result.Result) { HkCameraClient.NVRLoadVideoProcessEventHandler -= HkCameraClient_NVRLoadVideoProcessEventHandler; HkCameraClient.NVRLoadVideoProcessEventHandler += HkCameraClient_NVRLoadVideoProcessEventHandler; HkCameraClient.NVRLoadVideoCompleteEventHandler -= HkCameraClient_NVRLoadVideoCompleteEventHandler; HkCameraClient.NVRLoadVideoCompleteEventHandler += HkCameraClient_NVRLoadVideoCompleteEventHandler; // 进度轮询已在库方法内部自动启动,这里无需再次调用 HkCameraClient.StartDownloadProgressMonitor(); } else { MessageBox.Show($"[Sdk_NET_DVR_GetFileByTime_V40] 执行失败:{Result.Msg}"); } //btnStopDownload.Enabled = true; } 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) { CurrentVideoPath = NVRCom.GetVideoName(HkCameraClient.NVRVideoSavePath, "CODE"); var Result = HkCameraClient.Sdk_NET_DVR_GetFileByTime_V40(DateTime.Now.AddMinutes(-5), DateTime.Now, CurrentVideoPath); if (Result.Result) { HkCameraClient.NVRLoadVideoProcessEventHandler -= HkCameraClient_NVRLoadVideoProcessEventHandler; HkCameraClient.NVRLoadVideoProcessEventHandler += HkCameraClient_NVRLoadVideoProcessEventHandler; HkCameraClient.NVRLoadVideoCompleteEventHandler -= HkCameraClient_NVRLoadVideoCompleteEventHandler; HkCameraClient.NVRLoadVideoCompleteEventHandler += HkCameraClient_NVRLoadVideoCompleteEventHandler; // 进度轮询已在库方法内部自动启动,这里无需再次调用 } else { MessageBox.Show($"[Sdk_NET_DVR_GetFileByTime_V40] 执行失败:{Result.Msg}"); } } /// /// 下载完成事件:触发后入库并启动 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 _); } }); } private void btnRawStopLoadVideo_Click(object sender, EventArgs e) { HkCameraClient.Sdk_NET_DVR_StopGetFile(); } } }