2016-12-03 2 views
-2

Je surveillerais les données reçues sur un port série avec mon ordinateur et un Arduino. Sur l'arduino, le sketch envoie à l'USB la chaîne "aabb" evry 300ms. Avec pc je veux écouter, et en temps réel imprimer la chaîne dans un contrôle (Textbox). Pour ce faire, je crée un nouveau thread qui écoute dans une boucle ce qui arrive dans le port série, et quand cela arrive, il écrit par Invoke la chaîne dans la zone de texte. Les procédures fonctionnent si je déploie dans la classe du formulaire mais si j'utilise une classe externe, ce n'est pas le cas. Pour mieux expliquer la question, je coller le code de la classepourquoi Invoke ne fonctionne pas dans une autre classe

class SerialPortManager 
{ 
    public SerialPort Serial = new SerialPort(); 
    private Thread thr; 
    private string Log; 
    public TextBox textLog; 
    public string LastString; 
    public bool thrIsAlive; 
    [Browsable(false)] 
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 
    [EditorBrowsable(EditorBrowsableState.Advanced)] 
    [IODescriptionAttribute("ControlInvokeRequiredDescr")] 
    public bool InvokeRequired { get; private set; } 

    //DISPOSE 
    public void Dispose() 
    { 
     this.Dispose(); 
    } 

    //SET Textobox LOG 
    public void SetLogTxtB (TextBox txt) 
    { 
     textLog = txt; 
    } 

    //PORTE DISPONIBILI 
    public string[] Available_Ports() 
    { 
     return SerialPort.GetPortNames(); 
    } 

    //COSTRUTTORI 
    public SerialPortManager(string portname, int baudrate,bool InitializeConn) 
    { 
     Serial.BaudRate = baudrate; 
     Serial.PortName = portname; 
     if (InitializeConn == true) Serial.Open(); 
    } 
    public SerialPortManager() 
    { 

    } 

    //SETTA I PARAMETRI E INIZIALIZZA LA CONNESSIONE 
    public void SetConnectionParam(string portname, int baudrate, bool initializeConn) 
    { 
     Serial.Close(); 
     Serial.Dispose(); 
     Serial = new SerialPort(); 
     Serial.BaudRate = baudrate; 
     Serial.PortName = portname; 
     if (initializeConn == true) Serial.Open(); 
    } 

    //ASYNC LISTENER 
    public void AsyncListener() 
    { 
     thrIsAlive = true; 
     thr = new Thread(ThreadReader); 
     thr.Start(); 
    } 
    //PROCEDURA PER APPEND 
    public void AppendTextBox(string value) 
    { 
     if (InvokeRequired) 
     { 
      this.Invoke(new Action<string>(AppendTextBox), new object[] { value }); 
      return; 

     } 
     textLog.Text += value; 
    } 

    private void Invoke(Action<string> action, params object[] v) 
    { 
     throw new NotImplementedException(); 
    } 

    void ThreadReader() 
    { 
     while (thrIsAlive) 
     { 
      string temp = Serial.ReadLine(); 
      LastString = temp; 
      Log += LastString + "\n"; 
      AppendTextBox(LastString + "\n"); 
     } 

    } 
} 

Dans la forme que j'écris trois lignes

SerialPortManager PortMan = new Driver_Arduin.SerialPortManager("COM3", 9600,true); 
     PortMan.SetLogTxtB(textBox1); 
     PortMan.AsyncListener(); 

Si je tente d'exécuter le programme, il renvoie l'erreur "opération cross-thread ne permis". Maintenant, alors que je poste cette demande, je décide de faire une dernière tentative et changer la méthode AppendTextBox à:

public void AppendTextBox(string value) 
    { 
     if (textLog.InvokeRequired) 
     { 
      try 
      { 
       textLog.Invoke(new Action<string>(AppendTextBox), new object[] { value }); 
       return; 
      } 
      catch (ObjectDisposedException) 
      { 
       thrIsAlive = false; 
      } 
     } 
     textLog.Text += value; 
    } 

Et cela fonctionne enfin. Maintenant vérifié la puissance de Stackoverflow qui a résolu le problème avant de poster, je saurais pourquoi mon code fonctionne. Merci

Répondre

0

Dans SerialPortManager, vous devez utiliser un délégué plutôt que le contrôle Windows.

class SerialPortManager 
    { 
     public SerialPort Serial = new SerialPort(); 
     private Thread thr; 
     private string Log; 
     //public TextBox textLog; 
     public Action<string> textLog; 
..... 

Crète vous formez simplement la méthode:

public void SetTextBoxText(string value) 
    { 
     if (textBox1.InvokeRequired) 
     { 
      try 
      { 
       textBox1.Invoke(new Action<string>(AppendTextBox), new object[] { value }); 
       return; 
      } 
      catch (ObjectDisposedException) 
      { 
       thrIsAlive = false; 
      } 
     } 
     textBox1.Text += value; 
    } 

Set délégué PORTMAN:

SerialPortManager PortMan = new Driver_Arduin.SerialPortManager("COM3", 9600,true); 
     PortMan.SetLogTxtB=new Action<string>(SetTextBoxText); 
     PortMan.AsyncListener(); 

Si journal de sortie de nécessité de TextBox de PORTMAN appeler délégué TextLog.

void ThreadReader() 
    { 
     while (thrIsAlive) 
     { 
      string temp = Serial.ReadLine(); 
      LastString = temp; 
      Log += LastString + "\n"; 
      //AppendTextBox(LastString + "\n"); 
      textLog(LastString + "\n"); 
     } 

    } 
0

Outre que votre méthode Invoke dans SerialPortManager devrait jeter NotImplementedException le problème est que vous définissez votre propre InvokeRequired/Invoke.

Vous devez utiliser ces méthodes fournies par un contrôle WinForms pour qu'il sache si votre code s'exécute dans le thread (thread UI) qui a créé le contrôle et comment il peut changer le contexte de ce thread.

En fait, il semble que vous pouvez utiliser votre SerialPortManager mais utiliser InvokeRequired/Invoke de textLog comme vous faites déjà AppendTextBox. BTW, if (initializeConn == true) est plutôt inutile - if (initializeConn) est suffisant.