katcp 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,110 @@
1
+ --
2
+
3
+ This file includes an RDoc hack so that links can be made to open up into a
4
+ new frame rather then the current frame.
5
+
6
+ The "trick" is to append ``"target="_top'' to the URL.
7
+
8
+ ++
9
+
10
+ = Ruby/KATCP
11
+
12
+ == Introduction
13
+
14
+ <b>Ruby/KATCP</b> is a Ruby extension for communicating via the Karoo Array
15
+ Telescope Control Protocol (KATCP). More information about KATCP can be
16
+ found at the {CASPER
17
+ Wiki}[http://casper.berkeley.edu/wiki/KATCP"target="_top], including the {KATCP
18
+ specification
19
+ }[http://casper.berkeley.edu/wiki/images/1/11/NRF-KAT7-6.0-IFCE-002-Rev4.pdf"target="_top]
20
+ in PDF format.
21
+
22
+ == Features
23
+
24
+ * Provides a base KATCP client supporting the functionality described in the
25
+ KATCP specification.
26
+
27
+ * Provides an extended KATCP client to support the <tt>tcpborphserver2</tt>
28
+ KATCP server implementation on {ROACH
29
+ }[http://casper.berkeley.edu/wiki/ROACH"target="_top] boards.
30
+
31
+ * Automatically provides support for all current and any future KATCP request
32
+ types through the use of Ruby's +method_missing+ feature.
33
+
34
+ * Handles all escaping and unescaping of text transferred over KATCP.
35
+
36
+ * Provides convenient and flexible ways of converting packed binary data to
37
+ various numerical formats, including Array and {NArray
38
+ }[http://narray.rubyforge.org/"target="_top] objects.
39
+
40
+ * Plays nicely with +irb+, the interactive Ruby shell.
41
+
42
+ == Installation
43
+
44
+ {Ruby/KATCP}[http://rb-katcp.rubyforge.org/"target="_top] can be installed
45
+ via RubyGems[http://www.rubygems.org/"target="_top]:
46
+
47
+ gem install katcp
48
+
49
+ Ruby/KAT<b></b>CP was developed using Ruby 1.8.6 and 1.8.7. It has not been
50
+ tested against Ruby 1.9 (yet).
51
+
52
+ == Documentation
53
+
54
+ Documentation is available on the {Ruby/KATCP
55
+ Homepage}[http://rb-katcp.rubyforge.org/"target="_top] and is also installed
56
+ locally as part of the gem-based installation. To view the documentation
57
+ locally, run
58
+
59
+ gem server
60
+
61
+ then point your browser to <tt>http</tt><tt>://localhost:8808/</tt> and
62
+ navigate to (and follow) the katcp <b>[rdoc]</b> link. You can get more
63
+ information about RubyGem's documenation server by running:
64
+
65
+ gem help server
66
+
67
+ == Example
68
+
69
+ This example script, <tt>listdev.rb</tt>, can be used to list the devices on a
70
+ given ROACH host...
71
+
72
+ #!/usr/bin/env ruby
73
+
74
+ # listdev.rb - list devices on a ROACH
75
+ #
76
+ # $ listdev.rb roach030111
77
+ # XAUI0
78
+ # acc_len
79
+ # ant_levels
80
+ # ctrl
81
+ # gbe0
82
+ # gbe_ip0
83
+ # gbe_port
84
+ # [...]
85
+
86
+ require 'rubygems'
87
+ require 'katcp'
88
+
89
+ # Abort if no hostname is given
90
+ raise "\nusage: #{$0} HOSTNAME" unless ARGV[0]
91
+
92
+ # Create RoachClient object
93
+ roach = KATCP::RoachClient.new(ARGV[0])
94
+
95
+ # Get list of devices via "?listdev" KATCP request.
96
+ # Save returned KATCP::Response object in "resp".
97
+ resp = roach.listdev
98
+
99
+ # Extract first "word" of each line except the last
100
+ # one, which will be a "!listdev ok" reply line.
101
+ devices = resp.lines[0..-2].map {|l| l[1]}
102
+
103
+ # Output device list
104
+ puts devices
105
+
106
+ == Current Limitations
107
+
108
+ * Only the client side is implemented.
109
+ * Only TCP communication is supported (i.e. no RS/232 support).
110
+ * KATCP::RoachClient#uploadbof is not yet implemented.
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # listdev.rb - list devices on a ROACH
4
+ #
5
+ # $ listdev.rb roach030111
6
+ # XAUI0
7
+ # acc_len
8
+ # ant_levels
9
+ # ctrl
10
+ # gbe0
11
+ # gbe_ip0
12
+ # gbe_port
13
+ # [...]
14
+
15
+ require 'rubygems'
16
+ require 'katcp'
17
+
18
+ # Abort if no hostname is given
19
+ raise "\nusage: #{$0} HOSTNAME" unless ARGV[0]
20
+
21
+ # Create RoachClient object
22
+ roach = KATCP::RoachClient.new(ARGV[0])
23
+
24
+ # Get list of devices via "?listdev" KATCP request.
25
+ # Save returned KATCP::Response object in "resp".
26
+ resp = roach.listdev
27
+
28
+ # Extract first "word" of each line except the last
29
+ # one, which will be a "!listdev ok" reply line.
30
+ devices = resp.lines[0..-2].map {|l| l[1]}
31
+
32
+ # Output device list
33
+ puts devices
@@ -0,0 +1,141 @@
1
+ require 'katcp/client'
2
+
3
+ # Holds KATCP related classes etc.
4
+ module KATCP
5
+
6
+ # Facilitates talking to <tt>tcpborphserver2</tt>, a KATCP server
7
+ # implementation that runs on ROACH boards. Mostly just adds convenience
8
+ # methods for tab completion in irb.
9
+ #
10
+ # TODO: Add conveneince methods for converting binary payload data.
11
+ class RoachClient < Client
12
+
13
+ # call-seq:
14
+ # delbof(image_file) -> KATCP::Response
15
+ #
16
+ # Deletes gateware image file named by +image_file+.
17
+ def delbof(image_file)
18
+ request(:delbof, image_file)
19
+ end
20
+
21
+ # call-seq:
22
+ # echotest(ip_address, echo_port, byte_count) -> KATCP::Response
23
+ #
24
+ # Basic network echo tester.
25
+ def echotest(ip_address, echo_port, byte_count)
26
+ request(:echotest, ip_address, echo_port, byte_count)
27
+ end
28
+
29
+ # call-seq:
30
+ # listbof -> KATCP::Response
31
+ #
32
+ # Lists available gateware images.
33
+ def listbof
34
+ request(:listbof).sort!
35
+ end
36
+
37
+ # call-seq:
38
+ # listdev -> KATCP::Response
39
+ #
40
+ # Lists available registers.
41
+ def listdev
42
+ request(:listdev, :size).sort!
43
+ end
44
+
45
+ # call-seq:
46
+ # progdev -> KATCP::Response
47
+ # progdev(image_file) -> KATCP::Response
48
+ #
49
+ # Programs a gateware image specified by +image_file+. If +image_file+ is
50
+ # omitted, de-programs the FPGA.
51
+ def progdev(*args)
52
+ request(:progdev, *args)
53
+ end
54
+
55
+ # call-seq:
56
+ # read(register_name) -> KATCP::Response
57
+ # read(register_name, register_offset) -> KATCP::Response
58
+ # read(register_name, register_offset, byte_count) -> KATCP::Response
59
+ #
60
+ # Reads a +byte_count+ bytes starting at +register_offset+ offset from
61
+ # register (or block RAM) named by +register_name+. Binary data can be
62
+ # obtained from the Response via the Response#payload method.
63
+ def read(register_name, *args)
64
+ request(:read, register_name, *args)
65
+ end
66
+
67
+ # call-seq:
68
+ # status -> KATCP::Response
69
+ #
70
+ # Reports if gateware has been programmed.
71
+ def status
72
+ request(:status)
73
+ end
74
+
75
+ # call-seq:
76
+ # sysinit -> KATCP::Response
77
+ #
78
+ # Writes the timing ctl register that resets the entire system.
79
+ #
80
+ # [Presumably this depends on a certain register naming convention?]
81
+ def sysinit
82
+ request(:sysinit)
83
+ end
84
+
85
+ # call-seq:
86
+ # tap_start(tap_device register_name, ip_address) -> KATCP::Response
87
+ # tap_start(tap_device register_name, ip_address, port) -> KATCP::Response
88
+ # tap_start(tap_device register_name, ip_address, port, mac) -> KATCP::Response
89
+ #
90
+ # Start a tgtap instance.
91
+ def tap_start(tap_device, register_name, ip_address, *args)
92
+ request(:tap_start, tap_device, register_name, ip_address, *args)
93
+ end
94
+
95
+ # call-seq:
96
+ # tap_stop(register_name) -> KATCP::Response
97
+ #
98
+ # Stop a tgtap instance.
99
+ def tap_stop(register_name)
100
+ reqest(:tap_stop, register_name)
101
+ end
102
+
103
+ # call-seq:
104
+ # uploadbof(net_port, filename) -> KATCP::Response
105
+ # uploadbof(net_port, filename, size) -> KATCP::Response
106
+ #
107
+ # Upload a gateware image.
108
+ #
109
+ # NOT YET IMPLEMENTED
110
+ def uploadbof(net_port, filename, *args)
111
+ raise NotImplementedError.new('uploadbof not yet implemented')
112
+ end
113
+
114
+ # call-seq:
115
+ # wordread(register_name) -> KATCP::Response
116
+ # wordread(register_name, word_offset) -> KATCP::Response
117
+ # wordread(register_name, word_offset, length) -> KATCP::Response
118
+ #
119
+ # Reads word(s) from +register_name+.
120
+ def wordread(register_name, *args)
121
+ request(:wordread, register_name, *args)
122
+ end
123
+
124
+ # call-seq:
125
+ # wordwrite(register_name, word_offset, payload[, ...]) -> KATCP::Response
126
+ #
127
+ # Writes one or more words to a register (or block RAM).
128
+ def wordwrite(register_name, word_offset, *args)
129
+ request(:wordwrite, register_name, word_offset, *args)
130
+ end
131
+
132
+ # call-seq:
133
+ # write(register_name, register_offset, data_payload) -> KATCP::Response
134
+ #
135
+ # Write a given payload to an offset in a register.
136
+ def write(register_name, register_offset, data_payload)
137
+ request(:write, register_name, register_offset, data_payload)
138
+ end
139
+
140
+ end # class RoachClient
141
+ end # module KATCP
@@ -0,0 +1,258 @@
1
+ require 'monitor'
2
+ require 'socket'
3
+
4
+ require 'katcp/response'
5
+ require 'katcp/util'
6
+
7
+ # Holds KATCP related classes etc.
8
+ module KATCP
9
+
10
+ # Facilitates talking to a KATCP server.
11
+ class Client
12
+
13
+ # Creates a KATCP client that connects to a KATCP server at +remote_host+
14
+ # on +remote_port+. If +local_host+ and +local_port+ are specified, then
15
+ # those parameters are used on the local end to establish the connection.
16
+ def initialize(remote_host, remote_port=7147, local_host=nil, local_port=nil)
17
+
18
+ # Save remote_host and remote_port for #inspect
19
+ @remote_host = remote_host
20
+ @remote_port = remote_port
21
+
22
+ # @socket is the socket connecting to the KATCP server
23
+ @socket = TCPSocket.new(remote_host, remote_port, local_host, local_port)
24
+
25
+ # Init attribute(s)
26
+ @informs = []
27
+
28
+ # @reqlock is the Monitor object used to serialize requests sent to the
29
+ # KATCP server. Threads should not write to @socket or read from @rxq
30
+ # or change @reqname unless they have acquired (i.e. synchonized on)
31
+ # @reqlock.
32
+ @reqlock = Monitor.new
33
+
34
+ # @reqname is the request name currently being processed (nil if no
35
+ # current request).
36
+ @reqname = nil
37
+
38
+ # @rxq is an inter-thread queue
39
+ @rxq = Queue.new
40
+
41
+ # Start thread that reads data from server.
42
+ Thread.new do
43
+ # TODO: Monkey-patch gets so that it recognizes "\r" or "\n" as line
44
+ # endings. Currently only recognizes fixed strings, so for now go with
45
+ # "\n".
46
+ while line = @socket.gets("\n") do
47
+ # Split line into words and unescape each word
48
+ words = line.split.map! {|w| w.katcp_unescape!}
49
+ # Handle requests, replies, and informs based on first character of
50
+ # first word.
51
+ case words[0][0,1]
52
+ # Request
53
+ when '?'
54
+ # TODO Send 'unsupported' reply (or support requests from server?)
55
+ # Reply
56
+ when '!'
57
+ # TODO: Raise exception if name is not same as @reqname?
58
+ # TODO: Raise exception on non-ok?
59
+ # Enqueue words to @rxq
60
+ @rxq.enq(words)
61
+ # Inform
62
+ when '#'
63
+ # If the name is same as @reqname
64
+ if @reqname && @reqname == words[0][1..-1]
65
+ # Enqueue words to @rxq
66
+ @rxq.enq(words)
67
+ else
68
+ # Must be asynchronous inform message, add to list.
69
+ line.katcp_unescape!
70
+ line.chomp!
71
+ @informs << line
72
+ end
73
+ else
74
+ # Malformed line
75
+ # TODO: Log error better?
76
+ warn "malformed line: #{line.inspect}"
77
+ end
78
+
79
+ end # @socket.each_line block
80
+
81
+ warn "Read on socket returned EOF"
82
+ #TODO Close socket? Push EOF flag into @rxq?
83
+ end # Thread.new block
84
+ end
85
+
86
+ # TODO: Have a log_handler?
87
+
88
+ # call-seq:
89
+ # request(name, *arguments) -> KATCP::Response
90
+ #
91
+ # Sends request +name+ with +arguments+ to server. Returns KATCP::Response
92
+ # object.
93
+ #
94
+ # TODO: Raise exception if reply is not OK?
95
+ def request(name, *arguments)
96
+ # Massage name to allow Symbols and to allow '_' between words (since
97
+ # that is more natural for Symbols) in place of '-'
98
+ name = name.to_s.gsub('_','-')
99
+
100
+ # Escape arguments
101
+ arguments.map! {|arg| arg.to_s.katcp_escape}
102
+
103
+ # Create response
104
+ resp = Response.new
105
+
106
+ # Get lock on @reqlock
107
+ @reqlock.synchronize do
108
+ # Store request name
109
+ @reqname = name
110
+ # Send request
111
+ req = "?#{[name, *arguments].join(' ')}\n"
112
+ @socket.print req
113
+ # Loop on reply queue until done or error
114
+ begin
115
+ words = @rxq.deq
116
+ resp << words
117
+ end until words[0][0,1] == '!'
118
+ # Clear request name
119
+ @reqname = nil
120
+ end
121
+ resp
122
+ end
123
+
124
+ # call-seq:
125
+ # inform(clear=false) -> Array
126
+ #
127
+ # Returns Array of inform messages. If +clear+ is +true+, clear messages.
128
+ def informs(clear=false)
129
+ msgs = @informs
130
+ @informs = [] if clear
131
+ msgs
132
+ end
133
+
134
+ # Translates calls to missing methods into KATCP requests.
135
+ def method_missing(sym, *args)
136
+ request(sym, *args)
137
+ end
138
+
139
+ # Provides terse string representation of +self+.
140
+ def to_s
141
+ s = "#{@remote_host}:#{@remote_port}"
142
+ end
143
+
144
+ # Provides more detailed String representation of +self+
145
+ def inspect
146
+ "#<#{self.class.name} #{to_s} (#{@informs.length} inform messages)>"
147
+ end
148
+
149
+ # call-seq:
150
+ # client_list -> KATCP::Response
151
+ #
152
+ # Issues a client_list request to the server.
153
+ def client_list
154
+ request(:client_list)
155
+ end
156
+
157
+ # call-seq:
158
+ # configure(*args) -> KATCP::Response
159
+ #
160
+ # Issues a configure request to the server.
161
+ def configure(*args)
162
+ request(:configure, *args)
163
+ end
164
+
165
+ # call-seq:
166
+ # halt -> KATCP::Response
167
+ #
168
+ # Issues a halt request to the server. Shuts down the system.
169
+ def halt
170
+ request(:halt)
171
+ end
172
+
173
+ # call-seq:
174
+ # help -> KATCP::Response
175
+ # help(name) -> KATCP::Response
176
+ #
177
+ # Issues a help request to the server. If +name+ is a Symbol, all '_'
178
+ # characters are changed to '-'. Response inform lines are sorted.
179
+ def help(*args)
180
+ # Change '_' to '-' in Symbol args
181
+ args.map! do |arg|
182
+ arg = arg.to_s.gsub('-', '_') if Symbol === arg
183
+ arg
184
+ end
185
+ request(:help, *args).sort!
186
+ end
187
+
188
+ # call-seq:
189
+ # log_level -> KATCP::Response
190
+ # log_level(priority) -> KATCP::Response
191
+ #
192
+ # Query or set the minimum reported log priority.
193
+ def log_level(*args)
194
+ request(:log_level, *args)
195
+ end
196
+
197
+ # call-seq:
198
+ # mode -> KATCP::Response
199
+ # mode(new_mode) -> KATCP::Response
200
+ #
201
+ # Query or set the current mode.
202
+ def mode(*args)
203
+ request(:mode, *args)
204
+ end
205
+
206
+ # call-seq:
207
+ # restart -> KATCP::Response
208
+ #
209
+ # Issues a restart request to the server to restart the remote system.
210
+ def restart
211
+ request(:restart)
212
+ end
213
+
214
+ # call-seq:
215
+ # sensor_dump(*args) -> KATCP::Response
216
+ #
217
+ # Dumps the sensor tree.
218
+ def sensor_dump(*args)
219
+ request(:sensor_dump, *args)
220
+ end
221
+
222
+ # call-seq:
223
+ # sensor_list(*args) -> KATCP::Response
224
+ #
225
+ # Queries for list of available sensors. Response inform lines are sorted.
226
+ def sensor_list(*args)
227
+ request(:sensor_list, *args).sort!
228
+ end
229
+
230
+ # call-seq:
231
+ # sensor_sampling(sensor) -> KATCP::Response
232
+ # sensor_sampling(sensor, strategy, *parameters) -> KATCP::Response
233
+ #
234
+ # Quesry or set sampling parameters for a sensor.
235
+ def sensor_sampling(sensor, *args)
236
+ request(:sensor_sampling, sensor, *args)
237
+ end
238
+
239
+ # call-seq:
240
+ # sensor_value(sensor) -> KATCP::Response
241
+ #
242
+ # Query a sensor.
243
+ def sensor_value(sensor)
244
+ request(:sensor_value, sensor)
245
+ end
246
+
247
+ # call-seq:
248
+ # watchdog -> KATCP::Response
249
+ #
250
+ # Issues a watchdog request to the server.
251
+ def watchdog
252
+ request(:watchdog)
253
+ end
254
+
255
+ alias :ping :watchdog
256
+
257
+ end # class Client
258
+ end # module KATCP
data/lib/katcp/irb.rb ADDED
@@ -0,0 +1,3 @@
1
+ # Facilitate running katcp in an IRB session
2
+ require 'katcp'
3
+ IRB.conf[:PROMPT_MODE] = :DEFAULT
@@ -0,0 +1,209 @@
1
+ require 'katcp/util'
2
+ require 'katcp/version'
3
+
4
+ # Holds KATCP related classes etc.
5
+ module KATCP
6
+
7
+ # Class that holds response to request. Bascially like an Array with each
8
+ # element representing a line of the server's response. Each "line" is
9
+ # stored as an Array (of Strings) whose elements are the unescaped "words"
10
+ # (which may contain embedded spaces) of the reply.
11
+ class Response
12
+
13
+ # Default inspect_mode for new Response objects. When running in IRB,
14
+ # default is :to_s, otherwise, default is nil.
15
+ @@inspect_mode = (defined?(IRB) ? :to_s : nil)
16
+
17
+ # Returns default inspect mode for new Response objects.
18
+ def self.inspect_mode
19
+ @@inspect_mode
20
+ end
21
+
22
+ # Sets default inspect mode for new Response objects.
23
+ def self.inspect_mode=(mode)
24
+ @@inspect_mode = mode
25
+ end
26
+
27
+ # Inspect mode for +self+. +nil+ or <tt>:inspect</tt> means terse form.
28
+ # Other symbol (typically <tt>:to_s</tt>) means call that method
29
+ attr :inspect_mode
30
+
31
+ # Creates a new Response object with given inspect_mode and lines Array
32
+ def initialize(inspect_mode=@@inspect_mode, lines_array=[])
33
+ raise TypeError.new('lines must be an Array') unless Array === lines_array
34
+ @lines = lines_array
35
+ @inspect_mode = inspect_mode
36
+ end
37
+
38
+ # call-seq:
39
+ # lines -> Array of Arrays
40
+ #
41
+ # Returns a copy contained lines. Each line is itself an Array of words.
42
+ def lines
43
+ @lines.map {|words| words.map {|word| word.dup}}
44
+ end
45
+
46
+ # call-seq:
47
+ # grep(pattern, join=' ') -> Array or Strings
48
+ # grep(pattern, nil) -> Array of Arrays
49
+ # grep(pattern, join=' ') {|line_array| ...}-> Array of Strings
50
+ # grep(pattern, nil) {|line_array| ...}-> Array of ...
51
+ #
52
+ # Greps through lines for +pattern+. If a block is given, each line will
53
+ # be passed to the block and replaced in the returned Array by the block's
54
+ # return value. If +join+ is non-nil (default is a space character), then
55
+ # each element of the returned Array (i.e. each line) will have its
56
+ # elements (i.e. words) joined together using +join+ as a delimiter.
57
+ def grep(pattern, join=' ', &block)
58
+ matches = @lines.find_all {|l| !l.grep(pattern).empty?}
59
+ matches.map!(&block) if block_given?
60
+ matches.map! {|l| l.join(join)} if join
61
+ matches
62
+ end
63
+
64
+ # call-seq:
65
+ # dup -> KATCP::Response
66
+ #
67
+ # Returns a deep copy of self
68
+ def dup
69
+ self.class.new(@inspect_mode, lines)
70
+ end
71
+
72
+ # Pushes +line+ onto +self+. +line+ must be an Array of words (each of
73
+ # which may contain embedded spaces).
74
+ def <<(line)
75
+ raise TypeError.new('expected Array') unless Array === line
76
+ @lines << line
77
+ end
78
+
79
+ # call-seq:
80
+ # length -> Integer
81
+ #
82
+ # Returns number of lines in +self+ including all inform lines and the
83
+ # reply line, if present.
84
+ def length
85
+ @lines.length
86
+ end
87
+
88
+ # call-seq:
89
+ # response[index] -> Array or nil
90
+ # response[start, length] -> Array_of_Arrays or nil
91
+ # response[range] -> Array_of_Arrays or nil
92
+ #
93
+ # Returns subset of lines. Similar to Array#[].
94
+ def [](*args)
95
+ @lines[*args]
96
+ end
97
+
98
+ # call-seq:
99
+ # reqname -> String
100
+ #
101
+ # Returns name of request corresponding to +self+ if complete, otherwise
102
+ # nil.
103
+ def reqname
104
+ # All but first character of first word of last line, if complete
105
+ @lines[-1][0][1..-1] if complete?
106
+ end
107
+
108
+ # call-seq:
109
+ # complete? -> true_or_false
110
+ #
111
+ # Returns true if at least one line exists and the most recently added line
112
+ # is a reply line.
113
+ def complete?
114
+ # If at least one line exists, return true if last line, first word, first
115
+ # character is '!'.
116
+ @lines[-1] && @lines[-1][0][0,1] == '!'
117
+ end
118
+
119
+ # call-seq:
120
+ # status -> String
121
+ #
122
+ # Returns status from reply line if complete, otherwise
123
+ # <tt>'incomplete'</tt>.
124
+ def status
125
+ complete? ? @lines[-1][1] : 'incomplete'
126
+ end
127
+
128
+ # call-seq:
129
+ # ok? -> true_or_false
130
+ #
131
+ # Returns true if status is <tt>'ok'</tt>.
132
+ def ok?
133
+ 'ok' == status
134
+ end
135
+
136
+ # call-seq:
137
+ # sort! -> self
138
+ #
139
+ # Sorts the list of inform lines in-place and returns +self+
140
+ def sort!
141
+ n = complete? ? length-1 : length
142
+ @lines[0,n] = @lines[0,n].sort if n > 0
143
+ self
144
+ end
145
+
146
+ # call-seq:
147
+ # sort -> KATCP::Response
148
+ #
149
+ # Returns a copy of +self+ with inform lines sorted
150
+ def sort
151
+ dup.sort!
152
+ end
153
+
154
+ # call-seq:
155
+ # to_s -> String
156
+ #
157
+ # Rejoins words into lines and lines into one String
158
+ def to_s
159
+ @lines.map do |line|
160
+ line.join(' ')
161
+ end.join("\n")
162
+ end
163
+
164
+ # call-seq:
165
+ # payload -> String or nil
166
+ # payload(:to_i) -> Integer or nil
167
+ # payload(:unpack, 'N*') -> Array_of_Integers or nil
168
+ # payload(:to_na, NArray::INT) -> NArray_of_ints or nil
169
+ # etc...
170
+ #
171
+ # Returns contents of reply line ("words" joined by spaces) after status
172
+ # word if <tt>ok?</tt> returns true. Returns +nil+ if <tt>ok?</tt> is
173
+ # false or no payload exists. If +args+ are given, they are sent to the
174
+ # payload String (via String#send) and the results are returned.
175
+ #
176
+ # For example, passing <tt>:to_i</tt> will result in conversion of payload
177
+ # String to Integer via String#to_i. The String class can be
178
+ # monkey-patched as needed for additional conversion options.
179
+ def payload(*args)
180
+ if ok?
181
+ s = @lines[-1][2..-1].join(' ')
182
+ return s if args.empty?
183
+ s.send(*args)
184
+ end
185
+ end
186
+
187
+ # call-seq:
188
+ # inspect -> String
189
+ # inspect(mode) -> String
190
+ #
191
+ # Provides a terse (or not so terse) summary of +self+ depending on value
192
+ # of +mode+. If given, +mode+ will be treated as inspect_mode.
193
+ def inspect(mode=@inspect_mode)
194
+ if mode && mode != :inspect?
195
+ send(mode) rescue inspect(nil)
196
+ else
197
+ s = "#<KATCP::Response:0x#{object_id.to_s(16)}> "
198
+ if complete?
199
+ s += "#{status}"
200
+ if @lines.length > 1
201
+ s += ", #{length-1} lines"
202
+ end
203
+ else
204
+ s += "#{length} lines, incomplete"
205
+ end
206
+ end
207
+ end
208
+ end # class Response
209
+ end # module KATCP
data/lib/katcp/util.rb ADDED
@@ -0,0 +1,73 @@
1
+ require 'narray'
2
+
3
+ #TODO: Create ntoh! (etc) methods for narray
4
+
5
+ # String add-ons for KATCP
6
+ class String
7
+ # In-place escapes +self+ into KATCP format. Always returns +self+.
8
+ def katcp_escape!
9
+ empty? ? self[0..-1] = '\@' : self.gsub!(/[\\ \0\n\r\e\t]/) do |s|
10
+ case s
11
+ when "\\": '\\'
12
+ when " " : '\_'
13
+ when "\0": '\0'
14
+ when "\n": '\n'
15
+ when "\r": '\r'
16
+ when "\e": '\e'
17
+ when "\t": '\t'
18
+ end
19
+ end
20
+ self
21
+ end
22
+
23
+ # Escapes +self+ into KATCP format and returns new String.
24
+ def katcp_escape
25
+ dup.katcp_escape!
26
+ end
27
+
28
+ # In-place unescapes +self+ from KATCP format. Always returns +self+.
29
+ def katcp_unescape!
30
+ self == '\@' ? self[0..-1] = '' : self.gsub!(/\\[\\_0nret]/) do |s|
31
+ case s
32
+ when '\\': "\\"
33
+ when '\_': " "
34
+ when '\0': "\0"
35
+ when '\n': "\n"
36
+ when '\r': "\r"
37
+ when '\e': "\e"
38
+ when '\t': "\t"
39
+ end
40
+ end
41
+ self
42
+ end
43
+
44
+ # Unescapes +self+ from KATCP format and returns new String.
45
+ def katcp_unescape
46
+ dup.katcp_unescape!
47
+ end
48
+
49
+ # call-seq:
50
+ # to_na(typecode) -> NArray
51
+ # to_na(typecode, byteswap) -> NArray
52
+ # to_na(typecode,size[, ...]) -> NArray
53
+ # to_na(typecode,size[, ...], byteswap) -> NArray
54
+ #
55
+ # Convert String to NArray accoring to +typecode+ and call byte swap method
56
+ # given by Symbol +byteswap+. Pass +nil+ for +byteswap+ to skip the byte
57
+ # swap conversion.
58
+ #
59
+ # Because, as of this writing, KATCP servers typically run on big endian
60
+ # systems which return binary payload data in network byte order, the default
61
+ # +byteswap+ is <tt>:ntoh</tt>, which converts from network byte order to
62
+ # host (i.e. native) order.
63
+ def to_na(typecode, *args)
64
+ # Default to :ntoh
65
+ byteswap = :ntoh
66
+ if !args.empty? && (Symbol === args[-1] || args[-1].nil?)
67
+ byteswap = args.pop
68
+ end
69
+ na = NArray.to_na(self, typecode, *args)
70
+ na = na.send(byteswap) if byteswap
71
+ na
72
+ end
73
+ end
@@ -0,0 +1,7 @@
1
+ #--
2
+ # Define KATCP::VERSION
3
+ #++
4
+
5
+ module KATCP
6
+ VERSION = "0.0.2"
7
+ end
data/lib/katcp.rb ADDED
@@ -0,0 +1,2 @@
1
+ # This pulls in everything
2
+ require 'katcp/client/roach'
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: katcp
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 2
9
+ version: 0.0.2
10
+ platform: ruby
11
+ authors:
12
+ - David MacMahon
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-07-20 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: narray
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ - 5
31
+ - 9
32
+ version: 0.5.9
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ description: " Provides KATCP client library for Ruby. KATCP is the Karoo Array Telescope\n Control Protocol.\n"
36
+ email: davidm@astro.berkeley.edu
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - README
43
+ files:
44
+ - README
45
+ - lib/katcp.rb
46
+ - lib/katcp/client.rb
47
+ - lib/katcp/client/roach.rb
48
+ - lib/katcp/irb.rb
49
+ - lib/katcp/response.rb
50
+ - lib/katcp/util.rb
51
+ - lib/katcp/version.rb
52
+ - examples/listdev.rb
53
+ has_rdoc: true
54
+ homepage: http://rb-katcp.rubyforge.org/
55
+ licenses: []
56
+
57
+ post_install_message:
58
+ rdoc_options:
59
+ - -m
60
+ - README
61
+ - --title
62
+ - Ruby/KATCP 0.0.2 Documentation
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 1
72
+ - 8
73
+ - 1
74
+ version: 1.8.1
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ requirements: []
84
+
85
+ rubyforge_project: rb-katcp
86
+ rubygems_version: 1.3.7
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: KATCP library for Ruby
90
+ test_files: []
91
+