Recent

Author Topic: Best way to structure project?  (Read 3191 times)

guest60499

  • Guest
Best way to structure project?
« on: February 08, 2017, 11:59:43 pm »
I have a main form which tracks connections in a TVirtualStringTree, and subforms which are created at runtime and associated with a connection. I need to display data from that connection on a TRichMemo on the form associated with the connection.

I'm monitoring the connection socket in a TThread. The TTCPClient class has callback events which are run in this thread. This causes the GUI to hang after a number of updates. What is a better way to structure this class interaction?

I can use the producer-consumer pattern between the socket thread and the GUI thread, but how do I trigger a refresh of the TRichMemo without polling the state of the socket thread?

Edson

  • Hero Member
  • *****
  • Posts: 1087
Re: Best way to structure project?
« Reply #1 on: February 09, 2017, 12:17:36 am »
I can use the producer-consumer pattern between the socket thread and the GUI thread, but how do I trigger a refresh of the TRichMemo without polling the state of the socket thread?

Why don't use events?
Lazarus 2.0.6 - FPC 3.0.4 - x86_64-win64 on  Windows 8

guest60499

  • Guest
Re: Best way to structure project?
« Reply #2 on: February 09, 2017, 04:05:39 pm »
I can use the producer-consumer pattern between the socket thread and the GUI thread, but how do I trigger a refresh of the TRichMemo without polling the state of the socket thread?

Why don't use events?

I'm trying to use events. Unfortunately, for the Synapse socket classes, the event is run in the thread that calls any method of the socket and not in its own thread and not asynchronously. So I implemented my own event driven methods but they run into the issue of not being able to update the GUI from any thread but the main one.

I'm trying to fix it with synchronize, but it's quickly becoming a mess.

Thaddy

  • Hero Member
  • *****
  • Posts: 10482
Re: Best way to structure project?
« Reply #3 on: February 09, 2017, 04:36:26 pm »
I'm trying to use events. Unfortunately, for the Synapse socket classes, the event is run in the thread that calls any method

Uhm, then you are not using the events properly. Here's one option: not  necessarily the best
Create the event in the main thread.
Use SetEVent in the thread to signal there is data available
Use ResetEvent in the main thread after you processed the data

Look at the documentation for TEventObject
When you ask a question that is actually answered in the documentation, you are either lazy or a moron.

GetMem

  • Hero Member
  • *****
  • Posts: 3757
Re: Best way to structure project?
« Reply #4 on: February 09, 2017, 05:50:17 pm »
@guest

That thread list is potentially unsafe, as I explained in one of my previous posts. But for now the main issue is, you don't use synchronize or queue at all to update the main GUI. You should do something like this:
Code: Pascal  [Select][+][-]
  1. unit tcpclient;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils,  blcksock;
  9.  
  10. type
  11.   TErrorHook = procedure(Sender: TObject;
  12.                          const Value: Integer;
  13.                          const Desc: String) of object;
  14.   TConnectHook = procedure(Sender: TObject) of object;
  15.   TDisconnectHook = procedure(Sender: TObject) of object;
  16.  
  17.   PTCPClient = ^TTCPClient;
  18.  
  19.   { TTCPClient }
  20.  
  21.   TTCPClient = class(TThread)
  22.   private
  23.     FLine: String;
  24.     FHost: String;
  25.     FPort: Integer;
  26.     FCertFile: String;
  27.     FConnected: Boolean;
  28.     FOnError: TErrorHook;
  29.     FOnConnect: TConnectHook;
  30.     FOnDisconnect: TDisconnectHook;
  31.     procedure DoUpdateVisualControls;
  32.     procedure DoOnConnect;
  33.     procedure DoOnDisconnect;
  34.     procedure DoOnError;
  35.   protected
  36.     procedure Execute; override;
  37.   public
  38.     Socket: TTCPBlockSocket;
  39.     constructor Create(CreateSuspended: Boolean;
  40.                        const StackSize: SizeUInt=DefaultStackSize);
  41.     property Host: String read FHost write FHost;
  42.     property Port: Integer read FPort write FPort;
  43.     property CertFile: String read FCertFile write FCertFile;
  44.     property Connected: Boolean read FConnected;
  45.     property OnError: TErrorHook read FOnError write FOnError;
  46.     property OnConnect: TConnectHook read FOnConnect write FOnConnect;
  47.     property OnDisconnect: TDisconnectHook read FOnDisconnect
  48.                                            write FOnDisconnect;
  49.   end;
  50.  
  51. implementation
  52.  
  53. { TTCPClient }
  54.  
  55.  
  56. procedure TTCPClient.DoOnConnect;
  57. begin
  58.   if FOnConnect <> nil then
  59.     FOnConnect(Self);
  60. end;
  61.  
  62. procedure TTCPClient.DoOnDisconnect;
  63. begin
  64.   if FOnDisconnect <> nil then
  65.     FOnDisconnect(Self);
  66. end;
  67.  
  68. procedure TTCPClient.DoOnError;
  69. begin
  70.   if FOnError <> nil then
  71.     FOnError(Self, Socket.LastError, Socket.LastErrorDesc);
  72. end;
  73.  
  74. procedure TTCPClient.DoUpdateVisualControls;
  75. begin
  76.  
  77.   //update virtual tree for example, use  FLine here
  78. end;
  79.  
  80. procedure TTCPClient.Execute;
  81. var
  82.   Line: String;
  83. begin
  84.   try
  85.     Socket.SSL.CertCAFile := FCertFile;
  86.     Socket.Connect(FHost, IntToStr(FPort));
  87.     Socket.SSLDoConnect;
  88.  
  89.     if Socket.LastError <> 0 then
  90.     begin
  91.       Synchronize(@DoOnError);
  92.       Exit;
  93.     end
  94.     else
  95.     begin
  96.       Synchronize(@DoOnConnect);
  97.       FConnected := True;
  98.     end;
  99.     repeat
  100.       while Socket.CanRead(1000) do
  101.       begin
  102.         Line := Socket.RecvString(1);
  103.         if Line = '' then
  104.            Exit;
  105.         FLine := Line;
  106.         Synchronize(@DoUpdateVisualControls);
  107.        // WriteLn(Line);
  108.       end;
  109.     until False;
  110.   finally
  111.     DoOnDisconnect;
  112.     Socket.Free;
  113.   end;
  114. end;
  115.  
  116. constructor TTCPClient.Create(CreateSuspended: Boolean;
  117.                               const StackSize: SizeUInt=DefaultStackSize);
  118. begin
  119.   inherited Create(CreateSuspended, StackSize);
  120.   Socket := TTCPBlockSocket.Create;
  121.   Socket.ConvertLineEnd := True;
  122. end;
  123.  
  124. end.

 

TinyPortal © 2005-2018