mqtt-sn-ruby 0.1.8 → 0.1.9

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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mqtt-sn-ruby.rb +80 -62
  3. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b2cf90277e54e38c81138fd1e4f540e9e074e34b
4
- data.tar.gz: efffe5bdaaf339a20b16e8cacbf967b6cfafb915
3
+ metadata.gz: 449a87e9f6047a45e65ca158bd7d31232bf2c4e5
4
+ data.tar.gz: ee05043afe0575e5db38937e9419e69be3c85c3e
5
5
  SHA512:
6
- metadata.gz: 1070a821c0eb64abba5873774a566b90050fd8a9c15381442dca19d14d7d6d6e09c1b91776aaa45a7160f24b1e74296e57afbf5974c2ca34ac9085ad3bf8c1ea
7
- data.tar.gz: 55ecc32a4aca8acd05658c466a3303687ca5f19e91d579494f8346a0df90c06dd4bff397e376dfe643234db658317e35be684ed2f1462366f1224c8500a29439
6
+ metadata.gz: 44b2e7198864bedd959e60dc213cc98a6412c0f9d2b60dcfce36d1e43116fa386630ecc3b14e6b3af2c37f9b7d0d34fb96437d561484f5cdeabd9dfc92bef9d1
7
+ data.tar.gz: 854dab548ad0d2b63c8d3b48538948b4345ce2a08e5ece5d3d7f4251bd200110dd37e9926976ec17a309a30aa9256a245a33c348687917a6843477d38a9ccf4f
@@ -7,12 +7,14 @@ require 'json'
7
7
  require 'uri'
8
8
  require 'ipaddr'
9
9
  require 'time'
10
+ require 'thread'
10
11
 
11
12
 
12
13
  class MqttSN
13
14
 
14
15
  Nretry = 3 # Max retry
15
- Tretry = 3 # Timeout before retry
16
+ Tretry = 3 # Timeout before retry
17
+ MAX_IDLE = 60*3 # Forwarder will disconnect parties after this idle time
16
18
 
17
19
  SEARCHGW_TYPE =0x01
18
20
  GWINFO_TYPE =0x02
@@ -92,7 +94,7 @@ class MqttSN
92
94
  end
93
95
  end
94
96
 
95
- def log_thread
97
+ def log_thread
96
98
  while true do
97
99
  begin
98
100
  if not @log_q.empty?
@@ -122,16 +124,16 @@ class MqttSN
122
124
  end
123
125
  end
124
126
 
125
- def open_multicast_send_port
127
+ def open_multicast_send_port
126
128
  uri = URI.parse(@broadcast_uri)
127
-
129
+
128
130
  ip = IPAddr.new(uri.host).hton + IPAddr.new("0.0.0.0").hton
129
131
  socket_b = UDPSocket.new
130
132
  socket_b.setsockopt(Socket::IPPROTO_IP, Socket::IP_TTL, [1].pack('i'))
131
133
  socket_b
132
134
  end
133
135
 
134
- def open_multicast_recv_port
136
+ def open_multicast_recv_port
135
137
  uri = URI.parse(@broadcast_uri)
136
138
  ip = IPAddr.new(uri.host).hton + IPAddr.new("0.0.0.0").hton
137
139
  s = UDPSocket.new
@@ -174,7 +176,7 @@ class MqttSN
174
176
  @state=:disconnected
175
177
  @bcast_port=5000
176
178
  @keepalive=(hash[:keepalive]||25).to_i
177
- @forwarder=hash[:forwarder] #flag to indicate forward mode
179
+ @forwarder=hash[:forwarder] #flag to indicate forward mode
178
180
  @will_topic=nil
179
181
  @will_msg=nil
180
182
  @active_gw_id=nil
@@ -182,9 +184,9 @@ class MqttSN
182
184
  @local_ifs=Socket.getifaddrs.map { |i| i.addr.ip_address if i.addr.ipv4? }.compact
183
185
 
184
186
 
185
- @sem=Mutex.new
186
- @gsem=Mutex.new
187
- @log_q = Queue.new #log queue :)
187
+ @sem=Mutex.new
188
+ @gsem=Mutex.new
189
+ @log_q = Queue.new #log queue :)
188
190
  @msg_id=1
189
191
  @clients={}
190
192
  @gateways={}
@@ -194,7 +196,7 @@ class MqttSN
194
196
  if @server_uri
195
197
  note "Using Default Gateway: #{@server_uri}"
196
198
  add_gateway(0,{uri: @server_uri,source: "default"})
197
- pick_new_gateway
199
+ pick_new_gateway
198
200
  elsif @broadcast_uri
199
201
  note "Autodiscovery Active, using #{@broadcast_uri}"
200
202
  @autodiscovery=true
@@ -211,10 +213,10 @@ class MqttSN
211
213
  @topics={} #hash of registered topics is stored here
212
214
  @iq = Queue.new
213
215
  @dataq = Queue.new
214
-
216
+
215
217
  if @broadcast_uri
216
218
  @bcast_s=open_multicast_send_port
217
- @bcast=open_multicast_recv_port
219
+ @bcast=open_multicast_recv_port
218
220
 
219
221
  @roam_t=Thread.new do
220
222
  roam_thread @bcast
@@ -227,20 +229,21 @@ class MqttSN
227
229
  @s.bind("0.0.0.0",@local_port)
228
230
  @bcast_period=60
229
231
  else
230
- client_thread
232
+ client_thread
231
233
  end
232
234
  end
233
235
 
234
236
  def forwarder_thread
235
- if not @forwarder
237
+ if not @forwarder
236
238
  raise "Cannot Forward if no Forwarder!"
237
239
  end
238
- begin
240
+ begin
239
241
  last_kill=0
240
242
  stime=Time.now.to_i
241
243
  Thread.new do #maintenance
242
- while true do
244
+ while true do
243
245
  sleep 1
246
+ now=Time.now.to_i
244
247
  changes=false
245
248
  @clients.dup.each do |key,data|
246
249
  if data[:state]==:disconnected
@@ -248,6 +251,12 @@ class MqttSN
248
251
  note "- %s",dest
249
252
  @clients.delete key
250
253
  changes=true
254
+ elsif data[:last_send]<now-MAX_IDLE and data[:last_recv]<now-MAX_IDLE
255
+ dest="#{data[:ip]}:#{data[:port]}"
256
+ note "-- %s",dest
257
+ kill_client key
258
+ @clients.delete key
259
+ changes=true
251
260
  end
252
261
  end
253
262
  if changes
@@ -304,7 +313,7 @@ class MqttSN
304
313
  @gateways[@active_gw_id][:counter_send]+=1
305
314
  @clients[key][:last_recv]=Time.now.to_i
306
315
  @clients[key][:counter_recv]+=1
307
- begin
316
+ begin
308
317
  if @active_gw_id
309
318
  logger "cs %-24.24s -> %-24.24s | %s", @clients[key][:uri],@gateways[@active_gw_id][:uri],m.to_json
310
319
  else
@@ -317,6 +326,15 @@ class MqttSN
317
326
  end
318
327
  end
319
328
 
329
+ def kill_client key
330
+ puts "Killing Client #{key}:"
331
+ if c=@clients[key]
332
+ puts "Really Killing #{key}"
333
+ send_packet [DISCONNECT_TYPE],@s,c[:ip], c[:port]
334
+ send_packet [DISCONNECT_TYPE],c[:socket], @server,@port
335
+ end
336
+ end
337
+
320
338
  def kill_clients
321
339
  puts "Killing Clients:"
322
340
  @clients.each do |key,c|
@@ -349,7 +367,7 @@ class MqttSN
349
367
  end
350
368
 
351
369
  def send type,hash={},&block
352
- if @state==:disconnected and type!=:connect and type!=:will_topic and type!=:will_msg and type!=:searchgw
370
+ if @state==:disconnected and type!=:connect and type!=:will_topic and type!=:will_msg and type!=:searchgw
353
371
  if type==:disconnect
354
372
  return #already disconnected.. nothing to do
355
373
  elsif type==:publish and hash[:qos]==-1
@@ -364,7 +382,7 @@ class MqttSN
364
382
  hash[:id]="mqtt-sn-ruby-#{$$}"
365
383
  end
366
384
  note "Connecting as '#{hash[:id]}'"
367
- flags=0
385
+ flags=0
368
386
  flags+=CLEAN_FLAG if hash[:clean]
369
387
  flags+=RETAIN_FLAG if hash[:retain]
370
388
  flags+=WILL_FLAG if @will_topic
@@ -373,7 +391,7 @@ class MqttSN
373
391
  p<<b
374
392
  end
375
393
  @id=hash[:id]
376
- when :register
394
+ when :register
377
395
  raise "Need :topic to Publish!" if not hash[:topic]
378
396
  p=[REGISTER_TYPE,0,0,@msg_id >>8 ,@msg_id & 0xff]
379
397
  hash[:topic].each_byte do |b|
@@ -388,19 +406,19 @@ class MqttSN
388
406
  p=[PUBREC_TYPE,hash[:msg_id]>>8 ,hash[:msg_id] & 0xff]
389
407
  when :pub_comp
390
408
  p=[PUBCOMP_TYPE,hash[:msg_id]>>8 ,hash[:msg_id] & 0xff]
391
- when :will_topic
409
+ when :will_topic
392
410
  raise "Need :topic to :will_topic" if not hash[:topic]
393
411
  p=[WILLTOPIC_TYPE,0]
394
412
  hash[:topic].each_byte do |b|
395
413
  p<<b
396
414
  end
397
- when :will_topic_upd
415
+ when :will_topic_upd
398
416
  raise "Need :topic to :will_topic_upd" if not hash[:topic]
399
417
  p=[WILLTOPICUPD_TYPE,0]
400
418
  hash[:topic].each_byte do |b|
401
419
  p<<b
402
420
  end
403
- when :will_msg
421
+ when :will_msg
404
422
  raise "Need :msg to :will_msg" if not hash[:msg]
405
423
  p=[WILLMSG_TYPE]
406
424
  hash[:msg].each_byte do |b|
@@ -413,14 +431,14 @@ class MqttSN
413
431
  p<<b
414
432
  end
415
433
 
416
- when :subscribe
434
+ when :subscribe
417
435
  raise "Need :topic to :subscribe" if not hash[:topic]
418
436
  qos=hash[:qos]||0
419
- flags=0
437
+ flags=0
420
438
  if qos==-1
421
439
  flags+=QOSM1_FLAG
422
440
  else
423
- flags+=QOS1_FLAG*qos
441
+ flags+=QOS1_FLAG*qos
424
442
  end
425
443
  p=[SUBSCRIBE_TYPE,flags,@msg_id >>8 ,@msg_id & 0xff]
426
444
  hash[:topic].each_byte do |b|
@@ -428,7 +446,7 @@ class MqttSN
428
446
  end
429
447
  @msg_id+=1
430
448
 
431
- when :unsubscribe
449
+ when :unsubscribe
432
450
  raise "Need :topic to :unsubscribe" if not hash[:topic]
433
451
  p=[UNSUBSCRIBE_TYPE,0,@msg_id >>8 ,@msg_id & 0xff]
434
452
  hash[:topic].each_byte do |b|
@@ -439,12 +457,12 @@ class MqttSN
439
457
  when :publish
440
458
  raise "Need :topic_id to Publish!" if not hash[:topic_id]
441
459
  qos=hash[:qos]||0
442
- flags=0
460
+ flags=0
443
461
  flags+=RETAIN_FLAG if hash[:retain]
444
462
  if qos==-1
445
463
  flags+=QOSM1_FLAG
446
464
  else
447
- flags+=QOS1_FLAG*qos
465
+ flags+=QOS1_FLAG*qos
448
466
  end
449
467
  if hash[:topic_type]==:short
450
468
  flags+=TOPIC_SHORT_FLAG
@@ -456,7 +474,7 @@ class MqttSN
456
474
  p<<b.ord
457
475
  end
458
476
  @msg_id+=1
459
- when :pubrel
477
+ when :pubrel
460
478
  raise "Need the original :msg_id of the Publish for PubRel!" if not hash[:msg_id]
461
479
  p=[PUBREL_TYPE,hash[:msg_id] >>8 ,hash[:msg_id] & 0xff]
462
480
  when :ping
@@ -483,7 +501,7 @@ class MqttSN
483
501
  end
484
502
  return
485
503
  end
486
- @sem.synchronize do #one command at a time --
504
+ @sem.synchronize do #one command at a time --
487
505
  if hash[:expect]
488
506
  while not @iq.empty?
489
507
  mp=@iq.pop
@@ -570,9 +588,9 @@ class MqttSN
570
588
  debug={}
571
589
  debug[:debug]=MqttSN::hexdump(msg) if @debug
572
590
 
573
- if not @active_gw_id or not @gateways[@active_gw_id] or not @gateways[@active_gw_id][:socket]
591
+ if not @active_gw_id or not @gateways[@active_gw_id] or not @gateways[@active_gw_id][:socket]
574
592
  note "No active gw, wait ."
575
- while not @active_gw_id or not @gateways[@active_gw_id] or not @gateways[@active_gw_id][:socket]
593
+ while not @active_gw_id or not @gateways[@active_gw_id] or not @gateways[@active_gw_id][:socket]
576
594
  ret="-"
577
595
  if not ret=pick_new_gateway
578
596
  sleep 0.5
@@ -584,11 +602,11 @@ class MqttSN
584
602
  return
585
603
  end
586
604
  end
587
- note "Gw Ok!"
605
+ note "Gw Ok!"
588
606
  end
589
- ok=false
607
+ ok=false
590
608
  @gsem.synchronize do
591
- if @active_gw_id and @gateways[@active_gw_id] and @gateways[@active_gw_id][:socket]
609
+ if @active_gw_id and @gateways[@active_gw_id] and @gateways[@active_gw_id][:socket]
592
610
  ok=true
593
611
  @gateways[@active_gw_id][:last_send]=Time.now.to_i
594
612
  @gateways[@active_gw_id][:counter_send]+=1
@@ -629,7 +647,7 @@ class MqttSN
629
647
  client_port=stuff[1]
630
648
  return [r,client_ip,client_port]
631
649
  end
632
-
650
+
633
651
  def will_and_testament topic,msg
634
652
  if @state!=:disconnected #if already connected, send changes, otherwise wait until connect does it.
635
653
  if @will_topic!=topic
@@ -661,7 +679,7 @@ class MqttSN
661
679
  if m[:type]==:will_topic_req
662
680
  send :will_topic, topic: @will_topic, expect: [:will_msg_req] do |s,m| #add will here!
663
681
  if s==:ok
664
- send :will_msg, msg: @will_msg, expect: [:connect_ack] do |s,m|
682
+ send :will_msg, msg: @will_msg, expect: [:connect_ack] do |s,m|
665
683
  end
666
684
  end
667
685
  end
@@ -674,12 +692,12 @@ class MqttSN
674
692
  end
675
693
  end
676
694
 
677
- def disconnect
695
+ def disconnect
678
696
  send :disconnect, expect: :disconnect do |status,message|
679
697
  end
680
698
  end
681
699
 
682
- def goto_sleep duration
700
+ def goto_sleep duration
683
701
  send :disconnect, duration: duration, expect: :disconnect do |status,message|
684
702
  end
685
703
  end
@@ -689,7 +707,7 @@ class MqttSN
689
707
  # When subsciption is established with Broker, a :sub_ack message is given to code block.
690
708
  # Incoming messages are indicated with :got_data status.
691
709
  # If connection to Broker is disrupted, a :disconnect message is given.!
692
-
710
+
693
711
  def subscribe topic,hash={},&block # :yields: status, message
694
712
  send :subscribe, topic: topic, qos: hash[:qos],expect: :sub_ack do |s,m|
695
713
  if s==:ok
@@ -839,7 +857,7 @@ class MqttSN
839
857
  qos=(flags>>5)&0x03
840
858
  qos=-1 if qos==3
841
859
  m={type: :publish, qos: qos, topic_id: topic_id, topic_type:topic_type, topic: topic, msg_id: msg_id, msg: msg,flags: flags, status: :ok}
842
- m[:retain]=true if flags & RETAIN_FLAG == RETAIN_FLAG
860
+ m[:retain]=true if flags & RETAIN_FLAG == RETAIN_FLAG
843
861
  when PUBREL_TYPE
844
862
  msg_id=(r[2].ord<<8)+r[3].ord
845
863
  m={type: :pub_rel, msg_id: msg_id, status: :ok}
@@ -893,7 +911,7 @@ class MqttSN
893
911
  m
894
912
  end
895
913
 
896
- def process_message m
914
+ def process_message m
897
915
  done=false
898
916
  case m[:type]
899
917
  when :register
@@ -915,9 +933,9 @@ class MqttSN
915
933
  end
916
934
  if not @transfer
917
935
  @dataq<<m
918
- if m[:qos]==1
936
+ if m[:qos]==1
919
937
  send :publish_ack,topic_id: m[:topic_id], msg_id: m[:msg_id], return_code: 0
920
- elsif m[:qos]==2
938
+ elsif m[:qos]==2
921
939
  send :pub_rec, msg_id: m[:msg_id]
922
940
  end
923
941
  done=true
@@ -927,8 +945,8 @@ class MqttSN
927
945
  when :sub_ack
928
946
  @state=:subscribed
929
947
  when :pong
930
- @gsem.synchronize do #one command at a time --
931
- if @active_gw_id and @gateways[@active_gw_id]
948
+ @gsem.synchronize do #one command at a time --
949
+ if @active_gw_id and @gateways[@active_gw_id]
932
950
  @gateways[@active_gw_id][:last_ping]=Time.now.to_i
933
951
  end
934
952
  end
@@ -946,11 +964,11 @@ class MqttSN
946
964
  m
947
965
  end
948
966
 
949
- def client_thread
967
+ def client_thread
950
968
  Thread.new do #ping thread
951
969
  while true do
952
970
  begin
953
- while @state==:disconnected
971
+ while @state==:disconnected
954
972
  sleep 1
955
973
  end
956
974
  sleep @keepalive
@@ -976,7 +994,7 @@ class MqttSN
976
994
  do_sleep=true
977
995
  if @active_gw_id and @gateways[@active_gw_id] and @gateways[@active_gw_id][:socket] #if we are connected...
978
996
  if pac=MqttSN::poll_packet(@gateways[@active_gw_id][:socket]) #cannot block -- gateway may change...
979
- r,client_ip,client_port=pac
997
+ r,client_ip,client_port=pac
980
998
  m=MqttSN::parse_message r
981
999
  if @debug and m
982
1000
  m[:debug]=MqttSN::hexdump r
@@ -1010,7 +1028,7 @@ class MqttSN
1010
1028
  send_packet_bcast [GWINFO_TYPE,@options[:gw_id]]
1011
1029
  end
1012
1030
  when :advertise,:gwinfo
1013
- gw_id=m[:gw_id]
1031
+ gw_id=m[:gw_id]
1014
1032
  duration=m[:duration]||180
1015
1033
  uri="udp://#{client_ip}:1882"
1016
1034
  add_gateway(gw_id,{uri: uri, source: m[:type], duration:duration,stamp: Time.now.to_i})
@@ -1019,8 +1037,8 @@ class MqttSN
1019
1037
 
1020
1038
 
1021
1039
  def gateway_close cause
1022
- @gsem.synchronize do #one command at a time --
1023
-
1040
+ @gsem.synchronize do #one command at a time --
1041
+
1024
1042
  if @active_gw_id # if using one, mark it used, so it will be last reused
1025
1043
  note "Closing gw #{@active_gw_id} cause: #{cause}"
1026
1044
  @gateways[@active_gw_id][:last_use]=Time.now.to_i
@@ -1030,13 +1048,13 @@ class MqttSN
1030
1048
  end
1031
1049
  @active_gw_id=nil
1032
1050
  end
1033
- end
1051
+ end
1034
1052
  end
1035
1053
 
1036
- def pick_new_gateway
1054
+ def pick_new_gateway
1037
1055
  begin
1038
1056
  gateway_close nil
1039
- @gsem.synchronize do #one command at a time --
1057
+ @gsem.synchronize do #one command at a time --
1040
1058
  pick=nil
1041
1059
  pick_t=0
1042
1060
  @gateways.each do |gw_id,data|
@@ -1089,7 +1107,7 @@ class MqttSN
1089
1107
  while true do
1090
1108
  if @active_gw_id and @gateways[@active_gw_id] and @gateways[@active_gw_id][:socket]
1091
1109
  else # not so ok -- pick one!
1092
- #pick_new_gateway
1110
+ #pick_new_gateway
1093
1111
  end
1094
1112
  sleep 0.01
1095
1113
  end
@@ -1097,9 +1115,9 @@ class MqttSN
1097
1115
  end
1098
1116
  while true do
1099
1117
  begin
1100
- if @bcast
1118
+ if @bcast
1101
1119
  pac=MqttSN::poll_packet_block(socket)
1102
- r,client_ip,client_port=pac
1120
+ r,client_ip,client_port=pac
1103
1121
  m=MqttSN::parse_message r
1104
1122
  if @debug and m
1105
1123
  m[:debug]=MqttSN::hexdump r
@@ -1136,7 +1154,7 @@ class MqttSN
1136
1154
  end
1137
1155
  end
1138
1156
  end
1139
- log_flush
1157
+ log_flush
1140
1158
  end
1141
1159
 
1142
1160
  def sub options={},&block
@@ -1144,7 +1162,7 @@ class MqttSN
1144
1162
  note "Connecting.."
1145
1163
  connect options[:id] do |cs,cm|
1146
1164
  note "connect result: #{cs} #{cm}"
1147
- if cs==:ok
1165
+ if cs==:ok
1148
1166
  note "Subscribing.."
1149
1167
  subscribe options[:topic]||"test/message/123", qos: options[:qos] do |s,m|
1150
1168
  if s==:sub_ack
@@ -1166,4 +1184,4 @@ class MqttSN
1166
1184
  end
1167
1185
 
1168
1186
 
1169
- end
1187
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mqtt-sn-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ari Siitonen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-29 00:00:00.000000000 Z
11
+ date: 2014-12-01 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Ruby toolkit for MQTT-SN, compatible with RSMB, command line tools and
14
14
  API