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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 777754009990c745ded4b02654a54bc26a2e422d
4
- data.tar.gz: 0e92fba78c0e1938db874d2faed6bee9ef52890c
3
+ metadata.gz: 5f334ef3126b1f0def40579e6f202200d3dacfc3
4
+ data.tar.gz: 9ef4eb0748507a6b744f82011a9da894f909624e
5
5
  SHA512:
6
- metadata.gz: ee45850a33868e480871d6cf7b50bd8277a9a1d1d81e634341a64548d69c77b5145fa49d4ccd1524be6b4358af7ace9414222f3a074d796b9ebe85ee8d97a7a3
7
- data.tar.gz: b295155a5598e8f410b61a4591df6024133e2d9125364d27aa47a4aff10fb308d7ac9c4ac1a7cc8161d5afd5c122ee6b964372c692cba3be7820509426d86866
6
+ metadata.gz: 412649a90228d742ba6812d77924f74559d759199ed1258f9095a9b1e80d759979bc0ceb64065cf11d73a6c4b1b9294102ccd690b7c9ca2d42f695d3c29b86b5
7
+ data.tar.gz: 3533ef2b3774a816a66827341a14aebd4d70534cf08888881ac619f072b9aa09ab7700cc28ba406a8bcffe0a9f3bdf02e66d8c045289a386d31ef2930b6734a3
data/bin/console CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "bundler/setup"
4
- require "PahoMqttRuby"
4
+ require "paho-mqtt"
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
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
- # Your code goes here...
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
- DEFAULT_SSL_PORT = 8883
6
- DEFAULT_PORT = 1883
7
- SELECT_TIMEOUT = 0
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 => 10,
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
- timeout_resp = @last_ping_resp + (@keep_alive * 1.1).ceil
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
- # TODO => MOVE TO LOGGER
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
- raise "Reconnection retry counter is over (#{RECONNECT_RETRY_TIME}), could not reconnect to the server."
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 check_ack_alive(queue, mutex, max_packet)
351
- mutex.synchronize {
352
- now = Time.now
353
- cnt = 0
354
- queue.each do |pck|
355
- if now >= pck[:timestamp] + @ack_timeout
356
- pck[:packet].dup ||= true unless pck[:packet].class == PahoMqtt::Packet::Subscribe || pck[:packet].class == PahoMqtt::Packet::Unsubscribe
357
- unless cnt > max_packet
358
- append_to_writing(pck[:packet])
359
- pck[:timestamp] = now
360
- cnt += 1
361
- end
362
- end
363
- end
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 append_to_writing(packet)
368
- @writing_mutex.synchronize {
369
- @writing_queue.push(packet)
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
- @socket.close unless @socket.nil?
412
+ def config_socket
413
+ unless @socket.nil?
414
+ @socket.close
422
415
  @socket = nil
423
416
  end
424
417
 
425
- @connection_state_mutex.synchronize {
426
- @connection_state = MQTT_CS_DISCONNECT
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
- @writing_mutex.synchronize {
430
- @writing_queue = []
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
- @puback_mutex.synchronize {
434
- @waiting_puback = []
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
- @pubrec_mutex.synchronize {
438
- @waiting_pubrec = []
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
- @pubrel_mutex.synchronize {
442
- @waiting_pubrel = []
443
- }
452
+ @socket.close unless @socket.nil?
453
+ @socket = nil
444
454
 
445
- @pubcomp_mutex.synchronize {
446
- @waiting_pubcomp = []
447
- }
455
+ @last_ping_req = Time.now
456
+ @last_ping_resp = Time.now
448
457
 
449
- @last_packet_id = 0
450
- MQTT_ERR_SUCCESS
451
- end
452
-
453
- def publish(topic, payload="", retain=false, qos=0)
454
- if topic == "" || !topic.is_a?(String)
455
- raise "Publish error, topic is empty or invalid"
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
- def subscribe(*topics)
461
- unless topics.length == 0
462
- send_subscribe(topics)
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
- raise "Protocol Violation, subscribe topics list must not be empty."
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 unsubscribe(topics)
469
- unless topics.length == 0
470
- send_unsubscribe(topics)
471
- else
472
- raise "Protocol Violation, unsubscribe topics list must not be empty."
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
- private
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
- unless @socket.nil?
490
- @socket.close
491
- @socket = nil
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
- raise "Unknow packet received"
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
- # TODO => MOVE TO LOGGER
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
- # puts "New session created"
575
+ @logger.debug("New session created for the client") if @logger.is_a?(Logger)
527
576
  elsif !@clean_session && !packet.session_present
528
- # puts "Could not find session on server side, starting a new one."
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
- # puts "Retrieving previous session on server side."
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
- raise "Invalid qos value used."
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
- raise "Two packet subscribe packet cannot have the same id"
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
- raise "Two packet unsubscribe cannot have the same id"
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
- raise "Unknow qos level for a publish packet"
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
- # TODO => MOVE TO LOGGER
647
- # puts "Unable to connect with this version #{@mqtt_version}"
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
- # TODO => MOVE TO LOGGER
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
- raise "Invalid parameter type #{topics.class} and #{filters.class}"
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
@@ -1,3 +1,3 @@
1
1
  module PahoMqtt
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
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
- # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
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|benchmark)/}) }
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 "../lib/paho_client"
2
- require "../lib/packet_manager"
3
- require "pp"
1
+ require "paho-mqtt"
2
+ require "logger"
4
3
 
5
- cli = PahoRuby::Client.new
6
- cli.ssl = true
7
- cli.config_ssl_context("/Users/Pierre/certs/test/mykey.crt", "/Users/Pierre/certs/test/mykey.key")
8
- cli.connect('test.mosquitto.org', 8883)
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 { |topic, payload, qos| puts ">>>>> This is a LAMBDA callback for message event <<<<<\nTopic: #{topic}\nPayload: #{payload}\nQoS: #{qos}" }
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
- sleep 1
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 3
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 3
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.1
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: 2016-12-27 00:00:00.000000000 Z
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/test_init.rb
79
+ - samples/test_client.rb
79
80
  homepage: http://ruby-dev.jp
80
81
  licenses:
81
82
  - MIT