/// <summary>
        /// вернуть время обновления торг. сигнала (из файла, если есть таковой) или null
        /// </summary>
        public TradeSignalUpdate FindTradeSignal(int category, string ticker, string timeframeStr)
        {
            var fileName = MakeFileName(category, ticker, timeframeStr);

            if (!File.Exists(fileName))
            {
                return(null);
            }
            var locker = fileLocker.ReceiveValue(fileName);

            if (locker == null)
            {
                locker = new ReaderWriterLock();
                fileLocker.UpdateValues(fileName, locker);
            }
            try
            {
                locker.AcquireReaderLock(LockTimeout);
            }
            catch (ApplicationException)
            {
                Logger.Error("TradeSignalFileStorage.FindTradeSignal() read lock timeout");
                return(null);
            }
            try
            {
                // получить из документа время обновления
                DateTime timeUpdated;
                int      objectsCount;
                if (!TradeSignalXml.GetSignalUpdateParamsFromFile(fileName, TradeSignalXml.DefaultEncoding,
                                                                  out timeUpdated, out objectsCount))
                {
                    return(null);
                }
                return(new TradeSignalUpdate(category, ticker)
                {
                    TimeUpdated = timeUpdated,
                    ObjectCount = objectsCount
                });
            }
            catch (Exception ex)
            {
                Logger.Error("TradeSignalFileStorage.FindTradeSignal() error", ex);
                return(null);
            }
            finally
            {
                locker.ReleaseLock();
            }
        }
 /// <summary>
 /// прочитать файлы
 /// </summary>
 private void InitUpdateTimesFromFiles()
 {
     try
     {
         // перебрать все каталоги
         foreach (var dirPath in Directory.GetDirectories(storageFolder))
         {
             // получить из пути директории имя
             var dirName = Path.GetFileName(dirPath);
             // получить категорию
             var catId = dirName.ToIntSafe();
             if (!catId.HasValue)
             {
                 continue;
             }
             // прочитать все файлы в категории
             foreach (var filePath in Directory.GetFiles(dirPath, "*_*.xml"))
             {
                 var key = GetKeyFromFilePath(catId.Value, filePath);
                 if (key.categoryId == 0)
                 {
                     continue;
                 }
                 // прочитать XML, убедившись что он валиден, и получить из него
                 // время обновления
                 DateTime updateTime;
                 int      objectsCount;
                 if (!TradeSignalXml.GetSignalUpdateParamsFromFile(filePath, TradeSignalXml.DefaultEncoding,
                                                                   out updateTime, out objectsCount))
                 {
                     continue;
                 }
                 // добавить в словарь
                 updateTimes.Add(key, updateTime);
             }
         }
     }
     catch (Exception ex)
     {
         Logger.Error("SignalStorage.InitUpdateTimesFromFiles() error", ex);
         throw;
     }
 }
        public List <TradeSignalUpdate> GetAllTradeSignalUpdates()
        {
            var updates = new List <TradeSignalUpdate>();

            try
            {
                foreach (var filePath in Directory.GetFiles(signalFolder, "*_*_*.xml"))
                {
                    var update = ParseFileName(Path.GetFileNameWithoutExtension(filePath));
                    if (update == null)
                    {
                        continue;
                    }

                    // прочитать время обновления из файла
                    // получить доступ на чтение файла
                    var locker = fileLocker.ReceiveValue(filePath);
                    if (locker == null)
                    {
                        locker = new ReaderWriterLock();
                        fileLocker.UpdateValues(filePath, locker);
                    }
                    try
                    {
                        locker.AcquireReaderLock(LockTimeout);
                    }
                    catch (ApplicationException)
                    {
                        Logger.Error("TradeSignalFileStorage.GetAllTradeSignalUpdates() reader lock timeout");
                        continue;
                    }
                    try
                    {
                        // загрузить из файла
                        DateTime updateDate;
                        int      objectsCount;
                        if (TradeSignalXml.GetSignalUpdateParamsFromFile(filePath, TradeSignalXml.DefaultEncoding,
                                                                         out updateDate, out objectsCount))
                        {
                            update.TimeUpdated = updateDate;
                            update.ObjectCount = objectsCount;
                            updates.Add(update);
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.Error("TradeSignalFileStorage.GetAllTradeSignalUpdates() error", ex);
                        continue;
                    }
                    finally
                    {
                        locker.ReleaseLock();
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Error("Error in GetAllTradeSignalUpdates()", ex);
                return(null);
            }
            return(updates);
        }
        /// <summary>
        /// обновить торговые рекомендации (список объектов в формате XML)
        /// </summary>
        /// <param name="signalCatId">категория сигнала</param>
        /// <param name="ticker">тикер (USDCHF...)</param>
        /// <param name="timeframe">таймфрейм прогноза</param>
        /// <param name="signalXml">XML (well-formed) в виде строки</param>
        public void UpdateSignal(int signalCatId, string ticker, BarSettings timeframe, string signalXml)
        {
            // проверить параметры
            if (string.IsNullOrEmpty(signalXml))
            {
                Logger.ErrorFormat("UpdateSignal (cat={0}) error: signalXml is empty", signalCatId);
                return;
            }
            if (!DalSpot.Instance.GetTickerNames().Contains(ticker))
            {
                Logger.ErrorFormat("UpdateSignal (cat={0}) error: ticker \"{1}\" is not found", signalCatId, ticker);
                return;
            }

            // проверить XML
            if (!TradeSignalXml.XmlIsValid(ref signalXml, true))
            {
                Logger.ErrorFormat("UpdateSignal (cat={0}) error: ", signalCatId, ticker);
                return;
            }

            // записать файл
            var key = new SignalStorageKey {
                categoryId = signalCatId, ticker = ticker, timeframe = timeframe
            };

            if (!WriteFile(key, signalXml))
            {
                return;
            }

            // обновить словарь dicUpdateLocker
            try
            {
                dicUpdateLocker.AcquireWriterLock(LockTimeout);
            }
            catch (ApplicationException)
            {
                Logger.Error("SignalStorage - updateTimes write lock timeout");
                return;
            }

            try
            {
                if (updateTimes.ContainsKey(key))
                {
                    updateTimes[key] = DateTime.Now;
                }
                else
                {
                    updateTimes.Add(key, DateTime.Now);
                }
            }
            catch (Exception ex)
            {
                Logger.Error("SignalStorage - updateTimes update error", ex);
                return;
            }
            finally
            {
                dicUpdateLocker.ReleaseWriterLock();
            }
        }