Recent

Author Topic: Interact with octave using TProcess  (Read 16612 times)

FangQ

  • Full Member
  • ***
  • Posts: 134
Interact with octave using TProcess
« on: May 07, 2009, 01:00:03 am »
I want to interact to an octave or matlab session using pipe functions. I read about TProcess from wiki page (http://wiki.freepascal.org/Executing_External_Programs), particularly the example at https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/examples/process/

After some experiments, I wrote the attached code. A button to start an octave process (POctave:TProcess), and then call POctave.Input.Write() to input a user specified command, and  POctave.Output.Read() to get the output.

In my tests, the octave process started fine. However, when I sent the command 'version#10' to the pipe, the Output.Read only returned the copyright info printed at the beginning of octave console. This message ended at "type `news'", not even including the "octave:1>" prompt, not mention the output for "version" command.

I tried the same thing for matlab (started with  "matlab -nojvm -nodesktop"), I got the same results: Output.Read only returned the copyright info, and no more text available no matter what command I send.

I had a feeling that once octave or matlab started its own console, it somehow does not connect to the stdin/stdout/stderr pipes anymore. I am wondering if anyone can help me out on this? I compiled my code on CentOS5.2, with Lazarus 0.9.26 and fpc 2.2.2. My code are attached below.

thank you in advance.


Code: [Select]
// start octave process in the background
procedure TForm1.btStartServerClick(Sender: TObject);
begin
     POctave := TProcess.Create(nil);
     if(not POctave.Running) then begin
          POctave.CommandLine:='matlab -nojvm -nodesktop';
          POctave.Options := [poUsePipes];
          mmLog.Lines.Add('-- Executing octave --');
          POctave.Execute;
     end
end;

// input a command from user
procedure TForm1.btRunCommandClick(Sender: TObject);
var
   cmd: String;
begin
     cmd:=InputBox('Input','Please type your command','');
     RunOctaveCommand(cmd+'#10');
end;   

// and print the output
procedure TForm1.RunOctaveCommand(cmd: String);
var
    Buffer: string;
    BytesAvailable: DWord;
    BytesRead:LongInt;
    NoMoreOutput: boolean;
begin
   if(POctave.Running) then
      POctave.Input.Write(cmd[1], Length(cmd));

   if(POctave.Running) then
    begin
      BytesAvailable := POctave.Output.NumBytesAvailable;
      BytesRead := 0;
      while BytesAvailable>0 do
      begin
        SetLength(Buffer, BytesAvailable);
        BytesRead := POctave.OutPut.Read(Buffer[1], BytesAvailable);
        mmLog.Text := mmLog.Text + copy(Buffer,1, BytesRead);
        BytesAvailable := POctave.Output.NumBytesAvailable;
        NoMoreOutput := false;
      end;
      if BytesRead>0 then
        mmLog.SelStart := Length(mmLog.Text);
    end;
end;   


arnoldb

  • Jr. Member
  • **
  • Posts: 97
Re: Interact with octave using TProcess
« Reply #1 on: May 07, 2009, 10:09:48 am »
You are only checking the octave output results very quickly (and one time only).

Try adding a timer to your application with a 5 or 10 ms interval, and put the

Code: [Select]
   if(POctave.Running) then
    begin
      BytesAvailable := POctave.Output.NumBytesAvailable;
      BytesRead := 0;
      while BytesAvailable>0 do
      begin
        SetLength(Buffer, BytesAvailable);
        BytesRead := POctave.OutPut.Read(Buffer[1], BytesAvailable);
        mmLog.Text := mmLog.Text + copy(Buffer,1, BytesRead);
        BytesAvailable := POctave.Output.NumBytesAvailable;
        NoMoreOutput := false;
      end;
      if BytesRead>0 then
        mmLog.SelStart := Length(mmLog.Text);
    end;

code in the OnTimer Event.

FangQ

  • Full Member
  • ***
  • Posts: 134
Re: Interact with octave using TProcess
« Reply #2 on: May 07, 2009, 04:52:44 pm »
You are only checking the octave output results very quickly (and one time only).

Try adding a timer to your application with a 5 or 10 ms interval, and put the

thanks for the reply. I tried what you suggested, but got the same result. Only the header copyright info were printed, nothing more.

I attached my test unit with this post, the unit looks like the following. I appreciate for more suggestions.

Code: [Select]
unit calloctave;

{$mode objfpc}{$H+}

interface

uses
  Classes, Process, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  StdCtrls, ExtCtrls;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    mmLog: TMemo;
    POctave : TProcess;
    Timer1: TTimer;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  Form1: TForm1;

implementation

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
begin
     POctave := TProcess.Create(nil);
     if(not POctave.Running) then begin
          POctave.CommandLine:='octave';
          POctave.Options := [poUsePipes];
          mmLog.Lines.Add('-- Executing octave --');
          POctave.Execute;
          Timer1.Enabled:=true;
     end
end;

procedure TForm1.Button2Click(Sender: TObject);
var
     cmd: String;
begin
     cmd:=InputBox('Input','Please type your command','');
     cmd:=cmd+'#10';
     if(POctave.Running) then
      POctave.Input.Write(cmd[1], Length(cmd));
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
    if(POctave.Running) then POctave.Terminate(0);
    POctave.Free;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
    Buffer: string;
    BytesAvailable: DWord;
    BytesRead:LongInt;
    NoMoreOutput: boolean;
begin
   if(POctave.Running) then
    begin
      BytesAvailable := POctave.Output.NumBytesAvailable;
      BytesRead := 0;
      while BytesAvailable>0 do
      begin
        SetLength(Buffer, BytesAvailable);
        BytesRead := POctave.OutPut.Read(Buffer[1], BytesAvailable);
        mmLog.Text := mmLog.Text + copy(Buffer,1, BytesRead);
        BytesAvailable := POctave.Output.NumBytesAvailable;
        NoMoreOutput := false;
      end;
      if BytesRead>0 then
        mmLog.SelStart := Length(mmLog.Text);
    end;
end;

initialization
  {$I calloctave.lrs}

end.

FangQ

  • Full Member
  • ***
  • Posts: 134
Re: Interact with octave using TProcess
« Reply #3 on: May 07, 2009, 11:09:17 pm »
now I tend to believe the issue is from TProcess.

I wrote a simple perl script to test the interactive read/write from stdin/stdout, as below

Code: [Select]
#!/usr/bin/perl
my $b;
print "begin interactive mode\n";
while(1){
   $b=<STDIN>;
   $b=~s/[\r\n]+$//g;
   if($b eq "quit"){
       last;
   }
   print "you typed: $b\n";
}

I saved this file as test.pl, I ran the above program and execute test.pl, pstree shows that the thread test.pl is a sub-process of my gui, however, no matter what I send to this program via TProcess.Input.Write, nothing I can read out of TProcess.Output, even the first "begin interactive mode" message. Isn't that strange?

Running the example code at https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/examples/process/  gave me the same behavior. Can someone explain?

Marc

  • Administrator
  • Hero Member
  • *
  • Posts: 2583
Re: Interact with octave using TProcess
« Reply #4 on: May 11, 2009, 11:21:12 am »
now I tend to believe the issue is from TProcess.
iirc tprocess cannot execute scripts (it doesn't interepret them, thats something of your shell)

Quote
I saved this file as test.pl, I ran the above program and execute test.pl, pstree shows that the thread test.pl is a sub-process of my gui, ...

do you see "test.pl" as subprocess or do you see "/usr/bin/perl ./test.pl" as subprocess ?
If the first try executing "/usr/bin/perl ./test.pl"
//--
{$I stdsig.inc}
//-I still can't read someones mind
//-Bugs reported here will be forgotten. Use the bug tracker

Leledumbo

  • Hero Member
  • *****
  • Posts: 8757
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Interact with octave using TProcess
« Reply #5 on: May 11, 2009, 02:00:19 pm »
Wanna try my OctaveGUI? It uses TProcess and input output mechanism similar to what you're trying to achieve.

FangQ

  • Full Member
  • ***
  • Posts: 134
Re: Interact with octave using TProcess
« Reply #6 on: May 11, 2009, 03:56:22 pm »
Wanna try my OctaveGUI? It uses TProcess and input output mechanism similar to what you're trying to achieve.

thank you Leledumbo for sharing your code, it looks very promising. However, I am unable to compile it. Lazarus complains about missing uCmdBox, can you tell me where I can find this unit?

thanks

FangQ

  • Full Member
  • ***
  • Posts: 134
Re: Interact with octave using TProcess
« Reply #7 on: May 11, 2009, 04:19:35 pm »
Wanna try my OctaveGUI? It uses TProcess and input output mechanism similar to what you're trying to achieve.

My stupid. After reading your code, I realized I appended newline incorrectly. After changing cmd+'#10' to cmd+#10, my GUI now works!

thank you so much for showing me your implementation, a working example is really important :)

Leledumbo

  • Hero Member
  • *****
  • Posts: 8757
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Interact with octave using TProcess
« Reply #8 on: May 12, 2009, 05:48:41 am »
Quote
Lazarus complains about missing uCmdBox, can you tell me where I can find this unit?
Whoops! Forgot to tell that I'm using TCmdBox component from CmdLine package. Take it from Lazarus CCR.
Quote
thank you so much for showing me your implementation, a working example is really important
You're welcome. Glad I can be of help :D

 

TinyPortal © 2005-2018