diff --git a/apps/iot/include/iot.hrl b/apps/iot/include/iot.hrl index 5ecd9aa..d5ae976 100644 --- a/apps/iot/include/iot.hrl +++ b/apps/iot/include/iot.hrl @@ -65,6 +65,20 @@ increment_id = 0 :: integer() }). +%% 统计项 +-record(option, { + success_num = 0, + fail_num = 0 +}). + +%% 统计累加器 +-record(totalizator, { + key :: {SceneId :: integer(), Date :: calendar:date()}, + scene_id :: integer(), + date :: calendar:date(), + option :: #option{} +}). + %% 北向数据 -record(north_data, { id = 0 :: integer(), diff --git a/apps/iot/src/http_handler/totalizator_handler.erl b/apps/iot/src/http_handler/totalizator_handler.erl new file mode 100644 index 0000000..03a6312 --- /dev/null +++ b/apps/iot/src/http_handler/totalizator_handler.erl @@ -0,0 +1,35 @@ +%%%------------------------------------------------------------------- +%%% @author licheng5 +%%% @copyright (C) 2020, +%%% @doc +%%% +%%% @end +%%% Created : 26. 4月 2020 3:36 下午 +%%%------------------------------------------------------------------- +-module(totalizator_handler). +-author("licheng5"). +-include("iot.hrl"). + +%% API +-export([handle_request/4]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% helper methods +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% 重新加载对应的主机信息 +handle_request("POST", "/totalizator/query", _, #{<<"scene_ids">> := SceneIds, <<"dates">> := Dates0}) when is_list(SceneIds), is_list(Dates0) -> + Dates = lists:map(fun(Date) -> + [Year0, Month0, Day0] = binary:split(Date, <<"-">>, [global]), + Year = binary_to_integer(Year0), + Month = binary_to_integer(Month0), + Day = binary_to_integer(Day0), + {Year, Month, Day} + end, Dates0), + + List = mnesia_totalizator:query(SceneIds, Dates), + {ok, 200, iot_util:json_data(List)}; + +handle_request(_, Path, _, _) -> + Path1 = list_to_binary(Path), + {ok, 200, iot_util:json_error(-1, <<"url: ", Path1/binary, " not found??">>)}. \ No newline at end of file diff --git a/apps/iot/src/iot_app.erl b/apps/iot/src/iot_app.erl index f1549ed..323d913 100644 --- a/apps/iot/src/iot_app.erl +++ b/apps/iot/src/iot_app.erl @@ -44,6 +44,7 @@ start_http_server() -> {'_', [ {"/host/[...]", http_protocol, [host_handler]}, {"/device/[...]", http_protocol, [device_handler]}, + {"/totalizator/[...]", http_protocol, [totalizator_handler]}, {"/test/[...]", http_protocol, [test_handler]}, {"/ws", ws_channel, []} ]} @@ -69,11 +70,11 @@ start_udp_server() -> %% 启动内存数据库 start_mnesia() -> - QueueTab = 'queue_data:zhongdian', %% 启动数据库 ok = mnesia:start(), Tables = mnesia:system_info(tables), - LoadTables = [id_generator, QueueTab], + + LoadTables = [id_generator, totalizator, 'queue_data:zhongdian'], case lists:all(fun(Tab) -> lists:member(Tab, Tables) end, LoadTables) of true -> lager:debug("[iot_app] waiting for mnesia start: ~p", [LoadTables]), @@ -91,20 +92,10 @@ start_mnesia() -> ok = mnesia:start(), %% 创建数据库表 - %% id生成器 - mnesia:create_table(id_generator, [ - {attributes, record_info(fields, id_generator)}, - {record_name, id_generator}, - {disc_copies, [node()]}, - {type, ordered_set} - ]), - + mnesia_id_generator:create_table(), %% 数据转发缓存表 - mnesia:create_table(QueueTab, [ - {attributes, record_info(fields, north_data)}, - {record_name, north_data}, - {disc_copies, [node()]}, - {type, ordered_set} - ]) + mnesia_queue:create_table(), + %% 大数据统计表 + mnesia_totalizator:create_table() end. \ No newline at end of file diff --git a/apps/iot/src/mnesia/mnesia_id_generator.erl b/apps/iot/src/mnesia/mnesia_id_generator.erl index f911a9b..e7a9b6c 100644 --- a/apps/iot/src/mnesia/mnesia_id_generator.erl +++ b/apps/iot/src/mnesia/mnesia_id_generator.erl @@ -8,9 +8,19 @@ %%%------------------------------------------------------------------- -module(mnesia_id_generator). -author("aresei"). +-include("iot.hrl"). %% API --export([next_id/1]). +-export([next_id/1, create_table/0]). + +create_table() -> + %% id生成器 + mnesia:create_table(id_generator, [ + {attributes, record_info(fields, id_generator)}, + {record_name, id_generator}, + {disc_copies, [node()]}, + {type, ordered_set} + ]). next_id(Tab) when is_atom(Tab) -> mnesia:dirty_update_counter(id_generator, Tab, 1). \ No newline at end of file diff --git a/apps/iot/src/mnesia/mnesia_queue.erl b/apps/iot/src/mnesia/mnesia_queue.erl index d328478..bae59eb 100644 --- a/apps/iot/src/mnesia/mnesia_queue.erl +++ b/apps/iot/src/mnesia/mnesia_queue.erl @@ -13,8 +13,18 @@ -define(TAB_NAME, 'queue_data:zhongdian'). %% API +-export([create_table/0]). -export([insert/1, delete/1, table_size/0, dirty_fetch_next/1]). +create_table() -> + %% 数据转发缓存表 + mnesia:create_table(?TAB_NAME, [ + {attributes, record_info(fields, north_data)}, + {record_name, north_data}, + {disc_copies, [node()]}, + {type, ordered_set} + ]). + -spec insert(#north_data{}) -> ok | {error, Reason :: any()}. insert(Item = #north_data{}) -> Id = mnesia_id_generator:next_id(?TAB_NAME), diff --git a/apps/iot/src/mnesia/mnesia_totalizator.erl b/apps/iot/src/mnesia/mnesia_totalizator.erl new file mode 100644 index 0000000..833b9a9 --- /dev/null +++ b/apps/iot/src/mnesia/mnesia_totalizator.erl @@ -0,0 +1,103 @@ +%%%------------------------------------------------------------------- +%%% @author aresei +%%% @copyright (C) 2023, +%%% @doc +%%% +%%% @end +%%% Created : 26. 7月 2023 10:40 +%%%------------------------------------------------------------------- +-module(mnesia_totalizator). +-author("aresei"). +-include("iot.hrl"). +-include_lib("stdlib/include/qlc.hrl"). + +-define(TAB_NAME, totalizator). + +%% API +-export([create_table/0]). +-export([increment_success/2, increment_fail/2, delete/2, table_size/0, query/2]). + +create_table() -> + %% id生成器 + mnesia:create_table(?TAB_NAME, [ + {attributes, record_info(fields, totalizator)}, + {record_name, totalizator}, + {disc_copies, [node()]}, + {type, ordered_set} + ]). + +-spec query(SceneIds :: [integer()], Dates :: [calendar:date()]) -> [map()]. +query(SceneIds, Dates) when is_list(SceneIds), is_list(Dates) -> + lists:map(fun(Date) -> + Scenes = lists:map(fun(SceneId) -> + Key = {SceneId, Date}, + case mnesia:dirty_read(?TAB_NAME, Key) of + [R | _] -> + to_map(R); + [] -> + #{<<"scene_id">> => SceneId, <<"success_num">> => 0, <<"fail_num">> => 0} + end + end, SceneIds), + #{<<"date">> => format_date(Date), <<"scenes">> => Scenes} + end, Dates). + +-spec increment_success(SceneId :: integer(), IncNum :: integer()) -> ok | {error, Reason :: any()}. +increment_success(SceneId, IncNum) when is_integer(SceneId), is_integer(IncNum) -> + increment(SceneId, success, IncNum). + +-spec increment_fail(SceneId :: integer(), IncNum :: integer()) -> ok | {error, Reason :: any()}. +increment_fail(SceneId, IncNum) when is_integer(SceneId), is_integer(IncNum) -> + increment(SceneId, fail, IncNum). + +-spec increment(SceneId :: integer(), Type :: atom(), IncNum :: integer()) -> ok | {error, Reason :: any()}. +increment(SceneId, Type, IncNum) when is_integer(SceneId), is_integer(IncNum), is_atom(Type) -> + {Date, _} = calendar:local_time(), + Key = {SceneId, Date}, + Fun = fun() -> + case mnesia:read(?TAB_NAME, Key) of + [R = #totalizator{option = Option = #option{success_num = SuccessNum, fail_num = FailNum}} | _] -> + NOption = case Type of + success -> + Option#option{success_num = SuccessNum + IncNum}; + fail -> + Option#option{fail_num = FailNum + IncNum} + end, + NR = R#totalizator{option = NOption}, + mnesia:write(?TAB_NAME, NR, write); + [] -> + Option = case Type of + success -> + #option{success_num = IncNum}; + fail -> + #option{fail_num = IncNum} + end, + R = #totalizator{key = Key, scene_id = SceneId, date = Date, option = Option}, + mnesia:write(?TAB_NAME, R, write) + end + end, + + case mnesia:transaction(Fun) of + {atomic, ok} -> + ok; + {aborted, Reason} -> + {error, Reason} + end. + +-spec delete(SceneId :: integer(), Date :: calendar:date()) -> ok | {error, Reason :: any()}. +delete(SceneId, Date) when is_integer(SceneId), is_tuple(Date) -> + case mnesia:transaction(fun() -> mnesia:delete(?TAB_NAME, {SceneId, Date}, write) end) of + {atomic, ok} -> + ok; + {aborted, Reason} -> + {error, Reason} + end. + +-spec table_size() -> integer(). +table_size() -> + mnesia:table_info(?TAB_NAME, size). + +to_map(#totalizator{scene_id = SceneId, option = #option{success_num = SuccessNum, fail_num = FailNum}}) -> + #{<<"scene_id">> => SceneId, <<"success_num">> => SuccessNum, <<"fail_num">> => FailNum}. + +format_date({Year, Month, Day}) -> + iolist_to_binary(io_lib:format("~b-~2..0b-~2..0b", [Year, Month, Day])). \ No newline at end of file diff --git a/shell b/shell deleted file mode 100644 index c6a5023..0000000 --- a/shell +++ /dev/null @@ -1 +0,0 @@ -docker run -p 18080:18080/tcp -p 16379:16379/tcp -v /usr/local/var/mnesia/iot:/usr/local/var/mnesia/iot -v /var/log/iot:/data/iot/log -d iot:1.0 \ No newline at end of file