一、需求分析
开发一个项目微信小程序享寄存,软硬件结合,软硬件通信以前没有做过,网上的资料也很少,踩了不少的坑,特此记录一下。首先硬件和服务器建立一个socket长链接,前端微信小程序给服务器,发出命令给服务器,开锁,服务器如何通知客户端呢?
二、解决方案
在硬件客户端和服务器建立链接时,同时开启一个内部端口,进行监听
三、代码实现
start.php代码
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2017/12/30
* Time: 23:30
*/
namespace app\push\controller;
use app\api\model\User;
use app\common\model\Box;
use app\common\model\Chest;
use GatewayWorker\Gateway;
use My\RedisPackage;
use think\Cache;
use think\Db;
use think\Log;
use think\worker\Server;
class Worker extends Server
{
/**
* 每个进程启动
* @param $worker
*/
public function onWorkerStart($worker)
{
// 开启一个内部端口,方便内部系统推送数据,Text协议格式 文本+换行符
$inner_text_worker = new \Workerman\Worker('text://0.0.0.0:5678');
$inner_text_worker->onMessage = function($connection, $buffer)
{
// $data数组格式,里面有uid,表示向那个uid的页面推送数据
$data = json_decode($buffer, true);
$uid = $data['uid'];
$openArr = config('lock.open');
$index = $data['index'];
// 通过workerman,向uid的页面推送数据
$ret = $this->sendMessageByUid($uid, self::hexToString($openArr[$index]));
// 返回推送结果
$connection->send($ret ? 'ok' : 'fail');
};
// ## 执行监听 ##
$inner_text_worker->listen();
}
/**
* 收到信息
* @param $connection
* @param $data
*/
public function onMessage($connection, $data)
{
$receiveData = explode(',',$data);
//判断是否是心跳包
if(!empty($receiveData) && $receiveData[0] == 'NBES') {
$send = explode('=',$receiveData[1]);
if(isset($connection->id) && !array_key_exists($connection->id,self::$worker->uidConnections)) {
self::$worker->uidConnections[$connection->id] = $connection;
}
//对应起来数据库里面的柜子
$chest = Chest::getChestByGsm($send[1]);
if($chest->server_id != $connection->id) {
Chest::update(array('server_id'=>$connection->id,'simstatus'=>config('sim.status_normal')),array('bdz'=>$send[1]));
}
$connection->send($send[1]);
}else {
if(!empty($data)) {
$backSuccess = config('lock.backSuccess');
$backError = config('lock.backError');
$receive = self::stringToHexArray($data);
$boxOpenStatus = config('box.open');
$boxTrouble = config('box.trouble');
$closeSuccess = config('lock.closeSuccess');
$closeError = config('lock.closeError');
if($receive[0] == '0x8a') {
//读锁返回状态
$this->openReturnStatus($receive,$backError,$backSuccess,$boxOpenStatus,$boxTrouble,$connection);
}else if($receive[0] == '0x82'){
//关锁状态反馈
$this->closeReturnStatus($receive,$closeSuccess,$closeError,$boxTrouble,$boxOpenStatus,$connection);
}
}
}
}
/**
* 当连接建立时触发的回调函数
* @param $connection
*/
public function onConnect($connection)
{
echo 'connect ok -'.$connection->id;
}
/**
* 当连接断开时触发的回调函数
* @param $connection
*/
public function onClose($connection)
{
if(isset($connection->uid))
{
// 连接断开时删除映射
unset(self::$worker->uidConnections[$connection->uid]);
}
}
/**
* @param $uid
* @param $message
* @return bool
* 发送信息到客户端
*/
public function sendMessageByUid($uid, $message)
{
if(isset(self::$worker->uidConnections[$uid]))
{
$connection = self::$worker->uidConnections[$uid];
$connection->send($message);
return true;
}
return false;
}
/**
* 当客户端的连接上发生错误时触发
* @param $connection
* @param $code
* @param $msg
*/
public function onError($connection, $code, $msg)
{
echo "error $code $msg\n";
}
/**
* 说明:字符串转十六进制
* 参数:字符串,例如:$string = "cfg_power";
* 返回:十六进制,返回0x63,0x66,0x67,0x5f,0x70,0x6f,0x77,0x65,0x72,0x62,0x79
*/
public static function stringToHexArray($string)
{
$arr1 = str_split($string, 1);
foreach($arr1 as $akey=>$aval){
$arr1[$akey]="0x".bin2hex($aval);
}
return($arr1);
}
/**
* 说明:十六进制转字符串
* 参数:十六进制,例如:$hex = array(0x63,0x66,0x67,0x5f,0x70,0x6f,0x77,0x65,0x72,0x62,0x79);
* 返回:字符串 ,例如: $myStr = "cfg_power"
*/
public static function hexToString($hex)
{
$myStr="";
for($i=0;isset($hex[$i]);$i++)
{
$myStr.= chr($hex[$i]);
}
return($myStr);
}
/**
* @param $receive
* @param $closeSuccess
* @param $boxOpenStatus
* @param $connection
* 手动关闭箱子返回状态处理
*/
public function closeReturnStatus($receive,$closeSuccess,$closeError,$boxTrouble,$boxOpenStatus,$connection) {
if(in_array($receive,$closeSuccess)) {
$keys = array_keys($closeSuccess,$receive);
$key = $keys[0]+1;
$open = $boxOpenStatus['status_normal'];
$chest = Chest::get(array('server_id'=>$connection->id));
if(!empty($chest)) {
Box::update(array('open'=>$open),array('xsxh'=>$key,'cid'=>$chest->id));
}
}elseif (in_array($receive,$closeError)) {
$keys = array_keys($closeError,$receive);
$key = $keys[0]+1;
$status = $boxTrouble['status_padding'];
$chest = Chest::get(array('server_id'=>$connection->id));
if(!empty($chest)) {
Box::update(array('status'=>$status),array('xsxh'=>$key,'cid'=>$chest->id));
}
}
}
/**
* @param $receive
* @param $backError
* @param $backSuccess
* @param $boxOpenStatus
* @param $boxTrouble
* @param $connection
* 处理开门命令反馈的数据
*/
private function openReturnStatus($receive,$backError,$backSuccess,$boxOpenStatus,$boxTrouble,$connection) {
//开门返回状态
$open = 1;
$trouble = 0;
$key = 1;
if(in_array($receive,$backSuccess)) {
$keys = array_keys($backSuccess,$receive);
$key = $keys[0]+1;
$open = $boxOpenStatus['status_padding'];
$trouble = $boxTrouble['status_normal'];
}else if(in_array($receive,$backError)) {
$keys = array_keys($backError,$receive);
$key = $keys[0]+1;
$open = $boxOpenStatus['status_normal'];
$trouble = $boxTrouble['status_delete'];
}
$chest = Chest::get(array('server_id'=>$connection->id));
if(!empty($chest)) {
Box::update(array('open'=>$open,'status'=>$trouble),array('xsxh'=>$key,'cid'=>$chest->id));
}
}
}
Socket代码,发送指令到内部端口
/**
* @param $id 柜子的ID
* @param $index 箱子的ID 显示箱号
* @return bool|string
*
*/
public static function openLock($id,$index) {
// 建立socket连接到内部推送端口
$client = stream_socket_client('tcp://127.0.0.1:5678', $errno, $errmsg, 1);
// 推送的数据,包含uid字段,表示是给这个uid推送
$data = array('uid' => $id, 'index' => $index);
// 发送数据,注意5678端口是Text协议的端口,Text协议需要在数据末尾加上换行符
fwrite($client, json_encode($data) . "\n");
// 读取推送结果
return fread($client, 8192);
}
四、流程总结
首先硬件客户端和服务器建立一个socket链接,建立时开启一个内部端口进行监听,软件客户端(微信小程序或者app)发送http请求给服务器,服务器接受到请求,通过socket发送数据到内部端口,内部端口接受到数据发送指令给硬件客户端。
点击查看更多内容
9人点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦