Seite 1 von 2

Procedure als eigenen Prozess parallel ausführen

Verfasst: 27.07.2013 11:05
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

Re: Procedure als eigenen Prozess parallel ausführen

Verfasst: 27.07.2013 11:39
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

Re: Procedure als eigenen Prozess parallel ausführen

Verfasst: 27.07.2013 12:09
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

Re: Procedure als eigenen Prozess parallel ausführen

Verfasst: 27.07.2013 12:28
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.)

Re: Procedure als eigenen Prozess parallel ausführen

Verfasst: 27.07.2013 12:47
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...

Re: Procedure als eigenen Prozess parallel ausführen

Verfasst: 27.07.2013 13:24
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


Re: Procedure als eigenen Prozess parallel ausführen

Verfasst: 27.07.2013 13:57
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...

Re: Procedure als eigenen Prozess parallel ausführen

Verfasst: 27.07.2013 14:11
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

Re: Procedure als eigenen Prozess parallel ausführen

Verfasst: 27.07.2013 20:09
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

Re: Procedure als eigenen Prozess parallel ausführen

Verfasst: 27.07.2013 22:15
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