paho-mqtt 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/console +1 -1
- data/lib/paho-mqtt.rb +39 -1
- data/lib/paho.mqtt/paho_client.rb +301 -297
- data/lib/paho.mqtt/version.rb +1 -1
- data/paho-mqtt-0.0.1.gem +0 -0
- data/paho-mqtt.gemspec +2 -2
- data/samples/{test_init.rb → test_client.rb} +20 -11
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f334ef3126b1f0def40579e6f202200d3dacfc3
|
4
|
+
data.tar.gz: 9ef4eb0748507a6b744f82011a9da894f909624e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 412649a90228d742ba6812d77924f74559d759199ed1258f9095a9b1e80d759979bc0ceb64065cf11d73a6c4b1b9294102ccd690b7c9ca2d42f695d3c29b86b5
|
7
|
+
data.tar.gz: 3533ef2b3774a816a66827341a14aebd4d70534cf08888881ac619f072b9aa09ab7700cc28ba406a8bcffe0a9f3bdf02e66d8c045289a386d31ef2930b6734a3
|
data/bin/console
CHANGED
data/lib/paho-mqtt.rb
CHANGED
@@ -3,5 +3,43 @@ require "paho.mqtt/paho_client"
|
|
3
3
|
require "paho.mqtt/packet_manager"
|
4
4
|
|
5
5
|
module PahoMqtt
|
6
|
-
|
6
|
+
|
7
|
+
# Default connection setup
|
8
|
+
DEFAULT_SSL_PORT = 8883
|
9
|
+
DEFAULT_PORT = 1883
|
10
|
+
SELECT_TIMEOUT = 0
|
11
|
+
LOOP_TEMPO = 0.005
|
12
|
+
RECONNECT_RETRY_TIME = 3
|
13
|
+
RECONNECT_RETRY_TEMPO = 5
|
14
|
+
|
15
|
+
# MAX size of queue
|
16
|
+
MAX_PUBACK = 20
|
17
|
+
MAX_PUBREC = 20
|
18
|
+
MAX_PUBREL = 20
|
19
|
+
MAX_PUBCOMP = 20
|
20
|
+
MAX_WRITING = MAX_PUBACK + MAX_PUBREC + MAX_PUBREL + MAX_PUBCOMP
|
21
|
+
|
22
|
+
# Connection states values
|
23
|
+
MQTT_CS_NEW = 0
|
24
|
+
MQTT_CS_CONNECTED = 1
|
25
|
+
MQTT_CS_DISCONNECT = 2
|
26
|
+
MQTT_CS_CONNECT_ASYNC = 3
|
27
|
+
|
28
|
+
# Error values
|
29
|
+
MQTT_ERR_SUCCESS = 0
|
30
|
+
MQTT_ERR_FAIL = 1
|
31
|
+
|
32
|
+
Thread.abort_on_exception = true
|
33
|
+
|
34
|
+
class Exception < ::Exception
|
35
|
+
end
|
36
|
+
|
37
|
+
class ProtocolViolation < PahoMqtt::Exception
|
38
|
+
end
|
39
|
+
|
40
|
+
class ParameterException < PahoMqtt::Exception
|
41
|
+
end
|
42
|
+
|
43
|
+
class PacketException < PahoMqtt::Exception
|
44
|
+
end
|
7
45
|
end
|
@@ -1,45 +1,11 @@
|
|
1
1
|
require 'openssl'
|
2
2
|
require 'socket'
|
3
|
+
require 'logger'
|
3
4
|
|
4
5
|
module PahoMqtt
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
LOOP_TEMPO = 0.005
|
9
|
-
RECONNECT_RETRY_TIME = 3
|
10
|
-
RECONNECT_RETRY_TEMPO = 5
|
11
|
-
|
12
|
-
class Client
|
13
|
-
# MAX size of queue
|
14
|
-
MAX_PUBACK = 20
|
15
|
-
MAX_PUBREC = 20
|
16
|
-
MAX_PUBREL = 20
|
17
|
-
MAX_PUBCOMP = 20
|
18
|
-
MAX_WRITING = MAX_PUBACK + MAX_PUBREC + MAX_PUBREL + MAX_PUBCOMP
|
19
|
-
|
20
|
-
# Connection states values
|
21
|
-
MQTT_CS_NEW = 0
|
22
|
-
MQTT_CS_CONNECTED = 1
|
23
|
-
MQTT_CS_DISCONNECT = 2
|
24
|
-
MQTT_CS_CONNECT_ASYNC = 3
|
25
|
-
|
26
|
-
# Error values
|
27
|
-
MQTT_ERR_AGAIN = -1
|
28
|
-
MQTT_ERR_SUCCESS = 0
|
29
|
-
MQTT_ERR_NOMEM = 1
|
30
|
-
MQTT_ERR_PROTOCOL = 2
|
31
|
-
MQTT_ERR_INVAL = 3
|
32
|
-
MQTT_ERR_NO_CONN = 4
|
33
|
-
MQTT_ERR_CONN_REFUSED = 5
|
34
|
-
MQTT_ERR_NOT_FOUND = 6
|
35
|
-
MQTT_ERR_CONN_LOST = 7
|
36
|
-
MQTT_ERR_TLS = 8
|
37
|
-
MQTT_ERR_PAYLOAD_SIZE = 9
|
38
|
-
MQTT_ERR_NOT_SUPPORTED = 10
|
39
|
-
MQTT_ERR_AUTH = 11
|
40
|
-
MQTT_ERR_ACL_DENIED = 12
|
41
|
-
MQTT_ERR_UNKNOWN = 13
|
42
|
-
MQTT_ERR_ERRNO = 14
|
6
|
+
class Client
|
7
|
+
# Log file
|
8
|
+
attr_accessor :logger
|
43
9
|
|
44
10
|
# Connection related attributes:
|
45
11
|
attr_accessor :host
|
@@ -78,6 +44,7 @@ module PahoMqtt
|
|
78
44
|
attr_reader :connection_state
|
79
45
|
|
80
46
|
ATTR_DEFAULTS = {
|
47
|
+
:logger => nil,
|
81
48
|
:host => "",
|
82
49
|
:port => nil,
|
83
50
|
:mqtt_version => '3.1.1',
|
@@ -91,7 +58,7 @@ module PahoMqtt
|
|
91
58
|
:will_payload => nil,
|
92
59
|
:will_qos => 0,
|
93
60
|
:will_retain => false,
|
94
|
-
:keep_alive =>
|
61
|
+
:keep_alive => 60,
|
95
62
|
:ack_timeout => 5,
|
96
63
|
:on_connack => nil,
|
97
64
|
:on_suback => nil,
|
@@ -102,7 +69,7 @@ module PahoMqtt
|
|
102
69
|
:on_pubcomp => nil,
|
103
70
|
:on_message => nil,
|
104
71
|
}
|
105
|
-
|
72
|
+
|
106
73
|
def initialize(*args)
|
107
74
|
if args.last.is_a?(Hash)
|
108
75
|
attr = args.pop
|
@@ -156,10 +123,6 @@ module PahoMqtt
|
|
156
123
|
@client_id = prefix << Array.new(lenght) { charset.sample }.join
|
157
124
|
end
|
158
125
|
|
159
|
-
def next_packet_id
|
160
|
-
@last_packet_id = ( @last_packet_id || 0 ).next
|
161
|
-
end
|
162
|
-
|
163
126
|
def config_ssl_context(cert_path, key_path, ca_path=nil)
|
164
127
|
@ssl ||= true
|
165
128
|
@ssl_context = ssl_context
|
@@ -168,29 +131,6 @@ module PahoMqtt
|
|
168
131
|
self.root_ca = ca_path
|
169
132
|
end
|
170
133
|
|
171
|
-
def config_socket
|
172
|
-
unless @socket.nil?
|
173
|
-
@socket.close
|
174
|
-
@socket = nil
|
175
|
-
end
|
176
|
-
|
177
|
-
unless @host.nil? || @port < 0
|
178
|
-
tcp_socket = TCPSocket.new(@host, @port)
|
179
|
-
end
|
180
|
-
|
181
|
-
if @ssl
|
182
|
-
unless @ssl_context.nil?
|
183
|
-
@socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, @ssl_context)
|
184
|
-
@socket.sync_close = true
|
185
|
-
@socket.connect
|
186
|
-
else
|
187
|
-
raise "SSL context should be defined and set to open SSLSocket"
|
188
|
-
end
|
189
|
-
else
|
190
|
-
@socket = tcp_socket
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
134
|
def ssl_context
|
195
135
|
@ssl_context ||= OpenSSL::SSL::SSLContext.new
|
196
136
|
end
|
@@ -236,47 +176,6 @@ module PahoMqtt
|
|
236
176
|
setup_connection
|
237
177
|
end
|
238
178
|
|
239
|
-
def setup_connection
|
240
|
-
@mqtt_thread.kill unless @mqtt_thread.nil?
|
241
|
-
if @host.nil? || @host == ""
|
242
|
-
raise "Connection Failed, host cannot be nil or empty"
|
243
|
-
end
|
244
|
-
|
245
|
-
if @port.to_i <= 0
|
246
|
-
raise "Connection Failed port cannot be 0 >="
|
247
|
-
end
|
248
|
-
|
249
|
-
@socket.close unless @socket.nil?
|
250
|
-
@socket = nil
|
251
|
-
|
252
|
-
@last_ping_req = Time.now
|
253
|
-
@last_ping_resp = Time.now
|
254
|
-
|
255
|
-
# TODO => MOVE TO LOGGER
|
256
|
-
# puts "Try to connect to #{@host}"
|
257
|
-
config_socket
|
258
|
-
send_connect
|
259
|
-
|
260
|
-
# Waiting a Connack packet for "ack_timeout" second from the remote
|
261
|
-
connect_timeout = Time.now + @ack_timeout
|
262
|
-
while (Time.now <= connect_timeout) && (@connection_state != MQTT_CS_CONNECTED) do
|
263
|
-
receive_packet
|
264
|
-
end
|
265
|
-
|
266
|
-
if @connection_state != MQTT_CS_CONNECTED
|
267
|
-
# TODO => MOVE TO LOGGER
|
268
|
-
# puts "Didn't receive Connack answer from server #{@host}"
|
269
|
-
else
|
270
|
-
config_subscription
|
271
|
-
config_all_message_queue
|
272
|
-
@mqtt_thread = Thread.new do
|
273
|
-
@reconnect_thread.kill unless @reconnect_thread.nil? || !@reconnect_thread.alive?
|
274
|
-
while @connection_state == MQTT_CS_CONNECTED do
|
275
|
-
mqtt_loop
|
276
|
-
end
|
277
|
-
end
|
278
|
-
end
|
279
|
-
end
|
280
179
|
|
281
180
|
def loop_write(max_packet=MAX_WRITING)
|
282
181
|
@writing_mutex.synchronize {
|
@@ -310,32 +209,12 @@ module PahoMqtt
|
|
310
209
|
check_ack_alive(@waiting_suback, @suback_mutex, @waiting_suback.length)
|
311
210
|
check_ack_alive(@waiting_unsuback, @unsuback_mutex, @waiting_unsuback.length)
|
312
211
|
end
|
313
|
-
|
314
|
-
def check_keep_alive
|
315
|
-
if @keep_alive >= 0 && @connection_state == MQTT_CS_CONNECTED
|
316
|
-
now = Time.now
|
317
|
-
timeout_req = (@last_ping_req + (@keep_alive * 0.7).ceil)
|
318
212
|
|
319
|
-
if timeout_req <= now && @persistent
|
320
|
-
send_pingreq
|
321
|
-
@last_ping_req = now
|
322
|
-
end
|
323
213
|
|
324
|
-
|
325
|
-
if timeout_resp <= now
|
326
|
-
# TODO => MOVE TO LOGGER
|
327
|
-
#puts "Didn't get answer from server for a long time, trying to reconnect."
|
328
|
-
disconnect(false)
|
329
|
-
reconnect(RECONNECT_RETRY_TIME, RECONNECT_RETRY_TEMPO) if @persistent
|
330
|
-
end
|
331
|
-
end
|
332
|
-
end
|
333
|
-
|
334
|
-
def reconnect(retry_time=3, retry_tempo=3)
|
214
|
+
def reconnect(retry_time=RECONNECT_RETRY_TIME, retry_tempo=RECONNECT_RETRY_TEMPO)
|
335
215
|
@reconnect_thread = Thread.new do
|
336
216
|
retry_time.times do
|
337
|
-
|
338
|
-
#puts "Retrying to connect"
|
217
|
+
@logger.debug("New reconnect atempt...") if @logger.is_a?(Logger)
|
339
218
|
setup_connection
|
340
219
|
if @connection_state == MQTT_CS_CONNECTED
|
341
220
|
break
|
@@ -343,34 +222,155 @@ module PahoMqtt
|
|
343
222
|
sleep retry_tempo
|
344
223
|
end
|
345
224
|
end
|
346
|
-
|
225
|
+
if @connection_state != MQTT_CS_CONNECTED
|
226
|
+
@logger.error("Reconnection atempt counter is over.(#{RECONNECT_RETRY_TIME} times)") if @logger.is_a?(Logger)
|
227
|
+
disconnect(false)
|
228
|
+
exit
|
229
|
+
end
|
347
230
|
end
|
348
231
|
end
|
349
232
|
|
350
|
-
def
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
233
|
+
def disconnect(explicit=true)
|
234
|
+
@logger.debug("Disconnecting from #{@host}") if @logger.is_a?(Logger)
|
235
|
+
|
236
|
+
if explicit
|
237
|
+
send_disconnect
|
238
|
+
@mqtt_thread.kill if @mqtt_thread && @mqtt_thread.alive?
|
239
|
+
@mqtt_thread.kill if @mqtt_thread.alive?
|
240
|
+
@last_packet_id = 0
|
241
|
+
|
242
|
+
@writing_mutex.synchronize {
|
243
|
+
@writing_queue = []
|
244
|
+
}
|
245
|
+
|
246
|
+
@puback_mutex.synchronize {
|
247
|
+
@waiting_puback = []
|
248
|
+
}
|
249
|
+
|
250
|
+
@pubrec_mutex.synchronize {
|
251
|
+
@waiting_pubrec = []
|
252
|
+
}
|
253
|
+
|
254
|
+
@pubrel_mutex.synchronize {
|
255
|
+
@waiting_pubrel = []
|
256
|
+
}
|
257
|
+
|
258
|
+
@pubcomp_mutex.synchronize {
|
259
|
+
@waiting_pubcomp = []
|
260
|
+
}
|
261
|
+
end
|
262
|
+
|
263
|
+
@socket.close unless @socket.nil?
|
264
|
+
@socket = nil
|
265
|
+
|
266
|
+
@connection_state_mutex.synchronize {
|
267
|
+
@connection_state = MQTT_CS_DISCONNECT
|
364
268
|
}
|
269
|
+
MQTT_ERR_SUCCESS
|
270
|
+
end
|
271
|
+
|
272
|
+
def publish(topic, payload="", retain=false, qos=0)
|
273
|
+
if topic == "" || !topic.is_a?(String)
|
274
|
+
@logger.error("Publish topics is invalid, not a string or empty.") if @logger.is_a?(Logger)
|
275
|
+
raise ParameterException
|
276
|
+
end
|
277
|
+
send_publish(topic, payload, retain, qos)
|
365
278
|
end
|
366
279
|
|
367
|
-
def
|
368
|
-
|
369
|
-
|
370
|
-
|
280
|
+
def subscribe(*topics)
|
281
|
+
unless topics.length == 0
|
282
|
+
send_subscribe(topics)
|
283
|
+
else
|
284
|
+
@logger.error("Subscribe topics need one topic or a list of topics.") if @logger.is_a?(Logger)
|
285
|
+
disconnect(false)
|
286
|
+
raise ProtocolViolation
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def unsubscribe(*topics)
|
291
|
+
unless topics.length == 0
|
292
|
+
send_unsubscribe(topics)
|
293
|
+
else
|
294
|
+
@logger.error("Unsubscribe need at least one topics.") if @logger.is_a?(Logger)
|
295
|
+
disconnect(false)
|
296
|
+
raise ProtocolViolation
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def ping_host
|
301
|
+
send_pingreq
|
302
|
+
end
|
303
|
+
|
304
|
+
def add_topic_callback(topic, callback=nil, &block)
|
305
|
+
if topic.nil?
|
306
|
+
@logger.error("The topics where the callback is trying to be registered have been found nil.") if @logger.is_a?(Logger)
|
307
|
+
raise ParameterException
|
308
|
+
end
|
309
|
+
remove_topic_callback(topic)
|
310
|
+
|
311
|
+
if block_given?
|
312
|
+
@registered_callback.push([topic, block])
|
313
|
+
elsif !(callback.nil?) && callback.class == Proc
|
314
|
+
@registered_callback.push([topic, callback])
|
315
|
+
end
|
371
316
|
MQTT_ERR_SUCCESS
|
372
317
|
end
|
373
318
|
|
319
|
+
def remove_topic_callback(topic)
|
320
|
+
if topic.nil?
|
321
|
+
@logger.error("The topics where the callback is trying to be unregistered have been found nil.") if @logger.is_a?(Logger)
|
322
|
+
raise ParameterException
|
323
|
+
end
|
324
|
+
@registered_callback.delete_if {|pair| pair.first == topic}
|
325
|
+
MQTT_ERR_SUCCESS
|
326
|
+
end
|
327
|
+
|
328
|
+
def on_connack(&block)
|
329
|
+
@on_connack = block if block_given?
|
330
|
+
@on_connack
|
331
|
+
end
|
332
|
+
|
333
|
+
def on_suback(&block)
|
334
|
+
@on_suback = block if block_given?
|
335
|
+
@on_suback
|
336
|
+
end
|
337
|
+
|
338
|
+
def on_unsuback(&block)
|
339
|
+
@on_unsuback = block if block_given?
|
340
|
+
@on_unsuback
|
341
|
+
end
|
342
|
+
|
343
|
+
def on_puback(&block)
|
344
|
+
@on_puback = block if block_given?
|
345
|
+
@on_puback
|
346
|
+
end
|
347
|
+
|
348
|
+
def on_pubrec(&block)
|
349
|
+
@on_pubrec = block if block_given?
|
350
|
+
@on_pubrec
|
351
|
+
end
|
352
|
+
|
353
|
+
def on_pubrel(&block)
|
354
|
+
@on_pubrel = block if block_given?
|
355
|
+
@on_pubrel
|
356
|
+
end
|
357
|
+
|
358
|
+
def on_pubcomp(&block)
|
359
|
+
@on_pubcomp = block if block_given?
|
360
|
+
@on_pubcomp
|
361
|
+
end
|
362
|
+
|
363
|
+
def on_message(&block)
|
364
|
+
@on_message = block if block_given?
|
365
|
+
@on_message
|
366
|
+
end
|
367
|
+
|
368
|
+
private
|
369
|
+
|
370
|
+
def next_packet_id
|
371
|
+
@last_packet_id = ( @last_packet_id || 0 ).next
|
372
|
+
end
|
373
|
+
|
374
374
|
def config_subscription
|
375
375
|
unless @subscribed_topics == []
|
376
376
|
new_id = next_packet_id
|
@@ -402,82 +402,128 @@ module PahoMqtt
|
|
402
402
|
queue.each do |pck|
|
403
403
|
pck[:packet].dup ||= true
|
404
404
|
if cnt <= max_packet
|
405
|
-
append_to_writing(pck)
|
405
|
+
append_to_writing(pck[:packet])
|
406
406
|
cnt += 1
|
407
407
|
end
|
408
408
|
end
|
409
409
|
}
|
410
410
|
end
|
411
|
-
|
412
|
-
def disconnect(explicit=true)
|
413
|
-
# TODO => MOVE TO LOGGER
|
414
|
-
# puts "Disconnecting"
|
415
|
-
|
416
|
-
if explicit
|
417
|
-
send_disconnect
|
418
|
-
@mqtt_thread.kill if @mqtt_thread && @mqtt_thread.alive?
|
419
|
-
@mqtt_thread.kill if @mqtt_thread.alive?
|
420
411
|
|
421
|
-
|
412
|
+
def config_socket
|
413
|
+
unless @socket.nil?
|
414
|
+
@socket.close
|
422
415
|
@socket = nil
|
423
416
|
end
|
424
417
|
|
425
|
-
@
|
426
|
-
|
427
|
-
|
418
|
+
unless @host.nil? || @port < 0
|
419
|
+
begin
|
420
|
+
tcp_socket = TCPSocket.new(@host, @port)
|
421
|
+
rescue ::Exception => exp
|
422
|
+
@logger.warn("Could not open a socket with #{@host} on port #{@port}") if @logger.is_a?(Logger)
|
423
|
+
end
|
424
|
+
end
|
428
425
|
|
429
|
-
@
|
430
|
-
@
|
431
|
-
|
426
|
+
if @ssl
|
427
|
+
unless @ssl_context.nil?
|
428
|
+
@socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, @ssl_context)
|
429
|
+
@socket.sync_close = true
|
430
|
+
@socket.connect
|
431
|
+
else
|
432
|
+
@logger.error("The ssl context was found as nil while the socket's opening.") if @logger.is_a?(Logger)
|
433
|
+
raise Exception
|
434
|
+
end
|
435
|
+
else
|
436
|
+
@socket = tcp_socket
|
437
|
+
end
|
438
|
+
end
|
432
439
|
|
433
|
-
|
434
|
-
|
435
|
-
|
440
|
+
def setup_connection
|
441
|
+
@mqtt_thread.kill unless @mqtt_thread.nil?
|
442
|
+
if @host.nil? || @host == ""
|
443
|
+
@logger.error("The host was found as nil while the connection setup.") if @logger.is_a?(Logger)
|
444
|
+
raise ParameterException
|
445
|
+
end
|
436
446
|
|
437
|
-
@
|
438
|
-
@
|
439
|
-
|
447
|
+
if @port.to_i <= 0
|
448
|
+
@logger.error("The port value is invalid (<= 0). Could not setup the connection.") if @logger.is_a?(Logger)
|
449
|
+
raise ParameterException
|
450
|
+
end
|
440
451
|
|
441
|
-
|
442
|
-
|
443
|
-
}
|
452
|
+
@socket.close unless @socket.nil?
|
453
|
+
@socket = nil
|
444
454
|
|
445
|
-
@
|
446
|
-
|
447
|
-
}
|
455
|
+
@last_ping_req = Time.now
|
456
|
+
@last_ping_resp = Time.now
|
448
457
|
|
449
|
-
@
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
458
|
+
@logger.debug("Atempt to connect to host: #{@host}") if @logger.is_a?(Logger)
|
459
|
+
config_socket
|
460
|
+
|
461
|
+
unless @socket.nil?
|
462
|
+
send_connect
|
463
|
+
# Waiting a Connack packet for "ack_timeout" second from the remote
|
464
|
+
connect_timeout = Time.now + @ack_timeout
|
465
|
+
while (Time.now <= connect_timeout) && (@connection_state != MQTT_CS_CONNECTED) do
|
466
|
+
receive_packet
|
467
|
+
sleep 0.0001
|
468
|
+
end
|
456
469
|
end
|
457
|
-
send_publish(topic, payload, retain, qos)
|
458
|
-
end
|
459
470
|
|
460
|
-
|
461
|
-
|
462
|
-
|
471
|
+
if @connection_state != MQTT_CS_CONNECTED
|
472
|
+
@logger.warn("Connection failed. Couldn't recieve a Connack packet from: #{@host}, socket is \"#{@socket}\".") if @logger.is_a?(Logger)
|
473
|
+
|
474
|
+
unless Thread.current == @reconnect_thread
|
475
|
+
raise Exception.new("Connection failed. Check log for more details.")
|
476
|
+
end
|
463
477
|
else
|
464
|
-
|
478
|
+
config_subscription
|
479
|
+
@mqtt_thread = Thread.new do
|
480
|
+
@reconnect_thread.kill unless @reconnect_thread.nil? || !@reconnect_thread.alive?
|
481
|
+
while @connection_state == MQTT_CS_CONNECTED do
|
482
|
+
mqtt_loop
|
483
|
+
end
|
484
|
+
end
|
465
485
|
end
|
466
486
|
end
|
467
487
|
|
468
|
-
def
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
488
|
+
def check_keep_alive
|
489
|
+
if @keep_alive >= 0 && @connection_state == MQTT_CS_CONNECTED
|
490
|
+
now = Time.now
|
491
|
+
timeout_req = (@last_ping_req + (@keep_alive * 0.7).ceil)
|
492
|
+
|
493
|
+
if timeout_req <= now && @persistent
|
494
|
+
send_pingreq
|
495
|
+
@last_ping_req = now
|
496
|
+
end
|
497
|
+
|
498
|
+
timeout_resp = @last_ping_resp + (@keep_alive * 1.1).ceil
|
499
|
+
if timeout_resp <= now
|
500
|
+
@logger.debug("No activity over timeout, trying to reconnect to #{@host}") if @logger.is_a?(Logger)
|
501
|
+
disconnect(false)
|
502
|
+
reconnect if @persistent
|
503
|
+
end
|
473
504
|
end
|
474
505
|
end
|
475
|
-
|
476
|
-
|
477
|
-
|
506
|
+
|
507
|
+
def check_ack_alive(queue, mutex, max_packet)
|
508
|
+
mutex.synchronize {
|
509
|
+
now = Time.now
|
510
|
+
cnt = 0
|
511
|
+
queue.each do |pck|
|
512
|
+
if now >= pck[:timestamp] + @ack_timeout
|
513
|
+
pck[:packet].dup ||= true unless pck[:packet].class == PahoMqtt::Packet::Subscribe || pck[:packet].class == PahoMqtt::Packet::Unsubscribe
|
514
|
+
unless cnt > max_packet
|
515
|
+
append_to_writing(pck[:packet])
|
516
|
+
pck[:timestamp] = now
|
517
|
+
cnt += 1
|
518
|
+
end
|
519
|
+
end
|
520
|
+
end
|
521
|
+
}
|
522
|
+
end
|
523
|
+
|
478
524
|
def receive_packet
|
479
525
|
begin
|
480
|
-
result = IO.select([@socket], [], [], SELECT_TIMEOUT)
|
526
|
+
result = IO.select([@socket], [], [], SELECT_TIMEOUT) unless @socket.nil?
|
481
527
|
unless result.nil?
|
482
528
|
packet = PahoMqtt::Packet.read(@socket)
|
483
529
|
unless packet.nil?
|
@@ -485,16 +531,19 @@ module PahoMqtt
|
|
485
531
|
@last_ping_resp = Time.now
|
486
532
|
end
|
487
533
|
end
|
488
|
-
rescue Exception => exp
|
489
|
-
|
490
|
-
|
491
|
-
|
534
|
+
rescue ::Exception => exp
|
535
|
+
disconnect(false)
|
536
|
+
if @persistent
|
537
|
+
reconnect
|
538
|
+
else
|
539
|
+
@logger.error("The packet reading have failed.") if @logger.is_a?(Logger)
|
540
|
+
raise(exp)
|
492
541
|
end
|
493
|
-
raise(exp)
|
494
542
|
end
|
495
543
|
end
|
496
|
-
|
497
|
-
def handle_packet(packet)
|
544
|
+
|
545
|
+
def handle_packet(packet)
|
546
|
+
@logger.info("New packet #{packet.class} recieved.") if @logger.is_a?(Logger)
|
498
547
|
if packet.class == PahoMqtt::Packet::Connack
|
499
548
|
handle_connack(packet)
|
500
549
|
elsif packet.class == PahoMqtt::Packet::Suback
|
@@ -511,28 +560,29 @@ module PahoMqtt
|
|
511
560
|
handle_pubrel(packet)
|
512
561
|
elsif packet.class == PahoMqtt::Packet::Pubcomp
|
513
562
|
handle_pubcomp(packet)
|
514
|
-
elsif packet.class ==PahoMqtt::Packet::Pingresp
|
563
|
+
elsif packet.class == PahoMqtt::Packet::Pingresp
|
515
564
|
handle_pingresp
|
516
565
|
else
|
517
|
-
|
566
|
+
@logger.error("The packets header is invalid for packet: #{packet}") if @logger.is_a?(Logger)
|
567
|
+
raise PacketException
|
518
568
|
end
|
519
569
|
end
|
520
570
|
|
521
571
|
def handle_connack(packet)
|
522
|
-
if packet.return_code == 0x00
|
523
|
-
|
524
|
-
# puts "Connection accepted, ready to process"
|
572
|
+
if packet.return_code == 0x00
|
573
|
+
@logger.debug("Connack receive and connection accepted.") if @logger.is_a?(Logger)
|
525
574
|
if @clean_session && !packet.session_present
|
526
|
-
|
575
|
+
@logger.debug("New session created for the client") if @logger.is_a?(Logger)
|
527
576
|
elsif !@clean_session && !packet.session_present
|
528
|
-
|
577
|
+
@logger.debug("No previous session found by server, starting a new one.") if @logger.is_a?(Logger)
|
529
578
|
elsif !@clean_session && packet.session_present
|
530
|
-
|
579
|
+
@logger.debug("Previous session restored by the server.") if @logger.is_a?(Logger)
|
531
580
|
end
|
532
581
|
@connection_state_mutex.synchronize{
|
533
582
|
@connection_state = MQTT_CS_CONNECTED
|
534
583
|
}
|
535
584
|
else
|
585
|
+
disconnect(false)
|
536
586
|
handle_connack_error(packet.return_code)
|
537
587
|
end
|
538
588
|
config_all_message_queue
|
@@ -547,7 +597,7 @@ module PahoMqtt
|
|
547
597
|
def handle_pingresp
|
548
598
|
@last_ping_resp = Time.now
|
549
599
|
end
|
550
|
-
|
600
|
+
|
551
601
|
def handle_suback(packet)
|
552
602
|
adjust_qos = []
|
553
603
|
max_qos = packet.return_codes
|
@@ -562,11 +612,13 @@ module PahoMqtt
|
|
562
612
|
elsif max_qos[0] == 128
|
563
613
|
adjust_qos.delete(t)
|
564
614
|
else
|
565
|
-
|
615
|
+
@logger.error("The qos value is invalid in subscribe.") if @logger.is_a?(Logger)
|
616
|
+
raise PacketException
|
566
617
|
end
|
567
618
|
end
|
568
619
|
else
|
569
|
-
|
620
|
+
@logger.error("The packet id is invalid, already used.") if @logger.is_a?(Logger)
|
621
|
+
raise PacketException
|
570
622
|
end
|
571
623
|
@subscribed_mutex.synchronize {
|
572
624
|
@subscribed_topics.concat(adjust_qos)
|
@@ -583,7 +635,8 @@ module PahoMqtt
|
|
583
635
|
if to_unsub.length == 1
|
584
636
|
to_unsub = to_unsub.first[:packet].topics
|
585
637
|
else
|
586
|
-
|
638
|
+
@logger.error("The packet id is invalid, already used.") if @logger.is_a?(Logger)
|
639
|
+
raise PacketException
|
587
640
|
end
|
588
641
|
|
589
642
|
@subscribed_mutex.synchronize {
|
@@ -593,7 +646,7 @@ module PahoMqtt
|
|
593
646
|
}
|
594
647
|
@on_unsuback.call unless @on_unsuback.nil?
|
595
648
|
end
|
596
|
-
|
649
|
+
|
597
650
|
def handle_publish(packet)
|
598
651
|
case packet.qos
|
599
652
|
when 0
|
@@ -602,13 +655,14 @@ module PahoMqtt
|
|
602
655
|
when 2
|
603
656
|
send_pubrec(packet.id)
|
604
657
|
else
|
605
|
-
|
658
|
+
@logger.error("The packet qos value is invalid in publish.") if @logger.is_a?(Logger)
|
659
|
+
raise PacketException
|
606
660
|
end
|
607
661
|
|
608
662
|
@on_message.call(packet) unless @on_message.nil?
|
609
663
|
@registered_callback.assoc(packet.topic).last.call if @registered_callback.any? { |pair| pair.first == packet.topic}
|
610
664
|
end
|
611
|
-
|
665
|
+
|
612
666
|
def handle_puback(packet)
|
613
667
|
@puback_mutex.synchronize{
|
614
668
|
@waiting_puback.delete_if { |pck| pck[:id] == packet.id }
|
@@ -639,33 +693,44 @@ module PahoMqtt
|
|
639
693
|
@on_pubcomp.call unless @on_pubcomp.nil?
|
640
694
|
end
|
641
695
|
|
642
|
-
### MOVE TO ERROR HANDLER CLASS
|
643
696
|
def handle_connack_error(return_code)
|
644
697
|
case return_code
|
645
698
|
when 0x01
|
646
|
-
|
647
|
-
|
648
|
-
if @mqtt_version == "3.1.1"
|
699
|
+
@logger.debug("Unable to connect to the server with the version #{@mqtt_version}, trying 3.1") if @logger.is_a?(Logger)
|
700
|
+
if @mqtt_version != "3.1"
|
649
701
|
@mqtt_version = "3.1"
|
650
702
|
connect(@host, @port, @keep_alive)
|
651
703
|
end
|
652
704
|
when 0x02
|
653
|
-
|
705
|
+
@logger.warn("Client Identifier is correct but not allowed by remote server.") if @logger.is_a?(Logger)
|
706
|
+
MQTT_ERR_FAIL
|
654
707
|
when 0x03
|
655
|
-
|
708
|
+
@logger.warn("Connection established but MQTT service unvailable on remote server.") if @logger.is_a?(Logger)
|
709
|
+
MQTT_ERR_FAIL
|
656
710
|
when 0x04
|
657
|
-
|
711
|
+
@logger.warn("User name or user password is malformed.") if @logger.is_a?(Logger)
|
712
|
+
MQTT_ERR_FAIL
|
658
713
|
when 0x05
|
659
|
-
|
714
|
+
@logger.warn("Client is not authorized to connect to the server.") if @logger.is_a?(Logger)
|
715
|
+
MQTT_ERR_FAIL
|
660
716
|
end
|
661
717
|
end
|
662
718
|
|
663
719
|
def send_packet(packet)
|
664
720
|
@socket.write(packet.to_s)
|
721
|
+
@logger.info("A packet #{packet.class} have been sent.") if @logger.is_a?(Logger)
|
665
722
|
@last_ping_req = Time.now
|
666
723
|
MQTT_ERR_SUCCESS
|
667
724
|
end
|
668
725
|
|
726
|
+
|
727
|
+
def append_to_writing(packet)
|
728
|
+
@writing_mutex.synchronize {
|
729
|
+
@writing_queue.push(packet)
|
730
|
+
}
|
731
|
+
MQTT_ERR_SUCCESS
|
732
|
+
end
|
733
|
+
|
669
734
|
def send_connect
|
670
735
|
packet = PahoMqtt::Packet::Connect.new(
|
671
736
|
:version => @mqtt_version,
|
@@ -680,18 +745,20 @@ module PahoMqtt
|
|
680
745
|
:will_retain => @will_retain
|
681
746
|
)
|
682
747
|
send_packet(packet)
|
748
|
+
MQTT_ERR_SUCCESS
|
683
749
|
end
|
684
750
|
|
685
751
|
def send_disconnect
|
686
752
|
packet = PahoMqtt::Packet::Disconnect.new
|
687
753
|
send_packet(packet)
|
754
|
+
MQTT_ERR_SUCCESS
|
688
755
|
end
|
689
756
|
|
690
757
|
def send_pingreq
|
691
758
|
packet = PahoMqtt::Packet::Pingreq.new
|
692
|
-
|
693
|
-
# puts "Check if the connection is still alive."
|
759
|
+
@logger.debug("Checking if server is still alive.") if @logger.is_a?(Logger)
|
694
760
|
send_packet(packet)
|
761
|
+
MQTT_ERR_SUCCESS
|
695
762
|
end
|
696
763
|
|
697
764
|
def send_subscribe(topics)
|
@@ -706,8 +773,6 @@ module PahoMqtt
|
|
706
773
|
@suback_mutex.synchronize {
|
707
774
|
@waiting_suback.push({ :id => new_id, :packet => packet, :timestamp => Time.now })
|
708
775
|
}
|
709
|
-
else
|
710
|
-
raise "Protocol Violation, subscribe topics list must not be empty."
|
711
776
|
end
|
712
777
|
MQTT_ERR_SUCCESS
|
713
778
|
end
|
@@ -724,8 +789,6 @@ module PahoMqtt
|
|
724
789
|
@unsuback_mutex.synchronize {
|
725
790
|
@waiting_unsuback.push({:id => new_id, :packet => packet, :timestamp => Time.now})
|
726
791
|
}
|
727
|
-
else
|
728
|
-
raise "Protocol Violation, unsubscribe topics list must not be empty."
|
729
792
|
end
|
730
793
|
MQTT_ERR_SUCCESS
|
731
794
|
end
|
@@ -799,72 +862,13 @@ module PahoMqtt
|
|
799
862
|
MQTT_ERR_SUCCESS
|
800
863
|
end
|
801
864
|
|
802
|
-
def add_topic_callback(topic, callback=nil, &block)
|
803
|
-
raise "Trying to register a callback for an undefined topic" if topic.nil?
|
804
|
-
|
805
|
-
remove_topic_callback(topic)
|
806
|
-
|
807
|
-
if block_given?
|
808
|
-
@registered_callback.push([topic, block])
|
809
|
-
elsif !(callback.nil?) && callback.class == Proc
|
810
|
-
@registered_callback.push([topic, callback])
|
811
|
-
end
|
812
|
-
MQTT_ERR_SUCCESS
|
813
|
-
end
|
814
|
-
|
815
|
-
def remove_topic_callback(topic)
|
816
|
-
raise "Trying to unregister a callback for an undefined topic" if topic.nil?
|
817
|
-
|
818
|
-
@registered_callback.delete_if {|pair| pair.first == topic}
|
819
|
-
MQTT_ERR_SUCCESS
|
820
|
-
end
|
821
|
-
|
822
|
-
def on_connack(&block)
|
823
|
-
@on_connack = block if block_given?
|
824
|
-
@on_connack
|
825
|
-
end
|
826
|
-
|
827
|
-
def on_suback(&block)
|
828
|
-
@on_suback = block if block_given?
|
829
|
-
@on_suback
|
830
|
-
end
|
831
|
-
|
832
|
-
def on_unsuback(&block)
|
833
|
-
@on_unsuback = block if block_given?
|
834
|
-
@on_unsuback
|
835
|
-
end
|
836
|
-
|
837
|
-
def on_puback(&block)
|
838
|
-
@on_puback = block if block_given?
|
839
|
-
@on_puback
|
840
|
-
end
|
841
|
-
|
842
|
-
def on_pubrec(&block)
|
843
|
-
@on_pubrec = block if block_given?
|
844
|
-
@on_pubrec
|
845
|
-
end
|
846
|
-
|
847
|
-
def on_pubrel(&block)
|
848
|
-
@on_pubrel = block if block_given?
|
849
|
-
@on_pubrel
|
850
|
-
end
|
851
|
-
|
852
|
-
def on_pubcomp(&block)
|
853
|
-
@on_pubcomp = block if block_given?
|
854
|
-
@on_pubcomp
|
855
|
-
end
|
856
|
-
|
857
|
-
def on_message(&block)
|
858
|
-
@on_message = block if block_given?
|
859
|
-
@on_message
|
860
|
-
end
|
861
|
-
|
862
865
|
def match_filter(topics, filters)
|
863
866
|
if topics.is_a?(String) && filters.is_a?(String)
|
864
867
|
topic = topics.split('/')
|
865
868
|
filter = filters.split('/')
|
866
869
|
else
|
867
|
-
|
870
|
+
@logger.error("Topics and filters are not found as String while matching topics to filter.") if @logger.is_a?(Logger)
|
871
|
+
raise ParameterException
|
868
872
|
end
|
869
873
|
|
870
874
|
rc = false
|
data/lib/paho.mqtt/version.rb
CHANGED
data/paho-mqtt-0.0.1.gem
ADDED
Binary file
|
data/paho-mqtt.gemspec
CHANGED
@@ -17,12 +17,12 @@ Gem::Specification.new do |spec|
|
|
17
17
|
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
18
18
|
# delete this section to allow pushing this gem to any host.
|
19
19
|
if spec.respond_to?(:metadata)
|
20
|
-
#
|
20
|
+
# spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'
|
21
21
|
else
|
22
22
|
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
23
23
|
end
|
24
24
|
|
25
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features
|
25
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
26
26
|
spec.bindir = "exe"
|
27
27
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
28
|
spec.require_paths = ["lib"]
|
@@ -1,16 +1,20 @@
|
|
1
|
-
require "
|
2
|
-
require "
|
3
|
-
require "pp"
|
1
|
+
require "paho-mqtt"
|
2
|
+
require "logger"
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
file = File.open('paho.log', "a+")
|
5
|
+
log = Logger.new(file)
|
6
|
+
log.level = Logger::DEBUG
|
7
|
+
|
8
|
+
cli = PahoMqtt::Client.new({logger: log, persistent: true, keep_alive: 7})
|
9
|
+
|
10
|
+
cli.connect('localhost', 1883)
|
9
11
|
|
10
12
|
#########################################################
|
11
13
|
### Callback settings
|
14
|
+
waiting = true
|
15
|
+
cli.on_suback { waiting = false}
|
12
16
|
|
13
|
-
cli.on_message = lambda { |
|
17
|
+
cli.on_message = lambda { |p| puts ">>>>> This is a LAMBDA callback for message event <<<<<\nTopic: #{p.topic}\nPayload: #{p.payload}\nQoS: #{p.qos}" }
|
14
18
|
|
15
19
|
toto_toto = lambda { puts ">>>>> I am LAMBDA callback for the /toto/toto topic <<<<<" }
|
16
20
|
toto_tata = proc { puts ">>>>> I am PROC callback for the /toto/tata topic <<<<<" }
|
@@ -26,7 +30,9 @@ cli.add_topic_callback('/toto/toto', toto_toto)
|
|
26
30
|
|
27
31
|
cli.subscribe(['/toto/toto', 0], ['/toto/tata', 1], ['/toto/tutu', 2], ["/toto", 0])
|
28
32
|
|
29
|
-
|
33
|
+
while waiting do
|
34
|
+
sleep 0.0001
|
35
|
+
end
|
30
36
|
|
31
37
|
cli.publish("/toto/tutu", "It's me!", false, 2)
|
32
38
|
cli.publish("/toto/tutu", "It's you!", false, 1)
|
@@ -40,7 +46,7 @@ cli.publish("/toto/toto", "It's me!", false, 2)
|
|
40
46
|
cli.publish("/toto/toto", "It's you!", false, 1)
|
41
47
|
cli.publish("/toto/toto", "It's them!", false, 0)
|
42
48
|
|
43
|
-
sleep
|
49
|
+
sleep cli.ack_timeout
|
44
50
|
|
45
51
|
cli.on_message = nil
|
46
52
|
toto_tutu = lambda { puts ">>>>> Changing callback type to LAMBDA for the /toto/tutu topic <<<<<" }
|
@@ -57,8 +63,11 @@ cli.publish("/toto/tata", "It's me!", false, 2)
|
|
57
63
|
cli.publish("/toto/tata", "It's you!", false, 1)
|
58
64
|
cli.publish("/toto/tata", "It's them!", false, 0)
|
59
65
|
|
60
|
-
sleep
|
66
|
+
sleep cli.ack_timeout
|
67
|
+
|
61
68
|
cli.unsubscribe('+/tutu', "+/+")
|
69
|
+
|
70
|
+
puts "Waiting 10 sec for keeping alive..."
|
62
71
|
sleep 10
|
63
72
|
|
64
73
|
cli.disconnect
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paho-mqtt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pierre Goudet
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -73,9 +73,10 @@ files:
|
|
73
73
|
- lib/paho.mqtt/packet_manager.rb
|
74
74
|
- lib/paho.mqtt/paho_client.rb
|
75
75
|
- lib/paho.mqtt/version.rb
|
76
|
+
- paho-mqtt-0.0.1.gem
|
76
77
|
- paho-mqtt.gemspec
|
77
78
|
- samples/test_aws.rb
|
78
|
-
- samples/
|
79
|
+
- samples/test_client.rb
|
79
80
|
homepage: http://ruby-dev.jp
|
80
81
|
licenses:
|
81
82
|
- MIT
|