/// <summary>Обрабатывает поступивший запрос на пересчёт транзакций</summary>
        void ProcessRequest(OracleConnection Connection)
        {
            SendResponse(Connection, ResponseType.Start); // отправляет ответ: мы взялись за обработку запроса
              ChangeStatus("Request processing started");
              try
              {
            var SharedData = DataLoader.Allocate(); // получаем действующие в этот момент ОРД
            GetTransactions(Connection, SharedData); // получаем список неотменённых ранее транзакций для корректировки
            try
            {
              // начинаем обработку транзакций
              Log.Message("Транзакций для пересчёта: {0}", Request.Transactions.Count);
              Request.Counter.Start();
              Request.LastContract = null;
              foreach (RecalculationTransactionItem Item in Request.Transactions)
              {
            using (var TransactionConnection = OpenedConnection)
            {
              if (ProcessTransactionItem(TransactionConnection, Item, SharedData))
                Request.SuccessCount++;
              else
                Request.FailedCount++;
              /*if (!ProcessTransactionItem(Connection, Item, SharedData))
                throw new Exception("Ошибка транзакции. Обработка прекращена.");*/

              // пишем в лог информацию о транзакции
              try
              {
                OraQuery.ExecuteNonQuery(Connection, "insert into RecalculationLog (RecalcID, TransactionID, State, Message) values (:RecalcID, :TransactionID, :State, :Message)",
                  new string[] { "RecalcID", "TransactionID", "State", "Message" }, new object[] { Request.ID, Item.ID, Item.State, Item.ErrorMessage });
              }
              catch (OracleException e)
              {
                if (e.Code == 1) // ORA-00001: unique constraint
                {
                  OraQuery.ExecuteNonQuery(Connection, "update RecalculationLog set State = :State, Message = :Message where RecalcID = :RecalcID and TransactionID = :TransactionID",
                    new string[] { "RecalcID", "TransactionID", "State", "Message" }, new object[] { Request.ID, Item.ID, Item.State, Item.ErrorMessage });
                }
              }
            }
            if (Item.IsChanged)
              Request.ChangedCount++; // у нас одной изменённой после пересчета транзакцией больше
            Request.ProcessedCount++;
            SendResponse(Connection, ResponseType.Progress);
            ChangeStatus(string.Format("Processing: {0}/{1}", Request.ProcessedCount, Request.TotalCount));
              }
              // помечаем запрос как обработанный
              OraQuery.ExecuteNonQuery(Connection, "update Recalculations set Processed = 1 where ID = :ID", new string[] { "ID" }, new object[] { Request.ID });

              Request.Processed = true;
              Connection.Commit(); // сохраняем результат
              Request.Counter.Stop();
              SendResponse(Connection, ResponseType.Finish);
              Log.Message("Транзакций изменено по итогам пересчёта: {0}", Request.ChangedCount);
              Log.Message("Обработка запроса завершена успешно");
              ChangeStatus("Finished successfully");
            }
            finally
            {
              DataLoader.Release(SharedData);
            }
              }
              catch (Exception e)
              {
            if (e is OracleException && (e as OracleException).Code == 54) // ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired
              LogError(Connection, "Не удаётся заблокировать транзакции для обработки");
            else
              LogError(Connection, "Ошибка обработки:\n" + e.Message + "\n" + e.StackTrace);
            Connection.Rollback(); // отменяем изменения
            ChangeStatus("Finished with an error");
              }
        }
        private void CheckNotifier()
        {
            try
              {
            NotifierCheckState = NotifierCheckStates.Idle;
            while (true)
            {
              //Log.Message("Check Notifier state: {0}", NotifierCheckState);
              switch (NotifierCheckState)
              {
            case NotifierCheckStates.Idle:
              var StartWaitTime = DateTime.Now;
              ShowFullStatus("Sleeping...");
              while (DateTime.Now - StartWaitTime <= CheckChangeNotifierInterval)
              {
                Thread.Sleep(1000);
                if (NotifierCheckState != NotifierCheckStates.Idle)
                  break;
              }
              if (NotifierCheckState != NotifierCheckStates.CheckSuccess)
                NotifierCheckState = NotifierCheckStates.StartCheck;
              break;
            case NotifierCheckStates.StartCheck:
              using (var c = new OracleConnection(ConnectionString))
              {
                c.AutoCommit = false;
                c.Open();

                NotifierCheckState = NotifierCheckStates.CheckStarted; // must be set before, because notification can come right away
                NotifierCheckStartTime = DateTime.Now;

                int RowsUpdated = OraQuery.ExecuteNonQuery(c, string.Format("update {0} set id = id + 1 where rownum = 1", NotifierCheckTable));
                if (RowsUpdated > 0) c.Commit(); else c.Rollback();
              }
              break;
            case NotifierCheckStates.CheckStarted: // started, in processes
              if (DateTime.Now - NotifierCheckStartTime >= CheckChangeNotifierTimeout) // if no answer after starting
                NotifierCheckState = NotifierCheckStates.CheckFailed;
              else
                Thread.Sleep(1000);
              break;
            case NotifierCheckStates.CheckSuccess:
            case NotifierCheckStates.WasRestarted:
              NotifierCheckState = NotifierCheckStates.Idle;
              break;
            case NotifierCheckStates.CheckFailed: // need restart
              Log.Message("Check Notifier state: {0}", NotifierCheckState); // It is important state
              EventHandle.Set(); // wake up thread for restart
              Thread.Sleep(1000);
              break;
            default:
              Log.Message("Unknown status: {0}", NotifierCheckState);
              break;
              }
            }
              }
              catch (Exception e)
              {
            if (e is ThreadAbortException || e is ThreadInterruptedException) return;
            else Log.Message("Ошибка потока проверки нотификатора: {0}\n{1}", e.Message, e.StackTrace);
              }
        }