private static bool ProcessArrivedBook(TransportBookInfo book, bool isMyInfo) { if (book == null || string.IsNullOrWhiteSpace(book.AuthorLink)) { return(true); } if (Authors == null) { return(true); } var author = Authors.FindAuthor(book.AuthorLink); if (author == null) { return(true); } var alreadyStared = author.IsNew; if (author.IsIgnored || author.IsDeleted) { return(true); } var authorText = author.Texts.FirstOrDefault(t => t.Link == book.Link); // Link у нас - относительный путь, поэтому не преобразовываем bool isNew = false; bool isUpdated = false; var convertedTime = new DateTime(book.UpdateDate, DateTimeKind.Utc).ToLocalTime(); if (isMyInfo) { if (authorText == null) { return(true); } // просто маркируем свои штампы серверным, не меняя локальные даты проверок authorText.ServerStamp = book.UpdateDate; author.ServerStamp = book.UpdateDate; } else { if (authorText == null) { authorText = new AuthorText(); isNew = true; } if (authorText.ServerStamp > book.UpdateDate) { return(true); } if (!isNew) { book.Size = book.Size < 0 ? 0 : book.Size; // кооректируем, иногда бывает -1 isUpdated = ((authorText.Name != book.Name || authorText.Size != book.Size || (_setting.SkipBookDescription ? false : authorText.Description != book.Description))); } if (!isNew && !isUpdated) { authorText.ServerStamp = book.UpdateDate;// скорректируем локальное значение. поставим серверное return(true); } _messageBrokerTrayInfoCollectorTimer.Stop(); authorText.Description = book.Description; authorText.Genres = book.Genres; authorText.Link = book.Link; authorText.Name = book.Name; authorText.SectionName = book.SectionName; if (!authorText.IsNew) { authorText.SizeOld = authorText.Size; } authorText.Size = book.Size; authorText.UpdateDate = convertedTime; authorText.ServerStamp = book.UpdateDate; // ставим штамп сервера authorText.IsNew = true; if (isNew) { author.Texts.Add(authorText); } author.LastCheckDate = authorText.UpdateDate; try { if (author.NextCheckDate < author.LastCheckDate) { var elasticScheduler = new ElasticScheduler(_logger, _setting); elasticScheduler.MakePlan(author); elasticScheduler.SaveStatistics(); } author.UpdateDate = authorText.UpdateDate; author.ServerStamp = book.UpdateDate; // ставим штамп сервера author.IsNew = true; _logger.Add(string.Format("StatServer> Уведомление: обновился {0} ({1})", author.Name, authorText.Name), true, false); if (!_messageBrokerTrayInfo.Contains(author.Name) && !alreadyStared) { _messageBrokerTrayInfo = string.IsNullOrWhiteSpace(_messageBrokerTrayInfo) ? author.Name : _messageBrokerTrayInfo + "; " + author.Name; } _messageBrokerTrayInfoCollectorTimer.Start(); } catch (Exception ex) { _logger.Add(string.Format("Ошибка формирования плана проверок автора {0} - {1}", author.Name, ex)); } } return(false); }
private static void RecursiveAsyncUpdateAuthorFromStatServer(int counter, List <Author> authors, Action completed) { if (counter == authors.Count) { completed(); return; } var author = authors[counter++]; _logger.Add(string.Format("StatServer {0}> Проверяется автор {1}...", DateTime.Now.ToShortTimeString(), author.Name)); ApiStuff.ApiManager.GetInstance().GetAuthorUpdatesInfo(_logger, _setting, author.URL, author.ServerStamp, (serverStamp, transportBooks) => { var booksArrived = 0; if (transportBooks != null && transportBooks.Count > 0) { foreach (var transportBook in transportBooks) { ProcessArrivedBook(transportBook, false); booksArrived++; } _messageBrokerTrayInfoCollectorTimer.Start(); } // если обнов нет, а штамп сервера больше, то есть уже ктото проверил автора, маркируем локального автора, что проверен и высчитываем для него новую дату проверки if (serverStamp > author.ServerStamp) { var convertedTime = new DateTime(serverStamp, DateTimeKind.Utc).ToLocalTime(); author.LastCheckDate = convertedTime; try { var elasticScheduler = new ElasticScheduler(_logger, _setting); elasticScheduler.MakePlan(author); elasticScheduler.SaveStatistics(); if (author.NextCheckDate < DateTime.Now) { _logger.Add(string.Format( "StatServer {3}> Автор {0} уже проверен {1}. Получено {2} обновлений книг, однако требуется обращение на оригинальный сайт автора для уточнения информации", author.Name, author.LastCheckDate, booksArrived, DateTime.Now.ToShortTimeString()), false); } else { _logger.Add(string.Format( "StatServer {4}> Автор {0} уже проверен {1}. Получено {2} обновлений книг. Следующее время нашей проверки:{3}", author.Name, author.LastCheckDate, booksArrived, author.NextCheckDate, DateTime.Now.ToShortTimeString()), false); } author.ServerStamp = serverStamp; // ставим штамп сервера } catch (Exception ex) { _logger.Add(string.Format("Ошибка формирования плана проверок автора {0} - {1}", author.Name, ex)); } } else { _logger.Add(string.Format("StatServer {1}> Автора {0} никто не проверял. Будет произведено обращение на оригинальный сайт автора", author.Name, DateTime.Now.ToShortTimeString()), false); } if (_logger.Working) { RecursiveAsyncUpdateAuthorFromStatServer(counter, authors, completed); // запускаем проверку следующего автора. если авторы закончились - упраление вернется в completed() } }, (error) => { _logger.Add(error, true); RecursiveAsyncUpdateAuthorFromStatServer(counter, authors, completed); // запускаем проверку следующего автора. если авторы закончились - упраление вернется в completed() } ); }
private void WorkerDoWork(object sender, DoWorkEventArgs e) { var elasticScheduler = new ElasticScheduler(_logger, _setting); var list = (List <Author>)e.Argument; int index = 1; int authorsCnt = list.Count; foreach (Author author in list) { if (_worker.CancellationPending) { e.Cancel = true; return; } SyncRun(Action.IsUpdaterTrue, author); try { SyncRun(Action.SetStatus, new SetStatusParam { Message = string.Format("{1}/{2}: '{0}' проверяется с родного сайта", author.Name, index, list.Count), ToMessage = true, IsError = false }); string page = author.GetAuthorPage(); if (page != null) { author.DaysInaccessible = 0;// скидываем накопительный счетчик дней недоступности, так как страничка доступна стала. SyncRun(Action.UpdateAuthorText, new UpdateTextParam(author, page, index, list.Count)); } else { // проанализируем недоступность во времени var dayDate = new DateTime(author.LastCheckDate.Year, author.LastCheckDate.Month, author.LastCheckDate.Day); if (dayDate < DateTime.Today) // если последний раз проверял не сегодня и сегодня недоступен, увеличиваем маркер недоступности. То есть если сегодня уже был недоступен, то этот факт игнорируем. Таким образом мы раз в сутки учитываем недоступность { author.DaysInaccessible = author.DaysInaccessible + 1; // увеличиваем счетчик недоступности дней, там автор сам уйдет в игнор, если счетчик превысит константу _maxDaysInaccessibility } SyncRun(Action.SetStatus, new SetStatusParam { Message = string.Format("Недоступна страница '{0}'. {1}", author.Name, author.IsIgnored ? "Автор отключен от проверок из-за превышения кол-ва дней недоступности." : ""), ToMessage = true, IsError = true }); } } catch (Exception ex) { SyncRun(Action.SetStatus, new SetStatusParam { Message = ex.StackTrace, ToMessage = false, IsError = true }); SyncRun(Action.SetStatus, new SetStatusParam { Message = ex.Message, ToMessage = true, IsError = true }); SyncRun(Action.SetStatus, new SetStatusParam { Message = string.Format("{1}/{2}: '{0}' не проверен. Ошибка.", author.Name, index, list.Count), ToMessage = true, IsError = true }); } SyncRun(Action.IsUpdaterFalse, author); // пропишем дату последней проверки автора. Необходимо для рассчета следующего времени проверки author.LastCheckDate = DateTime.Now; // перерасчитаем следующее время проверки автора try { elasticScheduler.MakePlan(author); } catch (Exception ex) { SyncRun(Action.SetStatus, new SetStatusParam { Message = string.Format("Ошибка формирования плана проверок автора {0} - {1}", author.Name, ex), ToMessage = true, IsError = true }); } index++; // задержка, если проверка больше одного автора if (authorsCnt > 1) { var period = _setting.IntervalOfUpdate * 3600; // весь период обновлений var rnd = new Random(); var waitSpan = period / authorsCnt; if (App.BalanceInterval > 0) { waitSpan = App.BalanceInterval; } else { waitSpan = waitSpan < 10 ? rnd.Next(10, 15) : waitSpan; waitSpan = waitSpan > 15 ? 15 : waitSpan; } while (waitSpan > 0) { SyncRun(Action.SetStatus, new SetStatusParam { Message = string.Format("->Балансировка нагрузки: ожидание {0} секунд(ы)...", waitSpan), ToMessage = true, IsError = false }); if (_worker.CancellationPending) { e.Cancel = true; return; } Thread.Sleep(1000); waitSpan--; } } } // вызываем метод сохранения статистики. Однако внутри него он ориентируется на настройку "Сохранять статистику" try { elasticScheduler.SaveStatistics(); } catch (Exception ex) { SyncRun(Action.SetStatus, new SetStatusParam { Message = string.Format("Ошибка сохранение статистики. {0}", ex), ToMessage = true, IsError = true }); } var cachedAuthors = new List <Author>(); var cachedAuthorTexts = new List <AuthorText>(); foreach (Author author in list) { foreach (AuthorText authorText in author.Texts) { if (string.IsNullOrWhiteSpace(authorText.Name)) { continue; } authorText.UpdateIsCached(author); if ((authorText.IsNew) && (!authorText.IsCached)) { if ((authorText.Cached == true) || ((authorText.Cached == null) && (author.Cached == true)) || ((authorText.Cached == null) && ((author.Cached == null) && _setting.Cached))) { cachedAuthors.Add(author); cachedAuthorTexts.Add(authorText); } } } } if (cachedAuthors.Count > 0) { SyncRun(Action.SetStatus, new SetStatusParam { Message = string.Format("Кешируется {0} книг", cachedAuthors.Count), ToMessage = true, IsError = false }); for (int i = 0; i < cachedAuthors.Count; i++) { SyncRun(Action.SetStatus, new SetStatusParam { Message = string.Format("{1}/{2}: Кешируется '{0}'", cachedAuthorTexts[i].Name, i + 1, cachedAuthors.Count), ToMessage = true, IsError = false }); var cachedParam = new CachedParam { Author = cachedAuthors[i], AuthorText = cachedAuthorTexts[i] }; SyncRun(Action.CachedAdd, cachedParam); cachedParam.DownloadTextItem.DownloadTextComplete += ItemDownloadTextComplete; _manualEvent = new ManualResetEvent(false); // таймаут на закачку книги исходя из скорости 28 кбит/сек с учетом размера книги int timeout = Math.Max(60 * 1000, cachedAuthorTexts[i].Size * 60 * 1000 / 210); bool result = _manualEvent.WaitOne(timeout); cachedParam.DownloadTextItem.DownloadTextComplete -= ItemDownloadTextComplete; if (!result) { SyncRun(Action.SetStatus, new SetStatusParam { Message = string.Format("{1}/{2}: Закачка книги '{0}' прервана по таймауту", cachedAuthorTexts[i].Name, i + 1, cachedAuthors.Count), ToMessage = true, IsError = true }); SyncRun(Action.CachedRemove, cachedParam); } } } }