mqtt-sub_handler 0.0.4 → 0.1.0.dev

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: b8bedfaba639af0313ac3e07e014ea2b481b870c
4
- data.tar.gz: 5b1347d9c8111108728f0a2a2b83c8e35d7db7de
3
+ metadata.gz: 826fa334ef34a9c8b441343ea1ef8b80652ad4df
4
+ data.tar.gz: 07cfe57e8b9b7e0d355616f60042991104efff4f
5
5
  SHA512:
6
- metadata.gz: 1a319b293a833d5821f26705c666ca113308a3de252284ff22a44346e621bd84bbbb4f5dc7fb8ca7d948188a9302487c64388dbcda0fa4087ab2e7bd2d0ae943
7
- data.tar.gz: e022767bde84d460fff1fc3375335c43084944ff1f8bf4349e5a9ce80c73b8d405b39ea298d2226b27e8245e366dce014b88c5594f08d8288d73e79eaf51184a
6
+ metadata.gz: 3c9f4e85523610a599c764980fbbe1258e6641f8074c2f607d99256d7e9984c19be091c92bdd3b3b71c3d51da78e5db423c23167cb41d5d4d7b7c80cc3677507
7
+ data.tar.gz: fe6f9db5d8bf6b6166898f2f468935f0d555c6a31fddc7beffa56ce89b31c722964c54c41ab8f45daa5422195d54a6d9807978b5553ee5e7787cca27e66b8671
data/README.md CHANGED
@@ -1,11 +1,7 @@
1
1
 
2
- **Important note:**
3
- *Right now, I (Xasin) am still in the process of figuring out how to properly set up gems.
4
- This is my first one, and it has only recently (11.02.18) been published.*
5
-
6
- *I'm working on documenting the code, so if you can't figure out how to get it running, come back in a few days or make an issue explaining your problem!*
7
-
8
- ---
2
+ ### Where're the tests?
3
+ They're not here yet! But if you know how to set them up, I'd absolutely love to have you contribute.
4
+ Open an issue about that and we can figure things out!
9
5
 
10
6
  # A topic-based, asynchronous MQTT handler
11
7
  That's right, finally there's a *mostly* stable, connection-loss resistant MQTT handler out there that runs fully asynchronously.
@@ -40,10 +40,10 @@ class SubHandler
40
40
  return nil unless receivedTopicList.length >= topicPattern.length;
41
41
 
42
42
  topicPattern.each_index do |i|
43
- if(topicPattern[i] == "+") then
43
+ if(topicPattern[i] == "+")
44
44
  outputTopicList << receivedTopicList[i];
45
45
 
46
- elsif(topicPattern[i] == "#") then
46
+ elsif(topicPattern[i] == "#")
47
47
  outputTopicList.concat receivedTopicList[i..-1];
48
48
  return outputTopicList;
49
49
 
@@ -76,7 +76,7 @@ class SubHandler
76
76
  def raw_subscribe_to(topic, qos: 1)
77
77
  begin
78
78
  @conChangeMutex.lock
79
- if not @connected then
79
+ if not @connected
80
80
  @subscribeQueue << [topic, qos];
81
81
  @conChangeMutex.unlock
82
82
  else
@@ -128,7 +128,7 @@ class SubHandler
128
128
  # @yieldparam topicList [Array<String>] The wildcard topic branches matched.
129
129
  # @yieldreturn [Boolean] Whether or not the data was sufficient, and capture should be stopped.
130
130
  def wait_for(topic, qos: 1, timeout: nil)
131
- unless block_given? then
131
+ unless block_given?
132
132
  raise ArgumentError, "A block for data-processing needs to be passed!"
133
133
  end
134
134
 
@@ -136,10 +136,10 @@ class SubHandler
136
136
  register_subscription(subObject);
137
137
 
138
138
  begin
139
- Timeout::timeout(timeout) do
139
+ Timeout.timeout(timeout) do
140
140
  loop do
141
141
  return_data = subObject.waitpoint.wait()[1];
142
- if yield(return_data[0], return_data[1]) then
142
+ if yield(return_data[0], return_data[1])
143
143
  return true;
144
144
  end
145
145
  end
@@ -208,7 +208,7 @@ class SubHandler
208
208
 
209
209
  begin
210
210
  @conChangeMutex.lock
211
- if not @connected then
211
+ if not @connected
212
212
  @publishQueue << {topic: topic, data: data, qos: qos, retain: retain} unless qos == 0
213
213
  @conChangeMutex.unlock
214
214
  else
@@ -222,10 +222,51 @@ class SubHandler
222
222
  end
223
223
  alias publishTo publish_to
224
224
 
225
+ def ensure_clean_start()
226
+ @mqttWasStartedClean = @mqtt.clean_session
227
+ if @mqttWasStartedClean
228
+ begin
229
+ @mqtt.connect();
230
+ @mqtt.disconnect();
231
+ rescue MQTT::Exception
232
+ sleep 1;
233
+ retry
234
+ rescue SocketError, SystemCallError
235
+ sleep 5
236
+ retry
237
+ end
238
+ @mqtt.clean_session=false;
239
+ end
240
+ end
241
+ private :ensure_clean_start
242
+
243
+ def ensure_clean_exit()
244
+ if(@mqttWasStartedClean)
245
+ print "Logging out of mqtt server... "
246
+ begin
247
+ Timeout.timeout(10) {
248
+ begin
249
+ @mqtt.clean_session = true;
250
+ @mqtt.disconnect();
251
+ @mqtt.connect();
252
+ rescue MQTT::Exception, SocketError, SystemCallError
253
+ sleep 1
254
+ retry;
255
+ end
256
+ }
257
+ rescue Timeout::Error
258
+ puts "Timed out, aborting!";
259
+ else
260
+ puts "Done."
261
+ end
262
+ end
263
+ end
264
+ private :ensure_clean_exit
265
+
225
266
  def mqtt_resub_thread
226
267
  while(true)
227
268
  begin
228
- Timeout::timeout(10) {
269
+ Timeout.timeout(10) {
229
270
  @mqtt.connect()
230
271
  }
231
272
  @conChangeMutex.synchronize {
@@ -270,12 +311,12 @@ class SubHandler
270
311
  end
271
312
  def flush_pubqueue()
272
313
  puts "\n";
273
- if @publishQueue.empty? then
314
+ if @publishQueue.empty?
274
315
  puts "MQTT buffer empty, continuing."
275
316
  else
276
317
  print "Finishing sending of MQTT messages ... "
277
318
  begin
278
- Timeout::timeout(10) {
319
+ Timeout.timeout(10) {
279
320
  until @publishQueue.empty? do
280
321
  sleep 0.05;
281
322
  end
@@ -299,7 +340,7 @@ class SubHandler
299
340
  # mqtt = MQTT::SubHandler.new(MQTT::Client.new("Your.Client.Opts"))
300
341
  def initialize(mqttClient)
301
342
  @callbackList = Array.new();
302
- if mqttClient.is_a? String then
343
+ if mqttClient.is_a? String
303
344
  @mqtt = MQTT::Client.new(mqttClient);
304
345
  else
305
346
  @mqtt = mqttClient;
@@ -317,52 +358,19 @@ class SubHandler
317
358
  @trackerHash = Hash.new();
318
359
 
319
360
  @listenerThread = Thread.new do
320
- if @mqtt.clean_session
321
- @mqttWasStartedClean = true;
322
- begin
323
- @mqtt.connect();
324
- @mqtt.disconnect();
325
- rescue MQTT::Exception
326
- sleep 1;
327
- retry
328
- rescue SocketError, SystemCallError
329
- sleep 5
330
- retry
331
- end
332
- @mqtt.clean_session=false;
333
- end
334
-
335
- mqtt_resub_thread
361
+ ensure_clean_start();
362
+ mqtt_resub_thread();
336
363
  end
337
364
  @listenerThread.abort_on_exception = true;
338
365
 
339
366
  at_exit {
340
- flush_pubqueue
367
+ flush_pubqueue();
341
368
  @listenerThread.kill();
342
-
343
- if(@mqttWasStartedClean) then
344
- print "Logging out of mqtt server... "
345
- begin
346
- Timeout::timeout(10) {
347
- begin
348
- @mqtt.clean_session = true;
349
- @mqtt.disconnect();
350
- @mqtt.connect();
351
- rescue MQTT::Exception, SocketError, SystemCallError
352
- sleep 1
353
- retry;
354
- end
355
- }
356
- rescue Timeout::Error
357
- puts "Timed out, aborting!";
358
- else
359
- puts "Done."
360
- end
361
- end
369
+ ensure_clean_exit();
362
370
  }
363
371
 
364
372
  begin
365
- Timeout::timeout(10) {
373
+ Timeout.timeout(10) {
366
374
  until(@connected) do sleep 0.1; end
367
375
  }
368
376
  rescue Timeout::Error
@@ -0,0 +1,105 @@
1
+
2
+ require "mqtt/sub_handler"
3
+
4
+ module MQTT
5
+ module Testing
6
+ # This class is meant purely for testing.
7
+ # It completely removes the need for a external MQTT broker, and captures errors.
8
+ # Message processing can be done step-by-step, for better analysis of errors.
9
+ # Its interface is identical to the main class, making it indistinguishable.
10
+ class SubHandler < MQTT::SubHandler
11
+ attr_reader :message_log
12
+ attr_reader :publish_queue
13
+ attr_accessor :retained_topics
14
+
15
+ def call_interested(topic, data)
16
+ @callbackList.each do |h|
17
+ tMatch = SubHandler.getTopicMatch(topic, h.topic_split);
18
+ if tMatch then
19
+ begin
20
+ h.offer(tMatch, data)
21
+ rescue StandardError => e
22
+ if(@error_handler)
23
+ @error_handler.call(e);
24
+ else
25
+ raise
26
+ end
27
+ return
28
+ end
29
+ end
30
+ end
31
+ end
32
+ private :call_interested
33
+
34
+ def raw_subscribe_to(topic, qos: nil)
35
+ if(@retained_topics[topic]) then
36
+ publish_to(topic, @retained_topics[topic])
37
+ end
38
+ end
39
+ private :raw_subscribe_to
40
+
41
+ # Publish a message to topic.
42
+ # @param topic [String] The topic to push to.
43
+ # @param data [String] The data to be transmitted.
44
+ # @note The published data is not immediately processed.
45
+ # Use process_message or process_all
46
+ def publish_to(topic, data, qos: nil, retain: false)
47
+ @publish_queue << [topic, data];
48
+ @message_log[topic] << data;
49
+ @retained_topics[topic] = data if retain;
50
+ end
51
+
52
+ # Process a single MQTT message in queue, recording errors etc.
53
+ def process_message
54
+ return if @publish_queue.empty?
55
+ packet = @publish_queue.pop
56
+ call_interested(packet[0], packet[1]);
57
+ end
58
+
59
+ # Process all messages until the queue is empty.
60
+ # Do remember that the callbacks can publish new data, which then gets processed again!
61
+ # @param max_loops [Integer] Amount of loops to do before aborting
62
+ # @param error_on_loop [Boolean] Raise an error if too many loops happened?
63
+ def process_all(max_loops: 500, error_on_loop: true)
64
+ until @publish_queue.empty?
65
+ process_message
66
+ max_loops -= 1;
67
+ if(max_loops == 0)
68
+ if(error_on_loop)
69
+ raise RuntimeError, "MQTT Loop recursion detected!"
70
+ end
71
+ return
72
+ end
73
+ end
74
+ end
75
+
76
+ # Prepare the code for the next test by cleaning out all queues.
77
+ # The list of callbacks is not affected
78
+ # @param retained [Boolean, Hash<String, String>] Either a bool whether or not to clear retained messages,
79
+ # or a Hash with Topic-Keys containing String-Data to use.
80
+ def prepare(retained: true)
81
+ @publish_queue.clear();
82
+ if(retained.is_a? Hash)
83
+ @retained_topics = retained.clone
84
+ @retained_topics.each do |topic, data|
85
+ publish_to(topic, data);
86
+ end
87
+ elsif(retained)
88
+ @retained_topics = Hash.new();
89
+ end
90
+ @message_log.clear()
91
+ end
92
+
93
+ def initialize()
94
+ @callbackList = Array.new();
95
+ @retained_topics = Hash.new();
96
+ @publish_queue = Queue.new();
97
+ @message_log = Hash.new() do |h, key| h = Array.new() end;
98
+ end
99
+
100
+ def on_error(&handler)
101
+ @error_handler = handler;
102
+ end
103
+ end
104
+ end
105
+ end
@@ -9,7 +9,7 @@ module MQTT
9
9
  attr_reader :qos
10
10
  attr_reader :topic_split
11
11
 
12
- def initialize(topic, qos)
12
+ def initialize(topic, _qos)
13
13
  @topic = topic;
14
14
  @topic_split = SubHandler.getTopicSplit(topic);
15
15
 
@@ -56,7 +56,7 @@ module MQTT
56
56
  @callbackList = Array.new();
57
57
  end
58
58
 
59
- def offer(topicList, data)
59
+ def offer(_topicList, data)
60
60
  return if data == @value;
61
61
  oldValue = @value;
62
62
  @value = data;
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mqtt-sub_handler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.1.0.dev
5
5
  platform: ruby
6
6
  authors:
7
7
  - Xasin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-24 00:00:00.000000000 Z
11
+ date: 2018-05-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mqtt
@@ -34,6 +34,7 @@ files:
34
34
  - README.md
35
35
  - lib/mqtt/Waitpoint.rb
36
36
  - lib/mqtt/sub_handler.rb
37
+ - lib/mqtt/sub_testing.rb
37
38
  - lib/mqtt/subscription_classes.rb
38
39
  homepage: https://github.com/XasWorks/XasCode/tree/MQTT_GEM/Ruby/MQTT
39
40
  licenses:
@@ -50,9 +51,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
50
51
  version: '0'
51
52
  required_rubygems_version: !ruby/object:Gem::Requirement
52
53
  requirements:
53
- - - ">="
54
+ - - ">"
54
55
  - !ruby/object:Gem::Version
55
- version: '0'
56
+ version: 1.3.1
56
57
  requirements: []
57
58
  rubyforge_project:
58
59
  rubygems_version: 2.6.14.1