class TacScribe::Daemon

Main entry-point into Tac Scribe. Instantiate this class to start processing events.

Public Class Methods

new(db_host:, db_port:, db_name:, db_user:, db_password:, tacview_host:, tacview_port:, tacview_password:, tacview_client_name:, verbose_logging:, thread_count:, whitelist: nil, cinc_port:) click to toggle source
# File lib/tac_scribe/daemon.rb, line 16
def initialize(db_host:, db_port:, db_name:, db_user:, db_password:,
               tacview_host:, tacview_port:, tacview_password:,
               tacview_client_name:, verbose_logging:, thread_count:,
               whitelist: nil, cinc_port:)
  Datastore.instance.configure do |config|
    config.host = db_host
    config.port = db_port
    config.database = db_name
    config.username = db_user
    config.password = db_password
  end
  Datastore.instance.connect

  @event_queue = EventQueue.new

  @verbose_logging = verbose_logging

  @thread_count = thread_count
  @threads = {}
  @whitelist = Set.new(IO.read(whitelist).split) if whitelist

  @tacview_client = TacviewClient::Client.new(
    host: tacview_host,
    port: tacview_port,
    password: tacview_password,
    processor: @event_queue,
    client_name: tacview_client_name
  )

  @cinc_client = Cinc::Client.new(
    host: tacview_host,
    port: cinc_port
  )
end

Public Instance Methods

kill_threads() click to toggle source
# File lib/tac_scribe/daemon.rb, line 90
def kill_threads
  puts 'Killing Threads'
  @threads.each_pair do |key, thread|
    puts "Killing #{key} thread"
    thread.kill
    thread.join
  end
end
start_cinc_thread() click to toggle source
# File lib/tac_scribe/daemon.rb, line 111
def start_cinc_thread
  cinc_thread = Thread.new do
    loop do
      @cinc_client.connect
      while true
        @cinc_client.get_airbases.each do |airbase|
          airbase[:type] = "Ground+Static+Aerodrome"
          airbase[:skip_localization] = true
          @event_queue.update_object Hash[airbase.map{ |k, v| [k.to_sym, v] }]
        end
        sleep 60
      end
      rescue StandardError => e
        puts 'Exception in cinc thread'
        puts e.message
        puts e.backtrace
        sleep 30
        next
    end
  end

  cinc_thread.name = 'Cinc Processor'
  @threads[:cinc] = cinc_thread
end
start_db_sync_thread() click to toggle source
# File lib/tac_scribe/daemon.rb, line 136
def start_db_sync_thread
  db_write_thread = Thread.new do
    loop do
      sleep 1
      deleted = Datastore.instance.write_objects(Cache.instance.data.values)
      deleted.each { |id| Cache.instance.data.delete(id) }
    rescue StandardError
      next
    end
  end
  db_write_thread.name = 'Database Writing'
  @threads[:database] = db_write_thread
end
start_processing() click to toggle source

Starts processing and reconnects if the client was disconnected. Because connecting to Tacview always gives an initial unit dump we truncate the table each time we reconnect. This will make sure there are no ghost units hanging around after server restart for example

# File lib/tac_scribe/daemon.rb, line 56
def start_processing
  loop do
    puts 'Starting processing loop'
    @event_queue.clear
    Datastore.instance.truncate_table
    Cache.instance.clear
    start_processing_threads
    start_cinc_thread
    start_db_sync_thread
    start_reporting_thread
    @threads.each_pair do |key, _value|
      puts "#{key} thread started"
    end
    @tacview_client.connect
    # If this code is executed it means we have been disconnected without
    # exceptions. We will still sleep to stop a retry storm. Just not as
    # long.
    puts "Disconnected from Tacview"
    kill_threads
    sleep 10
  # Rescuing reliably from Net::HTTP is a complete bear so rescue
  # StandardError. It ain't pretty but it is reliable. We will puts
  # the exception just in case
  # https://stackoverflow.com/questions/5370697/what-s-the-best-way-to-handle-exceptions-from-nethttp
  rescue StandardError => e
    puts 'Exception in processing loop'
    puts e.message
    puts e.backtrace
    kill_threads
    sleep 30
    next
  end
end
start_processing_threads() click to toggle source
# File lib/tac_scribe/daemon.rb, line 99
def start_processing_threads
  @event_processor = EventProcessor.new(cache: Cache.instance,
                                        datastore: Datastore.instance,
                                        event_queue: @event_queue,
                                        whitelist: @whitelist)
  event_processor_thread = Thread.new do
    @event_processor.start
  end
  event_processor_thread.name = 'Event Processor'
  @threads[:processing] = event_processor_thread
end
start_reporting_thread() click to toggle source
# File lib/tac_scribe/daemon.rb, line 150
def start_reporting_thread
  return unless @verbose_logging

  reporting_thread = Thread.new do
    sleep 5
    loop do
      puts "#{Time.now.strftime('%FT%T')}\t" \
           "Events Incoming: #{@event_queue.events_written}\t" \
           "Processed: #{@event_processor.events_processed}\t" \
           "Ignored: #{@event_processor.events_ignored}\t" \
           "Queue Size: #{@event_queue.size}\t" \
           "Objects Written: #{Datastore.instance.written}\t" \
           "Deleted: #{Datastore.instance.deleted}"
      @event_queue.events_written = 0
      @event_processor.events_processed = 0
      @event_processor.events_ignored = 0
      Datastore.instance.written = 0
      Datastore.instance.deleted = 0
      sleep 1
    end
  end
  reporting_thread.name = 'Reporting'
  @threads[:reporting] = reporting_thread
end