OEM初版

This commit is contained in:
2025-10-10 17:54:53 +08:00
parent ccbe0f626f
commit 9036f967fc
16 changed files with 1736 additions and 237 deletions

View File

@@ -1,7 +0,0 @@
namespace FATrace.Com
{
public class Class1
{
}
}

View File

@@ -45,6 +45,15 @@ namespace FATrace.Com
}
}
/// <summary>
/// 根据Key取Value值
/// </summary>
/// <param name="key"></param>
public static string GetValue(string key)
{
return ConfigurationManager.AppSettings[key].ToString().Trim();
}
/// <summary>
/// 根据 key 读取字符串配置,不存在时返回默认值。
/// </summary>

24
FATrace.Com/NVRCom.cs Normal file
View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FATrace.Com
{
/// <summary>
/// NVR 公共的类
/// </summary>
public class NVRCom
{
/// <summary>
/// 获取视频名称
/// </summary>
/// <param name="configKey"></param>
/// <returns></returns>
public static string GetVideoName(string Path,string Code)
{
return $"{Path}\\{DateTime.Now.ToString("yyyy-MM-dd HHmmss")} {Code}.mp4";
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FATrace.HKNetLib.Common
{
/// <summary>
/// NVR Camera 模型
/// </summary>
public class NVRCamera
{
/// <summary>
/// 通道号
/// </summary>
public int ChNo { get; set; }
/// <summary>
/// 是否在线
/// </summary>
public byte IsOnline { get; set; }
/// <summary>
/// IP设备ID
/// </summary>
public ushort IPID { get; set; }
}
}

View File

@@ -192,4 +192,8 @@
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FATrace.Model\FATrace.Model.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,13 +1,11 @@
using FATrace.HKNetLib.Common;
using FATrace.HKNetLib.Common;
using FATrace.HKNetLib.Hardware;
using System;
using System.Collections.Generic;
using System.Linq;
using FATrace.Model;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using static FATrace.HKNetLib.Hardware.CHCNetSDK;
using System.Threading;
using System.Threading.Tasks;
namespace FATrace.HKNetLib.Wrapper
{
@@ -93,8 +91,73 @@ namespace FATrace.HKNetLib.Wrapper
}
}
#region NVR信息
/// <summary>
/// NVR IP
/// </summary>
public string NVR_IP { get; set; }
/// <summary>
/// NVR Port
/// </summary>
public ushort NVR_Port { get; set; }
/// <summary>
/// NVR UserName
/// </summary>
public string NVR_UserName { get; set; }
/// <summary>
/// NVR Pw
/// </summary>
public string NVR_Pw { get; set; }
/// <summary>
/// NVR VideoSavePath 保存的地址信息
/// </summary>
public string NVRVideoSavePath { get; set; }
#endregion
#region
/// <summary>
/// 事件发布的消息
/// </summary>
public event EventHandler<VideoAction> VideoActionEventHandler;
/// <summary>
/// 下发视频进度过程的Handler
/// </summary>
public event EventHandler<short> NVRLoadVideoProcessEventHandler;
/// <summary>
/// 下发视频完成的Handler
/// </summary>
public event EventHandler<string> NVRLoadVideoCompleteEventHandler;
/// <summary>
/// 当前NVR登录信息
/// </summary>
public CameraLoginInfo NVRLoginInfo { get; set; } = new CameraLoginInfo();
/// <summary>
/// NVR登录状态
/// </summary>
public bool NVRLoginState { get; set; }
#endregion
#region NVR开发
/// <summary>
/// NVRCamera 集合数据
/// </summary>
public List<NVRCamera> ListNVRCamera { get; set; } = new List<NVRCamera>();
/// <summary>
/// NVR设备信息 DVR_DEVICEINFO_V30
/// </summary>
@@ -105,7 +168,14 @@ namespace FATrace.HKNetLib.Wrapper
/// </summary>
public CHCNetSDK.NET_DVR_IPPARACFG_V40 DVR_IPPARACFG_V40 { get; set; }
/// <summary>
/// 模拟通道个数
/// </summary>
private uint dwAChanTotalNum { get; set; } = 0;
/// <summary>
/// 数字通道个数
/// </summary>
private uint dwDChanTotalNum { get; set; } = 0;
public NET_DVR_GET_STREAM_UNION DVR_GET_STREAM_UNION { get; set; }
@@ -153,6 +223,7 @@ namespace FATrace.HKNetLib.Wrapper
{
LastMsgErr = GetLastError();
//MessageBox.Show(str1);
NVRLoginState = false;
return false;
}
else
@@ -161,6 +232,12 @@ namespace FATrace.HKNetLib.Wrapper
//MessageBox.Show("Login Success!");
//btnLogin.Text = "Logout";
NVRLoginInfo.IP = DVRIPAddress;
NVRLoginInfo.Port = (ushort)DVRPortNumber;
NVRLoginInfo.Password = DVRPassword;
NVRLoginInfo.UserName = DVRUserName;
NVRLoginState = true;
dwAChanTotalNum = (uint)DVRDeviceInfoV30.byChanNum;
dwDChanTotalNum = (uint)DVRDeviceInfoV30.byIPChanNum + 256 * (uint)DVRDeviceInfoV30.byHighDChanNum;
@@ -236,6 +313,14 @@ namespace FATrace.HKNetLib.Wrapper
Marshal.StructureToPtr(DVR_GET_STREAM_UNION, ptrChanInfo, false);
DVR_IPCHANINFO = (CHCNetSDK.NET_DVR_IPCHANINFO)Marshal.PtrToStructure(ptrChanInfo, typeof(CHCNetSDK.NET_DVR_IPCHANINFO));
ListNVRCamera.Add(new NVRCamera()
{
//ChNo = DVR_IPCHANINFO.byChannel,
ChNo = (i + 1),
IPID = DVR_IPCHANINFO.byIPID,
IsOnline = DVR_IPCHANINFO.byEnable
});
//列出IP通道
//ListIPChannel(i + 1, DVR_IPCHANINFO.byEnable, DVR_IPCHANINFO.byIPID);
Marshal.FreeHGlobal(ptrChanInfo);
@@ -250,20 +335,19 @@ namespace FATrace.HKNetLib.Wrapper
}
/// <summary>
/// 根据时间获取DVR统计信息
/// 根据时间获取DVR 视频数据信息
/// </summary>
public (bool Result, string Msg) Sdk_NET_DVR_GetFileByTime_V40(DateTime startTime, DateTime endTime, string SaveVideFilePath)
{
if (m_lDownHandle >= 0)
{
//MessageBox.Show("Downloading, please stop firstly!");//正在下载,请先停止下载
return (Result: false, Msg: "正在下载,请先停止下载");
}
NET_DVR_PLAYCOND struDownPara = new NET_DVR_PLAYCOND();
struDownPara.dwChannel = (uint)iChannelNum[(int)iSelIndex]; //通道号 Channel number
struDownPara.dwChannel = (uint)iChannelNum[(int)iSelIndex];
//设置下载的开始时间 Set the starting time
// 设置下载的开始时间
struDownPara.struStartTime.dwYear = startTime.Year;
struDownPara.struStartTime.dwMonth = startTime.Month;
struDownPara.struStartTime.dwDay = startTime.Day;
@@ -271,7 +355,7 @@ namespace FATrace.HKNetLib.Wrapper
struDownPara.struStartTime.dwMinute = startTime.Minute;
struDownPara.struStartTime.dwSecond = startTime.Second;
//设置下载的结束时间 Set the stopping time
// 设置下载的结束时间
struDownPara.struStopTime.dwYear = endTime.Year;
struDownPara.struStopTime.dwMonth = endTime.Month;
struDownPara.struStopTime.dwDay = endTime.Day;
@@ -279,10 +363,7 @@ namespace FATrace.HKNetLib.Wrapper
struDownPara.struStopTime.dwMinute = endTime.Minute;
struDownPara.struStopTime.dwSecond = endTime.Second;
//string sVideoFileName; //录像文件保存路径和文件名 the path and file name to save
//sVideoFileName = "D:\\Downtest_Channel" + struDownPara.dwChannel + ".mp4";
//按时间下载 Download by time
// 按时间下载
m_lDownHandle = CHCNetSDK.NET_DVR_GetFileByTime_V40(m_lUserID, SaveVideFilePath, ref struDownPara);
if (m_lDownHandle < 0)
{
@@ -291,30 +372,153 @@ namespace FATrace.HKNetLib.Wrapper
}
uint iOutValue = 0;
//该接口指定了当前要下载的录像文件调用成功后还需要调用NET_DVR_PlayBackControl_V40接口的开始播放控制命令NET_DVR_PLAYSTART才能实现下载。
//V5.0.3.2或以后版本通过该接口保存录像保存的录像文件数据超过文件最大限制字节数默认为1024MBSDK会自动切片即新建文件进行保存文件名命名规则为“在接口传入的文件名基础上增加数字标识(例如:*_1.mp4、*_2.mp4)”。
//可以调用NET_DVR_GetSDKLocalCfg、NET_DVR_SetSDKLocalCfg(配置类型NET_DVR_LOCAL_CFG_TYPE_GENERAL)获取和设置切片模式和文件最大限制字节数。
// 该接口指定了当前要下载的录像文件,调用成功后,还需要调用 NET_DVR_PlayBackControl_V40 接口的开始播放控制命令NET_DVR_PLAYSTART才能实现下载。
if (!CHCNetSDK.NET_DVR_PlayBackControl_V40(m_lDownHandle, CHCNetSDK.NET_DVR_PLAYSTART, IntPtr.Zero, 0, IntPtr.Zero, ref iOutValue))
{
LastMsgErr = GetLastError();
try { CHCNetSDK.NET_DVR_StopGetFile(m_lDownHandle); } catch { }
m_lDownHandle = -1;
return (Result: false, Msg: $"[NET_DVR_PlayBackControl_V40] 执行下载控制失败:{LastMsgErr}");
}
return (Result: true, Msg: "下载成功!");
}
/// <summary>
/// 获取下载的进度
/// </summary>
/// <param name="downHandle"></param>
/// <returns></returns>
public int Sdk_NET_DVR_GetDownloadPos(Int32 downHandle)
{
return CHCNetSDK.NET_DVR_GetDownloadPos(downHandle);
StartDownloadProgressMonitor();
return (Result: true, Msg: "下载OK!");
}
#endregion
#region
private CancellationTokenSource _downloadCts;
private const int _downloadPollIntervalMs = 500;
/// <summary>
/// 启动下载进度轮询,定期发布 NVRLoadVideoProcessEventHandler 事件
/// </summary>
public void StartDownloadProgressMonitor()
{
// 先确保没有旧的轮询任务
StopDownloadProgressMonitor();
_downloadCts = new CancellationTokenSource();
var token = _downloadCts.Token;
Task.Run(async () =>
{
try
{
while (!token.IsCancellationRequested && m_lDownHandle >= 0)
{
int progress = -1;
try
{
progress = CHCNetSDK.NET_DVR_GetDownloadPos(m_lDownHandle);
}
catch
{
progress = -1;
}
if (progress >= 0)
{
short percent = (short)Math.Min(100, progress);
try
{
NVRLoadVideoProcessEventHandler?.Invoke(this, percent);
}
catch
{
// 事件订阅者异常不应中断轮询
}
if (progress >= 100)
{
// 完成:停止下载,退出轮询
Sdk_NET_DVR_StopGetFile();
NVRLoadVideoCompleteEventHandler?.Invoke(this, "下载完成");
break;
}
}
else
{
// 发生错误,尝试停止下载并退出
Sdk_NET_DVR_StopGetFile();
break;
}
try { await Task.Delay(_downloadPollIntervalMs, token); } catch { /* ignore */ }
}
}
catch
{
// 轮询线程异常吞掉,避免影响上层逻辑
}
}, token);
}
/// <summary>
/// 停止下载进度轮询
/// </summary>
private void StopDownloadProgressMonitor()
{
try
{
if (_downloadCts != null)
{
if (!_downloadCts.IsCancellationRequested)
{
_downloadCts.Cancel();
}
_downloadCts.Dispose();
_downloadCts = null;
}
}
catch
{
// 忽略取消中的异常
}
}
/// <summary>
/// 停止当前下载并清理资源
/// </summary>
/// <returns></returns>
public (bool Result, string Msg) Sdk_NET_DVR_StopGetFile()
{
try
{
// 停止轮询
StopDownloadProgressMonitor();
if (m_lDownHandle >= 0)
{
bool stopOk = CHCNetSDK.NET_DVR_StopGetFile(m_lDownHandle);
m_lDownHandle = -1;
if (!stopOk)
{
LastMsgErr = GetLastError();
return (false, $"[NET_DVR_StopGetFile] 停止下载失败:{LastMsgErr}");
}
}
return (true, "停止下载成功");
}
catch (Exception)
{
return (false, "停止下载异常");
}
}
#endregion
public bool IsOnline()
{
try

View File

@@ -0,0 +1,69 @@
using FreeSql.DataAnnotations;
namespace FATrace.Model
{
/// <summary>
/// OEM原料使用信息
/// </summary>
[Table(Name = "OEMRawUse")]
public class OEMRawUse
{
/// <summary>
/// 主键
/// </summary>
[Column(IsPrimary = true, IsIdentity = true)]
public long Id { get; set; }
/// <summary>
/// 内袋二维码
/// </summary>
[Column(Name = "InBagCode", IsNullable = false, StringLength = 100)]
public string? InBagCode { get; set; }
/// <summary>
/// 原料名称
/// </summary>
[Column(Name = "InBagCode", IsNullable = false, StringLength = 100)]
public string? RawName{ get; set; }
/// <summary>
/// 视频链接
/// </summary>
[Column(Name = "VideoUrl", IsNullable = false, StringLength = 100)]
public string? VideoUrl{ get; set; }
/// <summary>
/// 用户信息
/// </summary>
[Column(Name = "User", IsNullable = false, StringLength = 100)]
public string? User { get; set; }
/// <summary>
/// URl状态
/// </summary>
[Column(Name = "UrlState", IsNullable = false)]
public bool UrlState { get; set; }
///// <summary>
///// 视频信息
///// </summary>
//[Column(Name = "User", IsNullable = false, StringLength = 100)]
//public VideoAction? VideoAction { get; set; }
/// <summary>
/// 创建时间
/// </summary>
[Column(ServerTime = DateTimeKind.Local, CanUpdate = true)]
public DateTime CreateTime { get; set; }
/// <summary>
/// ///////////////////////////////////////////导航属性 LIN 一对一///////////////////////////////////////////////////////
/// </summary>
public long VideoActionId { get; set; } // 外键字段,必要
/// <summary>
/// 视频信息
/// </summary>
public VideoAction? VideoAction { get; set; }
}
}

View File

@@ -26,12 +26,6 @@ namespace FATrace.Model
[Column(Name = "User", IsNullable = false, StringLength = 100)]
public string? User { get; set; }
///// <summary>
///// 分类信息-CAN/LIN
///// </summary>
//[Column(Name = "CANLINInfo", IsNullable = false, MapType = typeof(string))]
//public CANLIN CANLINInfo { get; set; }
/// <summary>
/// 视频文件路径
/// </summary>

View File

@@ -3,11 +3,16 @@
<appSettings>
<add key="connecting1" value="Data Source=192.168.40.2;user instance=false;Initial Catalog=DissColorMachine;User ID=sa;Password=ABCabc123" />
<add key="connecting" value="Data Source=CT-PC;user instance=false;Initial Catalog=MoviconDb;User ID=sa;Password=12345678" />
<add key="connecting" value="Data Source=CT-PC;user instance=false;Initial Catalog=MoviconDb;TrustServerCertificate=True;User ID=sa;Password=12345678" />
<add key="RemoteConnecting" value="Data Source=CT-PC;user instance=false;Initial Catalog=MoviconDb;User ID=sa;Password=12345678" />
<add key="PLCIP" value="127.0.0.1" />
<add key="PLCPort" value="6000" />
<add key="PLCScan" value="600" />
<add key="NVRIP" value="192.168.0.15" />
<add key="NVRPort" value="8000" />
<add key="NVRUserName" value="admin" />
<add key="NVRPw" value="glico@2025" />
<add key="NVRVideoSavePath" value="D:\" />
<add key="InsCheckPort" value="COM10" />
<add key="InsCheckRate" value="9600" />
</appSettings>

View File

@@ -415,9 +415,13 @@
<PackageReference Include="FreeSql" Version="3.5.213" />
<PackageReference Include="FreeSql.Provider.SqlServer" Version="3.5.213" />
<PackageReference Include="HslCommunication" Version="12.3.0" />
<PackageReference Include="LibVLCSharp" Version="3.9.4" />
<PackageReference Include="LibVLCSharp.WinForms" Version="3.9.4" />
<PackageReference Include="NLog" Version="6.0.3" />
<PackageReference Include="NLog.Config" Version="4.7.15" />
<PackageReference Include="ReaLTaiizor" Version="3.8.1.3" />
<PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.21" />
<PackageReference Include="RestSharp" Version="110.2.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,9 +1,4 @@
using FATrace.Com;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FATrace.OEMApp
{
@@ -16,7 +11,7 @@ namespace FATrace.OEMApp
{
return new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.SqlServer, connectionString)
.UseAutoSyncStructure(false)
.UseAutoSyncStructure(true)
.Build();
}
catch (Exception ex)

View File

@@ -30,20 +30,79 @@
{
components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainApp));
statusStrip1 = new StatusStrip();
imageList1 = new ImageList(components);
materialTabControl1 = new ReaLTaiizor.Controls.MaterialTabControl();
tabPage1 = new TabPage();
materialCard4 = new ReaLTaiizor.Controls.MaterialCard();
label18 = new Label();
txtIOOutputTime = new TextBox();
label17 = new Label();
txtIOInputTime = new TextBox();
label16 = new Label();
txtIOInBagCode = new TextBox();
txtRemainBoxCount = new TextBox();
label15 = new Label();
txtOutBoxCount = new TextBox();
label14 = new Label();
txtInBoxCount = new TextBox();
label13 = new Label();
label12 = new Label();
label9 = new Label();
txtIORawName = new TextBox();
label10 = new Label();
txtIOOutBagCode = new TextBox();
label11 = new Label();
materialCard3 = new ReaLTaiizor.Controls.MaterialCard();
label8 = new Label();
materialCard2 = new ReaLTaiizor.Controls.MaterialCard();
btnTestAction = new Button();
label7 = new Label();
txtRURawName = new TextBox();
label6 = new Label();
txtRUInBagCode = new TextBox();
label5 = new Label();
tabPage2 = new TabPage();
btnStopHistoryPlay = new Button();
dataGridView1 = new DataGridView();
materialCard1 = new ReaLTaiizor.Controls.MaterialCard();
label4 = new Label();
txtSearchCode = new TextBox();
btnHistoryVideoSearch = new Button();
label3 = new Label();
PdtHistorySearchEnd = new ReaLTaiizor.Controls.PoisonDateTime();
label2 = new Label();
label1 = new Label();
PdtHistorySearchStart = new ReaLTaiizor.Controls.PoisonDateTime();
btnPlayHistoryVideo = new Button();
videoView1 = new LibVLCSharp.WinForms.VideoView();
tabPage3 = new TabPage();
metroProgressBar1 = new ReaLTaiizor.Controls.MetroProgressBar();
DownloadProgressBar = new ProgressBar();
btnStopLoadVideo = new Button();
btnLoadVideo = new Button();
btnNVRLogin = new Button();
DownTimer = new System.Windows.Forms.Timer(components);
DownloadProgressBar = new ProgressBar();
DownloadProgressBarMain = new ProgressBar();
materialTabControl1.SuspendLayout();
tabPage1.SuspendLayout();
materialCard4.SuspendLayout();
materialCard3.SuspendLayout();
materialCard2.SuspendLayout();
tabPage2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)dataGridView1).BeginInit();
materialCard1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)videoView1).BeginInit();
tabPage3.SuspendLayout();
SuspendLayout();
//
// statusStrip1
//
statusStrip1.Location = new Point(3, 1005);
statusStrip1.Name = "statusStrip1";
statusStrip1.Size = new Size(1914, 22);
statusStrip1.TabIndex = 1;
statusStrip1.Text = "statusStrip1";
//
// imageList1
//
imageList1.ColorDepth = ColorDepth.Depth32Bit;
@@ -76,6 +135,9 @@
//
// tabPage1
//
tabPage1.Controls.Add(materialCard4);
tabPage1.Controls.Add(materialCard3);
tabPage1.Controls.Add(materialCard2);
tabPage1.Font = new Font("Microsoft YaHei UI", 14.25F, FontStyle.Bold, GraphicsUnit.Point, 134);
tabPage1.ImageKey = "set2.png";
tabPage1.Location = new Point(4, 26);
@@ -86,8 +148,323 @@
tabPage1.Text = "主界面";
tabPage1.UseVisualStyleBackColor = true;
//
// materialCard4
//
materialCard4.BackColor = Color.FromArgb(255, 255, 255);
materialCard4.Controls.Add(label18);
materialCard4.Controls.Add(txtIOOutputTime);
materialCard4.Controls.Add(label17);
materialCard4.Controls.Add(txtIOInputTime);
materialCard4.Controls.Add(label16);
materialCard4.Controls.Add(txtIOInBagCode);
materialCard4.Controls.Add(txtRemainBoxCount);
materialCard4.Controls.Add(label15);
materialCard4.Controls.Add(txtOutBoxCount);
materialCard4.Controls.Add(label14);
materialCard4.Controls.Add(txtInBoxCount);
materialCard4.Controls.Add(label13);
materialCard4.Controls.Add(label12);
materialCard4.Controls.Add(label9);
materialCard4.Controls.Add(txtIORawName);
materialCard4.Controls.Add(label10);
materialCard4.Controls.Add(txtIOOutBagCode);
materialCard4.Controls.Add(label11);
materialCard4.Depth = 0;
materialCard4.ForeColor = Color.FromArgb(222, 0, 0, 0);
materialCard4.Location = new Point(14, 373);
materialCard4.Margin = new Padding(14);
materialCard4.MouseState = ReaLTaiizor.Helper.MaterialDrawHelper.MaterialMouseState.HOVER;
materialCard4.Name = "materialCard4";
materialCard4.Padding = new Padding(14);
materialCard4.Size = new Size(864, 535);
materialCard4.TabIndex = 5;
//
// label18
//
label18.AutoSize = true;
label18.ForeColor = Color.DimGray;
label18.Location = new Point(41, 260);
label18.Name = "label18";
label18.Size = new Size(88, 26);
label18.TabIndex = 17;
label18.Text = "出库时间";
//
// txtIOOutputTime
//
txtIOOutputTime.Location = new Point(154, 257);
txtIOOutputTime.Name = "txtIOOutputTime";
txtIOOutputTime.Size = new Size(446, 32);
txtIOOutputTime.TabIndex = 16;
//
// label17
//
label17.AutoSize = true;
label17.ForeColor = Color.DimGray;
label17.Location = new Point(41, 211);
label17.Name = "label17";
label17.Size = new Size(88, 26);
label17.TabIndex = 15;
label17.Text = "入库时间";
//
// txtIOInputTime
//
txtIOInputTime.Location = new Point(154, 208);
txtIOInputTime.Name = "txtIOInputTime";
txtIOInputTime.Size = new Size(446, 32);
txtIOInputTime.TabIndex = 14;
//
// label16
//
label16.AutoSize = true;
label16.ForeColor = Color.DimGray;
label16.Location = new Point(41, 109);
label16.Name = "label16";
label16.Size = new Size(107, 26);
label16.TabIndex = 13;
label16.Text = "内袋二维码";
//
// txtIOInBagCode
//
txtIOInBagCode.Location = new Point(154, 106);
txtIOInBagCode.Name = "txtIOInBagCode";
txtIOInBagCode.Size = new Size(446, 32);
txtIOInBagCode.TabIndex = 12;
//
// txtRemainBoxCount
//
txtRemainBoxCount.BackColor = SystemColors.ButtonHighlight;
txtRemainBoxCount.BorderStyle = BorderStyle.None;
txtRemainBoxCount.Font = new Font("Microsoft YaHei UI", 21.75F, FontStyle.Bold, GraphicsUnit.Point, 134);
txtRemainBoxCount.Location = new Point(507, 417);
txtRemainBoxCount.Name = "txtRemainBoxCount";
txtRemainBoxCount.ReadOnly = true;
txtRemainBoxCount.Size = new Size(100, 37);
txtRemainBoxCount.TabIndex = 11;
txtRemainBoxCount.Text = "100";
txtRemainBoxCount.TextAlign = HorizontalAlignment.Center;
//
// label15
//
label15.AutoSize = true;
label15.Font = new Font("Microsoft YaHei UI", 15.75F, FontStyle.Bold, GraphicsUnit.Point, 134);
label15.ForeColor = Color.DimGray;
label15.Location = new Point(496, 475);
label15.Name = "label15";
label15.Size = new Size(117, 28);
label15.TabIndex = 10;
label15.Text = "剩余总箱数";
//
// txtOutBoxCount
//
txtOutBoxCount.BackColor = SystemColors.ButtonHighlight;
txtOutBoxCount.BorderStyle = BorderStyle.None;
txtOutBoxCount.Font = new Font("Microsoft YaHei UI", 21.75F, FontStyle.Bold, GraphicsUnit.Point, 134);
txtOutBoxCount.Location = new Point(317, 417);
txtOutBoxCount.Name = "txtOutBoxCount";
txtOutBoxCount.ReadOnly = true;
txtOutBoxCount.Size = new Size(100, 37);
txtOutBoxCount.TabIndex = 9;
txtOutBoxCount.Text = "100";
txtOutBoxCount.TextAlign = HorizontalAlignment.Center;
//
// label14
//
label14.AutoSize = true;
label14.Font = new Font("Microsoft YaHei UI", 15.75F, FontStyle.Bold, GraphicsUnit.Point, 134);
label14.ForeColor = Color.DimGray;
label14.Location = new Point(306, 475);
label14.Name = "label14";
label14.Size = new Size(117, 28);
label14.TabIndex = 8;
label14.Text = "出库总箱数";
//
// txtInBoxCount
//
txtInBoxCount.BackColor = SystemColors.ButtonHighlight;
txtInBoxCount.BorderStyle = BorderStyle.None;
txtInBoxCount.Font = new Font("Microsoft YaHei UI", 21.75F, FontStyle.Bold, GraphicsUnit.Point, 134);
txtInBoxCount.Location = new Point(103, 417);
txtInBoxCount.Name = "txtInBoxCount";
txtInBoxCount.ReadOnly = true;
txtInBoxCount.Size = new Size(100, 37);
txtInBoxCount.TabIndex = 7;
txtInBoxCount.Text = "100";
txtInBoxCount.TextAlign = HorizontalAlignment.Center;
//
// label13
//
label13.AutoSize = true;
label13.Font = new Font("Microsoft YaHei UI", 15.75F, FontStyle.Bold, GraphicsUnit.Point, 134);
label13.ForeColor = Color.DimGray;
label13.Location = new Point(92, 475);
label13.Name = "label13";
label13.Size = new Size(117, 28);
label13.TabIndex = 6;
label13.Text = "入库总箱数";
//
// label12
//
label12.AutoSize = true;
label12.Font = new Font("Microsoft YaHei UI", 15.75F, FontStyle.Bold, GraphicsUnit.Point, 134);
label12.ForeColor = Color.FromArgb(64, 64, 64);
label12.Location = new Point(10, 319);
label12.Name = "label12";
label12.Size = new Size(159, 28);
label12.TabIndex = 5;
label12.Text = "成品出入库统计";
//
// label9
//
label9.AutoSize = true;
label9.ForeColor = Color.DimGray;
label9.Location = new Point(41, 160);
label9.Name = "label9";
label9.Size = new Size(88, 26);
label9.TabIndex = 4;
label9.Text = "原料名称";
//
// txtIORawName
//
txtIORawName.Location = new Point(154, 157);
txtIORawName.Name = "txtIORawName";
txtIORawName.Size = new Size(446, 32);
txtIORawName.TabIndex = 3;
//
// label10
//
label10.AutoSize = true;
label10.ForeColor = Color.DimGray;
label10.Location = new Point(41, 61);
label10.Name = "label10";
label10.Size = new Size(107, 26);
label10.TabIndex = 2;
label10.Text = "外箱二维码";
//
// txtIOOutBagCode
//
txtIOOutBagCode.Location = new Point(154, 58);
txtIOOutBagCode.Name = "txtIOOutBagCode";
txtIOOutBagCode.Size = new Size(446, 32);
txtIOOutBagCode.TabIndex = 1;
//
// label11
//
label11.AutoSize = true;
label11.Font = new Font("Microsoft YaHei UI", 15.75F, FontStyle.Bold, GraphicsUnit.Point, 134);
label11.ForeColor = Color.FromArgb(64, 64, 64);
label11.Location = new Point(10, 9);
label11.Name = "label11";
label11.Size = new Size(159, 28);
label11.TabIndex = 0;
label11.Text = "成品出入库信息";
//
// materialCard3
//
materialCard3.BackColor = Color.FromArgb(255, 255, 255);
materialCard3.Controls.Add(label8);
materialCard3.Depth = 0;
materialCard3.ForeColor = Color.FromArgb(222, 0, 0, 0);
materialCard3.Location = new Point(897, 8);
materialCard3.Margin = new Padding(14);
materialCard3.MouseState = ReaLTaiizor.Helper.MaterialDrawHelper.MaterialMouseState.HOVER;
materialCard3.Name = "materialCard3";
materialCard3.Padding = new Padding(14);
materialCard3.Size = new Size(1000, 900);
materialCard3.TabIndex = 1;
//
// label8
//
label8.AutoSize = true;
label8.Font = new Font("Microsoft YaHei UI", 15.75F, FontStyle.Bold, GraphicsUnit.Point, 134);
label8.ForeColor = Color.FromArgb(64, 64, 64);
label8.Location = new Point(12, 9);
label8.Name = "label8";
label8.Size = new Size(180, 28);
label8.TabIndex = 5;
label8.Text = "原料使用历史信息";
//
// materialCard2
//
materialCard2.BackColor = Color.FromArgb(255, 255, 255);
materialCard2.Controls.Add(DownloadProgressBarMain);
materialCard2.Controls.Add(btnTestAction);
materialCard2.Controls.Add(label7);
materialCard2.Controls.Add(txtRURawName);
materialCard2.Controls.Add(label6);
materialCard2.Controls.Add(txtRUInBagCode);
materialCard2.Controls.Add(label5);
materialCard2.Depth = 0;
materialCard2.ForeColor = Color.FromArgb(222, 0, 0, 0);
materialCard2.Location = new Point(14, 9);
materialCard2.Margin = new Padding(14);
materialCard2.MouseState = ReaLTaiizor.Helper.MaterialDrawHelper.MaterialMouseState.HOVER;
materialCard2.Name = "materialCard2";
materialCard2.Padding = new Padding(14);
materialCard2.Size = new Size(864, 348);
materialCard2.TabIndex = 0;
//
// btnTestAction
//
btnTestAction.Location = new Point(41, 223);
btnTestAction.Name = "btnTestAction";
btnTestAction.Size = new Size(142, 44);
btnTestAction.TabIndex = 5;
btnTestAction.Text = "结束操作";
btnTestAction.UseVisualStyleBackColor = true;
btnTestAction.Click += btnTestAction_Click;
//
// label7
//
label7.AutoSize = true;
label7.ForeColor = Color.DimGray;
label7.Location = new Point(41, 126);
label7.Name = "label7";
label7.Size = new Size(88, 26);
label7.TabIndex = 4;
label7.Text = "原料名称";
//
// txtRURawName
//
txtRURawName.Location = new Point(154, 123);
txtRURawName.Name = "txtRURawName";
txtRURawName.Size = new Size(446, 32);
txtRURawName.TabIndex = 3;
//
// label6
//
label6.AutoSize = true;
label6.ForeColor = Color.DimGray;
label6.Location = new Point(41, 68);
label6.Name = "label6";
label6.Size = new Size(107, 26);
label6.TabIndex = 2;
label6.Text = "内袋二维码";
//
// txtRUInBagCode
//
txtRUInBagCode.Location = new Point(154, 65);
txtRUInBagCode.Name = "txtRUInBagCode";
txtRUInBagCode.Size = new Size(446, 32);
txtRUInBagCode.TabIndex = 1;
//
// label5
//
label5.AutoSize = true;
label5.Font = new Font("Microsoft YaHei UI", 15.75F, FontStyle.Bold, GraphicsUnit.Point, 134);
label5.ForeColor = Color.FromArgb(64, 64, 64);
label5.Location = new Point(10, 9);
label5.Name = "label5";
label5.Size = new Size(138, 28);
label5.TabIndex = 0;
label5.Text = "原料使用信息";
//
// tabPage2
//
tabPage2.Controls.Add(btnStopHistoryPlay);
tabPage2.Controls.Add(dataGridView1);
tabPage2.Controls.Add(materialCard1);
tabPage2.Controls.Add(btnPlayHistoryVideo);
tabPage2.Controls.Add(videoView1);
tabPage2.Font = new Font("Microsoft YaHei UI", 14.25F, FontStyle.Bold, GraphicsUnit.Point, 134);
tabPage2.ImageKey = "Load.png";
tabPage2.Location = new Point(4, 26);
@@ -98,8 +475,145 @@
tabPage2.Text = "录像历史数据";
tabPage2.UseVisualStyleBackColor = true;
//
// btnStopHistoryPlay
//
btnStopHistoryPlay.Location = new Point(628, 14);
btnStopHistoryPlay.Name = "btnStopHistoryPlay";
btnStopHistoryPlay.Size = new Size(116, 45);
btnStopHistoryPlay.TabIndex = 12;
btnStopHistoryPlay.Text = "停止播放";
btnStopHistoryPlay.UseVisualStyleBackColor = true;
btnStopHistoryPlay.Click += btnStopHistoryPlay_Click;
//
// dataGridView1
//
dataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
dataGridView1.Location = new Point(17, 328);
dataGridView1.Name = "dataGridView1";
dataGridView1.Size = new Size(483, 584);
dataGridView1.TabIndex = 11;
//
// materialCard1
//
materialCard1.BackColor = Color.FromArgb(255, 255, 255);
materialCard1.Controls.Add(label4);
materialCard1.Controls.Add(txtSearchCode);
materialCard1.Controls.Add(btnHistoryVideoSearch);
materialCard1.Controls.Add(label3);
materialCard1.Controls.Add(PdtHistorySearchEnd);
materialCard1.Controls.Add(label2);
materialCard1.Controls.Add(label1);
materialCard1.Controls.Add(PdtHistorySearchStart);
materialCard1.Depth = 0;
materialCard1.ForeColor = Color.FromArgb(222, 0, 0, 0);
materialCard1.Location = new Point(17, 24);
materialCard1.Margin = new Padding(14);
materialCard1.MouseState = ReaLTaiizor.Helper.MaterialDrawHelper.MaterialMouseState.HOVER;
materialCard1.Name = "materialCard1";
materialCard1.Padding = new Padding(14);
materialCard1.Size = new Size(483, 287);
materialCard1.TabIndex = 10;
//
// label4
//
label4.AutoSize = true;
label4.ForeColor = Color.Gray;
label4.Location = new Point(196, 34);
label4.Name = "label4";
label4.Size = new Size(88, 26);
label4.TabIndex = 7;
label4.Text = "条码信息";
//
// txtSearchCode
//
txtSearchCode.Location = new Point(17, 63);
txtSearchCode.Name = "txtSearchCode";
txtSearchCode.Size = new Size(449, 32);
txtSearchCode.TabIndex = 6;
//
// btnHistoryVideoSearch
//
btnHistoryVideoSearch.ForeColor = Color.DimGray;
btnHistoryVideoSearch.Location = new Point(125, 214);
btnHistoryVideoSearch.Name = "btnHistoryVideoSearch";
btnHistoryVideoSearch.Size = new Size(210, 56);
btnHistoryVideoSearch.TabIndex = 5;
btnHistoryVideoSearch.Text = "历史记录视频搜索";
btnHistoryVideoSearch.UseVisualStyleBackColor = true;
btnHistoryVideoSearch.Click += btnHistoryVideoSearch_Click;
//
// label3
//
label3.AutoSize = true;
label3.ForeColor = Color.Gray;
label3.Location = new Point(288, 117);
label3.Name = "label3";
label3.Size = new Size(88, 26);
label3.TabIndex = 4;
label3.Text = "结束时间";
//
// PdtHistorySearchEnd
//
PdtHistorySearchEnd.FontSize = ReaLTaiizor.Extension.Poison.PoisonDateTimeSize.Medium;
PdtHistorySearchEnd.Location = new Point(268, 155);
PdtHistorySearchEnd.MinimumSize = new Size(0, 29);
PdtHistorySearchEnd.Name = "PdtHistorySearchEnd";
PdtHistorySearchEnd.Size = new Size(131, 32);
PdtHistorySearchEnd.TabIndex = 3;
//
// label2
//
label2.AutoSize = true;
label2.ForeColor = Color.Gray;
label2.Location = new Point(75, 116);
label2.Name = "label2";
label2.Size = new Size(88, 26);
label2.TabIndex = 2;
label2.Text = "开始时间";
//
// label1
//
label1.AutoSize = true;
label1.Font = new Font("Microsoft YaHei UI", 15.75F, FontStyle.Bold, GraphicsUnit.Point, 134);
label1.ForeColor = Color.Gray;
label1.Location = new Point(6, 7);
label1.Name = "label1";
label1.Size = new Size(96, 28);
label1.TabIndex = 1;
label1.Text = "搜索条件";
//
// PdtHistorySearchStart
//
PdtHistorySearchStart.FontSize = ReaLTaiizor.Extension.Poison.PoisonDateTimeSize.Medium;
PdtHistorySearchStart.Location = new Point(55, 154);
PdtHistorySearchStart.MinimumSize = new Size(0, 29);
PdtHistorySearchStart.Name = "PdtHistorySearchStart";
PdtHistorySearchStart.Size = new Size(131, 32);
PdtHistorySearchStart.TabIndex = 0;
//
// btnPlayHistoryVideo
//
btnPlayHistoryVideo.Location = new Point(506, 14);
btnPlayHistoryVideo.Name = "btnPlayHistoryVideo";
btnPlayHistoryVideo.Size = new Size(116, 45);
btnPlayHistoryVideo.TabIndex = 8;
btnPlayHistoryVideo.Text = "button1";
btnPlayHistoryVideo.UseVisualStyleBackColor = true;
btnPlayHistoryVideo.Click += btnPlayHistoryVideo_Click;
//
// videoView1
//
videoView1.BackColor = Color.Black;
videoView1.Location = new Point(506, 65);
videoView1.MediaPlayer = null;
videoView1.Name = "videoView1";
videoView1.Size = new Size(1394, 847);
videoView1.TabIndex = 7;
videoView1.Text = "videoView1";
//
// tabPage3
//
tabPage3.Controls.Add(metroProgressBar1);
tabPage3.Controls.Add(DownloadProgressBar);
tabPage3.Controls.Add(btnStopLoadVideo);
tabPage3.Controls.Add(btnLoadVideo);
@@ -112,6 +626,36 @@
tabPage3.Text = "相机设置";
tabPage3.UseVisualStyleBackColor = true;
//
// metroProgressBar1
//
metroProgressBar1.BackgroundColor = Color.FromArgb(238, 238, 238);
metroProgressBar1.BorderColor = Color.FromArgb(238, 238, 238);
metroProgressBar1.DisabledBackColor = Color.FromArgb(238, 238, 238);
metroProgressBar1.DisabledBorderColor = Color.FromArgb(238, 238, 238);
metroProgressBar1.DisabledProgressColor = Color.FromArgb(120, 65, 177, 225);
metroProgressBar1.IsDerivedStyle = true;
metroProgressBar1.Location = new Point(252, 181);
metroProgressBar1.Maximum = 100;
metroProgressBar1.Minimum = 0;
metroProgressBar1.Name = "metroProgressBar1";
metroProgressBar1.Orientation = ReaLTaiizor.Enum.Metro.ProgressOrientation.Horizontal;
metroProgressBar1.ProgressColor = Color.FromArgb(65, 177, 225);
metroProgressBar1.Size = new Size(125, 23);
metroProgressBar1.Style = ReaLTaiizor.Enum.Metro.Style.Light;
metroProgressBar1.StyleManager = null;
metroProgressBar1.TabIndex = 4;
metroProgressBar1.Text = "metroProgressBar1";
metroProgressBar1.ThemeAuthor = "Taiizor";
metroProgressBar1.ThemeName = "MetroLight";
metroProgressBar1.Value = 0;
//
// DownloadProgressBar
//
DownloadProgressBar.Location = new Point(247, 124);
DownloadProgressBar.Name = "DownloadProgressBar";
DownloadProgressBar.Size = new Size(130, 23);
DownloadProgressBar.TabIndex = 3;
//
// btnStopLoadVideo
//
btnStopLoadVideo.Location = new Point(409, 54);
@@ -142,22 +686,19 @@
btnNVRLogin.UseVisualStyleBackColor = true;
btnNVRLogin.Click += btnNVRLogin_Click;
//
// DownTimer
// DownloadProgressBarMain
//
DownTimer.Tick += DownTimer_Tick;
//
// DownloadProgressBar
//
DownloadProgressBar.Location = new Point(247, 124);
DownloadProgressBar.Name = "DownloadProgressBar";
DownloadProgressBar.Size = new Size(130, 23);
DownloadProgressBar.TabIndex = 3;
DownloadProgressBarMain.Location = new Point(1, 324);
DownloadProgressBarMain.Name = "DownloadProgressBarMain";
DownloadProgressBarMain.Size = new Size(862, 23);
DownloadProgressBarMain.TabIndex = 6;
//
// MainApp
//
AutoScaleDimensions = new SizeF(8F, 17F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(1920, 1030);
Controls.Add(statusStrip1);
Controls.Add(materialTabControl1);
DrawerTabControl = materialTabControl1;
Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Bold, GraphicsUnit.Point, 134);
@@ -168,12 +709,30 @@
WindowState = FormWindowState.Maximized;
Load += MainApp_Load;
materialTabControl1.ResumeLayout(false);
tabPage1.ResumeLayout(false);
materialCard4.ResumeLayout(false);
materialCard4.PerformLayout();
materialCard3.ResumeLayout(false);
materialCard3.PerformLayout();
materialCard2.ResumeLayout(false);
materialCard2.PerformLayout();
tabPage2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)dataGridView1).EndInit();
materialCard1.ResumeLayout(false);
materialCard1.PerformLayout();
((System.ComponentModel.ISupportInitialize)videoView1).EndInit();
tabPage3.ResumeLayout(false);
ResumeLayout(false);
PerformLayout();
}
#endregion
private StatusStrip statusStrip1;
private ImageList imageList1;
private ReaLTaiizor.Controls.MaterialTabControl materialTabControl1;
private TabPage tabPage1;
private TabPage tabPage2;
@@ -181,7 +740,49 @@
private Button btnNVRLogin;
private Button btnLoadVideo;
private Button btnStopLoadVideo;
private System.Windows.Forms.Timer DownTimer;
private ProgressBar DownloadProgressBar;
private ReaLTaiizor.Controls.MetroProgressBar metroProgressBar1;
private LibVLCSharp.WinForms.VideoView videoView1;
private Button btnPlayHistoryVideo;
private ReaLTaiizor.Controls.MaterialCard materialCard1;
private ReaLTaiizor.Controls.PoisonDateTime PdtHistorySearchStart;
private Label label1;
private Button btnHistoryVideoSearch;
private Label label3;
private ReaLTaiizor.Controls.PoisonDateTime PdtHistorySearchEnd;
private Label label2;
private Label label4;
private TextBox txtSearchCode;
private DataGridView dataGridView1;
private Button btnStopHistoryPlay;
private ReaLTaiizor.Controls.MaterialCard materialCard2;
private Label label5;
private Label label6;
private TextBox txtRUInBagCode;
private Label label7;
private TextBox txtRURawName;
private ReaLTaiizor.Controls.MaterialCard materialCard3;
private Label label8;
private ReaLTaiizor.Controls.MaterialCard materialCard4;
private Label label9;
private TextBox txtIORawName;
private Label label10;
private TextBox txtIOOutBagCode;
private Label label11;
private Label label12;
private Label label13;
private TextBox txtInBoxCount;
private TextBox txtOutBoxCount;
private Label label14;
private TextBox txtRemainBoxCount;
private Label label15;
private Label label16;
private TextBox txtIOInBagCode;
private Label label18;
private TextBox txtIOOutputTime;
private Label label17;
private TextBox txtIOInputTime;
private Button btnTestAction;
private ProgressBar DownloadProgressBarMain;
}
}

View File

@@ -1,24 +1,32 @@
using FATrace.Com;
using FATrace.HKNetLib.Hardware;
using FATrace.HKNetLib.Wrapper;
using FATrace.Model;
using LibVLCSharp.Shared;
using ReaLTaiizor.Forms;
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
namespace FATrace.OEMApp
{
public partial class MainApp : MaterialForm
{
/// <summary>
/// 海康Camera 客户端
/// </summary>
public HkCamera HkCameraClient { get; set; }
/// <summary>
/// 通道集合
/// </summary>
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 96, ArraySubType = UnmanagedType.U4)]
private int[] iChannelNum;
// 历史表列头中文映射
private readonly Dictionary<string, string> _historyHeaderMap = new Dictionary<string, string>
{
{ 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()
{
@@ -31,8 +39,20 @@ namespace FATrace.OEMApp
HkCameraClient = new HkCamera();
//保存SDK日志
CHCNetSDK.NET_DVR_SetLogToFile(3, "C:\\SdkLog\\", true);
iChannelNum = new int[96];
//读取配置
//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;
@@ -56,24 +76,43 @@ namespace FATrace.OEMApp
}
}
/// <summary>
/// 当前登录用户
/// </summary>
public string CurUserName { get; set; }
#region
/// <summary>
/// 当前播放的视频
/// </summary>
public string CurrentVideoPath { get; set; }
#endregion
#region
/// <summary>
/// 内包条码
/// </summary>
public string CurInBagCode { get; set; }
/// <summary>
/// 内包条码 原料名称
/// </summary>
public string CurInBagRawName { get; set; }
#endregion
private void btnNVRLogin_Click(object sender, EventArgs e)
{
//var Result = HkCameraClient.Login(new CameraLoginInfo()
//{
// IP = "192.168.1.168",
// Port = 8000,
// UserName = "admin",
// Password = "glico@2025",
//});
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");
//if (Result)
//{
// MessageBox.Show("登录成功");
// return;
//}
//MessageBox.Show("登录失败");
var result = HkCameraClient.Sdk_NET_DVR_Login_V30("192.168.1.168", 8000, "admin", "glico@2025");
var result = HkCameraClient.Sdk_NET_DVR_Login_V30(HkCameraClient.NVR_IP, HkCameraClient.NVR_Port, HkCameraClient.NVR_UserName, HkCameraClient.NVR_Pw);
if (result)
{
MessageBox.Show("登录成功");
@@ -83,24 +122,35 @@ namespace FATrace.OEMApp
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, $"D:\\Downtest_Channel{DateTime.Now.ToString("yyyy-MM-dd HHmmss")}.mp4");
var Result = HkCameraClient.Sdk_NET_DVR_GetFileByTime_V40(DateTime.Now.AddMinutes(-5), DateTime.Now, CurrentVideoPath);
if (Result.Result)
{
DownTimer.Interval = 2000;
DownTimer.Enabled = true;
HkCameraClient.NVRLoadVideoProcessEventHandler -= HkCameraClient_NVRLoadVideoProcessEventHandler;
HkCameraClient.NVRLoadVideoProcessEventHandler += HkCameraClient_NVRLoadVideoProcessEventHandler;
// 进度轮询已在库方法内部自动启动,这里无需再次调用
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)
@@ -151,59 +201,266 @@ namespace FATrace.OEMApp
private void btnStopLoadVideo_Click(object sender, EventArgs e)
{
//if (m_lDownHandle < 0)
//{
// return;
//}
//if (!CHCNetSDK.NET_DVR_StopGetFile(m_lDownHandle))
//{
// iLastErr = CHCNetSDK.NET_DVR_GetLastError();
// str = "NET_DVR_StopGetFile failed, error code= " + iLastErr; //下载控制失败,输出错误号
// MessageBox.Show(str);
// return;
//}
//timerDownload.Stop();
//MessageBox.Show("The downloading has been stopped succesfully!");
//m_lDownHandle = -1;
//DownloadProgressBar.Value = 0;
//btnStopDownload.Enabled = true;
var Result = HkCameraClient.Sdk_NET_DVR_StopGetFile();
if (Result.Result)
{
MessageBox.Show($"[暂停成功] {Result.Msg}");
}
else
{
MessageBox.Show($"[暂停失败] {Result.Msg}");
}
}
private void DownTimer_Tick(object sender, EventArgs e)
#region
private LibVLC _libVLC;
private MediaPlayer _mediaPlayer;
/// <summary>
/// 初始化MediaPlay
/// </summary>
private void InitMediaPlayer()
{
DownloadProgressBar.Maximum = 100;
DownloadProgressBar.Minimum = 0;
_libVLC = new LibVLC();
_mediaPlayer = new MediaPlayer(_libVLC);
videoView1.MediaPlayer = _mediaPlayer;
int iPos = 0;
}
//获取下载进度
iPos = HkCameraClient.Sdk_NET_DVR_GetDownloadPos(HkCameraClient.m_lDownHandle);
if ((iPos > DownloadProgressBar.Minimum) && (iPos < DownloadProgressBar.Maximum))
#endregion
/// <summary>
/// 测试手动播放
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnPlayHistoryVideo_Click(object sender, EventArgs e)
{
string path = "D:\\Downtest_Channel2025-09-12 102454.mp4";
_mediaPlayer.Play(new Media(_libVLC, path));
}
/// <summary>
/// 停止播放当前的视频文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStopHistoryPlay_Click(object sender, EventArgs e)
{
_mediaPlayer.Stop();
}
/// <summary>
/// 清理播放资源
/// </summary>
private void CleanMediaPlay()
{
_mediaPlayer.Stop();
_mediaPlayer.Dispose();
_libVLC.Dispose();
}
#region
/// <summary>
/// 采用 BindingList + BindingSource使数据变化自动触发 UI 刷新
/// </summary>
private BindingSource historyBindingSource { get; set; }
private BindingList<VideoAction> historyVideoBindingList { get; set; }
/// <summary>
/// 初始化历史记录表格的数据绑定
/// </summary>
private void InitHistoryGridBinding()
{
historyBindingSource = new BindingSource();
historyVideoBindingList = new BindingList<VideoAction>();
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<VideoAction>();
if (!string.IsNullOrEmpty(txtSearchCode.Text.Trim()))
{
DownloadProgressBar.Value = iPos;
query = query.Where(a => a.Code!.Contains(txtSearchCode.Text.Trim()));
}
query.Where(a => a.CreateTime >= PdtHistorySearchStart.Value && a.CreateTime <= PdtHistorySearchEnd.Value);
if (iPos == 100) //下载完成
// 拉取结果并刷新绑定列表
var resultList = query.ToList();
// 暂停变更通知,批量更新提高效率
historyVideoBindingList.RaiseListChangedEvents = false;
historyVideoBindingList.Clear();
foreach (var item in resultList)
{
DownloadProgressBar.Value = iPos;
if (!CHCNetSDK.NET_DVR_StopGetFile(HkCameraClient.m_lDownHandle))
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}");
}
}
/// <summary>
/// 双击行,播放该行对应的视频文件
/// </summary>
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)
{
MessageBox.Show($"[NET_DVR_StopGetFile] 执行失败:{HkCameraClient.GetLastError()}");
return;
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);
}
}
/// <summary>
/// 按照 _historyHeaderMap 配置列头、隐藏不需要的列,并做常用格式化
/// </summary>
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;
}
HkCameraClient.m_lDownHandle = -1;
DownTimer.Stop();
}
if (iPos == 200) //网络异常,下载失败
// 选中整行,易于操作
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridView1.MultiSelect = false;
dataGridView1.ReadOnly = true;
dataGridView1.AllowUserToAddRows = false;
}
#endregion
/// <summary>
/// 操作结束
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnTestAction_Click(object sender, EventArgs e)
{
var Result = HkCameraClient.Sdk_NET_DVR_GetFileByTime_V40(DateTime.Now.AddMinutes(-5), DateTime.Now, NVRCom.GetVideoName(HkCameraClient.NVRVideoSavePath, "cODE"));
if (Result.Result)
{
MessageBox.Show("The downloading is abnormal for the abnormal network!");
DownTimer.Stop();
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}");
}
}
/// <summary>
/// Handles the event triggered when the NVR (Network Video Recorder) completes loading a video.
/// Video下载完成
/// </summary>
/// <param name="sender">The source of the event. This can be <see langword="null"/> if the event is not raised by a specific object.</param>
/// <param name="e">A string containing information about the completed video load operation.</param>
private void HkCameraClient_NVRLoadVideoCompleteEventHandler(object? sender, string e)
{
//先保存当前的信息
var returnValues = FSqlContext.FDb.Insert<OEMRawUse>(new OEMRawUse()
{
InBagCode=CurInBagCode,
RawName=CurInBagRawName,
User= CurUserName,
UrlState=false,
VideoUrl="",//暂时无法获取URL因为服务器还没有扫描到文件信息
}).ExecuteInserted();
}
}
}

View File

@@ -117,6 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>134, 17</value>
</metadata>
<metadata name="imageList1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
@@ -124,8 +127,8 @@
<value>
AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs
LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAfCEAAAJNU0Z0AUkBTAIBAQgB
AAF4AQABeAEAARABAAEQAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABQAMAATADAAEBAQABIAYAATD/
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAaiEAAAJNU0Z0AUkBTAIBAQgB
AAEwAQEBMAEBARABAAEQAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABQAMAATADAAEBAQABIAYAATD/
AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AFIAAdsBlgESAf8B2wGWARIB/wQAAxIBGANJAYgB
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wQAAdsB
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8UAAHbAZYB
@@ -159,120 +162,117 @@
+QH/AgAC/wP1Af8CAAL/AgAC/wIAAv8CAAL/AgAC/wIAAv8CxwH2Af8D9QH/AgAC/wIAAfwB/wJoAe4B
/wQAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xQAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8B2wGWARIB/wHbAZYBEgH/AlEBUAGfBAABkgF2AUQB8wHbAZYBEgH/AdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wQAAdsBlgESAf8B2wGWARIB/ywAAdsBlgESAf8B2wGWARIB
/wNGAX8CAgFMAf8CAgHoAf8CAAHwAf8CiQH0Af8D9QH/AgAB9QH/AgAB9QH/A/UB/wIAAfUB/wIAAfUB
/wP1Af8CAAH1Af8CAAHwAf8CBgHrAf8CYgHqAf8DKgFAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8DSQGIBAADBgEIAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/1AAAdsB
lgESAf8B2wGWARIB/ywAAdsBlgESAf8B2wGWARIB/wNGAX8CAgFAAf8CIAHdAf8CCgHgAf8CAwHhAf8D
9QH/AgMB4QH/AgMB4QH/A/UB/wIDAeEB/wP1Af8D9QH/AgMB4QH/AiAB5AH/AiAB3QH/AlsB5AH/BAAD
CQEMAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/DAADDAEQAdsBlgESAf8B2wGWARIB/wFpAWMBWgHfFAAB
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8DKAE8BAADUgGgAdsB
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/BAAB2wGWARIB/wHbAZYB
EgH/KAADHQEoAdsBlgESAf8B2wGWARIB/wNGAX8CAgE0Af8COAHUAf8COAHYAf8COAHbAf8CAwHRAf8D
9QH/AgMB0QH/A/UB/wIDAdEB/wP1Af8CDQHSAf8COAHbAf8COAHYAf8COAHUAf8CVAHeAf8IAAHbAZYB
EgH/AdsBlgESAf8UAAHbAZYBEgH/AdsBlgESAf8UAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8EAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/CAAB2wGWARIB/wHbAZYBEgH/DAAB2wGWARIB
/wHbAZYBEgH/AdsBlgESAf8MAAHbAZYBEgH/AdsBlgESAf8EAANGAX8CAgEpAf8CUQHNAf8CTwHUAf8C
TwHVAf8CTwHVAf8D9QH/Ao0B4QH/Ak8B1QH/A/UB/wP1Af8CTwHVAf8CTwHVAf8CTwHUAf8CUQHNAf8C
TAHXAf8EAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/DAAB2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8B2wGWARIB/xAAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/AdsBlgESAf8B2wGWARIB/wQAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/AdsBlgESAf8IAAHbAZYBEgH/AdsBlgESAf8CXwFcAcgIAAHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/wgAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/BAADRgF/AgEBHgH/AmgBzAH/AmgB0QH/AmgB
0gH/AmgB0gH/AmgB0gH/A/UB/wJoAdIB/wP1Af8CaAHSAf8CaAHSAf8CaAHSAf8CaAHRAf8CaAHMAf8C
QwHKAf8EAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8EAAHbAZYBEgH/AdsB
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8DUgGgDAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/BAAB2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wwAAdsBlgESAf8MAAHbAZYBEgH/AdsB
lgESAf8B2wGWARIB/wwAAdsBlgESAf8IAANGAX8CAQEUAf8CgAHOAf8CgAHRAf8CgAHSAf8CgAHSAf8C
gAHSAf8D9QH/A/UB/wKAAdIB/wKAAdIB/wKAAdIB/wKAAdIB/wKAAdEB/wKAAc4B/wI5AboB/wQAAdsB
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xAAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B
lgESAf8B2wGWARIB/wHbAZYBEgH/AlEBUAGfBAACbwFgAfMB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8EAAHbAZYBEgH/AdsBlgESAf8sAAHbAZYBEgH/AdsBlgESAf8D
RgF/AgIBTAH/AgIB6AH/AgAB8AH/AokB9AH/A/UB/wIAAfUB/wIAAfUB/wP1Af8CAAH1Af8CAAH1Af8D
9QH/AgAB9QH/AgAB8AH/AgYB6wH/AmIB6gH/AyoBQAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/A0kBiAQAAwYBCAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf9QAAHbAZYB
EgH/AdsBlgESAf8sAAHbAZYBEgH/AdsBlgESAf8DRgF/AgIBQAH/AiAB3QH/AgoB4AH/AgMB4QH/A/UB
/wIDAeEB/wIDAeEB/wP1Af8CAwHhAf8D9QH/A/UB/wIDAeEB/wIgAeQB/wIgAd0B/wJbAeQB/wQAAwkB
DAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wwAAwwBEAHbAZYBEgH/AdsBlgESAf8CYwFdAd8UAAHbAZYB
EgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wMoATwEAANSAaAB2wGWARIB
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8EAAHbAZYBEgH/AdsBlgESAf8o
AAMdASgB2wGWARIB/wHbAZYBEgH/A0YBfwICATQB/wI4AdQB/wI4AdgB/wI4AdsB/wIDAdEB/wP1Af8C
AwHRAf8D9QH/AgMB0QH/A/UB/wINAdIB/wI4AdsB/wI4AdgB/wI4AdQB/wJUAd4B/wgAAdsBlgESAf8B
2wGWARIB/xQAAdsBlgESAf8B2wGWARIB/xQAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wQAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8cAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xgAAyoB
PwIEAU8B/wKWAb8B/wKXAdYB/wKXAdYB/wKXAdYB/wKXAdYB/wKXAdYB/wP1Af8ClwHWAf8ClwHWAf8C
lwHWAf8ClwHWAf8ClwHWAf8ClwHTAf8CCgF3Af8IAAMyAU8EAANaAb8B2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8DWgG/HAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8IAAHbAZYBEgH/AdsBlgESAf8MAAHbAZYBEgH/AdsB
lgESAf8B2wGWARIB/wwAAdsBlgESAf8B2wGWARIB/wQAA0YBfwICASkB/wJRAc0B/wJPAdQB/wJPAdUB
/wJPAdUB/wP1Af8CjQHhAf8CTwHVAf8D9QH/A/UB/wJPAdUB/wJPAdUB/wJPAdQB/wJRAc0B/wJMAdcB
/wQAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8MAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/EAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/wHbAZYBEgH/BAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8B2wGWARIB/xwAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/HAACCAFoAf8CVwFpAf8CrgHGAf8C
rwHeAf8CrwHeAf8CrwHeAf8CrwHeAf8CrwHeAf8CrwHeAf8CrwHeAf8CrwHeAf8CrwHeAf8CrwHeAf8C
CAF1Af8CCgFuAf8UAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/yAAA1gBuAHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/BAAB2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xwAAdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/HAADIQEvAggBZgH/AggBawH/AggBbgH/AggBbgH/AggBbgH/AggBbgH/AggBbgH/AggBbgH/AggB
bgH/AggBbgH/AggBbgH/AggBagH/AhABfgH/GAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8kAAJaAVgB
twHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8MAAHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8DSQGHHAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8kAAMqAT8D
RgF/A0YBfwNGAX8DRgF/A0YBfwNGAX8DRgF/A0YBfwNGAX9oAAJiAV0B3AHbAZYBEgH/AdsBlgESAf8D
PQFoJAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8MAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xgAAwwB
EAJHAUYBgAMtAUQUAAM6AWADOgFgbAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/HAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/GAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/CAACWwFZAcAB
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/EAACWwFZAcAB2wGWARIB/wHbAZYB
EgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8D
KgFAGAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8IAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/AdsBlgESAf8YAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/AdsBlgESAf8BqQF9ATUB+BAAAaUBhQE3AfcB2wGWARIB/wwAAzMBUAHbAZYBEgH/BAAB
2wGWARIB/xAAAdsBlgESAf8QAAJbAVkBwCAAAkcBRgGAAyoBQBQAAdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/AdsBlgESAf8MAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8IAAHbAZYB
EgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wQAAdsB
lgESAf8UAAHbAZYBEgH/BAAB2wGWARIB/xAAAdsBlgESAf8QAAJbAVkBwCAAAkcBRgGAAyoBQBAAAdsB
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xAAAdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/AdsBlgESAf8IAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8QAAMSARgB
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/BAAB2wGWARIB/xQAAdsBlgESAf8E
AAHbAZYBEgH/EAAB2wGWARIB/xAAAlsBWQHACAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/BAACRwFGAYADKgFAEAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xQAAz0B
aAHbAZYBEgH/AdsBlgESAf8EAAHbAZYBEgH/AzoBYAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/GAADGAEgAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8IAAHbAZYBEgH/EAAB2wGWARIB
/wQAAdsBlgESAf8QAAHbAZYBEgH/EAACWwFZAcAgAAJHAUYBgAMqAUAQAAHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/ygAAdsBlgESAf8IAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xwAAdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/DAABdAFsAVcB5wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8E
AAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8UAAJbAVkBwAgAAdsBlgESAf8B
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AkcBRgGAAyoBQBAAAdsBlgESAf8B
kwGAAUEB9ygAAdsBlgESAf8B2wGWARIB/wgAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/HAAB2wGWARIB
/wHbAZYBEgH/AdsBlgESAf9QAAJbAVkBwCAAAkcBRgGAAyoBQBAAAdsBlgESAf8oAAHbAZYBEgH/AdsB
lgESAf8B2wGWARIB/wgAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/HAAB2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8MAAJBAUABcAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8EAAHbAZYB
EgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8UAAJbAVkBwAgAAlsBWQHAAlsBWQHAAlsB
WQHADAACRwFGAYADKgFAGAAB2wGWARIB/wHbAZYBEgH/GAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/wQAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8cAAHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/wHbAZYBEgH/CAAB2wGWARIB/xAAAdsBlgESAf8EAAHbAZYBEgH/DAADKgFAAdsBlgESAf8Q
AAJbAVkBwAgAAzoBYAM6AWADOgFgDAACRwFGAYADKgFAFAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/xAAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8DDAEQAwYBCAHbAZYBEgH/AdsB
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8UAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/AdsBlgESAf8EAAHbAZYBEgH/FAAB2wGWARIB/wQAAdsBlgESAf8QAAHbAZYBEgH/EAACWwFZAcAg
AAJHAUYBgAMqAUAUAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8IAAHbAZYB
EgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8IAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB
lgESAf8B2wGWARIB/wgAAdsBlgESAf8B2wGWARIB/wJfAVwByAgAAdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/CAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8EAANGAX8CAQEeAf8CaAHMAf8CaAHRAf8CaAHSAf8C
aAHSAf8CaAHSAf8D9QH/AmgB0gH/A/UB/wJoAdIB/wJoAdIB/wJoAdIB/wJoAdEB/wJoAcwB/wJDAcoB
/wQAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wQAAdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wNSAaAMAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8EAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/DAAB2wGWARIB/wwAAdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/DAAB2wGWARIB/wgAA0YBfwIBARQB/wKAAc4B/wKAAdEB/wKAAdIB/wKAAdIB/wKAAdIB
/wP1Af8D9QH/AoAB0gH/AoAB0gH/AoAB0gH/AoAB0gH/AoAB0QH/AoABzgH/AjkBugH/BAAB2wGWARIB
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wQAAdsBlgESAf8UAAHbAZYBEgH/BAAB
2wGWARIB/xAAAdsBlgESAf8QAAJbAVkBwAJpAV0B6AMYASADGAEgAxgBIAMYASADGAEgAxgBIAMYASAB
2wGWARIB/wMqAUAYAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/A1YBsAgAAdsBlgESAf8B
2wGWARIB/wHbAZYBEgH/GAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8QAAHbAZYBEgH/AdsBlgESAf8QAAHbAZYBEgH/BAAB
2wGWARIB/xAAAdsBlgESAf8UAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/yAAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8B2wGWARIB/ygAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/AdsBlgESAf8B2wGWARIB/xgAAdsBlgESAf8B2wGWARIB/wMqAUAB2wGWARIB/wHbAZYBEgH/CAAB
2wGWARIB/wHbAZYBEgH/AWQBYQFbAdgCWwFZAcAB2wGWARIB/wHbAZYBEgH/YAAB2wGWARIB/wHbAZYB
EgH/AdsBlgESAf8B2wGWARIB/yQAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/DAAB2wGWARIB/wHbAZYB
EgH/AdsBlgESAf8YAAJHAUYBgAFpAWMBWgHfAlgBVgGzFAABaQFjAVoB3wFpAWMBWgHfAwYBCKAAAxgB
IAwAAlEBUAGfVAABQgFNAT4HAAE+AwABKAMAAUADAAEwAwABAQEAAQEFAAGAAQEWAAP/gQAB/wHyAQAB
gAH4AQ8BwAEBAf8BwAEAAYAB4AEDAYABAAH/AewBAAGAAcMB4wGAAQAB+AHMAQABgAHPAfkCAAH4AcAB
AAGAAY8B+QIAAdABQwEAAYABnwH8AgABgAEPAoABnwH8AgABAgEHAv8BnwH8AgABhwEPAoABnwH4AgAB
zwGfAQABgAHOATkCAAGHAQ8BAAGAAcYBMQIAAYIBBwEAAYAB7gE7AgABgAEPAQABgAH+AT8CAAHQAX8B
AAGAAf4BPwGAAQAB+AH/AQABgAH+AT8BgAEBAfgB/wGBAcAB/gE/AeABBwL/Af4BHwHxAccB4wHnA/8B
BwHwAQ8BwQGBAeABAwHxAYMB8AEHAZwBvQHvAfMB4QHBAYABAAG+Ab0B7wHzAcEB4QGDAcABvgG9AewB
EwHDAeIBBwHgAd4BvQHvAfMBxwH+AccB8QHAAYMB7AEDAc8B/AHHAfEC/wHvAfMB3wH4AccB8QHAAYMB
7AFzAfMB8AGHAfAB3gG5AewBcwHhAeABAwHgAb4BvQHvAfMB4AHBAYABAAG+Ab0B4AEDAfABYwHwAQcB
ngG9AfABBwH4AT8B8AEPAcEBgQL/Af4BHwHxAccC4wT/Af0B3wL/Cw==
lgESAf8B2wGWARIB/wHbAZYBEgH/EAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/BAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xwAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/GAADKgE/AgQB
TwH/ApYBvwH/ApcB1gH/ApcB1gH/ApcB1gH/ApcB1gH/ApcB1gH/A/UB/wKXAdYB/wKXAdYB/wKXAdYB
/wKXAdYB/wKXAdYB/wKXAdMB/wIKAXcB/wgAAzIBTwQAA1oBvwHbAZYBEgH/AdsBlgESAf8B2wGWARIB
/wNaAb8cAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/AdsBlgESAf8EAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/HAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8cAAIIAWgB/wJXAWkB/wKuAcYB/wKvAd4B
/wKvAd4B/wKvAd4B/wKvAd4B/wKvAd4B/wKvAd4B/wKvAd4B/wKvAd4B/wKvAd4B/wKvAd4B/wIIAXUB
/wIKAW4B/xQAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/IAADWAG4AdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8EAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/HAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8c
AAMhAS8CCAFmAf8CCAFrAf8CCAFuAf8CCAFuAf8CCAFuAf8CCAFuAf8CCAFuAf8CCAFuAf8CCAFuAf8C
CAFuAf8CCAFuAf8CCAFqAf8CEAF+Af8YAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/yQAAloBWAG3AdsB
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wwAAdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/AdsBlgESAf8B2wGWARIB/wNJAYccAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/yQAAyoBPwNGAX8D
RgF/A0YBfwNGAX8DRgF/A0YBfwNGAX8DRgF/A0YBf2gAAmIBXQHcAdsBlgESAf8B2wGWARIB/wM9AWgk
AAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wwAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/GAADDAEQAkcB
RgGAAy0BRBQAAzoBYAM6AWBsAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8c
AAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8YAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8IAAJbAVkBwAHbAZYB
EgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8QAAJbAVkBwAHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wMqAUAY
AAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wgAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/xgAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8B2wGWARIB/wJ8AVMB+BAAAXkBbwFRAfcB2wGWARIB/wwAAzMBUAHbAZYBEgH/BAAB2wGWARIB
/xAAAdsBlgESAf8QAAJbAVkBwCAAAkcBRgGAAyoBQBQAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8MAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8IAAHbAZYBEgH/AdsB
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wQAAdsBlgESAf8U
AAHbAZYBEgH/BAAB2wGWARIB/xAAAdsBlgESAf8QAAJbAVkBwCAAAkcBRgGAAyoBQBAAAdsBlgESAf8B
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xAAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8IAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8QAAMSARgB2wGWARIB
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/BAAB2wGWARIB/xQAAdsBlgESAf8EAAHbAZYB
EgH/EAAB2wGWARIB/xAAAlsBWQHACAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/BAACRwFGAYADKgFAEAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xQAAz0BaAHbAZYB
EgH/AdsBlgESAf8EAAHbAZYBEgH/AzoBYAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/GAAD
GAEgAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8IAAHbAZYBEgH/EAAB2wGWARIB/wQAAdsB
lgESAf8QAAHbAZYBEgH/EAACWwFZAcAgAAJHAUYBgAMqAUAQAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB
/ygAAdsBlgESAf8IAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xwAAdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/DAACZQFcAecB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/BAAB2wGWARIB
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/FAACWwFZAcAIAAHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wJHAUYBgAMqAUAQAAHbAZYBEgH/Am8BUQH3KAAB
2wGWARIB/wHbAZYBEgH/CAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8cAAHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/1AAAlsBWQHAIAACRwFGAYADKgFAEAAB2wGWARIB/ygAAdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/CAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8cAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wwAAkEB
QAFwAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wQAAdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xQAAlsBWQHACAACWwFZAcACWwFZAcACWwFZAcAMAAJHAUYB
gAMqAUAYAAHbAZYBEgH/AdsBlgESAf8YAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/BAAB
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xwAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8IAAHbAZYBEgH/EAAB2wGWARIB/wQAAdsBlgESAf8MAAMqAUAB2wGWARIB/xAAAlsBWQHACAAD
OgFgAzoBYAM6AWAMAAJHAUYBgAMqAUAUAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/EAAB
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wMMARADBgEIAdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/AdsBlgESAf8B2wGWARIB/xQAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB
/wQAAdsBlgESAf8UAAHbAZYBEgH/BAAB2wGWARIB/xAAAdsBlgESAf8QAAJbAVkBwCAAAkcBRgGAAyoB
QBQAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wgAAdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wgAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/BAAB2wGWARIB/xQAAdsBlgESAf8EAAHbAZYBEgH/EAAB
2wGWARIB/xAAAlsBWQHAAmkBYAHoAxgBIAMYASADGAEgAxgBIAMYASADGAEgAxgBIAHbAZYBEgH/AyoB
QBgAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8DVgGwCAAB2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8YAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/AdsBlgESAf8B2wGWARIB/xAAAdsBlgESAf8B2wGWARIB/xAAAdsBlgESAf8EAAHbAZYBEgH/EAAB
2wGWARIB/xQAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
lgESAf8B2wGWARIB/wHbAZYBEgH/IAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
EgH/KAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB
/wHbAZYBEgH/GAAB2wGWARIB/wHbAZYBEgH/AyoBQAHbAZYBEgH/AdsBlgESAf8IAAHbAZYBEgH/AdsB
lgESAf8CXwFbAdgCWwFZAcAB2wGWARIB/wHbAZYBEgH/YAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B
2wGWARIB/yQAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/DAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8Y
AAJHAUYBgAJjAV0B3wJYAVYBsxQAAmMBXQHfAmMBXQHfAwYBCKAAAxgBIAwAAlEBUAGfVAABQgFNAT4H
AAE+AwABKAMAAUADAAEwAwABAQEAAQEFAAGAAQEWAAP/gQAB/wHyAQABgAH4AQ8BwAEBAf8BwAEAAYAB
4AEDAYABAAH/AewBAAGAAcMB4wGAAQAB+AHMAQABgAHPAfkCAAH4AcABAAGAAY8B+QIAAdABQwEAAYAB
nwH8AgABgAEPAoABnwH8AgABAgEHAv8BnwH8AgABhwEPAoABnwH4AgABzwGfAQABgAHOATkCAAGHAQ8B
AAGAAcYBMQIAAYIBBwEAAYAB7gE7AgABgAEPAQABgAH+AT8CAAHQAX8BAAGAAf4BPwGAAQAB+AH/AQAB
gAH+AT8BgAEBAfgB/wGBAcAB/gE/AeABBwL/Af4BHwHxAccB4wHnA/8BBwHwAQ8BwQGBAeABAwHxAYMB
8AEHAZwBvQHvAfMB4QHBAYABAAG+Ab0B7wHzAcEB4QGDAcABvgG9AewBEwHDAeIBBwHgAd4BvQHvAfMB
xwH+AccB8QHAAYMB7AEDAc8B/AHHAfEC/wHvAfMB3wH4AccB8QHAAYMB7AFzAfMB8AGHAfAB3gG5AewB
cwHhAeABAwHgAb4BvQHvAfMB4AHBAYABAAG+Ab0B4AEDAfABYwHwAQcBngG9AfABBwH4AT8B8AEPAcEB
gQL/Af4BHwHxAccC4wT/Af0B3wL/Cw==
</value>
</data>
<metadata name="DownTimer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>134, 17</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>

View File

@@ -0,0 +1,65 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace FATrace.OEMApp.Model.Jellyfin
{
/// <summary>
/// Jellyfin /Items 接口响应模型
/// </summary>
public sealed class JellyfinItemsResponse
{
/// <summary>
/// 返回的项目集合
/// </summary>
[JsonPropertyName("Items")]
public List<JellyfinItem> Items { get; set; } = new();
/// <summary>
/// 记录总数
/// </summary>
[JsonPropertyName("TotalRecordCount")]
public int TotalRecordCount { get; set; }
/// <summary>
/// 起始索引
/// </summary>
[JsonPropertyName("StartIndex")]
public int StartIndex { get; set; }
}
/// <summary>
/// Jellyfin 媒体条目最小字段模型
/// </summary>
public sealed class JellyfinItem
{
/// <summary>
/// 名称
/// </summary>
[JsonPropertyName("Name")]
public string? Name { get; set; }
/// <summary>
/// 服务器 ID
/// </summary>
[JsonPropertyName("ServerId")]
public string? ServerId { get; set; }
/// <summary>
/// 项目 ID
/// </summary>
[JsonPropertyName("Id")]
public string? Id { get; set; }
/// <summary>
/// 频道 ID可能为 null
/// </summary>
[JsonPropertyName("ChannelId")]
public string? ChannelId { get; set; }
/// <summary>
/// 类型,如 Movie、Series
/// </summary>
[JsonPropertyName("Type")]
public string? Type { get; set; }
}
}

View File

@@ -0,0 +1,245 @@
using System;
using System.Diagnostics;
using System.Net;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using FATrace.OEMApp.Model.Jellyfin;
using NLog;
using RestSharp;
namespace FATrace.OEMApp.Services
{
/// <summary>
/// Jellyfin 客户端配置选项
/// </summary>
public sealed class JellyfinClientOptions
{
/// <summary>
/// Jellyfin 基础地址例如http://192.168.0.30:8096
/// </summary>
public required string BaseUrl { get; init; }
/// <summary>
/// API 密钥(建议使用 Jellyfin 后台创建的密钥)。
/// </summary>
public required string ApiKey { get; init; }
/// <summary>
/// 超时时间,毫秒。默认 10000 毫秒。
/// </summary>
public int TimeoutMs { get; init; } = 10000;
/// <summary>
/// 是否将 api_key 作为查询参数传递。默认 true。
/// 若为 false则通过请求头 X-Emby-Token 传递。
/// </summary>
public bool UseQueryApiKey { get; init; } = true;
/// <summary>
/// 自定义 User-Agent。若为空将使用默认。
/// </summary>
public string? UserAgent { get; init; }
}
/// <summary>
/// Jellyfin HTTP 异常
/// </summary>
public sealed class JellyfinHttpException : Exception
{
public HttpStatusCode StatusCode { get; }
public string? ResponseContent { get; }
public JellyfinHttpException(string message, HttpStatusCode statusCode, string? content, Exception? inner = null)
: base(message, inner)
{
StatusCode = statusCode;
ResponseContent = content;
}
}
/// <summary>
/// Jellyfin API 客户端封装(基于 RestSharp
/// </summary>
public sealed class JellyfinClient : IJellyfinClient, IDisposable
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly RestClient _client;
private readonly JellyfinClientOptions _options;
private readonly JsonSerializerOptions _jsonOptions = new()
{
PropertyNameCaseInsensitive = true
};
public JellyfinClient(JellyfinClientOptions options)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
if (string.IsNullOrWhiteSpace(_options.BaseUrl))
throw new ArgumentException("BaseUrl 不能为空", nameof(options));
if (string.IsNullOrWhiteSpace(_options.ApiKey))
throw new ArgumentException("ApiKey 不能为空", nameof(options));
if (!Uri.TryCreate(_options.BaseUrl, UriKind.Absolute, out var baseUri))
{
throw new ArgumentException($"无效的 BaseUrl: {_options.BaseUrl}", nameof(options));
}
var restOptions = new RestClientOptions(baseUri)
{
MaxTimeout = _options.TimeoutMs,
ThrowOnAnyError = false,
// UseUnsafeSerializer = false // 保持默认 System.Text.Json
};
_client = new RestClient(restOptions);
// 默认头
_client.AddDefaultHeader("Accept", "application/json");
_client.AddDefaultHeader("Accept-Charset", "utf-8");
if (!string.IsNullOrWhiteSpace(_options.UserAgent))
{
_client.AddDefaultHeader("User-Agent", _options.UserAgent);
}
// 通过 Header 传递 Token若未使用 Query
if (!_options.UseQueryApiKey)
{
_client.AddDefaultHeader("X-Emby-Token", _options.ApiKey);
}
}
/// <summary>
/// 获取 Jellyfin Items 列表。
/// </summary>
/// <param name="parentId">父级目录/库 ID</param>
/// <param name="sortBy">排序字段(默认 DateCreated</param>
/// <param name="sortOrder">排序方向Ascending/Descending默认 Descending</param>
/// <param name="limit">限制返回数量,默认 10</param>
/// <param name="includeItemTypes">包含的类型(例如 Movie, Series默认 Movie</param>
/// <param name="cancellationToken">取消令牌</param>
public async Task<JellyfinItemsResponse> GetItemsAsync(
string parentId,
string sortBy = "DateCreated",
string sortOrder = "Descending",
int? limit = 10,
string includeItemTypes = "Movie",
CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(parentId))
throw new ArgumentException("parentId 不能为空", nameof(parentId));
var request = new RestRequest("Items", Method.Get);
request.AddQueryParameter("ParentId", parentId);
if (!string.IsNullOrWhiteSpace(sortBy)) request.AddQueryParameter("SortBy", sortBy);
if (!string.IsNullOrWhiteSpace(sortOrder)) request.AddQueryParameter("SortOrder", sortOrder);
if (limit.HasValue && limit.Value > 0) request.AddQueryParameter("Limit", limit.Value.ToString());
if (!string.IsNullOrWhiteSpace(includeItemTypes)) request.AddQueryParameter("IncludeItemTypes", includeItemTypes);
if (_options.UseQueryApiKey)
{
request.AddQueryParameter("api_key", _options.ApiKey);
}
return await ExecuteAndDeserializeAsync<JellyfinItemsResponse>(request, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// 仅返回 Items 列表的便捷方法。
/// </summary>
public async Task<System.Collections.Generic.IReadOnlyList<JellyfinItem>> GetItemsOnlyAsync(
string parentId,
string sortBy = "DateCreated",
string sortOrder = "Descending",
int? limit = 10,
string includeItemTypes = "Movie",
CancellationToken cancellationToken = default)
{
var resp = await GetItemsAsync(parentId, sortBy, sortOrder, limit, includeItemTypes, cancellationToken).ConfigureAwait(false);
return resp.Items ?? new System.Collections.Generic.List<JellyfinItem>();
}
private async Task<T> ExecuteAndDeserializeAsync<T>(RestRequest request, CancellationToken ct)
{
var sw = Stopwatch.StartNew();
try
{
Logger.Info("Jellyfin 请求: {Method} {Resource}", request.Method, request.Resource);
var response = await _client.ExecuteAsync(request, ct).ConfigureAwait(false);
sw.Stop();
if (!response.IsSuccessful)
{
Logger.Warn("Jellyfin 响应失败: Status={StatusCode}, Elapsed={Elapsed}ms, ContentLength={Length}",
(int)response.StatusCode, sw.ElapsedMilliseconds, response.Content?.Length ?? 0);
throw new JellyfinHttpException(
$"请求失败HTTP {(int)response.StatusCode} {response.StatusDescription}",
response.StatusCode,
response.Content,
response.ErrorException);
}
if (string.IsNullOrWhiteSpace(response.Content))
{
throw new JellyfinHttpException("响应内容为空", response.StatusCode, null);
}
var result = JsonSerializer.Deserialize<T>(response.Content, _jsonOptions);
if (result == null)
{
throw new JellyfinHttpException("响应反序列化失败,返回结果为 null", response.StatusCode, response.Content);
}
Logger.Debug("Jellyfin 调用成功: {Method} {Resource}, Elapsed={Elapsed}ms", request.Method, request.Resource, sw.ElapsedMilliseconds);
return result;
}
catch (OperationCanceledException)
{
sw.Stop();
Logger.Warn("Jellyfin 请求被取消: {Resource}, Elapsed={Elapsed}ms", request.Resource, sw.ElapsedMilliseconds);
throw;
}
catch (JellyfinHttpException)
{
// 已包含上下文,直接抛出
throw;
}
catch (Exception ex)
{
sw.Stop();
Logger.Error(ex, "Jellyfin 请求异常: {Resource}, Elapsed={Elapsed}ms", request.Resource, sw.ElapsedMilliseconds);
throw new JellyfinHttpException("请求执行异常", HttpStatusCode.InternalServerError, null, ex);
}
}
public void Dispose()
{
// RestClient 在新版本基于 HttpClient通常无需显式释放。
// 如需自定义 HttpMessageHandler可在此释放。
}
}
/// <summary>
/// Jellyfin 客户端接口,便于后续依赖注入与单元测试。
/// </summary>
public interface IJellyfinClient
{
Task<JellyfinItemsResponse> GetItemsAsync(
string parentId,
string sortBy = "DateCreated",
string sortOrder = "Descending",
int? limit = 10,
string includeItemTypes = "Movie",
CancellationToken cancellationToken = default);
Task<System.Collections.Generic.IReadOnlyList<JellyfinItem>> GetItemsOnlyAsync(
string parentId,
string sortBy = "DateCreated",
string sortOrder = "Descending",
int? limit = 10,
string includeItemTypes = "Movie",
CancellationToken cancellationToken = default);
}
}