telos_lwcp 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9a4509b29db056c922920b686dafdd6aeb89162b
4
+ data.tar.gz: 776463f2ef27b16ff8a1020fb40044abf7ca6f54
5
+ SHA512:
6
+ metadata.gz: eb305f4fbad7fa6adcc364dc965a990664e36772ac5124284222ccea039892d1309780a6dd4dfd3e0f3e4e57b764d80679ef2cff70b7809905e96e55a0e45dc1
7
+ data.tar.gz: 3d16436e23cec85f25b368b3c81031944ddec5124c0be3891e9bd767e6f7190ab4e782aa2747de34e6878e221cee3baeaeef8a2b588fdc504a9674b12838710e
data/.rspec ADDED
File without changes
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem 'autotest'
7
+ gem 'rspec'
8
+ gem 'rspec-autotest'
9
+ end
10
+
11
+ group :development do
12
+ gem 'pry'
13
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,50 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ telos_lwcp (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ZenTest (4.11.1)
10
+ autotest (4.4.6)
11
+ ZenTest (>= 4.4.1)
12
+ coderay (1.1.1)
13
+ diff-lcs (1.2.5)
14
+ method_source (0.8.2)
15
+ pry (0.10.4)
16
+ coderay (~> 1.1.0)
17
+ method_source (~> 0.8.1)
18
+ slop (~> 3.4)
19
+ rake (10.5.0)
20
+ rspec (3.5.0)
21
+ rspec-core (~> 3.5.0)
22
+ rspec-expectations (~> 3.5.0)
23
+ rspec-mocks (~> 3.5.0)
24
+ rspec-autotest (1.0.0)
25
+ rspec-core (>= 2.99.0.beta1, < 4.0.0)
26
+ rspec-core (3.5.4)
27
+ rspec-support (~> 3.5.0)
28
+ rspec-expectations (3.5.0)
29
+ diff-lcs (>= 1.2.0, < 2.0)
30
+ rspec-support (~> 3.5.0)
31
+ rspec-mocks (3.5.0)
32
+ diff-lcs (>= 1.2.0, < 2.0)
33
+ rspec-support (~> 3.5.0)
34
+ rspec-support (3.5.0)
35
+ slop (3.6.0)
36
+
37
+ PLATFORMS
38
+ ruby
39
+
40
+ DEPENDENCIES
41
+ autotest
42
+ bundler (~> 1.7)
43
+ pry
44
+ rake (~> 10.0)
45
+ rspec
46
+ rspec-autotest
47
+ telos_lwcp!
48
+
49
+ BUNDLED WITH
50
+ 1.10.2
data/README.rdoc ADDED
@@ -0,0 +1,51 @@
1
+ = Telos LWCP gem
2
+
3
+ == Description
4
+
5
+ The telos_lwcp gem was written to communicate with Telos VX systems. Basic commands for control are implemented, and some low-level functions to implement the ones missing. Full documentation of the VX lwcp protocol extensions can be found here: http://magnetic.beep.pl/VX/vx_lwcp_reference.pdf
6
+
7
+ == Example
8
+
9
+ engine = TelosLWCP::Engine.new(host: '192.168.0.5')
10
+ engine.connect do |conn|
11
+ conn.login("user", "")
12
+ conn.select_studio(2)
13
+ conn.call(3, "09123456789")
14
+ end
15
+
16
+ == Implemented basic commands
17
+ === connect
18
+ === login(user, password)
19
+ === select_studio(studio_nr)
20
+ === seize(line_nr)
21
+ === call(line_nr, number)
22
+
23
+ == Low-level functions
24
+ === write(command, object, arguments={})
25
+
26
+ Sends a command to the server. This is always in the format of command, object & arguments. For example the call command is implemented as:
27
+
28
+ conn.write(:call, "studio.line#1", number: "09123456")
29
+
30
+ === wait(callbacks, &block)
31
+
32
+ Pauses the main thread to wait for specific callbacks from the reading thread. This is mostly used in collaboration with the write command. Full example of how the seize command is implemented:
33
+
34
+ conn.wait(
35
+ [:event, "studio.line#1"] => proc { true }, # Line is seized
36
+ [:ack, "studio.line#1"] => proc {|obj| TelosLWCP::Error.new(obj.system_items[:msg]) } # Line isn't seized, throw error
37
+ ) do
38
+ conn.write(:seize, "studio.line#1")
39
+ end
40
+
41
+ === subscribe(command:"", object:"", matcher:"", &block)
42
+
43
+ Subscribe to specific or general messages sent from the system. When passed with no parameters, every message is yielded to the block. command & object can both be strings or regexp's. matcher is a block that gets yielded the received message.
44
+
45
+ conn.subscribe(command: "event", object: /studio.line#\d+/) do |mess|
46
+ puts "Someone did smth: #{mess.arguments[:state]}"
47
+ end
48
+
49
+ == Contributing
50
+
51
+ Contributions are welcomed. Feel free to submit a pull request to fix bugs or add additional functionality to this script.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'rspec/core/rake_task'
2
+ require "bundler/gem_tasks"
3
+
4
+ RSpec::Core::RakeTask.new('spec')
5
+
6
+ task default: :spec
@@ -0,0 +1,122 @@
1
+ module TelosLWCP
2
+ class Command
3
+ class Outgoing
4
+ def initialize(command, object, arguments)
5
+ @command = command
6
+ @object = object
7
+ @arguments = arguments
8
+ end
9
+
10
+ def to_s
11
+ "#{@command} #{@object} #{arguments_str}\n"
12
+ end
13
+
14
+ def arguments_str
15
+ @arguments.map do |argument, value|
16
+ rv = case value
17
+ when TrueClass
18
+ 'TRUE'
19
+ when FalseClass
20
+ 'FALSE'
21
+ when Integer
22
+ value
23
+ when String
24
+ "\"#{value}\""
25
+ end
26
+ "#{argument}=#{rv}"
27
+ end.join(', ')
28
+ end
29
+ end
30
+
31
+ class Incoming
32
+ attr_accessor :command, :object, :arguments, :system_items
33
+
34
+ def initialize(string)
35
+ parts = string.match(/\A(?<command>\S+) (?<object>\S+) (?<arguments>.*)\Z/)
36
+ self.command = parts[:command]
37
+ self.object = parts[:object]
38
+ self.arguments = {}
39
+ self.system_items = {}
40
+
41
+ parse_extra_arguments parts[:arguments]
42
+ end
43
+
44
+ private
45
+ def parse_extra_arguments(raw_string)
46
+ buffer = ""
47
+ in_string = false
48
+ key = nil
49
+ array = nil
50
+ array_stack = []
51
+
52
+ # Translate stuff like
53
+ (raw_string + ' ').each_char do |char|
54
+ case char
55
+ when '=' # Assigning something, so everything before this is the key
56
+ key = buffer
57
+ buffer = ""
58
+ when '"' # Toggle in-string status
59
+ in_string = !in_string
60
+ buffer << char
61
+ when /\[/
62
+ arr = []
63
+ array_stack.any? ? array_stack.last << arr : array = arr
64
+ array_stack << arr
65
+ when /\]/
66
+ if !buffer.empty?
67
+ array_stack.last << raw_string_to_type(buffer)
68
+ buffer = ""
69
+ end
70
+ array_stack.pop
71
+ arguments[key.intern] = array if array_stack.empty?
72
+ when /[, ]/
73
+ if in_string
74
+ buffer << char
75
+ else
76
+ if key.nil? || buffer == ""
77
+ next
78
+ elsif array_stack.any?
79
+ array_stack.last << raw_string_to_type(buffer)
80
+ buffer = ""
81
+ next
82
+ elsif key =~ /^\$/
83
+ system_items[key.tr('$', '').intern] = raw_string_to_type(buffer)
84
+ else
85
+ arguments[key.intern] = raw_string_to_type(buffer)
86
+ end
87
+ buffer = ""
88
+ key = nil
89
+ end
90
+ else
91
+ buffer << char
92
+ end
93
+ end
94
+ end
95
+
96
+ def raw_string_to_type(raw)
97
+ case raw
98
+ when /^"(.*)"$/
99
+ $1
100
+ when /^\d+$/
101
+ raw.to_i
102
+ when /^TRUE$/
103
+ true
104
+ when /^FALSE$/
105
+ false
106
+ else
107
+ raw.intern
108
+ end
109
+ end
110
+ end
111
+
112
+ class << self
113
+ def outgoing(command, object, arguments)
114
+ Outgoing.new(command, object, arguments)
115
+ end
116
+
117
+ def incoming(string)
118
+ Incoming.new(string)
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,108 @@
1
+ require 'socket'
2
+
3
+ module TelosLWCP
4
+ class Error < StandardError; end
5
+
6
+ class Engine
7
+ attr_accessor :host, :port
8
+
9
+ def initialize host:, port: 20518
10
+ self.host = host
11
+ self.port = port
12
+
13
+ @listeners = Hash.new {|h, k| h[k] = {}}
14
+ @subscriptions = []
15
+ end
16
+
17
+ def connect
18
+ @server = TCPSocket.new host, port
19
+ start_reader_thread
20
+ if block_given?
21
+ yield self
22
+ disconnect
23
+ end
24
+ end
25
+
26
+ def disconnect
27
+ @reader_thread.terminate if @reader_thread
28
+ @server.close
29
+ end
30
+
31
+ def login(user, password)
32
+ wait(
33
+ [:ack, :cc] => proc {|r| r.arguments[:logged] }
34
+ ) do
35
+ write :login, :cc, user: user, password: password
36
+ end
37
+ end
38
+
39
+ def select_studio(id)
40
+ wait(
41
+ [:event, :studio] => proc {|r| r },
42
+ [:ack, :studio] => proc {|r| Error.new(r.system_items[:msg]) }
43
+ ) do
44
+ write :select, :studio, id: id
45
+ end
46
+ end
47
+
48
+ def seize(line_nr)
49
+ wait(
50
+ [:event, "studio.line##{line_nr}"] => proc { true },
51
+ [:ack, "studio.line##{line_nr}"] => proc {|r| Error.new(r.system_items[:msg]) }
52
+ ) do
53
+ write :seize, "studio.line##{line_nr}"
54
+ end
55
+ end
56
+
57
+ def call_number(line_nr, number)
58
+ wait(
59
+ [:event, "studio.line##{line_nr}"] => proc { true }
60
+ ) do
61
+ write :call, "studio.line##{line_nr}", number: number
62
+ end
63
+ end
64
+
65
+ def subscribe(command: nil, object: nil, matcher: nil, &block)
66
+ @subscriptions << Subscription.new(command: command, object: object, matcher: matcher, block: block)
67
+ end
68
+
69
+ # Low level
70
+ def write(command, object, arguments = {})
71
+ cmd = Command.outgoing(command, object, arguments)
72
+ @server.write(cmd.to_s)
73
+ end
74
+
75
+ def wait(expectations, read_timeout: 5, &block)
76
+ data = nil
77
+ thread = Thread.current
78
+ expectations.each do |(cmd, object), proc|
79
+ @listeners[cmd.to_s][object.to_s] = proc {|d|
80
+ data = proc.call(d);
81
+ expectations.each {|(cmd, object), _| @listeners[cmd.to_s][object.to_s] = nil }
82
+ thread.run
83
+ }
84
+ end
85
+ block.call
86
+ Timeout.timeout(read_timeout) do
87
+ Thread.stop
88
+ end
89
+ Error === data ? raise(data) : data
90
+ end
91
+
92
+ private
93
+ def start_reader_thread
94
+ Thread.abort_on_exception = true
95
+ @reader_thread = Thread.start do
96
+ loop do
97
+ raw = @server.gets.chomp
98
+ value = Command.incoming(raw)
99
+ if listener = @listeners[value.command][value.object]
100
+ listener.call(value)
101
+ end
102
+
103
+ @subscriptions.select {|sub| sub.match?(value) }.each {|sub| sub.call(value) }
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,22 @@
1
+ module TelosLWCP
2
+ class Subscription
3
+ attr_accessor :command, :object, :matcher, :block
4
+
5
+ def initialize(command:, object:, matcher:, block:)
6
+ self.command = Regexp === command ? command : /\A#{command}\Z/ if command
7
+ self.object = Regexp === object ? object : /\A#{object}\Z/ if object
8
+ self.matcher = matcher
9
+ self.block = block
10
+ end
11
+
12
+ def match?(cmd)
13
+ (command.nil? || command =~ cmd.command) &&
14
+ (object.nil? || object =~ cmd.object) &&
15
+ (matcher.nil? || matcher.call(cmd))
16
+ end
17
+
18
+ def call(cmd)
19
+ block.call(cmd)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module TelosLWCP
2
+ VERSION = "0.1.0"
3
+ end
data/lib/telos_lwcp.rb ADDED
@@ -0,0 +1,5 @@
1
+ module TelosLWCP
2
+ autoload :Engine, "telos_lwcp/engine"
3
+ autoload :Command, "telos_lwcp/command"
4
+ autoload :Subscription, "telos_lwcp/subscription"
5
+ end
@@ -0,0 +1,4 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require 'telos_lwcp'
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe TelosLWCP::Command do
4
+ describe "#new" do
5
+ context "simple initial server response" do
6
+ subject { TelosLWCP::Command.incoming('indi cc server_id="VX Engine", server_version="1.1.4", server_caps="brhi", lwcp_version=1, zone=EU, uid="5850AB402297", online=TRUE')}
7
+ it("parses the command") { expect(subject.command).to eq 'indi' }
8
+ it("parses the object" ) { expect(subject.object).to eq 'cc' }
9
+ it("handles strings in arguments") { expect(subject.arguments[:server_id]).to eq 'VX Engine'}
10
+ it("handles integers in arguments") { expect(subject.arguments[:lwcp_version]).to eq 1}
11
+ it("handles booleans in arguments") { expect(subject.arguments[:online]).to eq true }
12
+ it("handles constants in arguments") { expect(subject.arguments[:zone]).to eq :EU}
13
+ end
14
+
15
+ context "server response with extra data" do
16
+ subject { TelosLWCP::Command.incoming('ack studio $status=ERR $msg="User is not logged in."') }
17
+
18
+ it("parses constants in system items") { expect(subject.system_items[:status]).to eq :ERR }
19
+ it("parses strings in system items") { expect(subject.system_items[:msg]).to eq "User is not logged in." }
20
+ end
21
+
22
+ context "server response with arrays" do
23
+ subject { TelosLWCP::Command.incoming('event studio id=1, name="Studio 1", show_id=1, show_name="Show 1", next=0, pnext=0, busy_all=FALSE, num_lines=12, num_hybrids=8, num_hyb_fixed=4, mute=FALSE, show_locked=FALSE, auto_answer=FALSE, line_list=[[IDLE, IDLE, "Main-Studio", "10", NULL, 0, NULL, "", NONE], [IDLE, IDLE, "Main-Studio", "10", NULL, 0, NULL, "", NONE], [IDLE, IDLE, "Main-Studio", "10", NULL, 0, NULL, "", NONE], [IDLE, IDLE, "Line 20", "20", NULL, 0, NULL, "", NONE], [IDLE, IDLE, "67-614-448", "67614448", NULL, 0, NULL, "", NONE], [IDLE, IDLE, "67-614-449", "67614449", NULL, 0, NULL, "", NONE], [IDLE, IDLE, "Cisco 40", "40", NULL, 0, NULL, "", NONE], [IDLE, IDLE, "Cisco 80", "80", NULL, 0, NULL, "", NONE], NULL, [IDLE, IDLE, "VIP", "88", NULL, 1, NULL, "", NONE], [IDLE, IDLE, "NEWS", "89", NULL, 2, NULL, "", NONE], [IDLE, IDLE, "HOT-LINE", "90", NULL, 3, NULL, "", NONE]]') }
24
+
25
+ it { expect(subject.arguments[:line_list]).to be_an_instance_of Array }
26
+ it { expect(subject.arguments[:line_list].size).to eq 12 }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'telos_lwcp/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "telos_lwcp"
8
+ spec.version = TelosLWCP::VERSION
9
+ spec.authors = ["Jan De Poorter"]
10
+ spec.email = ["jan.depoorter@medialaan.be"]
11
+ spec.summary = %q{Library to communicate with Telos VC devices}
12
+ spec.description = %q{A library to communicate with Telos VX devices that support the LWCP protocol}
13
+ spec.homepage = "http://github.com/q-music/ruby-telos-lwcp"
14
+ spec.license = "Apache-2.0"
15
+
16
+ spec.required_ruby_version = '>= 2.0.0'
17
+
18
+ spec.files = `git ls-files -z`.split("\x0")
19
+ # spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.7"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: telos_lwcp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jan De Poorter
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-12-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: A library to communicate with Telos VX devices that support the LWCP
42
+ protocol
43
+ email:
44
+ - jan.depoorter@medialaan.be
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".rspec"
50
+ - Gemfile
51
+ - Gemfile.lock
52
+ - README.rdoc
53
+ - Rakefile
54
+ - lib/telos_lwcp.rb
55
+ - lib/telos_lwcp/command.rb
56
+ - lib/telos_lwcp/engine.rb
57
+ - lib/telos_lwcp/subscription.rb
58
+ - lib/telos_lwcp/version.rb
59
+ - spec/spec_helper.rb
60
+ - spec/telos_lwcp/command_spec.rb
61
+ - telos_lwcp.gemspec
62
+ homepage: http://github.com/q-music/ruby-telos-lwcp
63
+ licenses:
64
+ - Apache-2.0
65
+ metadata: {}
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 2.0.0
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project:
82
+ rubygems_version: 2.4.5
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: Library to communicate with Telos VC devices
86
+ test_files:
87
+ - spec/spec_helper.rb
88
+ - spec/telos_lwcp/command_spec.rb