Problém s komunikací přes seriové porty   otázka

VB.NET

Ahoj,

mám problém s komunikací přes 2 seriové porty. Mám připojeny dvě stejná zařízení přes seriové porty, které neustále vysílají textový řetězec o svém stavu do PC.

Vytvořil jsem si třídu, která má v sobě seriový port a má metody na připojení a odpojení seriového portu a dále má event indikující přijatá data.

V eventu přijatých dat zpracovávám textový řetězec a vytvářím bitmapu pro displej zobrazující teplotu, tlak a další stavové proměnné zařízení. Na konci eventu přes metodu INVOKE zavolám proceduru, která vytvořený obrázek přiřadí pictureboxu na hlavním formu.

Na hlavním formu jsem definoval dva objekty pro každé zařízení jeden.

Program se bezproblému spustí a komunikace se rozjede, ale po několika hodinách provozu se komunikace z ničeho nic přeruší a celý program spadne.

Nenapadá náhodou někoho, v čem by mohl být problém. Program nic jiného nedělá, než že přijímá data ze dvou zařízení a zobrazuje je na hlavním formu.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Zkuste ošetřit případné chyby, bez identifikace místa, kde chyba vznikne se nikam nepohnete. Pokud bude chyba opravde ve spojení, tak zjistěte, zda po daném čase padá jen jedno nebo obě spojení (například možný timeout). A při chybě spojení znovu navažte automaticky.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Problém je, že první spadne jedno spojení na chybu při zpracovávání přijatého řetězce a po chvíli i druhé a program vypíše, že přestal správně pracovat a windows ho ukončí.

Zítra zkusím přidat pár Try Catch, abych měl bližší určení místa chyby. Ale podle všeho to vypadá, že dojde k vyjímce při přiřazení obrázku do pictureboxu.

Nemohl by být problém, když by došlo k přiřazení do obou pictureboxu od obou zařízení současně?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Až budeme mít konkrétní chybu, pak bude mít smysl to dál řešit. Takhle to jsou jen dohady.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Tak podle všeho to vypadá, že jsem pěkný blbec. Podle hlášení to vypadá, že nastalo přesně to, co jsem si říkal, že nikdy nenastane. V třídě pro každé zařízení mám proceduru, která vytváří bitmapu se stavovými informacemi, ale v té bitmapě jsou i hlášení systému. Takže do té procedury přistupuji z hlavního vlákna (systémové hlášení), ale i ze sekundárního vlákna (stavové informace), které vtvořil event SerialPort.DataReceive. A vypadá to, že se stane, že do té procedury vstoupí obě vlákna v jeden čas.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Tak jsem našel problém padání mé aplikace a je to trochu složitější.

Do procedury, která vytváří bitmapu (informační displej) přistupuji

- z hlavního vlákna: zavoláním některé veřejné procedury třídy (např. připojení na sériový port)

- ze sekundárního vlákna: Event SerialPort.DataReceive (po rozebrání přijatého řetězce se přijaté informace vykreslují do bitmapy)

- a aby toho nebylo málo, tak do stejné procedury přistupuje ještě System.Timers.Timer, který kontroluje spojení a v případě, kdy po dobu 3 sekund není přijat žádný telegram, vypíše chybové hlášení.

Veřejné procedury třídy běží na hlavním vládně, SerialPort.DataReceive běží na jiném vlákně a událost System.Timers.Timer Elapse běží taky na jiném vlákně.

Jak tuto situaci vyřešit, aby nedocházelo ke konfliktům mezi vlákny? Musím proceduru pro vytváření bitmapy 3x zkopírovat pod jiným názvem a vždy ji volat pouze z daného vlákna nebo se to dá nějak synchronizovat?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Dobrý den, nebylo by řešením použít background worker?

Měl by jste 2 backgroud workery (pro každý port 1) a ty by sbírali data. V jejich události ReportProgress (nebo tak nějak se to jmenuje) by posílalí hlavnímu vláknu přijatá data a to by je vykreslovalo nebo jinak zpracovalo.

Popř. by jste si mohl vytvořit třetí background worker který by data vykresloval.

Background worker beží v samostatném vlákně, ale má implicitně řešenou synchronizaci s hlavním vláknem, takže nemůže dojít ke kolizi.

Jinak já se snažím držet jednoduchého doporučení: Co si v daném vlákně nevytvořím, na to z něj nesahám :)

nahlásit spamnahlásit spam 0 odpovědětodpovědět

uzamykať na "zámku"

http://msdn.microsoft.com/en-us/library/...

Ten zámok môžte dať napríklad do metódy, ktorú voláte z troch rôznych miest, a potom musíte "zabaliť" celý kód metódy do bloku SyncLock.

Dôležité je tiež použiť spoločný objekt expression (viz. odkaz hore) pre všetky vlákna, inak blok Sync Lock nebude fungovať.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Nemel byste nahodou nejaky priklad pouziti SyncLock.

Nikdy jsem se se SyncLock nesetkal a nevim jak to spravne pouzit.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Otvorili ste vôbec ten odkaz, čo som poslal?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Omlouvám se, za poslední dotaz. Použití SYNCLOCK je jednoduché. Podle toho příkladu co jste mi poslal to funguje skvěle, moc děkuji.

Objevil se, ale ještě jeden problém.

Po několika hodinách provozu, aplikace zkolabuje a vypíše tuto chybu:

Application: Vazeni K1.exe

Framework Version: v4.0.30319

Description: The process was terminated due to an unhandled exception.

Exception Info: System.OutOfMemoryException

Stack:

at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)

at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)

at System.Threading._TimerCallback.PerformTimerCallback(System.Object)

Tady je základní část kódu tříky, kde vzniká tato chyba. Chyba se generuje v proceduře DoDisplay(), ale nevím proč. Procedura jen přiřadí obrázek do PictureBoxu. Nevidí v tom kódu někdo chybu?

Imports System 
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.IO.Ports
Imports System.Windows.Forms

Public Class Scale

#Region "Definice promennych"
    'Seriovy port
    Private _comPort As New SerialPort()
    Private WithEvents _displayWindow As PictureBox
    Private _picBck As Bitmap
    Private g As Graphics
    'Delegat prirazeni grafiky
    Delegate Sub DoDisplayDelegate()
#End Region

#Region "Nastaveni a cteni promennych"
    'Nastaveni displeje vahy
    Public Property Display() As PictureBox
        Get
            Return _displayWindow
        End Get
        Set(ByVal value As PictureBox)
            _displayWindow = value
            'Vytvoreni grafiky
            _picBck = New Bitmap(_displayWindow.Width, _displayWindow.Height)
            g = Graphics.FromImage(_picBck)
            'Nastaveni kvality grafiky
            g.SmoothingMode = SmoothingMode.AntiAlias
            g.InterpolationMode = InterpolationMode.HighQualityBicubic
            g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit
						'Vykresleni dpsleje
            DisplayData()
        End Set
    End Property
#End Region

#Region "Prijata data ze serioveho portu"
    Private Sub comPort_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)

        Try
            'Nacteni radku z bufferu serioveho portu
            _readData = _comPort.ReadLine

						'***************************************************************************************
						'Kod pro zpracovani prijateho retezce						
						'***************************************************************************************

            'Akturalizace displeje			
            DisplayData()         
        Catch ex As Exception
						'Chyba cteni serioveho portu
            _comPortErrorRead = True
            'Akturalizace displeje
            DisplayData()
        End Try
    End Sub
    
        'Kontrolni casovac komunikace s vahou
    Private Sub _comPortTimer_Elapsed(sender As Object, e As Timers.ElapsedEventArgs) Handles _comPortTimer.Elapsed
        'Kontrola zda se neceka na prvni telegram
        If _comPortWait = False Then
            'Citac preruseni komunikace
            _comPortCout += 1
            '3s neprisel zadny telegram z vahy
            If _comPortCout >= 30 And _comPortNotCom = False Then
                'Chyba komunikace
                _comPortNotCom = True
                'Reset portu
                ResetPort()
                'Vaha neni pripravena
                _ready = False
                RaiseEvent ScaleReady(False)
                'Update displeji
                DisplayData()
            End If
        End If
    End Sub
#End Region

#Region "DisplayData"
    Private Sub DisplayData()
        'Zamceni procedury
        SyncLock GetType(Scale)
        'Kontrola zda je prirazen displej
        If Not IsNothing(_displayWindow) Then
            'Vymazani grafiky
            g.Clear(Color.Transparent)
						'***************************************************************************************
						'Kod pro vytvoreni grafiky
						'***************************************************************************************
						'Update displeje
            DoDisplay()
        End If
			'Ukonceni zamku
      End SyncLock
    End Sub
#End Region

#Region "Vykresleni displeje vahy"
    Protected Sub DoDisplay()   
       If _displayWindow.InvokeRequired Then
            'jsme v jiném vlákně
            _displayWindow.Invoke(New DoDisplayDelegate(AddressOf DoDisplay))
            Exit Sub
        End If
    	  _displayWindow.Image = _picBck
    End Sub
#End Region

End Class

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Neviem, či správne používate ten Sync Lock. Skúste si do triedy pridať novú Shared premennú typu Object, v "Shared Sub New()" nastaviť na "New Object", a tú použiť pre blok Sync Lock.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

To zamrzání aplikace nesouvísí s stím SyncLock :-(

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Zdravím,

v dokumentaci se píše:

"You cannot change the value of lockobject while executing a SyncLock block. The mechanism requires that the lock object remain unchanged."

Takže lockobject nesmí být třída Scale, protože měníte v SyncLock její vlastnosti. Založte novou třídu typu Object, kterou použijete jako lockobject. Viz příklad na http://msdn.microsoft.com/cs-cz/library/...

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.
  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback