添加项目文件。

This commit is contained in:
2025-09-15 17:59:48 +08:00
parent 872f090cc2
commit e7adae128e
91 changed files with 14260 additions and 0 deletions

View 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);
}
}
}

View 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}");
}
}
}

View 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();
}
}
}

View 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;
}
}
}

View 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);
}
}
}