From 18e4fa4b5736fb30497e31007bbe4f85dcac01fc Mon Sep 17 00:00:00 2001 From: Kai Date: Thu, 1 Jul 2021 09:51:33 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E5=8A=A0=E5=85=A5Quartz=202.=20=E5=9C=B0?= =?UTF-8?q?=E5=9C=96=E7=B8=BD=E8=A6=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/MapOverviewController.cs | 25 +++ SolarPower/Models/MapOverview.cs | 11 ++ SolarPower/Quartz/JobSchedule.cs | 46 +++++ .../Quartz/Jobs/OperationScheduleJob.cs | 148 +++++++++++++++ SolarPower/Quartz/SingletonJobFactory.cs | 27 +++ .../OperationScheduleBackgroundService.cs | 179 ------------------ .../Services/Implement/QuartzHostedService.cs | 71 +++++++ .../Implement/SendEmailBackgroundService.cs | 10 +- SolarPower/SolarPower.csproj | 1 + SolarPower/Startup.cs | 16 +- SolarPower/Views/MapOverview/Index.cshtml | 177 +++++++++++++++++ SolarPower/Views/Shared/_Layout.cshtml | 2 +- 12 files changed, 530 insertions(+), 183 deletions(-) create mode 100644 SolarPower/Controllers/MapOverviewController.cs create mode 100644 SolarPower/Models/MapOverview.cs create mode 100644 SolarPower/Quartz/JobSchedule.cs create mode 100644 SolarPower/Quartz/Jobs/OperationScheduleJob.cs create mode 100644 SolarPower/Quartz/SingletonJobFactory.cs delete mode 100644 SolarPower/Services/Implement/OperationScheduleBackgroundService.cs create mode 100644 SolarPower/Services/Implement/QuartzHostedService.cs create mode 100644 SolarPower/Views/MapOverview/Index.cshtml diff --git a/SolarPower/Controllers/MapOverviewController.cs b/SolarPower/Controllers/MapOverviewController.cs new file mode 100644 index 0000000..e1d6a87 --- /dev/null +++ b/SolarPower/Controllers/MapOverviewController.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Mvc; +using SolarPower.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SolarPower.Controllers +{ + public class MapOverviewController : Controller + { + public IActionResult Index() + { + return View(); + } + + //public async Task> GetAllDate() + //{ + // ApiResult apiResult = new ApiResult(); + + + //} + + } +} diff --git a/SolarPower/Models/MapOverview.cs b/SolarPower/Models/MapOverview.cs new file mode 100644 index 0000000..959535b --- /dev/null +++ b/SolarPower/Models/MapOverview.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SolarPower.Models +{ + public class MapOverview + { + } +} diff --git a/SolarPower/Quartz/JobSchedule.cs b/SolarPower/Quartz/JobSchedule.cs new file mode 100644 index 0000000..3452cd9 --- /dev/null +++ b/SolarPower/Quartz/JobSchedule.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Threading.Tasks; + +namespace SolarPower.Quartz +{ + /// + /// Job調度中間對象 + /// + public class JobSchedule + { + public JobSchedule(Type jobType, string cronExpression) + { + this.JobType = jobType ?? throw new ArgumentNullException(nameof(jobType)); + CronExpression = cronExpression ?? throw new ArgumentNullException(nameof(cronExpression)); + } + /// + /// Job類型 + /// + public Type JobType { get; private set; } + /// + /// Cron表達式 + /// + public string CronExpression { get; private set; } + /// + /// Job狀態 + /// + public JobStatus JobStatu { get; set; } = JobStatus.Init; + } + /// + /// Job運行狀態 + /// + public enum JobStatus : byte + { + [Description("初始化")] + Init = 0, + [Description("運行中")] + Running = 1, + [Description("調度中")] + Scheduling = 2, + [Description("已停止")] + Stopped = 3, + } +} diff --git a/SolarPower/Quartz/Jobs/OperationScheduleJob.cs b/SolarPower/Quartz/Jobs/OperationScheduleJob.cs new file mode 100644 index 0000000..5561c81 --- /dev/null +++ b/SolarPower/Quartz/Jobs/OperationScheduleJob.cs @@ -0,0 +1,148 @@ +using Microsoft.Extensions.Logging; +using Quartz; +using SolarPower.Models; +using SolarPower.Repository.Interface; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace SolarPower.Quartz.Jobs +{ + [DisallowConcurrentExecution] + public class OperationScheduleJob : IJob + { + private readonly ILogger logger; + private IOperationRepository operationRepository; + + public OperationScheduleJob(ILogger logger, IOperationRepository operationRepository) + { + this.logger = logger; + this.operationRepository = operationRepository; + } + + public async Task Execute(IJobExecutionContext context) + { + try + { + var getTime = await operationRepository.GetOperationSchedules(); + foreach (var a in getTime) + { + DateTime Updatedtime; + if (a.ScheduleType == 0)//日 + { + Updatedtime = Convert.ToDateTime(a.StartTime).AddDays(a.ScheduleNum); + } + else if (a.ScheduleType == 1)//周 + { + Updatedtime = Convert.ToDateTime(a.StartTime).AddDays(a.ScheduleNum * 7); + } + else if (a.ScheduleType == 2)//月 + { + Updatedtime = Convert.ToDateTime(a.StartTime).AddMonths(a.ScheduleNum); + } + else if (a.ScheduleType == 3)//季 + { + Updatedtime = Convert.ToDateTime(a.StartTime).AddMonths(a.ScheduleNum * 3); + } + else // 年 + { + Updatedtime = Convert.ToDateTime(a.StartTime).AddYears(a.ScheduleNum); + } + + if (Updatedtime < DateTime.Now) + { + var now = DateTime.Now.ToString("yyyy-MM-dd"); + var finalid = await operationRepository.GetCurrentSerialNumber("operation_plan_create", $"PowerStationId = {a.PowerStationId} AND CreatedAt LIKE '%{now}%'"); + var newSerialNumber = GetLastSerialNumber(finalid); + var OperationPlan = new OperationCreatePlan() + { + EmailType = a.EmailType, + ScheduleNum = a.ScheduleNum, + Description = a.Description, + WorkDay = a.WorkDay, + ScheduleType = a.ScheduleType, + SerialNumber = newSerialNumber, + StartTime = Updatedtime.ToString("yyyy-MM-dd hh:mm:ss"), + PowerStationId = a.PowerStationId, + Type = a.Type, + PlanId = DateTime.Now.ToString("yyyyMMdd") + newSerialNumber, + CreatedBy = a.CreatedBy + }; + List properties = new List() + { + "EmailType", + "ScheduleNum", + "Description", + "WorkDay", + "ScheduleType", + "SerialNumber", + "StartTime", + "PowerStationId", + "Type", + "PlanId", + "CreatedBy" + }; + await operationRepository.AddOperationPlan(OperationPlan, properties); + + var record = new PlanToRecord() + { + WorkType = a.Type, + PowerStationId = a.PowerStationId, + StartTime = Updatedtime.ToString("yyyy-MM-dd hh:mm:ss"), + CreatedBy = a.CreatedBy, + EndTime = Updatedtime.AddDays(a.WorkDay).ToString("yyyy-MM-dd hh:mm:ss") + }; + List properties2 = new List() + { + "WorkType", + "PowerStationId", + "StartTime", + "CreatedBy", + "EndTime" + }; + await operationRepository.AddToRecord(record, properties2); + var operation = await operationRepository.GetOneOperation(a.Id); + await operationRepository.DeleteOneByIdWithCustomTable(a.Id, "operation_plan_create"); + + } + + } + } + catch (Exception exception) + { + logger.LogError("【{0}】{1}", nameof(logger), exception.Message); + } + } + + /// + /// 取得最新的流水號 + /// + /// 當前的 + /// + /// 0: PadLeft;1: PadRight + /// + public string GetLastSerialNumber(string current = "", int pad = 4, byte direction = 0) + { + var tempSerialNumber = 0; + if (!string.IsNullOrEmpty(current)) + { + tempSerialNumber = Convert.ToInt32(current) + 1; + } + else + { + tempSerialNumber = 1; + } + + if (direction == 0) + { + return tempSerialNumber.ToString().Trim().PadLeft(pad, '0'); + } + else + { + return tempSerialNumber.ToString().Trim().PadRight(pad, '0'); + } + } + } +} diff --git a/SolarPower/Quartz/SingletonJobFactory.cs b/SolarPower/Quartz/SingletonJobFactory.cs new file mode 100644 index 0000000..5aa5f3f --- /dev/null +++ b/SolarPower/Quartz/SingletonJobFactory.cs @@ -0,0 +1,27 @@ +using Microsoft.Extensions.DependencyInjection; +using Quartz; +using Quartz.Spi; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SolarPower.Quartz +{ + public class SingletonJobFactory: IJobFactory + { + private readonly IServiceProvider _serviceProvider; + public SingletonJobFactory(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + } + public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) + { + return _serviceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob; + } + public void ReturnJob(IJob job) + { + + } + } +} diff --git a/SolarPower/Services/Implement/OperationScheduleBackgroundService.cs b/SolarPower/Services/Implement/OperationScheduleBackgroundService.cs deleted file mode 100644 index fc685ca..0000000 --- a/SolarPower/Services/Implement/OperationScheduleBackgroundService.cs +++ /dev/null @@ -1,179 +0,0 @@ -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using SolarPower.Models; -using SolarPower.Repository.Interface; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace SolarPower.Services.Implement -{ - public class OperationScheduleBackgroundService : IHostedService, IDisposable - { - static Timer _timer; - private int time_interval = 30; //查詢間隔(秒) - private readonly ILogger _log; - private int execCount = 0; - private readonly IOperationRepository operationRepository; - - public OperationScheduleBackgroundService(ILogger log, - IOperationRepository operationRepository - ) - { - this.operationRepository = operationRepository; - this._log = log; - } - - public void Dispose() - { - _timer?.Dispose(); - } - - public Task StartAsync(CancellationToken cancellationToken) - { - _timer = new Timer(DoWork, null, - TimeSpan.Zero, - TimeSpan.FromSeconds(time_interval)); - - return Task.CompletedTask; - } - - public async void DoWork(object state) - { - //利用 Interlocked 計數防止重複執行 - if (execCount <= 0) - { - Interlocked.Increment(ref execCount); - } - - if (execCount == 1) - { - try - { - var getTime = await operationRepository.GetOperationSchedules(); - foreach (OperationCreatePlanModal a in getTime) - { - DateTime Updatedtime; - if (a.ScheduleType == 0)//日 - { - Updatedtime = Convert.ToDateTime(a.StartTime).AddDays(a.ScheduleNum); - } - else if (a.ScheduleType == 1)//周 - { - Updatedtime = Convert.ToDateTime(a.StartTime).AddDays(a.ScheduleNum * 7); - } - else if (a.ScheduleType == 2)//月 - { - Updatedtime = Convert.ToDateTime(a.StartTime).AddMonths(a.ScheduleNum); - } - else if (a.ScheduleType == 3)//季 - { - Updatedtime = Convert.ToDateTime(a.StartTime).AddMonths(a.ScheduleNum * 3); - } - else // 年 - { - Updatedtime = Convert.ToDateTime(a.StartTime).AddYears(a.ScheduleNum); - } - - if (Updatedtime < DateTime.Now) - { - var now = DateTime.Now.ToString("yyyy-MM-dd"); - var finalid = await operationRepository.GetCurrentSerialNumber("operation_plan_create", $"PowerStationId = {a.PowerStationId} AND CreatedAt LIKE '%{now}%'"); - var newSerialNumber = GetLastSerialNumber(finalid); - var OperationPlan = new OperationCreatePlan() - { - EmailType = a.EmailType, - ScheduleNum = a.ScheduleNum, - Description = a.Description, - WorkDay = a.WorkDay, - ScheduleType = a.ScheduleType, - SerialNumber = newSerialNumber, - StartTime = Updatedtime.ToString("yyyy-MM-dd hh:mm:ss"), - PowerStationId = a.PowerStationId, - Type = a.Type, - PlanId = DateTime.Now.ToString("yyyyMMdd") + newSerialNumber, - CreatedBy = a.CreatedBy - }; - List properties = new List() - { - "EmailType", - "ScheduleNum", - "Description", - "WorkDay", - "ScheduleType", - "SerialNumber", - "StartTime", - "PowerStationId", - "Type", - "PlanId", - "CreatedBy" - }; - await operationRepository.AddOperationPlan(OperationPlan, properties); - - var record = new PlanToRecord() - { - WorkType = a.Type, - PowerStationId = a.PowerStationId, - StartTime = Updatedtime.ToString("yyyy-MM-dd hh:mm:ss"), - CreatedBy = a.CreatedBy, - EndTime = Updatedtime.AddDays(a.WorkDay).ToString("yyyy-MM-dd hh:mm:ss") - }; - List properties2 = new List() - { - "WorkType", - "PowerStationId", - "StartTime", - "CreatedBy", - "EndTime" - }; - await operationRepository.AddToRecord(record, properties2); - var operation = await operationRepository.GetOneOperation(a.Id); - await operationRepository.DeleteOneByIdWithCustomTable(a.Id, "operation_plan_create"); - - } - - } - } - catch (Exception ex) - { - _log.LogError("【OperationScheduleService】 - " + ex.Message); - } - finally - { - Interlocked.Decrement(ref execCount); - } - } - } - - public string GetLastSerialNumber(string current = "", int pad = 4, byte direction = 0) - { - var tempSerialNumber = 0; - if (!string.IsNullOrEmpty(current)) - { - tempSerialNumber = Convert.ToInt32(current) + 1; - } - else - { - tempSerialNumber = 1; - } - - if (direction == 0) - { - return tempSerialNumber.ToString().Trim().PadLeft(pad, '0'); - } - else - { - return tempSerialNumber.ToString().Trim().PadRight(pad, '0'); - } - } - - public Task StopAsync(CancellationToken cancellationToken) - { - //調整Timer為永不觸發,停用定期排程 - _timer?.Change(Timeout.Infinite, 0); - return Task.CompletedTask; - } - } -} diff --git a/SolarPower/Services/Implement/QuartzHostedService.cs b/SolarPower/Services/Implement/QuartzHostedService.cs new file mode 100644 index 0000000..0b9b000 --- /dev/null +++ b/SolarPower/Services/Implement/QuartzHostedService.cs @@ -0,0 +1,71 @@ +using Microsoft.Extensions.Hosting; +using Quartz; +using Quartz.Spi; +using SolarPower.Models; +using SolarPower.Quartz; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace SolarPower.Services.Implement +{ + public class QuartzHostedService : IHostedService + { + private readonly ISchedulerFactory _schedulerFactory; + private readonly IJobFactory _jobFactory; + private readonly IEnumerable _jobSchedules; + public QuartzHostedService(ISchedulerFactory schedulerFactory, IJobFactory jobFactory, IEnumerable jobSchedules) + { + _schedulerFactory = schedulerFactory ?? throw new ArgumentNullException(nameof(schedulerFactory)); + _jobFactory = jobFactory ?? throw new ArgumentNullException(nameof(jobFactory)); + _jobSchedules = jobSchedules ?? throw new ArgumentNullException(nameof(jobSchedules)); + } + public IScheduler Scheduler { get; set; } + public async Task StartAsync(CancellationToken cancellationToken) + { + Scheduler = await _schedulerFactory.GetScheduler(cancellationToken); + Scheduler.JobFactory = _jobFactory; + foreach (var jobSchedule in _jobSchedules) + { + var job = CreateJob(jobSchedule); + var trigger = CreateTrigger(jobSchedule); + await Scheduler.ScheduleJob(job, trigger, cancellationToken); + jobSchedule.JobStatu = JobStatus.Scheduling; + } + await Scheduler.Start(cancellationToken); + foreach (var jobSchedule in _jobSchedules) + { + jobSchedule.JobStatu = JobStatus.Running; + } + } + public async Task StopAsync(CancellationToken cancellationToken) + { + await Scheduler?.Shutdown(cancellationToken); + foreach (var jobSchedule in _jobSchedules) + { + + jobSchedule.JobStatu = JobStatus.Stopped; + } + } + private static IJobDetail CreateJob(JobSchedule schedule) + { + var jobType = schedule.JobType; + return JobBuilder + .Create(jobType) + .WithIdentity(jobType.FullName) + .WithDescription(jobType.Name) + .Build(); + } + private static ITrigger CreateTrigger(JobSchedule schedule) + { + return TriggerBuilder + .Create() + .WithIdentity($"{schedule.JobType.FullName}.trigger") + .WithCronSchedule(schedule.CronExpression) + .WithDescription(schedule.CronExpression) + .Build(); + } + } +} diff --git a/SolarPower/Services/Implement/SendEmailBackgroundService.cs b/SolarPower/Services/Implement/SendEmailBackgroundService.cs index ba429c3..27da572 100644 --- a/SolarPower/Services/Implement/SendEmailBackgroundService.cs +++ b/SolarPower/Services/Implement/SendEmailBackgroundService.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; @@ -36,7 +37,7 @@ namespace SolarPower.Services.Implement Interlocked.Increment(ref execCount); } - if (execCount == 1 && send_complete) + if (execCount == 1) { try { @@ -58,5 +59,10 @@ namespace SolarPower.Services.Implement _timer?.Change(Timeout.Infinite, 0); return Task.CompletedTask; } + + public void Dispose() + { + throw new NotImplementedException(); + } } } diff --git a/SolarPower/SolarPower.csproj b/SolarPower/SolarPower.csproj index 99a4a6d..c0572ef 100644 --- a/SolarPower/SolarPower.csproj +++ b/SolarPower/SolarPower.csproj @@ -27,6 +27,7 @@ + diff --git a/SolarPower/Startup.cs b/SolarPower/Startup.cs index d45e776..0f86fcb 100644 --- a/SolarPower/Startup.cs +++ b/SolarPower/Startup.cs @@ -8,9 +8,13 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using MySql.Data.MySqlClient; +using Quartz; +using Quartz.Impl; +using Quartz.Spi; using SolarPower.Helper; - using SolarPower.Models; +using SolarPower.Quartz; +using SolarPower.Quartz.Jobs; using SolarPower.Repository.Implement; using SolarPower.Repository.Interface; using SolarPower.Services; @@ -81,6 +85,16 @@ namespace SolarPower #region [JI //services.AddHostedService(); + + //K[QuartzA + services.AddSingleton(); + services.AddSingleton(); + //K[ڭ̪Job + services.AddSingleton(); + services.AddSingleton( + new JobSchedule(jobType: typeof(OperationScheduleJob), cronExpression: "0/5 * * * * ?") + ); + services.AddHostedService(); #endregion } diff --git a/SolarPower/Views/MapOverview/Index.cshtml b/SolarPower/Views/MapOverview/Index.cshtml new file mode 100644 index 0000000..2f21d5e --- /dev/null +++ b/SolarPower/Views/MapOverview/Index.cshtml @@ -0,0 +1,177 @@ +@{ + ViewData["MainNum"] = "1"; + ViewData["SubNum"] = "1"; + ViewData["Title"] = "地圖總覽"; +} + + +
+
+
+
+
+ + +@section Scripts{ + + + + + +} diff --git a/SolarPower/Views/Shared/_Layout.cshtml b/SolarPower/Views/Shared/_Layout.cshtml index 844d299..a6acd9b 100644 --- a/SolarPower/Views/Shared/_Layout.cshtml +++ b/SolarPower/Views/Shared/_Layout.cshtml @@ -89,7 +89,7 @@ -->
  • - + 總覽