lab05 written, not tested (awaiting online)

This commit is contained in:
Ivan I. Ovchinnikov 2023-04-24 20:30:36 +03:00
parent bac73b1a85
commit eebaa34291
3 changed files with 99 additions and 3 deletions

15
src/logging.hrl Normal file
View File

@ -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)).

View File

@ -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]).
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) ->

40
src/rss_reader.erl Normal file
View File

@ -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.