Comment rendre un Thread.Synchronize non dépendant de la VCL ?

emit57 Messages postés 3 Date d'inscription vendredi 26 mai 2006 Statut Membre Dernière intervention 8 février 2011 - 8 févr. 2011 à 16:52
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 - 9 févr. 2011 à 03:46
Bonjour,

Je m'explique:

J'utilise le composant JvHidControllerClass du projet Jedi bien connu des membres de DelphiFr.

Aujourd'hui, j'utilise ce composant pour récupérer des trames qui me viennent d'une passerelle CAN en USB HID.

Mon bus CAN est a 500kbits/s et peut être utilisé a sa vitesse max. Je reçois donc "pas mal" de trame USB qui m'arrive par l'événement OnData de la classe JvHidControllerClass.

Maintenant que le décor est posé, voici le pourquoi de ce post:

L'événement OnData est traité dans par la VCL de delphi, au même titre que les différents clics sur les boutons ou autre object de la form.
Donc lorsque j'ai un flux rapide de donnée (transfert d'image) qui déclenche mes évènements OnData et que je réduit la form par exemple, je me retrouve avec des trames perdues !

Je suppose que c'est parce la VCL est occupé a traité d'autres événements, et donc que le traitement de OnData n'est pas effectué !

Ma question est (enfin) : Comment puis je faire en sorte que l'événement OnData soit traité dans un thread séparé ?
A partir d'un thread séparé, je saurai traité un buffer qui ré-alimentera tranquillement ma form (pour afficher l'image par exemple)

Par avance merci.


-----------------
Gaétan Philbiche
Conception électronique

6 réponses

Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
8 févr. 2011 à 21:02
Salut,
eh bien il va falloir déplacer ton composant. Comme il se trouve sur la fiche principale, avec des méthodes associées aux événements appartenant à la fiche, les événements du composant seront appelés par la VCL. Pour éviter ça, tu peux gérer le composant dans le Thread lui-même. Comme ceci :

interface

type
 TMonThread = class(TThread)
 private
  FLeCompo: TJvHidControllerClass;
 public
  procedure OnData(les paramètres que j'ignore);
  constructor Create(CreateSuspended: Boolean); reintroduce;
  destructor Destroy; override;
 end;

implementation

procedure TMonThread.OnData(les paramètres que j'ignore);
begin
 // traiter la réception des trames, fait au sein du thread
end;

constructor TMonThread.Create(CreateSuspended: Boolean);
begin
 inherited;
 // trucs à l'initialisation
 FLeCompo := TJvHidControllerClass.Create(nil);
 FLeCompo.OnData := OnData; // affecter la méthode appartenant au thread au compo
end;

destructor TMonThread.Destroy;
begin
 FLeCompo.Free;
 inherited;
end;


Ajouter la procédure OnExecute évidemment. Ca devrait marcher normalement, à moins que le JvHidControllerClass ne veuille s'exécuter uniquement sur le thread principal (donc de la VCL). Si c'est le cas, j'ai peur que tu ne doive trouver d'autres astuces (perso je regarderais le code de ce composant Jedi pour essayer de l'adapter à tes besoins de thread, ça serait aussi plus propre).

Cordialement, Bacterius !
0
emit57 Messages postés 3 Date d'inscription vendredi 26 mai 2006 Statut Membre Dernière intervention 8 février 2011
8 févr. 2011 à 21:19
Salut Bacterius,

J'y avait bien pensé mais j'ai laissé de coté cette solution car pour moi, (mais visiblement j'avais tort)
Le thread ne traitait que Onexecute vu que dans cette procédure on trouve un while (not terminated).

je testerai ça demain, mais je suis d'accord que le plus judicieux serait de modifier le composant:

Pour info voici le thread du composant qui gère la lecture des données...

procedure TJvHidDeviceReadThread.Execute;
var
  SleepRet: DWORD;
begin
  SleepRet := WAIT_IO_COMPLETION;
  while not Terminated do
  begin
    // read data
    SleepRet := WAIT_IO_COMPLETION;
    FillChar(Report[0], Device.Caps.InputReportByteLength, #0);
    if Device.ReadFileEx(Report[0], Device.Caps.InputReportByteLength, @DummyReadCompletion) then
    begin
      // wait for read to complete
      repeat
        SleepRet := SleepEx(Device.ThreadSleepTime, True);
      until Terminated or (SleepRet = WAIT_IO_COMPLETION);
      // show data read
      if not Terminated then
      begin
        NumBytesRead := Device.HidOverlappedReadResult;
        if NumBytesRead > 0 then
          // synchronizing only works if the component is not instanciated in a DLL
          if IsLibrary then
            DoData
          else
            Synchronize(DoData);
      end;
    end
    else
    begin
      FErr := GetLastError;
      Synchronize(DoDataError);
    end;
  end;
  // cancel ReadFileEx call or the callback will
  // crash your program
  if SleepRet <> WAIT_IO_COMPLETION then
    Device.CancelIO(omhRead);
end;


Je pensais le modifier en utilisant ce genre de méthode pour passer les datas a la VCL:

 begin
      EnterCriticalSection(PrimeFrm.StringSection);
      PrimeFrm.StringBuf.Add(IntToStr(CurrentNum) + ' is prime.');
      LeaveCriticalSection(PrimeFrm.StringSection);
      PostMessage(PrimeFrm.Handle, WM_DATA_IN_BUF, 0, 0);
    end;


Qu'en penses tu ?

Merci
-----------------
Gaétan
0
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
8 févr. 2011 à 21:39
Ben en fait je me disais la même chose, la notion d'événements en delphi n'est pas vraiment compatible avec le modèle d'exécution des threads, mais bon.

La méthode des messages est une bonne astuce que j'utilise souvent pour passer des infos d'un thread à la fiche principale sans ralentir l'application. Normalement le code là devrait marcher si le traitement ne prend pas trop de temps (si l'application ne prend pas de plus en plus de retard par rapport à la passerelle), sinon ça risque de ramer et il est possible de perdre des frames car le composant n'aura plus de place dans ses buffers pour stocker les nouvelles données arrivant.

Cela dit, un composant dans un thread est un peu crade et j'encourage la reprise du code du compo pour l'adapter au thread. Il n'y a probablement besoin que de quelques lignes de code, pour initialiser la connexion dans la création du thread, la libérer à la fermeture et récupérer les trames dans le OnExecute. Ca serait le plus logique, et le plus dur sera de lire le code du compo

Cordialement, Bacterius !
0
emit57 Messages postés 3 Date d'inscription vendredi 26 mai 2006 Statut Membre Dernière intervention 8 février 2011
8 févr. 2011 à 22:12
Petite question subsidiaire...

Y'a t il un moyen simple de re-compiler mon composant "a la volée" et qu'il soit correctement intégré dans mon appli.
Car si il faut que je re-compil tout le projet Jedi pour faire mes modifs : Ca va être ultra galère !

Merci


-----------------
Gaétan
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
Guillemouze Messages postés 991 Date d'inscription samedi 25 octobre 2003 Statut Membre Dernière intervention 29 août 2013 6
8 févr. 2011 à 22:33
petite réponse subsidiaire :
Si les sources du composant est dans le chemin de recherche de delphi ou de ton projet, il devrait normalement être recompilé.
Le fait de recompiler le dpk de jedi n'a d’impact que dans l'EDI, à moins que tu n'ai coché l'option "construire avec les paquets d’exécution" (ou décoché je ne sais plus) dans les options de ton projet
0
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
9 févr. 2011 à 03:46
Si tu crées un nouveau compo qui utilise le code du composant Jedi (sans pour autant modifier le compo original lui-même), il suffit de compiler ce nouveau composant et ça devrait marcher. On a accès au code source des compos Jedi non ?

Cordialement, Bacterius !
0
Rejoignez-nous