mqtt-sn-ruby 0.1.8 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
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