diff --git a/apps/iot/src/iot_api.erl b/apps/iot/src/iot_api.erl new file mode 100644 index 0000000..c3ea881 --- /dev/null +++ b/apps/iot/src/iot_api.erl @@ -0,0 +1,137 @@ +%%%------------------------------------------------------------------- +%%% @author anlicheng +%%% @copyright (C) 2023, +%%% @doc +%%% +%%% @end +%%% Created : 24. 12月 2023 15:42 +%%%------------------------------------------------------------------- +-module(iot_api). +-author("anlicheng"). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). +-export([ai_event/1]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). + +-define(SERVER, ?MODULE). +-define(API_TOKEN, <<"wv6fGyBhl*7@AsD9">>). + +-record(state, { + +}). + +%%%=================================================================== +%%% API +%%%=================================================================== + +ai_event(Id) when is_integer(Id) -> + gen_server:cast(?MODULE, {ai_event, Id}). + +%% @doc Spawns the server and registers the local name (unique) +-spec(start_link() -> + {ok, Pid :: pid()} | ignore | {error, Reason :: term()}). +start_link() -> + gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== + +%% @private +%% @doc Initializes the server +-spec(init(Args :: term()) -> + {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} | + {stop, Reason :: term()} | ignore). +init([]) -> + {ok, #state{}}. + +%% @private +%% @doc Handling call messages +-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()}, + State :: #state{}) -> + {reply, Reply :: term(), NewState :: #state{}} | + {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} | + {noreply, NewState :: #state{}} | + {noreply, NewState :: #state{}, timeout() | hibernate} | + {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} | + {stop, Reason :: term(), NewState :: #state{}}). +handle_call(_Request, _From, State = #state{}) -> + {reply, ok, State}. + +%% @private +%% @doc Handling cast messages +-spec(handle_cast(Request :: term(), State :: #state{}) -> + {noreply, NewState :: #state{}} | + {noreply, NewState :: #state{}, timeout() | hibernate} | + {stop, Reason :: term(), NewState :: #state{}}). +handle_cast({ai_event, Id}, State = #state{}) -> + spawn_monitor(fun() -> + Sign = iot_util:md5(<>), + {ok, Url} = application:get_env(iot, api_url), + + Headers = [ + {<<"content-type">>, <<"application/json">>} + ], + ReqData = #{ + <<"sign">> => Sign, + <<"id">> => Id + }, + Body = iolist_to_binary(jiffy:encode(ReqData, [force_utf8])), + case hackney:request(post, Url, Headers, Body, [{pool, false}]) of + {ok, 200, _, ClientRef} -> + {ok, RespBody} = hackney:body(ClientRef), + hackney:close(ClientRef); + {ok, HttpCode, _, ClientRef} -> + {ok, RespBody} = hackney:body(ClientRef), + hackney:close(ClientRef), + lager:warning("[iot_api] send body: ~p, get error is: ~p", [Body, {HttpCode, RespBody}]); + {error, Reason} -> + lager:warning("[iot_api] send body: ~p, get error is: ~p", [Body, Reason]) + end + end), + + {noreply, State}. + +%% @private +%% @doc Handling all non call/cast messages +-spec(handle_info(Info :: timeout() | term(), State :: #state{}) -> + {noreply, NewState :: #state{}} | + {noreply, NewState :: #state{}, timeout() | hibernate} | + {stop, Reason :: term(), NewState :: #state{}}). +%% Task进程挂掉 +handle_info({'DOWN', _MRef, process, _Pid, normal}, State) -> + {keep_state, State}; + +handle_info({'DOWN', _MRef, process, _Pid, Reason}, State) -> + lager:notice("[iot_api] task process down with reason: ~p", [Reason]), + {keep_state, State}; + +handle_info(_Info, State = #state{}) -> + {noreply, State}. + +%% @private +%% @doc This function is called by a gen_server when it is about to +%% terminate. It should be the opposite of Module:init/1 and do any +%% necessary cleaning up. When it returns, the gen_server terminates +%% with Reason. The return value is ignored. +-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()), + State :: #state{}) -> term()). +terminate(_Reason, _State = #state{}) -> + ok. + +%% @private +%% @doc Convert process state when code is changed +-spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{}, + Extra :: term()) -> + {ok, NewState :: #state{}} | {error, Reason :: term()}). +code_change(_OldVsn, State = #state{}, _Extra) -> + {ok, State}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== diff --git a/apps/iot/src/iot_host.erl b/apps/iot/src/iot_host.erl index 914e6a4..2e85e77 100644 --- a/apps/iot/src/iot_host.erl +++ b/apps/iot/src/iot_host.erl @@ -490,7 +490,12 @@ handle_event(cast, {handle, {ai_event, Event0}}, ?STATE_ACTIVATED, State = #stat %% 保存数据到mysql Message = iolist_to_binary(jiffy:encode(Params, [force_utf8])), - ai_event_logs_bo:insert(UUID, DeviceUUID, SceneId, MicroId, EventType, Message), + case ai_event_logs_bo:insert(UUID, DeviceUUID, SceneId, MicroId, EventType, Message) of + {ok, LogId} -> + iot_api:ai_event(LogId); + _ -> + ok + end, iot_device:change_status(DevicePid, ?DEVICE_ONLINE), iot_ai_router:route_uuid(DeviceUUID, EventType, Params) diff --git a/apps/iot/src/iot_sup.erl b/apps/iot/src/iot_sup.erl index 5479484..e0b76cf 100644 --- a/apps/iot/src/iot_sup.erl +++ b/apps/iot/src/iot_sup.erl @@ -28,6 +28,15 @@ start_link() -> init([]) -> SupFlags = #{strategy => one_for_one, intensity => 1000, period => 3600}, Specs = [ + #{ + id => 'iot_api', + start => {'iot_api', start_link, []}, + restart => permanent, + shutdown => 2000, + type => supervisor, + modules => ['iot_api'] + }, + #{ id => 'iot_database_buffer', start => {'iot_database_buffer', start_link, []}, diff --git a/config/sys-dev.config b/config/sys-dev.config index a2ea5d6..c49c0a0 100644 --- a/config/sys-dev.config +++ b/config/sys-dev.config @@ -18,6 +18,8 @@ {port, 18080} ]}, + {api_url, "http://39.98.184.67:8800/api/v1/taskLog"}, + %% 目标服务器地址 {emqx_server, [ {host, {39, 98, 184, 67}}, diff --git a/config/sys-prod.config b/config/sys-prod.config index c3b9719..1f9cffc 100644 --- a/config/sys-prod.config +++ b/config/sys-prod.config @@ -23,6 +23,8 @@ {<<"test">>, <<"iot2023">>} ]}, + {api_url, "https://lgsiot.njau.edu.cn/api/v1/taskLog"}, + %% 配置中电的数据转发, mqtt协议 {zhongdian, [ {host, "172.30.6.161"},