none
Netzwerk-Kommunikation zwischen IoT und Desktop-App RRS feed

  • Frage

  • Guten Morgen,

    ich habe noch totale Schwierigkeiten zwischen einer klassischen Windows-Desktop-App und Windows UWP bzw. Core gedanklich hin und her zu Switchen.

    Was möchte ich tun:
    Ich möchte eine App für Windows IoT erstellen, welche auf einem Raspberry Pi 3 läuft. Die App soll eine Verbindung bereitstellen und dann auf Befehle warten. Die Befehle wiederum, MÜSSEN aus einer klassischen Desktop-Anwendung (.NET-Framework 4.6) kommen. Das sollte eigentlich kein Problem sein.

    Mein Problem ist nur, ich kann keine Bibliotheken in UWP einsetzen, welche für .NET-Framework x.y sind und umgekehrt kann ich auf einer klassischen Desktop-App keine UWP-Bibliotheken einsetzen. Ich komme mir auch bei UWP komplett "eingeschränkt" vor. Tu dies nicht, mach das nicht, nein so geht das nicht. Ich bin alle zwei Tage kurz davor alles hinzuwerfen. 

    Ich habe z.B. NetworkCommsDotNet gefunden was anfangs interessant aussah. Aber leider lässt es sich nicht in einem Windows IoT-Projekt einbinden. Ein anderes Beispiel hatte ich bei GitHub gefunden https://github.com/lillo42/IoT
    Aber hier besteht das Problem, dass nach rund 40 kleinen Paketen (alles Strings) die Verbindung abbricht. Außerdem ist es nicht für eine Desktop-Anwendung geeigent, oder ich habe es schlichtweg nicht verstanden.

    Habt ihr vielleicht einen Tipp für mich? Vermutlich suche ich einfach nur falsch oder es hat schlichtweg bei mir oben immer noch nicht "klick" gemacht... Am liebsten wäre mir VB.NET. Aber ich kann mir notfalls das auch von C# nach VB.NET übersetzen.

    Gruß
    Andy

    Montag, 23. Oktober 2017 04:53

Antworten

Alle Antworten

  • Hallo Andy,

    ich habe ein ähnliches Szenario mit einer Web API innerhalb der UWP realisiert. Dazu habe ich Restup verwendet.

    Im Grunde solltet jede Netzwerkkommunikation möglich sein die man aber am besten selbst entwickelt. Mit Windows 1703 und Net Standard 2 ergeben sich aber auch andere Möglichkeiten. Nach Microsoft aussage sollen damit jetzt 60-70% aller Nuget Pakete unter UWP laufen. 


    Gruß Thomas

    Sage nie, ich kann es nicht - sage nur, ich kann es noch nicht!

    Icon für UWP

    Cross Platform Canvas for UWP, Android, iOS

    UWP Community Toolkit Sample App

    Alle Größenangaben in UWP müssen durch 4 teilbar sein

    • Als Antwort markiert AndreasMahub Mittwoch, 25. Oktober 2017 04:40
    Montag, 23. Oktober 2017 05:13
  • Je nach dem was du machst könnte auch SignalR was für dich sein.

    http://dotnetbyexample.blogspot.de/2015/05/using-windows-10-uwp-app-and-signalr-on.html

    • Als Antwort markiert AndreasMahub Mittwoch, 25. Oktober 2017 04:48
    Montag, 23. Oktober 2017 10:35
  • Hallo Thomas,

    ich habe jetzt in den letzten zwei Tagen intensiv mit Restup experimentiert. Allerdings beschleicht mich der VERDACHT (ich betone es absichtlich), dass Restup eher für Datenbanken geeignet ist.

    Ein Beispiel:
    Anfrage kommt rein. UserID wird übergeben, Datenbank öffnen, Usernamen zurückgeben, Datenbank schließen, fertig.

    Allerdings habe ich keine Datenbank. Ich möchte von einem anderen PC aus z.B. eine LED ein- bzw. ausschalten. Dazu kann ich aber nicht jedes mal die GPIOs neu initialisieren bei jedem Aufruf. Mein Ziel ist es, auf eine andere Instanz zuzugreifen, wo alle Geräte und Zustände (bereits) bekannt sind.

    Aber so wie es (für mich) aussieht, wird bei jeder Anfrage eine neue Controller-Instanz erstellt und anschließend wieder "zerstört" (geschlossen).

    Mein Ziel ist folgendes:
    In Startup-Task wird der Server eingerichtet. Anschließend wird z.B. eine RGB-LED initialisiert. Die leuchtet beim starten auf dem Pi z.B. Blau. Jetzt möchte ich aus der ferne wissen, welche LED-Farbe leuchtet gerade, und es soll als Antwort Blau zurückkommen. Aber wie mache ich das ohne eine Datenbank oder ähnliches zu verwenden? WIe komme ich an die Klasse ran, wo alle Informationen zum aktuellen Zustand enthalten sind?

    Ich habe so den VERDACHT das geht gar nicht, oder?

    Gruß
    Andy

    Mittwoch, 25. Oktober 2017 04:48
  • Je nach dem was du machst könnte auch SignalR was für dich sein.

    http://dotnetbyexample.blogspot.de/2015/05/using-windows-10-uwp-app-and-signalr-on.html

    Guten Morgen Palin,

    ich glaube SignalR ist sehr sehr komplex und kompliziert und für meine Zwecke glaube ich schon fast überdimensoniert :-D Gucke ich mir aber dennoch bei Gelegenheit mal an. Aber ich habe z.B. mit Azure und so weiter noch nie etwas gemacht ;-)

    Gruß
    Andy

    Mittwoch, 25. Oktober 2017 04:49
  • Hallo Andy,

    mach dir doch einfach eine statische Klasse und speicher dort alle Zustände. Solange die App läuft, bleibt die Klasse bestehen. Auf eine statische Klasse kannst Du immer global zugreifen.

    Beim beenden der App gehen diese Zustände natürlich verloren. Willst Du diese auch speichern könntest Du deine Klasse in ein in einem JSON speichern.


    Gruß Thomas

    Sage nie, ich kann es nicht - sage nur, ich kann es noch nicht!

    Icon für UWP

    Cross Platform Canvas for UWP, Android, iOS

    UWP Community Toolkit Sample App

    Alle Größenangaben in UWP müssen durch 4 teilbar sein

    Mittwoch, 25. Oktober 2017 05:29
  • Hallo Andy,

    mach dir doch einfach eine statische Klasse und speicher dort alle Zustände. Solange die App läuft, bleibt die Klasse bestehen. Auf eine statische Klasse kannst Du immer global zugreifen.

    Beim beenden der App gehen diese Zustände natürlich verloren. Willst Du diese auch speichern könntest Du deine Klasse in ein in einem JSON speichern.

    Hallo Thomas,

    das klappt leider auch nicht so ganz, oder wir reden gerade aneinander vorbei.

    Wenn ich es richtig verstanden habe, dann lauscht der Restup-Server die ganze Zeit auf dem festgelegten Port. Kommt jetzt z.B. so etwas: 
    http://192.168.1.182:8800/api/LED1/set/Red
    dann wird das auch brav "abgefangen" und diese Information weitergeleitet, zum Beispiel hier hin:

    <RestController(InstanceCreationType.Singleton)>
    Public NotInheritable Class Test1
    
        <UriFormat("/{id}/set/{propName}")>
        Public Function GetWithSimpleParameters(id As String, propName As String) As IGetResponse
            Stop
    
            Return New GetResponse(GetResponse.ResponseStatus.OK)
        End Function
    End Class
    

    Das Problem ist nur, die Klasse Test1 wird bei jedem Aufruf von Restup komplett neu instanziiert. Sie kennt keine alten Zustände. Ich kann auch keine Events abfeuern (z.B. an StartupTask oder so) wo ich dann Befehle weitergeben könnte. 

    Jedenfalls nach dem, was ich bis jetzt (glaube) rausgefunden zu haben.

    Oder ich stelle mich gerade komplett blöd an, kann natürlich auch sein.

    Gruß
    Andy

    Mittwoch, 25. Oktober 2017 14:51
  • Die statische Klasse soll natürlich nicht der Controller sein. Teile die App in mindestens 2 sichten. Einmal die Steuerung der Leds und zudem noch die Entgegennahme der Befehle und Weiterleitung an die Steuerung. Vielleicht willst Du später die Befehle noch auf eine andere weise empfangen oder dich von der Web Api trennen. Dann brauchst Du immer nur eine Sicht anpassen.

    So in der Art könnte das dann aussehen

    Public Enum LedStats
    	None = 0
    	Red = 1
    	Green = 2
    End Enum
    
    Public NotInheritable Class MyStaticStateClass
    	Private Sub New()
    	End Sub
    	Public Shared Property Led1() As LedStats
    		Get
    			Return m_Led1
    		End Get
    		Set
    			m_Led1 = Value
    		End Set
    	End Property
    	Private Shared m_Led1 As LedStats
    
    	Public Shared Sub SetLedState(id As String, state As String)
    		'Hier könntest Du auch einmalig die GPIOs initialisieren
    
    		Dim s = CType([Enum].Parse(GetType(LedStats), state), LedStats)
    
    		Select Case id
    			Case "Led1"
    				Led1 = s
    				Exit Select
    		End Select
    	End Sub
    End Class
    
    Public Class [MyClass]
    	Public Sub GetWithSimpleParameters(id As String, state As String)
    		MyStaticStateClass.SetLedState(id, state)
    	End Sub
    End Class

     

    Gruß Thomas

    Sage nie, ich kann es nicht - sage nur, ich kann es noch nicht!

    Icon für UWP

    Cross Platform Canvas for UWP, Android, iOS

    UWP Community Toolkit Sample App

    Alle Größenangaben in UWP müssen durch 4 teilbar sein

    Mittwoch, 25. Oktober 2017 17:41
  • Hallo Thomas,

    aber ich kann doch auf eine Statische Klasse, die Daten halten soll, nicht von "überall" zugreifen.

    Ich zeig dir mal jetzt meinen Code, vielleicht wird das Verständnis einfacher, was ich vor habe.

    Das Programm beginnt mit StartupTask.vb und soll MEINE GPIO-Klasse initialisieren wo alle Eigenschaften, Methoden und Funktionen zur Kommunikation mit den PINs enthalten sind. Dann ein Gerät hinzufügen, in diesem Fall ein 4-Stelliges Display und dort erstmal die aktuellen Sekunden der aktuellen Zeit anzeigen, damit ich visuell sehen kann, das mein Programm gestartet wurde und bereit ist. Dann wird der Server gestartet.

    Imports System
    Imports System.Collections.Generic
    Imports System.Linq
    Imports System.Text
    Imports System.Net.Http
    Imports Windows.ApplicationModel.Background
    Imports Restup.Webserver.Models.Schemas
    Imports Restup.Webserver.Attributes
    Imports Restup.Webserver.Models.Contracts
    Imports Restup.Webserver.Rest
    Imports Restup.Webserver.Http
    
    Public NotInheritable Class StartupTask
        Implements IBackgroundTask
    
        Private _Display As Pluto.WindowsIoT.Device.FourDigit7SegmentsDisplay = Nothing
        Private _GPIOsInstance As Pluto.WindowsIoT.GPIOs = Nothing
        Private WithEvents _Server As Pluto.WindowsIoT.Server = Nothing
    
    
        Public Sub Run(taskInstance As IBackgroundTaskInstance) Implements IBackgroundTask.Run
    
            ' Display initialisieren
            Dim DisplayConfig As New Pluto.WindowsIoT.DeviceConfigItem
            DisplayConfig.Communication = Pluto.WindowsIoT.DeviceConfigCommuniationEnum.BitBanging
            DisplayConfig.PinData = 27
            DisplayConfig.PinClock = 18
            DisplayConfig.PinStrobe = 17
    
            _Display = New Pluto.WindowsIoT.Device.FourDigit7SegmentsDisplay(_GPIOsInstance, DisplayConfig)
            _Display.Show(DateTime.Now.TimeOfDay.Seconds.ToString) ' Damit wir visuell sehen, das das Programm auf dem Pi gestartet wurde.
    
            ' Jetzt Restup starten
            _Server = New Pluto.WindowsIoT.Server
            _Server.Start()
    
            ' Programm im Hintergrund laufen lassen.
            taskInstance.GetDeferral()
            AddHandler taskInstance.Canceled, AddressOf TaskEnd
    
        End Sub
    
        ''' <summary>
        ''' Wird ausgeführt, wenn der BackgroundTask beendet wurde.
        ''' </summary>
        Private Async Sub TaskEnd()
    
            'Await _Server.CloseAsncy()
    
        End Sub
    
        ''' <summary>
        ''' Wird ausgeführt, wenn ein neuer Befehl über den Restup gekommen ist.
        ''' </summary>
        ''' <param name="Cmd"></param>
        Private Sub NewCommand(Cmd As String) Handles _Server.NewCommand
            _Display.Show(Cmd)
        End Sub
    
    End Class
    
    
    

    Jetzt die Server-Klasse. Sie wartet bis die richtige URL aufgerufen wurde, ruft den jeweiligen Controller auf, der dann ein Event mit dem neuen "Text" auswerfen soll. Dieses Event soll (noch) StartupTask empfangen und dann an das Display den neuen Text senden.

    Imports Restup.Webserver.Models.Schemas
    Imports Restup.Webserver.Attributes
    Imports Restup.Webserver.Models.Contracts
    Imports Restup.Webserver.Rest
    Imports Restup.Webserver.Http
    
    <RestController(InstanceCreationType.PerCall)>
    Public NotInheritable Class Server
    
        Private _WebServer As Restup.Webserver.Http.HttpServer = Nothing
    
        Public Event NewCommand(Command As String)
    
        Public Sub New()
    
        End Sub
    
        ''' <summary>
        ''' Startet den Restup-Server um Nachrichten zu empfangen.
        ''' </summary>
        Public Sub Start()
    
            Dim restRouteHandler = New RestRouteHandler()
            restRouteHandler.RegisterController(Of Server)()
    
            Dim configuration = New HttpServerConfiguration().ListenOnPort(8800).RegisterRoute("api", restRouteHandler).EnableCors()
    
            Dim httpServer = New HttpServer(configuration)
            httpServer.StartServerAsync().Wait()
    
        End Sub
    
        ''' <summary>
        ''' Wird ausgeführt, wenn die richtige URL aufgerufen wurde.
        ''' Zum Beispiel: http://192.168.1.182:8800/api/Display1/set/1234
        ''' </summary>
        ''' <param name="id"></param>
        ''' <param name="propName"></param>
        ''' <returns></returns>
        <UriFormat("/{id}/set/{propName}")>
        Public Function GetWithSimpleParameters(id As String, propName As String) As IGetResponse
    
            RaiseEvent NewCommand(propName)
    
            Return New GetResponse(GetResponse.ResponseStatus.OK)
    
        End Function
    
    End Class
    

    Das funktioniert aber leider so nicht. Denn, was passiert jetzt? In der Klasse Server wird tatsächlich der Controller aufgerufen. Leider wird aber die Klasse Server von Restup aus komplett neu instanziiert, und folglich RaiseEvent komplett ins Leere.

    Jetzt ist meine Frage: Wie kann ich es schaffen, dass, wenn der Controller aufgerufen wird, der Controller einer BESTEHENDEN Klasseninstanz aufgerufen wird? Ist das überhaupt möglich? Und wenn nein, gibt es überhaupt eine Möglichkeit?

    Gruß
    Andy

    Mittwoch, 25. Oktober 2017 18:09
  • erst einmal darf man BackgroundTask so nicht nutzen. Die meisten BackgroundTask laufen nur 30 Sekunden, manche haben aber auch 10min jetzt um ihre Aufgabe zu erledigen. Innerhalb dieser Zeitspanne muss man deferral.Complete() aufrufen um zu signalisieren das man fertig ist. Macht man das nicht kill das System den BackgroundTask. Für mehr Infos die Doku. Das Programm "im Hintergrund laufen zu lassen" ist bei UWP nicht möglich, zumindest nicht dauerhaft. Wahrscheinlich läuft dein Programm nur in Visual Studio.

    Eine statische Klasse kann man nicht initialisieren und man kann von überall darauf zugreifen. Das ist ja auch der Sinn einer statischen Klasse mit ihren statischen Eigenschaften. Das ist möglich. In deinem Fall wäre aber auch ein Singleton möglich.

    Lass den BackgroundTask weg. Initialisiere man besten alles was Du brauchst direkt beim Programm start.

    Ich sehe nicht das Du irgendwo das NewCommand event abonnierst 


    Gruß Thomas

    Sage nie, ich kann es nicht - sage nur, ich kann es noch nicht!

    Icon für UWP

    Cross Platform Canvas for UWP, Android, iOS

    UWP Community Toolkit Sample App

    Alle Größenangaben in UWP müssen durch 4 teilbar sein

    • Als Antwort markiert AndreasMahub Mittwoch, 25. Oktober 2017 22:08
    Mittwoch, 25. Oktober 2017 20:30
  • erst einmal darf man BackgroundTask so nicht nutzen. Die meisten BackgroundTask laufen nur 30 Sekunden, manche haben aber auch 10min jetzt um ihre Aufgabe zu erledigen. Innerhalb dieser Zeitspanne muss man deferral.Complete() aufrufen um zu signalisieren das man fertig ist. Macht man das nicht kill das System den BackgroundTask. Für mehr Infos die Doku. Das Programm "im Hintergrund laufen zu lassen" ist bei UWP nicht möglich, zumindest nicht dauerhaft. Wahrscheinlich läuft dein Programm nur in Visual Studio.

    Eine statische Klasse kann man nicht initialisieren und man kann von überall darauf zugreifen. Das ist ja auch der Sinn einer statischen Klasse mit ihren statischen Eigenschaften. Das ist möglich. In deinem Fall wäre aber auch ein Singleton möglich.

    Lass den BackgroundTask weg. Initialisiere man besten alles was Du brauchst direkt beim Programm start.

    Ich sehe nicht das Du irgendwo das NewCommand event abonnierst 


    Gruß Thomas

    Sage nie, ich kann es nicht - sage nur, ich kann es noch nicht!

    Icon für UWP

    Cross Platform Canvas for UWP, Android, iOS

    UWP Community Toolkit Sample App

    Alle Größenangaben in UWP müssen durch 4 teilbar sein

    Thomas,

    ich glaube du hast recht. Als ich deinen Beitrag gelesen habe, wurde mir so einiges klarer!

    Erinnerst du dich noch an mein Eingangspost? Also an meine Eingangsfrage? Da hatte ich mich gewundert, warum "meine" SocketStream-Class nach 38-40 Anfragen sich beendet hatte. Vielleicht lag es an dem Backgroundtask?! 

    Windows IoT und Raspberry Pi sind für mich ein totales Neuland. Scheinbar muss ich hier was komplett falsch verstanden haben. Dein Link hat mir ebenfalls sehr geholfen! Ich dachte (vielleicht fälschlicherweise) der BackgorundTask wäre sowas wie ein Dienst bei Windows x86/x64.

    Also, heißt es für mich: sechs, setzen und wieder die "Schuldbank drücken". :-(

    Aber das bekomme ich schon hin :-)

    Gruß
    Andy

    Mittwoch, 25. Oktober 2017 22:18
  • man lernt nie aus ;)

    Ich habe mir das von dir verlinkte Projekt auf Github angeschaut und da wird der Backgroundtask ähnlich falsch benutzt. Vielleicht hast Du das davon?!. Deine Probleme mit dem SocketStream könnten damit zusammenhängen. Ich habe aber die Erfahrung gemacht, das wenn man die Projekte mit VS startet die Backgroundtask nicht vom System gekillt werden. VS räumt immer viel mehr Freiheiten ein, da muss etwas aufpassen und sich nicht wunder warum es außerhalb von VS nicht läuft.

    Bei UWP ist es in der tat so das man vieles nicht darf. Das hat immer seine Vor- und Nachteile. Für mich ist der wichtigste Vorteil das Schadsoftware nicht so leicht entwickelt werden kann und der Store hoffentlich "sauber" bleibt.


    Gruß Thomas

    Sage nie, ich kann es nicht - sage nur, ich kann es noch nicht!

    Icon für UWP

    Cross Platform Canvas for UWP, Android, iOS

    UWP Community Toolkit Sample App

    Alle Größenangaben in UWP müssen durch 4 teilbar sein

    Donnerstag, 26. Oktober 2017 20:23