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 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