Einfacher Webzugriff auf LabVIEW VI PHP-Anwendungen über ActiveX Server

Seit vielen Jahren ist LabVIEW in der Lage, das Web ohne komplizierte Veröffentlichungs- und Servereinstellungen von LabVIEW mit VI-Geräten zu verknüpfen, wobei nur der integrierte ActiveX-Server verwendet wird. Die LabVIEW 2020 Community Edition ist keine Ausnahme.

Für LabVIEW gibt es derzeit verschiedene Möglichkeiten, virtuelle Geräte im Web zu veröffentlichen, was unterschiedliche Kenntnisse und unterschiedliche Funktionen erfordert. In diesem Artikel werde ich sie nicht beschreiben, aber ich möchte Ihnen die nicht standardmäßige Verwendung des in LabVIEW integrierten ActiveX / COM-Servers vorstellen, um den Webzugriff auf VI zu organisieren und die LabVIEW-Umgebung selbst zu verwalten. Obwohl ActiveX / COM bereits eine alte Technologie ist, die weiterhin unter Windows funktioniert, können Sie LabVIEW- und VI-Geräte über den integrierten ActiveX-Server problemlos verwalten, auch über das Web.

Das erste, was Sie tun müssen, ist, denselben ActiveX-Server in LabVIEW aufzunehmen. Dies erfolgt in den Umgebungseinstellungen: Extras-> Optionen-> VI-Server, das Kontrollkästchen ActiveX.


Sie können mithilfe eines einfachen VBScript-Skripts überprüfen, ob der Server eingeschaltet ist und Zugriff darauf hat. Sie müssen die Textdatei labview_test.vbs auf dem Desktop erstellen und mit den folgenden Inhalten füllen:

Dim obj
Set obj = CreateObject("LabVIEW.Application")
'Dim vi
'Set vi = obj.GetVIReference("C:\Users\Dell\Desktop\LabVIEW Web ActiveX\ActiveX Server Executable _LV2012_NI Verified\Executable as ActiveX Server\ActiveX Server.vi")
WScript.Echo(obj.AppName & " ver: " & obj.Version)
'WScript.Echo(vi.GetControlValue("Count"))
'Set vi = Nothing
Set obj = Nothing

Führen Sie vor dem Ausführen des Skripts die LabVIEW-Umgebung aus. Es funktioniert jedoch, ohne zuerst die Umgebung zu starten. Während der Ausführung des Skripts wird die LabVIEW-Instanz als ActiveX / COM-Server gestartet, und nach Abschluss des Skripts wird die Instanz geschlossen, sodass Sie warten müssen, bis all dies „geladen und entladen“ ist. Die Ausgabe von labview_test.vbs ist der Name der Stammanwendung und ihrer Version.


Als Nächstes habe ich ein einfaches VI-Gerät "ActiveX Server.vi" erstellt. Es enthält mehrere Bedienelemente und Zusatzfunktionen. Dieses VI werden wir laden und verwalten.


Von LabVIEW brauchen wir nichts anderes. Jetzt können Sie mit den Webebenen beginnen.

Dorniger Weg


Zuerst habe ich ein bisschen mit dem Standard-Windows-Webserver Microsoft IIS experimentiert. Ich habe versucht, ASP-Seiten in VBScript mit den folgenden Inhalten zu erstellen:

<% @language = "vbscript" %>
<html><body>
<p>ASP can output HTML tags as well as plain text</p>
<%
	Dim obj
	Set obj = CreateObject("LabVIEW.Application")
	response.write(obj.AppName & " ver: " & obj.Version & "<br>" & vbCr)
	Dim vi
	Set vi = obj.GetVIReference("C:\Users\Dell\Desktop\LabVIEW Web ActiveX\ActiveX Server Executable _LV2012_NI Verified\Executable as ActiveX Server\ActiveX Server.vi")
	response.write(vi.GetControlValue("Count") & vbCr)
	set vi = Nothing
	set obj = Nothing
%>
</body></html>

Die GetVIReference () -Methode lädt das VI in den Speicher und stellt eine Verbindung damit her. Hauptparameter: absoluter Pfad zum ausgewählten VI.

Skriptausgabe an den Browser:

LabVIEW.exe ver: 20.0
123 

Es stimmt, ich musste ein wenig an den Einstellungen des IIS-Anwendungspools und der anonymen Benutzerüberprüfung basteln, die ich für den aktuellen Windows-Benutzer konfiguriert habe.

Ich entschied mich, nicht tief in ASP einzusteigen und wechselte zu PHP. Für IIS habe ich den PHP FastCGI-Daemon konfiguriert. Ich gebe Ihnen die Einstellungen nicht, sie sind für den Hauptteil dieses Artikels nicht wichtig. PHP konnte auch nach Typ auf das LabVIEW COM-Objekt zugreifen:

$obj = new COM('LabVIEW.Application');

In beiden Fällen, wenn LabVIEW ausgeführt wird und VI darin geöffnet ist (ActiveX Server.vi). Beim Anfordern eines PHP (ASP) -Skripts wurde parallel (und über einen längeren Zeitraum) eine neue Instanz von LabVIEW.exe gestartet und anschließend mithilfe der GetVIReference () -Methode eine eigene Instanz von ActiveX Server.vi geladen. Nach Abschluss des PHP-Skripts wurde die LabVIEW-Instanz geschlossen. Jene. Es gab keine Überschneidung mit einer bereits ausgeführten Instanz der LabVIEW-Umgebung. Mit dem bekannten Process Explorer-Dienstprogramm wird dies gut beobachtet. Das "Spiel" mit den Einstellungen des IIS-Anwendungspools ergab ebenfalls kein besonderes Ergebnis. Ich bin zu dem Schluss gekommen, dass IIS im Namen des Systems als Systemdämon fungiert und daher eine separate Instanz von LabVIEW.exe erstellt wird, die an den Systemkontext gebunden ist, und ich kann eine bereits geöffnete Instanz nicht als Windows-Benutzer wiederverwenden.

Dann entstand die Idee, einen Webserver eines Drittanbieters zu testen, der im einfachen Anwendungsmodus für den aktuellen Benutzer ausgeführt wird. Die Wahl fiel auf NGINX, außerdem habe ich es bereits als Reverse-Proxy für LabVIEW WebServices verwendet.

Nginx


Wir verwenden die derzeit verfügbare Version von nginx für Windows. Derzeit nginx-1.17.10. Um PHP mit NGINX zu verbinden, habe ich die folgende Beschreibung verwendet .

Einfache minimale NGINX-Einrichtung. Ich habe eine Datei: c: \ nginx-1.17.10 \ conf \ nginx.conf
Hinzufügen der Stammverzeichnisliste zum Browser:

nginx.conf:

location / {
	root   html;
	index  index.html index.htm;
	autoindex on;
}

Aktivieren von PHP über FastCGI im Serverstamm:

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
	root           html;
	fastcgi_pass   127.0.0.1:9000;
	fastcgi_index  index.php;
	fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
	include        fastcgi_params;
}

Php


Wir nehmen die aktuelle Version von PHP für Windows . Ich habe php-7.4.5-nts-Win32-vc15-x64.zip verwendet. Ich werde es in c: \ php-7.4.5-nts-Win32-vc15-x64 haben.

Benennen Sie php.ini um und konfigurieren Sie es (das ist php.). Ini-Entwicklung aus dem Archiv).

Nehmen Sie folgende Änderungen vor: php.ini:

short_open_tag = On
html_errors = On
error_reporting = E_ALL & ~E_NOTICE
extension_dir = "ext"
extension=gd2
extension=php_com_dotnet.dll

Hier ist die GD-Bibliothek für die Arbeit mit Bildern (wenn Sie einige Bilder von LabVIEW benötigen) und das Modul php_com_dotnet.dll für die Arbeit mit ActiveX / COM-Objekten in PHP verbunden.

Bei der Arbeit mit COM in PHP wurde ein unangenehmer Fehler beim Arbeiten mit Zeichenfolgen (VT_BSTR) entdeckt, die 0x0 Zeichen im Text enthalten. Es wird gelöst, indem php_com_dotnet.dll durch neu kompiliert mit dem Fix ersetzt wird. Die Fehlerbeschreibung und den Patch mit dem Fix finden Sie hier . Leider ist es in PHP noch nicht offiziell behoben. Ich habe php_com_dotnet.dll (für php-7.4.5-nts-Win32-vc15-x64) neu erstellt. Die feste Datei php_com_dotnet.dll finden Sie unter dem Link . Eine Anleitung für selbst erstellendes PHP und Erweiterungen finden Sie hier .

Standardmäßig wird NGINX auf TCP-Port 80 ausgeführt, dem PHP FastCGI-Daemon auf Port 9000. Überprüfen Sie, ob keine anderen funktionierenden Anwendungen diese Ports verwenden.

Das Starten und Stoppen der NGINX- und PHP FastCGI-Dämonen kann auf verschiedene Arten organisiert werden. Diese Cmd-Skripte wurden für meine Debugging-Anforderungen erstellt: start-restart-all.cmd, das im Hintergrund gestartet / neu gestartet wird (ohne dass Dämonenfenster geöffnet sind) und kill-all.cmd stoppt, das ich in das NGINX-Verzeichnis gestellt habe. Verwendetes Dienstprogramm zum Ausführen versteckter Konsolen, entnommen aus der Beschreibung .

start-restart-all.cmd:

rem @echo off
set PHP_FCGI_MAX_REQUESTS=0
@echo Shutting down servers...
taskkill /f /IM nginx.exe
taskkill /f /IM php-cgi.exe
@timeout 1
@echo Starting servers...
@rem start /b /D "C:\php-7.4.5-nts-Win32-vc15-x64" php-cgi.exe -b 127.0.0.1:9000
RunHiddenConsole.exe "C:\php-7.4.5-nts-Win32-vc15-x64\php-cgi.exe" -b 127.0.0.1:9000
start /b /D "c:\nginx-1.17.10\" nginx.exe
@timeout 3

kill-all.cmd:

taskkill /f /IM nginx.exe
taskkill /f /IM php-cgi.exe
pause

Ich möchte auf die Umgebungsvariable PHP_FCGI_MAX_REQUESTS achten. Standardmäßig ist es 500. Und nach 500 Anforderungen wird der PHP FastCGI-Dämon seine Arbeit abschließen, daher habe ich diesen Zähler für das Debuggen deaktiviert. Hier ist ein Zitat aus der Dokumentation zur Reflexion:

Dieses PHP-Verhalten kann deaktiviert werden, indem PHP_FCGI_MAX_REQUESTS auf 0 gesetzt wird. Dies kann jedoch ein Problem sein, wenn die PHP-Anwendung Ressourcen verliert. Alternativ kann PHP_FCGI_MAX_REQUESTS auf einen viel höheren Wert als den Standardwert eingestellt werden, um die Häufigkeit dieses Problems zu verringern.

Ich habe 2 PHP-Testskripte labview.php, labview_png.php geschrieben, die im Stammverzeichnis des Webservers C: \ nginx-1.17.10 \ html
labview.php abgelegt werden müssen - dies ist das Hauptbeispielskript
labview_png.php - Gibt ein PNG-Bild von einer Zeichenfolge vom Typ VT_BSTR zurück, die vom LabVIEW ActiveX-Server gelesen wurde.

labview.php
<?php
if(strpos(exec('tasklist /FI "IMAGENAME eq LabVIEW.exe" /NH'), 'LabVIEW.exe') === false)
	exit("  LabVIEW.exe");?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<title>LabVIEW PHP COM example</title>
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
	<script>
		// setTimeout(function(){
		//	window.location.reload(1);
		// }, 3000);
		// setInterval(function() {
		//	var myImageElement = document.getElementById('myImage');
		// 	myImageElement.src = 'labview_png.php?rand=' + Math.random();
		//}, 200);
	 
		$(document).ready(function(){
			setInterval(function(){
				$("#png").attr('src', 'labview_png.php?rand=' + Math.random());
				$("#auto").load(location.href + " #auto");
			}, 1000);
		});
</script>
	
</head> 
<body>
<?php
//phpinfo();
echo '_GET val: ';
foreach ($_GET as $key => $value)
	echo "$key=$value, ";
echo '<br>', PHP_EOL;

echo '_POST val: ';
foreach ($_POST as $key => $value)
	echo "$key=$value, ";
echo '<br>', PHP_EOL;

define('FPStateInfo', ['Invalid', 'Standard', 'Closed', 'Hidden', 'Minimized', 'Maximized']);
define('ExecStateInfo', ['eBad 0 VI has errors; it cannot run', 'eIdle 1 VI is not running, but the VI is in memory.', 'eRunTopLevel 2 VI is running as a top-level VI', 'eRunning 3 VI is running as a subV']);

$obj = new COM('LabVIEW.Application');
//com_print_typeinfo($obj);

$vi = $obj->GetVIReference('C:\Users\Dell\Desktop\LabVIEW Web ActiveX\ActiveX Server Executable _LV2012_NI Verified\Executable as ActiveX Server\ActiveX Server.vi');

//$vi->OpenFrontPanel();

echo '<form action="" method="post">';
echo '<input type="button" value="Refresh page" onClick=\'window.location.href=window.location.href\'>', PHP_EOL;

$fpstate = $vi->FPState();
$vistate = $vi->ExecState();

if ($_POST['action']==='run_vi' && $vistate <= 1) {
	$vi->Run(true); // async Boolean If TRUE, you do not need to wait for the VI to finish running. The default is FALSE.
} elseif ($_POST['action']==='stop_vi' && $vistate > 1) {
	//$vi->SetControlValue('stop', true);
	//sleep(1);
	$vi->Abort();
} elseif ($_POST['action']==='open_fp' && $fpstate==2) {
	$vi->OpenFrontPanel();
} elseif ($_POST['action']==='close_fp' && $fpstate!=2) {
	$vi->CloseFrontPanel();
}

if ($_POST['Count2']) {
	$vi->SetControlValue('Count2', $_POST['Count2']);
}

echo '<h3>SetControlValue(\'Count2\'):</h3>', PHP_EOL;
echo '<input onchange="this.form.submit()" type="number" name="Count2" value="', $vi->GetControlValue('Count2'), '">', PHP_EOL;

echo '<div id="auto">';

echo '<h3>AppName / Version:</h3>', PHP_EOL;
echo $obj->AppName(), ' / ', $obj->Version(), '<br>', PHP_EOL;

echo '<h3>ExportedVIs:</h3>', PHP_EOL;
foreach ($obj->ExportedVIs() as $value)
	echo $value, '<br>', PHP_EOL;

echo '<h3>FPState:</h3>', PHP_EOL;
$fpstate = $vi->FPState();
echo $fpstate, ', ', FPStateInfo[$fpstate], PHP_EOL;

echo '<button name="action" type="submit" value="open_fp">OpenFrontPanel</button>', PHP_EOL;
echo '<button name="action" type="submit" value="close_fp">CloseFrontPanel</button>', PHP_EOL;

echo '<h3>ExecState:</h3>', PHP_EOL;
$vistate = $vi->ExecState();

if ($vistate > 1) {
	echo '<font color="blue">', $vistate, ', ', ExecStateInfo[$vistate], '</font>', PHP_EOL;
} else {
	echo $vistate, ', ', ExecStateInfo[$vistate], PHP_EOL;
}

echo '<button name="action" type="submit" value="run_vi">Run VI</button>', PHP_EOL;
echo '<button name="action" type="submit" value="stop_vi">Abort VI</button>', PHP_EOL;
echo '</form>', PHP_EOL;

echo '<h3>GetControlValue(\'Count\') / GetControlValue(\'Count2\'):</h3>', PHP_EOL;
echo $vi->GetControlValue('Count'), ' / ', $vi->GetControlValue('Count2'), PHP_EOL;
//echo $vi->SetControlValue('Count2', $vi->GetControlValue('Count')+1), PHP_EOL;

echo '<h3>Array1:</h3>', PHP_EOL;
foreach ($vi->GetControlValue('Array1') as $value)
	echo $value, '<br>', PHP_EOL;

//$png_data = new variant(null, VT_UI1);
//$png_data = variant_set_type($vi->GetControlValue('png data'), VT_UI1);
//echo variant_cast($vi->GetControlValue('png1'), VT_BSTR), PHP_EOL;
//echo mb_strlen($vi->GetControlValue('String1')), PHP_EOL;
//echo variant_get_type($vi->GetControlValue('png1')), PHP_EOL;

echo '<h3>PNG data:</h3>', PHP_EOL;
$png_data = $vi->GetControlValue('PNG data');
echo 'PNG size:' , strlen($png_data), '<br>', PHP_EOL;


echo '</div>';

if ($vistate > 1 && $fpstate!=2) {
	echo '<img src="labview_png.php" id="png">';
}

// variant_set_type($variant, VT_BSTR)
//$png_data = variant_cast($vi->GetControlValue('png data'), VT_U1);


//echo  variant_get_type($png_data), PHP_EOL;
echo $vi->SetControlValue('String1', "123\x00555321");
//com_print_typeinfo($vi);
$obj = null;
?>
</body>
</html>


labview_png.php
<?php
if(strpos(exec('tasklist /FI "IMAGENAME eq LabVIEW.exe" /NH'), 'LabVIEW.exe') === false)
	exit("  LabVIEW.exe");
$obj = new COM('LabVIEW.Application');
$vi = $obj->GetVIReference('C:\Users\Dell\Desktop\LabVIEW Web ActiveX\ActiveX Server Executable _LV2012_NI Verified\Executable as ActiveX Server\ActiveX Server.vi');

$data = $vi->GetControlValue('PNG data');

$im = imagecreatefromstring($data);
if ($im !== false) {
    header('Content-Type: image/png');
    imagepng($im);
    imagedestroy($im);
}
else {
    echo ' .';
}
$obj = null;
?>


Es ist besser, Skripte auszuführen, wenn die LabVIEW-Umgebung ausgeführt wird. In diesem Fall verwenden die Skripte eine bereits geöffnete Instanz von LabVIEW wieder. Anstatt bei jedem Skriptaufruf eine COM-Instanz zu erstellen und zu schließen. Mein Skript verwendet ein bisschen AJAX und "Neustart". Wenn LabVIEW nicht wiederverwendet wird, führt dies zu einem "Schildkrötenmarathon" mit aufeinanderfolgenden Starts und Endungen von labview.exe.

Videobewertung:


Blinddarm. Konfigurieren von NGINX als Reverse-Proxy mit HTTP Basic-Zugriffsauthentifizierung für die Verwendung mit WebServices LabVIEW


Vor einiger Zeit habe ich ein bisschen mit WebServices LabVIEW experimentiert (um die Wahrheit über eine ziemlich alte Version von LabVIEW zu sagen). Dann stellte ich fest, dass Seiten (WebServices-Ressourcen) keine einfache Zugriffskontrolle haben. Es wurde vorgeschlagen, Benutzer auf dem Anwendungsserver zu konfigurieren und das "tote" Microsoft Silverlight zu verwenden. Und ich brauchte eine einfache Option, wie die Überprüfung des Kennworts für die HTTP-Basiszugriffsauthentifizierung.

Ich habe NGINX verwendet und es als Reverse-Web-Proxy mit aktiviertem auth_basic konfiguriert. Mit den folgenden Einstellungen erhält der Benutzer beim Zugriff auf die Adresse http: // Servername: 5500 nach Eingabe des Kennworts Zugriff auf die WebService-Anwendung, die unter http://127.0.0.1:8001/webservice1/ ausgeführt wird.
Alle Ressourcen der Anwendung webservice1 sind geschützt.

nginx.conf:

server {
    listen       5500;
    server_name  localhost;
    location / {
        auth_basic "Unauthorized";
        auth_basic_user_file htpasswd;
        root html;
        #autoindex on;
        #index index.html index.htm;
        proxy_pass http://127.0.0.1:8001/webservice1/;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }
}

und die htpasswd-Datei mit Benutzerkennwörtern:

admin:{PLAIN}1

Wenn Sie diese Idee weiterentwickeln, können Sie den Zugriff auf den NGINX-Proxy über HTTPS aktivieren und HTTP von NGINX an LabVIEW übergeben.

All Articles