From eebaa34291b23da9f7b39bcf7f342abe0710dd97 Mon Sep 17 00:00:00 2001 From: "Ivan I. Ovchinnikov" Date: Mon, 24 Apr 2023 20:30:36 +0300 Subject: [PATCH] lab05 written, not tested (awaiting online) --- src/logging.hrl | 15 +++++++++++++++ src/rss_queue.erl | 47 +++++++++++++++++++++++++++++++++++++++++++--- src/rss_reader.erl | 40 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 src/logging.hrl create mode 100644 src/rss_reader.erl diff --git a/src/logging.hrl b/src/logging.hrl new file mode 100644 index 0000000..f3b8acc --- /dev/null +++ b/src/logging.hrl @@ -0,0 +1,15 @@ +% Helper functions for logging to the error-logger, or printing trace messages +% to the console. + +-define(TRACE(Format, Data), + io:format("[TRACE] ~p ~p: " ++ Format, [?MODULE, self()] ++ Data)). + +-define(INFO(Format, Data), + error_logger:info_msg("~p ~p: " ++ Format, [?MODULE, self()] ++ Data)). + +-define(WARN(Format, Data), + error_logger:warning_msg("~p ~p: " ++ Format, [?MODULE, self()] ++ Data)). + +-define(ERROR(Format, Data), + error_logger:error_msg("~p ~p: " ++ Format, [?MODULE, self()] ++ Data)). + diff --git a/src/rss_queue.erl b/src/rss_queue.erl index 93ac081..890df91 100644 --- a/src/rss_queue.erl +++ b/src/rss_queue.erl @@ -2,10 +2,51 @@ -compile(export_all). - % 4 Создайте и экспортируйте вспомогательные функции для запуска нового серверного процесса, обслуживающего очередь +%% @doc init([]), init([Url]) +%% нужно написать функцию init, и в обоих версиях функции start которые запускают процессы добавить вызовы init. После этого, init может вызвать вашу функцию server с необходимым набором аргументов. +init([]) -> + start(); + +init([Url]) -> + start(Url). + +%% @doc start() +%% Для того чтобы сделать подписчику, вам надо добавить к состоянию очереди новое поле Subscribers, которое будет использоваться для хранения PID'ов очередей. start() -> - Queue = [], - spawn(?MODULE, server, [Queue]). + Queue = [], + Subscribers = sets:new(), + spawn(?MODULE, server, [Queue, Subscribers]). + +%% @doc start(Url) +%% Добавьте новую функцию start/1, которая имеет один аргумент URL, и запускает процесс rss_queue, который привязывается к процессу чтения, получая от него элементы ленты RSS, как это обсуждалось выше. +start(Url) -> + QPid = start(), + rss_reader:start(Url,QPid), + QPid. + +%% @doc Основный цикл программы, который слушает события +%% {add_item, RSSItem} и {get_all, RegPid}. +server(Queue, Subscribers) -> + receive +% Измените код обработки этого сообщения так, чтобы входящие элементы RSS ленты, новые или обновленные версии старых, дополнительно транслировались каждому подписчику очереди + {add_item, RSSItem} -> + UpdatedQueue = push_item(RSSItem, Queue, Subscribers), + server(UpdatedQueue, Subscribers); + {get_all, RegPid} -> + RegPid ! {self(), Queue}, + server(Queue, Subscribers); +% Добавьте обработку нового сообщения {unsubscribe, QPid}, которое удаляет заданный PID из множества подписчиков + {unsubscribe, QPid} -> + server(Queue, sets:del_element(QPid,Subscribers)); +%Обработайте новое сообщение {subscribe, QPid}, которое позволяет очереди подписаться на получение элементов ленты другой очереди + {subscribe, QPid} -> + [add_item(QPid,Item) || Item <- Queue], + server(Queue, sets:add_element(QPid,Subscribers)); +% Измените очередь так, чтобы она могла получать сообщения 'EXIT' или 'DOWN' от очередей подписчиков, в зависимости от того выбрали вы способ с установкой связи между процессами, или мониторинг очереди подписчика. + {'DOWN',_ , _, QPid, _} -> + server(Queue, sets:del_element(QPid,Subscribers)) + end. + %% @doc Функция для сравнения дат date_comporator(A, B) -> diff --git a/src/rss_reader.erl b/src/rss_reader.erl new file mode 100644 index 0000000..034af6b --- /dev/null +++ b/src/rss_reader.erl @@ -0,0 +1,40 @@ +-module(rss_reader). +-include("logging.hrl"). +-compile(export_all). +-define(RETRIEVE,3000). + +%% @doc start(Url, QPid) +%% Запуск сервера +start(Url,QPid)-> + ssl:start(), + spawn(?MODULE,server,[Url,QPid]). + +%% @doc реализация чтения +%% Загружаем ленту с указанного URL, с помощью функции httpc:request/1. +%% Если код ответа равен 200, извлекаем тело ответа и разбирает его XML содержимое с помощью функции xmerl_scan:string/1. +%% Когда информация извлечена из тела запроса, проверяем, что получена лента в формате RSS 2.0. Для этого есть написанная в задании 3 функция rss_parse:is_rss2_feed/1. +%% Если все вышеперечисленные шаги завершились без ошибок, с помощью написанной в прошлом задании вспомогательной функции rss_queue:add_feed/2 отправляем все элементы ленты в очередь, которая стоит в паре с этим процессом чтения. +%% Ждем заданное время, затем возвращаемся к началу и продолжаем все заново. + +%% Обычно, процессы чтения RSS новостей обновляют свои ленты через большие интервалы времени, начиная с 15 минут до часа. Однако, так вам будет сложно тестировать свою программу, поэтому на время разработки этот интервал можно уменьшить до 60 секунд или около того. Объявите с помощью макроса константу, например, RETRIEVE_INTERVAL, с тем чтобы в последствии вы могли бы легко изменить значение таймаута не отыскивая место в коде, где он используется. + +server(Url, QPid)-> + ?INFO("URL ~s~n", [Url]), + {ok,{{_,Code,_},_,Load}}=httpc:request(Url), + case Code of + 200 -> + ?INFO("HTTPCode ~s~n", [Code]), + + {RSS,_} = xmerl_scan:string(Load), + case rss_parse:is_rss2_feed(RSS) of + ok -> + rss_queue:add_feed(QPid,RSS), + receive + after ?RETRIEVE -> + server(Url, QPid) + end; + _ -> + {error,not_rss2_feed} + end; + _ -> {error,Code} + end.