php workerman對接部標808協(xié)議
公司業(yè)務需求,這幾天一直對著機器對接,整理出以下算法,實現(xiàn)對設備的交互。
服務端:
namespace 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進制數(shù)據(jù)$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);//狀態(tài)$status = $JT808->getPositionStatus($data16Array, 17);//經(jīng)度$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) {//執(zhí)行你的邏輯var_dump($Latitude);var_dump($Longitude);}}//發(fā)送給客戶端$sendClientData = $JT808->getVerifyNumberArray($data16Array);$connection->send($sendClientData);}};// 運行workerWorker::runAll();
JT808.php
數(shù)據(jù)解析算法
namespace app\server\controller;class JT808 {public static $sequenceNumber = 0; //流水號初始值/*** description 轉成10進制字符串數(shù)組* @param $string 16進制字符串* @return array 10進制數(shù)組*/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進制字符串數(shù)組轉成16進制字符串數(shù)組* @param $data 10進制字符串數(shù)組* @return mixed 16進制字符串數(shù)組*/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進制數(shù)組* @return array 補0之后的16進制數(shù)組*/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位的數(shù)組轉化位整形* @param array 接受數(shù)組* @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 數(shù)組* @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進制數(shù)組中獲取設備號數(shù)組* @param $data 接受到的16進制數(shù)組* @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 把一個二字節(jié)數(shù)組轉化成整型* @param $data 二字節(jié)數(shù)組* @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字節(jié)數(shù)組轉成int* @param $data 16進制字節(jié)數(shù)組* @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 [email protected]* @DateTime 2020-05-01T00:19:06+0800* @param [array] $data [數(shù)組]* @return [array] [返回2位數(shù)數(shù)組]*/public function checkCode($data) {$sum = 0;//去掉開頭的7eunset($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 將字節(jié)數(shù)組轉為字符串* @param array 字節(jié)數(shù)組* @return string 返回字符串*/public function bytesArrayToString($data) {$str = '';foreach ($data as $ch) {$str .= chr($ch);}return $str;}/*** [arrayToBytes 返回二進制內容]* @author [email protected]* @DateTime 2020-05-01T00:34:33+0800* @param [type] $data [數(shù)組]* @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進制數(shù)組* @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進制數(shù)組* @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進制數(shù)組來計算出設備號* @param $data 16進制數(shù)組* @return array 返回設備號數(shù)組*/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進制數(shù)組* @param $index 數(shù)組索引13* @return int 返回數(shù)字代表報警信息*/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 獲取狀態(tài)信息* @param $data array 16進制數(shù)組* @param $index 數(shù)組索引* @param $type 返回數(shù)據(jù)還是原本信息* @return array 狀態(tài)信息數(shù)組*/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 = "南緯";//判斷東西經(jīng),0東經(jīng),1西經(jīng)$isEasWes = substr($positionArray, -4, 1) == 0 ? $isEasWes = "東經(jīng)" : $isEasWes = "西經(jīng)";//判斷定位方式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進制數(shù)組* @param $index 數(shù)組索引* @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 獲取經(jīng)度* @param $data array 16進制數(shù)組* @param $index 數(shù)組索引* @param $type 返回字符類型 i整形 f浮點形* @return float|int 經(jīng)度*/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 [email protected]* @DateTime 2020-04-30T14:12:30+0800* @param [type] $data [16進制數(shù)組]* @param [type] $index [數(shù)組索引29]* @return [type] [高度]*/public function getHeight($data, $index) {$heightBytes = array_slice($data, $index, 2);$height = $this->twoBytesToInteger($heightBytes);return $height;}/*** [getSpeed 獲取速度]* @author [email protected]* @DateTime 2020-04-30T14:14:55+0800* @param [type] $data [16進制數(shù)組]* @param [type] $index [數(shù)組索引31]* @return [type] [速度]*/public function getSpeed($data, $index) {$speedBytes = array_slice($data, $index, 2);$speed = $this->twoBytesToInteger($speedBytes);return $speed;}/*** [getDirection 獲取方向]* @author [email protected]* @DateTime 2020-04-30T14:15:33+0800* @param [type] $data [16進制數(shù)組]* @param [type] $index [數(shù)組索引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進制數(shù)組* @param $index 數(shù)組索引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 返回流水號數(shù)組*/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進制數(shù)組* @return array 消息流水號數(shù)組*/public function getMessageNumberArray($data) {$messageNumber = array_slice($data, 11, 2);//$messageNumber = $this->supplementZero($messageNumber);return $messageNumber;}/*** description 獲取消息id* @param $data 16進制數(shù)組* @return array 消息id數(shù)組*/public function getMessageIdArray($data) {$messageId = array_slice($data, 1, 2);//$messageId = $this->supplementZero($messageId);return $messageId;}/*** description 獲取消息id* @param $data array 16進制數(shù)組* @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進制數(shù)組* @return array 消息體數(shù)組*/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 發(fā)送給客戶端的回傳數(shù)據(jù)* @param $data 16進制數(shù)組* @return string 返回客戶端字符串*/public function getVerifyNumberArray($data, $auth = '31313131') {//數(shù)組開始五位//$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);//數(shù)組開始5位和設備號合并$arrayStartAndEquipmentNumber = array_merge($array_attr, $equipmentNumber);//接上一步繼續(xù)與平臺流水號合并$startEquipmentAndSystemNumber = array_merge($arrayStartAndEquipmentNumber, $systemNumbers);//接上一步繼續(xù)與消息體合并$startEquipmentSystemAndMessageBody = array_merge($startEquipmentAndSystemNumber, $messageBody);//接上一步應答結果//$dataAndRet = array_merge($startEquipmentSystemAndMessageBody, $ret);$dataAndRet = $startEquipmentSystemAndMessageBody;//生成校驗碼////$dataAndRetXor = $this->getEveryXor($dataAndRet);$dataAndRetXor = $this->checkCode($dataAndRet);////var_dump($dataAndRetXor);//數(shù)組末尾兩位$arrayEndTwoBytes = array($dataAndRetXor, '7E');//整個數(shù)組$completeArray = array_merge($dataAndRet, $arrayEndTwoBytes);//發(fā)送給客戶端的字符串$sendClientStr = $this->arrayToBytes($completeArray);return $sendClientStr;}}
可以根據(jù)自己的需求去更改部分算法
評論
圖片
表情
