peak 1.0.1 → 1.0.2

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