Update a plot in LibPlotters

Engineering & Scientific library: http://zinnamturm.eu/downloadsIN.htm#Lib

Update a plot in LibPlotters

Postby dmaksimiuk » Tue Aug 04, 2015 8:07 pm

Hi All,
this is not per se a problem with the BBox GUI but it contains some elements of it.
I am trying to add a semi real-time monitor to visualise data coming from a dual channel ADC (STM42F407 SoC, https://www.olimex.com/Products/ARM/ST/ ... e-hardware).
In order to accomplish my goal, I am using the LibPlotters library. The simplified code is presented below.
Filling data vector with values of j simulates the reading process from an external device. So, first an empty plot is created. For j =1 I am updating the data vector and then
I am sending the update message to a plot. The same is for j=2. The problem is that I cannot see the updated plot for j=1, and only for j=2 a line is shown on the plot (at the end of my program).
Why sending the update message (for j=1) does not refresh the plot? Did I miss something?
I am using BBox v1.6-RC6.
Your suggestions are welcome.

Cheers,
Darek
Code: Select all
MODULE TestPlotLib;
IMPORT  Plotters := LibPlotters,  Ports, Log := StdLog, Fnt:= Fonts,
               Vec := LibVectors;



VAR   PLp: Plotters.Plotter;

PROCEDURE InitPlotter (VAR P: Plotters.Plotter);
     
   BEGIN
      P := Plotters.dir.NewPlotter(" Data display");
      P.SetMargin(5,5,5,5);
      P.Scale(0, 513.0, -1.0 , 11.0);
      P.Xaxis(0, 512.0,  0, 0, 0.0, 0, {});
      P.Yaxis(-1.0, 10.0, 0, 0,  0.0, 0, TRUE, {}); 
      P.SetFont2 ('New Courier', 8 * Fnt.point, {Fnt.italic}, Fnt.bold);
           P.XyLabel (256.0, 10., Plotters.jC, 'Test data');
           P.AddToolBar();       
   END InitPlotter;


PROCEDURE Run*();
   VAR data : Vec.Vector;
           i, j, k: INTEGER;
    BEGIN
       InitPlotter(PLp);
       Plotters.OpenAux(PLp, 0,0, TRUE, 'Test plot update');
       data := Vec.New(512);
       PLp.DrawVector(data, 0.0 , 1, 0.0, 1.0,  0, Ports.blue);
       FOR j := 1 TO 2 DO
          Log.String("Iteration : "); Log.Int(j); Log.Ln();
          FOR i := 0 TO LEN(data) -1 DO
             data[i] := j ;
         END;
         PLp.Update(); 
         Log.String("Pause start");Log.Ln();
         (*just delay *)
         FOR k := 0 TO 500000000 DO  END;
         Log.String("Pause end"); Log.Ln();
      END;
END Run;

BEGIN
END TestPlotLib.

 TestPlotLib.Run

  DevDebug.UnloadThis  TestPlotLib
dmaksimiuk
 
Posts: 28
Joined: Sun Jun 14, 2015 3:56 pm
Location: Delft, The Netherlands

Re: Update a plot in LibPlotters

Postby manumart1 » Wed Aug 05, 2015 8:00 am

I've tried to install subsystem Lib from http://www.zinnamturm.eu, but I've got an error compiling module LibPlot3D:

Code: Select all
MODULE  LibPlot3D;
    IMPORT Api := WinApi ...
   ...
   PROCEDURE (g : SurfGraphic) Restore2 (f : Views.Frame);
      VAR
         ...
         dc, k, res:  INTEGER;
         pts:  ARRAY  10  OF  Api.POINT;
   BEGIN
      ...
      res  :=  Api.Polygon (dc, pts[0], k);
      ...

The error is in parameter pts[0] --> actual parameter corresponding to open array is not an array

Regards
manumart1
 
Posts: 66
Joined: Tue Sep 17, 2013 6:25 am

Re: Update a plot in LibPlotters

Postby dmaksimiuk » Wed Aug 05, 2015 8:08 am

Hi,
Check out this topic: viewtopic.php?f=16&t=94
The solution to your problem is there.

Regards,
Darek

manumart1 wrote:I've tried to install subsystem Lib from http://www.zinnamturm.eu, but I've got an error compiling module LibPlot3D:

Code: Select all
MODULE  LibPlot3D;
    IMPORT Api := WinApi ...
   ...
   PROCEDURE (g : SurfGraphic) Restore2 (f : Views.Frame);
      VAR
         ...
         dc, k, res:  INTEGER;
         pts:  ARRAY  10  OF  Api.POINT;
   BEGIN
      ...
      res  :=  Api.Polygon (dc, pts[0], k);
      ...

The error is in parameter pts[0] --> actual parameter corresponding to open array is not an array

Regards
dmaksimiuk
 
Posts: 28
Joined: Sun Jun 14, 2015 3:56 pm
Location: Delft, The Netherlands

Re: Update a plot in LibPlotters

Postby manumart1 » Wed Aug 05, 2015 9:45 am

Thanks,
I changed pts[0] to pts.

Regarding your question, I've noticed:
  • The call to PLp.Update() inside the loop, doesn't seem to do the drawing of a line; instead the whole graphic seems to be updated completely only when the program finished.
  • If you want to draw 2 lines, I think you'll need 2 variables of type Vec.Vector.

Example to draw 3 lines:
Code: Select all
   PROCEDURE Run* ();
      VAR data: ARRAY 3 OF Vec.Vector;  (* 3 lines *)
         i, j, k: INTEGER;
   BEGIN
      InitPlotter(PLp);
      Plotters.OpenAux(PLp, 0, 0, TRUE, 'Test plot update');
      FOR j := 0 TO 2 DO
         Log.String("Iteration : "); Log.Int(j); Log.Ln();
         data[j] := Vec.New(512);
         FOR i := 0 TO LEN(data[j]) - 1 DO
            data[j][i] := j + 1
         END;
         PLp.DrawVector(data[j], 0.0, 1, 0.0, 1.0, 0, Ports.blue);
         PLp.Update();
         Log.String("Pause start"); Log.Ln();
         (*just delay *)
         WinApi.Sleep(1000);
         Log.String("Pause end"); Log.Ln()
      END
   END Run;


Regards,
Manuel
manumart1
 
Posts: 66
Joined: Tue Sep 17, 2013 6:25 am

Re: Update a plot in LibPlotters

Postby manumart1 » Wed Aug 05, 2015 10:54 am

I am not sure why I need Services.Action and procedure ProcessMessages, but this example manages to show 5 lines, one at a time:

Code: Select all
   TYPE
      (* Copied from ObxActions *)
      Action = POINTER TO RECORD (Services.Action)
         j: INTEGER
      END;

   VAR PLp: Plotters.Plotter;

   PROCEDURE ProcessMessages; (* Chris Burrows *)
      VAR msg: WinApi.MSG; res: INTEGER;
   BEGIN
      WHILE WinApi.PeekMessage(msg, 0, 0, 0, WinApi.PM_REMOVE) # 0 DO
         res := WinApi.TranslateMessage(msg);
         res := WinApi.DispatchMessageA(msg)
      END
   END ProcessMessages;

   PROCEDURE (a: Action) Do;
      VAR data: Vec.Vector;
         i: INTEGER;
   BEGIN
      ASSERT((1 <= a.j) & (a.j <= 5), 20);
      data := Vec.New(512);
      PLp.DrawVector(data, 0.0, 1, 0.0, 1.0, 0, Ports.blue);

      Log.String("Iteration : "); Log.Int(a.j); Log.Ln();
      FOR i := 0 TO LEN(data) - 1 DO
         data[i] := a.j
      END;
      PLp.Update();

      IF a.j < 5 THEN
         (* just delay *)
         Log.String("Pause start"); Log.Ln();
         WinApi.Sleep(2000);
         Log.String("Pause end"); Log.Ln();

         ProcessMessages();

         INC(a.j);
         Services.DoLater(a, Services.immediately)
      END
   END Do;


   PROCEDURE InitPlotter (VAR P: Plotters.Plotter);
   BEGIN
      P := Plotters.dir.NewPlotter(" Data display");
      P.SetMargin(5, 5, 5, 5);
      P.Scale(0, 513.0,  - 1.0, 11.0);
      P.Xaxis(0, 512.0, 0, 0, 0.0, 0, {});
      P.Yaxis( - 1.0, 10.0, 0, 0, 0.0, 0, TRUE, {});
      P.SetFont2('New Courier', 8 * Fnt.point, {Fnt.italic}, Fnt.bold);
      P.XyLabel(256.0, 10., Plotters.jC, 'Test data');
      P.AddToolBar()
   END InitPlotter;


   PROCEDURE Run* ();
      VAR a: Action;
   BEGIN
      InitPlotter(PLp);
      Plotters.OpenAux(PLp, 0, 0, TRUE, 'Test plot update');

      NEW(a);
      a.j := 1;
      Services.DoLater(a, Services.immediately)
   END Run;
manumart1
 
Posts: 66
Joined: Tue Sep 17, 2013 6:25 am

Re: Update a plot in LibPlotters

Postby dmaksimiuk » Mon Aug 10, 2015 8:22 pm

Hi Manuel,
thanks for you answer. I am not sure if the solution fits my requirements because I cannot create a new variable for every incoming data from the ADC converters (via a serial port @921.6kbits/s).
I am getting samples in blocks of 2048 bytes at rate 40 Hz, and in theory the stream of blocks is infinite. I am just curious if there is another mechanism that allows an update of the graph in the right way. The other solution is to integrate (via DLL) a third-party plot routines, and to be honest I would like to avoid it.

BTW, did somebody went this path and integrate such a package into the BBox environment? Any feedback is very welcome.

Cheers,
Darek
dmaksimiuk
 
Posts: 28
Joined: Sun Jun 14, 2015 3:56 pm
Location: Delft, The Netherlands

Re: Update a plot in LibPlotters

Postby manumart1 » Tue Aug 11, 2015 8:19 am

I don't know much, but I've heard about subsystem Gr (Data Acquisition and Monitoring Toolbox). It seems to fit your needs.
You can download it from http://www.zinnamturm.eu/downloadsDH.htm#Gr

See also
http://www.pas.rochester.edu/~skulski/Downloads.html
http://www.pas.rochester.edu/~skulski/P ... _Class.pdf

Regards,
Manuel
manumart1
 
Posts: 66
Joined: Tue Sep 17, 2013 6:25 am

Re: Update a plot in LibPlotters

Postby Robert » Sat Oct 03, 2015 4:59 pm

You need to replace the line
Code: Select all
PLp.Update

with
Code: Select all
PLp.UpdateNow


I can't remember if the published version includes the UpdateNow procedure. If not, the missing code is

Code: Select all
TYPE
  UpdateModelNow  =  RECORD (Models.UpdateMsg)
                       graphicOnly  :  BOOLEAN
                     END;

  UpdateViewNow   =  RECORD (Views.Message)
                       graphicOnly  :  BOOLEAN
                     END;

PROCEDURE (v : StdView) HandleModelMsg (VAR msg : Models.Message);
  VAR
    updateNow  :  UpdateViewNow;
  BEGIN
    WITH  msg  : UpdateModelNow  DO
      updateNow.graphicOnly  :=  msg.graphicOnly; Views.Broadcast (v, updateNow)
    |  msg  :  Models.UpdateMsg  DO
      Views.Update (v, Views.keepFrames)
    ELSE
    END
  END HandleModelMsg;

PROCEDURE (v : StdView) HandleViewMsg (f : Views.Frame; VAR msg : Views.Message);
  BEGIN
    WITH  msg : UpdateViewNow  DO
      IF  msg.graphicOnly  THEN
        v.RestoreGraphic (f, f.l, f.t, f.r, f.b)
      ELSE
        f.DrawRect (f.l, f.t, f.r, f.b, Ports.fill, Ports.background);
        v.Restore (f, f.l, f.t, f.r, f.b)
      END
    ELSE
    END
  END  HandleViewMsg;

PROCEDURE (p : Plotter) UpdateNow*, NEW, EXTENSIBLE;
  VAR
    msg  :  UpdateModelNow;
  BEGIN
    msg.graphicOnly  :=  FALSE; Models.Broadcast (p, msg)
  END  UpdateNow;


Let me know if you get this to work.

Regards

Robert
User avatar
Robert
 
Posts: 119
Joined: Sat Sep 28, 2013 11:04 am
Location: Edinburgh, Scotland

Re: Update a plot in LibPlotters

Postby Robert » Sat Oct 03, 2015 5:35 pm

If you want to draw 2 lines, I think you'll need 2 variables of type Vec.Vector.


Yes. For efficiency reasons the Plotter does not copy the Vector, which means you can plot the Vector, then change its values, then call "p.Update" and the
Plotter will be redrawn, with the new values, when the current Command finishes - or use UpdateNow if you do not want to wait until the end of the Command.

The reason the Actions technique works with Update is that the Command finishes after each line, then a new Command is started for the next line.

If you have lots of data (say 1000 lines worth) and want to show the last 5 at any time you need to create 5 Vectors, call p.DrawVector for each, then keep
updating the Vectors on a round-robbin basis calling p.UpdateNow every time the oldest Vector is refilled with new data.
User avatar
Robert
 
Posts: 119
Joined: Sat Sep 28, 2013 11:04 am
Location: Edinburgh, Scotland

Re: Update a plot in LibPlotters

Postby manumart1 » Mon Oct 05, 2015 6:48 am

The module LibPlotters (subsystem Lib from http://www.zinnamturm.eu/downloadsIN.htm#Lib) does not have Plotter.UpdateNow.

I modified module LibPlotters as you showed, but got an error:
Code: Select all
PROCEDURE (v : StdView) HandleViewMsg (f : Views.Frame; VAR msg : Views.Message);
BEGIN
  WITH  msg : UpdateViewNow  DO
    IF  msg.graphicOnly  THEN
      v.RestoreGraphic (f, f.l, f.t, f.r, f.b)    <-- undefined record field
    ELSE
      f.DrawRect (f.l, f.t, f.r, f.b, Ports.fill, Ports.background);
      v.Restore (f, f.l, f.t, f.r, f.b)
    END
  ELSE
  END
END  HandleViewMsg;

So RestoreGraphic is missing. I changed it to Restore and compiled ok.

It now works ok, i.e. each line is drawn separately and immediately, instead of all at the same time when the program finished.

But... if the delay is too long, after 6 seconds the cursor becomes busy and the screen freezes. I had to add a call to procedure ProcessMessages.
Code: Select all
VAR PLp: LibPlotters.Plotter;

PROCEDURE ProcessMessages; (* Chris Burrows *)
   VAR msg: WinApi.MSG; res: INTEGER;
BEGIN
   WHILE WinApi.PeekMessage(msg, 0, 0, 0, WinApi.PM_REMOVE) # 0 DO
      res := WinApi.TranslateMessage(msg);
      res := WinApi.DispatchMessageA(msg)
   END
END ProcessMessages;

PROCEDURE Run3* ();
   VAR data: Vec.Vector;
      j, i: INTEGER;
BEGIN
   InitPlotter(PLp);
   Plotters.OpenAux(PLp, 0, 0, TRUE, 'Test plot update');
   FOR j := 1 TO 5 DO
      Log.String("Iteration : "); Log.Int(j); Log.Ln();
      data := Vec.New(512);
      FOR i := 0 TO LEN(data) - 1 DO data[i] := j END;
      PLp.DrawVector(data, 0.0, 1, 0.0, 1.0, 0, Ports.blue);
      PLp.UpdateNow();
      (*just delay *)
      Log.String("Pause start"); Log.Ln();
      WinApi.Sleep(2000);
      Log.String("Pause end"); Log.Ln()
      ProcessMessages()
   END
END Run3;


Regards,
Manuel
manumart1
 
Posts: 66
Joined: Tue Sep 17, 2013 6:25 am

Next

Return to Lib

Who is online

Users browsing this forum: No registered users and 1 guest

cron