From 9f920ca6d2c307846994a7e367c4aff7ed824c82 Mon Sep 17 00:00:00 2001
From: tq <1916474859@qq,com>
Date: Wed, 19 Jun 2024 17:40:52 +0800
Subject: [PATCH] =?UTF-8?q?=E9=83=A8=E5=88=86=E7=AB=99=E5=A4=96=E5=85=85?=
=?UTF-8?q?=E7=94=B5=E6=9C=BA=E5=B8=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Common/Util/HttpUtil.cs | 28 ++
Entity/DbModel/Station/ChargeOrder.cs | 10 +-
Repository/Station/ChargeOrderRepository.cs | 33 ++
Service/Charger/Client/ChargerClient.cs | 74 ++++-
Service/Charger/Codec/Decoder.cs | 12 +
Service/Charger/Common/ChargerUtils.cs | 42 +++
.../OutCharger/PileChargeCompleteHandler.cs | 49 +++
.../PileStartChargeCompleteHandler.cs | 37 +++
.../OutCharger/PileStartChargeResHandler.cs | 61 ++++
.../OutCharger/PileStopChargeResHandler.cs | 46 +++
.../PileUploadRemoteSignalHandler.cs | 30 ++
.../OutCharger/PileUploadTelemetryHandler.cs | 30 ++
.../OutCharger/Req/PileChargeCompleteReq.cs | 173 ++++++++++
.../Req/PileStartChargeCompleteReq.cs | 137 ++++++++
.../OutCharger/Req/PileUploadRemoteSignal.cs | 306 ++++++++++++++++++
.../OutCharger/Req/PileUploadTelemetry.cs | 205 ++++++++++++
.../OutCharger/Resp/PileStartChargeRes.cs | 36 +++
.../OutCharger/Resp/PileStopChargeRes.cs | 28 ++
.../Req/OutCharger/Req/PileStartCharge.cs | 65 ++++
.../Host/Req/OutCharger/Req/PileStopCharge.cs | 42 +++
.../Resp/OutCharger/PileChargeCompleteRes.cs | 40 +++
.../OutCharger/PileStartChargeCompleteRes.cs | 49 +++
.../Msg/Http/Req/PileStartChargeHttpReq.cs | 44 +++
.../Msg/Http/Req/PileStopChargeHttpReq.cs | 20 ++
.../Msg/Http/Resp/PileStartChargeHttpRes.cs | 27 ++
.../Msg/Http/Resp/PileStopChargeHttpRes.cs | 17 +
.../Controllers/OutChargerController.cs | 84 +++++
27 files changed, 1721 insertions(+), 4 deletions(-)
create mode 100644 Common/Util/HttpUtil.cs
create mode 100644 Service/Charger/Handler/OutCharger/PileChargeCompleteHandler.cs
create mode 100644 Service/Charger/Handler/OutCharger/PileStartChargeCompleteHandler.cs
create mode 100644 Service/Charger/Handler/OutCharger/PileStartChargeResHandler.cs
create mode 100644 Service/Charger/Handler/OutCharger/PileStopChargeResHandler.cs
create mode 100644 Service/Charger/Handler/OutCharger/PileUploadRemoteSignalHandler.cs
create mode 100644 Service/Charger/Handler/OutCharger/PileUploadTelemetryHandler.cs
create mode 100644 Service/Charger/Msg/Charger/OutCharger/Req/PileChargeCompleteReq.cs
create mode 100644 Service/Charger/Msg/Charger/OutCharger/Req/PileStartChargeCompleteReq.cs
create mode 100644 Service/Charger/Msg/Charger/OutCharger/Req/PileUploadRemoteSignal.cs
create mode 100644 Service/Charger/Msg/Charger/OutCharger/Req/PileUploadTelemetry.cs
create mode 100644 Service/Charger/Msg/Charger/OutCharger/Resp/PileStartChargeRes.cs
create mode 100644 Service/Charger/Msg/Charger/OutCharger/Resp/PileStopChargeRes.cs
create mode 100644 Service/Charger/Msg/Host/Req/OutCharger/Req/PileStartCharge.cs
create mode 100644 Service/Charger/Msg/Host/Req/OutCharger/Req/PileStopCharge.cs
create mode 100644 Service/Charger/Msg/Host/Resp/OutCharger/PileChargeCompleteRes.cs
create mode 100644 Service/Charger/Msg/Host/Resp/OutCharger/PileStartChargeCompleteRes.cs
create mode 100644 Service/Charger/Msg/Http/Req/PileStartChargeHttpReq.cs
create mode 100644 Service/Charger/Msg/Http/Req/PileStopChargeHttpReq.cs
create mode 100644 Service/Charger/Msg/Http/Resp/PileStartChargeHttpRes.cs
create mode 100644 Service/Charger/Msg/Http/Resp/PileStopChargeHttpRes.cs
create mode 100644 WebStarter/Controllers/OutChargerController.cs
diff --git a/Common/Util/HttpUtil.cs b/Common/Util/HttpUtil.cs
new file mode 100644
index 0000000..0d9e8a6
--- /dev/null
+++ b/Common/Util/HttpUtil.cs
@@ -0,0 +1,28 @@
+using System.Text;
+using Newtonsoft.Json;
+
+namespace Common.Util;
+
+public static class HttpUtil
+{
+ private static readonly HttpClient httpClient = new HttpClient();
+
+ ///
+ /// 发送post请求
+ ///
+ ///
+ ///
+ ///
+ public static async void SendPostRequest(T data, string url)
+ {
+ string jsonStr = JsonConvert.SerializeObject(data);
+ var content = new StringContent(jsonStr, Encoding.UTF8, "application/json");
+
+ HttpResponseMessage response = await httpClient.PostAsync(url, content);
+
+ if (response.IsSuccessStatusCode)
+ {
+ await response.Content.ReadAsStringAsync();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Entity/DbModel/Station/ChargeOrder.cs b/Entity/DbModel/Station/ChargeOrder.cs
index d8c2473..94ad8e4 100644
--- a/Entity/DbModel/Station/ChargeOrder.cs
+++ b/Entity/DbModel/Station/ChargeOrder.cs
@@ -51,7 +51,7 @@ namespace Entity.DbModel.Station
///
[SugarColumn(ColumnName = "charger_no")]
public string ChargerNo { get; set; }
-
+
///
/// Desc:充电枪编号
/// Default:
@@ -59,6 +59,14 @@ namespace Entity.DbModel.Station
///
[SugarColumn(ColumnName = "charger_gun_no")]
public string ChargerGunNo { get; set; }
+
+ ///
+ /// Desc:站外充电枪编号,1枪或2枪
+ /// Default:
+ /// Nullable:True
+ ///
+ [SugarColumn(ColumnName = "out_charger_gun_no")]
+ public string OutChargerGunNo { get; set; }
///
/// Desc:0:站内充电 1:站外充电
diff --git a/Repository/Station/ChargeOrderRepository.cs b/Repository/Station/ChargeOrderRepository.cs
index 7d3833f..e178142 100644
--- a/Repository/Station/ChargeOrderRepository.cs
+++ b/Repository/Station/ChargeOrderRepository.cs
@@ -11,6 +11,38 @@ public class ChargeOrderRepository : BaseRepository
{
}
+ public void SaveChargeGunOrder(string chargeOrderNo, string chargerNo, string chargerGunNo,
+ string outChargerGunNo)
+ {
+ ChargeOrder order = new ChargeOrder();
+ order.Sn = chargeOrderNo;
+ order.CmdStatus = 0;
+ order.ChargerNo = chargerNo;
+ order.ChargerGunNo = chargerGunNo;
+ order.OutChargerGunNo = outChargerGunNo;
+ order.ChargeMode = 1;
+ order.StartMode = 1;
+ order.CloudChargeOrder = chargeOrderNo;
+ order.CreatedTime = DateTime.Now;
+ ChargeOrder chargeOrder = Insert(order);
+ }
+
+ public ChargeOrder? GetLatestChargeGunOrder(string pn, string chargerCode)
+ {
+ try
+ {
+ var chargeOrder = DbBaseClient.Queryable()
+ .Where(co => co.OutChargerGunNo == pn && co.ChargerNo == chargerCode)
+ .OrderByDescending(co => co.CreatedTime)
+ .First();
+ return chargeOrder;
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+
public ChargeOrder? QueryLatestByBatterySn(string? batterySn)
{
if (string.IsNullOrWhiteSpace(batterySn))
@@ -33,6 +65,7 @@ public class ChargeOrderRepository : BaseRepository
{
return new List(0);
}
+
return QueryListByClause(it => it.SwapOrderSn == swapOrderNo && it.BatteryNo == batteryNo, it => it.CreatedTime,
OrderByType.Asc);
}
diff --git a/Service/Charger/Client/ChargerClient.cs b/Service/Charger/Client/ChargerClient.cs
index 0cf0fe0..c35c30d 100644
--- a/Service/Charger/Client/ChargerClient.cs
+++ b/Service/Charger/Client/ChargerClient.cs
@@ -1,4 +1,5 @@
-using Autofac;
+using System.Collections.Concurrent;
+using Autofac;
using Entity.DbModel.Station;
using HybirdFrameworkCore.Autofac;
using HybirdFrameworkCore.Autofac.Attribute;
@@ -14,8 +15,10 @@ using Service.Charger.Codec;
using Service.Charger.Common;
using Service.Charger.Handler;
using Service.Charger.Msg;
+using Service.Charger.Msg.Charger.OutCharger.Req;
using Service.Charger.Msg.Charger.Req;
using Service.Charger.Msg.Charger.Resp;
+using Service.Charger.Msg.Host.OutCharger.Req;
using Service.Charger.Msg.Host.Req;
using Service.Charger.Msg.Host.Req.Bms;
@@ -47,7 +50,18 @@ public class ChargerClient : TcpClient
/// 是否已经开始充电
///
public bool IsCharged { get; set; } = false;
-
+
+
+
+ ///
+ /// 站外两枪时是否在充电
+ ///
+ public ConcurrentDictionary GunCharged = new ConcurrentDictionary
+ {
+ [1] = false,
+ [2] = false
+ };
+
public bool IsStopped { get; set; } = false;
public bool IsCanSendStopCmd { get; set; } = true;
@@ -129,7 +143,11 @@ public class ChargerClient : TcpClient
/// 充电机遥测数据
///
public UploadTelemetryData UploadTelemetryData = new UploadTelemetryData();
-
+
+ ///
+ /// 两个充电桩的遥测
+ ///
+ public ConcurrentDictionary PileUploadTelemetry = new();
///
///充电机实时充电功率
///
@@ -454,7 +472,57 @@ public class ChargerClient : TcpClient
this.Channel.WriteAndFlushAsync(req);
return Result.Success();
}
+ ///
+ /// 3.7.1 监控平台远程启动充电桩充电
+ ///
+ /// 充电枪ID号
+ /// SOC 限制
+ /// 功率调节指令类型
+ /// 功率调节参数
+ ///
+ /// 充电流水号
+ public Result SendStartOutCharger(byte pn, byte socValue, short changePower=360,byte changePowerCmdType=1 ,
+ string? chargeOrderNo = null)
+ {
+ if (!Connected)
+ {
+ return Result.Fail($"充电机{BinNo}未连接");
+ }
+
+ if (string.IsNullOrWhiteSpace(chargeOrderNo))
+ {
+ chargeOrderNo = ChargerUtils.GenChargeOrderSn();
+ }
+
+ Log().Info(
+ $"SendStartOutCharger pn={pn}, socValue={socValue}, changePower={changePower}, changePowerCmdType={changePowerCmdType}, chargeOrderNo={chargeOrderNo}");
+
+ PileStartCharge pileStartCharge = new PileStartCharge(pn, socValue, changePowerCmdType, changePower,chargeOrderNo);
+
+ this.Channel.WriteAndFlushAsync(pileStartCharge);
+
+ return Result.Success(chargeOrderNo);
+ }
+ ///
+ /// 3.7.3 监控平台远程停止充电桩充电
+ ///
+ ///
+ ///
+ ///
+ public Result SendStopOutCharger(byte pn, byte stopReason)
+ {
+ if (!Connected)
+ {
+ return Result.Fail($"充电机{BinNo}未连接");
+ }
+
+ Log().Info(
+ $"SendStartOutCharger pn={pn}, stopReason={stopReason}");
+ PileStopCharge pileStopCharge=new PileStopCharge(pn,stopReason);
+ this.Channel.WriteAndFlushAsync(pileStopCharge);
+ return Result.Success();
+ }
///
///
///
diff --git a/Service/Charger/Codec/Decoder.cs b/Service/Charger/Codec/Decoder.cs
index 7a58bf3..7a53b68 100644
--- a/Service/Charger/Codec/Decoder.cs
+++ b/Service/Charger/Codec/Decoder.cs
@@ -8,6 +8,8 @@ using Newtonsoft.Json;
using Service.Charger.Common;
using Service.Charger.Msg;
using Service.Charger.Msg.Bms;
+using Service.Charger.Msg.Charger.OutCharger.Req;
+using Service.Charger.Msg.Charger.OutCharger.Resp;
using Service.Charger.Msg.Charger.Req;
using Service.Charger.Msg.Charger.Resp;
@@ -239,6 +241,16 @@ public class Decoder : ByteToMessageDecoder
148 => ModelConvert.Decode(bytes),
_ => throw new InvalidOperationException("This should never be reached"),
},
+ 51 => recordType switch
+ {
+ 2 => ModelConvert.Decode(bytes),
+ 4 => ModelConvert.Decode(bytes),
+ 5 => ModelConvert.Decode(bytes),
+ 7 => ModelConvert.Decode(bytes),
+ 12 => ModelConvert.Decode(bytes),
+ 11 => ModelConvert.Decode(bytes),
+ _ => throw new InvalidOperationException("This should never be reached"),
+ },
#endregion
42 => ModelConvert.Decode(bytes),
diff --git a/Service/Charger/Common/ChargerUtils.cs b/Service/Charger/Common/ChargerUtils.cs
index cecea4e..07389d4 100644
--- a/Service/Charger/Common/ChargerUtils.cs
+++ b/Service/Charger/Common/ChargerUtils.cs
@@ -58,7 +58,49 @@ public static class ChargerUtils
return StaticStationInfo.StationNo + DateTime.Now.ToString(ChargerConst.DateFormat) +
GetRandomNumLimit99();
}
+ ///
+ /// 根据云平台下发,计算本地充电机枪号
+ ///
+ ///
+ ///
+ ///
+ public static byte GetTheGun(string number)
+ {
+ int parsedNumber;
+
+ if (int.TryParse(number, out parsedNumber))
+ {
+ if (parsedNumber % 2 == 0)
+ {
+ return 2;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ else
+ {
+ throw new ArgumentException("转换失败");
+ }
+ }
+
+ public static string GetOutChargerCode(string number)
+ {
+ int parsedNumber;
+ if (int.TryParse(number, out parsedNumber))
+ {
+ // 除2,向上取整,拿到本地充电机code
+ int ceilResult = (int)Math.Ceiling(parsedNumber / 2.0);
+ return ceilResult.ToString();
+ }
+ else
+ {
+ throw new ArgumentException("转换失败");
+ }
+
+ }
///
/// 计算Byte随机数值
///
diff --git a/Service/Charger/Handler/OutCharger/PileChargeCompleteHandler.cs b/Service/Charger/Handler/OutCharger/PileChargeCompleteHandler.cs
new file mode 100644
index 0000000..ff89574
--- /dev/null
+++ b/Service/Charger/Handler/OutCharger/PileChargeCompleteHandler.cs
@@ -0,0 +1,49 @@
+using DotNetty.Transport.Channels;
+using Entity.DbModel.Station;
+using HybirdFrameworkCore.Autofac.Attribute;
+using log4net;
+using Repository.Station;
+using Service.Charger.Client;
+using Service.Charger.Msg.Charger.OutCharger.Req;
+using Service.Charger.Msg.Host.Resp.OutCharger;
+
+namespace Service.Charger.Handler.OutCharger;
+
+///
+/// 3.7.7 充电桩上送停止完成帧
+///
+[Order(8)]
+[Scope("InstancePerDependency")]
+public class PileChargeCompleteHandler : SimpleChannelInboundHandler, IBaseHandler
+{
+ private static readonly ILog Log = LogManager.GetLogger(typeof(PileChargeCompleteHandler));
+
+ public ChargeOrderRepository ChargeOrderRepository { get; set; }
+
+ protected override void ChannelRead0(IChannelHandlerContext ctx, PileChargeCompleteReq msg)
+ {
+ if (ClientMgr.TryGetClient(ctx.Channel, out var sn, out var client))
+ {
+ Log.Info($"receive {msg} from {sn}");
+ if (msg.Result == 0)
+ {
+ ChargeOrder? chargeOrder = ChargeOrderRepository.GetLatestChargeGunOrder(msg.Pn.ToString(), sn);
+ if (chargeOrder == null)
+ {
+ return;
+ }
+
+ chargeOrder.EndTime = DateTime.Now;
+ chargeOrder.StopReason = 0;
+ chargeOrder.CanUpload = 1;
+
+ client.GunCharged[msg.Pn] = false;
+
+ ChargeOrderRepository.Update(chargeOrder);
+ }
+
+ PileChargeCompleteRes res = new PileChargeCompleteRes(msg.Pn,0);
+ ctx.Channel.WriteAndFlushAsync(res);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Service/Charger/Handler/OutCharger/PileStartChargeCompleteHandler.cs b/Service/Charger/Handler/OutCharger/PileStartChargeCompleteHandler.cs
new file mode 100644
index 0000000..15ee44f
--- /dev/null
+++ b/Service/Charger/Handler/OutCharger/PileStartChargeCompleteHandler.cs
@@ -0,0 +1,37 @@
+using DotNetty.Transport.Channels;
+using HybirdFrameworkCore.Autofac.Attribute;
+using log4net;
+using Service.Charger.Client;
+using Service.Charger.Msg.Charger.OutCharger.Req;
+using Service.Charger.Msg.Host.Resp.OutCharger;
+
+namespace Service.Charger.Handler.OutCharger;
+
+[Order(8)]
+[Scope("InstancePerDependency")]
+public class PileStartChargeCompleteHandler : SimpleChannelInboundHandler, IBaseHandler
+{
+ private static readonly ILog Log = LogManager.GetLogger(typeof(PileStartChargeCompleteHandler));
+
+ protected override void ChannelRead0(IChannelHandlerContext ctx, PileStartChargeCompleteReq msg)
+ {
+ if (ClientMgr.TryGetClient(ctx.Channel, out var sn, out var client))
+ {
+ Log.Info($"receive {msg} from {sn}");
+
+ if (client == null)
+ {
+ return;
+ }
+
+ if (msg.Result == 0)
+ {
+ client.GunCharged[msg.Pn] = true;
+ }
+ // 响应启动完成帧
+ PileStartChargeCompleteRes res=new PileStartChargeCompleteRes(msg.Pn,0,0);
+
+ ctx.Channel.WriteAndFlushAsync(res);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Service/Charger/Handler/OutCharger/PileStartChargeResHandler.cs b/Service/Charger/Handler/OutCharger/PileStartChargeResHandler.cs
new file mode 100644
index 0000000..ad2be87
--- /dev/null
+++ b/Service/Charger/Handler/OutCharger/PileStartChargeResHandler.cs
@@ -0,0 +1,61 @@
+using System.Text;
+using Common.Util;
+using DotNetty.Transport.Channels;
+using Entity.DbModel.Station;
+using HybirdFrameworkCore.Autofac.Attribute;
+using log4net;
+using Newtonsoft.Json;
+using Repository.Station;
+using Service.Charger.Client;
+using Service.Charger.Msg.Charger.OutCharger.Resp;
+using Service.Charger.Msg.Http.Resp;
+
+namespace Service.Charger.Handler.OutCharger;
+
+[Order(8)]
+[Scope("InstancePerDependency")]
+public class PileStartChargeResHandler : SimpleChannelInboundHandler, IBaseHandler
+{
+ private static readonly ILog Log = LogManager.GetLogger(typeof(PileStartChargeResHandler));
+ public ChargeOrderRepository ChargeOrderRepository { get; set; }
+
+
+ protected override void ChannelRead0(IChannelHandlerContext ctx, PileStartChargeRes msg)
+ {
+ if (ClientMgr.TryGetClient(ctx.Channel, out var sn, out var client))
+ {
+ Log.Info($"receive {msg} from {sn}");
+
+
+ ChargeOrder? chargeOrder = ChargeOrderRepository.GetLatestChargeGunOrder(msg.Pn.ToString(), sn);
+ if (chargeOrder == null)
+ {
+ return;
+ }
+
+ PileStartChargeHttpRes chargeRes = new PileStartChargeHttpRes();
+
+ chargeRes.con = chargeOrder.Sn;
+ chargeRes.pn = chargeOrder.ChargerGunNo;
+
+ if (msg.Result == 0)
+ {
+ chargeOrder.StartTime = DateTime.Now;
+ chargeOrder.CmdStatus = 1;
+ ChargeOrderRepository.Update(chargeOrder);
+
+
+ chargeRes.rs = "1";
+ chargeRes.ec = "0";
+ // 9.2.1.2 站控应答开始充电操作
+ HttpUtil.SendPostRequest(chargeRes, "http://127.0.0.1:5034/api/OutCharger/ResStartOutCharger");
+ }
+ else
+ {
+ chargeRes.rs = "2";
+ chargeRes.ec = msg.FailReason.ToString();
+ HttpUtil.SendPostRequest(chargeRes, "http://127.0.0.1:5034/api/OutCharger/ResStartOutCharger");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Service/Charger/Handler/OutCharger/PileStopChargeResHandler.cs b/Service/Charger/Handler/OutCharger/PileStopChargeResHandler.cs
new file mode 100644
index 0000000..a6f0664
--- /dev/null
+++ b/Service/Charger/Handler/OutCharger/PileStopChargeResHandler.cs
@@ -0,0 +1,46 @@
+using Common.Util;
+using DotNetty.Transport.Channels;
+using Entity.DbModel.Station;
+using HybirdFrameworkCore.Autofac.Attribute;
+using log4net;
+using Repository.Station;
+using Service.Charger.Client;
+using Service.Charger.Msg.Charger.OutCharger.Resp;
+using Service.Charger.Msg.Http.Resp;
+
+namespace Service.Charger.Handler.OutCharger;
+
+[Order(8)]
+[Scope("InstancePerDependency")]
+public class PileStopChargeResHandler : SimpleChannelInboundHandler, IBaseHandler
+{
+ private static readonly ILog Log = LogManager.GetLogger(typeof(PileStopChargeResHandler));
+
+ public ChargeOrderRepository ChargeOrderRepository { get; set; }
+
+
+ protected override void ChannelRead0(IChannelHandlerContext ctx, PileStopChargeRes msg)
+ {
+ if (ClientMgr.TryGetClient(ctx.Channel, out var sn, out var client))
+ {
+ Log.Info($"receive {msg} from {sn}");
+ ChargeOrder? chargeOrder = ChargeOrderRepository.GetLatestChargeGunOrder(msg.pn.ToString(), sn);
+
+ if (chargeOrder == null)
+ {
+ return;
+ }
+
+ if (msg.rs == 0 || msg.rs == 1)
+ {
+ client.GunCharged[msg.pn] = false;
+ chargeOrder.EndTime = DateTime.Now;
+ ChargeOrderRepository.Update(chargeOrder);
+ PileStopChargeHttpRes res = new PileStopChargeHttpRes();
+ res.pn = chargeOrder.ChargerGunNo;
+ res.rs = msg.rs.ToString();
+ HttpUtil.SendPostRequest(res, "http://127.0.0.1:5034/api/OutCharger/ResStartOutCharger");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Service/Charger/Handler/OutCharger/PileUploadRemoteSignalHandler.cs b/Service/Charger/Handler/OutCharger/PileUploadRemoteSignalHandler.cs
new file mode 100644
index 0000000..50e4aaf
--- /dev/null
+++ b/Service/Charger/Handler/OutCharger/PileUploadRemoteSignalHandler.cs
@@ -0,0 +1,30 @@
+using DotNetty.Transport.Channels;
+using log4net;
+using Service.Charger.Client;
+using Service.Charger.Handler;
+
+namespace Service.Charger.Msg.Charger.OutCharger.Req;
+///
+/// 3.7.11 充电桩遥信数据上报
+///
+public class PileUploadRemoteSignalHandler: SimpleChannelInboundHandler, IBaseHandler
+{
+ private static readonly ILog Log = LogManager.GetLogger(typeof(PileUploadRemoteSignalHandler));
+
+
+ protected override void ChannelRead0(IChannelHandlerContext ctx, PileUploadRemoteSignal msg)
+ {
+ if (ClientMgr.TryGetClient(ctx.Channel, out var sn, out var client))
+ {
+ //存储日志
+ Log.Info($"receive {msg} from {sn}");
+
+ client.Workstate = msg.WorkStatus;
+ client.IsCharged = msg.WorkStatus == 1 ? true : false;
+ client.TotalError = msg.TotalError;
+ client.TotalWarning = msg.TotalWarning;
+
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/Service/Charger/Handler/OutCharger/PileUploadTelemetryHandler.cs b/Service/Charger/Handler/OutCharger/PileUploadTelemetryHandler.cs
new file mode 100644
index 0000000..5a85c0c
--- /dev/null
+++ b/Service/Charger/Handler/OutCharger/PileUploadTelemetryHandler.cs
@@ -0,0 +1,30 @@
+using DotNetty.Transport.Channels;
+using HybirdFrameworkCore.Autofac.Attribute;
+using log4net;
+using Service.Charger.Client;
+using Service.Charger.Msg.Charger.OutCharger.Req;
+
+namespace Service.Charger.Handler.OutCharger;
+
+///
+/// 3.7.12 充电桩遥测数据上报
+///
+[Order(8)]
+[Scope("InstancePerDependency")]
+public class PileUploadTelemetryHandler : SimpleChannelInboundHandler, IBaseHandler
+{
+ private static readonly ILog Log = LogManager.GetLogger(typeof(PileUploadTelemetryHandler));
+
+ protected override void ChannelRead0(IChannelHandlerContext ctx, PileUploadTelemetry msg)
+ {
+ if (ClientMgr.TryGetClient(ctx.Channel, out var sn, out var client))
+ {
+ Log.Info($"receive {msg} from {sn}");
+
+ client.PileUploadTelemetry[msg.Pn] = msg;
+
+ client.RealTimeChargePower = msg.HighVoltageAcquisitionCurrent * msg.HighVoltageAcquisitionVoltage;
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/Service/Charger/Msg/Charger/OutCharger/Req/PileChargeCompleteReq.cs b/Service/Charger/Msg/Charger/OutCharger/Req/PileChargeCompleteReq.cs
new file mode 100644
index 0000000..f0b80cb
--- /dev/null
+++ b/Service/Charger/Msg/Charger/OutCharger/Req/PileChargeCompleteReq.cs
@@ -0,0 +1,173 @@
+using HybirdFrameworkCore.Autofac.Attribute;
+
+namespace Service.Charger.Msg.Charger.OutCharger.Req;
+
+///
+/// 3.7.7 充电桩上送停止完成帧
+///
+public class PileChargeCompleteReq : ASDU
+{
+ ///
+ /// 记录类型
+ ///
+ [Property(0, 8)]
+ public byte RecordType { get; set; }
+
+ ///
+ /// 充电枪ID号
+ /// 0x01:充电枪1;0x02:充电枪2;0x03:双枪充电;(0x00&0xFF无效)
+ ///
+ [Property(8, 8)]
+ public byte Pn { get; set; }
+
+ ///
+ /// 成功标识
+ ///
+ [Property(16, 8)]
+ public byte Result { get; set; }
+
+
+ ///
+ /// 失败原因
+ ///
+ [Property(24, 8)]
+ public byte FailReason { get; set; }
+
+ ///
+ ///BMS 中止充电原因
+ ///
+ [Property(32, 8)]
+ public byte SuspendingChargingReason { get; set; }
+
+ ///
+ ///BMS 充电故障原因
+ ///
+ [Property(40, 16)]
+ public ushort CauseOfChargingFault { get; set; }
+
+ ///
+ ///BMS 中止错误原因
+ ///
+ [Property(56, 8)]
+ public byte AbortErrorReason { get; set; }
+
+ ///
+ ///中止荷电状态 SOC(%)
+ ///
+ [Property(64, 8, PropertyReadConstant.Bit, 1, 0)]
+ public float SuspendTheStateOfCharge { get; set; }
+
+ ///
+ ///动力蓄电池单体最低电压(V)
+ ///
+ [Property(72, 16, PropertyReadConstant.Bit, 0.01, 2)]
+ public float MinimumVoltageOfTractionBattery { get; set; }
+
+ ///
+ ///动力蓄电池单体最高电压(V)
+ ///
+ [Property(88, 16, PropertyReadConstant.Bit, 0.01, 2)]
+ public float MaximumVoltageOfTractionBattery { get; set; }
+
+ ///
+ ///动力蓄电池最低温度(ºC)
+ ///
+ [Property(104, 8, PropertyReadConstant.Bit, 1, 0, 50)]
+ public float MinimumTemperatureOfTractionBattery { get; set; }
+
+ ///
+ ///动力蓄电池最高温度(ºC)
+ ///
+ [Property(112, 8, PropertyReadConstant.Bit, 1, 0, 50)]
+ public byte MaximumTemperatureOfTractionBattery { get; set; }
+
+ ///
+ ///接收SPN2560=0x00 的充电机辨识报文超时
+ ///
+ [Property(120, 2)]
+ public byte XOOIdentificationMessageTimeout { get; set; }
+
+ ///
+ ///接收SPN2560=0xAA 的充电机辨识报文超时
+ ///
+ [Property(122, 2)]
+ public byte XAAIdentificationMessageTimeout { get; set; }
+
+ ///
+ ///接收充电机的时间同步和充电机最大输出能力报文超时
+ ///
+ [Property(124, 2)]
+ public byte TimeSyncAndMaxOutCapTimeout { get; set; }
+
+ ///
+ ///接收充电机完成充电准备报文超时
+ ///
+ [Property(126, 2)]
+ public byte ReceiveFinishPrepareChargeTimeout { get; set; }
+
+ ///
+ ///接收充电机充电状态报文超时
+ ///
+ [Property(128, 2)]
+ public byte TimeReceiveChargerStatusMessage { get; set; }
+
+ ///
+ ///接收充电机中止充电报文超时
+ ///
+ [Property(130, 2)]
+ public byte TimeReceiveChargingSuspensionMessage { get; set; }
+
+ ///
+ ///接收充电机充电统计报文超时
+ ///
+ [Property(132, 2)]
+ public byte TimeReceiveChargingStatisticsMessageOfCharger { get; set; }
+
+ ///
+ ///接收BMS和车辆的辨识报文超时
+ ///
+ [Property(134, 2)]
+ public byte TimeoutReceivingIdentificationMessageBms { get; set; }
+
+ ///
+ ///接收电池充电参数报文超时
+ ///
+ [Property(136, 2)]
+ public byte TimeoutReceivingBatteryChargingParameterMessage { get; set; }
+
+ ///
+ ///接收 BMS完成充电准备报文超时
+ ///
+ [Property(138, 2)]
+ public byte TimeoutReceivingBmsChargingPreparationMessage { get; set; }
+
+ ///
+ ///接收电池充电要求报文超时
+ ///
+ [Property(140, 2)]
+ public byte TimeoutReceivingBatteryChargingMessage { get; set; }
+
+ ///
+ ///接收电池充电总状态报文超时
+ ///
+ [Property(142, 2)]
+ public byte TimeoutReceivingBatteryChargingTotalStatusMessage { get; set; }
+
+ ///
+ ///接收BMS中止充电报文超时
+ ///
+ [Property(144, 2)]
+ public byte TimeoutReceivingBmsChargingSuspensionMessage { get; set; }
+
+ ///
+ ///接收BMS充电统计报文超时
+ ///
+ [Property(146, 2)]
+ public byte TimeoutReceivingBmsChargingStatisticsMessage { get; set; }
+
+ ///
+ ///充电机检测到的其他错误
+ ///
+ [Property(148, 6)]
+ public byte OtherErrorsDetectedByTheCharger { get; set; }
+}
\ No newline at end of file
diff --git a/Service/Charger/Msg/Charger/OutCharger/Req/PileStartChargeCompleteReq.cs b/Service/Charger/Msg/Charger/OutCharger/Req/PileStartChargeCompleteReq.cs
new file mode 100644
index 0000000..c85bf26
--- /dev/null
+++ b/Service/Charger/Msg/Charger/OutCharger/Req/PileStartChargeCompleteReq.cs
@@ -0,0 +1,137 @@
+using HybirdFrameworkCore.Autofac.Attribute;
+
+namespace Service.Charger.Msg.Charger.OutCharger.Req;
+
+///
+/// 3.7.5 充电桩上送充电启动完成帧
+///
+public class PileStartChargeCompleteReq : ASDU
+{
+ ///
+ /// 记录类型
+ ///
+ [Property(0, 8)]
+ public byte RecordType { get; set; }
+
+ ///
+ /// 充电枪ID号
+ /// 0x01:充电枪1;0x02:充电枪2;0x03:双枪充电;(0x00&0xFF无效)
+ ///
+ [Property(8, 8)]
+ public byte Pn { get; set; }
+
+ ///
+ /// 成功标识 0:成功;1:失败
+ ///
+ [Property(16, 8)]
+ public byte Result { get; set; }
+
+ ///
+ /// 失败原因
+ /// 默认 0
+ ///
+ [Property(24, 8)]
+ public byte FailReason { get; set; }
+
+ ///
+ /// BMS 与充电桩通信协议版本号
+ ///
+ [Property(32, 24)]
+ public string? ConnProtocolVersion0 { get; set; }
+
+ ///
+ /// 充电桩与BMS 握手结果
+ ///
+ [Property(56, 8)]
+ public byte HandshakeResult { get; set; }
+
+ ///
+ /// 电池类型
+ ///
+ [Property(64, 8)]
+ public byte BatteryType { get; set; }
+
+ ///
+ /// 最高允许温度
+ ///
+ [Property(72, 8, PropertyReadConstant.Bit, 1, 0, 50)]
+ public Int16 MaxAllowTemp { get; set; }
+
+ ///
+ /// BMS最高允许充电电压
+ ///
+ [Property(80, 16, PropertyReadConstant.Bit, 0.1, 1, 0)]
+ public float BmsMaxAllowVoltage { get; set; }
+
+ ///
+ /// 单体最高允许充电电压
+ ///
+ [Property(96, 16, PropertyReadConstant.Bit, 0.01, 2, 0)]
+ public float SingleMaxAllowVoltage { get; set; }
+
+ ///
+ /// 最高允许充电电流
+ ///
+ [Property(112, 16, PropertyReadConstant.Bit, 0.1, 1, 400)]
+ public float MaxAllowCurrent { get; set; }
+
+ ///
+ /// 整车动力蓄电池额定总电压
+ ///
+ [Property(128, 16, PropertyReadConstant.Bit, 0.1, 1, 0)]
+ public float VehiclePowerBatteryTotalVoltage { get; set; }
+
+ ///
+ /// 整车动力蓄电池当前电压
+ ///
+ [Property(144, 16, PropertyReadConstant.Bit, 0.1, 1, 0)]
+ public float VehiclePowerBatteryCurrentVoltage { get; set; }
+
+ ///
+ /// 整车动力蓄电池额定容量
+ ///
+ [Property(160, 16, PropertyReadConstant.Bit, 0.1, 1, 0)]
+ public float VehiclePowerBatteryRatedCapacity { get; set; }
+
+ ///
+ ///整车动力蓄电池标称容量
+ ///
+ [Property(176, 16, PropertyReadConstant.Bit, 0.1, 1, 0)]
+ public float VehiclePowerBatteryNormalCapacity { get; set; }
+
+ ///
+ ///充电机最高输出电压
+ ///
+ [Property(192, 16, PropertyReadConstant.Bit, 0.1, 1, 0)]
+ public float ChargerMaxOutputVoltage { get; set; }
+
+ ///
+ ///充电机最低输出电压
+ ///
+ [Property(208, 16, PropertyReadConstant.Bit, 0.1, 1, 0)]
+ public float ChargerMinOutputVoltage { get; set; }
+
+ ///
+ ///充电机最大输出电流
+ ///
+ [Property(224, 16, PropertyReadConstant.Bit, 0.1, 1, 400)]
+ public float ChargerMaxOutputCurrent { get; set; }
+
+ ///
+ ///充电机最小输出电流
+ ///
+ [Property(240, 16, PropertyReadConstant.Bit, 0.1, 1, 400)]
+ public float ChargerMinOutputCurrent { get; set; }
+
+ ///
+ /// VIN
+ ///
+ [Property(256, 136)]
+ public string Vin { get; set; }
+
+ ///
+ /// 整车动力蓄电 池荷电状态
+ ///
+ [Property(392, 8, PropertyReadConstant.Bit, 0.01, 2, 0)]
+ public byte ChargeState { get; set; }
+}
\ No newline at end of file
diff --git a/Service/Charger/Msg/Charger/OutCharger/Req/PileUploadRemoteSignal.cs b/Service/Charger/Msg/Charger/OutCharger/Req/PileUploadRemoteSignal.cs
new file mode 100644
index 0000000..6f7ffac
--- /dev/null
+++ b/Service/Charger/Msg/Charger/OutCharger/Req/PileUploadRemoteSignal.cs
@@ -0,0 +1,306 @@
+using HybirdFrameworkCore.Autofac.Attribute;
+
+namespace Service.Charger.Msg.Charger.OutCharger.Req;
+///
+/// 3.7.11 充电桩遥信数据上报
+///
+public class PileUploadRemoteSignal: ASDU
+{
+ ///
+ /// 记录类型
+ ///
+ [Property(0, 8)]
+ public byte RecordType { get; set; }
+
+ ///
+ /// 充电枪ID号
+ /// 0x01:充电枪1;0x02:充电枪2;0x03:双枪充电;(0x00&0xFF无效)
+ ///
+ [Property(8, 8)]
+ public byte Pn { get; set; }
+
+ ///
+ /// 工作状态 00H:待机、01H:工作、02H:工作完成、03H:充/放电暂停
+ ///
+ [Property(16, 2)]
+ public byte WorkStatus { get; set; }
+
+ ///
+ /// 总故障:0-正常、1-故障
+ ///
+ [Property(18, 1)]
+ public bool TotalError { get; set; }
+
+ ///
+ /// 总告警:0-正常、1-告警
+ ///
+ [Property(19, 1)]
+ public bool TotalWarning { get; set; }
+ ///
+ /// 急停按钮动作故障:0-正常、1-故障
+ ///
+ [Property(20, 1)]
+ public bool EmergencyStop { get; set; }
+
+ ///
+ /// 烟感故障:0-正常、1-故障
+ ///
+ [Property(21, 1)]
+ public bool SmokeFault { get; set; }
+
+ ///
+ /// 充电桩交流输入断路器故障(系统供电断路器):0-正常、1-故障
+ ///
+ [Property(22, 1)]
+ public bool ChargeACInputCircuitBreakerFault { get; set; }
+
+ ///
+ /// 直流母线正极输出 接触器拒动/误 动故障:0-正常、1-故障
+ ///
+ [Property(23, 1)]
+ public bool DcBusPositElecContactorRefuFault { get; set; }
+
+ ///
+ /// 直流母线负极输出 接触器拒动/误 动故障::0-正常、1-故障
+ ///
+ [Property(24, 1)]
+ public bool DcBusNegatElecContactorRefuFault { get; set; }
+
+ ///
+ /// 直流母线正级输出 熔断器故障
+ ///
+ [Property(25, 1)]
+ public bool DcBusPositElecFusesFault { get; set; }
+
+ ///
+ /// 直流母线负级输出 熔断器故障
+ ///
+ [Property(26, 1)]
+ public bool DDcBusNegatElecFusesFault { get; set; }
+
+ ///
+ /// 充电接口电磁锁故障
+ ///
+ [Property(27, 1)]
+ public bool ChargingInterfaceLockError { get; set; }
+
+
+ ///
+ /// 充电桩风扇故障
+ ///
+ [Property(28, 1)]
+ public bool ChargerFanError { get; set; }
+
+
+ ///
+ /// 避雷器故障
+ ///
+ [Property(29, 1)]
+ public bool ArresterError { get; set; }
+
+
+ ///
+ /// 绝缘监测告警
+ ///
+ [Property(30, 1)]
+ public bool InsulationDetectionAlarm { get; set; }
+
+ ///
+ /// 绝缘监测故障
+ ///
+ [Property(31, 1)]
+ public bool InsulationDetectionError { get; set; }
+
+
+ ///
+ /// 电池极性反接故障
+ ///
+ [Property(32, 1)]
+ public bool BatteryPolarityReverseError { get; set; }
+
+ ///
+ /// 充电中车辆控制导引故障
+ ///
+ [Property(33, 1)]
+ public bool VeConGuidanceFailure { get; set; }
+
+ ///
+ /// 充电桩过温故障
+ ///
+ [Property(34, 1)]
+ public bool ChargingOverTempError { get; set; }
+
+ ///
+ /// 充电接口过温故障
+ ///
+ [Property(35, 1)]
+ public bool InterfaceOverFaulty { get; set; }
+
+ ///
+ /// 充电枪未归位告警
+ ///
+ [Property(36, 1)]
+ public bool ChargingGunNotHomingError { get; set; }
+ ///
+ /// BMS通信故障
+ ///
+ [Property(37, 1)]
+ public bool BmsConnError { get; set; }
+
+ ///
+ /// 充电桩输入电压过压故障
+ ///
+ [Property(38, 1)]
+ public bool ChargerInputOverVoltageError { get; set; }
+
+ ///
+ /// 充电桩输入电压欠压故障
+ ///
+ [Property(39, 1)]
+ public bool ChargerInputUnderVoltageError { get; set; }
+
+ ///
+ /// 直流母线输出过压故障
+ ///
+ [Property(40, 1)]
+ public bool DcBusOutputOverVoltageError { get; set; }
+
+ ///
+ /// 直流母线输出欠压故障
+ ///
+ [Property(41, 1)]
+ public bool DcBusOutputUnderVoltageError { get; set; }
+
+ ///
+ /// 直流母线输出过流故障
+ ///
+ [Property(42, 1)]
+ public bool DcBusOutputOverCurrentError { get; set; }
+
+ ///
+ /// 车辆连接状态 0-未连接、1-已连接
+ ///
+ [Property(43, 1)]
+ public bool VehicleConnStatus { get; set; }
+
+ ///
+ /// 充电桩充电枪座状态 0-已连接、1-未连接
+ ///
+ [Property(44, 1)]
+ public bool ChargeStationGunHolderStatus { get; set; }
+
+ ///
+ /// 充电接口电子锁状态 0-解锁、1-锁止
+ ///
+ [Property(45, 1)]
+ public bool ChargingInterfaceLockStatus { get; set; }
+
+ ///
+ /// 正极直流输出接触器状态 0-分断、1-闭合
+ ///
+ [Property(46, 1)]
+ public bool PositiveDcTransmissionContactorStatus { get; set; }
+
+ ///
+ /// 负极直流输出接触器状态 0-分断 1-闭合
+ ///
+ [Property(47, 1)]
+ public bool NegativeDcTransmissionContactorStatus { get; set; }
+
+ ///
+ /// 门禁故障 0-正常 1-故障
+ ///
+ [Property(48, 1)]
+ public bool EntranceGuardError { get; set; }
+
+ ///
+ /// 正极直流输出接触器粘连故障
+ ///
+ [Property(49, 1)]
+ public bool PConA3dhesionFailure { get; set; }
+
+ ///
+ /// 负极直流输出接触器粘连故故障
+ ///
+ [Property(50, 1)]
+ public bool NConadhesionFailure { get; set; }
+
+ ///
+ /// 泄放回路故障
+ ///
+ [Property(51, 1)]
+ public bool ReliefCircuitError { get; set; }
+
+ ///
+ /// 充电桩交流输入接触器据动/误动故(预留位置供其他适用)
+ ///
+ [Property(52, 1)]
+ public bool ConActivated { get; set; }
+
+ ///
+ /// 充电桩交流输入接触器粘连故障(预留位置供其他适用)
+ ///
+ [Property(53, 1)]
+ public bool ConAdhesionFailure { get; set; }
+
+ ///
+ /// 辅助电源故障
+ ///
+ [Property(54, 1)]
+ public bool AuxiliaryPowerError { get; set; }
+
+ ///
+ /// 模块输出反接
+ ///
+ [Property(55, 1)]
+ public bool ModuleOutputReverseError { get; set; }
+
+ ///
+ /// 充电桩交流接触器状态 0-分断 1-吸合
+ ///
+ [Property(56, 1)]
+ public bool AcContactorStatus { get; set; }
+
+ ///
+ /// 充电枪过温告警 0-正常 1-故障
+ ///
+ [Property(57, 1)]
+ public bool ChargingGunOverTempWarning { get; set; }
+
+ ///
+ /// 充电桩过温告警 0-正常 1-故障
+ ///
+ [Property(58, 1)]
+ public bool ChargerOverTempWarning { get; set; }
+
+ ///
+ /// 电表通信异常 0-正常 01H-故障
+ ///
+ [Property(59, 1)]
+ public bool MeterConnError { get; set; }
+
+ ///
+ /// 电表电度异常 0-正常 02H-故障
+ ///
+ [Property(60, 1)]
+ public bool MeterDataError { get; set; }
+
+ ///
+ /// 水浸告警 0-正常 03H-故障
+ ///
+ [Property(61, 1)]
+ public bool WaterloggingWarning { get; set; }
+
+ ///
+ /// 电池包辅助电源状态 0:辅助电源未给电池包供电、1:辅助电源正在给电池包供电
+ ///
+ [Property(62, 1)]
+ public bool BatteryPackAuxiliaryPowerStatus { get; set; }
+
+ ///
+ /// 逆功率报警 00H:正常 01H:故障
+ /// 发生该故障后一直保持,只到重合闸完成后,可以重合闸信号变为可用状态时才清零。
+ ///
+ [Property(63, 1)]
+ public bool ReversePowerWarning { get; set; }
+}
\ No newline at end of file
diff --git a/Service/Charger/Msg/Charger/OutCharger/Req/PileUploadTelemetry.cs b/Service/Charger/Msg/Charger/OutCharger/Req/PileUploadTelemetry.cs
new file mode 100644
index 0000000..15e7a08
--- /dev/null
+++ b/Service/Charger/Msg/Charger/OutCharger/Req/PileUploadTelemetry.cs
@@ -0,0 +1,205 @@
+using HybirdFrameworkCore.Autofac.Attribute;
+
+namespace Service.Charger.Msg.Charger.OutCharger.Req;
+
+///
+/// 3.7.12 充电桩遥测数据上报
+///
+public class PileUploadTelemetry : ASDU
+{
+ ///
+ /// 记录类型
+ ///
+ [Property(0, 8)]
+ public byte RecordType { get; set; }
+
+ ///
+ /// 充电枪ID号
+ /// 0x01:充电枪1;0x02:充电枪2;0x03:双枪充电;(0x00&0xFF无效)
+ ///
+ [Property(8, 8)]
+ public byte Pn { get; set; }
+
+ ///
+ /// 当前 SOC
+ ///
+ [Property(16, 8)]
+ public byte CurrentSoc { get; set; }
+
+ ///
+ /// 最高蓄电池温度
+ ///
+ [Property(24, 16, offset: 50)]
+ public Int16 MaxBatteryTemp { get; set; }
+
+ ///
+ /// 最高温度检测点编号
+ ///
+ [Property(40, 16)]
+ public ushort MaxTempDetectionPointNo { get; set; }
+
+ ///
+ /// 最低蓄电池温度数据分辨率:1ºC/位,-50 ºC 偏移量;数据范围:-50ºC ~+200ºC;
+ ///
+ [Property(56, 16, PropertyReadConstant.Bit, 1, 0, 50)]
+ public Int16 MinBatteryTemp { get; set; }
+
+ ///
+ /// 最低温度检测点编号
+ ///
+ [Property(72, 16)]
+ public ushort MinTempDetectionPointNo { get; set; }
+
+ ///
+ /// 单体电池最高电压
+ ///
+ [Property(88, 16, PropertyReadConstant.Bit, 0.01, 2, 0)]
+ public float SingleBatteryMaxVoltage { get; set; }
+
+ ///
+ /// 单体电池最低电压
+ ///
+ [Property(104, 16, PropertyReadConstant.Bit, 0.01, 2, 0)]
+ public float SingleBatteryMinVoltage { get; set; }
+
+ ///
+ /// 充电机环境温度
+ ///
+ [Property(120, 8, PropertyReadConstant.Bit, 1, 0, 50)]
+ public Int16 ChargerEnvTemp { get; set; }
+
+ ///
+ /// 充电导引电压
+ ///
+ [Property(128, 16, PropertyReadConstant.Bit, 0.01, 2, 0)]
+ public float ChargingPilotVoltage { get; set; }
+
+ ///
+ /// BMS 需求电压
+ ///
+ [Property(144, 16, PropertyReadConstant.Bit, 0.1, 1, 0)]
+ public float BmsNeedVoltage { get; set; }
+
+ ///
+ /// BMS 需求电流
+ ///
+ [Property(160, 16, PropertyReadConstant.Bit, 0.1, 1, 400)]
+ public float BmsNeedCurrent { get; set; }
+
+ ///
+ /// 充电模式 01H:恒压充电、02H恒流充电
+ ///
+ [Property(176, 8)]
+ public byte ChargeMode { get; set; }
+
+
+ ///
+ /// BMS 充电电压测量值
+ ///
+ [Property(184, 16, PropertyReadConstant.Bit, 0.1, 1, 0)]
+ public float BmsChargingVoltage { get; set; }
+
+
+ ///
+ /// BMS 充电电流测量值
+ ///
+ [Property(200, 16, PropertyReadConstant.Bit, 0.1, 1, 400)]
+ public float BmsChargingCurrent { get; set; }
+
+ ///
+ /// 估算剩余充电时间
+ ///
+ [Property(216, 16, PropertyReadConstant.Bit, 1, 0, 0)]
+ public ushort EstimatedRemainingTime { get; set; }
+
+ ///
+ /// 充电接口温度探头 1
+ ///
+ [Property(232, 8, PropertyReadConstant.Bit, 1, 0, 50)]
+ public Int16 ChargingInterfaceDetectionOneTemp { get; set; }
+
+ ///
+ /// 充电接口温度探头 2
+ ///
+ [Property(240, 8, PropertyReadConstant.Bit, 1, 0, 50)]
+ public Int16 ChargingInterfaceDetectionTwoTemp { get; set; }
+
+ ///
+ /// 充电接口温度探头 4
+ ///
+ [Property(248, 8, PropertyReadConstant.Bit, 1, 0, 50)]
+ public Int16 ChargingInterfaceDetectionFourTemp { get; set; }
+
+ ///
+ /// 直流电表当前电量
+ ///
+ [Property(256, 32, PropertyReadConstant.Bit, 0.01, 2)]
+ public float DcMeterCurrentPower { get; set; }
+
+ ///
+ /// 充电电压(直流电表电压)
+ ///
+ [Property(288, 16, PropertyReadConstant.Bit, 0.1, 1)]
+ public float DcMeterVoltage { get; set; }
+
+ ///
+ /// 充电电流(直流电表电流)
+ ///
+ [Property(304, 16, PropertyReadConstant.Bit, 0.1, 1)]
+ public float DcMeterCurrent { get; set; }
+
+ ///
+ /// 高压采集电压
+ ///
+ [Property(320, 16, PropertyReadConstant.Bit, 0.1, 1)]
+ public float HighVoltageAcquisitionVoltage { get; set; }
+
+ ///
+ /// 高压采集电流
+ ///
+ [Property(336, 16, PropertyReadConstant.Bit, 0.1, 1)]
+ public float HighVoltageAcquisitionCurrent { get; set; }
+
+ ///
+ /// 桩内部温度
+ ///
+ [Property(352, 8, PropertyReadConstant.Bit, 1, 0)]
+ public byte ChargerInsideTemp { get; set; }
+
+ ///
+ /// 本次充电时间
+ ///
+ [Property(360, 16)]
+ public ushort ChargingTime { get; set; }
+
+ ///
+ /// 模块进风口温度
+ ///
+ [Property(376, 8)]
+ public byte ModuleOneAirInletTemp { get; set; }
+
+ ///
+ /// 模块出风口温度
+ ///
+ [Property(384, 8)]
+ public byte ModuleTwoAirInletTemp { get; set; }
+
+ ///
+ /// 充电模式 0:站内充电 1:站外充电
+ ///
+ [Property(392, 8)]
+ public byte ChargeModel { get; set; }
+
+ ///
+ /// 充电启动方式 1:站控启动 2:本地充电
+ ///
+ [Property(400, 8)]
+ public byte ChargingStartMethod { get; set; }
+
+
+ ///
+ /// 交流电表当前电量
+ ///
+ [Property(408, 32, PropertyReadConstant.Bit, 0.01, 2)]
+ public float ACMeterCurrentBatteryValue { get; set; }
+}
\ No newline at end of file
diff --git a/Service/Charger/Msg/Charger/OutCharger/Resp/PileStartChargeRes.cs b/Service/Charger/Msg/Charger/OutCharger/Resp/PileStartChargeRes.cs
new file mode 100644
index 0000000..105a42b
--- /dev/null
+++ b/Service/Charger/Msg/Charger/OutCharger/Resp/PileStartChargeRes.cs
@@ -0,0 +1,36 @@
+using HybirdFrameworkCore.Autofac.Attribute;
+
+namespace Service.Charger.Msg.Charger.OutCharger.Resp;
+
+///
+/// 3.7.2 充电桩响应远程启动充电
+///
+public class PileStartChargeRes : ASDU
+{
+ ///
+ /// 记录类型
+ ///
+ [Property(0, 8)]
+ public byte RecordType { get; set; }
+
+ ///
+ /// 充电枪ID
+ /// 0x01:充电枪1;0x02:充电枪2;0x03:双枪充电;(0x00&0xFF无效)
+ ///
+ [Property(8, 8)]
+ public byte Pn { get; set; }
+
+ ///
+ /// 启动结果
+ /// 0 成功 1 失败
+ ///
+ [Property(16, 8)]
+ public byte Result { get; set; }
+
+ ///
+ /// 失败原因
+ /// 默认 0
+ ///
+ [Property(24, 8)]
+ public byte FailReason { get; set; }
+}
\ No newline at end of file
diff --git a/Service/Charger/Msg/Charger/OutCharger/Resp/PileStopChargeRes.cs b/Service/Charger/Msg/Charger/OutCharger/Resp/PileStopChargeRes.cs
new file mode 100644
index 0000000..c0081e5
--- /dev/null
+++ b/Service/Charger/Msg/Charger/OutCharger/Resp/PileStopChargeRes.cs
@@ -0,0 +1,28 @@
+using HybirdFrameworkCore.Autofac.Attribute;
+
+namespace Service.Charger.Msg.Charger.OutCharger.Resp;
+
+///
+/// 3.7.4 充电桩响应远程停止充电
+///
+public class PileStopChargeRes : ASDU
+{
+ ///
+ /// 记录类型
+ ///
+ [Property(0, 8)]
+ public byte RecordType { get; set; }
+
+ ///
+ /// 充电枪ID
+ /// 0x01:充电枪1;0x02:充电枪2;0x03:双枪充电;(0x00&0xFF无效)
+ ///
+ [Property(8, 8)]
+ public byte pn { get; set; }
+
+ ///
+ /// 启动结果 0 成功 1 设备已停机 0xFF 其他
+ ///
+ [Property(16, 8)]
+ public byte rs { get; set; }
+}
\ No newline at end of file
diff --git a/Service/Charger/Msg/Host/Req/OutCharger/Req/PileStartCharge.cs b/Service/Charger/Msg/Host/Req/OutCharger/Req/PileStartCharge.cs
new file mode 100644
index 0000000..4bd0419
--- /dev/null
+++ b/Service/Charger/Msg/Host/Req/OutCharger/Req/PileStartCharge.cs
@@ -0,0 +1,65 @@
+using HybirdFrameworkCore.Autofac.Attribute;
+
+namespace Service.Charger.Msg.Host.OutCharger.Req;
+
+///
+/// 3.7.1 监控平台远程启动充电桩充电
+///
+public class PileStartCharge : ASDU
+{
+ ///
+ /// 记录类型
+ ///
+ [Property(0, 8)]
+ public byte RecordType { get; set; }
+
+ ///
+ /// 充电枪ID号
+ /// 0x01:充电枪1;0x02:充电枪2;0x03:双枪充电;(0x00&0xFF无效)
+ ///
+ [Property(8, 8)]
+ public byte Pn { get; set; }
+
+ ///
+ /// SOC 限制
+ /// 百分比
+ ///
+ [Property(16, 8)]
+ public byte SocValue { get; set; }
+
+ ///
+ /// 功率调节指令类型
+ /// 默认 1 绝对功率值
+ ///
+ [Property(24, 8)]
+ public byte ChangePowerCmdType { get; set; } = 1;
+
+ ///
+ /// 功率调节参数
+ /// 0. 1kwh/位
+ ///
+ [Property(32, 16, PropertyReadConstant.Bit, 0.1, 1)]
+ public short ChangePower { get; set; }
+
+ ///
+ /// 充电流水号
+ ///
+ [Property(48, 256)]
+ public string ChargeOrderNo { get; set; }
+
+ public PileStartCharge(byte pn, byte socValue, byte changePowerCmdType, short changePower, string chargeOrderNo)
+ {
+ RecordType = 1;
+ FrameTypeNo = 51;
+ MsgBodyCount = 1;
+ TransReason = 3;
+ PublicAddr = 0;
+ MsgBodyAddr = new byte[] { 0, 0, 0 };
+
+ Pn = pn;
+ SocValue = socValue;
+ ChangePowerCmdType = changePowerCmdType;
+ ChangePower = changePower;
+ ChargeOrderNo = chargeOrderNo;
+ }
+}
\ No newline at end of file
diff --git a/Service/Charger/Msg/Host/Req/OutCharger/Req/PileStopCharge.cs b/Service/Charger/Msg/Host/Req/OutCharger/Req/PileStopCharge.cs
new file mode 100644
index 0000000..ee47f01
--- /dev/null
+++ b/Service/Charger/Msg/Host/Req/OutCharger/Req/PileStopCharge.cs
@@ -0,0 +1,42 @@
+using HybirdFrameworkCore.Autofac.Attribute;
+
+namespace Service.Charger.Msg.Host.OutCharger.Req;
+
+///
+/// 3.7.3 监控平台远程停止充电桩充电
+///
+public class PileStopCharge : ASDU
+{
+ ///
+ /// 记录类型
+ ///
+ [Property(0, 8)]
+ public byte RecordType { get; set; }
+
+ ///
+ /// 充电枪ID号
+ /// 0x01:充电枪1;0x02:充电枪2;0x03:双枪充电;(0x00&0xFF无效)
+ ///
+ [Property(8, 8)]
+ public byte Pn { get; set; }
+
+ ///
+ /// 停止原因
+ ///
+ [Property(16, 8)]
+ public byte StopReason { get; set; }
+
+ public PileStopCharge(byte pn,byte stopReason)
+ {
+ RecordType = 3;
+ FrameTypeNo = 51;
+ MsgBodyCount = 1;
+ TransReason = 3;
+ PublicAddr = 0;
+ MsgBodyAddr = new byte[] { 0, 0, 0 };
+
+ Pn = pn;
+
+ StopReason = stopReason;
+ }
+}
\ No newline at end of file
diff --git a/Service/Charger/Msg/Host/Resp/OutCharger/PileChargeCompleteRes.cs b/Service/Charger/Msg/Host/Resp/OutCharger/PileChargeCompleteRes.cs
new file mode 100644
index 0000000..dabc430
--- /dev/null
+++ b/Service/Charger/Msg/Host/Resp/OutCharger/PileChargeCompleteRes.cs
@@ -0,0 +1,40 @@
+using HybirdFrameworkCore.Autofac.Attribute;
+
+namespace Service.Charger.Msg.Host.Resp.OutCharger;
+///
+/// 3.7.8 监控平台应答充电桩停止完成帧
+///
+public class PileChargeCompleteRes : ASDU
+{
+ ///
+ /// 记录类型
+ ///
+ [Property(0, 8)]
+ public byte RecordType { get; set; }
+
+ ///
+ /// 充电枪ID号
+ /// 0x01:充电枪1;0x02:充电枪2;0x03:双枪充电;(0x00&0xFF无效)
+ ///
+ [Property(8, 8)]
+ public byte Pn { get; set; }
+
+ ///
+ /// 成功标识 0:成功;1:失败
+ ///
+ [Property(16, 8)]
+ public byte Result { get; set; }
+
+ public PileChargeCompleteRes(byte pn,byte result)
+ {
+ RecordType = 8;
+ FrameTypeNo = 51;
+ MsgBodyCount = 1;
+ TransReason = 4;
+ PublicAddr = 0;
+ MsgBodyAddr = new byte[] { 0, 0, 0 };
+
+ Pn = pn;
+ Result = result;
+ }
+}
\ No newline at end of file
diff --git a/Service/Charger/Msg/Host/Resp/OutCharger/PileStartChargeCompleteRes.cs b/Service/Charger/Msg/Host/Resp/OutCharger/PileStartChargeCompleteRes.cs
new file mode 100644
index 0000000..551c7ec
--- /dev/null
+++ b/Service/Charger/Msg/Host/Resp/OutCharger/PileStartChargeCompleteRes.cs
@@ -0,0 +1,49 @@
+using HybirdFrameworkCore.Autofac.Attribute;
+
+namespace Service.Charger.Msg.Host.Resp.OutCharger;
+
+///
+/// 3.7.6 监控平台应答充电桩启动完成帧
+///
+public class PileStartChargeCompleteRes : ASDU
+{
+ ///
+ /// 记录类型
+ ///
+ [Property(0, 8)]
+ public byte RecordType { get; set; }
+
+ ///
+ /// 充电枪ID号
+ /// 0x01:充电枪1;0x02:充电枪2;0x03:双枪充电;(0x00&0xFF无效)
+ ///
+ [Property(8, 8)]
+ public byte Pn { get; set; }
+
+ ///
+ /// 成功标识 0:成功;1:失败
+ ///
+ [Property(16, 8)]
+ public byte Result { get; set; }
+
+ ///
+ /// 失败原因
+ /// 0:成功1:交易流水号数据异常2:充电方式数据异常3:其他数据异常4:服务器异常5:服务器繁忙6:枪编号非法7 :服务器判定启动完成帧超时0xFF:其他错误
+ ///
+ [Property(24, 8)]
+ public byte FailReason { get; set; }
+
+ public PileStartChargeCompleteRes(byte pn,byte result,byte failReason)
+ {
+ RecordType = 6;
+ FrameTypeNo = 51;
+ MsgBodyCount = 1;
+ TransReason = 4;
+ PublicAddr = 0;
+ MsgBodyAddr = new byte[] { 0, 0, 0 };
+
+ Pn = pn;
+ Result = result;
+ FailReason = failReason;
+ }
+}
\ No newline at end of file
diff --git a/Service/Charger/Msg/Http/Req/PileStartChargeHttpReq.cs b/Service/Charger/Msg/Http/Req/PileStartChargeHttpReq.cs
new file mode 100644
index 0000000..e6820a1
--- /dev/null
+++ b/Service/Charger/Msg/Http/Req/PileStartChargeHttpReq.cs
@@ -0,0 +1,44 @@
+namespace Service.Charger.Msg.Http.Req;
+///
+/// 9.2.1.1 云平台下发开始充电操作
+///
+public class PileStartChargeHttpReq
+{
+ ///
+ /// 换电站编码
+ /// 换电站唯一码
+ ///
+ public string? sn { get; set; }
+
+ ///
+ /// 充电订单号
+ /// 云平台下发的充电订单编号,;当启动模式为本地主动启动(即插即充)时,该 值以 0 填充
+ ///
+ public string? con { get; set; }
+
+ ///
+ /// 充电枪编号
+ /// 充电枪的唯一标识码
+ ///
+ public string pn { get; set; }
+
+ ///
+ /// 充电方式
+ /// 0:自动(充满为止);1:按电量;
+ ///
+ public int? ct { get; set; }
+
+ ///
+ /// 充电参数
+ /// 按充电方式判断,除0外 电量:单位 kWh,精确到 0.01 时间:单位 min,精确到 0.01 金额:单位 元,精确到 0.01
+ ///
+ public string? cp { get; set; }
+
+ ///
+ /// 启动类型
+ /// 0:运营平台启动;1:APP 启动;2: 本地启动
+ ///
+ public int? st { get; set; }
+
+
+}
\ No newline at end of file
diff --git a/Service/Charger/Msg/Http/Req/PileStopChargeHttpReq.cs b/Service/Charger/Msg/Http/Req/PileStopChargeHttpReq.cs
new file mode 100644
index 0000000..138d8e7
--- /dev/null
+++ b/Service/Charger/Msg/Http/Req/PileStopChargeHttpReq.cs
@@ -0,0 +1,20 @@
+namespace Service.Charger.Msg.Http.Req;
+
+///
+/// 9.2.17 云端下发充电枪停止充电
+///
+public class PileStopChargeHttpReq
+{
+ ///
+ /// 换电站编码
+ /// 换电站唯一码
+ ///
+ public string sn { get; set; }
+
+
+ ///
+ /// 充电枪编号
+ /// 充电枪的唯一标识码
+ ///
+ public string pn { get; set; }
+}
\ No newline at end of file
diff --git a/Service/Charger/Msg/Http/Resp/PileStartChargeHttpRes.cs b/Service/Charger/Msg/Http/Resp/PileStartChargeHttpRes.cs
new file mode 100644
index 0000000..0b8c532
--- /dev/null
+++ b/Service/Charger/Msg/Http/Resp/PileStartChargeHttpRes.cs
@@ -0,0 +1,27 @@
+namespace Service.Charger.Msg.Http.Resp;
+
+///
+/// 9.2.1.2 站控应答开始充电操作
+///
+public class PileStartChargeHttpRes
+{
+ ///
+ /// 执行结果
+ ///
+ public string? rs { get; set; }
+
+ ///
+ /// 充电订单号
+ ///
+ public string? con { get; set; }
+
+ ///
+ /// 充电枪编号
+ ///
+ public string? pn { get; set; }
+
+ ///
+ /// 故障码
+ ///
+ public string? ec { get; set; }
+}
\ No newline at end of file
diff --git a/Service/Charger/Msg/Http/Resp/PileStopChargeHttpRes.cs b/Service/Charger/Msg/Http/Resp/PileStopChargeHttpRes.cs
new file mode 100644
index 0000000..2ae6728
--- /dev/null
+++ b/Service/Charger/Msg/Http/Resp/PileStopChargeHttpRes.cs
@@ -0,0 +1,17 @@
+namespace Service.Charger.Msg.Http.Resp;
+///
+/// 9.2.1.8 站控响应充电枪停止充电操作
+///
+public class PileStopChargeHttpRes
+{
+ ///
+ /// 充电枪ID
+ /// 0x01:充电枪1;0x02:充电枪2;0x03:双枪充电;(0x00&0xFF无效)
+ ///
+ public string pn { get; set; }
+
+ ///
+ /// 启动结果 0 成功 1 设备已停机 0xFF 其他
+ ///
+ public string rs { get; set; }
+}
\ No newline at end of file
diff --git a/WebStarter/Controllers/OutChargerController.cs b/WebStarter/Controllers/OutChargerController.cs
new file mode 100644
index 0000000..6ea61d9
--- /dev/null
+++ b/WebStarter/Controllers/OutChargerController.cs
@@ -0,0 +1,84 @@
+using HybirdFrameworkCore.Entity;
+using Microsoft.AspNetCore.Mvc;
+using Repository.Station;
+using Service.Charger.Client;
+using Service.Charger.Common;
+using Service.Charger.Msg.Http.Req;
+using Service.Init;
+
+namespace WebStarter.Controllers;
+
+///
+/// 站外充电机管理
+///
+[Produces("application/json")]
+[ApiController]
+[Route("api/[controller]")]
+public class OutChargerController
+{
+ private ChargeOrderRepository _chargeOrderRepository;
+
+ public OutChargerController(ChargeOrderRepository chargeOrderRepository)
+ {
+ _chargeOrderRepository = chargeOrderRepository;
+ }
+
+ ///
+ /// 云平台下发开始充电操作
+ ///
+ ///
+ ///
+ [HttpPost]
+ [Route("SendStartOutCharger")]
+ public Result SendStartOutCharger([FromBody] PileStartChargeHttpReq httpReq)
+ {
+ string chargerCode = ChargerUtils.GetOutChargerCode(httpReq.pn);
+
+ byte chargerGunCode = ChargerUtils.GetTheGun(httpReq.pn);
+
+ ChargerClient? chargerClient = ClientMgr.GetBySn(chargerCode);
+
+ if (chargerClient == null)
+ {
+ return Result.Fail("充电机未连接");
+ }
+
+ if (string.IsNullOrWhiteSpace(httpReq.con))
+ {
+ httpReq.con = ChargerUtils.GenChargeOrderSn();
+ }
+
+ byte chargeSoc = StaticStationInfo.ChargeSoc;
+ // 下发充电枪充电
+ chargerClient.SendStartOutCharger(chargerGunCode, chargeSoc, 360, 1, httpReq.con);
+ // 初始化订单
+ _chargeOrderRepository.SaveChargeGunOrder(httpReq.con, chargerCode, httpReq.pn, chargerGunCode.ToString());
+
+ return Result.Success(true);
+ }
+ ///
+ /// 云端下发充电枪停止充电
+ ///
+ ///
+ ///
+ [HttpPost]
+ [Route("SendStopOutCharger")]
+ public Result SendStopOutCharger([FromBody] PileStopChargeHttpReq httpReq)
+ {
+ string chargerCode = ChargerUtils.GetOutChargerCode(httpReq.pn);
+
+ byte chargerGunCode = ChargerUtils.GetTheGun(httpReq.pn);
+
+ ChargerClient? chargerClient = ClientMgr.GetBySn(chargerCode);
+
+ if (chargerClient == null)
+ {
+ return Result.Fail("充电机未连接");
+ }
+
+ // 下发充电枪停止充电
+ chargerClient.SendStopOutCharger(chargerGunCode, 0);
+
+ return Result.Success(true);
+ }
+}
\ No newline at end of file