(Teil 1) Netzwerkverbindung zwischen Windows Phone 7 und Netduino Plus
Netduino Plus und Windows Phone 7 Mobiltelefon |
Eigentlich hatte ich vor, schon früher einen neuen Post zu schreiben. Aber wie so oft fehlt mir leider die Zeit. Und da ich versuche, weniger Kaffee zu trinken, werden meine Abende auch immer kürzer.
Heute zeige ich ein einfaches und kurzes Beispiel, wie man mit dem Windows Phone 7 und dem Netduino eine Netzwerkverbindung herstellen kann. Der Quellcode ist wieder auf das Wesentliche gekürzt, um möglichst nur die Funktionsweise von notwendigen Klassen zu zeigen.
Das Ziel des Beispiel wird sein, einen Servo anzusteuern. Am besten fangen wir mit der Anwendung auf dem Windows Phone 7 an. Hierzu benötigen wir einen “Slider” um einen Stellwert zum Netduino übermitteln zu können sowie zwei “Textboxen” um später die Zieladresse und den Port zu bestimmen. Zuletzt einen Button, um die Verbindung herzustellen. Mit “Textblock” können Status und der eingestellte Wert angezeigt werden. (Eine fertige Beispiel-Solution habe ich am Ende des Posts eingefügt)
So könnte die Anwendung auf der App aussehen. |
Nun zum interessanten Teil der App. Um eine Netzwerkverbindung herzustellen, legen wir eine neue Klasse an.
1: /// <summary>
2: /// Stellt eine Netzwerkverbindung her
3: /// </summary>
4: public class NetworkRemote
5: {
6: /// <summary>
7: /// Mit Hilfe der Socket Klasse
8: /// richten wir den Client für diese Anwendung ein.
9: /// </summary>
10: private Socket _Socket = null;
11: /// <summary>
12: /// Wie lange auf die Antwort vom Server gewartet wird (in Milisekunden)
13: /// </summary>
14: private int _Timeout = 5000;
15: /// <summary>
16: /// Die Thread Klasse wird verwendet, damit die Oberfläche der Anwendung
17: /// nicht anhält, wenn z.B. gesendet wird.
18: /// </summary>
19: private Thread _ThreadSend;
20: /// <summary>
21: /// Wird verwendet, um die Rückantwort zum Thread zu ermöglichen.
22: /// </summary>
23: private ManualResetEvent _ClientDone = new ManualResetEvent(false);
24:
25: /// <summary>
26: /// Stellt zu der Host Adresse und Port Nummer eine TCP Verbindung her
27: /// </summary>
28: /// <param name="hostAddress"></param>
29: /// <param name="port"></param>
30: /// <returns></returns>
31: public string Connect(string hostAddress, int port)
32: {
33: // Rückanwort, ob nun die Verbindung hergestellt werden konnte.
34: string result = string.Empty;
35:
36: // Ziel Adresse und Port festlegen.
37: DnsEndPoint dnsEndPoint = new DnsEndPoint(hostAddress, port);
38:
39: // Die Socket Klasse initialisieren
40: _Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
41:
42: // Asynchrone Socket Event
43: SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
44:
45: // Endpunkt an Event übergeben.
46: socketEventArg.RemoteEndPoint = dnsEndPoint;
47:
48: // Event zuweisen
49: socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
50: {
51: // Ruft das Ergebnis der Anfrage
52: result = e.SocketError.ToString();
53:
54: // Signal, dass der Antrag vollständig ist
55: _ClientDone.Set();
56: });
57:
58: // Legt den Zustand des Ereignisses auf nicht signalisiert, so der Thread blockiert ist.
59: _ClientDone.Reset();
60:
61: // Verbindung herstellen, die asynchron verläuft
62: _Socket.ConnectAsync(socketEventArg);
63:
64: // Blockiert den Thread in Millisekunden.
65: _ClientDone.WaitOne(_Timeout);
66:
67: return result;
68: }
69: /// <summary>
70: /// Daten senden, nachdem eine Verbindung hergestellt wurde.
71: /// Das Senden wird in einem Thread ausgeführt
72: /// </summary>
73: /// <param name="data">Eingabe der Daten</param>
74: /// <returns>Ergbnis der gesendeten Daten</returns>
75: public void Send(byte[] data)
76: {
77: // Nur einen neuen Thread starten, wenn dieser nicht aktiv ist.
78: if (_ThreadSend == null || !_ThreadSend.IsAlive)
79: {
80: _ThreadSend = new Thread(new ParameterizedThreadStart(SendInThread));
81: _ThreadSend.Start(data);
82: }
83: }
84: /// <summary>
85: /// Der Vorgang sollte in einem Thread ausgeführt werden. Das verhindert,
86: /// dass die Oberfläche stehenbleibt
87: /// </summary>
88: /// <param name="obj"></param>
89: private void SendInThread(object obj)
90: {
91: // Status Meldung hinterlegen.
92: string response = "Operation Timeout";
93:
94: // Asynchrone Socket Event
95: SocketAsyncEventArgs socketEvent = new SocketAsyncEventArgs();
96:
97: // Endpunkt an Event übergeben.
98: socketEvent.RemoteEndPoint = _Socket.RemoteEndPoint;
99:
100: // Zuordnung auf Null setzen
101: socketEvent.UserToken = null;
102:
103: // Event zuweisen
104: socketEvent.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
105: {
106: response = e.SocketError.ToString();
107: _ClientDone.Set();
108: });
109:
110: // Übergabe der zu sendenden Daten.
111: socketEvent.SetBuffer((byte[])obj, 0, ((byte[])obj).Length);
112:
113: // Legt den Status des Ereignisses für Kein Signal, verursacht Thread Blockade
114: _ClientDone.Reset();
115:
116: // Stellt eine asynchrone Sendungsantwort ausserhalb des Socket
117: _Socket.SendToAsync(socketEvent);
118:
119: // Blockiert den Thread in Millisekunden.
120: _ClientDone.WaitOne(_Timeout);
121:
122: // Event senden.
123: MessageEvent(response);
124: }
125: /// <summary>
126: /// schließt die Socket Verbindung
127: /// </summary>
128: public void Close()
129: {
130: if (_Socket != null)
131: {
132: _Socket.Close();
133: }
134: }
135: /// <summary>
136: /// Ruft ab, ob der Thread für Senden noch aktiv ist
137: /// </summary>
138: public bool IsSending
139: {
140: get
141: {
142: if (_ThreadSend.ThreadState == ThreadState.Running)
143: {
144: return true;
145: }
146:
147: return false;
148: }
149: }
150: /// <summary>
151: /// Ruft ab, ob die Netzwerkverbindung besteht.
152: /// </summary>
153: public bool IsConnected
154: {
155: get
156: {
157: try
158: {
159: return _Socket.Connected;
160: }
161: catch
162: {
163: return false;
164: }
165: }
166: }
167:
168: public delegate void NetwokrMessageHandler(object sender, NetworkMessageEventArgs e);
169: public event NetwokrMessageHandler NetworkMessage;
170: /// <summary>
171: /// Event auslösen; durch Ausführen der Methode
172: /// wird an die obere Klasse ein Ereignis gesendet,
173: /// um z.B. einen Status wiederzugeben.
174: /// </summary>
175: /// <param name="message"></param>
176: public virtual void MessageEvent(string message)
177: {
178: if (NetworkMessage != null)
179: {
180: NetworkMessage(this, new NetworkMessageEventArgs(message));
181: }
182: }
183: }
Für Rückmeldungen wie Status, wird ein Event Objekt benötigt, damit ich als Anwender weiss, ob die Verbindung erfolgreich war.
1: public class NetworkMessageEventArgs
2: {
3: public string Message { get; private set; }
4:
5: public NetworkMessageEventArgs(string message)
6: {
7: Message = message;
8: }
9: }
Für das Beispiel ist eigentlich ein Protokoll noch zu viel, soll jedoch zeigen, das für die Übertragung weitere Werte hinzugefügt werden könnten.
1: /// <summary>
2: /// Ein Objekt das als Protokol für die Übertragung verwendet wird.
3: /// Der Inhalt kann beliebig erweitert werden.
4: /// Im Beispiel wird zunächst ein Wert übermittelt.
5: /// </summary>
6: public struct NetworkProtocolRemote
7: {
8: /// <summary>
9: /// Die Variable zum festlegen des Stellwertes.
10: /// </summary>
11: private int _ServoValue;
12: /// <summary>
13: /// Initialisiert das Protokol mit Variabeln.
14: /// </summary>
15: /// <param name="servoValue"></param>
16: public NetworkProtocolRemote(int servoValue)
17: {
18: _ServoValue = servoValue;
19: }
20: /// <summary>
21: /// Empfangenes Byte Array wird eingelesen und befüllt die Variablen.
22: /// </summary>
23: /// <param name="receive"></param>
24: public NetworkProtocolRemote(byte[] receive)
25: {
26: _ServoValue = 90;
27:
28: string protocol = Encoding.UTF8.GetString(receive, 0, receive.Length);
29: SetProperties(protocol);
30: }
31: /// <summary>
32: /// Der Inhalt des Empfangenen Protokols wird in die Variablen festlegt.
33: /// </summary>
34: /// <param name="protocol"></param>
35: private void SetProperties(string protocol)
36: {
37: // Das Protokol kann um weitere Variablen erweitert werden,
38: // daher habe ich hier eine Schleife eingesetzt,
39: // um die Variablen festzulegen.
40:
41: foreach (string item in protocol.Split(';'))
42: {
43: string[] sa = item.Split(':');
44:
45: switch (sa[0])
46: {
47: case ("ServoValue"):
48: {
49: _ServoValue = Convert.ToInt16(sa[1]);
50: break;
51: }
52: default:
53: {
54: break;
55: }
56: }
57: }
58: }
59: /// <summary>
60: /// Gibt den Inhalt des Objektes als Byte Array zurück
61: /// </summary>
62: /// <returns></returns>
63: public byte[] GetRemoteToByte()
64: {
65: string content = string.Empty;
66:
67: content += "ServoValue:" + _ServoValue + ";";
68:
69: return Encoding.UTF8.GetBytes(content);
70: }
71: /// <summary>
72: /// Ruft den Wert des Servos ab oder legt diesen fest.
73: /// </summary>
74: public int ServoValue
75: {
76: set { _ServoValue = value; }
77: get { return _ServoValue; }
78: }
79: }
Das Wesentliche zeigt die Klasse im ersten Code Schnipsel (natürlich lässt sich dies noch kürzer schreiben, aber ich wollte nicht zu viel auslassen).
Im zweiten Teil des Posts geht es mit dem Netduino weiter.
Zweiter Teil
Die fertige Windows Phone 7 App Solution für Visual Studio 2010:
WP7 App Solution - Download
Eine Windows Desktop Anwendung zum testen:
Desktop Test Anwendung - Download
Kommentare