mqtt-sub_handler 0.0.4 → 0.1.0.dev

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