php workerman对接部标808协议

php学习交流吧

共 18018字,需浏览 37分钟

 · 2021-08-12

公司业务需求,这几天一直对着机器对接,整理出以下算法,实现对设备的交互。


服务端:

<?phpnamespace app\server\controller;use Workerman\Worker;use Workerman\Lib\Timer;use Workerman\Connection\AsyncTcpConnection;use app\server\controller\JT808;use think\Db;$socket = new Worker('websocket://0.0.0.0:2346');// 设置transport开启ssl,websocket+ssl即wss//$socket->transport = 'ssl';// 启动1个进程对外提供服务$socket->count = 1;global $tcp;//下面换成自己的端口   $tcp = new Worker('tcp://0.0.0.0:1234');
$tcp->onMessage = function($connection, $data)use($tcp) { $JT808 = new JT808(); //16进制数据 $data16Arrays = $JT808->getTo16Bytes($data); //var_dump($data16Arrays); foreach ($data16Arrays as $key => $data16Array) { //获取消息id $MessageId = $JT808->getMessageIdNumber($data16Array); //设备号 $equipmentNumber = $JT808->getEquipmentNumber($data16Array); $equipmentNumber = '86339'.substr($equipmentNumber, 2); //位置信息上报获取 if ($MessageId == '0200' && $equipmentNumber) { //报警信息 $AlarmMessage = $JT808->getAlarmMessage($data16Array, 13); //状态 $status = $JT808->getPositionStatus($data16Array, 17); //经度 $Latitude = $JT808->getLatitude($data16Array, 21, 'f'); //纬度 $Longitude = $JT808->getLongitude($data16Array, 25, 'f'); //高度 $Height = $JT808->getHeight($data16Array, 29); //速度 $Speed = $JT808->getSpeed($data16Array, 31); //方向 $Direction = $JT808->getDirection($data16Array, 33); //时间 $Datetime = $JT808->getDatetime($data16Array, 35); if ($Latitude && $Longitude) { //执行你的逻辑 var_dump($Latitude); var_dump($Longitude); } } //发送给客户端 $sendClientData = $JT808->getVerifyNumberArray($data16Array); $connection->send($sendClientData); } };

// 运行workerWorker::runAll();


JT808.php

数据解析算法

<?phpnamespace app\server\controller;class JT808 {  public static $sequenceNumber = 0; //流水号初始值  /**   * description  转成10进制字符串数组   * @param $string 16进制字符串   * @return array   10进制数组   */  public function get10Bytes($string) {    $bytes = array();    $len = strlen($string);    for ($i = 0; $i < $len; $i++) {      array_push($bytes, ord($string[$i]));    }    return $bytes;  }
/** * description 10进制字符串数组转成16进制字符串数组 * @param $data 10进制字符串数组 * @return mixed 16进制字符串数组 */ public function getTo16Bytes($data) { //$get10Bytes = $this->get10Bytes($data); $content = bin2hex($data); $res = explode('7e7e', $content); $array = []; //解决粘包 if (count($res) > 1) { foreach ($res as $k => $v) { if ($k == reset($res)) { $array[$k] = str_split($v . '7e', 2); } else if ($k == end($res)) { $array[$k] = str_split('7e' . $v, 2); } else { $array[$k] = str_split('7e' . $v . '7e', 2); } } } else { $array[] = str_split($res[0], 2); } return $array;
/*$array = []; foreach ($get10Bytes as $k => $v) { $array[$k] = base_convert($v, 10, 16); } */ //return $array; }
/** * description 接受到的16进制字符补0 例如:01=>0x01 * @param $data 16进制数组 * @return array 补0之后的16进制数组 */ public function supplementZero($data) { $len = count($data); $res = []; for ($j = 0; $j < $len; $j++) { if (strlen($data[$j]) == 1) { $res[$j] = "0x" . "0" . $data[$j]; } else { $res[$j] = "0x" . $data[$j]; } } return $res; }
/** * description 把一个4位的数组转化位整形 * @param array 接受数组 * @return int 返回int */ public function bytesToInt($data) { $res = []; foreach ($data as $k => $v) { $res[$k] = intval(base_convert($v, 16, 10)); } $temp0 = $res[0] & 0xFF; $temp1 = $res[1] & 0xFF; $temp2 = $res[2] & 0xFF; $temp3 = $res[3] & 0xFF; return (($temp0 << 24) + ($temp1 << 16) + ($temp2 << 8) + $temp3); }
/** * description BCD码转字符串 * @param array 数组 * @return bool|string 返回字符串 */ public function bcdToString($data) { $len = count($data); $temp = ""; for ($i = 0; $i < $len; $i++) { // 高四位 $temp .= (($data[$i] & 0xf0) >> 4); // 低四位 $temp .= ($data[$i] & 0x0f); } return (substr($temp, 0, 1) == 0) ? substr($temp, 1) : $temp; }
/** * description 从接受到的16进制数组中获取设备号数组 * @param $data 接受到的16进制数组 * @return string 设备号id */ public function getSensorId($data) { $sensorArray = array_slice($data, 3, 6); $sensorArrayZero = $this->supplementZero($sensorArray); $res = []; foreach ($sensorArrayZero as $k => $v) { $res[$k] = intval(base_convert($v, 16, 10)); } $string = $this->bcdToString($res); return $string; }
/** * description 把一个二字节数组转化成整型 * @param $data 二字节数组 * @return int 整型 */ public function twoBytesToInteger($data) { $res = []; foreach ($data as $k => $v) { $res[$k] = intval(base_convert($v, 16, 10)); } $temp0 = $res[0] & 0xFF; $temp1 = $res[1] & 0xFF; return (($temp0 << 8) + $temp1); }
/** * description 接受内容中4字节数组转成int * @param $data 16进制字节数组 * @param int $a 开始位 * @return int int值 */ public function getNum($data, $a = 0) { $numArray = array_slice($data, $a, 4); $res = $this->bytesToInt($numArray); return $res; }
/** * description 按位异或 * @param $data * @return int */ public function getEveryXor($data) { $len = count($data); $rew = 0; for ($i = 1; $i < $len; $i++) { $rew = $rew ^ $data[$i]; } return $rew; } /** * [checkCode 生成验证码带开头7e] * @author litaotxws@163.com * @DateTime 2020-05-01T00:19:06+0800 * @param [array] $data [数组] * @return [array] [返回2位数数组] */ public function checkCode($data) { $sum = 0; //去掉开头的7e unset($data[0]); //for ($i = 0; $i < count($arr); $i++) { foreach ($data as $k => $v) { $sum = $sum ^ hexdec($v); } return str_pad(dechex($sum), 2, "0", STR_PAD_LEFT); } /** * description 将字节数组转为字符串 * @param array 字节数组 * @return string 返回字符串 */ public function bytesArrayToString($data) { $str = ''; foreach ($data as $ch) { $str .= chr($ch); } return $str; } /** * [arrayToBytes 返回二进制内容] * @author litaotxws@163.com * @DateTime 2020-05-01T00:34:33+0800 * @param [type] $data [数组] * @return [type] [二进制内容] */ public function arrayToBytes($data) { $ret = implode($data); return hex2bin($ret); } /** * description 拼接字符串 * @param $str * @param int $n * @param string $char * @return string */ public function getTurnStr($str, $n = 1, $char = '0') { for ($i = 0; $i < $n; $i++) { $str = $char . $str; } return $str;
//return str_pad($str, $n, $char, STR_PAD_LEFT); }
/** * description 转成二进制字符串 * @param $data array 16进制数组 * @return string 字符串 */ public function getTwoStr($data) { //转成2进制 $str = array(); $req = array(); foreach ($data as $key => $value) { $str[$key] = base_convert($data[$key], 16, 2); $leng = 8 - strlen($str[$key]); $req[] = $this->getTurnStr($str[$key], $leng, "0"); } //拼接字符串 $rtq = implode("", $req); return $rtq; } /** * description 获取设备号 * @param $data array 16进制数组 * @param $length num 补全长度 * @return bool|string 返回字符串设备号 */ public function getEquipmentNumber($data, $length = 12) { $equipmentArray = array_slice($data, 5, 6); $res = []; foreach ($equipmentArray as $k => $v) { $res[$k] = base_convert($v, 16, 10); } $equipmentNumber = $this->bcdToString($res); return str_pad($equipmentNumber, $length, "0", STR_PAD_LEFT); } /** * description 获取16进制数组来计算出设备号 * @param $data 16进制数组 * @return array 返回设备号数组 */ public function getEquipmentNumberArray($data) { $num_array = array_slice($data, 5, 6); /*$res = []; foreach ($num_array as $k => $v) { $res[$k] = base_convert($v, 16, 10); } */ return $num_array; } /** * description 获取报警信息 * @param $data array 16进制数组 * @param $index 数组索引13 * @return int 返回数字代表报警信息 */ public function getAlarmMessage($data, $index, $type = false) { $alarmArray = $this->getTwoStr(array_slice($data, $index, 4)); if ($type == true) { if (substr($alarmArray, -8, 1) == 1) { //主电源断电 $alarm = "主电源断电"; } elseif (substr($alarmArray, -30, 1) == 1) { //碰撞预警 $alarm = "碰撞预警"; } elseif (substr($alarmArray, -31, 1) == 1) { //侧翻预警 $alarm = "侧翻预警"; }// elseif (substr($alarmArray, -26, 1) == 1) { // //脱落(光感)报警 // $alarm = 4; // } else { //正常 $alarm = "一切正常"; } } // return $alarm; return $alarmArray; }
/** * description 获取状态信息 * @param $data array 16进制数组 * @param $index 数组索引 * @param $type 返回数据还是原本信息 * @return array 状态信息数组 */ public function getPositionStatus($data, $index, $type = false) { $positionArray = $this->getTwoStr(array_slice($data, $index, 4)); if ($type == true) { //判断是否定位,0定位,1未定位 $isPosition = substr($positionArray, -2, 1) == 0 ? $isPosition = "未定位" : $isPosition = "定位"; //判断南北纬,0北纬,1南纬 $isNorSou = substr($positionArray, -3, 1) == 0 ? $isNorSou = "北纬" : $isNorSou = "南纬"; //判断东西经,0东经,1西经 $isEasWes = substr($positionArray, -4, 1) == 0 ? $isEasWes = "东经" : $isEasWes = "西经"; //判断定位方式 if (substr($positionArray, -19, 1) == 1 && substr($positionArray, -20, 1) == 0) { //北斗定位 $positionMethod = "北斗定位"; } elseif (substr($positionArray, -19, 1) == 0 && substr($positionArray, -20, 1) == 1) { //GPS定位 $positionMethod = "GPS定位"; } elseif (substr($positionArray, -19, 1) == 1 && substr($positionArray, -20, 1) == 1) { //北斗GPS双定位 $positionMethod = "北斗GPS双定位"; } else { //北斗GPS都未定位 $positionMethod = "北斗GPS都未定位"; } $positionStatusArray = array( 'position' => $isPosition, 'ns' => $isNorSou, 'ew' => $isEasWes, 'gps' => $positionMethod, ); return $positionStatusArray; } else { return $positionArray; } }
/** * description 获取纬度 * @param $data array 16进制数组 * @param $index 数组索引 * @param $type 返回字符类型 i整形 f浮点形 * @return float|int 纬度 */ public function getLatitude($data, $index, $type = 'f') { $latitudeBytes = array_slice($data, $index, 4); $latitude = $this->bytesToInt($latitudeBytes); if ($type == 'f') { $number = $latitude / pow(10, 6); } if ($type == 'i') { $number = $latitude; } return $number; }
/** * description 获取经度 * @param $data array 16进制数组 * @param $index 数组索引 * @param $type 返回字符类型 i整形 f浮点形 * @return float|int 经度 */ public function getLongitude($data, $index, $type = 'f') { $longitudeBytes = array_slice($data, $index, 4); $longitude = $this->bytesToInt($longitudeBytes); if ($type == 'f') { $number = $longitude / pow(10, 6); } if ($type == 'i') { $number = $longitude; } return $number; } /** * [getHeight 获取高度] * @author litaotxws@163.com * @DateTime 2020-04-30T14:12:30+0800 * @param [type] $data [16进制数组] * @param [type] $index [数组索引29] * @return [type] [高度] */ public function getHeight($data, $index) { $heightBytes = array_slice($data, $index, 2); $height = $this->twoBytesToInteger($heightBytes); return $height; } /** * [getSpeed 获取速度] * @author litaotxws@163.com * @DateTime 2020-04-30T14:14:55+0800 * @param [type] $data [16进制数组] * @param [type] $index [数组索引31] * @return [type] [速度] */ public function getSpeed($data, $index) { $speedBytes = array_slice($data, $index, 2); $speed = $this->twoBytesToInteger($speedBytes); return $speed; } /** * [getDirection 获取方向] * @author litaotxws@163.com * @DateTime 2020-04-30T14:15:33+0800 * @param [type] $data [16进制数组] * @param [type] $index [数组索引33] * @return [type] [方向] */ public function getDirection($data, $index) { $directionBytes = array_slice($data, $index, 2); $direction = $this->twoBytesToInteger($directionBytes); return $direction; } /** * description 获取日期时间 * @param $data array 16进制数组 * @param $index 数组索引35 * @return string 日期时间字符串 */ public function getDatetime($data, $index) { $datetimeArray = array_slice($data, $index, 6); $res = []; foreach ($datetimeArray as $k => $v) { $res[$k] = base_convert($v, 16, 10); } $datetime = $this->bcdToString($res); $datetimeStr = "20" . substr($datetime, 0, 2) . "-" . substr($datetime, 2, 2) . "-" . substr($datetime, 4, 2) . " " . substr($datetime, 6, 2) . ":" . substr($datetime, 8, 2) . ":" . substr($datetime, 10, 2); return $datetimeStr; }
/** * description 获取平台流水号 * @return array 返回流水号数组 */ public function getSequenceNumberArray() { //计算流水号 $number = $this->$sequenceNumber++; if ($number > 65025) { // 255 * 255 -1 $number = 1; } //将十进制流水号换算成16进制流水号 $get16Number = base_convert($number, 10, 16); $af = substr($get16Number, 0, 2); $bf = substr($get16Number, 2); $systemNumber = []; //判断 if ($number > 0xff) { $systemNumber = array('0x' . $af, '0x' . $bf); } else { $systemNumber = array('0x00', '0x' . $get16Number); } foreach ($systemNumber as $k => $v) { $systemNumber[$k] = intval(base_convert($v, 16, 10)); } return $systemNumber; }
/** * description 获取消息流水号 * @param $data 16进制数组 * @return array 消息流水号数组 */ public function getMessageNumberArray($data) { $messageNumber = array_slice($data, 11, 2); //$messageNumber = $this->supplementZero($messageNumber); return $messageNumber; }
/** * description 获取消息id * @param $data 16进制数组 * @return array 消息id数组 */ public function getMessageIdArray($data) { $messageId = array_slice($data, 1, 2); //$messageId = $this->supplementZero($messageId); return $messageId; } /** * description 获取消息id * @param $data array 16进制数组 * @param $length num 消息体长度 前位补0 * @return bool|string 消息id字符串 */ public function getMessageIdNumber($data, $length = 4) { $messageArray = array_slice($data, 1, 2); $res = []; foreach ($messageArray as $k => $v) { $res[$k] = base_convert($v, 16, 10); } $messageNumber = $this->bcdToString($res); return str_pad($messageNumber, $length, "0", STR_PAD_LEFT); } /** * description 获取消息体 * @param $data 16进制数组 * @return array 消息体数组 */ public function getMessageBodyArray($data) { //消息体 = 消息流水号 + 消息id $messageNumber = $this->getMessageNumberArray($data); //$messageId = $this->getMessageIdArray($data); //$messageBody = array_merge($messageNumber, $messageId); /*foreach ($messageBody as $k => $v) { $res[$k] = intval(base_convert($v, 16, 10)); } */ return $messageNumber; }
/** * description 发送给客户端的回传数据 * @param $data 16进制数组 * @return string 返回客户端字符串 */ public function getVerifyNumberArray($data, $auth = '31313131') { //数组开始五位 //$arrayStartFiveBytes = array('7E', '80', '01'); //注册应答结果0 $ret = array('00'); //消息ID $messageId = $this->getMessageIdArray($data); $messageid = implode($messageId); //消息体 $messageBody = $this->getMessageBodyArray($data); if ($messageid == '0100') { $arrayStartFiveBytes = array('7E', '81', '00'); $jianquan = str_split($auth, 2); $messageBody = array_merge($messageBody, $ret, $jianquan); } else { $arrayStartFiveBytes = array('7E', '80', '01'); $jianquan = []; $messageBody = array_merge($messageBody, $messageId, $ret, $jianquan); } //设备号 $equipmentNumber = $this->getEquipmentNumberArray($data); //平台流水号 //$systemNumber = $this->getSequenceNumberArray(); $systemNumbers = $this->getMessageNumberArray($data); //消息体长度 /*if ($messageid == '0100') { $msglength = count(array_merge($systemNumbers, $messageId, $ret, $jianquan)); } else { $msglength = count(array_merge($systemNumbers, $messageId, $ret)); }*/ if($messageid == '0100'){ $msglength = count(array_merge($jianquan))+3; }else{ $msglength = count(array_merge($systemNumbers, $messageId, $ret)); } $msglength = decbin($msglength); //补齐16位,不加密,无版本号 $attr = sprintf("%016d", $msglength); //消息体属性 //前置补0 $attr_str = str_pad(dechex(bindec($attr)), 4, '0', STR_PAD_LEFT); //$attr_str = dechex(bindec($attr)); //分割字符 $attrarray = str_split($attr_str, 2); //与消息体合并 $array_attr = array_merge($arrayStartFiveBytes, $attrarray); //数组开始5位和设备号合并 $arrayStartAndEquipmentNumber = array_merge($array_attr, $equipmentNumber); //接上一步继续与平台流水号合并 $startEquipmentAndSystemNumber = array_merge($arrayStartAndEquipmentNumber, $systemNumbers); //接上一步继续与消息体合并 $startEquipmentSystemAndMessageBody = array_merge($startEquipmentAndSystemNumber, $messageBody); //接上一步应答结果 //$dataAndRet = array_merge($startEquipmentSystemAndMessageBody, $ret); $dataAndRet = $startEquipmentSystemAndMessageBody; //生成校验码 ////$dataAndRetXor = $this->getEveryXor($dataAndRet); $dataAndRetXor = $this->checkCode($dataAndRet); ////var_dump($dataAndRetXor); //数组末尾两位 $arrayEndTwoBytes = array($dataAndRetXor, '7E'); //整个数组 $completeArray = array_merge($dataAndRet, $arrayEndTwoBytes); //发送给客户端的字符串 $sendClientStr = $this->arrayToBytes($completeArray);
return $sendClientStr; }}?>


可以根据自己的需求去更改部分算法



浏览 46
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报