529 lines
21 KiB
C#
529 lines
21 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using MoviconHub.App.Models;
|
||
using NLog;
|
||
|
||
namespace MoviconHub.App.Services
|
||
{
|
||
/// <summary>
|
||
/// Db Server
|
||
/// </summary>
|
||
public class DBServices : IDisposable
|
||
{
|
||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||
private Timer _dataPollingTimer;
|
||
private readonly object _lockObject = new object();
|
||
private bool _isRunning = false;
|
||
private int _pollingInterval = 1000; // 默认轮询间隔1秒
|
||
private WebSocketData _realtimeData;
|
||
private CancellationTokenSource _cancellationTokenSource;
|
||
|
||
/// <summary>
|
||
/// 实时数据对象,用于缓存从RTVar表中读取的数据
|
||
/// </summary>
|
||
public WebSocketData RealtimeData
|
||
{
|
||
get { return _realtimeData; }
|
||
}
|
||
|
||
public SglModel SglModel { get; }
|
||
|
||
/// <summary>
|
||
/// 实时数据更新事件
|
||
/// </summary>
|
||
public event EventHandler<WebSocketData> DataUpdated;
|
||
|
||
/// <summary>
|
||
/// 初始化数据库服务
|
||
/// </summary>
|
||
public DBServices(SglModel sglModel)
|
||
{
|
||
_realtimeData = new WebSocketData();
|
||
|
||
_cancellationTokenSource = new CancellationTokenSource();
|
||
SglModel = sglModel;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 开始实时数据轮询
|
||
/// </summary>
|
||
/// <param name="interval">轮询间隔,单位毫秒</param>
|
||
/// <returns>是否成功启动</returns>
|
||
public bool StartPolling(int interval = 2000)
|
||
{
|
||
if (_isRunning)
|
||
return false;
|
||
|
||
lock (_lockObject)
|
||
{
|
||
if (_isRunning)
|
||
return false;
|
||
|
||
_pollingInterval = interval;
|
||
_isRunning = true;
|
||
_cancellationTokenSource = new CancellationTokenSource();
|
||
|
||
// 使用Task来轮询数据
|
||
Task.Run(async () => await PollDataAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
|
||
|
||
Logger.Info($"实时数据轮询已启动,间隔: {_pollingInterval}ms");
|
||
return true;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止实时数据轮询
|
||
/// </summary>
|
||
public void StopPolling()
|
||
{
|
||
if (!_isRunning)
|
||
return;
|
||
|
||
lock (_lockObject)
|
||
{
|
||
if (!_isRunning)
|
||
return;
|
||
|
||
_cancellationTokenSource.Cancel();
|
||
_isRunning = false;
|
||
Logger.Info("实时数据轮询已停止");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 异步轮询数据任务
|
||
/// </summary>
|
||
/// <param name="cancellationToken">取消标记</param>
|
||
private async Task PollDataAsync(CancellationToken cancellationToken)
|
||
{
|
||
while (!cancellationToken.IsCancellationRequested)
|
||
{
|
||
try
|
||
{
|
||
// 读取RTVar表数据
|
||
await ReadRTVarDataAsync();
|
||
|
||
// 等待指定的轮询间隔
|
||
await Task.Delay(_pollingInterval, cancellationToken);
|
||
}
|
||
catch (TaskCanceledException)
|
||
{
|
||
// 任务被取消,正常退出
|
||
break;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error($"实时数据轮询时发生错误: {ex.Message}");
|
||
// 发生错误时,等待一段时间后重试
|
||
await Task.Delay(5000, cancellationToken);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 异步读取RTVar表数据
|
||
/// </summary>
|
||
private async Task ReadRTVarDataAsync()
|
||
{
|
||
try
|
||
{
|
||
// 使用FSqlContext封装的FreeSql调用DB数据
|
||
var rtVars = await FSqlContext.FDb.Select<RTVar>().ToListAsync();
|
||
|
||
// 更新实时数据对象
|
||
UpdateWebSocketData(rtVars);
|
||
|
||
// 触发数据更新事件
|
||
//DataUpdated?.Invoke(this, _realtimeData);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error($"读取RTVar数据时发生错误: {ex.StackTrace}");
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 使用RTVar数据更新WebSocketData模型
|
||
/// </summary>
|
||
/// <param name="rtVars">RTVar数据列表</param>
|
||
private void UpdateWebSocketData(List<RTVar> rtVars)
|
||
{
|
||
try
|
||
{
|
||
|
||
if (rtVars == null || rtVars.Count == 0)
|
||
return;
|
||
|
||
lock (_lockObject)
|
||
{
|
||
|
||
// 更新设备状态信息
|
||
var deviceCodeVar = rtVars.FirstOrDefault(v => v.Name == "Device_Code");
|
||
if (deviceCodeVar != null)
|
||
_realtimeData.Device_Code = deviceCodeVar.Val;
|
||
|
||
var deviceNameVar = rtVars.FirstOrDefault(v => v.Name == "Device_Name");
|
||
if (deviceNameVar != null)
|
||
_realtimeData.Device_Name = deviceNameVar.Val;
|
||
|
||
var deviceManufacturerVar = rtVars.FirstOrDefault(v => v.Name == "Device_Manufacturer");
|
||
if (deviceManufacturerVar != null)
|
||
_realtimeData.Device_Manufacturer = deviceManufacturerVar.Val;
|
||
|
||
var statusVar = rtVars.FirstOrDefault(v => v.Name == "Device_Status");
|
||
if (statusVar != null)
|
||
_realtimeData.Device_Status = statusVar.Val;
|
||
|
||
// 解析故障信息
|
||
UpdateFaultDetails(rtVars);
|
||
|
||
// 解析组件信息
|
||
UpdateComponentsInfo(rtVars);
|
||
|
||
// 解析测试数据
|
||
UpdateTestData(rtVars);
|
||
|
||
//获取条码信息,确定是否需要搜索数据
|
||
SglModel.CodeReady = rtVars.FirstOrDefault(v => v.Name == "part_qrid").Val;
|
||
|
||
UpdateRemoteDb(RealtimeData);
|
||
|
||
//把最新的数据赋值给WebSocketClient中
|
||
WebSocketClientHelper.CurWebSocketData = RealtimeData;
|
||
|
||
//更新数据到远程数据库
|
||
|
||
// 记录日志
|
||
//Logger.Debug("实时数据已更新");
|
||
}
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error($"UpdateWebSocketData: {ex.StackTrace}");
|
||
}
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新远程数据库
|
||
/// </summary>
|
||
/// <param name="webSocketData"></param>
|
||
private void UpdateRemoteDb(WebSocketData webSocketData)
|
||
{
|
||
try
|
||
{
|
||
// 获取组件信息
|
||
var component = webSocketData.ListComponentsInfo?.FirstOrDefault();
|
||
|
||
// 创建CurRunClearState对象并映射数据
|
||
var curRunClearState = new CurRunClearState()
|
||
{
|
||
//只有一个,更新数据
|
||
Id = 1,
|
||
|
||
// 基本信息
|
||
DeviceCode = webSocketData.Device_Code,
|
||
DeviceName = webSocketData.Device_Name,
|
||
|
||
// 组件信息
|
||
part_qrid = component?.part_qrid,
|
||
part_num = component?.part_num,
|
||
part_position = component?.part_position,
|
||
component_name = component?.part_name,
|
||
vehicle_model = component?.part_Vehicle_model,
|
||
locomotive_number = component?.part_locomotive_number,
|
||
repair_process = component?.part_repair_process,
|
||
|
||
program_process = webSocketData.TestData.Test_FrameworkProgramProcess,
|
||
|
||
// 程序进程
|
||
Test_FrameworkProgramProcess = webSocketData.TestData.Test_FrameworkProgramProcess,
|
||
Test_FrameworkProgramProcessPercentage = webSocketData.TestData.Test_FrameworkProgramProcessPercentage,
|
||
|
||
// 设备状态
|
||
Test_PartsEquipmentStatus = webSocketData.TestData.Test_PartsEquipmentStatus,
|
||
|
||
// 清洗时长和用量
|
||
Test_FrameworkPerModelCleaningDuration = webSocketData.TestData.Test_FrameworkPerModelCleaningDuration,
|
||
Test_FrameworkPerModelCleaningAgentUsage = webSocketData.TestData.Test_FrameworkPerModelCleaningAgentUsage,
|
||
Test_FrameworkPerModelWaterUsage = webSocketData.TestData.Test_FrameworkPerModelWaterUsage,
|
||
|
||
// 水箱和清洗剂罐信息
|
||
WaterTank_Temp = webSocketData.TestData.Test_WaterTankTemperature,
|
||
AgentTank_Temp = webSocketData.TestData.Test_CleaningAgentTankTemperature,
|
||
WaterTank_Level = webSocketData.TestData.Test_WaterTankLevel,
|
||
AgentTank_Level = webSocketData.TestData.Test_CleaningAgentTankLevel,
|
||
|
||
// 浸泡池温度
|
||
SoakingTank1_Temp = webSocketData.TestData.Test_SoakingTank1Temperature,
|
||
SoakingTank2_Temp = webSocketData.TestData.Test_SoakingTank2Temperature,
|
||
|
||
// 运行模式
|
||
Test_WaterTankHeat = webSocketData.TestData.Test_WaterTankHeat,
|
||
Test_WaterTankAdd = webSocketData.TestData.Test_WaterTankAdd,
|
||
Test_CleaningAgentTankHeat = webSocketData.TestData.Test_CleaningAgentTankHeat,
|
||
Test_CleaningAgentTankAdd = webSocketData.TestData.Test_CleaningAgentTankAdd,
|
||
|
||
// 监控信息
|
||
Test_ElectricSurveillance = webSocketData.TestData.Test_ElectricSurveillance,
|
||
Test_SteamSurveillance = webSocketData.TestData.Test_SteamSurveillance
|
||
};
|
||
|
||
var Data = FRemoteSqlContext.FDb
|
||
.InsertOrUpdate<CurRunClearState>()
|
||
.SetSource(curRunClearState)
|
||
.ExecuteAffrows();
|
||
|
||
if (Data > 0)
|
||
{
|
||
Logger.Debug("实时数据已更新到远程数据库");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error($"UpdateRemoteDb: {ex.StackTrace}");
|
||
}
|
||
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 更新故障信息
|
||
/// </summary>
|
||
/// <param name="rtVars">RTVar数据列表</param>
|
||
private void UpdateFaultDetails(List<RTVar> rtVars)
|
||
{
|
||
var hasFault = rtVars.FirstOrDefault(v => v.Name.Contains("Fault_"));
|
||
if (hasFault != null && !string.IsNullOrEmpty(hasFault.Val))
|
||
{
|
||
if (_realtimeData.FaultDetails == null)
|
||
_realtimeData.FaultDetails = new FaultDetails();
|
||
|
||
var faultDevice = rtVars.FirstOrDefault(v => v.Name == "Fault_Code");
|
||
if (faultDevice != null)
|
||
_realtimeData.FaultDetails.Fault_Code = faultDevice.Val;
|
||
|
||
var faultTime = rtVars.FirstOrDefault(v => v.Name == "Fault_Time");
|
||
if (faultTime != null && DateTime.TryParse(faultTime.Val, out DateTime time))
|
||
_realtimeData.FaultDetails.Fault_Time = time;
|
||
|
||
var faultDescription = rtVars.FirstOrDefault(v => v.Name == "Fault_Description");
|
||
if (faultDescription != null)
|
||
_realtimeData.FaultDetails.Fault_Description = faultDescription.Val;
|
||
}
|
||
else
|
||
{
|
||
// 没有故障时,置空故障信息
|
||
_realtimeData.FaultDetails = null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新组件信息
|
||
/// </summary>
|
||
/// <param name="rtVars">RTVar数据列表</param>
|
||
private void UpdateComponentsInfo(List<RTVar> rtVars)
|
||
{
|
||
var componentsCountVar = rtVars.FirstOrDefault(v => v.Name.Contains("part_"));
|
||
if (componentsCountVar != null)
|
||
{
|
||
if (_realtimeData.ListComponentsInfo == null) _realtimeData.ListComponentsInfo = new List<ComponentsInfo>() { new ComponentsInfo() };
|
||
|
||
//else
|
||
// _realtimeData.ListComponentsInfo.Clear();
|
||
|
||
var component = _realtimeData.ListComponentsInfo.FirstOrDefault();
|
||
|
||
//var partQRCode = rtVars.FirstOrDefault(v => v.Name == $"part_qrid");
|
||
//if (partQRCode != null)
|
||
// component.part_qrid = partQRCode.Val;
|
||
|
||
var partNum = rtVars.FirstOrDefault(v => v.Name == $"part_num");
|
||
if (partNum != null)
|
||
component.part_num = partNum.Val;
|
||
|
||
var partPosition = rtVars.FirstOrDefault(v => v.Name == $"part_position");
|
||
if (partPosition != null)
|
||
component.part_position = partPosition.Val;
|
||
|
||
var componentName = rtVars.FirstOrDefault(v => v.Name == $"part_name");
|
||
if (componentName != null)
|
||
component.part_name = componentName.Val;
|
||
|
||
// 车型
|
||
var vehicleModel = rtVars.FirstOrDefault(v => v.Name == $"part_Vehicle_model");
|
||
if (vehicleModel != null)
|
||
component.part_Vehicle_model = vehicleModel.Val;
|
||
|
||
// 车号
|
||
var locomotiveNumber = rtVars.FirstOrDefault(v => v.Name == $"part_locomotive_number");
|
||
if (locomotiveNumber != null)
|
||
component.part_locomotive_number = locomotiveNumber.Val;
|
||
|
||
// 修程
|
||
var repairProcess = rtVars.FirstOrDefault(v => v.Name == $"part_repair_process");
|
||
if (repairProcess != null)
|
||
component.part_repair_process = repairProcess.Val;
|
||
|
||
// Id
|
||
var part_qrid = rtVars.FirstOrDefault(v => v.Name == $"part_qrid");
|
||
if (part_qrid != null)
|
||
component.part_qrid = part_qrid.Val;
|
||
|
||
}
|
||
else
|
||
{
|
||
// 没有组件信息时,初始化空列表
|
||
if (_realtimeData.ListComponentsInfo == null)
|
||
_realtimeData.ListComponentsInfo = new List<ComponentsInfo>() { new ComponentsInfo() };
|
||
//else
|
||
// _realtimeData.ListComponentsInfo.Clear();
|
||
}
|
||
}
|
||
|
||
///// <summary>
|
||
///// 更新组件信息
|
||
///// </summary>
|
||
///// <param name="rtVars">RTVar数据列表</param>
|
||
//private void UpdateComponentsInfoMulit(List<RTVar> rtVars)
|
||
//{
|
||
// var componentsCountVar = rtVars.FirstOrDefault(v => v.Name == "ComponentsCount");
|
||
// if (componentsCountVar != null && int.TryParse(componentsCountVar.Val, out int count) && count > 0)
|
||
// {
|
||
// if (_realtimeData.ListComponentsInfo == null)
|
||
// _realtimeData.ListComponentsInfo = new List<ComponentsInfo>();
|
||
// else
|
||
// _realtimeData.ListComponentsInfo.Clear();
|
||
|
||
// // 解析多个组件信息
|
||
// for (int i = 0; i < count; i++)
|
||
// {
|
||
// var component = new ComponentsInfo();
|
||
|
||
// var partQRCode = rtVars.FirstOrDefault(v => v.Name == $"Component_{i}_PartQRCode");
|
||
// if (partQRCode != null)
|
||
// component.part_qrid = partQRCode.Val;
|
||
|
||
// var partNum = rtVars.FirstOrDefault(v => v.Name == $"Component_{i}_PartNum");
|
||
// if (partNum != null)
|
||
// component.part_num = partNum.Val;
|
||
|
||
// var partPosition = rtVars.FirstOrDefault(v => v.Name == $"Component_{i}_PartPosition");
|
||
// if (partPosition != null && int.TryParse(partPosition.Val, out int position))
|
||
// component.part_position = position;
|
||
|
||
// var componentName = rtVars.FirstOrDefault(v => v.Name == $"Component_{i}_ComponentName");
|
||
// if (componentName != null)
|
||
// component.component_name = componentName.Val;
|
||
|
||
// // 车型
|
||
// var vehicleModel = rtVars.FirstOrDefault(v => v.Name == $"Component_{i}_VehicleModel");
|
||
// if (vehicleModel != null)
|
||
// component.Vehicle_model = vehicleModel.Val;
|
||
|
||
// // 车号
|
||
// var locomotiveNumber = rtVars.FirstOrDefault(v => v.Name == $"Component_{i}_LocomotiveNumber");
|
||
// if (locomotiveNumber != null)
|
||
// component.locomotive_number = locomotiveNumber.Val;
|
||
|
||
// // 修程
|
||
// var repairProcess = rtVars.FirstOrDefault(v => v.Name == $"Component_{i}_RepairProcess");
|
||
// if (repairProcess != null)
|
||
// component.repair_process = repairProcess.Val;
|
||
|
||
// // 添加到列表
|
||
// _realtimeData.ListComponentsInfo.Add(component);
|
||
// }
|
||
// }
|
||
// else
|
||
// {
|
||
// // 没有组件信息时,初始化空列表
|
||
// if (_realtimeData.ListComponentsInfo == null)
|
||
// _realtimeData.ListComponentsInfo = new List<ComponentsInfo>();
|
||
// else
|
||
// _realtimeData.ListComponentsInfo.Clear();
|
||
// }
|
||
//}
|
||
|
||
|
||
/// <summary>
|
||
/// 更新测试数据
|
||
/// </summary>
|
||
/// <param name="rtVars">RTVar数据列表</param>
|
||
private void UpdateTestData(List<RTVar> rtVars)
|
||
{
|
||
var hasTestData = rtVars.FirstOrDefault(v => v.Name.Contains("Test_"));
|
||
if (hasTestData != null && !string.IsNullOrEmpty(hasTestData.Val))
|
||
{
|
||
if (_realtimeData.TestData == null)
|
||
_realtimeData.TestData = new TestData();
|
||
|
||
// 从RTVar中提取测试数据
|
||
var testItems = rtVars.Where(v => v.Name.StartsWith("Test_")).ToList();
|
||
foreach (var item in testItems)
|
||
{
|
||
// 根据命名规则解析测试数据项
|
||
string itemName = item.Name;
|
||
|
||
// 使用反射设置属性
|
||
var property = typeof(TestData).GetProperty(itemName);
|
||
if (property != null && property.CanWrite)
|
||
{
|
||
if (property.PropertyType == typeof(string))
|
||
{
|
||
property.SetValue(_realtimeData.TestData, item.Val);
|
||
}
|
||
else if (property.PropertyType == typeof(int) && int.TryParse(item.Val, out int intValue))
|
||
{
|
||
property.SetValue(_realtimeData.TestData, intValue);
|
||
}
|
||
else if (property.PropertyType == typeof(double) && double.TryParse(item.Val, out double doubleValue))
|
||
{
|
||
property.SetValue(_realtimeData.TestData, doubleValue);
|
||
}
|
||
else if (property.PropertyType == typeof(DateTime) && DateTime.TryParse(item.Val, out DateTime dateValue))
|
||
{
|
||
property.SetValue(_realtimeData.TestData, dateValue);
|
||
}
|
||
else if (property.PropertyType == typeof(bool) && bool.TryParse(item.Val, out bool boolValue))
|
||
{
|
||
property.SetValue(_realtimeData.TestData, boolValue);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 没有测试数据时,置空测试数据对象
|
||
_realtimeData.TestData = null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取最新的RTVar数据(可在外部直接调用)
|
||
/// </summary>
|
||
/// <returns>WebSocketData对象</returns>
|
||
public async Task<WebSocketData> GetLatestDataAsync()
|
||
{
|
||
await ReadRTVarDataAsync();
|
||
return _realtimeData;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 释放资源
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
StopPolling();
|
||
_cancellationTokenSource?.Dispose();
|
||
_dataPollingTimer?.Dispose();
|
||
}
|
||
}
|
||
}
|