Procedure als eigenen Prozess parallel ausführen

Anfängerfragen zum Programmieren mit PureBasic.
elko68
Beiträge: 27
Registriert: 26.02.2010 14:50

Procedure als eigenen Prozess parallel ausführen

Beitrag von elko68 »

Hallo,

ich habe da noch ein Anfängerproblem.

In meiner Controller-Steuerung werden u.a. Schrittmotoren gesteuert - das klappt auch alles :wink:
Nun gebe ich dem Motor einen Sollwert vor und frage in einer Schleife solange die aktuelle position ab bis Sollposition = Istposition.
Während dieser Zeit der Abfrage ist natürlich dass Programm blockiert.
Wie kann ich diese Abfrage im Hintergrund laufen lassen und den aktuellen Wert auf meiner GUI ausgeben.

Gruß Frank

Code: Alles auswählen

Procedure.l Turn_Rotor()   ;Rotor bewegen
  
  Com_Out("A MVP ABS,0,"+Str(6667 * GetGadgetState(#TrackBar_0))) ;gebe Sollposition vor
  Posi_sol.l = Val(Mid(Com_In(),8,9))/6667 ;Rückantwort der Sollposition
  PrintN(Str(Posi_sol))     ;Kontrollausgabe Konsole
  
  Repeat
    Com_Out("A GAP 1,0,")     ;Istposition abfragen   
    Posi_ist.l = Val(Mid(Com_In(),7,9))/6667 ;Rückantwort der Istposition
    PrintN(Str(Posi_ist))     ;Kontrollausgabe Konsole
  Until Posi_sol = Posi_ist   ; abfragen bis die Istposition = Sollposition
  
EndProcedure
Windows 7x64 Prof , PureBasic 5.20 LTS
Benutzeravatar
KeyKon
Beiträge: 1412
Registriert: 10.09.2004 20:51
Computerausstattung: Laptop: i5 2,8 Ghz, 16GB DDR3 RAM, GeForce 555GT 2GB VRAM
PC: i7 4,3 Ghz, 32GB DDR3 RAM, GeForce 680 GTX 4GB VRAM
Win10 x64 Home/Prof
PB 5.30 (64bit)
Wohnort: Ansbach
Kontaktdaten:

Re: Procedure als eigenen Prozess parallel ausführen

Beitrag von KeyKon »

Ich nehme an du suchst nach:

CreateThread(@Turn_Rotor(),0)

Da Threads in PB immer ein Parameter gegönnt wird musst du allerdings noch einen Dummy-Parameter in deine Prozedur einbauen.
Desweiteren musst du bei Threads natürlich drauf achten, dass die Variablen die du dort verwendest vom Hauptprogramm nicht verändert werden können (außer wenn das gewollt ist, was ich mir in deinem Fall aber nicht vorstellen kann^^)

Ich weis aber nicht inwiefern das ansteuern von Com-Ports Threadsafe ist, darauf sollte man daher auch immer achten, es gibt einige Libs die nicht über mehrere Threads verteilt laufen können/sollen.

LG KeyKon
(\/) (°,,,°) (\/)
elko68
Beiträge: 27
Registriert: 26.02.2010 14:50

Re: Procedure als eigenen Prozess parallel ausführen

Beitrag von elko68 »

... danke für den Hinweis.

Wie muss ich mit CreateThread umgehen - d.h. wie rufe ich den Thread auf - oder sage ich nur CreateThread und rufe nur die Procedure auf und sie läuft dann im hintergrund ?

Gruß Frank

PS: Das Beispiel in der PB Hilfe hilft mir irgenwie nicht weiter
Windows 7x64 Prof , PureBasic 5.20 LTS
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Procedure als eigenen Prozess parallel ausführen

Beitrag von ts-soft »

Code: Alles auswählen

Procedure.i Turn_Rotor(dummy)   ;Rotor bewegen
  
  Com_Out("A MVP ABS,0,"+Str(6667 * GetGadgetState(#TrackBar_0))) ;gebe Sollposition vor
  Posi_sol.l = Val(Mid(Com_In(),8,9))/6667 ;Rückantwort der Sollposition
  PrintN(Str(Posi_sol))     ;Kontrollausgabe Konsole
  
  Repeat
    Com_Out("A GAP 1,0,")     ;Istposition abfragen   
    Posi_ist.l = Val(Mid(Com_In(),7,9))/6667 ;Rückantwort der Istposition
    PrintN(Str(Posi_ist))     ;Kontrollausgabe Konsole
  Until Posi_sol = Posi_ist   ; abfragen bis die Istposition = Sollposition
  
EndProcedure

CreateThread(@Turn_Rotor(), 0)
In den Compileroptionen Threadsafe aktivieren.

Evtl. wirst Du probleme mit Com_Out bekommen, da ich die Funktion nicht kenne.
Es wäre jedenfalls anzuraten, den Comport im Thread zu öffnen und dort zu schliessen.
Ausserdem darfst Du nur einen einzigen Thread dieser Procedure starten oder die
Nutzung der Seriellen Schnittstelle muß extra verwaltet werden (so das nur einer drauf
zugreift.)
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
mk-soft
Beiträge: 3844
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Procedure als eigenen Prozess parallel ausführen

Beitrag von mk-soft »

Hier mal ein kleines Grundgerüst mit einfacher Steuerung eines Threads

Code: Alles auswählen


Structure udtDaten
  cmd.i
  prog.i
  ; Daten
  info.s
EndStructure


Procedure thMyProg(*daten.udtdaten)
  
  Protected ExitThread
  
  With *daten
    ; Init thread
    \prog = 0
    ; Loop thread
    Repeat
      Select \prog
        Case 0
          Debug "Programm 0"
          Delay(10)
          \prog = 10
          
        Case 10
          Debug "Programm 10"
          Delay(1000)
          
        Case 20
          Debug "Programm 20"
          Delay(1000)
          
        Case 30
          Debug "Programm 30"
          Delay(500)
          
        Case 40
          Debug "Programm 40"
          Delay(100)
          
        Case 99
          Debug "Programm 99: Ende"
          ExitThread = #True
          \prog = 0
      EndSelect
      
      If \cmd > 0
        \prog = \cmd
        \cmd = 0
      EndIf
          
    Until ExitThread
    
    ; Exit Thread
    
  EndWith
  
EndProcedure

; Main
Enumeration ; Fenster
  #Window
EndEnumeration

Enumeration ; Menu
  #Menu
EndEnumeration

Enumeration ; MenuItems
  #Menu_Exit
  #Menu_Prog_10
  #Menu_Prog_20
  #Menu_Prog_30
  #Menu_Prog_40
  #Menu_Prog_99
EndEnumeration

Global ExitApplication
Global daten1.udtDaten

Procedure Main()
  
  Protected ev, thread, result
  
  If OpenWindow(#Window, #PB_Ignore, #PB_Ignore, 300, 200, "Thread Sample")
    If CreateMenu(#Menu, WindowID(#Window))
      MenuTitle("&Programm")
        MenuItem(#Menu_Prog_10, "Start 10")
        MenuItem(#Menu_Prog_20, "Start 20")
        MenuItem(#Menu_Prog_30, "Start 30")
        MenuItem(#Menu_Prog_40, "Start 40")
        MenuItem(#Menu_Prog_99, "Start 99")
        MenuBar()
        MenuItem(#Menu_Exit, "Be&enden")
    EndIf
    
    Repeat
      ev = WaitWindowEvent()
      Select ev
        Case #PB_Event_Menu
          Select EventMenu()
              
            Case #Menu_Prog_10
              If IsThread(thread) = 0
                thread = CreateThread(@thMyProg(), daten1)
              Else
                daten1\cmd = 10
              EndIf
              
            Case #Menu_Prog_20
              If daten1\prog > 0
                daten1\cmd = 20
              EndIf
              
            Case #Menu_Prog_30
              If daten1\prog > 0
                daten1\cmd = 30
              EndIf
              
            Case #Menu_Prog_40
              If daten1\prog > 0
                daten1\cmd = 40
              EndIf
              
            Case #Menu_Prog_99
              If daten1\prog > 0
                daten1\cmd = 99
              EndIf
              
            Case #Menu_Exit
              If IsThread(thread)
                result = MessageRequester("Achtung", "Programm läuft noch" + #LF$ + "Beenden erzwingen?", #PB_MessageRequester_YesNo)
                If result = #PB_MessageRequester_Yes
                  ExitApplication = #True
                EndIf
              Else
                ExitApplication = #True
              EndIf
              
          EndSelect
          
        Case #PB_Event_Gadget
          Select EventGadget()
              
          EndSelect
          
        Case #PB_Event_SizeWindow
          Select EventWindow()
              
          EndSelect
          
        Case #PB_Event_CloseWindow
          Select EventWindow()
            Case #Window
              If IsThread(thread)
                result = MessageRequester("Achtung", "Programm läuft noch" + #LF$ + "Beenden erzwingen?", #PB_MessageRequester_YesNo)
                If result = #PB_MessageRequester_Yes
                  ExitApplication = #True
                EndIf
              Else
                ExitApplication = #True
              EndIf
              
          EndSelect
          
      EndSelect
      
    Until ExitApplication
    
    If IsThread(thread)
      daten1\cmd = 99
      WaitThread(thread, 5000)
    EndIf
    
  EndIf
  
EndProcedure : Main()

End
Update2...
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
elko68
Beiträge: 27
Registriert: 26.02.2010 14:50

Re: Procedure als eigenen Prozess parallel ausführen

Beitrag von elko68 »

Hallo mk-soft,

Danke für dein Beispiel - aber dass ist mir im moment zu Fett - ich komme aus der Bascom Fraktion - habe bisher nur µC geproggt und versuche nun den Einstieg in PB.
Ich verstehe dein Beispiel nicht weil ich mich erst mit Structure befassen muss. - sorry ist nicht böse gemeint - ich ärgere mich nur über mich selbst weil ich auf der Stelle trete.

Ich stelle vieleich mal mein Projekt kurz vor:

Es soll eine Steuerung einer Komplexen Antennenanlage sein - bin Funkamateur.
Die Antennenanlage besteht aus einer Reihe von Busteilnehmern auf Basis RS485
Teilnehmer sind:
- Switchbox zum Schalten von Filter, Fernspeisungen und Empfänger
- zwei Antennenumschalter
- Antennentuner
- Antennenrotor

Diese Busteilnehmer sind vorhanden und werden derzeitig über einen Controller ATMega128 mit Touch-Display gesteuert.
Es ist alles in Bascom realisiert worden.
Es besteht eine permanente Verbindung zum RS485 Bus und es kann alles simultan bedient werden.
Realisiert wurde es über eine gemeinsame Mainloop.

Dieses Projekt (Controller) möchte ich nun in PB realisieren.

Ich hänge mal ein paar Programmschnipsel an.

Noch mal zu dem Steppmotoren.
Die haben ein RS485 Interface und können jederzeit mit Daten gefüttert werden - d.h. auch wenn er läuft kann ich ihm eine neue Pos mitteilen bzw die aktuelle pos jederzeit abfragen.
Motorantworten werden immer abgeholt so dass der Bus immer leer ist und sich nichts überschneiden kann.

Meine Idee war nun, da ich ja keinen ATMega mer habe sondern einen PC - so sollten prozesse auch im Hintergrund laufen können.
Mit einem TrackBar gebe ich die sollPosition vor - der Motor läuft. In einem TextGetgat soll dann die aktuellePos mitlaufen (momentan in der Console)
Während der Moter sich seiner Sollposition nähert möchte ich natürlich auch andere Busteilnehmer bedienen können.

Tja und nun habe ich nen Brett vorm Kopp und komme nicht weiter mit den Thread.

Ich hoffe ihr könnt mir weiterhelfen

Gruß Frank

Code: Alles auswählen

;##############################################################################################
;# ACB - Controller V0.1                                                                      #
;# Ansteuerung Fernspeisung, Outbox - Antennenumschalter, ATU - Vertical, Rotor - RX - Loop   #
;##############################################################################################

OpenConsole("Serial-Monitor") ;Console zum Test der Proceduren

IncludeFile "ACB-Com.pb"
IncludeFile "ACB-GUI.pb"
IncludeFile "ACB-Rotor.pb"
IncludeFile "ACB-ATU.pb"

Rotor_Ref_Init() ;Initialisierung Rotor
ATU_Ref_Init()   ;Initialisierung ATU



Set_GUI()    ;Button setzen
   Repeat
     EventID.l = WaitWindowEvent()       ;Event am Hauptfenster abwarten
     
     Select EventID  ;Event Button auswählen
       Case #PB_Event_Gadget
         
       Select EventGadget() ;Auswertung der Button und Aufruf der Unterprogramme
           
         Case #Button_160
           Select EventType()
             Case #PB_EventType_LeftClick : Set_160m()
           EndSelect
           
         Case #Button_80
           Select EventType()
             Case #PB_EventType_LeftClick : Set_80m()
           EndSelect
           
         Case #Button_40
           Select EventType()
             Case #PB_EventType_LeftClick : Set_40m()
           EndSelect
           
         Case #Button_30
           Select EventType()
             Case #PB_EventType_LeftClick : Set_30m()
           EndSelect
           
         Case #TrackBar_0
           Select EventType()
             Case #PB_EventType_LeftClick    : Turn_Rotor()
           EndSelect
           
           
           
           
       EndSelect ;Auswertung der Button und Aufruf der Unterprogramme
       
   EndSelect ;Event Button auswählen
   
   Until EventID = #PB_Event_CloseWindow
CloseSerialPort(1) 
End

Code: Alles auswählen

;##############################################################################################
;# Proceduren GUI                                                                             #
;##############################################################################################


Enumeration #PB_Compiler_EnumerationValue
  #Main_window
  #TrackBar_0
  #Button_160
  #Button_80
  #Button_40
  #Button_30
EndEnumeration

Procedure Set_GUI() ;Button setzen
  OpenWindow(#Main_window, 200, 200, 600, 400, "ACB - Controller Test01", #PB_Window_SystemMenu|#PB_Window_MinimizeGadget) ;Hauptfenster
  ButtonGadget(#Button_160, 10, 50, 50, 50, "160 m") ;Button_160
  ButtonGadget(#Button_80, 90, 50, 50, 50, "80 m") ;Button_80
  ButtonGadget(#Button_40, 170, 50, 50, 50, "40 m") ;Button_40
  ButtonGadget(#Button_30, 250, 50, 50, 50, "30 m") ;Button_30
  TrackBarGadget(#TrackBar_0, 90, 150, 180, 25, 0, 180, #PB_TrackBar_Ticks)
EndProcedure

Code: Alles auswählen

;##############################################################################################
;# Proceduren zur Datenkommunikation ueber die serielle Schnittstelle                         #
;##############################################################################################

If OpenSerialPort(1, "COM1", 9600, #PB_SerialPort_NoParity, 8, 1, #PB_SerialPort_NoHandshake, 64, 64) ;Serial Port öffnen
   Else
     MessageRequester("Achtung","Es wurde kein Com - Port gefunden") ;Fehlermeldung wenn kein Com - Port vorhanden
     End
EndIf

Procedure Com_Out(Serial_Out.s) ;Datenausgabe auf Serial Port
 WriteSerialPortString(1, Serial_Out + Chr(13))
 Delay(50)
EndProcedure

Procedure.s Com_In() ;Daten lesen vom Serial Port mit Rückgabestring
 Delay(50)  
 Antwort.s = Space(AvailableSerialPortInput(1))
 ReadSerialPortData(1, @Antwort, AvailableSerialPortInput(1)) 
 ProcedureReturn Antwort
 EndProcedure

Code: Alles auswählen

;##############################################################################################
;# Proceduren zur Rotorsteuerung                                                              #
;##############################################################################################

Procedure Rotor_Ref_Init() ;Motoradresse Rotor = C
OpenWindow(Rotor_Init, 250, 250, 220, 70, "ACB - Controller")
TextGadget(Text, 50, 20, 170, 45, "Wait for Rotor Ref Init")
Repeat                     ;Warten auf Referenzpunkt
Com_Out("A  GAP 3,0")
Motorantwort.s = Com_In()
Until Mid(Motorantwort,8,1) = "0"
Com_Out("A  SAP 4,0,2047")  ;Geschwindigkeit für Betrieb setzen
Com_In()                    ;Serial Buffer leeren
Com_Out("A  SAP 204,0,200") ;Freeweeling - automatische Motorabschaltung
Com_In()                    ;Serial Buffer leeren
Delay(500)
CloseWindow(Rotor_Init)
EndProcedure

Procedure Read_Rotor(Posi_sol.l)
  Repeat
    Com_Out("A GAP 1,0,")     ;Istposition abfragen   
    Posi_ist.l = Val(Mid(Com_In(),7,9))/6667 ;Rückantwort der Istposition
    PrintN(Str(Posi_ist))     ;Kontrollausgabe Konsole
  Until Posi_sol = Posi_ist   ; abfragen bis die Istposition = Sollposition
  
EndProcedure

CreateThread(@Read_Rotor(),Posi_sol)


Procedure Turn_Rotor()   ;Rotor bewegen
  
  Com_Out("A MVP ABS,0,"+Str(6667 * GetGadgetState(#TrackBar_0))) ;gebe Sollposition vor
  Posi_sol.l = Val(Mid(Com_In(),8,9))/6667 ;Rückantwort der Sollposition
  PrintN(Str(Posi_sol))     ;Kontrollausgabe Konsole
  Read_Rotor(Posi_sol)
  
EndProcedure

Code: Alles auswählen

;##############################################################################################
;# Proceduren zur Steuerung ATU Vertical                                                      #
;##############################################################################################

Procedure ATU_Ref_Init()
OpenWindow(Atu_Init, 250, 250, 220, 70, "ACB - Controller")
TextGadget(Text, 50, 20, 170, 45, "Wait for ATU Ref Init")
Delay(500)
CloseWindow(Atu_Init)

EndProcedure

Procedure Set_160m() ;Programmteildummy
  PrintN("TUNED 160m")
EndProcedure

Procedure Set_80m() ;Programmteildummy
  PrintN("TUNED 80m")
EndProcedure

Procedure Set_40m() ;Programmteildummy
  PrintN("TUNED 40m")
EndProcedure

Procedure Set_30m() ;Programmteildummy
  PrintN("TUNED 30m")
EndProcedure

Windows 7x64 Prof , PureBasic 5.20 LTS
Benutzeravatar
mk-soft
Beiträge: 3844
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Procedure als eigenen Prozess parallel ausführen

Beitrag von mk-soft »

Anwortet der Motor immer mit ein Chr(13) als Abschluss oder liefert er noch ein Checkbyte?
Es müsste ein globaler Interpreter für die eingehenden Protokolle geschrieben werden der dann die Ergebnisse in den entsprechenden variablen hinterlegt.
Habe schon ASCII-Interpreter mit SPS programmiert...
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
elko68
Beiträge: 27
Registriert: 26.02.2010 14:50

Re: Procedure als eigenen Prozess parallel ausführen

Beitrag von elko68 »

Hallo mk-soft,

ja ist immer chr(13), nein einen Interpreter braucht es nicht - die kommunikation mit der seriellen funst 1A - es sind ja auch noch andere Busteilnehmer, die werden ähnlich gefüttert.
Die Hardware läuft ja schon - nur dass ich jetzt nicht mit nem ATMega steuern will sondern direkt mit dem PC - der sollte ja mehr drauf haben als so ein popliger ATMega 8)

Gruß Frank
Windows 7x64 Prof , PureBasic 5.20 LTS
elko68
Beiträge: 27
Registriert: 26.02.2010 14:50

Re: Procedure als eigenen Prozess parallel ausführen

Beitrag von elko68 »

Hallo,

wie das mit CreateThread funktioniert habe ich nun herausgefunden - funktioniert aber nicht mit der Seriellen.
Meine Lösung war die Verwendung von Timern.

Danke nochmals für eure Antworten.

Gruß Frank
Windows 7x64 Prof , PureBasic 5.20 LTS
Benutzeravatar
KeyKon
Beiträge: 1412
Registriert: 10.09.2004 20:51
Computerausstattung: Laptop: i5 2,8 Ghz, 16GB DDR3 RAM, GeForce 555GT 2GB VRAM
PC: i7 4,3 Ghz, 32GB DDR3 RAM, GeForce 680 GTX 4GB VRAM
Win10 x64 Home/Prof
PB 5.30 (64bit)
Wohnort: Ansbach
Kontaktdaten:

Re: Procedure als eigenen Prozess parallel ausführen

Beitrag von KeyKon »

Hast du alle Seriellen Zugriffe in einen Thread gepackt?
So könnte es zumindest funktionieren...
Aber wenn dus auch anders lösen konntest ist es ja auch gut.

Mein Tipp für zukünftige Projekte, bei denen Kommunikation ausgeführt wird:
Einfach immer sämtliche Kommunikation in eine Prozedur auslagern die in einer Endlosschleife läuft und auf Befehle vom Hauptprogramm (GUI) wartet.
Diese Prozedur läuft dann dauerhaft als Thread parallel zur Oberfläche, das bringt meistens die wenigstens Komplikationen.

LG KeyKon
(\/) (°,,,°) (\/)
Antworten