katcp 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +110 -0
- data/examples/listdev.rb +33 -0
- data/lib/katcp/client/roach.rb +141 -0
- data/lib/katcp/client.rb +258 -0
- data/lib/katcp/irb.rb +3 -0
- data/lib/katcp/response.rb +209 -0
- data/lib/katcp/util.rb +73 -0
- data/lib/katcp/version.rb +7 -0
- data/lib/katcp.rb +2 -0
- metadata +91 -0
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.
|
data/examples/listdev.rb
ADDED
@@ -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
|
data/lib/katcp/client.rb
ADDED
@@ -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,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
|
data/lib/katcp.rb
ADDED
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
|
+
|