Netduino Listener

Präsentation und Organisation von eigenen Projekten
Antworten
Benutzeravatar
cloidnerux
Moderator
Beiträge: 3123
Registriert: Fr Sep 26, 2008 4:37 pm
Wohnort: Ram (Gibts wirklich)

Netduino Listener

Beitrag von cloidnerux » So Jan 08, 2012 7:52 pm

Vorgeschichte:
Ich habe ein Netduino Plus hier herumliegen. Dies ist ein sehr tolles, an die Arduinos angelehntes, gerät mit ARM Prozessor, der in C# programmiert wird.
Aber es hat einen Nachteil: Dem Netduino fehlt die Möglichkeit über USB Daten an eine Serielle-Schnittstelle zu senden. Dies ist sehr ärgerlich, denn ich nutze häufig eben dieses Gerät, um Messwerte zu sammeln.
Glücklicherweise verfügt mein Netduino Plus über eine Hardware-Ethernet Schnittstelle und gab mir somit die Möglichkeit doch noch Daten zu senden, aber jeder der schon einmal Netzwerkschnittstelllen Implementiert hat, weiß das das sehr umständlich ist, das für jeden neuen Messansatz die Schnittstellen neu zu programmieren.
Daher habe ich schnell ein Dynamisches Konzept aufgestellt und angefangen zu programmieren.
Daher auch der Name des Programmes: Netduino Listener, das Programm das dem netduino zuhört^^

Stand der Dinge:
Inzwischen sind 2 Dinge entstanden: Eine einigermaßen stabile Klasse für den netduino, damit die Netzwerkschnittstelle nicht andauernd neu Implementiert werden muss und es erlaubt, dem Protokoll gemäß Daten zu versenden. Diese Klasse kann sich entweder verbinden oder wartet auf eine Verbindung.

Zum anderen das Programm in einer auch einigermaßen stabilen Version, das dann doch etwas dynamischer wurde als zu Anfang geplant. Es beinhaltet das Ethernet-Input Modul zum empfangen der Daten und einem Datenverteiler. Die Visualisierung geschieht über externe *.dll in die einfach nur eine C# Form mit einem Speziellen Interface gekapselt sind. Ich habe selber schon 2,5 verschiedene Visualisierungen Implementiert: SingleStreamView, MultiStreamView(daher das 0,5) und ein GraphView, das sich auf ZedGraph stüzt.
Geplant ist noch, die Input-Modul in *.dlls zu kapseln, um auch Input über andere Schnittstellen, z.B USB oder serielle einfach zu ermöglichen.

Protkoll:
Das Protokoll an sich ist Textbasiert und fordert folgende Form für jeden Datensatz:

Code: Alles auswählen

<#ID>(#Type){#Data}
#ID kennzeichnet einen zusammenhängenden Datensatz, z.B alle Messwerte eines Temperatursensor haben die ID 1 usw. und muss ein ganzzahliger, positiver int sein.
#Type kennzeichnet den Typ des Datensatzes, bisher i(Int), f(float), d(double) und s(string). Der Typ muss ein einzelnes Char sein
#DataDer Datensatz als string, es muss sich hierbei um einen Validen string des typs handeln, da er ansonsten nicht vom NetduinoListener verarbeite wird.

Modul-Aufbau:
Das Programm soll so dynamisch wie möglich sein, daher war ein Modularer Aufbau erforderlich, der es ermöglicht schnell und ohne viel Probleme eine neue Visualisierung einzubauen. Dazu wurden alle wichtigen Interfaces und Klassen in eine eigene *.dll gekapselt, die dann als Verweis in VS eingestellt werden kann, sodass man sie in seinem Projekt nutzen kann. Für die Input-Module ist ähnliches geplant.

Wieso so Windows lastig?
Dem einen oder anderen wird es schon aufgefallen sein, dass das ganze sehr Windows und Visual Studio lastig ist. Dem ist auch so und war das Resultat daraus, das ich sowieso schon C# für den Netduino nutze und damit schon auf Windows unterwegs bin. Zudem ist C# und das .NET-Framework in sachen Windows-GUI Entwicklung sehr komfortabel und ich musste mich nicht erst in neue Frameworks einarbeiten.

Code:
Die Netduino-Klasse für die Netzwerkverbindung:

Code: Alles auswählen

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.NetduinoPlus;
using Microsoft.SPOT.Net.NetworkInformation;

namespace DataTransfer
{
    enum ConnectionStates
    {
        diconnect = 0,
        connected,
        acceptedConnection
    }
    /// <summary>
    /// Establishes an ethernet connection to send data
    /// </summary>
    class NetworkTransport
    {
        /// <summary>
        /// The socket to send
        /// </summary>
        Socket dataTransport;
        /// <summary>
        /// The Socket of the incomming connection
        /// </summary>
        Socket incomingConnection;
        /// <summary>
        /// The remote host
        /// </summary>
        IPAddress remoteIP;
        /// <summary>
        /// The ethernet endpoint
        /// </summary>
        IPEndPoint endpoint;
        /// <summary>
        /// The lcoal endpoint
        /// </summary>
        IPEndPoint localEndpoint;
        /// <summary>
        /// The state of the conection
        /// </summary>
        ConnectionStates states;
        /// <summary>
        /// Indicates if the connector has to wait to reconnect if the connection get lost
        /// </summary>
        bool forceWait;
        /// <summary>
        /// Default constructor, establishes a connection to 192.168.42.129 on port 1000
        /// </summary>
        public NetworkTransport()
        {
            var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();

            Debug.Assert(networkInterfaces[0] != null);

            var net = networkInterfaces[0];
            net.EnableStaticIP("192.168.42.10", "255.255.255.0", "192.168.42.1");
            localEndpoint = new IPEndPoint(IPAddress.Parse("192.168.42.10"), 1000);
            Debug.Print("DHCP Enabled: " + net.IsDhcpEnabled);
            Debug.Print("IP Address: " + net.IPAddress);
            remoteIP = IPAddress.Parse("192.168.0.244");
            endpoint = new IPEndPoint(remoteIP, 1000);
            states = ConnectionStates.diconnect;
        }

        /// <summary>
        /// Initialises the socket to send data to the given ip on the given port
        /// </summary>
        /// <param name="localIP">The local IP</param>
        /// <param name="subnetMask">The subnetmask</param>
        /// <param name="nameserver">The nameserver of the Network</param>
        /// <param name="targetIP">The remote host</param>
        /// <param name="port">The remote port</param>
        public NetworkTransport(string localIP, string subnetMask, string nameserver, string targetIP, int port)
        {
            var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();

            Debug.Assert(networkInterfaces[0] != null);

            var net = networkInterfaces[0];
            net.EnableStaticIP(localIP, subnetMask, nameserver);
            localEndpoint = new IPEndPoint(IPAddress.Parse(localIP), 1000);
            Debug.Print("DHCP Enabled: " + net.IsDhcpEnabled);
            Debug.Print("IP Address: " + net.IPAddress);
            remoteIP = IPAddress.Parse(targetIP);
            endpoint = new IPEndPoint(remoteIP, port);
            states = ConnectionStates.diconnect;
        }

        /// <summary>
        /// Waiting for an incomming connection
        /// </summary>
        /// <param name="force">Force the connector to wait if the connection gets lost</param>
        public void WaitForConnection(bool force)
        {
            dataTransport = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
            dataTransport.Bind(localEndpoint);
            dataTransport.Listen(1);
            incomingConnection = dataTransport.Accept();
            states = ConnectionStates.acceptedConnection;
            forceWait = force;
        }

        private void WaitForNewConnection()
        {
            incomingConnection = dataTransport.Accept();
            states = ConnectionStates.acceptedConnection;
        }

        /// <summary>
        /// Establish the connnection
        /// </summary>
        public bool Connect()
        {
            try
            {
                dataTransport = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                dataTransport.Connect(endpoint);
                states = ConnectionStates.connected;
            }
            catch
            {
                Debug.Print("Error while connecting");
                return false;
            }
            return true;
        }

        private void SendBytes(int id, byte[] data)
        {
            if (states == ConnectionStates.diconnect)
            {
                return;
            }
            try
            {
                if (states == ConnectionStates.connected)
                {
                    dataTransport.Send(data, 0, data.Length, SocketFlags.None);
                }
                else if (incomingConnection != null)
                {
                    incomingConnection.Send(data, 0, data.Length, SocketFlags.None);
                }
            }
            catch (SocketException ex)
            {
                switch (ex.ErrorCode)
                {
                    case 10055:
                        Thread.Sleep(100);
                        return;
                    case 10054:
                        if (states == ConnectionStates.acceptedConnection)
                        {
                            states = ConnectionStates.diconnect;
                            WaitForNewConnection();
                        }
                        break;
                    default:
                        Debug.Print(ex.ErrorCode.ToString() + ": " + ex.Message);
                        break;
                }
            }
            catch
            {
                Debug.Print("Error sending Network data. Try to reestablish the connection");
            }
        }

        /// <summary>
        /// Send data over the connection
        /// </summary>
        /// <param name="data">The data to send</param>
        public void NetworkSend(int id, string data)
        {
            byte[] bytes = Encoding.UTF8.GetBytes("<" + id.ToString() + ">(s){" + data + "}|");
            SendBytes(id, bytes);
        }

        public void NetworkSend(int id, int value)
        {

            byte[] bytes = Encoding.UTF8.GetBytes("<" + id.ToString() + ">(i){" + value.ToString() + "}|");
            SendBytes(id, bytes);
        }

        public void NetworkSend(int id, float value)
        {

            byte[] bytes = Encoding.UTF8.GetBytes("<" + id.ToString() + ">(f){" + value.ToString() + "}|");
            SendBytes(id, bytes);
        }
    }
}
Rechtschreibfehler dürfen ignoriert werden. Für Funktion wird nicht garantiert.

Den Sourcecode des Programms sowie das Programm an sich würde ich gegen nachfrage herausgeben, direkt veröffentlichen will ich es wegen der noch vorhandenen Fehler noch nicht.

Impressionen:
Bild1.png
Bild2.png
Bild3.png
Bild4.png
Bild5.png
MfG cloidnerux.
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.
Redundanz macht wiederholen unnötig.
quod erat expectandum

Benutzeravatar
cloidnerux
Moderator
Beiträge: 3123
Registriert: Fr Sep 26, 2008 4:37 pm
Wohnort: Ram (Gibts wirklich)

Re: Netduino Listener

Beitrag von cloidnerux » Mi Mai 22, 2013 10:50 pm

Mal ein Update. Ich hab mir mal ein Wochenende die mühe gemacht und habe die Software überarbeitet:
  • Vereinheitliche Datastreams
  • Stärkere Kapselung der Inputs und Outputs
  • Auflistung aller Inputs
  • Mehrer Instanzen des selben Inputs
Die Software ist nun nicht mehr nur ein Frontend für den Netduino Datenverkehr, sondern ist nun ein allgemeines Datenvisualisierungstool für alle Datentransfers, für die es ein Input-Modul gibt.
Die Idee ist, Eingabedaten auf ein einheitliches Format und Stream zu bringen und dann auf direkt auf diverse Ausgaben zu bringen, sodass der Programmieraufwand minimiert wird.
Ich werde demnächst das ganze mal bei github hochladen.

MfG cloidnerux
Redundanz macht wiederholen unnötig.
quod erat expectandum

Antworten