|
|
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;
|
|
|
|
|
|
/// <summary>
|
|
|
/// 示例程序
|
|
|
/// </summary>
|
|
|
[Scope("SingleInstance")]
|
|
|
public static class ClientMgr
|
|
|
{
|
|
|
|
|
|
private static readonly ILog Log = LogManager.GetLogger(typeof(ClientMgr));
|
|
|
|
|
|
public static readonly Dictionary<string, ChargerClient> Dictionary = new();
|
|
|
|
|
|
private static bool AutoChargeWorking { get; set; }
|
|
|
|
|
|
public static ChargerClient? GetBySn(string sn)
|
|
|
{
|
|
|
Dictionary.TryGetValue(sn, out var o);
|
|
|
return o;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 通过channel获取client
|
|
|
/// </summary>
|
|
|
/// <param name="channel"></param>
|
|
|
/// <param name="sn"></param>
|
|
|
/// <param name="client">获取不到,client则为空</param>
|
|
|
/// <returns></returns>
|
|
|
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<EquipInfoRepository>();
|
|
|
EquipNetInfoRepository netInfoRepository = AppInfo.Container.Resolve<EquipNetInfoRepository>();
|
|
|
BinInfoRepository binInfoRepository = AppInfo.Container.Resolve<BinInfoRepository>();
|
|
|
List<EquipInfo> equipInfos = equipInfoRepository.QueryListByClause(it => it.TypeCode == (int)EquipmentType.Charger);
|
|
|
if (equipInfos.Count > 0)
|
|
|
{
|
|
|
Dictionary<string, EquipInfo> set = equipInfos.ToDictionary(it => it.Code, it => it);
|
|
|
List<EquipNetInfo> equipNetInfos = netInfoRepository.QueryListByClause(it => set.Keys.Contains(it.Code));
|
|
|
Dictionary<string,BinInfo> 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);
|
|
|
}
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
///
|
|
|
/// </summary>
|
|
|
private static void StartAutoChargeThread()
|
|
|
{
|
|
|
if (!AutoChargeWorking)
|
|
|
{
|
|
|
Thread thread = new Thread(AutoChargeThread)
|
|
|
{
|
|
|
Name = @"AutoChargeThread"
|
|
|
};
|
|
|
thread.Start();
|
|
|
AutoChargeWorking = true;
|
|
|
}
|
|
|
}
|
|
|
/// <summary>
|
|
|
///
|
|
|
/// </summary>
|
|
|
private static void AutoChargeThread()
|
|
|
{
|
|
|
ElecPriceModelVersionRepository elecPriceModelVersionRepository =
|
|
|
AppInfo.Container.Resolve<ElecPriceModelVersionRepository>();
|
|
|
|
|
|
ElecPriceModelVersionDetailRepository elecPriceModelVersionDetailRepository =
|
|
|
AppInfo.Container.Resolve<ElecPriceModelVersionDetailRepository>();
|
|
|
|
|
|
BatteryOpModelRepository batteryOpModelRepository = AppInfo.Container.Resolve<BatteryOpModelRepository>();
|
|
|
BatteryOpModelDetailRepository batteryOpModelDetailRepository =
|
|
|
AppInfo.Container.Resolve<BatteryOpModelDetailRepository>();
|
|
|
|
|
|
BinInfoRepository binInfoRepository = AppInfo.Container.Resolve<BinInfoRepository>();
|
|
|
|
|
|
while (true)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
Thread.Sleep(1000 * 30);
|
|
|
DateTime now = DateTime.Now;
|
|
|
|
|
|
if (StaticStationInfo.AutoChargeEnabled != 1)
|
|
|
{
|
|
|
Log.Info("AutoChargeEnabled = 0 continue");
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
List<BinInfo> 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<ElecPriceModelVersionDetail> 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<BatteryOpModelDetail> batteryOpModelDetails = batteryOpModelDetailRepository.QueryListByClause(d => d.ModelId == oid);
|
|
|
List<BatteryOpModelDetail> opModelDetails = batteryOpModelDetails.Where(t =>
|
|
|
{
|
|
|
List<int> start = t.StartTime.Split(":").Select(int.Parse).ToList();
|
|
|
List<int> 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<BinInfo> 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<BinInfo> 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<BinInfo> 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<string>? 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<ChargerClient>();
|
|
|
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}");
|
|
|
}
|
|
|
} |