katcp 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+