Protocol376Simulator/Simulators/ConcentratorSimulator.cs
2025-05-08 17:26:10 +08:00

738 lines
29 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Protocol376Simulator.Interfaces;
using Protocol376Simulator.Models;
using Protocol376Simulator.Services;
using Serilog;
namespace Protocol376Simulator.Simulators
{
/// <summary>
/// 集中器模拟器类
/// </summary>
public class ConcentratorSimulator : ISimulator
{
public readonly string _concentratorAddress;
private readonly NetworkService _networkService;
private readonly HeartbeatService _heartbeatService;
private readonly ReconnectionService _reconnectionService;
private readonly StatisticsService _statisticsService;
private readonly DeviceDataService _deviceDataService;
private bool _autoResponse = true;
private DateTime _lastLoginTime = DateTime.MinValue;
private bool _loginConfirmed = false;
/// <summary>
/// 当接收到消息时触发
/// </summary>
public event EventHandler<byte[]> MessageReceived;
/// <summary>
/// 当状态变更时触发
/// </summary>
public event EventHandler<string> StatusChanged;
/// <summary>
/// 是否已连接
/// </summary>
public bool IsConnected => _networkService.IsConnected;
/// <summary>
/// 是否已登录
/// </summary>
public bool IsLoggedIn => _lastLoginTime > DateTime.MinValue && _loginConfirmed;
/// <summary>
/// 阀门状态
/// </summary>
public bool ValveStatus => _deviceDataService.ValveStatus;
/// <summary>
/// 成功发送的心跳次数
/// </summary>
public int SuccessfulHeartbeats => _heartbeatService.SuccessfulHeartbeats;
/// <summary>
/// 最后登录时间
/// </summary>
public DateTime LastLoginTime => _lastLoginTime;
/// <summary>
/// 是否启用自动重连
/// </summary>
public bool AutoReconnect
{
get => _reconnectionService.AutoReconnect;
set => _reconnectionService.AutoReconnect = value;
}
/// <summary>
/// 重连尝试次数
/// </summary>
public int ReconnectAttempts => _reconnectionService.ReconnectAttempts;
/// <summary>
/// 是否正在重连
/// </summary>
public bool IsReconnecting => _reconnectionService.IsReconnecting;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="concentratorAddress">集中器地址</param>
/// <param name="serverAddress">服务器地址</param>
/// <param name="serverPort">服务器端口</param>
public ConcentratorSimulator(string concentratorAddress, string serverAddress, int serverPort)
{
_concentratorAddress = concentratorAddress;
// 初始化网络服务
_networkService = new NetworkService(serverAddress, serverPort, $"集中器({concentratorAddress})");
_networkService.MessageReceived += OnMessageReceived;
_networkService.ConnectionStatusChanged += OnConnectionStatusChanged;
_networkService.ErrorOccurred += OnNetworkError;
// 初始化心跳服务
_heartbeatService = new HeartbeatService($"集中器({concentratorAddress})", SendHeartbeatMessageAsync);
// 初始化重连服务
_reconnectionService = new ReconnectionService(_networkService, $"集中器({concentratorAddress})");
_reconnectionService.ReconnectAttemptCompleted += OnReconnectAttemptCompleted;
// 初始化统计服务
_statisticsService = new StatisticsService($"集中器({concentratorAddress})");
// 初始化设备数据服务
_deviceDataService = new DeviceDataService($"集中器({concentratorAddress})");
_deviceDataService.ValveStatusChanged += OnValveStatusChanged;
}
/// <summary>
/// 启动模拟器
/// </summary>
/// <param name="autoLogin">是否自动登录</param>
/// <param name="autoHeartbeat">是否自动发送心跳</param>
public async Task StartAsync(bool autoLogin = false, bool autoHeartbeat = false)
{
try
{
Log.Information("集中器 (地址: {Address}) 正在启动...", _concentratorAddress);
// 连接到服务器
await _networkService.ConnectAsync();
// 如果启用自动登录,发送登录消息
if (autoLogin)
{
// 短暂延迟,确保连接稳定
await Task.Delay(100);
await SendLoginMessageAsync();
Log.Information("集中器 (地址: {Address}) 自动登录已发送", _concentratorAddress);
}
// 如果启用自动心跳,启动心跳任务
if (autoHeartbeat)
{
StartHeartbeat();
}
}
catch (Exception ex)
{
Log.Error(ex, "集中器 (地址: {Address}) 启动失败: {ErrorMessage}", _concentratorAddress, ex.Message);
StatusChanged?.Invoke(this, $"启动失败: {ex.Message}");
// 异常继续抛出,由调用者处理
throw;
}
}
/// <summary>
/// 停止模拟器
/// </summary>
public async Task StopAsync()
{
try
{
// 停止心跳服务
_heartbeatService.Stop();
// 断开网络连接
await _networkService.DisconnectAsync();
Log.Information("集中器 (地址: {Address}) 已停止", _concentratorAddress);
StatusChanged?.Invoke(this, "已停止");
}
catch (Exception ex)
{
Log.Error(ex, "集中器 (地址: {Address}) 停止时发生错误: {ErrorMessage}",
_concentratorAddress, ex.Message);
}
}
/// <summary>
/// 发送登录消息
/// </summary>
public async Task SendLoginMessageAsync()
{
if (!IsConnected)
{
Log.Warning("集中器 (地址: {Address}) 未连接,无法发送登录消息", _concentratorAddress);
return;
}
try
{
var loginMessage = Protocol376Message.CreateLoginMessage(_concentratorAddress);
await SendMessageAsync(loginMessage);
_lastLoginTime = DateTime.Now;
StatusChanged?.Invoke(this, "已发送登录请求");
Log.Information("集中器 (地址: {Address}) 发送登录消息", _concentratorAddress);
Log.Debug("集中器 (地址: {Address}) A&C报文详情: {MessageInfo}",
_concentratorAddress, loginMessage.GetMessageInfo());
}
catch (Exception ex)
{
Log.Error(ex, "集中器 (地址: {Address}) 发送登录消息失败: {ErrorMessage}",
_concentratorAddress, ex.Message);
_statisticsService.RecordError("登录消息发送失败");
throw;
}
}
/// <summary>
/// 发送心跳消息
/// </summary>
public async Task SendHeartbeatMessageAsync()
{
if (!IsConnected)
{
Log.Warning("集中器 (地址: {Address}) 未连接,无法发送心跳消息", _concentratorAddress);
return;
}
try
{
var heartbeatMessage = Protocol376Message.CreateHeartbeatMessage(_concentratorAddress);
await SendMessageAsync(heartbeatMessage);
Log.Information("集中器 (地址: {Address}) 发送心跳消息", _concentratorAddress);
Log.Debug("集中器 (地址: {Address}) 心跳报文详情: {MessageInfo}",
_concentratorAddress, heartbeatMessage.GetMessageInfo());
}
catch (Exception ex)
{
Log.Error(ex, "集中器 (地址: {Address}) 发送心跳消息失败: {ErrorMessage}",
_concentratorAddress, ex.Message);
_statisticsService.RecordError("心跳消息发送失败");
throw;
}
}
/// <summary>
/// 发送阀控操作消息
/// </summary>
/// <param name="valveOperation">阀门操作1=开阀2=关阀3=查询状态</param>
public async Task SendValveControlMessageAsync(byte valveOperation)
{
if (!IsConnected)
{
Log.Warning("集中器 (地址: {Address}) 未连接,无法发送阀控消息", _concentratorAddress);
return;
}
try
{
var valveMessage = Protocol376Message.CreateValveControlMessage(_concentratorAddress, valveOperation);
await SendMessageAsync(valveMessage);
Log.Information("集中器 (地址: {Address}) 发送阀控消息, 操作: {Operation}",
_concentratorAddress, valveOperation);
Log.Debug("集中器 (地址: {Address}) 阀控报文详情: {MessageInfo}",
_concentratorAddress, valveMessage.GetMessageInfo());
// 如果是开阀或关阀操作,更新阀门状态
if (valveOperation == 1)
{
_deviceDataService.UpdateValveStatus(true);
}
else if (valveOperation == 2)
{
_deviceDataService.UpdateValveStatus(false);
}
}
catch (Exception ex)
{
Log.Error(ex, "集中器 (地址: {Address}) 发送阀控消息失败: {ErrorMessage}",
_concentratorAddress, ex.Message);
_statisticsService.RecordError("阀控消息发送失败");
throw;
}
}
/// <summary>
/// 发送数据上传消息
/// </summary>
/// <param name="dataType">数据类型</param>
public async Task SendDataUploadMessageAsync(byte dataType)
{
if (!IsConnected)
{
Log.Warning("集中器 (地址: {Address}) 未连接,无法发送数据上传消息", _concentratorAddress);
return;
}
try
{
// 获取表计数据
byte[] meterData = _deviceDataService.GetMeterData(dataType);
if (meterData.Length == 0)
{
Log.Warning("集中器 (地址: {Address}) 数据类型 {DataType} 不存在表计数据",
_concentratorAddress, dataType);
return;
}
var dataMessage = Protocol376Message.CreateDataUploadMessage(_concentratorAddress, meterData);
await SendMessageAsync(dataMessage);
Log.Information("集中器 (地址: {Address}) 发送数据上传消息, 类型: {DataType}",
_concentratorAddress, dataType);
Log.Debug("集中器 (地址: {Address}) 上传报文详情: {MessageInfo}",
_concentratorAddress, dataMessage.GetMessageInfo());
}
catch (Exception ex)
{
Log.Error(ex, "集中器 (地址: {Address}) 发送数据上传消息失败: {ErrorMessage}",
_concentratorAddress, ex.Message);
_statisticsService.RecordError("数据上传消息发送失败");
throw;
}
}
/// <summary>
/// 发送读数据消息
/// </summary>
/// <param name="dataType">数据类型</param>
public async Task SendReadDataMessageAsync(byte dataType)
{
if (!IsConnected)
{
Log.Warning("集中器 (地址: {Address}) 未连接,无法发送读数据消息", _concentratorAddress);
return;
}
try
{
var readMessage = Protocol376Message.CreateReadDataMessage(_concentratorAddress, dataType);
await SendMessageAsync(readMessage);
Log.Information("集中器 (地址: {Address}) 发送读数据消息, 类型: {DataType}",
_concentratorAddress, dataType);
Log.Debug("集中器 (地址: {Address}) 读数据报文详情: {MessageInfo}",
_concentratorAddress, readMessage.GetMessageInfo());
}
catch (Exception ex)
{
Log.Error(ex, "集中器 (地址: {Address}) 发送读数据消息失败: {ErrorMessage}",
_concentratorAddress, ex.Message);
_statisticsService.RecordError("读数据消息发送失败");
throw;
}
}
/// <summary>
/// 发送设置参数消息
/// </summary>
/// <param name="paramType">参数类型</param>
/// <param name="paramData">参数数据</param>
public async Task SendSetParameterMessageAsync(byte paramType, byte[] paramData)
{
if (!IsConnected)
{
Log.Warning("集中器 (地址: {Address}) 未连接,无法发送设置参数消息", _concentratorAddress);
return;
}
try
{
var paramMessage = Protocol376Message.CreateSetParameterMessage(_concentratorAddress, paramType, paramData);
await SendMessageAsync(paramMessage);
Log.Information("集中器 (地址: {Address}) 发送设置参数消息, 类型: {ParamType}",
_concentratorAddress, paramType);
Log.Debug("集中器 (地址: {Address}) 参数设置报文详情: {MessageInfo}",
_concentratorAddress, paramMessage.GetMessageInfo());
}
catch (Exception ex)
{
Log.Error(ex, "集中器 (地址: {Address}) 发送设置参数消息失败: {ErrorMessage}",
_concentratorAddress, ex.Message);
_statisticsService.RecordError("设置参数消息发送失败");
throw;
}
}
/// <summary>
/// 发送消息的通用方法
/// </summary>
/// <param name="message">要发送的消息</param>
private async Task SendMessageAsync(IProtocolMessage message)
{
if (!IsConnected)
{
throw new InvalidOperationException("未连接到服务器,无法发送消息");
}
byte[] messageBytes = message.ToBytes();
await _networkService.SendMessageAsync(messageBytes);
// 记录统计信息
_statisticsService.RecordMessageSent();
_statisticsService.RecordMessageType(message.Type);
}
/// <summary>
/// 启动心跳
/// </summary>
public void StartHeartbeat()
{
_heartbeatService.Start();
StatusChanged?.Invoke(this, "心跳已启动");
Log.Information("集中器 (地址: {Address}) 自动心跳已启动", _concentratorAddress);
}
/// <summary>
/// 停止心跳
/// </summary>
public void StopHeartbeat()
{
_heartbeatService.Stop();
StatusChanged?.Invoke(this, "心跳已停止");
Log.Information("集中器 (地址: {Address}) 自动心跳已停止", _concentratorAddress);
}
/// <summary>
/// 设置是否自动响应
/// </summary>
/// <param name="enabled">是否启用自动响应</param>
public void SetAutoResponse(bool enabled)
{
_autoResponse = enabled;
Log.Information("集中器 (地址: {Address}) 自动响应已{Status}",
_concentratorAddress, enabled ? "启用" : "禁用");
}
/// <summary>
/// 更新表计数据
/// </summary>
public void UpdateMeterData(byte dataType, byte[] data)
{
_deviceDataService.SetMeterData(dataType, data);
}
/// <summary>
/// 获取模拟器状态
/// </summary>
public string GetStatus()
{
var status = new StringBuilder();
status.AppendLine($"集中器 (地址: {_concentratorAddress}) 状态:");
status.AppendLine($"连接状态: {(IsConnected ? "" : "")}");
status.AppendLine($"登录状态: {(IsLoggedIn ? "" : "")}");
if (IsLoggedIn)
{
status.AppendLine($"登录时间: {_lastLoginTime}");
}
status.AppendLine($"心跳状态: {(_heartbeatService.IsRunning ? "" : "")}");
status.AppendLine($"心跳次数: {_heartbeatService.SuccessfulHeartbeats}");
status.AppendLine($"阀门状态: {(_deviceDataService.ValveStatus ? "" : "")}");
return status.ToString();
}
/// <summary>
/// 获取通信统计信息
/// </summary>
public string GetCommunicationStatistics()
{
return _statisticsService.GetStatisticsReport();
}
/// <summary>
/// 设置重连参数
/// </summary>
public void SetReconnectParameters(bool autoReconnect, int maxAttempts, int delaySeconds)
{
_reconnectionService.SetReconnectParameters(autoReconnect, maxAttempts, delaySeconds);
}
/// <summary>
/// 接收消息处理
/// </summary>
private void OnMessageReceived(object sender, byte[] message)
{
try
{
// 触发接收消息事件
MessageReceived?.Invoke(this, message);
// 处理接收到的消息
ProcessReceivedMessage(message);
// 统计信息记录
_statisticsService.RecordMessageReceived();
}
catch (Exception ex)
{
Log.Error(ex, "集中器 (地址: {Address}) 处理接收消息时发生错误: {ErrorMessage}",
_concentratorAddress, ex.Message);
_statisticsService.RecordError($"消息处理错误: {ex.Message}");
}
}
/// <summary>
/// 处理接收到的消息
/// </summary>
private void ProcessReceivedMessage(byte[] message)
{
try
{
// 解析消息
var receivedMessage = Protocol376Message.ParseFromBytes(message);
// 检查是否是登录确认消息
CheckLoginConfirmation(receivedMessage);
// 如果启用了自动响应,处理自动响应
if (_autoResponse)
{
_ = HandleAutoResponse(receivedMessage);
}
Log.Debug("集中器 (地址: {Address}) 收到消息: {MessageInfo}",
_concentratorAddress, receivedMessage.GetMessageInfo());
}
catch (Exception ex)
{
Log.Warning("集中器 (地址: {Address}) 消息解析失败: {ErrorMessage}",
_concentratorAddress, ex.Message);
}
}
/// <summary>
/// 检查是否是登录确认消息
/// </summary>
private void CheckLoginConfirmation(Protocol376Message message)
{
// 检查是否是响应类型消息且AFN为登录响应
if (message.Type == MessageType.Response && message.Data.Length >= 4 && message.Data[0] == 0x00)
{
// 检查是否包含数据单元标识
if (message.Data[1] == 0x02 && message.Data[2] == 0x70)
{
// 检查结果码
if (message.Data[3] == 0x00)
{
_loginConfirmed = true;
Log.Information("集中器 (地址: {Address}) 登录确认成功", _concentratorAddress);
StatusChanged?.Invoke(this, "登录成功");
}
else
{
_loginConfirmed = false;
Log.Warning("集中器 (地址: {Address}) 登录确认失败, 结果码: {ResultCode}",
_concentratorAddress, message.Data[3]);
StatusChanged?.Invoke(this, $"登录失败, 结果码: {message.Data[3]}");
}
}
}
}
/// <summary>
/// 处理自动响应
/// </summary>
private async Task HandleAutoResponse(Protocol376Message receivedMessage)
{
try
{
// 根据接收到的消息类型,生成对应的响应消息
Protocol376Message responseMessage = null;
switch (receivedMessage.Type)
{
case MessageType.Login:
// 登录请求的响应
byte[] loginResponseData = new byte[] { 0x00, 0x02, 0x70, 0x00 }; // AFN=0, 成功
responseMessage = new Protocol376Message
{
ControlCode = 0x00,
Address = receivedMessage.Address,
Data = loginResponseData,
Type = MessageType.Response
};
break;
case MessageType.Heartbeat:
// 心跳消息的响应
byte[] heartbeatResponseData = new byte[] { 0x00, 0x02, 0x70, 0x00 }; // AFN=0, 成功
responseMessage = new Protocol376Message
{
ControlCode = 0x00,
Address = receivedMessage.Address,
Data = heartbeatResponseData,
Type = MessageType.Response
};
break;
case MessageType.ValveControl:
// 阀控操作的响应
byte[] valveResponseData = new byte[] { 0x00, 0x02, 0x70, 0x00 }; // AFN=0, 成功
responseMessage = new Protocol376Message
{
ControlCode = 0x00,
Address = receivedMessage.Address,
Data = valveResponseData,
Type = MessageType.Response
};
break;
case MessageType.DataUpload:
// 数据上传的响应
byte[] dataUploadResponseData = new byte[] { 0x00, 0x02, 0x70, 0x00 }; // AFN=0, 成功
responseMessage = new Protocol376Message
{
ControlCode = 0x00,
Address = receivedMessage.Address,
Data = dataUploadResponseData,
Type = MessageType.Response
};
break;
case MessageType.ReadData:
// 读数据请求的响应
if (receivedMessage.Data.Length >= 6)
{
byte dataType = receivedMessage.Data[5];
byte[] meterData = _deviceDataService.GetMeterData(dataType);
if (meterData.Length > 0)
{
// 构造响应数据
byte[] readDataResponseData = new byte[5 + meterData.Length];
readDataResponseData[0] = 0x0B; // AFN
readDataResponseData[1] = 0x02; // 数据单元标识1
readDataResponseData[2] = 0x70; // 数据单元标识2
readDataResponseData[3] = 0x00; // 结果码, 成功
readDataResponseData[4] = dataType; // 数据类型
// 拷贝表计数据
Array.Copy(meterData, 0, readDataResponseData, 5, meterData.Length);
responseMessage = new Protocol376Message
{
ControlCode = 0x00,
Address = receivedMessage.Address,
Data = readDataResponseData,
Type = MessageType.Response
};
}
}
break;
}
// 发送响应消息
if (responseMessage != null)
{
await SendMessageAsync(responseMessage);
Log.Debug("集中器 (地址: {Address}) 自动响应: {MessageInfo}",
_concentratorAddress, responseMessage.GetMessageInfo());
}
}
catch (Exception ex)
{
Log.Error(ex, "集中器 (地址: {Address}) 处理自动响应时发生错误: {ErrorMessage}",
_concentratorAddress, ex.Message);
_statisticsService.RecordError($"自动响应错误: {ex.Message}");
}
}
/// <summary>
/// 阀门状态变更处理
/// </summary>
private void OnValveStatusChanged(object sender, bool isOpen)
{
StatusChanged?.Invoke(this, $"阀门状态已变更为: {(isOpen ? "" : "")}");
}
/// <summary>
/// 连接状态变更处理
/// </summary>
private void OnConnectionStatusChanged(object sender, NetworkService.ConnectionStatus status)
{
switch (status)
{
case NetworkService.ConnectionStatus.Connected:
StatusChanged?.Invoke(this, "已连接到服务器");
break;
case NetworkService.ConnectionStatus.Disconnected:
_loginConfirmed = false;
StatusChanged?.Invoke(this, "与服务器断开连接");
break;
case NetworkService.ConnectionStatus.Failed:
_loginConfirmed = false;
StatusChanged?.Invoke(this, "连接失败");
break;
case NetworkService.ConnectionStatus.Reconnecting:
StatusChanged?.Invoke(this, "正在重新连接");
break;
}
}
/// <summary>
/// 网络错误处理
/// </summary>
private void OnNetworkError(object sender, Exception ex)
{
_statisticsService.RecordError($"网络错误: {ex.Message}");
StatusChanged?.Invoke(this, $"网络错误: {ex.Message}");
}
/// <summary>
/// 重连完成处理
/// </summary>
private async void OnReconnectAttemptCompleted(object sender, bool success)
{
if (success)
{
StatusChanged?.Invoke(this, "重连成功");
// 重新登录
try
{
await Task.Delay(1000); // 等待1秒确保连接稳定
await SendLoginMessageAsync();
Log.Information("集中器 (地址: {Address}) 重连后自动登录", _concentratorAddress);
}
catch (Exception ex)
{
Log.Error(ex, "集中器 (地址: {Address}) 重连后登录失败: {ErrorMessage}",
_concentratorAddress, ex.Message);
}
}
else
{
StatusChanged?.Invoke(this, $"重连失败 (尝试次数: {ReconnectAttempts})");
}
}
}
}