[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
RE: [FR-devel] Code review time
From: |
Bob Calco |
Subject: |
RE: [FR-devel] Code review time |
Date: |
Mon, 9 Sep 2002 00:04:35 -0400 |
Laurent:
%% -----Original Message-----
%% From: address@hidden
%% [mailto:address@hidden Behalf Of
%% Laurent Julliard
%% Sent: Sunday, September 08, 2002 4:09 PM
%% To: address@hidden
%% Subject: Re: [FR-devel] Code review time
<snip>
%% By the way I have a question for you. How would do the following in
%% Ruby: I use popen to start a remote process and I want the parent
%% process to read the output of the remote and write it on its own
%% stdout almost in real time?
I partially solved this problem way back when in a once-and-future project
that I called Maestro. I implemented DRb agents that allowed a client to
connect to a server, execute an arbitrary command on that machine (although
it could just as well be localhost), and pump the live output to a monitor,
which theoretically could be on an entirely different machine from both the
client and server, though for all practical purposes, I usually had it go to
the same machine as the client.
See ConsoleAgent.execute for the popen call, and ConsoleMonitor.write for
the display.
I created a console version, then a TK gui version, of the monitor and agent
classes. The Tk output speed was glacier-like, but the console blazing fast,
about as close to real time as my human eyes could discern. I was even able
to send "metaprogramming" type commands, dynamically build functions, you
name it, and it was zippy. Maybe recent fixes in Ruby's threading model will
allow the Tk GUI to perform faster. dunno, haven't played with it in ages.
Note: I was mucking around with the call to IO.popen to see what was causing
the Tk slowdown, and was in the middle of that when development of Maestro
came to a halt. Feel free to use this approach in FreeRIDE, and refactor it
as necessary.
Making it write to the client's standard out probably wouldn't take much.
Since that was not my goal, per se (I actually wanted a separate monitor) I
didn't think about it much. But I'm sure the client can contain a monitor
and set it up in its process to listen on a port to receive the output.
I didn't send the Tk Maestro::Editor stuff - I just included the GuiXXXX
classes as examples of adding the GUI layer to the base ConsoleXXX class.
You can use Fox or whatever for the GUI until Curt and I get wxRuby running.
:)
Go for it...
- Bob Calco
----------------------------client.rb
require 'drb'
module Maestro
class Client
def initialize(self_host='localhost',mon_host='localhost',
mon_port=9102,agent_host='localhost',agent_port=9101)
@self_host = self_host
@mon_host = mon_host
@mon_port = mon_port
@agent_host = agent_host
@agent_port = agent_port
end
def go(cmd)
DRb.start_service()
agentProxy =
DRbObject.new(nil,"druby://address@hidden:address@hidden")
agentProxy.executeCommand("address@hidden","address@hidden",@mon_port,cmd)
end
end
end
------------------------ monitor.rb
require 'drb'
require 'maestro/ui/editor'
module Maestro
class ConsoleMonitor
def initialize( host='localhost', port=9102 )
@host = host
@port = port
writeln "Maestro Monitor running on address@hidden at port address@hidden"
start
end
public
def write( job, host, port, str )
if str.type == String
puts "address@hidden:#{port}>" + str
else
for i in 0...str.length
puts "address@hidden:#{port}>" + str[i]
end
end
end
protected
def writeln(msg)
puts msg
end
def start
DRb.start_service( "druby://address@hidden:address@hidden", self )
DRb.thread.join
end
end
class GuiMonitor < ConsoleMonitor
def initialize( host='localhost', port=9102 )
@root = TkRoot.new { title '*MAESTRO Monitor*' }
@frame = TkFrame.new(@root)
@editor = Maestro::Editor.new( @frame, 80, 30, 9 )
@frame.pack( 'fill' => 'both', 'expand' => 1 )
super( host, port )
end # def
public
def write ( job, host, port, str )
if str.type == String
@editor.text.insert( 'end', "address@hidden:#{port}>" + str +
"\n" )
@editor.text.see( 'end' )
else
#Thread.new { |th|
for i in 0...str.length do
@editor.text.insert( 'end', "address@hidden:#{port}>" +
str[i] )
@editor.text.see( 'end' )
end
#}
end
end
protected
def start
DRb.start_service( "druby://address@hidden:address@hidden", self )
Tk.mainloop
end
def writeln ( msg )
@editor.text.insert 'end',msg+"\n"
@editor.text.see('end')
end
end # class
def InvokeMonitor( host='localhost', port=9102, gui=true )
if gui
Maestro::GuiMonitor.new( host, port )
else
Maestro::ConsoleMonitor.new( host, port )
end
end
module_function :InvokeMonitor
end
------------------------------------- agent.rb
require 'drb'
require 'tk'
require 'maestro/ui/editor'
module Maestro
class ConsoleAgent
@@jobs = 0
def initialize(host='localhost',port=9101)
@port = port
@host = host
@action_type = "script"
writeln "Maestro Agent running on address@hidden at port #{port}..."
start
end
public
def executeScript(cli_host,mon_host,mon_port,script,options)
@cmd = "rb32 #{script} #{options}"
return execute(cli_host,mon_host,mon_port)
end
def executeCommand(cli_host,mon_host,mon_port,command)
@cmd = "rb32 -e \"#{command}\""
address@hidden = command
@action_type = "command"
writeln "Connection from #{cli_host} accepted."
writeln "Executing job # #{@@jobs} for client #{cli_host}, "
writeln "redirecting output to #{mon_host} at port #{mon_port}..."
DRb.start_service
mon = DRbObject.new(nil,"druby://#{mon_host}:#{mon_port}")
@@jobs += 1
job = (@@jobs)
mon.write(job,@host,@port,"Connection request from address@hidden
accepted...")
mon.write(job,@host,@port,"About to execute address@hidden:
\"address@hidden"")
#a = Thread.new { |th|
execute(job,cli_host,mon_host,mon_port,mon)
#}
#a.join
writeln "Finished job # #{@@jobs}."
writeln "---------------------------------------------------------"
end
protected
def start
DRb.start_service("druby://address@hidden:address@hidden",self)
DRb.thread.join
end
def writeln(msg)
puts msg
end
def execute(job,cli_host,mon_host,mon_port,mon)
IO.popen("address@hidden","r+") { |ruby|
#mon.write(job,@host,@port, ruby.readlines )
while ruby.gets
mon.write(job,@host,@port,$_)
end
ruby.close_write
}
end
end
class GuiAgent < ConsoleAgent
def initialize(host='localhost',port=9101)
@root = TkRoot.new { title '*MAESTRO Agent*' }
@frame = TkFrame.new(@root)
@editor = Maestro::Editor.new( @frame, 80, 30, 9 )
@frame.pack( 'fill' => 'both', 'expand' => 1 )
super(host,port)
end # def
protected
def start
DRb.start_service("druby://address@hidden:address@hidden",self)
Tk.mainloop
end
def writeln ( msg )
@editor.text.insert 'end',msg+"\n"
@editor.text.see ('end')
end
end # class
def InvokeAgent(host='localhost',port=9101,gui=true)
if gui
Maestro::GuiAgent.new(host,port)
else
Maestro::ConsoleAgent.new(host,port)
end
end
module_function :InvokeAgent
end
end
----