Pilotage boucle de calcul assez longue

Vous débutez et vous avez besoin d'aide ? N'hésitez pas à poser vos questions
wawavoun
Messages : 12
Inscription : jeu. 25/févr./2010 16:12

Pilotage boucle de calcul assez longue

Message par wawavoun »

Bonjour à tous,

Je découvre progressivement PureBasic en écrivant un programme pour driver un programmateur d'eprom via rs232 et suis satisfait de ce que j'apprends petit à petit.

Je comprends les event loop et j'arrive même à gérer plusieurs fenêtres mais pas encore avec une tâche qui tourne en arrière plan !

Le squelette de mon code fonctionne mas je bute sur ce point.

Une opération sur l'eprom peut être assez longue donc une progress bar serait bien et aussi je voudrais pouvoir éventuellement l’interrompre.

Donc j'imagine que le lancement de la tâche dans la fenêtre principale ouvre une petite fenêtre de pilotage avec cette barre de progression et un bouton start et un autre bouton stop.

Cette fenêtre devrait être commune à plusieurs opérations différentes sur l'eprom donc le code (fondamentalement une longue boucle) qui réalise cette opération est dans un sous programme (un sous programme différent par opération).

Comment faire pour que je puisse surveiller dans la boucle les boutons start et stop de la fenêtre de pilotage et aussi que la progress bar soit mise à jour ?

J'ai essayé différente chose mais sans succès jusqu'à maintenant...

En parcourant la doc j'ai le sentiment que la fenêtre de pilotage devrait être un thread différent de celui du programme principal ???

Ce qui m'aiderait c'est d'avoir un petit bout de code qui fait ça !

Une fenêtre principale avec un bouton qui appelle un sous programme.
Ce sous programme ouvre la fenêtre de pilotage et attend le bouton start pour lancer un compteur (par exemple, compteur qui fait avancer la progress bar ou un affichage quelconque dans la fenêtre de pilotage).
Si on actionne le bouton stop le comptage s'arrête on demande confirmation de l'arrêt définitif de la procédure (par un message requester) , si on confirme on ferme la fenêtre de pilotage et on revient à la fenêtre principale et sinon on reprend le comptage avec la progress bar etc....
Si le comptage va au bout on affiche un message requester pour indiquer la bonne fin et on revient à la fenêtre principale en fermant la fenêtre de pilotage.

J'espère que c'est assez clair...

Peut être avez vous cela dans vos tiroirs ?

Merci, cordialement.
Philippe
wawavoun
Messages : 12
Inscription : jeu. 25/févr./2010 16:12

Re: Pilotage boucle de calcul assez longue

Message par wawavoun »

Voilà mon idée du truc avec en commentaire les bouts de code que "je ne sais pas faire"...

Bon c'est vraiment fait rapidement ! Ça compile mais ne peux pas tourner sans les compléments.

Code : Tout sélectionner

;
;
Global PilotWin
Global MainWin

Global ProgressBar_0, ButStart, ButStop
Global ButCalc


Procedure OpenPilotWin(x = 0, y = 0, width = 600, height = 400)
  PilotWin = OpenWindow(#PB_Any, x, y, width, height, "", #PB_Window_SystemMenu)
  ProgressBar_0 = ProgressBarGadget(#PB_Any, 180, 130, 270, 60, 0, 100)
  ButStart = ButtonGadget(#PB_Any, 170, 250, 100, 40, "Start")
  ButStop = ButtonGadget(#PB_Any, 360, 250, 90, 40, "Stop")
EndProcedure
;
;
Procedure OpenMainWin(x = 0, y = 0, width = 600, height = 400)
  MainWin = OpenWindow(#PB_Any, x, y, width, height, "", #PB_Window_SystemMenu)
  ButCalc = ButtonGadget(#PB_Any, 180, 210, 220, 40, "Calcul")
EndProcedure
;
;
Procedure.s Calculate()
  ;
  ;
  Define.i i, FlgButStart, FlgButStop
  ;
  ;
  OpenPilotWin()
  ;
  FlgBuSTop = #False
  FlgButStart = #False
  ;
  ;
  While FlgButStop = #False And FlgButStart = #False
  ; Ici on attend ButStart ou ButStop
  ; si ButStop --> FlgButStop = #True
  ; si ButStart --> FlgButStart = #True
  Wend
  ;
  If FlgButStop : CloseWindow(PilotWin) : ProcedureReturn "Aborted" : EndIf
  ;
  ; donc ButCalc
  ;  
  For i=1 To 10000000
    ;
    ;
    SetGadgetState(ProgressBar_0, 100 * i / 10000000)
    ;
    ; On regarde ButStop
    ; si ButStop --> FlgButStop = #True
    ; et on fait ce qu'il faut pour mettre ProgressBar_0 à jour
    ;
    If FlgButStop : CloseWindow(PilotWin) : ProcedureReturn "Aborted" : EndIf
    ;
    ;
  Next i
  ;
  ;
  CloseWindow(PilotWin)
  ProcedureReturn "Finished"
  ;
  ;
EndProcedure

;
OpenMainWin()
;
;
Repeat
;  
  Event = WaitWindowEvent()
  ;
  Select Event
      ;
      ;
    Case #PB_Event_Menu
      ;
      Select EventMenu()
         ;
      EndSelect
      ;
      ;
    Case #PB_Event_Gadget
      ;
      ;
      Select EventGadget()
        ;
        Case ButCalc : Debug Calculate()
          ;
      EndSelect
      ;
      ;
  EndSelect
  ;
  ;
Until Event = #PB_Event_CloseWindow ; Ferme toutes les fenêtres en quittant l'une d'elle
Avatar de l’utilisateur
MLD
Messages : 1103
Inscription : jeu. 05/févr./2009 17:58
Localisation : Bretagne

Re: Pilotage boucle de calcul assez longue

Message par MLD »

Bonjour wawavoun
Il y a plusieurs choses dans ton code qui ne sont pas cool.
1° N'utilise pas les #PB_Any partout car ton code va très vite devenir ingérable. mais Enumeration ,endenumeration.
2° Pour une tache longue il faut utiliser un Thread. Mais il ne peut pas contenir de fenêtre juste la tache longue, ceci ne bloquera pas ton programme principal.L'arrêt de la tâche longue sera géré a partir de la boucle générale de la fenêtre de commande.
Pour des programmes avec des fenêtres multiples, et des progress bar sympas fait des recherches sur le forum, tu trouveras certainement ton bonheur.
3°Evite aussi les globals systématiques, car ton code sera difficile a débuger.
PB est un super logiciel de codage, mais il faut un petit peu le maitrisé.
wawavoun
Messages : 12
Inscription : jeu. 25/févr./2010 16:12

Re: Pilotage boucle de calcul assez longue

Message par wawavoun »

Bonjour,

Oui je sens bien que c'est encore approximatif...

Merci pour les conseils. Je vais m'attaquer aux threads alors...!!!

Bonne journée.
Philippe
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: Pilotage boucle de calcul assez longue

Message par Ollivier »

Ce programme affiche la valeur d'un compteur toutes les 50ms, dix fois de suite.

Le compteur est threadé et le thread utilise le CPU pendant 1 ms toutes les 17 ms, pour faire ses incrémentations. C'est le thread qui décide de son rythme de travail, grâce à :

Code : Tout sélectionner

Delay(16)
réglé ici à 16 ms, et la condition de partage du temps :

Code : Tout sélectionner

Not (ElapsedMilliseconds() % 17)
condition vraie toutes les 17 ms et fausse tout le reste du temps.

Mais c'est le process qui décide ici, de la "vie" ou de la "mort" du thread, ainsi que de sa pause/reprise. Le process est ici prioritaire, cependant, le thread peut être interrogé pour graduer la priorité : c'est en étoffant la structure THREAD (rajout de variables après Count.I), le "bus" de communication entre le thread et le process que l'on peut sophistiquer le programme (vérifier si le thread n'a pas besoin de faire une sauvegarde avant d'être arrêté, augmenter/diminuer le rythme, dialogue process<->thread pour créer/gérer une fenêtre que le thread a besoin mais qu'il ne peut techniquement exécuter lui-même, etc...).

30 lignes de code, ça permet de faire quelques tests pour vérifier comment fonctionne un thread (rythme, simultanéïté, prise de ressources CPU, manipulation, communication).

*AP (Arrière-Plan) c'est le "bus" vu du process
*This c'est le "bus" vu du thread
AP c'est l'identifiant du thread obtenu durant sa création

PS : Utilise #PB_Any ou pas, c'est un point de vue, un avis, une opinion : tu es libre de faire comme bon te semble.

Code : Tout sélectionner

; "bus" entre le thread et le process
Structure THREAD
   Count.I
EndStructure

; thread
Procedure Thread(*This.THREAD)
  Repeat
    *This\Count + 1 ; on compte
    If Not ElapsedMilliseconds() % 17
      Delay(16) ; on partage le temps
    EndIf
  ForEver
EndProcedure

; process
Define *AP.THREAD = AllocateStructure(THREAD)
AP = CreateThread(@Thread(), *AP) ;démarrage
PauseThread(AP) ; une pause
*AP\Count = 0 ; une RàZ
ResumeThread(AP) ; une reprise
For I = 1 To 10 ; un affichage
  Delay(50)
  Debug *AP\Count
Next
KillThread(AP) ; un arrêt
End
Message terminé.
Avatar de l’utilisateur
falsam
Messages : 7244
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: Pilotage boucle de calcul assez longue

Message par falsam »

Peut être une autre façon de faire sans thread. Ce code ajoute une itération dans une list.
Bouton Start pour commencer ou reprendre un process.
Bouton Stop pour interrompre le process.

Code : Tout sélectionner

EnableExplicit

Enumeration window
  #mf  
EndEnumeration

Enumeration gadget
  #mfStart
  #mfAbort
  #mfList
  #mfStatus
EndEnumeration

Enumeration misc
  #mfTimer
EndEnumeration

Global flag.b

;Plan de l'application
Declare Start()
Declare StartProcess()
Declare StopProcess()
Declare MyProcess()
Declare Exit()

Start()

Procedure Start()
  OpenWindow(#mf,  0,  0, 800, 600, "Iteration controlée", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  ButtonGadget(#mfStart, 700, 20, 80, 24, "Start")
  ButtonGadget(#mfAbort, 700, 50, 80, 24, "Abort")
  DisableGadget(#mfAbort, #True)
  ListViewGadget(#mfList, 10, 10, 650, 500) 
  TextGadget(#mfStatus, 10, 520, 650, 24, "Cliquer sur le bouton Start.")
  
  ; Evenements
  BindGadgetEvent(#mfStart, @StartProcess())
  BindGadgetEvent(#mfAbort, @StopProcess())
  BindEvent(#PB_Event_Timer, @MyProcess())
  BindEvent(#PB_Event_CloseWindow, @Exit())
  
  Repeat : WaitWindowEvent(1) : ForEver
EndProcedure

Procedure StartProcess()
  Shared flag
  
  DisableGadget(#mfStart, #True)
  DisableGadget(#mfAbort, #False)
  flag = #False
  AddWindowTimer(#mf, #mfTimer, 1)
EndProcedure

Procedure StopProcess()
  Shared flag  
  
  DisableGadget(#mfStart, #False)
  DisableGadget(#mfAbort, #True)
  flag = #True
EndProcedure

Procedure MyProcess()
  Protected item.s
  Static n
  Shared flag
  
  ; Interruption du process ?
  If flag 
    ; oui => Suppression du timer
    RemoveWindowTimer(#mf, #mfTimer)
    MessageRequester("Information", "Process interrompu")
  Else    
    ; non => Ajoutr de l'itération
    n + 1
    item = "Item " + Str(n)
    SetGadgetText(#mfStatus, "Ajout de " + Item)
    AddGadgetItem(#mfList, -1, Item)
  EndIf 
EndProcedure

Procedure Exit()
  End  
EndProcedure
Configuration : Windows 11 Famille 64-bit - PB 6.03 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Répondre