mqtt-sn-ruby 0.0.10 → 0.0.11

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: 4bdd116c2da406a3163968c7f8111b7649a83cf8
4
- data.tar.gz: 775b6358cb108c2475679dc35e5844b0c9f68428
3
+ metadata.gz: 027cc20a4444e3c7057ac78c80f228c32eaf6821
4
+ data.tar.gz: 3196be2b522910d4a9be4f9fc36cf94c080a7987
5
5
  SHA512:
6
- metadata.gz: ee3244e7ffe27bd3ebcb2894a731f1e3ecc9afa8bd7807c6755ce035d9621293b83344590619f5a87354c2de6de901dbb3b087324a2eb3b812dfde512dddee2b
7
- data.tar.gz: 3a7b1be4136a6866bfed5d08dcd14f6a340f582affb25409d14f267e60a0ddf4837afa53c1b39b66ac029709117ddd31457bee156bac972b91fb4609b624b04e
6
+ metadata.gz: fd37c4ff4f979269e107062f940e3d391e98d5c04b018427c79e679d39625a45707c1ba141296404c21f48e1e4b8aee0bc85c7ad94c52716b157c242242572b9
7
+ data.tar.gz: ce37e48888c66fd0e085a44feae578fac2d79cc40f047fbb5c3a2a4ebf4be926bf76755566c3589e2b58a94e2692583aa4792bd6bddc23e561f061402ceb319a
@@ -14,7 +14,7 @@ else
14
14
  require 'mqtt-sn-ruby'
15
15
  end
16
16
 
17
- options = {}
17
+ options = {forwarder: true}
18
18
  OptionParser.new do |opts|
19
19
  opts.banner = "Usage: mqtt-sn-sub.rb [options]"
20
20
 
@@ -32,7 +32,7 @@ OptionParser.new do |opts|
32
32
  options[:local_port] = v.to_i
33
33
  end
34
34
  options[:gw_id] = 123
35
- opts.on("-i", "--id GwId", "MQTT-SN if of this GateWay (123)") do |v|
35
+ opts.on("-i", "--id GwId", "MQTT-SN id of this GateWay (123)") do |v|
36
36
  options[:gw_id] = v.to_i
37
37
  end
38
38
  opts.on("-h", "--http port", "Http port for debug/status JSON server (false)") do |v|
@@ -40,37 +40,52 @@ OptionParser.new do |opts|
40
40
  end
41
41
  end.parse!
42
42
 
43
-
43
+ $sn=MqttSN.new options
44
44
  if options[:http_port]
45
- require "sinatra/base"
46
45
  puts "Starting HTTP services at port #{options[:http_port]}"
47
- @hp=options[:http_port]
48
- class MySinatra < Sinatra::Base
49
- set :bind, '0.0.0.0'
50
- set :port, @hp
51
-
52
- get "/clients" do
53
- content_type :json
54
- MqttSN.get_clients.to_json
55
- end
56
- get "/gateways" do
57
- content_type :json
58
- MqttSN.get_gateways.to_json
59
- end
60
- end
46
+ $hp=options[:http_port]
61
47
  Thread.new do
62
- MySinatra.run!
48
+ server = TCPServer.new("20.20.20.21",$hp)
49
+ loop do
50
+ Thread.start(server.accept) do |client|
51
+ request = client.gets.split " "
52
+ type="text/html"
53
+ case request[1]
54
+ when '/gw'
55
+ response=$sn.gateways.to_json
56
+ status="200 OK"
57
+ type="text/json"
58
+ when '/cli'
59
+ response=$sn.clients.to_json
60
+ status="200 OK"
61
+ type="text/json"
62
+ else
63
+ status="404 Not Found"
64
+ response="?que"
65
+ end
66
+ client.print "HTTP/1.1 #{status}\r\n" +
67
+ "Content-Type: #{type}\r\n" +
68
+ "Content-Length: #{response.bytesize}\r\n" +
69
+ "Connection: close\r\n"
70
+ client.print "\r\n"
71
+ client.print response
72
+ client.close
73
+ puts "#{request} -> #{response}"
74
+ end
75
+ end
63
76
  end
64
77
  end
65
- options[:forwarder]=true
78
+
66
79
  puts "MQTT-SN-FORWARDER: #{options.to_json}"
80
+
67
81
  begin
68
- f=MqttSN.new options
82
+ $sn.forwarder_thread
69
83
  rescue SystemExit, Interrupt
70
84
  puts "\nExiting after Disconnect\n"
71
85
  rescue => e
72
86
  puts "\nError: '#{e}' -- Quit after Disconnect\n"
73
87
  pp e.backtrace
74
88
  end
89
+ $sn.kill_clients #if $sn
75
90
 
76
91
  puts "MQTT-SN-FORWARDER Done."
data/bin/mqtt-sn-pub.rb CHANGED
@@ -45,12 +45,9 @@ puts "MQTT-SN-PUB: #{options.to_json}"
45
45
  begin
46
46
  sn=MqttSN.new options
47
47
  sn.connect options[:id]
48
- sn.send :searchgw #replies may or may not come -- even multiple!
49
48
  sn.publish options[:topic]||"test/message/123", options[:msg]||"test_value", qos: options[:qos]
50
49
  puts "Sent ok."
51
- while not sn.log_empty?
52
- sleep 0.1
53
- end
50
+ sn.log_flush
54
51
  rescue SystemExit, Interrupt
55
52
  puts "\nExiting after Disconnect\n"
56
53
  rescue => e
data/bin/mqtt-sn-sub.rb CHANGED
@@ -41,22 +41,34 @@ OptionParser.new do |opts|
41
41
 
42
42
  end.parse!
43
43
 
44
+ $sn=MqttSN.new options
44
45
 
45
46
  if options[:http_port]
46
47
  puts "Starting HTTP services at port #{options[:http_port]}"
47
48
  $hp=options[:http_port]
48
49
  Thread.new do
49
- server = TCPServer.new("20.20.20.21",8083)
50
+ server = TCPServer.new("20.20.20.21",$hp)
50
51
  loop do
51
52
  Thread.start(server.accept) do |client|
52
- response= MqttSN::get_gateways.to_json
53
- client.print "HTTP/1.1 200 OK\r\n" +
54
- "Content-Type: text/json\r\n" +
53
+ request = client.gets.split " "
54
+ type="text/html"
55
+ case request[1]
56
+ when '/gw'
57
+ response=$sn.gateways.to_json
58
+ status="200 OK"
59
+ type="text/json"
60
+ else
61
+ status="404 Not Found"
62
+ response="?que"
63
+ end
64
+ client.print "HTTP/1.1 #{status}\r\n" +
65
+ "Content-Type: #{type}\r\n" +
55
66
  "Content-Length: #{response.bytesize}\r\n" +
56
67
  "Connection: close\r\n"
57
68
  client.print "\r\n"
58
69
  client.print response
59
70
  client.close
71
+ puts "#{request} -> #{response}"
60
72
  end
61
73
  end
62
74
  end
@@ -64,9 +76,9 @@ end
64
76
 
65
77
  puts "MQTT-SN-SUB: #{options.to_json}"
66
78
  begin
67
- sn=MqttSN.new options
68
- sn.connect options[:id]
69
- sn.subscribe options[:topic]||"test/message/123", qos: options[:qos] do |s,m|
79
+
80
+ $sn.connect options[:id]
81
+ $sn.subscribe options[:topic]||"test/message/123", qos: options[:qos] do |s,m|
70
82
  if s==:sub_ack
71
83
  puts "Subscribed Ok! Waiting for Messages!"
72
84
  else
@@ -79,7 +91,7 @@ rescue SystemExit, Interrupt
79
91
  rescue => e
80
92
  puts "\nError: '#{e}' -- Quit after Disconnect\n"
81
93
  end
82
- sn.disconnect if sn
94
+ $sn.disconnect if $sn
83
95
 
84
96
  puts "MQTT-SN-SUB Done."
85
97
 
data/lib/mqtt-sn-ruby.rb CHANGED
@@ -61,7 +61,33 @@ class MqttSN
61
61
  def note str,*args
62
62
  @log_q << sprintf(str,*args)
63
63
  end
64
-
64
+
65
+ def log_empty?
66
+ @log_q.empty? or not @verbose
67
+ end
68
+
69
+ def log_flush
70
+ while not sn.log_empty?
71
+ sleep 0.1
72
+ end
73
+ end
74
+
75
+ def log_thread
76
+ while true do
77
+ begin
78
+ if not @log_q.empty?
79
+ l=@log_q.pop
80
+ puts l
81
+ else
82
+ sleep 0.01
83
+ end
84
+ rescue => e
85
+ puts "Error: receive thread died: #{e}"
86
+ pp e.backtrace
87
+ end
88
+ end
89
+ end
90
+
65
91
  def self.open_port uri_s
66
92
  begin
67
93
  uri = URI.parse(uri_s)
@@ -92,6 +118,11 @@ class MqttSN
92
118
  s
93
119
  end
94
120
 
121
+
122
+ attr_accessor :clients
123
+ attr_accessor :gateways
124
+
125
+
95
126
  def initialize(hash={})
96
127
  @options=hash #save these
97
128
  @server_uri=hash[:server_uri]||"udp://localhost:1883"
@@ -99,13 +130,12 @@ class MqttSN
99
130
  @verbose=hash[:verbose]
100
131
  @state=:inited
101
132
  @bcast_port=5000
102
- @bcast_period=10
103
- @forward=hash[:forward] #flag to indicate forward mode
133
+ @forwarder=hash[:forwarder] #flag to indicate forward mode
104
134
  @will_topic=nil
105
135
  @will_msg=nil
136
+ @active_gw_id=nil
106
137
  @id="?"
107
138
 
108
-
109
139
  @msg_id=1
110
140
  @clients={}
111
141
  @gateways={}
@@ -115,12 +145,11 @@ class MqttSN
115
145
  log_thread
116
146
  end
117
147
 
118
-
119
148
  @sem=Mutex.new
120
149
  @topics={} #hash of registered topics is stored here
121
150
  @iq = Queue.new
122
151
  @dataq = Queue.new
123
- @s,@server,@port = MqttSN::open_port @server_uri
152
+
124
153
 
125
154
  @bcast_s=MqttSN::open_multicast_send_port @bcast_port
126
155
  @bcast=MqttSN::open_multicast_recv_port @bcast_port
@@ -128,22 +157,25 @@ class MqttSN
128
157
  @roam_t=Thread.new do
129
158
  roam_thread @bcast
130
159
  end
131
- if not @options[:forwarder]
132
- @client_t=Thread.new do
160
+ if not @forwarder
161
+ @client_t=Thread.new do
133
162
  client_thread @s
134
163
  end
135
164
  else
136
- @s.bind("0.0.0.0",hash[:local_port]||1883)
165
+ @s,@server,@port = MqttSN::open_port @server_uri
166
+ puts "Open port to Gateway: #{@server_uri}: #{@server},#{@port} -- local port: #{@local_port}"
167
+ @local_port=hash[:local_port]||1883
168
+ @s.bind("0.0.0.0",@local_port)
137
169
  @bcast_period=60
138
- loop do
139
- forwarder_thread @s
140
- end
141
170
  end
142
171
 
143
172
  end
144
173
 
145
174
 
146
- def forwarder_thread socket
175
+ def forwarder_thread
176
+ if not @forwarder
177
+ raise "Cannot Forward if no Forwarder!"
178
+ end
147
179
  begin
148
180
  last_kill=0
149
181
  stime=Time.now.to_i
@@ -165,7 +197,7 @@ class MqttSN
165
197
  end
166
198
  end
167
199
  while true
168
- pac=MqttSN::poll_packet_block(socket) #data from clients to our service sovket
200
+ pac=MqttSN::poll_packet_block(@s) #data from clients to our service sovket
169
201
  r,client_ip,client_port=pac
170
202
  key="#{client_ip}:#{client_port}"
171
203
  if not @clients[key]
@@ -202,6 +234,16 @@ class MqttSN
202
234
  end
203
235
  end
204
236
 
237
+ def kill_clients
238
+ puts "Killing Clients:"
239
+ @clients.each do |key,c|
240
+ puts "Killing #{key}"
241
+ send_packet [DISCONNECT_TYPE],@s,c[:ip], c[:port]
242
+ send_packet [DISCONNECT_TYPE],c[:socket], @server,@port
243
+ end
244
+ puts "Killing Clients Done."
245
+ end
246
+
205
247
 
206
248
  def self.hexdump data
207
249
  raw=""
@@ -225,7 +267,7 @@ class MqttSN
225
267
 
226
268
  def send type,hash={},&block
227
269
  #puts "" if @verbose
228
- if @state!=:connected and type!=:connect and type!=:will_topic and type!=:will_msg
270
+ if @state!=:connected and type!=:connect and type!=:will_topic and type!=:will_msg and type!=:searchgw
229
271
  if type==:disconnect
230
272
  return #already disconnected.. nothing to do
231
273
  else
@@ -241,7 +283,7 @@ class MqttSN
241
283
  flags+=CLEAN_FLAG if hash[:clean]
242
284
  flags+=RETAIN_FLAG if hash[:retain]
243
285
  flags+=WILL_FLAG if @will_topic
244
- p=[CONNECT_TYPE,flags,0x01,0,30]
286
+ p=[CONNECT_TYPE,flags,0x01,hash[:duration]>>8 ,hash[:duration] & 0xff]
245
287
  hash[:id].each_byte do |b|
246
288
  p<<b
247
289
  end
@@ -347,7 +389,7 @@ class MqttSN
347
389
  if type==:searchgw
348
390
  raw=send_packet p,@bcast,MULTICAST_ADDR,@bcast_port
349
391
  else
350
- raw=send_packet p,@s,@server,@port
392
+ raw=send_packet_gw p
351
393
  end
352
394
  hash[:debug]=raw if @debug
353
395
  return
@@ -363,7 +405,7 @@ class MqttSN
363
405
  if type==:searchgw
364
406
  raw=send_packet p,@bcast,MULTICAST_ADDR,@bcast_port
365
407
  else
366
- raw=send_packet p,@s,@server,@port
408
+ raw=send_packet_gw p
367
409
  end
368
410
  hash[:debug]=raw if @debug
369
411
  #puts "send:#{@id} #{type},#{hash.to_json}" if @verbose
@@ -388,7 +430,7 @@ class MqttSN
388
430
  break
389
431
  else
390
432
  retries+=1
391
- send_packet p,@s,@server,@port
433
+ send_packet_gw p
392
434
  puts "fail to get ack, retry #{retries} :#{@id} #{type},#{hash.to_json}"
393
435
  #need to set DUP flag !
394
436
  end
@@ -419,6 +461,28 @@ class MqttSN
419
461
  logger "< %-18.18s <- %-18.18s | %s",dest,src,MqttSN::parse_message(msg).to_json
420
462
  end
421
463
 
464
+ def send_packet_gw m
465
+ msg=MqttSN::build_packet m
466
+ waits=0
467
+ while not @active_gw_id or not @gateways[@active_gw_id] or not @gateways[@active_gw_id][:socket]
468
+ sleep 0.1
469
+ waits+=1
470
+ break if waits>30
471
+ end
472
+ if @active_gw_id and @gateways[@active_gw_id] and @gateways[@active_gw_id][:socket]
473
+ #get server & port from uri!
474
+ uri=URI.parse(@gateways[@active_gw_id][:uri])
475
+ #uri.scheme
476
+ MqttSN::send_raw_packet msg,@gateways[@active_gw_id][:socket],uri.host,uri.port
477
+ _,port,_,_ = @gateways[@active_gw_id][:socket].addr
478
+ src="udp://0.0.0.0:#{port}"
479
+ logger "< %-24.24s <- %-24.24s | %s",@gateways[@active_gw_id][:uri],src,MqttSN::parse_message(msg).to_json
480
+ else
481
+ puts "no gw to send.."
482
+ sleep 1
483
+ end
484
+ end
485
+
422
486
  def self.poll_packet socket
423
487
  #decide how to get data -- UDP-socket or FM-radio
424
488
  begin
@@ -442,15 +506,6 @@ class MqttSN
442
506
  client_port=stuff[1]
443
507
  return [r,client_ip,client_port]
444
508
  end
445
-
446
- def self.get_clients
447
- @clients
448
- end
449
-
450
- def self.get_gateways
451
- @gateways
452
- end
453
-
454
509
 
455
510
  def will_and_testament topic,msg
456
511
  if @state==:connected #if already connected, send changes, otherwise wait until connect does it.
@@ -477,8 +532,8 @@ class MqttSN
477
532
  @will_msg=msg
478
533
  end
479
534
 
480
- def connect id
481
- send :connect,id: id, clean: true, expect: [:connect_ack,:will_topic_req] do |s,m| #add will here!
535
+ def connect id,duration=30
536
+ send :connect,id: id, clean: false, duration: duration, expect: [:connect_ack,:will_topic_req] do |s,m| #add will here!
482
537
  if s==:ok
483
538
  if m[:type]==:will_topic_req
484
539
  puts "will topic!"
@@ -719,16 +774,17 @@ class MqttSN
719
774
  def client_thread socket
720
775
  while true do
721
776
  begin
722
- if pac=MqttSN::poll_packet_block(socket)
777
+ if @active_gw_id and @gateways[@active_gw_id] and @gateways[@active_gw_id][:socket] #if we are connected...
778
+ pac=MqttSN::poll_packet_block(@gateways[@active_gw_id][:socket])
723
779
  r,client_ip,client_port=pac
724
780
  m=MqttSN::parse_message r
725
781
  if @debug and m
726
782
  m[:debug]=MqttSN::hexdump r
727
783
  end
728
784
  dest="#{client_ip}:#{client_port}"
729
- _,port,_,_ = @s.addr
730
- src=port
731
- logger "> %-18.18s -> %-18.18s | %s",dest,":#{port}",m.to_json
785
+ _,port,_,_= @gateways[@active_gw_id][:socket].addr
786
+ src="udp://0.0.0.0:#{port}"
787
+ logger "i %-24.24s -> %-24.24s | %s",@gateways[@active_gw_id][:uri],src,m.to_json
732
788
  process_message m
733
789
  else
734
790
  sleep 0.01
@@ -743,36 +799,84 @@ class MqttSN
743
799
  def process_broadcast_message m,client_ip,client_port
744
800
  case m[:type]
745
801
  when :searchgw
746
- note "hey, someone is looking for gateway..."
747
802
  key="#{client_ip}:#{client_port}"
748
803
  dest="#{MULTICAST_ADDR};#{@bcast_port}"
749
804
  #actually -- send data on all gateways we know...
750
- if @options[:forwarder]
805
+ if @forwarder
751
806
  logger "r %-18.18s -> %-18.18s | %s",key,dest,m.to_json
752
807
  send_packet [GWINFO_TYPE,@options[:gw_id]],@bcast_s,MULTICAST_ADDR,@bcast_port
753
808
  end
754
809
  when :advertise,:gwinfo
755
- note "hey, we have gateway there!"
756
- if not @gateways[client_ip]
757
- @gateways[client_ip]={stamp: Time.now.to_i}
810
+ gw_id=m[:gw_id]
811
+ duration=m[:duration]||180
812
+ uri="udp://#{client_ip}:1882"
813
+ if not @gateways[gw_id]
814
+ @gateways[gw_id]={stamp: Time.now.to_i,uri: uri, duration: duration, source: m[:type], status: :ok}
815
+ else
816
+ if @gateways[gw_id][:uri]!=uri
817
+ note "conflict -- gateway has moved? or duplicate"
818
+ else
819
+ @gateways[gw_id][:stamp]=Time.now.to_i
820
+ @gateways[gw_id][:duration]=duration
821
+ @gateways[gw_id][:source]=m[:type]
822
+ end
823
+ end
824
+ end
825
+ end
826
+
827
+
828
+ def pick_new_gateway
829
+ begin
830
+ use_gw=nil
831
+ @gateways.each do |gw_id,data|
832
+ if data[:uri]
833
+ use_gw=gw_id
834
+ end
835
+ end
836
+ if use_gw
837
+ @active_gw_id=use_gw
838
+ puts "using gateway #{@active_gw_id} #{@gateways[@active_gw_id][:uri]}"
839
+ @s,@server,@port = MqttSN::open_port @gateways[@active_gw_id][:uri]
840
+ @gateways[use_gw][:socket]=@s
758
841
  else
759
- @gateways[client_ip][:stamp]=Time.now.to_i
842
+ #note "Error: no usable gw found !!"
760
843
  end
761
- @gateways[client_ip][:gw_id]=m[:gw_id]
762
- @gateways[client_ip][:duration]=m[:duration]
763
- note "gw: #{@gateways.to_json}"
844
+ rescue => e
845
+ puts "Error: receive thread died: #{e}"
846
+ pp e.backtrace
764
847
  end
765
848
  end
766
849
 
767
850
  def roam_thread socket
768
851
  @last_bcast=0
769
- if @options[:forwarder]
852
+ if @forwarder
770
853
  Thread.new do
771
854
  while true do
772
855
  send_packet [ADVERTISE_TYPE,@options[:gw_id],@bcast_period>>8,@bcast_period&0xff],@bcast_s,MULTICAST_ADDR,@bcast_port
773
856
  sleep @bcast_period
774
857
  end
775
858
  end
859
+ else #client should try to find some gateways..
860
+ Thread.new do
861
+ while true do
862
+ send :searchgw #replies may or may not come -- even multiple!
863
+ if @gateways=={}
864
+ sleep 5
865
+ else
866
+ #pp @gateways
867
+ sleep 30
868
+ end
869
+ end
870
+ end
871
+ Thread.new do
872
+ while true do
873
+ if @active_gw_id and @gateways[@active_gw_id] and @gateways[@active_gw_id][:socket]
874
+ else # not so ok -- pick one!
875
+ pick_new_gateway
876
+ end
877
+ sleep 0.01
878
+ end
879
+ end
776
880
  end
777
881
  while true do
778
882
  begin
@@ -796,24 +900,6 @@ class MqttSN
796
900
  end
797
901
  end
798
902
 
799
- def log_empty?
800
- @log_q.empty? or not @verbose
801
- end
802
903
 
803
- def log_thread
804
- while true do
805
- begin
806
- if not @log_q.empty?
807
- l=@log_q.pop
808
- puts l
809
- else
810
- sleep 0.01
811
- end
812
- rescue => e
813
- puts "Error: receive thread died: #{e}"
814
- pp e.backtrace
815
- end
816
- end
817
- end
818
904
 
819
905
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mqtt-sn-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.10
4
+ version: 0.0.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ari Siitonen