X-Git-Url: https://git.xandkar.net/?a=blobdiff_plain;f=life%2F001%2Fsrc%2Flife_observer.erl;fp=life%2F001%2Fsrc%2Flife_observer.erl;h=f2d6baac0db32e9921b3da4c56f8c878b438d157;hb=8d06c463b83b79e046250fbe60e5429f182e3a7a;hp=0000000000000000000000000000000000000000;hpb=4eb3be32ce405e69f39573b5e38171764cd0d789;p=cellular-automata.git diff --git a/life/001/src/life_observer.erl b/life/001/src/life_observer.erl new file mode 100644 index 0000000..f2d6baa --- /dev/null +++ b/life/001/src/life_observer.erl @@ -0,0 +1,132 @@ +-module(life_observer). +-behaviour(gen_event). + + +%% API +-export([start_link/2 + ,register_with_logger/0 + ,add_handler/2 + ,delete_handler/0 + ,log_generation/4 + ]). + +%% Callbacks +-export([init/1 + ,terminate/2 + ,code_change/3 + ,handle_event/2 + ,handle_call/2 + ,handle_info/2 + ]). + + +-define(EVENT_MGR_REF, ?MODULE). +-define(HANDLER, ?MODULE). + +-define(PATH_DIR__DATA, "data"). +-define(CSV_DELIMITER, ","). + + +-record(state, {log_file :: file:io_device()}). + + +%% ============================================================================ +%% API +%% ============================================================================ + +start_link(X, Y) -> + EventMgrName = {local, ?EVENT_MGR_REF}, + {ok, PID} = gen_event:start_link(EventMgrName), + ok = add_handler(X, Y), + {ok, PID}. + + +register_with_logger() -> + error_logger:add_report_handler(?HANDLER). + + +add_handler(X, Y) -> + Args = [X, Y], + gen_event:add_handler(?EVENT_MGR_REF, ?HANDLER, Args). + + +delete_handler() -> + Args = [], + gen_event:delete_handler(?EVENT_MGR_REF, ?HANDLER, Args). + + +log_generation(GenID, GenDuration, Dead, Alive) -> + Event = {generation, GenID, GenDuration, Dead, Alive}, + gen_event:notify(?EVENT_MGR_REF, Event). + + +%% ============================================================================ +%% Callbacks (unused) +%% ============================================================================ + +code_change(_Old, State, _Other) -> {ok, State}. +handle_call(_Request, State) -> Reply = ok, {ok, Reply, State}. +handle_info(_Info, State) -> {ok, State}. + + +%% ============================================================================ +%% Callbacks +%% ============================================================================ + +init([X, Y]) -> + N = X * Y, + {{Year, Month, Day}, {Hour, Min, Sec}} = calendar:local_time(), + DateTime = lists:flatten(io_lib:format( + "~4.10.0B-~2.10.0B-~2.10.0B--~2.10.0B:~2.10.0B:~2.10.0B", + [Year, Month, Day, Hour, Min, Sec] + )), + + FileNameComponents = [DateTime | [integer_to_list(I) || I <- [N, X, Y]]], + FileName = string:join(FileNameComponents, "--") ++ ".csv", + FilePath = filename:join(?PATH_DIR__DATA, FileName), + + ok = validate_makedir(file:make_dir(?PATH_DIR__DATA)), + {ok, LogFile} = file:open(FilePath, write), + + CSVHeaders = ["Generation", "Duration", "Dead", "Alive"], + ok = do_write(LogFile, list_to_csvline(CSVHeaders)), + + {ok, #state{log_file=LogFile}}. + + +terminate(_Reason, #state{log_file=LogFile}=State) -> + file:close(LogFile), + {ok, State}. + + +handle_event({generation, GenID, GenDuration, Dead, Alive}, + #state{log_file=LogFile}=State) -> + + Values = [integer_to_list(GenID) + ,float_to_string(GenDuration) + ,integer_to_list(Dead) + ,integer_to_list(Alive) + ], + ok = do_write(LogFile, list_to_csvline(Values)), + {ok, State}. + + +%% ============================================================================ +%% Internal +%% ============================================================================ + +validate_makedir(ok) -> ok; +validate_makedir({error, eexist}) -> ok; +validate_makedir(Other) -> Other. + + +do_write(File, Line) -> + ok = io:format(File, "~s~n", [Line]). + + +list_to_csvline(List) -> + string:join(List, ?CSV_DELIMITER). + + +float_to_string(Float) -> + io_lib:format("~f", [Float]).