Autor Beitrag
Michael65589
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Mo 26.09.11 19:57 
Hallo,
ich habe einen RS232 Receive Buffer programmiert. Im DataReceived Event steht nur:
RxString += serialPort1.ReadExisting();

In einem weiteren Thread prüfe ich den RxString ob meine Daten komplett empfangen wurden. Wenn das der Fall ist übergebe ich den empfangenen String einer Funktion. Dort wird der String mittels Split zerlegt. Dann möchte ich die zerlegten Strings in einer Form darstellen. Sobald aber das erste mal eine Funktion mit Invoke aufgerufen wurde, ist mein Stringarray, welches ich über Split erhalten habe kaputt.

Hab mir jetzt beholfen, indem ich nicht den übergebenen String splitte sondern den globalen.

Was ist falsch, oder ist generell an meiner Denke was falsch?

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace BreakTorqueTest
{
    public partial class Form1 : Form
    {
        static string RxString;
        Thread CheckFrameThread;
        static bool _runCheckFrameThread = false;
        string szFrameData;

        delegate void SetFrameCallBack(string sFrame);
        
        public Form1()
        {
            InitializeComponent();
            serialPort1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(serialPort1_DataReceived);
            // Set the read/write timeouts
            serialPort1.ReadTimeout = 500;
            serialPort1.WriteTimeout = 500;
            _runCheckFrameThread = true;

            CheckFrameThread = new Thread(this.CheckFrame);
            CheckFrameThread.Start();
        }

        void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            RxString += serialPort1.ReadExisting();
        } 

        private void btStart_Click(object sender, EventArgs e)
        {
            if (serialPort1.IsOpen == false)
            {
                serialPort1.Open();
            }
            // If the port is closed, don't try to send a character.

            if (!serialPort1.IsOpen) return;
            // If the port is Open, declare a char[] array with one element.
            char[] buff = new char[4];

            // Load element 0 with the key character.

            buff[0] = 'X';
            buff[1] = 'S';
            buff[2] = '7';
            buff[3] = '\r';

            // Send the one character buffer.
            try
            {
                serialPort1.Write(buff, 0, buff.Length);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

        }

        private void btStop_Click(object sender, EventArgs e)
        {
            char[] buff = new char[4];

            // Load element 0 with the key character.
            buff[0] = 'X';
            buff[1] = 'A';
            buff[2] = '5';
            buff[3] = '\r';

            // Send the one character buffer.
            try
            {
                serialPort1.Write(buff, 0, buff.Length);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            if (serialPort1.IsOpen == true)
            {
                serialPort1.Close();
            }
            _runCheckFrameThread = false;
            CheckFrameThread.Join(); 
        }
        
        public void OnFrameReceived(string sFrame)
        {
            string[] StringParts = szFrameData.Split(new string[] { ";" }, StringSplitOptions.None);

            if (this.lWatt_sec.InvokeRequired)
            {
                SetFrameCallBack d = new SetFrameCallBack(OnFrameReceived);
                this.Invoke(d, new Object[] { StringParts[0] });
            }
            else
            {
                this.lWatt_sec.Text = StringParts[0];
            }

            if (this.lWatt.InvokeRequired)
            {
                SetFrameCallBack d = new SetFrameCallBack(OnFrameReceived);
                this.Invoke(d, new Object[] { StringParts[1] });
            }
            else
            {
                this.lWatt.Text = StringParts[1];
            }

            if (this.lRpm_sec.InvokeRequired)
            {
                SetFrameCallBack d = new SetFrameCallBack(OnFrameReceived);
                this.Invoke(d, new Object[] { StringParts[2] });
            }
            else
            {
                this.lRpm_sec.Text = StringParts[2];
            }

            if (this.lPuls.InvokeRequired)
            {
                SetFrameCallBack d = new SetFrameCallBack(OnFrameReceived);
                this.Invoke(d, new Object[] { StringParts[3] });
            }
            else
            {
                this.lPuls.Text = StringParts[3];
            }

            if (this.lDeltaBeta.InvokeRequired)
            {
                SetFrameCallBack d = new SetFrameCallBack(OnFrameReceived);
                this.Invoke(d, new Object[] { StringParts[4] });
            }
            else
            {
                this.lDeltaBeta.Text = StringParts[4];
            }

            if (this.lGeschwindigkeit.InvokeRequired)
            {
                SetFrameCallBack d = new SetFrameCallBack(OnFrameReceived);
                this.Invoke(d, new Object[] { StringParts[5] });
            }
            else
            {
                this.lGeschwindigkeit.Text = StringParts[5];
            }
        }

        private void CheckFrame()
        {
            int posStart = 0;
            int posEnd = 0;

            while (_runCheckFrameThread)
            {
                if (RxString != null)
                {
                    posStart = RxString.IndexOf("!");
                    posEnd = RxString.IndexOf('\n');
                    if ((posEnd - posStart) == 38)
                    {
                        //Frame komplett
                        szFrameData = RxString.Substring(posStart + 136);
                        OnFrameReceived(szFrameData);
                        RxString = RxString.Remove(posStart, 39);
                    }
                }
            }
        }
    }
}
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Mo 26.09.11 21:00 
:welcome: in der EE!

Gibt es auch eine Definition zu "kaputt"? Deinen Code überblicke ich noch nicht ganz, da er unnötig komplex ist: Wirf erst einmal das ganze Invoke-Zeug aus OnFrameReceived raus, einmal
ausblenden C#-Quelltext
1:
Invoke(new Action(() => OnFrameReceived(szFrameData)));					

beim Aufruf (also in CheckFrame) genügt. Auch mit dem eigenen Thread für CheckFrame schaffst du dir nur unnötig Ärger, RxString sollte sich doch auch direkt in DataReceived verarbeiten lassen?

_________________
>λ=
Michael65589 Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Mo 26.09.11 21:07 
Kaputt heißt:

Nach dem Aufruf der Zeile
ausblenden C#-Quelltext
1:
string[] StringParts = szFrameData.Split(new string[] { ";" }, StringSplitOptions.None);					

hat StringParts sieben Elemente.

Nachdem

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
           if (this.lWatt_sec.InvokeRequired)
            {
                SetFrameCallBack d = new SetFrameCallBack(OnFrameReceived);
                this.Invoke(d, new Object[] { StringParts[0] });
            }
            else
            {
                this.lWatt_sec.Text = StringParts[0];
            }


aufgerufen wurde, springt er nochmal in die Zeile

ausblenden C#-Quelltext
1:
string[] StringParts = szFrameData.Split(new string[] { ";" }, StringSplitOptions.None);					


Danach hat StringParts nur noch ein Element.
Michael65589 Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Mo 26.09.11 21:17 
Hallo Kho,

es klappt, wenn ich die ganzen Invokes raus nehme und es durch das eine Invoke beim Aufruf von OnFrameReceived ersetzte.

ausblenden C#-Quelltext
1:
Invoke(new Action(() => OnFrameReceived(szFrameData)));					


Danke.

Ich dachte man muss für jedes GUI-Element ein separates Invoke verwenden.
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Mo 26.09.11 21:23 
Wenn du dich auf die Version beziehst, in der du überall sFrame statt zsFrame stehen hast, dann ist das nicht sehr verwunderlich, da du beim Invoke nur einen Teil des Original-Strings übergibst :) .

Edit: Schön zu hören, dass es funktioniert :D .

user profile iconMichael65589 hat folgendes geschrieben Zum zitierten Posting springen:
Ich dachte man muss für jedes GUI-Element ein separates Invoke verwenden.
Da normalerweise alle Steuerelemente im gleichen Thread, dem Haupt- oder GUI-Thread, erstellt wurden, musst du nur einmal vom Nebenthread dorthin wechseln.

Noch eine kleine Anmerkung zum Codestil: Die Polnische Notation ist unter .NET eher verpönt.

_________________
>λ=
ujr
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 102
Erhaltene Danke: 12



BeitragVerfasst: Mo 26.09.11 23:19 
Hallo,

user profile iconMichael65589 hat folgendes geschrieben Zum zitierten Posting springen:
Ich dachte man muss für jedes GUI-Element ein separates Invoke verwenden.


Im Gegenteil - da Invoke ein sehr "kostspieliger" Prozess ist, ist es natürlich sinnvoll, die Zahl der Aufrufe zu minimieren.

Ich habe allerdings den Eindruck, dass Du nicht so richtig weißt, was Du da gemacht hast. Zum einen ist InvokeRequired immer true (da die Funktion ja aus einem Nicht-GUI-Thread aufgerufen wird. Zum anderen rufst Du OnFrameReceived rekursiv immer neu auf - nämlich bei jedem Invoke. Weiterhin hat OnFrameReceived einen Parameter (sFrame), den Du gar nicht benutzt, stattdessen aber die Instanzvariable szFrame.

Wie es aber zustande kommt, dass StringParts nur ein Element hat, überblicke ich im Moment nicht (außer, der Parameter hieße szFrame, was den vorhergehenden Satz überflüssig machen würde - aber woher kommt dieser "Tipp"-fehler?)

Im Übrigen sind Vergleiche mit true oder false (==true, ==false) in den meisten Fällen sinnlos.
Michael65589 Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Di 27.09.11 06:21 
Hallo ujr,

ich hatte vorher sFrame statt szFrame benutzt. Da es aber nicht funktioniert hat, habe ich es mit szFrame gemacht.

Mit dem Tipp von Kha hat es dann auch mit dem Parameter funktioniert.

Und Du hast recht, so richtig weiß ich nicht was ich da mache. Vielleicht kannst Du es mir ja mit einfachen Worten ein bisschen näher bringen.
Ich weiß das ich nur in dem Thread, in dem ich die GUI-Elemente anlege, sie auch bedienen kann. Wenn man sie aus einem anderen Thread heraus bedienen will muss man mit Invoke arbeiten. Was Invoke aber genau macht und wie man es richtig einsetzt weiß ich nicht so richtig. Ich habe bis jetzt auch noch keine richtige Erklärung gefunden, außer das es wohl etliche Möglichkeiten gibt Invoke einzusetzen.

Wie funktioniert die Sache mit dem GUI-Thread.

Meine Funktion OnFrameReceive ist ja in gewisser Weise ein CallBack. Muss man also immer bei einem Aufruf einer Funktion von einem Thread in dem anderen mit Invoke arbeiten?

Kha, was meinst du mit polnische Notation? += ?

Gruß
Michael
ujr
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 102
Erhaltene Danke: 12



BeitragVerfasst: Di 27.09.11 09:08 
user profile iconMichael65589 hat folgendes geschrieben Zum zitierten Posting springen:
Vielleicht kannst Du es mir ja mit einfachen Worten ein bisschen näher bringen.


Schau Dir mal diese Erklärungen an:
[FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke)

Auch dort gibt es zwar ein Beispiel mit einem rekursiven Aufruf, der Unterschied ist aber offensichtlich - es wird nur eine Eigenschaft der GUI geändert, nicht viele nacheinander. Wenn Du viele GUI-Elemente ändern musst, kann das aber trotzdem so ähnlich aussehen:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
void DoCheapGuiAccess ()
{
   if (ctrl.InvokeRequired) { // Wenn Invoke nötig ist, ...
      // dann rufen wir die Methode selbst per Invoke auf
      ctrl.Invoke (new MethodInvoker (DoCheapGuiAccess));
      return;
   }
   // eigentlicher Zugriff; läuft jetzt auf jeden Fall im GUI-Thread
   ctrl.Text = "Hello World!";

   // hier weitere anschließen!
}


Du hattest diese Blöcke jedoch mehrfach in Deiner OnFrameReceived-Methode. Und wie schon erwähnt kann man sich InvokeRequired sparen, wenn der Aufruf nur aus einem Nicht-GUI-Thread erfolgen kann. Dann kann Invoke direkt verwendet werden. Auch ein rekursiver Aufruf ist dann nicht mehr nötig, sondern DoCheapGuiAccess (bzw. OnFrameReceived) kann gleich über Invoke aufgerufen werden, so wie es Dir Kha vorgeschlagen hat.

Zitat:

Meine Funktion OnFrameReceive ist ja in gewisser Weise ein CallBack. Muss man also immer bei einem Aufruf einer Funktion von einem Thread in dem anderen mit Invoke arbeiten?


Nur für den GUI-Thread - gewissermaßen eine spezielle Art der Synchronisation zwischen Threads. Details und Alternativen kannst Du im verlinkten Thread nachlesen.
(Auch sonst muss man natürlich zwischen Threads den Zugriff auf gemeinsam genutzte Daten synchronisieren. Das ist allerdings ein anderes Thema.)

Zitat:

Kha, was meinst du mit polnische Notation? += ?


Eigentlich ist's die ungarische ;-) - die Codierung eines Typs in den Präfix des Variablennamens.

Für diesen Beitrag haben gedankt: Kha
Michael65589 Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Di 27.09.11 09:56 
Hi ujr,

danke für die Antwort. Werde mir den Link zu Thread aktualisieren mal ansehen.

Gruß
Michael