添加项目文件。
This commit is contained in:
77
MoviconHub.App/Services/ApiHelper.cs
Normal file
77
MoviconHub.App/Services/ApiHelper.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using MoviconHub.App.Com;
|
||||
using MoviconHub.App.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MoviconHub.App.Services
|
||||
{
|
||||
public static class ApiHelper
|
||||
{
|
||||
private static ApiService _apiService;
|
||||
private static ApiConfig _apiConfig;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化API帮助类
|
||||
/// </summary>
|
||||
public static void Initialize()
|
||||
{
|
||||
//_apiConfig = ApiConfig.LoadConfig();
|
||||
//string baseUrl = $"http://{_apiConfig.ServerAddress}:{_apiConfig.ServerPort}/";
|
||||
string baseUrl = $"http://172.16.3.203:8000";
|
||||
_apiService = new ApiService(baseUrl);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取部件信息
|
||||
/// </summary>
|
||||
/// <param name="partQrCodeId">二维码零件ID</param>
|
||||
/// <param name="deviceCode">设备编码</param>
|
||||
/// <returns>部件信息</returns>
|
||||
public static async Task<PartInfoResponse> GetPartInfoAsync(string partQrCodeId, string deviceCode)
|
||||
{
|
||||
if (_apiService == null)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
//当前不需要设备条码
|
||||
return await _apiService.GetPartInfoAsync(partQrCodeId, deviceCode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取测试数据
|
||||
/// </summary>
|
||||
/// <param name="partQrCodeId">二维码零件ID</param>
|
||||
/// <param name="deviceCode">设备编码</param>
|
||||
/// <returns>测试数据</returns>
|
||||
public static async Task<TestDataResponse> GetTestDataAsync(string partQrCodeId, string deviceCode)
|
||||
{
|
||||
if (_apiService == null)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
return await _apiService.GetTestDataAsync(partQrCodeId, deviceCode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新服务器配置
|
||||
/// </summary>
|
||||
/// <param name="serverAddress">服务器地址</param>
|
||||
/// <param name="port">端口号</param>
|
||||
public static void UpdateServerConfig(string serverAddress, int port)
|
||||
{
|
||||
if (_apiService == null || _apiConfig == null)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
_apiConfig.ServerAddress = serverAddress;
|
||||
_apiConfig.ServerPort = port;
|
||||
_apiConfig.SaveConfig();
|
||||
|
||||
_apiService.ConfigureServerAddress(serverAddress, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
155
MoviconHub.App/Services/ApiService.cs
Normal file
155
MoviconHub.App/Services/ApiService.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using MoviconHub.App.Models;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using RestSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MoviconHub.App.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Api 服务类
|
||||
/// </summary>
|
||||
public class ApiService
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
private readonly string _baseUrl;
|
||||
private readonly RestClient _client;
|
||||
|
||||
/// <summary>
|
||||
/// 实例化函数
|
||||
/// </summary>
|
||||
/// <param name="baseUrl"></param>
|
||||
public ApiService(string baseUrl)
|
||||
{
|
||||
_baseUrl = baseUrl;
|
||||
_client = new RestClient(baseUrl);
|
||||
}
|
||||
|
||||
private string GetFullRequestUrl(RestRequest request)
|
||||
{
|
||||
var resource = request.Resource;
|
||||
var queryParams = string.Join("&", request.Parameters
|
||||
.Where(p => p.Type == ParameterType.QueryString)
|
||||
.Select(p => $"{p.Name}={Uri.EscapeDataString(p.Value?.ToString() ?? "")}"));
|
||||
|
||||
return $"{_baseUrl.TrimEnd('/')}/{resource.TrimStart('/')}{(string.IsNullOrEmpty(queryParams) ? "" : "?" + queryParams)}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取部件信息
|
||||
/// </summary>
|
||||
/// <param name="partQrCodeId">二维码零件ID</param>
|
||||
/// <param name="deviceCode">设备编码</param>
|
||||
/// <returns>部件信息</returns>
|
||||
public async Task<PartInfoResponse> GetPartInfoAsync(string partQrCodeId, string deviceCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new RestRequest("/mes/order/iotPartInfo/getPartInfo", Method.Get);
|
||||
request.AddParameter("PartQRCodeId", partQrCodeId);
|
||||
//request.AddParameter("DeviceCode", deviceCode);
|
||||
|
||||
var response = await _client.ExecuteAsync(request);
|
||||
|
||||
if (response.IsSuccessful)
|
||||
{
|
||||
Logger.Info($"{response.Content}");
|
||||
var Data = JsonConvert.DeserializeObject<PartInfoResponse>(response.Content);
|
||||
Logger.Info($"{Data.ToString()}");
|
||||
return JsonConvert.DeserializeObject<PartInfoResponse>(response.Content);
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error($"获取部件信息失败:{response.ErrorMessage}, 状态码:{response.StatusCode}");
|
||||
return new PartInfoResponse
|
||||
{
|
||||
Status = (int)response.StatusCode,
|
||||
Message = response.ErrorMessage,
|
||||
Data = null
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "获取部件信息时发生异常");
|
||||
return new PartInfoResponse
|
||||
{
|
||||
Status = 500,
|
||||
Message = $"发生异常:{ex.Message}",
|
||||
Data = null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取测试数据
|
||||
/// </summary>
|
||||
/// <param name="partQrCodeId">二维码零件ID</param>
|
||||
/// <param name="deviceCode">设备编码</param>
|
||||
/// <returns>测试数据</returns>
|
||||
public async Task<TestDataResponse> GetTestDataAsync(string partQrCodeId, string deviceCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new RestRequest("getTestdata", Method.Get);
|
||||
request.AddParameter("PartQRCodeId", partQrCodeId);
|
||||
request.AddParameter("DeviceCode", deviceCode);
|
||||
|
||||
var response = await _client.ExecuteAsync(request);
|
||||
|
||||
if (response.IsSuccessful)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<TestDataResponse>(response.Content);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error($"获取测试数据失败:{response.ErrorMessage}, 状态码:{response.StatusCode}");
|
||||
return new TestDataResponse
|
||||
{
|
||||
Status = (int)response.StatusCode,
|
||||
Message = response.ErrorMessage,
|
||||
Data = null
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "获取测试数据时发生异常");
|
||||
return new TestDataResponse
|
||||
{
|
||||
Status = 500,
|
||||
Message = $"发生异常:{ex.Message}",
|
||||
Data = null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置服务器地址和端口
|
||||
/// </summary>
|
||||
/// <param name="serverAddress">服务器地址</param>
|
||||
/// <param name="port">端口号</param>
|
||||
public void ConfigureServerAddress(string serverAddress, int port)
|
||||
{
|
||||
string newBaseUrl = $"http://{serverAddress}:{port}/";
|
||||
// 创建新的RestClient实例
|
||||
var newClient = new RestClient(newBaseUrl);
|
||||
|
||||
// 使用反射替换私有字段,因为RestClient实例不能直接修改BaseUrl
|
||||
var clientType = _client.GetType();
|
||||
var baseUrlField = clientType.GetField("_baseUrl", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
if (baseUrlField != null)
|
||||
{
|
||||
baseUrlField.SetValue(_client, newBaseUrl);
|
||||
}
|
||||
|
||||
Logger.Info($"服务器地址已更新为:{newBaseUrl}");
|
||||
}
|
||||
}
|
||||
}
|
||||
510
MoviconHub.App/Services/DBServices.cs
Normal file
510
MoviconHub.App/Services/DBServices.cs
Normal file
@@ -0,0 +1,510 @@
|
||||
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, "轮询RTVar数据时发生错误");
|
||||
// 发生错误时,等待一段时间后重试
|
||||
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(ex, "读取RTVar数据时发生错误");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用RTVar数据更新WebSocketData模型
|
||||
/// </summary>
|
||||
/// <param name="rtVars">RTVar数据列表</param>
|
||||
private void UpdateWebSocketData(List<RTVar> rtVars)
|
||||
{
|
||||
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("实时数据已更新");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新远程数据库
|
||||
/// </summary>
|
||||
/// <param name="webSocketData"></param>
|
||||
private void UpdateRemoteDb(WebSocketData webSocketData)
|
||||
{
|
||||
// 获取组件信息
|
||||
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("实时数据已更新到远程数据库");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <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();
|
||||
}
|
||||
}
|
||||
}
|
||||
561
MoviconHub.App/Services/WebSocketClient.cs
Normal file
561
MoviconHub.App/Services/WebSocketClient.cs
Normal file
@@ -0,0 +1,561 @@
|
||||
using HslCommunication;
|
||||
using MoviconHub.App.Com;
|
||||
using MoviconHub.App.Models;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MoviconHub.App.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// WebSocket客户端类,基于HslCommunication库实现
|
||||
/// </summary>
|
||||
public class WebSocketClient : IDisposable
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
private readonly WebSocketConfig _config;
|
||||
private HslCommunication.WebSocket.WebSocketClient _client;
|
||||
private Timer _heartbeatTimer;
|
||||
private Timer _reconnectTimer;
|
||||
private bool _isConnected = false;
|
||||
private bool _reconnecting = false;
|
||||
private bool _disposed = false;
|
||||
private int _reconnectAttempts = 0;
|
||||
private readonly int _maxReconnectInterval = 300000; // 最大重连间隔,默认5分钟
|
||||
private readonly object _lockObject = new object();
|
||||
|
||||
// 事件定义
|
||||
public event EventHandler<MoviconHub.App.Models.WebSocketMessageEventArgs> MessageReceived;
|
||||
public event EventHandler<WebSocketConnectEventArgs> Connected;
|
||||
public event EventHandler<WebSocketConnectEventArgs> Disconnected;
|
||||
public event EventHandler<WebSocketErrorEventArgs> Error;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化WebSocket客户端
|
||||
/// </summary>
|
||||
public WebSocketClient()
|
||||
{
|
||||
_config = WebSocketConfig.LoadConfig();
|
||||
InitializeClient();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化WebSocket客户端
|
||||
/// </summary>
|
||||
private void InitializeClient()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 初始化客户端并设置服务器地址和端口
|
||||
_client = new HslCommunication.WebSocket.WebSocketClient(_config.ServerAddress, _config.ServerPort, _config.Url);
|
||||
|
||||
// 设置日志记录器
|
||||
_client.LogNet = new HslCommunication.LogNet.LogNetSingle("websocket_logs.txt");
|
||||
|
||||
// 注册消息接收事件处理
|
||||
_client.OnClientApplicationMessageReceive += (message) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.Debug($"收到消息: {message.ToString()}");
|
||||
|
||||
// 尝试解析消息
|
||||
MoviconHub.App.Models.WebSocketMessage wsMessage =
|
||||
JsonConvert.DeserializeObject<MoviconHub.App.Models.WebSocketMessage>(message.ToString());
|
||||
|
||||
if (wsMessage != null)
|
||||
{
|
||||
// 触发消息接收事件
|
||||
MessageReceived?.Invoke(this, new MoviconHub.App.Models.WebSocketMessageEventArgs
|
||||
{
|
||||
Message = wsMessage,
|
||||
RawMessage = message.ToString()
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "处理接收到的WebSocket消息时发生错误");
|
||||
}
|
||||
};
|
||||
|
||||
// 注册连接成功事件
|
||||
_client.OnClientConnected += () =>
|
||||
{
|
||||
Logger.Info($"已成功连接到WebSocket服务器 {_config.ServerAddress}:{_config.ServerPort}");
|
||||
_isConnected = true;
|
||||
|
||||
|
||||
// 启动心跳定时器
|
||||
//StartHeartbeatTimer();
|
||||
|
||||
// 触发连接事件
|
||||
Connected?.Invoke(this, new WebSocketConnectEventArgs
|
||||
{
|
||||
ServerAddress = _config.ServerAddress,
|
||||
ServerPort = _config.ServerPort,
|
||||
IsReconnection = _reconnectAttempts > 0
|
||||
});
|
||||
};
|
||||
|
||||
// 注册网络错误事件 - 根据文档示例调整参数
|
||||
_client.OnNetworkError += (sender, e) =>
|
||||
{
|
||||
string errorMessage = "WebSocket网络连接错误";
|
||||
Logger.Error(errorMessage);
|
||||
HandleDisconnection();
|
||||
Error?.Invoke(this, new WebSocketErrorEventArgs { ErrorMessage = errorMessage });
|
||||
};
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "初始化WebSocket客户端时发生错误");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 连接到WebSocket服务器
|
||||
/// </summary>
|
||||
/// <returns>连接结果</returns>
|
||||
public async Task<bool> ConnectAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(WebSocketClient));
|
||||
|
||||
if (_isConnected)
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (_isConnected)
|
||||
return true;
|
||||
|
||||
_reconnecting = false;
|
||||
_reconnectAttempts = 0;
|
||||
}
|
||||
|
||||
// 确保设置了服务器地址和端口
|
||||
_client.IpAddress = _config.ServerAddress;
|
||||
_client.Port = _config.ServerPort;
|
||||
|
||||
Logger.Info($"正在连接到WebSocket服务器: ws://{_config.ServerAddress}:{_config.ServerPort}");
|
||||
|
||||
// 创建一个任务来包装同步连接方法
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
var connectTask = Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = _client.ConnectServer();
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
tcs.SetResult(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn($"WebSocket连接失败: {result.Message}");
|
||||
tcs.SetResult(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "WebSocket连接过程中发生异常");
|
||||
tcs.SetResult(false);
|
||||
}
|
||||
});
|
||||
|
||||
// 添加超时控制
|
||||
var timeoutTask = Task.Delay(_config.ConnectionTimeout);
|
||||
var completedTask = await Task.WhenAny(tcs.Task, timeoutTask);
|
||||
|
||||
if (completedTask == timeoutTask)
|
||||
{
|
||||
Logger.Warn($"连接到WebSocket服务器 {_config.ServerAddress}:{_config.ServerPort} 超时");
|
||||
Error?.Invoke(this, new WebSocketErrorEventArgs
|
||||
{
|
||||
Exception = new TimeoutException("WebSocket连接超时"),
|
||||
ErrorMessage = "连接超时"
|
||||
});
|
||||
|
||||
if (_config.AutoReconnect)
|
||||
StartReconnectTimer();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool connectionSuccess = await tcs.Task;
|
||||
|
||||
if (!connectionSuccess && _config.AutoReconnect)
|
||||
{
|
||||
StartReconnectTimer();
|
||||
}
|
||||
|
||||
return connectionSuccess;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, $"连接到WebSocket服务器 {_config.ServerAddress}:{_config.ServerPort} 时发生错误");
|
||||
Error?.Invoke(this, new WebSocketErrorEventArgs { Exception = ex, ErrorMessage = ex.Message });
|
||||
|
||||
if (_config.AutoReconnect)
|
||||
StartReconnectTimer();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 断开与WebSocket服务器的连接
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
// 停止定时器
|
||||
StopHeartbeatTimer();
|
||||
StopReconnectTimer();
|
||||
|
||||
if (_isConnected)
|
||||
{
|
||||
_client.ConnectClose();
|
||||
_isConnected = false;
|
||||
|
||||
Logger.Info("已断开与WebSocket服务器的连接");
|
||||
Disconnected?.Invoke(this, new WebSocketConnectEventArgs
|
||||
{
|
||||
ServerAddress = _config.ServerAddress,
|
||||
ServerPort = _config.ServerPort,
|
||||
IsReconnection = false
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "断开WebSocket连接时发生错误");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送消息
|
||||
/// </summary>
|
||||
/// <param name="message">要发送的消息</param>
|
||||
/// <returns>是否发送成功</returns>
|
||||
public bool SendMessage(MoviconHub.App.Models.WebSocketMessage message)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(WebSocketClient));
|
||||
|
||||
if (!_isConnected)
|
||||
{
|
||||
Logger.Warn("未连接到WebSocket服务器,无法发送消息");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string json = JsonConvert.SerializeObject(message);
|
||||
var sendResult = _client.SendServer(json);
|
||||
|
||||
if (sendResult.IsSuccess)
|
||||
{
|
||||
Logger.Debug($"消息已发送: {json}");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn($"发送消息失败: {sendResult.Message}");
|
||||
HandleDisconnection();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "发送WebSocket消息时发生错误");
|
||||
HandleDisconnection();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送设备数据
|
||||
/// 全量数据
|
||||
/// </summary>
|
||||
/// <param name="statusData">状态数据</param>
|
||||
/// <returns>是否发送成功</returns>
|
||||
public bool SendDeviceData(WebSocketData webSocketData)
|
||||
{
|
||||
MoviconHub.App.Models.WebSocketMessage message =
|
||||
MoviconHub.App.Models.WebSocketMessage.CreateDeviceData( webSocketData);
|
||||
return SendMessage(message);
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 发送设备状态数据
|
||||
/// </summary>
|
||||
/// <param name="statusData">状态数据</param>
|
||||
/// <returns>是否发送成功</returns>
|
||||
public bool SendDeviceStatus(DeviceStatusData statusData)
|
||||
{
|
||||
MoviconHub.App.Models.WebSocketMessage message =
|
||||
MoviconHub.App.Models.WebSocketMessage.CreateDeviceStatusMessage(_config.DeviceCode, statusData);
|
||||
return SendMessage(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送设备故障信息
|
||||
/// </summary>
|
||||
/// <param name="faultData">故障数据</param>
|
||||
/// <returns>是否发送成功</returns>
|
||||
public bool SendDeviceFault(FaultDetails faultData)
|
||||
{
|
||||
MoviconHub.App.Models.WebSocketMessage message =
|
||||
MoviconHub.App.Models.WebSocketMessage.CreateFaultMessage(_config.DeviceCode, faultData);
|
||||
return SendMessage(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送测试数据
|
||||
/// </summary>
|
||||
/// <param name="testData">测试数据</param>
|
||||
/// <returns>是否发送成功</returns>
|
||||
public bool SendTestData(TestData testData)
|
||||
{
|
||||
MoviconHub.App.Models.WebSocketMessage message =
|
||||
MoviconHub.App.Models.WebSocketMessage.CreateTestDataMessage(_config.DeviceCode, testData);
|
||||
return SendMessage(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新配置
|
||||
/// </summary>
|
||||
/// <param name="serverAddress">服务器地址</param>
|
||||
/// <param name="port">端口</param>
|
||||
/// <param name="deviceCode">设备编码</param>
|
||||
/// <param name="reconnect">是否重连</param>
|
||||
public async Task UpdateConfig(string serverAddress, int port, string deviceCode, bool reconnect = true)
|
||||
{
|
||||
bool wasConnected = _isConnected;
|
||||
|
||||
if (wasConnected)
|
||||
Disconnect();
|
||||
|
||||
_config.ServerAddress = serverAddress;
|
||||
_config.ServerPort = port;
|
||||
_config.DeviceCode = deviceCode;
|
||||
_config.SaveConfig();
|
||||
|
||||
// 重新创建客户端
|
||||
_client.ConnectClose();
|
||||
InitializeClient();
|
||||
|
||||
if (wasConnected && reconnect)
|
||||
await ConnectAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理断开连接情况
|
||||
/// </summary>
|
||||
private void HandleDisconnection()
|
||||
{
|
||||
if (!_isConnected)
|
||||
return;
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (!_isConnected)
|
||||
return;
|
||||
|
||||
_isConnected = false;
|
||||
StopHeartbeatTimer();
|
||||
|
||||
Logger.Warn("WebSocket连接已断开");
|
||||
|
||||
Disconnected?.Invoke(this, new WebSocketConnectEventArgs
|
||||
{
|
||||
ServerAddress = _config.ServerAddress,
|
||||
ServerPort = _config.ServerPort,
|
||||
IsReconnection = false
|
||||
});
|
||||
|
||||
if (_config.AutoReconnect && !_reconnecting)
|
||||
{
|
||||
StartReconnectTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 启动心跳定时器
|
||||
/// </summary>
|
||||
private void StartHeartbeatTimer()
|
||||
{
|
||||
StopHeartbeatTimer();
|
||||
|
||||
_heartbeatTimer = new Timer(SendHeartbeat, null,
|
||||
_config.HeartbeatInterval,
|
||||
_config.HeartbeatInterval);
|
||||
|
||||
Logger.Debug($"心跳定时器已启动,间隔: {_config.HeartbeatInterval}毫秒");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止心跳定时器
|
||||
/// </summary>
|
||||
private void StopHeartbeatTimer()
|
||||
{
|
||||
_heartbeatTimer?.Dispose();
|
||||
_heartbeatTimer = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送心跳消息
|
||||
/// </summary>
|
||||
private void SendHeartbeat(object state)
|
||||
{
|
||||
if (!_isConnected)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
MoviconHub.App.Models.WebSocketMessage heartbeat =
|
||||
MoviconHub.App.Models.WebSocketMessage.CreateHeartbeat(_config.DeviceCode);
|
||||
SendMessage(heartbeat);
|
||||
Logger.Debug("已发送心跳消息");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "发送心跳消息时发生错误");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动重连定时器
|
||||
/// </summary>
|
||||
private void StartReconnectTimer()
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (_reconnecting)
|
||||
return;
|
||||
|
||||
_reconnecting = true;
|
||||
|
||||
StopReconnectTimer();
|
||||
|
||||
_reconnectTimer = new Timer(ReconnectCallback, null,
|
||||
_config.ReconnectInterval,
|
||||
Timeout.Infinite); // 只执行一次,在重连方法中再次设置定时器
|
||||
|
||||
Logger.Info($"重连定时器已启动,间隔: {_config.ReconnectInterval}毫秒");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重连回调方法
|
||||
/// </summary>
|
||||
private void ReconnectCallback(object stateObj)
|
||||
{
|
||||
if (_isConnected || _disposed)
|
||||
return;
|
||||
|
||||
// 使用Task执行异步重连
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await ReconnectAsync();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止重连定时器
|
||||
/// </summary>
|
||||
private void StopReconnectTimer()
|
||||
{
|
||||
_reconnectTimer?.Dispose();
|
||||
_reconnectTimer = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行重连
|
||||
/// </summary>
|
||||
private async Task ReconnectAsync()
|
||||
{
|
||||
if (_isConnected || _disposed)
|
||||
return;
|
||||
|
||||
_reconnectAttempts++;
|
||||
Logger.Info($"正在尝试重连,第 {_reconnectAttempts} 次");
|
||||
|
||||
bool success = await ConnectAsync();
|
||||
|
||||
if (!success && _config.AutoReconnect && !_disposed)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (!_isConnected && !_disposed)
|
||||
{
|
||||
// 计算指数退避重连间隔
|
||||
int nextInterval = Math.Min(
|
||||
_config.ReconnectInterval * (int)Math.Pow(2, Math.Min(9, _reconnectAttempts - 1)), // 最多512倍,避免间隔过长
|
||||
_maxReconnectInterval); // 最大重试间隔
|
||||
|
||||
Logger.Info($"重连失败,{nextInterval}毫秒后将再次重试");
|
||||
|
||||
_reconnectTimer = new Timer(ReconnectCallback, null, nextInterval, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (success)
|
||||
{
|
||||
_reconnecting = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否已连接
|
||||
/// </summary>
|
||||
public bool IsConnected => _isConnected;
|
||||
|
||||
/// <summary>
|
||||
/// 释放资源
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放资源
|
||||
/// </summary>
|
||||
/// <param name="disposing">是否由Dispose调用</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
// 释放托管资源
|
||||
Disconnect();
|
||||
_heartbeatTimer?.Dispose();
|
||||
_reconnectTimer?.Dispose();
|
||||
_client?.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
193
MoviconHub.App/Services/WebSocketClientHelper.cs
Normal file
193
MoviconHub.App/Services/WebSocketClientHelper.cs
Normal file
@@ -0,0 +1,193 @@
|
||||
using MoviconHub.App.Com;
|
||||
using MoviconHub.App.Models;
|
||||
using NLog;
|
||||
using ReaLTaiizor.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MoviconHub.App.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// WebSocket客户端辅助类
|
||||
/// </summary>
|
||||
public static class WebSocketClientHelper
|
||||
{
|
||||
private static WebSocketClient _webSocketClient;
|
||||
private static WebSocketConfig _webSocketConfig;
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
/// <summary>
|
||||
/// ScanTask扫描Task
|
||||
/// </summary>
|
||||
private static Task ScanTask { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 扫描线程使能
|
||||
/// </summary>
|
||||
public static bool ThreadEnable { get; set; } = true;
|
||||
|
||||
public static WebSocketData CurWebSocketData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 初始化WebSocket客户端
|
||||
/// </summary>
|
||||
public static async void Initialize()
|
||||
{
|
||||
_webSocketConfig = WebSocketConfig.LoadConfig();
|
||||
_webSocketClient = new WebSocketClient();
|
||||
|
||||
// 注册事件处理程序
|
||||
_webSocketClient.MessageReceived += OnMessageReceived;
|
||||
_webSocketClient.Connected += OnConnected;
|
||||
_webSocketClient.Disconnected += OnDisconnected;
|
||||
_webSocketClient.Error += OnError;
|
||||
|
||||
await ConnectAsync();
|
||||
|
||||
Logger.Info("WebSocket客户端辅助类已初始化");
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///发布实时数据
|
||||
/// </summary>
|
||||
public static void PubRtDataStart()
|
||||
{
|
||||
ScanTask = Task.Run(async () =>
|
||||
{
|
||||
while (ThreadEnable)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(_webSocketConfig.Cycle * 1000);
|
||||
|
||||
if (SendDeviceData(CurWebSocketData))
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info("WebSocket客户端发送设备");
|
||||
}
|
||||
//SendDeviceData(CurWebSocketData);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 消息接收事件
|
||||
/// </summary>
|
||||
public static event EventHandler<WebSocketMessageEventArgs> MessageReceived;
|
||||
|
||||
/// <summary>
|
||||
/// 连接事件
|
||||
/// </summary>
|
||||
public static event EventHandler<WebSocketConnectEventArgs> Connected;
|
||||
|
||||
/// <summary>
|
||||
/// 断开连接事件
|
||||
/// </summary>
|
||||
public static event EventHandler<WebSocketConnectEventArgs> Disconnected;
|
||||
|
||||
/// <summary>
|
||||
/// 错误事件
|
||||
/// </summary>
|
||||
public static event EventHandler<WebSocketErrorEventArgs> Error;
|
||||
|
||||
/// <summary>
|
||||
/// 连接到WebSocket服务器
|
||||
/// </summary>
|
||||
/// <returns>连接结果</returns>
|
||||
public static async Task<bool> ConnectAsync()
|
||||
{
|
||||
if (_webSocketClient == null)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
return await _webSocketClient.ConnectAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 断开连接
|
||||
/// </summary>
|
||||
public static void Disconnect()
|
||||
{
|
||||
ThreadEnable = false;
|
||||
_webSocketClient?.Disconnect();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送设备状态数据
|
||||
/// </summary>
|
||||
/// <param name="statusData">状态数据</param>
|
||||
/// <returns>是否发送成功</returns>
|
||||
public static bool SendDeviceData(WebSocketData webSocketData)
|
||||
{
|
||||
if (_webSocketClient == null)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
return _webSocketClient.SendDeviceData(webSocketData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新配置
|
||||
/// </summary>
|
||||
/// <param name="serverAddress">服务器地址</param>
|
||||
/// <param name="port">端口</param>
|
||||
/// <param name="deviceCode">设备编码</param>
|
||||
/// <param name="reconnect">是否重连</param>
|
||||
public static async Task UpdateConfig(string serverAddress, int port, string deviceCode, bool reconnect = true)
|
||||
{
|
||||
if (_webSocketClient == null)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
await _webSocketClient.UpdateConfig(serverAddress, port, deviceCode, reconnect);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 客户端是否已连接
|
||||
/// </summary>
|
||||
public static bool IsConnected => _webSocketClient?.IsConnected ?? false;
|
||||
|
||||
// 内部事件处理方法
|
||||
private static void OnMessageReceived(object sender, WebSocketMessageEventArgs e)
|
||||
{
|
||||
Logger.Debug($"接收到消息:{e.RawMessage}");
|
||||
MessageReceived?.Invoke(sender, e);
|
||||
}
|
||||
|
||||
private static void OnConnected(object sender, WebSocketConnectEventArgs e)
|
||||
{
|
||||
Logger.Info($"已连接到WebSocket服务器 {e.ServerAddress}:{e.ServerPort}");
|
||||
Connected?.Invoke(sender, e);
|
||||
}
|
||||
|
||||
private static void OnDisconnected(object sender, WebSocketConnectEventArgs e)
|
||||
{
|
||||
Logger.Info($"已断开与WebSocket服务器 {e.ServerAddress}:{e.ServerPort} 的连接");
|
||||
Disconnected?.Invoke(sender, e);
|
||||
}
|
||||
|
||||
private static void OnError(object sender, WebSocketErrorEventArgs e)
|
||||
{
|
||||
Logger.Error(e.Exception, $"WebSocket错误: {e.ErrorMessage}");
|
||||
Error?.Invoke(sender, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user