初级的功能
This commit is contained in:
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace FATrace.Com
|
namespace FATrace.Com
|
||||||
{
|
{
|
||||||
@@ -16,9 +17,19 @@ namespace FATrace.Com
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="configKey"></param>
|
/// <param name="configKey"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string GetVideoName(string Path,string Code)
|
public static string GetVideoName(string basePath,string Code)
|
||||||
{
|
{
|
||||||
return $"{Path}\\{DateTime.Now.ToString("yyyy-MM-dd HHmmss")} {Code}.mp4";
|
// 清洗非法文件名字符,避免保存失败
|
||||||
|
string safeCode = Code;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var invalid = System.IO.Path.GetInvalidFileNameChars();
|
||||||
|
safeCode = new string(Code.Where(c => !invalid.Contains(c)).ToArray());
|
||||||
|
if (string.IsNullOrWhiteSpace(safeCode)) safeCode = "CODE";
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
return $"{basePath}\\{DateTime.Now.ToString("yyyy-MM-dd HHmmss")} {safeCode}.mp4";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using FATrace.HKNetLib.Hardware;
|
|||||||
using FATrace.Model;
|
using FATrace.Model;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.IO;
|
||||||
using static FATrace.HKNetLib.Hardware.CHCNetSDK;
|
using static FATrace.HKNetLib.Hardware.CHCNetSDK;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -445,15 +446,17 @@ namespace FATrace.HKNetLib.Wrapper
|
|||||||
{
|
{
|
||||||
// 发生错误,尝试停止下载并退出
|
// 发生错误,尝试停止下载并退出
|
||||||
Sdk_NET_DVR_StopGetFile();
|
Sdk_NET_DVR_StopGetFile();
|
||||||
|
try { NVRLoadVideoCompleteEventHandler?.Invoke(this, "下载失败"); } catch { }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
try { await Task.Delay(_downloadPollIntervalMs, token); } catch { /* ignore */ }
|
try { await Task.Delay(_downloadPollIntervalMs, token); } catch { /* ignore */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// 轮询线程异常吞掉,避免影响上层逻辑
|
// 轮询线程异常吞掉,避免影响上层逻辑
|
||||||
|
try { NVRLoadVideoCompleteEventHandler?.Invoke(this, $"下载失败:{ex.Message}"); } catch { }
|
||||||
}
|
}
|
||||||
}, token);
|
}, token);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace FATrace.Model
|
|||||||
public string? VideoFilePath { get; set; }
|
public string? VideoFilePath { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 失败时的错误信息。
|
/// 失败时的错误信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Column(StringLength = 500)]
|
[Column(StringLength = 500)]
|
||||||
public string? Error { get; set; }
|
public string? Error { get; set; }
|
||||||
|
|||||||
15
FATrace.OEMApp/MainApp.Designer.cs
generated
15
FATrace.OEMApp/MainApp.Designer.cs
generated
@@ -85,6 +85,7 @@ namespace FATrace.OEMApp
|
|||||||
btnStopLoadVideo = new Button();
|
btnStopLoadVideo = new Button();
|
||||||
btnLoadVideo = new Button();
|
btnLoadVideo = new Button();
|
||||||
btnNVRLogin = new Button();
|
btnNVRLogin = new Button();
|
||||||
|
LvLog = new ListView();
|
||||||
materialTabControl1.SuspendLayout();
|
materialTabControl1.SuspendLayout();
|
||||||
tabPage1.SuspendLayout();
|
tabPage1.SuspendLayout();
|
||||||
materialCard4.SuspendLayout();
|
materialCard4.SuspendLayout();
|
||||||
@@ -364,6 +365,7 @@ namespace FATrace.OEMApp
|
|||||||
// materialCard3
|
// materialCard3
|
||||||
//
|
//
|
||||||
materialCard3.BackColor = Color.FromArgb(255, 255, 255);
|
materialCard3.BackColor = Color.FromArgb(255, 255, 255);
|
||||||
|
materialCard3.Controls.Add(LvLog);
|
||||||
materialCard3.Controls.Add(label8);
|
materialCard3.Controls.Add(label8);
|
||||||
materialCard3.Depth = 0;
|
materialCard3.Depth = 0;
|
||||||
materialCard3.ForeColor = Color.FromArgb(222, 0, 0, 0);
|
materialCard3.ForeColor = Color.FromArgb(222, 0, 0, 0);
|
||||||
@@ -382,9 +384,9 @@ namespace FATrace.OEMApp
|
|||||||
label8.ForeColor = Color.FromArgb(64, 64, 64);
|
label8.ForeColor = Color.FromArgb(64, 64, 64);
|
||||||
label8.Location = new Point(17, 14);
|
label8.Location = new Point(17, 14);
|
||||||
label8.Name = "label8";
|
label8.Name = "label8";
|
||||||
label8.Size = new Size(180, 28);
|
label8.Size = new Size(138, 28);
|
||||||
label8.TabIndex = 5;
|
label8.TabIndex = 5;
|
||||||
label8.Text = "原料使用历史信息";
|
label8.Text = "系统运行日志";
|
||||||
//
|
//
|
||||||
// materialCard2
|
// materialCard2
|
||||||
//
|
//
|
||||||
@@ -722,6 +724,14 @@ namespace FATrace.OEMApp
|
|||||||
btnNVRLogin.UseVisualStyleBackColor = true;
|
btnNVRLogin.UseVisualStyleBackColor = true;
|
||||||
btnNVRLogin.Click += btnNVRLogin_Click;
|
btnNVRLogin.Click += btnNVRLogin_Click;
|
||||||
//
|
//
|
||||||
|
// LvLog
|
||||||
|
//
|
||||||
|
LvLog.Location = new Point(17, 58);
|
||||||
|
LvLog.Name = "LvLog";
|
||||||
|
LvLog.Size = new Size(966, 460);
|
||||||
|
LvLog.TabIndex = 6;
|
||||||
|
LvLog.UseCompatibleStateImageBehavior = false;
|
||||||
|
//
|
||||||
// MainApp
|
// MainApp
|
||||||
//
|
//
|
||||||
AutoScaleDimensions = new SizeF(8F, 17F);
|
AutoScaleDimensions = new SizeF(8F, 17F);
|
||||||
@@ -816,5 +826,6 @@ namespace FATrace.OEMApp
|
|||||||
private ProgressBar DownloadProgressBarMain;
|
private ProgressBar DownloadProgressBarMain;
|
||||||
private Button btnRawStopLoadVideo;
|
private Button btnRawStopLoadVideo;
|
||||||
private DataGridView gridRULog;
|
private DataGridView gridRULog;
|
||||||
|
private ListView LvLog;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,6 +37,57 @@ namespace FATrace.OEMApp
|
|||||||
{ nameof(VideoAction.CreateTime), "创建时间" }
|
{ nameof(VideoAction.CreateTime), "创建时间" }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private int _lvLogMaxItems = 1000;
|
||||||
|
private int _lastProgressLogged = -1;
|
||||||
|
|
||||||
|
private void InitLvLog()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LvLog.BeginUpdate();
|
||||||
|
LvLog.Clear();
|
||||||
|
LvLog.View = View.Details;
|
||||||
|
LvLog.FullRowSelect = true;
|
||||||
|
LvLog.GridLines = true;
|
||||||
|
LvLog.HeaderStyle = ColumnHeaderStyle.Nonclickable;
|
||||||
|
LvLog.Columns.Add("时间", 150);
|
||||||
|
LvLog.Columns.Add("级别", 60);
|
||||||
|
LvLog.Columns.Add("消息", 720);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
LvLog.EndUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AppendLog(string level, string message, Color? color = null)
|
||||||
|
{
|
||||||
|
if (LvLog == null || LvLog.IsDisposed) return;
|
||||||
|
if (InvokeRequired)
|
||||||
|
{
|
||||||
|
try { BeginInvoke(new Action(() => AppendLog(level, message, color))); } catch { }
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var item = new ListViewItem(DateTime.Now.ToString("HH:mm:ss.fff"));
|
||||||
|
item.SubItems.Add(level);
|
||||||
|
item.SubItems.Add(message ?? string.Empty);
|
||||||
|
if (color.HasValue) item.ForeColor = color.Value;
|
||||||
|
LvLog.Items.Add(item);
|
||||||
|
try { LvLog.EnsureVisible(LvLog.Items.Count - 1); } catch { }
|
||||||
|
if (LvLog.Items.Count > _lvLogMaxItems)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 200; i++) { if (LvLog.Items.Count == 0) break; LvLog.Items.RemoveAt(0); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogInfo(string msg) => AppendLog("INFO", msg, Color.FromArgb(33, 33, 33));
|
||||||
|
private void LogWarn(string msg) => AppendLog("WARN", msg, Color.DarkOrange);
|
||||||
|
private void LogError(string msg) => AppendLog("ERROR", msg, Color.DarkRed);
|
||||||
|
|
||||||
public MainApp()
|
public MainApp()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -46,6 +97,7 @@ namespace FATrace.OEMApp
|
|||||||
/// PLC数据服务
|
/// PLC数据服务
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private PLCDataService PLCDataService { get; set; }
|
private PLCDataService PLCDataService { get; set; }
|
||||||
|
private TimeClearDataService TimeClearService { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 主窗体加载:
|
/// 主窗体加载:
|
||||||
@@ -56,10 +108,18 @@ namespace FATrace.OEMApp
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void MainApp_Load(object sender, EventArgs e)
|
private void MainApp_Load(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
InitLvLog();
|
||||||
|
LogInfo("主界面初始化");
|
||||||
HkCameraClient = new HkCamera();
|
HkCameraClient = new HkCamera();
|
||||||
//保存SDK日志
|
//保存SDK日志
|
||||||
CHCNetSDK.NET_DVR_SetLogToFile(3, "C:\\SdkLog\\", true);
|
CHCNetSDK.NET_DVR_SetLogToFile(3, "C:\\SdkLog\\", true);
|
||||||
|
LogInfo("已启用海康SDK日志输出 C:\\SdkLog\\");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HkCameraClient.NVRLoadVideoProcessEventHandler += HkCameraClient_NVRLoadVideoProcessEventHandler;
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
//读取配置
|
//读取配置
|
||||||
//HkCameraClient.NVR_IP = ConfigHelper.GetValue("NVRIP");
|
//HkCameraClient.NVR_IP = ConfigHelper.GetValue("NVRIP");
|
||||||
@@ -71,6 +131,7 @@ namespace FATrace.OEMApp
|
|||||||
PLCDataService.ScanCodeEventHandler += PLCDataService_ScanCodeEventHandler;
|
PLCDataService.ScanCodeEventHandler += PLCDataService_ScanCodeEventHandler;
|
||||||
|
|
||||||
HkCameraClient.NVRVideoSavePath = ConfigHelper.GetValue("NVRVideoSavePath");
|
HkCameraClient.NVRVideoSavePath = ConfigHelper.GetValue("NVRVideoSavePath");
|
||||||
|
|
||||||
//NVR登录
|
//NVR登录
|
||||||
NVRLogin();
|
NVRLogin();
|
||||||
|
|
||||||
@@ -81,11 +142,18 @@ namespace FATrace.OEMApp
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
DownloadTaskWorker.Instance.Start(HkCameraClient);
|
DownloadTaskWorker.Instance.Start(HkCameraClient);
|
||||||
|
LogInfo("下载队列服务已启动");
|
||||||
JellyfinMonitorQueueService.Instance.Start();
|
JellyfinMonitorQueueService.Instance.Start();
|
||||||
|
LogInfo("Jellyfin 监控服务已启动");
|
||||||
|
TimeClearService = new TimeClearDataService();
|
||||||
|
TimeClearService.Info += (m) => LogInfo($"[清理]{m}");
|
||||||
|
TimeClearService.Start();
|
||||||
|
LogInfo("定时清理服务已启动");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine($"[TaskServices] 启动失败: {ex.Message}");
|
System.Diagnostics.Debug.WriteLine($"[TaskServices] 启动失败: {ex.Message}");
|
||||||
|
LogError($"后台服务启动失败: {ex.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化 gridRULog 并启动 UI 定时刷新
|
// 初始化 gridRULog 并启动 UI 定时刷新
|
||||||
@@ -115,6 +183,7 @@ namespace FATrace.OEMApp
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
MessageBox.Show($"初始化界面失败: {ex.Message}");
|
MessageBox.Show($"初始化界面失败: {ex.Message}");
|
||||||
|
LogError($"初始化界面失败: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +194,7 @@ namespace FATrace.OEMApp
|
|||||||
/// <param name="Code"></param>
|
/// <param name="Code"></param>
|
||||||
private void PLCDataService_ScanCodeEventHandler(object? sender, string Code)
|
private void PLCDataService_ScanCodeEventHandler(object? sender, string Code)
|
||||||
{
|
{
|
||||||
|
LogInfo($"扫码: {Code}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -135,7 +204,7 @@ namespace FATrace.OEMApp
|
|||||||
/// <param name="e"></param>
|
/// <param name="e"></param>
|
||||||
private void PLCDataService_PlcConnectedEventHandler(object? sender, string e)
|
private void PLCDataService_PlcConnectedEventHandler(object? sender, string e)
|
||||||
{
|
{
|
||||||
|
LogInfo($"PLC连接: {e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NVRLogin()
|
private void NVRLogin()
|
||||||
@@ -145,13 +214,16 @@ namespace FATrace.OEMApp
|
|||||||
HkCameraClient.NVR_UserName = ConfigHelper.GetValue("NVRUserName");
|
HkCameraClient.NVR_UserName = ConfigHelper.GetValue("NVRUserName");
|
||||||
HkCameraClient.NVR_Pw = ConfigHelper.GetValue("NVRPw");
|
HkCameraClient.NVR_Pw = ConfigHelper.GetValue("NVRPw");
|
||||||
|
|
||||||
|
LogInfo($"尝试登录NVR {HkCameraClient.NVR_IP}:{HkCameraClient.NVR_Port} 用户 {HkCameraClient.NVR_UserName}");
|
||||||
var result = HkCameraClient.Sdk_NET_DVR_Login_V30(HkCameraClient.NVR_IP, HkCameraClient.NVR_Port, HkCameraClient.NVR_UserName, HkCameraClient.NVR_Pw);
|
var result = HkCameraClient.Sdk_NET_DVR_Login_V30(HkCameraClient.NVR_IP, HkCameraClient.NVR_Port, HkCameraClient.NVR_UserName, HkCameraClient.NVR_Pw);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
MessageBox.Show("登录成功");
|
MessageBox.Show("登录成功");
|
||||||
|
LogInfo("NVR 登录成功");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MessageBox.Show($"登录失败:{HkCameraClient.LastMsgErr}");
|
MessageBox.Show($"登录失败:{HkCameraClient.LastMsgErr}");
|
||||||
|
LogError($"NVR 登录失败: {HkCameraClient.LastMsgErr}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,13 +269,16 @@ namespace FATrace.OEMApp
|
|||||||
HkCameraClient.NVR_UserName = ConfigHelper.GetValue("NVRUserName");
|
HkCameraClient.NVR_UserName = ConfigHelper.GetValue("NVRUserName");
|
||||||
HkCameraClient.NVR_Pw = ConfigHelper.GetValue("NVRPw");
|
HkCameraClient.NVR_Pw = ConfigHelper.GetValue("NVRPw");
|
||||||
|
|
||||||
|
LogInfo($"尝试登录NVR {HkCameraClient.NVR_IP}:{HkCameraClient.NVR_Port} 用户 {HkCameraClient.NVR_UserName}");
|
||||||
var result = HkCameraClient.Sdk_NET_DVR_Login_V30(HkCameraClient.NVR_IP, HkCameraClient.NVR_Port, HkCameraClient.NVR_UserName, HkCameraClient.NVR_Pw);
|
var result = HkCameraClient.Sdk_NET_DVR_Login_V30(HkCameraClient.NVR_IP, HkCameraClient.NVR_Port, HkCameraClient.NVR_UserName, HkCameraClient.NVR_Pw);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
MessageBox.Show("登录成功");
|
MessageBox.Show("登录成功");
|
||||||
|
LogInfo("NVR 登录成功");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MessageBox.Show($"登录失败:{HkCameraClient.LastMsgErr}");
|
MessageBox.Show($"登录失败:{HkCameraClient.LastMsgErr}");
|
||||||
|
LogError($"NVR 登录失败: {HkCameraClient.LastMsgErr}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,10 +294,11 @@ namespace FATrace.OEMApp
|
|||||||
code: CurInBagCode,
|
code: CurInBagCode,
|
||||||
rawName: CurInBagRawName,
|
rawName: CurInBagRawName,
|
||||||
user: CurUserName,
|
user: CurUserName,
|
||||||
start: DateTime.Now.AddMinutes(-5),
|
start: DateTime.Now.AddSeconds(-30),
|
||||||
end: DateTime.Now
|
end: DateTime.Now
|
||||||
);
|
);
|
||||||
MessageBox.Show($"已入队下载任务,Id={taskId}");
|
MessageBox.Show($"已入队下载任务,Id={taskId}");
|
||||||
|
LogInfo($"下载任务入队 Id={taskId} Code={CurInBagCode}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HkCameraClient_NVRLoadVideoProcessEventHandler(object? sender, short value)
|
private void HkCameraClient_NVRLoadVideoProcessEventHandler(object? sender, short value)
|
||||||
@@ -231,6 +307,11 @@ namespace FATrace.OEMApp
|
|||||||
{
|
{
|
||||||
DownloadProgressBarMain.Value = value;
|
DownloadProgressBarMain.Value = value;
|
||||||
}));
|
}));
|
||||||
|
if (value >= 100 && _lastProgressLogged != 100)
|
||||||
|
{
|
||||||
|
_lastProgressLogged = 100;
|
||||||
|
LogInfo("下载进度 100%");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnDownloadName_Click(object sender, EventArgs e)
|
private void btnDownloadName_Click(object sender, EventArgs e)
|
||||||
@@ -287,10 +368,12 @@ namespace FATrace.OEMApp
|
|||||||
if (Result.Result)
|
if (Result.Result)
|
||||||
{
|
{
|
||||||
MessageBox.Show($"[暂停成功] :{Result.Msg}");
|
MessageBox.Show($"[暂停成功] :{Result.Msg}");
|
||||||
|
LogInfo($"下载暂停: {Result.Msg}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MessageBox.Show($"[暂停失败] :{Result.Msg}");
|
MessageBox.Show($"[暂停失败] :{Result.Msg}");
|
||||||
|
LogWarn($"下载暂停失败: {Result.Msg}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,7 +595,7 @@ namespace FATrace.OEMApp
|
|||||||
code: CurInBagCode,
|
code: CurInBagCode,
|
||||||
rawName: CurInBagRawName,
|
rawName: CurInBagRawName,
|
||||||
user: CurUserName,
|
user: CurUserName,
|
||||||
start: DateTime.Now.AddMinutes(-5),
|
start: DateTime.Now.AddSeconds(-100),
|
||||||
end: DateTime.Now
|
end: DateTime.Now
|
||||||
);
|
);
|
||||||
MessageBox.Show($"[Test] 已入队下载任务,Id={taskId}");
|
MessageBox.Show($"[Test] 已入队下载任务,Id={taskId}");
|
||||||
@@ -526,10 +609,16 @@ namespace FATrace.OEMApp
|
|||||||
private void HkCameraClient_NVRLoadVideoCompleteEventHandler(object? sender, string e)
|
private void HkCameraClient_NVRLoadVideoCompleteEventHandler(object? sender, string e)
|
||||||
{
|
{
|
||||||
// 计算用于去重的 key(文件路径或文件名,小写)
|
// 计算用于去重的 key(文件路径或文件名,小写)
|
||||||
|
if (string.IsNullOrWhiteSpace(e) && string.IsNullOrWhiteSpace(this.CurrentVideoPath))
|
||||||
|
{
|
||||||
|
LogWarn("下载完成事件未提供文件名,忽略");
|
||||||
|
return;
|
||||||
|
}
|
||||||
var localNameOrPath = (!string.IsNullOrWhiteSpace(e) && (e.Contains("\\") || e.Contains("/") || e.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase)))
|
var localNameOrPath = (!string.IsNullOrWhiteSpace(e) && (e.Contains("\\") || e.Contains("/") || e.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase)))
|
||||||
? e
|
? e
|
||||||
: this.CurrentVideoPath;
|
: this.CurrentVideoPath;
|
||||||
var key = (System.IO.Path.GetFileName(localNameOrPath) ?? localNameOrPath).ToLowerInvariant();
|
var safePath = localNameOrPath ?? string.Empty;
|
||||||
|
var key = (System.IO.Path.GetFileName(safePath) ?? safePath).ToLowerInvariant();
|
||||||
|
|
||||||
// 若同一 key 已在处理中,则忽略本次回调
|
// 若同一 key 已在处理中,则忽略本次回调
|
||||||
if (!_downloadProcessingKeys.TryAdd(key, 0))
|
if (!_downloadProcessingKeys.TryAdd(key, 0))
|
||||||
@@ -538,6 +627,8 @@ namespace FATrace.OEMApp
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LogInfo($"下载完成: {localNameOrPath}");
|
||||||
|
|
||||||
// 先保存当前的信息,记录主键 Id
|
// 先保存当前的信息,记录主键 Id
|
||||||
var rawUse = new OEMRawUse()
|
var rawUse = new OEMRawUse()
|
||||||
{
|
{
|
||||||
@@ -853,5 +944,11 @@ namespace FATrace.OEMApp
|
|||||||
{
|
{
|
||||||
HkCameraClient.Sdk_NET_DVR_StopGetFile();
|
HkCameraClient.Sdk_NET_DVR_StopGetFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnFormClosing(FormClosingEventArgs e)
|
||||||
|
{
|
||||||
|
try { TimeClearService?.Stop(); } catch { }
|
||||||
|
base.OnFormClosing(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,8 +127,8 @@
|
|||||||
<value>
|
<value>
|
||||||
AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs
|
AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs
|
||||||
LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu
|
LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu
|
||||||
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAaiEAAAJNU0Z0AUkBTAIBAQgB
|
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAaCEAAAJNU0Z0AUkBTAIBAQgB
|
||||||
AAFQAQEBUAEBARABAAEQAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABQAMAATADAAEBAQABIAYAATD/
|
AAFYAQEBWAEBARABAAEQAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABQAMAATADAAEBAQABIAYAATD/
|
||||||
AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AFIAAdsBlgESAf8B2wGWARIB/wQAAxIBGANJAYgB
|
AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AFIAAdsBlgESAf8B2wGWARIB/wQAAxIBGANJAYgB
|
||||||
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wQAAdsB
|
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wQAAdsB
|
||||||
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8UAAHbAZYB
|
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8UAAHbAZYB
|
||||||
@@ -217,60 +217,60 @@
|
|||||||
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wMqAUAY
|
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wMqAUAY
|
||||||
AAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wgAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B
|
AAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wgAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B
|
||||||
2wGWARIB/xgAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
|
2wGWARIB/xgAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
|
||||||
lgESAf8B2wGWARIB/wJ8AVoB+BAAAXEBbwFRAfcB2wGWARIB/wwAAzMBUAHbAZYBEgH/BAAB2wGWARIB
|
lgESAf8B2wGWARIB/wJ8AVsB+BAAAm8BUQH3AdsBlgESAf8MAAMzAVAB2wGWARIB/wQAAdsBlgESAf8Q
|
||||||
/xAAAdsBlgESAf8QAAJbAVkBwCAAAkcBRgGAAyoBQBQAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
|
AAHbAZYBEgH/EAACWwFZAcAgAAJHAUYBgAMqAUAUAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
|
||||||
lgESAf8MAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8IAAHbAZYBEgH/AdsB
|
EgH/DAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/CAAB2wGWARIB/wHbAZYB
|
||||||
|
EgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B
|
||||||
|
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8EAAHbAZYBEgH/FAAB
|
||||||
|
2wGWARIB/wQAAdsBlgESAf8QAAHbAZYBEgH/EAACWwFZAcAgAAJHAUYBgAMqAUAQAAHbAZYBEgH/AdsB
|
||||||
|
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8QAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
|
||||||
|
EgH/CAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/EAADEgEYAdsBlgESAf8B
|
||||||
|
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wQAAdsBlgESAf8UAAHbAZYBEgH/BAAB2wGWARIB
|
||||||
|
/xAAAdsBlgESAf8QAAJbAVkBwAgAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB
|
||||||
|
/wQAAkcBRgGAAyoBQBAAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8UAAM9AWgB2wGWARIB
|
||||||
|
/wHbAZYBEgH/BAAB2wGWARIB/wM6AWAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xgAAxgB
|
||||||
|
IAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/CAAB2wGWARIB/xAAAdsBlgESAf8EAAHbAZYB
|
||||||
|
EgH/EAAB2wGWARIB/xAAAlsBWQHAIAACRwFGAYADKgFAEAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8o
|
||||||
|
AAHbAZYBEgH/CAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8cAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB
|
||||||
|
/wwAAmUBXAHnAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wQAAdsBlgESAf8B
|
||||||
|
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xQAAlsBWQHACAAB2wGWARIB/wHbAZYBEgH/AdsB
|
||||||
|
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8CRwFGAYADKgFAEAAB2wGWARIB/wJvAVEB9ygAAdsB
|
||||||
|
lgESAf8B2wGWARIB/wgAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/HAAB2wGWARIB/wHbAZYBEgH/AdsB
|
||||||
|
lgESAf9QAAJbAVkBwCAAAkcBRgGAAyoBQBAAAdsBlgESAf8oAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB
|
||||||
|
/wgAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/HAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8MAAJBAUAB
|
||||||
|
cAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8EAAHbAZYBEgH/AdsBlgESAf8B
|
||||||
|
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8UAAJbAVkBwAgAAlsBWQHAAlsBWQHAAlsBWQHADAACRwFGAYAD
|
||||||
|
KgFAGAAB2wGWARIB/wHbAZYBEgH/GAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wQAAdsB
|
||||||
|
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8cAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
|
||||||
|
EgH/CAAB2wGWARIB/xAAAdsBlgESAf8EAAHbAZYBEgH/DAADKgFAAdsBlgESAf8QAAJbAVkBwAgAAzoB
|
||||||
|
YAM6AWADOgFgDAACRwFGAYADKgFAFAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xAAAdsB
|
||||||
|
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8DDAEQAwYBCAHbAZYBEgH/AdsBlgESAf8B2wGWARIB
|
||||||
|
/wHbAZYBEgH/AdsBlgESAf8UAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8E
|
||||||
|
AAHbAZYBEgH/FAAB2wGWARIB/wQAAdsBlgESAf8QAAHbAZYBEgH/EAACWwFZAcAgAAJHAUYBgAMqAUAU
|
||||||
|
AAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8IAAHbAZYBEgH/AdsBlgESAf8B
|
||||||
|
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8IAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
|
||||||
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB
|
lgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB
|
||||||
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wQAAdsBlgESAf8U
|
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wQAAdsBlgESAf8UAAHbAZYBEgH/BAAB2wGWARIB/xAAAdsB
|
||||||
AAHbAZYBEgH/BAAB2wGWARIB/xAAAdsBlgESAf8QAAJbAVkBwCAAAkcBRgGAAyoBQBAAAdsBlgESAf8B
|
lgESAf8QAAJbAVkBwAJpAWAB6AMYASADGAEgAxgBIAMYASADGAEgAxgBIAMYASAB2wGWARIB/wMqAUAY
|
||||||
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xAAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
|
AAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/A1YBsAgAAdsBlgESAf8B2wGWARIB/wHbAZYB
|
||||||
lgESAf8IAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8QAAMSARgB2wGWARIB
|
EgH/GAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB
|
||||||
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/BAAB2wGWARIB/xQAAdsBlgESAf8EAAHbAZYB
|
/wHbAZYBEgH/AdsBlgESAf8QAAHbAZYBEgH/AdsBlgESAf8QAAHbAZYBEgH/BAAB2wGWARIB/xAAAdsB
|
||||||
EgH/EAAB2wGWARIB/xAAAlsBWQHACAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
|
lgESAf8UAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYB
|
||||||
EgH/BAACRwFGAYADKgFAEAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xQAAz0BaAHbAZYB
|
EgH/AdsBlgESAf8B2wGWARIB/yAAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB
|
||||||
EgH/AdsBlgESAf8EAAHbAZYBEgH/AzoBYAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/GAAD
|
/ygAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B
|
||||||
GAEgAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8IAAHbAZYBEgH/EAAB2wGWARIB/wQAAdsB
|
2wGWARIB/xgAAdsBlgESAf8B2wGWARIB/wMqAUAB2wGWARIB/wHbAZYBEgH/CAAB2wGWARIB/wHbAZYB
|
||||||
lgESAf8QAAHbAZYBEgH/EAACWwFZAcAgAAJHAUYBgAMqAUAQAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB
|
EgH/Al8BWwHYAlsBWQHAAdsBlgESAf8B2wGWARIB/2AAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
|
||||||
/ygAAdsBlgESAf8IAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xwAAdsBlgESAf8B2wGWARIB/wHbAZYB
|
lgESAf8kAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wwAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/GAAC
|
||||||
EgH/DAACZQFcAecB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/BAAB2wGWARIB
|
RwFGAYACYwFdAd8CWAFWAbMUAAJjAV0B3wJjAV0B3wMGAQigAAMYASAMAAJRAVABn1QAAUIBTQE+BwAB
|
||||||
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/FAACWwFZAcAIAAHbAZYBEgH/AdsBlgESAf8B
|
PgMAASgDAAFAAwABMAMAAQEBAAEBBQABgAEBFgAD/4EAAf8B8gEAAYAB+AEPAcABAQH/AcABAAGAAeAB
|
||||||
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wJHAUYBgAMqAUAQAAHbAZYBEgH/Am8BUQH3KAAB
|
AwGAAQAB/wHsAQABgAHDAeMBgAEAAfgBzAEAAYABzwH5AgAB+AHAAQABgAGPAfkCAAHQAUMBAAGAAZ8B
|
||||||
2wGWARIB/wHbAZYBEgH/CAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8cAAHbAZYBEgH/AdsBlgESAf8B
|
/AIAAYABDwKAAZ8B/AIAAQIBBwL/AZ8B/AIAAYcBDwKAAZ8B+AIAAc8BnwEAAYABzgE5AgABhwEPAQAB
|
||||||
2wGWARIB/1AAAlsBWQHAIAACRwFGAYADKgFAEAAB2wGWARIB/ygAAdsBlgESAf8B2wGWARIB/wHbAZYB
|
gAHGATECAAGCAQcBAAGAAe4BOwIAAYABDwEAAYAB/gE/AgAB0AF/AQABgAH+AT8BgAEAAfgB/wEAAYAB
|
||||||
EgH/CAAB2wGWARIB/wHbAZYBEgH/AdsBlgESAf8cAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wwAAkEB
|
/gE/AYABAQH4Af8BgQHAAf4BPwHgAQcC/wH+AR8B8QHHAeMB5wP/AQcB8AEPAcEBgQHgAQMB8QGDAfAB
|
||||||
QAFwAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wQAAdsBlgESAf8B2wGWARIB
|
BwGcAb0B7wHzAeEBwQGAAQABvgG9Ae8B8wHBAeEBgwHAAb4BvQHsARMBwwHiAQcB4AHeAb0B7wHzAccB
|
||||||
/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xQAAlsBWQHACAACWwFZAcACWwFZAcACWwFZAcAMAAJHAUYB
|
/gHHAfEBwAGDAewBAwHPAfwBxwHxAv8B7wHzAd8B+AHHAfEBwAGDAewBcwHzAfABhwHwAd4BuQHsAXMB
|
||||||
gAMqAUAYAAHbAZYBEgH/AdsBlgESAf8YAAHbAZYBEgH/AdsBlgESAf8B2wGWARIB/wHbAZYBEgH/BAAB
|
4QHgAQMB4AG+Ab0B7wHzAeABwQGAAQABvgG9AeABAwHwAWMB8AEHAZ4BvQHwAQcB+AE/AfABDwHBAYEC
|
||||||
2wGWARIB/wHbAZYBEgH/AdsBlgESAf8B2wGWARIB/xwAAdsBlgESAf8B2wGWARIB/wHbAZYBEgH/AdsB
|
/wH+AR8B8QHHAuME/wH9Ad8C/ws=
|
||||||
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>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using FATrace.Model;
|
|||||||
using FATrace.HKNetLib.Wrapper;
|
using FATrace.HKNetLib.Wrapper;
|
||||||
using NLog;
|
using NLog;
|
||||||
using FATrace.OEMApp;
|
using FATrace.OEMApp;
|
||||||
|
using System.IO;
|
||||||
using TaskStatus = FATrace.Model.TaskStatus;
|
using TaskStatus = FATrace.Model.TaskStatus;
|
||||||
|
|
||||||
namespace FATrace.OEMApp.Services
|
namespace FATrace.OEMApp.Services
|
||||||
@@ -27,10 +28,10 @@ namespace FATrace.OEMApp.Services
|
|||||||
|
|
||||||
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
||||||
private readonly SemaphoreSlim _singleRunner = new(1, 1); // 保证同一时间只有一个下载
|
private readonly SemaphoreSlim _singleRunner = new(1, 1); // 保证同一时间只有一个下载
|
||||||
private CancellationTokenSource? _cts;
|
private CancellationTokenSource? _cts; // 后台主循环的取消令牌(停止服务时发出)
|
||||||
private Task? _loopTask;
|
private Task? _loopTask; // 后台主循环 Task
|
||||||
|
|
||||||
private HkCamera? _hk;
|
private HkCamera? _hk; // 供下载与事件回调使用的海康客户端(由 UI 注入)
|
||||||
|
|
||||||
private DownloadTaskWorker() { }
|
private DownloadTaskWorker() { }
|
||||||
|
|
||||||
@@ -46,10 +47,11 @@ namespace FATrace.OEMApp.Services
|
|||||||
{
|
{
|
||||||
_hk = hk ?? throw new ArgumentNullException(nameof(hk));
|
_hk = hk ?? throw new ArgumentNullException(nameof(hk));
|
||||||
if (_cts != null) return; // 已启动
|
if (_cts != null) return; // 已启动
|
||||||
// 恢复上次未完成的运行中任务为待处理
|
// 恢复上次未完成的运行中任务为待处理,然后再循环执行
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var db = FSqlContext.FDb;
|
var db = FSqlContext.FDb;
|
||||||
|
// 将异常退出时处于 Running 的任务回滚为 Pending,避免卡住队列
|
||||||
db.Update<DownloadTask>()
|
db.Update<DownloadTask>()
|
||||||
.Set(a => a.Status, TaskStatus.Pending)
|
.Set(a => a.Status, TaskStatus.Pending)
|
||||||
.Set(a => a.Progress, 0)
|
.Set(a => a.Progress, 0)
|
||||||
@@ -67,10 +69,10 @@ namespace FATrace.OEMApp.Services
|
|||||||
.Set(a => a.UpdateTime, DateTime.Now)
|
.Set(a => a.UpdateTime, DateTime.Now)
|
||||||
.Where(a => a.Status == TaskStatus.Failed && a.TryCount < maxRetry)
|
.Where(a => a.Status == TaskStatus.Failed && a.TryCount < maxRetry)
|
||||||
.ExecuteAffrows();
|
.ExecuteAffrows();
|
||||||
|
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
_cts = new CancellationTokenSource();
|
_cts = new CancellationTokenSource();
|
||||||
|
//开始后台主循环
|
||||||
_loopTask = Task.Run(() => RunAsync(_cts.Token));
|
_loopTask = Task.Run(() => RunAsync(_cts.Token));
|
||||||
_logger.Info("[DownloadTaskWorker] 已启动");
|
_logger.Info("[DownloadTaskWorker] 已启动");
|
||||||
}
|
}
|
||||||
@@ -82,6 +84,8 @@ namespace FATrace.OEMApp.Services
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// 发出取消信号,RunAsync 将尽快退出;
|
||||||
|
// 正在处理的任务会在 token 取消时尝试停止 SDK 下载
|
||||||
_cts?.Cancel();
|
_cts?.Cancel();
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
@@ -97,29 +101,35 @@ namespace FATrace.OEMApp.Services
|
|||||||
/// <param name="code">业务条码/编号,关联 OEMRawUse.Code 与 VideoAction.Code。</param>
|
/// <param name="code">业务条码/编号,关联 OEMRawUse.Code 与 VideoAction.Code。</param>
|
||||||
/// <param name="rawName">原料名称。</param>
|
/// <param name="rawName">原料名称。</param>
|
||||||
/// <param name="user">操作用户。</param>
|
/// <param name="user">操作用户。</param>
|
||||||
/// <param name="start">NVR 下载开始时间(默认当前时间-5 分钟)。</param>
|
/// <param name="start">NVR 下载开始时间(默认当前时间-30 秒)。</param>
|
||||||
/// <param name="end">NVR 下载结束时间(默认当前时间)。</param>
|
/// <param name="end">NVR 下载结束时间(默认当前时间)。</param>
|
||||||
|
/// <param name="rawCode">原料条码(可选)。未提供时默认等于 code。</param>
|
||||||
/// <returns>新增 DownloadTask 的自增 Id。</returns>
|
/// <returns>新增 DownloadTask 的自增 Id。</returns>
|
||||||
public long Enqueue(string code, string rawName, string user, DateTime? start = null, DateTime? end = null)
|
public long Enqueue(string code, string rawName, string user, DateTime? start = null, DateTime? end = null, string? rawCode = null)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(code)) throw new ArgumentException("code 不能为空");
|
if (string.IsNullOrWhiteSpace(code)) throw new ArgumentException("code 不能为空");
|
||||||
if (string.IsNullOrWhiteSpace(rawName)) throw new ArgumentException("rawName 不能为空");
|
if (string.IsNullOrWhiteSpace(rawName)) throw new ArgumentException("rawName 不能为空");
|
||||||
if (string.IsNullOrWhiteSpace(user)) throw new ArgumentException("user 不能为空");
|
if (string.IsNullOrWhiteSpace(user)) throw new ArgumentException("user 不能为空");
|
||||||
|
|
||||||
var now = DateTime.Now;
|
var now = DateTime.Now;
|
||||||
|
// 构造持久化任务:
|
||||||
|
// - 默认回溯 30 秒(符合项目规则)
|
||||||
|
// - rawCode 未提供时回落到 code,避免非空列插入失败
|
||||||
var task = new DownloadTask
|
var task = new DownloadTask
|
||||||
{
|
{
|
||||||
Code = code,
|
Code = code,
|
||||||
RawName = rawName,
|
RawName = rawName,
|
||||||
|
RawCode = rawCode ?? code,
|
||||||
User = user,
|
User = user,
|
||||||
Status = TaskStatus.Pending,
|
Status = TaskStatus.Pending,
|
||||||
Progress = 0,
|
Progress = 0,
|
||||||
NvrStartTime = start ?? now.AddMinutes(-5),
|
NvrStartTime = start ?? now.AddSeconds(-30),
|
||||||
NvrEndTime = end ?? now,
|
NvrEndTime = end ?? now,
|
||||||
CreateTime = now,
|
CreateTime = now,
|
||||||
UpdateTime = now
|
UpdateTime = now
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 入库返回自增 Id,便于 UI 提示与后续跟踪
|
||||||
var id = FSqlContext.FDb.Insert<DownloadTask>(task).ExecuteIdentity();
|
var id = FSqlContext.FDb.Insert<DownloadTask>(task).ExecuteIdentity();
|
||||||
_logger.Info("[DownloadTaskWorker] 入队 DownloadTask: Id={Id}, Code={Code}", id, code);
|
_logger.Info("[DownloadTaskWorker] 入队 DownloadTask: Id={Id}, Code={Code}", id, code);
|
||||||
return id;
|
return id;
|
||||||
@@ -145,12 +155,13 @@ namespace FATrace.OEMApp.Services
|
|||||||
if (next == null)
|
if (next == null)
|
||||||
{
|
{
|
||||||
// 暂无任务,稍候再查
|
// 暂无任务,稍候再查
|
||||||
await Task.Delay(1000, token);
|
await Task.Delay(5000, token);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用信号量确保同一时间仅有一个任务进入下载处理
|
// 使用信号量确保同一时间仅有一个任务进入下载处理
|
||||||
await _singleRunner.WaitAsync(token);
|
await _singleRunner.WaitAsync(token);
|
||||||
|
// 通过 ContinueWith 在任务结束时释放信号量,避免阻塞主循环
|
||||||
_ = ProcessTaskAsync(next, token).ContinueWith(_ => _singleRunner.Release());
|
_ = ProcessTaskAsync(next, token).ContinueWith(_ => _singleRunner.Release());
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
@@ -185,6 +196,7 @@ namespace FATrace.OEMApp.Services
|
|||||||
|
|
||||||
var db = FSqlContext.FDb;
|
var db = FSqlContext.FDb;
|
||||||
|
|
||||||
|
// 步骤1:状态入库(Running/TryCount/Progress/UpdateTime)
|
||||||
// 标记运行中
|
// 标记运行中
|
||||||
t.Status = TaskStatus.Running;
|
t.Status = TaskStatus.Running;
|
||||||
t.TryCount += 1;
|
t.TryCount += 1;
|
||||||
@@ -196,6 +208,7 @@ namespace FATrace.OEMApp.Services
|
|||||||
.Where(a => a.Id == t.Id)
|
.Where(a => a.Id == t.Id)
|
||||||
.ExecuteAffrowsAsync();
|
.ExecuteAffrowsAsync();
|
||||||
|
|
||||||
|
// 步骤2:生成保存路径(含安全文件名),并确保保存目录存在
|
||||||
// 生成本地文件名/路径
|
// 生成本地文件名/路径
|
||||||
var saveBase = _hk.NVRVideoSavePath;
|
var saveBase = _hk.NVRVideoSavePath;
|
||||||
if (string.IsNullOrWhiteSpace(saveBase))
|
if (string.IsNullOrWhiteSpace(saveBase))
|
||||||
@@ -205,25 +218,36 @@ namespace FATrace.OEMApp.Services
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var filePath = NVRCom.GetVideoName(saveBase, t.Code ?? "CODE");
|
var filePath = NVRCom.GetVideoName(saveBase, t.Code ?? "CODE");
|
||||||
|
// 确保保存目录存在,避免 SDK 写文件失败
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var dir = Path.GetDirectoryName(filePath);
|
||||||
|
if (!string.IsNullOrWhiteSpace(dir) && !Directory.Exists(dir))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
await db.Update<DownloadTask>()
|
await db.Update<DownloadTask>()
|
||||||
.Set(a => a.VideoFilePath, filePath)
|
.Set(a => a.VideoFilePath, filePath)
|
||||||
.Set(a => a.UpdateTime, DateTime.Now)
|
.Set(a => a.UpdateTime, DateTime.Now)
|
||||||
.Where(a => a.Id == t.Id)
|
.Where(a => a.Id == t.Id)
|
||||||
.ExecuteAffrowsAsync();
|
.ExecuteAffrowsAsync();
|
||||||
|
|
||||||
|
// 步骤3:订阅 SDK 事件 -> TCS 转换
|
||||||
// 事件 -> TCS
|
// 事件 -> TCS
|
||||||
// 订阅两个事件:进度与完成。进度事件写回数据库;完成事件用于唤醒等待
|
// 订阅两个事件:进度与完成。进度事件写回数据库;完成事件用于唤醒等待
|
||||||
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
var tcs = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
||||||
void OnProgress(object? s, short p)
|
void OnProgress(object? s, short p)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
db.Update<DownloadTask>()
|
//db.Update<DownloadTask>()
|
||||||
.Set(a => a.Progress, Math.Max((short)0, Math.Min((short)100, p)))
|
// .Set(a => a.Progress, Math.Max((short)0, Math.Min((short)100, p)))
|
||||||
.Set(a => a.UpdateTime, DateTime.Now)
|
// .Set(a => a.UpdateTime, DateTime.Now)
|
||||||
.Where(a => a.Id == t.Id)
|
// .Where(a => a.Id == t.Id)
|
||||||
.ExecuteAffrows();
|
// .ExecuteAffrows();
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
@@ -231,14 +255,16 @@ namespace FATrace.OEMApp.Services
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
tcs.TrySetResult(true);
|
tcs.TrySetResult(msg);
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_hk.NVRLoadVideoProcessEventHandler += OnProgress;
|
_hk.NVRLoadVideoProcessEventHandler += OnProgress;
|
||||||
_hk.NVRLoadVideoCompleteEventHandler += OnComplete;
|
_hk.NVRLoadVideoCompleteEventHandler += OnComplete;
|
||||||
|
|
||||||
|
// 步骤4:调用 SDK 按时间下载,并等待完成事件(含超时/取消处理)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 发起下载(按时间范围)
|
// 发起下载(按时间范围)
|
||||||
@@ -249,18 +275,60 @@ namespace FATrace.OEMApp.Services
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// HkCamera 内部会启动进度监控:此处等待完成事件触发
|
// HkCamera 内部会启动进度监控:此处等待完成事件触发(带超时)
|
||||||
using (token.Register(() => tcs.TrySetCanceled()))
|
var timeWindowSec = Math.Max(1, (int)(t.NvrEndTime - t.NvrStartTime).TotalSeconds);
|
||||||
|
var timeoutSec = ConfigHelper.GetIntOrDefault("DownloadTaskTimeoutSeconds", Math.Max(120, Math.Min(1800, timeWindowSec * 3)));
|
||||||
|
Task completed = tcs.Task;
|
||||||
|
Task timeoutTask = Task.Delay(TimeSpan.FromSeconds(timeoutSec), token);
|
||||||
|
using (token.Register(() =>
|
||||||
{
|
{
|
||||||
await tcs.Task;
|
try { tcs.TrySetCanceled(); } catch { }
|
||||||
|
}))
|
||||||
|
{
|
||||||
|
var finished = await Task.WhenAny(completed, timeoutTask);
|
||||||
|
if (finished == timeoutTask)
|
||||||
|
{
|
||||||
|
try { _hk.Sdk_NET_DVR_StopGetFile(); } catch { }
|
||||||
|
await MarkFailedAsync(t, $"下载超时({timeoutSec}s)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var completeMsg = await ((Task<string>)tcs.Task);
|
||||||
|
// 依据完成事件消息判断成功/失败(包含“完成”视为成功)
|
||||||
|
var succeed = !string.IsNullOrWhiteSpace(completeMsg) && completeMsg.Contains("完成");
|
||||||
|
if (!succeed)
|
||||||
|
{
|
||||||
|
try { _hk.Sdk_NET_DVR_StopGetFile(); } catch { }
|
||||||
|
await MarkFailedAsync(t, string.IsNullOrWhiteSpace(completeMsg) ? "下载失败" : completeMsg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 步骤5:文件有效性检查(存在且大小>0)
|
||||||
|
// 文件有效性检查
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!File.Exists(filePath) || new FileInfo(filePath).Length <= 0)
|
||||||
|
{
|
||||||
|
await MarkFailedAsync(t, "下载文件不存在或为空");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 文件系统异常
|
||||||
|
await MarkFailedAsync(t, "下载文件验证异常");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 步骤6:入库——写 OEMRawUse 并创建 Jellyfin 监听任务
|
||||||
// 下载完成:写 OEMRawUse 并创建 JellyfinMonitorTask
|
// 下载完成:写 OEMRawUse 并创建 JellyfinMonitorTask
|
||||||
// 1) 插入 OEMRawUse(UrlState=false, VideoUrl 空)
|
// 1) 插入 OEMRawUse(UrlState=false, VideoUrl 空)
|
||||||
var rawUse = new OEMRawUse
|
var rawUse = new OEMRawUse
|
||||||
{
|
{
|
||||||
InBagCode = t.Code,
|
InBagCode = t.Code,
|
||||||
RawName = t.RawName,
|
RawName = t.RawName,
|
||||||
|
RawCode = t.RawCode,
|
||||||
User = t.User,
|
User = t.User,
|
||||||
UrlState = false,
|
UrlState = false,
|
||||||
VideoUrl = string.Empty,
|
VideoUrl = string.Empty,
|
||||||
@@ -286,6 +354,7 @@ namespace FATrace.OEMApp.Services
|
|||||||
};
|
};
|
||||||
db.Insert<JellyfinMonitorTask>(jfTask).ExecuteAffrows();
|
db.Insert<JellyfinMonitorTask>(jfTask).ExecuteAffrows();
|
||||||
|
|
||||||
|
// 步骤7:收尾——标记下载完成并记录日志
|
||||||
// 标记下载完成
|
// 标记下载完成
|
||||||
t.Status = TaskStatus.Completed;
|
t.Status = TaskStatus.Completed;
|
||||||
t.Progress = 100;
|
t.Progress = 100;
|
||||||
@@ -296,10 +365,12 @@ namespace FATrace.OEMApp.Services
|
|||||||
.Where(a => a.Id == t.Id)
|
.Where(a => a.Id == t.Id)
|
||||||
.ExecuteAffrowsAsync();
|
.ExecuteAffrowsAsync();
|
||||||
|
|
||||||
_logger.Info("[DownloadTaskWorker] 下载完成,已创建 Jellyfin 监控任务。DownloadTaskId={Id}, OEMRawUseId={RawUseId}", t.Id, rawUseId);
|
_logger.Info("[DownloadTaskWorker] 下载完成,已创建 Jellyfin 监控任务 DownloadTaskId={Id}, OEMRawUseId={RawUseId}", t.Id, rawUseId);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
|
// 取消:停止当前 SDK 下载并标记失败
|
||||||
|
try { _hk.Sdk_NET_DVR_StopGetFile(); } catch { }
|
||||||
await MarkFailedAsync(t, "任务被取消");
|
await MarkFailedAsync(t, "任务被取消");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -311,6 +382,7 @@ namespace FATrace.OEMApp.Services
|
|||||||
// 释放事件订阅,避免内存泄露或重复触发
|
// 释放事件订阅,避免内存泄露或重复触发
|
||||||
try { _hk.NVRLoadVideoProcessEventHandler -= OnProgress; } catch { }
|
try { _hk.NVRLoadVideoProcessEventHandler -= OnProgress; } catch { }
|
||||||
try { _hk.NVRLoadVideoCompleteEventHandler -= OnComplete; } catch { }
|
try { _hk.NVRLoadVideoCompleteEventHandler -= OnComplete; } catch { }
|
||||||
|
//_singleRunner.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,6 +394,8 @@ namespace FATrace.OEMApp.Services
|
|||||||
private Task MarkFailedAsync(DownloadTask t, string? error)
|
private Task MarkFailedAsync(DownloadTask t, string? error)
|
||||||
{
|
{
|
||||||
var db = FSqlContext.FDb;
|
var db = FSqlContext.FDb;
|
||||||
|
// 将状态置为 Failed,记录错误信息与时间;
|
||||||
|
// 不抛出异常,保证主循环可以继续处理后续任务
|
||||||
t.Status = TaskStatus.Failed;
|
t.Status = TaskStatus.Failed;
|
||||||
t.Error = error;
|
t.Error = error;
|
||||||
t.UpdateTime = DateTime.Now;
|
t.UpdateTime = DateTime.Now;
|
||||||
|
|||||||
142
FATrace.OEMApp/Services/TimeClearDataService.cs
Normal file
142
FATrace.OEMApp/Services/TimeClearDataService.cs
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using FATrace.Com;
|
||||||
|
using FATrace.Model;
|
||||||
|
using NLog;
|
||||||
|
|
||||||
|
namespace FATrace.OEMApp.Services
|
||||||
|
{
|
||||||
|
public sealed class TimeClearDataService
|
||||||
|
{
|
||||||
|
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
||||||
|
private CancellationTokenSource? _cts;
|
||||||
|
private Task? _loopTask;
|
||||||
|
private TimeSpan _runAt = new TimeSpan(2, 0, 0);
|
||||||
|
private int _retentionDays = 365;
|
||||||
|
private bool _enabled = true;
|
||||||
|
|
||||||
|
public event Action<string>? Info;
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
if (_cts != null) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_enabled = ConfigHelper.GetBoolOrDefault("DataCleanupEnabled", true);
|
||||||
|
var tod = ConfigHelper.GetStringOrDefault("DataCleanupTimeOfDay", "02:00:00");
|
||||||
|
if (!TimeSpan.TryParse(tod, out _runAt))
|
||||||
|
{
|
||||||
|
_runAt = new TimeSpan(2, 0, 0);
|
||||||
|
}
|
||||||
|
_retentionDays = Math.Max(1, ConfigHelper.GetIntOrDefault("DataRetentionDays", 365));
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
if (!_enabled)
|
||||||
|
{
|
||||||
|
_logger.Warn("[TimeClear] 已禁用,未启动");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cts = new CancellationTokenSource();
|
||||||
|
_loopTask = Task.Run(() => RunAsync(_cts.Token));
|
||||||
|
_logger.Info("[TimeClear] 服务已启动,时间点={RunAt}, 保留天数={Days}", _runAt, _retentionDays);
|
||||||
|
SafeInfo($"定时清理服务启动,时间点={_runAt}, 保留天数={_retentionDays}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
try { _cts?.Cancel(); } catch { }
|
||||||
|
_cts = null;
|
||||||
|
_logger.Info("[TimeClear] 服务已停止");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RunAsync(CancellationToken token)
|
||||||
|
{
|
||||||
|
while (!token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var now = DateTime.Now;
|
||||||
|
var next = GetNextRunTime(now);
|
||||||
|
var delay = next - now;
|
||||||
|
SafeInfo($"距离下一次清理还有 {delay:hh\\:mm\\:ss},计划时间 {next:yyyy-MM-dd HH:mm:ss}");
|
||||||
|
await Task.Delay(delay, token);
|
||||||
|
await CleanupOnceAsync(token);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "[TimeClear] 后台循环异常");
|
||||||
|
try { await Task.Delay(TimeSpan.FromMinutes(1), token); } catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DateTime GetNextRunTime(DateTime now)
|
||||||
|
{
|
||||||
|
var next = now.Date + _runAt;
|
||||||
|
if (next <= now.AddSeconds(1)) next = next.AddDays(1);
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CleanupOnceAsync(CancellationToken token)
|
||||||
|
{
|
||||||
|
var cutoff = DateTime.Now.AddDays(-_retentionDays);
|
||||||
|
_logger.Info("[TimeClear] 开始清理,截止时间: {Cutoff}", cutoff);
|
||||||
|
SafeInfo($"开始清理,截止时间: {cutoff:yyyy-MM-dd HH:mm:ss}");
|
||||||
|
|
||||||
|
long delJf = 0, delDl = 0, delRaw = 0, delAct = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
delJf = FSqlContext.FDb.Delete<JellyfinMonitorTask>().Where(a => a.CreateTime < cutoff).ExecuteAffrows();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warn(ex, "[TimeClear] 清理 JellyfinMonitorTask 失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
delDl = FSqlContext.FDb.Delete<DownloadTask>().Where(a => a.CreateTime < cutoff).ExecuteAffrows();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warn(ex, "[TimeClear] 清理 DownloadTask 失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
delRaw = FSqlContext.FDb.Delete<OEMRawUse>().Where(a => a.CreateTime < cutoff).ExecuteAffrows();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warn(ex, "[TimeClear] 清理 OEMRawUse 失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
delAct = FSqlContext.FDb.Delete<VideoAction>().Where(a => a.CreateTime < cutoff).ExecuteAffrows();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warn(ex, "[TimeClear] 清理 VideoAction 失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Info("[TimeClear] 清理完成: JellyfinMonitorTask={Jf}, DownloadTask={Dl}, OEMRawUse={Raw}, VideoAction={Act}", delJf, delDl, delRaw, delAct);
|
||||||
|
SafeInfo($"清理完成: Jf={delJf}, Dl={delDl}, Raw={delRaw}, Act={delAct}");
|
||||||
|
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SafeInfo(string msg)
|
||||||
|
{
|
||||||
|
try { Info?.Invoke(msg); } catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user