Seite 1 von 2

Serielle Schnittstelle in Threads ?

Verfasst: 25.12.2017 14:34
von tft
Hallo,

nachfolgender Code funktioniert Wenn ich Ihn normal als Procedure aufrufe super. Da es etwas lange dauert bis er alle
Com Schnittstellen geprüft hat. Wollte ich das Asynchron gestallten. Aber es kommt immer der Error : Das angegeben #SerialPort ist nicht inizialisiert.

Ist es ein grundsätzliches Problem oder mache ich was Falsch bei Threads????

Code: Alles auswählen

Procedure ChiChan_Find(*Wert)
  
  ; Suche ChiChan Controler
  
  Protected timer,timeout=500,exit,b.s,thisChar.b,ComCount,a.l,i,t.s
  Static AStringRead.b,  AStringPuffer$, AStringLastChar.b, AStringFirstChar.b
  
  If PortOpen=1
    CloseSerialPort(0)
    PortOpen = 0
    PutStringToEditorGadget(">DisConnect")
    Delay(100)
  EndIf
  
  Restore ComPort
  Read.s b.s
  While b.s<>"-1"
    ;Debug "Scan Com "+b
    i=  OpenSerialPort(0, b.s,57600, #PB_SerialPort_NoParity, 8, 1, #PB_SerialPort_NoHandshake, 1024, 1024)   
    If  i
      Delay(100)
      ;Debug "Open"
      ComCount = ComCount+1
      
      WriteSerialPortString(0,".ChiChan"+Chr(13)+Chr(10),#PB_Ascii);+Chr(0))
      While AvailableSerialPortOutput(0):Wend
      
      AStringPuffer$ = ""
      AStringFirstChar = 0
      AStringLastChar = 0
      
      timer = ElapsedMilliseconds() + timeout
      exit = 0
      
      While exit = 0
        
        If AvailableSerialPortInput(0)
          ;Debug  AvailableSerialPortInput(0) 
        EndIf
        
        While AvailableSerialPortInput(0) 
         ; Debug "Data found "
          
          ReadSerialPortData(0, *MemoryID, 1)
          thisChar = PeekB(*MemoryID)
          AStringLastChar = thisChar
          If thisChar > 31 ; ASCII Ab Space sind Daten
            If AStringFirstChar = 0
              AStringFirstChar = thisChar
            EndIf
            AStringPuffer$ = AStringPuffer$ + Chr(thisChar)
          Else             ; bei steuerzeichen beänden
            If AStringFirstChar <> 0
              exit = -1
            EndIf
            
            ;Debug "**"
          EndIf
          
        Wend
        
        If timer < ElapsedMilliseconds()
          exit = -1
          AStringPuffer$ = ""
          ;Debug "ChiChan not Found at "+b 
        EndIf
        
      Wend
   
      CloseSerialPort(0)
      Delay(100)
      ;Debug AStringPuffer$
      If AStringPuffer$ =".ChiChan" ; Das ist die echo antwort.
        ;Debug "ChiChan Found at "+b 
        ChiChanHaende = 1
        Verbindung.l = 57600
        PortName.s = b
        Conect()
        ;Debug "ComCount "+Str(ComCount)
        For i = 0 To ComCount-1
          t = GetGadgetItemText(#Main_Port, i)
          ;Debug t
          If GetGadgetItemText(#Main_Port, i) = PortName.s
            SetGadgetState(#Main_Port, i)
            ;Debug "SetComport at "+Str(i)
          EndIf
        Next
        
        i=0
        Restore BaudData
        Read.l a.l
        While a.l<>-1
          If GetGadgetItemText(#Main_Speed, i) = Str(Verbindung.l)
            SetGadgetState(#Main_Speed, i)
            ;Debug "SSetComSpeed at "+Str(Verbindung.l)
          EndIf
          Read.l a.l 
          i=i+1
        Wend       
        
        Break 
      EndIf

    Else
      ;Debug "Port not Found at "+ b
    EndIf
    Read.s b.s
  Wend

EndProcedure

Verursacht wird der Fehler durch diese Zeile : ReadSerialPortData(0, *MemoryID, 1).
An der Stelle habe ich bereits mehrfach auf die Schnittstelle zugegriffen.
*MemoryID ist ein Pufferfeld das ich am Anfang des Code deklariere : Global *MemoryID = AllocateMemory(1024)

Weis jemand was ich falsch mache ?

Gruss TFT

Re: Serielle Schnittstelle in Threads ?

Verfasst: 25.12.2017 18:43
von Imhotheb
tft hat geschrieben:Verursacht wird der Fehler durch diese Zeile : ReadSerialPortData(0, *MemoryID, 1).
An der Stelle habe ich bereits mehrfach auf die Schnittstelle zugegriffen.
*MemoryID ist ein Pufferfeld das ich am Anfang des Code deklariere : Global *MemoryID
= AllocateMemory(1024)
Gruss TFT
Ohne mir den Code weiter angesehen zu haben ...
Wennn du mit mehreren Thread auf den gleichen Speicherbereich zugreifst (*MemoryID) muss es irgendwann knallen ... sollte durch Mutex/Semaphoren abgesichert werden.

Ohne Gewähr und Pistole :wink:

Re: Serielle Schnittstelle in Threads ?

Verfasst: 26.12.2017 18:57
von #NULL
- Ist 'threadsafe' Compiler Option aktiviert?
- Nur ein Thread oder mehrere?
- was macht PutStringToEditorGadget()?
- was macht Conect()?
- Variable i wird für völlig unterschiedliche Sachen verwendet. Sollte zwar in dem Code wie er hier sichtbar ist kein Problem sein, ist aber generell keine gute Idee.
- sollte PortOpen vielleicht auch bei OpenSerialPort()/CloseSerialPort() gesetzt werden?

Re: Serielle Schnittstelle in Threads ?

Verfasst: 28.12.2017 11:06
von tft
Hallo,

- Ist 'threadsafe' Compiler Option aktiviert?

Ja.

- Nur ein Thread oder mehrere?

1

- was macht PutStringToEditorGadget()?

Schreibt den String in ein Editor GadGet, Zum Protokollieren.

- was macht Conect()?

Gute Frage ..... schau ich gleich mal nach.

Code: Alles auswählen

Procedure Conect()
  
  Protected timer
  
  DebugLog("{ Connect")
  
  If PortOpen=1
    CloseSerialPort(0)
    PortOpen = 0
    PutStringToEditorGadget(">DisConnect")
    Delay(100)
  EndIf
  
  timer = ElapsedMilliseconds()
  DebugLog("Open ComPort "+PortName.s)
  If OpenSerialPort(0, PortName.s, Verbindung.l, #PB_SerialPort_NoParity, 8, 1, #PB_SerialPort_NoHandshake, 1024, 1024)
    
    DebugLog("OpenPortTime "+Str(ElapsedMilliseconds()-timer))
    
    PortOpen = 1
    PutStringToEditorGadget(">Open Port "+PortName.s+" connect with "+Str(Verbindung.l)+" speed")
    ;AddGadgetItem(#Main_Console, -1, "Open Port "+PortName.s+" connect with "+Str(Verbindung.l)+" speed" )
    Delay(1000)
    
    If SendComandAndWait("..") = -1
      DebugLog("ExitCode SendComandAndWait -1")
      PortOpen=0
      CloseSerialPort(0)
      Delay(100)
    EndIf
    
    
  Else
    
    PutStringToEditorGadget(">Cant open port "+PortName.s)
    
  EndIf
  
  DebugLog("}")
  
  EndProcedure
- Variable i wird für völlig unterschiedliche Sachen verwendet. Sollte zwar in dem Code wie er hier sichtbar ist kein Problem sein, ist aber generell keine gute Idee.

Ich verwende kleingeschriebene Variablen immer als Laufvariablen. Für gewöhnlich Protected.

- sollte PortOpen vielleicht auch bei OpenSerialPort()/CloseSerialPort() gesetzt werden?

PortOpen ist eine Globale Variable die kontrolliert nur ob irgendwo ein Port geöffnet wurde. Ist er offen wird dieser immer geschlossen. Das betrift nicht den Scann.

Was mich verwirrt ist das die While Schleifen bei test auf AvailableSerialPortInput(0) zwar Daten erkennt, danach aber bei Read reklamiert wird.

Re: Serielle Schnittstelle in Threads ?

Verfasst: 28.12.2017 11:52
von ccode_new
Hallo tft,

-> COM-Zugriffe sind allgemein nicht threadsicher


<Ohne Gewähr>

Anbei: Vielleicht hilft das hier: http://www.purebasic.fr/english/viewtop ... 13&t=60936

Re: Serielle Schnittstelle in Threads ?

Verfasst: 28.12.2017 12:16
von #NULL
Bezüglich i meinte ich eher das hier:

Code: Alles auswählen

    i=  OpenSerialPort(..)   
    If  i
Für mich sieht das so aus als ob du ChiChan_Find() oder andere SerialPort-Funktionen außerhalb des Threads aufrufst.
Du kannst folgendes verwenden um zu sehen wo ports geschlossen wurden (einfuegen vor allen Aufrufen von CloseSerialPort(), also am besten in mainfile ganz oben):

Code: Alles auswählen

Procedure CloseSerialPort_proc(sp, txt.s)
  Debug "CLOSING SERIAL PORT " + Str(sp) + " : " + txt
  CloseSerialPort(sp)
EndProcedure

Macro CloseSerialPort(sp)
  CloseSerialPort_proc(sp, #PB_Compiler_File + ", " + #PB_Compiler_Line)
EndMacro

Re: Serielle Schnittstelle in Threads ?

Verfasst: 28.12.2017 14:44
von tft
Hallo,

stimmt ... dem könnte ich mal nachgehen. Danke

Re: Serielle Schnittstelle in Threads ?

Verfasst: 28.12.2017 16:26
von ts-soft
Globale Variablen, auch Integer, machen nur Sinn, solange sie nur von einem einzigem Thread gesetzt werden und die anderen Threads nur lesend darauf zu greifen.
http://www.ijon.de/comp/tutorials/threads/synchro.html hat geschrieben:Leider kann die Frage, welche Operationen atomar sind und welche nicht, nicht so einfach beantwortet werden. U.U. kann sogar das Schreiben und Lesen eines einzelnen Bytes nicht-atomar sein. Außerdem hängt die Atomarheit natürlich von der verwendeten Hardware ab. Streng genommen kann man im wesentlichen die Atomarheit keiner einzigen Operation voraussetzen.
Also: Alle globalen Variablen, die dem ersten Satz nicht entsprechen, sind entweder Lokal (Protected) zu deklarieren oder mit Mutex zu schützen!

Selbst wenn es bei Dir läuft, heißt es nicht, das es bei anderen auch so ist.

Gruß
Thomas

Re: Serielle Schnittstelle in Threads ?

Verfasst: 28.12.2017 19:18
von tft
Hallo,

dann sollte ich den Thread So separieren das während der Thread läuft entweder keine Globalen Variablen Verwendung finden, oder mit Mutex den Zugriff sperren, solange der Thread darauf zugreift. Was passiert aber wenn die Variable gesperrt ist und das Main Programm darauf zugreifen will. Wartet der Prozess bis die Freigabe erteilt ist?

Gruss TFT

Re: Serielle Schnittstelle in Threads ?

Verfasst: 28.12.2017 19:35
von ts-soft
@tft

Guck doch einfach in die Hilfe unter Thread / ...Mutex..., das sind schöne Beispiele und es wird alles erklärt, zum Beispiel: LockMutex(), UnlockMutex() usw.