A more readable list.
[cellular-automata.git] / 001 / src / life_observer.erl
1 -module(life_observer).
2 -behaviour(gen_event).
3
4
5 %% API
6 -export([start_link/2
7 ,register_with_logger/0
8 ,add_handler/2
9 ,delete_handler/0
10 ,log_generation/4
11 ]).
12
13 %% Callbacks
14 -export([init/1
15 ,terminate/2
16 ,code_change/3
17 ,handle_event/2
18 ,handle_call/2
19 ,handle_info/2
20 ]).
21
22
23 -define(EVENT_MGR_REF, ?MODULE).
24 -define(HANDLER, ?MODULE).
25
26 -define(PATH_DIR__DATA, "data").
27 -define(CSV_DELIMITER, ",").
28
29
30 -record(state, {log_file :: file:io_device()}).
31
32
33 %% ============================================================================
34 %% API
35 %% ============================================================================
36
37 start_link(X, Y) ->
38 EventMgrName = {local, ?EVENT_MGR_REF},
39 {ok, PID} = gen_event:start_link(EventMgrName),
40 ok = add_handler(X, Y),
41 {ok, PID}.
42
43
44 register_with_logger() ->
45 error_logger:add_report_handler(?HANDLER).
46
47
48 add_handler(X, Y) ->
49 Args = [X, Y],
50 gen_event:add_handler(?EVENT_MGR_REF, ?HANDLER, Args).
51
52
53 delete_handler() ->
54 Args = [],
55 gen_event:delete_handler(?EVENT_MGR_REF, ?HANDLER, Args).
56
57
58 log_generation(GenID, GenDuration, Dead, Alive) ->
59 Event = {generation, GenID, GenDuration, Dead, Alive},
60 gen_event:notify(?EVENT_MGR_REF, Event).
61
62
63 %% ============================================================================
64 %% Callbacks (unused)
65 %% ============================================================================
66
67 code_change(_Old, State, _Other) -> {ok, State}.
68 handle_call(_Request, State) -> Reply = ok, {ok, Reply, State}.
69 handle_info(_Info, State) -> {ok, State}.
70
71
72 %% ============================================================================
73 %% Callbacks
74 %% ============================================================================
75
76 init([X, Y]) ->
77 N = X * Y,
78 {{Year, Month, Day}, {Hour, Min, Sec}} = calendar:local_time(),
79 DateTime = lists:flatten(io_lib:format(
80 "~4.10.0B-~2.10.0B-~2.10.0B--~2.10.0B:~2.10.0B:~2.10.0B",
81 [Year, Month, Day, Hour, Min, Sec]
82 )),
83
84 FileNameComponents = [DateTime | [integer_to_list(I) || I <- [N, X, Y]]],
85 FileName = string:join(FileNameComponents, "--") ++ ".csv",
86 FilePath = filename:join(?PATH_DIR__DATA, FileName),
87
88 ok = validate_makedir(file:make_dir(?PATH_DIR__DATA)),
89 {ok, LogFile} = file:open(FilePath, write),
90
91 CSVHeaders = ["Generation", "Duration", "Dead", "Alive"],
92 ok = do_write(LogFile, list_to_csvline(CSVHeaders)),
93
94 {ok, #state{log_file=LogFile}}.
95
96
97 terminate(_Reason, #state{log_file=LogFile}=State) ->
98 file:close(LogFile),
99 {ok, State}.
100
101
102 handle_event({generation, GenID, GenDuration, Dead, Alive},
103 #state{log_file=LogFile}=State) ->
104
105 Values = [integer_to_list(GenID)
106 ,float_to_string(GenDuration)
107 ,integer_to_list(Dead)
108 ,integer_to_list(Alive)
109 ],
110 ok = do_write(LogFile, list_to_csvline(Values)),
111 {ok, State}.
112
113
114 %% ============================================================================
115 %% Internal
116 %% ============================================================================
117
118 validate_makedir(ok) -> ok;
119 validate_makedir({error, eexist}) -> ok;
120 validate_makedir(Other) -> Other.
121
122
123 do_write(File, Line) ->
124 ok = io:format(File, "~s~n", [Line]).
125
126
127 list_to_csvline(List) ->
128 string:join(List, ?CSV_DELIMITER).
129
130
131 float_to_string(Float) ->
132 io_lib:format("~f", [Float]).
This page took 0.048811 seconds and 4 git commands to generate.