سهولة الوصول إلى الويب من تطبيقات LabVIEW VI PHP عبر خادم ActiveX

لسنوات عديدة ، كان LabVIEW قادرًا على "ربط" الويب بأجهزة VI دون أي إعدادات نشر وخادم معقدة من LabVIEW ، وذلك باستخدام خادم ActiveX مضمن فقط. طبعة مجتمع LabVIEW 2020 ليست استثناء.

بالنسبة إلى LabVIEW في الوقت الحالي ، هناك عدة طرق لنشر الأجهزة الافتراضية على الويب ، مما يتطلب مستويات مختلفة من المعرفة وتوفير قدرات مختلفة. في هذه المقالة ، لن أصفها ، لكنني أريد أن أقدم لك الاستخدام غير القياسي لخادم ActiveX / COM المدمج في LabVIEW لتنظيم الوصول إلى الويب إلى VI ، بالإضافة إلى إدارة بيئة LabVIEW نفسها. على الرغم من أن ActiveX / COM هي بالفعل تقنية قديمة تستمر في العمل على Windows ، فمن خلال خادم ActiveX المدمج يمكنك بسهولة إدارة أجهزة LabVIEW و VI ، بما في ذلك عبر الويب.

أول شيء يجب القيام به هو تضمين نفس خادم ActiveX في LabVIEW ، ويتم ذلك في إعدادات البيئة: Tools-> Options-> VI Server ، خانة الاختيار ActiveX.


يمكنك التحقق من تشغيل الخادم وإمكانية الوصول إليه باستخدام برنامج نصي بسيط في VBScript. تحتاج إلى إنشاء ملف نصي labview_test.vbs على سطح المكتب وملؤه بالمحتويات التالية:

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

قبل تشغيل البرنامج النصي ، قم بتشغيل بيئة LabVIEW. ومع ذلك ، ستعمل دون إطلاق البيئة لأول مرة. أثناء تنفيذ النص البرمجي ، سيتم تشغيل مثيل LabVIEW كخادم ActiveX / COM ، وعند اكتمال النص البرمجي ، سيتم إغلاق المثيل ، لذا سيتعين عليك الانتظار حتى يتم تحميل "عمليات التحميل والتفريغ". سيكون إخراج labview_test.vbs هو اسم التطبيق الجذر وإصداره.


بعد ذلك ، قمت بإنشاء جهاز VI بسيط "ActiveX Server.vi". يحتوي على العديد من الضوابط والوظائف المساعدة. هذا السادس سنقوم بتحميله وإدارته.


من LabVIEW لا نحتاج إلى أي شيء آخر. يمكنك الآن البدء بطبقات الويب.

مسار شائك


في البداية جربت قليلاً مع خادم ويب Windows القياسي Microsoft IIS. حاولت إنشاء صفحات ASP على VBScript بالمحتويات التالية:

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

طريقة GetVIReference () تحميل السادس في الذاكرة وإنشاء اتصال معها. المعلمة الرئيسية: المسار المطلق للسادس المحدد.

إخراج البرنامج النصي للمتصفح:

LabVIEW.exe ver: 20.0
123 

صحيح ، كان عليّ العبث قليلاً بإعدادات تجمع تطبيقات IIS والتحقق من المستخدم المجهول ، الذي قمت بتكوينه لمستخدم Windows الحالي.

قررت عدم التعمق في ASP وتحولت إلى PHP. بالنسبة لـ IIS ، هيأت برنامج PHP FastCGI. لا أقدم الإعدادات ؛ فهي ليست مهمة للجزء الرئيسي من هذه المقالة. تمكن PHP أيضًا من الوصول إلى كائن LabVIEW COM حسب النوع:

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

في كلتا الحالتين ، عند تشغيل LabVIEW وفتح السادس فيه (ActiveX Server.vi). عند طلب برنامج نصي PHP (ASP) ، تم تشغيل نسخة جديدة من LabVIEW.exe بالتوازي (ولفترة زمنية كبيرة) ، ثم باستخدام طريقة GetVIReference () ، تم تحميل مثيله الخاص من ActiveX Server.vi. عند الانتهاء من البرنامج النصي PHP ، تم إغلاق مثيل LabVIEW. أولئك. لم يكن هناك تقاطع مع مثيل قيد التشغيل بالفعل لبيئة LabVIEW. باستخدام الأداة المساعدة Process Explorer المعروفة ، يتم ملاحظة ذلك جيدًا. كما لم تعط "اللعبة" مع إعدادات تجمع تطبيقات IIS نتيجة خاصة. بالنسبة لي ، استنتجت أن IIS يعمل كبرنامج خفي للنظام نيابة عن النظام ، وبالتالي يتم إنشاء نسخة منفصلة من LabVIEW.exe مرتبطة بسياق النظام ، ولا يمكنني الحصول على إعادة استخدام مثيل مفتوح بالفعل كمستخدم Windows.

ثم ظهرت الفكرة لتجربة خادم ويب تابع لجهة خارجية يعمل في وضع التطبيق البسيط نيابة عن المستخدم الحالي. وقع الاختيار على NGINX ، علاوة على ذلك ، لقد استخدمته بالفعل كوكيل عكسي لـ LabVIEW WebServices.

Nginx


نأخذ الإصدار الحالي المتاح من nginx لنظام التشغيل Windows. nginx-1.17.10 حاليًا. لتوصيل PHP بـ NGINX ، استخدمت الوصف التالي .

إعداد NGINX البسيط البسيط. لدي ملف: c: \ nginx-1.17.10 \ conf \ nginx.conf
إضافة قائمة الدليل الجذر إلى المستعرض:

nginx.conf:

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

تمكين PHP عبر FastCGI في جذر الخادم:

# 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 لنظام التشغيل Windows . لقد استخدمت php-7.4.5-nts-Win32-vc15-x64.zip ، وسوف أكون موجودًا في c: \ php-7.4.5-nts-Win32-vc15-x64

إعادة تسمية وتكوين php.ini (وهو php. في التنمية من الأرشيف). قم بإجراء التغييرات التالية:

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

هنا يتم توصيل مكتبة GD للعمل مع الصور (إذا كنت بحاجة إلى الحصول على بعض الصور من LabVIEW) ووحدة php_com_dotnet.dll للعمل مع كائنات ActiveX / COM في PHP.

أثناء العمل مع COM في PHP ، تم اكتشاف خطأ غير سار عند العمل مع سلاسل (VT_BSTR) تحتوي على 0x0 حرفًا في الجسم. يتم حلها عن طريق استبدال php_com_dotnet.dll بـ recompiled مع الإصلاح. يمكن العثور على وصف الخطأ وتصحيحه مع الإصلاح هنا . لسوء الحظ ، لا يزال غير ثابت رسميًا في PHP. لقد قمت بإعادة إنشاء php_com_dotnet.dll (لـ php-7.4.5-nts-Win32-vc15-x64) ، يمكن العثور على php_com_dotnet.dll الثابت في الرابط . يمكن العثور على دليل لبناء PHP والإضافات لبناء الذات هنا .

بشكل افتراضي ، سيتم تشغيل NGINX على منفذ TCP 80 ، برنامج PHP FastCGI على المنفذ 9000 ، تحقق من عدم وجود تطبيقات عمل أخرى تستخدم هذه المنافذ.

يمكن تنظيم وبدء شياطين NGINX و PHP FastCGI بعدة طرق. تم تشكيل نصوص cmd هذه لاحتياجات تصحيح الأخطاء: start-reset-all.cmd الذي يبدأ / يعيد التشغيل في الخلفية (بدون فتح نوافذ شيطانية) ويوقف kill-all.cmd الذي أضعه في دليل NGINX. استخدام الأداة المساعدة Run Hidden Console المأخوذة من الوصف .

بدء إعادة التشغيل 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

أريد الانتباه إلى متغير البيئة PHP_FCGI_MAX_REQUESTS. بشكل افتراضي ، يكون 500. وبعد 500 طلب ، سيكمل برنامج PHP FastCGI عمله ، لذلك قمت بتعطيل هذا العداد لتصحيح الأخطاء. هنا اقتباس من وثائق للتفكير:

يمكن تعطيل هذا السلوك PHP عن طريق تعيين PHP_FCGI_MAX_REQUESTS إلى 0 ، ولكن يمكن أن يكون ذلك مشكلة إذا تسرب تطبيق PHP الموارد. بدلاً من ذلك ، يمكن تعيين PHP_FCGI_MAX_REQUESTS على قيمة أعلى بكثير من القيمة الافتراضية لتقليل تكرار هذه المشكلة.

لقد كتبت 2 نصوص اختبار PHP labview.php ، labview_png.php ، والتي يجب وضعها في جذر خادم الويب C: \ nginx-1.17.10 \ html
labview.php - هذا هو المثال الرئيسي للنص البرمجي
labview_png.php - تقوم بإرجاع صورة PNG من سلسلة من نوع VT_BSTR تمت قراءتها من خادم 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;
?>


من الأفضل تنفيذ البرامج النصية عند تشغيل بيئة LabVIEW ، وفي هذه الحالة ستعيد البرامج النصية استخدام مثيل مفتوح بالفعل من LabVIEW. بدلاً من إنشاء وإغلاق مثيل COM مع كل مكالمة البرنامج النصي. يستخدم النص البرمجي قليلًا من AJAX و "إعادة التشغيل" ، ولن يؤدي إعادة استخدام LabVIEW إلى "ماراثون السلحفاة" من بدء وإنهاء متسلسل labview.exe.

مراجعة الفيديو:


الملحق. تكوين NGINX كوكيل عكسي مع مصادقة وصول HTTP الأساسية للعمل مع WebServices LabVIEW


لقد جربت بعض الوقت مع WebServices LabVIEW (لأقول الحقيقة في إصدار قديم من LabVIEW). ثم اكتشفت أن الصفحات (موارد WebServices) ليس لها تحكم بسيط في الوصول. تم اقتراح تكوين المستخدمين في Application Server واستخدام Microsoft Silverlight "الميت". وكنت بحاجة إلى بعض الخيارات البسيطة ، مثل التحقق من كلمة مرور مصادقة الوصول إلى HTTP الأساسية.

لقد استخدمت NGINX وقمت بتكوينه كوكيل ويب عكسي مع تمكين auth_basic. باستخدام الإعدادات أدناه ، عند الوصول إلى العنوان http: // server_name: 5500 بعد إدخال كلمة المرور ، يحصل المستخدم على الوصول إلى تطبيق WebService الذي يعمل على http://127.0.0.1:8001/webservice1/.
جميع موارد تطبيق webservice1 محمية.

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

وملف htpasswd بكلمات مرور المستخدم:

admin:{PLAIN}1

لمزيد من تطوير هذه الفكرة ، يمكنك تمكين الوصول إلى وكيل NGINX عبر HTTPS ، وترك HTTP من NGINX إلى LabVIEW.

All Articles