/__w/emqttsn/emqttsn/_build/test/cover/aggregate/emqttsn_cli.html

1 %%-------------------------------------------------------------------------
2 %% Copyright (c) 2020-2022 EMQ Technologies Co., Ltd. All Rights Reserved.
3 %%
4 %% Licensed under the Apache License, Version 2.0 (the "License");
5 %% you may not use this file except in compliance with the License.
6 %% You may obtain a copy of the License at
7 %%
8 %% http://www.apache.org/licenses/LICENSE-2.0
9 %%
10 %% Unless required by applicable law or agreed to in writing, software
11 %% distributed under the License is distributed on an "AS IS" BASIS,
12 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %% See the License for the specific language governing permissions and
14 %% limitations under the License.
15 %%-------------------------------------------------------------------------
16
17 %% @doc command tool for MQTT-SN client
18 %%
19 %% Caution: not need in most cases, unsless you
20 %% want to test command line, do not use it.
21 %%
22 -module(emqttsn_cli).
23
24 %% @headerfile "emqttsn.hrl"
25
26 -include("emqttsn.hrl").
27
28 -export([main/1]).
29
30 -type sub_cmd() :: pub | sub.
31
32 -define(CMD_NAME, "emqttsn").
33 -define(HELP_OPT, [{help, undefined, "help", boolean, "Help information"}]).
34 -define(CONN_SHORT_OPTS,
35 [{name,
36 $n,
37 "name",
38 {string, ?CLIENT_ID},
39 "client name(equal to client_id, unique for each client)"},
40 {host, $h, "host", {string, "127.0.0.1"}, "mqtt-sn server hostname or IP address"},
41 {port, $p, "port", {integer, 1884}, "mqtt-sn server port number"},
42 {iface, $I, "iface", string, "specify the network interface or ip address to use"},
43 {will, $w, "will", {bool, false}, "whether the client need a will message"},
44 {protocol_version,
45 $V,
46 "protocol-version",
47 {atom, ?MQTTSN_PROTO_V1_2},
48 "mqtt-sn protocol version: v1.2"},
49 {keepalive, $k, "keepalive", {integer, 300}, "keep alive in seconds"}]).
50 -define(CONN_LONG_OPTS,
51 [{will_topic, undefined, "will-topic", string, "Topic for will message"},
52 {will_msg, undefined, "will-message", string, "Payload in will message"},
53 {will_qos, undefined, "will-qos", {integer, 0}, "QoS for will message"},
54 {will_retain, undefined, "will-retain", {boolean, false}, "Retain in will message"}]).
55 -define(PUB_OPTS,
56 ?CONN_SHORT_OPTS
57 ++ [{topic_id_type,
58 $t,
59 "topic_id_type",
60 {integer, ?PRE_DEF_TOPIC_ID},
61 "mqtt topic id type(0 - topic id, 1 - predefined topic id, 2 "
62 "- short topic name)"},
63 {topic_id,
64 $i,
65 "topic_id",
66 integer,
67 "mqtt topic id on which to publish the message(exclusive with "
68 "topic_name)"},
69 {topic_name,
70 $m,
71 "topic_name",
72 string,
73 "mqtt topic name on which to publish the message(exclusive with "
74 "topic_id)"},
75 {qos,
76 $q,
77 "qos",
78 {integer, 0},
79 "qos level of assurance for delivery of an application message"},
80 {retain, $r, "retain", {boolean, false}, "retain message or not"}]
81 ++ ?HELP_OPT
82 ++ ?CONN_LONG_OPTS
83 ++ [{message,
84 undefined,
85 "message",
86 string,
87 "application message that is being published"}]).
88 -define(SUB_OPTS,
89 ?CONN_SHORT_OPTS
90 ++ [{topic_id_type,
91 $t,
92 "topic_id_type",
93 {integer, ?PRE_DEF_TOPIC_ID},
94 "mqtt topic id type(0 - topic id, 1 - predefined topic id, 2 "
95 "- short topic name)"},
96 {topic_id,
97 $i,
98 "topic_id",
99 integer,
100 "mqtt topic id on which to subscribe to(exclusive with topic_name)"},
101 {topic_name,
102 $m,
103 "topic_name",
104 string,
105 "mqtt topic name on which to subscribe to(exclusive with topic_id)"},
106 {qos,
107 $q,
108 "qos",
109 {integer, 0},
110 "maximum qos level at which the server can receive application "
111 "messages to the client"}]
112 ++ ?HELP_OPT
113 ++ ?CONN_LONG_OPTS
114 ++ []).
115
116 %% @doc Intro of a MQTT-SN client line call
117 %%
118 %% Caution: not need in most cases, unsless you
119 %% want to test command line, do not use it.
120 %%
121 %% @param Args array of arguments
122 %%
123 %% @returns A client object
124 %% @end
125 -spec main([string()]) -> ok.
126 main(["sub" | Argv]) ->
127 1 {ok, {Opts, _Args}} = getopt:parse(?SUB_OPTS, Argv),
128 1 ok = maybe_help(sub, Opts),
129
130 1 main(sub, Opts);
131 main(["pub" | Argv]) ->
132 1 {ok, {Opts, _Args}} = getopt:parse(?PUB_OPTS, Argv),
133 1 ok = maybe_help(pub, Opts),
134
135 1 ok = check_required_args(pub, [message], Opts),
136
137 1 main(pub, Opts);
138 main(_Argv) ->
139
:-(
io:format("Usage: ~s pub | sub [--help]~n", [?CMD_NAME]).
140
141 -spec main(pub | sub, [term()]) -> ok | no_return().
142 main(PubSub, Opts) ->
143 2 application:ensure_all_started(emqttsn),
144
145 2 NOpts = parse_cmd_opts(Opts),
146 2 Name = proplists:get_value(name, NOpts),
147 2 Host = proplists:get_value(host, NOpts),
148 2 Port = proplists:get_value(port, NOpts),
149
150 2 {ok, Client, Config} = emqttsn:start_link(Name, NOpts),
151 2 emqttsn:add_host(Client, Host, Port, 1),
152 2 emqttsn:connect(Client, 1, true),
153 2 io:format("Client ~s CONNECT finished", [Name]),
154 2 case PubSub of
155 pub ->
156 1 publish(Client, Config, NOpts),
157 1 disconnect(Client, NOpts);
158 sub ->
159 1 subscribe(Client, Config, NOpts),
160 1 loop_recv()
161 end,
162 1 ok.
163
164 -spec publish(emqtsn:client(), config(), [term()]) -> ok.
165 publish(Client, _Config, Opts) ->
166 1 Message = proplists:get_value(message, Opts),
167 1 Retain = proplists:get_value(retain, Opts),
168 1 TopicIdType = proplists:get_value(topic_id_type, Opts),
169 1 TopicIdOrName =
170 case TopicIdType of
171 ?SHORT_TOPIC_NAME ->
172
:-(
proplists:get_value(topic_name, Opts);
173 ?TOPIC_ID ->
174
:-(
proplists:get_value(topic_id, Opts);
175 ?PRE_DEF_TOPIC_ID ->
176 1 proplists:get_value(topic_id, Opts)
177 end,
178 1 emqttsn:publish(Client, Retain, TopicIdType, TopicIdOrName, Message, true),
179 1 ok.
180
181 -spec loop_recv() -> no_return().
182 loop_recv() ->
183 17175993 loop_recv().
184
185 -spec subscribe(emqtsn:client(), config(), [term()]) -> ok.
186 subscribe(Client, _Config, Opts) ->
187 1 TopicIdType = proplists:get_value(topic_id_type, Opts),
188 1 TopicIdOrName =
189 case TopicIdType of
190 ?SHORT_TOPIC_NAME ->
191
:-(
proplists:get_value(topic_name, Opts);
192 ?TOPIC_ID ->
193
:-(
proplists:get_value(topic_id, Opts);
194 ?PRE_DEF_TOPIC_ID ->
195 1 proplists:get_value(topic_id, Opts)
196 end,
197 1 MaxQos = proplists:get_value(qos, Opts),
198 1 emqttsn:subscribe(Client, TopicIdType, TopicIdOrName, MaxQos, true),
199 1 ok.
200
201 -spec disconnect(emqtsn:client(), [term()]) -> ok.
202 disconnect(Client, _Opts) ->
203 1 emqttsn:finalize(Client),
204 1 ok.
205
206 -spec maybe_help(sub_cmd(), [term()]) -> ok.
207 maybe_help(PubSub, Opts) ->
208 2 case proplists:get_value(help, Opts) of
209 true ->
210
:-(
usage(PubSub),
211
:-(
halt(0);
212 _ ->
213 2 ok
214 end.
215
216 -spec usage(sub_cmd()) -> ok | no_return().
217 usage(PubSub) ->
218
:-(
Opts =
219 case PubSub of
220 pub ->
221
:-(
?PUB_OPTS;
222 sub ->
223
:-(
?SUB_OPTS
224 end,
225
:-(
getopt:usage(Opts, ?CMD_NAME ++ " " ++ atom_to_list(PubSub)).
226
227 -spec check_required_args(sub_cmd(), [atom()], [term()]) -> ok | no_return().
228 check_required_args(PubSub, Keys, Opts) ->
229 1 lists:foreach(fun(Key) ->
230 1 case lists:keyfind(Key, 1, Opts) of
231 false ->
232
:-(
io:format("Error: '~s' required~n", [Key]),
233
:-(
usage(PubSub),
234
:-(
halt(1);
235 1 _ -> ok
236 end
237 end,
238 Keys),
239 1 ok.
240
241 -spec parse_cmd_opts([{atom(), term()}]) -> [{atom(), term()}].
242 parse_cmd_opts(Opts) ->
243 2 parse_cmd_opts(Opts, []).
244
245 -spec parse_cmd_opts([{atom(), term()}], [{atom(), term()}]) -> [{atom(), term()}].
246 parse_cmd_opts([], Acc) ->
247 2 Acc;
248 parse_cmd_opts([{host, Host} | Opts], Acc) ->
249 2 {ok, StdAddress} = inet:parse_ipv4_address(Host),
250 2 parse_cmd_opts(Opts, [{host, StdAddress} | Acc]);
251 parse_cmd_opts([{iface, Interface} | Opts], Acc) ->
252 2 NAcc =
253 case inet:parse_address(Interface) of
254 {ok, IPAddress0} ->
255
:-(
maybe_append(tcp_opts, {ifaddr, IPAddress0}, Acc);
256 _ ->
257 2 case inet:getifaddrs() of
258 {ok, IfAddrs} ->
259 2 case lists:filter(fun ({addr, {_, _, _, _}}) ->
260
:-(
true;
261 (_) ->
262
:-(
false
263 end,
264 proplists:get_value(Interface, IfAddrs, []))
265 of
266 [{addr, IPAddress0}] ->
267
:-(
maybe_append(tcp_opts, {ifaddr, IPAddress0}, Acc);
268 _ ->
269 2 Acc
270 end;
271 _ ->
272
:-(
Acc
273 end
274 end,
275 2 parse_cmd_opts(Opts, NAcc);
276 parse_cmd_opts([{protocol_version, 'v1.2'} | Opts], Acc) ->
277
:-(
parse_cmd_opts(Opts, [{proto_ver, ?MQTTSN_PROTO_V1_2} | Acc]);
278 parse_cmd_opts([{Key, Value} | Opts], Acc) ->
279 22 parse_cmd_opts(Opts, [{Key, Value} | Acc]).
280
281 -spec maybe_append(term(), term(), [{term(), term()}]) -> [{term(), term()}].
282 maybe_append(Key, Value, TupleList) ->
283
:-(
case lists:keytake(Key, 1, TupleList) of
284 {value, {Key, OldValue}, NewTupleList} ->
285
:-(
[{Key, [Value | OldValue]} | NewTupleList];
286 false ->
287
:-(
[{Key, [Value]} | TupleList]
288 end.
Line Hits Source