Windows-Tools mit GUI für einfache Steuerungsaufgaben erstellen



  • Hallo liebe Community! Ich bin neu hier und auf der Suche nach etwas Hilfe beim erstellen eines Programms mit Visual Studio 2015 in C++, welches compiliert eigenständig auf Windows-PCs ab Windows 7 lauffähig sein soll.

    Vorab kurz zu mir: Ich habe noch nie etwas mit Visual Studio gemacht, C++ kenne ich nur rudimentär von Arduino-Programmierung und in C habe ich vor vielen vielen Jahren mal unter Linux kleinere Programme geschrieben. Davor ziehmlich viel mit RealBasic auf Mac und später dann HTML/PHP usw. Programmierung als solches ist mir also nicht fremd 🙂 Schon immer war ich auf der Suche nach eine Möglichkeit ein Programm direkt unter Windows lauffähig zu schreiben, besonders für meine Lieblingshobbys rund um KFZ-Elektronik und KFZ-Netzwerke und hoffe hier auf ein klein wenig Unterstützung um nicht gleich bei den ersten Schritten in Windows-Programmierung zu scheitern.

    Zu meinem Vorhaben: Ich möchte ein Programm erstellen welches direkt, oder über einen virtuellen COM-Port mit einem USB CAN-Adapter kommuniziert. Ich habe hier zwei Modelle am Start, einer mit SLCAN und einer mit ELM327 Protokoll auf der Host-Seite. Beides reine ASCII-Protokolle und sehr simple Befehle.
    In einer GUI möchte ich verschiedene Buttons anbieten mit denen man z.B. bestimmte Fahrzeugfunktionen durch senden von CAN-Nachrichten simulieren kann. Dazu werden einzelne oder Sequenzen von CAN-Frames in bestimmten Zeitintervallen von 10 bis 1000 Millisekunden Wiederholungszeit über den CAN-Adapter auf den CAN-Bus geschickt. Jede Botschaft ist nur wenige Byte groß (max. 16).
    Eine einfache Funktion wäre z.B. "Rückwärtsgang", wobei ein Klick auf den Button die Botschaft sendet und das Fahrzeug, bzw. die Steuergeräte einen eingelegten R-Gang erkennen. Ein weiterer Klick deaktivert dies dann wieder. Wie auch immer man das dann wieder. Entweder wechselt der Button hierbei seinen Labeltext oder es gibt sowie wie Schiebeschalter wie man es aus WebGUIs kennt. Zusätzlich soll in einem virtuellen Cockpit dieser Zustand noch mit einer Anzeige (simulierte LED) dargestellt werden.
    Da die Funktionen wachsen werden muss die darunterliegende Logik in der Lage sein mehrere Botschaften zu "mischen" und einigermaßen zeitstabil zu schedulen. Am ende muss alles über das eine Interface.
    Auch wäre es gut solche Tests aufzeichnen und wiedergeben zu können.
    Die GUI soll also grob gesehen eine Anzeigetafel (virtuelles Cockpit) für die Zustandsanzeige und eine Schaltertafel für die Zustandseinstellung haben.

    Vermutlich ist das für ein erstes Projekt zu ambitioniert, aber damit wisst Ihr schonmal in welche Richtung das bei mir gehen soll.

    Wie gehe ich nun am besten vor?
    Ich denke irgendeine GUI zusammenzuklicken, Menüs zu erstellen, Menuhandler zu coden, das ist alles eher nur Fleißarbeit. Das knackige ist die Engine und die Kommunikationspipeline zum CAN-Interface. Natürlich kommen darüber auch Rückmeldungen vom CAN-Bus die ggf. angezeigt werden müssen. Das ganze ist also Bidirektional.
    Vor allem das mischen von verschiedenen Sendeintervallen der Botschaften ist sicher nicht ohne.

    Vorab schonmal vielen Dank für die Hilfe!!



  • Du hast zwar hier unter "WinAPI" gepostet, aber trotzdem würde ich dir raten ein GUI-Framework zu benutzen, s. [HOWTO] Welches Toolkit für GUIS? oder evtl. auch MFC - damit du dich zeitlich mehr mit der Logik beschäftigen kannst.

    Du solltest jedoch von vornherein die Logik komplett von dem UI-Code trennen (zumindestens eigene Verzeichnisse, evtl. auch als DLLs).

    Bieten die beiden Geräte denn eigene Schnittstellen (Treiber, Software, API) an oder sollen diese geräteunabhängig einfach per serieller Schnittstelle angesprochen werden?

    Wenn du konkrete Fragen zur CAN-Kommunikation hast, dann stelle sie ruhig (ich habe u.a. an einer zertifizierten ISOBUS-Umsetzung mitgearbeitet).



  • Hallo Th69! Schonmal herzlichen Dank für die Hinweise und toll das ich gleich beim ersten Versuch den "Richtigen" treffe :-)) Ich mache das nur aus Hobbygründen und weil es mich interessiert, nicht um damit irgendwas zu erreichen. Von daher gehe ich auch ganz entspannt an die Sache ran.

    Also, das mit Trennung von Businesslogic und GUI kenne ich bereits als "MVC-Pattern" von Web-Applikationen, volle Zustimmung. Damit wäre dann klar das BL und GUI eine Kommunikationsschnittstelle brauchen. Wie realisert man das unter windows? Shared-Memory und Strukturen? IPC?

    Aber vielleicht zäumen wir das Pferd einfach von hinten auf und sagst mir wie DU es machen würdest und ich versuche das zu übernehmen, quasi "Learning by Copying".

    Ich habe mal versucht das ein oder andere Tutorial für Visual Studio 2015 in C++ durchzugehen. Das meiste davon glaube ich verstanden zu haben, aber am Ende solcher Tutorials stehen dann immer so viele Fragen die einem keiner beantwortet.

    Du meinst ich sollte MFC (Microsoft-Foundation-Class, richtig?) nutzen? Geht das mit C++ oder muss ich dazu nach C# wechseln? Und was genau ist damit gemeint, beinhaltet?

    Bei der Applikationsentwicklung mit REALBasic (nennt sich heute Xojo) was das so organisiert, das man eine App-Klasse hatte in der man z.B. seine ganze BL programmierte. Die Fenster, egal ob Dialog, Meldung oder Basis-Fenster der Anwendung, wurden mit einem grafischen Dialogeditor modelliert. Auf den Fenstern wurden dann die Elemente wie Buttons, Listboxen, etc. platziert und über deren Events dann die Funktionen. Das fand ich ganz logisch und ging auch schnell von der Hand weil man praktisch immer einen Funktionsrahmen bekam in dem man codete.

    Bezüglich der Schnittstellen will ich möglichst kompatibel bleiben. Zwischen dem Port (egal ob direct auf USB oder via VCP) und meine BL soll es einen internen "Treiber" geben, welcher die abstrakten Kommandos vom Programm in Adapterspezifische (meist AT-Befehle wie beim Modem) umsetzt und ungehert. Ich möchte wenigstens die beiden Typen ELM und SLCAN unterstützen können.

    Der zentrale Teil der BL wird wohl der Message-Scheduler sein, mit dem zeitgerecht die Datenpakete über die Interfaces gesendet werden können. Hier muss ich einen Weg finden CAN-Frames in fest definierten Intervallen oder innerhalb einer Kommunikation (State-Machine) zu senden. Selbstverständlich muss alles asynchron laufen, aber das ist wohl eh klar?

    Ich denke man bildet am besten intern ein großes Array (Struct) ab, welches immer den aktuellen Zustand bestimmter Kompnenten hält. Die Änderungsereignisse kann man durch die Wertänderung triggern. Auf diese Ereignisse können dann wiederum Anzeigefunktionen reagieren.

    Ich will das mal am Beispiel des Blinkers beschreiben was ich mir dabei denke.

    Der Blinker kennt vier Zustände, AUS, LINKS, RECHTS, BEIDE. Grundsätzlich sind aber linker und rechter Blinke getrennte Komponenten des Fahrzeugs, also hat man pro Blinkerseite: AUS, AN. mehr nicht. Genauso wird es auf dem CAN-Bus auch gesendet, mit 1 Bit pro Seite. Die Module im Fahrzeug reagieren darauf (z.B. das Kombiinstrument und die Türmodule) und schalten analog dazu die Blinkanzeige/Lampe an und aus. Die CAN-Botschaft wird zyklisch gesendet, z.B. alle 100 ms. Ein Lampenzyklus ist jedoch länger, also erhält man mehrfach den gleichen Zustand bis sich dieser mal ändert.

    In Software würde ich das so behandeln. In der App gibt es ein großes Struct welches alle aktuellen Zustände hält. Dieses Struct hat dann einen Member pro Blinker, z.B. "carstat->turnlight_left" und "carstat->turnlight_right". Darin jeweils ein Boolean.

    Eine Empfängerroutine nimmt einfach jedes CAN-Paket vom Interface und platziert es in eine Queue. Auf der anderen Seite der Queue sitzt ein Decoder/Parser der gemäß des empfangenen Interfaces und der CAN-Informationen (ID, Bits) weiss was was ist. So "erkennt" er z.B. auch das Blinkersignal, liest den Zustand in der CAN-Botschaft, vergleicht diesen mit dem im Struct hinterlegten aktuellen Wert und setzt diesen, wenn er anders ist. Gleichzeitig erzeugt er einen Event in einer Eventqueue.

    Die Eventqueue läuft asynchron zum rest der Applikation und ruft Funktionen auf die sich dort für bestimmte Ereignisse registriert haben. Z.B. im Falle des Blinkers für links, das Anzeigesymbol im Fenster der Cockpit-GUI. Dieses erhält durch den Eventaufruf die Info das sich der Zustand geändert hat, liest den Zustand und stellt eine ein- oder ausgeschaltete LED grafisch dar.



  • Das kann alles in einem Prozeß laufen, du brauchst also kein IPC, sondern definierst von der Logik (BL) aus eine Schnittstelle, welche dann von der UI benutzt wird.
    Mir war nur wichtig, daß man das in einem Projekt von Anfang an so macht. Gerade bei dialogbasierten-Projekten habe ich schon des öfteren gesehen, daß der gesamte Code dann komplett im UI implementiert wurde (und das führt dann zum Chaos - und man kann dann auch die UI nicht so einfach tauschen).
    MVC ist dabei schon das richtige Verfahren dafür. 👍

    MFC habe ich (nur) erwähnt, weil es Bestandteil vom Visual Studio ist (im Gegensatz zu den anderen Frameworks, welche gesondert eingebunden werden müssen) - zumindestens bei der Professional oder Communiy Version. Dort gibt es dann (wie von dir durch "GUI zusammenzuklicken" erwähnt) einen Design-Editor.

    Für die Implementierung des Message-Schedulers würde ich zweistufig vorgehen:

    • eine allgemeine sog. "DataLayer"-Schicht, welche die CAN-Kommandos sendet und empfängt (ohne konkretes Wissen der Dateninhalte) - das Empfangen wird dann in einem eigenen Thread realisiert werden müssen, um eben asynchron zu laufen (und informiert per Event die übergeordnete Logik)
    • je nach Funktionalität (oder Gerät) eine Klasse, die dann die Zustände abbildet (und diese stellen dann die Schnittstelle zur UI dar) - evtl. noch eine "Manager"-Klasse, welche dann die Logiken bündelt. Ich persönlich würde dabei vermeiden einfach nur ein großes Array oder Struktur zu benutzen - je mehr Daten es werden, desto unübersichtlicher wird das dann (versuche also objektorientiert dabei vorzugehen ;-).

    Deine Beschreibung liest sich auf jeden Fall sehr gut - man merkt, du hast dir schone viele Gedanken gemacht.

    PS: Ich hatte das ISOBus-Projekt mit C# (WPF sowie Xamarin) erstellt.