init commit

This commit is contained in:
zhaowei.huang
2026-06-14 11:19:15 +08:00
parent e79a3815a5
commit c8e540d272
174 changed files with 60830 additions and 39 deletions
@@ -0,0 +1,405 @@
using SimpleLite.RCS;
using SimpleLite.RCS.CarTypes;
using SimpleCore;
using SimpleCore.Library;
using SimpleCore.PropType;
using StandardScene.Charge;
using StandardScene.TCP;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StandardScene.ChargeStationType
{
public class FLChargeStation : AbstractChargeStation
{
private AsyncTcpClient Client;
private IPEndPoint _endPoint;
// 重连相关
private Timer _reconnectTimer;
private bool _isManualDisconnect = false; // 标识是否为手动断开
private int _reconnectAttempts = 0; // 重连尝试次数
private const int MAX_RECONNECT_ATTEMPTS = 5; // 最大重连次数
private const int RECONNECT_INTERVAL_MS = 3000; // 重连间隔(毫秒)
private readonly object _connectionLock = new object(); // 连接锁
private bool _isConnecting = false; // 是否正在连接中
// 连接状态
public bool IsConnected { get; private set; } = false;
public DateTime? LastConnectedTime { get; private set; }
public DateTime? LastDisconnectedTime { get; private set; }
/// <summary>
/// 关闭当前TCP连接
/// </summary>
public override void CloseCommunication()
{
lock (_connectionLock)
{
_isManualDisconnect = true; // 标记为手动断开,不触发自动重连
// 停止重连定时器
if (_reconnectTimer != null)
{
_reconnectTimer.Dispose();
_reconnectTimer = null;
}
_isConnecting = false;
if (Client != null)
{
try
{
// 取消订阅事件,避免内存泄漏
Client.PlaintextReceived -= OnPlaintextReceived;
Client.ServerConnected -= OnServerConnected;
Client.ServerDisconnected -= OnServerDisconnected;
// 关闭连接
Client.Close();
Client = null;
IsConnected = false;
LastDisconnectedTime = DateTime.Now;
Diagnosis.Log($"FLChargeStation[{SiteId}] TCP connection closed manually");
}
catch (Exception ex)
{
Diagnosis.Log($"ERR:FLChargeStation[{SiteId}] failed to close connection: {ex.Message}");
}
}
}
}
/// <summary>
/// 创建TCP连接
/// </summary>
public override void CreateCommunication(IPAddress ip, int port)
{
lock (_connectionLock)
{
// 先关闭旧连接
if (Client != null)
{
CloseCommunication();
}
_endPoint = new IPEndPoint(ip, port);
_isManualDisconnect = false; // 重置手动断开标记
_reconnectAttempts = 0; // 重置重连次数
ConnectInternal();
}
}
/// <summary>
/// 内部连接方法
/// </summary>
private void ConnectInternal()
{
if (_isConnecting)
{
Diagnosis.Log($"FLChargeStation[{SiteId}] is already connecting, skip");
return;
}
try
{
_isConnecting = true;
// 创建新的TCP客户端
Client = new AsyncTcpClient(_endPoint.Address, _endPoint.Port);
Client.PlaintextReceived += OnPlaintextReceived;
Client.ServerConnected += OnServerConnected;
Client.ServerDisconnected += OnServerDisconnected;
// 连接
Client.Connect();
if (Client.Connected)
{
Diagnosis.Log($"FLChargeStation[{SiteId}] connecting to {_endPoint.Address}:{_endPoint.Port}...");
}
else
{
_isConnecting = false;
Diagnosis.Log($"FLChargeStation[{SiteId}] connecting to {_endPoint.Address}:{_endPoint.Port}... 未建立连接");
}
}
catch (Exception ex)
{
Diagnosis.Log($"ERR:FLChargeStation[{SiteId}] failed to create connection: {ex.Message}");
_isConnecting = false;
// 触发重连
if (!_isManualDisconnect)
{
ScheduleReconnect();
}
}
}
/// <summary>
/// 手动触发重连
/// </summary>
public void Reconnect()
{
lock (_connectionLock)
{
Diagnosis.Log($"FLChargeStation[{SiteId}] manual reconnect triggered");
_isManualDisconnect = false;
_reconnectAttempts = 0;
// 关闭现有连接
if (Client != null)
{
try
{
Client.PlaintextReceived -= OnPlaintextReceived;
Client.ServerConnected -= OnServerConnected;
Client.ServerDisconnected -= OnServerDisconnected;
Client.Close();
Client = null;
}
catch { }
}
// 重新连接
ConnectInternal();
}
}
/// <summary>
/// 安排自动重连
/// </summary>
private void ScheduleReconnect()
{
if (_isManualDisconnect)
{
Diagnosis.Log($"FLChargeStation[{SiteId}] manual disconnect, skip auto reconnect");
return;
}
if (_reconnectAttempts >= MAX_RECONNECT_ATTEMPTS)
{
Diagnosis.Log($"ERR:FLChargeStation[{SiteId}] max reconnect attempts ({MAX_RECONNECT_ATTEMPTS}) reached, stop reconnecting");
return;
}
_reconnectAttempts++;
// 停止现有定时器
if (_reconnectTimer != null)
{
_reconnectTimer.Dispose();
}
Diagnosis.Log($"FLChargeStation[{SiteId}] scheduling reconnect attempt {_reconnectAttempts}/{MAX_RECONNECT_ATTEMPTS} in {RECONNECT_INTERVAL_MS}ms");
// 创建新的定时器
_reconnectTimer = new Timer(
callback: _ => AttemptReconnect(),
state: null,
dueTime: RECONNECT_INTERVAL_MS,
period: Timeout.Infinite
);
}
/// <summary>
/// 尝试重连
/// </summary>
private void AttemptReconnect()
{
lock (_connectionLock)
{
if (_isManualDisconnect || IsConnected)
{
return;
}
Diagnosis.Log($"FLChargeStation[{SiteId}] attempting to reconnect (attempt {_reconnectAttempts}/{MAX_RECONNECT_ATTEMPTS})...");
try
{
// 清理旧客户端
if (Client != null)
{
try
{
Client.PlaintextReceived -= OnPlaintextReceived;
Client.ServerConnected -= OnServerConnected;
Client.ServerDisconnected -= OnServerDisconnected;
Client.Close();
}
catch { }
Client = null;
}
// 重新连接
ConnectInternal();
}
catch (Exception ex)
{
Diagnosis.Log($"ERR:FLChargeStation[{SiteId}] reconnect failed: {ex.Message}");
// 继续安排下一次重连
if (!_isManualDisconnect && _reconnectAttempts < MAX_RECONNECT_ATTEMPTS)
{
ScheduleReconnect();
}
}
}
}
private void OnPlaintextReceived(object sender, TcpDatagramReceivedEventArgs<byte[]> e)
{
var messageService = CommunicationMessageService.Instance;
var recBytes = e.Datagram.Take(35).ToArray();
string IP = ((AsyncTcpClient)sender).RemoteIPEndPoint.Address.ToString();
Diagnosis.Post($"{string.Join(" ", recBytes.Select(p => $"{p:X2}"))}", $"{IP}", true);
IsSafe = recBytes[28] == 2;
messageService.AddReceiveMessage(_endPoint.Address.ToString(), _endPoint.Port, BitConverter.ToString(recBytes).Replace("-", " "), "FRLDTall");
}
/// <summary>
/// 服务器连接成功事件
/// </summary>
private void OnServerConnected(object sender, TcpServerConnectedEventArgs e)
{
lock (_connectionLock)
{
_isConnecting = false;
IsConnected = true;
LastConnectedTime = DateTime.Now;
_reconnectAttempts = 0; // 重置重连次数
Diagnosis.Log($"FLChargeStation[{SiteId}] TCP connected successfully to {_endPoint.Address}:{_endPoint.Port}");
// 停止重连定时器
if (_reconnectTimer != null)
{
_reconnectTimer.Dispose();
_reconnectTimer = null;
}
}
}
/// <summary>
/// 服务器断开连接事件
/// </summary>
private void OnServerDisconnected(object sender, TcpServerDisconnectedEventArgs e)
{
lock (_connectionLock)
{
_isConnecting = false;
IsConnected = false;
LastDisconnectedTime = DateTime.Now;
Diagnosis.Log($"WARN:FLChargeStation[{SiteId}] TCP disconnected from {_endPoint.Address}:{_endPoint.Port}");
}
}
/// <summary>
/// 发送充电指令到充电站
/// </summary>
public override void SendToChargeStation(int isCharge, Car car,Site site)
{
// 检查连接状态
if (!IsConnected || Client == null)
{
Diagnosis.Log($"WARN:FLChargeStation[{SiteId}] not connected, cannot send charge command");
return;
}
try
{
var messageService = CommunicationMessageService.Instance;
float soc = 0f, voltage = 0, electricCurrent = 0;
int carId = 0;
float setVoltage = 55.0f;
float setElectricCurrent = 50.0f;
//Site site = null;
if (car != null)
{
soc = float.Parse(Commons.GetCarStatus(car, "Soc"));
voltage = float.Parse(Commons.GetCarStatus(car, "Voltage"));
electricCurrent = float.Parse(Commons.GetCarStatus(car, "ElectricCurrent"));
carId = car.id;
//site = SimpleLib.GetSite(car.status.holdingLocks.FirstOrDefault());
}
if (site != null && site.fields.ContainsKey("setVoltage") && site.fields.ContainsKey("setElectricCurrent"))
{
setVoltage = float.Parse(site.fields["setVoltage"]);
setElectricCurrent = float.Parse(site.fields["setElectricCurrent"]);
}
var msg = GetSendBytes((byte)(isCharge),
setVoltage, setElectricCurrent, 1000,
Convert.ToSingle(carId), Convert.ToSingle(soc), Convert.ToSingle(electricCurrent), Convert.ToSingle(voltage));
messageService.AddSendMessage(_endPoint.Address.ToString(), _endPoint.Port, BitConverter.ToString(msg).Replace("-", " "), "FRLDTall", site?.name);
// 发送数据,带异常处理
try
{
Client.Send(msg);
Diagnosis.Post($"{string.Join(" ", msg.Select(d => $"{d:X2}"))}",
$"sendChargeSite: {SiteId}", true);
}
catch (ObjectDisposedException)
{
Diagnosis.Log($"ERR:FLChargeStation[{SiteId}] client disposed during send, triggering reconnect");
IsConnected = false;
}
catch (SocketException ex)
{
Diagnosis.Log($"ERR:FLChargeStation[{SiteId}] socket error during send: {ex.SocketErrorCode}, triggering reconnect");
IsConnected = false;
}
}
catch (Exception e)
{
Diagnosis.Post($"ERR:FLChargeStation[{SiteId}] SwitchCharge Fail: {ExceptionFormatter.FormatEx(e)}");
}
}
private byte[] GetSendBytes(byte startCharge, float chargeVoltage, float chargeElectricCurrent,
float chargeTimeSpan, float carId,
float carSoc, float carElectricCurrent, float carVoltage)
{
var sendByte = new byte[32];
try
{
//BB 01 42 48 00 00 42 5C 00 00 00 00 00 02 00 5D 41 B4 00 00 41 28 00 00 00 00 00 00 00 00 00 EE
sendByte = new byte[2] { 0xBB, startCharge }
.Concat(BitConverter.GetBytes(chargeElectricCurrent).AsEnumerable().Reverse())
.Concat(BitConverter.GetBytes(chargeVoltage).AsEnumerable().Reverse())
.Concat(BitConverter.GetBytes((ushort)chargeTimeSpan).AsEnumerable().Reverse())
.Concat(BitConverter.GetBytes((ushort)carId).AsEnumerable().Reverse())
.Concat(BitConverter.GetBytes((ushort)carSoc).AsEnumerable().Reverse())
.Concat(BitConverter.GetBytes(carElectricCurrent).AsEnumerable().Reverse())
.Concat(BitConverter.GetBytes(carVoltage).AsEnumerable().Reverse())
.Concat(new byte[] { 00, 00, 00, 00, 00, 00, 00, 0xEE }).ToArray();
}
catch (Exception ex)
{
Diagnosis.Post($"下发充电控制{(startCharge == 1 ? "" : "")}异常+ex:{ExceptionFormatter.FormatEx(ex)}", "error");
}
return sendByte;
}
}
}
@@ -0,0 +1,456 @@
using SimpleLite.RCS;
using SimpleLite.RCS.CarTypes;
using SimpleCore;
using SimpleCore.Library;
using SimpleCore.PropType;
using StandardScene.Charge;
using StandardScene.TCP;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;
namespace StandardScene.ChargeStationType
{
public class MuXingChargeStation : AbstractChargeStation
{
public static TcpListener tcpListener;
public static Thread listenerThread;
public static NetworkStream stream;
public static TcpClient client;
//public Dictionary<IPAddress, (TcpClient, NetworkStream)> chargeClient = new Dictionary<IPAddress, (TcpClient, NetworkStream)> { };
public System.Timers.Timer _Timer;
public bool reciveHeartBeat;
public bool running;
public byte[] IdBytes = new byte[] { 0x0f, 0x01 };
private static IPEndPoint _endPoint;
/// <summary>
/// 关闭当前TCP连接
/// </summary>
public override void CloseCommunication()
{
try
{
running = false;
// 停止定时器
if (_Timer != null)
{
_Timer.Stop();
_Timer.Dispose();
_Timer = null;
}
// 关闭流
if (stream != null)
{
stream.Close();
stream = null;
}
// 关闭客户端
if (client != null)
{
client.Close();
client = null;
}
// 停止监听器
if (tcpListener != null)
{
tcpListener.Stop();
tcpListener = null;
}
Diagnosis.Log($"MuXingChargeStation[{SiteId}] TCP connection closed");
}
catch (Exception ex)
{
Diagnosis.Log($"ERR:MuXingChargeStation[{SiteId}] failed to close connection: {ex.Message}");
}
}
public override void CreateCommunication(IPAddress ip, int port)
{
// 先关闭旧连接
CloseCommunication();
_endPoint = new IPEndPoint(ip, port);
reciveHeartBeat = false;
running = true;
int n = 0;
DateTime offlineTime = DateTime.Now;
try
{
tcpListener = new TcpListener(ip, port);
tcpListener.Start();
Diagnosis.Post($"AGV与牧星充电站通信已建立,监听 IP: {ip}, 端口: {port}");
client = tcpListener.AcceptTcpClient();
client.SendTimeout = 5000;
client.ReceiveTimeout = 5000;
stream = client.GetStream();
}
catch (Exception ex)
{
Console.WriteLine($"与充电站通信建立失败: {ExceptionFormatter.FormatEx(ex)}");
throw;
}
while (running)
{
try
{
if (client == null)
{
client = tcpListener.AcceptTcpClient();
client.SendTimeout = 5000;
client.ReceiveTimeout = 5000;
stream = client.GetStream();
}
var type = ReceiveMesageType();
//item1:byte0 帧头
//item2:byte11 命令字
//item3:byte12 动作码/故障码/状态码
//item4:byte5 设备ID 低
//item5:byte6 设备ID 高
//0x10 充电桩登录回复 1次
if (type.Item1 == 0xAA && type.Item2 == 0x10)
{
var timestampBytes = GetTimeStamp();
byte[] dataByte = new byte[16]
{
0, 0x0d,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0x10,
0, 0,
0x00, 0x00
}; //长度必须大于10,与帧长度对应
dataByte = InitSendBytes(dataByte, type, timestampBytes);
byte[] mesSendByte =
CombineDataAndCRC(dataByte, CalculateCRC16(dataByte.Skip(1).ToArray())); //拼接crc(去掉包头)
Diagnosis.Post($"login => {string.Join(" ", mesSendByte.Select(d => $"{d:X2}"))}",
$"充电桩登录回复");
SendMessage(mesSendByte);
}
//0x12 充电桩对接完成回复 1次
else if (type.Item1 == 0xAA && type.Item2 == 0x12)
{
var timestampBytes = GetTimeStamp();
byte[] dataByte = new byte[12]
{
0, 0x09,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0x12
};
;
dataByte = InitSendBytes(dataByte, type, timestampBytes);
byte[] sendByte =
CombineDataAndCRC(dataByte, CalculateCRC16(dataByte.Skip(1).ToArray())); //拼接crc(去掉包头)
Diagnosis.Post($"docking => {string.Join(" ", sendByte.Select(d => $"{d:X2}"))}",
$"充电桩对接完成回复");
SendMessage(sendByte);
}
//0x13 故障上报回复
else if (type.Item1 == 0xAA && type.Item2 == 0x13)
{
var timestampBytes = GetTimeStamp();
byte[] dataByte = new byte[13]
{
0, 0x0A,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0x13,
type.Item3
};
dataByte = InitSendBytes(dataByte, type, timestampBytes);
byte[] sendByte =
CombineDataAndCRC(dataByte, CalculateCRC16(dataByte.Skip(1).ToArray())); //拼接crc(去掉包头)
Diagnosis.Post($"Error => {string.Join(" ", sendByte.Select(d => $"{d:X2}"))}",
$"故障上报回复");
SendMessage(sendByte);
}
//收到至少1次充电桩心跳包上传
if (type.Item1 == 0xAA && type.Item2 == 0xF0 && !reciveHeartBeat)
{
Diagnosis.Post($"收到充电桩心跳包上传", $"HeartBeatRecive");
reciveHeartBeat = true;
StartSendingHeartBeat();
}
//异常情况处理
//故障上报 故障码不为0 16 17 1
if (type.Item1 == 0xAA && type.Item2 == 0x13 &&
(type.Item3 != 0 && type.Item3 != 16 && type.Item3 != 17 && type.Item3 != 1))
{
//充电桩掉线
if ((type.Item3 & (1 << 3)) != 0 && reciveHeartBeat)
{
if (n == 0) offlineTime = DateTime.Now;
//充电桩掉线时间大于60s 重启WiFi模块
if ((DateTime.Now - offlineTime).TotalSeconds > 60)
{
var timestampBytes = GetTimeStamp();
//下发 命令码指令
byte[] dataByte = new byte[17]
{
0, 0x0E,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0x15,
0x04, //重启WiFi模块
0x00, 0x00, 0x00, 0x00
};
dataByte = InitSendBytes(dataByte, type, timestampBytes);
byte[] sendChargeByte =
CombineDataAndCRC(dataByte, CalculateCRC16(dataByte.Skip(1).ToArray()));
Diagnosis.Post(
$"Restart => {string.Join(" ", sendChargeByte.Select(d => $"{d:X2}"))}",
$"重启WiFi模块");
SendMessage(sendChargeByte);
n = 0;
offlineTime = DateTime.Now;
}
n++;
}
else
{
n = 0;
offlineTime = DateTime.Now;
}
}
//if (!client.Connected)
//{
// Console.WriteLine("客户端断开连接,退出循环");
// running = false;
//}
}
catch (Exception ex)
{
Console.WriteLine($"充电桩通信异常:{ExceptionFormatter.FormatEx(ex)}");
running = false;
client.Close();
tcpListener.Stop();
stream.Close();
}
}
}
public override void SendToChargeStation(int isCharge, Car car,Site site)
{
var messageService = CommunicationMessageService.Instance;
float setVoltage = 55.0f;
float setElectricCurrent = 50.0f;
float voltage = 430;
// Site site = null;
if (car != null)
{
// site = SimpleLib.GetSite(car.status.holdingLocks.FirstOrDefault());
int carId = 0;
if (car != null && car.GetType() != typeof(DummyCar))
{
voltage = float.Parse(Commons.GetCarStatus(car, "Voltage")) * 10;
voltage = voltage > 430 ? voltage : 430;
carId = car.id;
}
}
if (site != null && site.fields.ContainsKey("setVoltage") && site.fields.ContainsKey("setElectricCurrent"))
{
setVoltage = float.Parse(site.fields["setVoltage"]);
setElectricCurrent = float.Parse(site.fields["setElectricCurrent"]);
}
var byte1 = BitConverter.GetBytes(voltage);
var type = ReceiveMesageType();
var openChargePort = isCharge == 1 ? 2 : 3;
//下发打开充电口指令
var timestampBytes = GetTimeStamp();
byte[] dataByte = new byte[18]
{
0, 0x0F,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0x11,
(byte)openChargePort,//开始充电/结束充电
byte1[0], byte1[1],
0xc8, 0x00,//下发最大充电电流
0x02//电池种类
};
dataByte = InitSendBytes(dataByte, type, timestampBytes);
byte[] openCharge =
CombineDataAndCRC(dataByte, CalculateCRC16(dataByte.Skip(1).ToArray()));
if (client.Connected)
{
stream.WriteAsync(openCharge, 0, openCharge.Length);
stream.FlushAsync();
}
messageService.AddSendMessage(_endPoint.Address.ToString(), _endPoint.Port, BitConverter.ToString(openCharge).Replace("-", " "), site?.name, "MuXing");
Diagnosis.Post($"openCharge => {string.Join(" ", openCharge.Select(d => $"{d:X2}"))}",
$"下发开始充电");
}
public void StartSendingHeartBeat()
{
if (client == null)
{
Console.WriteLine("客户端为null,无法启动心跳包定时器");
return;
}
_Timer = new System.Timers.Timer(2000);
_Timer.Elapsed += SendHeartBeat;
_Timer.AutoReset = true; // 反复执行
_Timer.Enabled = true;
}
private void SendHeartBeat(Object item, ElapsedEventArgs e)
{
var timestampBytes = GetTimeStamp();
var type = ReceiveMesageType();
//下发心跳包报文
var rcs = new byte[]
{
0xBB, 0x09, 0x00, 0x03, 0x00,
IdBytes[0], IdBytes[1],
timestampBytes[0], timestampBytes[1], timestampBytes[2], timestampBytes[3],
0xF0
};
byte[] mesSendByte = CombineDataAndCRC(rcs, CalculateCRC16(rcs.Skip(1).ToArray()));
// 日志记录
Diagnosis.Post($"heartBeat => {string.Join(" ", mesSendByte.Select(d => $"{d:X2}"))}", $"下发心跳包");
// 发送心跳包
SendMessage(mesSendByte);
}
private byte[] GetTimeStamp()
{
// 获取当前时间戳(秒级别)
int timestamp = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds();
//将时间戳转换为字节数组,低字节在前
byte[] timestampBytes = BitConverter.GetBytes(timestamp);
return timestampBytes;
}
private static Tuple<byte, byte, byte, byte, byte> ReceiveMesageType()
{
byte[] message = new byte[1024];
int bytesRead = 0;
try
{
if (client != null && client.Connected && stream != null && stream.CanRead && client.Available > 0)
{
bytesRead = stream.Read(message, 0, 1024);
}
}
catch (Exception ex)
{
client = null;
stream = null;
Console.WriteLine($"Error reading message: {ex.Message}");
}
//message长度判断:至少需要 13 字节才能安全访问 message[12]
if (bytesRead >= 13)
{
string receivedMessage = BitConverter.ToString(message, 0, bytesRead);
Diagnosis.Post($"Received <= {string.Join(" ", message.Take(bytesRead).ToArray().Select(d => $"{d:X2}"))}", "读取报文");
Diagnosis.Post($"item1:{message[0]:x2},item2:{message[11]:x2},item3:{message[12]:x2},item4:{message[5]:x2},item5:{message[6]:x2},", "Tuple.Item");
var messageService = CommunicationMessageService.Instance;
messageService.AddReceiveMessage(_endPoint.Address.ToString(), _endPoint.Port, string.Join(",", message), "MuXing");
return Tuple.Create<byte, byte, byte, byte, byte>(message[0], message[11], message[12], message[5], message[6]);
}
return new Tuple<byte, byte, byte, byte, byte>(0, 0, 0, 0, 0);
}
public static byte[] CombineDataAndCRC(byte[] data, byte[] crc)
{
//byte[] crcBytes = BitConverter.GetBytes(crc);
// 合并数据和CRC
byte[] combined = new byte[data.Length + crc.Length];
Array.Copy(data, combined, data.Length);
Array.Copy(crc, 0, combined, data.Length, crc.Length);
return combined;
}
private static byte[] CalculateCRC16(byte[] data)
{
byte b = byte.MaxValue;
byte b2 = byte.MaxValue;
byte b3 = 1;
byte b4 = 160;
for (int i = 0; i < data.Length; i++)
{
b = (byte)(b ^ data[i]);
for (int j = 0; j <= 7; j++)
{
byte b5 = b2;
byte b6 = b;
b2 = (byte)(b2 >> 1);
b = (byte)(b >> 1);
if ((b5 & 1) == 1)
{
b = (byte)(b | 0x80u);
}
if ((b6 & 1) == 1)
{
b2 = (byte)(b2 ^ b4);
b = (byte)(b ^ b3);
}
}
}
return new byte[2]
{
b,b2
};
}
private void SendMessage(byte[] message)
{
try
{
if (client != null && stream != null)
{
stream.Write(message, 0, message.Length);
stream.Flush();
}
}
catch (Exception ex)
{
client = null;
Console.WriteLine($"Error sending message: {ExceptionFormatter.FormatEx(ex)}");
}
}
private byte[] InitSendBytes(byte[] sendBytes, Tuple<byte, byte, byte, byte, byte> type, byte[] timestampBytes)
{
sendBytes[0] = 0xBB; //帧头
sendBytes[2] = 0x00; //帧长 高
sendBytes[3] = 0x03; //设备类型 低
sendBytes[4] = 0x00; //设备类型 高
sendBytes[5] = IdBytes[0]; //设备ID 低
sendBytes[6] = IdBytes[1]; //设备ID 高
sendBytes[7] = timestampBytes[0]; //时间戳 低
sendBytes[8] = timestampBytes[1];
sendBytes[9] = timestampBytes[2];
sendBytes[10] = timestampBytes[3]; //时间戳 高
return sendBytes;
}
}
}
@@ -0,0 +1,147 @@
using SimpleLite.RCS;
using SimpleLite.RCS.CarTypes;
using SimpleCore;
using SimpleCore.Library;
using SimpleCore.PropType;
using StandardScene.Charge;
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using static SimpleCore.Traffic.TrafficControl;
namespace StandardScene.ChargeStationType
{
public class PCBChargeStation : AbstractChargeStation
{
private int _index = 0;
public int IndexReceive;
private IPEndPoint _endPoint;
public override void OnUdpMessage(byte[] message)
{
if (message != null && message.Length > 1)
IndexReceive = message[1];
}
public override void CreateCommunication(IPAddress ip, int port)
{
_endPoint = new IPEndPoint(ip, port);
}
public override void SendToChargeStation(int isCharge, Car car,Site site)
{
var messageService = CommunicationMessageService.Instance;
using (UdpClient udpClient = new UdpClient())
{
//float soc = 0f, voltage = 0, electricCurrent = 0, chargeTimeSpan = 30f;
float setVoltage = 29.2f;
float setElectricCurrent = 40.0f;
float soc = 0f, voltage = 0, electricCurrent = 0, chargeTimeSpan = 30f;
int carId = 0;
//Site site = null;
if (car != null)
{
carId = car.id;
soc = (float)Commons.CarValue(car, "Soc");
voltage = (float)Commons.CarValue(car, "Voltage");
electricCurrent = (float)Commons.CarValue(car, "ElectricCurrent");
//site = SimpleLib.GetSite(car.status.holdingLocks.FirstOrDefault());
}
if (site != null && site.fields.ContainsKey("setVoltage") && site.fields.ContainsKey("setElectricCurrent"))
{
setVoltage = float.Parse(site.fields["setVoltage"]);
setElectricCurrent = float.Parse(site.fields["setElectricCurrent"]);
}
var msg = GetSendBytes((byte)isCharge, setVoltage, setElectricCurrent, chargeTimeSpan,
carId, soc, electricCurrent, voltage);
messageService.AddSendMessage(_endPoint.Address.ToString(), _endPoint.Port, BitConverter.ToString(msg).Replace("-", " "), "FRLDShort", site?.name);
Diagnosis.Log($"ChargeStation ADD:[{BitConverter.ToString(msg).Replace("-", " ")}]", "UDP发送报文信息", true);
udpClient.SendAsync(msg, msg.Length, _endPoint);
Thread.Sleep(100);
}
}
private byte[] GetSendBytes(byte startCharge, float chargeVoltage, float chargeElectricCurrent, float chargeTimeSpan, int carId, float carSoc, float carElectricCurrent, float carVoltage)
{
var sendBytes = new byte[32];
var indexNo = (byte)GetIndexNo();
try
{
sendBytes = new byte[3] { 0xBB, indexNo, startCharge }
.Concat(BitConverter.GetBytes((int)chargeElectricCurrent * 10).AsEnumerable().Reverse())
.Concat(BitConverter.GetBytes((int)chargeVoltage * 10).AsEnumerable().Reverse())
.Concat(BitConverter.GetBytes((ushort)chargeTimeSpan).AsEnumerable().Reverse())
.Concat(BitConverter.GetBytes((ushort)carId).AsEnumerable().Reverse())
.Concat([(byte)carSoc])
.Concat(BitConverter.GetBytes((int)(carElectricCurrent * 10)).AsEnumerable().Reverse())
.Concat(BitConverter.GetBytes((int)(carVoltage * 10)).AsEnumerable().Reverse())
.Concat(new byte[] { 00, 00, 00, 00, 00, 00, 00, 0xEE }).ToArray();
var crcCode = GetCRC(sendBytes.Skip(1).Take(28).ToArray()).AsEnumerable().Reverse().ToArray();
sendBytes[29] = crcCode[0];
sendBytes[30] = crcCode[1];
}
catch (Exception ex)
{
Diagnosis.Log($"充电报文组包异常 ex => {ex.Message}", "充电", true);
}
return sendBytes;
}
private byte[] GetCRC(byte[] data)
{
byte b = byte.MaxValue;
byte b2 = byte.MaxValue;
byte b3 = 1;
byte b4 = 160;
for (int i = 0; i < data.Length; i++)
{
b = (byte)(b ^ data[i]);
for (int j = 0; j <= 7; j++)
{
byte b5 = b2;
byte b6 = b;
b2 = (byte)(b2 >> 1);
b = (byte)(b >> 1);
if ((b5 & 1) == 1)
{
b = (byte)(b | 0x80u);
}
if ((b6 & 1) == 1)
{
b2 = (byte)(b2 ^ b4);
b = (byte)(b ^ b3);
}
}
}
return new byte[2]
{
b,b2
};
}
private int GetIndexNo()
{
if (_index < 255)
{
_index = _index + 1;
}
else
{
_index = 0;
}
return _index;
}
}
}