class Xi::TidalClock

Constants

SYNC_INTERVAL_SEC

Attributes

attached[R]
attached?[R]
port[R]
server[R]

Public Class Methods

new(server: 'localhost', port: 9160, **opts) click to toggle source
Calls superclass method Xi::Clock::new
# File lib/xi/tidal_clock.rb, line 11
def initialize(server: 'localhost', port: 9160, **opts)
  @server = server
  @port = port
  @attached = true

  super(opts)

  @ws_thread = Thread.new { ws_thread_routine }
end

Public Instance Methods

attach() click to toggle source
# File lib/xi/tidal_clock.rb, line 30
def attach
  @attached = true
  self
end
cps=(new_cps) click to toggle source
# File lib/xi/tidal_clock.rb, line 21
def cps=(new_cps)
  fail NotImplementedError, 'cps is read-only'
end
dettach() click to toggle source
# File lib/xi/tidal_clock.rb, line 25
def dettach
  @attached = false
  self
end

Private Instance Methods

connect() click to toggle source
# File lib/xi/tidal_clock.rb, line 98
def connect
  @socket = TCPSocket.new(@server, @port)
rescue => err
  error(err)
  sleep 1
end
do_ws_sync() click to toggle source
# File lib/xi/tidal_clock.rb, line 44
def do_ws_sync
  return unless @attached

  # Try to connect to websocket server
  connect
  return if @socket.nil? || @socket.closed?

  # Offer a handshake
  @handshake = WebSocket::Handshake::Client.new(url: "ws://#{@server}:#{@port}")
  @socket.puts @handshake.to_s

  # Read server response
  while line = @socket.gets
    @handshake << line
    break if @handshake.finished?
  end

  unless @handshake.finished?
    debug(__method__, "Handshake didn't finished. Disconnect")
    @socket.close
    return
  end

  unless @handshake.valid?
    debug(__method__, "Handshake is not valid. Disconnect")
    @socket.close
    return
  end

  frame = WebSocket::Frame::Incoming::Server.new(version: @handshake.version)

  # Read loop
  loop do
    data, _ = @socket.recvfrom(4096)
    break if data.empty?

    frame << data
    while f = frame.next
      if (f.type == :close)
        debug(__method__, "Close frame received. Disconnect")
        @socket.close
        return
      else
        debug(__method__, "Frame: #{f}")
        hash = parse_frame_body(f.to_s)
        update_clock_from_server_data(hash)
      end
    end
  end

rescue => err
  error(err)
end
parse_frame_body(body) click to toggle source
# File lib/xi/tidal_clock.rb, line 105
def parse_frame_body(body)
  h = {}
  ts, _, cps = body.split(',')
  h[:ts] = Time.parse(ts)
  h[:cps] = cps.to_f
  h
end
update_clock_from_server_data(h) click to toggle source
# File lib/xi/tidal_clock.rb, line 113
def update_clock_from_server_data(h)
  # Do not set @init_ts for now
  #@init_ts = h[:ts].to_f
  @cps = h[:cps]
end
ws_thread_routine() click to toggle source
# File lib/xi/tidal_clock.rb, line 37
def ws_thread_routine
  loop do
    do_ws_sync
    sleep INTERVAL_SEC
  end
end