peak 1.0.1 → 1.0.2

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: 2b395c9e001b9be92ec7523d17b639f45551d64b
4
- data.tar.gz: bbb594e7ea2200d8a9452be19ef03115b21aecee
3
+ metadata.gz: df65f4faa4d3b4e36b51a9d6b4a5bba37bb2c992
4
+ data.tar.gz: 9b35a626888fe27afe140abe5dc0bef82d154ae7
5
5
  SHA512:
6
- metadata.gz: 5d45e7971b96c7996477fd314f19bb6763e9e0e2dfbebabf211f3d7aeb6dc548163771a09c50ec13b2f67db851dd4e17ed0d69749861e44743e5e98ac4ddd0ec
7
- data.tar.gz: f61760fa1c991287e410e042dbb3dca2d1f1397468c6599b86df77f50cc9a04ed6befd70d6493a10e9cdb1336d57d7ca263857b995e87dfd3e5e44c8748489fa
6
+ metadata.gz: 712d1e69d00909111a9bca843918276148103c3372b69f1b08ec61176195edbd31f936c12eef7f7960b5f73a3730094f1937431215fc4f0ae38a34ecbb9c2b4d
7
+ data.tar.gz: 8f3a5a14d1a7d28b2e16d1d6bde9df2d2a11b5a01d988d7ed0dcdf445fd56fd143a7d22aeb83cc34c615d2c28de7e9aac5f4c77a73359f6e1ae679b82181c7a0
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.2
4
+
5
+ * Added proper cleanup before exiting when a sigterm or sigint is received. This allows serial connections to be
6
+ shutdown and KISS mode on the TNC to be exited.
7
+ * Added a Domain-specific Language for the routing and mangling of frames.
8
+ * By default frames are now preemptively digipeated.
9
+
3
10
  ## 1.0.1
4
11
 
5
12
  * Added beacon plugin to transmit beacon every 10 minutes.
@@ -0,0 +1,13 @@
1
+ * Jeffrey Phillips Freeman (WI2ARD) - http://JeffreyFreeman.me
2
+ * Martin Murray (KD8LVZ)
3
+ * Paul McMillan - https://github.com/PaulMcMillan
4
+ * Russ Innes
5
+ * John Hogenmiller (KB3DFZ) - https://github.com/ytjohn
6
+ * Phil Gagnon N1HHG
7
+ * Ben Benesh - https://github.com/bbene
8
+ * Joe Goforth
9
+ * Rick Eason
10
+ * Jay Nugent
11
+ * Pete Loveall (AE5PL)
12
+ * darksidelemm - https://github.com/darksidelemm
13
+ * agmuino - https://github.com/agmuino
@@ -1,9 +1,10 @@
1
1
  = Peak
2
2
 
3
3
  {<img src="https://badge.fury.io/rb/peak.svg" alt="Gem Version" />}[https://badge.fury.io/rb/peak]
4
+ {<img src="http://img.shields.io/badge/yard-docs-blue.svg" />}[http://www.rubydoc.info/github/Syncleus/peak/master]
4
5
 
5
6
  Author:: Jeffrey Phillips Freeman, WI2ARD (freemo@gmail.com)
6
- Copyright:: Copyright (c) 2016 Syncleus, Inc.
7
+ Copyright:: Copyright (c) 2016 - present Syncleus, Inc.
7
8
 
8
9
  Peak is a full suite APRS client and reference implementation for the APEX protocol. APEX is a next generation APRS
9
10
  based protocol. This repository represents the official application implementing APEX. It is a full-feature application
@@ -34,12 +35,12 @@ This is Free software: Apache License v2
34
35
 
35
36
  == Development
36
37
 
37
- After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive
38
+ After checking out the repo, run +bin/setup+ to install dependencies. You can also run +bin/console+ for an interactive
38
39
  prompt that will allow you to experiment.
39
40
 
40
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the
41
- version number in `app_info.rb`, and then run `bundle exec rake release`, which will create a git tag for the version,
42
- push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
41
+ To install this gem onto your local machine, run <code>bundle exec rake install</code>. To release a new version, update the
42
+ version number in +app_info.rb+, and then run <code>bundle exec rake release</code>, which will create a git tag for the version,
43
+ push git commits and tags, and push the +.gem+ file to {rubygems.org}[https://rubygems.org].
43
44
 
44
45
  == Links
45
46
 
data/bin/peak CHANGED
@@ -14,7 +14,7 @@ class App
14
14
  Peak::main
15
15
  end
16
16
 
17
- description 'Peak: a reference implementation for the APEX protocol.'
17
+ description 'Peak is a reference implementation for the APEX protocol which is itself based on the APRS protocol.'
18
18
  version Peak::VERSION
19
19
 
20
20
  # Proxy to an OptionParser instance's on method
@@ -1,12 +1,25 @@
1
1
  require 'peak/echo'
2
2
  require 'peak/config_loader'
3
3
  require 'peak/plugin_loader'
4
+ require 'peak/routing/route'
4
5
 
5
6
  module Peak
6
7
 
7
8
  def self.main
8
9
  config = find_config(true)
10
+ unless config
11
+ echo_colorized_error('Could not find a valid configuration file in any of the default locations')
12
+ return
13
+ end
14
+
9
15
  port_map = init_port_map(config)
16
+ unless port_map
17
+ echo_colorized_error('Configuration could not be loaded, format was invalid.')
18
+ return
19
+ end
20
+
21
+ Signal.trap('INT') { throw :sig }
22
+ Signal.trap('TERM') { throw :sig }
10
23
 
11
24
  active_plugins = {}
12
25
  plugins = load_plugins
@@ -19,20 +32,45 @@ module Peak
19
32
  end
20
33
 
21
34
  # Handle any packets we read in.
22
- while true
23
- something_read = false
24
- port_map.values.each do |tnc_port|
25
- frame = tnc_port.read
26
- if frame
27
- something_read = true
28
- active_plugins.each_key do |plugin|
29
- plugin.handle_packet(frame, tnc_port)
35
+ catch (:sig) do
36
+ while true
37
+ something_read = false
38
+ port_map.values.each do |tnc_port|
39
+ frame = tnc_port.read
40
+ if frame
41
+ something_read = true
42
+ routed_frame = Routing::Route.handle_frame(frame, config, true, tnc_port.name)
43
+ if routed_frame
44
+ if routed_frame[:output_target]
45
+ port_map[routed_frame[:output_target]].write(routed_frame[:frame])
46
+ else
47
+ active_plugins.each_key do |plugin|
48
+ plugin.handle_packet(routed_frame[:frame], tnc_port)
49
+ end
50
+ end
51
+ end
30
52
  end
31
53
  end
32
- end
33
- unless something_read
34
- sleep(1)
54
+ unless something_read
55
+ sleep(1)
56
+ end
35
57
  end
36
58
  end
59
+
60
+ puts
61
+ puts 'Shutdown signal caught, shutting down...'
62
+
63
+ # Let's cleanup some stuff before exiting.
64
+ active_plugins.keys.each do |plugin|
65
+ plugin.stop
66
+ end
67
+ active_plugins.values.each do |plugin_thread|
68
+ plugin_thread.join
69
+ end
70
+ port_map.values.each do |port|
71
+ port.close
72
+ end
73
+
74
+ puts 'Peak successfully shutdown.'
37
75
  end
38
76
  end
@@ -1,3 +1,3 @@
1
1
  module Peak
2
- VERSION = "1.0.1"
2
+ VERSION = "1.0.2"
3
3
  end
@@ -41,7 +41,7 @@ module Peak
41
41
  tnc_name = section_name.strip.split(' ')[1].strip
42
42
  if tnc_name == 'IGATE'
43
43
  echo_colorized_error('IGATE was used as the name for a TNC in the configuration, this name is reserved')
44
- return false
44
+ return nil
45
45
  end
46
46
 
47
47
  kiss_tnc = nil
@@ -50,7 +50,7 @@ module Peak
50
50
  baud = section_content['baud']
51
51
  kiss_tnc = Apex::AprsKiss.new(Kiss::KissSerial.new(com_port, baud))
52
52
  else
53
- return false
53
+ return nil
54
54
  end
55
55
 
56
56
  if section_content.key?('kiss_init')
@@ -63,14 +63,14 @@ module Peak
63
63
  kiss_tnc.connect
64
64
  else
65
65
  echo_colorized_error('Invalid configuration, value assigned to kiss_init was not recognized: ' + kiss_init_string)
66
- return false
66
+ return nil
67
67
  end
68
68
  else
69
69
  kiss_tnc.connect
70
70
  end
71
71
 
72
72
  unless config_lookup_enforce(section_content, 'port_count')
73
- return false
73
+ return nil
74
74
  end
75
75
 
76
76
  port_count = section_content['port_count']
@@ -80,22 +80,22 @@ module Peak
80
80
  port_section_name = 'PORT ' + port_name
81
81
 
82
82
  unless config_lookup_enforce(config, port_section_name)
83
- return false
83
+ return nil
84
84
  end
85
85
  port_section = config[port_section_name]
86
86
 
87
87
  unless config_lookup_enforce(port_section, 'identifier')
88
- return false
88
+ return nil
89
89
  end
90
90
  port_identifier = port_section['identifier']
91
91
 
92
92
  unless config_lookup_enforce(port_section, 'net')
93
- return false
93
+ return nil
94
94
  end
95
95
  port_net = port_section['net']
96
96
 
97
97
  unless config_lookup_enforce(port_section, 'tnc_port')
98
- return false
98
+ return nil
99
99
  end
100
100
  tnc_port = port_section['tnc_port']
101
101
 
@@ -0,0 +1,687 @@
1
+ module Peak
2
+ module Routing
3
+ class Rules
4
+ protected
5
+ def initialize(frame, config, frame_port, next_target=nil)
6
+ @frame = frame
7
+ @next_target = next_target
8
+ @config = config
9
+ @frame_port = frame_port
10
+
11
+ @port_info = {}
12
+ config.each do |section_name, section_content|
13
+ if section_name.start_with?('TNC ')
14
+ tnc_name = section_name.strip.split(' ')[1].strip
15
+ if tnc_name != 'IGATE'
16
+ port_count = section_content['port_count']
17
+
18
+ (1..port_count).each do |port|
19
+ port_name = tnc_name + '-' + port.to_s
20
+ port_section_name = 'PORT ' + port_name
21
+ port_section = config[port_section_name]
22
+ port_identifier = port_section['identifier']
23
+ port_net = port_section['net']
24
+ tnc_port = port_section['tnc_port']
25
+
26
+ old_paradigm = port_section['old_paradigm']
27
+ unless old_paradigm
28
+ old_paradigm = []
29
+ end
30
+
31
+ new_paradigm_all = port_section['new_paradigm']
32
+ new_paradigm = []
33
+ if new_paradigm_all and new_paradigm_all.length > 0
34
+ new_paradigm_all.each do |new_paradigm_one|
35
+ new_paradigm << {:target => new_paradigm_one['target'], :max_hops => new_paradigm_one['max_hops']}
36
+ end
37
+ end
38
+
39
+ preemptive = port_section['preemptive']
40
+
41
+ @port_info[port_name] = {
42
+ :port_identifier => port_identifier,
43
+ :port_net => port_net,
44
+ :tnc_port => tnc_port,
45
+ :old_paradigm => old_paradigm,
46
+ :new_paradigm => new_paradigm,
47
+ :preemptive => preemptive
48
+ }
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ private
56
+ def self.args_parser(*args)
57
+ if !args or !args.length or args.length <= 0
58
+ condition = args[0]
59
+ true_target = :pass
60
+ false_target = :pass
61
+ elsif !!args[0] == args[0] # if the first argument is a boolean
62
+ condition = args[0]
63
+ true_target = args.length >= 2 ? args[1] : :pass
64
+ false_target = args.length >= 3 ? args[2] : :pass
65
+ else
66
+ condition = true
67
+ true_target = args.length >= 1 ? args[0] : :pass
68
+ false_target = args.length >= 2 ? args[1] : :pass
69
+ end
70
+
71
+ next_target = condition ? true_target : false_target
72
+
73
+ {
74
+ :condition => condition,
75
+ :true_target => true_target,
76
+ :false_target => false_target,
77
+ :next_target => next_target
78
+ }
79
+ end
80
+
81
+ private
82
+ def do_next_target(next_target)
83
+ if next_target != :pass
84
+ @next_target = next_target
85
+ throw :new_target
86
+ end
87
+ return
88
+ end
89
+
90
+ private
91
+ def select_next_hop(hops)
92
+ select_future_hops(hops)[0]
93
+ end
94
+
95
+ private
96
+ def select_future_hops(hops)
97
+ hops.select { |hop| !hop.end_with? '*' }
98
+ end
99
+
100
+ private
101
+ def select_net_band_hops(hops)
102
+ hops.select { |hop| hop =~ /\d{1,4}M$/i }
103
+ end
104
+
105
+ private
106
+ def select_net_freq_hops(hops)
107
+ hops.select { |hop| hop =~ /\d{1,4}M\d{1,3}$/i }
108
+ end
109
+
110
+ private
111
+ def select_net_hops(hops)
112
+ hops.select { |hop| hop =~ /\d{1,4}M\d{0,3}$/i }
113
+ end
114
+
115
+ private
116
+ def net_freg_to_band_hops(hops)
117
+ select_net_hops(hops).map { |hop| hop.gsub(/M\d{0,3}$/i, 'M') }
118
+ end
119
+
120
+ private
121
+ def all_my_names
122
+ names = @port_info.values.map { |info| info[:port_identifier].upcase }
123
+ names += net_freg_to_band_hops(@port_info.values.map {|info| info[:port_net].upcase })
124
+ names += select_net_freq_hops(@port_info.values.map {|info| info[:port_net].upcase })
125
+ names.uniq
126
+ end
127
+
128
+ private
129
+ def self.array_upcase(things)
130
+ things.map {|s|
131
+ if !s.is_a? Regexp
132
+ s.upcase
133
+ else
134
+ s
135
+ end
136
+ }
137
+ end
138
+
139
+ private
140
+ def self.is_hop_old_paradigm?(hop, old_paradigm)
141
+ if old_paradigm.length <= 0
142
+ return false
143
+ end
144
+
145
+ old_paradigm = Rules.array_upcase(old_paradigm)
146
+ hop = hop.upcase
147
+
148
+ old_paradigm.each do |target|
149
+ if target.is_a? Regexp
150
+ if hop =~ target
151
+ return true
152
+ end
153
+ elsif hop == target
154
+ return true
155
+ end
156
+ end
157
+
158
+ false
159
+ end
160
+
161
+ private
162
+ def self.is_hop_new_paradigm?(hop, new_paradigm_all)
163
+ if new_paradigm_all.length <= 0
164
+ return false
165
+ end
166
+
167
+ hop = hop.upcase
168
+ unless hop =~ /[A-Z]*-\d{1,2}/
169
+ return false
170
+ end
171
+ hop_split = hop.split('-')
172
+ if !hop_split or hop_split.length != 2
173
+ return false
174
+ end
175
+ hop_target = hop_split[0]
176
+ hop_hops = hop_split[1].to_i
177
+ if hop_hops <= 0
178
+ return false
179
+ end
180
+
181
+ new_paradigm_all.each do |new_paradigm|
182
+ if new_paradigm[:target].is_a? Regexp
183
+ if hop_target =~ new_paradigm[:target]
184
+ return true
185
+ end
186
+ elsif hop_target == new_paradigm[:target].upcase
187
+ return true
188
+ end
189
+ end
190
+
191
+ false
192
+ end
193
+
194
+ private
195
+ def self.is_hop_identifier_me(hop, identifiers)
196
+ if identifiers.include? hop.upcase
197
+ true
198
+ else
199
+ false
200
+ end
201
+ end
202
+
203
+ protected
204
+ def filter(*args)
205
+ args = Rules.args_parser(*args)
206
+ do_next_target(args[:next_target])
207
+ end
208
+
209
+ protected
210
+ def consume_next_hop(*args)
211
+ args = Rules.args_parser(*args)
212
+
213
+ if has_next_hop? and args[:condition]
214
+ catch(:done) do
215
+ @frame[:path].each do |hop|
216
+ unless hop.end_with? '*'
217
+ hop << '*'
218
+ throw :done
219
+ end
220
+ end
221
+ end
222
+ end
223
+
224
+ do_next_target(args[:next_target])
225
+ end
226
+
227
+ private
228
+ def self.consume_new_paradigm(hop)
229
+ hop_split = hop.split('-')
230
+ target = hop_split[0]
231
+ hop_count = hop_split[1].to_i
232
+ hop_count -= 1
233
+ if hop_count > 0
234
+ hop.replace( target + '-' + hop_count.to_s )
235
+ else
236
+ hop.replace(target + '*')
237
+ end
238
+ end
239
+
240
+ protected
241
+ def route(*args, preemptive: nil)
242
+ args = Rules.args_parser(*args)
243
+
244
+ if has_next_hop? and args[:condition]
245
+ detected = false
246
+
247
+ port_name = @frame_port
248
+ port_info = @port_info[port_name]
249
+
250
+ if preemptive == nil
251
+ preemptive = port_info[:preemptive]
252
+ if preemptive == nil
253
+ preemptive = true
254
+ end
255
+ end
256
+
257
+ old_paradigm = nil
258
+ if port_info.key? :old_paradigm and port_info[:old_paradigm].length > 0
259
+ old_paradigm = Rules.array_upcase(port_info[:old_paradigm])
260
+ end
261
+
262
+
263
+ new_paradigm = nil
264
+ if port_info.key? :new_paradigm and port_info[:new_paradigm].length > 0
265
+ new_paradigm = port_info[:new_paradigm]
266
+ end
267
+
268
+
269
+ next_hop = select_next_hop(@frame[:path])
270
+ if all_my_names.include? next_hop.upcase or Rules.is_hop_old_paradigm?(next_hop, old_paradigm)
271
+ next_hop << '*'
272
+ elsif Rules.is_hop_new_paradigm?(next_hop, new_paradigm)
273
+ Rules.consume_new_paradigm(next_hop)
274
+ end
275
+
276
+ if preemptive
277
+ future_hops = select_future_hops(@frame[:path])
278
+ identifiers = @port_info.values.map { |info| info[:port_identifier].upcase }
279
+ future_hops.reverse.each do |hop|
280
+ if detected
281
+ hop << '*'
282
+ elsif identifiers.include? hop.upcase
283
+ hop << '*'
284
+ detected = true
285
+ end
286
+ end
287
+ end
288
+ end
289
+
290
+ do_next_target(args[:next_target])
291
+ end
292
+
293
+ protected
294
+ def seen?
295
+ identifiers = @port_info.values.map { |info| info[:port_identifier].upcase }
296
+ if identifiers.include? @frame[:source].upcase
297
+ return true
298
+ end
299
+
300
+ @frame[:path].each do |hop|
301
+ unless hop.end_with? '*'
302
+ return false
303
+ end
304
+
305
+ if identifiers.include? hop.upcase.chomp('*')
306
+ return true
307
+ end
308
+ end
309
+
310
+ false
311
+ end
312
+
313
+ protected
314
+ def destination_me?
315
+ identifiers = @port_info.values.map { |info| info[:port_identifier].upcase }
316
+ if identifiers.include? @frame[:destination].upcase
317
+ true
318
+ else
319
+ false
320
+ end
321
+ end
322
+
323
+ protected
324
+ def has_next_hop?
325
+ @frame[:path].each do |hop|
326
+ unless hop.end_with? '*'
327
+ return true
328
+ end
329
+ end
330
+ return false
331
+ end
332
+
333
+ protected
334
+ def next_hop_identifier_me?
335
+ unless has_next_hop?
336
+ return false
337
+ end
338
+
339
+ identifiers = @port_info.values.map { |info| info[:port_identifier].upcase }
340
+ Rules.is_hop_identifier_me(select_next_hop(@frame[:path]), identifiers)
341
+ end
342
+
343
+ protected
344
+ def future_hop_identifier_me?
345
+ unless has_next_hop?
346
+ return false
347
+ end
348
+
349
+ identifiers = @port_info.values.map { |info| info[:port_identifier].upcase }
350
+ if (identifiers & select_future_hops(Rules.array_upcase(@frame[:path]))).empty?
351
+ false
352
+ else
353
+ true
354
+ end
355
+ end
356
+
357
+ protected
358
+ def next_hop_net_band_me?
359
+ unless has_next_hop?
360
+ return false
361
+ end
362
+
363
+ net_bands = net_freg_to_band_hops(@port_info.values.map {|info| info[:port_net].upcase })
364
+ next_bands = select_net_band_hops([select_next_hop(@frame[:path]).upcase])
365
+
366
+ if next_bands.length <= 0 or net_bands.length <= 0
367
+ return false
368
+ end
369
+
370
+ next_band = next_bands[0]
371
+
372
+ if net_bands.include? next_band
373
+ false
374
+ else
375
+ true
376
+ end
377
+ end
378
+
379
+ protected
380
+ def future_hop_net_band_me?
381
+ unless has_next_hop?
382
+ return false
383
+ end
384
+
385
+ net_bands = net_freg_to_band_hops(@port_info.values.map {|info| info[:port_net].upcase })
386
+ next_bands = Rules.array_upcase(select_net_band_hops(@frame[:path]))
387
+
388
+ if next_bands.length <= 0 or net_bands.length <= 0
389
+ return false
390
+ end
391
+
392
+ if (net_bands & next_bands).empty?
393
+ true
394
+ else
395
+ false
396
+ end
397
+ end
398
+
399
+ protected
400
+ def next_hop_net_freq_me?
401
+ unless has_next_hop?
402
+ return false
403
+ end
404
+
405
+ net_freqs = select_net_freq_hops(@port_info.values.map {|info| info[:port_net].upcase })
406
+ next_freqs= select_net_freq_hops([select_next_hop(@frame[:path]).upcase])
407
+
408
+ if next_freqs.length <= 0 or net_freqs.length <= 0
409
+ return false
410
+ end
411
+
412
+ next_band = next_bands[0]
413
+
414
+ if net_bands.include? next_band
415
+ false
416
+ else
417
+ true
418
+ end
419
+ end
420
+
421
+ protected
422
+ def future_hop_net_freq_me?
423
+ unless has_next_hop?
424
+ return false
425
+ end
426
+
427
+ net_freqs = select_net_freq_hops(@port_info.values.map {|info| info[:port_net].upcase })
428
+ next_freqs = select_net_freq_hops(select_future_hops(Rules.array_upcase(@frame[:path])))
429
+
430
+ if next_freqs.length <= 0 or net_freqs.length <= 0
431
+ return false
432
+ end
433
+
434
+ if (net_freqs & next_freqs).empty?
435
+ true
436
+ else
437
+ false
438
+ end
439
+ end
440
+
441
+ protected
442
+ def next_hop_old_paradigm?
443
+ unless has_next_hop?
444
+ return false
445
+ end
446
+
447
+ port_name = @frame_port
448
+ port_info = @port_info[port_name]
449
+
450
+ if !port_info.key? :old_paradigm or port_info[:old_paradigm].length <= 0
451
+ return false
452
+ end
453
+
454
+ old_paradigm = Rules.array_upcase(port_info[:old_paradigm])
455
+ next_hop = select_next_hop(@frame[:path]).upcase
456
+
457
+ Rules.is_hop_old_paradigm?(next_hop, old_paradigm)
458
+ end
459
+
460
+ protected
461
+ def future_hop_old_paradigm?
462
+ unless has_next_hop?
463
+ return false
464
+ end
465
+
466
+ port_name = @frame_port
467
+ port_info = @port_info[port_name]
468
+
469
+ if !port_info.key? :old_paradigm or port_info[:old_paradigm].length <= 0
470
+ return false
471
+ end
472
+
473
+ old_paradigm = Rules.array_upcase(port_info[:old_paradigm])
474
+ future_hops = select_future_hops(@frame[:path]).map { |s| s.upcase }
475
+
476
+ old_paradigm.each do |target|
477
+ if target.is_a? Regexp
478
+ future_hops.each do |hop|
479
+ if hop =~ target
480
+ return true
481
+ end
482
+ end
483
+ elsif future_hops.include? target
484
+ return true
485
+ end
486
+ end
487
+
488
+ false
489
+ end
490
+
491
+ protected
492
+ def next_hop_new_paradigm?
493
+ unless has_next_hop?
494
+ return false
495
+ end
496
+
497
+ port_name = @frame_port
498
+ port_info = @port_info[port_name]
499
+
500
+ if !port_info.key? :new_paradigm or port_info[:new_paradigm].length <= 0
501
+ return false
502
+ end
503
+
504
+ new_paradigm_all = port_info[:new_paradigm]
505
+ next_hop = select_next_hop(@frame[:path]).upcase
506
+
507
+ Rules.is_hop_new_paradigm?(next_hop, new_paradigm_all)
508
+ end
509
+
510
+ protected
511
+ def future_hop_new_paradigm?
512
+ unless has_next_hop?
513
+ return false
514
+ end
515
+
516
+ port_name = @frame_port
517
+ port_info = @port_info[port_name]
518
+
519
+ if !port_info.key? :new_paradigm or port_info[:new_paradigm].length <= 0
520
+ return false
521
+ end
522
+
523
+ new_paradigm_all = port_info[:new_paradigm]
524
+ future_hops = select_future_hops(@frame[:path]).map { |s| s.upcase }
525
+
526
+
527
+ new_paradigm_all.each do |new_paradigm|
528
+ if new_paradigm[:target].is_a? Regexp
529
+ future_hops.each do |hop|
530
+ if hop.include? '-'
531
+ hop_split = hop.split('-')
532
+ if hop_split and hop_split.length == 2
533
+ hop_target = hop_split[0]
534
+ hop_hops = hop_split[1].to_i
535
+ if hop_hops > 0 and hop_target =~ new_paradigm[:target]
536
+ return true
537
+ end
538
+ end
539
+ end
540
+ end
541
+ elsif future_hops.include? new_paradigm[:target].upcase
542
+ return true
543
+ end
544
+ end
545
+
546
+ false
547
+ end
548
+
549
+ protected
550
+ def next_hop_net_me?
551
+ if next_hop_net_band_me? or next_hop_net_freq_me?
552
+ true
553
+ else
554
+ false
555
+ end
556
+ end
557
+
558
+ protected
559
+ def future_hop_net_me?
560
+ if future_hop_net_band_me? or future_hop_net_freq_me?
561
+ true
562
+ else
563
+ false
564
+ end
565
+ end
566
+
567
+ protected
568
+ def next_hop_paradigm?
569
+ if next_hop_new_paradigm? or next_hop_old_paradigm?
570
+ true
571
+ else
572
+ false
573
+ end
574
+ end
575
+
576
+ protected
577
+ def future_hop_paradigm?
578
+ if future_hop_new_paradigm? or future_hop_old_paradigm?
579
+ true
580
+ else
581
+ false
582
+ end
583
+ end
584
+
585
+ protected
586
+ def next_hop_me?
587
+ if next_hop_net_me? or next_hop_identifier_me? or next_hop_paradigm?
588
+ true
589
+ else
590
+ false
591
+ end
592
+ end
593
+
594
+ protected
595
+ def future_hop_me?
596
+ if future_hop_net_me? or future_hop_identifier_me? or future_hop_paradigm?
597
+ true
598
+ else
599
+ false
600
+ end
601
+ end
602
+
603
+ protected
604
+ def mine?
605
+ if future_hop_me? or destination_me?
606
+ true
607
+ else
608
+ false
609
+ end
610
+ end
611
+
612
+ public
613
+ attr_reader :next_target, :frame
614
+ end
615
+
616
+ class Route
617
+ @@chains = {}
618
+
619
+ public
620
+ def self.inbound_chain(&block)
621
+ @@chains[:inbound] = {:target => :input, :block => block}
622
+ end
623
+
624
+ public
625
+ def self.outbound_chain(&block)
626
+ @@chains[:outbound] = {:target => :output, :block => block}
627
+ end
628
+
629
+ public
630
+ def self.side_chain(name, target, &block)
631
+ @@chains[name] = {:target => target, :block => block}
632
+ end
633
+
634
+ public
635
+ def self.reset
636
+ @@chains.clear
637
+ end
638
+ end
639
+
640
+ # ==== This is the part the user will implement ======
641
+
642
+ Route.inbound_chain {
643
+ filter destination_me?, :pass, :forward
644
+ filter :input # if this werent here packet would be dropped
645
+ }
646
+
647
+ Route.outbound_chain {
648
+ filter :output # if this werent here packet would be dropped
649
+ }
650
+
651
+ Route.side_chain(:forward, :output) {
652
+ filter seen?, :drop
653
+ filter future_hop_me?, :pass, :drop
654
+ route :output
655
+ }
656
+
657
+ # ==== Exiting custom code ========
658
+
659
+ class Route
660
+ public
661
+ def self.handle_frame(frame, config, is_inbound, name)
662
+ initial_chain = is_inbound ? :inbound : :outbound
663
+ unless @@chains.key? initial_chain
664
+ return nil
665
+ end
666
+
667
+ rules = Rules.new(frame, config, name, initial_chain)
668
+ more = true
669
+ while more and rules.next_target != :input and rules.next_target != :output and rules.next_target != :drop
670
+ catch(:new_target) do
671
+ rules.instance_eval &@@chains[rules.next_target][:block]
672
+ more = false
673
+ end
674
+ end
675
+
676
+ if rules.next_target == :drop or !more
677
+ return nil
678
+ end
679
+
680
+ {
681
+ :output_target => rules.next_target == :output ? name : nil,
682
+ :frame => rules.frame
683
+ }
684
+ end
685
+ end
686
+ end
687
+ end
@@ -20,12 +20,19 @@ PORT KENWOOD-1:
20
20
  identifier: WI2ARD-1
21
21
  net: 2M1
22
22
  tnc_port: 0
23
- beacon_path: [WIDE1-1, WIDE2-2]
24
- status_path: [WIDE1-1, WIDE2-2]
23
+ beacon_path: ['WIDE1-1', 'WIDE2-2']
24
+ status_path: ['WIDE1-1', 'WIDE2-2']
25
25
  beacon_text: '!/:=i@;N.G& --PHG5790/G/D R-I-R H24 C30'
26
26
  status_text: '>Listening on 146.52Mhz http://JeffreyFreeman.me'
27
27
  id_text: 'WI2ARD/30M1 GATE/2M1 WI2ARD-1/2M1 WIDEN-n IGATE'
28
- id_path: [WIDE1-1, WIDE2-2]
28
+ id_path: ['WIDE1-1', 'WIDE2-2']
29
+ preemptive: true
30
+ old_paradigm: ['ECHO', 'PA']
31
+ new_paradigm:
32
+ - target: !ruby/regexp '/^WIDE[1-2]{1}$/i'
33
+ max_hops: 2
34
+ - target: !ruby/regexp '/^PA[1-5]{1}$/i'
35
+ max_hops: 5
29
36
 
30
37
  PORT RPR-1:
31
38
  identifier: WI2ARD
@@ -37,6 +44,13 @@ PORT RPR-1:
37
44
  status_text: '>Robust Packet Radio http://JeffreyFreeman.me'
38
45
  id_text: 'WI2ARD/30M1 GATE/2M1 WI2ARD-1/2M1 WIDEN-n IGATE'
39
46
  id_path: [WIDE1-1]
47
+ preemptive: true
48
+ old_paradigm: ['ECHO', 'PA']
49
+ new_paradigm:
50
+ - target: !ruby/regexp '/^WIDE[1-2]{1}$/i'
51
+ max_hops: 2
52
+ - target: !ruby/regexp '/^PA[1-5]{1}$/i'
53
+ max_hops: 5
40
54
 
41
55
  IGATE:
42
56
  callsign: WI2ARD
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
  spec.email = ['jeffrey.freeman@syncleus.com']
12
12
 
13
13
  spec.summary = %q{Reference implementation for the APEX Radio protocol, an extension to APRS.}
14
- spec.description = %q{Reference implementation for the APEX Radio protocol, which is iteself an extension to the APRS protocol.}
14
+ spec.description = %q{Reference implementation for the APEX Radio protocol, which is itself an extension to the APRS protocol.}
15
15
  spec.homepage = 'http://apexprotocol.com'
16
16
 
17
17
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: peak
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeffrey Phillips Freeman
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-10-05 00:00:00.000000000 Z
11
+ date: 2016-10-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -108,7 +108,7 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0.14'
111
- description: Reference implementation for the APEX Radio protocol, which is iteself
111
+ description: Reference implementation for the APEX Radio protocol, which is itself
112
112
  an extension to the APRS protocol.
113
113
  email:
114
114
  - jeffrey.freeman@syncleus.com
@@ -118,6 +118,7 @@ extra_rdoc_files: []
118
118
  files:
119
119
  - ".gitignore"
120
120
  - CHANGELOG.md
121
+ - CONTRIBUTORS.md
121
122
  - Gemfile
122
123
  - LICENSE
123
124
  - README.rdoc
@@ -135,6 +136,7 @@ files:
135
136
  - lib/peak/plugins/id.rb
136
137
  - lib/peak/plugins/plugin_factory.rb
137
138
  - lib/peak/plugins/status.rb
139
+ - lib/peak/routing/route.rb
138
140
  - lib/peak/tnc_port.rb
139
141
  - peak.conf.example
140
142
  - peak.gemspec