From c6ff3cf2c4198684b75136a7ed5490b19afae00a Mon Sep 17 00:00:00 2001 From: lxw Date: Mon, 15 Jul 2024 17:37:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E8=83=BD=E8=BE=89=E5=85=85?= =?UTF-8?q?=E7=94=B5=E6=9C=BA=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Const/EquipmentType.cs | 3 +- Common/Util/HttpUtil.cs | 3 + Common/lib/HybirdFrameworkCore.dll | Bin 31744 -> 51200 bytes Common/lib/HybirdFrameworkDriver.dll | Bin 35840 -> 37888 bytes Entity/DbModel/Station/ChargeOrder.cs | 29 ++- Entity/DbModel/Station/EquipAlarmDefine.cs | 2 + .../Station/EquipAlarmProcessRecord.cs | 144 ++++++------ Entity/DbModel/Station/EquipAlarmRecord.cs | 2 + Entity/Dto/Resp/ChargerPileResp.cs | 38 ++++ .../Station/EquipAlarmDefineRepository.cs | 2 +- .../Station/EquipAlarmRecordRepository.cs | 6 - Repository/Station/EquipInfoRepository.cs | 2 +- Service/Charger/ChargerService.cs | 56 ++--- Service/Charger/Client/ChargerClient.cs | 115 ++++++++-- Service/Charger/Client/ChargerPile.cs | 5 + Service/Charger/Codec/Decoder.cs | 1 + Service/Charger/Common/ChargerConst.cs | 12 +- Service/Charger/Common/ChargerUtils.cs | 16 +- Service/Charger/Common/WaterCoolerConst.cs | 8 + .../Handler/FinishStartChargingHandler.cs | 22 +- .../Handler/FinishStopChargingHandler.cs | 5 +- .../OutCharger/PileAdjustPowerHandler.cs | 22 ++ .../OutCharger/PileStartChargeResHandler.cs | 3 +- .../OutCharger/PileStopChargeResHandler.cs | 2 +- .../PileUploadChargeRecordHandler.cs | 3 +- .../PileUploadRemoteSignalHandler.cs | 3 +- .../OutCharger/PileUploadTelemetryHandler.cs | 2 +- .../Handler/QueryBatterySnResHandler.cs | 5 +- .../Charger/Handler/RecordChargeHandler.cs | 36 +-- .../Handler/RemoteStartChargingResHandler.cs | 5 +- .../Charger/Handler/ResponseSettingHandler.cs | 34 +++ Service/Charger/Handler/UpAlarmHandler.cs | 118 +--------- .../Handler/UploadRemoteSignalDataHandler.cs | 153 ++++++++++++- .../Handler/UploadTelemetryDataHandler.cs | 10 +- .../OutCharger/Resp/PileAdjustPowerRes.cs | 35 +++ Service/Charger/Msg/Charger/Resp/UpBms.cs | 64 +++++- .../Req/OutCharger/Req/PileAdjustPower.cs | 42 ++++ .../Req/OutCharger/Req/PileStartCharge.cs | 2 +- .../Host/Req/OutCharger/Req/PileStopCharge.cs | 2 +- .../Charger/Msg/Http/Req/PileRealtimeReq.cs | 45 ++++ Service/Charger/MyTask/AutoChargeTask.cs | 110 +++++---- .../Charger/MyTask/EmeterEnergyRecordTask.cs | 5 + .../Charger/MyTask/PileChargeRealtimeTask.cs | 103 +++++++++ Service/Charger/MyTask/PileRealtimeTask.cs | 100 ++++++++ Service/Job/TestJob.cs | 32 +++ Service/MyTask/BatteryInfoUploadTask.cs | 93 ++++++++ Service/MyTask/ZipLogTask.cs | 9 + Service/RealTime/RealtimeClient.cs | 90 ++++++++ Service/RealTime/RtMsg.cs | 11 + Service/Service.csproj | 7 + Service/Station/BinInfoService.cs | 1 + Service/Swap/Dto/BatDataInfo.cs | 23 ++ Service/Swap/Dto/SingleBatInfo.cs | 84 +++++++ Service/WaterCool/Client/WaterCoolClient.cs | 2 +- .../WaterCool/Client/WaterCoolClientMgr.cs | 2 +- WebStarter/Controllers/ChargeController.cs | 5 +- .../Controllers/OutChargerController.cs | 64 +++++- WebStarter/Controllers/Test/GenController.cs | 32 ++- WebStarter/Program.cs | 24 +- WebStarter/WebStarter.csproj | 1 + WebStarter/appsettings.dev.json | 10 +- WebStarter/appsettings.prod.json | 11 + WebStarter/db/qrtz.sql | 214 ++++++++++++++++++ WebStarter/log4net.config | 68 +++--- .../obj/WebStarter.csproj.nuget.g.targets | 1 + .../obj/WinFormStarter.csproj.nuget.g.targets | 3 +- 66 files changed, 1759 insertions(+), 403 deletions(-) create mode 100644 Entity/Dto/Resp/ChargerPileResp.cs create mode 100644 Service/Charger/Common/WaterCoolerConst.cs create mode 100644 Service/Charger/Handler/OutCharger/PileAdjustPowerHandler.cs create mode 100644 Service/Charger/Handler/ResponseSettingHandler.cs create mode 100644 Service/Charger/Msg/Charger/OutCharger/Resp/PileAdjustPowerRes.cs create mode 100644 Service/Charger/Msg/Host/Req/OutCharger/Req/PileAdjustPower.cs create mode 100644 Service/Charger/Msg/Http/Req/PileRealtimeReq.cs create mode 100644 Service/Charger/MyTask/PileChargeRealtimeTask.cs create mode 100644 Service/Charger/MyTask/PileRealtimeTask.cs create mode 100644 Service/Job/TestJob.cs create mode 100644 Service/MyTask/BatteryInfoUploadTask.cs create mode 100644 Service/MyTask/ZipLogTask.cs create mode 100644 Service/RealTime/RealtimeClient.cs create mode 100644 Service/RealTime/RtMsg.cs create mode 100644 Service/Swap/Dto/BatDataInfo.cs create mode 100644 Service/Swap/Dto/SingleBatInfo.cs create mode 100644 WebStarter/db/qrtz.sql diff --git a/Common/Const/EquipmentType.cs b/Common/Const/EquipmentType.cs index 7f6f2f4..f14e24b 100644 --- a/Common/Const/EquipmentType.cs +++ b/Common/Const/EquipmentType.cs @@ -5,5 +5,6 @@ public enum EquipmentType Charger, Ammeter, WaterCool, - Plc + Plc, + BMS } diff --git a/Common/Util/HttpUtil.cs b/Common/Util/HttpUtil.cs index 0d9e8a6..db474ba 100644 --- a/Common/Util/HttpUtil.cs +++ b/Common/Util/HttpUtil.cs @@ -3,6 +3,9 @@ using Newtonsoft.Json; namespace Common.Util; +/// +/// http工具类 +/// public static class HttpUtil { private static readonly HttpClient httpClient = new HttpClient(); diff --git a/Common/lib/HybirdFrameworkCore.dll b/Common/lib/HybirdFrameworkCore.dll index 608abef475b2a6c3986c998d106b4cc6536f345a..c0fb98bd9851ccb28d0a26aefc647ee22d35e211 100644 GIT binary patch literal 51200 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 l`+&X|PS(--*-~xOA-R5l$ literal 31744 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 YR0{ty;5zr$IWzy6LjPCx{}c=SKeV$DIRF3v diff --git a/Common/lib/HybirdFrameworkDriver.dll b/Common/lib/HybirdFrameworkDriver.dll index 9c92151bebd6cb63dafb7a87cc2d88c4ca689bcf..3ffe052b753b977f374aeaeb389d7cccce7bc0dc 100644 GIT binary patch literal 37888 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 + /// [SugarColumn(IsPrimaryKey = true, IsIdentity = true, ColumnName = "id")] public int Id { get; set; } @@ -37,7 +37,7 @@ namespace Entity.DbModel.Station public string BatteryNo { get; set; } /// - /// Desc:启动报文状态;0-初始化;1-启动成功 + /// Desc:启动报文状态;0-初始化;1-启动成功;2-启动失败 /// Default:0 /// Nullable:True /// @@ -51,7 +51,7 @@ namespace Entity.DbModel.Station /// [SugarColumn(ColumnName = "charger_no")] public string ChargerNo { get; set; } - + /// /// Desc:充电枪编号 /// Default: @@ -59,7 +59,7 @@ namespace Entity.DbModel.Station /// [SugarColumn(ColumnName = "charger_gun_no")] public string ChargerGunNo { get; set; } - + /// /// Desc:站外充电枪编号,1枪或2枪 /// Default: @@ -266,9 +266,9 @@ namespace Entity.DbModel.Station /// Nullable:True /// [SugarColumn(ColumnName = "cloud_report_status")] - public int? CloudReportStatus { get; set; } + public int CloudReportStatus { get; set; } + - /// /// Desc:云平台订单编号 /// Default: @@ -283,6 +283,19 @@ namespace Entity.DbModel.Station /// Nullable:True /// [SugarColumn(ColumnName = "can_upload")] - public int? CanUpload { get; set; } + public int CanUpload { get; set; } + + /// + /// 启动方式:0-站控自动启动,1-站控手动启动,2-充电机启动 + /// + [SugarColumn(ColumnName = "start_type")] + public int StartType { get; set; } + + + /// + /// 上报次数 + /// + [SugarColumn(ColumnName = "reporting_times")] + public int ReportingTimes { get; set; } } -} \ No newline at end of file +} diff --git a/Entity/DbModel/Station/EquipAlarmDefine.cs b/Entity/DbModel/Station/EquipAlarmDefine.cs index 08b8aa7..95b84d4 100644 --- a/Entity/DbModel/Station/EquipAlarmDefine.cs +++ b/Entity/DbModel/Station/EquipAlarmDefine.cs @@ -67,4 +67,6 @@ public partial class EquipAlarmDefine : BaseModel /// [SugarColumn(ColumnName = "process_method")] public string ProcessMethod { get; set; } + + } \ No newline at end of file diff --git a/Entity/DbModel/Station/EquipAlarmProcessRecord.cs b/Entity/DbModel/Station/EquipAlarmProcessRecord.cs index b214b5f..9e00eb0 100644 --- a/Entity/DbModel/Station/EquipAlarmProcessRecord.cs +++ b/Entity/DbModel/Station/EquipAlarmProcessRecord.cs @@ -3,84 +3,84 @@ namespace Entity.DbModel.Station; /// -///设备报警处理记录 -/// -[SugarTable("equip_alarm_process_record")] -public partial class EquipAlarmProcessRecord : BaseModel -{ - public EquipAlarmProcessRecord() + ///设备报警处理记录 + /// + [SugarTable("equip_alarm_process_record")] + public partial class EquipAlarmProcessRecord : BaseModel { - } + public EquipAlarmProcessRecord() + { + } - /// - /// Desc:id - /// Default: - /// Nullable:False - /// - [SugarColumn(ColumnName = "id")] - public int Id { get; set; } + /// + /// Desc:id + /// Default: + /// Nullable:False + /// + [SugarColumn(ColumnName = "id")] + public int Id { get; set; } - /// - /// Desc:设备类型编码 - /// Default: - /// Nullable:True - /// - [SugarColumn(ColumnName = "equip_type_code")] - public int EquipTypeCode { get; set; } + /// + /// Desc:设备类型编码 + /// Default: + /// Nullable:True + /// + [SugarColumn(ColumnName = "equip_type_code")] + public int EquipTypeCode { get; set; } - /// - /// Desc:设备编码 - /// Default: - /// Nullable:True - /// - [SugarColumn(ColumnName = "equip_code")] - public string EquipCode { get; set; } + /// + /// Desc:设备编码 + /// Default: + /// Nullable:True + /// + [SugarColumn(ColumnName = "equip_code")] + public string EquipCode { get; set; } - /// - /// Desc:报警编码 - /// Default: - /// Nullable:True - /// - [SugarColumn(ColumnName = "error_code")] - public string ErrorCode { get; set; } + /// + /// Desc:报警编码 + /// Default: + /// Nullable:True + /// + [SugarColumn(ColumnName = "error_code")] + public string ErrorCode { get; set; } - /// - /// Desc:报警等级 - /// Default: - /// Nullable:True - /// - [SugarColumn(ColumnName = "error_level")] - public string ErrorLevel { get; set; } + /// + /// Desc:报警等级 + /// Default: + /// Nullable:True + /// + [SugarColumn(ColumnName = "error_level")] + public string ErrorLevel { get; set; } - /// - /// Desc:报警描述 - /// Default: - /// Nullable:True - /// - [SugarColumn(ColumnName = "error_msg")] - public string ErrorMsg { get; set; } + /// + /// Desc:报警描述 + /// Default: + /// Nullable:True + /// + [SugarColumn(ColumnName = "error_msg")] + public string ErrorMsg { get; set; } - /// - /// Desc:处理方法 - /// Default: - /// Nullable:True - /// - [SugarColumn(ColumnName = "process_method")] - public string ProcessMethod { get; set; } + /// + /// Desc:处理方法 + /// Default: + /// Nullable:True + /// + [SugarColumn(ColumnName = "process_method")] + public string ProcessMethod { get; set; } + /// + /// Desc:开始时间 + /// Default: + /// Nullable:True + /// + [SugarColumn(ColumnName = "start_time")] + public DateTime? StartTime { get; set; } + /// + /// Desc:处理时间 + /// Default: + /// Nullable:True + /// + [SugarColumn(ColumnName = "process_time")] + public DateTime? ProcessTime { get; set; } - /// - /// Desc:开始时间 - /// Default: - /// Nullable:True - /// - [SugarColumn(ColumnName = "start_time")] - public DateTime? StartTime { get; set; } - - /// - /// Desc:处理时间 - /// Default: - /// Nullable:True - /// - [SugarColumn(ColumnName = "process_time")] - public DateTime? ProcessTime { get; set; } -} \ No newline at end of file + + } \ No newline at end of file diff --git a/Entity/DbModel/Station/EquipAlarmRecord.cs b/Entity/DbModel/Station/EquipAlarmRecord.cs index 1862bfb..75e8fa8 100644 --- a/Entity/DbModel/Station/EquipAlarmRecord.cs +++ b/Entity/DbModel/Station/EquipAlarmRecord.cs @@ -75,4 +75,6 @@ public partial class EquipAlarmRecord : BaseModel /// [SugarColumn(ColumnName = "start_time")] public DateTime? StartTime { get; set; } + + } \ No newline at end of file diff --git a/Entity/Dto/Resp/ChargerPileResp.cs b/Entity/Dto/Resp/ChargerPileResp.cs new file mode 100644 index 0000000..d64a533 --- /dev/null +++ b/Entity/Dto/Resp/ChargerPileResp.cs @@ -0,0 +1,38 @@ +namespace Entity.Dto.Resp; + +/// +/// 充电桩信息 +/// +public class ChargerPileResp +{ + public ChargerPileResp() + { + } + + public string Sn { get; set; } + /// + /// 站外1枪是否充电 + /// + public bool GunChargedOne { get; set; } + /// + /// 站外2枪是否充电 + /// + public bool GunChargedTwo { get; set; } + /// + /// 站外1枪是否连接 + /// + public bool ChargedPileOne { get; set; } + /// + /// 站外2枪是否连接 + /// + public bool ChargedPileTwo { get; set; } + /// + /// 站外1枪充电功率 + /// + public float? ChargePilePowerOne { get; set; } + /// + /// 站外2枪充电功率 + /// + public float? ChargePilePowerTwo { get; set; } + +} \ No newline at end of file diff --git a/Repository/Station/EquipAlarmDefineRepository.cs b/Repository/Station/EquipAlarmDefineRepository.cs index c8b5e19..1163b51 100644 --- a/Repository/Station/EquipAlarmDefineRepository.cs +++ b/Repository/Station/EquipAlarmDefineRepository.cs @@ -10,7 +10,7 @@ public class EquipAlarmDefineRepository : BaseRepository public EquipAlarmDefineRepository(ISqlSugarClient sqlSugar) : base(sqlSugar) { } - + /// /// /// diff --git a/Repository/Station/EquipAlarmRecordRepository.cs b/Repository/Station/EquipAlarmRecordRepository.cs index 7fbbab5..40a71f6 100644 --- a/Repository/Station/EquipAlarmRecordRepository.cs +++ b/Repository/Station/EquipAlarmRecordRepository.cs @@ -10,10 +10,4 @@ public class EquipAlarmRecordRepository : BaseRepository public EquipAlarmRecordRepository(ISqlSugarClient sqlSugar) : base(sqlSugar) { } - - public EquipAlarmRecord? SelectByEquipCodeAndErrorCode(int equipTypeCode, string equipCode, string errorCode) - { - return this.QueryByClause(it => it.EquipTypeCode == equipTypeCode && - it.EquipCode == equipCode && it.ErrorCode == errorCode); - } } \ No newline at end of file diff --git a/Repository/Station/EquipInfoRepository.cs b/Repository/Station/EquipInfoRepository.cs index 4e7f73d..a4e679e 100644 --- a/Repository/Station/EquipInfoRepository.cs +++ b/Repository/Station/EquipInfoRepository.cs @@ -27,7 +27,7 @@ public class EquipInfoRepository: BaseRepository .WhereIF(isWhere1, expression1) .WhereIF(isWhere2, expression2) .WhereIF(isWhere3, expression3) - .OrderBuilder(input) + .OrderBuilder(input, "", "Id", false) .WithNoLockOrNot(blUseNoLock) .ToPageListAsync(pageNumber, pageSize, totalNumber); diff --git a/Service/Charger/ChargerService.cs b/Service/Charger/ChargerService.cs index 6512f24..256f4f6 100644 --- a/Service/Charger/ChargerService.cs +++ b/Service/Charger/ChargerService.cs @@ -48,9 +48,9 @@ public class ChargerService byte chargeSoc = StaticStationInfo.ChargeSoc; - float? chargePower = EquipInfoRepository.QueryPowerByCode(binNo); + float? chargePower = EquipInfoRepository.QueryPowerByCode(binInfo.ChargerNo); float? power = chargePower == null ? StaticStationInfo.ChargePower : chargePower; - return chargerClient.StartCharge(chargeSoc,(float)power); + return chargerClient.StartCharge(chargeSoc,(float)power, 1); } /// @@ -114,30 +114,30 @@ public class ChargerService SetPeakValleyTime setPeakValleyTime = new SetPeakValleyTime() { NumberTime = Convert.ToByte(elecPriceModelVersionDetails.Count), - StartHH1 = Convert.ToByte(elecPriceModelVersionDetails[0].StartHour), - StartHH2 = Convert.ToByte(elecPriceModelVersionDetails[1].StartHour), - StartHH3 = Convert.ToByte(elecPriceModelVersionDetails[2].StartHour), - StartHH4 = Convert.ToByte(elecPriceModelVersionDetails[3].StartHour), - StartHH5 = Convert.ToByte(elecPriceModelVersionDetails[4].StartHour), - StartHH6 = Convert.ToByte(elecPriceModelVersionDetails[5].StartHour), - StartHH7 = Convert.ToByte(elecPriceModelVersionDetails[6].StartHour), - StartHH8 = Convert.ToByte(elecPriceModelVersionDetails[7].StartHour), - StartMM1 = Convert.ToByte(elecPriceModelVersionDetails[0].StartMinute), - StartMM2 = Convert.ToByte(elecPriceModelVersionDetails[1].StartMinute), - StartMM3 = Convert.ToByte(elecPriceModelVersionDetails[2].StartMinute), - StartMM4 = Convert.ToByte(elecPriceModelVersionDetails[3].StartMinute), - StartMM5 = Convert.ToByte(elecPriceModelVersionDetails[4].StartMinute), - StartMM6 = Convert.ToByte(elecPriceModelVersionDetails[5].StartMinute), - StartMM7 = Convert.ToByte(elecPriceModelVersionDetails[6].StartMinute), - StartMM8 = Convert.ToByte(elecPriceModelVersionDetails[7].StartMinute), - TimePeak1 = Convert.ToByte(elecPriceModelVersionDetails[0].Type), - TimePeak2 = Convert.ToByte(elecPriceModelVersionDetails[1].Type), - TimePeak3 = Convert.ToByte(elecPriceModelVersionDetails[2].Type), - TimePeak4 = Convert.ToByte(elecPriceModelVersionDetails[3].Type), - TimePeak5 = Convert.ToByte(elecPriceModelVersionDetails[4].Type), - TimePeak6 = Convert.ToByte(elecPriceModelVersionDetails[5].Type), - TimePeak7 = Convert.ToByte(elecPriceModelVersionDetails[6].Type), - TimePeak8 = Convert.ToByte(elecPriceModelVersionDetails[7].Type) + StartHH1 = (byte)(elecPriceModelVersionDetails.Count > 0?Convert.ToByte(elecPriceModelVersionDetails[0].StartHour) :0), + StartHH2 = (byte)(elecPriceModelVersionDetails.Count > 1?Convert.ToByte(elecPriceModelVersionDetails[1].StartHour) : 0), + StartHH3 = (byte)(elecPriceModelVersionDetails.Count > 2?Convert.ToByte(elecPriceModelVersionDetails[2].StartHour):0), + StartHH4 = (byte)(elecPriceModelVersionDetails.Count > 3?Convert.ToByte(elecPriceModelVersionDetails[3].StartHour):0), + StartHH5 = (byte)(elecPriceModelVersionDetails.Count > 4?Convert.ToByte(elecPriceModelVersionDetails[4].StartHour):0), + StartHH6 = (byte)(elecPriceModelVersionDetails.Count > 5?Convert.ToByte(elecPriceModelVersionDetails[5].StartHour):0), + StartHH7 = (byte)(elecPriceModelVersionDetails.Count > 6?Convert.ToByte(elecPriceModelVersionDetails[6].StartHour):0), + StartHH8 = (byte)(elecPriceModelVersionDetails.Count > 7?Convert.ToByte(elecPriceModelVersionDetails[7].StartHour) : 0), + StartMM1 = (byte)(elecPriceModelVersionDetails.Count > 0?Convert.ToByte(elecPriceModelVersionDetails[0].StartMinute):0), + StartMM2 = (byte)(elecPriceModelVersionDetails.Count > 1?Convert.ToByte(elecPriceModelVersionDetails[1].StartMinute):0), + StartMM3 = (byte)(elecPriceModelVersionDetails.Count > 2?Convert.ToByte(elecPriceModelVersionDetails[2].StartMinute):0), + StartMM4 = (byte)(elecPriceModelVersionDetails.Count > 3?Convert.ToByte(elecPriceModelVersionDetails[3].StartMinute):0), + StartMM5 = (byte)(elecPriceModelVersionDetails.Count > 4?Convert.ToByte(elecPriceModelVersionDetails[4].StartMinute):0), + StartMM6 = (byte)(elecPriceModelVersionDetails.Count > 5?Convert.ToByte(elecPriceModelVersionDetails[5].StartMinute):0), + StartMM7 = (byte)(elecPriceModelVersionDetails.Count > 6?Convert.ToByte(elecPriceModelVersionDetails[6].StartMinute) : 0), + StartMM8 = (byte)(elecPriceModelVersionDetails.Count > 7 ? Convert.ToByte(elecPriceModelVersionDetails[7].StartMinute) : 0), + TimePeak1 = (byte)(elecPriceModelVersionDetails.Count > 0 ? Convert.ToByte(elecPriceModelVersionDetails[0].Type):0), + TimePeak2 = (byte)(elecPriceModelVersionDetails.Count > 1?Convert.ToByte(elecPriceModelVersionDetails[1].Type):0), + TimePeak3 = (byte)(elecPriceModelVersionDetails.Count > 2?Convert.ToByte(elecPriceModelVersionDetails[2].Type):0), + TimePeak4 = (byte)(elecPriceModelVersionDetails.Count > 3?Convert.ToByte(elecPriceModelVersionDetails[3].Type):0), + TimePeak5 = (byte)(elecPriceModelVersionDetails.Count > 4?Convert.ToByte(elecPriceModelVersionDetails[4].Type):0), + TimePeak6 = (byte)(elecPriceModelVersionDetails.Count > 5?Convert.ToByte(elecPriceModelVersionDetails[5].Type):0), + TimePeak7 = (byte)(elecPriceModelVersionDetails.Count > 6?Convert.ToByte(elecPriceModelVersionDetails[6].Type):0), + TimePeak8 = (byte)(elecPriceModelVersionDetails.Count > 7?Convert.ToByte(elecPriceModelVersionDetails[7].Type):0) }; return setPeakValleyTime; } @@ -153,7 +153,7 @@ public class ChargerService if (binInfos.Count > 0) batteryStatusInfoResp.btyTotalCount = binInfos.Count(); List canSwapCounts = BinInfoRepository.QueryListByClause(i => - i.Exists == 1 && i.Status == 1 && i.Soc > Convert.ToDecimal(StaticStationInfo.SwapSoc)); + i.Exists == 1 && i.Status == 1 && i.Soc >= Convert.ToDecimal(StaticStationInfo.SwapSoc) && i.ChargeStatus!=1); if (canSwapCounts.Count > 0) batteryStatusInfoResp.canSwapCount = canSwapCounts.Count(); List chargingCounts = @@ -162,4 +162,4 @@ public class ChargerService batteryStatusInfoResp.chargingCount = chargingCounts.Count(); return Result.Success(batteryStatusInfoResp); } -} \ No newline at end of file +} diff --git a/Service/Charger/Client/ChargerClient.cs b/Service/Charger/Client/ChargerClient.cs index 4b0d09c..b8cc721 100644 --- a/Service/Charger/Client/ChargerClient.cs +++ b/Service/Charger/Client/ChargerClient.cs @@ -1,4 +1,5 @@ using System.Collections.Concurrent; +using System.Reflection; using Autofac; using Entity.DbModel.Station; using HybirdFrameworkCore.Autofac; @@ -18,9 +19,9 @@ 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; +using Service.Charger.Msg.Host.Req.OutCharger.Req; namespace Service.Charger.Client; @@ -30,7 +31,6 @@ namespace Service.Charger.Client; [Scope("InstancePerDependency")] public class ChargerClient : TcpClient { - #region 属性 /// @@ -50,9 +50,8 @@ public class ChargerClient : TcpClient /// 是否已经开始充电 /// public bool IsCharged { get; set; } = false; - - - + + /// /// 站外两枪时是否在充电 /// @@ -61,6 +60,7 @@ public class ChargerClient : TcpClient [1] = false, [2] = false }; + /// /// 充电桩连接状态 /// @@ -69,7 +69,7 @@ public class ChargerClient : TcpClient [1] = false, [2] = false }; - + public bool IsStopped { get; set; } = false; public bool IsCanSendStopCmd { get; set; } = true; @@ -151,30 +151,37 @@ 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 ConcurrentDictionary ChargePilePower = new(); + /// ///充电机实时充电功率 /// public float RealTimeChargePower { get; set; } = 0; - + /// /// 心跳-桩状态 /// @@ -455,9 +462,20 @@ public class ChargerClient : TcpClient return Result.Fail($"charger-{BinNo} disconnect"); } + + + + setPeakValleyTime= GetMsgContents(setPeakValleyTime); + + + CurrentCmd = JsonConvert.SerializeObject(setPeakValleyTime, Formatting.Indented) + "\r\n" + BitUtls.BytesToHexStr(setPeakValleyTime.ToBytes()); + this.Channel.WriteAndFlushAsync(setPeakValleyTime); + + Log().Info($"SendSetPeakValleyTime{CurrentCmd} to chargeOrderNo={BinNo}"); + return Result.Success(); } @@ -494,6 +512,7 @@ public class ChargerClient : TcpClient this.Channel.WriteAndFlushAsync(req); return Result.Success(); } + /// /// 3.7.1 监控平台远程启动充电桩充电 /// @@ -503,7 +522,8 @@ public class ChargerClient : TcpClient /// 功率调节参数 /// /// 充电流水号 - public Result SendStartOutCharger(byte pn, byte socValue, short changePower=360,byte changePowerCmdType=1 , + public Result SendStartOutCharger(byte pn, byte socValue, short changePower = 360, + byte changePowerCmdType = 1, string? chargeOrderNo = null) { if (!Connected) @@ -519,12 +539,14 @@ public class ChargerClient : TcpClient Log().Info( $"SendStartOutCharger pn={pn}, socValue={socValue}, changePower={changePower}, changePowerCmdType={changePowerCmdType}, chargeOrderNo={chargeOrderNo}"); - PileStartCharge pileStartCharge = new PileStartCharge(pn, socValue, changePowerCmdType, changePower,chargeOrderNo); - + PileStartCharge pileStartCharge = + new PileStartCharge(pn, socValue, changePowerCmdType, changePower, chargeOrderNo); + this.Channel.WriteAndFlushAsync(pileStartCharge); return Result.Success(chargeOrderNo); } + /// /// 3.7.3 监控平台远程停止充电桩充电 /// @@ -537,14 +559,33 @@ public class ChargerClient : TcpClient { return Result.Fail($"充电机{BinNo}未连接"); } - + Log().Info( $"SendStartOutCharger pn={pn}, stopReason={stopReason}"); - PileStopCharge pileStopCharge=new PileStopCharge(pn,stopReason); + PileStopCharge pileStopCharge = new PileStopCharge(pn, stopReason); this.Channel.WriteAndFlushAsync(pileStopCharge); return Result.Success(); } + + /// + /// 3.7.9 监控平台发送充电桩功率调节指令 + /// + /// + /// + /// + public Result SendPileAdjustPower(byte pn, float expectedOperatingPower) + { + if (!Connected) + { + return Result.Fail($"charger-{BinNo} disconnect"); + } + + PileAdjustPower powerRegulation = new PileAdjustPower(pn, expectedOperatingPower); + this.Channel.WriteAndFlushAsync(powerRegulation); + return Result.Success(); + } + /// /// /// @@ -570,7 +611,7 @@ public class ChargerClient : TcpClient /// /// /// - public Result StartCharge(byte chargeSoc, float chargePower) + public Result StartCharge(byte chargeSoc, float chargePower, int startType) { if (string.IsNullOrWhiteSpace(BinNo)) { @@ -589,17 +630,17 @@ public class ChargerClient : TcpClient } BatteryNo = binInfo.BatteryNo; - if (string.IsNullOrWhiteSpace(BatteryNo)) + if (string.IsNullOrWhiteSpace(BatteryNo) || "-1" == BatteryNo) { return Result.Fail($"charger-{BinNo} battery not exist"); } - if (binInfo.AmtLock == 1) + if (binInfo.AmtLock == 1) { return Result.Fail($"仓-{BinNo} 被锁定"); } - if (binInfo.CanChargeFlag == 0) + if (binInfo.CanChargeFlag == 0) { return Result.Fail($"仓-{BinNo} 被禁用"); } @@ -620,6 +661,9 @@ public class ChargerClient : TcpClient return Result.Fail(chargeOrderNo.Msg); } + SwapOrderBatteryRepository swapOrderBatteryRepository = AppInfo.Container.Resolve(); + SwapOrderBattery? swapOrder = swapOrderBatteryRepository.QueryLatestOrderNoByBatterySn(BatteryNo); + ChargeOrderNo = chargeOrderNo.Data; _chargeOrderRepository.Insert(new ChargeOrder() { @@ -628,7 +672,9 @@ public class ChargerClient : TcpClient CmdStatus = 0, ChargerNo = BinNo, ChargeMode = 1, - StartMode = 1 + SwapOrderSn = swapOrder?.SwapOrderSn, + StartMode = 1, + StartType = startType }); return Result.Success(true, "发送成功"); @@ -659,4 +705,33 @@ public class ChargerClient : TcpClient ChannelUtils.AddAttr(Channel, ChargerConst.EqmCode, sn); ChannelUtils.AddAttr(Channel, ChargerConst.DestAddr, destAddr); } + + + /// + /// 获取尖峰平谷字节数组 + /// + /// + /// 鉴权消息体字节数组 + private SetPeakValleyTime GetMsgContents(SetPeakValleyTime timeRng) + { + for (int i = 0; i < timeRng.NumberTime; i++) + { + + PropertyInfo propertyStartTimePeriod1 = timeRng.GetType().GetProperty("StartHH" + (i + 1)); + propertyStartTimePeriod1.SetValue(timeRng, BitUtls.ByteToBCD(Convert.ToByte(timeRng.GetType().GetProperty("StartHH" + (i + 1)).GetValue(timeRng)))); + + + PropertyInfo propertyStartTimePeriod2 = timeRng.GetType().GetProperty("StartMM" + (i + 1)); + propertyStartTimePeriod2.SetValue(timeRng, BitUtls.ByteToBCD(Convert.ToByte(timeRng.GetType().GetProperty("StartMM" + (i + 1)).GetValue(timeRng)))); + + PropertyInfo propertyTimePeriodPeakIden = timeRng.GetType().GetProperty("TimePeak" + (i + 1)); + propertyTimePeriodPeakIden.SetValue(timeRng, timeRng.GetType().GetProperty("TimePeak" + (i + 1)).GetValue(timeRng)); + + + + } + + return timeRng; + } + } diff --git a/Service/Charger/Client/ChargerPile.cs b/Service/Charger/Client/ChargerPile.cs index 240b1a6..a5f3f07 100644 --- a/Service/Charger/Client/ChargerPile.cs +++ b/Service/Charger/Client/ChargerPile.cs @@ -46,5 +46,10 @@ public class ChargerPile /// 充电枪的唯一标识码 /// public string? pn { get; set; } + + /// + /// 工作状态 + /// + public byte WorkStatus { get; set; } } \ No newline at end of file diff --git a/Service/Charger/Codec/Decoder.cs b/Service/Charger/Codec/Decoder.cs index 869098d..6826491 100644 --- a/Service/Charger/Codec/Decoder.cs +++ b/Service/Charger/Codec/Decoder.cs @@ -250,6 +250,7 @@ public class Decoder : ByteToMessageDecoder 12 => ModelConvert.Decode(bytes), 11 => ModelConvert.Decode(bytes), 14 => ModelConvert.Decode(bytes), + 10 => ModelConvert.Decode(bytes), _ => throw new InvalidOperationException("This should never be reached"), }, #endregion diff --git a/Service/Charger/Common/ChargerConst.cs b/Service/Charger/Common/ChargerConst.cs index ccc94df..b3996f9 100644 --- a/Service/Charger/Common/ChargerConst.cs +++ b/Service/Charger/Common/ChargerConst.cs @@ -4,13 +4,15 @@ namespace Service.Charger.Common; public static class ChargerConst { - + public static readonly AttributeKey ChargerSn = AttributeKey.ValueOf("charger_sn"); - public static readonly AttributeKey WaterCoolSn = AttributeKey.ValueOf("water_cool_sn"); public static readonly AttributeKey EqmTypeNo = AttributeKey.ValueOf("eqm_type_no"); public static readonly AttributeKey EqmCode = AttributeKey.ValueOf("eqm_code"); public static readonly AttributeKey DestAddr = AttributeKey.ValueOf("dest_addr"); + + public static readonly string DateFormat = "yyMMddHHmmss"; + public static readonly string yyyyMMddHHmmss = "yyyyMMddHHmmss"; public static readonly byte[] StartChar = { 0x68 /* ,0xEE*/ }; public static readonly string AuthCode = "szhckj01"; @@ -27,13 +29,13 @@ public enum ChargingStatus { UnKnown=0, StartChargingSuccess=1, - + StopChargingSuccess=4, Authed=5, AuthFailed=6, - + StartChargingFailed=7, // Charging, StopChargingFailed=8 -} \ No newline at end of file +} diff --git a/Service/Charger/Common/ChargerUtils.cs b/Service/Charger/Common/ChargerUtils.cs index e0ca4b3..ab3e2aa 100644 --- a/Service/Charger/Common/ChargerUtils.cs +++ b/Service/Charger/Common/ChargerUtils.cs @@ -58,6 +58,12 @@ public static class ChargerUtils return StaticStationInfo.StationNo + DateTime.Now.ToString(ChargerConst.DateFormat) + GetRandomNumLimit99(); } + + public static string GenChargeOrderNo(string chargerSn) + { + return StaticStationInfo.StationNo +chargerSn + DateTime.Now.ToString(ChargerConst.yyyyMMddHHmmss) + + GetRandomNumLimit99(); + } /// /// 根据云平台下发,计算本地充电机枪号 /// @@ -84,7 +90,7 @@ public static class ChargerUtils throw new ArgumentException("转换失败"); } } - + public static string GetOutChargerCode(string number) { int parsedNumber; @@ -99,10 +105,10 @@ public static class ChargerUtils { throw new ArgumentException("转换失败"); } - + } - - + + /// /// 根据本地充电枪编号,充电机code,计算云平台下发充电枪编号 /// @@ -185,4 +191,4 @@ public static class ChargerUtils return results; } -} \ No newline at end of file +} diff --git a/Service/Charger/Common/WaterCoolerConst.cs b/Service/Charger/Common/WaterCoolerConst.cs new file mode 100644 index 0000000..906fe6d --- /dev/null +++ b/Service/Charger/Common/WaterCoolerConst.cs @@ -0,0 +1,8 @@ +using DotNetty.Common.Utilities; + +namespace Service.Charger.Common; + +public class WaterCoolerConst +{ + public static readonly AttributeKey WaterCoolSn = AttributeKey.ValueOf("water_cool_sn"); +} \ No newline at end of file diff --git a/Service/Charger/Handler/FinishStartChargingHandler.cs b/Service/Charger/Handler/FinishStartChargingHandler.cs index 9ea4c71..9b149fa 100644 --- a/Service/Charger/Handler/FinishStartChargingHandler.cs +++ b/Service/Charger/Handler/FinishStartChargingHandler.cs @@ -2,6 +2,7 @@ using DotNetty.Transport.Channels; using Entity.DbModel.Station; using HybirdFrameworkCore.Autofac.Attribute; +using HybirdFrameworkCore.Utils; using log4net; using Repository.Station; using Service.Charger.Client; @@ -28,7 +29,7 @@ namespace HybirdFrameworkServices.Charger.Handler public BinInfoRepository BinInfoRepository { get; set; } public ChargeOrderRepository ChargeOrderRepository { get; set; } public SwapOrderBatteryRepository SwapOrderBatteryRepository { get; set; } - + private static readonly ILog Log = LogManager.GetLogger(typeof(FinishStartChargingHandler)); protected override void ChannelRead0(IChannelHandlerContext ctx, FinishStartCharging msg) { @@ -47,7 +48,7 @@ namespace HybirdFrameworkServices.Charger.Handler { ChargeOrderRepository.Update(it => it.SwapOrderSn == swapOrderBattery.SwapOrderSn, it => it.Sn == client.ChargeOrderNo); - + List orders = ChargeOrderRepository.QueryBySwapOrderAndBatterySn(swapOrderBattery.SwapOrderSn, client.BatteryNo); if (orders.Count > 0) @@ -59,32 +60,39 @@ namespace HybirdFrameworkServices.Charger.Handler { cloudChargeOrder = order.CloudChargeOrder; } - + } if (string.IsNullOrWhiteSpace(cloudChargeOrder)) { - cloudChargeOrder = ChargerUtils.GenChargeOrderSn(); + cloudChargeOrder = ChargerUtils.GenChargeOrderNo(sn); } HashSet hashSet = orders.Select(it => it.Id).ToHashSet(); ChargeOrderRepository.Update(it => it.CloudChargeOrder == cloudChargeOrder, it => hashSet.Contains(it.Id)); } - - + + } else { Log.Error($"can not find swapOrderBattery by {client.BatteryNo}"); } - + } else if (msg.Result == 1) { client.IsCharged = false; } + if (ObjUtils.IsNotNullOrWhiteSpace( client.ChargeOrderNo)) + { + ChargeOrderRepository.Update(it => it.CmdStatus == (msg.Result == 0 ? 1 : 2), + it => it.Sn == client.ChargeOrderNo); + } + + 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}"); diff --git a/Service/Charger/Handler/FinishStopChargingHandler.cs b/Service/Charger/Handler/FinishStopChargingHandler.cs index e578840..ffd697f 100644 --- a/Service/Charger/Handler/FinishStopChargingHandler.cs +++ b/Service/Charger/Handler/FinishStopChargingHandler.cs @@ -50,7 +50,10 @@ namespace Service.Charger.Handler if (ObjUtils.IsNotEmpty(orders)) { List list = orders.Select(it => it.Id).ToList(); - ChargeOrderRepository.Update(it => it.CanUpload == 1, it => list.Contains(it.Id)); + + ChargeOrderRepository.Update(it => new ChargeOrder() + {CanUpload = 1 ,ReportingTimes=0 }, + it => list.Contains(it.Id)); } } } diff --git a/Service/Charger/Handler/OutCharger/PileAdjustPowerHandler.cs b/Service/Charger/Handler/OutCharger/PileAdjustPowerHandler.cs new file mode 100644 index 0000000..ca2a1c7 --- /dev/null +++ b/Service/Charger/Handler/OutCharger/PileAdjustPowerHandler.cs @@ -0,0 +1,22 @@ +using DotNetty.Transport.Channels; +using log4net; +using Service.Charger.Client; +using Service.Charger.Msg.Charger.OutCharger.Resp; + +namespace Service.Charger.Handler.OutCharger; +/// +/// 3.7.10 充电桩应答功率调节指令 +/// +public class PileAdjustPowerHandler : SimpleChannelInboundHandler, IBaseHandler +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(PileAdjustPowerHandler)); + + + protected override void ChannelRead0(IChannelHandlerContext ctx, PileAdjustPowerRes msg) + { + if (ClientMgr.TryGetClient(ctx.Channel, out var sn, out var client)) + { + Log.Info($"receive {msg} from {sn}"); + } + } +} \ No newline at end of file diff --git a/Service/Charger/Handler/OutCharger/PileStartChargeResHandler.cs b/Service/Charger/Handler/OutCharger/PileStartChargeResHandler.cs index b2913bd..33c92d2 100644 --- a/Service/Charger/Handler/OutCharger/PileStartChargeResHandler.cs +++ b/Service/Charger/Handler/OutCharger/PileStartChargeResHandler.cs @@ -11,6 +11,7 @@ using Service.Charger.Msg.Charger.OutCharger.Resp; using Service.Charger.Msg.Http.Resp; namespace Service.Charger.Handler.OutCharger; + /// /// 3.7.2 充电桩响应远程启动充电 /// @@ -20,7 +21,7 @@ public class PileStartChargeResHandler : SimpleChannelInboundHandler t.BatteryNo == client.BatteryNo, t => t.No == client.BinNo) > 0) diff --git a/Service/Charger/Handler/RecordChargeHandler.cs b/Service/Charger/Handler/RecordChargeHandler.cs index 6fc15e6..8252886 100644 --- a/Service/Charger/Handler/RecordChargeHandler.cs +++ b/Service/Charger/Handler/RecordChargeHandler.cs @@ -41,6 +41,8 @@ namespace Service.Charger.Handler if(ClientMgr.TryGetClient(ctx.Channel, out var sn, out var client)) { + + float[] powersPeriods = new float[4] { 0, 0, 0, 0 }; //元素索引顺序代表值;1:尖;2:峰;3:平;4:谷 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;} @@ -70,6 +72,7 @@ namespace Service.Charger.Handler if (db == null) { + TimeSpan timeSpan = endTime - startTime; ChargeOrder chargeOrder = new ChargeOrder() { Sn = client.ChargeOrderNo, @@ -79,7 +82,7 @@ namespace Service.Charger.Handler StartSoc = msg.SocBefore, StopSoc = msg.SocAfter, - ChargeTimeCount = (int)(endTime - startTime).TotalMinutes, + ChargeTimeCount = (int)Math.Round(timeSpan.TotalMinutes), ElecCount = Convert.ToDecimal(msg.ChargingPower), AcElecCount = Convert.ToDecimal(msg.AcMeterElecCount), StartAcElec = Convert.ToDecimal(msg.AcMeterDataBefore), @@ -87,15 +90,16 @@ namespace Service.Charger.Handler StartDcElec = Convert.ToDecimal(msg.DcMeterDataBefore), StopDcElec = Convert.ToDecimal(msg.DcMeterDataAfter), SharpElecCount = Convert.ToDecimal(powersPeriods[0]), - PeakElecCount = Convert.ToDecimal(powersPeriods[0]), - FlatElecCount = Convert.ToDecimal(powersPeriods[0]), - ValleyElecCount = Convert.ToDecimal(powersPeriods[0]), + PeakElecCount = Convert.ToDecimal(powersPeriods[1]), + FlatElecCount = Convert.ToDecimal(powersPeriods[2]), + ValleyElecCount = Convert.ToDecimal(powersPeriods[3]), AcSharpElecCount = Convert.ToDecimal(acPowersPeriods[0]), - AcPeakElecCount = Convert.ToDecimal(acPowersPeriods[0]), - AcFlatElecCount = Convert.ToDecimal(acPowersPeriods[0]), - AcValleyElecCount = Convert.ToDecimal(acPowersPeriods[0]), + AcPeakElecCount = Convert.ToDecimal(acPowersPeriods[1]), + AcFlatElecCount = Convert.ToDecimal(acPowersPeriods[2]), + AcValleyElecCount = Convert.ToDecimal(acPowersPeriods[3]), ChargeMode = msg.ChargeMode, - StartMode = msg.StartMode + StartMode = msg.StartMode, + StartType = 2 }; _chargeOrderRepository.Insert(chargeOrder); @@ -107,7 +111,7 @@ namespace Service.Charger.Handler db.StartSoc = msg.SocBefore; db.StopSoc = msg.SocAfter; TimeSpan? timeSpan = (db.EndTime - db.StartTime); - db.ChargeTimeCount= timeSpan?.Minutes; + db.ChargeTimeCount= (int)Math.Round(Convert.ToDecimal(timeSpan?.TotalMinutes)); db.ElecCount = Convert.ToDecimal(msg.ChargingPower); db.AcElecCount = Convert.ToDecimal(msg.AcMeterElecCount); db.StartAcElec = Convert.ToDecimal(msg.AcMeterDataBefore); @@ -115,19 +119,21 @@ namespace Service.Charger.Handler db.StartDcElec = Convert.ToDecimal(msg.DcMeterDataBefore); db.StopDcElec = Convert.ToDecimal(msg.DcMeterDataAfter); db.SharpElecCount = Convert.ToDecimal(powersPeriods[0]); - db.PeakElecCount = Convert.ToDecimal(powersPeriods[0]); - db.FlatElecCount = Convert.ToDecimal(powersPeriods[0]); - db.ValleyElecCount = Convert.ToDecimal(powersPeriods[0]); + db.PeakElecCount = Convert.ToDecimal(powersPeriods[1]); + db.FlatElecCount = Convert.ToDecimal(powersPeriods[2]); + db.ValleyElecCount = Convert.ToDecimal(powersPeriods[3]); db.AcSharpElecCount = Convert.ToDecimal(acPowersPeriods[0]); - db.AcPeakElecCount = Convert.ToDecimal(acPowersPeriods[0]); - db.AcFlatElecCount = Convert.ToDecimal(acPowersPeriods[0]); - db.AcValleyElecCount = Convert.ToDecimal(acPowersPeriods[0]); + db.AcPeakElecCount = Convert.ToDecimal(acPowersPeriods[1]); + db.AcFlatElecCount = Convert.ToDecimal(acPowersPeriods[2]); + db.AcValleyElecCount = Convert.ToDecimal(acPowersPeriods[3]); db.ChargeMode = msg.ChargeMode; db.StartMode = msg.StartMode; _chargeOrderRepository.Update(db); } ctx.Channel.WriteAndFlushAsync(new RecordChargeRespData()); + + Log.Info($"receive {msg} from {sn}"); } } diff --git a/Service/Charger/Handler/RemoteStartChargingResHandler.cs b/Service/Charger/Handler/RemoteStartChargingResHandler.cs index 35d60eb..21d3d49 100644 --- a/Service/Charger/Handler/RemoteStartChargingResHandler.cs +++ b/Service/Charger/Handler/RemoteStartChargingResHandler.cs @@ -45,8 +45,7 @@ namespace Service.Charger.Handler ChargeOrderRepository.Update( i => new ChargeOrder() { - CmdStatus = 1, - StartTime = client.ChargingStartTime + CmdStatus = 1 }, it => it.Id == one.Id); } @@ -63,4 +62,4 @@ namespace Service.Charger.Handler } } } -} \ No newline at end of file +} diff --git a/Service/Charger/Handler/ResponseSettingHandler.cs b/Service/Charger/Handler/ResponseSettingHandler.cs new file mode 100644 index 0000000..a21abde --- /dev/null +++ b/Service/Charger/Handler/ResponseSettingHandler.cs @@ -0,0 +1,34 @@ +using System.Text; +using DotNetty.Transport.Channels; +using HybirdFrameworkCore.Autofac.Attribute; +using log4net; +using Service.Charger.Client; +using Service.Charger.Handler; +using Service.Charger.Msg.Charger.Req; +using Service.Charger.Msg.Charger.Resp; +using Service.Charger.Msg.Host.Resp; + +namespace HybirdFrameworkServices.Charger.Handler +{ + /// + /// 3.4.10 监控网关响应尖峰平谷设置 + /// + /// 1,保存日志到log + /// + /// + [Order(8)] + [Scope("InstancePerDependency")] + public class ResponseSettingHandler : SimpleChannelInboundHandler, IBaseHandler + { + private static readonly ILog Log = LogManager.GetLogger(typeof(ResponseSettingHandler)); + protected override void ChannelRead0(IChannelHandlerContext ctx, ResponseSetting msg) + { + if (ClientMgr.TryGetClient(ctx.Channel, out var sn, out var client)) + { + Log.Info($"receive {msg} from {sn}"); + + } + + } + } +} diff --git a/Service/Charger/Handler/UpAlarmHandler.cs b/Service/Charger/Handler/UpAlarmHandler.cs index ac67b01..b210823 100644 --- a/Service/Charger/Handler/UpAlarmHandler.cs +++ b/Service/Charger/Handler/UpAlarmHandler.cs @@ -8,9 +8,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using Common.Const; -using Entity.DbModel.Station; -using Repository.Station; namespace Service.Charger.Handler { @@ -20,125 +17,12 @@ namespace Service.Charger.Handler { private static readonly ILog Log = LogManager.GetLogger(typeof(UpAlarmHandler)); - public EquipAlarmRecordRepository EquipAlarmRecordRepository { get; set; } - - public EquipAlarmDefineRepository EquipAlarmDefineRepository { get; set; } - - public EquipAlarmProcessRecordRepository EquipAlarmProcessRecordRepository { get; set; } - - protected override void ChannelRead0(IChannelHandlerContext ctx, UpAlarm msg) { if (ClientMgr.TryGetClient(ctx.Channel, out string sn, out var client)) { Log.Info($"receive {msg} from {sn}"); - - if (msg != null && msg.BatteryFaultCode > 4000) - { - SaveAlarmInfo(new List { msg.BatteryFaultCode.ToString() }, sn); - } - } - } - - private void SaveAlarmInfo(List lstAlarm, string sn) - { - string charger = "charger"; - - if (lstAlarm.Count > 0) - { - #region 有报警比较两边差异,新出现的报警就添加,消失的报警就处理并记录 - - var lstEquipAlarmRecord = - EquipAlarmRecordRepository.QueryListByClause(e => e.EquipTypeCode == 0).ToList(); - var sqllstAlarm = lstEquipAlarmRecord.Select(obj => obj.ErrorCode).ToList(); - - // 找出实时报警中存在但数据库中不存在的元素 - List uniqueToList1 = lstAlarm.Except(sqllstAlarm).ToList(); - - // 找出数据库中存在但实时报警中不存在的元素 - List uniqueToList2 = sqllstAlarm.Except(lstAlarm).ToList(); - - if (uniqueToList1.Count > 0) - { - //这里要添加新的报警数据 - foreach (var errorCode in uniqueToList1) - { - EquipAlarmDefine? alarmDefine = - EquipAlarmDefineRepository.SelectByEquipCodeAndErrorCode(0, charger, - errorCode); - if (alarmDefine != null) - { - EquipAlarmRecord record = new EquipAlarmRecord() - { - EquipTypeCode = alarmDefine.EquipTypeCode, - EquipCode = sn, - ErrorCode = alarmDefine.ErrorCode, - ErrorLevel = alarmDefine.ErrorLevel, - ErrorMsg = alarmDefine.ErrorMsg, - ProcessMethod = alarmDefine.ProcessMethod, - StartTime = DateTime.Now - }; - EquipAlarmRecordRepository.Insert(record); - } - } - } - - if (uniqueToList2.Count > 0) - { - //这些是要清除实时报警,并且处理记录的。 - // 使用LINQ找出ErrorCode在uniqueToList2中的EquipAlarmRecord对象 - List filteredObjectList = lstEquipAlarmRecord - .Where(obj => uniqueToList2.Contains(obj.ErrorCode)) - .ToList(); - foreach (var VARIABLE in filteredObjectList) - { - EquipAlarmProcessRecord EquipAlarmProcessRecord = new EquipAlarmProcessRecord(); - EquipAlarmProcessRecord.EquipTypeCode = VARIABLE.EquipTypeCode; - EquipAlarmProcessRecord.EquipCode = VARIABLE.EquipCode; - EquipAlarmProcessRecord.ErrorCode = VARIABLE.ErrorCode; - EquipAlarmProcessRecord.ErrorLevel = VARIABLE.ErrorLevel; - EquipAlarmProcessRecord.ErrorMsg = VARIABLE.ErrorMsg; - EquipAlarmProcessRecord.ProcessMethod = VARIABLE.ProcessMethod; - EquipAlarmProcessRecord.StartTime = VARIABLE.StartTime; - EquipAlarmProcessRecord.ProcessTime = DateTime.Now; - - EquipAlarmProcessRecordRepository.Insert(EquipAlarmProcessRecord); - } - - EquipAlarmRecordRepository.Delete(filteredObjectList); - } - - #endregion - } - else - { - #region 没报警把已处理记录更新并删除实时报警 - - var lstEquipAlarmRecord = - EquipAlarmRecordRepository.QueryListByClause(e => e.EquipTypeCode == 0).ToList(); - - if (lstEquipAlarmRecord.Count > 0) - { - foreach (var VARIABLE in lstEquipAlarmRecord) - { - EquipAlarmProcessRecord EquipAlarmProcessRecord = new EquipAlarmProcessRecord(); - EquipAlarmProcessRecord.EquipTypeCode = VARIABLE.EquipTypeCode; - EquipAlarmProcessRecord.EquipCode = VARIABLE.EquipCode; - EquipAlarmProcessRecord.ErrorCode = VARIABLE.ErrorCode; - EquipAlarmProcessRecord.ErrorLevel = VARIABLE.ErrorLevel; - EquipAlarmProcessRecord.ErrorMsg = VARIABLE.ErrorMsg; - EquipAlarmProcessRecord.ProcessMethod = VARIABLE.ProcessMethod; - EquipAlarmProcessRecord.StartTime = VARIABLE.StartTime; - EquipAlarmProcessRecord.ProcessTime = DateTime.Now; - - EquipAlarmProcessRecordRepository.Insert(EquipAlarmProcessRecord); - } - - EquipAlarmRecordRepository.Delete(e => e.EquipTypeCode == 0); - } - - #endregion } } } -} \ No newline at end of file +} diff --git a/Service/Charger/Handler/UploadRemoteSignalDataHandler.cs b/Service/Charger/Handler/UploadRemoteSignalDataHandler.cs index 1e839cb..1a387e3 100644 --- a/Service/Charger/Handler/UploadRemoteSignalDataHandler.cs +++ b/Service/Charger/Handler/UploadRemoteSignalDataHandler.cs @@ -10,6 +10,9 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Common.Const; +using Entity.DbModel.Station; +using Repository; using Repository.Station; namespace Service.Charger.Handler @@ -24,10 +27,18 @@ namespace Service.Charger.Handler { private static readonly ILog Log = LogManager.GetLogger(typeof(UploadRemoteSignalDataHandler)); private BinInfoRepository BinInfoRepository { get; set; } - - public UploadRemoteSignalDataHandler(BinInfoRepository _binInfoRepository) + private EquipAlarmRecordRepository EquipAlarmRecordRepository { get; set; } + private EquipAlarmProcessRecordRepository EquipAlarmProcessRecordRepository { get; set; } + private EquipAlarmDefineRepository EquipAlarmDefineRepository { get; set; } + public UploadRemoteSignalDataHandler(BinInfoRepository _binInfoRepository, + EquipAlarmRecordRepository _EquipAlarmRecordRepository, + EquipAlarmProcessRecordRepository _EquipAlarmProcessRecordRepository, + EquipAlarmDefineRepository _EquipAlarmDefineRepository) { BinInfoRepository = _binInfoRepository; + EquipAlarmRecordRepository = _EquipAlarmRecordRepository; + EquipAlarmProcessRecordRepository = _EquipAlarmProcessRecordRepository; + EquipAlarmDefineRepository = _EquipAlarmDefineRepository; } protected override void ChannelRead0(IChannelHandlerContext ctx, UploadRemoteSignalData msg) @@ -45,11 +56,145 @@ namespace Service.Charger.Handler //Desc:充电状态;0-未知;1-正在充电;2-无电池;3-禁用;4-充电完成 if (msg.WorkStatus == 1) - BinInfoRepository.Update(i => i.ChargeStatus == msg.WorkStatus, i => i.No == sn); + BinInfoRepository.Update(i => i.ChargeStatus == msg.WorkStatus, i => i.ChargerNo == sn); else if (msg.WorkStatus == 2 || msg.WorkStatus == 0) { - BinInfoRepository.Update(i => i.ChargeStatus == 4, i => i.No == sn); + BinInfoRepository.Update(i => i.ChargeStatus == 4, i => i.ChargerNo == sn); } + + #region 充电机故障显示 +//不准确,注释 +//if(sn=="C2008") + //if (msg.TotalWarning||msg.TotalError) + { + var lstEquipAlarmDefine = EquipAlarmDefineRepository.QueryListByClause(i => i.EquipCode == "充电机"); + + List lstAlarm = new List(); + + if (msg.EmergencyStop)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="1").ToList()[0].ErrorCode); + if (msg.SmokeFault)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="2").ToList()[0].ErrorCode); + if (msg.ChargeACInputCircuitBreakerFault)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="3").ToList()[0].ErrorCode); + if (msg.DcBusPositElecContactorRefuFault)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="4").ToList()[0].ErrorCode); + if (msg.DcBusNegatElecContactorRefuFault)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="5").ToList()[0].ErrorCode); + if (msg.DcBusPositElecFusesFault)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="6").ToList()[0].ErrorCode); + if (msg.DDcBusNegatElecFusesFault)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="7").ToList()[0].ErrorCode); + if (msg.ChargingInterfaceLockError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="8").ToList()[0].ErrorCode); + if (msg.ChargerFanError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="9").ToList()[0].ErrorCode); + if (msg.ArresterError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="10").ToList()[0].ErrorCode); + if (msg.InsulationDetectionAlarm)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="11").ToList()[0].ErrorCode); + if (msg.InsulationDetectionError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="12").ToList()[0].ErrorCode); + if (msg.BatteryPolarityReverseError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="13").ToList()[0].ErrorCode); + if (msg.VeConGuidanceFailure)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="14").ToList()[0].ErrorCode); + if (msg.ChargingOverTempError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="15").ToList()[0].ErrorCode); + if (msg.InterfaceOverFaulty)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="16").ToList()[0].ErrorCode); + if (msg.ChargingGunNotHomingError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="17").ToList()[0].ErrorCode); + if (msg.BmsConnError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="18").ToList()[0].ErrorCode); + if (msg.ChargerInputOverVoltageError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="19").ToList()[0].ErrorCode); + if (msg.ChargerInputUnderVoltageError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="20").ToList()[0].ErrorCode); + if (msg.DcBusOutputOverVoltageError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="21").ToList()[0].ErrorCode); + if (msg.DcBusOutputUnderVoltageError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="22").ToList()[0].ErrorCode); + if (msg.DcBusOutputOverCurrentError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="23").ToList()[0].ErrorCode); + if (!msg.VehicleConnStatus)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="24").ToList()[0].ErrorCode); + if (!msg.ChargeStationGunHolderStatus)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="25").ToList()[0].ErrorCode); + if (!msg.ChargingInterfaceLockStatus)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="26").ToList()[0].ErrorCode); + if (msg.PositiveDcTransmissionContactorStatus)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="27").ToList()[0].ErrorCode); + if (msg.NegativeDcTransmissionContactorStatus)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="28").ToList()[0].ErrorCode); + if (msg.EntranceGuardError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="29").ToList()[0].ErrorCode); + if (msg.PConA3dhesionFailure)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="30").ToList()[0].ErrorCode); + if (msg.NConadhesionFailure)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="31").ToList()[0].ErrorCode); + if (msg.ReliefCircuitError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="32").ToList()[0].ErrorCode); + if (msg.ConActivated)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="33").ToList()[0].ErrorCode); + if (msg.ConAdhesionFailure)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="34").ToList()[0].ErrorCode); + if (msg.AuxiliaryPowerError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="35").ToList()[0].ErrorCode); + if (msg.ModuleOutputReverseError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="36").ToList()[0].ErrorCode); + if (msg.AcContactorStatus)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="37").ToList()[0].ErrorCode); + if (msg.ChargingGunOverTempWarning)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="38").ToList()[0].ErrorCode); + if (msg.ChargerOverTempWarning)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="39").ToList()[0].ErrorCode); + if (msg.MeterConnError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="40").ToList()[0].ErrorCode); + if (msg.MeterDataError)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="41").ToList()[0].ErrorCode); + if (msg.WaterloggingWarning)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="42").ToList()[0].ErrorCode); + if (msg.ReversePowerWarning)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="43").ToList()[0].ErrorCode); + if (msg.BatteryPackAuxiliaryPowerStatus)lstAlarm.Add(sn+lstEquipAlarmDefine.Where(i=>i.ErrorCode=="44").ToList()[0].ErrorCode); + + + //查询当前充电机的实时报警信息 + var lstNowEquipAlarmRecord = EquipAlarmRecordRepository.QueryListByClause(i => i.EquipCode == sn); + var sqllstAlarm=lstNowEquipAlarmRecord.Select(obj => obj.ErrorCode).ToList();//当前报警列表 + + // 找出实时报警中存在但数据库中不存在的元素 + List uniqueToList1 = lstAlarm.Except(sqllstAlarm).ToList(); + + // 找出数据库中存在但实时报警中不存在的元素 + List uniqueToList2 = sqllstAlarm.Except(lstAlarm).ToList(); + if (uniqueToList1.Count > 0) + {//这里要添加新的报警数据 + foreach (var errorCode in uniqueToList1) + { + EquipAlarmDefine? alarmDefine = EquipAlarmDefineRepository.QueryByClause(i=>i.ErrorCode==errorCode.Replace(sn,"")); + if (alarmDefine != null) + { + EquipAlarmRecord record = new EquipAlarmRecord() + { + EquipTypeCode = alarmDefine.EquipTypeCode, + EquipCode = sn, + ErrorCode = errorCode, + ErrorLevel = alarmDefine.ErrorLevel, + ErrorMsg = alarmDefine.ErrorMsg, + ProcessMethod = alarmDefine.ProcessMethod, + StartTime= DateTime.Now + }; + EquipAlarmRecordRepository.Insert(record); + } + } + } + else if (uniqueToList2.Count > 0) + { + //这些是要清除实时报警,并且处理记录的。 + // 使用LINQ找出ErrorCode在uniqueToList2中的EquipAlarmRecord对象 + List filteredObjectList = lstNowEquipAlarmRecord + .Where(obj => uniqueToList2.Contains(obj.ErrorCode)) + .ToList(); + foreach (var VARIABLE in filteredObjectList) + { + EquipAlarmProcessRecord EquipAlarmProcessRecord = new EquipAlarmProcessRecord(); + EquipAlarmProcessRecord.EquipTypeCode = VARIABLE.EquipTypeCode; + EquipAlarmProcessRecord.EquipCode = VARIABLE.EquipCode; + EquipAlarmProcessRecord.ErrorCode = VARIABLE.ErrorCode; + EquipAlarmProcessRecord.ErrorLevel = VARIABLE.ErrorLevel; + EquipAlarmProcessRecord.ErrorMsg = VARIABLE.ErrorMsg; + EquipAlarmProcessRecord.ProcessMethod = VARIABLE.ProcessMethod; + EquipAlarmProcessRecord.StartTime = VARIABLE.StartTime; + EquipAlarmProcessRecord.ProcessTime=DateTime.Now; + + EquipAlarmProcessRecordRepository.Insert(EquipAlarmProcessRecord); + } + EquipAlarmRecordRepository.Delete(filteredObjectList); + } + } + //else + //{ + /*var lstNowEquipAlarmRecord = EquipAlarmRecordRepository.QueryListByClause(i => i.EquipCode == sn); + if (lstNowEquipAlarmRecord.Count > 0) + { + foreach (var VARIABLE in lstNowEquipAlarmRecord) + { + EquipAlarmProcessRecord EquipAlarmProcessRecord = new EquipAlarmProcessRecord(); + EquipAlarmProcessRecord.EquipTypeCode = VARIABLE.EquipTypeCode; + EquipAlarmProcessRecord.EquipCode = VARIABLE.EquipCode; + EquipAlarmProcessRecord.ErrorCode = VARIABLE.ErrorCode; + EquipAlarmProcessRecord.ErrorLevel = VARIABLE.ErrorLevel; + EquipAlarmProcessRecord.ErrorMsg = VARIABLE.ErrorMsg; + EquipAlarmProcessRecord.ProcessMethod = VARIABLE.ProcessMethod; + EquipAlarmProcessRecord.StartTime = VARIABLE.StartTime; + EquipAlarmProcessRecord.ProcessTime=DateTime.Now; + + EquipAlarmProcessRecordRepository.Insert(EquipAlarmProcessRecord); + } + } + EquipAlarmRecordRepository.Delete(i=>i.Id>0);*/ + //} + + #endregion } } } diff --git a/Service/Charger/Handler/UploadTelemetryDataHandler.cs b/Service/Charger/Handler/UploadTelemetryDataHandler.cs index 69b7456..39f0f60 100644 --- a/Service/Charger/Handler/UploadTelemetryDataHandler.cs +++ b/Service/Charger/Handler/UploadTelemetryDataHandler.cs @@ -1,5 +1,6 @@ using DotNetty.Transport.Channels; using HybirdFrameworkCore.Autofac.Attribute; +using HybirdFrameworkCore.Configuration; using HybirdFrameworkCore.Redis; using log4net; using Newtonsoft.Json; @@ -17,7 +18,7 @@ namespace Service.Charger.Handler public class UploadTelemetryDataHandler : SimpleChannelInboundHandler, IBaseHandler { private static readonly ILog Log = LogManager.GetLogger(typeof(UploadTelemetryDataHandler)); - + public RedisHelper RedisHelper { get; set; } protected override void ChannelRead0(IChannelHandlerContext ctx, UploadTelemetryData msg) { @@ -26,8 +27,11 @@ namespace Service.Charger.Handler msg.ChargerNo = sn; Log.Info($"receive {msg} from {sn}"); - RedisHelper.PublishAsync("UploadTelemetryData", JsonConvert.SerializeObject(msg)); - + if (!AppSettingsConstVars.DisabledTask.Contains("UploadTelemetryData")) + { + RedisHelper.PublishAsync("UploadTelemetryData", JsonConvert.SerializeObject(msg)); + } + client.UploadTelemetryData = msg; //充电机实时充电功率 client.RealTimeChargePower = msg.HighVoltageAcquisitionCurrent * msg.HighVoltageAcquisitionVoltage; diff --git a/Service/Charger/Msg/Charger/OutCharger/Resp/PileAdjustPowerRes.cs b/Service/Charger/Msg/Charger/OutCharger/Resp/PileAdjustPowerRes.cs new file mode 100644 index 0000000..7c4c1b3 --- /dev/null +++ b/Service/Charger/Msg/Charger/OutCharger/Resp/PileAdjustPowerRes.cs @@ -0,0 +1,35 @@ +using HybirdFrameworkCore.Autofac.Attribute; + +namespace Service.Charger.Msg.Charger.OutCharger.Resp; +/// +/// 3.7.10 充电桩应答功率调节指令 +/// +public class PileAdjustPowerRes: 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/Resp/UpBms.cs b/Service/Charger/Msg/Charger/Resp/UpBms.cs index b42593d..a48e0a5 100644 --- a/Service/Charger/Msg/Charger/Resp/UpBms.cs +++ b/Service/Charger/Msg/Charger/Resp/UpBms.cs @@ -7,16 +7,72 @@ namespace Service.Charger.Msg.Charger.Resp /// public class UpBms : ASDU { + /// + /// 记录类型 + /// + [Property(0, 8)] + public byte RecordType { get; set; } + /// ///PGN码 /// - [Property(0, 3, PropertyReadConstant.Byte)] + [Property(8, 24)] public string Pgn { get; set; } /// - /// CAN 帧数据 + /// 报警级别 + /// 0:正常 + /// 1:1 级报警 3:3 级报警 5:5 级报警 其余保留 + /// + [Property(32, 8)] + public short AlarmLevel { get; set; } + + /// + /// 电池箱所在位置编号 分辨率:1/位,偏移量:0,数值范围:1~250 + /// + [Property(40, 8)] + public short BatteryBoxLocationNumber { get; set; } + + /// + /// 电池箱能输出的最大电流值 分辨率:0.05A/位,偏移量:-1600A,数值范围:-1600A ~ 1612.75A + /// + [Property(48, 16, scale: 0.05, offset: -1600)] + public short BatteryBoxMaximumCurrentOutput { get; set; } + + /// + /// 电池箱能承受最大反馈电流值 分辨率:0.05A/位,偏移量:-1600A,数值范围:-1600A~1612.75A + /// + [Property(64, 16, scale: 0.05, offset: -1600)] + public short BatteryMaximumFeedback { get; set; } + + /// + /// 电池箱风扇状态 0:关闭1:开启 2:不可用 3:不可用 + /// + [Property(80, 2)] + public byte BatteryBoxFanStatus { get; set; } + + /// + /// 加热装置状态 0:关闭1:开启 2:不可用 3:不可用 + /// + [Property(82, 2)] + public byte HeaterCondition { get; set; } + + /// + /// 均衡状态 0:关闭1:开启 2:不可用 3:不可用 + /// + [Property(84, 2)] + public byte EquilibriumState { get; set; } + + /// + /// 高压互锁状态 0 断开 1 连接 + /// + [Property(86, 2)] + public byte HighVoltageInterlockState { get; set; } + + /// + /// 保留 /// - [Property(0, 8, PropertyReadConstant.Byte)] - public string CanData { get; set; } + [Property(88, 8)] + public short Reserve { get; set; } } } \ No newline at end of file diff --git a/Service/Charger/Msg/Host/Req/OutCharger/Req/PileAdjustPower.cs b/Service/Charger/Msg/Host/Req/OutCharger/Req/PileAdjustPower.cs new file mode 100644 index 0000000..eb31fc0 --- /dev/null +++ b/Service/Charger/Msg/Host/Req/OutCharger/Req/PileAdjustPower.cs @@ -0,0 +1,42 @@ +using HybirdFrameworkCore.Autofac.Attribute; + +namespace Service.Charger.Msg.Host.Req.OutCharger.Req; +/// +/// 3.7.9 监控平台发送充电桩功率调节指令 +/// +public class PileAdjustPower: 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(8, 16, PropertyReadConstant.Bit, 0.1, 1)] + public float ExpectPower { get; set; } + + + public PileAdjustPower(byte pn,float expectPower) + { + RecordType = 9; + FrameTypeNo = 51; + MsgBodyCount = 1; + TransReason = 3; + PublicAddr = 0; + MsgBodyAddr = new byte[] { 0, 0, 0 }; + + Pn = pn; + ExpectPower = expectPower; + } +} \ 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 index 4bd0419..a0c74a4 100644 --- a/Service/Charger/Msg/Host/Req/OutCharger/Req/PileStartCharge.cs +++ b/Service/Charger/Msg/Host/Req/OutCharger/Req/PileStartCharge.cs @@ -1,6 +1,6 @@ using HybirdFrameworkCore.Autofac.Attribute; -namespace Service.Charger.Msg.Host.OutCharger.Req; +namespace Service.Charger.Msg.Host.Req.OutCharger.Req; /// /// 3.7.1 监控平台远程启动充电桩充电 diff --git a/Service/Charger/Msg/Host/Req/OutCharger/Req/PileStopCharge.cs b/Service/Charger/Msg/Host/Req/OutCharger/Req/PileStopCharge.cs index ee47f01..e011708 100644 --- a/Service/Charger/Msg/Host/Req/OutCharger/Req/PileStopCharge.cs +++ b/Service/Charger/Msg/Host/Req/OutCharger/Req/PileStopCharge.cs @@ -1,6 +1,6 @@ using HybirdFrameworkCore.Autofac.Attribute; -namespace Service.Charger.Msg.Host.OutCharger.Req; +namespace Service.Charger.Msg.Host.Req.OutCharger.Req; /// /// 3.7.3 监控平台远程停止充电桩充电 diff --git a/Service/Charger/Msg/Http/Req/PileRealtimeReq.cs b/Service/Charger/Msg/Http/Req/PileRealtimeReq.cs new file mode 100644 index 0000000..1128c8e --- /dev/null +++ b/Service/Charger/Msg/Http/Req/PileRealtimeReq.cs @@ -0,0 +1,45 @@ +namespace Service.Charger.Msg.Http.Req; +/// +/// 9.2.1.5 站控上报充电枪实时数据上报 +/// +public class PileRealtimeReq +{ + /// + /// 换电站编码 + /// + public string sn { get; set; } + + + /// + /// Desc:充电枪编号 + /// Default: + /// Nullable:True + /// + public string? pn { get; set; } + + /// + /// 充电枪状态 + /// + public int ps { get; set; } + + /// + /// 插枪状态 + /// + public int con { get; set; } + + + /// + /// 单枪输出电压 + /// + public float pov { get; set; } + + /// + /// 单枪输出电流 + /// + public float poe { get; set; } + + /// + /// 单枪故障代码 + /// + public int ec { get; set; } +} \ No newline at end of file diff --git a/Service/Charger/MyTask/AutoChargeTask.cs b/Service/Charger/MyTask/AutoChargeTask.cs index 9bf4e17..1afc403 100644 --- a/Service/Charger/MyTask/AutoChargeTask.cs +++ b/Service/Charger/MyTask/AutoChargeTask.cs @@ -5,6 +5,7 @@ using HybirdFrameworkCore.AutoTask; using HybirdFrameworkCore.Entity; using HybirdFrameworkCore.Utils; using log4net; +using Newtonsoft.Json; using Repository.Station; using Service.Charger.Client; using Service.Init; @@ -41,8 +42,8 @@ public class AutoChargeTask : ITask DateTime now = DateTime.Now; - List binInfos = binInfoRepository.Query(); - if (binInfos.Count < 0) + List allBinInfos = binInfoRepository.Query(); + if (allBinInfos.Count < 0) { Log.Info("lack of binInfos"); return; @@ -54,7 +55,7 @@ public class AutoChargeTask : ITask chargerList.Where(it => it.AutoCharge == 1).Select(it => it.Code).ToHashSet(); - binInfos = binInfos.Where(it => autoChargeSet.Contains(it.ChargerNo)).ToList(); + var binInfos = allBinInfos.Where(it => autoChargeSet.Contains(it.ChargerNo)).ToList(); if (ObjUtils.IsEmpty(binInfos)) { @@ -76,10 +77,24 @@ public class AutoChargeTask : ITask List elecPriceModelVersionDetails = elecPriceModelVersionDetailRepository.QueryListByClause(it => it.Version == elecPriceModelVersion.Version); - ElecPriceModelVersionDetail? elecPriceModelVersionDetail = elecPriceModelVersionDetails.Where(i => + /*ElecPriceModelVersionDetail? elecPriceModelVersionDetail = elecPriceModelVersionDetails.Where(i => i.StartHour <= now.Hour && i.StartMinute <= now.Minute && i.EndHour > now.Hour && - i.EndMinute > now.Minute).FirstOrDefault(); + i.EndMinute > now.Minute).FirstOrDefault();*/ + ElecPriceModelVersionDetail? elecPriceModelVersionDetail = null; + + foreach (var VARIABLE in elecPriceModelVersionDetails) + { + // 构造开始和结束的DateTime对象,使用当前日期的年月日 + DateTime startTime = new DateTime(now.Year, now.Month, now.Day, VARIABLE.StartHour, + VARIABLE.StartMinute, 0); + DateTime endTime = new DateTime(now.Year, now.Month, now.Day, VARIABLE.EndHour, VARIABLE.EndMinute, 0); + if (DateTime.Now >= startTime && DateTime.Now <= endTime) + { + elecPriceModelVersionDetail = VARIABLE; + } + } + if (elecPriceModelVersionDetail == null) { Log.Info("lack of effective elecPriceModelVersionDetail"); @@ -110,12 +125,12 @@ public class AutoChargeTask : ITask now.Second >= start[2] && now.Second < end[2]; }).ToList();*/ - foreach (var VARIABLE in batteryOpModelDetails) + foreach (var t in batteryOpModelDetails) { - if (DateTime.Now >= DateTime.Parse(VARIABLE.StartTime) && - DateTime.Now <= DateTime.Parse(VARIABLE.EndTime)) + if (DateTime.Now >= DateTime.Parse(t.StartTime) && + DateTime.Now <= DateTime.Parse(t.EndTime)) { - opModelDetails.Add(VARIABLE); + opModelDetails.Add(t); } } @@ -126,63 +141,70 @@ public class AutoChargeTask : ITask } int needBatteryCount = opModelDetails[0].BatteryCount ?? 8; - List canSwapList = binInfos.Where(it => + List canSwapList = allBinInfos.Where(it => it.Soc != null && Convert.ToSingle(it.Soc) >= StaticStationInfo.SwapSoc && it.CanSwapFlag == 1) .ToList(); - if (canSwapList.Count == needBatteryCount) + /*if (canSwapList.Count == needBatteryCount) { Log.Info($"lack of needBatteryCount {needBatteryCount}"); return; - } - - if (canSwapList.Count > needBatteryCount) - { - List chargingList = binInfos.Where(it => it.ChargeStatus == 1).ToList(); + }*/ - int needStopCount = chargingList.Count - (canSwapList.Count - needBatteryCount); - if (needStopCount > 0) - { - //停电量低的 - chargingList.Sort((a, b) => (a.Soc ?? 0).CompareTo(b.Soc ?? 0)); - for (int i = 0; i < needStopCount; i++) - { - Log.Info($"auto stop charge {chargingList[i].No}"); - ClientMgr.GetBySn(chargingList[i].No)?.SendRemoteStopCharging(); - } - } - } - else + List chargingList = binInfos.Where(it => it.ChargeStatus == 1).ToList(); + Log.Info( + $"chargingList={JsonConvert.SerializeObject(chargingList.Select(info => info.Code + "," + info.ChargerNo).ToList())}"); + if (canSwapList.Count <= needBatteryCount) { List canChargeList = binInfos.Where(it => - it.Soc != null && Convert.ToSingle(it.Soc) < StaticStationInfo.ChargeSoc && - it.CanChargeFlag == 1) + it.Soc != null && Convert.ToSingle(it.Soc) < StaticStationInfo.SwapSoc && + it.CanChargeFlag == 1 && it.Exists == 1) .ToList(); //启动电量高的 canChargeList.Sort((a, b) => (b.Soc ?? 0).CompareTo(a.Soc ?? 0)); + byte chargeSoc = StaticStationInfo.ChargeSoc; int count = needBatteryCount - canSwapList.Count; + Log.Info( + $"need start count={count}, canChargeList={JsonConvert.SerializeObject(canChargeList.Select(info => info.Code + "," + info.ChargerNo).ToList())}"); int number = 0; foreach (var binInfo in canChargeList) { - float? chargePower = EquipInfoRepository.QueryPowerByCode(binInfo.Code); + float? chargePower = EquipInfoRepository.QueryPowerByCode(binInfo.ChargerNo); float? power = chargePower == null ? StaticStationInfo.ChargePower : chargePower; - Result? result = ClientMgr.GetBySn(binInfo.ChargerNo) - ?.StartCharge(chargeSoc, (float)power); - if (result is { IsSuccess: true }) - { - Log.Info($"auto start charge {binInfo.ChargerNo}"); - number++; - } - - if (count == number) + if (binInfo.ChargeStatus != 1) { - Log.Info($"auto start charge count {count}"); - break; + //没有充电时候在充电 + Result? result = ClientMgr.GetBySn(binInfo.ChargerNo) + ?.StartCharge(chargeSoc, (float)power, 0); + if (result is { IsSuccess: true }) + { + Log.Info($"auto start charge {binInfo.ChargerNo}"); + number++; + } + + if (count == number) + { + Log.Info($"auto start charge count {count}"); + break; + } } } } + #region 达到充电SOC自动停 + + List stopList = chargingList.Where(it => it.Soc > StaticStationInfo.ChargeSoc).ToList(); + Log.Info( + $"need stop count={stopList.Count}, stopList={JsonConvert.SerializeObject(stopList.Select(info => info.Code + "," + info.ChargerNo).ToList())}"); + foreach (var binInfo in stopList) + { + Log.Info($"auto stop charge {binInfo.No} soc:{binInfo.Soc}"); + ClientMgr.GetBySn(binInfo.ChargerNo)?.SendRemoteStopCharging(); + } + + #endregion + #endregion } catch (Exception e) @@ -205,4 +227,4 @@ public class AutoChargeTask : ITask { _stop = false; } -} \ No newline at end of file +} diff --git a/Service/Charger/MyTask/EmeterEnergyRecordTask.cs b/Service/Charger/MyTask/EmeterEnergyRecordTask.cs index 408aedb..9b59963 100644 --- a/Service/Charger/MyTask/EmeterEnergyRecordTask.cs +++ b/Service/Charger/MyTask/EmeterEnergyRecordTask.cs @@ -36,6 +36,11 @@ public class EmeterEnergyRecordTask : ITask { List list = new List(); List listEmeterEnergyChanges = new List(); + + DateTime time = DateTime.Now.AddDays(-7); + EmeterEnergyRepository.Delete(i=>i.UploadTimei.UploadTime +/// 9.2.1.7 站控上报充电枪充电遥测数据 +/// +[Scope] +public class PileChargeRealtimeTask : ITask +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(PileChargeRealtimeTask)); + private volatile bool _stop; + + + public string Name() + { + return "PileChargeRealtimeTask"; + } + + public int Interval() + { + return 1000 * 30; + } + + public void Handle() + { + + ConcurrentDictionary chargerClients = ClientMgr.Dictionary; + + if (chargerClients.Values.Count <= 0) + { + return; + } + + foreach (var clientPair in chargerClients) + { + ChargerClient client = clientPair.Value; + + ProcessClient(client, 1); + ProcessClient(client, 2); + } + } + + private void ProcessClient(ChargerClient client, byte gunNumber) + { + if (client.GunCharged[gunNumber]) + { + ChargerPile chargerPile = client.ChargerPile[gunNumber]; + PileUploadTelemetry telemetry = client.PileUploadTelemetry[gunNumber]; + PileUploadRemoteSignal pileUploadRemoteSignal = client.PileUploadRemoteSignal[gunNumber]; + + PileChargeRealtimeReq req = new PileChargeRealtimeReq + { + sn = StaticStationInfo.StationNo, + con = chargerPile.con, + cosn = chargerPile.cosn, + pn = chargerPile.pn, + rv = telemetry.BmsNeedVoltage, + re = telemetry.BmsNeedCurrent, + cm = telemetry.ChargeMode, + cdv = telemetry.BmsChargingVoltage, + cde = telemetry.BmsChargingCurrent, + soc = telemetry.CurrentSoc, + tr = telemetry.EstimatedRemainingTime, + pov = telemetry.DcMeterVoltage, + poe = telemetry.DcMeterCurrent, + tct = telemetry.ChargingTime, + lbtn = telemetry.MinTempDetectionPointNo, + lbt = telemetry.MinBatteryTemp, + hbtn = telemetry.MaxTempDetectionPointNo, + hbt = telemetry.MaxBatteryTemp, + hlbva = pileUploadRemoteSignal.ChargerInputOverVoltageError ? 1 : + pileUploadRemoteSignal.ChargerInputUnderVoltageError ? 2 : 0, + bia = pileUploadRemoteSignal.InsulationDetectionAlarm ? 1 : 0 + }; + + HttpUtil.SendPostRequest(req, "http://127.0.0.1:5034/api/OutCharger/SendPileChargeRealtime"); + } + } + + public bool Stoped() + { + return _stop; + } + + public void Stop() + { + _stop = true; + } + + public void ResetStop() + { + _stop = false; + } +} \ No newline at end of file diff --git a/Service/Charger/MyTask/PileRealtimeTask.cs b/Service/Charger/MyTask/PileRealtimeTask.cs new file mode 100644 index 0000000..3b6bf4c --- /dev/null +++ b/Service/Charger/MyTask/PileRealtimeTask.cs @@ -0,0 +1,100 @@ +using System.Collections.Concurrent; +using Common.Util; +using HybirdFrameworkCore.Autofac.Attribute; +using HybirdFrameworkCore.AutoTask; +using log4net; +using Service.Charger.Client; +using Service.Charger.Msg.Charger.OutCharger.Req; +using Service.Charger.Msg.Http.Req; +using Service.Init; + +namespace Service.Charger.MyTask; +/// +/// 9.2.1.5 站控上报充电枪实时数据上报 +/// +[Scope] +public class PileRealtimeTask : ITask +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(PileRealtimeTask)); + private volatile bool _stop; + + public string Name() + { + return "PileRealtimeTask"; + } + + public int Interval() + { + return 1000 * 5; + } + + public void Handle() + { + ConcurrentDictionary chargerClients = ClientMgr.Dictionary; + + if (chargerClients.Values.Count <= 0) + { + return; + } + + foreach (var kvp in chargerClients) + { + ChargerClient client = kvp.Value; + + HandleChargerPile(client, 1); + HandleChargerPile(client, 2); + } + } + + + + public bool Stoped() + { + return _stop; + } + + public void Stop() + { + _stop = true; + } + + public void ResetStop() + { + _stop = false; + } + + private void HandleChargerPile(ChargerClient client, byte pileIndex) + { + if (client.ChargedPile[pileIndex]) + { + ChargerPile chargerPile = client.ChargerPile[pileIndex]; + PileUploadTelemetry pileUploadTelemetry = client.PileUploadTelemetry[pileIndex]; + + PileRealtimeReq req = new PileRealtimeReq + { + sn = StaticStationInfo.StationNo, + pn = chargerPile.pn, + ps = GetPileStatus(chargerPile.WorkStatus), + pov = pileUploadTelemetry.BmsChargingVoltage, + poe = pileUploadTelemetry.BmsChargingCurrent, + ec = 0 + }; + + HttpUtil.SendPostRequest(req, "http://127.0.0.1:5034/api/OutCharger/SendPileRealtime"); + } + } + + private int GetPileStatus(int workStatus) + { + return workStatus switch + { + 0 => 1, + 1 => 3, + 2 => 4, + 3 => 5, + _ => 1 + }; + } + + +} \ No newline at end of file diff --git a/Service/Job/TestJob.cs b/Service/Job/TestJob.cs new file mode 100644 index 0000000..46f577c --- /dev/null +++ b/Service/Job/TestJob.cs @@ -0,0 +1,32 @@ +using Autofac; +using HybirdFrameworkCore.Autofac; +using HybirdFrameworkCore.Autofac.Attribute; +using HybirdFrameworkCore.Job; +using log4net; +using Repository.System; + +namespace Service.Job; + +[Scope] +public class TestJob : AbstractCronJob +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(TestJob)); + + private SysConfigRepository SysConfigRepository = AppInfo.Container.Resolve(); + + protected override Task Handle() + { + Log.Info("work"); + return Task.CompletedTask; + } + + protected override string Key() + { + return "test-job"; + } + + protected override string Cron() + { + return "0/2 * * * * ?"; + } +} diff --git a/Service/MyTask/BatteryInfoUploadTask.cs b/Service/MyTask/BatteryInfoUploadTask.cs new file mode 100644 index 0000000..21f350d --- /dev/null +++ b/Service/MyTask/BatteryInfoUploadTask.cs @@ -0,0 +1,93 @@ +using Autofac; +using Entity.DbModel.Station; +using HybirdFrameworkCore.Autofac; +using HybirdFrameworkCore.Autofac.Attribute; +using HybirdFrameworkCore.AutoTask; +using HybirdFrameworkCore.Redis; +using log4net; +using Newtonsoft.Json; +using Repository.Station; +using Service.Charger.Client; +using Service.Init; +using Service.Swap.Dto; + +namespace Service.MyTask; +[Scope] +public class BatteryInfoUploadTask : ITask +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(BatteryInfoUploadTask)); + private RedisHelper RedisHelper { get; set; } = AppInfo.Container.Resolve(); + private BinInfoRepository BinInfoRepository = AppInfo.Container.Resolve(); + + private static bool _stop; + + public string Name() + { + return nameof(BatteryInfoUploadTask); + } + + public int Interval() + { + return 1000 * 1; + } + + public void Handle() + { + Log.Info("begin start BatteryInfoUploadTask uoload"); + DateTime now = DateTime.Now; + List binInfos = BinInfoRepository.Query(); + List batInfos = binInfos.Where(it => it.Exists == 1).Select(it => + { + Log.Info("start BatteryInfoUploadTask uoload bininfo select"); + ChargerClient? client = ClientMgr.GetBySn(it.ChargerNo); + SingleBatInfo batInfo = new SingleBatInfo() + { + bn = it.BatteryNo, + bt = now, + sd = it.ChargerNo, + cno = Convert.ToInt32(it.ChargerNo.Substring(1)), + el = 0, + hc = Convert.ToInt32(it.ChargeStatus), + hst=Convert.ToSingle(client?.BatteryPackDataVoltage?.CellTemperatureMax), + hsv = Convert.ToSingle(client?.BatteryPackDataVoltage?.CellVoltageMax), + lst = Convert.ToSingle(client?.BatteryPackDataVoltage?.CellTemperatureMin), + lsv = Convert.ToSingle(client?.BatteryPackDataVoltage?.CellVoltageMin), + soc = Convert.ToSingle(it.Soc), + soe = Convert.ToSingle(it.Soe), + soh = Convert.ToSingle(it.Soh) + }; + return batInfo; + }).ToList(); + BatDataInfo batDataInfo = new BatDataInfo + { + batn = batInfos.Count, + sn = StaticStationInfo.StationNo, + datainfo = batInfos + }; + + try + { + Log.Info("start BatteryInfoUploadTask send redis"); + RedisHelper.PublishAsync("BatteryInfoUploadTask", JsonConvert.SerializeObject(batDataInfo)); + } + catch (Exception e) + { + Log.Info($"BatteryInfoUploadTask send redis error{e}"); + } + } + + public bool Stoped() + { + return _stop; + } + + public void Stop() + { + _stop = true; + } + + public void ResetStop() + { + _stop = false; + } +} diff --git a/Service/MyTask/ZipLogTask.cs b/Service/MyTask/ZipLogTask.cs new file mode 100644 index 0000000..84a7cbb --- /dev/null +++ b/Service/MyTask/ZipLogTask.cs @@ -0,0 +1,9 @@ +using HybirdFrameworkCore.Autofac.Attribute; +using HybirdFrameworkCore.AutoTask; + +namespace Service.MyTask; + +[Scope] +public class ZipLogTask : LogZipTask +{ +} diff --git a/Service/RealTime/RealtimeClient.cs b/Service/RealTime/RealtimeClient.cs new file mode 100644 index 0000000..5f20bc2 --- /dev/null +++ b/Service/RealTime/RealtimeClient.cs @@ -0,0 +1,90 @@ +using log4net; +using Microsoft.AspNetCore.SignalR.Client; +using Newtonsoft.Json; + +namespace Service.RealTime; + +public class RealtimeClient +{ + /// + /// 循环时用的随机数值 + /// + private static UInt16 _cysSeqNum = 0; + + + /// + /// 计算循环用UInt16随机数值 + /// + /// + public static UInt16 GetUInt16SeqNum() + { + if (_cysSeqNum < 65535) + { + _cysSeqNum += 1; + } + else + { + _cysSeqNum = 1; + } + + return _cysSeqNum; + } + + private static readonly ILog Log = LogManager.GetLogger(typeof(RealtimeClient)); + + private static readonly JsonSerializerSettings settings = new JsonSerializerSettings() + { + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + DateFormatString = "yyyy-MM-dd HH:mm:ss", + NullValueHandling = NullValueHandling.Ignore + }; + + private static HubConnection? hubConnection; + + public static void Init() + { + hubConnection = new HubConnectionBuilder().WithUrl("http://127.0.0.1:5034/realtime").WithAutomaticReconnect() + .Build(); + hubConnection.ServerTimeout = TimeSpan.FromMinutes(1); + hubConnection.KeepAliveInterval = TimeSpan.FromMinutes(1); + hubConnection.StartAsync(); + hubConnection.Reconnected += (s) => + { + Log.Info($"{s} connected"); + return Task.CompletedTask; + }; + hubConnection.On("ReceiveMsg", (string msg) => { Log.Info($"receive {msg}"); }); + } + + public static void SendMsg(string msg) + { + hubConnection?.SendAsync("SendMsg", "host", msg); + } + + public static void SendWithoutResult(string msg) + { + hubConnection?.SendAsync("InvokeWithoutResult", JsonConvert.SerializeObject(new RtMsg() + { + Id = GetUInt16SeqNum(), + Cmd = "cmd", + Datetime = DateTime.Now, + FromUser = "charger", + Msg = msg + })); + } + + public static string? SendWithResult(string msg) + { + var result = hubConnection?.InvokeAsync("Invoke", new RtMsg() + { + Id = GetUInt16SeqNum(), + Cmd = "cmd", + Datetime = DateTime.Now, + FromUser = "charger", + Msg = msg + }).Result; + + Log.Info($"result={result}"); + return result.FromUser; + } +} diff --git a/Service/RealTime/RtMsg.cs b/Service/RealTime/RtMsg.cs new file mode 100644 index 0000000..5aac8b7 --- /dev/null +++ b/Service/RealTime/RtMsg.cs @@ -0,0 +1,11 @@ +namespace Service.RealTime; + +public class RtMsg +{ + public int Id { get; set; } + public string FromUser { get; set; } + public string ToUser { get; set; } + public DateTime Datetime { get; set; } + public string Cmd { get; set; } + public string Msg { get; set; } +} diff --git a/Service/Service.csproj b/Service/Service.csproj index 8964429..9b3820c 100644 --- a/Service/Service.csproj +++ b/Service/Service.csproj @@ -27,8 +27,11 @@ + + + @@ -47,4 +50,8 @@ + + + + diff --git a/Service/Station/BinInfoService.cs b/Service/Station/BinInfoService.cs index fef3032..d8b93e9 100644 --- a/Service/Station/BinInfoService.cs +++ b/Service/Station/BinInfoService.cs @@ -16,6 +16,7 @@ public class BinInfoService : BaseServices public BinInfoService(BinInfoRepository binInfoRepository) { _binInfoRepository = binInfoRepository; + this.BaseDal = binInfoRepository; } /// /// 获取仓位数据 diff --git a/Service/Swap/Dto/BatDataInfo.cs b/Service/Swap/Dto/BatDataInfo.cs new file mode 100644 index 0000000..23346a6 --- /dev/null +++ b/Service/Swap/Dto/BatDataInfo.cs @@ -0,0 +1,23 @@ +namespace Service.Swap.Dto +{ + /// + /// 4.2.11.1 换电站电池包数据信息 + /// + public class BatDataInfo + { + /// + /// 场站编码 换电站唯一码 + /// + public string sn { get; set; } + + /// + /// 换电站电池包总 数量 + /// + public int batn { get; set; } + + /// + /// 电池信息 + /// + public List datainfo { get; set; } + } +} diff --git a/Service/Swap/Dto/SingleBatInfo.cs b/Service/Swap/Dto/SingleBatInfo.cs new file mode 100644 index 0000000..e906248 --- /dev/null +++ b/Service/Swap/Dto/SingleBatInfo.cs @@ -0,0 +1,84 @@ +namespace Service.Swap.Dto; + +public class SingleBatInfo +{ + /// + /// 电池序列号 + /// + public string bn { get; set; } + + /// + /// 充电架 ID 按电池架的编号 A1,A2… + /// + public string sd { get; set; } + + /// + /// 所在充电机序号 从 1 开始递增 + /// + public int cno { get; set; } + + /// + /// 是否在充电 0:未知 1:正在充电 2:未电池 + /// + public int hc { get; set; } + + /// + /// 电接头连接状态 0:未知 1:已经连接 2:未连接 + /// + public int el { get; set; } + + /// + /// 剩余能量 单位 0.1 kwh + /// + public float soe { get; set; } + + /// + /// 当前 SOC 0-100 单位 0.1 ,没有充电填 0 + /// + public float soc { get; set; } + + /// + /// 当前 SOH 0-100 单位 0.1 ,没有充电填 0 + /// + public float soh { get; set; } + + /// + /// 最低单体电压 单位 0.01V + /// + public float lsv { get; set; } + + /// + /// 最高单体电压 单位 0.01V + /// + public float hsv { get; set; } + + /// + /// 最低单体温度 单位 0.1℃ + /// + public float lst { get; set; } + + /// + /// 最高单体温度 单位 0.1℃ + /// + public float hst { get; set; } + + /// + /// 单体电池号 从 1 开始递增 + /// + public int sl { get; set; } + + /// + /// 单体电压 每一节电芯的单体电压 单位 0.1V ,如果没有该节电芯的数据,填65535.0 无效值 + /// + public float sv { get; set; } + + /// + /// 单体温度 每一节电芯的单体温度 单位 0.1℃ ,如果没有该节电芯的数据,填65535.0 无效值 + /// + public float st { get; set; } + + /// + /// 更新时间 格式 ”yyyy-MM-dd HH:mm:ss ” + /// + public DateTime bt { get; set; } +} diff --git a/Service/WaterCool/Client/WaterCoolClient.cs b/Service/WaterCool/Client/WaterCoolClient.cs index 7208337..6fb6347 100644 --- a/Service/WaterCool/Client/WaterCoolClient.cs +++ b/Service/WaterCool/Client/WaterCoolClient.cs @@ -46,7 +46,7 @@ public class WaterCoolClient : TcpClient /// public void SessionAttr(string sn, string destAddr) { - ChannelUtils.AddAttr(Channel, ChargerConst.WaterCoolSn, sn); + ChannelUtils.AddAttr(Channel, WaterCoolerConst.WaterCoolSn, sn); ChannelUtils.AddAttr(Channel, ChargerConst.EqmTypeNo, sn); ChannelUtils.AddAttr(Channel, ChargerConst.EqmCode, sn); ChannelUtils.AddAttr(Channel, ChargerConst.DestAddr, destAddr); diff --git a/Service/WaterCool/Client/WaterCoolClientMgr.cs b/Service/WaterCool/Client/WaterCoolClientMgr.cs index 98a5689..9d16b6d 100644 --- a/Service/WaterCool/Client/WaterCoolClientMgr.cs +++ b/Service/WaterCool/Client/WaterCoolClientMgr.cs @@ -43,7 +43,7 @@ public class WaterCoolClientMgr /// public static bool TryGetClient(IChannel channel, out string sn, out WaterCoolClient? client) { - string? snt = ChannelUtils.GetAttr(channel, ChargerConst.WaterCoolSn); + string? snt = ChannelUtils.GetAttr(channel, WaterCoolerConst.WaterCoolSn); if (!string.IsNullOrWhiteSpace(snt)) { diff --git a/WebStarter/Controllers/ChargeController.cs b/WebStarter/Controllers/ChargeController.cs index f8f63fc..3ca32ea 100644 --- a/WebStarter/Controllers/ChargeController.cs +++ b/WebStarter/Controllers/ChargeController.cs @@ -77,13 +77,14 @@ public class ChargeController : ControllerBase return Result.Fail("功率值范围1到360"); } - ChargerClient? chargerClient = ClientMgr.GetBySn(code); + string _code = _binInfoService.QueryByClause(i => i.Code == code).ChargerNo; + ChargerClient? chargerClient = ClientMgr.GetBySn(_code); if (chargerClient != null) { chargerClient.SendPowerRegulation(power); - _equipInfoRepository.Update(i => i.ChargePower == power, it => it.Code == code); + _equipInfoRepository.Update(i => i.ChargePower == power, it => it.Code == _code); return Result.Success(true); } diff --git a/WebStarter/Controllers/OutChargerController.cs b/WebStarter/Controllers/OutChargerController.cs index 0cca89a..e00413e 100644 --- a/WebStarter/Controllers/OutChargerController.cs +++ b/WebStarter/Controllers/OutChargerController.cs @@ -1,4 +1,5 @@ -using HybirdFrameworkCore.Entity; +using Entity.Dto.Resp; +using HybirdFrameworkCore.Entity; using Microsoft.AspNetCore.Mvc; using Repository.Station; using Service.Charger.Client; @@ -32,6 +33,7 @@ public class OutChargerController [Route("SendStartOutCharger")] public Result SendStartOutCharger([FromBody] PileStartChargeHttpReq httpReq) { + string chargerCode = ChargerUtils.GetOutChargerCode(httpReq.pn); byte chargerGunCode = ChargerUtils.GetTheGun(httpReq.pn); @@ -92,4 +94,64 @@ public class OutChargerController return Result.Success(true); } + + /// + /// 给充电枪发送功率调节指令 + /// + /// 充电机code + /// 1枪 or 2枪 + /// 功率 + /// + [HttpGet] + [Route("SendPowerRegulation/{code}/{pn}/{power}")] + public Result SendPowerRegulation(string code,byte pn, float power) + { + if (power <=0 || power > 280) + { + return Result.Fail("功率值范围1到360"); + } + + if (pn != 1 && pn != 2) + { + return Result.Fail("请选择1枪或者2枪"); + } + + ChargerClient? chargerClient = ClientMgr.GetBySn(code); + + if (chargerClient != null) + { + chargerClient.SendPileAdjustPower(pn,power); + return Result.Success(true); + } + + return Result.Fail("充电机未连接"); + } + + /// + /// 获取充电桩信息 + /// + /// + [HttpGet] + [Route("GetChargerPile")] + public Result> GetChargerPile() + { + var chargerClients = ClientMgr.Dictionary; + if (chargerClients.IsEmpty) + { + return Result>.Fail("没有充电机连接"); + } + + var list = chargerClients.Values.Select(client => new ChargerPileResp + { + Sn = client.Sn, + GunChargedOne = client.GunCharged[1], + GunChargedTwo = client.GunCharged[2], + ChargedPileOne = client.ChargedPile[1], + ChargedPileTwo = client.ChargedPile[2], + ChargePilePowerOne = client.ChargePilePower[1], + ChargePilePowerTwo = client.ChargePilePower[2] + }).ToList(); + + return Result>.Success(list); + } } \ No newline at end of file diff --git a/WebStarter/Controllers/Test/GenController.cs b/WebStarter/Controllers/Test/GenController.cs index 843ff1b..95e0100 100644 --- a/WebStarter/Controllers/Test/GenController.cs +++ b/WebStarter/Controllers/Test/GenController.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Mvc; -using Repository.Station; +using Service.RealTime; using SqlSugar; namespace WebStarter.Controllers.Test; @@ -13,7 +13,7 @@ public class GenController : ControllerBase public GenController(ISqlSugarClient sqlSugarClient) { _sqlSugarClient = sqlSugarClient; - + } /// @@ -35,8 +35,28 @@ public class GenController : ControllerBase Console.WriteLine("生成完毕"); } - - + + /// + /// 测试发送 + /// + /// + /// + [HttpGet("send/{msg}")] + public string? Send(string msg) + { + return RealtimeClient.SendWithResult(msg); + } + + /// + /// 测试发送 + /// + /// + /// + [HttpGet("send3/{msg}")] + public void Send3(string msg) + { + RealtimeClient.SendMsg(msg); + } static string ToPascalCase(string input) @@ -54,4 +74,6 @@ public class GenController : ControllerBase return res; } -} \ No newline at end of file + + +} diff --git a/WebStarter/Program.cs b/WebStarter/Program.cs index 09528cf..8eeb5fe 100644 --- a/WebStarter/Program.cs +++ b/WebStarter/Program.cs @@ -4,9 +4,11 @@ using HybirdFrameworkCore.Autofac; using HybirdFrameworkCore.AutoTask; using HybirdFrameworkCore.Configuration; using HybirdFrameworkCore.Entity; +using HybirdFrameworkCore.Job; using HybirdFrameworkCore.Redis; using log4net; using Service.Charger.Client; +using Service.RealTime; using Service.WaterCool.Client; using SqlSugar; using SqlSugar.IOC; @@ -22,10 +24,17 @@ builder.Host.ConfigureContainer(cb => var db = new SqlSugarScope(new ConnectionConfig { ConfigId = AppSettingsConstVars.ConfigId, - ConnectionString = AppSettingsConstVars.DbSqlConnection, // �������ݿ������ַ��� + ConnectionString = AppSettingsConstVars.DbSqlConnection, DbType = AppSettingsConstVars.DbDbType == IocDbType.MySql.ToString() ? DbType.MySql : DbType.SqlServer, IsAutoCloseConnection = true, - InitKeyType = InitKeyType.Attribute // ���ʹ��ʵ��������Խ���������ʶ��������Ϊ InitKeyType.Attribute + InitKeyType = InitKeyType.Attribute + }, c => + { + c.Aop.OnLogExecuting = (sql, pars) => + { + string nativeSql = UtilMethods.GetNativeSql(sql, pars); + log.Info(nativeSql); + }; }); return db; }).As().InstancePerLifetimeScope(); @@ -75,7 +84,6 @@ builder.Services.AddControllers().AddJsonOptions(configure => }); var app = builder.Build(); - // Configure the HTTP request pipeline. app.UseSwagger(); app.UseSwaggerUI(); @@ -97,6 +105,16 @@ AppInfo.Container = app.Services.GetAutofacRoot(); ClientMgr.InitClient(); TaskInit.Init(); +QuartzSchedulerFactory.Init(); +app.Lifetime.ApplicationStopping.Register(QuartzSchedulerFactory.Shutdown); + +bool.TryParse(AppSettingsHelper.GetContent("SignalR", "Enabled"), out var signalrEnabled); +if (signalrEnabled) +{ + RealtimeClient.Init(); +} + + // 水冷连接 WaterCoolClientMgr.InitClient(); diff --git a/WebStarter/WebStarter.csproj b/WebStarter/WebStarter.csproj index 852c130..9827bea 100644 --- a/WebStarter/WebStarter.csproj +++ b/WebStarter/WebStarter.csproj @@ -15,6 +15,7 @@ + diff --git a/WebStarter/appsettings.dev.json b/WebStarter/appsettings.dev.json index 1165b91..5bcf292 100644 --- a/WebStarter/appsettings.dev.json +++ b/WebStarter/appsettings.dev.json @@ -10,7 +10,7 @@ "Url": "http://121.4.95.243:8090/Updates/AutoUpdaterStarter.xml" }, "Redis": { - "Connection": "192.168.2.2:6379,password=123456", + "Connection": "192.168.2.2:6379,password=dev", "InstanceName": "local", "DefaultDB": "8" }, @@ -27,5 +27,13 @@ } } }, + "Log": { + "Src": "D:\\RiderProjects\\hn_back_charger\\WebStarter\\bin\\Debug\\net6.0\\logs", + "Dest": "D:\\zip", + "Days": 7 + }, + "Job": { + "Enabled": false + }, "AllowedHosts": "*" } diff --git a/WebStarter/appsettings.prod.json b/WebStarter/appsettings.prod.json index 82082d3..3ac71ca 100644 --- a/WebStarter/appsettings.prod.json +++ b/WebStarter/appsettings.prod.json @@ -27,5 +27,16 @@ } } }, + "Log": { + "Src": "D:\\RiderProjects\\hn_back_charger\\WebStarter\\bin\\Debug\\net6.0\\logs", + "Dest": "D:\\zip", + "Days": 7 + }, + "Job": { + "Enabled": false + }, + "Task": { + "Disabled": "UploadTelemetryData" + }, "AllowedHosts": "*" } diff --git a/WebStarter/db/qrtz.sql b/WebStarter/db/qrtz.sql new file mode 100644 index 0000000..ee17aaf --- /dev/null +++ b/WebStarter/db/qrtz.sql @@ -0,0 +1,214 @@ +/* + Navicat Premium Data Transfer + + Source Server : rszn + Source Server Type : MySQL + Source Server Version : 80034 (8.0.34-0ubuntu0.22.04.1) + Source Host : 180.76.133.253:16306 + Source Schema : huanneng_dev + + Target Server Type : MySQL + Target Server Version : 80034 (8.0.34-0ubuntu0.22.04.1) + File Encoding : 65001 + + Date: 27/06/2024 17:23:00 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for qrtz_blob_triggers +-- ---------------------------- +DROP TABLE IF EXISTS `qrtz_blob_triggers`; +CREATE TABLE `qrtz_blob_triggers` ( + `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `TRIGGER_NAME` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `TRIGGER_GROUP` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `BLOB_DATA` blob NULL, + PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE, + INDEX `SCHED_NAME`(`SCHED_NAME` ASC, `TRIGGER_NAME` ASC, `TRIGGER_GROUP` ASC) USING BTREE, + CONSTRAINT `qrtz_blob_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for qrtz_calendars +-- ---------------------------- +DROP TABLE IF EXISTS `qrtz_calendars`; +CREATE TABLE `qrtz_calendars` ( + `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `CALENDAR_NAME` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `CALENDAR` blob NOT NULL, + PRIMARY KEY (`SCHED_NAME`, `CALENDAR_NAME`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for qrtz_cron_triggers +-- ---------------------------- +DROP TABLE IF EXISTS `qrtz_cron_triggers`; +CREATE TABLE `qrtz_cron_triggers` ( + `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `TRIGGER_NAME` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `TRIGGER_GROUP` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `CRON_EXPRESSION` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `TIME_ZONE_ID` varchar(80) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE, + CONSTRAINT `qrtz_cron_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for qrtz_fired_triggers +-- ---------------------------- +DROP TABLE IF EXISTS `qrtz_fired_triggers`; +CREATE TABLE `qrtz_fired_triggers` ( + `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `ENTRY_ID` varchar(140) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `TRIGGER_NAME` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `TRIGGER_GROUP` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `INSTANCE_NAME` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `FIRED_TIME` bigint NOT NULL, + `SCHED_TIME` bigint NOT NULL, + `PRIORITY` int NOT NULL, + `STATE` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `JOB_NAME` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `JOB_GROUP` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `IS_NONCONCURRENT` tinyint(1) NULL DEFAULT NULL, + `REQUESTS_RECOVERY` tinyint(1) NULL DEFAULT NULL, + PRIMARY KEY (`SCHED_NAME`, `ENTRY_ID`) USING BTREE, + INDEX `IDX_QRTZ_FT_TRIG_INST_NAME`(`SCHED_NAME` ASC, `INSTANCE_NAME` ASC) USING BTREE, + INDEX `IDX_QRTZ_FT_INST_JOB_REQ_RCVRY`(`SCHED_NAME` ASC, `INSTANCE_NAME` ASC, `REQUESTS_RECOVERY` ASC) USING BTREE, + INDEX `IDX_QRTZ_FT_J_G`(`SCHED_NAME` ASC, `JOB_NAME` ASC, `JOB_GROUP` ASC) USING BTREE, + INDEX `IDX_QRTZ_FT_JG`(`SCHED_NAME` ASC, `JOB_GROUP` ASC) USING BTREE, + INDEX `IDX_QRTZ_FT_T_G`(`SCHED_NAME` ASC, `TRIGGER_NAME` ASC, `TRIGGER_GROUP` ASC) USING BTREE, + INDEX `IDX_QRTZ_FT_TG`(`SCHED_NAME` ASC, `TRIGGER_GROUP` ASC) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for qrtz_job_details +-- ---------------------------- +DROP TABLE IF EXISTS `qrtz_job_details`; +CREATE TABLE `qrtz_job_details` ( + `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `JOB_NAME` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `JOB_GROUP` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `DESCRIPTION` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `JOB_CLASS_NAME` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `IS_DURABLE` tinyint(1) NOT NULL, + `IS_NONCONCURRENT` tinyint(1) NOT NULL, + `IS_UPDATE_DATA` tinyint(1) NOT NULL, + `REQUESTS_RECOVERY` tinyint(1) NOT NULL, + `JOB_DATA` blob NULL, + PRIMARY KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) USING BTREE, + INDEX `IDX_QRTZ_J_REQ_RECOVERY`(`SCHED_NAME` ASC, `REQUESTS_RECOVERY` ASC) USING BTREE, + INDEX `IDX_QRTZ_J_GRP`(`SCHED_NAME` ASC, `JOB_GROUP` ASC) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for qrtz_locks +-- ---------------------------- +DROP TABLE IF EXISTS `qrtz_locks`; +CREATE TABLE `qrtz_locks` ( + `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `LOCK_NAME` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + PRIMARY KEY (`SCHED_NAME`, `LOCK_NAME`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for qrtz_paused_trigger_grps +-- ---------------------------- +DROP TABLE IF EXISTS `qrtz_paused_trigger_grps`; +CREATE TABLE `qrtz_paused_trigger_grps` ( + `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `TRIGGER_GROUP` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + PRIMARY KEY (`SCHED_NAME`, `TRIGGER_GROUP`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for qrtz_scheduler_state +-- ---------------------------- +DROP TABLE IF EXISTS `qrtz_scheduler_state`; +CREATE TABLE `qrtz_scheduler_state` ( + `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `INSTANCE_NAME` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `LAST_CHECKIN_TIME` bigint NOT NULL, + `CHECKIN_INTERVAL` bigint NOT NULL, + PRIMARY KEY (`SCHED_NAME`, `INSTANCE_NAME`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for qrtz_simple_triggers +-- ---------------------------- +DROP TABLE IF EXISTS `qrtz_simple_triggers`; +CREATE TABLE `qrtz_simple_triggers` ( + `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `TRIGGER_NAME` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `TRIGGER_GROUP` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `REPEAT_COUNT` bigint NOT NULL, + `REPEAT_INTERVAL` bigint NOT NULL, + `TIMES_TRIGGERED` bigint NOT NULL, + PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE, + CONSTRAINT `qrtz_simple_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for qrtz_simprop_triggers +-- ---------------------------- +DROP TABLE IF EXISTS `qrtz_simprop_triggers`; +CREATE TABLE `qrtz_simprop_triggers` ( + `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `TRIGGER_NAME` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `TRIGGER_GROUP` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `STR_PROP_1` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `STR_PROP_2` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `STR_PROP_3` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `INT_PROP_1` int NULL DEFAULT NULL, + `INT_PROP_2` int NULL DEFAULT NULL, + `LONG_PROP_1` bigint NULL DEFAULT NULL, + `LONG_PROP_2` bigint NULL DEFAULT NULL, + `DEC_PROP_1` decimal(13, 4) NULL DEFAULT NULL, + `DEC_PROP_2` decimal(13, 4) NULL DEFAULT NULL, + `BOOL_PROP_1` tinyint(1) NULL DEFAULT NULL, + `BOOL_PROP_2` tinyint(1) NULL DEFAULT NULL, + `TIME_ZONE_ID` varchar(80) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE, + CONSTRAINT `qrtz_simprop_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for qrtz_triggers +-- ---------------------------- +DROP TABLE IF EXISTS `qrtz_triggers`; +CREATE TABLE `qrtz_triggers` ( + `SCHED_NAME` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `TRIGGER_NAME` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `TRIGGER_GROUP` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `JOB_NAME` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `JOB_GROUP` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `DESCRIPTION` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `NEXT_FIRE_TIME` bigint NULL DEFAULT NULL, + `PREV_FIRE_TIME` bigint NULL DEFAULT NULL, + `PRIORITY` int NULL DEFAULT NULL, + `TRIGGER_STATE` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `TRIGGER_TYPE` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `START_TIME` bigint NOT NULL, + `END_TIME` bigint NULL DEFAULT NULL, + `CALENDAR_NAME` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `MISFIRE_INSTR` smallint NULL DEFAULT NULL, + `JOB_DATA` blob NULL, + PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE, + INDEX `IDX_QRTZ_T_J`(`SCHED_NAME` ASC, `JOB_NAME` ASC, `JOB_GROUP` ASC) USING BTREE, + INDEX `IDX_QRTZ_T_JG`(`SCHED_NAME` ASC, `JOB_GROUP` ASC) USING BTREE, + INDEX `IDX_QRTZ_T_C`(`SCHED_NAME` ASC, `CALENDAR_NAME` ASC) USING BTREE, + INDEX `IDX_QRTZ_T_G`(`SCHED_NAME` ASC, `TRIGGER_GROUP` ASC) USING BTREE, + INDEX `IDX_QRTZ_T_STATE`(`SCHED_NAME` ASC, `TRIGGER_STATE` ASC) USING BTREE, + INDEX `IDX_QRTZ_T_N_STATE`(`SCHED_NAME` ASC, `TRIGGER_NAME` ASC, `TRIGGER_GROUP` ASC, `TRIGGER_STATE` ASC) USING BTREE, + INDEX `IDX_QRTZ_T_N_G_STATE`(`SCHED_NAME` ASC, `TRIGGER_GROUP` ASC, `TRIGGER_STATE` ASC) USING BTREE, + INDEX `IDX_QRTZ_T_NEXT_FIRE_TIME`(`SCHED_NAME` ASC, `NEXT_FIRE_TIME` ASC) USING BTREE, + INDEX `IDX_QRTZ_T_NFT_ST`(`SCHED_NAME` ASC, `TRIGGER_STATE` ASC, `NEXT_FIRE_TIME` ASC) USING BTREE, + INDEX `IDX_QRTZ_T_NFT_MISFIRE`(`SCHED_NAME` ASC, `MISFIRE_INSTR` ASC, `NEXT_FIRE_TIME` ASC) USING BTREE, + INDEX `IDX_QRTZ_T_NFT_ST_MISFIRE`(`SCHED_NAME` ASC, `MISFIRE_INSTR` ASC, `NEXT_FIRE_TIME` ASC, `TRIGGER_STATE` ASC) USING BTREE, + INDEX `IDX_QRTZ_T_NFT_ST_MISFIRE_GRP`(`SCHED_NAME` ASC, `MISFIRE_INSTR` ASC, `NEXT_FIRE_TIME` ASC, `TRIGGER_GROUP` ASC, `TRIGGER_STATE` ASC) USING BTREE, + CONSTRAINT `qrtz_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) REFERENCES `qrtz_job_details` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/WebStarter/log4net.config b/WebStarter/log4net.config index c2449e8..0afbc89 100644 --- a/WebStarter/log4net.config +++ b/WebStarter/log4net.config @@ -18,7 +18,7 @@ - + @@ -26,144 +26,144 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -172,7 +172,7 @@ - + diff --git a/WebStarter/obj/WebStarter.csproj.nuget.g.targets b/WebStarter/obj/WebStarter.csproj.nuget.g.targets index 5b31449..4ef9a86 100644 --- a/WebStarter/obj/WebStarter.csproj.nuget.g.targets +++ b/WebStarter/obj/WebStarter.csproj.nuget.g.targets @@ -3,6 +3,7 @@ + diff --git a/WinFormStarter/obj/WinFormStarter.csproj.nuget.g.targets b/WinFormStarter/obj/WinFormStarter.csproj.nuget.g.targets index 6a3c0bd..d495d0d 100644 --- a/WinFormStarter/obj/WinFormStarter.csproj.nuget.g.targets +++ b/WinFormStarter/obj/WinFormStarter.csproj.nuget.g.targets @@ -2,6 +2,7 @@ + - \ No newline at end of file +