refactor(gw-lwm2m): refine lwm2m
This commit is contained in:
parent
83630982bc
commit
b16cf44bf6
|
@ -127,4 +127,34 @@ gateway: {
|
||||||
#listener.udp.1: {}
|
#listener.udp.1: {}
|
||||||
#listener.dtls.1: {}
|
#listener.dtls.1: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lwm2m_xml_dir: "{{ platform_etc_dir }}/lwm2m_xml"
|
||||||
|
|
||||||
|
lwm2m.1: {
|
||||||
|
|
||||||
|
lifetime_min: 1s
|
||||||
|
|
||||||
|
lifetime_max: 86400s
|
||||||
|
|
||||||
|
qmode_time_windonw: 22
|
||||||
|
|
||||||
|
auto_observe: false
|
||||||
|
|
||||||
|
mountpoint: "lwm2m/%e/"
|
||||||
|
|
||||||
|
## always | contains_object_list
|
||||||
|
update_msg_publish_condition: contains_object_list
|
||||||
|
|
||||||
|
translators: {
|
||||||
|
command: "dn/#"
|
||||||
|
response: "up/resp"
|
||||||
|
notify: "up/notify"
|
||||||
|
register: "up/resp"
|
||||||
|
update: "up/resp"
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.udp.1 {
|
||||||
|
bind: 5783
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
{vsn, "0.1.0"},
|
{vsn, "0.1.0"},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{mod, {emqx_gateway_app, []}},
|
{mod, {emqx_gateway_app, []}},
|
||||||
{applications, [kernel, stdlib, grpc]},
|
{applications, [kernel, stdlib, grpc, lwm2m_coap]},
|
||||||
{env, []},
|
{env, []},
|
||||||
{modules, []},
|
{modules, []},
|
||||||
{licenses, ["Apache 2.0"]},
|
{licenses, ["Apache 2.0"]},
|
||||||
|
|
|
@ -44,7 +44,8 @@ load_default_gateway_applications() ->
|
||||||
|
|
||||||
gateway_type_searching() ->
|
gateway_type_searching() ->
|
||||||
%% FIXME: Hardcoded apps
|
%% FIXME: Hardcoded apps
|
||||||
[emqx_stomp_impl, emqx_sn_impl, emqx_exproto_impl, emqx_coap_impl].
|
[emqx_stomp_impl, emqx_sn_impl, emqx_exproto_impl,
|
||||||
|
emqx_coap_impl, emqx_lwm2m_impl].
|
||||||
|
|
||||||
load(Mod) ->
|
load(Mod) ->
|
||||||
try
|
try
|
||||||
|
@ -81,10 +82,14 @@ create_gateway_by_default([{Type, Name, Confs}|More]) ->
|
||||||
create_gateway_by_default(More).
|
create_gateway_by_default(More).
|
||||||
|
|
||||||
zipped_confs() ->
|
zipped_confs() ->
|
||||||
All = maps:to_list(emqx_config:get([gateway])),
|
All = maps:to_list(
|
||||||
|
maps:without(exclude_options(), emqx_config:get([gateway]))),
|
||||||
lists:append(lists:foldr(
|
lists:append(lists:foldr(
|
||||||
fun({Type, Gws}, Acc) ->
|
fun({Type, Gws}, Acc) ->
|
||||||
{Names, Confs} = lists:unzip(maps:to_list(Gws)),
|
{Names, Confs} = lists:unzip(maps:to_list(Gws)),
|
||||||
Types = [ Type || _ <- lists:seq(1, length(Names))],
|
Types = [ Type || _ <- lists:seq(1, length(Names))],
|
||||||
[lists:zip3(Types, Names, Confs) | Acc]
|
[lists:zip3(Types, Names, Confs) | Acc]
|
||||||
end, [], All)).
|
end, [], All)).
|
||||||
|
|
||||||
|
exclude_options() ->
|
||||||
|
[lwm2m_xml_dir].
|
||||||
|
|
|
@ -34,8 +34,10 @@ structs() -> ["gateway"].
|
||||||
fields("gateway") ->
|
fields("gateway") ->
|
||||||
[{stomp, t(ref(stomp))},
|
[{stomp, t(ref(stomp))},
|
||||||
{mqttsn, t(ref(mqttsn))},
|
{mqttsn, t(ref(mqttsn))},
|
||||||
{exproto, t(ref(exproto))},
|
{coap, t(ref(coap))},
|
||||||
{coap, t(ref(coap))}
|
{lwm2m, t(ref(lwm2m))},
|
||||||
|
{lwm2m_xml_dir, t(string())},
|
||||||
|
{exproto, t(ref(exproto))}
|
||||||
];
|
];
|
||||||
|
|
||||||
fields(stomp) ->
|
fields(stomp) ->
|
||||||
|
@ -74,6 +76,21 @@ fields(mqttsn_predefined) ->
|
||||||
, {topic, t(string())}
|
, {topic, t(string())}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
fields(lwm2m) ->
|
||||||
|
[{"$id", t(ref(lwm2m_structs))}
|
||||||
|
];
|
||||||
|
|
||||||
|
fields(lwm2m_structs) ->
|
||||||
|
[ {lifetime_min, t(duration())}
|
||||||
|
, {lifetime_max, t(duration())}
|
||||||
|
, {qmode_time_windonw, t(integer())}
|
||||||
|
, {auto_observe, t(boolean())}
|
||||||
|
, {mountpoint, t(string())}
|
||||||
|
, {update_msg_publish_condition, t(union([always, contains_object_list]))}
|
||||||
|
, {translators, t(ref(translators))}
|
||||||
|
, {listener, t(ref(udp_listener_group))}
|
||||||
|
];
|
||||||
|
|
||||||
fields(exproto) ->
|
fields(exproto) ->
|
||||||
[{"$id", t(ref(exproto_structs))}];
|
[{"$id", t(ref(exproto_structs))}];
|
||||||
|
|
||||||
|
@ -100,6 +117,9 @@ fields(clientinfo_override) ->
|
||||||
, {clientid, t(string())}
|
, {clientid, t(string())}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
fields(translators) ->
|
||||||
|
[{"$name", t(string())}];
|
||||||
|
|
||||||
fields(udp_listener_group) ->
|
fields(udp_listener_group) ->
|
||||||
[ {udp, t(ref(udp_listener))}
|
[ {udp, t(ref(udp_listener))}
|
||||||
, {dtls, t(ref(dtls_listener))}
|
, {dtls, t(ref(dtls_listener))}
|
||||||
|
|
|
@ -363,9 +363,12 @@ check_epn(undefined) -> false;
|
||||||
check_epn(_) -> true.
|
check_epn(_) -> true.
|
||||||
|
|
||||||
check_lifetime(undefined) -> false;
|
check_lifetime(undefined) -> false;
|
||||||
check_lifetime(LifeTime) when is_integer(LifeTime) ->
|
check_lifetime(LifeTime0) when is_integer(LifeTime0) ->
|
||||||
Max = proplists:get_value(lifetime_max, lwm2m_coap_responder:options(), 315360000),
|
LifeTime = timer:seconds(LifeTime0),
|
||||||
Min = proplists:get_value(lifetime_min, lwm2m_coap_responder:options(), 0),
|
Envs = proplists:get_value(config, lwm2m_coap_responder:options(), #{}),
|
||||||
|
Max = maps:get(lifetime_max, Envs, 315360000),
|
||||||
|
Min = maps:get(lifetime_min, Envs, 0),
|
||||||
|
|
||||||
if
|
if
|
||||||
LifeTime >= Min, LifeTime =< Max ->
|
LifeTime >= Min, LifeTime =< Max ->
|
||||||
true;
|
true;
|
||||||
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2021 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%
|
||||||
|
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
%% you may not use this file except in compliance with the License.
|
||||||
|
%% You may obtain a copy of the License at
|
||||||
|
%%
|
||||||
|
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
%%
|
||||||
|
%% Unless required by applicable law or agreed to in writing, software
|
||||||
|
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
%% See the License for the specific language governing permissions and
|
||||||
|
%% limitations under the License.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
%% @doc The LwM2M Gateway Implement interface
|
||||||
|
-module(emqx_lwm2m_impl).
|
||||||
|
|
||||||
|
-behavior(emqx_gateway_impl).
|
||||||
|
|
||||||
|
%% APIs
|
||||||
|
-export([ load/0
|
||||||
|
, unload/0
|
||||||
|
]).
|
||||||
|
|
||||||
|
-export([]).
|
||||||
|
|
||||||
|
-export([ init/1
|
||||||
|
, on_insta_create/3
|
||||||
|
, on_insta_update/4
|
||||||
|
, on_insta_destroy/3
|
||||||
|
]).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% APIs
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
load() ->
|
||||||
|
RegistryOptions = [ {cbkmod, ?MODULE}
|
||||||
|
],
|
||||||
|
emqx_gateway_registry:load(lwm2m, RegistryOptions, []).
|
||||||
|
|
||||||
|
unload() ->
|
||||||
|
%% XXX:
|
||||||
|
lwm2m_coap_server_registry:remove_handler(
|
||||||
|
[<<"rd">>],
|
||||||
|
emqx_lwm2m_coap_resource, undefined
|
||||||
|
),
|
||||||
|
emqx_gateway_registry:unload(lwm2m).
|
||||||
|
|
||||||
|
init(_) ->
|
||||||
|
%% Handler
|
||||||
|
_ = lwm2m_coap_server:start_registry(),
|
||||||
|
lwm2m_coap_server_registry:add_handler(
|
||||||
|
[<<"rd">>],
|
||||||
|
emqx_lwm2m_coap_resource, undefined
|
||||||
|
),
|
||||||
|
%% Xml registry
|
||||||
|
{ok, _} = emqx_lwm2m_xml_object_db:start_link(
|
||||||
|
emqx_config:get([gateway, lwm2m_xml_dir])
|
||||||
|
),
|
||||||
|
|
||||||
|
%% XXX: Self managed table?
|
||||||
|
%% TODO: Improve it later
|
||||||
|
{ok, _} = emqx_lwm2m_cm:start_link(),
|
||||||
|
|
||||||
|
GwState = #{},
|
||||||
|
{ok, GwState}.
|
||||||
|
|
||||||
|
%% TODO: deinit
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% emqx_gateway_registry callbacks
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
on_insta_create(_Insta = #{ id := InstaId,
|
||||||
|
rawconf := RawConf
|
||||||
|
}, Ctx, _GwState) ->
|
||||||
|
Listeners = emqx_gateway_utils:normalize_rawconf(RawConf),
|
||||||
|
ListenerPids = lists:map(fun(Lis) ->
|
||||||
|
start_listener(InstaId, Ctx, Lis)
|
||||||
|
end, Listeners),
|
||||||
|
{ok, ListenerPids, _InstaState = #{ctx => Ctx}}.
|
||||||
|
|
||||||
|
on_insta_update(NewInsta, OldInsta, GwInstaState = #{ctx := Ctx}, GwState) ->
|
||||||
|
InstaId = maps:get(id, NewInsta),
|
||||||
|
try
|
||||||
|
%% XXX: 1. How hot-upgrade the changes ???
|
||||||
|
%% XXX: 2. Check the New confs first before destroy old instance ???
|
||||||
|
on_insta_destroy(OldInsta, GwInstaState, GwState),
|
||||||
|
on_insta_create(NewInsta, Ctx, GwState)
|
||||||
|
catch
|
||||||
|
Class : Reason : Stk ->
|
||||||
|
logger:error("Failed to update stomp instance ~s; "
|
||||||
|
"reason: {~0p, ~0p} stacktrace: ~0p",
|
||||||
|
[InstaId, Class, Reason, Stk]),
|
||||||
|
{error, {Class, Reason}}
|
||||||
|
end.
|
||||||
|
|
||||||
|
on_insta_destroy(_Insta = #{ id := InstaId,
|
||||||
|
rawconf := RawConf
|
||||||
|
}, _GwInstaState, _GwState) ->
|
||||||
|
Listeners = emqx_gateway_utils:normalize_rawconf(RawConf),
|
||||||
|
lists:foreach(fun(Lis) ->
|
||||||
|
stop_listener(InstaId, Lis)
|
||||||
|
end, Listeners).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Internal funcs
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
start_listener(InstaId, Ctx, {Type, ListenOn, SocketOpts, Cfg}) ->
|
||||||
|
ListenOnStr = emqx_gateway_utils:format_listenon(ListenOn),
|
||||||
|
case start_listener(InstaId, Ctx, Type, ListenOn, SocketOpts, Cfg) of
|
||||||
|
{ok, Pid} ->
|
||||||
|
io:format("Start lwm2m ~s:~s listener on ~s successfully.~n",
|
||||||
|
[InstaId, Type, ListenOnStr]),
|
||||||
|
Pid;
|
||||||
|
{error, Reason} ->
|
||||||
|
io:format(standard_error,
|
||||||
|
"Failed to start lwm2m ~s:~s listener on ~s: ~0p~n",
|
||||||
|
[InstaId, Type, ListenOnStr, Reason]),
|
||||||
|
throw({badconf, Reason})
|
||||||
|
end.
|
||||||
|
|
||||||
|
start_listener(InstaId, Ctx, Type, ListenOn, SocketOpts, Cfg) ->
|
||||||
|
Name = name(InstaId, udp),
|
||||||
|
NCfg = Cfg#{ctx => Ctx},
|
||||||
|
NSocketOpts = merge_default(SocketOpts),
|
||||||
|
Options = [{config, NCfg}|NSocketOpts],
|
||||||
|
case Type of
|
||||||
|
udp ->
|
||||||
|
lwm2m_coap_server:start_udp(Name, ListenOn, Options);
|
||||||
|
dtls ->
|
||||||
|
lwm2m_coap_server:start_dtls(Name, ListenOn, Options)
|
||||||
|
end.
|
||||||
|
|
||||||
|
name(InstaId, Type) ->
|
||||||
|
list_to_atom(lists:concat([InstaId, ":", Type])).
|
||||||
|
|
||||||
|
merge_default(Options) ->
|
||||||
|
Default = emqx_gateway_utils:default_udp_options(),
|
||||||
|
case lists:keytake(udp_options, 1, Options) of
|
||||||
|
{value, {udp_options, TcpOpts}, Options1} ->
|
||||||
|
[{udp_options, emqx_misc:merge_opts(Default, TcpOpts)}
|
||||||
|
| Options1];
|
||||||
|
false ->
|
||||||
|
[{udp_options, Default} | Options]
|
||||||
|
end.
|
||||||
|
|
||||||
|
stop_listener(InstaId, {Type, ListenOn, SocketOpts, Cfg}) ->
|
||||||
|
StopRet = stop_listener(InstaId, Type, ListenOn, SocketOpts, Cfg),
|
||||||
|
ListenOnStr = emqx_gateway_utils:format_listenon(ListenOn),
|
||||||
|
case StopRet of
|
||||||
|
ok -> io:format("Stop lwm2m ~s:~s listener on ~s successfully.~n",
|
||||||
|
[InstaId, Type, ListenOnStr]);
|
||||||
|
{error, Reason} ->
|
||||||
|
io:format(standard_error,
|
||||||
|
"Failed to stop lwm2m ~s:~s listener on ~s: ~0p~n",
|
||||||
|
[InstaId, Type, ListenOnStr, Reason]
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
StopRet.
|
||||||
|
|
||||||
|
stop_listener(InstaId, Type, ListenOn, _SocketOpts, _Cfg) ->
|
||||||
|
Name = name(InstaId, Type),
|
||||||
|
case Type of
|
||||||
|
udp ->
|
||||||
|
lwm2m_coap_server:stop_udp(Name, ListenOn);
|
||||||
|
dtls ->
|
||||||
|
lwm2m_coap_server:stop_dtls(Name, ListenOn)
|
||||||
|
end.
|
|
@ -75,7 +75,8 @@ call(Pid, Msg, Timeout) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
init(CoapPid, EndpointName, Peername = {_Peerhost, _Port}, RegInfo = #{<<"lt">> := LifeTime, <<"lwm2m">> := Ver}) ->
|
init(CoapPid, EndpointName, Peername = {_Peerhost, _Port}, RegInfo = #{<<"lt">> := LifeTime, <<"lwm2m">> := Ver}) ->
|
||||||
Mountpoint = proplists:get_value(mountpoint, lwm2m_coap_responder:options(), ""),
|
Envs = proplists:get_value(config, lwm2m_coap_responder:options(), #{}),
|
||||||
|
Mountpoint = iolist_to_binary(maps:get(mountpoint, Envs, "")),
|
||||||
Lwm2mState = #lwm2m_state{peername = Peername,
|
Lwm2mState = #lwm2m_state{peername = Peername,
|
||||||
endpoint_name = EndpointName,
|
endpoint_name = EndpointName,
|
||||||
version = Ver,
|
version = Ver,
|
||||||
|
@ -89,7 +90,10 @@ init(CoapPid, EndpointName, Peername = {_Peerhost, _Port}, RegInfo = #{<<"lt">>
|
||||||
ok ->
|
ok ->
|
||||||
_ = run_hooks('client.connack', [conninfo(Lwm2mState), success], undefined),
|
_ = run_hooks('client.connack', [conninfo(Lwm2mState), success], undefined),
|
||||||
|
|
||||||
Sockport = proplists:get_value(port, lwm2m_coap_responder:options(), 5683),
|
%% FIXME:
|
||||||
|
Sockport = 5683,
|
||||||
|
%Sockport = proplists:get_value(port, lwm2m_coap_responder:options(), 5683),
|
||||||
|
|
||||||
ClientInfo1 = maps:put(sockport, Sockport, ClientInfo),
|
ClientInfo1 = maps:put(sockport, Sockport, ClientInfo),
|
||||||
Lwm2mState1 = Lwm2mState#lwm2m_state{started_at = time_now(),
|
Lwm2mState1 = Lwm2mState#lwm2m_state{started_at = time_now(),
|
||||||
mountpoint = maps:get(mountpoint, ClientInfo1)},
|
mountpoint = maps:get(mountpoint, ClientInfo1)},
|
||||||
|
@ -124,8 +128,10 @@ update_reg_info(NewRegInfo, Lwm2mState=#lwm2m_state{life_timer = LifeTimer, regi
|
||||||
coap_pid = CoapPid, endpoint_name = Epn}) ->
|
coap_pid = CoapPid, endpoint_name = Epn}) ->
|
||||||
UpdatedRegInfo = maps:merge(RegInfo, NewRegInfo),
|
UpdatedRegInfo = maps:merge(RegInfo, NewRegInfo),
|
||||||
|
|
||||||
_ = case proplists:get_value(update_msg_publish_condition,
|
Envs = proplists:get_value(config, lwm2m_coap_responder:options(), #{}),
|
||||||
lwm2m_coap_responder:options(), contains_object_list) of
|
|
||||||
|
_ = case maps:get(update_msg_publish_condition,
|
||||||
|
Envs, contains_object_list) of
|
||||||
always ->
|
always ->
|
||||||
send_to_broker(<<"update">>, #{<<"data">> => UpdatedRegInfo}, Lwm2mState);
|
send_to_broker(<<"update">>, #{<<"data">> => UpdatedRegInfo}, Lwm2mState);
|
||||||
contains_object_list ->
|
contains_object_list ->
|
||||||
|
@ -294,7 +300,8 @@ auto_observe_object_list(Expected, Registered) ->
|
||||||
|
|
||||||
send_auto_observe(CoapPid, RegInfo, EndpointName) ->
|
send_auto_observe(CoapPid, RegInfo, EndpointName) ->
|
||||||
%% - auto observe the objects
|
%% - auto observe the objects
|
||||||
case proplists:get_value(auto_observe, lwm2m_coap_responder:options(), false) of
|
Envs = proplists:get_value(config, lwm2m_coap_responder:options(), #{}),
|
||||||
|
case proplists:get_value(auto_observe, Envs, false) of
|
||||||
false ->
|
false ->
|
||||||
?LOG(info, "Auto Observe Disabled", []);
|
?LOG(info, "Auto Observe Disabled", []);
|
||||||
TrueOrObjList ->
|
TrueOrObjList ->
|
||||||
|
@ -379,7 +386,12 @@ get_cached_downlink_messages() ->
|
||||||
is_cache_mode(RegInfo, StartedAt) ->
|
is_cache_mode(RegInfo, StartedAt) ->
|
||||||
case is_psm(RegInfo) orelse is_qmode(RegInfo) of
|
case is_psm(RegInfo) orelse is_qmode(RegInfo) of
|
||||||
true ->
|
true ->
|
||||||
QModeTimeWind = proplists:get_value(qmode_time_window, lwm2m_coap_responder:options(), 22),
|
Envs = proplists:get_value(
|
||||||
|
config,
|
||||||
|
lwm2m_coap_responder:options(),
|
||||||
|
#{}
|
||||||
|
),
|
||||||
|
QModeTimeWind = maps:get(qmode_time_window, Envs, 22),
|
||||||
Now = time_now(),
|
Now = time_now(),
|
||||||
if (Now - StartedAt) >= QModeTimeWind -> true;
|
if (Now - StartedAt) >= QModeTimeWind -> true;
|
||||||
true -> false
|
true -> false
|
||||||
|
@ -400,15 +412,17 @@ is_qmode(_) -> false.
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
downlink_topic(EventType, Lwm2mState = #lwm2m_state{mountpoint = Mountpoint}) ->
|
downlink_topic(EventType, Lwm2mState = #lwm2m_state{mountpoint = Mountpoint}) ->
|
||||||
Topics = proplists:get_value(topics, lwm2m_coap_responder:options(), []),
|
Envs = proplists:get_value(config, lwm2m_coap_responder:options(), #{}),
|
||||||
DnTopic = proplists:get_value(downlink_topic_key(EventType), Topics,
|
Topics = maps:get(translators, Envs, #{}),
|
||||||
default_downlink_topic(EventType)),
|
DnTopic = maps:get(downlink_topic_key(EventType), Topics,
|
||||||
|
default_downlink_topic(EventType)),
|
||||||
take_place(mountpoint(iolist_to_binary(DnTopic), Mountpoint), Lwm2mState).
|
take_place(mountpoint(iolist_to_binary(DnTopic), Mountpoint), Lwm2mState).
|
||||||
|
|
||||||
uplink_topic(EventType, Lwm2mState = #lwm2m_state{mountpoint = Mountpoint}) ->
|
uplink_topic(EventType, Lwm2mState = #lwm2m_state{mountpoint = Mountpoint}) ->
|
||||||
Topics = proplists:get_value(topics, lwm2m_coap_responder:options(), []),
|
Envs = proplists:get_value(config, lwm2m_coap_responder:options(), #{}),
|
||||||
UpTopic = proplists:get_value(uplink_topic_key(EventType), Topics,
|
Topics = maps:get(translators, Envs, #{}),
|
||||||
default_uplink_topic(EventType)),
|
UpTopic = maps:get(uplink_topic_key(EventType), Topics,
|
||||||
|
default_uplink_topic(EventType)),
|
||||||
take_place(mountpoint(iolist_to_binary(UpTopic), Mountpoint), Lwm2mState).
|
take_place(mountpoint(iolist_to_binary(UpTopic), Mountpoint), Lwm2mState).
|
||||||
|
|
||||||
downlink_topic_key(EventType) when is_binary(EventType) ->
|
downlink_topic_key(EventType) when is_binary(EventType) ->
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
% This module is for future use. Disabled now.
|
% This module is for future use. Disabled now.
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([ start_link/0
|
-export([ start_link/1
|
||||||
, stop/0
|
, stop/0
|
||||||
, find_name/1
|
, find_name/1
|
||||||
, find_objectid/1
|
, find_objectid/1
|
||||||
|
@ -49,8 +49,8 @@
|
||||||
%% API Function Definitions
|
%% API Function Definitions
|
||||||
%% ------------------------------------------------------------------
|
%% ------------------------------------------------------------------
|
||||||
|
|
||||||
start_link() ->
|
start_link(XmlDir) ->
|
||||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
gen_server:start_link({local, ?MODULE}, ?MODULE, [XmlDir], []).
|
||||||
|
|
||||||
find_objectid(ObjectId) ->
|
find_objectid(ObjectId) ->
|
||||||
ObjectIdInt = case is_list(ObjectId) of
|
ObjectIdInt = case is_list(ObjectId) of
|
||||||
|
@ -85,10 +85,10 @@ stop() ->
|
||||||
%% gen_server Function Definitions
|
%% gen_server Function Definitions
|
||||||
%% ------------------------------------------------------------------
|
%% ------------------------------------------------------------------
|
||||||
|
|
||||||
init([]) ->
|
init([XmlDir]) ->
|
||||||
_ = ets:new(?LWM2M_OBJECT_DEF_TAB, [set, named_table, protected]),
|
_ = ets:new(?LWM2M_OBJECT_DEF_TAB, [set, named_table, protected]),
|
||||||
_ = ets:new(?LWM2M_OBJECT_NAME_TO_ID_TAB, [set, named_table, protected]),
|
_ = ets:new(?LWM2M_OBJECT_NAME_TO_ID_TAB, [set, named_table, protected]),
|
||||||
load(emqx_config:get([emqx_lwm2m, xml_dir])),
|
load(XmlDir),
|
||||||
{ok, #state{}}.
|
{ok, #state{}}.
|
||||||
|
|
||||||
handle_call(_Request, _From, State) ->
|
handle_call(_Request, _From, State) ->
|
||||||
|
@ -108,8 +108,6 @@ terminate(_Reason, _State) ->
|
||||||
code_change(_OldVsn, State, _Extra) ->
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,240 +0,0 @@
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
||||||
%%
|
|
||||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
%% you may not use this file except in compliance with the License.
|
|
||||||
%% You may obtain a copy of the License at
|
|
||||||
%%
|
|
||||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
%%
|
|
||||||
%% Unless required by applicable law or agreed to in writing, software
|
|
||||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
%% See the License for the specific language governing permissions and
|
|
||||||
%% limitations under the License.
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(emqx_tlv_SUITE).
|
|
||||||
|
|
||||||
% -compile(export_all).
|
|
||||||
% -compile(nowarn_export_all).
|
|
||||||
|
|
||||||
% -define(LOGT(Format, Args), logger:debug("TEST_SUITE: " ++ Format, Args)).
|
|
||||||
|
|
||||||
% -include("src/lwm2m/include/emqx_lwm2m.hrl").
|
|
||||||
% -include_lib("lwm2m_coap/include/coap.hrl").
|
|
||||||
% -include_lib("eunit/include/eunit.hrl").
|
|
||||||
|
|
||||||
|
|
||||||
% all() -> [case01, case02, case03, case03_0, case04, case05, case06, case07, case08, case09].
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
% init_per_suite(Config) ->
|
|
||||||
% Config.
|
|
||||||
|
|
||||||
% end_per_suite(Config) ->
|
|
||||||
% Config.
|
|
||||||
|
|
||||||
|
|
||||||
% case01(_Config) ->
|
|
||||||
% Data = <<16#C8, 16#00, 16#14, 16#4F, 16#70, 16#65, 16#6E, 16#20, 16#4D, 16#6F, 16#62, 16#69, 16#6C, 16#65, 16#20, 16#41, 16#6C, 16#6C, 16#69, 16#61, 16#6E, 16#63, 16#65>>,
|
|
||||||
% R = emqx_lwm2m_tlv:parse(Data),
|
|
||||||
% Exp = [
|
|
||||||
% #{tlv_resource_with_value => 16#00, value => <<"Open Mobile Alliance">>}
|
|
||||||
% ],
|
|
||||||
% ?assertEqual(Exp, R),
|
|
||||||
% EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
|
||||||
% ?assertEqual(EncodedBinary, Data).
|
|
||||||
|
|
||||||
% case02(_Config) ->
|
|
||||||
% Data = <<16#86, 16#06, 16#41, 16#00, 16#01, 16#41, 16#01, 16#05>>,
|
|
||||||
% R = emqx_lwm2m_tlv:parse(Data),
|
|
||||||
% Exp = [
|
|
||||||
% #{tlv_multiple_resource => 16#06, value => [
|
|
||||||
% #{tlv_resource_instance => 16#00, value => <<1>>},
|
|
||||||
% #{tlv_resource_instance => 16#01, value => <<5>>}
|
|
||||||
% ]}
|
|
||||||
% ],
|
|
||||||
% ?assertEqual(Exp, R),
|
|
||||||
% EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
|
||||||
% ?assertEqual(EncodedBinary, Data).
|
|
||||||
|
|
||||||
% case03(_Config) ->
|
|
||||||
% Data = <<16#C8, 16#00, 16#14, 16#4F, 16#70, 16#65, 16#6E, 16#20, 16#4D, 16#6F, 16#62, 16#69, 16#6C, 16#65, 16#20, 16#41, 16#6C, 16#6C, 16#69, 16#61, 16#6E, 16#63, 16#65, 16#C8, 16#01, 16#16, 16#4C, 16#69, 16#67, 16#68, 16#74, 16#77, 16#65, 16#69, 16#67, 16#68, 16#74, 16#20, 16#4D, 16#32, 16#4D, 16#20, 16#43, 16#6C, 16#69, 16#65, 16#6E, 16#74, 16#C8, 16#02, 16#09, 16#33, 16#34, 16#35, 16#30, 16#30, 16#30, 16#31, 16#32, 16#33>>,
|
|
||||||
% R = emqx_lwm2m_tlv:parse(Data),
|
|
||||||
% Exp = [
|
|
||||||
% #{tlv_resource_with_value => 16#00, value => <<"Open Mobile Alliance">>},
|
|
||||||
% #{tlv_resource_with_value => 16#01, value => <<"Lightweight M2M Client">>},
|
|
||||||
% #{tlv_resource_with_value => 16#02, value => <<"345000123">>}
|
|
||||||
% ],
|
|
||||||
% ?assertEqual(Exp, R),
|
|
||||||
% EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
|
||||||
% ?assertEqual(EncodedBinary, Data).
|
|
||||||
|
|
||||||
% case03_0(_Config) ->
|
|
||||||
% Data = <<16#87, 16#02, 16#41, 16#7F, 16#07, 16#61, 16#01, 16#36, 16#01>>,
|
|
||||||
% R = emqx_lwm2m_tlv:parse(Data),
|
|
||||||
% Exp = [
|
|
||||||
% #{tlv_multiple_resource => 16#02, value => [
|
|
||||||
% #{tlv_resource_instance => 16#7F, value => <<16#07>>},
|
|
||||||
% #{tlv_resource_instance => 16#0136, value => <<16#01>>}
|
|
||||||
% ]}
|
|
||||||
% ],
|
|
||||||
% ?assertEqual(Exp, R),
|
|
||||||
% EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
|
||||||
% ?assertEqual(EncodedBinary, Data).
|
|
||||||
|
|
||||||
% case04(_Config) ->
|
|
||||||
% % 6.4.3.1 Single Object Instance Request Example
|
|
||||||
% Data = <<16#C8, 16#00, 16#14, 16#4F, 16#70, 16#65, 16#6E, 16#20, 16#4D, 16#6F, 16#62, 16#69, 16#6C, 16#65, 16#20, 16#41, 16#6C, 16#6C, 16#69, 16#61, 16#6E, 16#63, 16#65, 16#C8, 16#01, 16#16, 16#4C, 16#69, 16#67, 16#68, 16#74, 16#77, 16#65, 16#69, 16#67, 16#68, 16#74, 16#20, 16#4D, 16#32, 16#4D, 16#20, 16#43, 16#6C, 16#69, 16#65, 16#6E, 16#74, 16#C8, 16#02, 16#09, 16#33, 16#34, 16#35, 16#30, 16#30, 16#30, 16#31, 16#32, 16#33, 16#C3, 16#03, 16#31, 16#2E, 16#30, 16#86, 16#06, 16#41, 16#00, 16#01, 16#41, 16#01, 16#05, 16#88, 16#07, 16#08, 16#42, 16#00, 16#0E, 16#D8, 16#42, 16#01, 16#13, 16#88, 16#87, 16#08, 16#41, 16#00, 16#7D, 16#42, 16#01, 16#03, 16#84, 16#C1, 16#09, 16#64, 16#C1, 16#0A, 16#0F, 16#83, 16#0B, 16#41, 16#00, 16#00, 16#C4, 16#0D, 16#51, 16#82, 16#42, 16#8F, 16#C6, 16#0E, 16#2B, 16#30, 16#32, 16#3A, 16#30, 16#30, 16#C1, 16#10, 16#55>>,
|
|
||||||
% R = emqx_lwm2m_tlv:parse(Data),
|
|
||||||
% Exp = [
|
|
||||||
% #{tlv_resource_with_value => 16#00, value => <<"Open Mobile Alliance">>},
|
|
||||||
% #{tlv_resource_with_value => 16#01, value => <<"Lightweight M2M Client">>},
|
|
||||||
% #{tlv_resource_with_value => 16#02, value => <<"345000123">>},
|
|
||||||
% #{tlv_resource_with_value => 16#03, value => <<"1.0">>},
|
|
||||||
% #{tlv_multiple_resource => 16#06, value => [
|
|
||||||
% #{tlv_resource_instance => 16#00, value => <<1>>},
|
|
||||||
% #{tlv_resource_instance => 16#01, value => <<5>>}
|
|
||||||
% ]},
|
|
||||||
% #{tlv_multiple_resource => 16#07, value => [
|
|
||||||
% #{tlv_resource_instance => 16#00, value => <<16#0ED8:16>>},
|
|
||||||
% #{tlv_resource_instance => 16#01, value => <<16#1388:16>>}
|
|
||||||
% ]},
|
|
||||||
% #{tlv_multiple_resource => 16#08, value => [
|
|
||||||
% #{tlv_resource_instance => 16#00, value => <<16#7d>>},
|
|
||||||
% #{tlv_resource_instance => 16#01, value => <<16#0384:16>>}
|
|
||||||
% ]},
|
|
||||||
% #{tlv_resource_with_value => 16#09, value => <<16#64>>},
|
|
||||||
% #{tlv_resource_with_value => 16#0A, value => <<16#0F>>},
|
|
||||||
% #{tlv_multiple_resource => 16#0B, value => [
|
|
||||||
% #{tlv_resource_instance => 16#00, value => <<16#00>>}
|
|
||||||
% ]},
|
|
||||||
% #{tlv_resource_with_value => 16#0D, value => <<16#5182428F:32>>},
|
|
||||||
% #{tlv_resource_with_value => 16#0E, value => <<"+02:00">>},
|
|
||||||
% #{tlv_resource_with_value => 16#10, value => <<"U">>}
|
|
||||||
% ],
|
|
||||||
% ?assertEqual(Exp, R),
|
|
||||||
% EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
|
||||||
% ?assertEqual(EncodedBinary, Data).
|
|
||||||
|
|
||||||
% case05(_Config) ->
|
|
||||||
% % 6.4.3.2 Multiple Object Instance Request Examples
|
|
||||||
% % A) Request on Single-Instance Object
|
|
||||||
% Data = <<16#08, 16#00, 16#79, 16#C8, 16#00, 16#14, 16#4F, 16#70, 16#65, 16#6E, 16#20, 16#4D, 16#6F, 16#62, 16#69, 16#6C, 16#65, 16#20, 16#41, 16#6C, 16#6C, 16#69, 16#61, 16#6E, 16#63, 16#65, 16#C8, 16#01, 16#16, 16#4C, 16#69, 16#67, 16#68, 16#74, 16#77, 16#65, 16#69, 16#67, 16#68, 16#74, 16#20, 16#4D, 16#32, 16#4D, 16#20, 16#43, 16#6C, 16#69, 16#65, 16#6E, 16#74, 16#C8, 16#02, 16#09, 16#33, 16#34, 16#35, 16#30, 16#30, 16#30, 16#31, 16#32, 16#33, 16#C3, 16#03, 16#31, 16#2E, 16#30, 16#86, 16#06, 16#41, 16#00, 16#01, 16#41, 16#01, 16#05, 16#88, 16#07, 16#08, 16#42, 16#00, 16#0E, 16#D8, 16#42, 16#01, 16#13, 16#88, 16#87, 16#08, 16#41, 16#00, 16#7D, 16#42, 16#01, 16#03, 16#84, 16#C1, 16#09, 16#64, 16#C1, 16#0A, 16#0F, 16#83, 16#0B, 16#41, 16#00, 16#00, 16#C4, 16#0D, 16#51, 16#82, 16#42, 16#8F, 16#C6, 16#0E, 16#2B, 16#30, 16#32, 16#3A, 16#30, 16#30, 16#C1, 16#10, 16#55>>,
|
|
||||||
% R = emqx_lwm2m_tlv:parse(Data),
|
|
||||||
% Exp = [
|
|
||||||
% #{tlv_object_instance => 16#00, value => [
|
|
||||||
% #{tlv_resource_with_value => 16#00, value => <<"Open Mobile Alliance">>},
|
|
||||||
% #{tlv_resource_with_value => 16#01, value => <<"Lightweight M2M Client">>},
|
|
||||||
% #{tlv_resource_with_value => 16#02, value => <<"345000123">>},
|
|
||||||
% #{tlv_resource_with_value => 16#03, value => <<"1.0">>},
|
|
||||||
% #{tlv_multiple_resource => 16#06, value => [
|
|
||||||
% #{tlv_resource_instance => 16#00, value => <<1>>},
|
|
||||||
% #{tlv_resource_instance => 16#01, value => <<5>>}
|
|
||||||
% ]},
|
|
||||||
% #{tlv_multiple_resource => 16#07, value => [
|
|
||||||
% #{tlv_resource_instance => 16#00, value => <<16#0ED8:16>>},
|
|
||||||
% #{tlv_resource_instance => 16#01, value => <<16#1388:16>>}
|
|
||||||
% ]},
|
|
||||||
% #{tlv_multiple_resource => 16#08, value => [
|
|
||||||
% #{tlv_resource_instance => 16#00, value => <<16#7d>>},
|
|
||||||
% #{tlv_resource_instance => 16#01, value => <<16#0384:16>>}
|
|
||||||
% ]},
|
|
||||||
% #{tlv_resource_with_value => 16#09, value => <<16#64>>},
|
|
||||||
% #{tlv_resource_with_value => 16#0A, value => <<16#0F>>},
|
|
||||||
% #{tlv_multiple_resource => 16#0B, value => [
|
|
||||||
% #{tlv_resource_instance => 16#00, value => <<16#00>>}
|
|
||||||
% ]},
|
|
||||||
% #{tlv_resource_with_value => 16#0D, value => <<16#5182428F:32>>},
|
|
||||||
% #{tlv_resource_with_value => 16#0E, value => <<"+02:00">>},
|
|
||||||
% #{tlv_resource_with_value => 16#10, value => <<"U">>}
|
|
||||||
% ]}
|
|
||||||
% ],
|
|
||||||
% ?assertEqual(Exp, R),
|
|
||||||
% EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
|
||||||
% ?assertEqual(EncodedBinary, Data).
|
|
||||||
|
|
||||||
% case06(_Config) ->
|
|
||||||
% % 6.4.3.2 Multiple Object Instance Request Examples
|
|
||||||
% % B) Request on Multiple-Instances Object having 2 instances
|
|
||||||
% Data = <<16#08, 16#00, 16#0E, 16#C1, 16#00, 16#01, 16#C1, 16#01, 16#00, 16#83, 16#02, 16#41, 16#7F, 16#07, 16#C1, 16#03, 16#7F, 16#08, 16#02, 16#12, 16#C1, 16#00, 16#03, 16#C1, 16#01, 16#00, 16#87, 16#02, 16#41, 16#7F, 16#07, 16#61, 16#01, 16#36, 16#01, 16#C1, 16#03, 16#7F>>,
|
|
||||||
% R = emqx_lwm2m_tlv:parse(Data),
|
|
||||||
% Exp = [
|
|
||||||
% #{tlv_object_instance => 16#00, value => [
|
|
||||||
% #{tlv_resource_with_value => 16#00, value => <<16#01>>},
|
|
||||||
% #{tlv_resource_with_value => 16#01, value => <<16#00>>},
|
|
||||||
% #{tlv_multiple_resource => 16#02, value => [
|
|
||||||
% #{tlv_resource_instance => 16#7F, value => <<16#07>>}
|
|
||||||
% ]},
|
|
||||||
% #{tlv_resource_with_value => 16#03, value => <<16#7F>>}
|
|
||||||
% ]},
|
|
||||||
% #{tlv_object_instance => 16#02, value => [
|
|
||||||
% #{tlv_resource_with_value => 16#00, value => <<16#03>>},
|
|
||||||
% #{tlv_resource_with_value => 16#01, value => <<16#00>>},
|
|
||||||
% #{tlv_multiple_resource => 16#02, value => [
|
|
||||||
% #{tlv_resource_instance => 16#7F, value => <<16#07>>},
|
|
||||||
% #{tlv_resource_instance => 16#0136, value => <<16#01>>}
|
|
||||||
% ]},
|
|
||||||
% #{tlv_resource_with_value => 16#03, value => <<16#7F>>}
|
|
||||||
% ]}
|
|
||||||
% ],
|
|
||||||
% ?assertEqual(Exp, R),
|
|
||||||
% EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
|
||||||
% ?assertEqual(EncodedBinary, Data).
|
|
||||||
|
|
||||||
% case07(_Config) ->
|
|
||||||
% % 6.4.3.2 Multiple Object Instance Request Examples
|
|
||||||
% % C) Request on Multiple-Instances Object having 1 instance only
|
|
||||||
% Data = <<16#08, 16#00, 16#0F, 16#C1, 16#00, 16#01, 16#C4, 16#01, 16#00, 16#01, 16#51, 16#80, 16#C1, 16#06, 16#01, 16#C1, 16#07, 16#55>>,
|
|
||||||
% R = emqx_lwm2m_tlv:parse(Data),
|
|
||||||
% Exp = [
|
|
||||||
% #{tlv_object_instance => 16#00, value => [
|
|
||||||
% #{tlv_resource_with_value => 16#00, value => <<16#01>>},
|
|
||||||
% #{tlv_resource_with_value => 16#01, value => <<86400:32>>},
|
|
||||||
% #{tlv_resource_with_value => 16#06, value => <<16#01>>},
|
|
||||||
% #{tlv_resource_with_value => 16#07, value => <<$U>>}]}
|
|
||||||
% ],
|
|
||||||
% ?assertEqual(Exp, R),
|
|
||||||
% EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
|
||||||
% ?assertEqual(EncodedBinary, Data).
|
|
||||||
|
|
||||||
% case08(_Config) ->
|
|
||||||
% % 6.4.3.3 Example of Request on an Object Instance containing an Object Link Resource
|
|
||||||
% % Example 1) request to Object 65 Instance 0: Read /65/0
|
|
||||||
% Data = <<16#88, 16#00, 16#0C, 16#44, 16#00, 16#00, 16#42, 16#00, 16#00, 16#44, 16#01, 16#00, 16#42, 16#00, 16#01, 16#C8, 16#01, 16#0D, 16#38, 16#36, 16#31, 16#33, 16#38, 16#30, 16#30, 16#37, 16#35, 16#35, 16#35, 16#30, 16#30, 16#C4, 16#02, 16#12, 16#34, 16#56, 16#78>>,
|
|
||||||
% R = emqx_lwm2m_tlv:parse(Data),
|
|
||||||
% Exp = [
|
|
||||||
% #{tlv_multiple_resource => 16#00, value => [
|
|
||||||
% #{tlv_resource_instance => 16#00, value => <<16#00, 16#42, 16#00, 16#00>>},
|
|
||||||
% #{tlv_resource_instance => 16#01, value => <<16#00, 16#42, 16#00, 16#01>>}
|
|
||||||
% ]},
|
|
||||||
% #{tlv_resource_with_value => 16#01, value => <<"8613800755500">>},
|
|
||||||
% #{tlv_resource_with_value => 16#02, value => <<16#12345678:32>>}
|
|
||||||
% ],
|
|
||||||
% ?assertEqual(Exp, R),
|
|
||||||
% EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
|
||||||
% ?assertEqual(EncodedBinary, Data).
|
|
||||||
|
|
||||||
% case09(_Config) ->
|
|
||||||
% % 6.4.3.3 Example of Request on an Object Instance containing an Object Link Resource
|
|
||||||
% % Example 2) request to Object 66: Read /66: TLV payload will contain 2 Object Instances
|
|
||||||
% Data = <<16#08, 16#00, 16#26, 16#C8, 16#00, 16#0B, 16#6D, 16#79, 16#53, 16#65, 16#72, 16#76, 16#69, 16#63, 16#65, 16#20, 16#31, 16#C8, 16#01, 16#0F, 16#49, 16#6E, 16#74, 16#65, 16#72, 16#6E, 16#65, 16#74, 16#2E, 16#31, 16#35, 16#2E, 16#32, 16#33, 16#34, 16#C4, 16#02, 16#00, 16#43, 16#00, 16#00, 16#08, 16#01, 16#26, 16#C8, 16#00, 16#0B, 16#6D, 16#79, 16#53, 16#65, 16#72, 16#76, 16#69, 16#63, 16#65, 16#20, 16#32, 16#C8, 16#01, 16#0F, 16#49, 16#6E, 16#74, 16#65, 16#72, 16#6E, 16#65, 16#74, 16#2E, 16#31, 16#35, 16#2E, 16#32, 16#33, 16#35, 16#C4, 16#02, 16#FF, 16#FF, 16#FF, 16#FF>>,
|
|
||||||
% R = emqx_lwm2m_tlv:parse(Data),
|
|
||||||
% Exp = [
|
|
||||||
% #{tlv_object_instance => 16#00, value => [
|
|
||||||
% #{tlv_resource_with_value => 16#00, value => <<"myService 1">>},
|
|
||||||
% #{tlv_resource_with_value => 16#01, value => <<"Internet.15.234">>},
|
|
||||||
% #{tlv_resource_with_value => 16#02, value => <<16#00, 16#43, 16#00, 16#00>>}
|
|
||||||
% ]},
|
|
||||||
% #{tlv_object_instance => 16#01, value => [
|
|
||||||
% #{tlv_resource_with_value => 16#00, value => <<"myService 2">>},
|
|
||||||
% #{tlv_resource_with_value => 16#01, value => <<"Internet.15.235">>},
|
|
||||||
% #{tlv_resource_with_value => 16#02, value => <<16#FF, 16#FF, 16#FF, 16#FF>>}
|
|
||||||
% ]}
|
|
||||||
% ],
|
|
||||||
% ?assertEqual(Exp, R),
|
|
||||||
% EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
|
||||||
% ?assertEqual(EncodedBinary, Data).
|
|
||||||
|
|
|
@ -1,171 +0,0 @@
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
||||||
%%
|
|
||||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
%% you may not use this file except in compliance with the License.
|
|
||||||
%% You may obtain a copy of the License at
|
|
||||||
%%
|
|
||||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
%%
|
|
||||||
%% Unless required by applicable law or agreed to in writing, software
|
|
||||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
%% See the License for the specific language governing permissions and
|
|
||||||
%% limitations under the License.
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(test_mqtt_broker).
|
|
||||||
|
|
||||||
% -compile(nowarn_export_all).
|
|
||||||
% -compile(export_all).
|
|
||||||
|
|
||||||
% -define(LOGT(Format, Args), logger:debug("TEST_BROKER: " ++ Format, Args)).
|
|
||||||
|
|
||||||
% -record(state, {subscriber}).
|
|
||||||
|
|
||||||
% -include_lib("emqx/include/emqx.hrl").
|
|
||||||
|
|
||||||
% -include_lib("emqx/include/emqx_mqtt.hrl").
|
|
||||||
|
|
||||||
% -include_lib("eunit/include/eunit.hrl").
|
|
||||||
|
|
||||||
% start(_, <<"attacker">>, _, _, _) ->
|
|
||||||
% {stop, auth_failure};
|
|
||||||
% start(ClientId, Username, Password, _Channel, KeepaliveInterval) ->
|
|
||||||
% true = is_binary(ClientId),
|
|
||||||
% (true = ( is_binary(Username)) orelse (Username == undefined) ),
|
|
||||||
% (true = ( is_binary(Password)) orelse (Password == undefined) ),
|
|
||||||
% self() ! {keepalive, start, KeepaliveInterval},
|
|
||||||
% {ok, []}.
|
|
||||||
|
|
||||||
% publish(Topic, Payload, Qos) ->
|
|
||||||
% ClientId = <<"lwm2m_test_suite">>,
|
|
||||||
% Msg = emqx_message:make(ClientId, Qos, Topic, Payload),
|
|
||||||
% emqx:publish(Msg).
|
|
||||||
|
|
||||||
% subscribe(Topic) ->
|
|
||||||
% gen_server:call(?MODULE, {subscribe, Topic, self()}).
|
|
||||||
|
|
||||||
% unsubscribe(Topic) ->
|
|
||||||
% gen_server:call(?MODULE, {unsubscribe, Topic}).
|
|
||||||
|
|
||||||
% get_subscrbied_topics() ->
|
|
||||||
% [Topic || {_Client, Topic} <- ets:tab2list(emqx_subscription)].
|
|
||||||
|
|
||||||
% start_link() ->
|
|
||||||
% gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
|
||||||
|
|
||||||
% stop() ->
|
|
||||||
% gen_server:stop(?MODULE).
|
|
||||||
|
|
||||||
% init(_Param) ->
|
|
||||||
% {ok, #state{subscriber = []}}.
|
|
||||||
|
|
||||||
% handle_call({subscribe, Topic, Proc}, _From, State=#state{subscriber = SubList}) ->
|
|
||||||
% ?LOGT("test broker subscribe Topic=~p, Pid=~p~n", [Topic, Proc]),
|
|
||||||
% is_binary(Topic) orelse error("Topic should be a binary"),
|
|
||||||
% {reply, {ok, []}, State#state{subscriber = [{Topic, Proc}|SubList]}};
|
|
||||||
|
|
||||||
% handle_call(get_subscribed_topics, _From, State=#state{subscriber = SubList}) ->
|
|
||||||
% Response = subscribed_topics(SubList, []),
|
|
||||||
% ?LOGT("test broker get subscribed topics=~p~n", [Response]),
|
|
||||||
% {reply, Response, State};
|
|
||||||
|
|
||||||
% handle_call({unsubscribe, Topic}, _From, State=#state{subscriber = SubList}) ->
|
|
||||||
% ?LOGT("test broker unsubscribe Topic=~p~n", [Topic]),
|
|
||||||
% is_binary(Topic) orelse error("Topic should be a binary"),
|
|
||||||
% NewSubList = proplists:delete(Topic, SubList),
|
|
||||||
% {reply, {ok, []}, State#state{subscriber = NewSubList}};
|
|
||||||
|
|
||||||
|
|
||||||
% handle_call({publish, {Topic, Msg, MatchedTopicFilter}}, _From, State=#state{subscriber = SubList}) ->
|
|
||||||
% (is_binary(Topic) and is_binary(Msg)) orelse error("Topic and Msg should be binary"),
|
|
||||||
% Pid = proplists:get_value(MatchedTopicFilter, SubList),
|
|
||||||
% ?LOGT("test broker publish topic=~p, Msg=~p, Pid=~p, MatchedTopicFilter=~p, SubList=~p~n", [Topic, Msg, Pid, MatchedTopicFilter, SubList]),
|
|
||||||
% (Pid == undefined) andalso ?LOGT("!!!!! this topic ~p has never been subscribed, please specify a valid topic filter", [MatchedTopicFilter]),
|
|
||||||
% ?assertNotEqual(undefined, Pid),
|
|
||||||
% Pid ! {deliver, #message{topic = Topic, payload = Msg}},
|
|
||||||
% {reply, ok, State};
|
|
||||||
|
|
||||||
% handle_call(stop, _From, State) ->
|
|
||||||
% {stop, normal, stopped, State};
|
|
||||||
|
|
||||||
% handle_call(Req, _From, State) ->
|
|
||||||
% ?LOGT("test_broker_server: ignore call Req=~p~n", [Req]),
|
|
||||||
% {reply, {error, badreq}, State}.
|
|
||||||
|
|
||||||
|
|
||||||
% handle_cast(Msg, State) ->
|
|
||||||
% ?LOGT("test_broker_server: ignore cast msg=~p~n", [Msg]),
|
|
||||||
% {noreply, State}.
|
|
||||||
|
|
||||||
% handle_info(Info, State) ->
|
|
||||||
% ?LOGT("test_broker_server: ignore info=~p~n", [Info]),
|
|
||||||
% {noreply, State}.
|
|
||||||
|
|
||||||
% terminate(Reason, _State) ->
|
|
||||||
% ?LOGT("test_broker_server: terminate Reason=~p~n", [Reason]),
|
|
||||||
% ok.
|
|
||||||
|
|
||||||
% code_change(_OldVsn, State, _Extra) ->
|
|
||||||
% {ok, State}.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
% subscribed_topics([], Acc) ->
|
|
||||||
% Acc;
|
|
||||||
% subscribed_topics([{Topic,_Pid}|T], Acc) ->
|
|
||||||
% subscribed_topics(T, [Topic|Acc]).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
% -record(keepalive, {statfun, statval, tsec, tmsg, tref, repeat = 0}).
|
|
||||||
|
|
||||||
% -type(keepalive() :: #keepalive{}).
|
|
||||||
|
|
||||||
% %% @doc Start a keepalive
|
|
||||||
% -spec(start(fun(), integer(), any()) -> undefined | keepalive()).
|
|
||||||
% start(_, 0, _) ->
|
|
||||||
% undefined;
|
|
||||||
% start(StatFun, TimeoutSec, TimeoutMsg) ->
|
|
||||||
% {ok, StatVal} = StatFun(),
|
|
||||||
% #keepalive{statfun = StatFun, statval = StatVal,
|
|
||||||
% tsec = TimeoutSec, tmsg = TimeoutMsg,
|
|
||||||
% tref = timer(TimeoutSec, TimeoutMsg)}.
|
|
||||||
|
|
||||||
% %% @doc Check keepalive, called when timeout.
|
|
||||||
% -spec(check(keepalive()) -> {ok, keepalive()} | {error, any()}).
|
|
||||||
% check(KeepAlive = #keepalive{statfun = StatFun, statval = LastVal, repeat = Repeat}) ->
|
|
||||||
% case StatFun() of
|
|
||||||
% {ok, NewVal} ->
|
|
||||||
% if NewVal =/= LastVal ->
|
|
||||||
% {ok, resume(KeepAlive#keepalive{statval = NewVal, repeat = 0})};
|
|
||||||
% Repeat < 1 ->
|
|
||||||
% {ok, resume(KeepAlive#keepalive{statval = NewVal, repeat = Repeat + 1})};
|
|
||||||
% true ->
|
|
||||||
% {error, timeout}
|
|
||||||
% end;
|
|
||||||
% {error, Error} ->
|
|
||||||
% {error, Error}
|
|
||||||
% end.
|
|
||||||
|
|
||||||
% resume(KeepAlive = #keepalive{tsec = TimeoutSec, tmsg = TimeoutMsg}) ->
|
|
||||||
% KeepAlive#keepalive{tref = timer(TimeoutSec, TimeoutMsg)}.
|
|
||||||
|
|
||||||
% %% @doc Cancel Keepalive
|
|
||||||
% -spec(cancel(keepalive()) -> ok).
|
|
||||||
% cancel(#keepalive{tref = TRef}) ->
|
|
||||||
% cancel(TRef);
|
|
||||||
% cancel(undefined) ->
|
|
||||||
% ok;
|
|
||||||
% cancel(TRef) ->
|
|
||||||
% catch erlang:cancel_timer(TRef).
|
|
||||||
|
|
||||||
% timer(Sec, Msg) ->
|
|
||||||
% erlang:send_after(timer:seconds(Sec), self(), Msg).
|
|
||||||
|
|
||||||
|
|
||||||
% log(Format, Args) ->
|
|
||||||
% logger:debug(Format, Args).
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,238 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%
|
||||||
|
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
%% you may not use this file except in compliance with the License.
|
||||||
|
%% You may obtain a copy of the License at
|
||||||
|
%%
|
||||||
|
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
%%
|
||||||
|
%% Unless required by applicable law or agreed to in writing, software
|
||||||
|
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
%% See the License for the specific language governing permissions and
|
||||||
|
%% limitations under the License.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(emqx_tlv_SUITE).
|
||||||
|
|
||||||
|
-compile(export_all).
|
||||||
|
-compile(nowarn_export_all).
|
||||||
|
|
||||||
|
-define(LOGT(Format, Args), logger:debug("TEST_SUITE: " ++ Format, Args)).
|
||||||
|
|
||||||
|
-include("src/lwm2m/include/emqx_lwm2m.hrl").
|
||||||
|
-include_lib("lwm2m_coap/include/coap.hrl").
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
|
||||||
|
all() -> [case01, case02, case03, case03_0, case04, case05, case06, case07, case08, case09].
|
||||||
|
|
||||||
|
init_per_suite(Config) ->
|
||||||
|
Config.
|
||||||
|
|
||||||
|
end_per_suite(Config) ->
|
||||||
|
Config.
|
||||||
|
|
||||||
|
|
||||||
|
case01(_Config) ->
|
||||||
|
Data = <<16#C8, 16#00, 16#14, 16#4F, 16#70, 16#65, 16#6E, 16#20, 16#4D, 16#6F, 16#62, 16#69, 16#6C, 16#65, 16#20, 16#41, 16#6C, 16#6C, 16#69, 16#61, 16#6E, 16#63, 16#65>>,
|
||||||
|
R = emqx_lwm2m_tlv:parse(Data),
|
||||||
|
Exp = [
|
||||||
|
#{tlv_resource_with_value => 16#00, value => <<"Open Mobile Alliance">>}
|
||||||
|
],
|
||||||
|
?assertEqual(Exp, R),
|
||||||
|
EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
||||||
|
?assertEqual(EncodedBinary, Data).
|
||||||
|
|
||||||
|
case02(_Config) ->
|
||||||
|
Data = <<16#86, 16#06, 16#41, 16#00, 16#01, 16#41, 16#01, 16#05>>,
|
||||||
|
R = emqx_lwm2m_tlv:parse(Data),
|
||||||
|
Exp = [
|
||||||
|
#{tlv_multiple_resource => 16#06, value => [
|
||||||
|
#{tlv_resource_instance => 16#00, value => <<1>>},
|
||||||
|
#{tlv_resource_instance => 16#01, value => <<5>>}
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
?assertEqual(Exp, R),
|
||||||
|
EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
||||||
|
?assertEqual(EncodedBinary, Data).
|
||||||
|
|
||||||
|
case03(_Config) ->
|
||||||
|
Data = <<16#C8, 16#00, 16#14, 16#4F, 16#70, 16#65, 16#6E, 16#20, 16#4D, 16#6F, 16#62, 16#69, 16#6C, 16#65, 16#20, 16#41, 16#6C, 16#6C, 16#69, 16#61, 16#6E, 16#63, 16#65, 16#C8, 16#01, 16#16, 16#4C, 16#69, 16#67, 16#68, 16#74, 16#77, 16#65, 16#69, 16#67, 16#68, 16#74, 16#20, 16#4D, 16#32, 16#4D, 16#20, 16#43, 16#6C, 16#69, 16#65, 16#6E, 16#74, 16#C8, 16#02, 16#09, 16#33, 16#34, 16#35, 16#30, 16#30, 16#30, 16#31, 16#32, 16#33>>,
|
||||||
|
R = emqx_lwm2m_tlv:parse(Data),
|
||||||
|
Exp = [
|
||||||
|
#{tlv_resource_with_value => 16#00, value => <<"Open Mobile Alliance">>},
|
||||||
|
#{tlv_resource_with_value => 16#01, value => <<"Lightweight M2M Client">>},
|
||||||
|
#{tlv_resource_with_value => 16#02, value => <<"345000123">>}
|
||||||
|
],
|
||||||
|
?assertEqual(Exp, R),
|
||||||
|
EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
||||||
|
?assertEqual(EncodedBinary, Data).
|
||||||
|
|
||||||
|
case03_0(_Config) ->
|
||||||
|
Data = <<16#87, 16#02, 16#41, 16#7F, 16#07, 16#61, 16#01, 16#36, 16#01>>,
|
||||||
|
R = emqx_lwm2m_tlv:parse(Data),
|
||||||
|
Exp = [
|
||||||
|
#{tlv_multiple_resource => 16#02, value => [
|
||||||
|
#{tlv_resource_instance => 16#7F, value => <<16#07>>},
|
||||||
|
#{tlv_resource_instance => 16#0136, value => <<16#01>>}
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
?assertEqual(Exp, R),
|
||||||
|
EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
||||||
|
?assertEqual(EncodedBinary, Data).
|
||||||
|
|
||||||
|
case04(_Config) ->
|
||||||
|
% 6.4.3.1 Single Object Instance Request Example
|
||||||
|
Data = <<16#C8, 16#00, 16#14, 16#4F, 16#70, 16#65, 16#6E, 16#20, 16#4D, 16#6F, 16#62, 16#69, 16#6C, 16#65, 16#20, 16#41, 16#6C, 16#6C, 16#69, 16#61, 16#6E, 16#63, 16#65, 16#C8, 16#01, 16#16, 16#4C, 16#69, 16#67, 16#68, 16#74, 16#77, 16#65, 16#69, 16#67, 16#68, 16#74, 16#20, 16#4D, 16#32, 16#4D, 16#20, 16#43, 16#6C, 16#69, 16#65, 16#6E, 16#74, 16#C8, 16#02, 16#09, 16#33, 16#34, 16#35, 16#30, 16#30, 16#30, 16#31, 16#32, 16#33, 16#C3, 16#03, 16#31, 16#2E, 16#30, 16#86, 16#06, 16#41, 16#00, 16#01, 16#41, 16#01, 16#05, 16#88, 16#07, 16#08, 16#42, 16#00, 16#0E, 16#D8, 16#42, 16#01, 16#13, 16#88, 16#87, 16#08, 16#41, 16#00, 16#7D, 16#42, 16#01, 16#03, 16#84, 16#C1, 16#09, 16#64, 16#C1, 16#0A, 16#0F, 16#83, 16#0B, 16#41, 16#00, 16#00, 16#C4, 16#0D, 16#51, 16#82, 16#42, 16#8F, 16#C6, 16#0E, 16#2B, 16#30, 16#32, 16#3A, 16#30, 16#30, 16#C1, 16#10, 16#55>>,
|
||||||
|
R = emqx_lwm2m_tlv:parse(Data),
|
||||||
|
Exp = [
|
||||||
|
#{tlv_resource_with_value => 16#00, value => <<"Open Mobile Alliance">>},
|
||||||
|
#{tlv_resource_with_value => 16#01, value => <<"Lightweight M2M Client">>},
|
||||||
|
#{tlv_resource_with_value => 16#02, value => <<"345000123">>},
|
||||||
|
#{tlv_resource_with_value => 16#03, value => <<"1.0">>},
|
||||||
|
#{tlv_multiple_resource => 16#06, value => [
|
||||||
|
#{tlv_resource_instance => 16#00, value => <<1>>},
|
||||||
|
#{tlv_resource_instance => 16#01, value => <<5>>}
|
||||||
|
]},
|
||||||
|
#{tlv_multiple_resource => 16#07, value => [
|
||||||
|
#{tlv_resource_instance => 16#00, value => <<16#0ED8:16>>},
|
||||||
|
#{tlv_resource_instance => 16#01, value => <<16#1388:16>>}
|
||||||
|
]},
|
||||||
|
#{tlv_multiple_resource => 16#08, value => [
|
||||||
|
#{tlv_resource_instance => 16#00, value => <<16#7d>>},
|
||||||
|
#{tlv_resource_instance => 16#01, value => <<16#0384:16>>}
|
||||||
|
]},
|
||||||
|
#{tlv_resource_with_value => 16#09, value => <<16#64>>},
|
||||||
|
#{tlv_resource_with_value => 16#0A, value => <<16#0F>>},
|
||||||
|
#{tlv_multiple_resource => 16#0B, value => [
|
||||||
|
#{tlv_resource_instance => 16#00, value => <<16#00>>}
|
||||||
|
]},
|
||||||
|
#{tlv_resource_with_value => 16#0D, value => <<16#5182428F:32>>},
|
||||||
|
#{tlv_resource_with_value => 16#0E, value => <<"+02:00">>},
|
||||||
|
#{tlv_resource_with_value => 16#10, value => <<"U">>}
|
||||||
|
],
|
||||||
|
?assertEqual(Exp, R),
|
||||||
|
EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
||||||
|
?assertEqual(EncodedBinary, Data).
|
||||||
|
|
||||||
|
case05(_Config) ->
|
||||||
|
% 6.4.3.2 Multiple Object Instance Request Examples
|
||||||
|
% A) Request on Single-Instance Object
|
||||||
|
Data = <<16#08, 16#00, 16#79, 16#C8, 16#00, 16#14, 16#4F, 16#70, 16#65, 16#6E, 16#20, 16#4D, 16#6F, 16#62, 16#69, 16#6C, 16#65, 16#20, 16#41, 16#6C, 16#6C, 16#69, 16#61, 16#6E, 16#63, 16#65, 16#C8, 16#01, 16#16, 16#4C, 16#69, 16#67, 16#68, 16#74, 16#77, 16#65, 16#69, 16#67, 16#68, 16#74, 16#20, 16#4D, 16#32, 16#4D, 16#20, 16#43, 16#6C, 16#69, 16#65, 16#6E, 16#74, 16#C8, 16#02, 16#09, 16#33, 16#34, 16#35, 16#30, 16#30, 16#30, 16#31, 16#32, 16#33, 16#C3, 16#03, 16#31, 16#2E, 16#30, 16#86, 16#06, 16#41, 16#00, 16#01, 16#41, 16#01, 16#05, 16#88, 16#07, 16#08, 16#42, 16#00, 16#0E, 16#D8, 16#42, 16#01, 16#13, 16#88, 16#87, 16#08, 16#41, 16#00, 16#7D, 16#42, 16#01, 16#03, 16#84, 16#C1, 16#09, 16#64, 16#C1, 16#0A, 16#0F, 16#83, 16#0B, 16#41, 16#00, 16#00, 16#C4, 16#0D, 16#51, 16#82, 16#42, 16#8F, 16#C6, 16#0E, 16#2B, 16#30, 16#32, 16#3A, 16#30, 16#30, 16#C1, 16#10, 16#55>>,
|
||||||
|
R = emqx_lwm2m_tlv:parse(Data),
|
||||||
|
Exp = [
|
||||||
|
#{tlv_object_instance => 16#00, value => [
|
||||||
|
#{tlv_resource_with_value => 16#00, value => <<"Open Mobile Alliance">>},
|
||||||
|
#{tlv_resource_with_value => 16#01, value => <<"Lightweight M2M Client">>},
|
||||||
|
#{tlv_resource_with_value => 16#02, value => <<"345000123">>},
|
||||||
|
#{tlv_resource_with_value => 16#03, value => <<"1.0">>},
|
||||||
|
#{tlv_multiple_resource => 16#06, value => [
|
||||||
|
#{tlv_resource_instance => 16#00, value => <<1>>},
|
||||||
|
#{tlv_resource_instance => 16#01, value => <<5>>}
|
||||||
|
]},
|
||||||
|
#{tlv_multiple_resource => 16#07, value => [
|
||||||
|
#{tlv_resource_instance => 16#00, value => <<16#0ED8:16>>},
|
||||||
|
#{tlv_resource_instance => 16#01, value => <<16#1388:16>>}
|
||||||
|
]},
|
||||||
|
#{tlv_multiple_resource => 16#08, value => [
|
||||||
|
#{tlv_resource_instance => 16#00, value => <<16#7d>>},
|
||||||
|
#{tlv_resource_instance => 16#01, value => <<16#0384:16>>}
|
||||||
|
]},
|
||||||
|
#{tlv_resource_with_value => 16#09, value => <<16#64>>},
|
||||||
|
#{tlv_resource_with_value => 16#0A, value => <<16#0F>>},
|
||||||
|
#{tlv_multiple_resource => 16#0B, value => [
|
||||||
|
#{tlv_resource_instance => 16#00, value => <<16#00>>}
|
||||||
|
]},
|
||||||
|
#{tlv_resource_with_value => 16#0D, value => <<16#5182428F:32>>},
|
||||||
|
#{tlv_resource_with_value => 16#0E, value => <<"+02:00">>},
|
||||||
|
#{tlv_resource_with_value => 16#10, value => <<"U">>}
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
?assertEqual(Exp, R),
|
||||||
|
EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
||||||
|
?assertEqual(EncodedBinary, Data).
|
||||||
|
|
||||||
|
case06(_Config) ->
|
||||||
|
% 6.4.3.2 Multiple Object Instance Request Examples
|
||||||
|
% B) Request on Multiple-Instances Object having 2 instances
|
||||||
|
Data = <<16#08, 16#00, 16#0E, 16#C1, 16#00, 16#01, 16#C1, 16#01, 16#00, 16#83, 16#02, 16#41, 16#7F, 16#07, 16#C1, 16#03, 16#7F, 16#08, 16#02, 16#12, 16#C1, 16#00, 16#03, 16#C1, 16#01, 16#00, 16#87, 16#02, 16#41, 16#7F, 16#07, 16#61, 16#01, 16#36, 16#01, 16#C1, 16#03, 16#7F>>,
|
||||||
|
R = emqx_lwm2m_tlv:parse(Data),
|
||||||
|
Exp = [
|
||||||
|
#{tlv_object_instance => 16#00, value => [
|
||||||
|
#{tlv_resource_with_value => 16#00, value => <<16#01>>},
|
||||||
|
#{tlv_resource_with_value => 16#01, value => <<16#00>>},
|
||||||
|
#{tlv_multiple_resource => 16#02, value => [
|
||||||
|
#{tlv_resource_instance => 16#7F, value => <<16#07>>}
|
||||||
|
]},
|
||||||
|
#{tlv_resource_with_value => 16#03, value => <<16#7F>>}
|
||||||
|
]},
|
||||||
|
#{tlv_object_instance => 16#02, value => [
|
||||||
|
#{tlv_resource_with_value => 16#00, value => <<16#03>>},
|
||||||
|
#{tlv_resource_with_value => 16#01, value => <<16#00>>},
|
||||||
|
#{tlv_multiple_resource => 16#02, value => [
|
||||||
|
#{tlv_resource_instance => 16#7F, value => <<16#07>>},
|
||||||
|
#{tlv_resource_instance => 16#0136, value => <<16#01>>}
|
||||||
|
]},
|
||||||
|
#{tlv_resource_with_value => 16#03, value => <<16#7F>>}
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
?assertEqual(Exp, R),
|
||||||
|
EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
||||||
|
?assertEqual(EncodedBinary, Data).
|
||||||
|
|
||||||
|
case07(_Config) ->
|
||||||
|
% 6.4.3.2 Multiple Object Instance Request Examples
|
||||||
|
% C) Request on Multiple-Instances Object having 1 instance only
|
||||||
|
Data = <<16#08, 16#00, 16#0F, 16#C1, 16#00, 16#01, 16#C4, 16#01, 16#00, 16#01, 16#51, 16#80, 16#C1, 16#06, 16#01, 16#C1, 16#07, 16#55>>,
|
||||||
|
R = emqx_lwm2m_tlv:parse(Data),
|
||||||
|
Exp = [
|
||||||
|
#{tlv_object_instance => 16#00, value => [
|
||||||
|
#{tlv_resource_with_value => 16#00, value => <<16#01>>},
|
||||||
|
#{tlv_resource_with_value => 16#01, value => <<86400:32>>},
|
||||||
|
#{tlv_resource_with_value => 16#06, value => <<16#01>>},
|
||||||
|
#{tlv_resource_with_value => 16#07, value => <<$U>>}]}
|
||||||
|
],
|
||||||
|
?assertEqual(Exp, R),
|
||||||
|
EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
||||||
|
?assertEqual(EncodedBinary, Data).
|
||||||
|
|
||||||
|
case08(_Config) ->
|
||||||
|
% 6.4.3.3 Example of Request on an Object Instance containing an Object Link Resource
|
||||||
|
% Example 1) request to Object 65 Instance 0: Read /65/0
|
||||||
|
Data = <<16#88, 16#00, 16#0C, 16#44, 16#00, 16#00, 16#42, 16#00, 16#00, 16#44, 16#01, 16#00, 16#42, 16#00, 16#01, 16#C8, 16#01, 16#0D, 16#38, 16#36, 16#31, 16#33, 16#38, 16#30, 16#30, 16#37, 16#35, 16#35, 16#35, 16#30, 16#30, 16#C4, 16#02, 16#12, 16#34, 16#56, 16#78>>,
|
||||||
|
R = emqx_lwm2m_tlv:parse(Data),
|
||||||
|
Exp = [
|
||||||
|
#{tlv_multiple_resource => 16#00, value => [
|
||||||
|
#{tlv_resource_instance => 16#00, value => <<16#00, 16#42, 16#00, 16#00>>},
|
||||||
|
#{tlv_resource_instance => 16#01, value => <<16#00, 16#42, 16#00, 16#01>>}
|
||||||
|
]},
|
||||||
|
#{tlv_resource_with_value => 16#01, value => <<"8613800755500">>},
|
||||||
|
#{tlv_resource_with_value => 16#02, value => <<16#12345678:32>>}
|
||||||
|
],
|
||||||
|
?assertEqual(Exp, R),
|
||||||
|
EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
||||||
|
?assertEqual(EncodedBinary, Data).
|
||||||
|
|
||||||
|
case09(_Config) ->
|
||||||
|
% 6.4.3.3 Example of Request on an Object Instance containing an Object Link Resource
|
||||||
|
% Example 2) request to Object 66: Read /66: TLV payload will contain 2 Object Instances
|
||||||
|
Data = <<16#08, 16#00, 16#26, 16#C8, 16#00, 16#0B, 16#6D, 16#79, 16#53, 16#65, 16#72, 16#76, 16#69, 16#63, 16#65, 16#20, 16#31, 16#C8, 16#01, 16#0F, 16#49, 16#6E, 16#74, 16#65, 16#72, 16#6E, 16#65, 16#74, 16#2E, 16#31, 16#35, 16#2E, 16#32, 16#33, 16#34, 16#C4, 16#02, 16#00, 16#43, 16#00, 16#00, 16#08, 16#01, 16#26, 16#C8, 16#00, 16#0B, 16#6D, 16#79, 16#53, 16#65, 16#72, 16#76, 16#69, 16#63, 16#65, 16#20, 16#32, 16#C8, 16#01, 16#0F, 16#49, 16#6E, 16#74, 16#65, 16#72, 16#6E, 16#65, 16#74, 16#2E, 16#31, 16#35, 16#2E, 16#32, 16#33, 16#35, 16#C4, 16#02, 16#FF, 16#FF, 16#FF, 16#FF>>,
|
||||||
|
R = emqx_lwm2m_tlv:parse(Data),
|
||||||
|
Exp = [
|
||||||
|
#{tlv_object_instance => 16#00, value => [
|
||||||
|
#{tlv_resource_with_value => 16#00, value => <<"myService 1">>},
|
||||||
|
#{tlv_resource_with_value => 16#01, value => <<"Internet.15.234">>},
|
||||||
|
#{tlv_resource_with_value => 16#02, value => <<16#00, 16#43, 16#00, 16#00>>}
|
||||||
|
]},
|
||||||
|
#{tlv_object_instance => 16#01, value => [
|
||||||
|
#{tlv_resource_with_value => 16#00, value => <<"myService 2">>},
|
||||||
|
#{tlv_resource_with_value => 16#01, value => <<"Internet.15.235">>},
|
||||||
|
#{tlv_resource_with_value => 16#02, value => <<16#FF, 16#FF, 16#FF, 16#FF>>}
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
?assertEqual(Exp, R),
|
||||||
|
EncodedBinary = emqx_lwm2m_tlv:encode(Exp),
|
||||||
|
?assertEqual(EncodedBinary, Data).
|
||||||
|
|
|
@ -0,0 +1,171 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%
|
||||||
|
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
%% you may not use this file except in compliance with the License.
|
||||||
|
%% You may obtain a copy of the License at
|
||||||
|
%%
|
||||||
|
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
%%
|
||||||
|
%% Unless required by applicable law or agreed to in writing, software
|
||||||
|
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
%% See the License for the specific language governing permissions and
|
||||||
|
%% limitations under the License.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(test_mqtt_broker).
|
||||||
|
|
||||||
|
-compile(nowarn_export_all).
|
||||||
|
-compile(export_all).
|
||||||
|
|
||||||
|
-define(LOGT(Format, Args), logger:debug("TEST_BROKER: " ++ Format, Args)).
|
||||||
|
|
||||||
|
-record(state, {subscriber}).
|
||||||
|
|
||||||
|
-include_lib("emqx/include/emqx.hrl").
|
||||||
|
|
||||||
|
-include_lib("emqx/include/emqx_mqtt.hrl").
|
||||||
|
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
start(_, <<"attacker">>, _, _, _) ->
|
||||||
|
{stop, auth_failure};
|
||||||
|
start(ClientId, Username, Password, _Channel, KeepaliveInterval) ->
|
||||||
|
true = is_binary(ClientId),
|
||||||
|
(true = ( is_binary(Username)) orelse (Username == undefined) ),
|
||||||
|
(true = ( is_binary(Password)) orelse (Password == undefined) ),
|
||||||
|
self() ! {keepalive, start, KeepaliveInterval},
|
||||||
|
{ok, []}.
|
||||||
|
|
||||||
|
publish(Topic, Payload, Qos) ->
|
||||||
|
ClientId = <<"lwm2m_test_suite">>,
|
||||||
|
Msg = emqx_message:make(ClientId, Qos, Topic, Payload),
|
||||||
|
emqx:publish(Msg).
|
||||||
|
|
||||||
|
subscribe(Topic) ->
|
||||||
|
gen_server:call(?MODULE, {subscribe, Topic, self()}).
|
||||||
|
|
||||||
|
unsubscribe(Topic) ->
|
||||||
|
gen_server:call(?MODULE, {unsubscribe, Topic}).
|
||||||
|
|
||||||
|
get_subscrbied_topics() ->
|
||||||
|
[Topic || {_Client, Topic} <- ets:tab2list(emqx_subscription)].
|
||||||
|
|
||||||
|
start_link() ->
|
||||||
|
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||||
|
|
||||||
|
stop() ->
|
||||||
|
gen_server:stop(?MODULE).
|
||||||
|
|
||||||
|
init(_Param) ->
|
||||||
|
{ok, #state{subscriber = []}}.
|
||||||
|
|
||||||
|
handle_call({subscribe, Topic, Proc}, _From, State=#state{subscriber = SubList}) ->
|
||||||
|
?LOGT("test broker subscribe Topic=~p, Pid=~p~n", [Topic, Proc]),
|
||||||
|
is_binary(Topic) orelse error("Topic should be a binary"),
|
||||||
|
{reply, {ok, []}, State#state{subscriber = [{Topic, Proc}|SubList]}};
|
||||||
|
|
||||||
|
handle_call(get_subscribed_topics, _From, State=#state{subscriber = SubList}) ->
|
||||||
|
Response = subscribed_topics(SubList, []),
|
||||||
|
?LOGT("test broker get subscribed topics=~p~n", [Response]),
|
||||||
|
{reply, Response, State};
|
||||||
|
|
||||||
|
handle_call({unsubscribe, Topic}, _From, State=#state{subscriber = SubList}) ->
|
||||||
|
?LOGT("test broker unsubscribe Topic=~p~n", [Topic]),
|
||||||
|
is_binary(Topic) orelse error("Topic should be a binary"),
|
||||||
|
NewSubList = proplists:delete(Topic, SubList),
|
||||||
|
{reply, {ok, []}, State#state{subscriber = NewSubList}};
|
||||||
|
|
||||||
|
|
||||||
|
handle_call({publish, {Topic, Msg, MatchedTopicFilter}}, _From, State=#state{subscriber = SubList}) ->
|
||||||
|
(is_binary(Topic) and is_binary(Msg)) orelse error("Topic and Msg should be binary"),
|
||||||
|
Pid = proplists:get_value(MatchedTopicFilter, SubList),
|
||||||
|
?LOGT("test broker publish topic=~p, Msg=~p, Pid=~p, MatchedTopicFilter=~p, SubList=~p~n", [Topic, Msg, Pid, MatchedTopicFilter, SubList]),
|
||||||
|
(Pid == undefined) andalso ?LOGT("!!!!! this topic ~p has never been subscribed, please specify a valid topic filter", [MatchedTopicFilter]),
|
||||||
|
?assertNotEqual(undefined, Pid),
|
||||||
|
Pid ! {deliver, #message{topic = Topic, payload = Msg}},
|
||||||
|
{reply, ok, State};
|
||||||
|
|
||||||
|
handle_call(stop, _From, State) ->
|
||||||
|
{stop, normal, stopped, State};
|
||||||
|
|
||||||
|
handle_call(Req, _From, State) ->
|
||||||
|
?LOGT("test_broker_server: ignore call Req=~p~n", [Req]),
|
||||||
|
{reply, {error, badreq}, State}.
|
||||||
|
|
||||||
|
|
||||||
|
handle_cast(Msg, State) ->
|
||||||
|
?LOGT("test_broker_server: ignore cast msg=~p~n", [Msg]),
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
handle_info(Info, State) ->
|
||||||
|
?LOGT("test_broker_server: ignore info=~p~n", [Info]),
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
terminate(Reason, _State) ->
|
||||||
|
?LOGT("test_broker_server: terminate Reason=~p~n", [Reason]),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
|
{ok, State}.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
subscribed_topics([], Acc) ->
|
||||||
|
Acc;
|
||||||
|
subscribed_topics([{Topic,_Pid}|T], Acc) ->
|
||||||
|
subscribed_topics(T, [Topic|Acc]).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-record(keepalive, {statfun, statval, tsec, tmsg, tref, repeat = 0}).
|
||||||
|
|
||||||
|
-type(keepalive() :: #keepalive{}).
|
||||||
|
|
||||||
|
%% @doc Start a keepalive
|
||||||
|
-spec(start(fun(), integer(), any()) -> undefined | keepalive()).
|
||||||
|
start(_, 0, _) ->
|
||||||
|
undefined;
|
||||||
|
start(StatFun, TimeoutSec, TimeoutMsg) ->
|
||||||
|
{ok, StatVal} = StatFun(),
|
||||||
|
#keepalive{statfun = StatFun, statval = StatVal,
|
||||||
|
tsec = TimeoutSec, tmsg = TimeoutMsg,
|
||||||
|
tref = timer(TimeoutSec, TimeoutMsg)}.
|
||||||
|
|
||||||
|
%% @doc Check keepalive, called when timeout.
|
||||||
|
-spec(check(keepalive()) -> {ok, keepalive()} | {error, any()}).
|
||||||
|
check(KeepAlive = #keepalive{statfun = StatFun, statval = LastVal, repeat = Repeat}) ->
|
||||||
|
case StatFun() of
|
||||||
|
{ok, NewVal} ->
|
||||||
|
if NewVal =/= LastVal ->
|
||||||
|
{ok, resume(KeepAlive#keepalive{statval = NewVal, repeat = 0})};
|
||||||
|
Repeat < 1 ->
|
||||||
|
{ok, resume(KeepAlive#keepalive{statval = NewVal, repeat = Repeat + 1})};
|
||||||
|
true ->
|
||||||
|
{error, timeout}
|
||||||
|
end;
|
||||||
|
{error, Error} ->
|
||||||
|
{error, Error}
|
||||||
|
end.
|
||||||
|
|
||||||
|
resume(KeepAlive = #keepalive{tsec = TimeoutSec, tmsg = TimeoutMsg}) ->
|
||||||
|
KeepAlive#keepalive{tref = timer(TimeoutSec, TimeoutMsg)}.
|
||||||
|
|
||||||
|
%% @doc Cancel Keepalive
|
||||||
|
-spec(cancel(keepalive()) -> ok).
|
||||||
|
cancel(#keepalive{tref = TRef}) ->
|
||||||
|
cancel(TRef);
|
||||||
|
cancel(undefined) ->
|
||||||
|
ok;
|
||||||
|
cancel(TRef) ->
|
||||||
|
catch erlang:cancel_timer(TRef).
|
||||||
|
|
||||||
|
timer(Sec, Msg) ->
|
||||||
|
erlang:send_after(timer:seconds(Sec), self(), Msg).
|
||||||
|
|
||||||
|
|
||||||
|
log(Format, Args) ->
|
||||||
|
logger:debug(Format, Args).
|
|
@ -346,6 +346,7 @@ relx_overlay(ReleaseType) ->
|
||||||
, {copy, "bin/emqx", "bin/emqx-{{release_version}}"} %% for relup
|
, {copy, "bin/emqx", "bin/emqx-{{release_version}}"} %% for relup
|
||||||
, {copy, "bin/emqx_ctl", "bin/emqx_ctl-{{release_version}}"} %% for relup
|
, {copy, "bin/emqx_ctl", "bin/emqx_ctl-{{release_version}}"} %% for relup
|
||||||
, {copy, "bin/install_upgrade.escript", "bin/install_upgrade.escript-{{release_version}}"} %% for relup
|
, {copy, "bin/install_upgrade.escript", "bin/install_upgrade.escript-{{release_version}}"} %% for relup
|
||||||
|
, {copy, "apps/emqx_gateway/src/lwm2m/lwm2m_xml", "etc/lwm2m_xml"}
|
||||||
, {template, "bin/emqx.cmd", "bin/emqx.cmd"}
|
, {template, "bin/emqx.cmd", "bin/emqx.cmd"}
|
||||||
, {template, "bin/emqx_ctl.cmd", "bin/emqx_ctl.cmd"}
|
, {template, "bin/emqx_ctl.cmd", "bin/emqx_ctl.cmd"}
|
||||||
, {copy, "bin/nodetool", "bin/nodetool"}
|
, {copy, "bin/nodetool", "bin/nodetool"}
|
||||||
|
|
Loading…
Reference in New Issue