Problème affichage Label suite a Invoke

Résolu
MaxSourDev - 22 mai 2013 à 12:08
 Utilisateur anonyme - 27 mai 2013 à 10:22
--------------------------------------------------------------------------------

Bonjour,

Je développe actuellement une application qui communique avec une balance via le port RS232.

Pour cela j'ai créé une classe de Balance qui manipule un objet SerialPort.
La communication fonctionne bien. Les données sont bien récupérées.

Après avoir récupéré les données je souhaite les afficher dans un WinForm au travers d'un label. J'ai vérifié le label possède bien la bonne valeur dans sa propriété Text mais il n'est pas visible sur l'interface utilisateur (pourant le visible est a True). Il s'agit surement d'un problème de gestion de Thread mais je n'arrive pas a comprendre d'où cela provient.

Voici le code de ma classe Balance :

 
Imports System.IO.Ports
Imports System.Windows.Forms
 
Public Class Balance
    Private comPort As SerialPort
 
    'control dans lequel on veux afficher le résultat'
    Private _display As Control
    Private Delegate Sub SetTextCallback(ByVal text As String)
 
 
    Public Property Display() As Control
        Get
            Return _display
        End Get
        Set(ByVal value As Control)
            _display = value
        End Set
    End Property
 
 
    'initialisation du port COM'
    Public Sub New()
        comPort = New SerialPort
        comPort.PortName = "COM1"
        comPort.Parity = IO.Ports.Parity.None
        comPort.BaudRate = 9600
        comPort.StopBits = IO.Ports.StopBits.One
        comPort.RtsEnable = True
 
        AddHandler comPort.DataReceived, AddressOf dataReceived
 
    End Sub
 
    'Ouverture du port COM'
    Public Sub Open()
        If Not comPort.IsOpen Then
            comPort.Open()
        End If
    End Sub
 
 
    'Fermeture du port COM'
    Public Sub Close()
        If comPort.IsOpen Then
            comPort.Close()
        End If
    End Sub
 
 
    'Demande de récupération de la pesé sur la balance'
    Public Sub Peser()
        comPort.Write("PRT" & Chr(13) & Chr(10))
    End Sub
 
    'Procédure éxécuté quand la balance répond'
    Private Sub dataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
        Dim mess As String = comPort.ReadExisting()
 
        'Permet de formater la valeur afin d enlever les caractère indésiable (signe, unité, blanc)'
        Dim nb As Integer
        For Each c In mess
            If Not Integer.TryParse(c, nb) Then
                If c <> "." Then
                    mess = mess.Replace(c, "")
                End If
            End If
        Next
        mess = mess.Trim
 
        'on souhaite afficher le résultat' 
        DisplayData(mess)
 
    End Sub
 
    'procédure qui permet affichage dans le control souhaité'
    Private Sub DisplayData(Text As String)
        If _display.InvokeRequired Then
            _display.Invoke(New SetTextCallback(AddressOf DisplayData), New Object() {(Text)})
        Else
            _display.Text = Text
        End If
    End Sub
 
 
End Class


Avez vous une idée ?

26 réponses

Je viens de trouver le problème.

Enfaite après la première réception d'information il recevait encore d'autre information malgré que tout était correcte dès la première réception du coup il me changeait deux fois le text du label.

J'ai donc viré le handler une fois qu'il est passé une fois dans le dataReceived et ça fonctionne.

En tout cas merci de l'interet que vous avez porté à mon problème et de votre réactivité.

A bientôt
3
Utilisateur anonyme
22 mai 2013 à 17:59
Si ta classe est destinée plus tard à devenir une DLL, tu devrais implémenter un événement plutôt que d'y intégrer un contrôle à moins qu'elle n'hérite de Label. Cet événement (thread safe) fournirait le texte à afficher sur un contrôle d'un form par exemple.
3
Utilisateur anonyme
23 mai 2013 à 20:47
J'ai une méthode que j'utilise personnellement qui n'est peut-être pas la meilleure mais qui fonctionne.
Je l'ai adaptée à ton cas et commentée.
Voici le code de la dll :
Option Strict On
Imports System.Windows.Forms

Public Class Class1
    'timer simulant un thread
    Dim WithEvents tmr As New Timers.Timer(5000)
    'délégué avec signature du timer
    Public Delegate Sub tmr_ElapsedDelegate(ByVal sender As Object, ByVal e As Timers.ElapsedEventArgs)
    'événement
    Public Event PoidsReceived(ByVal sender As Object, ByVal e As PoidsReceivedEventArgs)
    'on mémorise ici le formulaire (ou autre d'ailleurs)
    Dim _control As Control

    'on fait passer le formulaire (ou autre) dans le constructeur de la classe
    Sub New(ByVal controle As Control)
        _control = controle
        'on démarre le timer (thread)
        tmr.Start()
    End Sub

    Private Sub tmr_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles tmr.Elapsed
        'si invoke est requis...
        If _control.InvokeRequired Then
            'on invoque le délégué (qui exécutera cette même sub) en lui passant les paramètres sender et e
            _control.Invoke(New tmr_ElapsedDelegate(AddressOf tmr_Elapsed), sender, e)
        Else
            'on déclenche l'événement qui est maintenant thread safe
            RaiseEvent PoidsReceived(Me, New PoidsReceivedEventArgs(50))
        End If
    End Sub

    'argument d'événement
    Public Class PoidsReceivedEventArgs
        Inherits EventArgs

        Private _poids As Decimal

        Public Property Poids() As Decimal
            Get
                Return _poids
            End Get
            Set(ByVal value As Decimal)
                _poids = value
            End Set
        End Property

        Public Sub New(ByVal poids As Decimal)
            _poids = poids
        End Sub
    End Class

End Class


Et voici le code du formulaire de test :

Option Strict On
'import de la dll (faire en plus une référence à la dll dans les propriétés du projet)
Imports ClassLibrary1

Public Class Form1
    'déclaration de la dll avec ses événements
    Dim WithEvents maclasse As ClassLibrary1.Class1

    'déclaration d'un label à intégrer au form
    Dim lblPoids As New Label With {.Parent = Me}

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'instance de la dll en lui passant le formulaire 
        maclasse = New ClassLibrary1.Class1(Me)
    End Sub

    'événement PoidsReceived
    Private Sub maclasse_PoidsReceived(ByVal sender As Object, ByVal e As ClassLibrary1.Class1.PoidsReceivedEventArgs) Handles maclasse.PoidsReceived
        lblPoids.Text = e.Poids.ToString
    End Sub
End Class
3
Utilisateur anonyme
24 mai 2013 à 12:48
Me reste plus qu'a trouver une solution au cas : Aucune donnée n'est retourné. Je pense qu'un timer dans la winform devrait suffir pour ce point.

Je ne connais pas ton protocole mais s'il s'agit d'un délai, pourquoi ne pas mettre ce timer dans la dll et déclencher un nouvel événement ?
3

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
Je viens de résoudre mon problème. Il semblerait que le Form.Timer ne soit pas fait pour le multi-threading et de ce fait il est impossible d'utiliser 2 Timer en parallèle. En revanche le System.Timers.Timer convient tout à fait à ce scénario. J'ai donc changé le type de mes timers, changé l'évenement (ce n'est plus Tick mais Elapsed) et ça fonctionne très bien.

Je pense que j'en ai fini de cette classe. Merci a tous de votre aide et à bientôt.
3
Utilisateur anonyme
22 mai 2013 à 12:20
Salut,

Private _display As Control

On parle bien d'un label ? Pourquoi avoir déclaré l'objet en type Control ?

Ton délégué ainsi que ta sub DisplayData attendent un paramètre de type String et tu leur envoies un type Object :
New Object() {(Text)}
Text simplement suffirait.
Active Option Strict dans les propriétés de ton projet.
0
Utilisateur anonyme
22 mai 2013 à 12:24
De plus, la classe doit être instanciée et ajoutée au form.
Dim MonLabel as new Label() with {.parent = me}

ou :
Dim MonLabel as new Label()
Me.controls.add(monlabel)
0
Bonjour,

Merci de tes réponses.

J'ai mis un Type Control afin de rendre mon code plus générique. Il est possible que je réutilise cette classe dans l'avenir et je vais surement en faire une DLL quand j'aurai finit de la développer. Etant donné que Label hérite de Control cela ne pose aucun problème (du moins je pense). Pour info au début j'avais mis un type Label et j'avais le même comportement.

Pour le délégué je viens de corriger en effet c'est une erreur bien que avec L'héritage ça ddevrait fonctionner.

Pour ce qui est de l'utilisation de la classe voici le code contenu dans mon Form :

 Private Sub btnPeser_Click(sender As System.Object, e As System.EventArgs) Handles btnPesAvecColle1.Click,
                                                                                       btnPesAvecColle2.Click,
                                                                                       btnPesSSColle1.Click,
                                                                                       btnPesSSColle2.Click

        Dim btn As Button = sender
        Dim lbl As Label = Nothing
        Dim affiche As Boolean = False
        Dim poidsText As String = ""
        Dim err As Boolean = False

        Select Case btn.Name
            Case "btnPesSSColle1"
                lbl = tbPoidsSSColle1

            Case "btnPesSSColle2"
                lbl = tbPoidsSSColle2

            Case "btnPesAvecColle1"
                lbl = tbPoidsAvecColle1

            Case "btnPesAvecColle2"
                lbl = tbPoidsAvecColle2
        End Select

        AddHandler lbl.TextChanged, AddressOf tbPoids_TextChanged

        Try

            btn.Enabled = False

            bal.Display = lbl

            bal.Peser()

        Catch ex As Exception
            err = True
        End Try
   ' code de vérification qui n'influ pas derrière ...
End Sub
0
Utilisateur anonyme
22 mai 2013 à 13:25
Bonjour,

Tout comme banana32 a laissé entendre, ne vois pas pourquoi se compliquer la vie avec:


'control dans lequel on veux afficher le résultat'
Private _display As Control
Private Delegate Sub SetTextCallback(ByVal text As String)

Public Property Display() As Control
Get
Return _display
End Get
Set(ByVal value As Control)
_display = value
End Set
End Property

'procédure qui permet affichage dans le control souhaité'
Private Sub DisplayData(ByVal Text As String)
[del]If _display.InvokeRequired Then
_display.Invoke(New SetTextCallback(AddressOf DisplayData), New Object() {(Text)})/del
Else
_display.Text = Text
End If
End Sub

- Ainsi que quelques avertissements à ne pas prendre à la légère:
'initialisation du port COM'
(Public Sub New()' dans le type généré par le concepteur 'WindowsApplication1.Balance' doit appeler la méthode InitializeComponent.)
'Bien entendu il faut remplacer toutes les procédure qui font appel à 'New' par 'NewCom'
Public Sub New()
  Public Sub NawCom()
        comPort = New SerialPort
        comPort.PortName = "COM1"
        comPort.Parity = IO.Ports.Parity.None
        comPort.BaudRate = 9600
        comPort.StopBits = IO.Ports.StopBits.One
        comPort.RtsEnable =  True
        AddHandler comPort.DataReceived, AddressOf dataReceived
    End Sub

- Et:
'Fermeture du port COM'
(La sub 'Close' masque un membre surchargeable déclaré dans la class 'Form' de base.)
'Bien entendu il faut remplacer toutes les procédure qui font appel à 'Close' par 'CloseCom'
Public Sub Close()
  Public Sub CloseCom()
        If comPort.IsOpen Then
            comPort.Close()
        End If
    End Sub

- Et u avais raison quant à la gestion de Thread, pour ce faire tu vas insérer un Timer (Timer1) à partir de ta boite à outils sur ta form.
Et ton code pourrait se résumer finalement à ça:

Option Explicit On
Option Strict On
Imports System.IO.Ports
Imports System.Windows.Forms

Public Class Balance
    Private comPort As SerialPort
    Dim MonLabel As New Label()  'control dans lequel on veux afficher le résultat'
    'Détermine les propriétés du port
    Public Sub NawCom()
        comPort  = New SerialPort
        comPort.PortName = "COM1"
        comPort.Parity = IO.Ports.Parity.None
        comPort.BaudRate = 9600
        comPort.StopBits = IO.Ports.StopBits.One
        comPort.RtsEnable = True
        AddHandler comPort.DataReceived, AddressOf dataReceived
    End Sub
    'Ouverture du port COM'
    Public Sub Open()
        If Not comPort.IsOpen Then
            comPort.Open()
        End If
    End Sub
    'Fermeture du port
    Public Sub CloseCom()
        If comPort.IsOpen Then
            comPort.Close()
        End If
    End Sub
    'Demande de récupération de la pesé sur la balance'
    Public Sub Peser()
        comPort.Write("PRT" & Chr(13) & Chr(10))
    End Sub
    'Procédure éxécuté quand la balance répond'
    Private Sub dataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
        Timer1.Enabled = True
    End Sub
    'Démarrage de la form
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Timer1.Interval = 100
        Timer1.Enabled = False
        Me.Controls.Add(MonLabel)
    End Sub
    'Ce qui execute le Timer
    Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        Timer1.Enabled = False
        Dim mess As String = comPort.ReadExisting()
        'Permet de formater la valeur afin d enlever les caractère indésiable (signe, unité, blanc)'
        Dim nb As Integer
        For Each c In mess
            If Not Integer.TryParse(c, nb) Then
                If c <> "." Then
                    mess = mess.Replace(c, "")
                End If
            End If
        Next
        mess = mess.Trim
        'on souhaite afficher le résultat' 
        MonLabel.Text = mess
    End Sub
End Class



Cordialement


CF2i - Guadeloupe
Ingénierie Informatique
0
Je pense que je n'ai pas été assez clair.

Balance n'est qu'une classe utilitaire servant à utiliser les fonctionnalités de SerialPort.

J'ai un formulaire frmPrincipal qui utilise l'objet une instance d'un objet Balance. L'initialisation du formulaire se fait correctement. L'application fonctionne bien sauf cette histoire de label qui disparait suite à la mise à jour du texte quand la balance reçoit des données.

Je pense vraiment que le thread graphique n'arrive pas a redessiner le label mais je ne compred pas pourquoi. D'ailleur 1 fois sur 10 il s'affiche.

J'ai mis un espion sur le label et un point d'arret dans le TextChanged et vérifié toute les propriétés de mon label est sont toute correcte y compris le Text.

Je me suis inspiré d'un code source trouvé sur le net qui fonctionne très bien sauf qu'il utilie un RichTextBox et non un Label mais sinon aucun différence.
0
En fait, le seul problème vient du refresh graphique de mon Control (Label pour ce cas ici).

Pourquoi utiliser un Timer ? Juste à décaler le changement de texte de 100ms ?

Voici le code dont je me suis inspiré pour créer ma classe :

Classe CommManager qui gère le port com :

Imports System
Imports System.Text
Imports System.Drawing
Imports System.IO.Ports
Imports System.Windows.Forms
'*****************************************************************************************
'                           LICENSE INFORMATION
'*****************************************************************************************
'   PCCom.SerialCommunication Version 1.0.0.0
'   Class file for managing serial port communication
'
'   Copyright (C) 2007  
'   Richard L. McCutchen 
'   Email: richard@psychocoder.net
'   Created: 20OCT07
'
'   This program is free software: you can redistribute it and/or modify
'   it under the terms of the GNU General Public License as published by
'   the Free Software Foundation, either version 3 of the License, or
'   (at your option) any later version.
'
'   This program is distributed in the hope that it will be useful,
'   but WITHOUT ANY WARRANTY; without even the implied warranty of
'   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
'   GNU General Public License for more details.
'
'   You should have received a copy of the GNU General Public License
'   along with this program.  If not, see <http://www.gnu.org/licenses/>.
'*****************************************************************************************


Public Class CommManager
#Region "Manager Enums"
    ''' <summary>
    ''' enumeration to hold our transmission types
    ''' </summary>
    Public Enum TransmissionType
        Text
        Hex
    End Enum

    ''' <summary>
    ''' enumeration to hold our message types
    ''' </summary>
    Public Enum MessageType
        Incoming
        Outgoing
        Normal
        Warning
        [Error]
    End Enum
#End Region

#Region "Manager Variables"
    'property variables
    Private _baudRate As String = String.Empty
    Private _parity As String = String.Empty
    Private _stopBits As String = String.Empty
    Private _dataBits As String = String.Empty
    Private _portName As String = String.Empty
    Private _transType As TransmissionType
    Private _displayWindow As RichTextBox
    Private _msg As String
    Private _type As MessageType
    'global manager variables
    Private MessageColor As Color() = {Color.Blue, Color.Green, Color.Black, Color.Orange, Color.Red}
    Private comPort As New SerialPort()
    Private write As Boolean = True
#End Region

#Region "Manager Properties"
    ''' <summary>
    ''' Property to hold the BaudRate
    ''' of our manager class
    ''' </summary>
    Public Property BaudRate() As String
        Get
            Return _baudRate
        End Get
        Set(ByVal value As String)
            _baudRate = value
        End Set
    End Property

    ''' <summary>
    ''' property to hold the Parity
    ''' of our manager class
    ''' </summary>
    Public Property Parity() As String
        Get
            Return _parity
        End Get
        Set(ByVal value As String)
            _parity = value
        End Set
    End Property

    ''' <summary>
    ''' property to hold the StopBits
    ''' of our manager class
    ''' </summary>
    Public Property StopBits() As String
        Get
            Return _stopBits
        End Get
        Set(ByVal value As String)
            _stopBits = value
        End Set
    End Property

    ''' <summary>
    ''' property to hold the DataBits
    ''' of our manager class
    ''' </summary>
    Public Property DataBits() As String
        Get
            Return _dataBits
        End Get
        Set(ByVal value As String)
            _dataBits = value
        End Set
    End Property

    ''' <summary>
    ''' property to hold the PortName
    ''' of our manager class
    ''' </summary>
    Public Property PortName() As String
        Get
            Return _portName
        End Get
        Set(ByVal value As String)
            _portName = value
        End Set
    End Property

    ''' <summary>
    ''' property to hold our TransmissionType
    ''' of our manager class
    ''' </summary>
    Public Property CurrentTransmissionType() As TransmissionType
        Get
            Return _transType
        End Get
        Set(ByVal value As TransmissionType)
            _transType = value
        End Set
    End Property

    ''' <summary>
    ''' property to hold our display window
    ''' value
    ''' </summary>
    Public Property DisplayWindow() As RichTextBox
        Get
            Return _displayWindow
        End Get
        Set(ByVal value As RichTextBox)
            _displayWindow = value
        End Set
    End Property

    ''' <summary>
    ''' Property to hold the message being sent
    ''' through the serial port
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Message() As String
        Get
            Return _msg
        End Get
        Set(ByVal value As String)
            _msg = value
        End Set
    End Property

    ''' <summary>
    ''' Message to hold the transmission type
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Type() As MessageType
        Get
            Return _type
        End Get
        Set(ByVal value As MessageType)
            _type = value
        End Set
    End Property
#End Region

#Region "Manager Constructors"
    ''' <summary>
    ''' Constructor to set the properties of our Manager Class
    ''' </summary>
    ''' Desired BaudRate


    ''' Desired Parity


    ''' Desired StopBits


    ''' Desired DataBits


    ''' Desired PortName


    Public Sub New(ByVal baud As String, ByVal par As String, ByVal sBits As String, ByVal dBits As String, ByVal name As String, ByVal rtb As RichTextBox)
        _baudRate = baud
        _parity = par
        _stopBits = sBits
        _dataBits = dBits
        _portName = name
        _displayWindow = rtb
        'now add an event handler
        AddHandler comPort.DataReceived, AddressOf comPort_DataReceived
    End Sub

    ''' <summary>
    ''' Comstructor to set the properties of our
    ''' serial port communicator to nothing
    ''' </summary>
    Public Sub New()
        _baudRate = String.Empty
        _parity = String.Empty
        _stopBits = String.Empty
        _dataBits = String.Empty
        _portName = "COM1"
        _displayWindow = Nothing
        'add event handler
        AddHandler comPort.DataReceived, AddressOf comPort_DataReceived
    End Sub
#End Region

#Region "WriteData"
    Public Sub WriteData(ByVal msg As String)
        Select Case CurrentTransmissionType
            Case TransmissionType.Text
                'first make sure the port is open
                'if its not open then open it
                If Not (comPort.IsOpen = True) Then
                    comPort.Open()
                End If
                'send the message to the port
                comPort.Write(msg & Chr(13) & Chr(10))
                'display the message
                _type = MessageType.Outgoing
                _msg = msg + "" + Environment.NewLine + ""
                DisplayData(_type, _msg)
                Exit Select
            Case TransmissionType.Hex
                Try
                    'convert the message to byte array
                    Dim newMsg As Byte() = HexToByte(msg)
                    If Not write Then
                        DisplayData(_type, _msg)
                        Exit Sub
                    End If
                    'send the message to the port
                    comPort.Write(newMsg, 0, newMsg.Length)
                    'convert back to hex and display
                    _type = MessageType.Outgoing
                    _msg = ByteToHex(newMsg) + "" + Environment.NewLine + ""
                    DisplayData(_type, _msg)
                Catch ex As FormatException
                    'display error message
                    _type = MessageType.Error
                    _msg = ex.Message + "" + Environment.NewLine + ""
                    DisplayData(_type, _msg)
                Finally
                    _displayWindow.SelectAll()
                End Try
                Exit Select
            Case Else
                'first make sure the port is open
                'if its not open then open it
                If Not (comPort.IsOpen = True) Then
                    comPort.Open()
                End If
                'send the message to the port
                comPort.Write(msg)
                'display the message
                _type = MessageType.Outgoing
                _msg = msg + "" + Environment.NewLine + ""
                DisplayData(MessageType.Outgoing, msg + "" + Environment.NewLine + "")
                Exit Select
        End Select
    End Sub
#End Region

#Region "HexToByte"
    ''' <summary>
    ''' method to convert hex string into a byte array
    ''' </summary>
    ''' string to convert


    ''' <returns>a byte array</returns>
    Private Function HexToByte(ByVal msg As String) As Byte()
        If msg.Length Mod 2 = 0 Then
            'remove any spaces from the string
            _msg = msg
            _msg = msg.Replace(" ", "")
            'create a byte array the length of the
            'divided by 2 (Hex is 2 characters in length)
            Dim comBuffer As Byte() = New Byte(_msg.Length / 2 - 1) {}
            For i As Integer = 0 To _msg.Length - 1 Step 2
                comBuffer(i / 2) = CByte(Convert.ToByte(_msg.Substring(i, 2), 16))
            Next
            write = True
            'loop through the length of the provided string
            'convert each set of 2 characters to a byte
            'and add to the array
            'return the array
            Return comBuffer
        Else
            _msg = "Invalid format"
            _type = MessageType.Error
            ' DisplayData(_Type, _msg)
            write = False
            Return Nothing
        End If
    End Function
#End Region

#Region "ByteToHex"
    ''' <summary>
    ''' method to convert a byte array into a hex string
    ''' </summary>
    ''' byte array to convert


    ''' <returns>a hex string</returns>
    Private Function ByteToHex(ByVal comByte As Byte()) As String
        'create a new StringBuilder object
        Dim builder As New StringBuilder(comByte.Length * 3)
        'loop through each byte in the array
        For Each data As Byte In comByte
            builder.Append(Convert.ToString(data, 16).PadLeft(2, "0"c).PadRight(3, " "c))
            'convert the byte to a string and add to the stringbuilder
        Next
        'return the converted value
        Return builder.ToString().ToUpper()
    End Function
#End Region

#Region "DisplayData"
    ''' <summary>
    ''' Method to display the data to and
    ''' from the port on the screen
    ''' </summary>
    ''' <remarks></remarks>
    <STAThread()> _
    Private Sub DisplayData(ByVal type As MessageType, ByVal msg As String)
        _displayWindow.Invoke(New EventHandler(AddressOf DoDisplay))
    End Sub
#End Region

#Region "OpenPort"
    Public Function OpenPort() As Boolean
        Try
            'first check if the port is already open
            'if its open then close it
            If comPort.IsOpen = True Then
                comPort.Close()
            End If

            'set the properties of our SerialPort Object
            comPort.BaudRate = Integer.Parse(_baudRate)
            'BaudRate
            comPort.DataBits = Integer.Parse(_dataBits)
            'DataBits
            comPort.StopBits = DirectCast([Enum].Parse(GetType(StopBits), _stopBits), StopBits)
            'StopBits
            comPort.Parity = DirectCast([Enum].Parse(GetType(Parity), _parity), Parity)
            'Parity
            comPort.PortName = _portName
            'PortName
            'now open the port
            comPort.Open()
            'display message
            _type = MessageType.Normal
            _msg = "Port opened at " + DateTime.Now + "" + Environment.NewLine + ""
            DisplayData(_type, _msg)
            'return true
            Return True
        Catch ex As Exception
            DisplayData(MessageType.[Error], ex.Message)
            Return False
        End Try
    End Function
#End Region

#Region " ClosePort "
    Public Sub ClosePort()
        If comPort.IsOpen Then
            _msg = "Port closed at " + DateTime.Now + "" + Environment.NewLine + ""
            _type = MessageType.Normal
            DisplayData(_type, _msg)
            comPort.Close()
        End If
    End Sub
#End Region

#Region "SetParityValues"
    Public Sub SetParityValues(ByVal obj As Object)
        For Each str As String In [Enum].GetNames(GetType(Parity))
            DirectCast(obj, ComboBox).Items.Add(str)
        Next
    End Sub
#End Region

#Region "SetStopBitValues"
    Public Sub SetStopBitValues(ByVal obj As Object)
        For Each str As String In [Enum].GetNames(GetType(StopBits))
            DirectCast(obj, ComboBox).Items.Add(str)
        Next
    End Sub
#End Region

#Region "SetPortNameValues"
    Public Sub SetPortNameValues(ByVal obj As Object)

        For Each str As String In SerialPort.GetPortNames()
            DirectCast(obj, ComboBox).Items.Add(str)
        Next
    End Sub
#End Region

#Region "comPort_DataReceived"
    ''' <summary>
    ''' method that will be called when theres data waiting in the buffer
    ''' </summary>
    ''' 


    ''' 


    Private Sub comPort_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
        'determine the mode the user selected (binary/string)
        Select Case CurrentTransmissionType
            Case TransmissionType.Text
                'user chose string
                'read data waiting in the buffer
                Dim msg As String = comPort.ReadExisting()
                'display the data to the user
                _type = MessageType.Incoming
                _msg = msg
                DisplayData(MessageType.Incoming, msg + "" + Environment.NewLine + "")
                Exit Select
            Case TransmissionType.Hex
                'user chose binary
                'retrieve number of bytes in the buffer
                Dim bytes As Integer = comPort.BytesToRead
                'create a byte array to hold the awaiting data
                Dim comBuffer As Byte() = New Byte(bytes - 1) {}
                'read the data and store it
                comPort.Read(comBuffer, 0, bytes)
                'display the data to the user
                _type = MessageType.Incoming
                _msg = ByteToHex(comBuffer) + "" + Environment.NewLine + ""
                DisplayData(MessageType.Incoming, ByteToHex(comBuffer) + "" + Environment.NewLine + "")
                Exit Select
            Case Else
                'read data waiting in the buffer
                Dim str As String = comPort.ReadExisting()
                'display the data to the user
                _type = MessageType.Incoming
                _msg = str + "" + Environment.NewLine + ""
                DisplayData(MessageType.Incoming, str + "" + Environment.NewLine + "")
                Exit Select
        End Select
    End Sub
#End Region

#Region "DoDisplay"
    Private Sub DoDisplay(ByVal sender As Object, ByVal e As EventArgs)
        _displayWindow.SelectedText = String.Empty
        _displayWindow.SelectionFont = New Font(_displayWindow.SelectionFont, FontStyle.Bold)
        _displayWindow.SelectionColor = MessageColor(CType(_type, Integer))
        _displayWindow.AppendText(_msg)
        _displayWindow.ScrollToCaret()
    End Sub
#End Region
End Class


code de la l'interface Utilisateur :

Imports PCComm
Public Class frmMain
    Private comm As New CommManager()
    Private transType As String = String.Empty

    Private Sub cboPort_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cboPort.SelectedIndexChanged
        comm.PortName = cboPort.Text()
    End Sub

    ''' <summary>
    ''' Method to initialize serial port
    ''' values to standard defaults
    ''' </summary>
    Private Sub SetDefaults()
        cboPort.SelectedIndex = 0
        cboBaud.SelectedText = "9600"
        cboParity.SelectedIndex = 0
        cboStop.SelectedIndex = 1
        cboData.SelectedIndex = 1
    End Sub

    ''' <summary>
    ''' methos to load our serial
    ''' port option values
    ''' </summary>
    Private Sub LoadValues()
        comm.SetPortNameValues(cboPort)
        comm.SetParityValues(cboParity)
        comm.SetStopBitValues(cboStop)
    End Sub

    ''' <summary>
    ''' method to set the state of controls
    ''' when the form first loads
    ''' </summary>
    Private Sub SetControlState()
        rdoText.Checked = True
        cmdSend.Enabled = False
        cmdClose.Enabled = False
    End Sub

    Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        LoadValues()
        SetDefaults()
        SetControlState()
    End Sub

    Private Sub cmdClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdClose.Click
        comm.ClosePort()
        SetControlState()
        SetDefaults()
    End Sub

    Private Sub cmdOpen_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdOpen.Click
        comm.Parity = cboParity.Text
        comm.StopBits = cboStop.Text
        comm.DataBits = cboData.Text
        comm.BaudRate = cboBaud.Text
        comm.DisplayWindow = rtbDisplay
        comm.OpenPort()

        cmdOpen.Enabled = False
        cmdClose.Enabled = True
        cmdSend.Enabled = True
    End Sub

    Private Sub cmdSend_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdSend.Click
        comm.Message = txtSend.Text
        comm.Type = CommManager.MessageType.Normal
        comm.WriteData(txtSend.Text)
        txtSend.Text = String.Empty
        txtSend.Focus()
    End Sub

    Private Sub rdoHex_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles rdoHex.CheckedChanged
        If rdoHex.Checked() Then
            comm.CurrentTransmissionType = PCComm.CommManager.TransmissionType.Hex
        Else
            comm.CurrentTransmissionType = PCComm.CommManager.TransmissionType.Text
        End If
    End Sub

    Private Sub cboBaud_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cboBaud.SelectedIndexChanged
        comm.BaudRate = cboBaud.Text()
    End Sub

    Private Sub cboParity_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cboParity.SelectedIndexChanged
        comm.Parity = cboParity.Text()
    End Sub

    Private Sub cboStop_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cboStop.SelectedIndexChanged
        comm.StopBits = cboStop.Text()
    End Sub

    Private Sub cboData_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cboData.SelectedIndexChanged
        comm.StopBits = cboStop.Text()
    End Sub

End Class
0
Utilisateur anonyme
22 mai 2013 à 14:05
Ton problème au départ est le thread du serialport, ta form est un Thread, ton SerialPort est un autre Thread, il ne peut pas accéder aux objets de ta form.

Tu ne peux pas faire (par exemple):
Private Sub dataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
        MonLabel.Text = comPort.readexisting
    End Sub

Ça ne fonctionnera pas...

Fais une nouvelle Form et tu testes ça:
(plus besoin de glisser le Timer je l'ai crée dynamiquement)

Option Explicit On
Option Strict On
Imports System.IO.Ports
Imports System.Windows.Forms

Public Class Balance
    Dim Timer As New Timer
    Private comPort As SerialPort
    Dim MonLabel As New Label()  'control dans lequel on veux afficher le résultat'

    'Démarrage de la form
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Timer.Interval = 100
        Timer.Enabled = False
        Me.Controls.Add(MonLabel)
        'MonLabel.Left = ...
        'MonLabel.Top = ...
        AddHandler Timer.Tick, AddressOf Timer_Tick
    End Sub

    'Détermine les propriétés du port
    Public Sub NawCom()
        comPort = New SerialPort
        comPort.PortName = "COM1"
        comPort.Parity = IO.Ports.Parity.None
        comPort.BaudRate = 9600
        comPort.StopBits = IO.Ports.StopBits.One
        comPort.RtsEnable = True
        AddHandler comPort.DataReceived, AddressOf dataReceived
    End Sub

    'Ouverture du port COM'
    Public Sub Open()
        If Not comPort.IsOpen Then
            comPort.Open()
        End If
    End Sub

    'Fermeture du port
    Public Sub CloseCom()
        If comPort.IsOpen Then
            comPort.Close()
        End If
    End Sub

    'Demande de récupération de la pesé sur la balance'
    Public Sub Peser()
        comPort.Write("PRT" & vbCrLf)
    End Sub

    'Procédure éxécuté quand la balance répond'
    Private Sub dataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
        Timer.Enabled = True
    End Sub

    'Ce qui execute le Timer
    Private Sub Timer_Tick(ByVal sender As Object, ByVal e As System.EventArgs)
        Timer.Enabled = False
        Dim mess As String = comPort.ReadExisting()
        'Permet de formater la valeur afin d enlever les caractère indésiable (signe, unité, blanc)'
        Dim nb As Integer
        For Each c In mess
            If Not Integer.TryParse(c, nb) Then
                If c <> "." Then
                    mess = mess.Replace(c, "")
                End If
            End If
        Next
        mess = mess.Trim
        'on souhaite afficher le résultat' 
        MonLabel.Text = mess
    End Sub
End Class


Cordialement


CF2i - Guadeloupe
Ingénierie Informatique
0
Utilisateur anonyme
22 mai 2013 à 14:13
décaler le changement de texte de 100ms


Je ne connais pas ta balance (j'en ai pas une sous la main) et je ne connais pas la longueur des chainnes qu'elle doit envoyer.
Et par expérience, je sais qu'il est conseillé d'attendre quelques millisecondes afin que le buffer soit rempli, sous peine de perdre des données.

A 9600 Bauds, 100 ms devraient suffire à l'envoi d'un nombre conséquent de caractères.


Cordialement


CF2i - Guadeloupe
Ingénierie Informatique
0
Il n'y a aucune perte de données elle sont bien retourné entièrement, d'ailleur la propriété Text du Label contient la valeur indiqué sur la balance.

Pour la gestion des Thread différents, cette méthode est la pour justement faire un pont entre les 2 threads :

Private Sub DisplayData(Text As String)
        If _display.InvokeRequired Then
            _display.Invoke(New SetTextCallback(AddressOf DisplayData), New Object() {(Text)})
        Else
            _display.Text = Text
        End If
    End Sub


Le invoke permet de faire référence au thread qui a créé le Label et vu que le label provient de ma Form c'est bien le Thread Graphique (si j'avait un problème de communication inter-thread j'aurai une erreur).
0
Utilisateur anonyme
22 mai 2013 à 14:37
J'ai pas de balance pour tester ni ta méthode ni la mienne d'ailleurs,(il y a que toi qui peux tester) et je ne suis pas la pour te "forcer" à choisir quoi que ce soit non plus...
Je t'ai juste dit de tester pour voir ce que ça donne.


Cordialement


CF2i - Guadeloupe
Ingénierie Informatique
0
Utilisateur anonyme
22 mai 2013 à 14:45
Ouais ....

Je rappelle juste ce que tu as dit antérieurement, contradictoirement:
"Il n'y a aucune perte de données elle sont bien retourné entièrement"

- En suite tu dis:
"après la première réception d'information il recevait encore d'autre information"
- Pourquoi à ton avis (d'après mon expérience) j'avais mis un timer à 100ms ???

"Je viens de trouver le problème."
C'est bien que tu l'as trouvé ton problème...

Allez, bon courage.


Cordialement


CF2i - Guadeloupe
Ingénierie Informatique
0
En effet il y afait une perte de données mais je n'avais pas vu qu'il passait plusieurs fois dans le dataReceived c'est une piste que j'avais omis de tester. En effet ton timer résou le problème du temps d'attente pour avoir toute les données. Peut être que mon Buffer est trop petit aussi.
0
Utilisateur anonyme
22 mai 2013 à 14:54
Peut être que mon Buffer est trop petit aussi


Personnellement je l'aurais gardé avec sa taille par défaut 4096(je crois) car je pense que le constructeur de la balance ne se serait pas amusé à augmenter le nombre de données, hors taille standard, sous peine d'avoir pas mal de problèmes avec les développeurs et les dépassements de capacité.


Cordialement


CF2i - Guadeloupe
Ingénierie Informatique
0
Bonne idée,

Je n'y avais pas pensé.

Merci beaucoup
0
@banana32 : J'ai implémenté un event comme tu me l'a suggéré mais je ne suis pas sur de ce que j'ai fais concernant la partie ThreadSafe de l'event

J'ai créé une classe qui hérite de eventArgs :
Public Class PoidsReceivedEventArgs
    Inherits EventArgs

    Private _poids As Decimal
    Public Property Poids() As Decimal
        Get
            Return _poids
        End Get
        Set(ByVal value As Decimal)
            _poids = value
        End Set
    End Property


    Public Sub New(poids As Decimal)
        MyBase.New()
        Me.Poids = poids
    End Sub

End Class


J'ai créé un délégué :
Public Delegate Sub PoidsReceivedEventHandler(sender As Object, e As PoidsReceivedEventArgs)


J'ai créé un Event :
Public Event PoidsReceived As PoidsReceivedEventHandler


Je déclenche l'évenement dans le dataReceived.
Je fais mes traitements dans mon form dans une procédure qui écoute cet évenement.

Du coup j'avais des erreur de multi-threading
Donc j'ai fait :

 If monLabel.InvokeRequired Then
       monLabel.Invoke(Sub() monLabel.Enabled = True)
 Else
       monLabel.Enabled = True
 End If


Est-ce que cela suffit à le rendre ThreadSafe ?
0
Rejoignez-nous