private void UpgradeTask(CancellationToken cancelToken)
        {
            IUpgradeTasksModel upgradeTaskDataModel = ServiceLocator.Current.GetInstance <IUpgradeTasksModel>();
            var deviceComparer = new DeviceEqualityComparer();

            try
            {
                FtpServer.Instance.NewDataConnection    += FtpServer_NewDataConnection;
                FtpServer.Instance.ClosedDataConnection += FtpServer_ClosedDataConnection;
                FtpServer.Instance.SentData             += FtpServer_SentData;

                upgradeTaskDataModel.RefillDataAsync().Wait();
                //upgradeTaskDataModel.Data.Where(
                upgradeTaskDataModel.Select(
                    t => t.Status == UpgradeStatus.InProgress &&
                    (t.LastStartTime == null ||
                     t.LastStartTime < _serviceStartTime)).ToList()
                .ForEach(task => MarkUpgradeTaskTBD(upgradeTaskDataModel, task));

                while (true)
                {
                    _token.ThrowIfCancellationRequested();

                    // 刪除被使用者標記要刪除且非運行中的 Task
                    //var tasksToBeRemoved = upgradeTaskDataModel.Data.Where(
                    var tasksToBeRemoved = upgradeTaskDataModel.Select(
                        t => t.DeletedByUser &&
                        t.Status != UpgradeStatus.InProgress).ToList();
                    if (tasksToBeRemoved != null)
                    {
                        upgradeTaskDataModel.Delete(tasksToBeRemoved);
                    }

                    // 挑選出狀態為運行中但超過 20 秒未動作的 tasks,將其標記為 TBD
                    upgradeTaskDataModel.Select(
                        t => t.Status == UpgradeStatus.InProgress &&
                        (t.LastUpdateTime == null ||
                         (DateTime.Now - (DateTime)t.LastUpdateTime).TotalSeconds > 20)).ToList()
                    .ForEach(t => { MarkUpgradeTaskTBD(upgradeTaskDataModel, t); });

                    //var upgradeTasks = upgradeTaskDataModel.Data.Where(
                    //    t => t.Status != UpgradeStatus.Removed).ToList();
                    var upgradeTasks = upgradeTaskDataModel.Select(
                        t => t.Status != UpgradeStatus.Removed,
                        t => t.Device,
                        t => t.UpgradeFile).ToList();
                    if (upgradeTasks != null)
                    {
                        var devices = (from t in upgradeTasks
                                       where t.Device.online == 1
                                       select t.Device).Distinct(deviceComparer).ToList();

                        //List<Task> tasks = new List<Task>();

                        foreach (var device in devices)
                        {
                            _token.ThrowIfCancellationRequested();

                            //tasks.Add(Task.Run(() => UpgradeTaskForEachDevice(upgradeTaskDataModel, device, cancelToken)));
                            UpgradeTaskForEachDevice(upgradeTaskDataModel, device, cancelToken);
                        }
                        //Task.WaitAll(tasks.ToArray());
                    }
                    Thread.Sleep(100);
                }
            }
            catch (OperationCanceledException) { }
            finally
            {
                FtpServer.Instance.NewDataConnection    -= FtpServer_NewDataConnection;
                FtpServer.Instance.ClosedDataConnection -= FtpServer_ClosedDataConnection;
                FtpServer.Instance.SentData             -= FtpServer_SentData;
            }
        }
        // ConcurrentQueue<Task> updateTaskQueueForUpgradeTask = new ConcurrentQueue<Task>();
        private void UpgradeTaskForEachDevice(IUpgradeTasksModel upgradeTaskDataModel, Device device, CancellationToken cancelToken)
        {
            // 刪除被使用者標記要刪除且非運行中的 Task
            //var tasksToBeRemoved = upgradeTaskDataModel.Data.Where(
            var tasksToBeRemoved = upgradeTaskDataModel.Select(
                t => t.DeletedByUser &&
                t.Status != UpgradeStatus.InProgress).ToList();

            if (tasksToBeRemoved != null)
            {
                upgradeTaskDataModel.Delete(tasksToBeRemoved);
            }

            //var updateTasksPerDevice = upgradeTaskDataModel.Data.Where(
            //    t => t.DeviceID == device.id && t.Status != UpgradeStatus.Removed
            var updateTasksPerDevice = upgradeTaskDataModel.Select(
                t => t.DeviceID == device.id && t.Status != UpgradeStatus.Removed,
                t => t.Device,
                t => t.UpgradeFile
                ).OrderByDescending(t => t.UpgradeFile.filetype);
            UpgradeTask candidateTask = null;
            bool        stopTrace     = false;

            try
            {
                foreach (var task in updateTasksPerDevice)
                {
                    if (candidateTask == null ||
                        candidateTask.UpgradeFile.filetype != task.UpgradeFile.filetype)
                    {
                        //if (candidateTask != null && candidateTask.Status == UpgradeStatus.TBD)
                        //{
                        // put candidateTask to working queue
                        //PutUpgradeTaskInWorkingQueue(upgradeTaskDataModel, candidateTask);
                        //}
                        candidateTask = task;
                        stopTrace     = false;
                    }
                    else if (!stopTrace)
                    {
                        // similar tasks:
                        // case 1.1: (x, T)         // remove
                        //           (1, T)         // pick
                        // case 1.2: (1, C or W)
                        //           (1, T)         // remove
                        // case 1.3: (1, C or W)
                        //           (2, T)         // pick if 1 is C
                        // case 2.1: (x, T)         // remove
                        //           (x, T)         // remove
                        //           (1, T)         // pick
                        // case 2.2: (1, C or W)
                        //           (x, T)         // remove
                        //           (1, T)         // remove
                        // case 2.3: (1, C or W)
                        //           (x, T)         // remove
                        //           (2, T)         // pick if 1 is C
                        if (task.Status == UpgradeStatus.TBD)
                        {
                            MarkUpgradeTaskRemoved(upgradeTaskDataModel, task);
                        }
                        else
                        {   // Complete or Working
                            if (candidateTask.UpgradeID == task.UpgradeID)
                            {
                                MarkUpgradeTaskRemoved(upgradeTaskDataModel, candidateTask);
                            }
                            else
                            {
                                if (task.Status != UpgradeStatus.Complete)
                                {
                                    candidateTask = task;
                                }
                            }
                            stopTrace = true;
                        }
                    }
                }
            } catch (Exception) { return; }

            //if (candidateTask != null && candidateTask.Status == UpgradeStatus.TBD)
            //{
            //    // put candidateTask to working queue
            //    //PutUpgradeTaskInWorkingQueue(upgradeTaskDataModel, candidateTask);
            //}
            if (device.online == 0)
            {
                return;
            }

            var taskInProgress = (from t in updateTasksPerDevice.AsParallel()
                                  where t.Status == UpgradeStatus.InProgress
                                  select t).FirstOrDefault();

            if (taskInProgress == null)
            {
                candidateTask = (from t in updateTasksPerDevice.AsParallel()
                                 where t.Status == UpgradeStatus.TBD &&
                                 !t.DeletedByUser
                                 orderby t.ID
                                 select t).FirstOrDefault();
                if (candidateTask != null)
                {
                    PutUpgradeTaskInWorkingQueue(upgradeTaskDataModel, candidateTask);
                }
            }
        }