588 lines
20 KiB
C#
588 lines
20 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
|
|
namespace StandardScene.Charge
|
|
{
|
|
/// <summary>
|
|
/// 通讯报文数据服务(单例模式)
|
|
/// </summary>
|
|
public class CommunicationMessageService
|
|
{
|
|
private static CommunicationMessageService _instance;
|
|
private static readonly object _lock = new object();
|
|
private readonly object _dataLock = new object();
|
|
private readonly AlarmConfigDataService dataService;
|
|
private readonly LinkedList<CommunicationMessage> _messages;
|
|
private const int MaxMessages = 100; // 最多保留100条
|
|
|
|
/// <summary>
|
|
/// 报文添加事件
|
|
/// </summary>
|
|
public event EventHandler<CommunicationMessage> MessageAdded;
|
|
|
|
/// <summary>
|
|
/// 获取单例实例
|
|
/// </summary>
|
|
public static CommunicationMessageService Instance
|
|
{
|
|
get
|
|
{
|
|
if (_instance == null)
|
|
{
|
|
lock (_lock)
|
|
{
|
|
if (_instance == null)
|
|
{
|
|
_instance = new CommunicationMessageService();
|
|
}
|
|
}
|
|
}
|
|
return _instance;
|
|
}
|
|
}
|
|
|
|
private CommunicationMessageService()
|
|
{
|
|
_messages = new LinkedList<CommunicationMessage>();
|
|
dataService = AlarmConfigDataService.Instance;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 添加报文
|
|
/// </summary>
|
|
public void AddMessage(CommunicationMessage message)
|
|
{
|
|
if (message == null)
|
|
return;
|
|
|
|
lock (_dataLock)
|
|
{
|
|
// 添加到链表头部(最新的在前面)
|
|
_messages.AddFirst(message);
|
|
|
|
// 如果超过最大数量,移除最旧的
|
|
while (_messages.Count > MaxMessages)
|
|
{
|
|
_messages.RemoveLast();
|
|
}
|
|
}
|
|
|
|
// 触发事件
|
|
MessageAdded?.Invoke(this, message);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 添加发送报文
|
|
/// </summary>
|
|
public void AddSendMessage(string ipAddress, int port, string rawData, string type, string stationId = null)
|
|
{
|
|
var message = new CommunicationMessage
|
|
{
|
|
Direction = MessageDirection.Send,
|
|
IpAddress = ipAddress,
|
|
Port = port,
|
|
RawData = rawData,
|
|
Length = rawData.Split(' ')?.Length ?? 0, // 假设是十六进制字符串
|
|
StationId = stationId,
|
|
Type = type
|
|
};
|
|
AddMessage(message);
|
|
|
|
// 发送报文后,解析并更新充电桩数据(发送方向)
|
|
ParseSendDataAndUpdateStation(ipAddress, port, rawData, type);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 添加接收报文
|
|
/// </summary>
|
|
public void AddReceiveMessage(string ipAddress, int port, string rawData, string type, string stationId = null)
|
|
{
|
|
var message = new CommunicationMessage
|
|
{
|
|
Direction = MessageDirection.Receive,
|
|
IpAddress = ipAddress,
|
|
Port = port,
|
|
RawData = rawData,
|
|
Length = rawData.Split(' ')?.Length ?? 0,
|
|
StationId = stationId,
|
|
Type = type
|
|
};
|
|
AddMessage(message);
|
|
|
|
// 接收到报文后,解析并更新充电桩数据(接收方向)
|
|
ParseReceiveDataAndUpdateStation(ipAddress, port, rawData, type);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析发送报文并更新充电桩数据
|
|
/// </summary>
|
|
private void ParseSendDataAndUpdateStation(string ipAddress, int port, string rawData, string type)
|
|
{
|
|
try
|
|
{
|
|
var dataService = ChargeStationDataService.Instance;
|
|
|
|
// 根据IP地址查找充电桩
|
|
var station = dataService.GetStationByIp(ipAddress, port);
|
|
if (station == null)
|
|
{
|
|
return; // 未找到对应充电桩,不处理
|
|
}
|
|
|
|
// 解析发送报文数据
|
|
var parsedData = ParseSendRawData(rawData, type);
|
|
if (parsedData == null)
|
|
{
|
|
return; // 解析失败,不处理
|
|
}
|
|
|
|
// 更新充电桩数据(发送方向)
|
|
UpdateStationFromSendData(station, parsedData);
|
|
|
|
// 更新到数据服务
|
|
dataService.UpdateStation(station, out string errorMessage);
|
|
|
|
}
|
|
catch
|
|
{
|
|
// 静默处理异常,不影响报文记录
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析接收报文并更新充电桩数据
|
|
/// </summary>
|
|
public void ParseReceiveDataAndUpdateStation(string ipAddress, int port, string rawData, string type)
|
|
{
|
|
try
|
|
{
|
|
var dataService = ChargeStationDataService.Instance;
|
|
|
|
// 根据IP地址查找充电桩
|
|
var station = dataService.GetStationByIp(ipAddress);
|
|
if (station == null)
|
|
{
|
|
return; // 未找到对应充电桩,不处理
|
|
}
|
|
|
|
// 解析接收报文数据
|
|
var parsedData = ParseReceiveRawData(rawData, type);
|
|
if (parsedData == null)
|
|
{
|
|
return; // 解析失败,不处理
|
|
}
|
|
|
|
// 更新充电桩数据(接收方向)
|
|
UpdateStationFromReceiveData(station, parsedData);
|
|
|
|
// 合并发送和接收数据,更新到数据服务
|
|
dataService.UpdateStation(station, out string errorMessage);
|
|
}
|
|
catch
|
|
{
|
|
// 静默处理异常,不影响报文记录
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析发送报文数据
|
|
/// </summary>
|
|
public ParsedSendData ParseSendRawData(string rawData, string type)
|
|
{
|
|
try
|
|
{
|
|
// 将逗号分隔的字符串转换为字节数组
|
|
var parts = rawData.Split(' ');
|
|
if (parts.Length < 10) // 发送报文至少10字节
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var bytes = new byte[parts.Length];
|
|
for (int i = 0; i < parts.Length; i++)
|
|
{
|
|
if (!byte.TryParse(parts[i], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out bytes[i]))
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
byte chargeCommand = 0;
|
|
double setVoltage = 0;
|
|
double setCurrent = 0;
|
|
short carId = 0;
|
|
int carSoc = 0;
|
|
double carVoltage = 0;
|
|
double carCurrent = 0;
|
|
|
|
if (type == "FRLDShort")
|
|
{
|
|
chargeCommand = bytes[2];
|
|
|
|
setCurrent = BitConverter.ToInt32(new byte[] { bytes[6], bytes[5], bytes[4], bytes[3] }, 0) / 10f;
|
|
setVoltage = BitConverter.ToInt32(new byte[] { bytes[10], bytes[9], bytes[8], bytes[7] }, 0) / 10f;
|
|
carId = BitConverter.ToInt16(new byte[] { bytes[14], bytes[13] }, 0);
|
|
carSoc = bytes[15];
|
|
carVoltage = BitConverter.ToInt32(new byte[] { bytes[19], bytes[18], bytes[17], bytes[16] }, 0);
|
|
carCurrent = BitConverter.ToInt32(new byte[] { bytes[23], bytes[22], bytes[21], bytes[20] }, 0);
|
|
|
|
}
|
|
else if (type == "FRLDTall")
|
|
{
|
|
chargeCommand = bytes[1];
|
|
|
|
setCurrent = BitConverter.ToSingle(new byte[] { bytes[5], bytes[4], bytes[3], bytes[2] }, 0);
|
|
setVoltage = BitConverter.ToSingle(new byte[] { bytes[9], bytes[8], bytes[7], bytes[6] }, 0);
|
|
carId = BitConverter.ToInt16(new byte[] { bytes[13], bytes[12] }, 0);
|
|
carSoc = BitConverter.ToInt16(new byte[] { bytes[15], bytes[14] }, 0);
|
|
carVoltage = BitConverter.ToSingle(new byte[] { bytes[19], bytes[18], bytes[17], bytes[16] }, 0);
|
|
carCurrent = BitConverter.ToSingle(new byte[] { bytes[23], bytes[22], bytes[21], bytes[20] }, 0);
|
|
}
|
|
|
|
// 解析发送报文(根据实际协议)
|
|
var parsed = new ParsedSendData
|
|
{
|
|
|
|
ChargeCommand = chargeCommand,
|
|
SetVoltage = setVoltage,
|
|
SetCurrent = setCurrent,
|
|
CurrentVehicleId = carId,
|
|
BatteryLevel = carSoc,
|
|
CarVoltage = carVoltage,
|
|
CarCurrent = carCurrent,
|
|
// 发送时间
|
|
SendTime = DateTime.Now
|
|
};
|
|
|
|
return parsed;
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析接收报文数据
|
|
/// </summary>
|
|
public ParsedReceiveData ParseReceiveRawData(string rawData, string type)
|
|
{
|
|
try
|
|
{
|
|
// 将逗号分隔的字符串转换为字节数组
|
|
var parts = rawData.Split(' ');
|
|
if (parts.Length < 30) // 假设报文至少30字节
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var bytes = new byte[parts.Length];
|
|
for (int i = 0; i < parts.Length; i++)
|
|
{
|
|
if (!byte.TryParse(parts[i], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out bytes[i]))
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
double realTimeVoltage = 0;
|
|
double realTimeCurrent = 0;
|
|
byte chargeStationStatus = 0;
|
|
byte chargeId = 0;
|
|
short batteryAH = 0;
|
|
byte mechanismStatus = 0;
|
|
byte alarmValue = 0;
|
|
|
|
|
|
if (type == "FRLDShort")
|
|
{
|
|
|
|
realTimeCurrent = BitConverter.ToInt32(new byte[] { bytes[5], bytes[4], bytes[3], bytes[2] }, 0) / 10f;
|
|
realTimeVoltage = BitConverter.ToInt32(new byte[] { bytes[9], bytes[8], bytes[7], bytes[6] }, 0) / 10f;
|
|
chargeStationStatus = bytes[14];
|
|
chargeId = bytes[15];
|
|
batteryAH = BitConverter.ToInt16(new byte[] { bytes[17], bytes[16] }, 0);
|
|
mechanismStatus = bytes[28];
|
|
}
|
|
else if (type == "FRLDTall")
|
|
{
|
|
chargeStationStatus = bytes[14];
|
|
mechanismStatus = bytes[28];
|
|
alarmValue = bytes[15];
|
|
}
|
|
|
|
|
|
// 根据实际协议解析接收数据
|
|
var parsed = new ParsedReceiveData
|
|
{
|
|
RealTimeCurrent = realTimeCurrent,
|
|
RealTimeVoltage = realTimeVoltage,
|
|
Status = type == "FRLDTall" ? ParseStationStatusFRLDTall(chargeStationStatus) : ParseStationStatus(chargeStationStatus),
|
|
ChargeID = chargeId,
|
|
BatteryAH = batteryAH,
|
|
MechanismStatus = type == "FRLDTall" ? ParseMechanismStatusFRLDTall(mechanismStatus) : ParseMechanismStatus(mechanismStatus),
|
|
HasAlarm = chargeStationStatus == 2,
|
|
AlarmCode = alarmValue,
|
|
// AlarmLevel = ParseAlarmLevel(bytes[20])
|
|
ReceiveTime = DateTime.Now
|
|
};
|
|
|
|
return parsed;
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// 从发送报文更新充电桩数据
|
|
/// </summary>
|
|
private void UpdateStationFromSendData(ChargeStation station, ParsedSendData parsedData)
|
|
{
|
|
// 更新发送的设定值
|
|
//station.SetVoltage = parsedData.SetVoltage;
|
|
//station.SetElectricCurrent = parsedData.SetCurrent;
|
|
|
|
// 更新最后发送时间
|
|
station.LastSendTime = parsedData.SendTime;
|
|
|
|
// 根据发送的充电指令更新状态
|
|
if (parsedData.ChargeCommand == 1)
|
|
{
|
|
// 发送了启动充电指令
|
|
station.ChargeCommandStatus = ChargeCommandStatus.Started;
|
|
}
|
|
else if (parsedData.ChargeCommand == 0)
|
|
{
|
|
// 发送了停止充电指令
|
|
station.ChargeCommandStatus = ChargeCommandStatus.Stopped;
|
|
}
|
|
|
|
station.BatteryLevel = parsedData.BatteryLevel;
|
|
station.CurrentVehicle = parsedData.CurrentVehicleId.ToString();
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// 从接收报文更新充电桩数据
|
|
/// </summary>
|
|
private void UpdateStationFromReceiveData(ChargeStation station, ParsedReceiveData parsedData)
|
|
{
|
|
station.LastReceiveTime = parsedData.ReceiveTime;
|
|
station.MechanismStatus = parsedData.MechanismStatus;
|
|
station.RealTimeVoltage = parsedData.RealTimeVoltage;
|
|
station.RealTimeCurrent = parsedData.RealTimeCurrent;
|
|
station.Status = parsedData.Status;
|
|
station.HasAlarm = parsedData.HasAlarm;
|
|
station.AlarmLevel = parsedData.AlarmLevel;
|
|
if (parsedData.HasAlarm)
|
|
{
|
|
var alarmInfo = dataService.GetAlarmConfigAlarmCode(parsedData.AlarmCode);
|
|
if (alarmInfo != null)
|
|
{
|
|
station.AlarmMessage = $"报警级别: {GetAlarmLevelText(alarmInfo.Level)}:{alarmInfo.AlarmContent}";
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
station.AlarmMessage = string.Empty;
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析机构状态
|
|
/// </summary>
|
|
private MechanismStatus ParseMechanismStatus(byte statusByte)
|
|
{
|
|
switch (statusByte)
|
|
{
|
|
case 1: return MechanismStatus.Retracted;
|
|
case 2: return MechanismStatus.Extended;
|
|
default: return MechanismStatus.Extending;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析机构状态
|
|
/// </summary>
|
|
private MechanismStatus ParseMechanismStatusFRLDTall(byte statusByte)
|
|
{
|
|
switch (statusByte)
|
|
{
|
|
case 1: return MechanismStatus.Extended;
|
|
case 2: return MechanismStatus.Retracted;
|
|
default: return MechanismStatus.Extending;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析报警级别
|
|
/// </summary>
|
|
private AlarmLevel ParseAlarmLevel(byte alarmByte)
|
|
{
|
|
if (alarmByte == 0) return AlarmLevel.None;
|
|
if (alarmByte <= 2) return AlarmLevel.Low;
|
|
if (alarmByte <= 5) return AlarmLevel.Medium;
|
|
if (alarmByte <= 8) return AlarmLevel.High;
|
|
return AlarmLevel.Critical;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析充电桩状态
|
|
/// </summary>
|
|
private ChargeStationStatus ParseStationStatus(byte statusByte)
|
|
{
|
|
switch (statusByte)
|
|
{
|
|
case 0: return ChargeStationStatus.Idle;
|
|
case 1: return ChargeStationStatus.Charging;
|
|
case 2: return ChargeStationStatus.Fault;
|
|
case 3: return ChargeStationStatus.Battery;
|
|
default: return ChargeStationStatus.Idle;
|
|
}
|
|
}
|
|
|
|
private ChargeStationStatus ParseStationStatusFRLDTall(byte statusByte)
|
|
{
|
|
switch (statusByte)
|
|
{
|
|
case 0: return ChargeStationStatus.Idle;
|
|
case 2: return ChargeStationStatus.Idle;
|
|
case 3: return ChargeStationStatus.Charging;
|
|
case 4: return ChargeStationStatus.Fault;
|
|
default: return ChargeStationStatus.Idle;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取报警级别文本
|
|
/// </summary>
|
|
private string GetAlarmLevelText(AlarmLevel level)
|
|
{
|
|
switch (level)
|
|
{
|
|
case AlarmLevel.None: return "无";
|
|
case AlarmLevel.Low: return "低";
|
|
case AlarmLevel.Medium: return "中";
|
|
case AlarmLevel.High: return "高";
|
|
case AlarmLevel.Critical: return "严重";
|
|
default: return "未知";
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析后的发送报文数据(内部类)
|
|
/// </summary>
|
|
public class ParsedSendData
|
|
{
|
|
public byte ChargeCommand { get; set; }
|
|
public double SetVoltage { get; set; }
|
|
public double SetCurrent { get; set; }
|
|
public double BatteryLevel { get; set; }
|
|
public int CurrentVehicleId { get; set; }
|
|
public double CarVoltage { get; set; }
|
|
public double CarCurrent { get; set; }
|
|
public DateTime SendTime { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析后的接收报文数据(内部类)
|
|
/// </summary>
|
|
public class ParsedReceiveData
|
|
{
|
|
public CommunicationStatus CommStatus { get; set; }
|
|
public ChargeCommandStatus ChargeCommandStatus { get; set; }
|
|
public MechanismStatus MechanismStatus { get; set; }
|
|
public double RealTimeVoltage { get; set; }
|
|
public double RealTimeCurrent { get; set; }
|
|
public int ChargeID { get; set; }
|
|
public float BatteryAH { get; set; }
|
|
public bool HasAlarm { get; set; }
|
|
public AlarmLevel AlarmLevel { get; set; }
|
|
public int AlarmCode { get; set; }
|
|
public ChargeStationStatus Status { get; set; }
|
|
|
|
public DateTime ReceiveTime { get; set; }
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// 获取所有报文
|
|
/// </summary>
|
|
public List<CommunicationMessage> GetAllMessages()
|
|
{
|
|
lock (_dataLock)
|
|
{
|
|
return _messages.ToList();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据IP筛选报文
|
|
/// </summary>
|
|
public List<CommunicationMessage> GetMessagesByIp(string ipAddress)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(ipAddress))
|
|
return GetAllMessages();
|
|
|
|
lock (_dataLock)
|
|
{
|
|
return _messages.Where(m => m.IpAddress == ipAddress).ToList();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据充电桩ID筛选报文
|
|
/// </summary>
|
|
public List<CommunicationMessage> GetMessagesByStationId(string stationId)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(stationId))
|
|
return GetAllMessages();
|
|
|
|
lock (_dataLock)
|
|
{
|
|
return _messages.Where(m => m.StationId == stationId).ToList();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 清空所有报文
|
|
/// </summary>
|
|
public void Clear()
|
|
{
|
|
lock (_dataLock)
|
|
{
|
|
_messages.Clear();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取所有唯一IP地址列表
|
|
/// </summary>
|
|
public List<string> GetUniqueIpAddresses()
|
|
{
|
|
lock (_dataLock)
|
|
{
|
|
return _messages
|
|
.Select(m => m.IpAddress)
|
|
.Distinct()
|
|
.OrderBy(ip => ip)
|
|
.ToList();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|