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 +4 -4
- data/.rubocop.yml +14 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +16 -1
- data/data/whitelist.example +5 -0
- data/db/001_create_unit_table.rb +2 -0
- data/lib/tac_scribe/cache.rb +148 -0
- data/lib/tac_scribe/daemon.rb +72 -13
- data/lib/tac_scribe/datastore.rb +22 -68
- data/lib/tac_scribe/event_processor.rb +41 -29
- data/lib/tac_scribe/event_queue.rb +12 -22
- data/lib/tac_scribe/version.rb +1 -1
- data/tac_scribe.gemspec +2 -2
- metadata +50 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a4f7e36f9c7d3dcc3fcad923b1840c2a1ff889de5f076e27511f3d0e6de61a82
|
4
|
+
data.tar.gz: 8c00eaf5d6816c45a15aaf2776028af026654c887814f8d84458fc7520c4b8c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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/
|
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
|
-
|
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
|
-
## [
|
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
|
data/db/001_create_unit_table.rb
CHANGED
@@ -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
|
data/lib/tac_scribe/daemon.rb
CHANGED
@@ -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
|
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
|
-
@
|
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
|
-
|
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
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
@
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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__),
|
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(
|
data/lib/tac_scribe/datastore.rb
CHANGED
@@ -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
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
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 '
|
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
|
-
|
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.
|
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
|
68
|
+
return if ignore_unit?(even)
|
60
69
|
|
61
|
-
|
62
|
-
|
63
|
-
|
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 @
|
67
|
-
|
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
|
-
|
88
|
+
false
|
71
89
|
end
|
72
90
|
|
73
91
|
# Process a delete event for an object
|
74
92
|
#
|
75
|
-
# @param
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
103
|
+
def update_latitude(value)
|
104
|
+
Cache.instance.reference_latitude = value
|
81
105
|
end
|
82
106
|
|
83
|
-
|
84
|
-
|
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 :
|
9
|
+
attr_accessor :events_written
|
10
10
|
|
11
|
-
def initialize
|
12
|
-
@verbose_logging = verbose_logging
|
11
|
+
def initialize
|
13
12
|
@events = Queue.new
|
14
|
-
|
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
|
34
|
-
@
|
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
|
-
|
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
|
-
|
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
|
-
|
74
|
+
@events << { type: :set_latitude, value: BigDecimal(value) }
|
85
75
|
when 'ReferenceLongitude'
|
86
|
-
|
76
|
+
@events << { type: :set_longitude, value: BigDecimal(value) }
|
87
77
|
when 'ReferenceTime'
|
88
78
|
@reference_time = @time = Time.parse(value)
|
89
79
|
end
|
data/lib/tac_scribe/version.rb
CHANGED
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.
|
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:
|
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: '
|
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: '
|
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
|
-
|
236
|
-
|
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: []
|