using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Quartz; using SolarPower.Models.PowerStation; using SolarPower.Repository.Interface; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading.Tasks; namespace SolarPower.Quartz.Jobs { [DisallowConcurrentExecution] public class CalcPowerStationJob : IJob { private readonly ILogger logger; private readonly IPowerStationRepository powerStationRepository; private double carbonRate; public CalcPowerStationJob(ILogger logger, IPowerStationRepository powerStationRepository) { this.logger = logger; this.powerStationRepository = powerStationRepository; } public async Task Execute(IJobExecutionContext context) { try { #region step1. 找出所有電站 logger.LogInformation("【CalcPowerStationJob】【開始取得電站資料】"); var powerStations = await powerStationRepository.GetAllAsync(); logger.LogInformation("【CalcPowerStationJob】【取得成功電站資料】"); logger.LogInformation("【CalcPowerStationJob】【電站資料】 - {0}", System.Text.Json.JsonSerializer.Serialize(powerStations)); #endregion List powerStationHistoriesHour = new List(); List pyrheliometerHistoriesHour = new List(); List TempHistoriesHour = new List(); List inverterHistories = new List(); List calcPowerStations = new List(); List weatherObservations = new List(); var DateTimeNow = DateTime.Now; var count = 0; #region 氣象觀測(取資料) logger.LogInformation("【CalcPowerStationJob】【開始取得氣象觀測】"); var client = new HttpClient(); var UVUri = "https://opendata.cwb.gov.tw/api/v1/rest/datastore/O-A0003-001?Authorization=CWB-EA24220B-DDCC-4188-84E5-AD37A0E03F80"; HttpResponseMessage response = client.GetAsync(UVUri).Result; String jsonUVs = response.Content.ReadAsStringAsync().Result.ToString(); Root2 observation = JsonConvert.DeserializeObject(jsonUVs); logger.LogInformation("【CalcPowerStationJob】【取得成功氣象觀測】"); #endregion #region step2. 從電站的DB及電站編號找出該電站的控制器 foreach (var powerStation in powerStations) { if (count > 0) { break; } var calcPowerStation = new PowerStation(); calcPowerStation.Id = powerStation.Id; var dateTime = DateTimeNow.AddHours(-1).ToString("yyyy-MM-dd HH"); #region step2-1. 取得該電站的當前這小時的歷史資料 var table_name = String.Format("`{0}`.s{1}01_station", powerStation.SiteDB, powerStation.Code); logger.LogInformation("【CalcPowerStationJob】【開始取得電站[{0}]在{1}的每小時歷史資料】", powerStation.Code, dateTime); var history = await powerStationRepository.GetPowerStationHistoryPerHour(dateTime, table_name); logger.LogInformation("【CalcPowerStationJob】【取得成功電站[{0}]在{1}的每小時歷史資料】", powerStation.Code, dateTime); logger.LogInformation("【CalcPowerStationJob】【電站[{0}]在{1}的每小時歷史資料】 - {2}", powerStation.Code, dateTime, System.Text.Json.JsonSerializer.Serialize(history)); var lastmoneyhistorybyhour = await powerStationRepository.GetLastMoneyAndCarbonInHour(powerStation.Id,0,dateTime); if (history != null) { logger.LogInformation("【CalcPowerStationJob】【開始計算電站[{0}]在{1}的每小時歷史資料】", powerStation.Code, dateTime); history.PowerStationId = powerStation.Id; history.Timestamp = Convert.ToDateTime(history.Timestamp + ":00:00").ToString("yyyy-MM-dd HH:mm:ss"); #region 計算單一電站每小時發電量、發電金額等資料 #region 發電量 //每小時發電量(直接填寫 calcPowerStation.kwh = history.KWH; //今日發電量(直接填寫 calcPowerStation.Today_kWh = history.TodayKWh; //總發電量(直接填寫 calcPowerStation.Total_kWh = history.TotalKWH; #endregion #region 發電金額 //發電金額 switch (powerStation.SolarType) { case (int)SolarTypeEnum.SelfSold: //自建躉售 //今日發電金額 計算方式:todaykWh * 授電費率 calcPowerStation.Today_Money = history.TodayKWh * powerStation.PowerRate; history.MONEY = history.KWH * powerStation.PowerRate; //總發電金額 計算方式:totalkWh * 授電費率 calcPowerStation.Total_Money = history.TotalKWH * powerStation.PowerRate; break; case (int)SolarTypeEnum.HireSold: //租建躉售 //找出該電站的所有土地房屋資訊 var landBuildings = await powerStationRepository.GetAllLandBuildingInfoByPowerStationId(powerStation.Id, powerStation.SiteDB); var sumLeaseRate = 0.00; var avgLeaseRate = 0.00; foreach (var landBuilding in landBuildings) { sumLeaseRate += landBuilding.LeaseRate; } avgLeaseRate = sumLeaseRate / landBuildings.Count(); //今日發電金額計算方式:todaykWh * 出借費率(各個土地房屋租借比率平均) calcPowerStation.Today_Money = history.TodayKWh * avgLeaseRate; history.MONEY = history.KWH * avgLeaseRate; //總發電金額 計算方式:totalkWh * 授電費率 calcPowerStation.Total_Money = history.TotalKWH * avgLeaseRate; break; case (int)SolarTypeEnum.SelfUse: //自建自用 //今日發電金額 計算方式:todaykWh * 授電費率 calcPowerStation.Today_Money = history.TodayKWh * powerStation.PowerRate; history.MONEY = history.KWH * powerStation.PowerRate; //總發電金額 計算方式:totalkWh * 授電費率 calcPowerStation.Total_Money = history.TotalKWH * powerStation.PowerRate; break; } #endregion #region kWh/kWp //直接填寫 calcPowerStation.Today_kwhkwp = history.KWHKWP; #endregion #region PR //直接填寫 calcPowerStation.Today_PR = history.PR; #endregion #region 減碳量 carbonRate = Convert.ToDouble(await powerStationRepository.GetOneVariableByName("CarbonRate")); //今日減碳量( 今日發電量 * (0.554/1000)[抓資料庫值] calcPowerStation.Today_Carbon = history.TodayKWh * carbonRate; history.CARBON = history.KWH * carbonRate; //總減碳量(總發電量 * (0.554/1000)[抓資料庫值] calcPowerStation.Total_Carbon = history.TotalKWH * carbonRate; #endregion history.TODAYCARBON = lastmoneyhistorybyhour.TODAYCARBON + history.KWH * carbonRate; history.TOTALCARBON = lastmoneyhistorybyhour.TOTALCARBON + history.KWH * carbonRate; history.TODAYMONEY = lastmoneyhistorybyhour.TODAYMONEY + history.KWH * powerStation.PowerRate; history.TOTALMONEY = lastmoneyhistorybyhour.TOTALMONEY + history.KWH * powerStation.PowerRate; #endregion powerStationHistoriesHour.Add(history); logger.LogInformation("【CalcPowerStationJob】【計算完成電站[{0}]在{1}的每小時歷史資料】", powerStation.Code, dateTime); } #endregion #region step2-2. 取得該電站的當前這小時的日照度歷史資料 //1. 找出該電站所有日照計設備(包含共享 logger.LogInformation("【CalcPowerStationJob】【開始取得電站[{0}]在{1}的日照計設備資訊】", powerStation.Code, dateTime); var deviceInfos = await powerStationRepository.GetListPyrheliometerByPowerStationId(powerStation.Id, powerStation.SiteDB); logger.LogInformation("【CalcPowerStationJob】【取得成功電站[{0}]在{1}的日照計設備資訊】", powerStation.Code, dateTime); logger.LogInformation("【CalcPowerStationJob】【電站[{0}]在{1}的日照計設備資訊】 - {2}", powerStation.Code, dateTime, System.Text.Json.JsonSerializer.Serialize(deviceInfos)); if (deviceInfos != null) { //2. 計算該電站所有日照計設的每小時的平均在依照日照計數量平均 logger.LogInformation("【CalcPowerStationJob】【開始計算電站[{0}]在{1}的日照計的日照度】", powerStation.Code, dateTime); var pyrheliometerHistory = await powerStationRepository.GetPyrheliometerHistoryPerHour(dateTime, deviceInfos, 0); if (pyrheliometerHistory != null) { calcPowerStation.Today_irradiance = pyrheliometerHistory.Irradiance; pyrheliometerHistory.Timestamp = Convert.ToDateTime(pyrheliometerHistory.Timestamp + ":00:00").ToString("yyyy-MM-dd HH:mm:ss"); pyrheliometerHistory.PowerStationId = powerStation.Id; pyrheliometerHistoriesHour.Add(pyrheliometerHistory); } logger.LogInformation("【CalcPowerStationJob】【計算完成電站[{0}]在{1}的日照計的日照度】", powerStation.Code, dateTime); } //2. 計算該電站所有溫度計設的每小時的平均在依照溫度計數量平均 // 找出該電站所有溫度計設備(包含共享 logger.LogInformation("【CalcPowerStationJob】【開始取得電站[{0}]在{1}的溫度計設備資訊】", powerStation.Code, dateTime); var tempdeviceInfos = await powerStationRepository.GetListTempByPowerStationId(powerStation.Id, powerStation.SiteDB); logger.LogInformation("【CalcPowerStationJob】【取得成功電站[{0}]在{1}的溫度計設備資訊】", powerStation.Code, dateTime); logger.LogInformation("【CalcPowerStationJob】【電站[{0}]在{1}的溫度計設備資訊】 - {2}", powerStation.Code, dateTime, System.Text.Json.JsonSerializer.Serialize(tempdeviceInfos)); if (tempdeviceInfos != null) { logger.LogInformation("【CalcPowerStationJob】【開始計算電站[{0}]在{1}的溫度計的平均溫度】", powerStation.Code, dateTime); var tempHistory = await powerStationRepository.GetPyrheliometerHistoryPerHour(dateTime, tempdeviceInfos, 1); if (tempHistory != null) { tempHistory.Timestamp = Convert.ToDateTime(tempHistory.Timestamp + ":00:00").ToString("yyyy-MM-dd HH:mm:ss"); tempHistory.PowerStationId = powerStation.Id; TempHistoriesHour.Add(tempHistory); logger.LogInformation("【CalcPowerStationJob】【計算完成電站[{0}]在{1}的溫度計的平均溫度】", powerStation.Code, dateTime); } } #endregion #region step2-3. 計算該電站所有逆變器每小時值 logger.LogInformation("【CalcPowerStationJob】【開始取得電站[{0}]在{1}的逆變器設備資訊】", powerStation.Code, dateTime); var controllers = await powerStationRepository.GetAllDeviceControllerId(powerStation.Id, powerStation.SiteDB); var inverters = await powerStationRepository.InverterTable(controllers, powerStation.SiteDB); var inverterIds = inverters.Where(x => x.Enabled == 1 && x.Status != 0).Select(x => x.InverterId).ToList(); logger.LogInformation("【CalcPowerStationJob】【取得成功電站[{0}]在{1}的逆變器設備資訊】", powerStation.Code, dateTime); logger.LogInformation("【CalcPowerStationJob】【電站[{0}]在{1}的逆變器設備資訊】 - {2}", powerStation.Code, dateTime, System.Text.Json.JsonSerializer.Serialize(inverterIds)); var inverter_table_name = String.Format("`{0}`.s{1}01_inv", powerStation.SiteDB, powerStation.Code); logger.LogInformation("【CalcPowerStationJob】【開始計算電站[{0}]在{1}的逆變器的資訊】", powerStation.Code, dateTime); inverterHistories = await powerStationRepository.CalcInverterHisyortHourData(dateTime, powerStation.SiteDB, inverter_table_name, inverterIds); //取得日照計要找的欄位資訊 var pyrheliometer = await powerStationRepository.GetFirstPyrheliometerInfo(powerStation.Id, powerStation.SiteDB); var pyrheliometerValue = await powerStationRepository.GetFirstPyrheliometerValue(dateTime, pyrheliometer.DBName, pyrheliometer.TableName, pyrheliometer.ColName); foreach (var inverterHistory in inverterHistories) { inverterHistory.Irradiance = pyrheliometerValue; inverterHistory.DC1KW = inverterHistory.DC1W / 1000; inverterHistory.DC2KW = inverterHistory.DC2W / 1000; inverterHistory.DC3KW = inverterHistory.DC3W / 1000; inverterHistory.DC4KW = inverterHistory.DC4W / 1000; inverterHistory.DC5KW = inverterHistory.DC5W / 1000; inverterHistory.DCKW = (inverterHistory.DC1W + inverterHistory.DC2W + inverterHistory.DC3W + inverterHistory.DC4W + inverterHistory.DC5W) / 1000; inverterHistory.ACKW = (inverterHistory.AC1W + inverterHistory.AC2W + inverterHistory.AC3W) / 1000; inverterHistory.TIMESTAMP = Convert.ToDateTime(inverterHistory.TIMESTAMP + ":00:00").ToString("yyyy-MM-dd HH:mm:ss"); inverterHistory.PowerStationId = powerStation.Id; } logger.LogInformation("【CalcPowerStationJob】【計算完成電站[{0}]在{1}的逆變器的資訊】", powerStation.Code, dateTime); #endregion #region 確認是否有觀測站(沒有則新增) if (powerStation.WeathersStationId == null) { var weatherStationId = ""; double shortLocation = 9999; foreach (var Location in observation.Records.Location) { var powerLocation = powerStation.Coordinate.Split(','); var nowLocation = Math.Sqrt(Math.Pow(Convert.ToDouble(powerLocation[0]) - Convert.ToDouble(Location.Lat), 2) + Math.Pow(Convert.ToDouble(powerLocation[1]) - Convert.ToDouble(Location.Lon), 2)); if (nowLocation < shortLocation) { shortLocation = nowLocation; weatherStationId = Location.StationId; calcPowerStation.TodayWeatherTemp = Convert.ToDouble(Location.WeatherElement[0].ElementValue); } } calcPowerStation.WeathersStationId = weatherStationId; } #endregion WeatherObservation weatherObservation = new WeatherObservation(); if (powerStation.WeathersStationId != null) { foreach (var Location in observation.Records.Location) { if (Location.StationId == powerStation.WeathersStationId) { calcPowerStation.TodayWeatherTemp = Convert.ToDouble(Location.WeatherElement[0].ElementValue); weatherObservation.PowerStationId = powerStation.Id; weatherObservation.Temp = Convert.ToDouble(Location.WeatherElement[0].ElementValue); weatherObservation.ObsTime = Location.Time.ObsTime.ToString(); calcPowerStation.WeathersStationId = powerStation.WeathersStationId; break; } } } logger.LogInformation("【CalcPowerStationJob】【開始取得電站[{0}]在{1}的天氣預報的資訊】", powerStation.Code, dateTime); var weather = await powerStationRepository.SelectNowWeather(powerStation.CityId); logger.LogInformation("【CalcPowerStationJob】【取得成功電站[{0}]在{1}的天氣預報的資訊】", powerStation.Code, dateTime); if (weather != null) { calcPowerStation.TodayWeather = weather.WeatherKey; calcPowerStation.RateOfRain = weather.PoP; } weatherObservations.Add(weatherObservation); calcPowerStations.Add(calcPowerStation); count++; } #endregion #region step3. 將historiers INSERT 到 power_station_history_hour 資料表 List history_properties = new List() { "PowerStationId", "TIMESTAMP", "SITEID", "SITETYPE", "KWH", "TODAYKWH", "TOTALKWH", "KWHKWP", "PR", "MP", "SolarHour", "MONEY", "CARBON", "TODAYMONEY", "TOTALMONEY", "TODAYCARBON", "TOTALCARBON" }; await powerStationRepository.AddPowerStationHistory(powerStationHistoriesHour, history_properties); #endregion #region step4. 將Pyrheliometer History INSERT 到 sensor_history_hour 資料表 List pyrheliometer_history_properties = new List() { "PowerStationId", "TIMESTAMP", "Irradiance" }; await powerStationRepository.AddPyrheliometerHistory(pyrheliometerHistoriesHour, pyrheliometer_history_properties); List Temp_history_properties = new List() { "PowerStationId", "TIMESTAMP", "Temperature" }; await powerStationRepository.AddTempHistory(TempHistoriesHour, Temp_history_properties); #endregion #region step5. calcPowerStations UPDATE 到 power_station 資料表 List power_station_properties = new List() { "Id", "kwh", "Today_kwh", "Total_kwh", "today_kwhkwp", "today_money", "total_money", "today_PR", "today_carbon", "total_carbon", "today_irradiance", "WeathersStationId", "TodayWeatherTemp", "TodayWeather", "RateOfRain" }; await powerStationRepository.UpdateList(calcPowerStations, power_station_properties); #endregion #region step6. 將 inverter INSERT 到 inverter_history_hour 資料表 List inverter_history_properties = new List() { "PowerStationId", "INVERTERID", "TIMESTAMP", "Irradiance", "AC1V", "AC1A", "AC1W", "AC1F", "AC1WH", "AC2V", "AC2A", "AC2W", "AC2F", "AC2WH", "AC3V", "AC3A", "AC3W", "AC3F", "AC3WH", "DC1V", "DC1A", "DC1W", "DC1KW", "DC1WH", "DC2V", "DC2A", "DC2W", "DC2KW", "DC2WH", "DC3V", "DC3A", "DC3W", "DC3KW", "DC3WH", "DC4V", "DC4A", "DC4W", "DC4KW", "DC4WH", "DC5V", "DC5A", "DC5W", "DC5KW", "DC5WH", "PR", "RA1", "RA2", "RA3", "RA4", "RA5", "DCKW", "ACKW", "KWH", "TODAYKWH", "KWHKWP", }; await powerStationRepository.AddInverterHistory(inverterHistories, inverter_history_properties); #endregion List weather_observation_properties = new List() { "PowerStationId", "ObsTime", "Temp" }; await powerStationRepository.AddWeatherObservation(weatherObservations, weather_observation_properties); } catch (Exception exception) { logger.LogError("【{0}】{1}", "CalcPowerStationJob", exception.Message); } } } }