Class: Rapid::Baps::Responses::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/ury_rapid/baps/responses/parser.rb

Overview

An interpreter that reads and converts raw command data from the BAPS server into response messages.

The Parser sits between the Reader, which buffers and allows access to raw BAPS protocol values, and the responses channel, which takes completed response messages and sends them to the Responder.

Constant Summary collapse

CONFIG_TYPE_MAP =

A map of configuration types to their meta-protocol types.

{
  Types::Config::CHOICE => :uint32,
  Types::Config::INT    => :uint32,
  Types::Config::STR    => :string
}
DURATION =
duration uint32
TEXT_CONTENTS =
contents string
TITLE =
title string
LOAD_ARGUMENTS =
{
  Types::Track::VOID    => [TITLE],
  Types::Track::FILE    => [TITLE, DURATION],
  Types::Track::LIBRARY => [TITLE, DURATION],
  Types::Track::TEXT    => [TITLE, TEXT_CONTENTS]
}

Instance Method Summary collapse

Constructor Details

#initialize(channel, reader) ⇒ Parser

Initialises this Parser

Examples:

Initialising a response parser

channel = EventMachine::Channel.new
reader = Reader.new
rp = Parser.new(channel, reader)

31
32
33
34
35
36
37
38
39
40
41
# File 'lib/ury_rapid/baps/responses/parser.rb', line 31

def initialize(channel, reader)
  @channel = channel
  @reader = reader

  # Set up to expect the welcome message
  @expected = [%i(message string)]
  @response = OpenStruct.new(
    code: Codes::System::WELCOME_MESSAGE,
    subcode: 0
  )
end

Instance Method Details

#add_arguments(track_type) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Adds the correct expected arguments for a load body


212
213
214
# File 'lib/ury_rapid/baps/responses/parser.rb', line 212

def add_arguments(track_type)
  @expected.unshift(*LOAD_ARGUMENTS[track_type])
end

#code_name(code) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Finds a semi-human-readable name for a BAPS response code


138
139
140
# File 'lib/ury_rapid/baps/responses/parser.rb', line 138

def code_name(code)
  Codes.code_symbol(code)
end

#commandvoid

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Attempt to scrape a command word off the top of the buffer

If successful, the parser then interprets the word and sets up to parse the rest of the command phrase.


62
63
64
65
66
67
68
69
70
# File 'lib/ury_rapid/baps/responses/parser.rb', line 62

def command
  # We could use the second return from reader.command to skip an
  # unknown message, but BAPS is quite dodgy at implementing this in
  # places, so we don't do it in practice.
  @reader.command do |code|
    parse_command(CommandWord.new(code))
    word
  end
end

#config_setting(name) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Reads a config setting

Config settings are one of the uglier areas of BAPS's meta-protocol, as the format of the config value depends on the preceding config type. As such, it's much easier to treat them specially in the parser.

This command only parses the setting type itself; it pushes the correct type for the value into @expected as the next argument.


171
172
173
174
175
176
177
178
179
# File 'lib/ury_rapid/baps/responses/parser.rb', line 171

def config_setting(name)
  @reader.uint32 do |config_type|
    @response[name] = config_type
    @reader.send(CONFIG_TYPE_MAP[config_type]) do |value|
      @response.value = value
      word
    end
  end
end

#continue_responsevoid

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Attempt to grab an argument word from the reader

This function expects there to indeed be another argument word. The caller should ensure this.


259
260
261
262
263
264
265
266
267
# File 'lib/ury_rapid/baps/responses/parser.rb', line 259

def continue_response
  parameter = @expected.shift
  name, type, *args = parameter

  # Some command words can be read from the buffer directly, whereas
  # the parser has its own logic for the more complex ones.
  own_type = respond_to?(type, true)
  own_type ? send(type, name, *args) : primitive(parameter)
end

#finish_responseBoolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Finishes a response and creates a clean slate for the next one


245
246
247
248
249
# File 'lib/ury_rapid/baps/responses/parser.rb', line 245

def finish_response
  @channel.push(@response)
  @response = nil
  command
end

#load_body(name) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Reads the body of a LOAD command

LOAD commands change their format depending on the track type, so we have to parse them specially.

This command only parses the loaded item type itself; it pushes the correct arguments for each item type into @expected as the arguments immediately following this one.


196
197
198
199
200
201
202
# File 'lib/ury_rapid/baps/responses/parser.rb', line 196

def load_body(name)
  @reader.uint32 do |track_type|
    add_arguments(track_type)
    @response[name] = track_type
    word
  end
end

#parse_command(raw_code) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Parses a command word and sets up to parse the following arguments


79
80
81
82
83
# File 'lib/ury_rapid/baps/responses/parser.rb', line 79

def parse_command(raw_code)
  code, subcode = raw_code.split
  @expected = structure_with_code(code)
  @response = response_with_code(code, subcode)
end

#primitive(parameter) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parses a word with a primitive type

A primitive type is one which is implemented in BapsReader, and only requires one buffer read. Other types are implemented in terms of special parser logic on these types, by the Parser itself.


230
231
232
233
234
235
236
237
# File 'lib/ury_rapid/baps/responses/parser.rb', line 230

def primitive(parameter)
  name, arg_type, *args = parameter

  @reader.public_send(arg_type, *args) do |value|
    @response[name] = value
    word
  end
end

#response_with_code(code, subcode) ⇒ Hash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Constructs an initial response from the given code and subcode


127
128
129
# File 'lib/ury_rapid/baps/responses/parser.rb', line 127

def response_with_code(code, subcode)
  OpenStruct.new(name: code_name(code), code: code, subcode: subcode)
end

#startvoid

This method returns an undefined value.

Starts this Parser

Examples:

Starts a Parser

rp.start

50
51
52
# File 'lib/ury_rapid/baps/responses/parser.rb', line 50

def start
  word
end

#structure_with_code(code) ⇒ Array

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Retrieves the expected set of arguments for the given BAPS command


94
95
96
97
# File 'lib/ury_rapid/baps/responses/parser.rb', line 94

def structure_with_code(code)
  structure = Baps::Responses::Structures.structure(code)
  structure.nil? ? unknown_response(code) : structure.clone
end

#unknown_response(code) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Fails if the response with the given code is not understood

In order to understand the structure of the following bytes from the BAPS server, every response received must be one for which the BAPS service has a known expected format. Thus, if an unknown response is found, the service cannot continue and fails.


112
113
114
# File 'lib/ury_rapid/baps/responses/parser.rb', line 112

def unknown_response(code)
  fail(Rapid::Common::Exceptions::InvalidPlayoutResponse, code.to_s(16))
end

#wordBoolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Attempt to grab an argument word from the reader

If there are no arguments left, the parser is set back into command reading mode and the completed response is sent to the dispatch.


151
152
153
# File 'lib/ury_rapid/baps/responses/parser.rb', line 151

def word
  @expected.empty? ? finish_response : continue_response
end