Akses Web Mudah ke Aplikasi PHP LabVIEW VI melalui ActiveX Server

Selama bertahun-tahun, LabVIEW telah dapat "mengacaukan" perangkat Web ke VI tanpa pengaturan penerbitan dan server yang rumit dari LabVIEW, hanya menggunakan server ActiveX bawaan. LabVIEW 2020 Edisi komunitas tidak terkecuali.

Untuk LabVIEW saat ini ada beberapa cara untuk menerbitkan perangkat virtual di Web, yang membutuhkan berbagai tingkat pengetahuan dan menyediakan kemampuan yang berbeda. Dalam artikel ini saya tidak akan menjelaskannya, tetapi saya ingin memperkenalkan Anda kepada penggunaan ActiveX / COM server non-standar yang terintegrasi dalam LabVIEW untuk mengatur akses Web ke VI, serta untuk mengontrol lingkungan LabVIEW itu sendiri. Meskipun ActiveX / COM sudah merupakan teknologi lama yang terus hidup di Windows, melalui ActiveX server terintegrasi Anda dapat dengan mudah mengelola perangkat LabVIEW dan VI, termasuk melalui Web.

Hal pertama yang harus dilakukan adalah memasukkan server ActiveX yang sama ini di LabVIEW, ini dilakukan dalam pengaturan lingkungan: Tools-> Options-> VI Server, kotak centang ActiveX.


Anda dapat memverifikasi bahwa server dihidupkan dan memiliki akses ke sana, menggunakan skrip sederhana dalam VBScript. Anda perlu membuat file teks labview_test.vbs di desktop dan mengisinya dengan konten berikut:

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

Sebelum menjalankan skrip, jalankan lingkungan LabVIEW. Namun, ini akan berhasil tanpa terlebih dahulu meluncurkan lingkungan. Selama eksekusi skrip, instance LabVIEW akan diluncurkan sebagai server ActiveX / COM, dan setelah skrip selesai, instance akan ditutup, jadi Anda harus menunggu sampai semua "memuat dan membongkar" ini. Output dari labview_test.vbs akan menjadi nama aplikasi root dan versinya.


Selanjutnya, saya membuat perangkat VI sederhana "ActiveX Server.vi". Ini berisi beberapa kontrol dan fungsi tambahan. VI ini kami akan memuat dan mengelolanya.


Dari LabVIEW kami tidak membutuhkan yang lain. Sekarang Anda bisa mulai dengan lapisan Web.

Jalur berduri


Pada awalnya saya bereksperimen sedikit dengan server Web Windows standar Microsoft IIS. Saya mencoba membuat halaman ASP di VBScript dengan konten berikut:

<% @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>

Metode GetVIReference () memuat VI ke dalam memori dan membuat koneksi dengannya. Parameter utama: jalur absolut ke VI yang dipilih.

Output skrip ke browser:

LabVIEW.exe ver: 20.0
123 

Benar, saya harus sedikit mengotak-atik pengaturan kumpulan aplikasi IIS dan verifikasi pengguna anonim, yang saya konfigurasikan untuk pengguna Windows saat ini.

Saya memutuskan untuk tidak masuk ke ASP dan beralih ke PHP. Untuk IIS, saya mengkonfigurasi daemon PHP FastCGI. Saya tidak memberi Anda pengaturan, mereka tidak penting untuk bagian utama dari artikel ini. PHP juga berhasil mengakses objek LabVIEW COM berdasarkan tipe:

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

Dalam kedua kasus, ketika LabVIEW berjalan dan VI terbuka di dalamnya (ActiveX Server.vi). Saat meminta skrip PHP (ASP), instance baru LabVIEW.exe diluncurkan secara paralel (dan untuk waktu yang cukup lama), kemudian menggunakan metode GetVIReference (), ia memuat instance ActiveX Server.vi miliknya sendiri. Setelah menyelesaikan skrip PHP, instance LabVIEW ditutup. Itu tidak ada persimpangan dengan instance lingkungan LabVIEW yang sudah berjalan. Menggunakan utilitas Process Explorer yang terkenal, ini diamati dengan baik. "Game" dengan pengaturan kumpulan aplikasi IIS juga tidak memberikan hasil khusus. Bagi saya sendiri, saya menyimpulkan bahwa IIS berfungsi sebagai daemon sistem atas nama sistem, dan oleh karena itu contoh terpisah dari LabVIEW.exe dibuat yang terikat dengan konteks sistem, dan saya tidak bisa menggunakan kembali contoh yang sudah terbuka sebagai pengguna Windows.

Kemudian muncul ide untuk mencoba server web pihak ketiga yang berjalan dalam mode aplikasi sederhana atas nama pengguna saat ini. Pilihannya jatuh pada NGINX, apalagi, saya sudah menggunakannya sebagai proxy terbalik untuk LabVIEW WebServices.

Nginx


Kami mengambil versi nginx saat ini untuk Windows. Saat ini nginx-1.17.10. Untuk menghubungkan PHP dengan NGINX, saya menggunakan deskripsi berikut .

Pengaturan NGINX minimal yang sederhana. Saya punya file: c: \ nginx-1.17.10 \ conf \ nginx.conf
Menambahkan daftar direktori root ke browser:

nginx.conf:

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

Mengaktifkan PHP melalui FastCGI di root server:

# 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


Kami mengambil versi PHP untuk Windows saat ini . Saya menggunakan php-7.4.5-nts-Win32-vc15-x64.zip, saya akan meletakkannya di c: \ php-7.4.5-nts-Win32-vc15-x64

Ubah nama dan konfigurasikan php.ini (yang merupakan php. in-development dari arsip). Buat perubahan berikut:

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

Di sini pustaka GD untuk bekerja dengan gambar terhubung (jika Anda perlu mendapatkan beberapa gambar dari LabVIEW) dan modul php_com_dotnet.dll untuk bekerja dengan objek ActiveX / COM dalam PHP.

Saat bekerja dengan COM di PHP, bug yang tidak menyenangkan ditemukan ketika bekerja dengan string (VT_BSTR) yang berisi 0x0 karakter di dalam tubuh. Itu dipecahkan dengan mengganti php_com_dotnet.dll dengan dikompilasi ulang dengan perbaikan. Deskripsi bug dan tambalan dengan perbaikan dapat ditemukan di sini . Sayangnya, ini masih belum diperbaiki secara resmi di PHP. Saya membangun kembali php_com_dotnet.dll (untuk php-7.4.5-nts-Win32-vc15-x64), php_com_dotnet.dll tetap dapat ditemukan di tautan . Panduan untuk pengembangan PHP sendiri dan ekstensi dapat ditemukan di sini .

Secara default, NGINX akan berjalan pada port TCP 80, daemon PHP FastCGI pada port 9000, periksa apakah tidak ada aplikasi lain yang berfungsi menggunakan port ini.

Memulai dan menghentikan daemon NGINX dan PHP FastCGI dapat diatur dengan berbagai cara. Script cmd ini terbentuk untuk kebutuhan debugging saya: start-restart-all.cmd yang dimulai / restart di latar belakang (tanpa jendela setan terbuka) dan berhenti kill-all.cmd yang saya masukkan ke direktori NGINX. Utilitas Run Run Console Digunakan, diambil dari deskripsi .

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

Saya ingin memperhatikan variabel lingkungan PHP_FCGI_MAX_REQUESTS. Secara default, ini adalah 500. Dan setelah 500 permintaan, daemon PHP FastCGI akan menyelesaikan pekerjaannya, jadi saya menonaktifkan penghitung ini untuk debugging. Berikut ini adalah kutipan dari dokumentasi untuk refleksi:

Perilaku PHP ini dapat dinonaktifkan dengan mengatur PHP_FCGI_MAX_REQUESTS ke 0, tetapi itu bisa menjadi masalah jika aplikasi PHP membocorkan sumber daya. Atau, PHP_FCGI_MAX_REQUESTS dapat diatur ke nilai yang jauh lebih tinggi daripada standar untuk mengurangi frekuensi masalah ini.

Saya menulis 2 skrip pengujian PHP labview.php, labview_png.php, yang harus ditempatkan di root dari server web C: \ nginx-1.17.10 \ html
labview.php - ini adalah contoh script utama
labview_png.php - mengembalikan gambar PNG dari string tipe VT_BSTR yang dibaca dari server LabVIEW ActiveX.

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;
?>


Lebih baik untuk mengeksekusi skrip ketika lingkungan LabVIEW berjalan, dalam hal ini skrip akan menggunakan kembali contoh LabVIEW yang sudah terbuka. Alih-alih membuat dan menutup instance COM dengan setiap panggilan skrip. Skrip saya menggunakan sedikit AJAX dan "restart", dan tidak menggunakan kembali LabVIEW akan menghasilkan "tortoise marathon" mulai berurutan dan pengakhiran labview.exe.

Ulasan video:


Lampiran. Mengkonfigurasi NGINX sebagai proxy terbalik dengan otentikasi akses HTTP Dasar untuk bekerja dengan WebServices LabVIEW


Beberapa waktu lalu saya bereksperimen sedikit dengan WebServices LabVIEW (untuk mengatakan yang sebenarnya pada versi LabVIEW yang agak lama). Kemudian saya menemukan bahwa halaman (sumber daya WebServices) tidak memiliki kontrol akses yang sederhana. Diusulkan untuk mengonfigurasi pengguna di Server Aplikasi dan menggunakan Microsoft Silverlight "mati". Dan saya memerlukan beberapa opsi sederhana, seperti verifikasi kata sandi otentikasi akses HTTP Basic.

Saya menggunakan NGINX dan mengkonfigurasinya sebagai proxy web terbalik dengan auth_basic diaktifkan. Menggunakan pengaturan di bawah ini, ketika mengakses alamat http: // server_name: 5500 setelah memasukkan kata sandi, pengguna mendapatkan akses ke aplikasi WebService yang berjalan di http://127.0.0.1:8001/webservice1/.
Semua sumber daya dari aplikasi webservice1 dilindungi.

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;
    }
}

dan file htpasswd dengan kata sandi pengguna:

admin:{PLAIN}1

Lebih lanjut mengembangkan ide ini, Anda dapat mengaktifkan akses ke proxy NGINX melalui HTTPS, dan meninggalkan HTTP dari NGINX ke LabVIEW.

All Articles