class ADAM6050::Server

The server listens to a speciefied UDP port and delegates incomming messages to the different handlers.

Constants

DEFAULT_PORT

@return [Integer] the dafault port of the UDP server.

Attributes

logger[R]

@return [Logger] the logger used by the server.

state[R]

@return [Integer] the current state.

Public Class Methods

new(password: nil, logger: Logger.new(STDOUT)) click to toggle source

@param password [String] the plain text password to use when validating

new clients.

@param logger [Logger] the logger to use.

# File lib/adam6050/server.rb, line 19
def initialize(password: nil, logger: Logger.new(STDOUT))
  @session  = Session.new
  @handlers = [
    Handler::Login.new(password),
    Handler::Status.new,
    Handler::Read.new,
    Handler::Write.new
  ]
  @state = State.initial
  @state_lock = Mutex.new
  @logger = logger
end

Public Instance Methods

run(host: nil, port: DEFAULT_PORT, &block) click to toggle source

Starts a new UDP server that listens on the given port. The state is updated atomically and yielded to an optional block everytime a change is made. By returning `false` the block can cancel the state update. This call is blocking.

@yield [Integer] the updated state. @yield [Integer] the old state.

@param host [String] the host to listen on. @param port [Integer] the UDP port to listen on. @return [nil]

# File lib/adam6050/server.rb, line 43
def run(host: nil, port: DEFAULT_PORT, &block)
  logger.info "Listening on port #{port}"

  Socket.udp_server_loop host, port do |msg, sender|
    logger.debug { "#{sender.remote_address.inspect} -> '#{msg.inspect}'" }
    handler = @handlers.find { |h| h.handles? msg } || next
    @state_lock.synchronize do
      handle(handler, msg, sender, &block)
    end
  end
  nil
end
update() { |state| ... } click to toggle source

Updates the state atomicly. The current state will be yielded to the given block and the return value used as the next state.

@yield [Integer] the current state.

# File lib/adam6050/server.rb, line 60
def update
  @state_lock.synchronize do
    @state = yield @state
  end
end

Private Instance Methods

abort_state_change?(next_state) { |next_state, state| ... } click to toggle source

@yield [Integer] the next state. @yield [Integer] the current state.

@param next_state [Integer] the next state. @return [true] if the next state differ from the current and the

(optional) given block returns `false`.

@return [false] otherwise.

# File lib/adam6050/server.rb, line 99
def abort_state_change?(next_state)
  return false if next_state == @state

  commit = !block_given? || yield(next_state, @state)
  commit == false
end
handle(handler, msg, sender, &block) click to toggle source

@yield see abort_state_change?

@param handler [Handler] the handler selected to handle the message. @param msg [String] the received message. @param sender [UDPSource] the UDP client. @param block [Proc] @return [nil]

# File lib/adam6050/server.rb, line 75
def handle(handler, msg, sender, &block)
  @session.validate! sender if handler.validate?

  next_state, reply = handler.handle msg, @state, @session, sender

  return if abort_state_change?(next_state, &block)
  @state = next_state

  return unless reply

  sender.reply reply + "\r"
  logger.debug reply
rescue Session::InvalidSender => e
  sender.reply "?\r"
  logger.warn e.message
end