diff --git a/Common/Util/DateUtils.cs b/Common/Util/DateUtils.cs index 8b574b3..2681935 100644 --- a/Common/Util/DateUtils.cs +++ b/Common/Util/DateUtils.cs @@ -22,4 +22,16 @@ public class DateUtils DateTime tomorrow = today.Date.AddDays(1); // 获取明天的日期 return new DateTime(tomorrow.Year, tomorrow.Month, tomorrow.Day, 0, 0, 0); // 获取明天的0点 } + + /// + /// 组装只包含时分部分的时间 + /// + /// + /// + /// + public static string GetFormattedTime(byte hour, byte minute) + { + DateTime time = new DateTime(1, 1, 1, hour, minute, 0); + return time.ToString("HH:mm"); + } } \ No newline at end of file diff --git a/Entity/DbModel/Station/BinInfo.cs b/Entity/DbModel/Station/BinInfo.cs index ba0bcf9..e1f3cf5 100644 --- a/Entity/DbModel/Station/BinInfo.cs +++ b/Entity/DbModel/Station/BinInfo.cs @@ -97,6 +97,14 @@ namespace Entity.DbModel.Station /// [SugarColumn(ColumnName = "elec_plugin_status")] public string ElecPluginStatus { get; set; } + + /// + /// Desc:最后结束充电时间 结束充电后更新 + /// Default: + /// Nullable:True + /// + [SugarColumn(ColumnName = "last_charge_finish_time")] + public DateTime? LastChargeFinishTime { get; set; } /// /// Desc:是否有水插头;0-无水插头;1-有水插头 diff --git a/Repository/Station/ChargeOrderRepository.cs b/Repository/Station/ChargeOrderRepository.cs index e178142..241b4b8 100644 --- a/Repository/Station/ChargeOrderRepository.cs +++ b/Repository/Station/ChargeOrderRepository.cs @@ -11,11 +11,11 @@ public class ChargeOrderRepository : BaseRepository { } - public void SaveChargeGunOrder(string chargeOrderNo, string chargerNo, string chargerGunNo, + public void SaveChargeGunOrder(string chargeOrder,string chargeOrderNo, string chargerNo, string chargerGunNo, string outChargerGunNo) { ChargeOrder order = new ChargeOrder(); - order.Sn = chargeOrderNo; + order.Sn = chargeOrder; order.CmdStatus = 0; order.ChargerNo = chargerNo; order.ChargerGunNo = chargerGunNo; @@ -24,7 +24,7 @@ public class ChargeOrderRepository : BaseRepository order.StartMode = 1; order.CloudChargeOrder = chargeOrderNo; order.CreatedTime = DateTime.Now; - ChargeOrder chargeOrder = Insert(order); + Insert(order); } public ChargeOrder? GetLatestChargeGunOrder(string pn, string chargerCode) diff --git a/Service/Charger/Client/ChargerClient.cs b/Service/Charger/Client/ChargerClient.cs index c35c30d..4b0d09c 100644 --- a/Service/Charger/Client/ChargerClient.cs +++ b/Service/Charger/Client/ChargerClient.cs @@ -61,6 +61,14 @@ public class ChargerClient : TcpClient [1] = false, [2] = false }; + /// + /// 充电桩连接状态 + /// + public ConcurrentDictionary ChargedPile = new ConcurrentDictionary + { + [1] = false, + [2] = false + }; public bool IsStopped { get; set; } = false; public bool IsCanSendStopCmd { get; set; } = true; @@ -145,14 +153,28 @@ public class ChargerClient : TcpClient public UploadTelemetryData UploadTelemetryData = new UploadTelemetryData(); /// - /// 两个充电桩的遥测 + /// 充放电机上传单体动力蓄电池电压极值统计 + /// + public VoltageExtremumStatistics? VoltageExtremumStatistics = new VoltageExtremumStatistics(); + + /// + /// 充电桩的遥测 /// public ConcurrentDictionary PileUploadTelemetry = new(); /// + /// 充电桩的遥信 + /// + public ConcurrentDictionary PileUploadRemoteSignal = new(); + + /// + /// 充电桩状态信息 + /// + public ConcurrentDictionary ChargerPile = new(); + /// ///充电机实时充电功率 /// public float RealTimeChargePower { get; set; } = 0; - + /// /// 心跳-桩状态 /// diff --git a/Service/Charger/Client/ChargerPile.cs b/Service/Charger/Client/ChargerPile.cs new file mode 100644 index 0000000..240b1a6 --- /dev/null +++ b/Service/Charger/Client/ChargerPile.cs @@ -0,0 +1,50 @@ +namespace Service.Charger.Client; + +/// +/// 充电桩状态信息 +/// +public class ChargerPile +{ + /// + /// 充电方式 + /// 0:自动(充满为止);1:按电量;2:按时间;3:按金额; + /// + 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; } + + /// + /// 车辆vin + /// + public string Vin { get; set; } + + + /// + /// 充电订单号 + /// 云平台下发的充电订单编号,;当启动模式为本地主动启动(即插即充)时,该 值以 0 填充 + /// + public string con { get; set; } + + /// + /// 充电流水号 + /// + public string cosn { get; set; } + + /// + /// 充电枪编号 + /// 充电枪的唯一标识码 + /// + public string? pn { get; set; } + +} \ No newline at end of file diff --git a/Service/Charger/Codec/Decoder.cs b/Service/Charger/Codec/Decoder.cs index 7a53b68..869098d 100644 --- a/Service/Charger/Codec/Decoder.cs +++ b/Service/Charger/Codec/Decoder.cs @@ -249,6 +249,7 @@ public class Decoder : ByteToMessageDecoder 7 => ModelConvert.Decode(bytes), 12 => ModelConvert.Decode(bytes), 11 => ModelConvert.Decode(bytes), + 14 => ModelConvert.Decode(bytes), _ => throw new InvalidOperationException("This should never be reached"), }, #endregion diff --git a/Service/Charger/Common/ChargerUtils.cs b/Service/Charger/Common/ChargerUtils.cs index 07389d4..e0ca4b3 100644 --- a/Service/Charger/Common/ChargerUtils.cs +++ b/Service/Charger/Common/ChargerUtils.cs @@ -101,6 +101,46 @@ public static class ChargerUtils } } + + + /// + /// 根据本地充电枪编号,充电机code,计算云平台下发充电枪编号 + /// + /// + /// + /// + /// + public static string ReverseCalculateChargerGun(byte chargerGun, string chargerCode) + { + int number; + + if (int.TryParse(chargerCode, out number)) + { + int originalChargerGun; + + if (chargerGun == 2) + { + // 站外2枪 + originalChargerGun = number * 2; + } + else if (chargerGun == 1) + { + // 站外1枪 + originalChargerGun = number * 2 - 1; + } + else + { + throw new ArgumentException("无效的chargerGun"); + } + + return originalChargerGun.ToString(); + } + else + { + throw new ArgumentException("转换失败"); + } + } + /// /// 计算Byte随机数值 /// diff --git a/Service/Charger/Handler/FinishStopChargingHandler.cs b/Service/Charger/Handler/FinishStopChargingHandler.cs index 65a189e..e578840 100644 --- a/Service/Charger/Handler/FinishStopChargingHandler.cs +++ b/Service/Charger/Handler/FinishStopChargingHandler.cs @@ -60,8 +60,16 @@ namespace Service.Charger.Handler client.IsStopped = false; } - int update = BinInfoRepository.Update(t => t.ChargeStatus == chargeStatus, t => t.No == client.BinNo); - Log.Info($"update {update} start charge finish status {chargeStatus} for {client.BinNo}"); + + + BinInfo? binInfo = BinInfoRepository.QueryByBinNo(client.BinNo); + if (binInfo!=null) + { + binInfo.ChargeStatus = chargeStatus; + binInfo.LastChargeFinishTime = DateTime.Now; + bool update = BinInfoRepository.Update(binInfo); + Log.Info($"update {update} start charge finish status {chargeStatus} for {client.BinNo}"); + } ChargingStopFsdRes stopFsdRes = new ChargingStopFsdRes(0); diff --git a/Service/Charger/Handler/OutCharger/PileChargeCompleteHandler.cs b/Service/Charger/Handler/OutCharger/PileChargeCompleteHandler.cs index ff89574..5e72ddf 100644 --- a/Service/Charger/Handler/OutCharger/PileChargeCompleteHandler.cs +++ b/Service/Charger/Handler/OutCharger/PileChargeCompleteHandler.cs @@ -33,9 +33,7 @@ public class PileChargeCompleteHandler : SimpleChannelInboundHandler +/// 3.7.5 充电桩上送充电启动完成帧 +/// [Order(8)] [Scope("InstancePerDependency")] public class PileStartChargeCompleteHandler : SimpleChannelInboundHandler, IBaseHandler @@ -27,6 +29,7 @@ public class PileStartChargeCompleteHandler : SimpleChannelInboundHandler +/// 3.7.2 充电桩响应远程启动充电 +/// [Order(8)] [Scope("InstancePerDependency")] public class PileStartChargeResHandler : SimpleChannelInboundHandler, IBaseHandler diff --git a/Service/Charger/Handler/OutCharger/PileStopChargeResHandler.cs b/Service/Charger/Handler/OutCharger/PileStopChargeResHandler.cs index a6f0664..3ce17bb 100644 --- a/Service/Charger/Handler/OutCharger/PileStopChargeResHandler.cs +++ b/Service/Charger/Handler/OutCharger/PileStopChargeResHandler.cs @@ -9,7 +9,9 @@ using Service.Charger.Msg.Charger.OutCharger.Resp; using Service.Charger.Msg.Http.Resp; namespace Service.Charger.Handler.OutCharger; - +/// +/// 3.7.4 充电桩响应远程停止充电 +/// [Order(8)] [Scope("InstancePerDependency")] public class PileStopChargeResHandler : SimpleChannelInboundHandler, IBaseHandler @@ -32,10 +34,7 @@ public class PileStopChargeResHandler : SimpleChannelInboundHandler +/// 3.7.13 充电桩上送充电记录 +/// +[Order(8)] +[Scope("InstancePerDependency")] +public class PileUploadChargeRecordHandler : SimpleChannelInboundHandler, IBaseHandler +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(PileUploadChargeRecordHandler)); + + public ChargeOrderRepository ChargeOrderRepository { get; set; } + + + protected override void ChannelRead0(IChannelHandlerContext ctx, PileUploadChargeRecord msg) + { + if (ClientMgr.TryGetClient(ctx.Channel, out var sn, out var client)) + { + if (client == null) + { + return; + } + + float[] powersPeriods = new float[4] { 0, 0, 0, 0 }; //元素索引顺序代表值;1:尖;2:峰;3:平;4:谷 + var acPowersPeriods = PeaksAndValleys(msg, powersPeriods); + + Log.Info($"receive {msg} from {sn}"); + ChargeOrder chargeOrder = ChargeOrderRepository.QueryByClause(it => it.Sn == msg.ChargeOrderNo); + + var startTime = new DateTime(msg.StartYear + 2000, msg.StartMonth, msg.StartDay, msg.StartHour, + msg.StartMinute, msg.StartSecond); + var endTime = new DateTime(msg.EndYear + 2000, msg.EndMonth, msg.EndDay, msg.EndHour, msg.EndMinute, + msg.EndSecond); + + + float chargingPower = msg.ChargingPower; + var socBefore = msg.SocBefore; + byte socAfter = msg.SocAfter; + + void UpdateChargeOrder(ChargeOrder order) + { + order.StartTime = startTime; + order.EndTime = endTime; + order.StartSoc = socBefore; + order.StopSoc = socAfter; + order.ChargeTimeCount = (int)(endTime - startTime).TotalMinutes; + order.ElecCount = Convert.ToDecimal(chargingPower); + order.AcElecCount = Convert.ToDecimal(msg.AcMeterElecCount); + order.StartAcElec = Convert.ToDecimal(msg.AcMeterDataBefore); + order.StopAcElec = Convert.ToDecimal(msg.AcMeterDataAfter); + order.StartDcElec = Convert.ToDecimal(msg.DcMeterDataBefore); + order.StopDcElec = Convert.ToDecimal(msg.DcMeterDataAfter); + order.SharpElecCount = Convert.ToDecimal(powersPeriods[0]); + order.PeakElecCount = Convert.ToDecimal(powersPeriods[1]); + order.FlatElecCount = Convert.ToDecimal(powersPeriods[2]); + order.ValleyElecCount = Convert.ToDecimal(powersPeriods[3]); + order.AcSharpElecCount = Convert.ToDecimal(acPowersPeriods[0]); + order.AcPeakElecCount = Convert.ToDecimal(acPowersPeriods[1]); + order.AcFlatElecCount = Convert.ToDecimal(acPowersPeriods[2]); + order.AcValleyElecCount = Convert.ToDecimal(acPowersPeriods[3]); + order.ChargeMode = msg.ChargeMode; + order.StartMode = msg.StartMode; + } + + if (chargeOrder == null) + { + ChargeOrder newOrder = new ChargeOrder() + { + Sn = msg.ChargeOrderNo + }; + UpdateChargeOrder(newOrder); + ChargeOrderRepository.Insert(newOrder); + + PileEndChargeReq req = new PileEndChargeReq(); + + + req.sn = StaticStationInfo.StationNo; + + req.con = msg.ChargeOrderNo; + req.cosn = msg.ChargeOrderNo; + + + UploadChargeRecord(msg, client, req, sn, startTime, endTime, chargingPower, socBefore, socAfter); + } + else + { + UpdateChargeOrder(chargeOrder); + // 充电完成入库 + ChargeOrderRepository.Update(chargeOrder); + + PileEndChargeReq req = new PileEndChargeReq(); + + + req.sn = StaticStationInfo.StationNo; + req.con = chargeOrder.CloudChargeOrder; + req.cosn = chargeOrder.Sn; + + // 充电完成上报云平台 + UploadChargeRecord(msg, client, req, sn, startTime, endTime, chargingPower, socBefore, socAfter); + } + } + + ctx.Channel.WriteAndFlushAsync(new PileUploadChargeRecordRes(msg.Pn)); + } + + private static void UploadChargeRecord(PileUploadChargeRecord msg, ChargerClient client, PileEndChargeReq req, + string sn, DateTime startTime, DateTime endTime, float chargingPower, byte socBefore, byte socAfter) + { + ChargerPile chargerPile = client.ChargerPile[msg.Pn]; + req.pn = ChargerUtils.ReverseCalculateChargerGun(msg.Pn, sn); + req.ct = chargerPile.ct; + req.cp = chargerPile.cp; + req.st = chargerPile.st; + req.cst = startTime; + req.cet = endTime; + req.ceq = chargingPower; + req.cssoc = socBefore; + req.cesoc = socAfter; + + req.ctn = msg.ChargingTimeCount; + + List periodList = new List(); + // 利用反射组装充电时间段数据 + for (int i = 1; i <= msg.ChargingTimeCount; i++) + { + PeriodReq periodReq = new PeriodReq(); + // 获取属性 + var startTimeHourProperty = typeof(PileUploadChargeRecord).GetProperty($"StartTime{i}"); + var startTimeMinuteProperty = typeof(PileUploadChargeRecord).GetProperty($"StartTimeMinute{i}"); + + if (startTimeHourProperty != null && startTimeMinuteProperty != null) + { + // 获取值 + byte startTimeHour = (byte)startTimeHourProperty.GetValue(msg); + byte startTimeMinute = (byte)startTimeMinuteProperty.GetValue(msg); + periodReq.StartTimeMinute = DateUtils.GetFormattedTime(startTimeHour, startTimeMinute); + } + + var chargingPowerProperty = typeof(PileUploadChargeRecord).GetProperty($"ChargingPowerOfTime{i}"); + var flagOfTimeProperty = typeof(PileUploadChargeRecord).GetProperty($"FlagOfTime{i}"); + if (chargingPowerProperty != null && flagOfTimeProperty != null) + { + periodReq.ChargingPowerOfTime = (float)chargingPowerProperty.GetValue(msg); + periodReq.FlagOfTime = (byte)flagOfTimeProperty.GetValue(msg); + } + + periodList.Add(periodReq); + } + + req.ctl = periodList; + + req.cvin = chargerPile.Vin; + + HttpUtil.SendPostRequest(req, "http://127.0.0.1:5034/api/OutCharger/SendPileEndCharge"); + } + + private static float[] PeaksAndValleys(PileUploadChargeRecord msg, float[] powersPeriods) + { + if (msg.FlagOfTime1 >= 1 && msg.FlagOfTime1 <= 4) + { + powersPeriods[msg.FlagOfTime1 - 1] += msg.ChargingPowerOfTime1; + } + + if (msg.FlagOfTime2 >= 1 && msg.FlagOfTime2 <= 4) + { + powersPeriods[msg.FlagOfTime2 - 1] += msg.ChargingPowerOfTime2; + } + + if (msg.FlagOfTime3 >= 1 && msg.FlagOfTime3 <= 4) + { + powersPeriods[msg.FlagOfTime3 - 1] += msg.ChargingPowerOfTime3; + } + + if (msg.FlagOfTime4 >= 1 && msg.FlagOfTime4 <= 4) + { + powersPeriods[msg.FlagOfTime4 - 1] += msg.ChargingPowerOfTime4; + } + + if (msg.FlagOfTime5 >= 1 && msg.FlagOfTime5 <= 4) + { + powersPeriods[msg.FlagOfTime5 - 1] += msg.ChargingPowerOfTime5; + } + + if (msg.FlagOfTime6 >= 1 && msg.FlagOfTime6 <= 4) + { + powersPeriods[msg.FlagOfTime6 - 1] += msg.ChargingPowerOfTime6; + } + + if (msg.FlagOfTime7 >= 1 && msg.FlagOfTime7 <= 4) + { + powersPeriods[msg.FlagOfTime7 - 1] += msg.ChargingPowerOfTime7; + } + + if (msg.FlagOfTime8 >= 1 && msg.FlagOfTime8 <= 4) + { + powersPeriods[msg.FlagOfTime8 - 1] += msg.ChargingPowerOfTime8; + } + + float[] acPowersPeriods = new float[4] { 0, 0, 0, 0 }; //元素索引顺序代表值;1:尖;2:峰;3:平;4:谷 + if (msg.AcFlagOfTime1 >= 1 && msg.AcFlagOfTime1 <= 4) + { + acPowersPeriods[msg.AcFlagOfTime1 - 1] += msg.AcChargingPowerOfTime1; + } + + if (msg.AcFlagOfTime2 >= 1 && msg.AcFlagOfTime2 <= 4) + { + acPowersPeriods[msg.AcFlagOfTime2 - 1] += msg.AcChargingPowerOfTime2; + } + + if (msg.AcFlagOfTime3 >= 1 && msg.AcFlagOfTime3 <= 4) + { + acPowersPeriods[msg.AcFlagOfTime3 - 1] += msg.AcChargingPowerOfTime3; + } + + if (msg.AcFlagOfTime4 >= 1 && msg.AcFlagOfTime4 <= 4) + { + acPowersPeriods[msg.AcFlagOfTime4 - 1] += msg.AcChargingPowerOfTime4; + } + + if (msg.AcFlagOfTime5 >= 1 && msg.AcFlagOfTime5 <= 4) + { + acPowersPeriods[msg.AcFlagOfTime5 - 1] += msg.AcChargingPowerOfTime5; + } + + if (msg.AcFlagOfTime6 >= 1 && msg.AcFlagOfTime6 <= 4) + { + acPowersPeriods[msg.AcFlagOfTime6 - 1] += msg.AcChargingPowerOfTime6; + } + + if (msg.AcFlagOfTime7 >= 1 && msg.AcFlagOfTime7 <= 4) + { + acPowersPeriods[msg.AcFlagOfTime7 - 1] += msg.AcChargingPowerOfTime7; + } + + if (msg.AcFlagOfTime8 >= 1 && msg.AcFlagOfTime8 <= 4) + { + acPowersPeriods[msg.AcFlagOfTime8 - 1] += msg.AcChargingPowerOfTime8; + } + + return acPowersPeriods; + } +} \ No newline at end of file diff --git a/Service/Charger/Handler/OutCharger/PileUploadRemoteSignalHandler.cs b/Service/Charger/Handler/OutCharger/PileUploadRemoteSignalHandler.cs index 50e4aaf..590b0d9 100644 --- a/Service/Charger/Handler/OutCharger/PileUploadRemoteSignalHandler.cs +++ b/Service/Charger/Handler/OutCharger/PileUploadRemoteSignalHandler.cs @@ -2,8 +2,9 @@ using log4net; using Service.Charger.Client; using Service.Charger.Handler; +using Service.Charger.Msg.Charger.OutCharger.Req; -namespace Service.Charger.Msg.Charger.OutCharger.Req; +namespace Service.Charger.Handler.OutCharger; /// /// 3.7.11 充电桩遥信数据上报 /// @@ -18,13 +19,17 @@ public class PileUploadRemoteSignalHandler: SimpleChannelInboundHandler it.Sn == msg.ChargerOrderNo); + + DateTime startTime = new DateTime((msg.StartYear+2000) , msg.StartMonth ,msg.StartDay ,msg.StartHour, msg.StartMinute, msg.StartSecond); + DateTime endTime = new DateTime(msg.EndYear + 2000, msg.EndMonth, msg.EndDay, msg.EndHour, msg.EndMinute, msg.EndSecond); + if (db == null) { ChargeOrder chargeOrder = new ChargeOrder() { Sn = client.ChargeOrderNo, BatteryNo = client.BatteryNo, - StartTime = new DateTime((msg.StartYear+2000) , msg.StartMonth ,msg.StartDay ,msg.StartHour, msg.StartMinute, msg.StartSecond), - EndTime = new DateTime(msg.EndYear + 2000, msg.EndMonth, msg.EndDay, msg.EndHour, msg.EndMinute, msg.EndSecond), + StartTime = startTime, + EndTime = endTime, StartSoc = msg.SocBefore, StopSoc = msg.SocAfter, - ChargeTimeCount= msg.ChargingTimeCount, + + ChargeTimeCount = (int)(endTime - startTime).TotalMinutes, ElecCount = Convert.ToDecimal(msg.ChargingPower), AcElecCount = Convert.ToDecimal(msg.AcMeterElecCount), StartAcElec = Convert.ToDecimal(msg.AcMeterDataBefore), diff --git a/Service/Charger/Handler/VoltageExtremumStatisticsHandler.cs b/Service/Charger/Handler/VoltageExtremumStatisticsHandler.cs index d7b2f59..76c59a1 100644 --- a/Service/Charger/Handler/VoltageExtremumStatisticsHandler.cs +++ b/Service/Charger/Handler/VoltageExtremumStatisticsHandler.cs @@ -23,6 +23,7 @@ namespace Service.Charger.Handler if (ClientMgr.TryGetClient(ctx.Channel, out string sn, out var client)) { Log.Info($"receive {msg} from {sn}"); + client.VoltageExtremumStatistics = msg; } } } diff --git a/Service/Charger/Msg/Charger/OutCharger/Req/PileUploadChargeRecord.cs b/Service/Charger/Msg/Charger/OutCharger/Req/PileUploadChargeRecord.cs new file mode 100644 index 0000000..af8661e --- /dev/null +++ b/Service/Charger/Msg/Charger/OutCharger/Req/PileUploadChargeRecord.cs @@ -0,0 +1,598 @@ +using HybirdFrameworkCore.Autofac.Attribute; + +namespace Service.Charger.Msg.Charger.OutCharger.Req; + +/// +/// 3.7.13 充电桩上送充电记录 +/// +public class PileUploadChargeRecord : 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, 256)] + public string ChargeOrderNo { get; set; } + + /// + /// 充电开始时间-秒 + /// + [Property(272, 8)] + public byte StartSecond { get; set; } + + /// + /// 充电开始时间-分 + /// + [Property(280, 8)] + public byte StartMinute { get; set; } + + /// + /// 充电开始时间-时 + /// + [Property(288, 8)] + public byte StartHour { get; set; } + + /// + /// 充电开始时间-日 + /// + [Property(296, 8)] + public byte StartDay { get; set; } + + /// + /// 充电开始时间-月 + /// + [Property(304, 8)] + public byte StartMonth { get; set; } + + /// + /// 充电开始时间-年 + /// + [Property(312, 8)] + public byte StartYear { get; set; } + + + /// + /// 充电结束时间-秒 + /// + [Property(320, 8)] + public byte EndSecond { get; set; } + + /// + /// 充电结束时间-分 + /// + [Property(328, 8)] + public byte EndMinute { get; set; } + + /// + /// 充电结束时间-时 + /// + [Property(336, 8)] + public byte EndHour { get; set; } + + /// + /// 充电结束时间-日 + /// + [Property(344, 8)] + public byte EndDay { get; set; } + + /// + /// 充电结束时间-月 + /// + [Property(352, 8)] + public byte EndMonth { get; set; } + + /// + /// 充电结束时间-年 + /// + [Property(360, 8)] + public byte EndYear { get; set; } + + + /// + /// 充电开始时间 秒-分-时-日-月-年 + /// + public string StartTime { get; set; } + + /// + /// 充电结束时间 + /// + public string EndTime { get; set; } + + /// + /// 1 枪充电前电能表数据 + /// + [Property(368, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float FirstGunEnergyMeterDataBefore { get; set; } + + + /// + /// 1 枪充电后电能表数据 + /// + [Property(400, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float FirstGunEnergyMeterDataAfter { get; set; } + + /// + /// 2 枪充电前电能表数据 + /// + [Property(432, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float SecondGunEnergyMeterDataBefore { get; set; } + + + /// + /// 2 枪充电后电能表数据 + /// + [Property(464, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float SecondGunEnergyMeterDataAfter { get; set; } + + /// + /// 充电电量 + /// + [Property(496, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float ChargingPower { get; set; } + + /// + /// 充电前SOC + /// + [Property(528, 8)] + public byte SocBefore { get; set; } + + /// + /// 充电后SOC + /// + [Property(536, 8)] + public byte SocAfter { get; set; } + + /// + /// 充电时段数量 + /// + [Property(544, 8)] + public byte ChargingTimeCount { get; set; } + + /// + /// 时段1 开始时间 时 + /// + [Property(552, 8)] + public byte StartTime1 { get; set; } + + /// + /// 时段1 开始时间 分 + /// + [Property(560, 8)] + public byte StartTimeMinute1 { get; set; } + + + /// + /// 时段1 电量 + /// + [Property(568, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float ChargingPowerOfTime1 { get; set; } + + /// + /// 时段1 标识 + /// + [Property(600, 8)] + public byte FlagOfTime1 { get; set; } + + /// + /// 时段2 开始时间 时 + /// + [Property(608, 8)] + public byte StartTime2 { get; set; } + + /// + /// 时段2 开始时间 分 + /// + [Property(616, 8)] + public byte StartTimeMinute2 { get; set; } + + /// + /// 时段2 电量 + /// + [Property(624, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float ChargingPowerOfTime2 { get; set; } + + /// + /// 时段2 标识 + /// + [Property(656, 8)] + public byte FlagOfTime2 { get; set; } + + /// + /// 时段3 开始时间 时 + /// + [Property(664, 8)] + public byte StartTime3 { get; set; } + + /// + /// 时段3 开始时间 分 + /// + [Property(672, 8)] + public byte StartTimeMinute3 { get; set; } + + /// + /// 时段3 电量 + /// + [Property(680, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float ChargingPowerOfTime3 { get; set; } + + /// + /// 时段3 标识 + /// + [Property(712, 8)] + public byte FlagOfTime3 { get; set; } + + /// + /// 时段4 开始时间 时 + /// + [Property(720, 8)] + public byte StartTime4 { get; set; } + + /// + /// 时段4 开始时间 分 + /// + [Property(728, 8)] + public byte StartTimeMinute4 { get; set; } + + /// + /// 时段4 电量 + /// + [Property(736, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float ChargingPowerOfTime4 { get; set; } + + /// + /// 时段4 标识 + /// + [Property(768, 8)] + public byte FlagOfTime4 { get; set; } + + + /// + /// 时段5 开始时间 时 + /// + [Property(776, 8)] + public byte StartTime5 { get; set; } + + /// + /// 时段5 开始时间 分 + /// + [Property(784, 8)] + public byte StartTimeMinute5 { get; set; } + + /// + /// 时段5 电量 + /// + [Property(792, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float ChargingPowerOfTime5 { get; set; } + + /// + /// 时段5 标识 + /// + [Property(824, 8)] + public byte FlagOfTime5 { get; set; } + + + /// + /// 时段6 开始时间 时 + /// + [Property(832, 8)] + public byte StartTime6 { get; set; } + + /// + /// 时段6 开始时间 分 + /// + [Property(840, 8)] + public byte StartTimeMinute6 { get; set; } + + /// + /// 时段6 电量 + /// + [Property(848, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float ChargingPowerOfTime6 { get; set; } + + /// + /// 时段6 标识 + /// + [Property(880, 8)] + public byte FlagOfTime6 { get; set; } + + + /// + /// 时段7 开始时间 时 + /// + [Property(888, 8)] + public byte StartTime7 { get; set; } + + /// + /// 时段7 开始时间 分 + /// + [Property(896, 8)] + public byte StartTimeMinute7 { get; set; } + + /// + /// 时段7 电量 + /// + [Property(904, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float ChargingPowerOfTime7 { get; set; } + + /// + /// 时段7 标识 + /// + [Property(936, 8)] + public byte FlagOfTime7 { get; set; } + + + /// + /// 时段8 开始时间 时 + /// + [Property(944, 8)] + public byte StartTime8 { get; set; } + + /// + /// 时段8 开始时间 分 + /// + [Property(952, 8)] + public byte StartTimeMinute8 { get; set; } + + /// + /// 时段8 电量 + /// + [Property(960, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float ChargingPowerOfTime8 { get; set; } + + /// + /// 时段8 标识 + /// + [Property(992, 8)] + public byte FlagOfTime8 { get; set; } + + /// + /// 充电模式 0:站内充电 1:站外充电 + /// + [Property(1000, 8)] + public byte ChargeMode { get; set; } + + /// + /// 启动模式 0: 站控启动 1:本地启动 + /// + [Property(1008, 8)] + public byte StartMode { get; set; } + + + /// + /// 充电前直流表值 + /// + [Property(1016, 32, PropertyReadConstant.Bit, 0.01, 2)] + + public float DcMeterDataBefore { get; set; } + + /// + /// 充电后直流表值 + /// + [Property(1048, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float DcMeterDataAfter { get; set; } + + + /// + /// 充电前交流表值 + /// + [Property(1080, 32, PropertyReadConstant.Bit, 0.01, 2)] + + public float AcMeterDataBefore { get; set; } + + /// + /// 充电后交流表值 + /// + [Property(1112, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float AcMeterDataAfter { get; set; } + + /// + /// 本次充电交流表值 + /// + [Property(1144, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float AcMeterElecCount { get; set; } + + /// + /// 时段1 开始时间 时 + /// + [Property(1176, 8)] + public byte AcStartTime1 { get; set; } + + /// + /// 时段1 开始时间 分 + /// + [Property(1184, 8)] + public byte AcStartTimeMinute1 { get; set; } + + /// + /// 时段1 电量 + /// + [Property(1192, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float AcChargingPowerOfTime1 { get; set; } + + /// + /// 时段1 标识 + /// + [Property(1224, 8)] + public byte AcFlagOfTime1 { get; set; } + + + /// + /// 时段2 开始时间 时 + /// + [Property(1232, 8)] + public byte AcStartTime2 { get; set; } + + /// + /// 时段2 开始时间 分 + /// + [Property(1240, 8)] + public byte AcStartTimeMinute2 { get; set; } + + /// + /// 时段2 电量 + /// + [Property(1248, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float AcChargingPowerOfTime2 { get; set; } + + /// + /// 时段2 标识 + /// + [Property(1280, 8)] + public byte AcFlagOfTime2 { get; set; } + + /// + /// 时段3 开始时间 时 + /// + [Property(1288, 8)] + public byte AcStartTime3 { get; set; } + + /// + /// 时段3 开始时间 分 + /// + [Property(1296, 8)] + public byte AcStartTimeMinute3 { get; set; } + + /// + /// 时段3 电量 + /// + [Property(1304, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float AcChargingPowerOfTime3 { get; set; } + + /// + /// 时段3 标识 + /// + [Property(1336, 8)] + public byte AcFlagOfTime3 { get; set; } + + /// + /// 时段4 开始时间 时 + /// + [Property(1344, 8)] + public byte AcStartTime4 { get; set; } + + /// + /// 时段4 开始时间 分 + /// + [Property(1352, 8)] + public byte AcStartTimeMinute4 { get; set; } + + /// + /// 时段4 电量 + /// + [Property(1360, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float AcChargingPowerOfTime4 { get; set; } + + /// + /// 时段4 标识 + /// + [Property(1392, 8)] + public byte AcFlagOfTime4 { get; set; } + + /// + /// 时段5 开始时间 时 + /// + [Property(1400, 8)] + public byte AcStartTime5 { get; set; } + + /// + /// 时段5 开始时间 分 + /// + [Property(1408, 8)] + public byte AcStartTimeMinute5 { get; set; } + + /// + /// 时段5 电量 + /// + [Property(1416, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float AcChargingPowerOfTime5 { get; set; } + + /// + /// 时段5 标识 + /// + [Property(1448, 8)] + public byte AcFlagOfTime5 { get; set; } + + /// + /// 时段6 开始时间 时 + /// + [Property(1456, 8)] + public byte AcStartTime6 { get; set; } + + /// + /// 时段6 开始时间 分 + /// + [Property(1464, 8)] + public byte AcStartTimeMinute6 { get; set; } + + /// + /// 时段6 电量 + /// + [Property(1472, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float AcChargingPowerOfTime6 { get; set; } + + /// + /// 时段6 标识 + /// + [Property(1504, 8)] + public byte AcFlagOfTime6 { get; set; } + + /// + /// 时段7 开始时间 时 + /// + [Property(1512, 8)] + public byte AcStartTime7 { get; set; } + + /// + /// 时段7 开始时间 分 + /// + [Property(1520, 8)] + public byte AcStartTimeMinute7 { get; set; } + + /// + /// 时段7 电量 + /// + [Property(1528, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float AcChargingPowerOfTime7 { get; set; } + + /// + /// 时段7 标识 + /// + [Property(1560, 8)] + public byte AcFlagOfTime7 { get; set; } + + /// + /// 时段8 开始时间 时 + /// + [Property(1568, 8)] + public byte AcStartTime8 { get; set; } + + /// + /// 时段8 开始时间 分 + /// + [Property(1572, 8)] + public byte AcStartTimeMinute8 { get; set; } + + /// + /// 时段8 电量 + /// + [Property(1580, 32, PropertyReadConstant.Bit, 0.01, 2)] + public float AcChargingPowerOfTime8 { get; set; } + + /// + /// 时段8 标识 + /// + [Property(1612, 8)] + public byte AcFlagOfTime8 { get; set; } +} \ No newline at end of file diff --git a/Service/Charger/Msg/Host/Resp/OutCharger/PileUploadChargeRecordRes.cs b/Service/Charger/Msg/Host/Resp/OutCharger/PileUploadChargeRecordRes.cs new file mode 100644 index 0000000..f9f311d --- /dev/null +++ b/Service/Charger/Msg/Host/Resp/OutCharger/PileUploadChargeRecordRes.cs @@ -0,0 +1,33 @@ +using HybirdFrameworkCore.Autofac.Attribute; + +namespace Service.Charger.Msg.Host.Resp.OutCharger; +/// +/// 3.7.14 主动上送充电记录响应 +/// +public class PileUploadChargeRecordRes : 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; } + + public PileUploadChargeRecordRes(byte pn) + { + FrameTypeNo = 51; + RecordType = 14; + MsgBodyCount = 1; + TransReason = 4; + PublicAddr = 0; + MsgBodyAddr = new byte[] { 0, 0, 0 }; + + Pn = pn; + } +} \ No newline at end of file diff --git a/Service/Charger/Msg/Http/Req/PeriodReq.cs b/Service/Charger/Msg/Http/Req/PeriodReq.cs new file mode 100644 index 0000000..a08db25 --- /dev/null +++ b/Service/Charger/Msg/Http/Req/PeriodReq.cs @@ -0,0 +1,21 @@ +namespace Service.Charger.Msg.Http.Req; +/// +/// 上报充电桩结束的计费时间段列表 +/// +public class PeriodReq +{ + /// + /// 时段 开始时间 + /// + public string? StartTimeMinute { get; set; } + + /// + /// 时段 电量 + /// + public float ChargingPowerOfTime { get; set; } + + /// + /// 时段 标识 + /// + public byte FlagOfTime { get; set; } +} \ No newline at end of file diff --git a/Service/Charger/Msg/Http/Req/PileChargeRealtimeReq.cs b/Service/Charger/Msg/Http/Req/PileChargeRealtimeReq.cs new file mode 100644 index 0000000..11666ad --- /dev/null +++ b/Service/Charger/Msg/Http/Req/PileChargeRealtimeReq.cs @@ -0,0 +1,152 @@ +namespace Service.Charger.Msg.Http.Req; + +/// +/// 9.2.1.7 站控上报充电枪充电遥测数据 +/// +public class PileChargeRealtimeReq +{ + /// + /// 换电站编码 + /// + public string sn { get; set; } + + /// + /// 充电订单号 + /// 云平台下发的充电订单编号,;当启动模式为本地主动启动(即插即充)时,该 值以 0 填充 + /// + public string con { get; set; } + + /// + /// 充电流水号 + /// 充电记录唯一编码 + /// + public string cosn { get; set; } + + /// + /// Desc:充电枪编号 + /// Default: + /// Nullable:True + /// + public string? pn { get; set; } + + /// + /// 需求电压 + /// + public float rv { get; set; } + + /// + /// 需求电流 + /// + public float re { get; set; } + + + /// + /// 充电模式 01H:恒压充电、02H恒流充电 + /// + public int cm { get; set; } + + /// + /// 充电电压测量值 + /// + public float cdv { get; set; } + + /// + /// 充电电流测量值 + /// + public float cde { get; set; } + + /// + /// 当前荷电状态 S0C (%) + /// + public float soc { get; set; } + + /// + /// 估算剩余充电时间 + /// + public int tr { get; set; } + + /// + /// 电桩电压输岀值 + /// + public float pov { get; set; } + + /// + /// 电桩电流输岀值 + /// + public float poe { get; set; } + + /// + /// 累计充电时间 + /// + public int tct { get; set; } + + /// + /// 最高单体动力 蓄电池电压所 在编号 + /// + public int? hbvn { get; set; } + + /// + /// 最高单体动力 蓄电池电压 + /// + public float? hbv { get; set; } + + /// + /// 最高温度检测 点编号 + /// + public int hbtn { get; set; } + + /// + /// 最高动力蓄电池温度 + /// + public int hbt { get; set; } + + /// + /// 最低单体动力 蓄电池电压所在编号 + /// + public int lbvn { get; set; } + + /// + /// 最低单体动力蓄电池电压 + /// + public float lbv { get; set; } + + /// + /// 最低动力蓄电池温度检测点 编号 + /// + public int lbtn { get; set; } + + /// + /// 最低动力蓄电 池温度 + /// + public int lbt { get; set; } + + /// + /// 单体动力蓄电 池电压过高 / 过低告警 + /// + public int hlbva { get; set; } + + /// + /// 整车动力蓄电 池荷电状态 S0C 过高/过低 告警 + /// + public int hlbsa { get; set; } + + /// + /// 动力蓄电池充电过流告警 + /// + public int baa { get; set; } + + /// + /// 动力蓄电池温度过高告警 + /// + public int bta { get; set; } + + /// + /// 动力蓄电池绝缘状态告警 + /// + public int bia { get; set; } + + /// + /// 动力蓄电池组 输岀连接器连 接状态告警 + /// + public int bca { get; set; } +} \ No newline at end of file diff --git a/Service/Charger/Msg/Http/Req/PileEndChargeReq.cs b/Service/Charger/Msg/Http/Req/PileEndChargeReq.cs new file mode 100644 index 0000000..7b0e47c --- /dev/null +++ b/Service/Charger/Msg/Http/Req/PileEndChargeReq.cs @@ -0,0 +1,95 @@ +namespace Service.Charger.Msg.Http.Req; +/// +/// 9.2.1.3 站控上报充电枪充电结束事件 +/// +public class PileEndChargeReq +{ + /// + /// 换电站编码 + /// + public string sn { get; set; } + + + /// + /// 充电订单号 + /// 云平台下发的充电订单编号,;当启动模式为本地主动启动(即插即充)时,该 值以 0 填充 + /// + public string con { get; set; } + + + /// + /// 充电流水号 + /// 充电记录唯一编码 + /// + public string cosn { get; set; } + + /// + /// Desc:充电枪编号 + /// Default: + /// Nullable:True + /// + public string? pn { get; set; } + + /// + /// 充电方式 + /// 0:自动(充满为止); 1:按电量; 2:按时间; 3:按金额; + /// + 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; } + + /// + /// 本次充电订单的开始时间 格式 yyyy-MM-dd HH:mm:ss + /// + public DateTime cst { get; set; } + + /// + /// 本次充电订单的开始时间 格式 yyyy-MM-dd HH:mm:ss + /// + public DateTime? cet { get; set; } + + /// + /// 充电电量 + /// 至少保留两位有效小数 + /// + public float ceq { get; set; } + + /// + /// Desc:充电开始soc + /// Default: + /// Nullable:True + /// + public int? cssoc { get; set; } + + /// + /// Desc:充电结束soc + /// Default: + /// Nullable:True + /// + public int? cesoc { get; set; } + + /// + /// 计费时间段个数 + /// + public int? ctn { get; set; } + + /// + /// 计费时间段列表 + /// + public List ctl { get; set; } + + /// + /// 充电车辆车架号 + /// + public string cvin { get; set; } +} \ No newline at end of file diff --git a/Service/Charger/Msg/Http/Req/PileStartChargeHttpReq.cs b/Service/Charger/Msg/Http/Req/PileStartChargeHttpReq.cs index e6820a1..77fad7b 100644 --- a/Service/Charger/Msg/Http/Req/PileStartChargeHttpReq.cs +++ b/Service/Charger/Msg/Http/Req/PileStartChargeHttpReq.cs @@ -8,7 +8,7 @@ public class PileStartChargeHttpReq /// 换电站编码 /// 换电站唯一码 /// - public string? sn { get; set; } + public string sn { get; set; } /// /// 充电订单号 @@ -26,19 +26,19 @@ public class PileStartChargeHttpReq /// 充电方式 /// 0:自动(充满为止);1:按电量; /// - public int? ct { get; set; } + public int ct { get; set; } /// /// 充电参数 /// 按充电方式判断,除0外 电量:单位 kWh,精确到 0.01 时间:单位 min,精确到 0.01 金额:单位 元,精确到 0.01 /// - public string? cp { get; set; } + public string cp { get; set; } /// /// 启动类型 /// 0:运营平台启动;1:APP 启动;2: 本地启动 /// - public int? st { get; set; } + public int st { get; set; } } \ No newline at end of file diff --git a/Service/Charger/MyTask/EmeterEnergyRecordTask.cs b/Service/Charger/MyTask/EmeterEnergyRecordTask.cs index f7bee2e..408aedb 100644 --- a/Service/Charger/MyTask/EmeterEnergyRecordTask.cs +++ b/Service/Charger/MyTask/EmeterEnergyRecordTask.cs @@ -16,7 +16,7 @@ public class EmeterEnergyRecordTask : ITask { private static readonly ILog Log = LogManager.GetLogger(typeof(EmeterEnergyRecordTask)); private volatile bool _stop; - + public EmeterEnergyRepository EmeterEnergyRepository { get; set; } public EmeterEnergyChangeRepository EmeterEnergyChangeRepository { get; set; } diff --git a/WebStarter/Controllers/OutChargerController.cs b/WebStarter/Controllers/OutChargerController.cs index 6ea61d9..0cca89a 100644 --- a/WebStarter/Controllers/OutChargerController.cs +++ b/WebStarter/Controllers/OutChargerController.cs @@ -43,19 +43,30 @@ public class OutChargerController return Result.Fail("充电机未连接"); } + + string chargeGunOrder = ChargerUtils.GenChargeOrderSn(); if (string.IsNullOrWhiteSpace(httpReq.con)) { - httpReq.con = ChargerUtils.GenChargeOrderSn(); + httpReq.con = chargeGunOrder; } + chargerClient.ChargerPile[chargerGunCode].ct = httpReq.ct; + chargerClient.ChargerPile[chargerGunCode].cp = httpReq.cp; + chargerClient.ChargerPile[chargerGunCode].st = httpReq.st; + chargerClient.ChargerPile[chargerGunCode].con = httpReq.con; + chargerClient.ChargerPile[chargerGunCode].cosn = chargeGunOrder; + chargerClient.ChargerPile[chargerGunCode].pn = httpReq.pn; + byte chargeSoc = StaticStationInfo.ChargeSoc; // 下发充电枪充电 - chargerClient.SendStartOutCharger(chargerGunCode, chargeSoc, 360, 1, httpReq.con); + chargerClient.SendStartOutCharger(chargerGunCode, chargeSoc, 360, 1, chargeGunOrder); // 初始化订单 - _chargeOrderRepository.SaveChargeGunOrder(httpReq.con, chargerCode, httpReq.pn, chargerGunCode.ToString()); + _chargeOrderRepository.SaveChargeGunOrder(chargeGunOrder, httpReq.con, chargerCode, httpReq.pn, + chargerGunCode.ToString()); return Result.Success(true); } + /// /// 云端下发充电枪停止充电 ///