Page 1 sur 1

Pilotage boucle de calcul assez longue

Publié : jeu. 13/mai/2021 18:27
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

Re: Pilotage boucle de calcul assez longue

Publié : jeu. 13/mai/2021 20:21
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

Re: Pilotage boucle de calcul assez longue

Publié : ven. 14/mai/2021 9:24
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é.

Re: Pilotage boucle de calcul assez longue

Publié : ven. 14/mai/2021 9:31
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

Re: Pilotage boucle de calcul assez longue

Publié : ven. 14/mai/2021 9:34
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é.

Re: Pilotage boucle de calcul assez longue

Publié : ven. 14/mai/2021 10:32
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