diff --git a/.gitignore b/.gitignore index 80aa7a7..4802d8a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ tzdata/ \#* db/ out +zones diff --git a/src/erldev.erl b/src/erldev.erl deleted file mode 100644 index 8e86c96..0000000 --- a/src/erldev.erl +++ /dev/null @@ -1,48 +0,0 @@ --module(erldev). -%% Contains development-related functions. - -%-compile([export_all]). --export([make_app/1]). - - - - -%% "Compiles" a *.app.src file into a .app file, and places it in -%% the appropriate ebin folder -make_app([AppDirAtom]) -> - %%@todo: module name conflicts? - AppDir = atom_to_list(AppDirAtom), - Modules = get_modules(AppDir), - AppSrc = try get_app_src(AppDir) - catch error:{badmatch, _} -> -% error_logger:error_msg("Couldn't get AppSrcFile for ~p~n", [AppDir]), - erlang:halt() - end, - {application, Name, Opts} = AppSrc, - NewOpts = [ {modules, Modules} | Opts ], - AppFile = filename:join([AppDir, "ebin", atom_to_list(Name) ++ ".app"]), - ok = file:write_file(AppFile, io_lib:format("~p.~n", [{application, Name, NewOpts}])), - error_logger:info_msg("Wrote .app contents to: ~p~n", [AppFile]), - ok. - - - -%% Returns a list of module names of all *.erl files in a given Application -%% folder (assuming OTP directory structure) -get_modules(AppDir) -> - lists:map( - fun(X)-> filename:basename(X, ".erl") end, - filelib:wildcard( - filename:join([AppDir, "src", "*.erl"]) - ) - ). - - - -%% Converts the contsnts of the *.app.src file into erlang terms -get_app_src(AppDir) -> - [AppSrcFile] = filelib:wildcard(filename:join([AppDir, "src", "*.app.src"])), - {ok, [AppSrc]} = file:consult(AppSrcFile), - %error_logger:info_msg("AppSrc: ~p~n", [AppSrc]), - AppSrc. - diff --git a/src/ezic.app.src b/src/ezic.app.src index 07ce430..56e9707 100644 --- a/src/ezic.app.src +++ b/src/ezic.app.src @@ -6,6 +6,5 @@ kernel, stdlib ]}, - {mod, {ezic_app, ezic_db_ets}}, - {env, [{db_dir, "db"}, {tzdata_dir, "priv/tzdata"}]} - ]}. \ No newline at end of file + {env, [{tzdata_dir, "priv/tzdata"}]} + ]}. diff --git a/src/ezic_app.erl b/src/ezic_app.erl deleted file mode 100644 index 1dde13f..0000000 --- a/src/ezic_app.erl +++ /dev/null @@ -1,47 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : ezic_app.erl -%%% Author : aj -%%% Description : -%%% -%%% Created : 15 Nov 2010 by aj -%%%------------------------------------------------------------------- --module(ezic_app). - --behaviour(application). - -%% Application callbacks --export([start/2, stop/1]). - -%%==================================================================== -%% Application callbacks -%%==================================================================== -%%-------------------------------------------------------------------- -%% Function: start(Type, StartArgs) -> {ok, Pid} | -%% {ok, Pid, State} | -%% {error, Reason} -%% Description: This function is called whenever an application -%% is started using application:start/1,2, and should start the processes -%% of the application. If the application is structured according to the -%% OTP design principles as a supervision tree, this means starting the -%% top supervisor of the tree. -%%-------------------------------------------------------------------- -start(_Type, StartArgs) -> - case ezic_sup:start_link(StartArgs) of - {ok, Pid} -> - {ok, Pid}; - Error -> - Error - end. - -%%-------------------------------------------------------------------- -%% Function: stop(State) -> void() -%% Description: This function is called whenever an application -%% has stopped. It is intended to be the opposite of Module:start/2 and -%% should do any necessary cleaning up. The return value is ignored. -%%-------------------------------------------------------------------- -stop(_State) -> - ok. - -%%==================================================================== -%% Internal functions -%%==================================================================== diff --git a/src/ezic_db.erl b/src/ezic_db.erl index fada991..54c6b3f 100644 --- a/src/ezic_db.erl +++ b/src/ezic_db.erl @@ -1,158 +1,24 @@ -module(ezic_db). -include("include/ezic.hrl"). --include_lib("eunit/include/eunit.hrl"). +-export([flatzone/2]). --behaviour(gen_server). +flatzone(Date, ZoneName) -> + ModuleName = ezic_zone_map:find(ZoneName), + FlatZones = ModuleName:flatzones(), + {D, #tztime{time = T, flag = F}} = Date, + DComp = {D, T}, + case lists:filter(filter(DComp, F), FlatZones) of + [] -> {error, no_zone}; + [FlatZone] -> FlatZone; + Matches = [_, _] -> {error, {ambiguous_zone, Matches}}; + Matches -> {error, {should_not_happen, {Matches, Date, ZoneName}}} + end. - --export([ - zones/1 - , rules/1 - , flatzones/1 - , flatzone/2 - %, insert/2 - , get_all/1 - , insert_all/1 - , wipe/1 - , flatten/0 - , get_implementation/0 - ]). - --export([ - start_link/1 - , init/1 - , code_change/3 - , handle_call/3 - , handle_cast/2 - , handle_info/2 - , terminate/2 - ]). - - --record(state, {mod}). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% PUBLIC API -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -zones(TzName) -> - gen_server:call(?MODULE, {zones, TzName}). - - -rules(TzName) -> - gen_server:call(?MODULE, {rules, TzName}). - - -flatzones(TzName) -> - gen_server:call(?MODULE, {flatzones, TzName}). - - -flatzone(Date, TzName) -> - gen_server:call(?MODULE, {flatzone, Date, TzName}). - - -get_all(Tab) -> - gen_server:call(?MODULE, {all, Tab}). - - -insert_all(Records) -> - gen_server:call(?MODULE, {insert_all, Records}). - - -%wipe() -> -% gen_server:call(?MODULE, {wipe}). - - -wipe(Tab) -> - gen_server:call(?MODULE, {wipe, Tab}). - - -flatten() -> - gen_server:call(?MODULE, {flatten}). - -get_implementation() -> - gen_server:call(?MODULE, {implementation}). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% GEN_SERVER -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -start_link(StartArgs) -> - gen_server:start_link({local, ?MODULE}, ?MODULE, StartArgs, []). - - -%% TODO: create a db behavior -init(DbModule) when DbModule =:= ezic_db_mnesia; - DbModule=:= ezic_db_ets -> - DbModule:init(), - State = #state{mod = DbModule}, - {ok, State}. - -%%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -%% DB Lookup -%%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -handle_call({zones, Name}, _, State) -> - Mod= State#state.mod, - Matches= Mod:zones(Name), - {reply, Matches, State}; -handle_call({rules, Name}, _, State) -> - Mod= State#state.mod, - Matches= Mod:rules(Name), - {reply, Matches, State}; -handle_call({all, Tab}, _, State) -> - Mod= State#state.mod, - Matches= Mod:get_all(Tab), - {reply, Matches, State}; -handle_call({flatzones, Name}, _, State) -> - Mod= State#state.mod, - Result= Mod:flatzones(Name), - {reply, Result, State}; -handle_call({flatzone, Date, Name}, _, State) -> - Mod= State#state.mod, - Result= Mod:flatzone(Date, Name), - {reply, Result, State}; -handle_call({insert_all, Records}, _, State) -> - Mod= State#state.mod, - Mod:insert_all(Records), - {noreply, State}; -handle_call({wipe, Tab}, _, State) -> - Mod= State#state.mod, - Result= Mod:wipe(Tab), - {reply, Result, State}; -handle_call({flatten}, _, State) -> - Mod= State#state.mod, - Zones= Mod:get_all(zone), - Rules= Mod:get_all(rule), - FlatZone= ezic_flatten:flatten(Zones, Rules), - Result= Mod:insert_all(FlatZone), - {reply, Result, State}; -handle_call({implementation}, _, State) -> - Mod= State#state.mod, - Impl= Mod:implementation(), - {reply, Impl, State}; -handle_call(_, _, State) -> - {noreply, State}. - - -%%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -%% Other gen_server -%%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -code_change(_, State, _) -> - {ok, State}. - - -handle_cast(_, State) -> - {noreply, State}. - -handle_info(_, State) -> - {noreply, State}. - -terminate(_, _State) -> - ok. +filter(DComp, u) -> + fun(#flatzone{utc_from = From, utc_to = To}) -> + (From =< DComp) and ((DComp =< To) or (To == current)) + end; +filter(DComp, w) -> + fun(#flatzone{wall_from = From, wall_to = To}) -> + (From =< DComp) and ((DComp =< To) or (To == current)) + end. diff --git a/src/ezic_db_ets.erl b/src/ezic_db_ets.erl deleted file mode 100644 index 843fee9..0000000 --- a/src/ezic_db_ets.erl +++ /dev/null @@ -1,149 +0,0 @@ --module(ezic_db_ets). --include("include/ezic.hrl"). --include_lib("eunit/include/eunit.hrl"). - --define(DB_FILENAME, "db.e2f"). - --export([ - init/0 - , zones/1 - , rules/1 - , flatzones/1 - , flatzone/2 - %, insert/2 - , get_all/1 - , insert_all/1 - , wipe/1 - , implementation/0 - ]). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% PUBLIC API -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -implementation() -> ?MODULE. - -zones(TzName) -> - ets:select(zone, [{#zone{name=TzName, _='_'}, [], ['$_']}]). - -rules(TzName) -> - ets:select(rule, [{#rule{name=TzName, _='_'}, [], ['$_']}]). - -flatzones(TzName) -> - ets:select(flatzone, [{#flatzone{tzname=TzName, _='_'}, [], ['$_']}]). - - -flatzone(Date, TzName) -> - FlatZones= ets:select(flatzone, ezic_flatten:ms(Date, TzName)), - case length(FlatZones) of - 1 -> - hd(FlatZones); - 2 -> - {error, {ambiguous_zone, FlatZones}}; - 0 -> - {error, no_zone}; - _ -> - {error, {should_not_happen, {FlatZones, Date, TzName}}} - end. - - -get_all(Tab) -> - try ets:lookup(Tab, Tab) of X -> X - catch error:X -> {error, X} - end. - - - - -insert_all(Records) -> - Zones= [ZI || ZI <- Records, ZI = #zone{}], - ets:insert(zone, Zones), - Rules= [RI || RI <- Records, RI = #rule{}], - ets:insert(rule, Rules), - FlatZones= [FZ || FZ <- Records, FZ = #flatzone{}], - ets:insert(flatzone, FlatZones). - - -%wipe() -> -% gen_server:call(?MODULE, {wipe}). - - -wipe(Tab) -> - ets:delete(Tab, Tab). - - -init() -> - {ok, DbDir}= application:get_env(db_dir), - Filename= filename:join(DbDir, ?DB_FILENAME), - case db_sane(Filename) of - true -> load_tabfile(Filename); - false -> create_tables(Filename) - end, - {ok, []}. - -%%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -%% Private functions for initializing ets -%%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -%% -> true if table exists -db_sane(Filename) -> - %% @todo ensure data is present for each record type. - - case ets:tabfile_info(Filename) of - {ok, _} -> true; - {error, _} -> false - end. - - - -%% creates the dets table and populates it. -create_tables(Filename) -> - {ok, Zones, Rules, _, _} = ezic_record:separate(ezic_loader:load()), - - ets:new(zone, [duplicate_bag, named_table]), - true = ets:insert(zone, Zones), - - ets:new(rule, [duplicate_bag, named_table]), - true = ets:insert(rule, Rules), - - ets:new(flatzone, [duplicate_bag, named_table]), - %% error_logger:info_msg("~p~n", [Rules]), - FlatZones = ezic_flatten:flatten(Zones, Rules), - true = ets:insert(flatzone, FlatZones), - - % combine into one ets - Ets= ets:new(ezic_db_ets, [duplicate_bag]), - ets:insert(Ets, ets:lookup(zone, zone)), - ets:insert(Ets, ets:lookup(rule, rule)), - ets:insert(Ets, ets:lookup(flatzone, flatzone)), - - % save to disk - ets:tab2file(Ets, Filename), - - ets:delete(Ets), - - ok. - - - -%% loads or creates the dets table. -load_tabfile(Filename) -> - {ok, Ets}= ets:file2tab(Filename), - - Zones= ets:lookup(Ets, zone), - ets:new(zone, [duplicate_bag, named_table]), - ets:insert(zone, Zones), - - Rules= ets:lookup(Ets, rule), - ets:new(rule, [duplicate_bag, named_table]), - ets:insert(rule, Rules), - - FlatZones= ets:lookup(Ets, flatzone), - ets:new(flatzone, [duplicate_bag, named_table]), - ets:insert(flatzone, FlatZones), - - ets:delete(Ets), - ok. diff --git a/src/ezic_db_mnesia.erl b/src/ezic_db_mnesia.erl deleted file mode 100644 index 60d3b11..0000000 --- a/src/ezic_db_mnesia.erl +++ /dev/null @@ -1,159 +0,0 @@ --module(ezic_db_mnesia). --include("include/ezic.hrl"). --include_lib("stdlib/include/qlc.hrl"). --include_lib("eunit/include/eunit.hrl"). - - --export([ - init/0 - , zones/1 - , rules/1 - , flatzones/1 - , flatzone/2 - %, insert/2 - , get_all/1 - , insert_all/1 - , wipe/1 - , implementation/0 - ]). - --define(create(Record), - {atomic, ok} = mnesia:create_table(Record, - [{type, bag} - , {disc_copies, [node()]} - , {attributes, record_info(fields, Record)} - ])). - - -implementation() -> ?MODULE. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% READ - db reading methods -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - - -% retrieve all zones by name -zones(Name) -> - F = fun()-> - Q = qlc:q([Z || Z=#zone{name=N} <- mnesia:table(zone), N=:=Name]), - qlc:e(Q) - end, - {atomic, Zones}= mnesia:transaction(F), - Zones. - -% retrieve all rules by name -rules(Name) -> - F = fun()-> - Q = qlc:q([R || R=#rule{name=N} <- mnesia:table(rule), N=:=Name]), - qlc:e(Q) - end, - {atomic, Rules}= mnesia:transaction(F), - Rules. - -% retrieve all flatzones by tz name -flatzones(Name) -> - F = fun()-> - Q = qlc:q([Z || Z=#flatzone{tzname=N} <- mnesia:table(flatzone), N=:=Name]), - qlc:e(Q) - end, - {atomic, FlatZones}= mnesia:transaction(F), - FlatZones. - -% get all records from table -get_all(Tab) when is_atom(Tab) -> - F = fun() -> - Q = qlc:q([R || R<- mnesia:table(Tab)]), - qlc:e(Q) - end, - {atomic, Ret}= mnesia:transaction(F), - Ret. - -%% get a flatzone for a specific date -%% Date :: {date(), #tztime{}} -%% returns #flatzone{} -%% or throws either error: -%% * {ambiguous_zone, [Z1,Z2,...]} -%% * {no_zone} -flatzone(Date, TzName) -> - %% @todo validate Date - F = fun()-> - Q = qlc:q( - [Fz || Fz=#flatzone{tzname=N} <- mnesia:table(flatzone) - , N=:=TzName - , ezic_flatten:contains_date(Fz, Date)]), - qlc:e(Q) - end, - {atomic, FlatZones}= mnesia:transaction(F), - - case length(FlatZones) of - 1 -> - hd(FlatZones); - 2 -> - erlang:error(ambiguous_zone, FlatZones); - 0 -> - erlang:error(no_zone); - _ -> - erlang:error(should_not_happen, {FlatZones, Date, TzName}) - end. - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% ADMIN - initialization and administration methods -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -% initialize the db -init() -> - create_tabs(mnesia:create_schema([node()])), - mnesia:wait_for_tables([rule, zone, link, leap, flatzone], 3000). - - -% WARNING: deletes all db files -%wipe() -> -% mnesia:stop(), -% mnesia:delete_schema([node()]). - - - -% WARNING: deletes all entries in table Tab -wipe(Tab) -> - mnesia:transaction( - fun()-> - mnesia:delete(Tab, '_', write) - end). - - - -create_tabs(ok) -> - mnesia:start(), - - ?create(rule), - ?create(zone), - ?create(link), - ?create(leap), - ?create(flatzone), - - {ok, Zones, Rules, _, _} = ezic_record:separate(ezic_loader:load()), - insert_all(Zones), - insert_all(Rules), - FlatZones = ezic_flatten:flatten(Zones, Rules), - error_logger:info_msg("~p~n", [FlatZones]), - insert_all(FlatZones), - - ok; -create_tabs({error, {_, {already_exists,_}}}) -> - mnesia:start(), - ok; -create_tabs(E) -> - ?debugVal(E). - - -insert_all(Records) -> - mnesia:transaction( - fun() -> - lists:foreach( - fun(R)-> mnesia:write(R) end, - Records) - end). diff --git a/src/ezic_flatten.erl b/src/ezic_flatten.erl index 226fb8c..608d11c 100644 --- a/src/ezic_flatten.erl +++ b/src/ezic_flatten.erl @@ -9,7 +9,7 @@ %% @todo move MAXYEAR to config file --define(MAXYEAR, 2500). % last year to process flatzones for. +-define(MAXYEAR, 2050). % last year to process flatzones for. -export([ diff --git a/src/ezic_generator.erl b/src/ezic_generator.erl new file mode 100644 index 0000000..7b1e4de --- /dev/null +++ b/src/ezic_generator.erl @@ -0,0 +1,46 @@ +-module(ezic_generator). +-export([generate/0]). +-include("include/ezic.hrl"). + +generate() -> + {ok, Zones, Rules, _, _} = ezic_record:separate(ezic_loader:load()), + FlatZones = ezic_flatten:flatten(Zones, Rules), + + filelib:ensure_dir("zones/"), + {ok, MapFile} = file:open("zones/ezic_zone_map.erl", [write]), + file:write(MapFile, "-module(ezic_zone_map).\n"), + file:write(MapFile, "-export([find/1]).\n"), + generate_files(FlatZones, MapFile, []), + ok. + +generate_files([], MapFile, Files) -> + lists:foreach(fun({_, File}) -> + file:write(File, "]."), + file:close(File) + end, Files), + + file:write(MapFile, "find(Z) -> throw({zone_not_found, Z}).\n"), + file:close(MapFile); + +generate_files([FlatZone = #flatzone{tzname = Name} | Rest], MapFile, Files) -> + {File, NewFiles} = case orddict:find(Name, Files) of + error -> + IO = create_file(Name, MapFile), + {IO, orddict:store(Name, IO, Files)}; + {ok, IO} -> + file:write(IO, ",\n"), + {IO, Files} + end, + file:write(File, io_lib:format("~1000p", [FlatZone])), + generate_files(Rest, MapFile, NewFiles). + +create_file(ZoneName, MapFile) -> + io:format("Creating file for zone ~p~n", [ZoneName]), + ModuleName = "ezic_zone_" ++ re:replace(string:to_lower(ZoneName), "/", "_", [global, {return, list}]), + FileName = "zones" ++ "/" ++ ModuleName ++ ".erl", + {ok, File} = file:open(FileName, [write]), + file:write(File, io_lib:format("-module(~p).\n", [list_to_atom(ModuleName)])), + file:write(File, "-export([flatzones/0]).\n"), + file:write(File, "flatzones() ->\n["), + file:write(MapFile, io_lib:format("find(~p) -> ~p;\n", [ZoneName, list_to_atom(ModuleName)])), + File. diff --git a/src/ezic_loader.erl b/src/ezic_loader.erl index cfbdcc6..b98eba3 100644 --- a/src/ezic_loader.erl +++ b/src/ezic_loader.erl @@ -43,7 +43,7 @@ parse_dir(Dir) -> % returns a list of tzdata records from file parse_file(File) -> - %% error_logger:info_msg("Parsing File: ~p~n", [File]), + error_logger:info_msg("Parsing File: ~p~n", [File]), {ok, FD} = file:open(File, [read]), parse_lines(file:read_line(FD), FD, []). diff --git a/src/ezic_sup.erl b/src/ezic_sup.erl deleted file mode 100644 index 02c872f..0000000 --- a/src/ezic_sup.erl +++ /dev/null @@ -1,49 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : ezic_sup.erl -%%% Author : aj -%%% Description : -%%% -%%% Created : 15 Nov 2010 by aj -%%%------------------------------------------------------------------- --module(ezic_sup). - --behaviour(supervisor). - -%% API --export([start_link/1]). - -%% Supervisor callbacks --export([init/1]). - --define(SERVER, ?MODULE). - -%%==================================================================== -%% API functions -%%==================================================================== -%%-------------------------------------------------------------------- -%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} -%% Description: Starts the supervisor -%%-------------------------------------------------------------------- -start_link(StartArgs) -> - supervisor:start_link(?MODULE, StartArgs). - -%%==================================================================== -%% Supervisor callbacks -%%==================================================================== -%%-------------------------------------------------------------------- -%% Func: init(Args) -> {ok, {SupFlags, [ChildSpec]}} | -%% ignore | -%% {error, Reason} -%% Description: Whenever a supervisor is started using -%% supervisor:start_link/[2,3], this function is called by the new process -%% to find out about restart strategy, maximum restart frequency and child -%% specifications. -%%-------------------------------------------------------------------- -init(StartArgs) -> - EzicDb = {ezic_db,{ezic_db, start_link, [StartArgs]}, - permanent,2000,worker,[ezic_db]}, - {ok,{{one_for_all,3,30}, [EzicDb]}}. - -%%==================================================================== -%% Internal functions -%%====================================================================