%%-------------------------------------------------------------------- %% Copyright (c) 2023-2024 EMQ Technologies Co., Ltd. All Rights Reserved. %%-------------------------------------------------------------------- -module(emqx_jt808_parser_SUITE). -compile(export_all). -compile(nowarn_export_all). -include("emqx_jt808.hrl"). -define(LOGT(Format, Args), ct:print("TEST_SUITE: " ++ Format, Args)). -define(FRM_FLAG, 16#7e:8). -define(RESERVE, 0). -define(NO_FRAGMENT, 0). -define(WITH_FRAGMENT, 1). -define(NO_ENCRYPT, 0). -define(MSG_SIZE(X), X:10 / big - integer). -define(word, 16 / big - integer). -define(dword, 32 / big - integer). -include_lib("eunit/include/eunit.hrl"). all() -> emqx_common_test_helpers:all(?MODULE). init_per_suite(Config) -> Config. end_per_suite(Config) -> Config. t_case01_register(_Config) -> Parser = emqx_jt808_frame:initial_parse_state(#{}), Manuf = <<"examp">>, Model = <<"33333333333333333333">>, DevId = <<"1234567">>, Color = 3, Plate = <<"ujvl239">>, RegisterPacket = <<58:?word, 59:?word, Manuf/binary, Model/binary, DevId/binary, Color, Plate/binary>>, MsgId = 16#0100, PhoneBCD = <<16#00, 16#01, 16#23, 16#45, 16#67, 16#89>>, MsgSn = 78, Size = size(RegisterPacket), Header = <>, Stream = encode(Header, RegisterPacket), {ok, Map, Rest, State} = emqx_jt808_frame:parse(Stream, Parser), ?assertEqual( #{ <<"header">> => #{ <<"msg_id">> => MsgId, <<"encrypt">> => ?NO_ENCRYPT, <<"phone">> => <<"000123456789">>, <<"len">> => Size, <<"msg_sn">> => MsgSn }, <<"body">> => #{ <<"province">> => 58, <<"city">> => 59, <<"manufacturer">> => Manuf, <<"model">> => Model, <<"dev_id">> => DevId, <<"color">> => Color, <<"license_number">> => Plate } }, Map ), ?assertEqual(<<>>, Rest), ?assertEqual(#{data => <<>>, phase => searching_head_hex7e}, State), ok. t_case02_register_ack(_Config) -> % register ack MsgId = 16#8100, MsgSn = 35, Seq = 22, Result = 1, Code = <<"abcdef">>, DownlinkJson = #{ <<"header">> => #{ <<"msg_id">> => MsgId, <<"encrypt">> => ?NO_ENCRYPT, <<"phone">> => <<"000123456789">>, <<"msg_sn">> => MsgSn }, <<"body">> => #{<<"seq">> => Seq, <<"result">> => Result, <<"auth_code">> => Code} }, Stream = emqx_jt808_frame:serialize_pkt(DownlinkJson, #{}), RegisterAckPacket = <>, PhoneBCD = <<16#00, 16#01, 16#23, 16#45, 16#67, 16#89>>, Size = size(RegisterAckPacket), Header = <>, StreamByHand = encode(Header, RegisterAckPacket), ?assertEqual(StreamByHand, Stream), ok. t_case04_MC_LOCATION_REPORT(_Config) -> Parser = emqx_jt808_frame:initial_parse_state(#{}), Data = <<126, 2, 0, 0, 60, 1, 136, 118, 99, 137, 114, 0, 229, 0, 0, 0, 0, 0, 4, 0, 0, 1, 49, 122, 103, 6, 147, 104, 81, 0, 14, 0, 0, 0, 39, 23, 16, 25, 25, 53, 56, 1, 4, 0, 0, 63, 178, 3, 2, 0, 0, 37, 4, 0, 0, 0, 0, 42, 2, 0, 0, 43, 4, 0, 0, 0, 0, 48, 1, 31, 49, 1, 0, 171, 126>>, {ok, Map, Rest, State} = emqx_jt808_frame:parse(Data, Parser), ?assertEqual( #{ <<"header">> => #{ <<"encrypt">> => 0, <<"len">> => 60, <<"msg_id">> => 512, <<"msg_sn">> => 229, <<"phone">> => <<"018876638972">> }, <<"body">> => #{ <<"alarm">> => 0, <<"altitude">> => 14, <<"direction">> => 39, <<"extra">> => #{ <<"analog">> => #{<<"ad0">> => 0, <<"ad1">> => 0}, <<"gnss_sat_num">> => 0, <<"io_status">> => #{<<"deep_sleep">> => 0, <<"sleep">> => 0}, <<"mileage">> => 16306, <<"rssi">> => 31, <<"signal">> => #{ <<"abs">> => 0, <<"air_conditioner">> => 0, <<"brake">> => 0, <<"cluth">> => 0, <<"fog">> => 0, <<"heater">> => 0, <<"high_beam">> => 0, <<"horn">> => 0, <<"left_turn">> => 0, <<"low_beam">> => 0, <<"neutral">> => 0, <<"retarder">> => 0, <<"reverse">> => 0, <<"right_turn">> => 0, <<"side_marker">> => 0 }, <<"speed">> => 0 }, <<"latitude">> => 20019815, <<"longitude">> => 110323793, <<"speed">> => 0, <<"status">> => 262144, <<"time">> => <<"171019193538">> } }, Map ), ?assertEqual(<<>>, Rest), ?assertEqual(#{data => <<>>, phase => searching_head_hex7e}, State), ok. t_case05_MC_BULK_LOCATION_REPORT(_Config) -> Parser = emqx_jt808_frame:initial_parse_state(#{}), Data = <<126, 7, 4, 1, 57, 1, 136, 118, 99, 137, 114, 0, 231, 0, 5, 1, 0, 60, 0, 0, 0, 0, 0, 4, 0, 3, 1, 49, 115, 43, 6, 145, 211, 81, 0, 31, 0, 166, 0, 171, 23, 8, 49, 16, 73, 51, 1, 4, 0, 0, 60, 85, 3, 2, 0, 0, 37, 4, 0, 0, 0, 0, 42, 2, 0, 0, 43, 4, 0, 0, 0, 0, 48, 1, 0, 49, 1, 12, 0, 60, 0, 0, 0, 0, 0, 4, 0, 3, 1, 49, 114, 74, 6, 145, 212, 2, 0, 29, 0, 222, 0, 125, 2, 23, 8, 49, 16, 73, 56, 1, 4, 0, 0, 60, 85, 3, 2, 0, 0, 37, 4, 0, 0, 0, 0, 42, 2, 0, 0, 43, 4, 0, 0, 0, 0, 48, 1, 0, 49, 1, 12, 0, 60, 0, 0, 0, 0, 0, 4, 0, 3, 1, 49, 109, 222, 6, 145, 225, 80, 0, 37, 1, 169, 0, 109, 23, 8, 49, 16, 80, 17, 1, 4, 0, 0, 60, 89, 3, 2, 0, 0, 37, 4, 0, 0, 0, 0, 42, 2, 0, 0, 43, 4, 0, 0, 0, 0, 48, 1, 0, 49, 1, 12, 0, 60, 0, 0, 0, 0, 0, 4, 0, 3, 1, 49, 90, 235, 6, 146, 0, 24, 0, 43, 2, 136, 0, 114, 23, 8, 49, 16, 81, 17, 1, 4, 0, 0, 60, 99, 3, 2, 0, 0, 37, 4, 0, 0, 0, 0, 42, 2, 0, 0, 43, 4, 0, 0, 0, 0, 48, 1, 0, 49, 1, 12, 0, 60, 0, 0, 0, 0, 0, 4, 0, 3, 1, 49, 91, 120, 6, 146, 43, 21, 0, 43, 2, 247, 0, 83, 23, 8, 49, 16, 82, 20, 1, 4, 0, 0, 60, 111, 3, 2, 0, 0, 37, 4, 0, 0, 0, 0, 42, 2, 0, 0, 43, 4, 0, 0, 0, 0, 48, 1, 0, 49, 1, 12, 132, 126>>, {ok, Map, Rest, State} = emqx_jt808_frame:parse(Data, Parser), ?assertEqual( #{ <<"body">> => #{ <<"length">> => 5, <<"location">> => [ #{ <<"alarm">> => 0, <<"altitude">> => 31, <<"direction">> => 171, <<"extra">> => #{ <<"analog">> => #{ <<"ad0">> => 0, <<"ad1">> => 0 }, <<"gnss_sat_num">> => 12, <<"io_status">> => #{ <<"deep_sleep">> => 0, <<"sleep">> => 0 }, <<"mileage">> => 15445, <<"rssi">> => 0, <<"signal">> => #{ <<"abs">> => 0, <<"air_conditioner">> => 0, <<"brake">> => 0, <<"cluth">> => 0, <<"fog">> => 0, <<"heater">> => 0, <<"high_beam">> => 0, <<"horn">> => 0, <<"left_turn">> => 0, <<"low_beam">> => 0, <<"neutral">> => 0, <<"retarder">> => 0, <<"reverse">> => 0, <<"right_turn">> => 0, <<"side_marker">> => 0 }, <<"speed">> => 0 }, <<"latitude">> => 20017963, <<"longitude">> => 110220113, <<"speed">> => 166, <<"status">> => 262147, <<"time">> => <<"170831104933">> }, #{ <<"alarm">> => 0, <<"altitude">> => 29, <<"direction">> => 126, <<"extra">> => #{ <<"analog">> => #{ <<"ad0">> => 0, <<"ad1">> => 0 }, <<"gnss_sat_num">> => 12, <<"io_status">> => #{ <<"deep_sleep">> => 0, <<"sleep">> => 0 }, <<"mileage">> => 15445, <<"rssi">> => 0, <<"signal">> => #{ <<"abs">> => 0, <<"air_conditioner">> => 0, <<"brake">> => 0, <<"cluth">> => 0, <<"fog">> => 0, <<"heater">> => 0, <<"high_beam">> => 0, <<"horn">> => 0, <<"left_turn">> => 0, <<"low_beam">> => 0, <<"neutral">> => 0, <<"retarder">> => 0, <<"reverse">> => 0, <<"right_turn">> => 0, <<"side_marker">> => 0 }, <<"speed">> => 0 }, <<"latitude">> => 20017738, <<"longitude">> => 110220290, <<"speed">> => 222, <<"status">> => 262147, <<"time">> => <<"170831104938">> }, #{ <<"alarm">> => 0, <<"altitude">> => 37, <<"direction">> => 109, <<"extra">> => #{ <<"analog">> => #{ <<"ad0">> => 0, <<"ad1">> => 0 }, <<"gnss_sat_num">> => 12, <<"io_status">> => #{ <<"deep_sleep">> => 0, <<"sleep">> => 0 }, <<"mileage">> => 15449, <<"rssi">> => 0, <<"signal">> => #{ <<"abs">> => 0, <<"air_conditioner">> => 0, <<"brake">> => 0, <<"cluth">> => 0, <<"fog">> => 0, <<"heater">> => 0, <<"high_beam">> => 0, <<"horn">> => 0, <<"left_turn">> => 0, <<"low_beam">> => 0, <<"neutral">> => 0, <<"retarder">> => 0, <<"reverse">> => 0, <<"right_turn">> => 0, <<"side_marker">> => 0 }, <<"speed">> => 0 }, <<"latitude">> => 20016606, <<"longitude">> => 110223696, <<"speed">> => 425, <<"status">> => 262147, <<"time">> => <<"170831105011">> }, #{ <<"alarm">> => 0, <<"altitude">> => 43, <<"direction">> => 114, <<"extra">> => #{ <<"analog">> => #{ <<"ad0">> => 0, <<"ad1">> => 0 }, <<"gnss_sat_num">> => 12, <<"io_status">> => #{ <<"deep_sleep">> => 0, <<"sleep">> => 0 }, <<"mileage">> => 15459, <<"rssi">> => 0, <<"signal">> => #{ <<"abs">> => 0, <<"air_conditioner">> => 0, <<"brake">> => 0, <<"cluth">> => 0, <<"fog">> => 0, <<"heater">> => 0, <<"high_beam">> => 0, <<"horn">> => 0, <<"left_turn">> => 0, <<"low_beam">> => 0, <<"neutral">> => 0, <<"retarder">> => 0, <<"reverse">> => 0, <<"right_turn">> => 0, <<"side_marker">> => 0 }, <<"speed">> => 0 }, <<"latitude">> => 20011755, <<"longitude">> => 110231576, <<"speed">> => 648, <<"status">> => 262147, <<"time">> => <<"170831105111">> }, #{ <<"alarm">> => 0, <<"altitude">> => 43, <<"direction">> => 83, <<"extra">> => #{ <<"analog">> => #{ <<"ad0">> => 0, <<"ad1">> => 0 }, <<"gnss_sat_num">> => 12, <<"io_status">> => #{ <<"deep_sleep">> => 0, <<"sleep">> => 0 }, <<"mileage">> => 15471, <<"rssi">> => 0, <<"signal">> => #{ <<"abs">> => 0, <<"air_conditioner">> => 0, <<"brake">> => 0, <<"cluth">> => 0, <<"fog">> => 0, <<"heater">> => 0, <<"high_beam">> => 0, <<"horn">> => 0, <<"left_turn">> => 0, <<"low_beam">> => 0, <<"neutral">> => 0, <<"retarder">> => 0, <<"reverse">> => 0, <<"right_turn">> => 0, <<"side_marker">> => 0 }, <<"speed">> => 0 }, <<"latitude">> => 20011896, <<"longitude">> => 110242581, <<"speed">> => 759, <<"status">> => 262147, <<"time">> => <<"170831105214">> } ], <<"type">> => 1 }, <<"header">> => #{ <<"encrypt">> => 0, <<"len">> => 313, <<"msg_id">> => 1796, <<"msg_sn">> => 231, <<"phone">> => <<"018876638972">> } }, Map ), ?assertEqual(<<>>, Rest), ?assertEqual(#{data => <<>>, phase => searching_head_hex7e}, State), ok. t_case10_segmented_packet(_Config) -> Parser = emqx_jt808_frame:initial_parse_state(#{}), Manuf = <<"examp">>, Model = <<"33333333333333333333">>, DevId = <<"1234567">>, Color = 3, Plate = <<"ujvl239">>, RegisterPacket = <<58:?word, 59:?word, Manuf/binary, Model/binary, DevId/binary, Color, Plate/binary>>, MsgId = 16#0100, PhoneBCD = <<16#00, 16#01, 16#23, 16#45, 16#67, 16#89>>, MsgSn = 78, Size = size(RegisterPacket), Header = <>, Stream = encode(Header, RegisterPacket), <> = Stream, {more, Parser2} = emqx_jt808_frame:parse(Part1, Parser), ?assertMatch(#{phase := escaping_hex7d}, Parser2), {ok, Map, Rest, State} = emqx_jt808_frame:parse(Part2, Parser2), ?assertEqual( #{ <<"header">> => #{ <<"msg_id">> => MsgId, <<"encrypt">> => ?NO_ENCRYPT, <<"phone">> => <<"000123456789">>, <<"len">> => Size, <<"msg_sn">> => MsgSn }, <<"body">> => #{ <<"province">> => 58, <<"city">> => 59, <<"manufacturer">> => Manuf, <<"model">> => Model, <<"dev_id">> => DevId, <<"color">> => Color, <<"license_number">> => Plate } }, Map ), ?assertEqual(<<>>, Rest), ?assertEqual(#{data => <<>>, phase => searching_head_hex7e}, State), ok. t_case11_prefix_register(_Config) -> Parser = emqx_jt808_frame:initial_parse_state(#{}), Manuf = <<"examp">>, Model = <<"33333333333333333333">>, DevId = <<"1234567">>, Color = 3, Plate = <<"ujvl239">>, RegisterPacket = <<58:?word, 59:?word, Manuf/binary, Model/binary, DevId/binary, Color, Plate/binary>>, MsgId = 16#0100, PhoneBCD = <<16#00, 16#01, 16#23, 16#45, 16#67, 16#89>>, MsgSn = 78, Size = size(RegisterPacket), Header = <>, Stream = encode(Header, RegisterPacket), MessBinary = <<0, 1, 2, 3, Stream/binary>>, ?LOGT("MessBinary=~p", [binary_to_hex_string(MessBinary)]), {ok, Map, Rest, State} = emqx_jt808_frame:parse(MessBinary, Parser), ?assertEqual( #{ <<"header">> => #{ <<"msg_id">> => MsgId, <<"encrypt">> => ?NO_ENCRYPT, <<"phone">> => <<"000123456789">>, <<"len">> => Size, <<"msg_sn">> => MsgSn }, <<"body">> => #{ <<"province">> => 58, <<"city">> => 59, <<"manufacturer">> => Manuf, <<"model">> => Model, <<"dev_id">> => DevId, <<"color">> => Color, <<"license_number">> => Plate } }, Map ), ?assertEqual(<<>>, Rest), ?assertEqual(#{data => <<>>, phase => searching_head_hex7e}, State), ok. t_case12_0x7e_in_message(_Config) -> Parser = emqx_jt808_frame:initial_parse_state(#{}), Manuf = <<"examp">>, Model = <<"33333333333333333333">>, DevId = <<"1234567">>, Color = 3, Plate = <<"ujvl239">>, % pay attention to this AlarmInt = 16#7e, AlarmDigit = 16#7d, RegisterPacket = <>, MsgId = 16#0100, PhoneBCD = <<16#00, 16#01, 16#23, 16#45, 16#67, 16#89>>, MsgSn = 78, Size = size(RegisterPacket), Header = <>, Stream = encode(Header, RegisterPacket), {ok, Map, Rest, State} = emqx_jt808_frame:parse(Stream, Parser), ?assertEqual( #{ <<"header">> => #{ <<"msg_id">> => MsgId, <<"encrypt">> => ?NO_ENCRYPT, <<"phone">> => <<"000123456789">>, <<"len">> => Size, <<"msg_sn">> => MsgSn }, <<"body">> => #{ <<"province">> => AlarmInt, <<"city">> => AlarmDigit, <<"manufacturer">> => Manuf, <<"model">> => Model, <<"dev_id">> => DevId, <<"color">> => Color, <<"license_number">> => Plate } }, Map ), ?assertEqual(<<>>, Rest), ?assertEqual(#{data => <<>>, phase => searching_head_hex7e}, State), ok. t_case13_partial_0x7d_in_message(_Config) -> Parser = emqx_jt808_frame:initial_parse_state(#{}), Manuf = <<"examp">>, Model = <<"33333333333333333333">>, DevId = <<"1234567">>, Color = 3, Plate = <<"ujvl239">>, % pay attention to this AlarmInt = 16#7e, AlarmDigit = 16#7d, RegisterPacket = <>, MsgId = 16#0100, PhoneBCD = <<16#00, 16#01, 16#23, 16#45, 16#67, 16#89>>, MsgSn = 78, Size = size(RegisterPacket), Header = <>, Stream = encode(Header, RegisterPacket), <> = Stream, <<_:14/binary, 16#7d:8>> = Part1, {more, Parser2} = emqx_jt808_frame:parse(Part1, Parser), ?assertMatch(#{phase := escaping_hex7d}, Parser2), {ok, Map, Rest, State} = emqx_jt808_frame:parse(Part2, Parser2), ?assertEqual( #{ <<"header">> => #{ <<"msg_id">> => MsgId, <<"encrypt">> => ?NO_ENCRYPT, <<"phone">> => <<"000123456789">>, <<"len">> => Size, <<"msg_sn">> => MsgSn }, <<"body">> => #{ <<"province">> => AlarmInt, <<"city">> => AlarmDigit, <<"manufacturer">> => Manuf, <<"model">> => Model, <<"dev_id">> => DevId, <<"color">> => Color, <<"license_number">> => Plate } }, Map ), ?assertEqual(<<>>, Rest), ?assertEqual(#{data => <<>>, phase => searching_head_hex7e}, State), ok. t_case14_custome_location_data(_) -> Bin = <<126, 2, 0, 0, 64, 1, 65, 72, 7, 53, 80, 3, 106, 0, 0, 0, 0, 0, 0, 0, 1, 1, 195, 232, 22, 6, 89, 10, 16, 0, 0, 0, 0, 0, 0, 33, 6, 34, 9, 21, 69, 1, 4, 0, 0, 0, 0, 48, 1, 23, 49, 1, 10, 235, 22, 0, 12, 0, 178, 137, 134, 4, 66, 25, 25, 144, 147, 71, 153, 0, 6, 0, 137, 255, 255, 255, 255, 36, 126>>, Parser = emqx_jt808_frame:initial_parse_state(#{}), {ok, Packet, Rest, State} = emqx_jt808_frame:parse(Bin, Parser), ?assertEqual(<<>>, Rest), ?assertEqual(#{data => <<>>, phase => searching_head_hex7e}, State), _ = emqx_utils_json:encode(Packet). t_case14_reserved_location_data(_) -> Bin = <<126, 2, 0, 0, 49, 1, 145, 114, 3, 130, 104, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 211, 181, 215, 6, 51, 228, 71, 0, 4, 0, 0, 0, 138, 34, 4, 25, 22, 5, 70, 1, 4, 0, 0, 0, 0, 5, 3, 0, 0, 0, 48, 1, 31, 49, 1, 15, 130, 2, 0, 125, 2, 23, 126>>, Parser = emqx_jt808_frame:initial_parse_state(#{}), {ok, Packet, Rest, State} = emqx_jt808_frame:parse(Bin, Parser), ?assertEqual(<<>>, Rest), ?assertEqual(#{data => <<>>, phase => searching_head_hex7e}, State), _ = emqx_utils_json:encode(Packet). t_case15_custome_client_query_ack(_) -> Bin = <<126, 1, 4, 2, 17, 78, 244, 128, 18, 137, 25, 0, 43, 0, 42, 52, 0, 0, 0, 1, 4, 0, 0, 0, 180, 0, 0, 0, 8, 4, 0, 0, 1, 44, 0, 0, 0, 16, 5, 99, 109, 110, 101, 116, 0, 0, 0, 17, 0, 0, 0, 0, 18, 0, 0, 0, 0, 19, 12, 52, 55, 46, 57, 57, 46, 57, 56, 46, 50, 53, 52, 0, 0, 0, 23, 7, 48, 46, 48, 46, 48, 46, 48, 0, 0, 0, 24, 4, 0, 0, 31, 249, 0, 0, 0, 32, 4, 0, 0, 0, 0, 0, 0, 0, 39, 4, 0, 0, 0, 30, 0, 0, 0, 41, 4, 0, 0, 0, 30, 0, 0, 0, 48, 4, 0, 0, 0, 20, 0, 0, 0, 64, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 68, 1, 0, 0, 0, 0, 69, 4, 0, 0, 0, 0, 0, 0, 0, 72, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 85, 4, 0, 0, 0, 120, 0, 0, 0, 86, 4, 0, 0, 0, 20, 0, 0, 0, 93, 2, 0, 30, 0, 0, 0, 128, 4, 0, 0, 136, 203, 0, 0, 0, 129, 2, 0, 0, 0, 0, 0, 130, 2, 0, 0, 0, 0, 0, 131, 0, 0, 0, 0, 132, 1, 0, 0, 0, 240, 1, 2, 16, 0, 0, 0, 240, 2, 2, 3, 57, 0, 0, 240, 3, 1, 41, 0, 0, 240, 4, 2, 0, 116, 0, 0, 240, 5, 4, 0, 53, 111, 127, 0, 0, 240, 6, 4, 0, 3, 51, 18, 0, 0, 240, 7, 1, 1, 0, 0, 240, 8, 2, 0, 30, 0, 0, 240, 10, 2, 0, 30, 0, 0, 240, 11, 6, 0, 0, 0, 0, 0, 0, 0, 0, 240, 12, 1, 1, 0, 0, 240, 13, 18, 117, 112, 103, 114, 97, 100, 101, 46, 99, 112, 115, 100, 110, 97, 46, 99, 111, 109, 0, 0, 240, 14, 4, 0, 0, 123, 40, 0, 0, 240, 15, 1, 1, 0, 0, 240, 16, 3, 0, 16, 3, 0, 0, 240, 17, 2, 0, 32, 0, 0, 240, 18, 3, 0, 22, 40, 0, 0, 240, 19, 4, 0, 17, 25, 32, 0, 0, 240, 20, 3, 55, 0, 10, 0, 0, 240, 21, 1, 50, 0, 0, 240, 22, 2, 3, 25, 0, 0, 240, 23, 2, 30, 50, 0, 0, 240, 24, 21, 48, 44, 48, 44, 53, 56, 46, 50, 49, 53, 46, 53, 48, 46, 53, 48, 44, 54, 48, 48, 56, 0, 0, 241, 1, 1, 160, 0, 0, 241, 2, 1, 130, 0, 0, 255, 148, 93, 14, 22, 1, 56, 57, 56, 54, 48, 52, 57, 53, 49, 48, 50, 49, 55, 48, 51, 48, 52, 53, 53, 56, 17, 2, 52, 54, 48, 48, 56, 49, 53, 56, 51, 52, 48, 52, 53, 53, 56, 4, 3, 0, 1, 3, 5, 0, 3, 6, 0, 3, 7, 0, 5, 8, 3, 0, 1, 5, 8, 14, 0, 0, 5, 8, 15, 0, 0, 5, 8, 41, 0, 0, 5, 8, 48, 0, 0, 5, 8, 42, 0, 0, 5, 8, 43, 0, 0, 5, 8, 4, 0, 0, 138, 126>>, Parser = emqx_jt808_frame:initial_parse_state(#{}), {ok, Packet, Rest, State} = emqx_jt808_frame:parse(Bin, Parser), ?assertEqual(<<>>, Rest), ?assertEqual(#{data => <<>>, phase => searching_head_hex7e}, State), _ = emqx_utils_json:encode(Packet). encode(Header, Body) -> S1 = <
>, Crc = make_crc(S1, undefined), S2 = do_escape(<>), Stream = <<16#7e:8, S2/binary, 16#7e:8>>, %?LOGT("encode a packet=~p", [binary_to_hex_string(Stream)]), Stream. make_crc(<<>>, Xor) -> ?LOGT("crc is ~p", [Xor]), Xor; make_crc(<>, undefined) -> make_crc(Rest, C); make_crc(<>, Xor) -> make_crc(Rest, C bxor Xor). do_escape(Binary) -> do_escape(Binary, <<>>). do_escape(<<>>, Acc) -> Acc; do_escape(<<16#7e, Rest/binary>>, Acc) -> do_escape(Rest, <>); do_escape(<<16#7d, Rest/binary>>, Acc) -> do_escape(Rest, <>); do_escape(<>, Acc) -> do_escape(Rest, <>). binary_to_hex_string(Data) -> lists:flatten([io_lib:format("~2.16.0B ", [X]) || <> <= Data]).