using Autofac; using DotNetty.Transport.Channels; using Entity.DbModel.Station; using HybirdFrameworkCore.Autofac; using HybirdFrameworkCore.Autofac.Attribute; using HybirdFrameworkCore.Entity; using HybirdFrameworkDriver.Session; using log4net; using Repository.Station; using Service.Charger.Common; using Service.Equipment; using Service.Init; namespace Service.Charger.Client; /// /// 示例程序 /// [Scope("SingleInstance")] public static class ClientMgr { private static readonly ILog Log = LogManager.GetLogger(typeof(ClientMgr)); public static readonly Dictionary Dictionary = new(); private static bool AutoChargeWorking { get; set; } public static ChargerClient? GetBySn(string sn) { Dictionary.TryGetValue(sn, out var o); return o; } /// /// 通过channel获取client /// /// /// /// 获取不到,client则为空 /// public static bool TryGetClient(IChannel channel, out string sn, out ChargerClient? client) { string? snt = ChannelUtils.GetAttr(channel, ChargerConst.ChargerSn); if (!string.IsNullOrWhiteSpace(snt)) { var chargerClient = GetBySn(snt); if (chargerClient != null) { sn = snt; client = chargerClient; return true; } } sn = string.Empty; client = null; return false; } public static void AddBySn(string sn, ChargerClient client) { Dictionary[sn] = client; } //TODO 连接、鉴权,开始充电,结束充电,设置尖峰平谷,读取尖峰平谷,发送功率调节指令,发送辅助源控制指令,下发掉线停止充电, public static void InitClient() { EquipInfoRepository equipInfoRepository = AppInfo.Container.Resolve(); EquipNetInfoRepository netInfoRepository = AppInfo.Container.Resolve(); BinInfoRepository binInfoRepository = AppInfo.Container.Resolve(); List equipInfos = equipInfoRepository.QueryListByClause(it => it.TypeCode == (int)EquipmentType.Charger); if (equipInfos.Count > 0) { Dictionary set = equipInfos.ToDictionary(it => it.Code, it => it); List equipNetInfos = netInfoRepository.QueryListByClause(it => set.Keys.Contains(it.Code)); Dictionary binInfoMap = binInfoRepository.QueryListByClause(it => set.Keys.Contains(it.ChargerNo)) .ToDictionary(it => it.ChargerNo, it => it); foreach (EquipNetInfo netInfo in equipNetInfos) { Task.Run(() => { binInfoMap.TryGetValue(netInfo.Code, out var binInfo); ConnClient(netInfo, binInfo); }); } StartAutoChargeThread(); StartQueryBatteryInfoThread(); } } private static void StartQueryBatteryInfoThread() { Thread thread = new Thread(QueryBatteryInfo) { Name = @"QueryBatteryInfoThread" }; thread.Start(); } private static void QueryBatteryInfo() { while (true) { try { Thread.Sleep(1000); foreach (var (key, client) in Dictionary) { client.SendQueryBattery(); } } catch (Exception e) { Log.Error("QueryBatteryInfo error", e); } } } /// /// /// private static void StartAutoChargeThread() { if (!AutoChargeWorking) { Thread thread = new Thread(AutoChargeThread) { Name = @"AutoChargeThread" }; thread.Start(); AutoChargeWorking = true; } } /// /// /// private static void AutoChargeThread() { ElecPriceModelVersionRepository elecPriceModelVersionRepository = AppInfo.Container.Resolve(); ElecPriceModelVersionDetailRepository elecPriceModelVersionDetailRepository = AppInfo.Container.Resolve(); BatteryOpModelRepository batteryOpModelRepository = AppInfo.Container.Resolve(); BatteryOpModelDetailRepository batteryOpModelDetailRepository = AppInfo.Container.Resolve(); BinInfoRepository binInfoRepository = AppInfo.Container.Resolve(); while (true) { try { Thread.Sleep(1000 * 30); DateTime now = DateTime.Now; if (StaticStationInfo.AutoChargeEnabled != 1) { Log.Info("AutoChargeEnabled = 0 continue"); continue; } List binInfos = binInfoRepository.Query(); if (binInfos.Count < 0) { Log.Info("lack of binInfos"); continue; } #region 电价模型 int ceid = StaticStationInfo.Ceid; ElecPriceModelVersion elecPriceModelVersion = elecPriceModelVersionRepository.QueryByClause(i => i.Version == ceid); if (elecPriceModelVersion == null) { Log.Info("lack of effective elecPriceModelVersion"); continue; } List elecPriceModelVersionDetails = elecPriceModelVersionDetailRepository.QueryListByClause(it => it.Version == elecPriceModelVersion.Version); ElecPriceModelVersionDetail? elecPriceModelVersionDetail = elecPriceModelVersionDetails.Where(i => i.StartHour <= now.Hour && i.StartMinute <= now.Minute && i.EndHour > now.Hour && i.EndMinute > now.Minute).FirstOrDefault(); if (elecPriceModelVersionDetail == null) { Log.Info("lack of effective elecPriceModelVersionDetail"); continue; } #endregion #region 运营模型 int oid = int.Parse(StaticStationInfo.Oid); BatteryOpModel batteryOpModel = batteryOpModelRepository.QueryByClause(d => d.ModelId == oid); if (batteryOpModel == null) { Log.Info("lack of effective batteryOpModel"); continue; } List batteryOpModelDetails = batteryOpModelDetailRepository.QueryListByClause(d => d.ModelId == oid); List opModelDetails = batteryOpModelDetails.Where(t => { List start = t.StartTime.Split(":").Select(int.Parse).ToList(); List end = t.EndTime.Split(":").Select(int.Parse).ToList(); return now.Hour >= start[0] && now.Hour < end[0] && now.Minute >= start[1] && now.Minute < end[1] && now.Second >= start[2] && now.Second < end[2] ; }).ToList(); if (opModelDetails.Count == 0) { Log.Info("lack of effective batteryOpModelDetails"); continue; } int needBatteryCount = opModelDetails[0].BatteryCount ?? 8; List canSwapList = binInfos.Where(it => it.Soc != null && Convert.ToSingle(it.Soc) >= StaticStationInfo.SwapSoc).ToList(); if (canSwapList.Count == needBatteryCount) { Log.Info($"lack of needBatteryCount {needBatteryCount}"); continue; } 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}"); GetBySn(chargingList[i].No)?.SendRemoteStopCharging(); } } } else { List canChargeList = binInfos.Where(it => it.Soc != null && Convert.ToSingle(it.Soc) < StaticStationInfo.SwapSoc && it.CanChargeFlag == 1).ToList(); //启动电量高的 canChargeList.Sort((a,b) => (b.Soc??0).CompareTo(a.Soc??0)); byte chargeSoc = StaticStationInfo.ChargeSoc; float chargePower = StaticStationInfo.ChargePower; int count = needBatteryCount - canSwapList.Count; int number = 0; foreach (var binInfo in canChargeList) { Result? result = GetBySn(binInfo.ChargerNo)?.SendRemoteStartCharging(chargeSoc, chargePower); if (result is {IsSuccess: true} ) { Log.Info($"auto start charge {binInfo.ChargerNo}"); number++; } if (count == number) { Log.Info($"auto start charge count {count}"); break; } } } #endregion } catch (Exception e) { Log.Error("AutoChargeThread error", e); } } } private static void ConnClient(EquipNetInfo netInfo, BinInfo? binInfo) { Log.Info($"begin to connect {netInfo.Code} {netInfo.NetAddr}:{netInfo.NetPort}"); ChargerClient client = AppInfo.Container.Resolve(); client.BinNo = binInfo?.No; client.BatteryNo = binInfo?.BatteryNo; client.InitBootstrap(netInfo.NetAddr, int.Parse(netInfo.NetPort)); Task.Run(() => { client.Connect(); client.SessionAttr(netInfo.Code, netInfo.DestAddr); Log.Info($"succeed to connect {netInfo.Code} {netInfo.NetAddr}:{netInfo.NetPort}"); }); AddBySn(netInfo.Code, client); Log.Info($"begin to connect {netInfo.Code} {netInfo.NetAddr}:{netInfo.NetPort}"); } }