mqtt-rails 1.0
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 +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE +210 -0
- data/README.md +323 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/mqtt-rails.rb +144 -0
- data/lib/mqtt_rails/client.rb +414 -0
- data/lib/mqtt_rails/connection_helper.rb +172 -0
- data/lib/mqtt_rails/exception.rb +52 -0
- data/lib/mqtt_rails/handler.rb +274 -0
- data/lib/mqtt_rails/packet.rb +33 -0
- data/lib/mqtt_rails/packet/base.rb +315 -0
- data/lib/mqtt_rails/packet/connack.rb +102 -0
- data/lib/mqtt_rails/packet/connect.rb +183 -0
- data/lib/mqtt_rails/packet/disconnect.rb +38 -0
- data/lib/mqtt_rails/packet/pingreq.rb +29 -0
- data/lib/mqtt_rails/packet/pingresp.rb +38 -0
- data/lib/mqtt_rails/packet/puback.rb +44 -0
- data/lib/mqtt_rails/packet/pubcomp.rb +44 -0
- data/lib/mqtt_rails/packet/publish.rb +148 -0
- data/lib/mqtt_rails/packet/pubrec.rb +44 -0
- data/lib/mqtt_rails/packet/pubrel.rb +62 -0
- data/lib/mqtt_rails/packet/suback.rb +75 -0
- data/lib/mqtt_rails/packet/subscribe.rb +124 -0
- data/lib/mqtt_rails/packet/unsuback.rb +49 -0
- data/lib/mqtt_rails/packet/unsubscribe.rb +84 -0
- data/lib/mqtt_rails/publisher.rb +181 -0
- data/lib/mqtt_rails/sender.rb +129 -0
- data/lib/mqtt_rails/ssl_helper.rb +61 -0
- data/lib/mqtt_rails/subscriber.rb +166 -0
- data/lib/mqtt_rails/version.rb +3 -0
- data/mqtt-rails.gemspec +33 -0
- data/samples/client_blocking(reading).rb +29 -0
- data/samples/client_blocking(writing).rb +18 -0
- data/samples/getting_started.rb +49 -0
- data/samples/test_client.rb +69 -0
- metadata +126 -0
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "mqtt-rails"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/lib/mqtt-rails.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
# Copyright (c) 2016-2017 Pierre Goudet <p-goudet@ruby-dev.jp>
|
2
|
+
#
|
3
|
+
# All rights reserved. This program and the accompanying materials
|
4
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
5
|
+
# and Eclipse Distribution License v1.0 which accompany this distribution.
|
6
|
+
#
|
7
|
+
# The Eclipse Public License is available at
|
8
|
+
# https://eclipse.org/org/documents/epl-v10.php.
|
9
|
+
# and the Eclipse Distribution License is available at
|
10
|
+
# https://eclipse.org/org/documents/edl-v10.php.
|
11
|
+
#
|
12
|
+
# Contributors:
|
13
|
+
# Pierre Goudet - initial committer
|
14
|
+
|
15
|
+
require "mqtt_rails/version"
|
16
|
+
require "mqtt_rails/client"
|
17
|
+
require "mqtt_rails/exception"
|
18
|
+
require "mqtt_rails/packet"
|
19
|
+
|
20
|
+
module MqttRails
|
21
|
+
extend self
|
22
|
+
|
23
|
+
MAX_PACKET_ID = 65535
|
24
|
+
|
25
|
+
# Default connection setup
|
26
|
+
DEFAULT_SSL_PORT = 8883
|
27
|
+
DEFAULT_PORT = 1883
|
28
|
+
SELECT_TIMEOUT = 0.002
|
29
|
+
|
30
|
+
# MAX size of queue
|
31
|
+
MAX_SUBACK = 10
|
32
|
+
MAX_UNSUBACK = 10
|
33
|
+
MAX_PUBLISH = 1000
|
34
|
+
MAX_QUEUE = 1000
|
35
|
+
|
36
|
+
# Connection states values
|
37
|
+
MQTT_CS_NEW = 0
|
38
|
+
MQTT_CS_CONNECTED = 1
|
39
|
+
MQTT_CS_DISCONNECT = 2
|
40
|
+
|
41
|
+
# Error values
|
42
|
+
MQTT_ERR_SUCCESS = 0
|
43
|
+
MQTT_ERR_FAIL = 1
|
44
|
+
|
45
|
+
PACKET_TYPES = [
|
46
|
+
nil,
|
47
|
+
MqttRails::Packet::Connect,
|
48
|
+
MqttRails::Packet::Connack,
|
49
|
+
MqttRails::Packet::Publish,
|
50
|
+
MqttRails::Packet::Puback,
|
51
|
+
MqttRails::Packet::Pubrec,
|
52
|
+
MqttRails::Packet::Pubrel,
|
53
|
+
MqttRails::Packet::Pubcomp,
|
54
|
+
MqttRails::Packet::Subscribe,
|
55
|
+
MqttRails::Packet::Suback,
|
56
|
+
MqttRails::Packet::Unsubscribe,
|
57
|
+
MqttRails::Packet::Unsuback,
|
58
|
+
MqttRails::Packet::Pingreq,
|
59
|
+
MqttRails::Packet::Pingresp,
|
60
|
+
MqttRails::Packet::Disconnect,
|
61
|
+
nil
|
62
|
+
]
|
63
|
+
|
64
|
+
CONNACK_ERROR_MESSAGE = {
|
65
|
+
0x02 => "Client Identifier is correct but not allowed by remote server.",
|
66
|
+
0x03 => "Connection established but MQTT service unvailable on remote server.",
|
67
|
+
0x04 => "User name or user password is malformed.",
|
68
|
+
0x05 => "Client is not authorized to connect to the server."
|
69
|
+
}
|
70
|
+
|
71
|
+
CLIENT_ATTR_DEFAULTS = {
|
72
|
+
:host => "",
|
73
|
+
:port => nil,
|
74
|
+
:mqtt_version => '3.1.1',
|
75
|
+
:clean_session => true,
|
76
|
+
:persistent => false,
|
77
|
+
:blocking => false,
|
78
|
+
:client_id => nil,
|
79
|
+
:username => nil,
|
80
|
+
:password => nil,
|
81
|
+
:ssl => false,
|
82
|
+
:will_topic => nil,
|
83
|
+
:will_payload => nil,
|
84
|
+
:will_qos => 0,
|
85
|
+
:will_retain => false,
|
86
|
+
:keep_alive => 60,
|
87
|
+
:ack_timeout => 5,
|
88
|
+
:on_connack => nil,
|
89
|
+
:on_suback => nil,
|
90
|
+
:on_unsuback => nil,
|
91
|
+
:on_puback => nil,
|
92
|
+
:on_pubrel => nil,
|
93
|
+
:on_pubrec => nil,
|
94
|
+
:on_pubcomp => nil,
|
95
|
+
:on_message => nil,
|
96
|
+
}
|
97
|
+
|
98
|
+
Thread.abort_on_exception = true
|
99
|
+
|
100
|
+
def match_filter(topics, filters)
|
101
|
+
check_topics(topics, filters)
|
102
|
+
index = 0
|
103
|
+
rc = false
|
104
|
+
topic = topics.split('/')
|
105
|
+
filter = filters.split('/')
|
106
|
+
while index < [topic.length, filter.length].max do
|
107
|
+
if is_end?(topic[index], filter[index])
|
108
|
+
break
|
109
|
+
elsif is_wildcard?(filter[index])
|
110
|
+
rc = index == (filter.length - 1)
|
111
|
+
break
|
112
|
+
elsif keep_running?(filter[index], topic[index])
|
113
|
+
index = index + 1
|
114
|
+
else
|
115
|
+
break
|
116
|
+
end
|
117
|
+
end
|
118
|
+
is_matching?(rc, topic.length, filter.length, index)
|
119
|
+
end
|
120
|
+
|
121
|
+
def keep_running?(filter_part, topic_part)
|
122
|
+
filter_part == topic_part || filter_part == '+'
|
123
|
+
end
|
124
|
+
|
125
|
+
def is_wildcard?(filter_part)
|
126
|
+
filter_part == '#'
|
127
|
+
end
|
128
|
+
|
129
|
+
def is_end?(topic_part, filter_part)
|
130
|
+
topic_part.nil? || filter_part.nil?
|
131
|
+
end
|
132
|
+
|
133
|
+
def is_matching?(rc, topic_length, filter_length, index)
|
134
|
+
rc || index == [topic_length, filter_length].max
|
135
|
+
end
|
136
|
+
|
137
|
+
def check_topics(topics, filters)
|
138
|
+
if topics.is_a?(String) && filters.is_a?(String)
|
139
|
+
else
|
140
|
+
Rails.logger.error("Topics or Wildcards are not found as String.")
|
141
|
+
raise ArgumentError
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,414 @@
|
|
1
|
+
# Copyright (c) 2016-2017 Pierre Goudet <p-goudet@ruby-dev.jp>
|
2
|
+
#
|
3
|
+
# All rights reserved. This program and the accompanying materials
|
4
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
5
|
+
# and Eclipse Distribution License v1.0 which accompany this distribution.
|
6
|
+
#
|
7
|
+
# The Eclipse Public License is available at
|
8
|
+
# https://eclipse.org/org/documents/epl-v10.php.
|
9
|
+
# and the Eclipse Distribution License is available at
|
10
|
+
# https://eclipse.org/org/documents/edl-v10.php.
|
11
|
+
#
|
12
|
+
# Contributors:
|
13
|
+
# Pierre Goudet - initial committer
|
14
|
+
|
15
|
+
require 'mqtt_rails/handler'
|
16
|
+
require 'mqtt_rails/connection_helper'
|
17
|
+
require 'mqtt_rails/sender'
|
18
|
+
require 'mqtt_rails/publisher'
|
19
|
+
require 'mqtt_rails/subscriber'
|
20
|
+
require 'mqtt_rails/ssl_helper'
|
21
|
+
|
22
|
+
module MqttRails
|
23
|
+
class Client
|
24
|
+
# Connection related attributes:
|
25
|
+
attr_accessor :host
|
26
|
+
attr_accessor :port
|
27
|
+
attr_accessor :mqtt_version
|
28
|
+
attr_accessor :clean_session
|
29
|
+
attr_accessor :persistent
|
30
|
+
attr_accessor :reconnect_limit
|
31
|
+
attr_accessor :reconnect_delay
|
32
|
+
attr_accessor :blocking
|
33
|
+
attr_accessor :client_id
|
34
|
+
attr_accessor :username
|
35
|
+
attr_accessor :password
|
36
|
+
attr_accessor :ssl
|
37
|
+
|
38
|
+
# Last will attributes:
|
39
|
+
attr_accessor :will_topic
|
40
|
+
attr_accessor :will_payload
|
41
|
+
attr_accessor :will_qos
|
42
|
+
attr_accessor :will_retain
|
43
|
+
|
44
|
+
# Timeout attributes:
|
45
|
+
attr_accessor :keep_alive
|
46
|
+
attr_accessor :ack_timeout
|
47
|
+
|
48
|
+
#Read Only attribute
|
49
|
+
attr_reader :connection_state
|
50
|
+
attr_reader :ssl_context
|
51
|
+
|
52
|
+
def initialize(*args)
|
53
|
+
@last_packet_id = 0
|
54
|
+
@ssl_context = nil
|
55
|
+
@sender = nil
|
56
|
+
@handler = Handler.new
|
57
|
+
@connection_helper = nil
|
58
|
+
@connection_state = MQTT_CS_DISCONNECT
|
59
|
+
@connection_state_mutex = Mutex.new
|
60
|
+
@mqtt_thread = nil
|
61
|
+
@reconnect_thread = nil
|
62
|
+
@id_mutex = Mutex.new
|
63
|
+
@reconnect_limit = 3
|
64
|
+
@reconnect_delay = 5
|
65
|
+
|
66
|
+
if args.last.is_a?(Hash)
|
67
|
+
attr = args.pop
|
68
|
+
else
|
69
|
+
attr = {}
|
70
|
+
end
|
71
|
+
|
72
|
+
CLIENT_ATTR_DEFAULTS.merge(attr).each_pair do |k,v|
|
73
|
+
self.send("#{k}=", v)
|
74
|
+
end
|
75
|
+
|
76
|
+
if @ssl
|
77
|
+
@ssl_context = OpenSSL::SSL::SSLContext.new
|
78
|
+
end
|
79
|
+
|
80
|
+
if @port.nil?
|
81
|
+
if @ssl
|
82
|
+
@port = DEFAULT_SSL_PORT
|
83
|
+
else
|
84
|
+
@port = DEFAULT_PORT
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
if @client_id.nil? || @client_id == ""
|
89
|
+
@client_id = generate_client_id
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def generate_client_id(prefix='paho_ruby', lenght=16)
|
94
|
+
charset = Array('A'..'Z') + Array('a'..'z') + Array('0'..'9')
|
95
|
+
@client_id = prefix << Array.new(lenght) { charset.sample }.join
|
96
|
+
end
|
97
|
+
|
98
|
+
def config_ssl_context(cert_path, key_path, ca_path=nil)
|
99
|
+
@ssl ||= true
|
100
|
+
@ssl_context = SSLHelper.config_ssl_context(cert_path, key_path, ca_path)
|
101
|
+
end
|
102
|
+
|
103
|
+
def connect(host=@host, port=@port, keep_alive=@keep_alive, persistent=@persistent, blocking=@blocking)
|
104
|
+
@persistent = persistent
|
105
|
+
@blocking = blocking
|
106
|
+
@host = host
|
107
|
+
@port = port.to_i
|
108
|
+
@keep_alive = keep_alive
|
109
|
+
@connection_state_mutex.synchronize do
|
110
|
+
@connection_state = MQTT_CS_NEW
|
111
|
+
end
|
112
|
+
@mqtt_thread.kill unless @mqtt_thread.nil?
|
113
|
+
|
114
|
+
init_connection unless reconnect?
|
115
|
+
@connection_helper.send_connect(session_params)
|
116
|
+
begin
|
117
|
+
init_pubsub
|
118
|
+
@connection_state = @connection_helper.do_connect(reconnect?)
|
119
|
+
if connected?
|
120
|
+
build_pubsub
|
121
|
+
daemon_mode unless @blocking
|
122
|
+
end
|
123
|
+
rescue LowVersionException
|
124
|
+
downgrade_version
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def daemon_mode
|
129
|
+
@mqtt_thread = Thread.new do
|
130
|
+
@reconnect_thread.kill unless @reconnect_thread.nil? || !@reconnect_thread.alive?
|
131
|
+
begin
|
132
|
+
while connected? do
|
133
|
+
mqtt_loop
|
134
|
+
end
|
135
|
+
rescue SystemCallError => e
|
136
|
+
if @persistent
|
137
|
+
reconnect
|
138
|
+
else
|
139
|
+
raise e
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def connected?
|
146
|
+
@connection_state == MQTT_CS_CONNECTED
|
147
|
+
end
|
148
|
+
|
149
|
+
def reconnect?
|
150
|
+
Thread.current == @reconnect_thread
|
151
|
+
end
|
152
|
+
|
153
|
+
def loop_write
|
154
|
+
begin
|
155
|
+
@sender.writing_loop
|
156
|
+
rescue WritingException
|
157
|
+
if check_persistence
|
158
|
+
reconnect
|
159
|
+
else
|
160
|
+
raise WritingException
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def loop_read
|
166
|
+
begin
|
167
|
+
MAX_QUEUE.times do
|
168
|
+
result = @handler.receive_packet
|
169
|
+
break if result.nil?
|
170
|
+
end
|
171
|
+
rescue FullQueueException
|
172
|
+
Rails.logger.warn("Early exit in reading loop. The maximum packets have been reach for #{packet.type_name}")
|
173
|
+
rescue ReadingException
|
174
|
+
if check_persistence
|
175
|
+
reconnect
|
176
|
+
else
|
177
|
+
raise ReadingException
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def mqtt_loop
|
183
|
+
loop_read
|
184
|
+
loop_write
|
185
|
+
loop_misc
|
186
|
+
end
|
187
|
+
|
188
|
+
def loop_misc
|
189
|
+
if @connection_helper.check_keep_alive(@persistent, @keep_alive) == MQTT_CS_DISCONNECT
|
190
|
+
reconnect if check_persistence
|
191
|
+
end
|
192
|
+
@publisher.check_waiting_publisher
|
193
|
+
@subscriber.check_waiting_subscriber
|
194
|
+
sleep SELECT_TIMEOUT
|
195
|
+
end
|
196
|
+
|
197
|
+
def reconnect
|
198
|
+
@reconnect_thread = Thread.new do
|
199
|
+
counter = 0
|
200
|
+
while (@reconnect_limit >= counter || @reconnect_limit == -1) do
|
201
|
+
counter += 1
|
202
|
+
Rails.logger.info("New reconnect attempt...")
|
203
|
+
connect
|
204
|
+
if connected?
|
205
|
+
break
|
206
|
+
else
|
207
|
+
sleep @reconnect_delay
|
208
|
+
end
|
209
|
+
end
|
210
|
+
unless connected?
|
211
|
+
Rails.logger.error("Reconnection attempt counter is over. (#{@reconnect_limit} times)")
|
212
|
+
disconnect(false)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def disconnect(explicit=true)
|
218
|
+
@connection_helper.do_disconnect(@publisher, explicit, @mqtt_thread)
|
219
|
+
@connection_state_mutex.synchronize do
|
220
|
+
@connection_state = MQTT_CS_DISCONNECT
|
221
|
+
end
|
222
|
+
if explicit && @clean_session
|
223
|
+
@last_packet_id = 0
|
224
|
+
@subscriber.clear_queue
|
225
|
+
end
|
226
|
+
MQTT_ERR_SUCCESS
|
227
|
+
end
|
228
|
+
|
229
|
+
def publish(topic, payload="", retain=false, qos=0)
|
230
|
+
if topic == "" || !topic.is_a?(String)
|
231
|
+
Rails.logger.error("Publish topics is invalid, not a string or empty.")
|
232
|
+
raise ArgumentError
|
233
|
+
end
|
234
|
+
id = next_packet_id
|
235
|
+
@publisher.send_publish(topic, payload, retain, qos, id)
|
236
|
+
end
|
237
|
+
|
238
|
+
def subscribe(*topics)
|
239
|
+
begin
|
240
|
+
id = next_packet_id
|
241
|
+
unless @subscriber.send_subscribe(topics, id) == MqttRails::MQTT_ERR_SUCCESS
|
242
|
+
reconnect if check_persistence
|
243
|
+
end
|
244
|
+
MQTT_ERR_SUCCESS
|
245
|
+
rescue ProtocolViolation
|
246
|
+
Rails.logger.error("Subscribe topics need one topic or a list of topics.")
|
247
|
+
raise ProtocolViolation
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def unsubscribe(*topics)
|
252
|
+
begin
|
253
|
+
id = next_packet_id
|
254
|
+
unless @subscriber.send_unsubscribe(topics, id) == MQTT_ERR_SUCCESS
|
255
|
+
reconnect if check_persistence
|
256
|
+
end
|
257
|
+
MQTT_ERR_SUCCESS
|
258
|
+
rescue ProtocolViolation
|
259
|
+
Rails.logger.error("Unsubscribe need at least one topic.")
|
260
|
+
raise ProtocolViolation
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def ping_host
|
265
|
+
@sender.send_pingreq
|
266
|
+
end
|
267
|
+
|
268
|
+
def add_topic_callback(topic, callback=nil, &block)
|
269
|
+
@handler.register_topic_callback(topic, callback, &block)
|
270
|
+
end
|
271
|
+
|
272
|
+
def remove_topic_callback(topic)
|
273
|
+
@handler.clear_topic_callback(topic)
|
274
|
+
end
|
275
|
+
|
276
|
+
def on_connack(&block)
|
277
|
+
@handler.on_connack = block if block_given?
|
278
|
+
@handler.on_connack
|
279
|
+
end
|
280
|
+
|
281
|
+
def on_suback(&block)
|
282
|
+
@handler.on_suback = block if block_given?
|
283
|
+
@handler.on_suback
|
284
|
+
end
|
285
|
+
|
286
|
+
def on_unsuback(&block)
|
287
|
+
@handler.on_unsuback = block if block_given?
|
288
|
+
@handler.on_unsuback
|
289
|
+
end
|
290
|
+
|
291
|
+
def on_puback(&block)
|
292
|
+
@handler.on_puback = block if block_given?
|
293
|
+
@handler.on_puback
|
294
|
+
end
|
295
|
+
|
296
|
+
def on_pubrec(&block)
|
297
|
+
@handler.on_pubrec = block if block_given?
|
298
|
+
@handler.on_pubrec
|
299
|
+
end
|
300
|
+
|
301
|
+
def on_pubrel(&block)
|
302
|
+
@handler.on_pubrel = block if block_given?
|
303
|
+
@handler.on_pubrel
|
304
|
+
end
|
305
|
+
|
306
|
+
def on_pubcomp(&block)
|
307
|
+
@handler.on_pubcomp = block if block_given?
|
308
|
+
@handler.on_pubcomp
|
309
|
+
end
|
310
|
+
|
311
|
+
def on_message(&block)
|
312
|
+
@handler.on_message = block if block_given?
|
313
|
+
@handler.on_message
|
314
|
+
end
|
315
|
+
|
316
|
+
def on_connack=(callback)
|
317
|
+
@handler.on_connack = callback if callback.is_a?(Proc)
|
318
|
+
end
|
319
|
+
|
320
|
+
def on_suback=(callback)
|
321
|
+
@handler.on_suback = callback if callback.is_a?(Proc)
|
322
|
+
end
|
323
|
+
|
324
|
+
def on_unsuback=(callback)
|
325
|
+
@handler.on_unsuback = callback if callback.is_a?(Proc)
|
326
|
+
end
|
327
|
+
|
328
|
+
def on_puback=(callback)
|
329
|
+
@handler.on_puback = callback if callback.is_a?(Proc)
|
330
|
+
end
|
331
|
+
|
332
|
+
def on_pubrec=(callback)
|
333
|
+
@handler.on_pubrec = callback if callback.is_a?(Proc)
|
334
|
+
end
|
335
|
+
|
336
|
+
def on_pubrel=(callback)
|
337
|
+
@handler.on_pubrel = callback if callback.is_a?(Proc)
|
338
|
+
end
|
339
|
+
|
340
|
+
def on_pubcomp=(callback)
|
341
|
+
@handler.on_pubcomp = callback if callback.is_a?(Proc)
|
342
|
+
end
|
343
|
+
|
344
|
+
def on_message=(callback)
|
345
|
+
@handler.on_message = callback if callback.is_a?(Proc)
|
346
|
+
end
|
347
|
+
|
348
|
+
def registered_callback
|
349
|
+
@handler.registered_callback
|
350
|
+
end
|
351
|
+
|
352
|
+
def subscribed_topics
|
353
|
+
@subscriber.subscribed_topics
|
354
|
+
end
|
355
|
+
|
356
|
+
|
357
|
+
private
|
358
|
+
|
359
|
+
def next_packet_id
|
360
|
+
@id_mutex.synchronize do
|
361
|
+
@last_packet_id = 0 if @last_packet_id >= MAX_PACKET_ID
|
362
|
+
@last_packet_id = @last_packet_id.next
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
def downgrade_version
|
367
|
+
Rails.logger.info("Connection refused: unacceptable protocol version #{@mqtt_version}, trying 3.1")
|
368
|
+
if @mqtt_version != "3.1"
|
369
|
+
@mqtt_version = "3.1"
|
370
|
+
connect(@host, @port, @keep_alive)
|
371
|
+
else
|
372
|
+
raise ProtocolVersionException.new("Unsupported MQTT version")
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
def init_pubsub
|
377
|
+
@subscriber.nil? ? @subscriber = Subscriber.new(@sender) : @subscriber.sender = @sender
|
378
|
+
@publisher.nil? ? @publisher = Publisher.new(@sender) : @publisher.sender = @sender
|
379
|
+
@handler.config_pubsub(@publisher, @subscriber)
|
380
|
+
end
|
381
|
+
|
382
|
+
def build_pubsub
|
383
|
+
@subscriber.config_subscription(next_packet_id)
|
384
|
+
@sender.flush_waiting_packet
|
385
|
+
@publisher.config_all_message_queue
|
386
|
+
end
|
387
|
+
|
388
|
+
def init_connection
|
389
|
+
@connection_helper = ConnectionHelper.new(@host, @port, @ssl, @ssl_context, @ack_timeout)
|
390
|
+
@connection_helper.handler = @handler
|
391
|
+
@sender = @connection_helper.sender
|
392
|
+
end
|
393
|
+
|
394
|
+
def session_params
|
395
|
+
{
|
396
|
+
:version => @mqtt_version,
|
397
|
+
:clean_session => @clean_session,
|
398
|
+
:keep_alive => @keep_alive,
|
399
|
+
:client_id => @client_id,
|
400
|
+
:username => @username,
|
401
|
+
:password => @password,
|
402
|
+
:will_topic => @will_topic,
|
403
|
+
:will_payload => @will_payload,
|
404
|
+
:will_qos => @will_qos,
|
405
|
+
:will_retain => @will_retain
|
406
|
+
}
|
407
|
+
end
|
408
|
+
|
409
|
+
def check_persistence
|
410
|
+
disconnect(false)
|
411
|
+
@persistent
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|