using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using static System.Configuration.ConfigurationManager;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Xml;
using Microsoft.VisualBasic;
using Microsoft.VisualBasic.CompilerServices;
using static iMESCore.DataBase.iMESSql;

namespace IEWService
{

    public partial class IEWService
    {

        #region 列舉型別

        public enum ExecuteResultEnum : int
        {
            SUCCESS = 0,     // 成功
            FAIL = 1        // 失敗
        }

        // 警示DB類型
        public enum AlertDBTypeEnum : int
        {
            RealTime = 0,    // 即時資料庫
            History = 1     // 歷史資料庫
        }

        // 警示類型
        // 2014/01/17 YF, 加入PRD & OP
        public enum AlertTypeEnum : int
        {
            ERF = 1,         // ERFNo
            LOT = 2,         // LotNo
            EQP = 3,         // EQPNo
            ACC = 4,         // AccNo
            MO = 5,          // MONo
            PRD = 6,         // PRD
            OP = 7          // OP
        }

        // 資料型態
        public enum DataTypeEnum : int
        {
            NumericType = 0,     // 數值型態
            StringType = 1,      // 字串型態
            DateTimeType = 3    // 日期型態
        }

        // 條件週期
        public enum DataPeriodEnum : int
        {
            Minute = 0,          // 分鐘
            Hour = 1,            // 小時
            Day = 2,             // 天
            Month = 3           // 月
        }

        // 警示事件類型
        public enum AlertEventTypeEnum : int
        {
            DEF = 0,     // 預設事件
            CUS = 1     // 自定義事件
        }

        #endregion
        private const int CHECKEVENT_INTERVAL = 300;         // 每隔300秒鐘, 檢查是否有符合週期的預警事件

        private int MAX_RUNNINGJOB_COUNT = 5;             // 允許同時執行預警事件數量
        private BackgroundWorker[] workers;                   // 用來執行預警事件的執行緒
        private BackgroundWorker WorkerMain;                  // 主執行緒, 用來啟動Event, EventLog, AutoRunLog and Delivery執行緒
        private BackgroundWorker WorkerEvent;                 // 預警事件執行緒, 指派QueueEvent的預警事件給workers
        private BackgroundWorker WorkerEventLog;              // 預警事件Log執行緒, 將QueueEventLog的資料寫入資料庫
        private BackgroundWorker WorkerAutoRunLog;            // AutoRunLog執行緒, 將QueueAutoRunLog的資料寫入資料庫
        private BackgroundWorker WorkerDelivery;              // 派送執行緒, 派送QueueDelivery的資料

        private IEWQueue<AlertEvent> _QueueEvent;

        private IEWQueue<AlertEvent> QueueEvent
        {
            [MethodImpl(MethodImplOptions.Synchronized)]
            get
            {
                return _QueueEvent;
            }

            [MethodImpl(MethodImplOptions.Synchronized)]
            set
            {
                if (_QueueEvent != null)
                {
                    _QueueEvent.AddComplete -= QueueEvent_AddComplete;
                }

                _QueueEvent = value;
                if (_QueueEvent != null)
                {
                    _QueueEvent.AddComplete += QueueEvent_AddComplete;
                }
            }
        }
        private IEWQueue<AlertEvent> _QueueEventLog;

        private IEWQueue<AlertEvent> QueueEventLog
        {
            [MethodImpl(MethodImplOptions.Synchronized)]
            get
            {
                return _QueueEventLog;
            }

            [MethodImpl(MethodImplOptions.Synchronized)]
            set
            {
                if (_QueueEventLog != null)
                {
                    _QueueEventLog.AddComplete -= QueueEventLog_AddComplete;
                }

                _QueueEventLog = value;
                if (_QueueEventLog != null)
                {
                    _QueueEventLog.AddComplete += QueueEventLog_AddComplete;
                }
            }
        }
        private IEWQueue<AutoRunLog> _QueueAutoRunLog;

        private IEWQueue<AutoRunLog> QueueAutoRunLog
        {
            [MethodImpl(MethodImplOptions.Synchronized)]
            get
            {
                return _QueueAutoRunLog;
            }

            [MethodImpl(MethodImplOptions.Synchronized)]
            set
            {
                if (_QueueAutoRunLog != null)
                {
                    _QueueAutoRunLog.AddComplete -= QueueAutoRunLog_AddComplete;
                }

                _QueueAutoRunLog = value;
                if (_QueueAutoRunLog != null)
                {
                    _QueueAutoRunLog.AddComplete += QueueAutoRunLog_AddComplete;
                }
            }
        }
        private IEWQueue<AlertEvent> _QueueDelivery;

        private IEWQueue<AlertEvent> QueueDelivery
        {
            [MethodImpl(MethodImplOptions.Synchronized)]
            get
            {
                return _QueueDelivery;
            }

            [MethodImpl(MethodImplOptions.Synchronized)]
            set
            {
                if (_QueueDelivery != null)
                {
                    _QueueDelivery.AddComplete -= QueueDelivery_AddComplete;
                }

                _QueueDelivery = value;
                if (_QueueDelivery != null)
                {
                    _QueueDelivery.AddComplete += QueueDelivery_AddComplete;
                }
            }
        }

        // 即時&歷史資料庫連線相關資料
        private DatabaseInfo dbi = new DatabaseInfo();

        private iMESLog.MESLog logger;

        public IEWService()
        {
            QueueEvent = new IEWQueue<AlertEvent>();        // 預警事件Queue, 符合執行週期的事件會放入此Queue
            QueueEventLog = new IEWQueue<AlertEvent>();     // 預警事件Log Queue, 預警事件執行後, 將觸發結果放入此Queue
            QueueAutoRunLog = new IEWQueue<AutoRunLog>();   // AutoRunLog Queue, 預警事件執行後, 將要Log的資料放入此Queue
            QueueDelivery = new IEWQueue<AlertEvent>();     // 派送Queue, 預警事件觸發後, 將要派送的資料放入此Queue
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {

            try
            {
                // 取出允許同時執行預警事件數量
                MAX_RUNNINGJOB_COUNT = Convert.ToInt32(AppSettings["MaxRunningJobCount"]);

                logger = new iMESLog.MESLog(GetType().ToString());
            }
            catch (Exception ex)
            {
            }

            workers = new BackgroundWorker[MAX_RUNNINGJOB_COUNT];

            // 建立主執行緒
            WorkerMain = new BackgroundWorker();
            WorkerMain.DoWork += Main_DoWork;
            WorkerMain.RunWorkerCompleted += Main_RunWorkerCompleted;
            WorkerMain.RunWorkerAsync();

            // 設定檢查是否有符合週期的預警事件的間隔秒數
            TimerCheckEvent.Interval = CHECKEVENT_INTERVAL * 1000;
            TimerCheckEvent.Enabled = true;

            funWriteTxtFile("Service Started.");

        }

        protected override void OnStop()
        {

            TimerCheckEvent.Enabled = false;

            var result = new AutoRunLog();

            try
            {
                result.AlertEventID = "IEW Service Stop";
                result.AlertEventName = result.AlertEventID;
                result.StartTime = DateTime.Now;

                workers = null;
                WorkerMain = null;
                WorkerEvent = null;
                WorkerEventLog = null;
                WorkerAutoRunLog = null;
                WorkerDelivery = null;

                QueueEvent = null;
                QueueEventLog = null;
                QueueAutoRunLog = null;
                QueueDelivery = null;

                dbi = null;

                result.ExecuteResult = ExecuteResultEnum.SUCCESS;
                result.ErrorMessage = "Success";
                funWriteTxtFile("Service Stopped.");
            }

            catch (Exception ex)
            {
                result.ExecuteResult = ExecuteResultEnum.FAIL;
                result.ErrorMessage = ex.Message;
                funWriteTxtFile("Service Error : " + ex.Message + ".", ex);
            }
            finally
            {
                result.EndTime = DateTime.Now;
                WriteToAnEventLog(result, EventLogEntryType.Information, 9000);
                result = null;
            }

        }

        private void TimerCheckEvent_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {

            try
            {

                TimerCheckEvent.Enabled = false;
                funWriteTxtFile("CheckEvent");
                CheckEvent();
            }

            catch (Exception ex)
            {
            }

            finally
            {
                TimerCheckEvent.Enabled = true;
            }

        }

        #region CheckEvent

        /// <summary>
        /// 取出符合執行週期的預警事件
        /// </summary>
        /// <remarks></remarks>
        private void CheckEvent()
        {

            var result = new AutoRunLog();
            string strSQL;
            DataTable dtTemp = null;
            string str10MinIntervalSQLString;
            string strSpecificSQLString;
            int intCheckTimePoint = 6;
            int intCheckTimeOver = 60 - intCheckTimePoint;

            try
            {

                result.LogID = "N/A";
                result.AlertEventID = "CheckEvent";
                result.AlertEventName = result.AlertEventID;
                result.StartTime = DateTime.Now;

                // 將失效事件的狀態改為非作用中
                strSQL = " Update TBLIEWALERTEVENTBASIS " + " Set    Status = 1 ";

                if (dbi.RealDB.DatabaseType.ToLower() != "oracle")
                {
                    strSQL += " Where  INVALIDDATE < GETDATE() ";
                }
                else
                {
                    strSQL += " Where  INVALIDDATE < Sysdate ";
                }

                //var cn = CreateConnection(dbi.RealDB.ConnectionString);
                //using (var cmd = CreateCommand(strSQL, cn))
                //{
                //    string cmdTmp = cmd.ToString();
                //    //cn.Open();
                //    cmd.ExecuteNonQuery();
                //    cn.Close();
                //}
                ExecuteSQLNoneQuery(dbi.RealDB.DatabaseType, dbi.RealDB.ConnectionString, ref strSQL);


                // 取出符合執行週期的預警事件
                if (dbi.RealDB.DatabaseType.ToLower() != "oracle")
                {
                    // SQL Server
                    // 20190213 FixBy KevinChang 0054942: 6.0.3.5 智慧預警派送頻率問題
                    // str10MinIntervalSQLString = "(RUNFREQUENT = 'Each10Min' and (LASTRUNTIME is null or LASTRUNTIME <= Cast('" & Format(Now.AddMinutes(-10).AddSeconds(intCheckTimePoint * 60), "yyyy/MM/dd HH:mm:ss") & "' as datetime)))"
                    str10MinIntervalSQLString = "(RUNFREQUENT = 'Each10Min' and (LASTRUNTIME is null or LASTRUNTIME <= Cast('" + Strings.Format(DateTime.Now.AddMinutes(-10), "yyyy/MM/dd HH:mm:ss") + "' as datetime)))";
                }
                else
                {
                    // Oracle
                    // 20190213 FixBy KevinChang 0054942: 6.0.3.5 智慧預警派送頻率問題
                    // str10MinIntervalSQLString = "(RUNFREQUENT = 'Each10Min' and (LASTRUNTIME is null or LASTRUNTIME <= TO_DATE('" & Format(Now.AddMinutes(-10).AddSeconds(intCheckTimePoint * 60), "yyyy/MM/dd HH:mm:ss") & "','YYYY/MM/DD HH24:MI:SS')))"
                    str10MinIntervalSQLString = "(RUNFREQUENT = 'Each10Min' and (LASTRUNTIME is null or LASTRUNTIME <= TO_DATE('" + Strings.Format(DateTime.Now.AddMinutes(-10), "yyyy/MM/dd HH:mm:ss") + "','YYYY/MM/DD HH24:MI:SS')))";
                }

                // 取出指定時間要執行的job
                if (dbi.RealDB.DatabaseType.ToLower() != "oracle")
                {
                    // SQL Server
                    strSpecificSQLString = "( LASTRUNTIME < CAST('" + Strings.Format(DateTime.Now.AddSeconds(-intCheckTimePoint * 60), "yyyy/MM/dd HH:mm:ss") + "' as datetime))";
                }
                else
                {
                    // Oracle
                    strSpecificSQLString = "( LASTRUNTIME < TO_DATE('" + Strings.Format(DateTime.Now.AddSeconds(-intCheckTimePoint * 60), "yyyy/MM/dd HH:mm:ss") + "','YYYY/MM/DD HH24:MI:SS'))";
                }

                strSpecificSQLString += " and ((RUNYEAR is null or RUNYEAR = '" + Strings.Format(DateTime.Now.Year, "0000") + "')";
                strSpecificSQLString += " and ( RUNMONTH is null or RUNMONTH = '" + Strings.Format(DateTime.Now.Month, "00") + "')";
                strSpecificSQLString += " and ( RUNDAY is null or RUNDAY = '" + Strings.Format(DateTime.Now.Day, "00") + "')";
                strSpecificSQLString += " and ( RUNWEEK is null or RUNWEEK = '" + DateTime.Now.DayOfWeek.ToString() + "')";
                strSpecificSQLString += " and ( RUNHOUR is null or RUNHOUR = '" + Strings.Format(DateTime.Now, "HH") + "')";

                // 取出指定時間要執行的job之2
                // 若n分鐘內, 包含跨小時, 跨日, 跨月, 跨年,則
                if (DateTime.Now.Minute > intCheckTimeOver)
                {
                    strSpecificSQLString += " and ( RUNMIN>='" + Strings.Format(DateTime.Now.Minute, "00") + "' AND RUNMIN<'60'))";
                    strSpecificSQLString += " or (( RUNYEAR is null or  RUNYEAR='" + Strings.Format(DateTime.Now.AddMinutes(intCheckTimePoint).Year, "0000") + "' or RUNYEAR = '')";
                    strSpecificSQLString += " and ( RUNMONTH is null or RUNMONTH='" + Strings.Format(DateTime.Now.AddMinutes(intCheckTimePoint).Month, "00") + "' or RUNMONTH ='')";
                    strSpecificSQLString += " and ( RUNDAY is null or RUNDAY='" + Strings.Format(DateTime.Now.AddMinutes(intCheckTimePoint).Day, "00") + "' or RUNDAY = '')";
                    strSpecificSQLString += " and ( RUNWEEK is null or RUNWEEK='" + DateTime.Now.AddMinutes(intCheckTimePoint).DayOfWeek.ToString() + "' or RUNWEEK = '')";
                    strSpecificSQLString += " and ( RUNHOUR is null or RUNHOUR='" + Strings.Format(DateTime.Now.AddMinutes(intCheckTimePoint), "HH") + "')";
                    strSpecificSQLString += " and ( RUNMIN>='00' AND RUNMIN<'" + Strings.Format(DateTime.Now.AddMinutes(intCheckTimePoint).Minute, "00") + "'))";
                }
                else
                {
                    strSpecificSQLString += " and ( RUNMIN>='" + Strings.Format(DateTime.Now.Minute, "00") + "' AND RUNMIN<'" + Strings.Format(DateTime.Now.AddMinutes(intCheckTimePoint).Minute, "00") + "'))";
                }

                strSQL = " Select * " + " From   tblIEWAlertEventBasis " + " Where  Status = 0 " + " And ( (LASTRUNTIME is null) or (" + strSpecificSQLString + ") or (" + str10MinIntervalSQLString + "))";
                //using (var da = new System.Data.OleDb.OleDbDataAdapter(strSQL, dbi.RealDB.ConnectionString))
                //{
                //    dtTemp = new DataTable();
                //    da.Fill(dtTemp);
                //}
                ExecuteSQLQuery_Adapter(strSQL, dtTemp, dbi.RealDB.ConnectionString, dbi.RealDB.DatabaseType);

                // 將要執行且不存在Quene的預警事件放入Queue
                foreach (DataRow row in dtTemp.Rows)
                {
                    var ae = new AlertEvent(row);
                    if (!QueueEvent.Contains(ae))
                    {
                        ae.LogID = Guid.NewGuid().ToString();
                        QueueEvent.Add(ae);

                        funWriteTxtFile(string.Format("Add Event To Queue.{2}{0}:{1}:", ae.AlertEventID, ae.AlertEventName, Constants.vbCrLf));
                    }
                }

                result.EndTime = DateTime.Now;
                result.ExecuteResult = ExecuteResultEnum.SUCCESS;
                result.ErrorMessage = "Success";
            }

            catch (Exception ex)
            {
                result.EndTime = DateTime.Now;
                result.ExecuteResult = ExecuteResultEnum.FAIL;
                result.ErrorMessage = ex.Message;
                QueueAutoRunLog.Add(result);
            }
            finally
            {
                if (dtTemp != null)
                {
                    dtTemp.Dispose();
                    dtTemp = null;
                }
                result = null;
            }

        }

        #endregion

        #region 主執行緒

        /// <summary>
        /// 主執行緒非同步作業開始, 用來啟動其它執行緒
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks></remarks>
        private void Main_DoWork(object sender, DoWorkEventArgs e)
        {

            var result = new AutoRunLog();

            try
            {

                result.AlertEventID = "IEW Service Start";
                funWriteTxtFile("IEW Service Start");

                result.AlertEventName = result.AlertEventID;
                result.StartTime = DateTime.Now;

                // 建立執行預警事件的執行緒
                for (int i = 0, loopTo = MAX_RUNNINGJOB_COUNT - 1; i <= loopTo; i++)
                {
                    workers[i] = new BackgroundWorker();
                    workers[i].DoWork += DoWork;
                    workers[i].RunWorkerCompleted += RunWorkerCompleted;
                }
                // 啟動預警事件QUEUE的執行緒
                WorkerEvent = new BackgroundWorker();
                WorkerEvent.DoWork += Event_DoWork;
                WorkerEvent.RunWorkerCompleted += Event_RunWorkerCompleted;

                // 啟動預警事件Log QUEUE的執行緒
                WorkerEventLog = new BackgroundWorker();
                WorkerEventLog.DoWork += EventLog_DoWork;
                WorkerEventLog.RunWorkerCompleted += EventLog_RunWorkerCompleted;

                // 啟動AutoRunLog QUEUE的執行緒 
                WorkerAutoRunLog = new BackgroundWorker();
                WorkerAutoRunLog.DoWork += AutoRunLog_DoWork;
                WorkerAutoRunLog.RunWorkerCompleted += AutoRunLog_RunWorkerCompleted;

                // 啟動派送QUEUE的執行緒
                WorkerDelivery = new BackgroundWorker();
                WorkerDelivery.DoWork += Delivery_DoWork;
                WorkerDelivery.RunWorkerCompleted += Delivery_RunWorkerCompleted;

                result.ExecuteResult = ExecuteResultEnum.SUCCESS;
                result.ErrorMessage = "Success";
                funWriteTxtFile("Success");
            }
            catch (Exception ex)
            {
                result.ExecuteResult = ExecuteResultEnum.FAIL;
                result.ErrorMessage = ex.Message;
                funWriteTxtFile(ex.Message, ex);
            }
            finally
            {
                result.EndTime = DateTime.Now;
                e.Result = result;
            }

        }

        /// <summary>
        /// 主執行緒非同步作業結束
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks></remarks>
        private void Main_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {

            AutoRunLog result = (AutoRunLog)e.Result;

            // 寫入Windows事件檢視器, 可由事件檢視器查看是否啟動成功
            if (result.ExecuteResult == ExecuteResultEnum.SUCCESS)
            {
                WriteToAnEventLog(result, EventLogEntryType.Information, 9000);
            }
            else
            {
                WriteToAnEventLog(result, EventLogEntryType.Error, 9000);
            }

        }

        #endregion

        #region 預警事件Queue執行緒

        /// <summary>
        /// 預警事件Queue非同步作業開始
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks></remarks>
        private void Event_DoWork(object sender, DoWorkEventArgs e)
        {

            AlertEvent ae = null;
            AutoRunLog result = null;
            AutoRunLog result2 = null;

            try
            {

                result2 = new AutoRunLog();
                result2.LogID = "N/A";
                result2.AlertEventID = "Event_DoWork";
                result2.AlertEventName = result2.AlertEventID;
                result2.StartTime = DateTime.Now;

                // 逐筆處理Queue內的預警事件
                while (QueueEvent.Count > 0)
                {

                    try
                    {

                        // 取出Queue中的第一筆預警事件
                        ae = QueueEvent[0];

                        result = new AutoRunLog(ae);

                        // 找出空閒的執行緒
                        for (int i = 0, loopTo = MAX_RUNNINGJOB_COUNT - 1; i <= loopTo; i++)
                        {

                            if (workers[i].IsBusy)
                            {
                                continue;
                            }
                            else
                            {
                                // 指派AlertEvent給執行緒, 並開始執行
                                workers[i].RunWorkerAsync(ae);
                                // 指派事件成功
                                result.ExecuteResult = ExecuteResultEnum.SUCCESS;
                                result.ErrorMessage = "指派事件成功";
                                break;
                            }

                        }
                    }

                    catch (Exception ex)
                    {
                        // 指派事件失敗
                        result.ExecuteResult = ExecuteResultEnum.FAIL;
                        result.ErrorMessage = "指派事件失敗, " + ex.Message;
                    }
                    finally
                    {
                        if (result.ExecuteResult == ExecuteResultEnum.SUCCESS || result.ExecuteResult == ExecuteResultEnum.FAIL)
                        {
                            result.EndTime = DateTime.Now;
                            QueueEvent.Remove(ae);
                            QueueAutoRunLog.Add(result);
                        }
                    }

                }

                result2 = null;
            }

            catch (Exception ex)
            {
                result2.EndTime = DateTime.Now;
                result2.ExecuteResult = ExecuteResultEnum.FAIL;
                result2.ErrorMessage = ex.Message;
                funWriteTxtFile(ex.Message, ex);
            }
            finally
            {
                e.Result = result2;
            }

        }

        /// <summary>
        /// 預警事件Queue非同步作業結束
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks></remarks>
        private void Event_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {

            AutoRunLog result = (AutoRunLog)e.Result;

            if (result != null && result.ExecuteResult == ExecuteResultEnum.FAIL)
            {
                QueueAutoRunLog.Add(result);
            }

        }

        #endregion

        #region 預警事件執行緒

        /// <summary>
        /// 執行預警事件非同步執行作業開始
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks></remarks>
        private void DoWork(object sender, DoWorkEventArgs e)
        {

            BackgroundWorker worker = (BackgroundWorker)sender;
            AlertEvent ae = (AlertEvent)e.Argument;
            DataTable dtTemp = null;
            DataTable dtResult = null;
            string strSQL = "";
            string strConnString = "";
            AutoRunLog result = null;

            try
            {

                funWriteTxtFile(string.Format("Process Queue.{2}{0}:{1}", ae.AlertEventID, ae.AlertEventName, Constants.vbCrLf));

                result = new AutoRunLog(ae);

                // 取出預警事件語法及條件
                if (ae.AlertEventType == AlertEventTypeEnum.CUS)
                {
                    // 自定義事件
                    strSQL = " Select A.AlertEventID, A.AlertEventName, A.Subject, A.Recipient, A.Description, A.Creator, B.ItemNo, " + "        B.CondField, B.CondDataType, B.CondOperator, B.CondValue, B.CondPeriod, B.CondPeriodTime, " + "        C.AlertDBType, C.AlertType, C.AlertScript, B.InsertName " + " From   tblIEWAlertEventBasis A " + "        Left Join tblIEWAlertCondition B " + "           On A.AlertEventID = B.AlertEventID " + "        Inner Join tblIEWAlertBasis C " + "           On A.AlertEventSource = C.AlertNo " + " Where  A.AlertEventID = '" + ae.AlertEventID + "'";
                }
                // 預設事件
                // Oracle & MSSQL Script使用不同欄位
                else if (dbi.RealDB.DatabaseType.ToLower() == "oracle")
                {
                    strSQL = " Select A.AlertEventID, A.AlertEventName, A.Subject, A.Recipient, A.Description, A.Creator, B.ItemNo, " + "        B.CondField, B.CondDataType, B.CondOperator, B.CondValue, B.CondPeriod, B.CondPeriodTime, " + "        C.AlertDBType, C.AlertType, C.EventScript As AlertScript, B.InsertName " + " From   tblIEWAlertEventBasis A " + "        Left Join tblIEWAlertCondition B " + "           On A.AlertEventID = B.AlertEventID " + "        Inner Join tblIEWDefaultEventBasis C " + "           On A.AlertEventSource = C.DefEventID " + " Where  A.AlertEventID = '" + ae.AlertEventID + "'";
                }
                else
                {
                    strSQL = " Select A.AlertEventID, A.AlertEventName, A.Subject, A.Recipient, A.Description, A.Creator, B.ItemNo, " + "        B.CondField, B.CondDataType, B.CondOperator, B.CondValue, B.CondPeriod, B.CondPeriodTime, " + "        C.AlertDBType, C.AlertType, C.EventScript_MSSQL As AlertScript, B.InsertName " + " From   tblIEWAlertEventBasis A " + "        Left Join tblIEWAlertCondition B " + "           On A.AlertEventID = B.AlertEventID " + "        Inner Join tblIEWDefaultEventBasis C " + "           On A.AlertEventSource = C.DefEventID " + " Where  A.AlertEventID = '" + ae.AlertEventID + "'";
                }

                //using (var da = new System.Data.OleDb.OleDbDataAdapter(strSQL, dbi.RealDB.ConnectionString))
                //{
                //    // 2022/11/2,Ning,121417: 台基：服务日志报错：未将对象引用设置到对象的实例
                //    // da.SelectCommand.Parameters.AddWithValue("AlertEventID", ae.AlertEventID)
                //    dtTemp = new DataTable();
                //    da.Fill(dtTemp);
                //}
                //IDbConnection cn = CreateConnection();
                ExecuteSQLQuery_Adapter(strSQL, dtTemp, dbi.RealDB.ConnectionString, dbi.RealDB.DatabaseType);

                if (dtTemp.Rows.Count == 0)
                {
                    throw new Exception("AlertEventID(" + ae.AlertEventID + ")" + Constants.vbCrLf + "警示事件主檔取出失敗!");
                }

                if (string.IsNullOrEmpty(dtTemp.Rows[0]["AlertScript"].ToString()))
                {
                    throw new Exception("AlertEventID(" + ae.AlertEventID + ")" + Constants.vbCrLf + "AlertEventType(" + dtTemp.Rows[0]["AlertEventType"].ToString() + ")" + Constants.vbCrLf + "AlertEventSource(" + dtTemp.Rows[0]["AlertEventSource"].ToString() + ")" + Constants.vbCrLf + "警示語法是空值!");
                }

                string strDBType;
                if (Convert.ToInt32(dtTemp.Rows[0]["AlertDBType"]) == (int)AlertDBTypeEnum.RealTime)
                {
                    strConnString = dbi.RealDB.ConnectionString;
                    strDBType = dbi.RealDB.DatabaseType;
                }
                else
                {
                    strConnString = dbi.HistoryDB.ConnectionString;
                    strDBType = dbi.HistoryDB.DatabaseType;
                }

                // 警示類型 1:ERF, 2:LOT, 3:EQP, 4:ACC, 5:MO
                ae.AlertType = (AlertTypeEnum)Conversions.ToInteger(dtTemp.Rows[0]["AlertType"]);

                // 產生SQL語法
                strSQL = GenerateSQLScript(dtTemp);

                //using (var da = new System.Data.OleDb.OleDbDataAdapter(strSQL, strConnString))
                //{
                //    dtResult = new DataTable();
                //    da.Fill(dtResult);
                //}
                //IDbConnection CNstrConnString = CreateConnection(strConnString);
                ExecuteSQLQuery_Adapter(strSQL, dtResult, strConnString, strDBType);

                if (dtResult != null && dtResult.Rows.Count > 0)
                {
                    ae.ResultTable = dtResult;
                    // 寫入派送Queue 
                    QueueDelivery.Add(ae);
                    // 寫入警示事件Log Queue
                    QueueEventLog.Add(ae);

                    funWriteTxtFile(string.Format("Add Queue To Delivery.{2}{0}:{1}", ae.AlertEventID, ae.AlertEventName, Constants.vbCrLf));
                }

                result.ExecuteResult = ExecuteResultEnum.SUCCESS;
                result.ErrorMessage = "執行事件成功";
                funWriteTxtFile("執行事件成功 執行事件名稱: " + ae.AlertEventName + "");
            }
            catch (Exception ex)
            {
                result.ExecuteResult = ExecuteResultEnum.FAIL;
                result.ErrorMessage = "執行事件失敗, " + ex.Message;
                funWriteTxtFile("執行事件失敗 執行事件名稱: " + ae.AlertEventName + " " + ex.Message, ex);
            }
            finally
            {
                dtTemp = null;
                dtResult = null;
                e.Result = result;
            }

        }

        /// <summary>
        /// 預警事件非同步執行作業結束
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks></remarks>
        private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {

            BackgroundWorker worker = (BackgroundWorker)sender;
            AutoRunLog result = (AutoRunLog)e.Result;

            if (result == null)
            {
                return;
            }

            result.EndTime = DateTime.Now;

            // 寫入AutoRunLog Queue
            QueueAutoRunLog.Add(result);

            // 寫回最後執行時間 
            WriteLastRunTime(result);

        }

        /// <summary>
        /// 產生SQL語法
        /// </summary>
        /// <param name="pTable"></param>
        /// <returns></returns>
        /// <remarks></remarks>
        private string GenerateSQLScript(DataTable pTable)
        {

            string strResult = "";
            string strOperator;
            DataTypeEnum intDataType;
            string strSymbol;
            string strCondValue;
            // Dim strScript As String
            string strCondition = "";
            string strCondition2 = "";
            // Dim intGroupBy, intOrderBy As Integer
            // Dim strBody As String
            // Dim i As Integer
            var htScriptCond = new Hashtable();

            // 逐筆產生Where條件
            foreach (DataRow row in pTable.Rows)
            {

                // 運算子或週期需指定其一
                if (string.IsNullOrEmpty(row["CondOperator"].ToString()) && row["CondPeriod"] is DBNull)
                {
                    throw new Exception("AlertEventID(" + row["A.AlertEventID"].ToString() + "), ItemNo(" + row["ItemNo"].ToString() + ")需指定運算子或週期!");
                }

                // 指定運算子, 條件值不可是空值
                if (!string.IsNullOrEmpty(row["CondOperator"].ToString()) && string.IsNullOrEmpty(row["CondValue"].ToString()))
                {
                    throw new Exception("AlertEventID(" + row["A.AlertEventID"].ToString() + "), ItemNo(" + row["ItemNo"].ToString() + ")條件值不可是空值!");
                }

                // 指定週期, 週期時間不可是空值
                if (string.IsNullOrEmpty(row["CondOperator"].ToString()) && row["CondPeriodTime"] is DBNull)
                {
                    throw new Exception("AlertEventID(" + row["A.AlertEventID"].ToString() + "), ItemNo(" + row["ItemNo"].ToString() + ")週期時間不可是空值!");
                }

                strCondition2 = "";
                intDataType = (DataTypeEnum)Conversions.ToInteger(row["CondDataType"]);
                strOperator = row["CondOperator"].ToString().ToUpper();
                strCondValue = row["CondValue"].ToString();

                if (!string.IsNullOrEmpty(row["InsertName"].ToString()) && !htScriptCond.ContainsKey(row["InsertName"].ToString().ToUpper()))
                {
                    htScriptCond.Add(row["InsertName"].ToString().ToUpper(), "");
                }

                if (string.IsNullOrEmpty(strOperator))
                {
                    // 依週期
                    if (dbi.RealDB.DatabaseType.ToLower() == "oracle")
                    {
                        strCondition2 += " And (Sysdate - " + row["CondField"].ToString() + ") ";
                        switch ((DataPeriodEnum)Conversions.ToInteger(row["CondPeriod"]))
                        {
                            case DataPeriodEnum.Minute:
                                {
                                    strCondition2 += " * 24 * 60 ";
                                    break;
                                }
                            case DataPeriodEnum.Hour:
                                {
                                    strCondition2 += " * 24 ";
                                    break;
                                }
                            case DataPeriodEnum.Day:
                                {
                                    strCondition2 += "";
                                    break;
                                }
                            case DataPeriodEnum.Month:
                                {
                                    strCondition2 += " / 30 ";
                                    break;
                                }

                            default:
                                {
                                    throw new Exception("無法識別條件週期CondPeriod(" + row["CondPeriod"].ToString() + ")!");
                                }
                        }
                        strCondition2 += " >= " + row["CondPeriodTime"].ToString();
                    }
                    else
                    {
                        string interval = "";
                        switch ((DataPeriodEnum)Conversions.ToInteger(row["CondPeriod"]))
                        {
                            case DataPeriodEnum.Minute:
                                {
                                    interval = DataPeriodEnum.Minute.ToString();
                                    break;
                                }
                            case DataPeriodEnum.Hour:
                                {
                                    interval = DataPeriodEnum.Hour.ToString();
                                    break;
                                }
                            case DataPeriodEnum.Day:
                                {
                                    interval = DataPeriodEnum.Day.ToString();
                                    break;
                                }
                            case DataPeriodEnum.Month:
                                {
                                    interval = DataPeriodEnum.Month.ToString();
                                    break;
                                }

                            default:
                                {
                                    throw new Exception("無法識別條件週期CondPeriod(" + row["CondPeriod"].ToString() + ")!");
                                }
                        }
                        strCondition2 += " And DateDiff(" + interval + ", " + row["CondField"].ToString() + ", GETDATE()) >= " + row["CondPeriodTime"].ToString();
                    }
                }

                else
                {
                    // 依運算子
                    // 資料型態的符號
                    if (intDataType == DataTypeEnum.StringType)
                    {
                        strSymbol = "'";
                    }
                    else
                    {
                        strSymbol = "";
                    }

                    // 依運算子設定條件值
                    switch (strOperator ?? "")
                    {
                        case "IN":
                            {
                                strCondValue = strCondValue.ToString().Replace(",", strSymbol + "," + strSymbol);
                                break;
                            }
                        case "LIKE":
                            {
                                strCondValue += "%";
                                break;
                            }
                    }

                    strCondValue = strSymbol + strCondValue + strSymbol;

                    strCondition2 += " And " + row["CondField"].ToString() + " " + strOperator + " ";

                    // 2022/7/5, Ning, 運算子為IN時, 需加上前後的括號
                    if (strOperator == "IN")
                    {
                        strCondition2 = strCondition2 + "(";
                    }

                    if (intDataType == DataTypeEnum.DateTimeType)
                    {

                        if (dbi.RealDB.DatabaseType.ToLower() == "oracle")
                        {
                            strCondition2 += " To_Date('" + Strings.Format(Conversions.ToDate(strCondValue), "yyyy/MM/dd H:mm:ss") + "','YYYY/MM/DD HH24:MI:SS')";
                        }
                        else
                        {
                            strCondition2 += " '" + Strings.Format(Conversions.ToDate(strCondValue), "yyyy/MM/dd H:mm:ss") + "' ";
                        }
                    }

                    else
                    {
                        strCondition2 += strCondValue;
                    }

                    // 2022/7/5, Ning, 運算子為IN時, 需加上前後的括號
                    if (strOperator == "IN")
                    {
                        strCondition2 = strCondition2 + ")";
                    }

                }

                if (string.IsNullOrEmpty(row["InsertName"].ToString()))
                {
                    strCondition += strCondition2;
                }
                else
                {
                    htScriptCond[row["InsertName"].ToString().ToUpper()] = htScriptCond[row["InsertName"].ToString().ToUpper()].ToString() + strCondition2;
                }

            }

            // 判斷Where
            // strScript = pTable.Rows(0)("AlertScript").ToString.ToLower

            // If (strCondition <> "") Then

            // If strScript.Contains("where") = True Then '有Where，需要判斷

            // If strScript.Contains(")") = False Then '沒有括弧，where在最外層
            // '不置換And成為Where
            // Else '有括弧，判斷where是否在最外層

            // Dim blnHaveWhere As Boolean = False '
            // Dim ScriptChar() As Char = strScript.ToCharArray
            // i = 0
            // For j As Integer = 0 To ScriptChar.Length - 1
            // If ScriptChar(j) = "(" Then '進入子查詢, 紀錄+1
            // i += 1
            // ElseIf ScriptChar(j) = ")" Then '離開子查詢, 紀錄-1
            // i -= 1
            // ElseIf ScriptChar(j) = "w" Then 'where的起始點
            // If j + 4 <= ScriptChar.Length - 1 Then
            // If strScript.Substring(j, 5) = "where" AndAlso i <= 0 Then
            // blnHaveWhere = True
            // Exit For
            // End If
            // End If
            // Else '其他字元不處理
            // End If
            // Next

            // If blnHaveWhere = False Then
            // strCondition = Replace(strCondition, "And", "Where", 1, 1, CompareMethod.Text)
            // End If

            // End If

            // Else '一個where都沒有，將開頭And換成Where
            // strCondition = Replace(strCondition, "And", "Where", 1, 1, CompareMethod.Text)
            // End If

            // End If

            // 將有指定位置的條件插入
            foreach (DictionaryEntry de in htScriptCond)
                pTable.Rows[0]["AlertScript"] = pTable.Rows[0]["AlertScript"].ToString().Replace("/*" + de.Key.ToString() + "*/", de.Value.ToString());

            // ' 處理Group By及Order By
            // If (strScript.LastIndexOf("group by") <> -1) Then ' 有group by，需要判斷

            // If (strScript.Contains(")")) = False Then ' 沒有括弧，group by在最外層

            // intGroupBy = strScript.LastIndexOf("group by")

            // Else ' 有括弧，判斷group by是否在最外層

            // Dim blnHaveGroupBy As Boolean = False
            // Dim ScriptChar() As Char = strScript.ToCharArray

            // i = 0

            // For j As Integer = 0 To ScriptChar.Length - 1
            // If ScriptChar(j) = "(" Then '進入子查詢, 紀錄+1
            // i += 1
            // ElseIf ScriptChar(j) = ")" Then '離開子查詢, 紀錄-1
            // i -= 1
            // ElseIf ScriptChar(j) = "g" Then 'where的起始點
            // If j + 7 <= ScriptChar.Length - 1 Then
            // If strScript.Substring(j, 8) = "group by" AndAlso i <= 0 Then
            // blnHaveGroupBy = True
            // Exit For
            // End If
            // End If
            // Else '其他字元不處理
            // End If
            // Next

            // If blnHaveGroupBy = False Then
            // intGroupBy = -1
            // Else
            // intGroupBy = strScript.LastIndexOf("group by")
            // End If

            // End If

            // Else '一個group by都沒有
            // intGroupBy = -1
            // End If

            // If (strScript.LastIndexOf("order by") <> -1) Then '有order by，需要判斷

            // If (strScript.Contains(")")) = False Then '沒有括弧，group by在最外層

            // intOrderBy = strScript.LastIndexOf("order by")

            // Else '有括弧，判斷group by是否在最外層

            // Dim blnHaveOrderBy As Boolean = False
            // Dim ScriptChar() As Char = strScript.ToCharArray

            // i = 0

            // For j As Integer = 0 To ScriptChar.Length - 1
            // If ScriptChar(j) = "(" Then '進入子查詢, 紀錄+1
            // i += 1
            // ElseIf ScriptChar(j) = ")" Then '離開子查詢, 紀錄-1
            // i -= 1
            // ElseIf ScriptChar(j) = "o" Then 'where的起始點
            // If j + 7 <= ScriptChar.Length - 1 Then
            // If (strScript.Substring(j, 8) = "order by" AndAlso i <= 0) Then
            // blnHaveOrderBy = True
            // Exit For
            // End If
            // End If
            // Else '其他字元不處理
            // End If
            // Next

            // If blnHaveOrderBy = False Then
            // intOrderBy = -1
            // Else
            // intOrderBy = strScript.LastIndexOf("order by")
            // End If

            // End If

            // Else '一個order by都沒有
            // intOrderBy = -1
            // End If

            // If intGroupBy = -1 Then
            // If intOrderBy = -1 Then
            // 'Script中無GroupBy及OrderBy
            // strResult = pTable.Rows(0)("AlertScript").ToString & " " & strCondition
            // Else
            // Dim strOrderBy As String = Mid(strScript, strScript.LastIndexOf("order by"))
            // 'Script中無GroupBy但有OrderBy
            // strBody = Mid(pTable.Rows(0)("AlertScript").ToString, 1, Len(strScript) - Len(strOrderBy))
            // strResult = strBody & " " & strCondition & " " & strOrderBy
            // End If
            // Else
            // Dim strGroupBy As String = Mid(pTable.Rows(0)("AlertScript").ToString, strScript.LastIndexOf("group by"))
            // 'Script中有GroupBy，並可能有OrderBy
            // strBody = Mid(pTable.Rows(0)("AlertScript").ToString, 1, Len(strScript) - Len(strGroupBy))
            // strResult = strBody & " " & strCondition & " " & strGroupBy
            // End If


            // 2014/01/27, Hank, 以Select * From包原本語法再插入無特殊位置的條件式
            strCondition = Strings.Replace(strCondition, "And", "Where", 1, 1);
            strResult = Conversions.ToString(Operators.ConcatenateObject(Operators.ConcatenateObject(Operators.ConcatenateObject("Select * From ( ", pTable.Rows[0]["AlertScript"]), " ) T "), strCondition));

            return strResult;

        }

        #endregion

        #region AlertEventLog執行緒

        /// <summary>
        /// 預警事件Log非同步作業開始
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks></remarks>
        private void EventLog_DoWork(object sender, DoWorkEventArgs e)
        {

            string strSQL;
            AlertEvent ae = null;
            AutoRunLog result = null;
            AutoRunLog result2 = null;
            IDbConnection cn = null;
            IDbCommand cmd = null;
            IDbTransaction tx = null;
            DateTime dtAlertTime;

            try
            {

                result2 = new AutoRunLog();
                result2.LogID = "N/A";
                result2.AlertEventID = "EventLog_DoWork";
                result2.AlertEventName = result2.AlertEventID;
                result2.StartTime = DateTime.Now;

                // 開啟資料庫連線
                if (QueueEventLog.Count > 0)
                {
                    cn = CreateConnection(dbi.RealDB.ConnectionString);
                    //cn.Open();
                    cmd = CreateCommand("", cn);
                }

                while (QueueEventLog.Count > 0)
                {

                    try
                    {

                        // 取出預警事件Log的第一筆, 並設定起始時間
                        ae = QueueEventLog[0];

                        result = new AutoRunLog(ae);

                        tx = cn.BeginTransaction();
                        cmd.Transaction = tx;

                        dtAlertTime = DateTime.Now;

                        string FIELD01 = "";
                        string FIELD02 = "";
                        string FIELD03 = "";
                        string FIELD04 = "";
                        string FIELD05 = "";
                        string strAlertTime = "";

                        foreach (DataRow row in ae.ResultTable.Rows)
                        {
                            cmd.Parameters.Clear();
                            // 20221107,13871,0121824: 台基预警：预警频率不是设定的预警频率，固定的5分钟一预警,註解此
                            // cmd.Parameters.AddWithValue("LogID", ae.LogID)
                            // cmd.Parameters.AddWithValue("AlertEventID", ae.AlertEventID & "")
                            // cmd.Parameters.AddWithValue("AlertType", ae.AlertType)
                            // cmd.Parameters.AddWithValue("AlertEventName", ae.AlertEventName & "")
                            // cmd.Parameters.AddWithValue("AlertTime", dtAlertTime)
                            // cmd.Parameters.AddWithValue("AlertDesc", ae.Description & "")
                            // cmd.Parameters.AddWithValue("AlertCreator", ae.Creator & "")
                            // cmd.Parameters.AddWithValue("FIELD01", "")
                            // cmd.Parameters.AddWithValue("FIELD02", "")
                            // cmd.Parameters.AddWithValue("FIELD03", "")
                            // cmd.Parameters.AddWithValue("FIELD04", "")
                            // cmd.Parameters.AddWithValue("FIELD05", "")

                            switch (ae.AlertType)
                            {
                                case AlertTypeEnum.ERF:
                                    {
                                        // cmd.Parameters("FIELD01").Value = row("ERFNo").ToString
                                        FIELD01 = row["ERFNo"].ToString();
                                        break;
                                    }
                                case AlertTypeEnum.LOT:
                                    {
                                        // cmd.Parameters("FIELD01").Value = row("LotNo").ToString
                                        FIELD01 = row["LotNo"].ToString();
                                        break;
                                    }
                                case AlertTypeEnum.EQP:
                                    {
                                        // cmd.Parameters("FIELD01").Value = row("EquipmentNo").ToString
                                        FIELD01 = row["EquipmentNo"].ToString();
                                        break;
                                    }
                                case AlertTypeEnum.ACC:
                                    {
                                        // cmd.Parameters("FIELD01").Value = row("AccessoryNo").ToString
                                        FIELD01 = row["AccessoryNo"].ToString();
                                        if (ae.ResultTable.Columns.Contains("AccessoryVersion"))
                                        {
                                            // cmd.Parameters("FIELD02").Value = row("AccessoryVersion").ToString
                                            FIELD02 = row["AccessoryVersion"].ToString();
                                        }
                                        else
                                        {
                                            // cmd.Parameters("FIELD02").Value = "*"
                                            FIELD02 = "*";
                                        }

                                        break;
                                    }
                                case AlertTypeEnum.MO:
                                    {
                                        // cmd.Parameters("FIELD01").Value = row("MONo").ToString
                                        FIELD01 = row["MONo"].ToString();
                                        break;
                                    }
                                case AlertTypeEnum.PRD:
                                    {
                                        // cmd.Parameters("FIELD01").Value = row("ProductNo").ToString
                                        FIELD01 = row["ProductNo"].ToString();
                                        if (ae.ResultTable.Columns.Contains("ProductVersion"))
                                        {
                                            // cmd.Parameters("FIELD02").Value = row("ProductVersion").ToString
                                            FIELD02 = row["ProductVersion"].ToString();
                                        }
                                        else
                                        {
                                            // cmd.Parameters("FIELD02").Value = "*"
                                            FIELD02 = "*";
                                        }

                                        break;
                                    }
                                case AlertTypeEnum.OP:
                                    {
                                        // cmd.Parameters("FIELD01").Value = row("OPNo").ToString
                                        FIELD01 = row["OPNo"].ToString();
                                        break;
                                    }
                            }

                            // 20221107,13871,0121824: 台基预警：预警频率不是设定的预警频率，固定的5分钟一预警
                            if (dbi.RealDB.DatabaseType.ToLower() != "oracle")
                            {
                                strAlertTime = "'" + Strings.Format(Conversions.ToDate(dtAlertTime), "yyyy/MM/dd H:mm:ss") + "'";
                            }
                            else
                            {
                                strAlertTime = "To_Date('" + Strings.Format(Conversions.ToDate(dtAlertTime), "yyyy/MM/dd H:mm:ss") + "','YYYY/MM/DD HH24:MI:SS')";
                            }


                            // 20221107,13871,0121824: 台基预警：预警频率不是设定的预警频率，固定的5分钟一预警
                            strSQL = " Insert into tblIEWAlertEventLog " + " (LogID, AlertEventID, AlertType, AlertEventName, AlertTime, AlertDesc, AlertCreator, " + "  FIELD01, FIELD02, FIELD03, FIELD04, FIELD05) Values " + string.Format(" ('{0}', '{1}', '{2}', '{3}', {4}, '{5}', '{6}', '{7}', '{8}', '{9}', '{10}', '{11}') ", ae.LogID, ae.AlertEventID, (int)ae.AlertType, ae.AlertEventName, strAlertTime, ae.Description, ae.Creator, FIELD01, FIELD02, FIELD03, FIELD04, FIELD05);

                            cmd.CommandText = strSQL;

                            cmd.ExecuteNonQuery();
                            //string cmdTmp = cmd.ToString();

                        }

                        tx.Commit();

                        result.ExecuteResult = ExecuteResultEnum.SUCCESS;
                        result.ErrorMessage = "寫入記錄成功";
                    }

                    catch (Exception ex)
                    {
                        result.ExecuteResult = ExecuteResultEnum.FAIL;
                        result.ErrorMessage = "寫入記錄失敗, " + ex.Message;
                        if (tx != null)
                        {
                            tx.Rollback();
                        }
                    }
                    finally
                    {
                        if (result.ExecuteResult == ExecuteResultEnum.SUCCESS || result.ExecuteResult == ExecuteResultEnum.FAIL)
                        {
                            result.EndTime = DateTime.Now;
                            QueueEventLog.Remove(ae);
                            QueueAutoRunLog.Add(result);
                        }
                        if (tx != null)
                        {
                            tx = null;
                        }
                    }

                }

                result2 = null;
            }

            catch (Exception ex)
            {
                result2.EndTime = DateTime.Now;
                result2.ExecuteResult = ExecuteResultEnum.FAIL;
                result2.ErrorMessage = ex.Message;
            }
            finally
            {
                if (cn.State == ConnectionState.Open)
                {
                    cn.Close();
                    cn = null;
                }
                if (cmd != null)
                {
                    cmd = null;
                }
                e.Result = result2;
            }

        }

        /// <summary>
        /// 預警事件Log非同步作業結束
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks></remarks>
        private void EventLog_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {

            AutoRunLog result = (AutoRunLog)e.Result;

            if (result != null && result.ExecuteResult == ExecuteResultEnum.FAIL)
            {
                QueueAutoRunLog.Add(result);
            }

        }

        #endregion

        #region AutoRunLog執行緒

        /// <summary>
        /// AutoRunLog非同步作業開始
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks></remarks>
        private void AutoRunLog_DoWork(object sender, DoWorkEventArgs e)
        {

            AutoRunLog log = null;
            AutoRunLog result2 = null;
            IDbConnection cn = null;
            IDbCommand cmd = null;
            DataTable dtLog = null;
            string strSQL = "";
            string strReturnMsg = "";

            try
            {

                result2 = new AutoRunLog();
                result2.LogID = "N/A";
                result2.AlertEventID = "AutoRunLog_DoWork";
                result2.AlertEventName = result2.AlertEventID;
                result2.StartTime = DateTime.Now;

                // 開啟資料庫連線
                if (QueueAutoRunLog.Count > 0)
                {
                    cn = CreateConnection(dbi.RealDB.ConnectionString);
                    //cn.Open();
                    cmd = CreateCommand("", cn);
                }

                while (QueueAutoRunLog.Count > 0)
                {

                    try
                    {

                        log = QueueAutoRunLog[0];

                        // LogID存在 --> Append Message, LogID不存在 --> Insert Message
                        strSQL = " Select * " + " From   tblIEWAutoRunLog " + " Where  LogID = '" + log.LogID + "'";

                        cmd.CommandText = strSQL;
                        cmd.Parameters.Clear();
                        // 20221107,13871,0121824: 台基预警：预警频率不是设定的预警频率，固定的5分钟一预警,註解此
                        // cmd.Parameters.AddWithValue("LogID", log.LogID & "")

                        //using (var da = new System.Data.OleDb.OleDbDataAdapter(cmd))
                        //{
                        dtLog = new DataTable();
                        //da.Fill(dtLog);
                        ExecuteSQLQuery_Adapter(cmd, dtLog);
                        //}

                        if (dtLog.Rows.Count == 0 || log.LogID == "N/A")
                        {
                            // Insert
                            //strSQL = " Insert into tblIEWAutoRunLog " + " (AlertEventID, AlertEventName, Status, StartTime, EndTime, RunTime, ReturnMsg, LogID) Values " + " (?, ?, ?, ?, ?, ?, ?, ?) ";
                            strSQL = " Insert into tblIEWAutoRunLog " + " (AlertEventID, AlertEventName, Status, StartTime, EndTime, RunTime, ReturnMsg, LogID) Values " + " (:p1, :p2, :p3, :p4, :p5, :p6, :p7, :p8) ";

                            strReturnMsg = Strings.Format(log.EndTime, "yyyy/MM/dd HH:mm") + ", " + log.ErrorMessage.ToString();

                            cmd.CommandText = strSQL;
                            cmd.Parameters.Clear();
                            //cmd.Parameters.AddWithValue("AlertEventID", log.AlertEventID.ToString());
                            //cmd.Parameters.AddWithValue("AlertEventName", log.AlertEventName.ToString());
                            //cmd.Parameters.AddWithValue("Status", log.ExecuteResult.ToString());
                            //cmd.Parameters.AddWithValue("StartTime", log.StartTime);
                            //cmd.Parameters.AddWithValue("EndTime", log.EndTime);
                            //cmd.Parameters.AddWithValue("RunTime", DateAndTime.DateDiff(DateInterval.Second, log.StartTime, log.EndTime));
                            //cmd.Parameters.AddWithValue("ReturnMsg", strReturnMsg);
                            //cmd.Parameters.AddWithValue("LogID", log.LogID.ToString());
                            AddCommandParameter(cmd, "p1", log.AlertEventID);
                            AddCommandParameter(cmd, "p2", log.AlertEventName);
                            AddCommandParameter(cmd, "p3", log.ExecuteResult);
                            AddCommandParameter(cmd, "p4", log.StartTime);
                            AddCommandParameter(cmd, "p5", log.EndTime);
                            AddCommandParameter(cmd, "p6", DateAndTime.DateDiff(DateInterval.Second, log.StartTime, log.EndTime));
                            AddCommandParameter(cmd, "p7", strReturnMsg);
                            AddCommandParameter(cmd, "p8", log.LogID);
                            cmd.ExecuteNonQuery();
                        }
                        else
                        {
                            // Append
                            //strSQL = " Update tblIEWAutoRunLog " + " Set    Status = ?, " + "        EndTime = ?, " + "        RunTime = ?, " + "        ReturnMsg = ? " + " Where  LogID = ? ";
                            strSQL = " Update tblIEWAutoRunLog " + " Set    Status = :p1, " + "        EndTime = :p2, " + "        RunTime = :p3, " + "        ReturnMsg = :p4 " + " Where  LogID = :p5 ";
                            cmd.CommandText = strSQL;
                            cmd.Parameters.Clear();

                            if (log.ExecuteResult == ExecuteResultEnum.FAIL)
                            {
                                //cmd.Parameters.AddWithValue("Status", log.ExecuteResult.ToString());
                                AddCommandParameter(cmd, "p1", log.ExecuteResult);
                            }
                            else
                            {
                                //cmd.Parameters.AddWithValue("Status", dtLog.Rows[0]["Status"]);
                                AddCommandParameter(cmd, "p1", dtLog.Rows[0]["Status"]);
                            }

                            strReturnMsg = Strings.Format(log.EndTime, "yyyy/MM/dd HH:mm") + ", " + log.ErrorMessage.ToString();

                            if (Operators.ConditionalCompareObjectNotEqual(Operators.ConcatenateObject(dtLog.Rows[0]["ReturnMsg"], ""), "", false))
                            {
                                strReturnMsg = Conversions.ToString(Operators.ConcatenateObject(Operators.ConcatenateObject(dtLog.Rows[0]["ReturnMsg"], Constants.vbCrLf), strReturnMsg));
                            }

                            //cmd.Parameters.AddWithValue("EndTime", log.EndTime);
                            //cmd.Parameters.AddWithValue("RunTime", DateAndTime.DateDiff(DateInterval.Second, Conversions.ToDate(dtLog.Rows[0]["StartTime"]), log.EndTime));
                            //cmd.Parameters.AddWithValue("ReturnMsg", strReturnMsg);
                            //cmd.Parameters.AddWithValue("LogID", log.LogID.ToString());
                            AddCommandParameter(cmd, "p2", log.EndTime);
                            AddCommandParameter(cmd, "p3", DateAndTime.DateDiff(DateInterval.Second, Conversions.ToDate(dtLog.Rows[0]["StartTime"]), log.EndTime));
                            AddCommandParameter(cmd, "p4", strReturnMsg);
                            AddCommandParameter(cmd, "p5", log.LogID);
                            cmd.ExecuteNonQuery();
                        }
                    }

                    catch (Exception ex)
                    {
                        log.ErrorMessage = log.ErrorMessage + Constants.vbCrLf + ex.Message;
                        WriteToAnEventLog(log, EventLogEntryType.Error, 9000);
                    }
                    finally
                    {
                        QueueAutoRunLog.Remove(log);
                    }

                }

                result2 = null;
            }

            catch (Exception ex)
            {
                result2.EndTime = DateTime.Now;
                result2.ExecuteResult = ExecuteResultEnum.FAIL;
                result2.ErrorMessage = ex.Message;
            }
            finally
            {
                if (cn.State == ConnectionState.Open)
                {
                    cn.Close();
                    cn = null;
                }
                if (cmd != null)
                {
                    cmd = null;
                }
                if (dtLog != null)
                {
                    dtLog = null;
                }
                e.Result = result2;
            }

        }

        /// <summary>
        /// AutoRunLog非同步作業結束
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks></remarks>
        private void AutoRunLog_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {

            AutoRunLog result = (AutoRunLog)e.Result;

            if (result != null && result.ExecuteResult == ExecuteResultEnum.FAIL)
            {
                WriteToAnEventLog(result, EventLogEntryType.Error, 9000);
            }

        }

        #endregion

        #region 派送執行緒

        /// <summary>
        /// 派送非同步作業開始
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks></remarks>
        /// Modify Notes：2016-06-02, Joe, 調整程式加MCloud推播功能
        private void Delivery_DoWork(object sender, DoWorkEventArgs e)
        {


            AlertEvent ae = null;
            AutoRunLog result = null;
            AutoRunLog result2 = null;

            try
            {

                result2 = new AutoRunLog();
                result2.LogID = "N/A";
                result2.AlertEventID = "Delivery_DoWork";
                result2.AlertEventName = result2.AlertEventID;
                result2.StartTime = DateTime.Now;

                while (QueueDelivery.Count > 0)
                {
                    try
                    {

                        ae = QueueDelivery[0];

                        funWriteTxtFile(string.Format("Process Delivery.{2}{0}:{1}", ae.AlertEventID, ae.AlertEventName, Constants.vbCrLf));

                        result = new AutoRunLog(ae);

                        // 2020/04/17 雋辰,修改Notice處理方式
                        string[] notices = ae.NoticeType.Split(',');
                        var appNotices = new List<string>();
                        foreach (string notice in notices)
                        {
                            if (notice == "0")
                            {
                                funSendMail(ref ae);
                            }
                            else if (notice == "3")
                            {
                                funSendMCloud(ref ae);
                            }
                            else if (notice == "4")
                            {
                                appNotices.Add("Line");
                            }
                            else if (notice == "5")
                            {
                                appNotices.Add("WeChat");
                            }
                        }
                        if (appNotices.Count > 0)
                            funSendApp(ref ae, appNotices);

                        result.ExecuteResult = ExecuteResultEnum.SUCCESS;
                        result.ErrorMessage = "派送事件成功";
                    }

                    catch (Exception ex)
                    {
                        result.ExecuteResult = ExecuteResultEnum.FAIL;
                        result.ErrorMessage = "派送事件失敗, " + ex.Message;
                    }
                    finally
                    {
                        if (result.ExecuteResult == ExecuteResultEnum.SUCCESS || result.ExecuteResult == ExecuteResultEnum.FAIL)
                        {
                            result.EndTime = DateTime.Now;
                            QueueDelivery.Remove(ae);
                            QueueAutoRunLog.Add(result);
                        }
                    }
                }
            }

            catch (Exception ex)
            {
                result2.EndTime = DateTime.Now;
                result2.ExecuteResult = ExecuteResultEnum.FAIL;
                result2.ErrorMessage = ex.Message;
            }
            finally
            {
                e.Result = result2;
            }

        }

        // 2016-05-31, Joe, 派送方式加MCloud推播
        private void funSendMCloud(ref AlertEvent ae)
        {

            string pKey = "";
            string pPushMsg = "";
            string OutXml = "";
            string strFrom = "SFT";
            string strTargetUser = "DS";
            string strAlert = ae.AlertEventName;
            string strKeyField = "";

            try
            {
                // strTargetUser = GetAlertRecipients(ae)

                // 組XML，內容要以Base64加密
                pPushMsg = "<PushMsg>";
                pPushMsg = pPushMsg + "<From>" + strFrom + "</From>";
                pPushMsg = pPushMsg + "<TargetUser>" + strTargetUser + "</TargetUser>";
                pPushMsg = pPushMsg + "<Message>";
                pPushMsg = pPushMsg + "<alert>" + strAlert + "</alert>";
                pPushMsg = pPushMsg + "<keyfield>" + strKeyField + "</keyfield>";
                pPushMsg = pPushMsg + "</Message>";
                pPushMsg = pPushMsg + "</PushMsg>";

                // Call MCloud Push Message Service
                using (var wsMCloud = new wsMCloud.PushService())
                {
                    OutXml = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(wsMCloud.PushDevicePMG(pKey, Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(pPushMsg)))));
                }

                // 利用XmlDoc物件處理ReturnValue 
                var XmlDoc = new XmlDocument();
                XmlDoc.LoadXml(OutXml);
                if (XmlDoc.DocumentElement["srvcode"].InnerXml != "000")
                {
                    throw new Exception(XmlDoc.DocumentElement["srvmsg"].InnerXml);
                }
            }

            catch (Exception ex)
            {
                throw;
            }

        }

        private void funSendMail(ref AlertEvent ae)
        {

            // Dim mailserver As Net.Mail.SmtpClient = Nothing
            System.Net.Mail.MailMessage mail = null;

            Hashtable htSmtp = null;
            string bReturn = "";
            int SecureSocketOptions = 1;

            try
            {

                // mailserver = New Net.Mail.SmtpClient
                mail = new System.Net.Mail.MailMessage();
                htSmtp = new Hashtable();

                // 取出SMTP的設定
                //using (var da = new System.Data.OleDb.OleDbDataAdapter("Select * From tblSYSParameter Where ParameterType = 'SMTPInfo'", dbi.RealDB.ConnectionString))
                //{

                using (var dt = new DataTable())
                {
                    string strSQL = "Select * From tblSYSParameter Where ParameterType = 'SMTPInfo'";
                    //da.Fill(dt);
                    ExecuteSQLQuery_Adapter(strSQL, dt, dbi.RealDB.ConnectionString, dbi.RealDB.DatabaseType);
                    foreach (DataRow row in dt.Rows)
                        htSmtp.Add(row["ParameterNo"], row["ParameterValue"]);

                }

                //}



                htSmtp["SMTPPassword"] = DeCrypt(htSmtp["SMTPPassword"].ToString());

                // 0116180: 【晶睿】mes邮件服务器测试不成功,使用iMESMail套件
                // mailserver.Credentials = New Net.NetworkCredential(htSmtp("SMTPUser").ToString, htSmtp("SMTPPassword").ToString)
                // mailserver.Port = Convert.ToInt32(htSmtp("SMTPPort"))
                // mailserver.Host = htSmtp("SMTPServer").ToString
                // '2019/05/08 Shih Kai, 根據參數:SMTPSSL(是否加密連線SSL),決定是否進行SSL加密
                // If htSmtp("SMTPSSL").ToString = "1" Then
                // mailserver.EnableSsl = True
                // Else
                // mailserver.EnableSsl = False
                // End If

                // ' 寄件者
                // mail.From = New System.Net.Mail.MailAddress(htSmtp("SYSEMail").ToString, htSmtp("SMTPUser").ToString, System.Text.Encoding.UTF8)

                // ' 主旨
                // mail.Subject = ae.Subject

                // ' 收件者 
                var argmailto = mail.To;
                AddMailAddress(ae, ref argmailto);

                // ' 內文
                // ' 內文-表頭
                var mailcontent = new EmailContent();
                mailcontent.AlertEventName = ae.AlertEventName;
                mailcontent.AlertType = ae.AlertType;
                mailcontent.AlertTime = DateTime.Now;
                mailcontent.AlertDescription = ae.Description;

                foreach (DataColumn col in ae.ResultTable.Columns)
                    mailcontent.thead.Add(col.ColumnName);

                // 內文-表身
                foreach (DataRow row in ae.ResultTable.Rows)
                {

                    var tr = new EmailContent.tr();

                    foreach (DataColumn col in ae.ResultTable.Columns)
                        tr.Add(row[col.ColumnName].ToString());

                    mailcontent.tbody.Add(tr);

                }

                // mail.IsBodyHtml = True
                // mail.Body = mailcontent.Generate

                // ' 發信
                // mailserver.Send(mail)

                var EmailAddress = new Collection();

                foreach (System.Net.Mail.MailAddress item in mail.To)
                    EmailAddress.Add(item.Address);
                try
                {
                    SecureSocketOptions = Conversions.ToInteger(htSmtp["SMTPSSL"].ToString());
                }
                catch (Exception ex)
                {

                }

                // 0116180: 【晶睿】mes邮件服务器测试不成功,使用iMESMail套件
                iMESCore.Mail.iMESMail.Send(htSmtp["SMTPServer"].ToString(), Convert.ToInt32(htSmtp["SMTPPort"]), htSmtp["SYSEMail"].ToString(), "", htSmtp["SMTPUser"].ToString(), htSmtp["SMTPPassword"].ToString(), SecureSocketOptions, ae.Subject, mailcontent.Generate(), EmailAddress);
            }

            catch (Exception ex)
            {
                throw;
            }
            finally
            {
                // mailserver = Nothing
                mail = null;
                htSmtp = null;
            }

        }

        /// <summary>
        /// 2020/04/17 雋辰,發送app訊息
        /// </summary>
        /// <param name="ae"></param>
        /// <param name="pipe"></param>
        private void funSendApp(ref AlertEvent ae, List<string> pipe)
        {

            var objMDS = new kcWIP.clsMDS();

            try
            {
                string message = "";
                string lBreak = @"\n";

                // 主旨
                message = string.Format($"IEW 警示事件:{ae.Subject}{lBreak}{lBreak}");
                // 內容
                message += string.Format($"警示事件名稱:{ae.AlertEventName}{lBreak}{lBreak}警示類別:{ae.AlertType}{lBreak}{lBreak}警示時間:{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")}{lBreak}{lBreak}說明:{ae.Description}{lBreak}{lBreak}");
                // 表格
                var tab = new Dictionary<string, string>();
                foreach (DataColumn col in ae.ResultTable.Columns)
                {
                    tab[col.ColumnName] = "";
                    foreach (DataRow row in ae.ResultTable.Rows)
                    {
                        if (col.ColumnName.ToUpper() == "STARTTIME" && Information.IsDate(row[col.ColumnName].ToString()))
                        {
                            tab[col.ColumnName] += Conversions.ToDate(row[col.ColumnName].ToString()).ToString("yyyy/MM/dd HH:mm:ss") + ",";
                        }
                        else
                        {
                            tab[col.ColumnName] += row[col.ColumnName].ToString() + ",";
                        }
                    }
                    tab[col.ColumnName] = tab[col.ColumnName].TrimEnd(',');
                }
                foreach (string key in tab.Keys)
                {
                    if (string.IsNullOrEmpty(tab[key]))
                        continue;
                    message += string.Format($"{key}:{tab[key]}{lBreak}");
                }

                objMDS.invoke_Message_Platform(AppSettings["MSG_SERVICE"].ToString(), objMDS.Create_MsgPlatform_Request("ServicesSTD.Module_MSG.send_notification_process", ae.Creator, message, Get_Users(ae), pipe));
            }
            catch (Exception ex)
            {
                throw;
            }
        }

        /// <summary>
        /// 派送非同步作業結束
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks></remarks>
        private void Delivery_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {

            AutoRunLog result = (AutoRunLog)e.Result;

            if (result != null && result.ExecuteResult == ExecuteResultEnum.FAIL)
            {
                QueueAutoRunLog.Add(result);
            }

        }

        #endregion

        #region 事件檢視器

        /// <summary>
        /// 寫到 Windows Event Log
        /// </summary>
        /// <remarks></remarks>
        private void WriteToAnEventLog(EventBase e, EventLogEntryType pEventType = EventLogEntryType.Information, int pEventID = 0)
        {

            string strSource = "MES IEW Service";
            string strLog = "Application";
            string strMessage = "";

            try
            {
                if (!EventLog.SourceExists(strSource))
                {
                    EventLog.CreateEventSource(strSource, strLog);
                }
                strMessage = "AlertEvent: " + e.AlertEventID + Constants.vbCrLf + "StartTime: " + Strings.Format(e.StartTime, "yyyy/MM/dd HH:mm:ss") + Constants.vbCrLf + "EndTime: " + Strings.Format(e.EndTime, "yyyy/MM/dd HH:mm:ss") + Constants.vbCrLf + "ErrorMessage: " + e.ErrorMessage;
                funWriteTxtFile(strMessage);
                EventLog.WriteEntry(strSource, strMessage, pEventType, pEventID);
            }
            catch (Exception ex)
            {

            }

        }

        #endregion

        #region 預警事件的最後執行時間

        private void WriteLastRunTime(AutoRunLog result)
        {

            string strSQL = "";

            try
            {

                // 20221107,13871,0121824: 台基预警：预警频率不是设定的预警频率，固定的5分钟一预警
                string strEndTime = "";
                if (dbi.RealDB.DatabaseType.ToLower() != "oracle")
                {
                    strEndTime = "'" + Strings.Format(Conversions.ToDate(result.EndTime), "yyyy/MM/dd H:mm:ss") + "'";
                }
                else
                {
                    strEndTime = "To_Date('" + Strings.Format(Conversions.ToDate(result.EndTime), "yyyy/MM/dd H:mm:ss") + "','YYYY/MM/DD HH24:MI:SS')";
                }

                strSQL = " Update tblIEWAlertEventBasis " + " Set    LastRunTime = " + strEndTime + " Where  AlertEventID = '" + result.AlertEventID + "'";


                //using (var cn = CreateConnection(dbi.RealDB.ConnectionString))
                //{

                //    using (var cmd = CreateCommand(strSQL, cn))
                //    {

                //        //cn.Open();

                //        // 20221107,13871,0121824: 台基预警：预警频率不是设定的预警频率，固定的5分钟一预警,註解此
                //        // cmd.Parameters.AddWithValue("LastRunTime", result.EndTime)
                //        // cmd.Parameters.AddWithValue("AlertEventID", result.AlertEventID)
                //        cmd.ExecuteNonQuery();

                //        cn.Close();

                //    }

                //}
                ExecuteSQLNoneQuery(dbi.RealDB.DatabaseType, dbi.RealDB.ConnectionString, ref strSQL);
            }

            catch (Exception ex)
            {
            }
            finally
            {
            }

        }

        #endregion

        #region 加入收件者

        /// <summary>
        /// 依警示事件編號取回收件人, 若無收件人, 則以預警事件的建立人為收件人 
        /// </summary>
        /// <remarks></remarks>
        private void AddMailAddress(AlertEvent ae, ref System.Net.Mail.MailAddressCollection mailto)
        {

            string strSQL;
            IDbConnection cn = null;
            //IDbCommand cmd = null;
            DataTable dtTemp = null;

            try
            {
                // 2014/01/24, Hank, 預警事件的建立人也要發送
                strSQL = " Select EmailAddress From tblUSRUserBasis " + " Where  UserNo = '" + ae.Creator + "' " + " And    EmailAddress is not null " + " Union " + " Select B.EmailAddress " + " From   tblIEWAlertRecipients A, tblUSRUserBasis B " + " Where  A.Recipient = B.UserNo " + " And    A.AlertEventID = '" + ae.AlertEventID + "' " + " And    A.RecipientType = 99 " + " And    B.IssueState = 2 " + " And    B.EmailAddress is not null " + " Union " + " Select C.EmailAddress " + " From   tblIEWAlertRecipients A, tblUSRUserGroup B, tblUSRUserBasis C " + " Where  A.Recipient = B.GroupNo " + " And    A.RecipientType = B.GroupType " + " And    B.UserNo = C.UserNo " + " And    A.AlertEventID = '" + ae.AlertEventID + "' " + " And    A.RecipientType <> 99 " + " And    C.EmailAddress is not null ";

                cn = CreateConnection(dbi.RealDB.ConnectionString);
                //cn.Open();
                //cmd = CreateCommand(strSQL, cn);

                //using (var da = new System.Data.OleDb.OleDbDataAdapter(cmd))
                //{
                //    dtTemp = new DataTable();
                //    da.Fill(dtTemp);
                //}
                ExecuteSQLQuery_Adapter(strSQL, dtTemp, cn);

                foreach (DataRow row in dtTemp.Rows)
                    mailto.Add(row["EmailAddress"].ToString());

                if (mailto.Count == 0)
                {
                    throw new Exception("無任何收件人資訊!");
                }
            }

            // If (dtTemp.Rows.Count = 0) Then

            // ' 若無收件人, 則以預警事件的建立人為收件人 
            // strSQL = " Select EmailAddress From tblUSRUserBasis Where UserNo = '" & di.Creator & "' "
            // cmd.CommandText = strSQL
            // If (cmd.ExecuteScalar.ToString & "" = "") Then
            // Throw New Exception("無任何收件人資訊!")
            // Else
            // mailto.Add(cmd.ExecuteScalar.ToString)
            // End If

            // Else

            // For Each row As DataRow In dtTemp.Rows
            // mailto.Add(row("EmailAddress").ToString)
            // Next

            // End If

            catch (Exception ex)
            {
                throw;
            }
            finally
            {
                if (cn != null && cn.State == ConnectionState.Open)
                {
                    cn.Close();
                    cn.Dispose();
                    cn = null;
                }
                //if (cmd != null)
                //{
                //    cmd.Dispose();
                //    cmd = null;
                //}
                if (dtTemp != null)
                {
                    dtTemp.Dispose();
                    dtTemp = null;
                }
            }

        }

        /// <summary>
        /// 2020/04/17 雋辰,取得需要發送的UserNo
        /// </summary>
        /// <param name="ae"></param>
        /// <returns></returns>
        private List<string> Get_Users(AlertEvent ae)
        {
            string strSQL;
            IDbConnection cn = null;
            //IDbCommand cmd = null;
            DataTable dtTemp = null;
            var result = new List<string>();

            try
            {
                // 2014/01/24, Hank, 預警事件的建立人也要發送
                strSQL = " Select UserNo From tblUSRUserBasis " + " Where  UserNo = '" + ae.Creator + "' " + " And    UserNo is not null " + " Union " + " Select B.UserNo" + " From   tblIEWAlertRecipients A, tblUSRUserBasis B " + " Where  A.Recipient = B.UserNo " + " And    A.AlertEventID = '" + ae.AlertEventID + "' " + " And    A.RecipientType = 99 " + " And    B.IssueState = 2 " + " And    B.UserNo is not null " + " Union " + " Select C.UserNo" + " From   tblIEWAlertRecipients A, tblUSRUserGroup B, tblUSRUserBasis C " + " Where  A.Recipient = B.GroupNo " + " And    A.RecipientType = B.GroupType " + " And    B.UserNo = C.UserNo " + " And    A.AlertEventID = '" + ae.AlertEventID + "' " + " And    A.RecipientType <> 99 " + " And    C.UserNo is not null ";

                cn = CreateConnection(dbi.RealDB.ConnectionString);
                //cn.Open();
                //cmd = CreateCommand(strSQL, cn);

                //using (var da = new System.Data.OleDb.OleDbDataAdapter(cmd))
                //{
                //    dtTemp = new DataTable();
                //    da.Fill(dtTemp);
                //}
                ExecuteSQLQuery_Adapter(strSQL, dtTemp, cn);

                foreach (DataRow row in dtTemp.Rows)
                {
                    if (!result.Contains(row["UserNo"].ToString()))
                        result.Add(row["UserNo"].ToString());
                }

                if (result.Count == 0)
                {
                    throw new Exception(string.Format($"無任何接收者資訊"));
                }

                return result;
            }

            catch (Exception ex)
            {
                throw;
            }
            finally
            {
                if (cn != null && cn.State == ConnectionState.Open)
                {
                    cn.Close();
                    cn.Dispose();
                    cn = null;
                }
                //if (cmd != null)
                //{
                //    cmd.Dispose();
                //    cmd = null;
                //}
                if (dtTemp != null)
                {
                    dtTemp.Dispose();
                    dtTemp = null;
                }
            }
        }

        // 2016-06-02, Joe, Add
        private string GetAlertRecipients(AlertEvent ae)
        {
            string GetAlertRecipientsRet = default(string);

            string strSQL;
            IDbConnection cn = null;
            //IDbCommand cmd = null;
            DataTable dtTemp = null;
            string strReturn = "";

            try
            {
                strSQL = " Select A.* " + " From   tblIEWAlertRecipients A " + " Where  A.AlertEventID = '" + ae.AlertEventID + "' " + " And    A.RecipientType <> 99 ";

                cn = CreateConnection(dbi.RealDB.ConnectionString);
                //cn.Open();
                //cmd = CreateCommand(strSQL, cn);

                //using (var da = new System.Data.OleDb.OleDbDataAdapter(cmd))
                //{
                //    dtTemp = new DataTable();
                //    da.Fill(dtTemp);
                //}
                ExecuteSQLQuery_Adapter(strSQL, dtTemp, cn);

                foreach (DataRow row in dtTemp.Rows)
                {
                    if (string.IsNullOrEmpty(strReturn))
                    {
                        strReturn = row["RECIPIENT"].ToString();
                    }
                    else
                    {
                        strReturn = strReturn + "├" + row["RECIPIENT"].ToString();
                    }
                }

                if (string.IsNullOrEmpty(strReturn))
                {
                    throw new Exception("無任何警示群組資訊!");
                }
            }


            catch (Exception ex)
            {
                throw;
            }
            finally
            {
                if (cn != null && cn.State == ConnectionState.Open)
                {
                    cn.Close();
                    cn.Dispose();
                    cn = null;
                }
                //if (cmd != null)
                //{
                //    cmd.Dispose();
                //    cmd = null;
                //}
                if (dtTemp != null)
                {
                    dtTemp.Dispose();
                    dtTemp = null;
                }

                GetAlertRecipientsRet = strReturn;
            }

            return GetAlertRecipientsRet;

        }

        #endregion

        #region Other Class

        /// <summary>
        /// 警示事件主檔
        /// </summary>
        /// <remarks></remarks>
        private class AlertEvent : EventBase, IEquatable<AlertEvent>
        {

            public AlertEventTypeEnum AlertEventType;     // 警示事件類型 0:Default(預設事件) 1:Custom(自定義事件)
            public string AlertEventSource;               // 警示事件來源(警示編號/預設事件ID)
            public string NoticeType;                     // 警示通知方式 '0':Email(郵件) '1':SMS(簡訊) '0,1':EMail+SMS(郵件+簡訊) '2':MCloud(推播)
            public string Subject;                        // 訊息主旨
            public string Description;                    // 說明
            public string Creator;                        // 建立人
            public DataTable ResultTable;

            public AlertEvent(DataRow row)
            {
                AlertEventID = row["AlertEventID"].ToString();
                AlertEventName = row["AlertEventName"].ToString();
                AlertEventType = (AlertEventTypeEnum)Conversions.ToInteger(row["AlertEventType"]);
                AlertEventSource = row["AlertEventSource"].ToString();
                NoticeType = row["NoticeType"].ToString();
                Subject = row["Subject"].ToString();
                Description = row["Description"].ToString();
                Creator = row["Creator"].ToString();
            }

            // 判斷是否有相同的警示事件編號
            public bool Equals(AlertEvent other)
            {

                if ((AlertEventID ?? "") == (other.AlertEventID ?? ""))
                {
                    return true;
                }
                else
                {
                    return false;
                }

            }

        }

        // ''' <summary>
        // ''' 警示事件結果記錄表(Log)
        // ''' </summary>
        // ''' <remarks></remarks>
        // Private Class AlertEventLog
        // Inherits EventBase

        // Public AlertTime As Date                        ' 警示時間
        // Public AlertDesc As String                      ' 說明
        // Public AlertCreator As String                   ' 警示建立人
        // Public AlertResult As DataTable                 ' 警示事件觸發結果

        // Public Sub New()
        // Me.Worker = WorkerEnum.ALERTEVENTLOG
        // End Sub

        // Public Sub New(ae As AlertEvent, pAlertResult As DataTable)
        // Me.AlertEventID = ae.AlertEventID
        // Me.AlertEventName = ae.AlertEventName
        // Me.AlertType = ae.AlertType
        // Me.AlertTime = Now
        // Me.AlertDesc = ae.Description
        // Me.AlertCreator = ae.Creator
        // Me.AlertResult = pAlertResult
        // Me.Worker = WorkerEnum.ALERTEVENTLOG
        // End Sub

        // End Class

        /// <summary>
        /// AutoRun Log
        /// </summary>
        /// <remarks></remarks>
        private class AutoRunLog : EventBase
        {

            public AutoRunLog()
            {
            }

            public AutoRunLog(AlertEvent ae)
            {
                LogID = ae.LogID;
                AlertEventID = ae.AlertEventID;
                AlertEventName = ae.AlertEventName;
                StartTime = DateTime.Now;
            }

            // Public Sub New(ael As AlertEventLog)
            // Me.AlertEventID = ael.AlertEventID
            // Me.AlertEventName = ael.AlertEventName
            // Me.StartTime = ael.StartTime
            // Me.EndTime = ael.EndTime
            // Me.ExecuteResult = ael.ExecuteResult
            // Me.ErrorMessage = ael.ErrorMessage
            // Me.Worker = ael.Worker
            // End Sub

            // Public Sub New(di As DeliveryInfo)
            // Me.AlertEventID = di.AlertEventID
            // Me.AlertEventName = di.AlertEventName
            // Me.StartTime = di.StartTime
            // Me.EndTime = di.EndTime
            // Me.ExecuteResult = di.ExecuteResult
            // Me.ErrorMessage = di.ErrorMessage
            // Me.Worker = di.Worker
            // End Sub

        }

        /// <summary>
        /// Base
        /// </summary>
        /// <remarks></remarks>
        private abstract class EventBase
        {
            public string LogID;
            public string AlertEventID;                   // 警示事件ID
            public string AlertEventName;                 // 警示事件名稱
            public DateTime StartTime;                        // 事件開始時間
            public DateTime EndTime;                          // 事件結束時間
            public ExecuteResultEnum ExecuteResult;       // 執行結果
            public string ErrorMessage;                   // 錯誤訊息
            public AlertTypeEnum AlertType;               // 警示類型 1:ERF, 2:LOT, 3:EQP, 4:ACC, 5:MO
        }

        // ''' <summary>
        // ''' 派送資訊
        // ''' </summary>
        // ''' <remarks></remarks>
        // Private Class DeliveryInfo
        // Inherits EventBase

        // Public Subject As String                        ' 訊息主旨
        // Public Description As String                    ' 說明
        // Public Creator As String                        ' 預警事件建立者, 若AlertEventID無對應的收件人, 則以此為收件人
        // Public AlertContent As DataTable                ' 訊息內容

        // Public Sub New()
        // Me.Worker = WorkerEnum.DELIVERY
        // End Sub

        // Public Sub New(ae As AlertEvent, pAlertContent As DataTable)
        // Me.AlertEventID = ae.AlertEventID
        // Me.AlertEventName = ae.AlertEventName
        // Me.AlertType = ae.AlertType
        // Me.Subject = ae.Subject
        // Me.Description = ae.Description
        // Me.Creator = ae.Creator
        // Me.AlertContent = pAlertContent
        // Me.Worker = WorkerEnum.DELIVERY
        // End Sub

        // End Class

        /// <summary>
        /// Email內容
        /// </summary>
        /// <remarks></remarks>
        private class EmailContent
        {

            #region tr

            public class tr : IList<string>
            {

                private List<string> listTR = new List<string>();

                public void Add(string item)
                {
                    listTR.Add(item);
                }

                public void Clear()
                {
                    listTR.Clear();
                }

                public bool Contains(string item)
                {
                    return listTR.Contains(item);
                }

                public void CopyTo(string[] array, int arrayIndex)
                {

                }

                public int Count
                {
                    get
                    {
                        return listTR.Count;
                    }
                }

                public bool IsReadOnly
                {
                    get
                    {
                        return false;
                    }
                }

                public bool Remove(string item)
                {
                    return listTR.Remove(item);
                }

                public IEnumerator<string> GetEnumerator()
                {
                    return listTR.GetEnumerator();
                }

                public int IndexOf(string item)
                {
                    return listTR.IndexOf(item);
                }

                public void Insert(int index, string item)
                {
                    listTR.Insert(index, item);
                }

                public string this[int index]
                {
                    get
                    {
                        return listTR[index];
                    }
                    set
                    {
                        listTR[index] = value;
                    }
                }

                public void RemoveAt(int index)
                {
                    listTR.RemoveAt(index);
                }

                public IEnumerator GetEnumerator1()
                {
                    return listTR.GetEnumerator();
                }

                IEnumerator IEnumerable.GetEnumerator() => GetEnumerator1();
            }

            #endregion

            public string AlertEventName;         // 警示事件名稱
            public AlertTypeEnum AlertType;       // 警示類別
            public DateTime AlertTime;                // 警示時間
            public string AlertDescription;       // 說明
            public List<string> thead = new List<string>();
            public List<tr> tbody = new List<tr>();

            public string Generate()
            {

                int idx = 0;
                string strTemp = "";
                string strContent = "";

                strContent += "<div style='font-size: 16px;'>警示事件名稱: " + AlertEventName + "</div><br />";
                strContent += "<div style='font-size: 16px;'>警示類別: " + AlertType.ToString() + "</div><br />";
                strContent += "<div style='font-size: 16px;'>警示時間: " + Conversions.ToString(AlertTime) + "</div><br />";
                strContent += "<div style='font-size: 16px;'>說明: " + AlertDescription + "</div><br />";

                // header
                strContent += "<table width='100%' border='1' cellspacing='0' style='border: 1px solid #4472C4;'>";
                strContent += "<tr style='background-color: #4472C4; color: white; height: 24px;'>";

                foreach (string s in thead)
                {
                    strContent += "<th style='font-weight: normal; border: 1px solid #DDDDDD;'>";
                    strContent += s;
                    strContent += "</th>";
                }

                strContent += "</tr>";

                // body
                foreach (tr r in tbody)
                {

                    idx += 1;

                    if (idx % 2 == 1)
                    {
                        // 奇數列
                        strContent += "<tr style='height: 24px;'>";
                    }
                    else
                    {
                        // 偶數列
                        strContent += "<tr style='background-color: #D9E2F3; height: 24px;'>";
                    }

                    strTemp = "";

                    foreach (string d in r)
                    {
                        strTemp += "<td style='border: 1px solid #DDDDDD;'>";
                        strTemp += d;
                        strTemp += "</td>";
                    }

                    strContent += strTemp;
                    strContent += "</tr>";

                }

                strContent += "</table>";

                return strContent;

            }

        }

        #endregion

        #region 資料庫物件

        private class DatabaseInfo
        {

            public struct DatabaseProperty
            {
                public string DatabaseType;
                public string DatabaseOwner;
                public string DatabasePassword;
                public string ConnectionString;
            }

            public DatabaseProperty RealDB = new DatabaseProperty();
            public DatabaseProperty HistoryDB = new DatabaseProperty();

            public DatabaseInfo()
            {
                iMESLicxManager.clsDecoding objLicMag = new iMESLicxManager.clsDecoding();
                try
                {
                    RealDB.DatabaseType = AppSettings["DatabaseType"];
                    RealDB.DatabaseOwner = AppSettings["DatabaseOwner"];
                    RealDB.DatabasePassword = objLicMag.PasswordDecoding(AppSettings["DatabasePassword"]);
                    RealDB.ConnectionString = AppSettings["ConnectionString"] + ";Password=" + RealDB.DatabasePassword;
                    HistoryDB.DatabaseType = AppSettings["ReportDatabaseType"];
                    HistoryDB.DatabaseOwner = AppSettings["ReportDatabaseOwner"];
                    HistoryDB.DatabasePassword = objLicMag.PasswordDecoding(AppSettings["ReportDatabasePassword"]);
                    HistoryDB.ConnectionString = AppSettings["ReportConnectionString"] + ";Password=" + HistoryDB.DatabasePassword;
                }
                catch (Exception ex)
                {
                    throw;
                }
                finally
                {
                    objLicMag = null;
                }

            }

        }

        #endregion

        #region 解密

        // 對應的解密,中文字解碼有問題
        public string DeCrypt(string strCryptThis)
        {
            string DeCryptRet = default(string);

            string g_Key = "xNDFz6LH67LOv7xKbWFpbMu1wejrM7SzvV4tLRvq3X47m708O1xMHLoaMNCqGhoaEN";
            int iDeCryptChar;
            string strDecrypted = "";
            int i, iKeyChar, iStringChar;

            try
            {

                strCryptThis = System.Text.Encoding.ASCII.GetString(Convert.FromBase64String(strCryptThis));

                var loopTo = Strings.Len(strCryptThis);
                for (i = 1; i <= loopTo; i++)
                {
                    iKeyChar = Strings.Asc(Strings.Mid(g_Key, i, 1));
                    iStringChar = Strings.Asc(Strings.Mid(strCryptThis, i, 1));
                    iDeCryptChar = iKeyChar ^ iStringChar;
                    strDecrypted = strDecrypted + Strings.Chr(iDeCryptChar);
                }
                DeCryptRet = strDecrypted;
            }

            catch (Exception ex)
            {
                throw;
            }

            return DeCryptRet;

        }

        #endregion

        #region Queue

        private class IEWQueue<T> : IList<T>
        {

            public event AddCompleteEventHandler AddComplete;

            public delegate void AddCompleteEventHandler();

            private List<T> q = new List<T>();

            public void Add(T item)
            {
                q.Add(item);
                AddComplete?.Invoke();
            }

            public void Clear()
            {
                q.Clear();
            }

            public bool Contains(T item)
            {
                return q.Contains(item);
            }

            public void CopyTo(T[] array, int arrayIndex)
            {
                q.CopyTo(array, arrayIndex);
            }

            public int Count
            {
                get
                {
                    return q.Count;
                }
            }

            public bool IsReadOnly
            {
                get
                {
                    return false;
                }
            }

            public bool Remove(T item)
            {
                return q.Remove(item);
            }

            public IEnumerator<T> GetEnumerator()
            {
                return q.GetEnumerator();
            }

            public int IndexOf(T item)
            {
                return q.IndexOf(item);
            }

            public void Insert(int index, T item)
            {
                q.Insert(index, item);
            }

            public T this[int index]
            {
                get
                {
                    return q[index];
                }
                set
                {
                    q[index] = value;
                }
            }

            public void RemoveAt(int index)
            {
                q.RemoveAt(index);
            }

            public IEnumerator GetEnumerator1()
            {
                return q.GetEnumerator();
            }

            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator1();
        }

        #endregion

        #region AddComplete Event

        private void QueueEvent_AddComplete()
        {

            if (!WorkerEvent.IsBusy)
            {
                WorkerEvent.RunWorkerAsync();
            }

        }

        private void QueueEventLog_AddComplete()
        {

            if (!WorkerEventLog.IsBusy)
            {
                WorkerEventLog.RunWorkerAsync();
            }

        }

        private void QueueAutoRunLog_AddComplete()
        {

            if (!WorkerAutoRunLog.IsBusy)
            {
                WorkerAutoRunLog.RunWorkerAsync();
            }

        }

        private void QueueDelivery_AddComplete()
        {

            if (!WorkerDelivery.IsBusy)
            {
                WorkerDelivery.RunWorkerAsync();
            }

        }

        #endregion

        private int funWriteTxtFile(string WriteTxt, Exception ex = null)
        {
            int funWriteTxtFileRet = default(int);

            funWriteTxtFileRet = -1;

            try
            {
                if (logger != null)
                {
                    logger.WriteLog(WriteTxt, iMESLog.iMESLogLevel.Info, ex);
                }
                else
                {
                    if (!Directory.Exists(AppDomain.CurrentDomain.BaseDirectory + @"\Log"))
                    {
                        Directory.CreateDirectory(AppDomain.CurrentDomain.BaseDirectory + @"\Log");
                    }

                    var fs = new FileStream(AppDomain.CurrentDomain.BaseDirectory + @"\Log\MESIEW_Log_" + Strings.Format(DateTime.Now, "yyyyMMdd") + ".txt", FileMode.OpenOrCreate, FileAccess.ReadWrite);
                    var w = new StreamWriter(fs);

                    w.BaseStream.Seek(0L, SeekOrigin.End);
                    w.WriteLine(Strings.Format(DateTime.Now, "yyyy/MM/dd HH:mm:ss") + "  " + WriteTxt);
                    w.Close();
                    fs.Close();
                }

                funWriteTxtFileRet = 0;
            }

            catch (Exception e1)
            {

            }

            return funWriteTxtFileRet;

        }
    }
}