diff --git a/apps/emqx_lwm2m/src/emqx_lwm2m.app.src b/apps/emqx_lwm2m/src/emqx_lwm2m.app.src index 209499fc5..f4afe8fbc 100644 --- a/apps/emqx_lwm2m/src/emqx_lwm2m.app.src +++ b/apps/emqx_lwm2m/src/emqx_lwm2m.app.src @@ -1,6 +1,6 @@ {application,emqx_lwm2m, [{description,"EMQ X LwM2M Gateway"}, - {vsn, "4.3.2"}, % strict semver, bump manually! + {vsn, "4.3.3"}, % strict semver, bump manually! {modules,[]}, {registered,[emqx_lwm2m_sup]}, {applications,[kernel,stdlib,lwm2m_coap]}, diff --git a/apps/emqx_lwm2m/src/emqx_lwm2m.appup.src b/apps/emqx_lwm2m/src/emqx_lwm2m.appup.src index b6d49302f..07af339fd 100644 --- a/apps/emqx_lwm2m/src/emqx_lwm2m.appup.src +++ b/apps/emqx_lwm2m/src/emqx_lwm2m.appup.src @@ -1,13 +1,19 @@ %% -*-: erlang -*- -{VSN, +{"4.3.3", [ {<<"4.3.[0-1]">>, [ {restart_application, emqx_lwm2m} + ]}, + {"4.3.2", [ + {load_module, emqx_lwm2m_message, brutal_purge, soft_purge, []} ]} ], [ {<<"4.3.[0-1]">>, [ {restart_application, emqx_lwm2m} + ]}, + {"4.3.2", [ + {load_module, emqx_lwm2m_message, brutal_purge, soft_purge, []} ]} ] }. diff --git a/apps/emqx_lwm2m/src/emqx_lwm2m_message.erl b/apps/emqx_lwm2m/src/emqx_lwm2m_message.erl index 6b8bc8d50..494f26522 100644 --- a/apps/emqx_lwm2m/src/emqx_lwm2m_message.erl +++ b/apps/emqx_lwm2m/src/emqx_lwm2m_message.erl @@ -22,6 +22,9 @@ , opaque_to_json/2 , translate_json/1 ]). +-ifdef(TEST). +-export([ bits/1 ]). +-endif. -include("emqx_lwm2m.hrl"). @@ -364,23 +367,6 @@ encode_number(Int) when is_integer(Int) -> encode_number(Float) when is_float(Float) -> <>. -encode_int(Int) when Int >= 0 -> - binary:encode_unsigned(Int); -encode_int(Int) when Int < 0 -> - Size = byte_size_of_signed(-Int) * 8, - <>. - -byte_size_of_signed(UInt) -> - byte_size_of_signed(UInt, 0). - -byte_size_of_signed(UInt, N) -> - BitSize = (8*N - 1), - Max = (1 bsl BitSize), - if - UInt =< Max -> N; - UInt > Max -> byte_size_of_signed(UInt, N+1) - end. - binary_to_number(NumStr) -> try binary_to_integer(NumStr) @@ -388,3 +374,26 @@ binary_to_number(NumStr) -> error:badarg -> binary_to_float(NumStr) end. + +encode_int(Int) -> + Bits = bits(Int), + <>. + +bits(I) when I < 0 -> bits_neg(I); +bits(I) -> bits_pos(I). + +%% Quote: +%% Integer: An 8, 16, 32 or 64-bit signed integer. +%% The valid range of the value for a Resource SHOULD be defined. +%% This data type is also used for the purpose of enumeration. +%% +%% NOTE: Integer should not be encoded to 24-bits, 40-bits, etc. +bits_pos(I) when I < (1 bsl 7) -> 8; +bits_pos(I) when I < (1 bsl 15) -> 16; +bits_pos(I) when I < (1 bsl 31) -> 32; +bits_pos(I) when I < (1 bsl 63) -> 64. + +bits_neg(I) when I >= -((1 bsl 7)) -> 8; +bits_neg(I) when I >= -((1 bsl 15)) -> 16; +bits_neg(I) when I >= -((1 bsl 31)) -> 32; +bits_neg(I) when I >= -((1 bsl 63)) -> 64. diff --git a/apps/emqx_lwm2m/test/emqx_lwm2m_SUITE.erl b/apps/emqx_lwm2m/test/emqx_lwm2m_SUITE.erl index 7f2f37466..5a65fba4f 100644 --- a/apps/emqx_lwm2m/test/emqx_lwm2m_SUITE.erl +++ b/apps/emqx_lwm2m/test/emqx_lwm2m_SUITE.erl @@ -41,6 +41,7 @@ all() -> , {group, test_grp_5_write_attr} , {group, test_grp_6_observe} , {group, test_grp_8_object_19} + , {group, test_grp_bugs} ]. suite() -> [{timetrap, {seconds, 90}}]. @@ -106,6 +107,9 @@ groups() -> {test_grp_9_psm_queue_mode, [RepeatOpt], [ case90_psm_mode, case90_queue_mode + ]}, + {test_grp_bugs, [RepeatOpt], [ + case_bug_emqx_4989 ]} ]. @@ -145,6 +149,56 @@ end_per_testcase(_AllTestCase, Config) -> %% Cases %%-------------------------------------------------------------------- +case_bug_emqx_4989(Config) -> + %% https://github.com/emqx/emqx/issues/4989 + % 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">> => <<"/1/0/2">>, + <<"type">> => <<"Integer">>, + <<"value">> => 129 + } + }, + 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(<<"/1/0/2">>, Path2), + ?assertMatch([#{value := 129}], emqx_lwm2m_message:tlv_to_json(Path2, 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">> => <<"/1/0/2">>, + <<"code">> => <<"2.04">>, + <<"codeMsg">> => <<"changed">> + }, + <<"msgType">> => <<"write">> + }), + ?assertEqual(ReadResult, test_recv_mqtt_response(RespTopic)). + case01_register(Config) -> % ---------------------------------------- % REGISTER command diff --git a/apps/emqx_lwm2m/test/emqx_lwm2m_message_tests.erl b/apps/emqx_lwm2m/test/emqx_lwm2m_message_tests.erl new file mode 100644 index 000000000..f59e75edd --- /dev/null +++ b/apps/emqx_lwm2m/test/emqx_lwm2m_message_tests.erl @@ -0,0 +1,21 @@ +-module(emqx_lwm2m_message_tests). + +-include_lib("eunit/include/eunit.hrl"). + +-import(emqx_lwm2m_message, [bits/1]). + +bits_pos_test() -> + ?assertEqual(8, bits(0)), + ?assertEqual(8, bits(1)), + ?assertEqual(8, bits(127)), + ?assertEqual(16, bits(128)), + ?assertEqual(16, bits(129)), + ok. + +bits_neg_test() -> + ?assertEqual(8, bits(-1)), + ?assertEqual(8, bits(-2)), + ?assertEqual(8, bits(-127)), + ?assertEqual(8, bits(-128)), + ?assertEqual(16, bits(-129)), + ok.