$value){ $bin .= sprintf('%02X',$value); if($space) $bin .= ' '; } return trim($bin); } private static function packValue( $value ){ if($value['cmd'] == 0x81){//登录后响应 return pack('C1C1C1N1',$value['version'],$value['cmd'],$value['result'],time()); } elseif($value['cmd'] == 0x82){//定位数据上报后响应 return pack('C1C1',$value['version'],$value['cmd']); }elseif($value['cmd'] == 0x83){//心跳上报后响应 return pack('C1C1C1',$value['version'],$value['cmd'],$value['result']); }elseif($value['cmd'] == 0x04){//下发远程控制数据 return pack('C1C1C1H10H10',$value['version'],$value['cmd'],$value['type'],$value['tid'],$value['msgid']); }elseif($value['cmd'] == 0x94){//下发远程控制数据后,二次下发应答 return pack('C1C1C1',$value['version'],$value['cmd'],$value['result']); }elseif($value['cmd'] == 0x05){//下发远程查询参数 $bin = pack('C1C1C1H10H10',$value['version'],$value['cmd'],$value['type'],$value['tid'],$value['msgid']); /* $data = unpack("C*chars",$bin); $hex = ''; foreach($data as $key=>$value){ $hex .= sprintf('%02X',$value); if($space) $hex .= ' '; } echo 'pack_hex:'.$hex.PHP_EOL; */ return $bin; //return pack('C1C1C1H10H10',$value['version'],$value['cmd'],$value['type'],$value['tid'],$value['msgid']); }elseif($value['cmd'] == 0x06){//下发远程设置参数 //设置域名及端口时,指令值是32字节(域名)+2字节(端口号) if($value['type'] == 0xC){ return pack('C1C1C1a32n1H10H10',$value['version'],$value['cmd'],$value['type'],$value['domain'],$value['port'],$value['tid'],$value['msgid']); } return pack('C1C1C1v1H10H10',$value['version'],$value['cmd'],$value['type'],$value['code'],$value['tid'],$value['msgid']); } /* if($value['cmd'] == 0x01){//登录后响应 return pack('C1C1C1H*',$value['type'],$value['cmd'],$value['result'],$value['time']); } elseif($value['cmd'] == 0x02){//运动上报后响应 return pack('C1C1H*',$value['type'],$value['cmd'],$value['ext']); } elseif($value['cmd'] == 0x03){//静止上报后响应 return pack('C1C1C1H*',$value['type'],$value['cmd'],$value['alarm_status'],$value['ext']); } elseif($value['cmd'] == 0x04){//测试上报后响应 return pack('C1C1C1',$value['type'],$value['cmd'],$value['result']); } elseif($value['cmd'] == 0x05){//配置下发 return pack('C1C1C1C1C1',$value['type'],$value['cmd'],$value['vibrate_time'],$value['vibrate_count'],$value['report_time']); } elseif($value['cmd'] == 0x07){//重启或恢复出厂设置 return pack('C1C1C1',$value['type'],$value['cmd'],$value['reset']); } elseif($value['cmd'] == 0x08){//开锁 return pack('C1C1C1',$value['type'],$value['cmd'],$value['unlock']); } elseif($value['cmd'] == 0x09){//锁状态上报响应 return pack('C1C1',$value['cmd'],$value['lock_status']); } */ else{ return false; } } private static function unpackValue( $cmd, $value, $length ){ if($length != strlen($value)){ return null; } if($cmd == 0x01){ //gps设备登录 //解析时间 //$info = unpack('C1version/C1cmd/N1time/H10tid/H16imei/H16imsi/a20iccid',$value); $info = unpack('C1version/C1cmd/N1time/H10tid/H16imei/H16imsi',$value); return array( 'version' => $info['version'],//版本号 'cmd' => $info['cmd'],//功能码 'time' => $info['time'],//时间戳 'tid' => $info['tid'],//终端id 'imei' => $info['imei'], 'imsi' => $info['imsi'], //'iccid' => $info['iccid'],//sim卡卡号(20字节字符) ); } elseif($cmd == 0x02){ //运动中/静止时数据上报 $info = unpack('C1version/C1cmd/H10tid/N1time/nlng1/nlng2/nlat1/nlat2/n1altitude/n1max_speed/C1state/H4lac/H4cid/C1gps_satellite_count/C1gsm_rssi/n1main_bettery_voltage/C1spare_bettery_voltage/C1lock',$value); //经纬度解析 $lat_a = floor($info['lat1']/100); $lat_b = (($info['lat1'] - $lat_a*100).'.'.sprintf("%04d",$info['lat2']) )/60; $lat = $lat_a + $lat_b; $lng_a = floor($info['lng1']/100); $lng_b = (($info['lng1'] - $lng_a*100).'.'.sprintf("%04d",$info['lng2']) )/60; $lng = $lng_a + $lng_b; return array( 'version' => $info['version'],//版本号 'cmd' => $info['cmd'],//功能码 'tid' => $info['tid'],//终端id 'time' => $info['time'],//时间戳 'lat' => $lat, 'lng' => $lng, 'altitude' => $info['altitude'], //海拔 'max_speed' => $info['max_speed']/10,//gps在十秒内的最大速度,换算后单位: km/h 'status_hex' => self::bin2str(substr($value,23,1), false),//车辆状态 'state' => $info['state'],//车辆状态 'lac' => $info['lac'], 'cid' => $info['cid'], 'gps_satellite_count' => $info['gps_satellite_count'],//gps卫星颗数 'gsm_rssi_hex' => self::bin2str(substr($value,29,1), false), 'gsm_rssi' => $info['gsm_rssi'], 'main_bettery_voltage' => $info['main_bettery_voltage']/10, //主电池电压 'spare_bettery_voltage' => $info['spare_bettery_voltage'], //备用电池电压 'lock' => $info['lock'] //车辆开关状态 0-关锁 1-开锁 ); } elseif($cmd == 0x03){ //心跳信息 $info = unpack('C1version/C1cmd/H10tid/n1main_bettery_voltage/C1spare_bettery_voltage',$value); return array( 'version' => $info['version'],//版本号 'cmd' => $info['cmd'],//功能码 'tid' => $info['tid'],//终端id 'main_bettery_voltage' => $info['main_bettery_voltage']/10, //主电池电压 'spare_bettery_voltage' => $info['spare_bettery_voltage'], //备用电池电压 ); }elseif($cmd == 0x04){ //指定一个设备,下发控制指令(转发服务端下发指令) $info = unpack('C1version/C1cmd/C1type/H10tid/H10msgid',$value); return array( "version" => $info['version'], "cmd" => $info['cmd'], "type" => $info['type'], "tid" => $info['tid'], "msgid" => $info['msgid'] ); }elseif($cmd == 0x05){ //下发查询指令(转发服务端下发指令) $info = unpack('C1version/C1cmd/C1type/H10tid/H10msgid',$value); return array( "version" => $info['version'], "cmd" => $info['cmd'], "type" => $info['type'], "tid" => $info['tid'], "msgid" => $info['msgid'] ); }elseif($cmd == 0x06){ //下发设置指令(转发服务端下发指令) $type = ord(substr($value, 2, 1)); if($type == 0x0C){ //设置域名和端口 $info = unpack('C1version/C1cmd/C1type/a32domain/n1port/H10tid/H10msgid',$value); return array( "version" => $info['version'], "cmd" => $info['cmd'], "type" => $info['type'], "domain" => $info['domain'], "port" => $info['port'], "tid" => $info['tid'], "msgid" => $info['msgid'] ); } $info = unpack('C1version/C1cmd/C1type/v1code/H10tid/H10msgid',$value); return array( "version" => $info['version'], "cmd" => $info['cmd'], "type" => $info['type'], "code" => $info['code'], "tid" => $info['tid'], "msgid" => $info['msgid'] ); }elseif($cmd == 0x84){ //服务端下发远程控制后gps应答 $info = unpack('C1version/C1cmd/C1result/H10tid/H10msgid',$value); return array( 'version' => $info['version'],//版本号 'cmd' => $info['cmd'],//功能码 'tid' => $info['tid'],//终端id 'result' => $info['result'],//执行结果 0x00:失败 0x01:成功 0x02:运动中 0x03:外接电源不在位 "msgid" => $info['msgid'] ); }elseif($cmd == 0x85){ //远程查询后终端上报 $type = ord(substr($value, 2, 1)); if($type == 0x05){ //查询电压终端响应结果 $info = unpack('C1version/C1cmd/C1type/H10tid/H10msgid/n1result',$value); return array( 'version' => $info['version'],//版本号 'cmd' => $info['cmd'],//功能码 'type' => $info['type'], //应答指令 'tid' => $info['tid'],//终端id "msgid" => $info['msgid'], 'result' => $info['result']/10 //应答结果:电压要除以10 ); }elseif($type == 0x06){ //查询经纬度终端响应结果 $info = unpack('C1version/C1cmd/C1type/H10tid/H10msgid/nlng1/nlng2/nlat1/nlat2',$value); //经纬度解析 $lat_a = floor($info['lat1']/100); $lat_b = (($info['lat1'] - $lat_a*100).'.'.sprintf("%04d",$info['lat2']) )/60; $lat = $lat_a + $lat_b; $lng_a = floor($info['lng1']/100); $lng_b = (($info['lng1'] - $lng_a*100).'.'.sprintf("%04d",$info['lng2']) )/60; $lng = $lng_a + $lng_b; return array( 'version' => $info['version'],//版本号 'cmd' => $info['cmd'],//功能码 'type' => $info['type'], //应答指令 'tid' => $info['tid'],//终端id "msgid" => $info['msgid'], 'lat' => $lat, //纬度 'lng' => $lng //经度 ); }else{ return $value; } }elseif($cmd == 0x86){ //远程设置下发后终端上报 $info = unpack('C1version/C1cmd/C1type/C1result/H10tid/H10msgid',$value); return array( 'version' => $info['version'],//版本号 'cmd' => $info['cmd'],//功能码 'type' => $info['type'],//指令类型 'result' => $info['result'],//指令值 'tid' => $info['tid'],//终端id "msgid" => $info['msgid'], ); } return $value; } private static function unpackValue_old( $cmd, $value, $length ){ if($length != strlen($value)){ return null; } if($cmd == 0x01){ //gps设备登录 //解析时间 $year = Ord(substr($value,2,1))+2000; $month = Ord(substr($value,3,1)); $day = Ord(substr($value,4,1)); $hour = Ord(substr($value,5,1)); $minute = Ord(substr($value,6,1)); $second = Ord(substr($value,7,1)); $time = sprintf("%d-%02d-%02d %02d:%02d:%02d",$year ,$month, $day, $hour, $minute, $second); return array( 'type' => self::bin2str(substr($value,0,1), false) , 'cmd' => self::bin2str(substr($value,1,1), false),//功能码 'time' => $time, 'code' => self::bin2str(substr($value,8,2), false),//产品代码 'version' => self::bin2str(substr($value,10,1), false), 'imei' => self::bin2str(substr($value,11,8), false), 'imsi' => self::bin2str(substr($value,19,8), false), 'ext' => self::bin2str(substr($value,27,$length-27), false) ); } elseif($cmd == 0x02){ //运动时数据上报 //解析时间 $year = Ord(substr($value,2,1))+2000; $month = Ord(substr($value,3,1)); $day = Ord(substr($value,4,1)); $hour = Ord(substr($value,5,1)); $minute = Ord(substr($value,6,1)); $second = Ord(substr($value,7,1)); $time = sprintf("%d-%02d-%02d %02d:%02d:%02d",$year ,$month, $day, $hour, $minute, $second); //解析最大速度 $max_speed = unpack('n1',substr($value,8,2)); //经纬度解析 $lng1 = unpack('nlng1',substr($value,15,2)); $lng2 = unpack('nlng2',substr($value,17,2)); $lng = $lng1['lng1'].'.'.sprintf("%04d",$lng2['lng2']); $lat1 = unpack('nlat1',substr($value,19,2)); $lat2 = unpack('nlat2',substr($value,21,2)); $lat = $lat1['lat1'].'.'.sprintf("%04d",$lat2['lat2']); //解析x,y,z加速度 $x_max_acc = unpack('n1',substr($value,24,2)); $y_max_acc = unpack('n1',substr($value,26,2)); $z_max_acc = unpack('n1',substr($value,28,2)); //解析海拔高度 $altitude = unpack('n1',substr($value,30,2)); return array( 'type' => self::bin2str(substr($value,0,1), false) , 'cmd' => self::bin2str(substr($value,1,1), false),//功能码 'time' => $time, 'max_speed' => $max_speed[1]/10, 'error_code' => self::bin2str(substr($value,10,1), false),//故障代码 'lac' => self::bin2str(substr($value,11,2), false), 'cid' => self::bin2str(substr($value,13,2), false), 'lng' => $lng, 'lat' => $lat, 'status' => self::bin2str(substr($value,23,1), false), 'x_max_acc' => $x_max_acc[1], 'y_max_acc' => $y_max_acc[1], 'z_max_acc' => $z_max_acc[1], 'altitude' => $altitude[1], //海拔 'voltage' => Ord(substr($value,32,1))/10, //备用电池电压 'gps_satellite_count' => Ord(substr($value,33,1)),//gps卫星颗数 'gsm_rssi' => Ord(substr($value,34,1)), 'ext' => self::bin2str(substr($value, 35, $length-35), false), //扩展字节 ); } elseif($cmd == 0x03){ //静止时数据上报 //解析时间 $year = Ord(substr($value,2,1))+2000; $month = Ord(substr($value,3,1)); $day = Ord(substr($value,4,1)); $hour = Ord(substr($value,5,1)); $minute = Ord(substr($value,6,1)); $second = Ord(substr($value,7,1)); $time = sprintf("%d-%02d-%02d %02d:%02d:%02d",$year ,$month, $day, $hour, $minute, $second); //解析最大速度 $max_speed = unpack('n1',substr($value,8,2)); //经纬度解析 $lng1 = unpack('nlng1',substr($value,15,2)); $lng2 = unpack('nlng2',substr($value,17,2)); $lng = $lng1['lng1'].'.'.sprintf("%04d",$lng2['lng2']); $lat1 = unpack('nlat1',substr($value,19,2)); $lat2 = unpack('nlat2',substr($value,21,2)); $lat = $lat1['lat1'].'.'.sprintf("%04d",$lat2['lat2']); //解析x,y,z加速度 $x_max_acc = unpack('n1',substr($value,24,2)); $y_max_acc = unpack('n1',substr($value,26,2)); $z_max_acc = unpack('n1',substr($value,28,2)); //解析海拔高度 $altitude = unpack('n1',substr($value,30,2)); return array( 'type' => self::bin2str(substr($value,0,1), false) , 'cmd' => self::bin2str(substr($value,1,1), false),//功能码 'time' => $time, 'max_speed' => $max_speed[1]/10, 'error_code' => self::bin2str(substr($value,10,1), false),//故障代码 'lac' => self::bin2str(substr($value,11,2), false), 'cid' => self::bin2str(substr($value,13,2), false), 'lng' => $lng, 'lat' => $lat, 'status' => self::bin2str(substr($value,23,1), false), 'x_max_acc' => $x_max_acc[1], 'y_max_acc' => $y_max_acc[1], 'z_max_acc' => $z_max_acc[1], 'altitude' => $altitude[1], //海拔 'voltage' => Ord(substr($value,32,1))/10, //备用电池电压 'gps_satellite_count' => Ord(substr($value,33,1)),//gps卫星颗数 'gsm_rssi' => Ord(substr($value,34,1)), 'ext' => self::bin2str(substr($value, 35, $length-35), false), //扩展字节 ); }elseif($cmd == 0x04){ //测试 //解析时间 $year = Ord(substr($value,2,1))+2000; $month = Ord(substr($value,3,1)); $day = Ord(substr($value,4,1)); $hour = Ord(substr($value,5,1)); $minute = Ord(substr($value,6,1)); $second = Ord(substr($value,7,1)); $time = sprintf("%d-%02d-%02d %02d:%02d:%02d",$year ,$month, $day, $hour, $minute, $second); return array( 'type' => self::bin2str(substr($value,0,1), false) , 'cmd' => self::bin2str(substr($value,1,1), false),//功能码 'time' => $time, 'ext' => self::bin2str(substr($value, 8, $length-8), false), //扩展字节 ); }elseif($cmd == 0x05){ //配置下发后响应 return array( 'type' => self::bin2str(substr($value,0,1), false) , 'cmd' => self::bin2str(substr($value,1,1), false),//功能码 'resp' => self::bin2str(substr($value,2,1), false),//应答字 0x00-成功 0x01-失败 ); }elseif($cmd == 0x05){ //重启或恢复出厂设置后响应 return array( 'type' => self::bin2str(substr($value,0,1), false) , 'cmd' => self::bin2str(substr($value,1,1), false),//功能码 'resp' => self::bin2str(substr($value,2,1), false),//应答字 0x00-成功 0x01-失败 ); }elseif($cmd == 0x08){ //开锁下发后响应 return array( 'type' => self::bin2str(substr($value,0,1), false) , 'cmd' => self::bin2str(substr($value,1,1), false),//功能码 'resp' => self::bin2str(substr($value,2,1), false),//应答字 0x00-成功 0x01-失败 ); }elseif($cmd == 0x09){ //锁状态请求 return array( 'type' => self::bin2str(substr($value,0,1), false) , 'cmd' => self::bin2str(substr($value,1,1), false),//功能码 'req' => self::bin2str(substr($value,2,1), false),//状态字 0x00-开启 0x01-关闭 ); } return $value; } public static function crc8( $ptr ){ $crc = 0; $len = strlen($ptr); echo 'crc8_len:'.$len.PHP_EOL; $j = 0; while($len--) { //$crc ^= ord(substr($ptr,$j,1)); $chr = ord($ptr[$j]); //echo '$chr:'.$chr.PHP_EOL; //echo '$crc:'.$crc.PHP_EOL; $crc ^= $chr; //echo '$crc ^= $chr:'.$crc.PHP_EOL; if($crc>=256){ $crc %= 256; } $j++; for($i = 0;$i < 8;$i++){ //echo '$crc & 0x80:'. ($crc & 0x80) .PHP_EOL; if($crc & 0x80){ //echo '$crc << 1:'.($crc << 1) .PHP_EOL; $crc = ($crc << 1) ^ 0x07; if($crc>=256){ $crc %= 256; } //echo '($crc << 1) ^ 0x07:'.(($crc << 1) ^ 0x07) .PHP_EOL; }else{ $crc <<= 1; //echo '$crc << 1:'.($crc << 1) .PHP_EOL; } } } return $crc; } public static function decode( $buffer ){ //解码头部数据 $unpack_head = unpack('C1magic/C1length/C1version/C1cmd', $buffer); //****临时处理: 0x86指令且长度为0x0D,修改长度为0x0E **** if($unpack_head['cmd'] == 0x86 && $unpack_head['length'] == 0x0D){ //重新设置长度 $buffer = substr($buffer,0,1).chr(hexdec('0E')).substr($buffer,2); $unpack_head = unpack('C1magic/C1length/C1version/C1cmd', $buffer); //重新计算校验码 $bin = substr($buffer,1,$unpack_head['length']+1); $sign = self::crc8($bin); $buffer = substr($buffer,0,strlen($buffer)-1).chr($sign); } $unpack_head['method'] = sprintf("method%'04x", $unpack_head['cmd']); //判断头部引导符 if( $unpack_head['magic'] != 0xFF ) return null; echo 'buffer_length:'.strlen($buffer).PHP_EOL; $packet = self::bin2str($buffer, true); echo 'packet:'.$packet.PHP_EOL; //判断签名是否正确 $crc_bin = substr($buffer,1,$unpack_head['length']+1); $decode_sign = self::crc8($crc_bin); //echo sprintf("decode_sign = %02x\n", $decode_sign).PHP_EOL; $real_sign = Ord(substr($buffer,-1)); //echo sprintf("real_sign = %02x\n", $real_sign).PHP_EOL; if($decode_sign != $real_sign){ echo 'decode sign error! '.sprintf("decode_sign = %02x", $decode_sign) .','.sprintf("real_sign = %02x", $real_sign) .PHP_EOL; return null; } //如果长度等于0则没有值数据 if($unpack_head['length'] == 0) return $unpack_head; //解码值数据 $value = substr($buffer,2,$unpack_head['length']); $unpack_value = self::unpackValue($unpack_head['cmd'],$value,$unpack_head['length']); if(!$unpack_value) return null; $unpack_value['packet'] = $packet; //头部+值一起返回 return array_merge($unpack_head,$unpack_value); } public static function encode( $value ){ echo 'encode:'.PHP_EOL; var_dump($value); //版本号+命令码+装载数据 打包为二进制数据包 $pack_value = self::packValue($value); echo 'pack_value:'.self::bin2str($pack_value).PHP_EOL; $length = strlen($pack_value); echo 'length:'.$length.PHP_EOL; //准备生成校验码的数据(从包长度开始,到装载数据结束) $bin = chr($length).$pack_value; //生成校验码 $sign = self::crc8($bin); echo sprintf("encode_sign_hex = %02x\n", $sign).PHP_EOL; //最终响应数据 $buf = pack('C1C1', 0xFF,$length) . $pack_value . pack('C1', $sign); echo 'encode_value:'.self::bin2str($buf).PHP_EOL; return $buf; } public static function input( $buffer, $connection ){ if (strlen($buffer) < 2) { return 0; } $unpack_data = unpack('C1magic/C1length/C1version/C1cmd', $buffer); if($unpack_data['length']==0){ return $unpack_data['length'] + 2; } //****临时代码:嘉为86指令数据长度有错误,少了一个字节,暂时特殊处理下**** if($unpack_data['cmd']==0x86 && $unpack_data['length']==0x0D){ return $unpack_data['length'] + 4; } return $unpack_data['length'] + 3; } public static function str2bin( $text ){ if (!is_string($text)) return null; $arr = explode(' ',$text); $bin = ''; foreach($arr as $hex){ if(strlen($hex) == 2){ $bin .= chr( hexdec($hex) ); } } return $bin; } }