libowl 1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,220 @@
1
+ #This class abstracts the details of connecting to a
2
+ #GRAIL3 world model as a solver.
3
+
4
+ require 'socket'
5
+ require 'libowl/buffer_manip.rb'
6
+ require 'libowl/wm_data.rb'
7
+ require 'libowl/transient_request.rb'
8
+
9
+ class SolverWorldModel
10
+ #TODO Move constants to their own file
11
+
12
+ #Message constants
13
+ KEEP_ALIVE = 0;
14
+ TYPE_ANNOUNCE = 1;
15
+ START_TRANSIENT = 2;
16
+ STOP_TRANSIENT = 3;
17
+ SOLVER_DATA = 4;
18
+ CREATE_URI = 5;
19
+ EXPIRE_URI = 6;
20
+ DELETE_URI = 7;
21
+ EXPIRE_ATTRIBUTE = 8;
22
+ DELETE_ATTRIBUTE = 9;
23
+
24
+ attr_accessor :name_to_alias, :alias_to_name, :connected
25
+ @name_to_alias
26
+ @alias_to_name
27
+
28
+ #The origin string of this solver
29
+ @origin
30
+
31
+ #Callback for when the world model requests transient data
32
+ @start_transient_callback
33
+ #Callback for when the world model no longer wants a transient type
34
+ @stop_transient_callback
35
+
36
+ #Handle a message of currently unknown type
37
+ def handleMessage()
38
+ #Get the message length as n unsigned integer
39
+ inlen = (@socket.recvfrom(4)[0]).unpack('N')[0]
40
+ inbuff = @socket.recvfrom(inlen)[0]
41
+ #Byte that indicates message type
42
+ control = inbuff.unpack('C')[0]
43
+ if control == START_TRANSIENT
44
+ if (@start_transient_callback != nil)
45
+ @start_transient_callback.call(decodeStartTransient(inbuff[1, inbuff.length - 1]))
46
+ end
47
+ elsif control == STOP_TRANSIENT
48
+ if (@stop_transient_callback != nil)
49
+ @stop_transient_callback.call(decodeStopTransient(inbuff[1, inbuff.length - 1]))
50
+ end
51
+ end
52
+ return control
53
+ end
54
+
55
+ #Decode a start transient message
56
+ def decodeStartTransient(inbuff)
57
+ num_aliases = inbuff.unpack('N')[0]
58
+ rest = inbuff[4, inbuff.length - 1]
59
+ new_trans_requests = []
60
+ for i in 1..num_aliases do
61
+ type_alias = rest.unpack('N')[0]
62
+ total_expressions = rest.unpack('N')[0]
63
+ t_request = TransientRequest.new(type_alias, [])
64
+ for j in 1..total_expressions do
65
+ exp, rest = splitURIFromRest(rest[4, rest.length - 1])
66
+ t_request.expressions.push(exp)
67
+ end
68
+ new_trans_requests.push(t_request)
69
+ end
70
+ return new_trans_requests
71
+ end
72
+
73
+ #Decode a stop transient message
74
+ def decodeStopTransient(inbuff)
75
+ num_aliases = inbuff.unpack('N')[0]
76
+ rest = inbuff[4, inbuff.length - 1]
77
+ new_trans_requests = []
78
+ for i in 1..num_aliases do
79
+ type_alias = rest.unpack('N')[0]
80
+ total_expressions = rest.unpack('N')[0]
81
+ t_request = TransientRequest.new(type_alias, [])
82
+ for j in 1..total_expressions do
83
+ exp, rest = splitURIFromRest(rest[4, rest.length - 1])
84
+ t_request.expressions.push(exp)
85
+ end
86
+ new_trans_requests.push(t_request)
87
+ end
88
+ return new_trans_requests
89
+ end
90
+
91
+ def initialize(host, port, origin, start_transient_callback = nil, stop_transient_callback = nil)
92
+ @origin = origin
93
+ @connected = false
94
+ @host = host
95
+ @port = port
96
+ @socket = TCPSocket.open(host, port)
97
+ handshake = ""
98
+ ver_string = "GRAIL world model protocol"
99
+ #The handshake is the length of the message, the protocol string, and the version (0).
100
+ handshake << [ver_string.length].pack('N') << ver_string << "\x00\x00"
101
+ #Receive a handshake and then send one
102
+ @socket.send(handshake, 0)
103
+ inshake = @socket.recvfrom(handshake.length)[0]
104
+ while (inshake.length < handshake.length)
105
+ puts "Waiting for #{handshake.length - inshake.length} byte more of handshake."
106
+ inshake += @socket.recvfrom(handshake.length - inshake.length)[0]
107
+ end
108
+
109
+ @connected = true
110
+ for i in 1..handshake.length
111
+ if handshake[i] != inshake[i]
112
+ puts "Handshake failure!"
113
+ puts "For byte i we sent #{handshake[i]} but got #{inshake[i]}"
114
+ @connected = false
115
+ end
116
+ end
117
+
118
+ @name_to_alias = {}
119
+ @alias_to_name = {}
120
+
121
+ @start_transient_callback = start_transient_callback
122
+ @stop_transient_callback = stop_transient_callback
123
+ end
124
+
125
+ #Add some SolutionType objects to the known list
126
+ def addSolutionTypes(attributes)
127
+ new_aliases = []
128
+ attributes.each { |attr|
129
+ if (@name_to_alias[attr.name] == nil)
130
+ new_alias = @name_to_alias.length
131
+ @name_to_alias[attr.name] = new_alias
132
+ @alias_to_name[new_alias] = attr.name
133
+ new_aliases.push([attr.name, new_alias])
134
+ end
135
+ }
136
+ if (new_aliases.length > 0)
137
+ makeTypeAnnounce(new_aliases)
138
+ end
139
+ end
140
+
141
+ def makeTypeAnnounce(type_pairs)
142
+ buff = [TYPE_ANNOUNCE].pack('C') + [type_pairs.length].pack('N')
143
+ for pair in type_pairs do
144
+ #TODO Not supporting transient types for now so always write a zero
145
+ #for the transient on/off byte
146
+ buff += [pair[1]].pack('N') + strToSizedUTF16(pair[0]) + [0].pack('C')
147
+ end
148
+ #Add the origin string to the end of the message
149
+ buff += strToUnicode(@origin)
150
+ @socket.send("#{[buff.length].pack('N')}#{buff}", 0)
151
+ end
152
+
153
+ ##
154
+ #Push URI attributes, automatically declaring new solution types
155
+ #as non-stremaing types if they were not previously declared.
156
+ def pushData(wmdata_vector, create_uris = false)
157
+ buff = [SOLVER_DATA].pack('C')
158
+ if (create_uris)
159
+ buff += [1].pack('C')
160
+ else
161
+ buff += [0].pack('C')
162
+ end
163
+
164
+ #Push back the total number of solutions
165
+ total_solns = wmdata_vector.inject(0){|sum, wmdata|
166
+ sum + wmdata.attributes.length
167
+ }
168
+ buff += [total_solns].pack('N')
169
+
170
+ #Now create each solution and push it back into the buffer
171
+ wmdata_vector.each{|wmdata|
172
+ #First make sure all of the solutions types have been declared
173
+ addSolutionTypes(wmdata.attributes)
174
+ #Now push back this attribute's data using an alias for the name
175
+ wmdata.attributes.each{|attr|
176
+ buff += [@name_to_alias[attr.name]].pack('N') +
177
+ packuint64(attr.creation) +
178
+ strToSizedUTF16(wmdata.uri) + [attr.data.length].pack('N') + attr.data
179
+ }
180
+ }
181
+ #Send the message with its length prepended to the front
182
+ @socket.send("#{[buff.length].pack('N')}#{buff}", 0)
183
+ end
184
+
185
+ ##
186
+ #Create an object with the given name in the world model.
187
+ def createURI(uri, creation_time)
188
+ buff = [CREATE_URI].pack('C')
189
+ buff += strToSizedUTF16(uri)
190
+ buff += packuint64(creation_time)
191
+ buff += strToUnicode(@origin)
192
+ #Send the message with its length prepended to the front
193
+ @socket.send("#{[buff.length].pack('N')}#{buff}", 0)
194
+ end
195
+
196
+ ##
197
+ #Expire the object with the given name in the world model, indicating that it
198
+ #is no longer valid after the given time.
199
+ def expireURI(uri, expiration_time)
200
+ buff = [EXPIRE_URI].pack('C')
201
+ buff += strToSizedUTF16(uri)
202
+ buff += packuint64(expiration_time)
203
+ buff += strToUnicode(@origin)
204
+ #Send the message with its length prepended to the front
205
+ @socket.send("#{[buff.length].pack('N')}#{buff}", 0)
206
+ end
207
+
208
+ ##
209
+ #Delete an object in the world model.
210
+ def deleteURI(uri)
211
+ buff = [DELETE_URI].pack('C')
212
+ buff += strToSizedUTF16(uri)
213
+ buff += strToUnicode(@origin)
214
+ #Send the message with its length prepended to the front
215
+ @socket.send("#{[buff.length].pack('N')}#{buff}", 0)
216
+ end
217
+
218
+ #TODO Expire a URI's attribute
219
+ #TODO Delete a URI's attribute
220
+ end
@@ -0,0 +1,80 @@
1
+ ################################################################################
2
+ #This file defines the StepResponse class, an object that represents data from
3
+ #an owl world model that is sent for a streaming request. An instance of this
4
+ #class will continually yield new data until the streaming request is cancelled.
5
+ #
6
+ # Copyright (c) 2013 Bernhard Firner
7
+ # All rights reserved.
8
+ #
9
+ # This program is free software; you can redistribute it and/or
10
+ # modify it under the terms of the GNU General Public License
11
+ # as published by the Free Software Foundation; either version 2
12
+ # of the License, or (at your option) any later version.
13
+ #
14
+ # This program is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with this program; if not, write to the Free Software
21
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22
+ # or visit http://www.gnu.org/licenses/gpl-2.0.html
23
+ #
24
+ ################################################################################
25
+
26
+ #Incrementeal response of a client streaming request to the world model
27
+ class StepResponse
28
+ ##
29
+ #Initialize with the ClientWorldConnection that spawed this Response and
30
+ #the key of the request.
31
+ def initialize(cwc, key)
32
+ @cwc = cwc
33
+ @request_key = key
34
+ end
35
+
36
+ ##
37
+ #Get the data of this StepResponse, blocking until that data is ready or
38
+ #an error occurs.
39
+ def next()
40
+ while (not (hasNext() or isError()))
41
+ sleep(1)
42
+ end
43
+ if (isError())
44
+ raise getError()
45
+ else
46
+ return @cwc.getNext(@request_key)
47
+ end
48
+ end
49
+
50
+ ##
51
+ #Returns true if data is available for a call to next().
52
+ def hasNext()
53
+ return @cwc.hasNext(@request_key)
54
+ end
55
+
56
+ ##
57
+ #Returns true if an error has occured.
58
+ def isError()
59
+ return @cwc.hasError(@request_key)
60
+ end
61
+
62
+ ##
63
+ #Get the error that occured.
64
+ def getError()
65
+ return @cwc.getError(@request_key)
66
+ end
67
+
68
+ ##
69
+ #True if this streaming request will have no more data.
70
+ def isComplete()
71
+ return @cwc.isComplete(@request_key)
72
+ end
73
+
74
+ ##
75
+ #Cancel the streaming request.
76
+ def cancel()
77
+ return @cwc.cancelRequest(@request_key)
78
+ end
79
+ end
80
+
@@ -0,0 +1,14 @@
1
+ #Transient specification - one attribute name an a list of name expressions
2
+
3
+ class TransientRequest
4
+ #Attribute name
5
+ @name
6
+ #Requested expressions
7
+ @expressions
8
+
9
+ def initialize(name, expressions)
10
+ @name = name
11
+ @expressions = expressions
12
+ end
13
+ end
14
+
@@ -0,0 +1,61 @@
1
+ #Solution types used when communicating with the world model
2
+
3
+ ##
4
+ #Function to fetch the current time in milliseconds. This is the time format
5
+ #used in the Owl system and is included in every attribute pushed into the
6
+ #world model and every attribute retrieved from the world model.
7
+ def getMsecTime()
8
+ t = Time.now
9
+ return t.tv_sec * 1000 + t.usec/10**3
10
+ end
11
+
12
+ class WMAttribute
13
+ attr_accessor :name, :data, :creation, :expiration, :origin
14
+ #Name of this attribute
15
+ @name
16
+ #Binary buffer of attribute data
17
+ @data
18
+ #The creation and expiration dates of this attribute
19
+ #in milliseconds since midnight Jan 1, 1970
20
+ @creation
21
+ @expiration
22
+ #The origin of this data
23
+ @origin
24
+
25
+ def initialize(name, data, creation, expiration = 0, origin = "empty")
26
+ @name = name
27
+ @data = data
28
+ @creation = creation
29
+ @expiration = expiration
30
+ @origin = origin
31
+ end
32
+
33
+ def to_s()
34
+ return "\t#{@name}, #{@creation}, #{@expiration}, #{@origin}: #{@data.unpack('H*')}\n"
35
+ end
36
+ end
37
+
38
+ class WMData
39
+ attr_accessor :uri, :attributes, :ticket
40
+ #The object that this data modifies
41
+ @uri
42
+ #The attributes of the object
43
+ @attributes
44
+ #The request ticket that this data is associated with
45
+ @ticket
46
+
47
+ def initialize(uri, attributes, ticket = 0)
48
+ @uri = uri
49
+ @attributes = attributes
50
+ @ticket = ticket
51
+ end
52
+
53
+ def to_s()
54
+ str = "#{@uri}:\n"
55
+ for attr in @attributes do
56
+ str += "\t#{attr.name}, #{attr.creation}, #{attr.expiration}, #{attr.origin}: #{attr.data.unpack('H*')}\n"
57
+ end
58
+ return str
59
+ end
60
+ end
61
+
data/lib/libowl.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'libowl/solver_aggregator'
2
+ require 'libowl/client_world_connection'
3
+ require 'libowl/solver_world_model'
4
+ require 'libowl/wm_data'
5
+ require 'libowl/buffer_manip'
6
+
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: libowl
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.2'
5
+ platform: ruby
6
+ authors:
7
+ - Bernhard Firner
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-08-20 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: The libowl network protocols are the protocols used to interact with
14
+ the Owl platform or any other system that uses the GRAIL network protocols. Go to
15
+ www.owlplatform.com and github.com/OwlPlatform for more information.
16
+ email: bfirner@eden.rutgers.edu
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - README.md
22
+ - LICENSE
23
+ - lib/libowl.rb
24
+ - lib/libowl/solution_types.rb
25
+ - lib/libowl/transient_request.rb
26
+ - lib/libowl/aggregator_rules.rb
27
+ - lib/libowl/client_world_connection.rb
28
+ - lib/libowl/client_world_model.rb
29
+ - lib/libowl/solver_world_model.rb
30
+ - lib/libowl/buffer_manip.rb
31
+ - lib/libowl/response.rb
32
+ - lib/libowl/step_response.rb
33
+ - lib/libowl/message_constants.rb
34
+ - lib/libowl/wm_data.rb
35
+ - lib/libowl/sensor_sample.rb
36
+ - lib/libowl/solver_aggregator.rb
37
+ homepage: https://github.com/OwlPlatform/libruby
38
+ licenses:
39
+ - LGPL-2.1
40
+ metadata: {}
41
+ post_install_message:
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ requirements: []
56
+ rubyforge_project:
57
+ rubygems_version: 2.0.7
58
+ signing_key:
59
+ specification_version: 3
60
+ summary: Protocols for interacting with the owl platform.
61
+ test_files: []