Eine gut gestaltete LabVIEW-Anwendung ist unerlässlich, um die Effizienz bei der Entwicklung und Wartung von Software zu maximieren. In diesem Dokument werden grundlegende Konstrukte, Entwurfsmuster und Architekturen im Zusammenhang mit dem Softwaredesign in LabVIEW vorgestellt. Dieser Inhalt ist nicht umfassend, sondern liefert einen nützlichen Ausgangspunkt für einige der gängigsten LabVIEW-Software-Design-Elemente. Folgen Sie den Links im gesamten Dokument, um weitere Informationen zu erhalten.
Jeder der folgenden Abschnitte beschreibt ein gängiges Konstrukt, das in der LabVIEW-Programmierung zur Erfüllung einer bestimmten Aufgabe verwendet wird. Wenn Sie sich mit diesen grundlegenden Konstrukten vertraut machen, können Sie ihre Verwendung in einer LabVIEW-Anwendung leichter erkennen und verstehen.
Mit Typdefinitionen (oft als „Typdefinitionen“ abgekürzt) können Sie einen Datentyp angeben, der in der gesamten Anwendung einheitlich definiert wird. Hier sehen Sie zum Beispiel eine Cluster-Typdefinition:
Wenn Sie diese Typdefinition auf ein Frontpanel oder Blockdiagramm ablegen, erscheint sie als Cluster, aber mit einem schwarzen Dreieck in der linken oberen Ecke an den Anschlüssen und Konstanten:
Typedefs sind äußerst nützlich, um in Ihrer Anwendung konsistente Typen zu definieren. Um dies zu veranschaulichen, beachten Sie, was passiert, wenn wir dem typedef einen neuen Parameter (in diesem Fall einen Konfigurationspfad) hinzufügen:
Durch die Änderung des typedef an einem einzigen Ort (der Quelltypedef .ctl-Datei) wird diese Änderung automatisch in der gesamten Anwendung verbreitet:
Wenn der Cluster keine Typdefinition wäre, müsste jede Instanz des Clusters (auf Frontpaneln und Blockdiagrammen) in Ihrer gesamten Anwendung manuell aktualisiert werden. Mit seltenen Ausnahmen sollten Sie alle Cluster und Enums, die Sie während des Schreibens von LabVIEW-Anwendungen erstellen, typisieren.
Durch die Verwendung von Typdefinitionen verbessern Sie die Wartbarkeit Ihres Codes, indem Sie gemeinsam genutzte Datentypen mit einer Quelle verwenden.
Die Ereignisstruktur bietet die Möglichkeit, auf Ereignisse der Benutzeroberfläche und programmatische Ereignisse in einem LabVIEW-Blockdiagramm zu reagieren. So können Sie beispielsweise ein Ereignisdiagramm registrieren, das immer dann ausgeführt wird, wenn sich der Wert eines Frontpanel-Elements ändert. Programmatisch erzeugte Ereignisse können auch mit der Funktion „Benutzerereignis erzeugen“ in einem Blockdiagramm registriert werden.
Eine Ereignisstruktur wird in der Regel in eine While-Schleife eingefügt, so dass der Code während der Ausführung auf mehrere Ereignisse reagieren kann. Konfigurieren Sie die Ereignisstruktur so, dass sie unterschiedliche Rahmen für die verschiedenen Ereignisse enthält, die erkannt werden sollen. Jeder Ereignis-Frame enthält den Handling-Code, der bei jedem Ereignis ausgeführt wird.
Sehen Sie sich die folgenden Projekte an, die mit LabVIEW geliefert werden, um einige Beispiele zu veranschaulichen, die Ereignisse der Benutzeroberfläche und programmatische Ereignisse veranschaulichen:
Die funktionale globale Variable (auch bekannt als „FGV“, „LabVIEW 2 Style global“ oder „Action Engine“) ist ein Datenspeichermechanismus. Die FGV speichert Daten in einem nicht initialisierten Schieberegister oder einem nicht initialisierten Rückkopplungsknoten und ermöglicht Ihnen den Zugriff auf diese Daten von überall (d. h. „global“) in einer Anwendung. Das Blockdiagramm eines einfachen FGV-VIs enthält Daten (Eingang) und Daten (Ausgang) sowie eine Enum-Operation mit zwei Aktionen („Festlegen“ und „Ermitteln“):
Der Funktionsumfang dieser einfachen Get/Set-FGV entspricht der Verwendung einer globalen Variablen. In der Praxis wird Ihr FGV wahrscheinlich zusätzlich zu „Festlegen“ und „Ermitteln“ zusätzliche Funktionen ausführen.
Sie können die FGV verwenden, um Referenzen zu speichern und komplexere Operationen durchzuführen. Sehen Sie sich zum Beispiel die Datei-I/O-FGV unten an (die die Verwendung eines nicht initialisierten Schieberegisters anstelle eines Rückkopplungsknotens veranschaulicht). Diese FGV speichert die Dateireferenz zwischen aufeinander folgenden Aufrufen und führt mehrere verschiedene Aktionen (Öffnen, Lesen, Schreiben und Schließen) in einem VI aus.
Beachten Sie, dass FGVs genauso anfällig für Laufzeitprobleme sind wie globale Variablen. In der Regel sollten Sie vermeiden, an mehr als einer Stelle in Ihrem Code an globale Daten zu schreiben.
Ein Unterpanel ist ein Frontpanel-Element, mit dem das Frontpanel eines anderen VIs angezeigt werden kann. Mit Hilfe eines Unterpanels können Sie eine dynamische Benutzeroberfläche entwerfen, in der Sie ganze Abschnitte Ihrer Benutzeroberfläche austauschen können, indem Sie ein anderes VI angeben, das im Unterpanel angezeigt werden soll. Das Unterpanel fördert auch die modulare Anwendungsentwicklung, bei der logische Gruppierungen von Frontpanel-Objekten in separaten VIs enthalten sein können. Mit diesem Entwurf können Sie einzelne VIs, die in Unterpanels angezeigt werden, bearbeiten, ohne den Programmcode anderer Anzeige-VIs oder des Haupt-Anwendungs-VIs zu ändern.
Das folgende Beispiel durchsucht einen Ordner nach Plugin-VIs und fügt diese Plugins in einen Textring ein. Wenn Sie ein Objekt im Textring auswählen, wird das dazugehörige Plugin-VI in das Unterpanel des Haupt-VIs eingefügt. Auf diese Weise können Sie neue Plugin-VIs auf dem Datenträger hinzufügen, ohne Änderungen am Haupt-VI vorzunehmen.
Führen Sie das folgende Beispiel aus, das mit LabVIEW geliefert wird, um mehr mit Unterpanels zu experimentieren:
Per Voreinstellung sind VIs in LabVIEW ablaufvariant. Dies bedeutet einfach, dass nur eine Instanz eines VI gleichzeitig ausgeführt werden kann. Im folgenden Diagramm muss also eines der SubVI-Instanzen des VIs „Process.vi“ warten, bis das andere beendet ist.
Wenn mehrere Instanzen eines VIs parallel ausgeführt werden sollen, muss das VI ablaufinvariant sein. Gehen Sie dazu zu Datei > VI-Eigenschaften > Ausführung und ändern Sie die Einstellung „Ablaufinvarianz“ in „Ablaufinvariante Ausführung mit gemeinsam genutzter Kopie“ oder „Ablaufinvariante Ausführung mit vorbelegter Kopie“.
Wenn Sie ein VI ablaufinvariant gestalten, können mehrere Instanzen parallel ausgeführt werden:
In folgenden Szenarien können Sie Ihre VIs ablaufinvariant gestalten:
Beachten Sie, dass funktionale globale Variablen fast immer ablaufvariant sind, da sie globale Zustandsdaten speichern, die Ihre gesamte Anwendung gemeinsam nutzen muss.
Führen Sie das folgende Beispiel aus, das mit LabVIEW geliefert wird, um mit ablaufinvarianten VIs zu experimentieren:
Wenn Sie ein VI meist von einem anderen VI aus aufrufen, verwenden Sie einen regulären SubVI-Aufruf. Beim Aufrufen eines SubVIs muss das aufrufende Diagramm warten, bis das SubVI seine Ausführung beendet hat, bevor es fortfahren kann. Dies ist ein Beispiel für einen synchronen Aufruf.
In manchen Situationen kann es jedoch erforderlich sein, dass Sie ein SubVI starten, das aufrufende VI jedoch weiter ausführen, während das SubVI weiter ausgeführt wird. Dies ist ein Beispiel für einen asynchronen Aufruf. Mit asynchronen VI-Aufrufen können Sie dynamisch eine beliebige Anzahl von VIs parallel ausführen. Darüber hinaus ist die Verwendung ablaufinvarianter VIs ermöglicht es Ihnen, eine beliebige Anzahl von Instanzen desselben VIs zu erzeugen und diese asynchron auszuführen.
Im folgenden Beispiel ruft das aufrufende VI mehrere Instanzen des ablaufinvarianten VIs „Process.vi“ asynchron auf. Jedes VI wird unabhängig voneinander ausgeführt und gibt nach Beendigung der Ausführung Daten an das aufrufende VI aus. Das aufrufende VI kann anderen Blockdiagramm-Code parallel ausführen und muss nicht auf den Abschluss der Prozess-VI-Instanzen warten.
Sehen Sie sich das folgende Projekt an, das mit LabVIEW geliefert wird, für einige Beispiele, die weitere Einzelheiten zu asynchronen VI-Aufrufen veranschaulichen:
Objektorientierte Programmierung wird in LabVIEW über LabVIEW-Klassen erleichtert. Objektorientierte Programmierung in LabVIEW verwendet Konzepte aus anderen objektorientierten Programmiersprachen wie C++ und Java, einschließlich Klassenstruktur, Kapselung und Vererbung. Mit objektorientierter Programmierung kann Programmcode erstellt werden, der sich einfach und ohne Einfluss auf andere Abschnitte der Anwendung bearbeiten und warten lässt. Es gibt viele objektorientierte Entwurfsmuster, die auf LabVIEW angewendet werden können. Diese werden im Dokument Anwendung von gebräuchlichen objektorientierten (OO) Entwurfsmustern auf LabVIEW beschrieben.
Entdecken Sie die Projekte im folgenden Ordner, der im Produktumfang von LabVIEW enthalten ist, für mehrere Beispiele, die die Grundlagen der Programmierung mit LabVIEW-Klassen veranschaulichen:
Ein Entwurfsmuster ist ein theoretischer Mechanismus zur Ausführung von synchronem oder asynchronem Programmcode. Die meisten realen Architekturen (siehe Abschnitt unten) verwenden ein oder mehrere Entwurfsmuster als Teil ihres Kernausführungsmechanismus. Das Verständnis des Verhaltens der Entwurfsmuster in diesem Abschnitt kann Ihnen helfen, das Verhalten komplexerer Architekturen, die auf diesen Entwurfsmustern basieren, besser zu verstehen.
Ein Zustandsautomat ist die LabVIEW-Blockdiagrammimplementierung eines Zustandsdiagramms oder Ablaufdiagramms. Ein gegebener „Zustand“ verfügt über Blockdiagrammlogik, um den nächsten auszuführenden Zustand zu bestimmen. Manche Zustandsautomaten werden von einer Benutzeroberfläche gesteuert, bei der die Benutzereingabe oder die Blockdiagrammlogik bestimmen kann, welcher Zustand als Nächstes ausgeführt wird.
Ein einfacher LabVIEW-Zustandsautomat besteht aus folgenden Hauptkomponenten:
Sehen Sie sich das folgende Projekt an, das mit LabVIEW geliefert wird, um mehr über Zustandsautomaten zu erfahren:
Erstellen Sie Ihr eigenes Zustandsautomatenprojekt in LabVIEW mit Hilfe der Projektvorlage „Einfacher Zustandsautomat“. Wählen Sie dazu das Menü „Datei“ in LabVIEW >> „Projekt erstellen“ >> „Einfacher Zustandsautomat“. Sie können diese Vorlage als Ausgangspunkt für Ihre eigene Anwendung modifizieren.
Das Erzeuger/Verbraucher-Entwurfsmuster zeigt, wie mit Hilfe einer Queue Daten zwischen mehreren Schleifen ausgetauscht werden, die mit unterschiedlichen Raten ausgeführt werden. Die Schleife, die die Funktion „Element einfügen“ enthält, ist „Erzeuger“ und die Schleife, die die Funktion „Element aus Queue entfernen“ enthält, ist „Verbraucher“. Die Verwendung einer Queue sorgt dafür, dass keine Daten verloren gehen, wenn die Schleifen mit unterschiedlichen Geschwindigkeiten laufen.
Beim Erzeuger/Verbraucher-Entwurfsmuster ist zu beachten, dass es weitgehend theoretischist. In der Praxis sehen Sie das grundlegende Erzeuger/Verbraucher-Muster nur selten im realen Programmcode. Stattdessen ist es viel wahrscheinlicher, dass Sie den Handler für Nachrichten-Queues sehen.
Führen Sie das folgende Beispiel aus, das mit LabVIEW geliefert wird, um mehr über die Verwendung von Queues zum Austausch von Daten zwischen Schleifen zu erfahren:
Der Handler für Nachrichten-Queues ist eine Implementierung des Erzeuger/Verbraucher-Entwurfsmusters, das auch eine Ereignisstruktur für die Benutzeroberfläche und die programmatische Ereigniserstellung enthält. Die Ereignisverarbeitungsschleife enthält eine Ereignisstruktur, die Nachrichten in die Queue einfügt (erzeugt) und eine Nachrichtenverarbeitungsschleife, die sie aus der Queue entnimmt (konsumiert). Die Nachrichtenverarbeitungsschleife kann auch nach Bedarf Nachrichten für sich selbst generieren. Die „Nachrichten“ in diesem Fall sind Cluster, die einen String (der die Case-Struktur in der MHL antreibt) und einen Variant enthalten, der nachrichtenspezifische Daten beliebigen Typs enthalten kann. Im Handler für Nachrichten-Queues kann die Nachrichtenverarbeitungsschleife auch mit Hilfe von Benutzerereignissen mit der Ereignisverarbeitungsschleife kommunizieren.
Sehen Sie sich das folgende Projekt an, das mit LabVIEW geliefert wird, um mehr über den Handler für Nachrichten-Queues zu erfahren:
Erstellen Sie Ihr eigenes Projekt auf Grundlage des Handlers für Nachrichten-Queues in LabVIEW mit Hilfe der entsprechenden Projektvorlage. Wählen Sie dazu das Menü „Datei“ in LabVIEW >> „Projekt erstellen…“ >> „Handler für Nachrichten-Queues“.
Damit eine umfangreiche LabVIEW Anwendung erweiterbar und wartbar ist, muss sie gut strukturiertsein.
Stellen Sie sich eine theoretische LabVIEW-Anwendung zum Testen eines Geräts vor. Sie wird Programmcode für die Konfiguration der Anwendung, die Verbindung mit dem Gerät, die Durchführung von Messungen, die Anzeige von Daten, die Protokollierung von Daten, die Bearbeitung von Fehlerbedingungen und möglicherweise viele weitere Funktionen enthalten.
Wenn sich alle beschriebenen Funktionen dieser Anwendung im selben LabVIEW-Blockdiagramm befinden, ist es unmöglich, die Funktionen der Anwendung isoliert zu entwickeln, zu testen und auf Fehler zu untersuchen. Wenn es zum Beispiel ein Problem mit dem Logging-Code gibt, gibt es keine Möglichkeit, nur das Logging zu debuggen, ohne die gesamte Anwendung auszuführen. Darüber hinaus kann sich eine Änderung des Protokollcodes unbeabsichtigt auf einen anderen Teil der Anwendung auswirken, da er sich im selben VI befindet.
Jede der oben beschriebenen Funktionen eignet sich besser als asynchroner Prozess, bei dem jede Funktion modular ist und unabhängig voneinander entwickelt, getestet und auf Fehler untersucht werden kann. Außerdem wäre ein robuster Kommunikationsmechanismus zwischen den Prozessen erforderlich.
Ein strukturierter Ansatz zur Implementierung von LabVIEW-Code zur Erleichterung dieses modularen asynchronen Entwurfs wird als Architektur bezeichnet. Einige LabVIEW-Teams entwickeln eigene Architekturen. Dies kann eine schwierige Aufgabe sein, da es viele Überlegungen, Fallstricke und Vorsichtsmaßnahmen gibt, die mit der Entwicklung der Code-Infrastruktur und des Nachrichtenaustauschs verbunden sind, die für die Implementierung einer erfolgreichen asynchronen LabVIEW-Anwendung erforderlich sind. Darüber hinaus erfordert die Menge an Boilerplate-Code, die für die Implementierung eines robusten Frameworks benötigt wird, genügend Werkzeuge innerhalb des Frameworks, um sich wiederholende Aufgaben zu automatisieren (um Entwicklern zu helfen, Fehler zu vermeiden, wenn sie versuchen, selbst Framework-Code manuell zu programmieren).
Glücklicherweise wurde diese harte Arbeit bereits von mehreren Unternehmen (sowohl innerhalb als auch außerhalb von NI) geleistet, die der LabVIEW-Community ihre verbraucherfreundlichen Architekturen anbieten. Zwei der heute am häufigsten verwendeten LabVIEW-Architekturen sind das Akteur-Framework und DQMH.
Das Akteur-Framework (AF) ist eine von NI unterstützte Architektur, die mit LabVIEW geliefert wird. Es handelt sich um eine objektorientierte Implementierung des Handlers für Nachrichten-Queues. Anstelle von Nachrichtenverarbeitungs-Schleifen-Frames in einer Case-Struktur und „Element einfügen“-Funktionen stellen Nachrichtenklassen sogenannte „Ausführen“-Methoden-VIs zur Ausführung von spezifischem Programmcode in einem Akteur-Hauptteil-VI bereit.
Der klassenbasierte Entwurf des AF bietet einen erweiterbaren Rahmen für die Implementierung von Unterakteuren, die das Verhalten der übergeordneten Akteure erben, was mit dem auf Case-Strukturen basierenden Standardansatz des Handlers für Nachrichten-Queues nicht möglich ist. Darüber hinaus können alle Kernfunktionen des Frameworks (Initialisierung, Meldungen, Fehlerbeantwortung usw.) in einem bestimmten Akteur aufgrund des objektorientierten Designs des Frameworks erweitert oder überschrieben werden.
Weitere Informationen zum Akteur-Framework finden Sie im folgenden Projekt, das mit LabVIEW geliefert wird:
Erstellen Sie Ihr eigenes Projekt auf Grundlage des Akteur-Frameworks in LabVIEW mit Hilfe der entsprechenden Projektvorlage. Wählen Sie dazu das Menü „Datei“ in LabVIEW >> „Projekt erstellen…“ >> „Akteur-Framework“.
Beachten Sie, dass der Erfolg mit dem Akteur-Framework von einer starken Grundkenntnis der objektorientierten Programmierung sowie einer angemessenen Unterweisung und einem Verständnis des Frameworks selbst abhängt. Es stehen zahlreiche Schulungsressourcen rund um das Akteur-Framework zur Verfügung. Zu den beliebtesten zählen:
Wenn Sie anfangen, die AF-Architektur in Ihren eigenen Anwendungen zu verwenden, können Sie mit anderen Benutzern in der Akteur-Framework – NI Community-Benutzergruppe interagieren, um Fragen zu stellen und bewährte Praktiken mit anderen Programmierern zu diskutieren.
Der Delacor-Handler für Nachrichten-Queues (DQMH®) verwendet einen ähnlichen Mechanismus wie der Standard-Handler für Nachrichten-Queues für die Kommunikation innerhalb eines gegebenen Prozesses (oder „Modul“), aber die Kommunikation zwischen asynchronen Modulen erfolgt über Benutzerereignisse. DQMH ist keine objektorientierte Architektur, obwohl es LabVIEW-Klassen für einige kleinere Komponenten des Framework verwendet.
DQMH ist eine Drittanbieter-Architektur, die vom DQMH-Konsortium entwickelt und gepflegt wurde. Das Hauptziel des ursprünglichen DQMH-Entwicklungsteams war es, der LabVIEW-Community ein kostenloses Framework zur Verfügung zu stellen, das für Entwickler auf CLAD-/CLD-Ebene zugänglich ist.
Sie können DQMH mit Hilfe des VI-Paketmanagers installieren.
Sobald das DQMH-Paket installiert ist, können Sie das folgende Projekt erkunden, um mehr zu erfahren:
Für DQMH stehen unzählige Trainingsressourcen zur Verfügung. Wenn Sie anfangen, die DQMH-Architektur in Ihren eigenen Anwendungen zu verwenden, können Sie mit anderen Benutzern in der DQMH-Konsortiums-Toolkit – NI Community-Benutzergruppe interagieren, um Fragen zu stellen und bewährte Praktiken mit anderen Programmierern zu diskutieren.
DQMH® ist eine eingetragene Marke des DQMH Consortium, LLC.
AF und DQMH sind die beliebtesten LabVIEW-Architekturen der Welt, aber sie sind keineswegs die einzigen. Weitere Architekturen, die der LabVIEW-Community zur Verfügung stehen, sind: