paho-mqtt 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|