From b16cf44bf63a9fcb98664d5100a426222124780d Mon Sep 17 00:00:00 2001 From: JianBo He Date: Mon, 2 Aug 2021 09:30:54 +0800 Subject: [PATCH] refactor(gw-lwm2m): refine lwm2m --- apps/emqx_gateway/etc/emqx_gateway.conf | 30 + apps/emqx_gateway/src/emqx_gateway.app.src | 2 +- apps/emqx_gateway/src/emqx_gateway_app.erl | 9 +- apps/emqx_gateway/src/emqx_gateway_schema.erl | 24 +- .../src/lwm2m/emqx_lwm2m_coap_resource.erl | 9 +- .../src/lwm2m/emqx_lwm2m_impl.erl | 173 ++ .../src/lwm2m/emqx_lwm2m_protocol.erl | 38 +- .../src/lwm2m/emqx_lwm2m_xml_object_db.erl | 12 +- .../src/lwm2m/test/emqx_lwm2m_SUITE.erl | 1953 ---------------- .../src/lwm2m/test/emqx_tlv_SUITE.erl | 240 -- .../src/lwm2m/test/test_mqtt_broker.erl | 171 -- apps/emqx_gateway/test/emqx_lwm2m_SUITE.erl | 1971 +++++++++++++++++ apps/emqx_gateway/test/emqx_tlv_SUITE.erl | 238 ++ apps/emqx_gateway/test/test_mqtt_broker.erl | 171 ++ rebar.config.erl | 1 + 15 files changed, 2651 insertions(+), 2391 deletions(-) create mode 100644 apps/emqx_gateway/src/lwm2m/emqx_lwm2m_impl.erl delete mode 100644 apps/emqx_gateway/src/lwm2m/test/emqx_lwm2m_SUITE.erl delete mode 100644 apps/emqx_gateway/src/lwm2m/test/emqx_tlv_SUITE.erl delete mode 100644 apps/emqx_gateway/src/lwm2m/test/test_mqtt_broker.erl create mode 100644 apps/emqx_gateway/test/emqx_lwm2m_SUITE.erl create mode 100644 apps/emqx_gateway/test/emqx_tlv_SUITE.erl create mode 100644 apps/emqx_gateway/test/test_mqtt_broker.erl diff --git a/apps/emqx_gateway/etc/emqx_gateway.conf b/apps/emqx_gateway/etc/emqx_gateway.conf index cdb776af1..b27e723a9 100644 --- a/apps/emqx_gateway/etc/emqx_gateway.conf +++ b/apps/emqx_gateway/etc/emqx_gateway.conf @@ -127,4 +127,34 @@ gateway: { #listener.udp.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 + } + } } diff --git a/apps/emqx_gateway/src/emqx_gateway.app.src b/apps/emqx_gateway/src/emqx_gateway.app.src index 541f31a23..e84ae0f7f 100644 --- a/apps/emqx_gateway/src/emqx_gateway.app.src +++ b/apps/emqx_gateway/src/emqx_gateway.app.src @@ -3,7 +3,7 @@ {vsn, "0.1.0"}, {registered, []}, {mod, {emqx_gateway_app, []}}, - {applications, [kernel, stdlib, grpc]}, + {applications, [kernel, stdlib, grpc, lwm2m_coap]}, {env, []}, {modules, []}, {licenses, ["Apache 2.0"]}, diff --git a/apps/emqx_gateway/src/emqx_gateway_app.erl b/apps/emqx_gateway/src/emqx_gateway_app.erl index 582a3d26a..f4918e75d 100644 --- a/apps/emqx_gateway/src/emqx_gateway_app.erl +++ b/apps/emqx_gateway/src/emqx_gateway_app.erl @@ -44,7 +44,8 @@ load_default_gateway_applications() -> gateway_type_searching() -> %% 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) -> try @@ -81,10 +82,14 @@ create_gateway_by_default([{Type, Name, Confs}|More]) -> create_gateway_by_default(More). 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( fun({Type, Gws}, Acc) -> {Names, Confs} = lists:unzip(maps:to_list(Gws)), Types = [ Type || _ <- lists:seq(1, length(Names))], [lists:zip3(Types, Names, Confs) | Acc] end, [], All)). + +exclude_options() -> + [lwm2m_xml_dir]. diff --git a/apps/emqx_gateway/src/emqx_gateway_schema.erl b/apps/emqx_gateway/src/emqx_gateway_schema.erl index f5f2b9ab0..51b0f182b 100644 --- a/apps/emqx_gateway/src/emqx_gateway_schema.erl +++ b/apps/emqx_gateway/src/emqx_gateway_schema.erl @@ -34,8 +34,10 @@ structs() -> ["gateway"]. fields("gateway") -> [{stomp, t(ref(stomp))}, {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) -> @@ -74,6 +76,21 @@ fields(mqttsn_predefined) -> , {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) -> [{"$id", t(ref(exproto_structs))}]; @@ -100,6 +117,9 @@ fields(clientinfo_override) -> , {clientid, t(string())} ]; +fields(translators) -> + [{"$name", t(string())}]; + fields(udp_listener_group) -> [ {udp, t(ref(udp_listener))} , {dtls, t(ref(dtls_listener))} diff --git a/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_coap_resource.erl b/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_coap_resource.erl index 8a7b41291..588dd523e 100644 --- a/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_coap_resource.erl +++ b/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_coap_resource.erl @@ -363,9 +363,12 @@ check_epn(undefined) -> false; check_epn(_) -> true. check_lifetime(undefined) -> false; -check_lifetime(LifeTime) when is_integer(LifeTime) -> - Max = proplists:get_value(lifetime_max, lwm2m_coap_responder:options(), 315360000), - Min = proplists:get_value(lifetime_min, lwm2m_coap_responder:options(), 0), +check_lifetime(LifeTime0) when is_integer(LifeTime0) -> + LifeTime = timer:seconds(LifeTime0), + Envs = proplists:get_value(config, lwm2m_coap_responder:options(), #{}), + Max = maps:get(lifetime_max, Envs, 315360000), + Min = maps:get(lifetime_min, Envs, 0), + if LifeTime >= Min, LifeTime =< Max -> true; diff --git a/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_impl.erl b/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_impl.erl new file mode 100644 index 000000000..d94c9fa8b --- /dev/null +++ b/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_impl.erl @@ -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. diff --git a/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_protocol.erl b/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_protocol.erl index e13b19e0a..2ee61ec70 100644 --- a/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_protocol.erl +++ b/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_protocol.erl @@ -75,7 +75,8 @@ call(Pid, Msg, Timeout) -> end. 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, endpoint_name = EndpointName, version = Ver, @@ -89,7 +90,10 @@ init(CoapPid, EndpointName, Peername = {_Peerhost, _Port}, RegInfo = #{<<"lt">> ok -> _ = 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), Lwm2mState1 = Lwm2mState#lwm2m_state{started_at = time_now(), 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}) -> UpdatedRegInfo = maps:merge(RegInfo, NewRegInfo), - _ = case proplists:get_value(update_msg_publish_condition, - lwm2m_coap_responder:options(), contains_object_list) of + Envs = proplists:get_value(config, lwm2m_coap_responder:options(), #{}), + + _ = case maps:get(update_msg_publish_condition, + Envs, contains_object_list) of always -> send_to_broker(<<"update">>, #{<<"data">> => UpdatedRegInfo}, Lwm2mState); contains_object_list -> @@ -294,7 +300,8 @@ auto_observe_object_list(Expected, Registered) -> send_auto_observe(CoapPid, RegInfo, EndpointName) -> %% - 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 -> ?LOG(info, "Auto Observe Disabled", []); TrueOrObjList -> @@ -379,7 +386,12 @@ get_cached_downlink_messages() -> is_cache_mode(RegInfo, StartedAt) -> case is_psm(RegInfo) orelse is_qmode(RegInfo) of 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(), if (Now - StartedAt) >= QModeTimeWind -> true; true -> false @@ -400,15 +412,17 @@ is_qmode(_) -> false. %%-------------------------------------------------------------------- downlink_topic(EventType, Lwm2mState = #lwm2m_state{mountpoint = Mountpoint}) -> - Topics = proplists:get_value(topics, lwm2m_coap_responder:options(), []), - DnTopic = proplists:get_value(downlink_topic_key(EventType), Topics, - default_downlink_topic(EventType)), + Envs = proplists:get_value(config, lwm2m_coap_responder:options(), #{}), + Topics = maps:get(translators, Envs, #{}), + DnTopic = maps:get(downlink_topic_key(EventType), Topics, + default_downlink_topic(EventType)), take_place(mountpoint(iolist_to_binary(DnTopic), Mountpoint), Lwm2mState). uplink_topic(EventType, Lwm2mState = #lwm2m_state{mountpoint = Mountpoint}) -> - Topics = proplists:get_value(topics, lwm2m_coap_responder:options(), []), - UpTopic = proplists:get_value(uplink_topic_key(EventType), Topics, - default_uplink_topic(EventType)), + Envs = proplists:get_value(config, lwm2m_coap_responder:options(), #{}), + Topics = maps:get(translators, Envs, #{}), + UpTopic = maps:get(uplink_topic_key(EventType), Topics, + default_uplink_topic(EventType)), take_place(mountpoint(iolist_to_binary(UpTopic), Mountpoint), Lwm2mState). downlink_topic_key(EventType) when is_binary(EventType) -> diff --git a/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_xml_object_db.erl b/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_xml_object_db.erl index c7e68d281..ea5f878d3 100644 --- a/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_xml_object_db.erl +++ b/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_xml_object_db.erl @@ -22,7 +22,7 @@ % This module is for future use. Disabled now. %% API --export([ start_link/0 +-export([ start_link/1 , stop/0 , find_name/1 , find_objectid/1 @@ -49,8 +49,8 @@ %% API Function Definitions %% ------------------------------------------------------------------ -start_link() -> - gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). +start_link(XmlDir) -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [XmlDir], []). find_objectid(ObjectId) -> ObjectIdInt = case is_list(ObjectId) of @@ -85,10 +85,10 @@ stop() -> %% gen_server Function Definitions %% ------------------------------------------------------------------ -init([]) -> +init([XmlDir]) -> _ = ets:new(?LWM2M_OBJECT_DEF_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{}}. handle_call(_Request, _From, State) -> @@ -108,8 +108,6 @@ terminate(_Reason, _State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. - - %%-------------------------------------------------------------------- %% Internal functions %%-------------------------------------------------------------------- diff --git a/apps/emqx_gateway/src/lwm2m/test/emqx_lwm2m_SUITE.erl b/apps/emqx_gateway/src/lwm2m/test/emqx_lwm2m_SUITE.erl deleted file mode 100644 index 1e9c8f43c..000000000 --- a/apps/emqx_gateway/src/lwm2m/test/emqx_lwm2m_SUITE.erl +++ /dev/null @@ -1,1953 +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_lwm2m_SUITE). - -% -compile(export_all). -% -compile(nowarn_export_all). - -% -define(PORT, 5683). - -% -define(LOGT(Format, Args), ct:pal("TEST_SUITE: " ++ Format, Args)). - -% -include("src/lwm2m/include/emqx_lwm2m.hrl"). -% -include_lib("lwm2m_coap/include/coap.hrl"). -% -include_lib("eunit/include/eunit.hrl"). -% -include_lib("common_test/include/ct.hrl"). - -% %%-------------------------------------------------------------------- -% %% Setups -% %%-------------------------------------------------------------------- - -% all() -> -% [ {group, test_grp_0_register} -% , {group, test_grp_1_read} -% , {group, test_grp_2_write} -% , {group, test_grp_3_execute} -% , {group, test_grp_4_discover} -% , {group, test_grp_5_write_attr} -% , {group, test_grp_6_observe} -% , {group, test_grp_8_object_19} -% ]. - -% suite() -> [{timetrap, {seconds, 90}}]. - -% groups() -> -% RepeatOpt = {repeat_until_all_ok, 1}, -% [ -% {test_grp_0_register, [RepeatOpt], [ -% case01_register, -% case01_register_additional_opts, -% case01_register_incorrect_opts, -% case01_register_report, -% case02_update_deregister, -% case03_register_wrong_version, -% case04_register_and_lifetime_timeout, -% case05_register_wrong_epn, -% case06_register_wrong_lifetime, -% case07_register_alternate_path_01, -% case07_register_alternate_path_02, -% case08_reregister -% ]}, -% {test_grp_1_read, [RepeatOpt], [ -% case10_read, -% case10_read_separate_ack, -% case11_read_object_tlv, -% case11_read_object_json, -% case12_read_resource_opaque, -% case13_read_no_xml -% ]}, -% {test_grp_2_write, [RepeatOpt], [ -% case20_write, -% case21_write_object, -% case22_write_error, -% case20_single_write -% ]}, -% {test_grp_create, [RepeatOpt], [ -% case_create_basic -% ]}, -% {test_grp_delete, [RepeatOpt], [ -% case_delete_basic -% ]}, -% {test_grp_3_execute, [RepeatOpt], [ -% case30_execute, case31_execute_error -% ]}, -% {test_grp_4_discover, [RepeatOpt], [ -% case40_discover -% ]}, -% {test_grp_5_write_attr, [RepeatOpt], [ -% case50_write_attribute -% ]}, -% {test_grp_6_observe, [RepeatOpt], [ -% case60_observe -% ]}, -% {test_grp_7_block_wize_transfer, [RepeatOpt], [ -% case70_read_large, case70_write_large -% ]}, -% {test_grp_8_object_19, [RepeatOpt], [ -% case80_specail_object_19_1_0_write, -% case80_specail_object_19_0_0_notify -% %case80_specail_object_19_0_0_response, -% %case80_normal_object_19_0_0_read -% ]}, -% {test_grp_9_psm_queue_mode, [RepeatOpt], [ -% case90_psm_mode, -% case90_queue_mode -% ]} -% ]. - -% init_per_suite(Config) -> -% emqx_ct_helpers:start_apps([emqx]), -% Config. - -% end_per_suite(Config) -> -% timer:sleep(300), -% emqx_ct_helpers:stop_apps([emqx]), -% Config. - -% init_per_testcase(_AllTestCase, Config) -> -% application:set_env(emqx_lwm2m, bind_udp, [{5683, []}]), -% application:set_env(emqx_lwm2m, bind_dtls, [{5684, []}]), -% application:set_env(emqx_lwm2m, xml_dir, emqx_ct_helpers:deps_path(emqx_lwm2m, "lwm2m_xml")), -% application:set_env(emqx_lwm2m, lifetime_max, 86400), -% application:set_env(emqx_lwm2m, lifetime_min, 1), -% application:set_env(emqx_lwm2m, mountpoint, "lwm2m/%e/"), -% {ok, _Started} = application:ensure_all_started(emqx_lwm2m), -% {ok, ClientUdpSock} = gen_udp:open(0, [binary, {active, false}]), - -% {ok, C} = emqtt:start_link([{host, "localhost"},{port, 1883},{clientid, <<"c1">>}]), -% {ok, _} = emqtt:connect(C), -% timer:sleep(100), - -% [{sock, ClientUdpSock}, {emqx_c, C} | Config]. - -% end_per_testcase(_AllTestCase, Config) -> -% timer:sleep(300), -% gen_udp:close(?config(sock, Config)), -% emqtt:disconnect(?config(emqx_c, Config)), -% ok = application:stop(emqx_lwm2m), -% ok = application:stop(lwm2m_coap). - -% %%-------------------------------------------------------------------- -% %% Cases -% %%-------------------------------------------------------------------- - -% case01_register(Config) -> -% % ---------------------------------------- -% % REGISTER command -% % ---------------------------------------- -% UdpSock = ?config(sock, Config), -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId = 12, -% SubTopic = list_to_binary("lwm2m/"++Epn++"/dn/#"), - -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1", [?PORT, Epn]), -% #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, -% [], -% MsgId), - -% %% checkpoint 1 - response -% #coap_message{type = Type, method = Method, id = RspId, options = Opts} = -% test_recv_coap_response(UdpSock), -% ack = Type, -% {ok, created} = Method, -% RspId = MsgId, -% Location = proplists:get_value(location_path, Opts), -% ?assertNotEqual(undefined, Location), - -% %% checkpoint 2 - verify subscribed topics -% timer:sleep(50), -% ?LOGT("all topics: ~p", [test_mqtt_broker:get_subscrbied_topics()]), -% true = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()), - -% % ---------------------------------------- -% % DE-REGISTER command -% % ---------------------------------------- -% ?LOGT("start to send DE-REGISTER command", []), -% MsgId3 = 52, -% test_send_coap_request( UdpSock, -% delete, -% sprintf("coap://127.0.0.1:~b~s", [?PORT, join_path(Location, <<>>)]), -% #coap_content{payload = <<>>}, -% [], -% MsgId3), -% #coap_message{type = ack, id = RspId3, method = Method3} = test_recv_coap_response(UdpSock), -% {ok,deleted} = Method3, -% MsgId3 = RspId3, -% timer:sleep(50), -% false = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()). - -% case01_register_additional_opts(Config) -> -% % ---------------------------------------- -% % REGISTER command -% % ---------------------------------------- -% UdpSock = ?config(sock, Config), -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId = 12, -% SubTopic = list_to_binary("lwm2m/"++Epn++"/dn/#"), - -% AddOpts = "ep=~s<=345&lwm2m=1&apn=psmA.eDRX0.ctnb&cust_opt=shawn&im=123&ct=1.4&mt=mdm9620&mv=1.2", -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b/rd?" ++ AddOpts, [?PORT, Epn]), -% #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, -% [], -% MsgId), - -% %% checkpoint 1 - response -% #coap_message{type = Type, method = Method, id = RspId, options = Opts} = -% test_recv_coap_response(UdpSock), -% Type = ack, -% Method = {ok, created}, -% RspId = MsgId, -% Location = proplists:get_value(location_path, Opts), -% ?assertNotEqual(undefined, Location), - -% %% checkpoint 2 - verify subscribed topics -% timer:sleep(50), - -% true = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()), - -% % ---------------------------------------- -% % DE-REGISTER command -% % ---------------------------------------- -% ?LOGT("start to send DE-REGISTER command", []), -% MsgId3 = 52, -% test_send_coap_request( UdpSock, -% delete, -% sprintf("coap://127.0.0.1:~b~s", [?PORT, join_path(Location, <<>>)]), -% #coap_content{payload = <<>>}, -% [], -% MsgId3), -% #coap_message{type = ack, id = RspId3, method = Method3} = test_recv_coap_response(UdpSock), -% {ok,deleted} = Method3, -% MsgId3 = RspId3, -% timer:sleep(50), -% false = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()). - -% case01_register_incorrect_opts(Config) -> -% % ---------------------------------------- -% % REGISTER command -% % ---------------------------------------- -% UdpSock = ?config(sock, Config), -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId = 12, - - -% AddOpts = "ep=~s<=345&lwm2m=1&incorrect_opt", -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b/rd?" ++ AddOpts, [?PORT, Epn]), -% #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, -% [], -% MsgId), - -% %% checkpoint 1 - response -% #coap_message{type = ack, method = Method, id = MsgId} = -% test_recv_coap_response(UdpSock), -% ?assertEqual({error,bad_request}, Method). - -% case01_register_report(Config) -> -% % ---------------------------------------- -% % REGISTER command -% % ---------------------------------------- -% UdpSock = ?config(sock, Config), -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId = 12, -% SubTopic = list_to_binary("lwm2m/"++Epn++"/dn/#"), -% ReportTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), ReportTopic, qos0), -% timer:sleep(200), - -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1", [?PORT, Epn]), -% #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, -% [], -% MsgId), - -% #coap_message{type = Type, method = Method, id = RspId, options = Opts} = -% test_recv_coap_response(UdpSock), -% Type = ack, -% Method = {ok, created}, -% RspId = MsgId, -% Location = proplists:get_value(location_path, Opts), -% ?assertNotEqual(undefined, Location), - -% timer:sleep(50), -% true = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()), - -% ReadResult = emqx_json:encode(#{ -% <<"msgType">> => <<"register">>, -% <<"data">> => #{ -% <<"alternatePath">> => <<"/">>, -% <<"ep">> => list_to_binary(Epn), -% <<"lt">> => 345, -% <<"lwm2m">> => <<"1">>, -% <<"objectList">> => [<<"/1">>, <<"/2">>, <<"/3">>, <<"/4">>, <<"/5">>] -% } -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(ReportTopic)), - -% % ---------------------------------------- -% % DE-REGISTER command -% % ---------------------------------------- -% ?LOGT("start to send DE-REGISTER command", []), -% MsgId3 = 52, -% test_send_coap_request( UdpSock, -% delete, -% sprintf("coap://127.0.0.1:~b~s", [?PORT, join_path(Location, <<>>)]), -% #coap_content{payload = <<>>}, -% [], -% MsgId3), -% #coap_message{type = ack, id = RspId3, method = Method3} = test_recv_coap_response(UdpSock), -% {ok,deleted} = Method3, -% MsgId3 = RspId3, -% timer:sleep(50), -% false = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()). - -% case02_update_deregister(Config) -> -% % ---------------------------------------- -% % REGISTER command -% % ---------------------------------------- -% UdpSock = ?config(sock, Config), -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId = 12, -% SubTopic = list_to_binary("lwm2m/"++Epn++"/dn/#"), -% ReportTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), ReportTopic, qos0), -% timer:sleep(200), - -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1", [?PORT, Epn]), -% #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, -% [], -% MsgId), -% timer:sleep(100), -% #coap_message{type = ack, method = Method, options = Opts} = test_recv_coap_response(UdpSock), -% ?assertEqual({ok,created}, Method), - -% ?LOGT("Options got: ~p", [Opts]), -% Location = proplists:get_value(location_path, Opts), -% Register = emqx_json:encode(#{ -% <<"msgType">> => <<"register">>, -% <<"data">> => #{ -% <<"alternatePath">> => <<"/">>, -% <<"ep">> => list_to_binary(Epn), -% <<"lt">> => 345, -% <<"lwm2m">> => <<"1">>, -% <<"objectList">> => [<<"/1">>, <<"/2">>, <<"/3">>, <<"/4">>, <<"/5">>] -% } -% }), -% ?assertEqual(Register, test_recv_mqtt_response(ReportTopic)), - -% % ---------------------------------------- -% % UPDATE command -% % ---------------------------------------- -% ?LOGT("start to send UPDATE command", []), -% MsgId2 = 27, -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b~s?lt=789", [?PORT, join_path(Location, <<>>)]), -% #coap_content{content_format = <<"text/plain">>, payload = <<", , , , , ">>}, -% [], -% MsgId2), -% #coap_message{type = ack, id = RspId2, method = Method2} = test_recv_coap_response(UdpSock), -% {ok,changed} = Method2, -% MsgId2 = RspId2, -% Update = emqx_json:encode(#{ -% <<"msgType">> => <<"update">>, -% <<"data">> => #{ -% <<"alternatePath">> => <<"/">>, -% <<"ep">> => list_to_binary(Epn), -% <<"lt">> => 789, -% <<"lwm2m">> => <<"1">>, -% <<"objectList">> => [<<"/1">>, <<"/2">>, <<"/3">>, <<"/4">>, <<"/5">>, <<"/6">>] -% } -% }), -% ?assertEqual(Update, test_recv_mqtt_response(ReportTopic)), - -% % ---------------------------------------- -% % DE-REGISTER command -% % ---------------------------------------- -% ?LOGT("start to send DE-REGISTER command", []), -% MsgId3 = 52, -% test_send_coap_request( UdpSock, -% delete, -% sprintf("coap://127.0.0.1:~b~s", [?PORT, join_path(Location, <<>>)]), -% #coap_content{payload = <<>>}, -% [], -% MsgId3), -% #coap_message{type = ack, id = RspId3, method = Method3} = test_recv_coap_response(UdpSock), -% {ok,deleted} = Method3, -% MsgId3 = RspId3, - -% timer:sleep(50), -% false = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()). - -% case03_register_wrong_version(Config) -> -% % ---------------------------------------- -% % REGISTER command -% % ---------------------------------------- -% UdpSock = ?config(sock, Config), -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId = 12, -% SubTopic = list_to_binary("lwm2m/"++Epn++"/dn/#"), -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=8.3", [?PORT, Epn]), -% #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, -% [], -% MsgId), -% #coap_message{type = ack, method = Method} = test_recv_coap_response(UdpSock), -% ?assertEqual({error,precondition_failed}, Method), -% timer:sleep(50), - -% false = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()). - -% case04_register_and_lifetime_timeout(Config) -> -% % ---------------------------------------- -% % REGISTER command -% % ---------------------------------------- -% UdpSock = ?config(sock, Config), -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId = 12, -% SubTopic = list_to_binary("lwm2m/"++Epn++"/dn/#"), - -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b/rd?ep=~s<=2&lwm2m=1", [?PORT, Epn]), -% #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, -% [], -% MsgId), -% timer:sleep(100), -% #coap_message{type = ack, method = Method} = test_recv_coap_response(UdpSock), -% ?assertEqual({ok,created}, Method), - -% true = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()), - -% % ---------------------------------------- -% % lifetime timeout -% % ---------------------------------------- -% timer:sleep(4000), - -% false = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()). - -% case05_register_wrong_epn(Config) -> -% % ---------------------------------------- -% % REGISTER command -% % ---------------------------------------- -% MsgId = 12, -% UdpSock = ?config(sock, Config), - -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b/rd?lt=345&lwm2m=1.0", [?PORT]), -% #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, -% [], -% MsgId), -% #coap_message{type = ack, method = Method} = test_recv_coap_response(UdpSock), -% ?assertEqual({error,bad_request}, Method). - -% case06_register_wrong_lifetime(Config) -> -% % ---------------------------------------- -% % REGISTER command -% % ---------------------------------------- -% UdpSock = ?config(sock, Config), -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId = 12, - -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b/rd?ep=~s&lwm2m=1", [?PORT, Epn]), -% #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, -% [], -% MsgId), -% #coap_message{type = ack, method = Method} = test_recv_coap_response(UdpSock), -% ?assertEqual({error,bad_request}, Method), -% timer:sleep(50), -% ?assertEqual([], test_mqtt_broker:get_subscrbied_topics()). - -% case07_register_alternate_path_01(Config) -> -% % ---------------------------------------- -% % REGISTER command -% % ---------------------------------------- -% UdpSock = ?config(sock, Config), -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId = 12, -% SubTopic = list_to_binary("lwm2m/"++Epn++"/dn/#"), -% ReportTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), ReportTopic, qos0), -% timer:sleep(200), - -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1", [?PORT, Epn]), -% #coap_content{content_format = <<"text/plain">>, -% payload = <<";rt=\"oma.lwm2m\";ct=11543,,,">>}, -% [], -% MsgId), -% timer:sleep(50), -% true = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()). - -% case07_register_alternate_path_02(Config) -> -% % ---------------------------------------- -% % REGISTER command -% % ---------------------------------------- -% UdpSock = ?config(sock, Config), -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId = 12, -% SubTopic = list_to_binary("lwm2m/"++Epn++"/dn/#"), -% ReportTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), ReportTopic, qos0), -% timer:sleep(200), - -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1", [?PORT, Epn]), -% #coap_content{content_format = <<"text/plain">>, -% payload = <<";rt=\"oma.lwm2m\";ct=11543,,,">>}, -% [], -% MsgId), -% timer:sleep(50), -% true = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()). - -% case08_reregister(Config) -> -% % ---------------------------------------- -% % REGISTER command -% % ---------------------------------------- -% UdpSock = ?config(sock, Config), -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId = 12, -% SubTopic = list_to_binary("lwm2m/"++Epn++"/dn/#"), -% ReportTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), ReportTopic, qos0), -% timer:sleep(200), - -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1", [?PORT, Epn]), -% #coap_content{content_format = <<"text/plain">>, -% payload = <<";rt=\"oma.lwm2m\";ct=11543,,,">>}, -% [], -% MsgId), -% timer:sleep(50), -% true = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()), - -% ReadResult = emqx_json:encode( -% #{ -% <<"msgType">> => <<"register">>, -% <<"data">> => #{ -% <<"alternatePath">> => <<"/lwm2m">>, -% <<"ep">> => list_to_binary(Epn), -% <<"lt">> => 345, -% <<"lwm2m">> => <<"1">>, -% <<"objectList">> => [<<"/1/0">>, <<"/2/0">>, <<"/3/0">>] -% } -% } -% ), -% ?assertEqual(ReadResult, test_recv_mqtt_response(ReportTopic)), -% timer:sleep(1000), - -% %% the same lwm2mc client registers to server again -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1", [?PORT, Epn]), -% #coap_content{content_format = <<"text/plain">>, -% payload = <<";rt=\"oma.lwm2m\";ct=11543,,,">>}, -% [], -% MsgId + 1), -% %% verify the lwm2m client is still online -% ?assertEqual(ReadResult, test_recv_mqtt_response(ReportTopic)). - -% case10_read(Config) -> -% UdpSock = ?config(sock, Config), -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId1 = 15, -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), -% % step 1, device register ... -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1", [?PORT, Epn]), -% #coap_content{content_format = <<"text/plain">>, -% payload = <<";rt=\"oma.lwm2m\";ct=11543,,,">>}, -% [], -% MsgId1), -% #coap_message{method = Method1} = test_recv_coap_response(UdpSock), -% ?assertEqual({ok,created}, Method1), -% test_recv_mqtt_response(RespTopic), - -% % step2, send a READ command to device -% CmdId = 206, -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% Command = #{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"read">>, -% <<"data">> => #{ -% <<"path">> => <<"/3/0/0">> -% } -% }, -% CommandJson = emqx_json:encode(Command), -% ?LOGT("CommandJson=~p", [CommandJson]), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, -% ?LOGT("LwM2M client got ~p", [Request2]), - -% ?assertEqual(get, Method2), -% ?assertEqual(<<"/lwm2m/3/0/0">>, get_coap_path(Options2)), -% ?assertEqual(<<>>, Payload2), -% timer:sleep(50), - -% test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, content}, #coap_content{content_format = <<"text/plain">>, payload = <<"EMQ">>}, Request2, true), -% timer:sleep(100), - -% ReadResult = emqx_json:encode(#{ <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"read">>, -% <<"data">> => #{ -% <<"code">> => <<"2.05">>, -% <<"codeMsg">> => <<"content">>, -% <<"reqPath">> => <<"/3/0/0">>, -% <<"content">> => [#{ -% path => <<"/3/0/0">>, -% value => <<"EMQ">> -% }] -% } -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). - -% case10_read_separate_ack(Config) -> -% UdpSock = ?config(sock, Config), -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId1 = 15, -% ObjectList = <<", , , , ">>, -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), - -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), - -% % step 1, device register ... -% std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), - -% % step2, send a READ command to device -% CmdId = 206, -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% Command = #{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"read">>, -% <<"data">> => #{ -% <<"path">> => <<"/3/0/0">> -% } -% }, -% CommandJson = emqx_json:encode(Command), -% ?LOGT("CommandJson=~p", [CommandJson]), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, -% ?LOGT("LwM2M client got ~p", [Request2]), - -% ?assertEqual(get, Method2), -% ?assertEqual(<<"/3/0/0">>, get_coap_path(Options2)), -% ?assertEqual(<<>>, Payload2), - -% test_send_empty_ack(UdpSock, "127.0.0.1", ?PORT, Request2), -% ReadResultACK = emqx_json:encode(#{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"ack">>, -% <<"data">> => #{ -% <<"path">> => <<"/3/0/0">> -% } -% }), -% ?assertEqual(ReadResultACK, test_recv_mqtt_response(RespTopic)), -% timer:sleep(100), - -% test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, content}, #coap_content{content_format = <<"text/plain">>, payload = <<"EMQ">>}, Request2, false), -% timer:sleep(100), - -% ReadResult = emqx_json:encode(#{ <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"read">>, -% <<"data">> => #{ -% <<"code">> => <<"2.05">>, -% <<"codeMsg">> => <<"content">>, -% <<"reqPath">> => <<"/3/0/0">>, -% <<"content">> => [#{ -% path => <<"/3/0/0">>, -% value => <<"EMQ">> -% }] -% } -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). - -% case11_read_object_tlv(Config) -> -% % step 1, device register ... -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId1 = 15, -% UdpSock = ?config(sock, Config), -% ObjectList = <<", , , , ">>, -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), - -% std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), - -% % step2, send a READ command to device -% CmdId = 207, -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% Command = #{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"read">>, -% <<"data">> => #{ -% <<"path">> => <<"/3/0">> -% } -% }, -% CommandJson = emqx_json:encode(Command), -% ?LOGT("CommandJson=~p", [CommandJson]), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2} = Request2, -% ?LOGT("LwM2M client got ~p", [Request2]), - -% ?assertEqual(get, Method2), -% timer:sleep(50), - -% Tlv = <<16#08, 16#00, 16#3C, 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>>, -% test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, content}, #coap_content{content_format = <<"application/vnd.oma.lwm2m+tlv">>, payload = Tlv}, Request2, true), -% timer:sleep(100), - -% ReadResult = emqx_json:encode(#{ <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"read">>, -% <<"data">> => #{ -% <<"code">> => <<"2.05">>, -% <<"codeMsg">> => <<"content">>, -% <<"reqPath">> => <<"/3/0">>, -% <<"content">> => [ -% #{ -% path => <<"/3/0/0">>, -% value => <<"Open Mobile Alliance">> -% }, -% #{ -% path => <<"/3/0/1">>, -% value => <<"Lightweight M2M Client">> -% }, -% #{ -% path => <<"/3/0/2">>, -% value => <<"345000123">> -% } -% ] -% } -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). - -% case11_read_object_json(Config) -> -% % step 1, device register ... -% UdpSock = ?config(sock, Config), -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId1 = 15, - -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% ObjectList = <<", , , , ">>, -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), - -% std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), - -% % step2, send a READ command to device -% CmdId = 206, -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% Command = #{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"read">>, -% <<"data">> => #{ -% <<"path">> => <<"/3/0">> -% } -% }, -% CommandJson = emqx_json:encode(Command), -% ?LOGT("CommandJson=~p", [CommandJson]), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2} = Request2, -% ?LOGT("LwM2M client got ~p", [Request2]), - -% ?assertEqual(get, Method2), -% timer:sleep(50), - -% Json = <<"{\"bn\":\"/3/0\",\"e\":[{\"n\":\"0\",\"sv\":\"Open Mobile Alliance\"},{\"n\":\"1\",\"sv\":\"Lightweight M2M Client\"},{\"n\":\"2\",\"sv\":\"345000123\"}]}">>, -% test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, content}, #coap_content{content_format = <<"application/vnd.oma.lwm2m+json">>, payload = Json}, Request2, true), -% timer:sleep(100), - -% ReadResult = emqx_json:encode(#{ <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"read">>, -% <<"data">> => #{ -% <<"code">> => <<"2.05">>, -% <<"codeMsg">> => <<"content">>, -% <<"reqPath">> => <<"/3/0">>, -% <<"content">> => [ -% #{ -% path => <<"/3/0/0">>, -% value => <<"Open Mobile Alliance">> -% }, -% #{ -% path => <<"/3/0/1">>, -% value => <<"Lightweight M2M Client">> -% }, -% #{ -% path => <<"/3/0/2">>, -% value => <<"345000123">> -% } -% ] -% } -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). - -% case12_read_resource_opaque(Config) -> -% % step 1, device register ... -% UdpSock = ?config(sock, Config), -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId1 = 15, -% ObjectList = <<", , , , ">>, -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), - -% std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), - -% % step2, send a READ command to device -% CmdId = 206, -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% Command = #{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"read">>, -% <<"data">> => #{ -% <<"path">> => <<"/3/0/8">> -% } -% }, -% CommandJson = emqx_json:encode(Command), -% ?LOGT("CommandJson=~p", [CommandJson]), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2} = Request2, -% ?LOGT("LwM2M client got ~p", [Request2]), - -% ?assertEqual(get, Method2), -% timer:sleep(50), - -% Opaque = <<20, 21, 22, 23>>, -% test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, content}, #coap_content{content_format = <<"application/octet-stream">>, payload = Opaque}, Request2, true), -% timer:sleep(100), - -% ReadResult = emqx_json:encode(#{ <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"read">>, -% <<"data">> => #{ -% <<"code">> => <<"2.05">>, -% <<"codeMsg">> => <<"content">>, -% <<"reqPath">> => <<"/3/0/8">>, -% <<"content">> => [ -% #{ -% path => <<"/3/0/8">>, -% value => base64:encode(Opaque) -% } -% ] -% } -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). - -% case13_read_no_xml(Config) -> -% % step 1, device register ... -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId1 = 15, -% UdpSock = ?config(sock, Config), -% ObjectList = <<", , , , ">>, -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), - -% std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), - -% % step2, send a READ command to device -% CmdId = 206, -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% Command = #{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"read">>, -% <<"data">> => #{ -% <<"path">> => <<"/9723/0/0">> -% } -% }, -% CommandJson = emqx_json:encode(Command), -% ?LOGT("CommandJson=~p", [CommandJson]), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2} = Request2, -% ?LOGT("LwM2M client got ~p", [Request2]), - -% ?assertEqual(get, Method2), -% timer:sleep(50), - -% test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, content}, #coap_content{content_format = <<"text/plain">>, payload = <<"EMQ">>}, Request2, true), -% timer:sleep(100), - -% ReadResult = emqx_json:encode(#{ <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"read">>, -% <<"data">> => #{ -% <<"reqPath">> => <<"/9723/0/0">>, -% <<"code">> => <<"4.00">>, -% <<"codeMsg">> => <<"bad_request">> -% } -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). - -% case20_single_write(Config) -> -% % step 1, device register ... -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId1 = 15, -% UdpSock = ?config(sock, Config), -% ObjectList = <<", , , , ">>, -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), - -% std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), - -% % step2, send a WRITE command to device -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% CmdId = 307, -% Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"write">>, -% <<"data">> => #{ -% <<"path">> => <<"/3/0/13">>, -% <<"type">> => <<"Integer">>, -% <<"value">> => <<"12345">> -% } -% }, -% CommandJson = emqx_json:encode(Command), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, -% Path2 = get_coap_path(Options2), -% ?assertEqual(put, Method2), -% ?assertEqual(<<"/3/0/13">>, Path2), -% Tlv_Value = <<3:2, 0:1, 0:2, 2:3, 13, 12345:16>>, -% ?assertEqual(Tlv_Value, Payload2), -% timer:sleep(50), - -% test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, changed}, #coap_content{}, Request2, true), -% timer:sleep(100), - -% ReadResult = emqx_json:encode(#{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"data">> => #{ -% <<"reqPath">> => <<"/3/0/13">>, -% <<"code">> => <<"2.04">>, -% <<"codeMsg">> => <<"changed">> -% }, -% <<"msgType">> => <<"write">> -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). - -% case20_write(Config) -> -% % step 1, device register ... -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId1 = 15, -% UdpSock = ?config(sock, Config), -% ObjectList = <<", , , , ">>, -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), - -% std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), - -% % step2, send a WRITE command to device -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% CmdId = 307, -% Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"write">>, -% <<"data">> => #{ -% <<"basePath">> => <<"/3/0/13">>, -% <<"content">> => [#{ -% type => <<"Float">>, -% value => <<"12345.0">> -% }] -% } -% }, -% CommandJson = emqx_json:encode(Command), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, -% Path2 = get_coap_path(Options2), -% ?assertEqual(put, Method2), -% ?assertEqual(<<"/3/0/13">>, Path2), -% Tlv_Value = <<200, 13, 8, 64,200,28,128,0,0,0,0>>, -% ?assertEqual(Tlv_Value, Payload2), -% timer:sleep(50), - -% test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, changed}, #coap_content{}, Request2, true), -% timer:sleep(100), - -% WriteResult = emqx_json:encode(#{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"data">> => #{ -% <<"reqPath">> => <<"/3/0/13">>, -% <<"code">> => <<"2.04">>, -% <<"codeMsg">> => <<"changed">> -% }, -% <<"msgType">> => <<"write">> -% }), -% ?assertEqual(WriteResult, test_recv_mqtt_response(RespTopic)). - -% case21_write_object(Config) -> -% % step 1, device register ... -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId1 = 15, -% UdpSock = ?config(sock, Config), -% ObjectList = <<", , , , ">>, -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), - -% std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), - -% % step2, send a WRITE command to device -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% CmdId = 307, -% Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"write">>, -% <<"data">> => #{ -% <<"basePath">> => <<"/3/0/">>, -% <<"content">> => [#{ -% path => <<"13">>, -% type => <<"Integer">>, -% value => <<"12345">> -% },#{ -% path => <<"14">>, -% type => <<"String">>, -% value => <<"87x">> -% }] -% } -% }, -% CommandJson = emqx_json:encode(Command), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, -% Path2 = get_coap_path(Options2), -% ?assertEqual(post, Method2), -% ?assertEqual(<<"/3/0">>, Path2), -% Tlv_Value = <<3:2, 0:1, 0:2, 2:3, 13, 12345:16, -% 3:2, 0:1, 0:2, 3:3, 14, "87x">>, -% ?assertEqual(Tlv_Value, Payload2), -% timer:sleep(50), - -% test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, changed}, #coap_content{}, Request2, true), -% timer:sleep(100), - - -% ReadResult = emqx_json:encode(#{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"write">>, -% <<"data">> => #{ -% <<"reqPath">> => <<"/3/0/">>, -% <<"code">> => <<"2.04">>, -% <<"codeMsg">> => <<"changed">> -% } -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). - -% case22_write_error(Config) -> -% % step 1, device register ... -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId1 = 15, -% UdpSock = ?config(sock, Config), -% ObjectList = <<", , , , ">>, -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), - -% std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), - -% % step2, send a WRITE command to device -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% CmdId = 307, -% Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"write">>, -% <<"data">> => #{ -% <<"basePath">> => <<"/3/0/1">>, -% <<"content">> => [ -% #{ -% type => <<"Integer">>, -% value => <<"12345">> -% } -% ] -% } -% }, -% CommandJson = emqx_json:encode(Command), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2, options=Options2} = Request2, -% Path2 = get_coap_path(Options2), -% ?assertEqual(put, Method2), -% ?assertEqual(<<"/3/0/1">>, Path2), -% timer:sleep(50), - -% test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {error, bad_request}, #coap_content{}, Request2, true), -% timer:sleep(100), - -% ReadResult = emqx_json:encode(#{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"data">> => #{ -% <<"reqPath">> => <<"/3/0/1">>, -% <<"code">> => <<"4.00">>, -% <<"codeMsg">> => <<"bad_request">> -% }, -% <<"msgType">> => <<"write">> -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). - -% case_create_basic(Config) -> -% % step 1, device register ... -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId1 = 15, -% UdpSock = ?config(sock, Config), -% ObjectList = <<", , , ">>, -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), - -% std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), - -% % step2, send a CREATE command to device -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% CmdId = 307, -% Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"create">>, -% <<"data">> => #{ -% <<"path">> => <<"/5">> -% } -% }, -% CommandJson = emqx_json:encode(Command), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, -% Path2 = get_coap_path(Options2), -% ?assertEqual(post, Method2), -% ?assertEqual(<<"/5">>, Path2), -% ?assertEqual(<<"">>, Payload2), -% timer:sleep(50), - -% test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, created}, #coap_content{}, Request2, true), -% timer:sleep(100), - -% ReadResult = emqx_json:encode(#{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"data">> => #{ -% <<"reqPath">> => <<"/5">>, -% <<"code">> => <<"2.01">>, -% <<"codeMsg">> => <<"created">> -% }, -% <<"msgType">> => <<"create">> -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). - -% case_delete_basic(Config) -> -% % step 1, device register ... -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId1 = 15, -% UdpSock = ?config(sock, Config), -% ObjectList = <<", , , , , ">>, -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), - -% std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), - -% % step2, send a CREATE command to device -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% CmdId = 307, -% Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"delete">>, -% <<"data">> => #{ -% <<"path">> => <<"/5/0">> -% } -% }, -% CommandJson = emqx_json:encode(Command), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, -% Path2 = get_coap_path(Options2), -% ?assertEqual(delete, Method2), -% ?assertEqual(<<"/5/0">>, Path2), -% ?assertEqual(<<"">>, Payload2), -% timer:sleep(50), - -% test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, deleted}, #coap_content{}, Request2, true), -% timer:sleep(100), - -% ReadResult = emqx_json:encode(#{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"data">> => #{ -% <<"reqPath">> => <<"/5/0">>, -% <<"code">> => <<"2.02">>, -% <<"codeMsg">> => <<"deleted">> -% }, -% <<"msgType">> => <<"delete">> -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). - -% case30_execute(Config) -> -% % step 1, device register ... -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId1 = 15, -% UdpSock = ?config(sock, Config), -% ObjectList = <<", , , , ">>, -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), - -% std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), - -% % step2, send a WRITE command to device -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% CmdId = 307, -% Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"execute">>, -% <<"data">> => #{ -% <<"path">> => <<"/3/0/4">>, -% %% "args" should not be present for "/3/0/4", only for testing the encoding here -% <<"args">> => <<"2,7">> -% } -% }, -% CommandJson = emqx_json:encode(Command), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, -% Path2 = get_coap_path(Options2), -% ?assertEqual(post, Method2), -% ?assertEqual(<<"/3/0/4">>, Path2), -% ?assertEqual(<<"2,7">>, Payload2), -% timer:sleep(50), - -% test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, changed}, #coap_content{}, Request2, true), -% timer:sleep(100), - -% ReadResult = emqx_json:encode(#{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"data">> => #{ -% <<"reqPath">> => <<"/3/0/4">>, -% <<"code">> => <<"2.04">>, -% <<"codeMsg">> => <<"changed">> -% }, -% <<"msgType">> => <<"execute">> -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). - -% case31_execute_error(Config) -> -% % step 1, device register ... -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId1 = 15, -% UdpSock = ?config(sock, Config), -% ObjectList = <<", , , , ">>, -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), - -% std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), - -% % step2, send a WRITE command to device -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% CmdId = 307, -% Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"execute">>, -% <<"data">> => #{ -% <<"path">> => <<"/3/0/4">>, -% <<"args">> => <<"2,7">> -% } -% }, -% CommandJson = emqx_json:encode(Command), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, -% Path2 = get_coap_path(Options2), -% ?assertEqual(post, Method2), -% ?assertEqual(<<"/3/0/4">>, Path2), -% ?assertEqual(<<"2,7">>, Payload2), -% timer:sleep(50), - -% test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {error, uauthorized}, #coap_content{}, Request2, true), -% timer:sleep(100), - -% ReadResult = emqx_json:encode(#{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"data">> => #{ -% <<"reqPath">> => <<"/3/0/4">>, -% <<"code">> => <<"4.01">>, -% <<"codeMsg">> => <<"uauthorized">> -% }, -% <<"msgType">> => <<"execute">> -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). - -% case40_discover(Config) -> -% % step 1, device register ... -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId1 = 15, -% UdpSock = ?config(sock, Config), -% ObjectList = <<", , , , ">>, -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), - -% std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), - -% % step2, send a WRITE command to device -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% CmdId = 307, -% Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"discover">>, -% <<"data">> => #{ -% <<"path">> => <<"/3/0/7">> -% } }, -% CommandJson = emqx_json:encode(Command), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, -% Path2 = get_coap_path(Options2), -% ?assertEqual(get, Method2), -% ?assertEqual(<<"/3/0/7">>, Path2), -% ?assertEqual(<<>>, Payload2), -% timer:sleep(50), - -% PayloadDiscover = <<";dim=8;pmin=10;pmax=60;gt=50;lt=42.2,">>, -% test_send_coap_response(UdpSock, -% "127.0.0.1", -% ?PORT, -% {ok, content}, -% #coap_content{content_format = <<"application/link-format">>, payload = PayloadDiscover}, -% Request2, -% true), -% timer:sleep(100), - -% ReadResult = emqx_json:encode(#{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"discover">>, -% <<"data">> => #{ -% <<"reqPath">> => <<"/3/0/7">>, -% <<"code">> => <<"2.05">>, -% <<"codeMsg">> => <<"content">>, -% <<"content">> => -% [<<";dim=8;pmin=10;pmax=60;gt=50;lt=42.2">>, <<"">>] -% } -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). - -% case50_write_attribute(Config) -> -% % step 1, device register ... -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId1 = 15, -% UdpSock = ?config(sock, Config), -% ObjectList = <<", , , , ">>, -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), - -% std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), - -% % step2, send a WRITE command to device -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% CmdId = 307, -% Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"write-attr">>, -% <<"data">> => #{ -% <<"path">> => <<"/3/0/9">>, -% <<"pmin">> => <<"1">>, -% <<"pmax">> => <<"5">>, -% <<"lt">> => <<"5">> -% } }, -% CommandJson = emqx_json:encode(Command), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(100), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, -% ?LOGT("got options: ~p", [Options2]), -% Path2 = get_coap_path(Options2), -% Query2 = lists:sort(get_coap_query(Options2)), -% ?assertEqual(put, Method2), -% ?assertEqual(<<"/3/0/9">>, Path2), -% ?assertEqual(lists:sort([<<"pmax=5">>,<<"lt=5">>,<<"pmin=1">>]), Query2), -% ?assertEqual(<<>>, Payload2), -% timer:sleep(50), - -% test_send_coap_response(UdpSock, -% "127.0.0.1", -% ?PORT, -% {ok, changed}, -% #coap_content{}, -% Request2, -% true), -% timer:sleep(100), - -% ReadResult = emqx_json:encode(#{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"data">> => #{ -% <<"reqPath">> => <<"/3/0/9">>, -% <<"code">> => <<"2.04">>, -% <<"codeMsg">> => <<"changed">> -% }, -% <<"msgType">> => <<"write-attr">> -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). - -% case60_observe(Config) -> -% % step 1, device register ... -% Epn = "urn:oma:lwm2m:oma:3", -% MsgId1 = 15, -% UdpSock = ?config(sock, Config), -% ObjectList = <<", , , , ">>, -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% RespTopicAD = list_to_binary("lwm2m/"++Epn++"/up/notify"), -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% emqtt:subscribe(?config(emqx_c, Config), RespTopicAD, qos0), -% timer:sleep(200), - -% std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), - -% % step2, send a OBSERVE command to device -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% CmdId = 307, -% Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"observe">>, -% <<"data">> => #{ -% <<"path">> => <<"/3/0/10">> -% } -% }, -% CommandJson = emqx_json:encode(Command), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, -% Path2 = get_coap_path(Options2), -% Observe = get_coap_observe(Options2), -% ?assertEqual(get, Method2), -% ?assertEqual(<<"/3/0/10">>, Path2), -% ?assertEqual(Observe, 0), -% ?assertEqual(<<>>, Payload2), -% timer:sleep(50), - -% test_send_coap_observe_ack( UdpSock, -% "127.0.0.1", -% ?PORT, -% {ok, content}, -% #coap_content{content_format = <<"text/plain">>, payload = <<"2048">>}, -% Request2), -% timer:sleep(100), - -% ReadResult = emqx_json:encode(#{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"observe">>, -% <<"data">> => #{ -% <<"reqPath">> => <<"/3/0/10">>, -% <<"code">> => <<"2.05">>, -% <<"codeMsg">> => <<"content">>, -% <<"content">> => [#{ -% path => <<"/3/0/10">>, -% value => 2048 -% }] -% } -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)), - -% %% step3 the notifications -% timer:sleep(200), -% ObSeq = 3, -% test_send_coap_notif( UdpSock, -% "127.0.0.1", -% ?PORT, -% #coap_content{content_format = <<"text/plain">>, payload = <<"4096">>}, -% ObSeq, -% Request2), -% timer:sleep(100), -% #coap_message{} = test_recv_coap_response(UdpSock), - -% ReadResult2 = emqx_json:encode(#{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"notify">>, -% <<"seqNum">> => ObSeq, -% <<"data">> => #{ -% <<"reqPath">> => <<"/3/0/10">>, -% <<"code">> => <<"2.05">>, -% <<"codeMsg">> => <<"content">>, -% <<"content">> => [#{ -% path => <<"/3/0/10">>, -% value => 4096 -% }] -% } -% }), -% ?assertEqual(ReadResult2, test_recv_mqtt_response(RespTopicAD)), - -% %% Step3. cancel observe -% CmdId3 = 308, -% Command3 = #{<<"requestID">> => CmdId3, <<"cacheID">> => CmdId3, -% <<"msgType">> => <<"cancel-observe">>, -% <<"data">> => #{ -% <<"path">> => <<"/3/0/10">> -% } -% }, -% CommandJson3 = emqx_json:encode(Command3), -% test_mqtt_broker:publish(CommandTopic, CommandJson3, 0), -% timer:sleep(50), -% Request3 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method3, options=Options3, payload=Payload3} = Request3, -% Path3 = get_coap_path(Options3), -% Observe3 = get_coap_observe(Options3), -% ?assertEqual(get, Method3), -% ?assertEqual(<<"/3/0/10">>, Path3), -% ?assertEqual(Observe3, 1), -% ?assertEqual(<<>>, Payload3), -% timer:sleep(50), - -% test_send_coap_observe_ack( UdpSock, -% "127.0.0.1", -% ?PORT, -% {ok, content}, -% #coap_content{content_format = <<"text/plain">>, payload = <<"1150">>}, -% Request3), -% timer:sleep(100), - -% ReadResult3 = emqx_json:encode(#{ -% <<"requestID">> => CmdId3, <<"cacheID">> => CmdId3, -% <<"msgType">> => <<"cancel-observe">>, -% <<"data">> => #{ -% <<"reqPath">> => <<"/3/0/10">>, -% <<"code">> => <<"2.05">>, -% <<"codeMsg">> => <<"content">>, -% <<"content">> => [#{ -% path => <<"/3/0/10">>, -% value => 1150 -% }] -% } -% }), -% ?assertEqual(ReadResult3, test_recv_mqtt_response(RespTopic)). - -% case80_specail_object_19_0_0_notify(Config) -> -% % step 1, device register, with extra register options -% Epn = "urn:oma:lwm2m:oma:3", -% RegOptionWangYi = "&apn=psmA.eDRX0.ctnb&im=13456&ct=2.0&mt=MDM9206&mv=4.0", -% MsgId1 = 15, -% UdpSock = ?config(sock, Config), - -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), - -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1"++RegOptionWangYi, [?PORT, Epn]), -% #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, -% [], -% MsgId1), -% #coap_message{method = Method1} = test_recv_coap_response(UdpSock), -% ?assertEqual({ok,created}, Method1), -% ReadResult = emqx_json:encode(#{ -% <<"msgType">> => <<"register">>, -% <<"data">> => #{ -% <<"alternatePath">> => <<"/">>, -% <<"ep">> => list_to_binary(Epn), -% <<"lt">> => 345, -% <<"lwm2m">> => <<"1">>, -% <<"objectList">> => [<<"/1">>, <<"/2">>, <<"/3">>, <<"/4">>, <<"/5">>], -% <<"apn">> => <<"psmA.eDRX0.ctnb">>, -% <<"im">> => <<"13456">>, -% <<"ct">> => <<"2.0">>, -% <<"mt">> => <<"MDM9206">>, -% <<"mv">> => <<"4.0">> -% } -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)), - -% % step2, send a OBSERVE command to device -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% CmdId = 307, -% Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"observe">>, -% <<"data">> => #{ -% <<"path">> => <<"/19/0/0">> -% } -% }, -% CommandJson = emqx_json:encode(Command), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, -% Path2 = get_coap_path(Options2), -% Observe = get_coap_observe(Options2), -% ?assertEqual(get, Method2), -% ?assertEqual(<<"/19/0/0">>, Path2), -% ?assertEqual(Observe, 0), -% ?assertEqual(<<>>, Payload2), -% timer:sleep(50), - -% test_send_coap_observe_ack( UdpSock, -% "127.0.0.1", -% ?PORT, -% {ok, content}, -% #coap_content{content_format = <<"text/plain">>, payload = <<"2048">>}, -% Request2), -% timer:sleep(100). - -% %% step 3, device send uplink data notifications - -% case80_specail_object_19_1_0_write(Config) -> -% Epn = "urn:oma:lwm2m:oma:3", -% RegOptionWangYi = "&apn=psmA.eDRX0.ctnb&im=13456&ct=2.0&mt=MDM9206&mv=4.0", -% MsgId1 = 15, -% UdpSock = ?config(sock, Config), -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), - -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1"++RegOptionWangYi, [?PORT, Epn]), -% #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, -% [], -% MsgId1), -% #coap_message{method = Method1} = test_recv_coap_response(UdpSock), -% ?assertEqual({ok,created}, Method1), -% test_recv_mqtt_response(RespTopic), - -% % step2, send a WRITE command to device -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% CmdId = 307, -% Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"write">>, -% <<"data">> => #{ -% <<"path">> => <<"/19/1/0">>, -% <<"type">> => <<"Opaque">>, -% <<"value">> => base64:encode(<<12345:32>>) -% } -% }, - -% CommandJson = emqx_json:encode(Command), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50), -% Request2 = test_recv_coap_request(UdpSock), -% #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, -% Path2 = get_coap_path(Options2), -% ?assertEqual(put, Method2), -% ?assertEqual(<<"/19/1/0">>, Path2), -% ?assertEqual(<<3:2, 0:1, 0:2, 4:3, 0, 12345:32>>, Payload2), -% timer:sleep(50), - -% test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, changed}, #coap_content{}, Request2, true), -% timer:sleep(100), - -% ReadResult = emqx_json:encode(#{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"data">> => #{ -% <<"reqPath">> => <<"/19/1/0">>, -% <<"code">> => <<"2.04">>, -% <<"codeMsg">> => <<"changed">> -% }, -% <<"msgType">> => <<"write">> -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). - -% case90_psm_mode(Config) -> -% server_cache_mode(Config, "ep=~s<=345&lwm2m=1&apn=psmA.eDRX0.ctnb"). - -% case90_queue_mode(Config) -> -% server_cache_mode(Config, "ep=~s<=345&lwm2m=1&b=UQ"). - -% server_cache_mode(Config, RegOption) -> -% application:set_env(?APP, qmode_time_window, 2), - -% % step 1, device register, with apn indicates "PSM" mode -% Epn = "urn:oma:lwm2m:oma:3", - -% MsgId1 = 15, -% UdpSock = ?config(sock, Config), -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), -% timer:sleep(200), - -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b/rd?"++RegOption, [?PORT, Epn]), -% #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, -% [], -% MsgId1), -% #coap_message{type = ack, method = Method1, options = Opts} = test_recv_coap_response(UdpSock), -% ?assertEqual({ok,created}, Method1), -% ?LOGT("Options got: ~p", [Opts]), -% Location = proplists:get_value(location_path, Opts), -% test_recv_mqtt_response(RespTopic), - -% %% server not in PSM mode -% send_read_command_1(0, UdpSock), -% verify_read_response_1(0, UdpSock), - -% %% server inters into PSM mode -% timer:sleep(2), - -% %% verify server caches downlink commands -% send_read_command_1(1, UdpSock), -% send_read_command_1(2, UdpSock), -% send_read_command_1(3, UdpSock), - -% ?assertEqual(timeout_test_recv_coap_request, test_recv_coap_request(UdpSock)), - -% device_update_1(UdpSock, Location), - -% verify_read_response_1(1, UdpSock), -% verify_read_response_1(2, UdpSock), -% verify_read_response_1(3, UdpSock). - -% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% %%% Internal Functions -% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -% send_read_command_1(CmdId, _UdpSock) -> -% Epn = "urn:oma:lwm2m:oma:3", -% CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, -% Command = #{ -% <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"read">>, -% <<"data">> => #{ -% <<"path">> => <<"/3/0/0">> -% } -% }, -% CommandJson = emqx_json:encode(Command), -% test_mqtt_broker:publish(CommandTopic, CommandJson, 0), -% timer:sleep(50). - -% verify_read_response_1(CmdId, UdpSock) -> -% Epn = "urn:oma:lwm2m:oma:3", -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), - -% %% device receives a command -% Request = test_recv_coap_request(UdpSock), -% ?LOGT("LwM2M client got ~p", [Request]), - -% %% device replies the commond -% test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, content}, #coap_content{content_format = <<"text/plain">>, payload = <<"EMQ">>}, Request, true), - -% ReadResult = emqx_json:encode(#{ <<"requestID">> => CmdId, <<"cacheID">> => CmdId, -% <<"msgType">> => <<"read">>, -% <<"data">> => #{ -% <<"code">> => <<"2.05">>, -% <<"codeMsg">> => <<"content">>, -% <<"content">> => [#{ -% path => <<"/3/0/0">>, -% value => <<"EMQ">> -% }] -% } -% }), -% ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). - -% device_update_1(UdpSock, Location) -> -% Epn = "urn:oma:lwm2m:oma:3", -% RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), -% ?LOGT("send UPDATE command", []), -% MsgId2 = 27, -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b~s?lt=789", [?PORT, join_path(Location, <<>>)]), -% #coap_content{payload = <<>>}, -% [], -% MsgId2), -% #coap_message{type = ack, id = MsgId2, method = Method2} = test_recv_coap_response(UdpSock), -% {ok,changed} = Method2, -% test_recv_mqtt_response(RespTopic). - -% test_recv_mqtt_response(RespTopic) -> -% receive -% {publish, #{topic := RespTopic, payload := RM}} -> -% ?LOGT("test_recv_mqtt_response Response=~p", [RM]), -% RM -% after 1000 -> timeout_test_recv_mqtt_response -% end. - -% test_send_coap_request(UdpSock, Method, Uri, Content, Options, MsgId) -> -% is_record(Content, coap_content) orelse error("Content must be a #coap_content!"), -% is_list(Options) orelse error("Options must be a list"), -% case resolve_uri(Uri) of -% {coap, {IpAddr, Port}, Path, Query} -> -% Request0 = lwm2m_coap_message:request(con, Method, Content, [{uri_path, Path}, {uri_query, Query} | Options]), -% Request = Request0#coap_message{id = MsgId}, -% ?LOGT("send_coap_request Request=~p", [Request]), -% RequestBinary = lwm2m_coap_message_parser:encode(Request), -% ?LOGT("test udp socket send to ~p:~p, data=~p", [IpAddr, Port, RequestBinary]), -% ok = gen_udp:send(UdpSock, IpAddr, Port, RequestBinary); -% {SchemeDiff, ChIdDiff, _, _} -> -% error(lists:flatten(io_lib:format("scheme ~s or ChId ~s does not match with socket", [SchemeDiff, ChIdDiff]))) -% end. - -% test_recv_coap_response(UdpSock) -> -% {ok, {Address, Port, Packet}} = gen_udp:recv(UdpSock, 0, 2000), -% Response = lwm2m_coap_message_parser:decode(Packet), -% ?LOGT("test udp receive from ~p:~p, data1=~p, Response=~p", [Address, Port, Packet, Response]), -% #coap_message{type = ack, method = Method, id=Id, token = Token, options = Options, payload = Payload} = Response, -% ?LOGT("receive coap response Method=~p, Id=~p, Token=~p, Options=~p, Payload=~p", [Method, Id, Token, Options, Payload]), -% Response. - -% test_recv_coap_request(UdpSock) -> -% case gen_udp:recv(UdpSock, 0, 2000) of -% {ok, {_Address, _Port, Packet}} -> -% Request = lwm2m_coap_message_parser:decode(Packet), -% #coap_message{type = con, method = Method, id=Id, token = Token, payload = Payload, options = Options} = Request, -% ?LOGT("receive coap request Method=~p, Id=~p, Token=~p, Options=~p, Payload=~p", [Method, Id, Token, Options, Payload]), -% Request; -% {error, Reason} -> -% ?LOGT("test_recv_coap_request failed, Reason=~p", [Reason]), -% timeout_test_recv_coap_request -% end. - -% test_send_coap_response(UdpSock, Host, Port, Code, Content, Request, Ack) -> -% is_record(Content, coap_content) orelse error("Content must be a #coap_content!"), -% is_list(Host) orelse error("Host is not a string"), - -% {ok, IpAddr} = inet:getaddr(Host, inet), -% Response = lwm2m_coap_message:response(Code, Content, Request), -% Response2 = case Ack of -% true -> Response#coap_message{type = ack}; -% false -> Response -% end, -% ?LOGT("test_send_coap_response Response=~p", [Response2]), -% ok = gen_udp:send(UdpSock, IpAddr, Port, lwm2m_coap_message_parser:encode(Response2)). - -% test_send_empty_ack(UdpSock, Host, Port, Request) -> -% is_list(Host) orelse error("Host is not a string"), -% {ok, IpAddr} = inet:getaddr(Host, inet), -% EmptyACK = lwm2m_coap_message:ack(Request), -% ?LOGT("test_send_empty_ack EmptyACK=~p", [EmptyACK]), -% ok = gen_udp:send(UdpSock, IpAddr, Port, lwm2m_coap_message_parser:encode(EmptyACK)). - -% test_send_coap_observe_ack(UdpSock, Host, Port, Code, Content, Request) -> -% is_record(Content, coap_content) orelse error("Content must be a #coap_content!"), -% is_list(Host) orelse error("Host is not a string"), - -% {ok, IpAddr} = inet:getaddr(Host, inet), -% Response = lwm2m_coap_message:response(Code, Content, Request), -% Response1 = lwm2m_coap_message:set(observe, 0, Response), -% Response2 = Response1#coap_message{type = ack}, - -% ?LOGT("test_send_coap_observe_ack Response=~p", [Response2]), -% ResponseBinary = lwm2m_coap_message_parser:encode(Response2), -% ok = gen_udp:send(UdpSock, IpAddr, Port, ResponseBinary). - -% test_send_coap_notif(UdpSock, Host, Port, Content, ObSeq, Request) -> -% is_record(Content, coap_content) orelse error("Content must be a #coap_content!"), -% is_list(Host) orelse error("Host is not a string"), - -% {ok, IpAddr} = inet:getaddr(Host, inet), -% Notif = lwm2m_coap_message:response({ok, content}, Content, Request), -% NewNotif = lwm2m_coap_message:set(observe, ObSeq, Notif), -% ?LOGT("test_send_coap_notif Response=~p", [NewNotif]), -% NotifBinary = lwm2m_coap_message_parser:encode(NewNotif), -% ?LOGT("test udp socket send to ~p:~p, data=~p", [IpAddr, Port, NotifBinary]), -% ok = gen_udp:send(UdpSock, IpAddr, Port, NotifBinary). - -% std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic) -> -% test_send_coap_request( UdpSock, -% post, -% sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1", [?PORT, Epn]), -% #coap_content{content_format = <<"text/plain">>, payload = ObjectList}, -% [], -% MsgId1), -% #coap_message{method = {ok,created}} = test_recv_coap_response(UdpSock), -% test_recv_mqtt_response(RespTopic), -% timer:sleep(100). - -% resolve_uri(Uri) -> -% {ok, #{scheme := Scheme, -% host := Host, -% port := PortNo, -% path := Path} = URIMap} = emqx_http_lib:uri_parse(Uri), -% Query = maps:get(query, URIMap, ""), -% {ok, PeerIP} = inet:getaddr(Host, inet), -% {Scheme, {PeerIP, PortNo}, split_path(Path), split_query(Query)}. - -% split_path([]) -> []; -% split_path([$/]) -> []; -% split_path([$/ | Path]) -> split_segments(Path, $/, []). - -% split_query([]) -> []; -% split_query(Path) -> split_segments(Path, $&, []). - -% split_segments(Path, Char, Acc) -> -% case string:rchr(Path, Char) of -% 0 -> -% [make_segment(Path) | Acc]; -% N when N > 0 -> -% split_segments(string:substr(Path, 1, N-1), Char, -% [make_segment(string:substr(Path, N+1)) | Acc]) -% end. - -% make_segment(Seg) -> -% list_to_binary(emqx_http_lib:uri_decode(Seg)). - - -% get_coap_path(Options) -> -% get_path(Options, <<>>). - -% get_coap_query(Options) -> -% proplists:get_value(uri_query, Options, []). - -% get_coap_observe(Options) -> -% get_observe(Options). - - -% get_path([], Acc) -> -% %?LOGT("get_path Acc=~p", [Acc]), -% Acc; -% get_path([{uri_path, Path1}|T], Acc) -> -% %?LOGT("Path=~p, Acc=~p", [Path1, Acc]), -% get_path(T, join_path(Path1, Acc)); -% get_path([{_, _}|T], Acc) -> -% get_path(T, Acc). - -% get_observe([]) -> -% undefined; -% get_observe([{observe, V}|_T]) -> -% V; -% get_observe([{_, _}|T]) -> -% get_observe(T). - -% join_path([], Acc) -> Acc; -% join_path([<<"/">>|T], Acc) -> -% join_path(T, Acc); -% join_path([H|T], Acc) -> -% join_path(T, <>). - -% sprintf(Format, Args) -> -% lists:flatten(io_lib:format(Format, Args)). diff --git a/apps/emqx_gateway/src/lwm2m/test/emqx_tlv_SUITE.erl b/apps/emqx_gateway/src/lwm2m/test/emqx_tlv_SUITE.erl deleted file mode 100644 index d4d3c70cf..000000000 --- a/apps/emqx_gateway/src/lwm2m/test/emqx_tlv_SUITE.erl +++ /dev/null @@ -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). - diff --git a/apps/emqx_gateway/src/lwm2m/test/test_mqtt_broker.erl b/apps/emqx_gateway/src/lwm2m/test/test_mqtt_broker.erl deleted file mode 100644 index 4a6c77444..000000000 --- a/apps/emqx_gateway/src/lwm2m/test/test_mqtt_broker.erl +++ /dev/null @@ -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). diff --git a/apps/emqx_gateway/test/emqx_lwm2m_SUITE.erl b/apps/emqx_gateway/test/emqx_lwm2m_SUITE.erl new file mode 100644 index 000000000..0e19d9b4f --- /dev/null +++ b/apps/emqx_gateway/test/emqx_lwm2m_SUITE.erl @@ -0,0 +1,1971 @@ +%%-------------------------------------------------------------------- +%% 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_lwm2m_SUITE). + +-compile(export_all). +-compile(nowarn_export_all). + +-define(PORT, 5783). + +-define(LOGT(Format, Args), ct:pal("TEST_SUITE: " ++ Format, Args)). + +-include("src/lwm2m/include/emqx_lwm2m.hrl"). +-include_lib("lwm2m_coap/include/coap.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("common_test/include/ct.hrl"). + +-define(CONF_DEFAULT, <<" +gateway: { + lwm2m_xml_dir: \"../../lib/emqx_gateway/src/lwm2m/lwm2m_xml\" + lwm2m.1: { + lifetime_min: 1s + lifetime_max: 86400s + qmode_time_windonw: 22 + auto_observe: false + mountpoint: \"lwm2m/%e/\" + 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 + } + } +} +">>). + +%%-------------------------------------------------------------------- +%% Setups +%%-------------------------------------------------------------------- + +all() -> + [ {group, test_grp_0_register} + , {group, test_grp_1_read} + , {group, test_grp_2_write} + , {group, test_grp_3_execute} + , {group, test_grp_4_discover} + , {group, test_grp_5_write_attr} + , {group, test_grp_6_observe} + , {group, test_grp_8_object_19} + ]. + +suite() -> [{timetrap, {seconds, 90}}]. + +groups() -> + RepeatOpt = {repeat_until_all_ok, 1}, + [ + {test_grp_0_register, [RepeatOpt], [ + case01_register, + case01_register_additional_opts, + case01_register_incorrect_opts, + case01_register_report, + case02_update_deregister, + case03_register_wrong_version, + case04_register_and_lifetime_timeout, + case05_register_wrong_epn, + case06_register_wrong_lifetime, + case07_register_alternate_path_01, + case07_register_alternate_path_02, + case08_reregister + ]}, + {test_grp_1_read, [RepeatOpt], [ + case10_read, + case10_read_separate_ack, + case11_read_object_tlv, + case11_read_object_json, + case12_read_resource_opaque, + case13_read_no_xml + ]}, + {test_grp_2_write, [RepeatOpt], [ + case20_write, + case21_write_object, + case22_write_error, + case20_single_write + ]}, + {test_grp_create, [RepeatOpt], [ + case_create_basic + ]}, + {test_grp_delete, [RepeatOpt], [ + case_delete_basic + ]}, + {test_grp_3_execute, [RepeatOpt], [ + case30_execute, case31_execute_error + ]}, + {test_grp_4_discover, [RepeatOpt], [ + case40_discover + ]}, + {test_grp_5_write_attr, [RepeatOpt], [ + case50_write_attribute + ]}, + {test_grp_6_observe, [RepeatOpt], [ + case60_observe + ]}, + {test_grp_7_block_wize_transfer, [RepeatOpt], [ + case70_read_large, case70_write_large + ]}, + {test_grp_8_object_19, [RepeatOpt], [ + case80_specail_object_19_1_0_write, + case80_specail_object_19_0_0_notify + %case80_specail_object_19_0_0_response, + %case80_normal_object_19_0_0_read + ]}, + {test_grp_9_psm_queue_mode, [RepeatOpt], [ + case90_psm_mode, + case90_queue_mode + ]} + ]. + +init_per_suite(Config) -> + emqx_ct_helpers:start_apps([emqx]), + Config. + +end_per_suite(Config) -> + timer:sleep(300), + emqx_ct_helpers:stop_apps([emqx]), + Config. + +init_per_testcase(_AllTestCase, Config) -> + ok = emqx_config:init_load(emqx_gateway_schema, ?CONF_DEFAULT), + {ok, _} = application:ensure_all_started(emqx_gateway), + {ok, ClientUdpSock} = gen_udp:open(0, [binary, {active, false}]), + + {ok, C} = emqtt:start_link([{host, "localhost"},{port, 1883},{clientid, <<"c1">>}]), + {ok, _} = emqtt:connect(C), + timer:sleep(100), + + [{sock, ClientUdpSock}, {emqx_c, C} | Config]. + +end_per_testcase(_AllTestCase, Config) -> + timer:sleep(300), + gen_udp:close(?config(sock, Config)), + emqtt:disconnect(?config(emqx_c, Config)), + ok = application:stop(emqx_gateway). + +%%-------------------------------------------------------------------- +%% Cases +%%-------------------------------------------------------------------- + +case01_register(Config) -> + % ---------------------------------------- + % REGISTER command + % ---------------------------------------- + UdpSock = ?config(sock, Config), + Epn = "urn:oma:lwm2m:oma:3", + MsgId = 12, + SubTopic = list_to_binary("lwm2m/"++Epn++"/dn/#"), + + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1", [?PORT, Epn]), + #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, + [], + MsgId), + + %% checkpoint 1 - response + #coap_message{type = Type, method = Method, id = RspId, options = Opts} = + test_recv_coap_response(UdpSock), + ack = Type, + {ok, created} = Method, + RspId = MsgId, + Location = proplists:get_value(location_path, Opts), + ?assertNotEqual(undefined, Location), + + %% checkpoint 2 - verify subscribed topics + timer:sleep(50), + ?LOGT("all topics: ~p", [test_mqtt_broker:get_subscrbied_topics()]), + true = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()), + + % ---------------------------------------- + % DE-REGISTER command + % ---------------------------------------- + ?LOGT("start to send DE-REGISTER command", []), + MsgId3 = 52, + test_send_coap_request( UdpSock, + delete, + sprintf("coap://127.0.0.1:~b~s", [?PORT, join_path(Location, <<>>)]), + #coap_content{payload = <<>>}, + [], + MsgId3), + #coap_message{type = ack, id = RspId3, method = Method3} = test_recv_coap_response(UdpSock), + {ok,deleted} = Method3, + MsgId3 = RspId3, + timer:sleep(50), + false = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()). + +case01_register_additional_opts(Config) -> + % ---------------------------------------- + % REGISTER command + % ---------------------------------------- + UdpSock = ?config(sock, Config), + Epn = "urn:oma:lwm2m:oma:3", + MsgId = 12, + SubTopic = list_to_binary("lwm2m/"++Epn++"/dn/#"), + + AddOpts = "ep=~s<=345&lwm2m=1&apn=psmA.eDRX0.ctnb&cust_opt=shawn&im=123&ct=1.4&mt=mdm9620&mv=1.2", + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b/rd?" ++ AddOpts, [?PORT, Epn]), + #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, + [], + MsgId), + + %% checkpoint 1 - response + #coap_message{type = Type, method = Method, id = RspId, options = Opts} = + test_recv_coap_response(UdpSock), + Type = ack, + Method = {ok, created}, + RspId = MsgId, + Location = proplists:get_value(location_path, Opts), + ?assertNotEqual(undefined, Location), + + %% checkpoint 2 - verify subscribed topics + timer:sleep(50), + + true = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()), + + % ---------------------------------------- + % DE-REGISTER command + % ---------------------------------------- + ?LOGT("start to send DE-REGISTER command", []), + MsgId3 = 52, + test_send_coap_request( UdpSock, + delete, + sprintf("coap://127.0.0.1:~b~s", [?PORT, join_path(Location, <<>>)]), + #coap_content{payload = <<>>}, + [], + MsgId3), + #coap_message{type = ack, id = RspId3, method = Method3} = test_recv_coap_response(UdpSock), + {ok,deleted} = Method3, + MsgId3 = RspId3, + timer:sleep(50), + false = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()). + +case01_register_incorrect_opts(Config) -> + % ---------------------------------------- + % REGISTER command + % ---------------------------------------- + UdpSock = ?config(sock, Config), + Epn = "urn:oma:lwm2m:oma:3", + MsgId = 12, + + + AddOpts = "ep=~s<=345&lwm2m=1&incorrect_opt", + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b/rd?" ++ AddOpts, [?PORT, Epn]), + #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, + [], + MsgId), + + %% checkpoint 1 - response + #coap_message{type = ack, method = Method, id = MsgId} = + test_recv_coap_response(UdpSock), + ?assertEqual({error,bad_request}, Method). + +case01_register_report(Config) -> + % ---------------------------------------- + % REGISTER command + % ---------------------------------------- + UdpSock = ?config(sock, Config), + Epn = "urn:oma:lwm2m:oma:3", + MsgId = 12, + SubTopic = list_to_binary("lwm2m/"++Epn++"/dn/#"), + ReportTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), ReportTopic, qos0), + timer:sleep(200), + + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1", [?PORT, Epn]), + #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, + [], + MsgId), + + #coap_message{type = Type, method = Method, id = RspId, options = Opts} = + test_recv_coap_response(UdpSock), + Type = ack, + Method = {ok, created}, + RspId = MsgId, + Location = proplists:get_value(location_path, Opts), + ?assertNotEqual(undefined, Location), + + timer:sleep(50), + true = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()), + + ReadResult = emqx_json:encode(#{ + <<"msgType">> => <<"register">>, + <<"data">> => #{ + <<"alternatePath">> => <<"/">>, + <<"ep">> => list_to_binary(Epn), + <<"lt">> => 345, + <<"lwm2m">> => <<"1">>, + <<"objectList">> => [<<"/1">>, <<"/2">>, <<"/3">>, <<"/4">>, <<"/5">>] + } + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(ReportTopic)), + + % ---------------------------------------- + % DE-REGISTER command + % ---------------------------------------- + ?LOGT("start to send DE-REGISTER command", []), + MsgId3 = 52, + test_send_coap_request( UdpSock, + delete, + sprintf("coap://127.0.0.1:~b~s", [?PORT, join_path(Location, <<>>)]), + #coap_content{payload = <<>>}, + [], + MsgId3), + #coap_message{type = ack, id = RspId3, method = Method3} = test_recv_coap_response(UdpSock), + {ok,deleted} = Method3, + MsgId3 = RspId3, + timer:sleep(50), + false = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()). + +case02_update_deregister(Config) -> + % ---------------------------------------- + % REGISTER command + % ---------------------------------------- + UdpSock = ?config(sock, Config), + Epn = "urn:oma:lwm2m:oma:3", + MsgId = 12, + SubTopic = list_to_binary("lwm2m/"++Epn++"/dn/#"), + ReportTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), ReportTopic, qos0), + timer:sleep(200), + + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1", [?PORT, Epn]), + #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, + [], + MsgId), + timer:sleep(100), + #coap_message{type = ack, method = Method, options = Opts} = test_recv_coap_response(UdpSock), + ?assertEqual({ok,created}, Method), + + ?LOGT("Options got: ~p", [Opts]), + Location = proplists:get_value(location_path, Opts), + Register = emqx_json:encode(#{ + <<"msgType">> => <<"register">>, + <<"data">> => #{ + <<"alternatePath">> => <<"/">>, + <<"ep">> => list_to_binary(Epn), + <<"lt">> => 345, + <<"lwm2m">> => <<"1">>, + <<"objectList">> => [<<"/1">>, <<"/2">>, <<"/3">>, <<"/4">>, <<"/5">>] + } + }), + ?assertEqual(Register, test_recv_mqtt_response(ReportTopic)), + + % ---------------------------------------- + % UPDATE command + % ---------------------------------------- + ?LOGT("start to send UPDATE command", []), + MsgId2 = 27, + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b~s?lt=789", [?PORT, join_path(Location, <<>>)]), + #coap_content{content_format = <<"text/plain">>, payload = <<", , , , , ">>}, + [], + MsgId2), + #coap_message{type = ack, id = RspId2, method = Method2} = test_recv_coap_response(UdpSock), + {ok,changed} = Method2, + MsgId2 = RspId2, + Update = emqx_json:encode(#{ + <<"msgType">> => <<"update">>, + <<"data">> => #{ + <<"alternatePath">> => <<"/">>, + <<"ep">> => list_to_binary(Epn), + <<"lt">> => 789, + <<"lwm2m">> => <<"1">>, + <<"objectList">> => [<<"/1">>, <<"/2">>, <<"/3">>, <<"/4">>, <<"/5">>, <<"/6">>] + } + }), + ?assertEqual(Update, test_recv_mqtt_response(ReportTopic)), + + % ---------------------------------------- + % DE-REGISTER command + % ---------------------------------------- + ?LOGT("start to send DE-REGISTER command", []), + MsgId3 = 52, + test_send_coap_request( UdpSock, + delete, + sprintf("coap://127.0.0.1:~b~s", [?PORT, join_path(Location, <<>>)]), + #coap_content{payload = <<>>}, + [], + MsgId3), + #coap_message{type = ack, id = RspId3, method = Method3} = test_recv_coap_response(UdpSock), + {ok,deleted} = Method3, + MsgId3 = RspId3, + + timer:sleep(50), + false = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()). + +case03_register_wrong_version(Config) -> + % ---------------------------------------- + % REGISTER command + % ---------------------------------------- + UdpSock = ?config(sock, Config), + Epn = "urn:oma:lwm2m:oma:3", + MsgId = 12, + SubTopic = list_to_binary("lwm2m/"++Epn++"/dn/#"), + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=8.3", [?PORT, Epn]), + #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, + [], + MsgId), + #coap_message{type = ack, method = Method} = test_recv_coap_response(UdpSock), + ?assertEqual({error,precondition_failed}, Method), + timer:sleep(50), + + false = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()). + +case04_register_and_lifetime_timeout(Config) -> + % ---------------------------------------- + % REGISTER command + % ---------------------------------------- + UdpSock = ?config(sock, Config), + Epn = "urn:oma:lwm2m:oma:3", + MsgId = 12, + SubTopic = list_to_binary("lwm2m/"++Epn++"/dn/#"), + + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b/rd?ep=~s<=2&lwm2m=1", [?PORT, Epn]), + #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, + [], + MsgId), + timer:sleep(100), + #coap_message{type = ack, method = Method} = test_recv_coap_response(UdpSock), + ?assertEqual({ok,created}, Method), + + true = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()), + + % ---------------------------------------- + % lifetime timeout + % ---------------------------------------- + timer:sleep(4000), + + false = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()). + +case05_register_wrong_epn(Config) -> + % ---------------------------------------- + % REGISTER command + % ---------------------------------------- + MsgId = 12, + UdpSock = ?config(sock, Config), + + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b/rd?lt=345&lwm2m=1.0", [?PORT]), + #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, + [], + MsgId), + #coap_message{type = ack, method = Method} = test_recv_coap_response(UdpSock), + ?assertEqual({error,bad_request}, Method). + +case06_register_wrong_lifetime(Config) -> + % ---------------------------------------- + % REGISTER command + % ---------------------------------------- + UdpSock = ?config(sock, Config), + Epn = "urn:oma:lwm2m:oma:3", + MsgId = 12, + + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b/rd?ep=~s&lwm2m=1", [?PORT, Epn]), + #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, + [], + MsgId), + #coap_message{type = ack, method = Method} = test_recv_coap_response(UdpSock), + ?assertEqual({error,bad_request}, Method), + timer:sleep(50), + ?assertEqual([], test_mqtt_broker:get_subscrbied_topics()). + +case07_register_alternate_path_01(Config) -> + % ---------------------------------------- + % REGISTER command + % ---------------------------------------- + UdpSock = ?config(sock, Config), + Epn = "urn:oma:lwm2m:oma:3", + MsgId = 12, + SubTopic = list_to_binary("lwm2m/"++Epn++"/dn/#"), + ReportTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), ReportTopic, qos0), + timer:sleep(200), + + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1", [?PORT, Epn]), + #coap_content{content_format = <<"text/plain">>, + payload = <<";rt=\"oma.lwm2m\";ct=11543,,,">>}, + [], + MsgId), + timer:sleep(50), + true = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()). + +case07_register_alternate_path_02(Config) -> + % ---------------------------------------- + % REGISTER command + % ---------------------------------------- + UdpSock = ?config(sock, Config), + Epn = "urn:oma:lwm2m:oma:3", + MsgId = 12, + SubTopic = list_to_binary("lwm2m/"++Epn++"/dn/#"), + ReportTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), ReportTopic, qos0), + timer:sleep(200), + + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1", [?PORT, Epn]), + #coap_content{content_format = <<"text/plain">>, + payload = <<";rt=\"oma.lwm2m\";ct=11543,,,">>}, + [], + MsgId), + timer:sleep(50), + true = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()). + +case08_reregister(Config) -> + % ---------------------------------------- + % REGISTER command + % ---------------------------------------- + UdpSock = ?config(sock, Config), + Epn = "urn:oma:lwm2m:oma:3", + MsgId = 12, + SubTopic = list_to_binary("lwm2m/"++Epn++"/dn/#"), + ReportTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), ReportTopic, qos0), + timer:sleep(200), + + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1", [?PORT, Epn]), + #coap_content{content_format = <<"text/plain">>, + payload = <<";rt=\"oma.lwm2m\";ct=11543,,,">>}, + [], + MsgId), + timer:sleep(50), + true = lists:member(SubTopic, test_mqtt_broker:get_subscrbied_topics()), + + ReadResult = emqx_json:encode( + #{ + <<"msgType">> => <<"register">>, + <<"data">> => #{ + <<"alternatePath">> => <<"/lwm2m">>, + <<"ep">> => list_to_binary(Epn), + <<"lt">> => 345, + <<"lwm2m">> => <<"1">>, + <<"objectList">> => [<<"/1/0">>, <<"/2/0">>, <<"/3/0">>] + } + } + ), + ?assertEqual(ReadResult, test_recv_mqtt_response(ReportTopic)), + timer:sleep(1000), + + %% the same lwm2mc client registers to server again + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1", [?PORT, Epn]), + #coap_content{content_format = <<"text/plain">>, + payload = <<";rt=\"oma.lwm2m\";ct=11543,,,">>}, + [], + MsgId + 1), + %% verify the lwm2m client is still online + ?assertEqual(ReadResult, test_recv_mqtt_response(ReportTopic)). + +case10_read(Config) -> + UdpSock = ?config(sock, Config), + Epn = "urn:oma:lwm2m:oma:3", + MsgId1 = 15, + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + % step 1, device register ... + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1", [?PORT, Epn]), + #coap_content{content_format = <<"text/plain">>, + payload = <<";rt=\"oma.lwm2m\";ct=11543,,,">>}, + [], + MsgId1), + #coap_message{method = Method1} = test_recv_coap_response(UdpSock), + ?assertEqual({ok,created}, Method1), + test_recv_mqtt_response(RespTopic), + + % step2, send a READ command to device + CmdId = 206, + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + Command = #{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"read">>, + <<"data">> => #{ + <<"path">> => <<"/3/0/0">> + } + }, + CommandJson = emqx_json:encode(Command), + ?LOGT("CommandJson=~p", [CommandJson]), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, + ?LOGT("LwM2M client got ~p", [Request2]), + + ?assertEqual(get, Method2), + ?assertEqual(<<"/lwm2m/3/0/0">>, get_coap_path(Options2)), + ?assertEqual(<<>>, Payload2), + timer:sleep(50), + + test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, content}, #coap_content{content_format = <<"text/plain">>, payload = <<"EMQ">>}, Request2, true), + timer:sleep(100), + + ReadResult = emqx_json:encode(#{ <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"read">>, + <<"data">> => #{ + <<"code">> => <<"2.05">>, + <<"codeMsg">> => <<"content">>, + <<"reqPath">> => <<"/3/0/0">>, + <<"content">> => [#{ + path => <<"/3/0/0">>, + value => <<"EMQ">> + }] + } + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). + +case10_read_separate_ack(Config) -> + UdpSock = ?config(sock, Config), + Epn = "urn:oma:lwm2m:oma:3", + MsgId1 = 15, + ObjectList = <<", , , , ">>, + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + + % step 1, device register ... + std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), + + % step2, send a READ command to device + CmdId = 206, + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + Command = #{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"read">>, + <<"data">> => #{ + <<"path">> => <<"/3/0/0">> + } + }, + CommandJson = emqx_json:encode(Command), + ?LOGT("CommandJson=~p", [CommandJson]), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, + ?LOGT("LwM2M client got ~p", [Request2]), + + ?assertEqual(get, Method2), + ?assertEqual(<<"/3/0/0">>, get_coap_path(Options2)), + ?assertEqual(<<>>, Payload2), + + test_send_empty_ack(UdpSock, "127.0.0.1", ?PORT, Request2), + ReadResultACK = emqx_json:encode(#{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"ack">>, + <<"data">> => #{ + <<"path">> => <<"/3/0/0">> + } + }), + ?assertEqual(ReadResultACK, test_recv_mqtt_response(RespTopic)), + timer:sleep(100), + + test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, content}, #coap_content{content_format = <<"text/plain">>, payload = <<"EMQ">>}, Request2, false), + timer:sleep(100), + + ReadResult = emqx_json:encode(#{ <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"read">>, + <<"data">> => #{ + <<"code">> => <<"2.05">>, + <<"codeMsg">> => <<"content">>, + <<"reqPath">> => <<"/3/0/0">>, + <<"content">> => [#{ + path => <<"/3/0/0">>, + value => <<"EMQ">> + }] + } + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). + +case11_read_object_tlv(Config) -> + % step 1, device register ... + Epn = "urn:oma:lwm2m:oma:3", + MsgId1 = 15, + UdpSock = ?config(sock, Config), + ObjectList = <<", , , , ">>, + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + + std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), + + % step2, send a READ command to device + CmdId = 207, + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + Command = #{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"read">>, + <<"data">> => #{ + <<"path">> => <<"/3/0">> + } + }, + CommandJson = emqx_json:encode(Command), + ?LOGT("CommandJson=~p", [CommandJson]), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2} = Request2, + ?LOGT("LwM2M client got ~p", [Request2]), + + ?assertEqual(get, Method2), + timer:sleep(50), + + Tlv = <<16#08, 16#00, 16#3C, 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>>, + test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, content}, #coap_content{content_format = <<"application/vnd.oma.lwm2m+tlv">>, payload = Tlv}, Request2, true), + timer:sleep(100), + + ReadResult = emqx_json:encode(#{ <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"read">>, + <<"data">> => #{ + <<"code">> => <<"2.05">>, + <<"codeMsg">> => <<"content">>, + <<"reqPath">> => <<"/3/0">>, + <<"content">> => [ + #{ + path => <<"/3/0/0">>, + value => <<"Open Mobile Alliance">> + }, + #{ + path => <<"/3/0/1">>, + value => <<"Lightweight M2M Client">> + }, + #{ + path => <<"/3/0/2">>, + value => <<"345000123">> + } + ] + } + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). + +case11_read_object_json(Config) -> + % step 1, device register ... + UdpSock = ?config(sock, Config), + Epn = "urn:oma:lwm2m:oma:3", + MsgId1 = 15, + + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + ObjectList = <<", , , , ">>, + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + + std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), + + % step2, send a READ command to device + CmdId = 206, + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + Command = #{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"read">>, + <<"data">> => #{ + <<"path">> => <<"/3/0">> + } + }, + CommandJson = emqx_json:encode(Command), + ?LOGT("CommandJson=~p", [CommandJson]), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2} = Request2, + ?LOGT("LwM2M client got ~p", [Request2]), + + ?assertEqual(get, Method2), + timer:sleep(50), + + Json = <<"{\"bn\":\"/3/0\",\"e\":[{\"n\":\"0\",\"sv\":\"Open Mobile Alliance\"},{\"n\":\"1\",\"sv\":\"Lightweight M2M Client\"},{\"n\":\"2\",\"sv\":\"345000123\"}]}">>, + test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, content}, #coap_content{content_format = <<"application/vnd.oma.lwm2m+json">>, payload = Json}, Request2, true), + timer:sleep(100), + + ReadResult = emqx_json:encode(#{ <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"read">>, + <<"data">> => #{ + <<"code">> => <<"2.05">>, + <<"codeMsg">> => <<"content">>, + <<"reqPath">> => <<"/3/0">>, + <<"content">> => [ + #{ + path => <<"/3/0/0">>, + value => <<"Open Mobile Alliance">> + }, + #{ + path => <<"/3/0/1">>, + value => <<"Lightweight M2M Client">> + }, + #{ + path => <<"/3/0/2">>, + value => <<"345000123">> + } + ] + } + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). + +case12_read_resource_opaque(Config) -> + % step 1, device register ... + UdpSock = ?config(sock, Config), + Epn = "urn:oma:lwm2m:oma:3", + MsgId1 = 15, + ObjectList = <<", , , , ">>, + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + + std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), + + % step2, send a READ command to device + CmdId = 206, + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + Command = #{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"read">>, + <<"data">> => #{ + <<"path">> => <<"/3/0/8">> + } + }, + CommandJson = emqx_json:encode(Command), + ?LOGT("CommandJson=~p", [CommandJson]), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2} = Request2, + ?LOGT("LwM2M client got ~p", [Request2]), + + ?assertEqual(get, Method2), + timer:sleep(50), + + Opaque = <<20, 21, 22, 23>>, + test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, content}, #coap_content{content_format = <<"application/octet-stream">>, payload = Opaque}, Request2, true), + timer:sleep(100), + + ReadResult = emqx_json:encode(#{ <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"read">>, + <<"data">> => #{ + <<"code">> => <<"2.05">>, + <<"codeMsg">> => <<"content">>, + <<"reqPath">> => <<"/3/0/8">>, + <<"content">> => [ + #{ + path => <<"/3/0/8">>, + value => base64:encode(Opaque) + } + ] + } + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). + +case13_read_no_xml(Config) -> + % step 1, device register ... + Epn = "urn:oma:lwm2m:oma:3", + MsgId1 = 15, + UdpSock = ?config(sock, Config), + ObjectList = <<", , , , ">>, + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + + std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), + + % step2, send a READ command to device + CmdId = 206, + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + Command = #{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"read">>, + <<"data">> => #{ + <<"path">> => <<"/9723/0/0">> + } + }, + CommandJson = emqx_json:encode(Command), + ?LOGT("CommandJson=~p", [CommandJson]), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2} = Request2, + ?LOGT("LwM2M client got ~p", [Request2]), + + ?assertEqual(get, Method2), + timer:sleep(50), + + test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, content}, #coap_content{content_format = <<"text/plain">>, payload = <<"EMQ">>}, Request2, true), + timer:sleep(100), + + ReadResult = emqx_json:encode(#{ <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"read">>, + <<"data">> => #{ + <<"reqPath">> => <<"/9723/0/0">>, + <<"code">> => <<"4.00">>, + <<"codeMsg">> => <<"bad_request">> + } + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). + +case20_single_write(Config) -> + % step 1, device register ... + Epn = "urn:oma:lwm2m:oma:3", + MsgId1 = 15, + UdpSock = ?config(sock, Config), + ObjectList = <<", , , , ">>, + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + + std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), + + % step2, send a WRITE command to device + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + CmdId = 307, + Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"write">>, + <<"data">> => #{ + <<"path">> => <<"/3/0/13">>, + <<"type">> => <<"Integer">>, + <<"value">> => <<"12345">> + } + }, + CommandJson = emqx_json:encode(Command), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, + Path2 = get_coap_path(Options2), + ?assertEqual(put, Method2), + ?assertEqual(<<"/3/0/13">>, Path2), + Tlv_Value = <<3:2, 0:1, 0:2, 2:3, 13, 12345:16>>, + ?assertEqual(Tlv_Value, Payload2), + timer:sleep(50), + + test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, changed}, #coap_content{}, Request2, true), + timer:sleep(100), + + ReadResult = emqx_json:encode(#{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"data">> => #{ + <<"reqPath">> => <<"/3/0/13">>, + <<"code">> => <<"2.04">>, + <<"codeMsg">> => <<"changed">> + }, + <<"msgType">> => <<"write">> + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). + +case20_write(Config) -> + % step 1, device register ... + Epn = "urn:oma:lwm2m:oma:3", + MsgId1 = 15, + UdpSock = ?config(sock, Config), + ObjectList = <<", , , , ">>, + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + + std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), + + % step2, send a WRITE command to device + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + CmdId = 307, + Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"write">>, + <<"data">> => #{ + <<"basePath">> => <<"/3/0/13">>, + <<"content">> => [#{ + type => <<"Float">>, + value => <<"12345.0">> + }] + } + }, + CommandJson = emqx_json:encode(Command), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, + Path2 = get_coap_path(Options2), + ?assertEqual(put, Method2), + ?assertEqual(<<"/3/0/13">>, Path2), + Tlv_Value = <<200, 13, 8, 64,200,28,128,0,0,0,0>>, + ?assertEqual(Tlv_Value, Payload2), + timer:sleep(50), + + test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, changed}, #coap_content{}, Request2, true), + timer:sleep(100), + + WriteResult = emqx_json:encode(#{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"data">> => #{ + <<"reqPath">> => <<"/3/0/13">>, + <<"code">> => <<"2.04">>, + <<"codeMsg">> => <<"changed">> + }, + <<"msgType">> => <<"write">> + }), + ?assertEqual(WriteResult, test_recv_mqtt_response(RespTopic)). + +case21_write_object(Config) -> + % step 1, device register ... + Epn = "urn:oma:lwm2m:oma:3", + MsgId1 = 15, + UdpSock = ?config(sock, Config), + ObjectList = <<", , , , ">>, + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + + std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), + + % step2, send a WRITE command to device + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + CmdId = 307, + Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"write">>, + <<"data">> => #{ + <<"basePath">> => <<"/3/0/">>, + <<"content">> => [#{ + path => <<"13">>, + type => <<"Integer">>, + value => <<"12345">> + },#{ + path => <<"14">>, + type => <<"String">>, + value => <<"87x">> + }] + } + }, + CommandJson = emqx_json:encode(Command), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, + Path2 = get_coap_path(Options2), + ?assertEqual(post, Method2), + ?assertEqual(<<"/3/0">>, Path2), + Tlv_Value = <<3:2, 0:1, 0:2, 2:3, 13, 12345:16, + 3:2, 0:1, 0:2, 3:3, 14, "87x">>, + ?assertEqual(Tlv_Value, Payload2), + timer:sleep(50), + + test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, changed}, #coap_content{}, Request2, true), + timer:sleep(100), + + + ReadResult = emqx_json:encode(#{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"write">>, + <<"data">> => #{ + <<"reqPath">> => <<"/3/0/">>, + <<"code">> => <<"2.04">>, + <<"codeMsg">> => <<"changed">> + } + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). + +case22_write_error(Config) -> + % step 1, device register ... + Epn = "urn:oma:lwm2m:oma:3", + MsgId1 = 15, + UdpSock = ?config(sock, Config), + ObjectList = <<", , , , ">>, + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + + std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), + + % step2, send a WRITE command to device + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + CmdId = 307, + Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"write">>, + <<"data">> => #{ + <<"basePath">> => <<"/3/0/1">>, + <<"content">> => [ + #{ + type => <<"Integer">>, + value => <<"12345">> + } + ] + } + }, + CommandJson = emqx_json:encode(Command), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2, options=Options2} = Request2, + Path2 = get_coap_path(Options2), + ?assertEqual(put, Method2), + ?assertEqual(<<"/3/0/1">>, Path2), + timer:sleep(50), + + test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {error, bad_request}, #coap_content{}, Request2, true), + timer:sleep(100), + + ReadResult = emqx_json:encode(#{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"data">> => #{ + <<"reqPath">> => <<"/3/0/1">>, + <<"code">> => <<"4.00">>, + <<"codeMsg">> => <<"bad_request">> + }, + <<"msgType">> => <<"write">> + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). + +case_create_basic(Config) -> + % step 1, device register ... + Epn = "urn:oma:lwm2m:oma:3", + MsgId1 = 15, + UdpSock = ?config(sock, Config), + ObjectList = <<", , , ">>, + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + + std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), + + % step2, send a CREATE command to device + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + CmdId = 307, + Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"create">>, + <<"data">> => #{ + <<"path">> => <<"/5">> + } + }, + CommandJson = emqx_json:encode(Command), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, + Path2 = get_coap_path(Options2), + ?assertEqual(post, Method2), + ?assertEqual(<<"/5">>, Path2), + ?assertEqual(<<"">>, Payload2), + timer:sleep(50), + + test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, created}, #coap_content{}, Request2, true), + timer:sleep(100), + + ReadResult = emqx_json:encode(#{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"data">> => #{ + <<"reqPath">> => <<"/5">>, + <<"code">> => <<"2.01">>, + <<"codeMsg">> => <<"created">> + }, + <<"msgType">> => <<"create">> + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). + +case_delete_basic(Config) -> + % step 1, device register ... + Epn = "urn:oma:lwm2m:oma:3", + MsgId1 = 15, + UdpSock = ?config(sock, Config), + ObjectList = <<", , , , , ">>, + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + + std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), + + % step2, send a CREATE command to device + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + CmdId = 307, + Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"delete">>, + <<"data">> => #{ + <<"path">> => <<"/5/0">> + } + }, + CommandJson = emqx_json:encode(Command), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, + Path2 = get_coap_path(Options2), + ?assertEqual(delete, Method2), + ?assertEqual(<<"/5/0">>, Path2), + ?assertEqual(<<"">>, Payload2), + timer:sleep(50), + + test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, deleted}, #coap_content{}, Request2, true), + timer:sleep(100), + + ReadResult = emqx_json:encode(#{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"data">> => #{ + <<"reqPath">> => <<"/5/0">>, + <<"code">> => <<"2.02">>, + <<"codeMsg">> => <<"deleted">> + }, + <<"msgType">> => <<"delete">> + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). + +case30_execute(Config) -> + % step 1, device register ... + Epn = "urn:oma:lwm2m:oma:3", + MsgId1 = 15, + UdpSock = ?config(sock, Config), + ObjectList = <<", , , , ">>, + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + + std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), + + % step2, send a WRITE command to device + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + CmdId = 307, + Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"execute">>, + <<"data">> => #{ + <<"path">> => <<"/3/0/4">>, + %% "args" should not be present for "/3/0/4", only for testing the encoding here + <<"args">> => <<"2,7">> + } + }, + CommandJson = emqx_json:encode(Command), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, + Path2 = get_coap_path(Options2), + ?assertEqual(post, Method2), + ?assertEqual(<<"/3/0/4">>, Path2), + ?assertEqual(<<"2,7">>, Payload2), + timer:sleep(50), + + test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, changed}, #coap_content{}, Request2, true), + timer:sleep(100), + + ReadResult = emqx_json:encode(#{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"data">> => #{ + <<"reqPath">> => <<"/3/0/4">>, + <<"code">> => <<"2.04">>, + <<"codeMsg">> => <<"changed">> + }, + <<"msgType">> => <<"execute">> + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). + +case31_execute_error(Config) -> + % step 1, device register ... + Epn = "urn:oma:lwm2m:oma:3", + MsgId1 = 15, + UdpSock = ?config(sock, Config), + ObjectList = <<", , , , ">>, + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + + std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), + + % step2, send a WRITE command to device + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + CmdId = 307, + Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"execute">>, + <<"data">> => #{ + <<"path">> => <<"/3/0/4">>, + <<"args">> => <<"2,7">> + } + }, + CommandJson = emqx_json:encode(Command), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, + Path2 = get_coap_path(Options2), + ?assertEqual(post, Method2), + ?assertEqual(<<"/3/0/4">>, Path2), + ?assertEqual(<<"2,7">>, Payload2), + timer:sleep(50), + + test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {error, uauthorized}, #coap_content{}, Request2, true), + timer:sleep(100), + + ReadResult = emqx_json:encode(#{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"data">> => #{ + <<"reqPath">> => <<"/3/0/4">>, + <<"code">> => <<"4.01">>, + <<"codeMsg">> => <<"uauthorized">> + }, + <<"msgType">> => <<"execute">> + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). + +case40_discover(Config) -> + % step 1, device register ... + Epn = "urn:oma:lwm2m:oma:3", + MsgId1 = 15, + UdpSock = ?config(sock, Config), + ObjectList = <<", , , , ">>, + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + + std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), + + % step2, send a WRITE command to device + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + CmdId = 307, + Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"discover">>, + <<"data">> => #{ + <<"path">> => <<"/3/0/7">> + } }, + CommandJson = emqx_json:encode(Command), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, + Path2 = get_coap_path(Options2), + ?assertEqual(get, Method2), + ?assertEqual(<<"/3/0/7">>, Path2), + ?assertEqual(<<>>, Payload2), + timer:sleep(50), + + PayloadDiscover = <<";dim=8;pmin=10;pmax=60;gt=50;lt=42.2,">>, + test_send_coap_response(UdpSock, + "127.0.0.1", + ?PORT, + {ok, content}, + #coap_content{content_format = <<"application/link-format">>, payload = PayloadDiscover}, + Request2, + true), + timer:sleep(100), + + ReadResult = emqx_json:encode(#{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"discover">>, + <<"data">> => #{ + <<"reqPath">> => <<"/3/0/7">>, + <<"code">> => <<"2.05">>, + <<"codeMsg">> => <<"content">>, + <<"content">> => + [<<";dim=8;pmin=10;pmax=60;gt=50;lt=42.2">>, <<"">>] + } + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). + +case50_write_attribute(Config) -> + % step 1, device register ... + Epn = "urn:oma:lwm2m:oma:3", + MsgId1 = 15, + UdpSock = ?config(sock, Config), + ObjectList = <<", , , , ">>, + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + + std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), + + % step2, send a WRITE command to device + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + CmdId = 307, + Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"write-attr">>, + <<"data">> => #{ + <<"path">> => <<"/3/0/9">>, + <<"pmin">> => <<"1">>, + <<"pmax">> => <<"5">>, + <<"lt">> => <<"5">> + } }, + CommandJson = emqx_json:encode(Command), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(100), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, + ?LOGT("got options: ~p", [Options2]), + Path2 = get_coap_path(Options2), + Query2 = lists:sort(get_coap_query(Options2)), + ?assertEqual(put, Method2), + ?assertEqual(<<"/3/0/9">>, Path2), + ?assertEqual(lists:sort([<<"pmax=5">>,<<"lt=5">>,<<"pmin=1">>]), Query2), + ?assertEqual(<<>>, Payload2), + timer:sleep(50), + + test_send_coap_response(UdpSock, + "127.0.0.1", + ?PORT, + {ok, changed}, + #coap_content{}, + Request2, + true), + timer:sleep(100), + + ReadResult = emqx_json:encode(#{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"data">> => #{ + <<"reqPath">> => <<"/3/0/9">>, + <<"code">> => <<"2.04">>, + <<"codeMsg">> => <<"changed">> + }, + <<"msgType">> => <<"write-attr">> + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). + +case60_observe(Config) -> + % step 1, device register ... + Epn = "urn:oma:lwm2m:oma:3", + MsgId1 = 15, + UdpSock = ?config(sock, Config), + ObjectList = <<", , , , ">>, + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + RespTopicAD = list_to_binary("lwm2m/"++Epn++"/up/notify"), + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + emqtt:subscribe(?config(emqx_c, Config), RespTopicAD, qos0), + timer:sleep(200), + + std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic), + + % step2, send a OBSERVE command to device + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + CmdId = 307, + Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"observe">>, + <<"data">> => #{ + <<"path">> => <<"/3/0/10">> + } + }, + CommandJson = emqx_json:encode(Command), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, + Path2 = get_coap_path(Options2), + Observe = get_coap_observe(Options2), + ?assertEqual(get, Method2), + ?assertEqual(<<"/3/0/10">>, Path2), + ?assertEqual(Observe, 0), + ?assertEqual(<<>>, Payload2), + timer:sleep(50), + + test_send_coap_observe_ack( UdpSock, + "127.0.0.1", + ?PORT, + {ok, content}, + #coap_content{content_format = <<"text/plain">>, payload = <<"2048">>}, + Request2), + timer:sleep(100), + + ReadResult = emqx_json:encode(#{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"observe">>, + <<"data">> => #{ + <<"reqPath">> => <<"/3/0/10">>, + <<"code">> => <<"2.05">>, + <<"codeMsg">> => <<"content">>, + <<"content">> => [#{ + path => <<"/3/0/10">>, + value => 2048 + }] + } + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)), + + %% step3 the notifications + timer:sleep(200), + ObSeq = 3, + test_send_coap_notif( UdpSock, + "127.0.0.1", + ?PORT, + #coap_content{content_format = <<"text/plain">>, payload = <<"4096">>}, + ObSeq, + Request2), + timer:sleep(100), + #coap_message{} = test_recv_coap_response(UdpSock), + + ReadResult2 = emqx_json:encode(#{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"notify">>, + <<"seqNum">> => ObSeq, + <<"data">> => #{ + <<"reqPath">> => <<"/3/0/10">>, + <<"code">> => <<"2.05">>, + <<"codeMsg">> => <<"content">>, + <<"content">> => [#{ + path => <<"/3/0/10">>, + value => 4096 + }] + } + }), + ?assertEqual(ReadResult2, test_recv_mqtt_response(RespTopicAD)), + + %% Step3. cancel observe + CmdId3 = 308, + Command3 = #{<<"requestID">> => CmdId3, <<"cacheID">> => CmdId3, + <<"msgType">> => <<"cancel-observe">>, + <<"data">> => #{ + <<"path">> => <<"/3/0/10">> + } + }, + CommandJson3 = emqx_json:encode(Command3), + test_mqtt_broker:publish(CommandTopic, CommandJson3, 0), + timer:sleep(50), + Request3 = test_recv_coap_request(UdpSock), + #coap_message{method = Method3, options=Options3, payload=Payload3} = Request3, + Path3 = get_coap_path(Options3), + Observe3 = get_coap_observe(Options3), + ?assertEqual(get, Method3), + ?assertEqual(<<"/3/0/10">>, Path3), + ?assertEqual(Observe3, 1), + ?assertEqual(<<>>, Payload3), + timer:sleep(50), + + test_send_coap_observe_ack( UdpSock, + "127.0.0.1", + ?PORT, + {ok, content}, + #coap_content{content_format = <<"text/plain">>, payload = <<"1150">>}, + Request3), + timer:sleep(100), + + ReadResult3 = emqx_json:encode(#{ + <<"requestID">> => CmdId3, <<"cacheID">> => CmdId3, + <<"msgType">> => <<"cancel-observe">>, + <<"data">> => #{ + <<"reqPath">> => <<"/3/0/10">>, + <<"code">> => <<"2.05">>, + <<"codeMsg">> => <<"content">>, + <<"content">> => [#{ + path => <<"/3/0/10">>, + value => 1150 + }] + } + }), + ?assertEqual(ReadResult3, test_recv_mqtt_response(RespTopic)). + +case80_specail_object_19_0_0_notify(Config) -> + % step 1, device register, with extra register options + Epn = "urn:oma:lwm2m:oma:3", + RegOptionWangYi = "&apn=psmA.eDRX0.ctnb&im=13456&ct=2.0&mt=MDM9206&mv=4.0", + MsgId1 = 15, + UdpSock = ?config(sock, Config), + + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1"++RegOptionWangYi, [?PORT, Epn]), + #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, + [], + MsgId1), + #coap_message{method = Method1} = test_recv_coap_response(UdpSock), + ?assertEqual({ok,created}, Method1), + ReadResult = emqx_json:encode(#{ + <<"msgType">> => <<"register">>, + <<"data">> => #{ + <<"alternatePath">> => <<"/">>, + <<"ep">> => list_to_binary(Epn), + <<"lt">> => 345, + <<"lwm2m">> => <<"1">>, + <<"objectList">> => [<<"/1">>, <<"/2">>, <<"/3">>, <<"/4">>, <<"/5">>], + <<"apn">> => <<"psmA.eDRX0.ctnb">>, + <<"im">> => <<"13456">>, + <<"ct">> => <<"2.0">>, + <<"mt">> => <<"MDM9206">>, + <<"mv">> => <<"4.0">> + } + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)), + + % step2, send a OBSERVE command to device + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + CmdId = 307, + Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"observe">>, + <<"data">> => #{ + <<"path">> => <<"/19/0/0">> + } + }, + CommandJson = emqx_json:encode(Command), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, + Path2 = get_coap_path(Options2), + Observe = get_coap_observe(Options2), + ?assertEqual(get, Method2), + ?assertEqual(<<"/19/0/0">>, Path2), + ?assertEqual(Observe, 0), + ?assertEqual(<<>>, Payload2), + timer:sleep(50), + + test_send_coap_observe_ack( UdpSock, + "127.0.0.1", + ?PORT, + {ok, content}, + #coap_content{content_format = <<"text/plain">>, payload = <<"2048">>}, + Request2), + timer:sleep(100). + + %% step 3, device send uplink data notifications + +case80_specail_object_19_1_0_write(Config) -> + Epn = "urn:oma:lwm2m:oma:3", + RegOptionWangYi = "&apn=psmA.eDRX0.ctnb&im=13456&ct=2.0&mt=MDM9206&mv=4.0", + MsgId1 = 15, + UdpSock = ?config(sock, Config), + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1"++RegOptionWangYi, [?PORT, Epn]), + #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, + [], + MsgId1), + #coap_message{method = Method1} = test_recv_coap_response(UdpSock), + ?assertEqual({ok,created}, Method1), + test_recv_mqtt_response(RespTopic), + + % step2, send a WRITE command to device + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + CmdId = 307, + Command = #{<<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"write">>, + <<"data">> => #{ + <<"path">> => <<"/19/1/0">>, + <<"type">> => <<"Opaque">>, + <<"value">> => base64:encode(<<12345:32>>) + } + }, + + CommandJson = emqx_json:encode(Command), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50), + Request2 = test_recv_coap_request(UdpSock), + #coap_message{method = Method2, options=Options2, payload=Payload2} = Request2, + Path2 = get_coap_path(Options2), + ?assertEqual(put, Method2), + ?assertEqual(<<"/19/1/0">>, Path2), + ?assertEqual(<<3:2, 0:1, 0:2, 4:3, 0, 12345:32>>, Payload2), + timer:sleep(50), + + test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, changed}, #coap_content{}, Request2, true), + timer:sleep(100), + + ReadResult = emqx_json:encode(#{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"data">> => #{ + <<"reqPath">> => <<"/19/1/0">>, + <<"code">> => <<"2.04">>, + <<"codeMsg">> => <<"changed">> + }, + <<"msgType">> => <<"write">> + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). + +case90_psm_mode(Config) -> + server_cache_mode(Config, "ep=~s<=345&lwm2m=1&apn=psmA.eDRX0.ctnb"). + +case90_queue_mode(Config) -> + server_cache_mode(Config, "ep=~s<=345&lwm2m=1&b=UQ"). + +server_cache_mode(Config, RegOption) -> + application:set_env(?APP, qmode_time_window, 2), + + % step 1, device register, with apn indicates "PSM" mode + Epn = "urn:oma:lwm2m:oma:3", + + MsgId1 = 15, + UdpSock = ?config(sock, Config), + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + emqtt:subscribe(?config(emqx_c, Config), RespTopic, qos0), + timer:sleep(200), + + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b/rd?"++RegOption, [?PORT, Epn]), + #coap_content{content_format = <<"text/plain">>, payload = <<", , , , ">>}, + [], + MsgId1), + #coap_message{type = ack, method = Method1, options = Opts} = test_recv_coap_response(UdpSock), + ?assertEqual({ok,created}, Method1), + ?LOGT("Options got: ~p", [Opts]), + Location = proplists:get_value(location_path, Opts), + test_recv_mqtt_response(RespTopic), + + %% server not in PSM mode + send_read_command_1(0, UdpSock), + verify_read_response_1(0, UdpSock), + + %% server inters into PSM mode + timer:sleep(2), + + %% verify server caches downlink commands + send_read_command_1(1, UdpSock), + send_read_command_1(2, UdpSock), + send_read_command_1(3, UdpSock), + + ?assertEqual(timeout_test_recv_coap_request, test_recv_coap_request(UdpSock)), + + device_update_1(UdpSock, Location), + + verify_read_response_1(1, UdpSock), + verify_read_response_1(2, UdpSock), + verify_read_response_1(3, UdpSock). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Internal Functions +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +send_read_command_1(CmdId, _UdpSock) -> + Epn = "urn:oma:lwm2m:oma:3", + CommandTopic = <<"lwm2m/", (list_to_binary(Epn))/binary, "/dn/dm">>, + Command = #{ + <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"read">>, + <<"data">> => #{ + <<"path">> => <<"/3/0/0">> + } + }, + CommandJson = emqx_json:encode(Command), + test_mqtt_broker:publish(CommandTopic, CommandJson, 0), + timer:sleep(50). + +verify_read_response_1(CmdId, UdpSock) -> + Epn = "urn:oma:lwm2m:oma:3", + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + + %% device receives a command + Request = test_recv_coap_request(UdpSock), + ?LOGT("LwM2M client got ~p", [Request]), + + %% device replies the commond + test_send_coap_response(UdpSock, "127.0.0.1", ?PORT, {ok, content}, #coap_content{content_format = <<"text/plain">>, payload = <<"EMQ">>}, Request, true), + + ReadResult = emqx_json:encode(#{ <<"requestID">> => CmdId, <<"cacheID">> => CmdId, + <<"msgType">> => <<"read">>, + <<"data">> => #{ + <<"code">> => <<"2.05">>, + <<"codeMsg">> => <<"content">>, + <<"content">> => [#{ + path => <<"/3/0/0">>, + value => <<"EMQ">> + }] + } + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). + +device_update_1(UdpSock, Location) -> + Epn = "urn:oma:lwm2m:oma:3", + RespTopic = list_to_binary("lwm2m/"++Epn++"/up/resp"), + ?LOGT("send UPDATE command", []), + MsgId2 = 27, + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b~s?lt=789", [?PORT, join_path(Location, <<>>)]), + #coap_content{payload = <<>>}, + [], + MsgId2), + #coap_message{type = ack, id = MsgId2, method = Method2} = test_recv_coap_response(UdpSock), + {ok,changed} = Method2, + test_recv_mqtt_response(RespTopic). + +test_recv_mqtt_response(RespTopic) -> + receive + {publish, #{topic := RespTopic, payload := RM}} -> + ?LOGT("test_recv_mqtt_response Response=~p", [RM]), + RM + after 1000 -> timeout_test_recv_mqtt_response + end. + +test_send_coap_request(UdpSock, Method, Uri, Content, Options, MsgId) -> + is_record(Content, coap_content) orelse error("Content must be a #coap_content!"), + is_list(Options) orelse error("Options must be a list"), + case resolve_uri(Uri) of + {coap, {IpAddr, Port}, Path, Query} -> + Request0 = lwm2m_coap_message:request(con, Method, Content, [{uri_path, Path}, {uri_query, Query} | Options]), + Request = Request0#coap_message{id = MsgId}, + ?LOGT("send_coap_request Request=~p", [Request]), + RequestBinary = lwm2m_coap_message_parser:encode(Request), + ?LOGT("test udp socket send to ~p:~p, data=~p", [IpAddr, Port, RequestBinary]), + ok = gen_udp:send(UdpSock, IpAddr, Port, RequestBinary); + {SchemeDiff, ChIdDiff, _, _} -> + error(lists:flatten(io_lib:format("scheme ~s or ChId ~s does not match with socket", [SchemeDiff, ChIdDiff]))) + end. + +test_recv_coap_response(UdpSock) -> + {ok, {Address, Port, Packet}} = gen_udp:recv(UdpSock, 0, 2000), + Response = lwm2m_coap_message_parser:decode(Packet), + ?LOGT("test udp receive from ~p:~p, data1=~p, Response=~p", [Address, Port, Packet, Response]), + #coap_message{type = ack, method = Method, id=Id, token = Token, options = Options, payload = Payload} = Response, + ?LOGT("receive coap response Method=~p, Id=~p, Token=~p, Options=~p, Payload=~p", [Method, Id, Token, Options, Payload]), + Response. + +test_recv_coap_request(UdpSock) -> + case gen_udp:recv(UdpSock, 0, 2000) of + {ok, {_Address, _Port, Packet}} -> + Request = lwm2m_coap_message_parser:decode(Packet), + #coap_message{type = con, method = Method, id=Id, token = Token, payload = Payload, options = Options} = Request, + ?LOGT("receive coap request Method=~p, Id=~p, Token=~p, Options=~p, Payload=~p", [Method, Id, Token, Options, Payload]), + Request; + {error, Reason} -> + ?LOGT("test_recv_coap_request failed, Reason=~p", [Reason]), + timeout_test_recv_coap_request + end. + +test_send_coap_response(UdpSock, Host, Port, Code, Content, Request, Ack) -> + is_record(Content, coap_content) orelse error("Content must be a #coap_content!"), + is_list(Host) orelse error("Host is not a string"), + + {ok, IpAddr} = inet:getaddr(Host, inet), + Response = lwm2m_coap_message:response(Code, Content, Request), + Response2 = case Ack of + true -> Response#coap_message{type = ack}; + false -> Response + end, + ?LOGT("test_send_coap_response Response=~p", [Response2]), + ok = gen_udp:send(UdpSock, IpAddr, Port, lwm2m_coap_message_parser:encode(Response2)). + +test_send_empty_ack(UdpSock, Host, Port, Request) -> + is_list(Host) orelse error("Host is not a string"), + {ok, IpAddr} = inet:getaddr(Host, inet), + EmptyACK = lwm2m_coap_message:ack(Request), + ?LOGT("test_send_empty_ack EmptyACK=~p", [EmptyACK]), + ok = gen_udp:send(UdpSock, IpAddr, Port, lwm2m_coap_message_parser:encode(EmptyACK)). + +test_send_coap_observe_ack(UdpSock, Host, Port, Code, Content, Request) -> + is_record(Content, coap_content) orelse error("Content must be a #coap_content!"), + is_list(Host) orelse error("Host is not a string"), + + {ok, IpAddr} = inet:getaddr(Host, inet), + Response = lwm2m_coap_message:response(Code, Content, Request), + Response1 = lwm2m_coap_message:set(observe, 0, Response), + Response2 = Response1#coap_message{type = ack}, + + ?LOGT("test_send_coap_observe_ack Response=~p", [Response2]), + ResponseBinary = lwm2m_coap_message_parser:encode(Response2), + ok = gen_udp:send(UdpSock, IpAddr, Port, ResponseBinary). + +test_send_coap_notif(UdpSock, Host, Port, Content, ObSeq, Request) -> + is_record(Content, coap_content) orelse error("Content must be a #coap_content!"), + is_list(Host) orelse error("Host is not a string"), + + {ok, IpAddr} = inet:getaddr(Host, inet), + Notif = lwm2m_coap_message:response({ok, content}, Content, Request), + NewNotif = lwm2m_coap_message:set(observe, ObSeq, Notif), + ?LOGT("test_send_coap_notif Response=~p", [NewNotif]), + NotifBinary = lwm2m_coap_message_parser:encode(NewNotif), + ?LOGT("test udp socket send to ~p:~p, data=~p", [IpAddr, Port, NotifBinary]), + ok = gen_udp:send(UdpSock, IpAddr, Port, NotifBinary). + +std_register(UdpSock, Epn, ObjectList, MsgId1, RespTopic) -> + test_send_coap_request( UdpSock, + post, + sprintf("coap://127.0.0.1:~b/rd?ep=~s<=345&lwm2m=1", [?PORT, Epn]), + #coap_content{content_format = <<"text/plain">>, payload = ObjectList}, + [], + MsgId1), + #coap_message{method = {ok,created}} = test_recv_coap_response(UdpSock), + test_recv_mqtt_response(RespTopic), + timer:sleep(100). + +resolve_uri(Uri) -> + {ok, #{scheme := Scheme, + host := Host, + port := PortNo, + path := Path} = URIMap} = emqx_http_lib:uri_parse(Uri), + Query = maps:get(query, URIMap, ""), + {ok, PeerIP} = inet:getaddr(Host, inet), + {Scheme, {PeerIP, PortNo}, split_path(Path), split_query(Query)}. + +split_path([]) -> []; +split_path([$/]) -> []; +split_path([$/ | Path]) -> split_segments(Path, $/, []). + +split_query([]) -> []; +split_query(Path) -> split_segments(Path, $&, []). + +split_segments(Path, Char, Acc) -> + case string:rchr(Path, Char) of + 0 -> + [make_segment(Path) | Acc]; + N when N > 0 -> + split_segments(string:substr(Path, 1, N-1), Char, + [make_segment(string:substr(Path, N+1)) | Acc]) + end. + +make_segment(Seg) -> + list_to_binary(emqx_http_lib:uri_decode(Seg)). + + +get_coap_path(Options) -> + get_path(Options, <<>>). + +get_coap_query(Options) -> + proplists:get_value(uri_query, Options, []). + +get_coap_observe(Options) -> + get_observe(Options). + + +get_path([], Acc) -> + %?LOGT("get_path Acc=~p", [Acc]), + Acc; +get_path([{uri_path, Path1}|T], Acc) -> + %?LOGT("Path=~p, Acc=~p", [Path1, Acc]), + get_path(T, join_path(Path1, Acc)); +get_path([{_, _}|T], Acc) -> + get_path(T, Acc). + +get_observe([]) -> + undefined; +get_observe([{observe, V}|_T]) -> + V; +get_observe([{_, _}|T]) -> + get_observe(T). + +join_path([], Acc) -> Acc; +join_path([<<"/">>|T], Acc) -> + join_path(T, Acc); +join_path([H|T], Acc) -> + join_path(T, <>). + +sprintf(Format, Args) -> + lists:flatten(io_lib:format(Format, Args)). diff --git a/apps/emqx_gateway/test/emqx_tlv_SUITE.erl b/apps/emqx_gateway/test/emqx_tlv_SUITE.erl new file mode 100644 index 000000000..fd62ccf9d --- /dev/null +++ b/apps/emqx_gateway/test/emqx_tlv_SUITE.erl @@ -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). + diff --git a/apps/emqx_gateway/test/test_mqtt_broker.erl b/apps/emqx_gateway/test/test_mqtt_broker.erl new file mode 100644 index 000000000..dd85340b6 --- /dev/null +++ b/apps/emqx_gateway/test/test_mqtt_broker.erl @@ -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). diff --git a/rebar.config.erl b/rebar.config.erl index 4098bb7ab..ac84ead5a 100644 --- a/rebar.config.erl +++ b/rebar.config.erl @@ -346,6 +346,7 @@ relx_overlay(ReleaseType) -> , {copy, "bin/emqx", "bin/emqx-{{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, "apps/emqx_gateway/src/lwm2m/lwm2m_xml", "etc/lwm2m_xml"} , {template, "bin/emqx.cmd", "bin/emqx.cmd"} , {template, "bin/emqx_ctl.cmd", "bin/emqx_ctl.cmd"} , {copy, "bin/nodetool", "bin/nodetool"}