牛骨文教育服务平台(让学习变的简单)

OHSCE入门教程(一)-高可靠性PHP通信&amp

"PHP是最好的(互联网+物联网)语言!",据说发布PHP开源产品都要惯例的喊上这么一句。 -2016.10.17 发此文之际是OHSCEV0.1.22版本

    开启第一篇教程前总要卖点情怀,本人本科学习建筑电气与智能化专业(即电气工程及其自动化专业),一直是名正经的电气工程师。其实本来和这个号称是“Web语言”的PHP八竿子打不着,可是随着国家所谓“互联网+“的引导,也翻山越岭的将WEB领域逐渐和各行各业拉到了一起。从很多行业的角度看“互联网+“很大程度就是“WEB+”,于是PHP这门最好的语言便进入了我的技术栈,我发现PHP是最具潜力的工控、物联网&智能化语言。

    打开PHP的官网,我们可以看见两行白色的欢迎语:“PHP is a popular general-purpose scripting language that is especially suited to web development.Fast, flexible and pragmatic, PHP powers everything from your blog to the most popular websites in the world.”  -意译:PHP是一各流行且全能的脚本语言,特别合适于与互联网相关的领域。它快速、灵活且实用,健壮的PHP能驱动小到你的博客大到著名的FACEBOOK、百度等每个事物。

   工控工业4.0、物联网、行业智能化将是PHP的下一个最具潜力的领域。本着不造轮子多改进的原则我曾用款现有的PHP通信框架(不含串口通信功能)做过一次控制系统的实现,结果那几天搅合的我一团糟,很多奇怪的问题和很高的失误率,其实人家的框架是非常优秀的WEB框架可能是控制领域的技术栈和传统的WEB领域有着很大的区别,传统的web工程师更多的工作内容和一个强大的浏览器或APP交互而控制工程师则不是,于是我决定从底层重写一套代码(同时也加上了串口通信和共享内存辅助托管功能)。这套代码专为控制环境量身定制,是专业针对工控工业4.0、物联网、行业智能化场景(当然兼备了做WEB的能力,毕竟“”互联网+“”嘛)。其实母程序已几经实布和商布,我们将其核心部分剥离出来继而做了开源化改动并合并了OpenIAC计划成为了为控制场景量身定制的高可靠性PHP通信&控制框架-OHSCE。它简单高效并且特别亲切于工业自动化工程师、硬件工程师、物联网工程师、追求效率的PHP工程师的写法风格,也能让传统的PHP-WEB工程师轻松上手。

     言归正传我们以搭建一个简单的串口服务器作为第一教程的内容,一个简单的串口服务器包含了上行网络(以太网)和现场总线(这里是RS485通信)。

     以太网做上行网络是大势所趋,RS485是目前使用最普遍广泛也是最具性价比的现场通信手段,当然很多其它通信手段都可以和RS232/485转接,故我们以它做例。

      #用UDP还是TCP?

      UDP是一个非常好的选择,它无链接、高效并且节约资源,但是缺点是会有丢包的可能性,不过您可以通过主从应答来保证您的数据的可靠性,而且您可以灵活的掌握是否应答,甚至这各特性都可以逻辑中的一部分。不过对于串口服务器这个场景来说TCP是更合适的选择,因为做一个串口的需求很简单,可靠连接读写数据并转发到串口上,串口写读数据并回转回来。我们确定使用TCP

      示例所使用的机器的COM7是将使用的串口。

      #欲善其事必利其器

      下载最新版本的OHSCE:

      您可以到OHSCE的官网(http://www.ohsce.org)或GITHUB(https://github.com/OpenIBC/Ohsce)下载到最新的OHSCE。PS.记得支持我一下哦

      安装PHP5.4+ 并配置PHP.INI至可用状态。

      配置OHSCE的配置文件 位于.../config/

      开启Curl扩展、Shmop扩展、Sockets扩展

      创建OhsceComserver.php文件

      加载OHSCE

<?php
ini_set("memory_limit","64M"); //重置php可以使用的内存大小为64M
set_time_limit(0); //重置运行时长为无限制
ob_implicit_flush(1);
include("loadohsce.php"); //载入Ohsce加载文件

      #开始构造串口服务器的身躯

      首先我们打开我们需要转发的串口

<?php
//......
$comid="COM7"; //windows下是comx linux下是/dev/ttyX
Ohsce_eng_serial_creat($hscecom,$comid); //创建一个COM7 9600,n,8,1的待调用串口资源
Ohsce_eng_serial_open($hscecom); //调用资源并占用该串口

       PS:Ohsce_eng_serial_creat函数默认为您填充了您所省略的所有相关参数,当然您可以手动指定他们以创建各种串口资源。

Ohsce_eng_serial_creat(&$OHSCESerial,$com,$flags="1",$mode=0,$baud=9600,$parity="n",$data=8,$stop=1,$fc="none",$xon="off",$to="off",$octs="off",$odsr="off",$idsr="off",$dtr="on",$rts="on")

      详见:Ohsce_eng_serial_creat

     在创建TCP服务器之前我们需要先构造它的回调函数。    所谓回调函数就是当有新的TCP客户端到达、客户端发送新的消息到达是会调用的函数。

<?php
//...................
function comserveraccept(&$socket,$ip,$port,$zv){

global $hscecom;                                         //调用$hscecom串口资源
$ohsce_cs_data=Ohsce_socketread($socket,1024);           //读取1024字节数据
if(($ohsce_cs_data!=null)or($ohsce_cs_data[0]!=false)){
Ohsce_eng_serial_write($hscecom,$ohsce_cs_data[1],false);//写入串口
Ohsce_eng_serial_read($hscecom,$data,null,true);         //读取返回数据
Ohsce_socketwrite($socket,$data);                        //回转所读取的数据
}
return true;

}
function comservera(&$socket,$buf,$len,$zv){

global $hscecom;
Ohsce_eng_serial_write($hscecom,$buf,false);
Ohsce_eng_serial_read($hscecom,$data,null,true);
Ohsce_socketwrite($socket,$data);
return true;

}
function comserveralways(&$oibc_clients_zv){

global $hscecom;
Ohsce_eng_serial_read($hscecom,$data,null,true);
if((!is_null($data))and(strlen($data)>0)){
	foreach($oibc_clients_zv["clients"] as $okey => $osclient){
		if($okey=="0"){
			continue;
		}
		Ohsce_socketwrite($osclient,$data);
	}
}
return true;

}

      其中comserveralways函数是每圈循环都会执行一次的函数,也就是常驻函数。在我们的串口服务器程序中,他会将最新收到的串口数据转发广播出去。

      comservera(&$socket,$buf,$len,$zv)和comserveraccept(&$socket,$ip,$port,$zv)函数会被固定传入这些变量。其中$socket是本次活动的socket资源的指针。$buf是收到的数据。$len是数据长度。$ip是新到客户端的ip地址。$port是其端口。$zv是固定结构体,其结构如下:$oibc_clients_zv=array("clients"=>&$oibc_clients,"ip"=>&$oibc_clients_id_ip,"id"=>&$oibc_clients_id)

    其中$oibc_clients数组为当前所有socket资源,key值为0者为服务监听者。$ip数组为ip对照表。$id数组为id备份表。当然如果有需要您也可以print_r之一探究竟。

   小提示:print_r函数是您的好帮手,很多时候文档很难面面俱到QQ群我也很难随时关注,这时候print_r可能比度娘还亲切。

    #赋予它生命的力量

    好了,回调函数和常驻函数都造好了,下面我们改让它拥有跑起来的能力了。首先先创建一个可复用的TCP服务端资源,第二步传入让它跑起来,就那么简单。

<?php
Ohsce_eng_socket_server($ohsceserver,"tcp","7626","127.0.0.1",array("callback"=>"comservera","accept"=>"comserveraccept","fap"=>"comserveralways"),"comserveraccept");

                                          //创建一个可以复用的SOCKETSERVER资源$ohsceserver,协议为TCP,监听端口为7626,绑定IP127.0.0.1,回调函数为comservera,首次回调函数为comserveraccept,常驻函数为comserveralways.最后一个comserveraccept是为了兼容OHSCEV0.1.22以前的版本。

Ohsce_eng_socket_server_runtcp($ohsceserver); //运行它

   当然更详细的可以参考手册文档: Ohsce_eng_socket_server  Ohsce_eng_socket_server_runtcp

   PS:一个彩蛋,当你的实际生产中最好不要使用7626端口,因为它太著名了,曾经我们漫游在整个互联网上寻找开了7626端口的小伙伴。简单的来说,容易招黑,而且招来的都是老黑:)

   #一个个人认为的好习惯

  无论您的程序多么的完美,最后记得阻截并跳转去抛出错误。这样当您的程序扩展和改动其它部分时至少能保证某一部分运行的不错。

<?php
//..程序头......
$errmsg="Unknow";
//.............
//..程序身躯....
//.............
goto terror; //前往并抛出错误
//.............
terror: //抛出错误的锚点
exit($errmsg);

   关于goto,可以理解为JMP指令,熟悉的身影但是用法略有不同。在PHP中GOTO是不可以从一个函数跳到另一个函数的,同时也是不可以从一个文件跳到另一个文件的。其实,这个限制带来了您程序可读性的提升和性能&一致性的平衡。

   PS.其实在很多高级语言中别说凶残的SETJMP了,连GOTO/JMP都被取缔了。原因是在号几十年前以为叫“Edsger Wybe Dijkstra”的先生提出了GOTO有害论,于是被很多人奉为圣旨而流传下来甚至在不少语言中干脆就被砍掉了(如JAVA)。这的确,大大的降低了程序设计对人员要求的门槛,但随着发展,尤其我们进入了泛(互)物(联)联(网)网(+)时代,GOTO的亲切与作用显得愈发的重要。不过进入21世纪不久 Dijkstra先生就去世了,英明的PHP设计团队在2009年将GOTO关键字重新引入了这门“世界上最好的语言”,所以从5.3开始我们又可以使用高效、简洁、具备自底向上一致性的GOTO关键字了。

   #牛刀小试

  至此,一个简单的串口服务器原型工程完成了。其实它不仅仅是一个教程案例,更是一个非常实用的功能,我将它包装了一下,这个原型便以Alpha的身份加入了V0.1.22_BETA开源版的Engine中。在后续的版本中它将跟随着OHSCE的版本更新变得越来越健壮。

<?php
//.......................................
comserver:
$oibc_cnp_csa=getopt("r:m:p:c:");
Ohsce_eng_serial_creat($hscecom,trim($oibc_cnp_csa["c"]));
Ohsce_eng_serial_open($hscecom);
function comserveraccept(&$socket,$ip,$port,$zv){

global $hscecom;
$ohsce_cs_data=Ohsce_socketread($socket,1024);
if(($ohsce_cs_data!=null)or($ohsce_cs_data[0]!=false)){
Ohsce_eng_serial_write($hscecom,$ohsce_cs_data[1],false);
Ohsce_eng_serial_read($hscecom,$data,null,true);
Ohsce_socketwrite($socket,$data);
}
return true;

}
function comservera(&$socket,$buf,$len,$zv){

global $hscecom;
Ohsce_eng_serial_write($hscecom,$buf,false);
Ohsce_eng_serial_read($hscecom,$data,null,true);
Ohsce_socketwrite($socket,$data);
return true;

}
function comserveralways(&$oibc_clients_zv){

global $hscecom;
Ohsce_eng_serial_read($hscecom,$data,null,true);
if((!is_null($data))and(strlen($data)>0)){
	foreach($oibc_clients_zv["clients"] as $okey => $osclient){
		if($okey=="0"){
			continue;
		}
		Ohsce_socketwrite($osclient,$data);
	}
}
return true;

}
Ohsce_eng_socket_server($ohsceserver,"tcp",intval(trim($oibc_cnp_csa["p"])),OHSCE_MYIP_SYSTEM,array("callback"=>"comservera","accept"=>"comserveraccept","fap"=>"comserveralways"),"comserveraccept");
Ohsce_eng_socket_server_runtcp($ohsceserver); //开始运行
goto terror;
//.......................................

       好了下面我们启动它并使用TCP连接串口服务器在COM7串口上使用MODBUS-RTU协议读取一台压力变送器的数据。

     tcpComClient.php:

<?php
ini_set("memory_limit","88M");//重置php可以使用的内存大小为64M
set_time_limit(0);
ob_implicit_flush(1);
error_reporting(0);
include("loadohsce.php");
Ohsce_eng_socket_client($ohsceclient,"tcp",7628,"127.0.0.1"); //创建一个TCP客户端资源并连接27.0.0.1:7626
Ohsce_socketsend($ohsceclient["socket"],"x01x03x00x01x00x04x15xc9"); //发送数据
//Ohsce_socketsend($ohsceclient["socket"],array("in"=>"01030001000415c9","bin"=>true));
echo Ohsce_socketread($ohsceclient["socket"],1024)[1]; //收取回复数据
sleep(30);

     运行效果:

     PHP是一个健壮的全能脚本语言,特别适合于网络有关的场景。工业控制、物联网、行业智能化方向,OHSCE是您强大的战舰。

      OHSCE官方网站: HTTP://WWW.OHSCE.ORG

      技术&交流:Q群-374756165 (随风星海@作者)

     手册地址:http://www.ohsce.com/index.php/book/ohscelib/

      GITHUB:https://github.com/OpenIBC/Ohsce

      GIT@OSC:https://git.oschina.net/SFXH/Ohsce

      捐助&支持:http://www.ohsce.com/index.php/company/