c# 定時啟動一個操作、任務

  • 2021 年 4 月 18 日
  • 筆記
// 定時啟動一個操作、任務
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;

/*
功能:定時啟動一個操作。
作者:李茂平
用法:
this.TimeWork = new TimeWork();

this.TimeWork.AddWork(new TimeWorkItem()
{
任務名稱 = "任務一",
Work = this.ClockMorningDownload,
計劃啟動時間 = "09:00",
任務說明 = "任務一說明 "
});

this.TimeWork.SetEnable(true);

*/
namespace ClassLibrary
{
    /// <summary>
    /// 定時執行任務的類,精度每分鐘
    /// </summary>
    public class TimeWork : ObservableCollection<TimeWorkItem>
    {
        /// <summary>
        /// 使用多執行緒計時器
        /// </summary>
        private readonly System.Timers.Timer timer1 = new System.Timers.Timer();

        /// <summary>
        /// 運行的分鐘數
        /// </summary>
        private int CountMin { get; set; } = 0;

        /// <summary>
        /// 當任務完成時
        /// </summary>
        public Action<TimeWorkItem, TimeWorkLogItem> OnWorkFinished { get; set; }
        /// <summary>
        /// 執行記錄
        /// </summary>
        public ObservableCollection<TimeWorkLogItem> LogItems { get; set; } = new ObservableCollection<TimeWorkLogItem>();
        /// <summary>
        /// 構造函數,創建精度為1分鐘的定時器
        /// </summary>
        public TimeWork()
        {
            this.timer1.Interval = 1 * 1000 * 60;    // 1分鐘的定時器
            this.timer1.Elapsed += timerMinute_Elapsed;
            this.timer1.Enabled = false;    // 初始化為不發生定時事件
        }

        /// <summary>
        /// 是否啟用定時任務
        /// </summary>
        /// <param name="bEnable"></param>
        public void SetEnable(bool bEnable)
        {
            this.timer1.Enabled = bEnable;
            ResetRunTime();
        }

        /// <summary>
        /// 定時器時間到,檢查啟動項目
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void timerMinute_Elapsed(object sender, ElapsedEventArgs e)
        {
            CountMin++;

            if (CountMin % 600 == 0) // 每10個小時重置一次
            {
                ResetRunTime();
            }

            DoTimerWork();
        }

        /// <summary>
        /// 重新設定運行時間
        /// </summary>
        public void ResetRunTime()
        {
            foreach (var item in this)
            {
                item.InitRunTimes();
            }
        }

        /// <summary>
        /// 定時器時間到,檢查啟動項目
        /// </summary>
        private void DoTimerWork()
        {
            DateTime now = DateTime.Now;
            foreach (var item in this)
            {
                foreach (var time in item.RunTimes)
                {
                    if (time.Hours == now.Hour &&
                         time.Minutes == now.Minute)
                    {
                        Thread t = new Thread(() =>
                        {
                            this.DoWork(item);
                        });
                        t.Start();
                    }
                }
            }
        }

        /// <summary>
        /// 添加一個定時執行的項目
        /// </summary>
        /// <param name="item"></param>
        public void AddWork(TimeWorkItem item)
        {
            this.Add(item);
        }

        /// <summary>
        /// 執行任務
        /// </summary>
        /// <param name="item"></param>
        public void DoWork(TimeWorkItem item)
        {
            if (item != null && item.Work != null)
            {
                DateTime begin = DateTime.Now;
                item.Work(); // 執行工作

                if (item.添加到執行記錄)
                    LogItem(item, begin, DateTime.Now);

                item.上次執行時間 = begin.ToString("T");
            }
        }

        /// <summary>
        /// 添加一條記錄到文件,記錄已經執行的任務
        /// </summary>
        /// <param name="item"></param>
        private void LogItem(TimeWorkItem item, DateTime begin, DateTime end)
        {
            TimeWorkLogItem logItem = new TimeWorkLogItem();

            logItem.任務名稱 = item.任務名稱;
            logItem.執行時間 = begin;
            logItem.完成時間 = end;
            this.LogItems.Add(logItem);

            if (OnWorkFinished != null)
            {
                OnWorkFinished(item, logItem);
            }

            //App.Data.UcDbModel.AddTimeWorksLog(logItem);
        }
    }
    /// <summary>
    /// MVVM模式下,更改屬性的通知
    /// 首先定義NotificationObject類。目的是綁定數據屬性。
    /// 這個類的作用是實現了INotifyPropertyChanged介面。
    /// WPF中類要實現這個介面,其屬性成員才具備通知UI的能力
    /// </summary>

    public class NotificationObjectEF : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    /// <summary>
    /// 定時工作項目
    /// </summary>
    public class TimeWorkItem : NotificationObjectEF
    {
        public Action Work { get; set; }
        public string 任務名稱 { get; set; }
        public string 任務說明 { get; set; }
        /// <summary>
        /// 設定的計劃啟動日期,空格分開,如:每周6,每月1,空白意味著每天
        /// 空白 -- 啟動
        /// 周一 到 周日 啟動
        /// 5月2日 啟動
        /// </summary>
        public string 計劃啟動日期 { get; set; }
        /// <summary>
        /// 空格分開的多個時間:15:00 16:32
        /// </summary>
        public string 計劃啟動時間 { get; set; }
        /// <summary>
        /// 間隔多少分鐘重複,每天執行
        /// </summary>
        public int 間隔分鐘 { get; set; }
        /// <summary>
        /// 是否記錄到執行記錄
        /// </summary>
        public bool 添加到執行記錄 { get; set; } = true;

        private string 上次執行時間_;

        public string 上次執行時間
        {
            get
            {
                return 上次執行時間_;
            }
            set
            {
                上次執行時間_ = value;
                RaisePropertyChanged("上次執行時間");
            }
        }

        /// <summary>
        /// 運行任務的時間表
        /// </summary>
        public ObservableCollection<TimeSpan> RunTimes { get; set; } = new ObservableCollection<TimeSpan>();

        /// <summary>
        /// 運行任務的時間表
        /// </summary>
        private string runTimeStr;

        /// <summary>
        /// 運行任務的時間表
        /// </summary>
        public string RunTimesStr
        {
            get
            {
                return runTimeStr;
            }
            set
            {
                runTimeStr = value;
                RaisePropertyChanged("RunTimesStr");
            }
        }

        /// <summary>
        /// 初始化時間
        /// </summary>
        public void InitRunTimes()
        {
            // 分析時間
            RunTimes.Clear();

            if (!string.IsNullOrEmpty(計劃啟動時間) && IsTodayRun())
            {

                string[] times = 計劃啟動時間.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                foreach (var item in times)
                {
                    DateTime dt = new DateTime();
                    if (DateTime.TryParse(item, out dt))
                    {
                        RunTimes.Add(dt.TimeOfDay);
                    }
                }
            }

            if (間隔分鐘 > 0 && string.IsNullOrEmpty(計劃啟動日期) && string.IsNullOrEmpty(計劃啟動時間))
            {
                DateTime now = DateTime.Now;
                DateTime oneTime = now.AddMinutes(間隔分鐘);
                DateTime EndTime = now.AddHours(12);

                while (oneTime <= EndTime)
                {
                    RunTimes.Add(oneTime.TimeOfDay);
                    oneTime = oneTime.AddMinutes(間隔分鐘);
                }
            }

           
                // 設置字元串
            string newRunTimesStr = "";
            foreach (TimeSpan ts in RunTimes)
            {
                newRunTimesStr += $"{ts.Hours:00}:{ts.Minutes:00} ";
            }
            RunTimesStr = newRunTimesStr;
        }
        /// <summary>
        /// 判斷今日是否是執行日期.
        /// 空白 -- 是
        /// 周一 到周日 是
        /// 5月2日 是
        /// </summary>
        /// <returns></returns>
        private bool IsTodayRun()
        {
            if (string.IsNullOrEmpty(this.計劃啟動日期))
                return true;

            string[] days = 計劃啟動日期.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            foreach (var item in days)
            {
                if (item.Contains("") || item.Contains("星期"))
                {
                    if (DateTime.Now.DayOfWeek == ConvertToDayOfWeek(item))
                        return true;
                }
                if (item.Contains("") || item.Contains(""))
                {
                    string full = $"{DateTime.Now.Year}年" + item;
                    if(DateTime.TryParse(full, out DateTime setDate))
                    {
                        if (setDate.Date == DateTime.Now.Date)
                            return true;
                    }
                   
                }

            }
            return false;
        }

        private DayOfWeek ConvertToDayOfWeek(string item)
        {
            switch(item)
            {
                case "周一":
                case "星期一":
                    return DayOfWeek.Monday;
                case "周二":
                case "星期二":
                    return DayOfWeek.Tuesday;
                case "周三":
                case "星期三":
                    return DayOfWeek.Wednesday;
                case "周四":
                case "星期四":
                    return DayOfWeek.Thursday;
                case "周五":
                case "星期五":
                    return DayOfWeek.Monday;
                case "周六":
                case "星期六":
                    return DayOfWeek.Saturday;
                case "周日":
                case "星期日":
                    return DayOfWeek.Sunday;
                default:
                    return DayOfWeek.Sunday;

            }
        }
    }

    [Serializable]
    public class TimeWorkLogItem
    {
        /// <summary>
        /// 名稱
        /// </summary>
        public string 任務名稱 { get; set; }

        /// <summary>
        /// 上次執行時間
        /// </summary>
        public DateTime 執行時間 { get; set; }

        /// <summary>
        /// 完成時間
        /// </summary>
        public DateTime 完成時間 { get; set; }
    }
}