freeride-devel
[Top][All Lists]
Advanced

[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

----






reply via email to

[Prev in Thread] Current Thread [Next in Thread]