tac_scribe 0.3.0-java → 0.6.0-java

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
  SHA256:
3
- metadata.gz: f53013c85197970f92c1b1d77608bbb6c0f6d7d70b09640a4d566282ecd2ecd0
4
- data.tar.gz: e05a92c6f3da05c2f2925bb0ab18740ec31e3629906c56472e3fa94b5c1891c8
3
+ metadata.gz: a4f7e36f9c7d3dcc3fcad923b1840c2a1ff889de5f076e27511f3d0e6de61a82
4
+ data.tar.gz: 8c00eaf5d6816c45a15aaf2776028af026654c887814f8d84458fc7520c4b8c5
5
5
  SHA512:
6
- metadata.gz: 54e42d58f65f1d4e49f1affc4211d8829d4b26a1dfc810c5656c37346e552fad692371c3e0738f7a7629d378d88e481aad570a1a8ab47e329cc2c91aac601f20
7
- data.tar.gz: 71d84cd363227f4ffd2684e00c04ae97e714b74f25339f1cf004d5741c33bf770131d7fb51b2b346f112d575135549ef001c0cb6c71b914cf0a72310659c926b
6
+ metadata.gz: e2e9a0c6b73d94bf7cbef046022ce40ca967287611357cef8d2349dd2b84252c2da1249c54d9c6480a6b06c506bf29fc65dbe5895d18816fab255b28278a5766
7
+ data.tar.gz: 7749e5326bb78bf985e827ed412baf83e87da9c9c5c91ee29f189d96d28b5b23e1b6bd1af1253b1d50ed4b7a183cbe282676ef72446fed02c224e6bd3e10c1be
data/.rubocop.yml CHANGED
@@ -14,7 +14,10 @@ Metrics/BlockLength:
14
14
  # There isn't much sense breaking up the OptionParser block since splitting
15
15
  # into db and tacview option methods will just break the method length cop
16
16
  # and splitting further doesn't aid readability
17
- - exe/start_scribe
17
+ - exe/tac_scribe
18
+ # Cannot really avoid this
19
+ - tac_scribe.gemspec
20
+
18
21
  Metrics/MethodLength:
19
22
  Exclude:
20
23
  # Breaking up the initializer doesn't really do much for readability
@@ -29,3 +32,13 @@ Style/GlobalVars:
29
32
  Exclude:
30
33
  # Using a global variable makes it available to the Pry console
31
34
  - bin/console
35
+ AllCops:
36
+ Exclude:
37
+ # These files will need a MAJOR refactoring to get rid of these warnings
38
+ - lib/tac_scribe/cache.rb
39
+ - lib/tac_scribe/datastore.rb
40
+ - lib/tac_scribe/daemon.rb
41
+ ClassVars:
42
+ Exclude:
43
+ # We are fine with this and understand the risks. We are not inheriting.
44
+ - lib/tac_scribe/event_processor.rb
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- jruby
1
+ ruby-2.6.3
data/CHANGELOG.md CHANGED
@@ -4,7 +4,22 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## [Unreleased]
7
+ ## [0.6.0]
8
+ ### Added
9
+ - Calculates and stores speed of units
10
+
11
+ ### Fixed
12
+ - Made more robust against failures and more logging messages
13
+
14
+ ## [0.5.0]
15
+ ### Changed
16
+ - Added missing db column to migration file
17
+ - Clear the cache on server restart
18
+ - Add more info to logging
19
+
20
+ ## [0.4.0]
21
+ ### Changed
22
+ - Switch to periodic writing to the database
8
23
 
9
24
  ## [0.3.0] - 2019-10-27
10
25
  ### Changed
@@ -0,0 +1,5 @@
1
+ Sea+Watercraft+AircraftCarrier
2
+ Air+Rotorcraft
3
+ Air+FixedWing
4
+ Ground+Static+Aerodrome
5
+ Navaid+Static+Bullseye
@@ -12,7 +12,9 @@ Sequel.migration do
12
12
  String :group, null: true
13
13
  Integer :coalition
14
14
  Integer :heading
15
+ Integer :speed
15
16
  Time :updated_at
17
+ Boolean :deleted
16
18
  # TODO: GIST Index on the position
17
19
  end
18
20
  end
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'georuby'
4
+ require 'sequel'
5
+ require 'sequel-postgis-georuby'
6
+ require 'singleton'
7
+ require 'concurrent-ruby'
8
+ require 'haversine'
9
+
10
+ module TacScribe
11
+ # The in-memory cache that is updated by events in real-time before the data
12
+ # is synced to the DB
13
+ class Cache
14
+ include Singleton
15
+ include GeoRuby::SimpleFeatures
16
+
17
+ @@cache = Concurrent::Hash.new
18
+
19
+ attr_accessor :reference_latitude, :reference_longitude
20
+
21
+ def data
22
+ @@cache
23
+ end
24
+
25
+ def write_object(object)
26
+ if reference_latitude != 0 || reference_longitude != 0
27
+ localize_position(object)
28
+ end
29
+
30
+ id = object[:object_id]
31
+ object[:id] = id
32
+
33
+ cache_object = @@cache[id]
34
+
35
+ object[:position] = set_position(object, cache_object)
36
+
37
+ %i[object_id latitude longitude color country].each do |key|
38
+ object.delete(key)
39
+ end
40
+
41
+ if object[:coalition]
42
+ object[:coalition] = case object[:coalition]
43
+ when 'Allies'
44
+ 0
45
+ when 'Enemies'
46
+ 1
47
+ else
48
+ 2
49
+ end
50
+ end
51
+
52
+ if cache_object
53
+ object[:heading] = calculate_heading(cache_object, object)
54
+ object[:speed] = calculate_speed(cache_object, object)
55
+ cache_object.merge!(object)
56
+ else
57
+ object[:heading] = -1
58
+ object[:speed] = 0
59
+ cache_object = object
60
+ end
61
+
62
+ if !cache_object.key?(:altitude) || !cache_object[:altitude]
63
+ cache_object[:altitude] = 0
64
+ end
65
+ if !cache_object.key?(:coalition) || !cache_object[:coalition]
66
+ cache_object[:coalition] = 2
67
+ end
68
+
69
+ cache_object[:pilot] = nil unless cache_object.key?(:pilot)
70
+
71
+ cache_object[:group] = nil unless cache_object.key?(:group)
72
+
73
+ cache_object[:deleted] = false unless cache_object.key?(:deleted)
74
+
75
+ cache_object[:updated_at] = Time.now
76
+
77
+ @@cache[id] = cache_object
78
+ end
79
+
80
+ def delete_object(id)
81
+ @@cache[id][:deleted] = true if @@cache[id]
82
+ end
83
+
84
+ def clear
85
+ @@cache.clear
86
+ end
87
+
88
+ private
89
+
90
+ def localize_position(object)
91
+ if reference_latitude && object.key?(:latitude)
92
+ object[:latitude] = reference_latitude + object[:latitude]
93
+ end
94
+ if reference_longitude && object.key?(:longitude)
95
+ object[:longitude] = reference_longitude + object[:longitude]
96
+ end
97
+ end
98
+
99
+ def set_position(object, cache_object)
100
+ longitude = if object.key?(:longitude)
101
+ object[:longitude]
102
+ else
103
+ cache_object[:position].y
104
+ end
105
+
106
+ latitude = if object.key?(:latitude)
107
+ object[:latitude]
108
+ else
109
+ cache_object[:position].x
110
+ end
111
+
112
+ # This "Point" class is not lat/long aware which is why we are
113
+ # flipping things up here because when it converts to SQL we need
114
+ # the long to be first since that is what postgresql expects.
115
+ Point.from_x_y(longitude, latitude)
116
+ end
117
+
118
+ def calculate_heading(cache_object, object)
119
+ if cache_object[:position] == object[:position]
120
+ return cache_object[:heading]
121
+ end
122
+
123
+ begin
124
+ cache_object[:position].bearing_to(object[:position]).to_i
125
+ rescue Math::DomainError => e
126
+ puts 'Could not calculate heading: ' + e.message
127
+ puts 'Old Position: ' + cache_object[:position].inspect
128
+ puts 'New Position: ' + object[:position].inspect
129
+ end
130
+ end
131
+
132
+ def calculate_speed(cache_object, object)
133
+ time = object[:game_time] - cache_object[:game_time]
134
+
135
+ start_point = cache_object[:position]
136
+ end_point = object[:position]
137
+
138
+ # Because of the above issues with Point and Lat/Lon
139
+ # the values are reverse here :(
140
+ distance = Haversine.distance(start_point.y, start_point.x,
141
+ end_point.y, end_point.x).to_meters
142
+
143
+ speed = distance / time
144
+
145
+ speed.to_i
146
+ end
147
+ end
148
+ end
@@ -4,6 +4,7 @@ require 'json'
4
4
  require 'tacview_client'
5
5
  require 'set'
6
6
  require_relative 'datastore'
7
+ require_relative 'cache'
7
8
  require_relative 'event_queue'
8
9
  require_relative 'event_processor'
9
10
 
@@ -24,11 +25,13 @@ module TacScribe
24
25
  end
25
26
  Datastore.instance.connect
26
27
 
27
- @event_queue = EventQueue.new verbose_logging: verbose_logging
28
+ @event_queue = EventQueue.new
29
+
30
+ @verbose_logging = verbose_logging
28
31
 
29
32
  @populate_airfields = populate_airfields
30
33
  @thread_count = thread_count
31
- @processing_threads = []
34
+ @threads = {}
32
35
  @whitelist = Set.new(IO.read(whitelist).split) if whitelist
33
36
 
34
37
  @client = TacviewClient::Client.new(
@@ -47,43 +50,99 @@ module TacScribe
47
50
  # for example
48
51
  def start_processing
49
52
  loop do
50
- kill_processing_threads
53
+ puts 'Starting processing loop'
51
54
  @event_queue.clear
52
55
  Datastore.instance.truncate_table
56
+ Cache.instance.clear
53
57
  start_processing_threads
58
+ start_db_sync_thread
59
+ start_reporting_thread
54
60
  populate_airfields if @populate_airfields
61
+ @threads.each_pair do |key, _value|
62
+ puts "#{key} thread started"
63
+ end
55
64
  @client.connect
65
+ # If this code is executed it means we have been disconnected without
66
+ # exceptions. We will still sleep to stop a retry storm. Just not as
67
+ # long.
68
+ sleep 10
56
69
  # Rescuing reliably from Net::HTTP is a complete bear so rescue
57
70
  # StandardError. It ain't pretty but it is reliable. We will puts
58
71
  # the exception just in case
59
72
  # https://stackoverflow.com/questions/5370697/what-s-the-best-way-to-handle-exceptions-from-nethttp
60
73
  rescue StandardError => e
74
+ puts 'Exception in processing loop'
61
75
  puts e.message
62
76
  puts e.backtrace
77
+ kill_threads
63
78
  sleep 30
64
79
  next
65
80
  end
66
81
  end
67
82
 
68
- def kill_processing_threads
69
- @processing_threads.each do |thr|
70
- thr.kill
71
- thr.join
83
+ def kill_threads
84
+ puts 'Killing Threads'
85
+ @threads.each_pair do |key, thread|
86
+ puts "Killing #{key} thread"
87
+ thread.kill
88
+ thread.join
72
89
  end
73
90
  end
74
91
 
75
92
  def start_processing_threads
76
- @thread_count.times do
77
- @processing_threads << Thread.new do
78
- EventProcessor.new(datastore: Datastore.instance,
79
- event_queue: @event_queue,
80
- whitelist: @whitelist).start
93
+ @event_processor = EventProcessor.new(cache: Cache.instance,
94
+ datastore: Datastore.instance,
95
+ event_queue: @event_queue,
96
+ whitelist: @whitelist)
97
+ event_processor_thread = Thread.new do
98
+ @event_processor.start
99
+ end
100
+ event_processor_thread.name = 'Event Processor'
101
+ @threads[:processing] = event_processor_thread
102
+ end
103
+
104
+ def start_db_sync_thread
105
+ db_write_thread = Thread.new do
106
+ loop do
107
+ sleep 1
108
+ deleted = Datastore.instance.write_objects(Cache.instance.data.values)
109
+ deleted.each { |id| Cache.instance.data.delete(id) }
110
+ rescue StandardError
111
+ next
112
+ end
113
+ end
114
+ db_write_thread.name = 'Database Writing'
115
+ @threads[:database] = db_write_thread
116
+ end
117
+
118
+ def start_reporting_thread
119
+ return unless @verbose_logging
120
+
121
+ reporting_thread = Thread.new do
122
+ sleep 5
123
+ loop do
124
+ puts "#{Time.now.strftime('%FT%T')}\t" \
125
+ "Events Incoming: #{@event_queue.events_written}\t" \
126
+ "Processed: #{@event_processor.events_processed}\t" \
127
+ "Ignored: #{@event_processor.events_ignored}\t" \
128
+ "Queue Size: #{@event_queue.size}\t" \
129
+ "Objects Written: #{Datastore.instance.written}\t" \
130
+ "Deleted: #{Datastore.instance.deleted}"
131
+ @event_queue.events_written = 0
132
+ @event_processor.events_processed = 0
133
+ @event_processor.events_ignored = 0
134
+ Datastore.instance.written = 0
135
+ Datastore.instance.deleted = 0
136
+ sleep 1
81
137
  end
82
138
  end
139
+ reporting_thread.name = 'Reporting'
140
+ @threads[:reporting] = reporting_thread
83
141
  end
84
142
 
85
143
  def populate_airfields
86
- json = File.read(File.join(File.dirname(__FILE__), '../../data/airfields.json'))
144
+ json = File.read(File.join(File.dirname(__FILE__),
145
+ '../../data/airfields.json'))
87
146
  airfields = JSON.parse(json)
88
147
  airfields.each_with_index do |airfield, i|
89
148
  @event_queue.update_object(
@@ -14,7 +14,7 @@ module TacScribe
14
14
  include Singleton
15
15
  include GeoRuby::SimpleFeatures
16
16
 
17
- attr_accessor :db
17
+ attr_accessor :db, :written, :deleted
18
18
 
19
19
  @configuration = nil
20
20
  @db = nil
@@ -52,24 +52,29 @@ module TacScribe
52
52
  @db[:units].truncate
53
53
  end
54
54
 
55
- def write_object(event, timestamp)
56
- unit = get_unit(event[:object_id])
57
-
58
- # Tacview omits values that don't change to save
59
- # data bandwidth and storage. If something has not changed then
60
- # use the old value
61
- current_position = get_position(event, unit)
62
-
63
- if unit
64
- update_unit(event, unit, current_position, timestamp)
65
- else
66
- insert_unit(event, current_position, timestamp)
55
+ def write_objects(objects)
56
+ objects = objects.map do |object|
57
+ obj = object.clone
58
+ obj.delete(:game_time)
59
+ obj
67
60
  end
68
- end
69
61
 
70
- def delete_object(object_id)
71
- count = @db[:units].where(id: object_id).delete
72
- "Deleted #{object_id} #{object_id.class} - #{count}"
62
+ @db[:units].insert_conflict(
63
+ constraint: :units_pkey,
64
+ update: { position: Sequel[:excluded][:position],
65
+ altitude: Sequel[:excluded][:altitude],
66
+ heading: Sequel[:excluded][:heading],
67
+ speed: Sequel[:excluded][:speed],
68
+ updated_at: Sequel[:excluded][:updated_at],
69
+ deleted: Sequel[:excluded][:deleted] }
70
+ )
71
+ .multi_insert(objects)
72
+ deleted_ids = @db[:units].where(deleted: true).select_map(:id)
73
+ @db[:units].where(deleted: true).delete
74
+ self.written = objects.size
75
+ self.deleted = deleted_ids.size
76
+
77
+ deleted_ids
73
78
  end
74
79
 
75
80
  private
@@ -85,56 +90,5 @@ module TacScribe
85
90
  "/#{@configuration.database}"
86
91
  end
87
92
  end
88
-
89
- def get_unit(id)
90
- @db[:units].where(id: id).first
91
- end
92
-
93
- def update_unit(event, unit, current_position, timestamp)
94
- heading = calculate_heading(unit[:position],
95
- current_position,
96
- unit[:heading])
97
-
98
- @db[:units].where(id: event[:object_id]).update(
99
- position: current_position[:lat_lon],
100
- altitude: current_position[:altitude],
101
- heading: heading,
102
- updated_at: timestamp
103
- )
104
- end
105
-
106
- def insert_unit(event, current_position, timestamp)
107
- @db[:units].insert(id: event[:object_id],
108
- position: current_position[:lat_lon],
109
- altitude: current_position[:altitude],
110
- type: event[:type],
111
- name: event[:name],
112
- group: event[:group],
113
- pilot: event[:pilot],
114
- coalition: event[:coalition] == 'Allies' ? 0 : 1,
115
- updated_at: timestamp)
116
- end
117
-
118
- def get_position(event, unit)
119
- {
120
- lat_lon: Point.from_x_y(
121
- event.fetch(:longitude) { unit ? unit[:position].y : nil },
122
- event.fetch(:latitude) { unit ? unit[:position].x : nil }
123
- ),
124
- altitude: event.fetch(:altitude) { unit ? unit[:altitude] : nil }
125
- }
126
- end
127
-
128
- def calculate_heading(old_position, current_position, current_heading)
129
- return current_heading if old_position == current_position[:lat_lon]
130
-
131
- begin
132
- old_position.bearing_to(current_position[:lat_lon]).to_i
133
- rescue Math::DomainError => e
134
- puts 'Could not calculate heading: ' + e.message
135
- puts 'Old Position: ' + old_position.inspect
136
- puts 'New Position: ' + current_position.inspect
137
- end
138
- end
139
93
  end
140
94
  end
@@ -3,22 +3,27 @@
3
3
  require 'tacview_client/base_processor'
4
4
  require 'time'
5
5
  require 'concurrent-ruby'
6
- require_relative 'datastore'
6
+ require_relative 'cache'
7
7
 
8
8
  module TacScribe
9
9
  # Processes the events emitted by the Ruby Tacview Client
10
10
  class EventProcessor
11
11
  @@ignored_units = Concurrent::Set.new
12
12
 
13
- def initialize(datastore:, event_queue:, whitelist: nil)
13
+ attr_accessor :events_processed, :events_ignored
14
+
15
+ def initialize(cache:, datastore:, event_queue:, whitelist: nil)
16
+ @cache = cache
14
17
  @datastore = datastore
15
18
  @event_queue = event_queue
16
19
  @whitelist = whitelist
20
+ self.events_processed = 0
21
+ self.events_ignored = 0
17
22
  end
18
23
 
19
24
  def start
20
25
  loop do
21
- wrapped_event = @event_queue.get_event
26
+ wrapped_event = @event_queue.shift
22
27
  process_event(wrapped_event)
23
28
  rescue StandardError => e
24
29
  puts wrapped_event
@@ -33,6 +38,10 @@ module TacScribe
33
38
  update_object(wrapped_event[:event], wrapped_event[:time])
34
39
  when :delete_object
35
40
  delete_object(wrapped_event[:object_id])
41
+ when :set_latitude
42
+ update_latitude wrapped_event[:value]
43
+ when :set_longitude
44
+ update_longitude wrapped_event[:value]
36
45
  end
37
46
  end
38
47
 
@@ -56,44 +65,47 @@ module TacScribe
56
65
  # @option event [BigDecimal] :altitude The object altitude above sea level
57
66
  # in meters to 1 decimal place.
58
67
  def update_object(event, time)
59
- return if @@ignored_units.include?(event[:object_id])
68
+ return if ignore_unit?(even)
60
69
 
61
- if @whitelist && event[:type] && !@whitelist.include?(event[:type])
62
- @@ignored_units << event[:object_id]
63
- return
70
+ event[:game_time] = time
71
+
72
+ self.events_processed += 1
73
+ @cache.write_object(event)
74
+ end
75
+
76
+ def ignore_unit?(event)
77
+ if @@ignored_units.include?(event[:object_id])
78
+ self.events_ignored += 1
79
+ return true
64
80
  end
65
81
 
66
- if @event_queue.reference_latitude || @event_queue.reference_longitude
67
- localize_position(event)
82
+ if @whitelist && event[:type] && !@whitelist.include?(event[:type])
83
+ @@ignored_units << event[:object_id]
84
+ self.events_ignored += 1
85
+ return true
68
86
  end
69
87
 
70
- @datastore.write_object(event, time)
88
+ false
71
89
  end
72
90
 
73
91
  # Process a delete event for an object
74
92
  #
75
- # @param object_id [String] A hexadecimal number representing the object
76
- # ID
77
- def delete_object(object_id)
78
- return if @@ignored_units.delete?(object_id)
93
+ # @param id [String] A hexadecimal number representing the object ID
94
+ def delete_object(id)
95
+ if @@ignored_units.delete?(id)
96
+ nil
97
+ else
98
+ @cache.delete_object(id)
99
+ end
100
+ self.events_processed += 1
101
+ end
79
102
 
80
- @datastore.delete_object(object_id)
103
+ def update_latitude(value)
104
+ Cache.instance.reference_latitude = value
81
105
  end
82
106
 
83
- # If we have reference lat/long then use that as a base and update the event
84
- # position
85
- def localize_position(event)
86
- # There is no combination of layouts for these lines that doesn' trip
87
- # at least one rubocop cop so just ignore the guard clause. The best
88
- # one is using an inline if but that breaks the line length.
89
- # rubocop:disable Style/GuardClause
90
- if event[:latitude]
91
- event[:latitude] = @event_queue.reference_latitude + event[:latitude]
92
- end
93
- if event[:longitude]
94
- event[:longitude] = @event_queue.reference_longitude + event[:longitude]
95
- end
96
- # rubocop:enable Style/GuardClause
107
+ def update_longitude(value)
108
+ Cache.instance.reference_longitude = value
97
109
  end
98
110
  end
99
111
  end
@@ -6,32 +6,22 @@ require 'time'
6
6
  module TacScribe
7
7
  # Processes the events emitted by the Ruby Tacview Client
8
8
  class EventQueue < TacviewClient::BaseProcessor
9
- attr_accessor :reference_latitude, :reference_longitude
9
+ attr_accessor :events_written
10
10
 
11
- def initialize(verbose_logging:)
12
- @verbose_logging = verbose_logging
11
+ def initialize
13
12
  @events = Queue.new
14
- @event_write = 0
15
- @event_read = 0
16
-
17
- return unless verbose_logging == true
18
-
19
- Thread.new do
20
- loop do
21
- puts "#{Time.now.strftime('%FT%T')} - Queue Size: #{@events.size} \t Events Added: #{@event_write} \t Events Processed: #{@event_read}"
22
- @event_write = 0
23
- @event_read = 0
24
- sleep 1
25
- end
26
- end
13
+ self.events_written = 0
27
14
  end
28
15
 
29
16
  def clear
30
17
  @events.clear
31
18
  end
32
19
 
33
- def get_event
34
- @event_read += 1
20
+ def size
21
+ @events.size
22
+ end
23
+
24
+ def shift
35
25
  @events.shift
36
26
  end
37
27
 
@@ -53,7 +43,7 @@ module TacScribe
53
43
  # @option event [BigDecimal] :altitude The object altitude above sea level
54
44
  # in meters to 1 decimal place.
55
45
  def update_object(event)
56
- @event_write += 1
46
+ self.events_written += 1
57
47
  @events << { type: :update_object, event: event, time: @time }
58
48
  end
59
49
 
@@ -62,7 +52,7 @@ module TacScribe
62
52
  # @param object_id [String] A hexadecimal number representing the object
63
53
  # ID
64
54
  def delete_object(object_id)
65
- @event_write += 1
55
+ self.events_written += 1
66
56
  @events << { type: :delete_object, object_id: object_id }
67
57
  end
68
58
 
@@ -81,9 +71,9 @@ module TacScribe
81
71
  def set_property(property:, value:)
82
72
  case property
83
73
  when 'ReferenceLatitude'
84
- self.reference_latitude = BigDecimal(value)
74
+ @events << { type: :set_latitude, value: BigDecimal(value) }
85
75
  when 'ReferenceLongitude'
86
- self.reference_longitude = BigDecimal(value)
76
+ @events << { type: :set_longitude, value: BigDecimal(value) }
87
77
  when 'ReferenceTime'
88
78
  @reference_time = @time = Time.parse(value)
89
79
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TacScribe
4
- VERSION = '0.3.0'
4
+ VERSION = '0.6.0'
5
5
  end
data/tac_scribe.gemspec CHANGED
@@ -21,12 +21,11 @@ Gem::Specification.new do |spec|
21
21
  spec.bindir = 'exe'
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ['lib']
24
+ spec.platform = 'java'
24
25
 
25
26
  spec.metadata['yard.run'] = 'yri'
26
27
 
27
28
  if RUBY_PLATFORM == 'java'
28
- # TODO: Specifying a verison chokes bundler for some reason
29
- spec.platform = 'java'
30
29
  spec.add_dependency 'activerecord-jdbcpostgresql-adapter'
31
30
  else
32
31
  spec.add_dependency 'pg', '~>1.1'
@@ -38,6 +37,7 @@ Gem::Specification.new do |spec|
38
37
  # of an API breaking change are higher then normal. Therefore lock the
39
38
  # version
40
39
  spec.add_dependency 'concurrent-ruby', '~>1.1.5'
40
+ spec.add_dependency 'haversine', '~>0.3'
41
41
  spec.add_dependency 'sequel-postgis-georuby', '0.1.2'
42
42
  spec.add_dependency 'tacview_client', '~>0.1'
43
43
 
metadata CHANGED
@@ -1,178 +1,192 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tac_scribe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.6.0
5
5
  platform: java
6
6
  authors:
7
7
  - Jeffrey Jones
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-10-26 00:00:00.000000000 Z
11
+ date: 2020-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
+ name: pg
14
15
  requirement: !ruby/object:Gem::Requirement
15
16
  requirements:
16
- - - ">="
17
+ - - "~>"
17
18
  - !ruby/object:Gem::Version
18
- version: '0'
19
- name: activerecord-jdbcpostgresql-adapter
20
- prerelease: false
19
+ version: '1.1'
21
20
  type: :runtime
21
+ prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '1.1'
27
27
  - !ruby/object:Gem::Dependency
28
+ name: georuby
28
29
  requirement: !ruby/object:Gem::Requirement
29
30
  requirements:
30
31
  - - "~>"
31
32
  - !ruby/object:Gem::Version
32
33
  version: '2.5'
33
- name: georuby
34
- prerelease: false
35
34
  type: :runtime
35
+ prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '2.5'
41
41
  - !ruby/object:Gem::Dependency
42
+ name: sequel
42
43
  requirement: !ruby/object:Gem::Requirement
43
44
  requirements:
44
45
  - - "~>"
45
46
  - !ruby/object:Gem::Version
46
47
  version: '5.22'
47
- name: sequel
48
- prerelease: false
49
48
  type: :runtime
49
+ prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '5.22'
55
55
  - !ruby/object:Gem::Dependency
56
+ name: concurrent-ruby
56
57
  requirement: !ruby/object:Gem::Requirement
57
58
  requirements:
58
59
  - - "~>"
59
60
  - !ruby/object:Gem::Version
60
61
  version: 1.1.5
61
- name: concurrent-ruby
62
- prerelease: false
63
62
  type: :runtime
63
+ prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: 1.1.5
69
69
  - !ruby/object:Gem::Dependency
70
+ name: haversine
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.3'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.3'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sequel-postgis-georuby
70
85
  requirement: !ruby/object:Gem::Requirement
71
86
  requirements:
72
87
  - - '='
73
88
  - !ruby/object:Gem::Version
74
89
  version: 0.1.2
75
- name: sequel-postgis-georuby
76
- prerelease: false
77
90
  type: :runtime
91
+ prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
94
  - - '='
81
95
  - !ruby/object:Gem::Version
82
96
  version: 0.1.2
83
97
  - !ruby/object:Gem::Dependency
98
+ name: tacview_client
84
99
  requirement: !ruby/object:Gem::Requirement
85
100
  requirements:
86
101
  - - "~>"
87
102
  - !ruby/object:Gem::Version
88
103
  version: '0.1'
89
- name: tacview_client
90
- prerelease: false
91
104
  type: :runtime
105
+ prerelease: false
92
106
  version_requirements: !ruby/object:Gem::Requirement
93
107
  requirements:
94
108
  - - "~>"
95
109
  - !ruby/object:Gem::Version
96
110
  version: '0.1'
97
111
  - !ruby/object:Gem::Dependency
112
+ name: bundler
98
113
  requirement: !ruby/object:Gem::Requirement
99
114
  requirements:
100
115
  - - "~>"
101
116
  - !ruby/object:Gem::Version
102
117
  version: '2.0'
103
- name: bundler
104
- prerelease: false
105
118
  type: :development
119
+ prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
122
  - - "~>"
109
123
  - !ruby/object:Gem::Version
110
124
  version: '2.0'
111
125
  - !ruby/object:Gem::Dependency
126
+ name: rake
112
127
  requirement: !ruby/object:Gem::Requirement
113
128
  requirements:
114
129
  - - "~>"
115
130
  - !ruby/object:Gem::Version
116
131
  version: '10.0'
117
- name: rake
118
- prerelease: false
119
132
  type: :development
133
+ prerelease: false
120
134
  version_requirements: !ruby/object:Gem::Requirement
121
135
  requirements:
122
136
  - - "~>"
123
137
  - !ruby/object:Gem::Version
124
138
  version: '10.0'
125
139
  - !ruby/object:Gem::Dependency
140
+ name: rspec
126
141
  requirement: !ruby/object:Gem::Requirement
127
142
  requirements:
128
143
  - - "~>"
129
144
  - !ruby/object:Gem::Version
130
145
  version: '3.8'
131
- name: rspec
132
- prerelease: false
133
146
  type: :development
147
+ prerelease: false
134
148
  version_requirements: !ruby/object:Gem::Requirement
135
149
  requirements:
136
150
  - - "~>"
137
151
  - !ruby/object:Gem::Version
138
152
  version: '3.8'
139
153
  - !ruby/object:Gem::Dependency
154
+ name: rubocop
140
155
  requirement: !ruby/object:Gem::Requirement
141
156
  requirements:
142
157
  - - "~>"
143
158
  - !ruby/object:Gem::Version
144
159
  version: '0.73'
145
- name: rubocop
146
- prerelease: false
147
160
  type: :development
161
+ prerelease: false
148
162
  version_requirements: !ruby/object:Gem::Requirement
149
163
  requirements:
150
164
  - - "~>"
151
165
  - !ruby/object:Gem::Version
152
166
  version: '0.73'
153
167
  - !ruby/object:Gem::Dependency
168
+ name: simplecov
154
169
  requirement: !ruby/object:Gem::Requirement
155
170
  requirements:
156
171
  - - "~>"
157
172
  - !ruby/object:Gem::Version
158
173
  version: '0.17'
159
- name: simplecov
160
- prerelease: false
161
174
  type: :development
175
+ prerelease: false
162
176
  version_requirements: !ruby/object:Gem::Requirement
163
177
  requirements:
164
178
  - - "~>"
165
179
  - !ruby/object:Gem::Version
166
180
  version: '0.17'
167
181
  - !ruby/object:Gem::Dependency
182
+ name: yard
168
183
  requirement: !ruby/object:Gem::Requirement
169
184
  requirements:
170
185
  - - "~>"
171
186
  - !ruby/object:Gem::Version
172
187
  version: '0.9'
173
- name: yard
174
- prerelease: false
175
188
  type: :development
189
+ prerelease: false
176
190
  version_requirements: !ruby/object:Gem::Requirement
177
191
  requirements:
178
192
  - - "~>"
@@ -201,10 +215,12 @@ files:
201
215
  - bin/console
202
216
  - bin/setup
203
217
  - data/airfields.json
218
+ - data/whitelist.example
204
219
  - db/001_create_unit_table.rb
205
220
  - db/README.md
206
221
  - exe/tac_scribe
207
222
  - lib/tac_scribe.rb
223
+ - lib/tac_scribe/cache.rb
208
224
  - lib/tac_scribe/daemon.rb
209
225
  - lib/tac_scribe/datastore.rb
210
226
  - lib/tac_scribe/event_processor.rb
@@ -217,7 +233,7 @@ licenses:
217
233
  - AGPL-3.0-or-later
218
234
  metadata:
219
235
  yard.run: yri
220
- post_install_message:
236
+ post_install_message:
221
237
  rdoc_options: []
222
238
  require_paths:
223
239
  - lib
@@ -232,9 +248,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
232
248
  - !ruby/object:Gem::Version
233
249
  version: '0'
234
250
  requirements: []
235
- rubyforge_project:
236
- rubygems_version: 2.7.6
237
- signing_key:
251
+ rubygems_version: 3.0.3
252
+ signing_key:
238
253
  specification_version: 4
239
254
  summary: Write Tacview data to PostGIS database
240
255
  test_files: []