using Dapper; using EVCB_OCPP.Domain; using EVCB_OCPP.WSServer.Helper; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Configuration; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Data; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace EVCB_OCPP.WSServer.Service { public class LoadBalanceSetting { public int StationId { set; get; } public int LBMode { set; get; } public int LBCurrent { set; get; } } public class LoadingBalanceService { //ConcurrentDictionary _lockDic = new ConcurrentDictionary(); ConcurrentDictionary _semaphoreDic = new ConcurrentDictionary(); private readonly SqlConnectionFactory mainDbConnectionFactory; private readonly SqlConnectionFactory webDbConnectionFactory; //private readonly string mainConnectionString; //private readonly string webConnectionString; public LoadingBalanceService(SqlConnectionFactory mainDbConnectionFactory, SqlConnectionFactory webDbConnectionFactory) { this.mainDbConnectionFactory = mainDbConnectionFactory; this.webDbConnectionFactory = webDbConnectionFactory; //mainConnectionString = configuration.GetConnectionString("MainDBContext"); //webConnectionString = configuration.GetConnectionString("WebDBContext"); } public async Task GetStationIdByMachineId(string machineId) { int stationId = 0; using (SqlConnection conn = await webDbConnectionFactory.CreateAsync()) { var parameters = new DynamicParameters(); parameters.Add("@MachineId", machineId, DbType.String, ParameterDirection.Input, 36); string strSql = "Select StationId from [dbo].[StationMachine] where MachineId=@MachineId ; "; stationId = await conn.ExecuteScalarAsync(strSql, parameters); } return stationId; } async public Task IsNeedtoCancelSetting(int stationId, string machineId, string chargeBoxId) { var setting = await GetLoadBalance(stationId); if (setting == null) return false; var semaphore = GetSemaphore(stationId); await semaphore.WaitAsync(); if (setting.LBMode > 0 && setting.LBMode < 3 && !await IsStillInTransactions(chargeBoxId)) { // renew table // UpdateLoadbalanceRecord(stationId, machineId, 0, DateTime.UtcNow); return true; } if (setting.LBMode >= 3 || setting.LBMode < 1) { // CloseLoadbalanceRecord(stationId); } semaphore.Release(); return false; } //private object GetLock(int stationId) //{ // if (!_lockDic.ContainsKey(stationId)) // { // _lockDic.TryAdd(stationId, new object()); // } // return _lockDic[stationId]; //} private SemaphoreSlim GetSemaphore(int stationId) { if (!_semaphoreDic.ContainsKey(stationId)) { _semaphoreDic.TryAdd(stationId, new SemaphoreSlim(1)); } return _semaphoreDic[stationId]; } private void CloseLoadbalanceRecord(int stationId) { using (SqlConnection conn = mainDbConnectionFactory.Create()) { var parameters = new DynamicParameters(); parameters.Add("@StationId", stationId, DbType.Int32, ParameterDirection.Input); parameters.Add("@FinishedOn", DateTime.UtcNow, DbType.DateTime, ParameterDirection.Input); string strSql = "Update [dbo].[LoadingBalance] SET FinishedOn=@FinishedOn where StationId=@StationId and FinishedOn='1991/01/01'; "; conn.Execute(strSql, parameters); } } private void UpdateLoadbalanceRecord(int stationId, string machineId, decimal power, DateTime? finishedOn, bool keepgoing = false) { using (SqlConnection conn = mainDbConnectionFactory.Create()) { if (finishedOn.HasValue) { var parameters = new DynamicParameters(); parameters.Add("@MachineId", machineId, DbType.String, ParameterDirection.Input, 36); parameters.Add("@FinishedOn", finishedOn.Value, DbType.DateTime, ParameterDirection.Input); string strSql = "Update [dbo].[LoadingBalance] SET FinishedOn=@FinishedOn where MachineId=@MachineId and FinishedOn='1991/01/01'; "; conn.Execute(strSql, parameters); } else { if (keepgoing) { var parameters = new DynamicParameters(); parameters.Add("@MachineId", machineId, DbType.String, ParameterDirection.Input, 36); parameters.Add("@Power", power, DbType.Decimal, ParameterDirection.Input); string strSql = "Update [dbo].[LoadingBalance] SET Power=@Power where MachineId=@MachineId and FinishedOn='1991/01/01'; "; conn.Execute(strSql, parameters); } else { var parameters = new DynamicParameters(); parameters.Add("@StationId", stationId, DbType.Int32, ParameterDirection.Input); parameters.Add("@MachineId", machineId, DbType.String, ParameterDirection.Input, 36); parameters.Add("@Power", power, DbType.Decimal, ParameterDirection.Input); parameters.Add("@CreatedOn", DateTime.UtcNow, DbType.DateTime, ParameterDirection.Input); parameters.Add("@FinishedOn", new DateTime(1991, 1, 1, 0, 0, 0, DateTimeKind.Utc), DbType.DateTime, ParameterDirection.Input); string strSql = "INSERT INTO [dbo].[LoadingBalance] " + "([StationId],[MachineId],[Power],[CreatedOn],[FinishedOn]) " + "VALUES(@StationId,@MachineId,@Power,@CreatedOn,@FinishedOn);"; conn.Execute(strSql, parameters); } } } } private async Task IsStillInTransactions(string chargeBoxId) { bool result = false; using (SqlConnection conn = await mainDbConnectionFactory.CreateAsync()) { var parameters = new DynamicParameters(); parameters.Add("@ChargeBoxId", chargeBoxId, DbType.String, ParameterDirection.Input, 50); string strSql = "Select count(*) from [dbo].[TransactionRecord] where ChargeBoxId=@ChargeBoxId and StopTime='1991/01/01'; "; result = await conn.ExecuteScalarAsync(strSql, parameters); } return result; } private decimal? GetCurrentSetting(string machineId) { decimal? result = (decimal?)null; using (SqlConnection conn = mainDbConnectionFactory.Create()) { var parameters = new DynamicParameters(); parameters.Add("@MachineId", machineId, DbType.String, ParameterDirection.Input, 36); string strSql = "Select Power from [dbo].[LoadingBalance] where MachineId=@MachineId and FinishedOn='1991/01/01'; "; result = conn.ExecuteScalar(strSql, parameters); } return result; } async public Task> GetSettingPower(int stationId) { Dictionary dic = new Dictionary(); var setting = await GetLoadBalance(stationId); if (setting == null) return null; var semaphore = GetSemaphore(stationId); await semaphore.WaitAsync(); if (setting != null) { if (setting.LBMode == 1) { dic = await GetAveragePower(stationId, setting.LBCurrent); } } semaphore.Release(); return dic; } async public Task GetLoadBalance(int stationId) { LoadBalanceSetting setting = null; using (SqlConnection conn = await webDbConnectionFactory.CreateAsync()) { var parameters = new DynamicParameters(); parameters.Add("@StationId", stationId, DbType.Int32, ParameterDirection.Input); string strSql = "Select LBMode,LBCurrent from [dbo].[Station] where Id=@StationId ; "; setting = await conn.QueryFirstOrDefaultAsync(strSql, parameters); //etting = result.FirstOrDefaultAsync(); } return setting; } async private Task> GetIdsbyStationId(int stationId) { List machineIds = new List(); using (SqlConnection conn = await webDbConnectionFactory.CreateAsync()) { var parameters = new DynamicParameters(); parameters.Add("@StationId", stationId, DbType.Int16, ParameterDirection.Input); string strSql = "Select MachineId from [dbo].[StationMachine] where StationId=@StationId; "; var result = await conn.QueryAsync(strSql, parameters); machineIds = result.ToList(); } return machineIds; } async private Task> GetAveragePower(int stationId, int availableCapacity) { Dictionary dic = new Dictionary(); availableCapacity = (int)(availableCapacity * 1000 / 1.05M); int keepPower = 0; //讀取上一次斷線但還沒充完電的分配量 (var offlineCPs, keepPower) = await GetChargeBoxIdbyOfflineCharging(stationId); //扣除Keep充電功率 = 分配充電量 var totalPower = availableCapacity - keepPower; if (totalPower > 0) { //總量 * 該樁的額定功率/該站充電中樁的總額定功率 var onlineChargingCPs = await GetOnlineChargerwithCharging(stationId); if (onlineChargingCPs.Count > 0) { int singlePower = (int)Decimal.Divide(totalPower, onlineChargingCPs.Count); foreach (var id in onlineChargingCPs) { dic.Add(id, singlePower); } } } return dic; } async private Task> GetOnlineChargerwithCharging(int stationId) { List results = new List(); List machineIds = await GetIdsbyStationId(stationId); List chargeboxids = new List(); using (SqlConnection conn = await mainDbConnectionFactory.CreateAsync()) { string onlineChargerSql = "Select ChargeBoxId from [dbo].[Machine] where Id in @machineIds and [Online]=1; "; var sqlParams = new DynamicParameters(); sqlParams.Add("@machineIds", machineIds, size: 36); var onlineResult = await conn.QueryAsync(onlineChargerSql, sqlParams); chargeboxids = onlineResult.ToList(); foreach (var chargeboxid in chargeboxids) { string txSql = "SELECT TOP(1) [Id] from [dbo].[TransactionRecord] where ChargeBoxId=@ChargeBoxId and StopTime = '1991-01-01 00:00:00.000'; "; var param = new DynamicParameters(); param.Add("ChargeBoxId", chargeboxid, DbType.String, ParameterDirection.Input, size: 50); var txId = await conn.ExecuteScalarAsync(txSql, param); if (txId > 0) { results.Add(chargeboxid); } } } return results; } /// /// 取得斷線樁號 /// /// 站點代號 /// 總額定功率 /// private async Task<(List,int ratedPowers)> GetChargeBoxIdbyOfflineCharging(int stationId) { List machineIds = await GetIdsbyStationId(stationId); List result = new List(); int ratedPowers = 0; using (SqlConnection conn = await mainDbConnectionFactory.CreateAsync()) { string offlineChargerSql = "Select ChargeBoxId from [dbo].[Machine] where Id in @machineIds and [Online]=0; "; var sqlParams = new DynamicParameters(); sqlParams.Add("@machineIds", machineIds, size: 36); result = (await conn.QueryAsync(offlineChargerSql, sqlParams)).ToList(); foreach (var charger in result) { string txSql = "SELECT TOP(1) [Id] from [dbo].[TransactionRecord] where ChargeBoxId=@ChargeBoxId and StopTime = '1991-01-01 00:00:00.000'; "; var param = new DynamicParameters(); param.Add("ChargeBoxId", charger, DbType.String, ParameterDirection.Input, 50); var txId = await conn.ExecuteScalarAsync(txSql, param); if (txId > 0) { string ratedPowerSql = "Select Sum(RatedPower) from [dbo].[Machine] where ChargeBoxId=@ChargeBoxId and [Online]=0; "; ratedPowers += await conn.ExecuteScalarAsync(ratedPowerSql, param); } } } ratedPowers *= 1000; return (result, ratedPowers); } private decimal GetRatedPowerbyChargeBoxId(string chargeBoxId) { decimal ratedPower = 0; using (SqlConnection conn = mainDbConnectionFactory.Create()) { var parameters = new DynamicParameters(); parameters.Add("@machineId", chargeBoxId, DbType.String, ParameterDirection.Input, 36); string strSql = "Select RatedPower from [dbo].[Machine] where Id=@machineId; "; ratedPower = conn.ExecuteScalar(strSql, parameters); } return ratedPower; } private decimal GetRatedPowerbyId(string machineId) { decimal ratedPower = 0; using (SqlConnection conn = mainDbConnectionFactory.Create()) { var parameters = new DynamicParameters(); parameters.Add("@machineId", machineId, DbType.String, ParameterDirection.Input, 36); string strSql = "Select RatedPower from [dbo].[Machine] where Id=@machineId; "; ratedPower = conn.ExecuteScalar(strSql, parameters); } return ratedPower; } } }