iot_cloud/apps/iot/src/iot_device.erl
2025-08-22 13:22:28 +08:00

126 lines
5.5 KiB
Erlang

%%%-------------------------------------------------------------------
%%% @copyright (C) 2023, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 14. 8月 2023 11:40
%%%-------------------------------------------------------------------
-module(iot_device).
-author("aresei").
-include("iot.hrl").
%% API
-export([new/1, is_activated/1, change_status/2, reload/1, auth/2]).
%% 终端是否授权
-define(DEVICE_AUTH_DENIED, 0).
-define(DEVICE_AUTH_AUTHED, 1).
%% 状态
-define(STATE_DENIED, denied).
-define(STATE_ACTIVATED, activated).
-record(device, {
device_uuid :: binary(),
auth_state = ?STATE_DENIED,
status = ?DEVICE_OFFLINE
}).
%%%===================================================================
%%% API
%%%===================================================================
-spec new(DeviceInfo :: binary() | map()) -> error | {ok, Device :: #device{}}.
new(DeviceUUID) when is_binary(DeviceUUID) ->
case device_bo:get_device_by_uuid(DeviceUUID) of
{ok, #{<<"device_uuid">> := DeviceUUID, <<"authorize_status">> := AuthorizeStatus, <<"status">> := Status}} ->
{ok, #device{device_uuid = DeviceUUID, status = Status, auth_state = auth_state(AuthorizeStatus)}};
undefined ->
lager:warning("[iot_device] device uuid: ~p, loaded from mysql failed", [DeviceUUID]),
error
end;
new(#{<<"device_uuid">> := DeviceUUID, <<"authorize_status">> := AuthorizeStatus, <<"status">> := Status}) ->
{ok, #device{device_uuid = DeviceUUID, status = Status, auth_state = auth_state(AuthorizeStatus)}}.
-spec is_activated(Device :: #device{}) -> boolean().
is_activated(#device{auth_state = AuthState}) ->
AuthState =:= ?STATE_ACTIVATED.
-spec change_status(Device :: #device{}, NewStatus :: integer()) -> NDevice :: #device{}.
change_status(Device = #device{status = Status}, NewStatus) when is_integer(NewStatus), Status =:= NewStatus ->
Device;
change_status(Device = #device{device_uuid = DeviceUUID}, ?DEVICE_ONLINE) ->
{ok, _} = device_bo:change_status(DeviceUUID, ?DEVICE_ONLINE),
report_event(DeviceUUID, ?DEVICE_ONLINE),
Device#device{status = ?DEVICE_ONLINE};
change_status(Device = #device{device_uuid = DeviceUUID}, ?DEVICE_OFFLINE) ->
{ok, #{<<"status">> := Status}} = device_bo:get_device_by_uuid(DeviceUUID),
case Status of
?DEVICE_NOT_JOINED ->
lager:debug("[iot_device] device: ~p, device_maybe_offline, not joined, can not change to offline", [DeviceUUID]),
Device#device{status = ?DEVICE_NOT_JOINED};
?DEVICE_OFFLINE ->
lager:debug("[iot_device] device: ~p, device_maybe_offline, is offline, do nothing", [DeviceUUID]),
Device#device{status = ?DEVICE_OFFLINE};
?DEVICE_ONLINE ->
{ok, _} = device_bo:change_status(DeviceUUID, ?DEVICE_OFFLINE),
report_event(DeviceUUID, ?DEVICE_OFFLINE),
Device#device{status = ?DEVICE_OFFLINE}
end.
-spec reload(Device :: #device{}) -> error | {ok, NDevice :: #device{}}.
reload(Device = #device{device_uuid = DeviceUUID}) ->
lager:debug("[iot_device] will reload: ~p", [DeviceUUID]),
case device_bo:get_device_by_uuid(DeviceUUID) of
{ok, #{<<"authorize_status">> := AuthorizeStatus, <<"status">> := Status}} ->
{ok, Device#device{device_uuid = DeviceUUID, status = Status, auth_state = auth_state(AuthorizeStatus)}};
undefined ->
lager:warning("[iot_device] device uuid: ~p, loaded from mysql failed", [DeviceUUID]),
error
end.
-spec auth(Device :: #device{}, Auth :: boolean()) -> NDevice :: #device{}.
auth(Device = #device{auth_state = StateName, device_uuid = DeviceUUID}, Auth) when is_boolean(Auth) ->
case {StateName, Auth} of
{?STATE_DENIED, false} ->
lager:debug("[iot_device] device_uuid: ~p, auth: false, will keep state_name: ~p", [DeviceUUID, ?STATE_DENIED]),
Device;
{?STATE_DENIED, true} ->
Device#device{auth_state = ?STATE_ACTIVATED};
{?STATE_ACTIVATED, false} ->
lager:debug("[iot_device] device_uuid: ~p, auth: false, state_name from: ~p, to: ~p", [DeviceUUID, ?STATE_ACTIVATED, ?STATE_DENIED]),
Device#device{auth_state = ?STATE_DENIED};
{?STATE_ACTIVATED, true} ->
lager:debug("[iot_device] device_uuid: ~p, auth: true, will keep state_name: ~p", [DeviceUUID, ?STATE_ACTIVATED]),
Device
end.
%%%===================================================================
%%% Internal functions
%%%===================================================================
-spec auth_state(integer()) -> atom().
auth_state(?DEVICE_AUTH_AUTHED) ->
?STATE_ACTIVATED;
auth_state(?DEVICE_AUTH_DENIED) ->
?STATE_DENIED.
-spec report_event(DeviceUUID :: binary(), NewStatus :: integer()) -> no_return().
report_event(DeviceUUID, NewStatus) when is_binary(DeviceUUID), is_integer(NewStatus) ->
TextMap = #{
0 => <<"离线"/utf8>>,
1 => <<"在线"/utf8>>
},
%% 设备的状态信息上报给中电
Timestamp = iot_util:timestamp_of_seconds(),
FieldsList = [#{
<<"key">> => <<"device_status">>,
<<"value">> => NewStatus,
<<"value_text">> => maps:get(NewStatus, TextMap),
<<"unit">> => 0,
<<"type">> => <<"DI">>,
<<"name">> => <<"设备状态"/utf8>>,
<<"timestamp">> => Timestamp
}],
iot_router:route_uuid(DeviceUUID, FieldsList, Timestamp),
lager:debug("[iot_device] device_uuid: ~p, route fields: ~p", [DeviceUUID, FieldsList]).