diff --git a/src/host/iot_host.erl b/src/host/iot_host.erl index 2f23064..9898c14 100644 --- a/src/host/iot_host.erl +++ b/src/host/iot_host.erl @@ -36,6 +36,9 @@ host_id :: integer(), %% 从数据库里面读取到的数据 uuid :: binary(), + %% 主机状态 + host_status :: integer(), + has_session = false :: boolean(), %% 心跳计数器 heartbeat_counter = 0 :: integer(), @@ -179,7 +182,7 @@ start_link(Name, UUID) when is_binary(UUID) -> init([UUID]) -> ok = iot_log:set_metadata(), case iot_api_client:get_host_by_uuid(UUID) of - {ok, #host_info{id = HostId, authorize_status = AuthorizeStatus}} -> + {ok, #host_info{id = HostId, authorize_status = AuthorizeStatus, status = HostStatus}} -> %% 通过host_id注册别名, 可以避免通过查询数据库获取HostPid AliasName = get_alias_name(HostId), global:register_name(AliasName, self()), @@ -191,7 +194,7 @@ init([UUID]) -> true -> ?STATE_ACTIVATED; false -> ?STATE_DENIED end, - {ok, StateName, #state{host_id = HostId, uuid = UUID, has_session = false}}; + {ok, StateName, #state{host_id = HostId, uuid = UUID, host_status = HostStatus, has_session = false}}; undefined -> logger:warning("[iot_host] host uuid: ~p, load host info failed", [UUID]), ignore @@ -309,24 +312,38 @@ handle_event(cast, {handle, {ping, Metrics}}, ?STATE_ACTIVATED, State = #state{u {keep_state, State#state{metrics = Metrics}}; %% 心跳机制 -handle_event(cast, heartbeat, _, State = #state{uuid = UUID, heartbeat_counter = HeartbeatCounter}) -> - maybe_mark_host_online(UUID), - {keep_state, State#state{heartbeat_counter = HeartbeatCounter + 1}}; +handle_event(cast, heartbeat, _, State = #state{uuid = UUID, heartbeat_counter = HeartbeatCounter, host_status = HostStatus}) -> + case maybe_mark_host_online(UUID, HostStatus) of + keep_status -> + {keep_state, State#state{heartbeat_counter = HeartbeatCounter + 1}}; + {next_status, NStatus} -> + {keep_state, State#state{heartbeat_counter = HeartbeatCounter + 1, host_status = NStatus}} + end; %% UDP 心跳丢失但 SSL channel 仍在时,不能把 host 标记为离线。 -handle_event(info, {timeout, _, heartbeat_ticker}, _, State = #state{uuid = UUID, heartbeat_counter = 0, channel_pid = ChannelPid}) when is_pid(ChannelPid) -> - logger:warning("[iot_host] uuid: ~p, udp heartbeat lost but ssl channel is alive: ~p", [UUID, ChannelPid]), - maybe_mark_host_online(UUID), +handle_event(info, {timeout, _, heartbeat_ticker}, _, + State = #state{uuid = UUID, heartbeat_counter = 0, host_status = HostStatus, channel_pid = ChannelPid}) when is_pid(ChannelPid) -> + erlang:start_timer(?HEARTBEAT_INTERVAL, self(), heartbeat_ticker), - {keep_state, State#state{heartbeat_counter = 0}}; + logger:warning("[iot_host] uuid: ~p, udp heartbeat lost but ssl channel is alive: ~p", [UUID, ChannelPid]), + case maybe_mark_host_online(UUID, HostStatus) of + keep_status -> + {keep_state, State#state{heartbeat_counter = 0}}; + {next_status, NStatus} -> + {keep_state, State#state{heartbeat_counter = 0, host_status = NStatus}} + end; %% 没有收到 UDP 心跳且没有 SSL channel,主机下线, 设备状态不变 -handle_event(info, {timeout, _, heartbeat_ticker}, _, State = #state{uuid = UUID, heartbeat_counter = 0}) -> +handle_event(info, {timeout, _, heartbeat_ticker}, _, State = #state{uuid = UUID, heartbeat_counter = 0, host_status = HostStatus}) -> logger:warning("[iot_host] uuid: ~p, heartbeat lost, devices will unknown", [UUID]), - maybe_mark_host_offline(UUID), erlang:start_timer(?HEARTBEAT_INTERVAL, self(), heartbeat_ticker), + case maybe_mark_host_offline(UUID, HostStatus) of + keep_status -> + {keep_state, State#state{channel_pid = undefined, has_session = false, heartbeat_counter = 0}}; + {next_status, NStatus} -> + {keep_state, State#state{channel_pid = undefined, host_status = NStatus, has_session = false, heartbeat_counter = 0}}; - {keep_state, State#state{channel_pid = undefined, has_session = false, heartbeat_counter = 0}}; + end; %% 其他情况下需要重置系统计数器 handle_event(info, {timeout, _, heartbeat_ticker}, _, State = #state{}) -> @@ -398,34 +415,22 @@ flush_reply(Ref) -> request_ref() -> crypto:strong_rand_bytes(16). --spec maybe_mark_host_offline(binary()) -> ok. -maybe_mark_host_offline(UUID) -> - case iot_api_client:get_host_by_uuid(UUID) of - {ok, #host_info{status = ?HOST_NOT_JOINED}} -> - logger:debug("[iot_host] host: ~p, host_maybe_offline, host not joined, can not change to offline", [UUID]), - ok; - {ok, #host_info{status = ?HOST_OFFLINE}} -> - logger:debug("[iot_host] host: ~p, host_maybe_offline, host now is offline, do nothing", [UUID]), - ok; - {ok, #host_info{status = ?HOST_ONLINE}} -> - _ = iot_api_client:change_host_status(UUID, ?HOST_OFFLINE), - ok; - Other -> - logger:warning("[iot_host] host: ~p, load status failed while marking offline: ~p", [UUID, Other]), - ok +-spec maybe_mark_host_online(binary(), integer()) -> {next_status, NStatus :: integer()} | keep_status. +maybe_mark_host_online(UUID, HostStatus) -> + case HostStatus of + ?HOST_OFFLINE -> + _ = iot_api_client:change_host_status(UUID, ?HOST_ONLINE), + {next_status, ?HOST_ONLINE}; + _ -> + keep_status end. --spec maybe_mark_host_online(binary()) -> ok. -maybe_mark_host_online(UUID) -> - case iot_api_client:get_host_by_uuid(UUID) of - {ok, #host_info{status = ?HOST_OFFLINE}} -> - _ = iot_api_client:change_host_status(UUID, ?HOST_ONLINE), - ok; - {ok, #host_info{status = ?HOST_ONLINE}} -> - ok; - {ok, #host_info{status = ?HOST_NOT_JOINED}} -> - ok; - Other -> - logger:warning("[iot_host] host: ~p, load status failed while marking online: ~p", [UUID, Other]), - ok - end. +-spec maybe_mark_host_offline(binary(), integer()) -> {next_status, NStatus :: integer()} | keep_status. +maybe_mark_host_offline(UUID, HostStatus) -> + case HostStatus of + ?HOST_ONLINE -> + _ = iot_api_client:change_host_status(UUID, ?HOST_OFFLINE), + {next_status, ?HOST_OFFLINE}; + _ -> + keep_status + end. \ No newline at end of file