CheckEVSEOnlineJob.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. using Dapper;
  2. using EVCB_OCPP.TaskScheduler.Models;
  3. using Quartz;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Configuration;
  7. using System.Data.SqlClient;
  8. using System.Diagnostics;
  9. using System.Linq;
  10. using System.Text;
  11. using System.Threading.Tasks;
  12. namespace EVCB_OCPP.TaskScheduler.Jobs
  13. {
  14. /// <summary>
  15. /// Update EVSE's online status
  16. /// </summary>
  17. [DisallowConcurrentExecution]
  18. public class CheckEVSEOnlineJob : IJob
  19. {
  20. private NLog.ILogger logger = NLog.LogManager.GetCurrentClassLogger();
  21. private DateTime latestHeartbeatTime = DateTime.Now;
  22. private List<EVSEOnlineRecord> updateData = new List<EVSEOnlineRecord>();
  23. private List<EVSEOnlineRecord> insertData = new List<EVSEOnlineRecord>();
  24. private string mainDBConnectString = ConfigurationManager.ConnectionStrings["MainDBContext"].ToString();
  25. private string onlineDBConnectString = ConfigurationManager.ConnectionStrings["OnlineLogDBContext"].ToString();
  26. public CheckEVSEOnlineJob()
  27. {
  28. CreateEVSEOnlineRecordTable();
  29. }
  30. //測試方式
  31. // A: 假裝更新心跳包
  32. // B: 一直開著程式 / 一段時間關程式(關掉時間點 online/offline)
  33. public async Task Execute(IJobExecutionContext context)
  34. {
  35. logger.Debug(this.ToString() + " :Starting........");
  36. try
  37. {
  38. List<EVSECurrentStatus> _EVSEs = GetEVSEs();
  39. var checktime = DateTime.UtcNow.AddDays(-3);
  40. _EVSEs = _EVSEs.Where(x => x.HeartbeatUpdatedOn > checktime).ToList();
  41. foreach (var evse in _EVSEs)
  42. {
  43. latestHeartbeatTime = evse.HeartbeatUpdatedOn;
  44. if (IsOnlineNow(evse))
  45. {
  46. if (evse.Online)
  47. {
  48. if (latestHeartbeatTime.Minute % 10 == 0)
  49. {
  50. //on-on
  51. //現在是整點,找上一筆填入end time and insert start time
  52. // var _pickDate = currentTime.AddHours(-1);
  53. var _pickDate = latestHeartbeatTime;
  54. bool isCrossDay = false;
  55. int _startHour = _pickDate.AddHours(-6).Hour; //往回巡視六個小時前紀錄
  56. //跨日處理
  57. if (_startHour > _pickDate.Hour)
  58. {
  59. isCrossDay = true;
  60. }
  61. var _picks = await GetOnlineRecords(_pickDate, evse.CustomerId.ToString(), evse.ChargeBoxId, isCrossDay ? 0 : _startHour, _pickDate.Hour);
  62. if (isCrossDay)
  63. {
  64. var _picksCrossData = await GetOnlineRecords(_pickDate.AddHours(-6), evse.CustomerId.ToString(), evse.ChargeBoxId, _startHour, 23);
  65. _picks.AddRange(_picksCrossData);
  66. }
  67. _picks = _picks.OrderBy(x => x.OnlineTime).ToList();
  68. bool _isIgnore = true;
  69. for (int i = 0; i < _picks.Count; i++)
  70. {
  71. if (_picks[i].HourIndex != evse.HeartbeatUpdatedOn.Hour && _picks[i].OfflineTime == DefaultSetting.DefaultNullTime)
  72. {
  73. _picks[i].OfflineTime = new DateTime(_picks[i].OnlineTime.AddHours(1).Year,
  74. _picks[i].OnlineTime.AddHours(1).Month, _picks[i].OnlineTime.AddHours(1).Day, _picks[i].OnlineTime.AddHours(1).Hour, 0, 0);
  75. _picks[i].TotalMinute = (int)_picks[i].OfflineTime.Subtract(_picks[i].OnlineTime).TotalMinutes;
  76. updateData.Add(_picks[i]);
  77. var checkTime = _picks[i].OfflineTime;
  78. while ((int)latestHeartbeatTime.Subtract(checkTime).TotalHours > 0)
  79. {
  80. var _existedCount = _picks.Where(x => x.HourIndex == checkTime.Hour).ToList().Count;
  81. if (_existedCount == 0)
  82. {
  83. insertData.Add(new EVSEOnlineRecord()
  84. {
  85. CustomerId = evse.CustomerId,
  86. StationId = "0",
  87. HourIndex = checkTime.Hour,
  88. ChargeBoxId = evse.ChargeBoxId,
  89. OnlineTime = new DateTime(checkTime.Year, checkTime.Month, checkTime.Day, checkTime.Hour, 0, 0),
  90. OfflineTime = new DateTime(checkTime.AddHours(1).Year, checkTime.AddHours(1).Month, checkTime.AddHours(1).Day, checkTime.AddHours(1).Hour, 0, 0),
  91. TotalMinute = 60
  92. });
  93. checkTime = checkTime.AddHours(1);
  94. }
  95. }
  96. }
  97. }
  98. _isIgnore = _picks.Where(x => x.HourIndex == latestHeartbeatTime.Hour).ToList().Count == 0 ? false : true;
  99. if (!_isIgnore)
  100. {
  101. insertData.Add(new EVSEOnlineRecord()
  102. {
  103. CustomerId = evse.CustomerId,
  104. StationId = "0",
  105. HourIndex = evse.HeartbeatUpdatedOn.Hour,
  106. ChargeBoxId = evse.ChargeBoxId,
  107. OnlineTime = new DateTime(latestHeartbeatTime.Year,
  108. latestHeartbeatTime.Month, latestHeartbeatTime.Day, latestHeartbeatTime.Hour, 0, 0),
  109. OfflineTime = DefaultSetting.DefaultNullTime
  110. });
  111. }
  112. }
  113. }
  114. else
  115. {
  116. //off - on
  117. UpdateEVSECurrentStatus(evse.CustomerId.ToString(), evse.ChargeBoxId, true, DefaultSetting.DefaultNullTime);
  118. insertData.Add(new EVSEOnlineRecord()
  119. {
  120. CustomerId = evse.CustomerId,
  121. StationId = "0",
  122. HourIndex = evse.HeartbeatUpdatedOn.Hour,
  123. ChargeBoxId = evse.ChargeBoxId,
  124. OnlineTime = evse.HeartbeatUpdatedOn,
  125. });
  126. }
  127. }
  128. else
  129. {
  130. if (evse.Online)
  131. {
  132. //on -off
  133. UpdateEVSECurrentStatus(evse.CustomerId.ToString(), evse.ChargeBoxId, false, evse.HeartbeatUpdatedOn);
  134. var _pickDate = evse.HeartbeatUpdatedOn.Date;
  135. var _picks = await GetOnlineRecords(_pickDate, evse.CustomerId.ToString(), evse.ChargeBoxId, _pickDate.Hour, evse.HeartbeatUpdatedOn.Hour);
  136. _picks = _picks.Where(x => x.OfflineTime == DefaultSetting.DefaultNullTime).ToList();
  137. foreach (var item in _picks)
  138. {
  139. if (evse.HeartbeatUpdatedOn.Hour - item.OnlineTime.Hour > 0)
  140. {
  141. item.OfflineTime = new DateTime(item.OnlineTime.AddHours(1).Year, item.OnlineTime.AddHours(1).Month, item.OnlineTime.AddHours(1).Day, item.OnlineTime.AddHours(1).Hour, 0, 0);
  142. item.TotalMinute = (int)item.OfflineTime.Subtract(item.OnlineTime).TotalMinutes;
  143. updateData.Add(item);
  144. var checkTime = item.OfflineTime;
  145. while (evse.HeartbeatUpdatedOn.Hour - checkTime.Hour >= 0)
  146. {
  147. insertData.Add(new EVSEOnlineRecord()
  148. {
  149. CustomerId = evse.CustomerId,
  150. StationId = "0",
  151. HourIndex = checkTime.Hour,
  152. ChargeBoxId = evse.ChargeBoxId,
  153. OnlineTime = checkTime,
  154. OfflineTime = evse.HeartbeatUpdatedOn.Hour == checkTime.Hour ? evse.HeartbeatUpdatedOn : new DateTime(checkTime.AddHours(1).Year, checkTime.AddHours(1).Month, checkTime.AddHours(1).Day, checkTime.AddHours(1).Hour, 0, 0)
  155. });
  156. checkTime = evse.HeartbeatUpdatedOn.Hour == checkTime.Hour ? insertData.LastOrDefault().OfflineTime.AddHours(1) : insertData.LastOrDefault().OfflineTime;
  157. }
  158. }
  159. else
  160. {
  161. item.OfflineTime = evse.HeartbeatUpdatedOn;
  162. item.TotalMinute = (int)item.OfflineTime.Subtract(item.OnlineTime).TotalMinutes;
  163. updateData.Add(item);
  164. }
  165. }
  166. }
  167. else
  168. {
  169. //off-off do nothing
  170. }
  171. }
  172. if ((insertData.Count + updateData.Count) % 100 == 0)
  173. {
  174. await UpdateOnlineRecords(updateData, insertData);
  175. ClearCache();
  176. }
  177. }
  178. await UpdateOnlineRecords(updateData, insertData);
  179. }
  180. catch (Exception ex)
  181. {
  182. logger.Debug("ERROR " + this.ToString() + ex.ToString());
  183. }
  184. logger.Debug(this.ToString() + " :Finished........");
  185. }
  186. private List<EVSECurrentStatus> GetEVSEs()
  187. {
  188. List<EVSECurrentStatus> result = new List<EVSECurrentStatus>();
  189. try
  190. {
  191. using (var dbConn = new SqlConnection(mainDBConnectString))
  192. {
  193. dbConn.Open();
  194. string sqlstring = "SELECT m.CustomerId,m.Id,m.ChargeBoxId,m.Online,m.HeartbeatUpdatedOn,MachineConfigurations.ConfigureSetting HeartbeatInterval"
  195. + " FROM [dbo].[Machine] m,[dbo].[MachineConfigurations] where MachineConfigurations.ConfigureName = 'HeartbeatInterval' and m.ChargeBoxId = MachineConfigurations.ChargeBoxId";
  196. result = dbConn.Query<EVSECurrentStatus>(sqlstring).ToList();
  197. }
  198. }
  199. catch (Exception ex)
  200. {
  201. logger.Error("Query Data Error " + ex.ToString());
  202. }
  203. return result;
  204. }
  205. private void UpdateEVSECurrentStatus(string customerId, string ChargeBoxId, bool turnOn, DateTime offlineTime)
  206. {
  207. try
  208. {
  209. string sqlString = string.Format("UPDATE [dbo].[Machine] SET Online=@Online {0} WHERE chargeBoxId=@chargeBoxId and customerId=@customerId", turnOn ? "" : " ,OfflineOn=@OfflineOn");
  210. using (var dbConn = new SqlConnection(mainDBConnectString))
  211. {
  212. dbConn.Open();
  213. var parameters = new DynamicParameters();
  214. parameters.Add("@Online", turnOn, System.Data.DbType.Boolean);
  215. parameters.Add("@chargeBoxId", ChargeBoxId, System.Data.DbType.String);
  216. parameters.Add("@customerId", customerId, System.Data.DbType.String);
  217. if (!turnOn)
  218. {
  219. parameters.Add("@OfflineOn", offlineTime, System.Data.DbType.DateTime);
  220. }
  221. dbConn.Execute(sqlString, parameters);
  222. }
  223. }
  224. catch (Exception ex)
  225. {
  226. logger.Error("Update Data Error " + ex.ToString());
  227. }
  228. }
  229. async private Task UpdateOnlineRecords(List<EVSEOnlineRecord> updateItems, List<EVSEOnlineRecord> insertItems)
  230. {
  231. List<EVSEOnlineRecord> records = new List<EVSEOnlineRecord>();
  232. try
  233. {
  234. for (int i = 0; i < updateItems.Count; i++)
  235. {
  236. string sqlString = string.Format("UPDATE [dbo].[EVSEOnlineRecord_{0}] SET OfflineTime=@OfflineTime , TotalMinute=@TotalMinute WHERE customerId=@customerId and chargeBoxId=@chargeBoxId and " +
  237. "OnlineTime=@OnlineTime", updateItems[i].OnlineTime.Date.ToString("yyMMdd"));
  238. using (var dbConn = new SqlConnection(onlineDBConnectString))
  239. {
  240. dbConn.Open();
  241. await dbConn.ExecuteAsync(sqlString, updateItems[i]);
  242. }
  243. }
  244. for (int i = 0; i < insertItems.Count; i++)
  245. {
  246. string sqlString = string.Format("INSERT INTO [dbo].[EVSEOnlineRecord_{0}] (\"CustomerId\",\"StationId\",\"ChargeBoxId\",\"HourIndex\",\"TotalMinute\",\"OnlineTime\",\"OfflineTime\")" +
  247. "VALUES(@CustomerId,@StationId, @ChargeBoxId,@HourIndex,@TotalMinute,@OnlineTime,@OfflineTime); ", insertItems[i].OnlineTime.Date.ToString("yyMMdd"));
  248. using (var dbConn = new SqlConnection(onlineDBConnectString))
  249. {
  250. dbConn.Open();
  251. await dbConn.ExecuteAsync(sqlString, insertItems[i]);
  252. }
  253. }
  254. }
  255. catch (Exception ex)
  256. {
  257. logger.Error("Update Data Error " + ex.ToString());
  258. }
  259. }
  260. async private Task<List<EVSEOnlineRecord>> GetOnlineRecords(DateTime pickDate, string customerId, string chargeBoxId, int startHourCondition, int stopHourCondition)
  261. {
  262. List<EVSEOnlineRecord> records = new List<EVSEOnlineRecord>();
  263. try
  264. {
  265. string sqlString = string.Format("SELECT * FROM [dbo].[EVSEOnlineRecord_{0}] WHERE customerId=@customerId and chargeBoxId=@chargeBoxId and " +
  266. "HourIndex >= @startHourCondition and HourIndex <= @stopHourCondition and OfflineTime=@OfflineTime", pickDate.ToString("yyMMdd"));
  267. using (var dbConn = new SqlConnection(onlineDBConnectString))
  268. {
  269. dbConn.Open();
  270. var parameters = new DynamicParameters();
  271. parameters.Add("@customerId", new Guid(customerId), System.Data.DbType.Guid);
  272. parameters.Add("@chargeBoxId", chargeBoxId, System.Data.DbType.String);
  273. parameters.Add("@startHourCondition", startHourCondition, System.Data.DbType.Int32);
  274. parameters.Add("@stopHourCondition", stopHourCondition, System.Data.DbType.Int32);
  275. parameters.Add("@OfflineTime", DefaultSetting.DefaultNullTime, System.Data.DbType.Date);
  276. var result = await dbConn.QueryAsync<EVSEOnlineRecord>(sqlString, parameters);
  277. records = result.ToList();
  278. }
  279. }
  280. catch (Exception ex)
  281. {
  282. logger.Error("Query Data Error " + ex.ToString());
  283. }
  284. return records;
  285. }
  286. private void CreateEVSEOnlineRecordTable()
  287. {
  288. try
  289. {
  290. string sqlString = string.Format("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME ='EVSEOnlineRecord_{0}'", latestHeartbeatTime.ToString("yyMMdd"));
  291. using (var dbConn = new SqlConnection(onlineDBConnectString))
  292. {
  293. bool exists = dbConn.ExecuteScalar<bool>(sqlString);
  294. if (!exists)
  295. {
  296. dbConn.Execute(string.Format(@"
  297. CREATE TABLE [dbo].[EVSEOnlineRecord_{0}] (
  298. [CustomerId] [UNIQUEIDENTIFIER] NOT NULL,
  299. [StationId] [nvarchar](36) NOT NULL,
  300. [ChargeBoxId] [nvarchar](36) NOT NULL,
  301. [HourIndex] [int] NOT NULL,
  302. [TotalMinute] [int] NOT NULL,
  303. [OnlineTime] [datetime] NOT NULL,
  304. [OfflineTime] [datetime] NOT NULL
  305. ) ON [PRIMARY]
  306. ", latestHeartbeatTime.ToString("yyMMdd")));
  307. }
  308. }
  309. }
  310. catch (Exception ex)
  311. {
  312. logger.Error("Query Data Error " + ex.ToString());
  313. }
  314. }
  315. private void ClearCache()
  316. {
  317. if (updateData != null)
  318. {
  319. updateData.Clear();
  320. }
  321. if (insertData != null)
  322. {
  323. insertData.Clear();
  324. }
  325. }
  326. private bool IsOnlineNow(EVSECurrentStatus currentEVSE)
  327. {
  328. bool isOnline = false;
  329. int heartbeatInterval = 60;
  330. int.TryParse(currentEVSE.HeartbeatInterval, out heartbeatInterval);
  331. var checkTime = DateTime.UtcNow.AddSeconds(-(heartbeatInterval * 2));
  332. if (currentEVSE.HeartbeatUpdatedOn > checkTime)
  333. {
  334. isOnline = true;
  335. }
  336. return isOnline;
  337. }
  338. }
  339. }