git 6 years ago
parent
commit
3533f13511

+ 6 - 0
.gitignore

@@ -0,0 +1,6 @@
+.env
+composer.lock
+vendor/
+.data/
+.debug
+workerman.pid

+ 2 - 0
Home/Common/common.php

@@ -0,0 +1,2 @@
+<?php
+require_once(COMMON_PATH . 'jzgl_lib.php');

+ 104 - 0
Home/Common/jzgl_lib.php

@@ -0,0 +1,104 @@
+<?php
+
+ 
+	function log_debug( $msg ){
+		if (!APP_DEBUG) return;
+		static $time_cache = 0, $msg_cache = '';
+		if (log_cache($time_cache, $msg_cache, $msg)) {
+			return;
+		}
+		$logger = \Jiaruan\FileLogger::getInstance(SOLUTION_LOG_PATH . '/jzgl/');
+		$logger->log('',\Jiaruan\FileLogger::DEBUG,$msg);
+		
+	}
+	
+ 
+	function log_error( $msg ){
+		static $time_cache = 0, $msg_cache = '';
+		if (log_cache($time_cache, $msg_cache, $msg)) {
+			return;
+		}
+		$logger = \Jiaruan\FileLogger::getInstance(SOLUTION_LOG_PATH . '/jzgl/');
+		$logger->log('',\Jiaruan\FileLogger::ERROR,$msg);
+	}
+	
+ 
+	function log_info( $msg ){
+		if (!APP_DEBUG) return;
+		static $time_cache = 0, $msg_cache = '';
+		if (log_cache($time_cache, $msg_cache, $msg)) {
+			return;
+		}
+		$logger = \Jiaruan\FileLogger::getInstance(SOLUTION_LOG_PATH . '/jzgl/');
+		$logger->log('',\Jiaruan\FileLogger::INFO,$msg);
+	}
+	
+ 
+	function createProtoClass( $type ){
+		static $instances = array();
+		if($instances[$type])
+			return $instances[$type];
+		if($type == C('协议类型_基站开机登陆')){
+			$instances[$type] = new \Jiaruan\ProtoLogin;
+		}
+		else if($type == C('协议类型_基站定时心跳')){
+			$instances[$type] = new \Jiaruan\ProtoHeartbeat;
+		}
+		else if($type == C('协议类型_用户布控车辆')){
+			$instances[$type] = new \Jiaruan\ProtoMonit;
+		}
+		else if($type == C('协议类型_用户撤控车辆')){
+			$instances[$type] = new \Jiaruan\ProtoUnmonit;
+		}
+		else if($type == C('协议类型_用户重启基站')){
+			$instances[$type] = new \Jiaruan\ProtoReboot;
+		}
+		else if($type == C('协议类型_用户升级基站')){
+			$instances[$type] = new \Jiaruan\ProtoUpgrade;
+		}
+		else if($type == C('协议类型_用户检查基站')){
+			$instances[$type] = new \Jiaruan\ProtoCheck;
+		}
+		else if($type == C('协议类型_用户下发命令行')){
+			$instances[$type] = new \Jiaruan\ProtoCmdLine;
+		}
+		else{
+			$instances[$type] = null;
+		}
+		return $instances[$type];
+	}
+	
+ 
+	function get_station_cityid( $mac ){
+		$ret = MM("jms_station",'fdv2')->where(array("DeviceNumber" => $mac))->getField('CityId');
+		return $ret == null ? 0 : $ret;
+	}
+	
+ 
+	function get_vehicle_cityid( $vehicle_number ){
+		$ret = MM("jms_vehicle",'fdv2')->where(array("DeviceNumber" => $vehicle_number))->getField('CityId');
+		return $ret == null ? 0 : $ret;
+	}
+	
+ 
+	function log_cache( &$time_cache, &$msg_cache, &$msg ){
+		if ($time_cache == 0) {
+			$time_cache = time();
+		}
+		if ($msg_cache == '') {
+			$msg_cache = "\n";
+		}
+		$msg_cache .= "[" .date("H:i:s", time()) . "]" . $msg . "\n";
+		//关闭主进程 或 日志大于15k 或 时间大于2秒 写日志到磁盘
+		if (strpos($msg, "stop:") !== 0
+			&& strlen($msg_cache) < 1024 * 15
+			&& time() - $time_cache < 2) {
+			return true;
+		}
+		$msg = $msg_cache;
+		$msg_cache = '';
+		$time_cache = time();
+		return false;
+	}
+	
+

+ 11 - 0
Home/Conf/config.php

@@ -0,0 +1,11 @@
+<?php
+return array(
+	
+	'告警类型_经过告警' => 0,
+	'告警类型_被盗告警' => 1,
+	
+	'车辆运动方向_进入小区' => 1,
+	'车辆运动方向_走出小区' => 2,
+	
+	
+);

+ 113 - 0
Home/Lib/Action/IndexAction.class.php

@@ -0,0 +1,113 @@
+<?php
+
+
+
+class IndexAction extends Action {
+			
+
+
+ 
+	public  function index(  ){
+		Vendor('Workerman352.Autoloader');
+		$tcp_worker = new Workerman\Worker("tcp://0.0.0.0:20160");
+		
+		Workerman\Worker::runAll();//保持运行
+	}
+	
+ 
+	public  function test(  ){
+		$this->redis_vsignal = Redis("fdzs_vsignal_{no}","zset")->prefix('fdzs_vsignal_');
+		$where = array( 0,time());
+		$vehicle = '864127124d17';
+		$list = $this->redis_vsignal->table($vehicle)->where($where)->select();
+		$sum = 0;
+		$count = 0;
+		foreach($list as $signal => $time){
+			$signal = json_decode($signal,true);
+			$sum += $signal['Rssi'];
+			$count++;
+		}
+		if(!$count){
+			return false;
+		}
+		$avgRssi = $sum/$count;
+		echo '$avgRssi = '.$avgRssi.PHP_EOL;
+		return $avgRssi;
+		var_dump($list);exit;
+		$alarm_info['ID'] = create_guid();
+		
+		$result = M('rchy_alarm')->createAdd($alarm_info);
+		var_dump($result);exit;
+		$this->redis_vsignal = Redis("rchy_vsignal_{no}","zset")->prefix('fdzs_vsignal_');
+		/*
+		//模拟插入信号数据
+		for($i=0;$i<5;$i++){
+			$row = array();
+			$row['VehicleNumber'] = 'FFFF11112222';
+			$row['StationCode'] = 'A11066';
+			$row['rssi'] = 60+$i;
+			$row['Timestamp'] = time();
+			$vehicle_no = strtolower($row['VehicleNumber']);
+			$signal = array(
+				'StationCode' => $row['StationCode'],
+				'Rssi' => $row['rssi'],
+				'Timestamp' => $row['Timestamp']
+			);
+			$data = array( json_encode($signal) => $row['Timestamp'] );
+			$this->redis_vsignal->table($vehicle_no)->add($data);
+			echo 'insert '.$i.PHP_EOL;
+			sleep(1);
+		}
+		*/
+		$rchy_alarm_list = Redis("rchy_alarm_list","queue");
+		$alarm_info = $rchy_alarm_list->pop();
+		var_dump($alarm_info);exit;
+		$time = time();
+		while(time() - $time<60){
+			$vehicle_no = strtolower('864127129ac7');
+			$last_signal = $this->redis_vsignal->table($vehicle_no)->max();
+			var_dump($last_signal);
+		}
+		exit;
+		
+		//输出轨迹查询结果
+		$vehicle_no = strtolower('FFFF11112222');
+		$now = time();
+		$cond = array($now-60,$now);
+		$cond = array(1531208748,$now);
+		$cond = array(0,$now);
+		$first_info = $this->redis_vsignal->table($vehicle_no)->where($cond)->min();
+		echo $first_info[1].PHP_EOL;
+		var_dump($first_info);exit;
+		
+		$last_info = $this->redis_vsignal->table($vehicle_no)->where($cond)->max();
+		var_dump($last_info);exit;
+		$signal_list = $this->redis_vsignal->table($vehicle_no)->where($cond)->select();
+		
+		//var_dump($signal_list);
+		$list = array();
+		foreach($signal_list as $key=>$row){
+			$signal = json_decode($key,true);
+			if(!$signal){
+				return false;
+			}
+			$list[] = $signal;
+		}
+		var_dump($list);
+		
+		/*
+		输出结果:按分数升序
+		array(3) {
+		  ["{"StationCode":"A11066","Rssi":62,"Timestamp":1531208748}"]=>
+		  float(1531208748)
+		  ["{"StationCode":"A11066","Rssi":63,"Timestamp":1531208749}"]=>
+		  float(1531208749)
+		  ["{"StationCode":"A11066","Rssi":64,"Timestamp":1531208750}"]=>
+		  float(1531208750)
+		}
+		
+		*/
+	}
+	
+
+}

+ 692 - 0
Home/Lib/Action/RcgjfwDecodeAction.class.php

@@ -0,0 +1,692 @@
+<?php
+class RcgjfwDecodeAction extends Action {
+	/*
+	参数说明:
+		packet: 
+		packet_text:带空格16进制文本 
+
+	*/
+	private function import($packet, $packet_text){
+		//解析消息体
+		$result = $this->rfid_proto->tcp_unpack_msg($packet);
+		if(! $result['Success'] ){
+			
+			$this->rfid_proto->decode_error_log('unpack_fail','unpack packet failed ,result : '.json_encode($result));
+			return;
+		}
+		//记录从redis获取到的基站数据包
+		$this->rfid_proto->decode_packet_log($result['StationCode'],$result['StationCode'].' => '.$packet_text);
+		
+		//获取一条车辆标签(电子标签编码)
+		$FirstLabelNumber = $result['VehicleList'][0]['VehicleNumber'];
+		//tcp_log('tcp packet received. station code= ' . $result['StationCode'] . ', label_number= ' . $FirstLabelNumber);
+		//未入库基站,缓存命中,直接返回
+		$not_exit_station_key = 'not_exit_station_'.$result['StationCode'];
+		$not_exit_station = \Jiaruan\StaticCache::get($not_exit_station_key);
+		if( \Jiaruan\StaticCache::ishit($not_exit_station) ){
+			decode_error_log('not_existed_station', 'station not existed,cath is hit, station_code =  ' . $result['StationCode']);
+			return;
+		}
+		//已开局基站,缓存一小时基站信息,避免频繁查询
+		$station_key = 'station_info_'.$result['StationCode'];
+		$station_info = \Jiaruan\StaticCache::get($station_key);
+		if( !\Jiaruan\StaticCache::ishit($station_info) ){
+			try{
+				//获取基站信息
+				$cond = array('DeviceCode' => $result['StationCode']);
+				//排除指定字段,防止旧系统查询不存在的字段时报错
+				$station_info = M('jms_station')->where($cond)->field('IsDuplicate,ChargerId,PhotoUrl,SimCardNo,DeviceType,AddTime,InstallationTime,OpenSucessTime,OnlineTime,Comment,IsRecycled,PoliceId,MoveStationLeader,MoveStationPhone,ManufactType',true)->find();
+		
+				//$station_info = M('jms_station')->where($cond)->field('ID,StationNumericId,FirstLabelNumber,InstallationStatus,CityId,Longitude,Latitude,Address,StationType,DeviceNumber,DeviceName,')->find();
+			}catch (Exception $e){
+				$msg = 'get_station_info ,cach exception!!  error_code = '.$e->getCode().', error_msg = '.$e->getMessage().', station_code = '.$result['StationCode'];
+				decode_error_log('get_station_conn_mysql_exception',$msg);
+			}
+			
+			//不存在的基站,缓存十分钟,并返回
+			if(!$station_info){ 
+				decode_error_log('not_existed_station', 'station not existed, station_code =  ' . $result['StationCode']);
+				\Jiaruan\StaticCache::set($not_exit_station_key,true,600);
+				return;
+			}
+			
+			if ( $station_info['InstallationStatus'] == C('开局状态_已成功') ){
+				\Jiaruan\StaticCache::set($station_key,$station_info,3600);
+			}
+		}
+		//对数据包进行日志记录
+		/*
+		$data = array(
+			'StationId' => $station_info['ID'] ? : 0,
+			'StationCode' => $result['StationCode'],
+			'PacketData' => $packet_text,
+			'AddTime' => date('Y-m-d H:i:s')
+		);
+		*/
+		
+		$data = array(
+			'StationId' => $station_info['StationNumericId'] ? : ($station_info['ID']?:0),//兼容旧版本
+			'StationCode' => $result['StationCode'],
+			'PacketData' => $packet_text,
+			'AddTime' => date('Y-m-d H:i:s')
+		);
+		
+		//原始包存到表格存储
+		$rawData = $data;//tablestore
+		$rawData['StationCode'] = $station_info['DeviceNumber'];//tablestore
+		$this->routeStore->addRawPacket($station_info['CityId'], $rawData);
+		
+		
+		//移动基站,过大的数据包,跳过处理
+		if($station_info['StationType']==C('基站类型_移动基站')&&$result['body_length']>1000){
+			decode_error_log('too_big_packet_move_station','move_station packet too big, skip decode; station_code => '.$result['StationCode']);
+			return;
+		}
+		//普通基站,过大的数据包,记录一下,但继续处理
+		if($station_info['StationType']==C('基站类型_普通基站')&&$result['body_length']>1000){
+			decode_error_log('too_big_packet_normal_station','normal_station packet too big, station_code => '.$result['StationCode']);
+		}
+		/*
+		$start = microtime(true);
+		if( ! M('jms_rpacket')->createAdd($data) ){
+			decode_error_log('add_rpacket_fail','add rpacket to jms_rpacket fail, error_message:'.M('jms_rpacket')->getLastSql().', data: '.json_encode($data));
+			return;
+		}
+		echo '************time0: '.use_microtime($start).PHP_EOL;
+		*/
+		
+		
+		//更新基站信息(已开局基站,间隔一分钟更新一次,避免频繁更新)
+		$station_key_update_time = 'station_key_update_time_'. $result['StationCode'];
+		$station_update_time = \Jiaruan\StaticCache::get($station_key_update_time);
+		if( !\Jiaruan\StaticCache::ishit($station_update_time) && $FirstLabelNumber && !is_mock_route($FirstLabelNumber) ){
+			$data = array();
+			if(!$station_info['FirstLabelNumber'] && $FirstLabelNumber)
+				$data['FirstLabelNumber'] = $FirstLabelNumber;
+			if( $station_info['InstallationStatus'] == C('开局状态_开局中') ){
+				$data['InstallationStatus'] = C('开局状态_已成功');
+				$data['OpenSucessTime'] = date('Y-m-d H:i:s');
+			}
+			//异常基站跳过更新基站状态
+			$cond = array('DeviceCode' => $result['StationCode']);
+			$station_status = M('jms_station')->where($cond)->getField('DeviceStatus');
+			if($station_status != C('基站状态_异常')){
+				$data['DeviceStatus'] = C('基站状态_在线');
+			}
+			$data['OnlineTime'] = date('Y-m-d H:i:s');
+			$start = microtime(true);
+			try{
+				$cond = array('ID'=>$station_info['ID']);
+				if(! M('jms_station')->where($cond)->save($data) ){
+					decode_error_log('update_station_info',"update station info failed. sql = " . M('jms_station')->getLastSql().',db_error = '. M('jms_station')->getDbError());
+				}
+			}catch (Exception $e) {
+				$msg = 'update_station_info ,cach exception!!  error_code = '.$e->getCode().', error_msg = '.$e->getMessage().', station_code = '.$result['StationCode'];
+				decode_error_log('update_station_conn_mysql_exception',$msg);
+			}
+			
+			echo '************time1:'.use_microtime($start).PHP_EOL;
+			//已开局基站,缓存更新时间
+			if( $station_info['InstallationStatus'] == C('开局状态_已成功') ){
+				\Jiaruan\StaticCache::set($station_key_update_time,time(),60);
+			}
+		}
+		
+		//还没有指定城市,跳过
+		if(! $station_info['CityId'] ){ 
+			decode_error_log('cityid_empty','station cityid not existed, station_code: '.$result['StationCode']);
+			return;
+		}
+		
+		//还没有开局成功,跳过
+		if($station_info['InstallationStatus'] != C('开局状态_已成功') ){ 
+			decode_error_log('not_kaiju','station not kaiju, station_code: '.$result['StationCode']);
+			return;
+		}
+		/*
+				//检测基站近一分钟内是否更新过,更新过的跳过
+				if($this->check_station_is_cathed($station_info['DeviceNumber'])){
+					echo 'skip update station vehicle route;station_code: '.$station_info['DeviceCode'].PHP_EOL;
+					return;
+				}*/
+		//保存到车辆表
+		foreach($result['VehicleList'] as $key=>$row){
+			$result['VehicleList'][$key]['StationCode'] = $result['StationCode'];
+			$result['VehicleList'][$key]['CityId'] = $station_info['CityId'];
+			$start = microtime(true);
+		
+			$not_exist_key = 'not_exist_key_'.$row['VehicleNumber']; 
+			$not_exist = \Jiaruan\StaticCache::get($not_exist_key);
+			// 未入库的标签缓存命中就跳过
+			if( \Jiaruan\StaticCache::ishit($not_exist) ){
+				continue;
+			}
+			try{
+				$cond = array('DeviceNumber'=> $row['VehicleNumber'] );
+				$vehicle_info = M('jms_vehicle')->where($cond)->field('ID,VehicleNumericId,UserId,StationCode,DeviceStatus,LicensePlate')->find();
+			}catch (Exception $e){
+				$msg = 'get_vehicle_info ,cach exception!!  error_code = '.$e->getCode().', error_msg = '.$e->getMessage().', vehicle_no = '.$row['VehicleNumber'];
+				decode_error_log('get_vehicle_conn_mysql_exception',$msg);
+				continue;
+			}
+			//获取未入库标签,缓存十分钟,避免频繁查询
+			if(!$vehicle_info){
+				$result['VehicleList'][$key]['VehicleId'] = 0;
+				$result['VehicleList'][$key]['VehicleStatus'] = -1; //-1表示不存在的车辆标签 
+				//tcp_log( 'skip save vehicle (not exists) '. $row['VehicleNumber'] );
+				decode_error_log('not_exist_vehicles','skip save vehicle (not exists) '. $row['VehicleNumber'].',station_code:'.$result['StationCode']);
+				\Jiaruan\StaticCache::get($not_exist_key,true,600);
+				continue;
+			}
+			
+		
+			$row['StationCode'] = $result['StationCode'];
+			$row['Longitude'] = $station_info['Longitude'];
+			$row['Latitude'] = $station_info['Latitude'];
+			$row['Address'] = $station_info['Address'];
+		
+			$start = microtime(true);
+			$result['VehicleList'][$key]['VehicleId'] = $vehicle_info['VehicleNumericId'] ? : ($vehicle_info['ID']?:0);//兼容旧版本
+			$result['VehicleList'][$key]['LastStationCode'] = $vehicle_info['StationCode'];
+			$result['VehicleList'][$key]['VehicleStatus'] = $vehicle_info['DeviceStatus'];
+			$result['VehicleList'][$key]['LicensePlate'] = $vehicle_info['LicensePlate'];
+			
+			$notkaihu_key = 'cach_notkaihu_'.$row['VehicleNumber']; 
+			$not_kaihu = \Jiaruan\StaticCache::get($notkaihu_key);
+			//获取未开户标签缓存命中,跳过
+			if( \Jiaruan\StaticCache::ishit($not_kaihu) ){
+				echo 'no kaihu , cach is hit, skip,vehicle_no =  '.$row['VehicleNumber'].PHP_EOL;
+				continue;
+			}
+			//模拟轨迹数据,跳过更新车辆信息
+			if(is_mock_route($row['VehicleNumber'])){
+				echo 'mock_route,skip update jms_vehicle'.PHP_EOL;
+				continue;
+			}
+			//未开户车辆,缓存十分钟,避免频繁查询
+			if(!$vehicle_info['UserId']){
+				echo 'no kaihu skip '.PHP_EOL;
+				echo '************time: '.use_microtime($start).PHP_EOL;
+				\Jiaruan\StaticCache::set($notkaihu_key,true,600);
+				continue;
+			}
+			//移动基站跳过更新
+			if($station_info['StationType']==C('基站类型_移动基站')){
+				echo 'skip move station route '.PHP_EOL;
+				continue;
+			}
+			
+			try{
+				if( M('jms_vehicle')->createSave($cond,$row) ){
+					decode_log( 'save vehicle ok '. $row['VehicleNumber'] );
+					echo '************time2:'.use_microtime($start).PHP_EOL;
+				}else{
+					decode_error_log( 'update_vehicle_info ', 'update_vehicle_info failed,vehicle_no = '. $row['VehicleNumber']  . ', sql = ' .M('jms_vehicle')->getLastSql().',db_error = '. M('jms_vehicle')->getDbError() );		
+				}
+			}catch (Exception $e) {
+				$msg = 'update_vehicle_info ,cach exception!!  error_code = '.$e->getCode().', error_msg = '.$e->getMessage().', vehicle_no = '.$row['VehicleNumber'];
+				decode_error_log('update_vehicle_conn_mysql_exception',$msg);
+			}
+			
+		
+		}
+		/*
+		//如果是移动基站,轨迹保存到临时监控轨迹表中
+		if($station_info['StationType']==C('基站类型_移动基站')){
+			$this->add_monitor_sroute($result,$station_info);
+		}else{
+			$this->add_normal_sroute($result,$station_info);
+		}
+		*/
+		//添加解包日志
+		vehicle_route_log('decode',$result);
+		station_route_log('decode',$result);
+		//推送车辆轨迹信号到redis
+		$this->push_redis_vsignal($result['VehicleList']);
+		
+		// tablestore
+		if (C("是否支持表格存储")) {
+			//if (!in_array($station_info['CityId'], [1, 28])) {//28义乌 1鹿邑
+			//	return;
+			//}
+			//$this->TsAddRawPacket($station_info['CityId'], $rawData);
+			if ($station_info['StationType'] == C('基站类型_移动基站')) {
+				$this->add_monitor_tablestore_sroute($result, $station_info);
+			} else {
+				$this->add_normal_tablsestore_sroute($result, $station_info);
+			}
+		}
+	}
+	/*
+
+	*/
+	public function index(){
+		$redis_raw_packet = Redis("fdqu_raw_packet","queue");
+		//实例化信号处理类
+		$this->rfid_proto = new Rlgs\Proto\RfidStation2();
+		$start_time = time();
+		do{
+			//query one record
+			$packet_text = $redis_raw_packet->pop();
+			if(!$packet_text){
+				echo 'no raw packet.'.PHP_EOL;
+				sleep(1);
+				continue;
+			}
+			//parse packet
+			$packet = $this->rfid_proto->tcp_str2bin($packet_text);
+			if(!$packet){
+				
+				$this->rfid_proto->decode_error_log('parse_fail','parse packet failed ,packet_text : '.$packet_text);
+				continue;
+			}
+			$this->import($packet,$packet_text);
+		}while(time()-$start_time<60);
+		
+		
+	}
+	/*
+	参数说明:
+		signal_list: 
+
+	*/
+	private function push_redis_vsignal($signal_list){
+		foreach($signal_list as $key=>$row){
+			if($row['IsOnline']){
+				$data = array(
+					'CityId' => $row['CityId'],
+					'VehicleNumber'=>$row['VehicleNumber'],
+					'StationCode'=> $row['StationCode'],
+					'OnlineTime' => $row['OnlineTime'],
+					'Timestamp' => $row['Timestamp'],
+					'SignalCount' => $row['SignalCount'],
+					'AddTime' => date('Y-m-d H:i:s'),
+				);
+				$r = Redis("fdqu_vehicle_signal","queue"); 
+				$r->push($data);
+				echo '成功添加一条轨迹信号到redis'. PHP_EOL;
+			}
+		}
+	}
+	/*
+	参数说明:
+		result: 
+		station_info: 
+
+	*/
+	private function add_monitor_sroute($result, $station_info){
+		/*
+		//先删除该移动基站2天以前的轨迹
+		$cond = array(
+			'StationId'=>$station_info['ID'],
+			'OnlineTime' => array( 'lt',date('Y-m-d 00:00:00',strtotime("-2 day"))  )
+		);
+		if(M('jms_monitor_sroute')->where($cond)->find()){
+			$result = M('jms_monitor_sroute')->where($cond)->delete();
+			tcp_log(  'success delete 2 days ago data count : '.$result.PHP_EOL );
+		}*/
+		//添加车辆轨迹到临时表
+		$addTime = date('Y-m-d H:i:s');
+		foreach($result['VehicleList'] as $row){
+			$row['StationCode'] = $result['StationCode'];
+			if(!$row['VehicleId']){
+				decode_log( '[not found]skip add route '. $row['StationName'] . ' '. $row['VehicleNumber'] );
+				continue;
+			}
+			$start = microtime(true);
+			//不接收所有标签数据的基站,要检查车辆是否已开户
+			if(!$station_info['IsReceiveAll']){
+				$cond = array('DeviceNumber'=>$row['VehicleNumber']);
+				if(!M('jms_vehicle')->where($cond)->getField('UserId')){
+					$end = microtime(true);
+					decode_log( 'not kaihu , skip add monitor route '. $row['VehicleNumber']);
+					continue;
+				}
+			}
+			echo '************time1:'.use_microtime($start).PHP_EOL;
+			if($row['IsOnline']){
+				//查看车辆最近一个基站是否这个基站
+				$row['CityId'] = $station_info['CityId'];
+				$row['StationId'] = $station_info['StationNumericId']?:$station_info['ID'];
+				$row['StationName'] = $station_info['DeviceName'];
+				$row['Longitude'] = $station_info['Longitude'];
+				$row['Latitude'] = $station_info['Latitude'];
+				$row['Address'] = $station_info['Address'];
+				$row['AddTime'] = $addTime;
+				//如果是被盗状态车辆,轨迹同时保存到固定基站轨迹表
+				$cond = array('DeviceNumber'=>$row['VehicleNumber']);
+				$vehicle_status = M('jms_vehicle')->where($cond)->getField('DeviceStatus');
+				$start = microtime(true);
+				if($vehicle_status == C('车辆状态_被盗')){
+					if( M('jms_sroute')->createAdd($row) ){
+						decode_log( 'stolen monitor_route add to sroute ok '. $row['StationName'] . ' '. $row['VehicleNumber']);
+					}
+					else{
+						decode_log( 'stolen monitor_route add to sroute fail '. $row['StationName'] . ' '. $row['VehicleNumber'] . ' sql-' .M('jms_sroute')->getLastSql());
+					}
+					echo '************time2:'.use_microtime($start).PHP_EOL;
+				}
+				$start = microtime(true);
+				if( M('jms_monitor_sroute')->createAdd($row) )
+					decode_log( 'add monitor_sroute ok '. $row['StationName'] . ' '. $row['VehicleNumber'] .' OnlineTime: '.$row['OnlineTime'].' ,AddTime: '.$addTime.' ,timestamp: '.$row['time']);
+				else
+					decode_log( 'add monitor_sroute fail '. $row['StationName'] . ' '. $row['VehicleNumber'] . ' sql-' .M('jms_sroute_tmp')->getLastSql());
+				echo '************time3:'.use_microtime($start).PHP_EOL;
+			}
+			else{
+				decode_log( '[offline]skip monitor_sroute add route '. $row['StationName'] . ' '. $row['VehicleNumber'] );
+			}
+		}
+	}
+	/*
+	参数说明:
+		result: 
+		station_info: 
+
+	*/
+	private function add_normal_sroute($result, $station_info){
+		//保存到轨迹表
+		$addTime = date('Y-m-d H:i:s');
+		foreach($result['VehicleList'] as $row){
+			if(!$row['Timestamp']){
+				decode_error_log('time_stamp_empty','skip update(time_stamp is empty),vehicle: '. $row['VehicleNumber'].',station_code: '.$result['StationCode']);
+				continue;
+			}
+			$cond = array('DeviceNumber'=>$row['VehicleNumber']);
+			if(!M('jms_vehicle')->where($cond)->getField('UserId')){
+				decode_log( 'not kaihu , skip add route ' .$row['VehicleNumber'] );
+				continue;
+			}
+			$row['StationCode'] = $result['StationCode'];
+			if(! $row['VehicleId']){
+				decode_log( '[not found]skip add route '. $row['StationName'] . ' '. $row['VehicleNumber'] );
+				continue;
+			}
+			if($row['IsOnline']){
+				//查看车辆最近一个基站是否这个基站
+				if($result['StationCode'] != $result['LastStationCode']){ //不是的话,是个新的轨迹
+					$row['CityId'] = $station_info['CityId'];
+					//分表轨迹数据要,stationid要用整数类型,不能用guid类型
+					$row['StationId'] = $station_info['StationNumericId']?:($station_info['ID']?:0);//兼容旧版本
+					$row['StationName'] = $station_info['DeviceName'];
+					$row['Longitude'] = $station_info['Longitude'];
+					$row['Latitude'] = $station_info['Latitude'];
+					$row['Address'] = $station_info['Address'];
+					$row['AddTime'] = $addTime;
+					$start = microtime(true);
+					if( M('jms_vroute')->createAdd($row) )
+						decode_log( 'add vroute ok '. $row['StationName'] . ' '. $row['VehicleNumber'] );
+					else
+						decode_log( 'add vroute fail '. $row['StationName'] . ' '. $row['VehicleNumber'] . ' sql-' .M('jms_route')->getLastSql());
+					echo '************time2:'.use_microtime($start).PHP_EOL;
+					$start = microtime(true);
+					if( M('jms_sroute')->createAdd($row) )
+						decode_log( 'add sroute ok '. $row['StationName'] . ' '. $row['VehicleNumber'] );
+					else
+						decode_log( 'add sroute fail '. $row['StationName'] . ' '. $row['VehicleNumber'] . ' sql-' .M('jms_route')->getLastSql());
+					echo '************time3:'.use_microtime($start).PHP_EOL;
+					
+					//如果是被盗状态车辆,轨迹同时保存到监控轨迹表
+					$cond = array('DeviceNumber'=>$row['VehicleNumber']);
+					$vehicle_status = M('jms_vehicle')->where($cond)->getField('DeviceStatus');
+					if($vehicle_status == C('车辆状态_被盗')){
+						$row['StationType'] = C('基站类型_普通基站');
+						$start = microtime(true);
+						if( M('jms_monitor_sroute')->createAdd($row) ){
+							decode_log( 'add monitor_sroute ok '. $row['StationName'] . ' '. $row['VehicleNumber'] );
+						}
+						else{
+							decode_log( 'add monitor_sroute fail '. $row['StationName'] . ' '. $row['VehicleNumber'] . ' sql-' .M('jms_monitor_sroute')->getLastSql());
+						}	
+						echo '************time1:'.use_microtime($start).PHP_EOL;
+					}
+				}
+				else{
+					decode_log( '[same-station]skip add route '. $row['StationName'] . ' '. $row['VehicleNumber'] );
+				}
+			}
+			else{
+				decode_log( '[offline]skip add route '. $row['StationName'] . ' '. $row['VehicleNumber'] );
+			}
+		}
+	}
+	/*
+
+	*/
+	public function delete_monitor_route(){
+		
+		//先删除该移动基站2天以前的轨迹
+		$cond = array(
+			'OnlineTime' => array( 'lt',date('Y-m-d 00:00:00',strtotime("-2 day"))  )
+		);
+		if(M('jms_monitor_sroute')->where($cond)->find()){
+			echo 'start to delete '.PHP_EOL;
+			$result = M('jms_monitor_sroute')->where($cond)->delete();
+			echo 'finished to delete '.PHP_EOL;
+			decode_log(  'success delete 2 days ago data result : '.$result.PHP_EOL );
+		}else{
+			echo 'no data to delete '.PHP_EOL;
+		}
+		exit;
+	}
+	/*
+	参数说明:
+		station_no:基站标签 
+
+	*/
+	private function check_station_is_cathed($station_no){
+		$cache=S('is_cached_station_'.$station_no);
+		if(!$cache){
+			S('is_cached_station_'.$station_no,1,60);
+			return false;
+		}else{
+			return true;
+		}
+	}
+	/*
+	参数说明:
+		result: 
+		station_info: 
+
+	*/
+	public function add_normal_tablsestore_sroute($result, $station_info){
+		//保存到轨迹表
+		$addTime = date('Y-m-d H:i:s');
+		$monitorRows = []; // tablestore
+		$normalRows = []; // tablestore
+		foreach($result['VehicleList'] as $row){
+			$cond = array('DeviceNumber'=>$row['VehicleNumber']);
+			if(!M('jms_vehicle')->where($cond)->getField('UserId')){
+				continue;
+			}
+			$row['StationCode'] = $result['StationCode'];
+			if(! $row['VehicleId']){
+				continue;
+			}
+			if($row['IsOnline']){
+				//查看车辆最近一个基站是否这个基站
+				if($result['StationCode'] != $result['LastStationCode']){ //不是的话,是个新的轨迹
+					$row['StationType'] = C('基站类型_普通基站');//tablestore
+					$row['CityId'] = $station_info['CityId'];
+					//分表轨迹数据要,stationid要用整数类型,不能用guid类型
+					$row['StationId'] = $station_info['StationNumericId']?:($station_info['ID']?:0);//兼容旧版本
+					$row['StationName'] = $station_info['DeviceName'];
+					$row['Longitude'] = $station_info['Longitude'];
+					$row['Latitude'] = $station_info['Latitude'];
+					$row['Address'] = $station_info['Address'];
+					$row['AddTime'] = $addTime;
+					
+					//如果是被盗状态车辆,轨迹同时保存到监控轨迹表
+					$cond = array('DeviceNumber'=>$row['VehicleNumber']);
+					$vehicle_status = M('jms_vehicle')->where($cond)->getField('DeviceStatus');
+					if($vehicle_status == C('车辆状态_被盗')){
+						$row['StationType'] = C('基站类型_普通基站');
+						$row['StationCode'] = $station_info['DeviceNumber'];// tablestore
+						$monitorRows[] = $row; // tablestore
+						
+					} else {
+					    $row['StationCode'] = $station_info['DeviceNumber'];// tablestore
+					}
+					 $normalRows[] = $row; // tablestore
+				}
+			}
+		} 
+		$this->routeStore->addMonitorRoute($monitorRows); // tablestore
+		$this->routeStore->addNormalRoute($normalRows); // tablestore
+	}
+	/*
+	参数说明:
+		result: 
+		station_info: 
+
+	*/
+	public function add_monitor_tablestore_sroute($result, $station_info){
+		/*
+		//先删除该移动基站2天以前的轨迹
+		$cond = array(
+			'StationId'=>$station_info['ID'],
+			'OnlineTime' => array( 'lt',date('Y-m-d 00:00:00',strtotime("-2 day"))  )
+		);
+		if(M('jms_monitor_sroute')->where($cond)->find()){
+			$result = M('jms_monitor_sroute')->where($cond)->delete();
+			tcp_log(  'success delete 2 days ago data count : '.$result.PHP_EOL );
+		}*/
+		//添加车辆轨迹到临时表
+		$monitorRows = []; // tablestore
+		$mobileRows = []; // tablestore
+		$addTime = date('Y-m-d H:i:s');
+		foreach($result['VehicleList'] as $row){
+			$row['StationCode'] = $result['StationCode'];
+			if(!$row['VehicleId']){
+		// 		tcp_log( '[not found]skip add route '. $row['StationName'] . ' '. $row['VehicleNumber'] );
+				continue;
+			}
+			$start = microtime(true);
+			//不接收所有标签数据的基站,要检查车辆是否已开户
+			if(!$station_info['IsReceiveAll']){
+				$cond = array('DeviceNumber'=>$row['VehicleNumber']);
+				if(!M('jms_vehicle')->where($cond)->getField('UserId')){
+					$end = microtime(true);
+		// 			tcp_log( 'not kaihu , skip add monitor route '. $row['VehicleNumber']);
+					continue;
+				}
+			} 
+			echo '************time1:'.use_microtime($start).PHP_EOL;
+			if($row['IsOnline']){
+				//查看车辆最近一个基站是否这个基站
+				$row['StationType'] = C('基站类型_移动基站');// tablestore
+				$row['CityId'] = $station_info['CityId'];
+				$row['StationId'] = $station_info['StationNumericId']?:$station_info['ID'];
+				$row['StationName'] = $station_info['DeviceName'];
+				$row['Longitude'] = $station_info['Longitude'];
+				$row['Latitude'] = $station_info['Latitude'];
+				$row['Address'] = $station_info['Address'];
+				$row['AddTime'] = $addTime;
+				//如果是被盗状态车辆,轨迹同时保存到固定基站轨迹表
+				$cond = array('DeviceNumber'=>$row['VehicleNumber']);
+				$vehicle_status = M('jms_vehicle')->where($cond)->getField('DeviceStatus');
+				$start = microtime(true);
+				if($vehicle_status == C('车辆状态_被盗')){
+		// 			if( M('jms_sroute')->createAdd($row) ){
+		// 				tcp_log( 'stolen monitor_route add to sroute ok '. $row['StationName'] . ' '. $row['VehicleNumber']);
+		// 			}
+		// 			else{
+		// 				tcp_log( 'stolen monitor_route add to sroute fail '. $row['StationName'] . ' '. $row['VehicleNumber'] . ' sql-' .M('jms_sroute')->getLastSql());
+		// 			}
+		// 			echo '************time2:'.use_microtime($start).PHP_EOL;
+					$row['StationCode'] = $station_info['DeviceNumber'];// tablestore
+					$monitorRows[] = $row; // tablestore
+				} else {
+				    $row['StationCode'] = $station_info['DeviceNumber'];// tablestore
+				    
+				}
+				$mobileRows[] = $row; // tablestore
+		// 		$start = microtime(true);
+		// 		if( M('jms_monitor_sroute')->createAdd($row) )
+		// 			tcp_log( 'add monitor_sroute ok '. $row['StationName'] . ' '. $row['VehicleNumber'] .' OnlineTime: '.$row['OnlineTime'].' ,AddTime: '.$addTime.' ,timestamp: '.$row['time']);
+		// 		else
+		// 			tcp_log( 'add monitor_sroute fail '. $row['StationName'] . ' '. $row['VehicleNumber'] . ' sql-' .M('jms_sroute_tmp')->getLastSql());
+		// 		echo '************time3:'.use_microtime($start).PHP_EOL;
+			}
+			else{
+		// 		tcp_log( '[offline]skip monitor_sroute add route '. $row['StationName'] . ' '. $row['VehicleNumber'] );
+			}
+		}
+		//移动基站的数据全部添加到移动表里面
+		//$this->routeStore->addMonitorRoute($monitorRows); // tablestore
+		$this->routeStore->addMobileRoute($mobileRows); // tablestore
+	}
+	/*
+
+	*/
+	public function index_test(){
+		/*
+		在cli模式下,用手工生成的数据,测试解码程序使用 
+		例子:php index.php decode/index_test '9c bc 01 31 00 ca 00 20 88 88 '
+		*/
+		
+		//$redis_raw_packet = Redis("fdqu_raw_packet","queue");
+		$start_time = time();
+		if (C("是否支持表格存储")) {
+			//C("是否支持表格存储", false);
+			$this->routeStore = new \Jiaruan\RouteStore();// tablestore
+			$this->routeStore->reAddTimeoutInfo();
+		}
+		do{ 
+			//query one record
+			//$packet_text = $redis_raw_packet->pop();
+			
+			global $argv;
+			if (count($argv) != 3 || strpos($argv[2], "9c bc") !==0) { 
+				echo "参数错误";
+				exit();
+			}
+			$packet_text = $argv[2];//'9c bc 01 31 00 cd 00 10 88 88 cd 88 88 ca 01 01 00 5a 54 68 cb 58 00 01';
+			
+			if(!$packet_text){
+				echo 'no raw packet.'.PHP_EOL;
+				sleep(1);
+				break;
+				continue;
+			}
+			//parse packet
+			$packet = tcp_str2bin($packet_text);
+			if(!$packet){
+				echo 'parse packet failed.'.PHP_EOL;
+				decode_error_log('parse_fail','parse packet failed ,packet_text : '.$packet_text);
+				break;
+				continue;
+			}
+			$this->import($packet,$packet_text);
+			break;
+		}while(time()-$start_time<60);
+		
+		
+	}
+	/*
+	参数说明:
+		cityId: 
+		data: 
+
+	*/
+	private function TsAddRawPacket($cityId, $data){
+		$tableName = 'yiwushi_28_rpacket';//\Jiaruan\ClTableStoreGridTp::getCountyTableName($cityId, "rpacket");
+		$model = new OtsModel($tableName);
+		$addTime = strtotime($data['AddTime']);
+		$stationMac = strtoupper(substr(md5($data['StationCode']), 0, 2)) . '-' . $data['StationCode'] . '-' . date('ymd', $addTime);
+		$micro = str_pad(explode(' ',  microtime())[0] * 1000000, 6, 0, STR_PAD_LEFT);
+		$onlineTime = $addTime . "." . $micro;
+		$ret = $model->add(['StationMac' => $stationMac, 'OnlineTime' => $onlineTime, 'PacketData' => $data['PacketData']]);
+		if ($ret == false || $ret != 1) {
+			echo $model->getError();
+			$error = $model->getDbError();
+			if (strpos($error, "cURL error 28:") !== false) {//received超时 记录到日志里面 需要重试
+				
+			} else {
+				
+			}
+			echo $error;
+		}
+	}
+
+}

+ 532 - 0
Home/Lib/Action/RcgjfwIndexAction.class.php

@@ -0,0 +1,532 @@
+<?php
+
+
+
+class RcgjfwIndexAction extends Action {
+			
+
+
+ 
+	public  function index(  ){
+		if (!C('rcgjfw.人车标签_允许的最大时间差')){
+			echo '常量【人车标签_允许的最大时间差】未定义'.PHP_EOL;
+			return;
+		}
+		if (!C('rcgjfw.被盗告警_间隔时间')){
+			echo '常量【被盗告警_间隔时间】未定义'.PHP_EOL;
+			return;
+		}
+		if (!C('rcgjfw.车辆标签_rssi阀值')){
+			echo '常量【车辆标签_rssi阀值】未定义'.PHP_EOL;
+			return;
+		}
+		if (!C('rcgjfw.车主标签_rssi阀值')){
+			echo '常量【车辆标签_rssi阀值】未定义'.PHP_EOL;
+			return;
+		}
+		Vendor('Workerman352.Autoloader');
+		$tcp_worker = new Workerman\Worker("tcp://0.0.0.0:20160");
+		$tcp_worker->count = 1;
+		$tcp_worker->onWorkerStart = function($worker){
+			
+		};
+		$tcp_worker->onConnect = function($connection){
+			$this->onConnect($connection);
+		};
+		$tcp_worker->onMessage = function($connection,$data){
+			$this->onMessage($connection,$data);
+		};
+		$tcp_worker->onClose = function($connection){
+			$this->onClose($connection);
+		};
+		//实例化信号处理类
+		$this->rfid_proto = new Rlgs\Proto\RfidStation2();
+		
+		$this->rchy_alarm_list = Redis("rchy_alarm_list","queue");
+		$this->redis_vsignal = Redis("rchy_vsignal_{no}","zset")->prefix('rchy_vsignal_');
+		//$this->cache = \Rchy\Cache\RedisCache::getInstance();
+		
+		Workerman\Worker::runAll();//保持运行
+		
+	}
+	
+ 
+	private  function onMessage( $connection, $data ){
+		$connection->m_packet .= $data;
+	}
+	
+ 
+	private  function onConnect( $connection ){
+		echo  PHP_EOL . $connection->id . ' connected'. PHP_EOL;
+	}
+	
+ 
+	private  function onClose( $connection ){
+		//记录数据包文本
+		$packet_text = $this->rfid_proto->tcp_hex2str($connection->m_packet,true) ;
+		//$this->rfid_proto->tcp_log($connection->id . ' closed ' . $packet_text);
+		//echo 'packet_text => '.$packet_text.PHP_EOL;
+		//解析消息体
+		$packet = $this->rfid_proto->tcp_str2bin($packet_text);
+		$result = $this->rfid_proto->tcp_unpack_msg($packet);
+		if(! $result['Success'] ){
+			echo 'unpack packet failed'.PHP_EOL;
+			$this->rfid_proto->decode_error_log('unpack_fail','unpack packet failed ,result : '.json_encode($result));
+			return;
+		}
+		//echo 'decode_data => '.json_encode($result).PHP_EOL;
+		//记录基站数据包
+		//$this->rfid_proto->decode_packet_log($result['StationCode'],$result['StationCode'].' => '.$packet_text);
+		if (!$result['StationCode']){
+			echo 'StationCode is empty!'.PHP_EOL;
+			$this->rfid_proto->decode_error_log('unpack_fail','StationCode is empty! ');
+			return false;
+		}
+		
+		if (!$result['VehicleList']){
+			echo 'VehicleList is empty!'.PHP_EOL;
+			return ;
+		}
+		
+		//检查是否有效基站
+		$station_info = \Rchy\Cache\RedisCache::getStationInfo($result['StationCode']);
+		if(!$station_info){
+			$this->rfid_proto->decode_error_log('unpack_fail','getStationInfo failed ,StationCode : '.$result['StationCode']);
+			return false;
+		}
+		
+		//更新车主标签时间
+		echo '======= updateRfidTime start ======='.$connection->id.PHP_EOL;
+		$this->updateRfidTime($result);
+		echo '======= updateRfidTime over ======='.$connection->id.PHP_EOL;
+		//添加报警
+		echo '======= addAlarm start ======='.$connection->id.PHP_EOL;
+		$this->addAlarm($result);
+		echo '======= addAlarm over ======='.$connection->id.PHP_EOL;
+		/*
+		//轨迹包redis主库
+		$redis_raw_packet = Redis("fdqu_raw_packet","queue");
+		$redis_raw_packet->push($packet_text);
+		
+		//数据解包
+		$result = $this->unpackMsg($packet_text);
+		
+		//生成车辆轨迹日志
+		$this->addTcpLog($result);
+		*/
+	}
+	
+ 
+	private  function addTcpLog( $result ){
+		//一个基站一个日志文件
+		$this->rfid_proto->station_route_log('tcp',$result);
+		//一个车辆一个日志文件
+		$this->rfid_proto->vehicle_route_log('tcp',$result);
+		
+	}
+	
+ 
+	private  function unpackMsg( $packet_text ){
+		$packet = $this->rfid_proto->tcp_str2bin($packet_text);
+		if(!$packet){
+			echo 'parse packet failed.'.PHP_EOL;
+			$this->rfid_proto->tcp_error_log('parse_fail','parse packet failed ,packet_text : '.$packet_text);
+			return false;
+		}
+		$result = $this->rfid_proto->tcp_unpack_msg($packet);
+		if(! $result['Success'] ){
+			echo 'unpack fail: ' . $result['Message'].PHP_EOL;
+			$this->rfid_proto->tcp_error_log('unpack_fail',$result);
+			return false;
+		}
+		return $result;
+	}
+	
+ 
+	private  function addAlarm( $result ){
+		//获取当前所有车主rfid的rssi数据
+		$chezhu_rfids = array();
+		foreach ($result['VehicleList'] as $key => $row){
+			$chezhu_info = \Rchy\Cache\RedisCache::getCheZhuInfo($row['VehicleNumber']);
+			if(!$chezhu_info){
+				continue;
+			}
+			$chezhu_rfids[$row['VehicleNumber']]['Rssi'] = $row['rssi'];
+			$chezhu_rfids[$row['VehicleNumber']]['SignalCount'] = $row['SignalCount'];
+		}
+		//var_dump($chezhu_rfids);
+		//var_dump($result['VehicleList']);
+		foreach ($result['VehicleList'] as $key => $row){
+			$row['StationCode'] = $result['StationCode'];
+			if (!$row['VehicleNumber']){
+				echo 'VehicleNumber empty!'.PHP_EOL;
+				continue;
+			}
+		
+			//获取车辆信息
+			$vehicle_info = \Rchy\Cache\RedisCache::getVehicleInfo($row['VehicleNumber']);
+			//车辆不存在跳过
+			if (!$vehicle_info){
+				//echo 'vehicle not existed, vehilce_number = '.$row['VehicleNumber'].', rssi = '.$row['rssi'].PHP_EOL;
+				continue;
+			}
+			//未绑定车主rfid跳过
+			if (!$vehicle_info['CheZhuRfid']){
+				echo 'vehicle not kaihu, vehilce_number: '.$row['VehicleNumber'].', rssi = '.$row['rssi'].PHP_EOL;
+				continue;
+			}
+			echo 'VehicleNumber: '.$row['VehicleNumber'].',rssi = '.$row['rssi'].PHP_EOL;
+		
+		
+			//车辆标签
+			$vehicle_no = strtolower($row['VehicleNumber']);
+			//末次信号(改用本地缓存了)
+			//$last_signal_info = $this->redis_vsignal->table($vehicle_no)->max();
+		
+			//获取最近一段时间内所有rssi,求均值
+			$avgRssi = $this->getAvgRssi($row);
+			if($avgRssi === false){
+				echo 'getAvgRssi failed! VehicleNumber = '.$row['VehicleNumber'].PHP_EOL;
+				continue;
+			}
+			//车辆rssi信号弱的,过滤掉,防止收到车信号的时候,没有收到车主信号,从而误报
+			if ($avgRssi > C('rcgjfw.车辆标签_rssi阀值')){
+				echo 'avgRssi > '.C('rcgjfw.车辆标签_rssi阀值').', avgRssi = '.$avgRssi.', VehicleNumber = '.$row['VehicleNumber'].PHP_EOL;
+				continue;
+			}
+		
+			//有效信号加入到 rchy_vsignal_{no} 表
+			$signal = array(
+				'StationCode' => $row['StationCode'],
+				'Rssi' => $row['rssi'],
+				'Timestamp' => $row['Timestamp']
+			);
+		
+			//获取最近一次的信号信息
+			$last_signal_info = S('last_signal_info'.$row['VehicleNumber']);
+			//更新最近一次的信号信息
+			S('last_signal_info'.$row['VehicleNumber'], $signal, 24*60*60);
+		
+			$alarm_info = array();
+			$alarm_info['VehicleNumber'] = $row['VehicleNumber'];
+			$alarm_info['AlarmTime'] = time();
+			$alarm_info['OnlineTime'] = $row['OnlineTime'];
+			$alarm_info['VehicleRssi'] = $row['rssi'];
+			$alarm_info['VehicleSignalCount'] = $row['SignalCount'];
+			$alarm_info['CheZhuRssi'] = $chezhu_rfids[$vehicle_info['CheZhuRfid']]['Rssi'];//车主或者授权车主信号强度,这里还需要完善
+			$alarm_info['CheZhuSignalCount'] = $chezhu_rfids[$vehicle_info['CheZhuRfid']]['SignalCount'];
+			$alarm_info['StationCode'] = $row['StationCode'];
+			//检查车辆经过时,在限定的时间内,是否有接收到任一授权标签信号
+			$is_stolen = $this->checkVehicleStatus($vehicle_info['CheZhuRfid'], $row);
+			$cur_time = time();
+			//检查是否被盗报警
+			if ( $is_stolen ){
+				$alarm_info['AlarmType'] = \Rchy\Enum\AlarmType::STOLEN;
+				//上次被盗告警时间
+				$latest_stolen_alarm_time = S('latest_stolen_alarm_time_'.$row['VehicleNumber']);
+				//避免频繁告警检测
+				if ($latest_stolen_alarm_time && $cur_time - $latest_stolen_alarm_time <= C('rcgjfw.被盗告警_间隔时间')){	
+					echo '车辆'.$row['VehicleNumber'].'刚被盗告警过,跳过告警! latest_stolen_alarm_time = '.$latest_stolen_alarm_time.', cur_time = '.$cur_time.PHP_EOL;
+					return ;
+				}
+				//被盗告警时,清空过车告警次数
+				S('pass_alarm_count_'.$row['VehicleNumber'], 0, 24*60*60);
+				echo 'VehicleNumber:'.$row['VehicleNumber'].', 告警类型: 被盗告警'.PHP_EOL;
+		
+				//更新本次被盗告警时间
+				S('latest_stolen_alarm_time_'.$row['VehicleNumber'], $cur_time);
+		
+			}else{
+				//获取过车告警次数
+				$pass_alarm_count = S('pass_alarm_count_'.$row['VehicleNumber']);
+				if(!$pass_alarm_count){
+					$pass_alarm_count = 0;
+				}
+				/*
+				//上次告警时间
+				$latest_pass_alarm_time = S('latest_pass_alarm_time_'.$row['VehicleNumber']);
+				//避免频繁告警检测
+				if ($latest_pass_alarm_time && $cur_time - $latest_pass_alarm_time <= C('rcgjfw.被盗告警_间隔时间')){	
+					echo '车辆'.$row['VehicleNumber'].'刚过车告警过,跳过! 已过车告警'.$pass_alarm_count.'次, latest_pass_alarm_time = '.$latest_pass_alarm_time.', cur_time = '.$cur_time.PHP_EOL;
+					return ;
+				}
+		*/
+		
+				$alarm_info['AlarmType'] = \Rchy\Enum\AlarmType::PASS;
+				$old_pass_alarm_count = $pass_alarm_count;
+				$pass_alarm_count++;
+		
+		
+				//告警超过3次,且当前告警时间和上次告警时间差不超过半分钟,不告警
+				if($pass_alarm_count>300000 &&($last_signal_info['Timestamp'] && abs($row['Timestamp']-$last_signal_info['Timestamp']) < 30) ){
+					echo '车辆:'.$row['VehicleNumber'].'已连续过车告警'.$old_pass_alarm_count.'次,不再告警!'.PHP_EOL;
+					return ;
+				}
+		
+				//告警超过3次,且当前告警时间和上次告警时间差超过半分钟,认为是车辆离开后又回来,清空上次告警次数
+				if($pass_alarm_count>300000 &&($last_signal_info['Timestamp'] && abs($row['Timestamp']-$last_signal_info['Timestamp']) >= 30) ){
+					echo '车辆:'.$row['VehicleNumber'].'离开后又回来,清空告警次数'.PHP_EOL;
+					$pass_alarm_count = 1;
+				}
+				echo 'pass_alarm_count = '.$pass_alarm_count.', last_chezhu_signal_time = '.$last_signal_info['Timestamp'].', now = '.$row['Timestamp']. PHP_EOL;
+				//更新过车告警次数
+				S('pass_alarm_count_'.$row['VehicleNumber'], $pass_alarm_count, 24*60*60);
+				echo '车辆:'.$row['VehicleNumber'].'已连续过车告警'.$pass_alarm_count.'次'.PHP_EOL;
+				echo 'VehicleNumber:'.$row['VehicleNumber'].', 告警类型: 过车告警'.PHP_EOL;
+		
+				//记录本次过车告警时间
+				S('latest_pass_alarm_time_'.$row['VehicleNumber'], $cur_time);
+			}
+			$this->rchy_alarm_list->push(json_encode($alarm_info));
+			echo '添加告警成功, VehicleNumber:'.$row['VehicleNumber'].', AlarmType: '.$alarm_info['AlarmType'].PHP_EOL;
+			//echo 'alarm_info: '.json_encode($alarm_info).PHP_EOL;
+		
+		
+		
+		
+		}
+	}
+	
+ 
+	private  function updateRfidTime( $result ){
+		
+		foreach($result['VehicleList'] as $key => $row){
+			$row['StationCode'] = $result['StationCode'];
+			if(!$row['VehicleNumber']){
+				//echo 'VehicleNumber not existed, VehicleNumber: '.$row['VehicleNumber'].PHP_EOL;
+			   continue;
+			}
+			//检查车主标签是否有效,无效跳过,有效的更新上报信息
+			$vehicle_info = \Rchy\Cache\RedisCache::getCheZhuInfo($row['VehicleNumber']);
+			if(!$vehicle_info){
+				continue;
+			}
+			echo 'CheZhuRfid = '.$row['VehicleNumber'].', rssi = '.$row['rssi'].', time:'.date('Y-m-d H:i:s',$row['Timestamp']).PHP_EOL;
+			//如果基站时间不准确,用服务器时间
+			if($row['Timestamp'] < time()-3600){
+				$row['Timestamp'] = time();
+			}
+			//获取最近一段时间内所有rssi,求均值
+			$avgRssi = $this->getAvgRssi($row);
+			if($avgRssi === false){
+				echo 'getAvgRssi failed! CheZhuRfid = '.$row['VehicleNumber'].PHP_EOL;
+				continue;
+			}
+			//检查车主标签rssi,过滤掉弱信号
+			if ($avgRssi > C('rcgjfw.车主标签_rssi阀值')){
+				echo 'avgRssi > '.C('rcgjfw.车主标签_rssi阀值').', avgRssi = '.$avgRssi.', CheZhuRfid = '.$row['VehicleNumber'].PHP_EOL;
+				continue;
+			}
+			echo 'avgRssi = '.$avgRssi.', CheZhuRfid = '.$row['VehicleNumber'].PHP_EOL;
+			
+			$info = array(
+				'Timestamp' => $row['Timestamp'],
+				'StationCode' => $row['StationCode'],
+				'Rssi' => $row['rssi']
+			);
+			//更新车主RFID上报信息
+			 S('last_chezhu_rfid_'.$row['VehicleNumber'], $info, 60*60*24);
+			//echo 'last_chezhu_rfid_'.$row['VehicleNumber'] .PHP_EOL;
+			//var_dump(S('last_chezhu_rfid_'.$row['VehicleNumber']));
+			
+			//echo '更新车主标签上报信息成功,CheZhuRfid: '.$row['VehicleNumber'].', 上报时间:'.date('Y-m-d H:i:s',$row['Timestamp']).', rssi: '.$row['rssi'].PHP_EOL;
+			
+		}
+	}
+	
+ 
+	private  function checkVehicleStatus( $chezhu_rfid, $row ){
+		if(!$chezhu_rfid){
+			echo 'checkVehicleStatus failed, chezhu_rfid is empty!'.PHP_EOL;
+			return false;
+		}
+		if(!$row){
+			echo 'checkVehicleStatus failed, row is empty!'.PHP_EOL;
+			return false;
+		}
+		
+		//获取车辆已授权车主标签
+		$all_rfids = \Rchy\Cache\RedisCache::getVehicleAuthors($row['VehicleNumber']);
+		if(!$all_rfids){
+			$all_rfids = array();
+		}
+		//加入车主标签
+		if(!in_array($chezhu_rfid,$all_rfids)){
+			array_push($all_rfids,$chezhu_rfid);
+		}
+		
+		$is_stolen = true;
+		foreach ($all_rfids as $rfid){
+			//获取授权车主标签上报信息
+			$last_rfid_info = S('last_chezhu_rfid_'.$rfid);
+			//var_dump($last_rfid_info);
+			$last_rfid_time = $last_rfid_info['Timestamp'];
+			$rfid_pass_station = $last_rfid_info['StationCode'];
+			echo 'chezhu_rfid = '.$rfid.', now = '.$row['Timestamp'].', last_rfid_time = '.$last_rfid_time.', rfid_pass_station = '. $rfid_pass_station . PHP_EOL;
+			//车辆和授权车主同时出现在同一个小区门口,则认为是过车告警
+			if ( $last_rfid_time && abs($row['Timestamp']-$last_rfid_time) < C('rcgjfw.人车标签_允许的最大时间差') && $rfid_pass_station == $row['StationCode']){
+				$is_stolen = false;
+				break;
+			}
+		}
+		echo 'all_rfids:'.PHP_EOL;
+		var_dump($all_rfids);
+		
+		return $is_stolen;
+	}
+	
+ 
+	private  function checkVehicleDirection( $vehicle_no, $now ){
+		//开发中。。。
+		return false;
+		if(!$vehicle_no){
+			echo 'checkVehicleDirection failed, vehicle_no is empty!'.PHP_EOL;
+			return false;
+		}
+		if(!$now){
+			echo 'checkVehicleDirection failed, now is empty!'.PHP_EOL;
+			return false;
+		}
+		//获取最近一段时间内的信号
+		$cond = array($now-60,$now);
+		$signal_list = $this->redis_vsignal->table($vehicle_no)->where($cond)->select();
+		//查询结果默认是按时间升序,这里翻转一下
+		$signal_list = array_reverse($signal_list);
+		if(!$signal_list){
+			echo 'checkVehicleDirection failed,signal_list is empty!'.PHP_EOL;
+			return false;
+		}
+		
+		//最后一个信号点所在的基站
+		$last_station = $signal_list[0]['StationCode'];
+		if(!$last_station){
+			echo 'checkVehicleDirection failed,last_station is empty!'.PHP_EOL;
+			return false;
+		}
+		//获取一段时间内同一基站连续的几个信号强度点
+		$rssi_list = array();
+		foreach($signal_list as $key=>$row){
+			$signal = json_decode($key,true);
+			if(!$signal){
+				echo 'json_decode error'.PHP_EOL;
+				return false;
+			}
+			if($last_station != $signal['StationCode']){
+				echo 'is not the last_station rssi,stop push! last_station = '.$last_station.', cur_station = '.$signal['StationCode'].PHP_EOL;
+				break;
+			}
+			$rssi_list[] = $signal['rssi'];
+		}
+		
+		if( count($rssi_list)<3 ){
+			echo 'checkVehicleDirection failed,count($rssi_list)<3 !! count($rssi_list) = '.count($rssi_list).PHP_EOL;
+			return false;
+		}
+		
+		//<信号一直递增或者一直递减的,车辆方向暂时不好判断>
+		//判断车辆rssi信号强度是否递减(此时,时间是降序)
+		$is_rssi_decrease = $this->isRssiDecrease($rssi_list, count($rssi_list));
+		if($is_rssi_decrease){
+			echo '检测到车辆rssi信号强度递增,车辆正在靠近基站!StationCode = '.$last_station.PHP_EOL;
+			return false;
+		}
+		//如果rssi先逐渐增强,后逐渐减弱,且峰值右侧第一个信号大于峰值左侧第一个信号,则认为是进入小区
+		
+		//如果rssi先逐渐增强,后逐渐减弱,且峰值右侧第一个信号小于峰值左侧第一个信号,则认为是走出小区
+		
+		echo '判断不出车辆运动方向!VehicleNumber = '. $vehicle_no .PHP_EOL;
+		return false;
+	}
+	
+ 
+	private  function isRssiDecrease( $arr, $len ){
+		if ($len == 1)
+		    return true;
+		
+		return ($arr[$len - 2] >= $arr[$len - 1]) && $this->isRssiDecrease($arr, $len-1);
+	}
+	
+ 
+	private  function isRssiDrab( $arr, $len ){
+		if(!$arr){
+			echo 'isRssiDrab failed, arr is empty!!'.PHP_EOL;
+			return false;
+		}
+		
+		if(!$len){
+			echo 'isRssiDrab failed, len is empty!!'.PHP_EOL;
+			return false;
+		}
+		
+		if($this->isRssiIncrease($arr, $len)){
+			echo 'rssi increase'.PHP_EOL;
+			return true;
+		}
+		
+		if($this->isRssiDecrease($arr, $len)){
+			echo 'rssi decrease'.PHP_EOL;
+			return true;
+		}
+		
+		echo 'rssi is not drab '.PHP_EOL;
+		return false;
+	}
+	
+ 
+	private  function isRssiIncrease( $arr, $len ){
+		if ($len == 1)
+		    return true;
+		
+		return ($arr[$len - 2] <= $arr[$len - 1]) && $this->isRssiIncrease($arr, $len-1);
+	}
+	
+ 
+	private  function getAvgRssi( $data ){
+		$rfid = $data['VehicleNumber'];
+		if(!$rfid){
+			echo 'getAvgRssi failed ,VehicleNumber not existed!'.PHP_EOL;
+			return false;
+		}
+		$station_code = $data['StationCode'];
+		if(!$station_code){
+			echo 'getAvgRssi failed ,StationCode not existed!'.PHP_EOL;
+			return false;
+		}
+		
+		$key = $station_code.'_'.$rfid;
+		//添加新车辆轨迹点
+		$route_point = array('Rfid'=>$rfid,'Time'=>$data['Timestamp'],'Rssi'=>$data['rssi']);
+		$signal_data = array(json_encode($route_point) => $data['Timestamp']);
+		$this->redis_vsignal->table($key)->add($signal_data);
+		//查询时间窗内,所有rssi
+		$interval = C('rcgjfw.信号均值_采集时间窗');
+		if(!$interval){
+			$interval = 4;
+		}
+		$start = $data['Timestamp']-$interval;
+		$where = array( $start,$data['Timestamp']);
+		$list = $this->redis_vsignal->table($key)->where($where)->select();
+		
+		//计算平均值
+		$sum = 0;
+		$count = 0;
+		$info = array();
+		foreach($list as $signal => $time){
+			$signal = json_decode($signal,true);
+			$sum += $signal['Rssi'];
+			$count++;
+			$info[$time] = $signal['Rssi'];
+		}
+		echo 'key:'.$key.', rssi_pool:'.json_encode($info).PHP_EOL;
+		
+		if(!$count){
+			echo 'getAvgRssi failed ,rssi_pool empty!'.PHP_EOL;
+			return false;
+		}
+		$avgRssi = round($sum/$count);
+		echo 'rfid: '.$rfid.', station_code: '.$station_code.', avgRssi = '.$avgRssi.PHP_EOL;
+		return $avgRssi;
+	}
+	
+
+}

+ 503 - 0
Home/Lib/Action/RcgjfwXqIndexAction.class.php

@@ -0,0 +1,503 @@
+<?php
+
+
+
+class RcgjfwXqIndexAction extends Action {
+			
+
+
+ 
+	public  function index(  ){
+		if (!C('rcgjfw.人车标签_允许的最大时间差')){
+			echo '常量【人车标签_允许的最大时间差】未定义'.PHP_EOL;
+			return;
+		}
+		if (!C('rcgjfw.被盗告警_间隔时间')){
+			echo '常量【被盗告警_间隔时间】未定义'.PHP_EOL;
+			return;
+		}
+		if (!C('rcgjfw.车辆标签_rssi阀值')){
+			echo '常量【车辆标签_rssi阀值】未定义'.PHP_EOL;
+			return;
+		}
+		if (!C('rcgjfw.车主标签_rssi阀值')){
+			echo '常量【车辆标签_rssi阀值】未定义'.PHP_EOL;
+			return;
+		}
+		Vendor('Workerman352.Autoloader');
+		$tcp_worker = new Workerman\Worker("tcp://0.0.0.0:20160");
+		$tcp_worker->count = 1;
+		$tcp_worker->onWorkerStart = function($worker){
+			
+		};
+		$tcp_worker->onConnect = function($connection){
+			$this->onConnect($connection);
+		};
+		$tcp_worker->onMessage = function($connection,$data){
+			$this->onMessage($connection,$data);
+		};
+		$tcp_worker->onClose = function($connection){
+			$this->onClose($connection);
+		};
+		//实例化信号处理类
+		$this->rfid_proto = new Rlgs\Proto\RfidStation2();
+		
+		$this->rchy_alarm_list = Redis("rchy_alarm_list","queue");
+		$this->redis_vsignal = Redis("rchy_vsignal_{no}","zset")->prefix('rchy_vsignal_');
+		//$this->cache = \Rchy\Cache\RedisCache::getInstance();
+		
+		Workerman\Worker::runAll();//保持运行
+		
+	}
+	
+ 
+	private  function onMessage( $connection, $data ){
+		$connection->m_packet .= $data;
+	}
+	
+ 
+	private  function onConnect( $connection ){
+		echo  PHP_EOL . $connection->id . ' connected'. PHP_EOL;
+	}
+	
+ 
+	private  function onClose( $connection ){
+		//记录数据包文本
+		$packet_text = $this->rfid_proto->tcp_hex2str($connection->m_packet,true) ;
+		//$this->rfid_proto->tcp_log($connection->id . ' closed ' . $packet_text);
+		//echo 'packet_text => '.$packet_text.PHP_EOL;
+		//解析消息体
+		$packet = $this->rfid_proto->tcp_str2bin($packet_text);
+		$result = $this->rfid_proto->tcp_unpack_msg($packet);
+		if(! $result['Success'] ){
+			echo 'unpack packet failed'.PHP_EOL;
+			$this->rfid_proto->decode_error_log('unpack_fail','unpack packet failed ,result : '.json_encode($result));
+			return;
+		}
+		//echo 'decode_data => '.json_encode($result).PHP_EOL;
+		//记录基站数据包
+		//$this->rfid_proto->decode_packet_log($result['StationCode'],$result['StationCode'].' => '.$packet_text);
+		if (!$result['StationCode']){
+			echo 'StationCode is empty!'.PHP_EOL;
+			$this->rfid_proto->decode_error_log('unpack_fail','StationCode is empty! ');
+			return false;
+		}
+		
+		if (!$result['VehicleList']){
+			echo 'VehicleList is empty!'.PHP_EOL;
+			return ;
+		}
+		
+		//检查是否有效基站
+		$station_info = \Rchy\Cache\RedisCache::getStationInfo($result['StationCode']);
+		if(!$station_info){
+			$this->rfid_proto->decode_error_log('unpack_fail','getStationInfo failed ,StationCode : '.$result['StationCode']);
+			return false;
+		}
+		
+		//更新车主标签时间
+		echo '======= updateRfidTime start ======='.$connection->id.PHP_EOL;
+		$this->updateRfidTime($result);
+		echo '======= updateRfidTime over ======='.$connection->id.PHP_EOL;
+		//添加报警
+		echo '======= addAlarm start ======='.$connection->id.PHP_EOL;
+		$this->addAlarm($result);
+		echo '======= addAlarm over ======='.$connection->id.PHP_EOL;
+		/*
+		//轨迹包redis主库
+		$redis_raw_packet = Redis("fdqu_raw_packet","queue");
+		$redis_raw_packet->push($packet_text);
+		
+		//数据解包
+		$result = $this->unpackMsg($packet_text);
+		
+		//生成车辆轨迹日志
+		$this->addTcpLog($result);
+		*/
+	}
+	
+ 
+	private  function addTcpLog( $result ){
+		//一个基站一个日志文件
+		$this->rfid_proto->station_route_log('tcp',$result);
+		//一个车辆一个日志文件
+		$this->rfid_proto->vehicle_route_log('tcp',$result);
+		
+	}
+	
+ 
+	private  function unpackMsg( $packet_text ){
+		$packet = $this->rfid_proto->tcp_str2bin($packet_text);
+		if(!$packet){
+			echo 'parse packet failed.'.PHP_EOL;
+			$this->rfid_proto->tcp_error_log('parse_fail','parse packet failed ,packet_text : '.$packet_text);
+			return false;
+		}
+		$result = $this->rfid_proto->tcp_unpack_msg($packet);
+		if(! $result['Success'] ){
+			echo 'unpack fail: ' . $result['Message'].PHP_EOL;
+			$this->rfid_proto->tcp_error_log('unpack_fail',$result);
+			return false;
+		}
+		return $result;
+	}
+	
+ 
+	private  function addAlarm( $result ){
+		//获取当前所有车主rfid的rssi数据
+		$chezhu_rfids = array();
+		foreach ($result['VehicleList'] as $key => $row){
+			$chezhu_info = \Rchy\Cache\RedisCache::getCheZhuInfo($row['VehicleNumber']);
+			if(!$chezhu_info){
+				continue;
+			}
+			$chezhu_rfids[$row['VehicleNumber']]['Rssi'] = $row['rssi'];
+			$chezhu_rfids[$row['VehicleNumber']]['SignalCount'] = $row['SignalCount'];
+		}
+		//var_dump($result['VehicleList']);
+		foreach ($result['VehicleList'] as $key => $row){
+			$row['StationCode'] = $result['StationCode'];
+			if (!$row['VehicleNumber']){
+				echo 'VehicleNumber empty!'.PHP_EOL;
+				continue;
+			}
+		
+			//获取车辆信息
+			$vehicle_info = \Rchy\Cache\RedisCache::getVehicleInfo($row['VehicleNumber']);
+			//车辆不存在跳过
+			if (!$vehicle_info){
+				//echo 'vehicle not existed, vehilce_number = '.$row['VehicleNumber'].', rssi = '.$row['rssi'].PHP_EOL;
+				continue;
+			}
+			//未绑定车主rfid跳过
+			if (!$vehicle_info['CheZhuRfid']){
+				echo 'vehicle not kaihu, vehilce_number: '.$row['VehicleNumber'].', rssi = '.$row['rssi'].PHP_EOL;
+				continue;
+			}
+			echo 'VehicleNumber: '.$row['VehicleNumber'].',rssi = '.$row['rssi'].PHP_EOL;
+		
+		
+			//车辆标签
+			$vehicle_no = strtolower($row['VehicleNumber']);
+			//末次信号(改用本地缓存了)
+			//$last_signal_info = $this->redis_vsignal->table($vehicle_no)->max();
+		
+			//获取最近一段时间内所有rssi,求均值
+			$avgRssi = $this->getAvgRssi($row);
+			if($avgRssi === false){
+				echo 'getAvgRssi failed! VehicleNumber = '.$row['VehicleNumber'].PHP_EOL;
+				continue;
+			}
+			//车辆rssi信号弱的,过滤掉,防止收到车信号的时候,没有收到车主信号,从而误报
+			if ($avgRssi > C('rcgjfw.车辆标签_rssi阀值')){
+				echo 'avgRssi > '.C('rcgjfw.车辆标签_rssi阀值').', avgRssi = '.$avgRssi.', VehicleNumber = '.$row['VehicleNumber'].PHP_EOL;
+				continue;
+			}
+		
+			//有效信号加入到 rchy_vsignal_{no} 表
+			$signal = array(
+				'StationCode' => $row['StationCode'],
+				'Rssi' => $row['rssi'],
+				'Timestamp' => $row['Timestamp']
+			);
+		
+			//获取最近一次的信号信息
+			$last_signal_info = S('last_signal_info'.$row['VehicleNumber']);
+			//更新最近一次的信号信息
+			S('last_signal_info'.$row['VehicleNumber'], $signal, 24*60*60);
+		
+			$alarm_info = array();
+			$alarm_info['VehicleNumber'] = $row['VehicleNumber'];
+			$alarm_info['AlarmTime'] = time();
+			$alarm_info['OnlineTime'] = $row['OnlineTime'];
+			$alarm_info['VehicleRssi'] = $row['rssi'];
+			$alarm_info['VehicleSignalCount'] = $row['SignalCount'];
+			$alarm_info['CheZhuRssi'] = $chezhu_rfids[$vehicle_info['CheZhuRfid']]['Rssi'];//车主或者授权车主信号强度,这里还需要完善
+			$alarm_info['CheZhuSignalCount'] = $chezhu_rfids[$vehicle_info['CheZhuRfid']]['SignalCount'];
+			$alarm_info['StationCode'] = $row['StationCode'];
+			//检查车辆经过时,在限定的时间内,是否有接收到任一授权标签信号
+			$is_stolen = $this->checkVehicleStatus($vehicle_info['CheZhuRfid'], $row);
+			$cur_time = time();
+			//检查是否被盗报警
+			if ( $is_stolen ){
+				$alarm_info['AlarmType'] = \Rchy\Enum\AlarmType::STOLEN;
+				//上次被盗告警时间
+				$latest_stolen_alarm_time = S('latest_stolen_alarm_time_'.$row['VehicleNumber']);
+				//避免频繁告警检测
+				if ($latest_stolen_alarm_time && $cur_time - $latest_stolen_alarm_time <= C('rcgjfw.被盗告警_间隔时间')){	
+					echo '车辆'.$row['VehicleNumber'].'刚被盗告警过,跳过告警! latest_stolen_alarm_time = '.$latest_stolen_alarm_time.', cur_time = '.$cur_time.PHP_EOL;
+					return ;
+				}
+				echo 'VehicleNumber:'.$row['VehicleNumber'].', 告警类型: 被盗告警'.PHP_EOL;
+		
+				//更新本次被盗告警时间
+				S('latest_stolen_alarm_time_'.$row['VehicleNumber'], $cur_time);
+		
+			}else{
+				
+				//上次告警时间
+				$latest_pass_alarm_time = S('latest_pass_alarm_time_'.$row['VehicleNumber']);
+				//避免频繁告警检测
+				if ($latest_pass_alarm_time && $cur_time - $latest_pass_alarm_time <= C('rcgjfw.过车告警_间隔时间')){	
+					echo '车辆'.$row['VehicleNumber'].'刚过车告警过,跳过! 已过车告警'.$pass_alarm_count.'次, latest_pass_alarm_time = '.$latest_pass_alarm_time.', cur_time = '.$cur_time.PHP_EOL;
+					return ;
+				}
+				$alarm_info['AlarmType'] = \Rchy\Enum\AlarmType::PASS;
+				echo 'pass_alarm_count = '.$pass_alarm_count.', last_chezhu_signal_time = '.$last_signal_info['Timestamp'].', now = '.$row['Timestamp']. PHP_EOL;
+				echo 'VehicleNumber:'.$row['VehicleNumber'].', 告警类型: 过车告警'.PHP_EOL;
+				//记录本次过车告警时间
+				S('latest_pass_alarm_time_'.$row['VehicleNumber'], $cur_time);
+			}
+			$this->rchy_alarm_list->push(json_encode($alarm_info));
+			echo '添加告警成功, VehicleNumber:'.$row['VehicleNumber'].', AlarmType: '.$alarm_info['AlarmType'].PHP_EOL;
+			//echo 'alarm_info: '.json_encode($alarm_info).PHP_EOL;
+		
+		
+		
+		
+		}
+	}
+	
+ 
+	private  function updateRfidTime( $result ){
+		
+		foreach($result['VehicleList'] as $key => $row){
+			$row['StationCode'] = $result['StationCode'];
+			if(!$row['VehicleNumber']){
+				//echo 'VehicleNumber not existed, VehicleNumber: '.$row['VehicleNumber'].PHP_EOL;
+			   continue;
+			}
+			//检查车主标签是否有效,无效跳过,有效的更新上报信息
+			$vehicle_info = \Rchy\Cache\RedisCache::getCheZhuInfo($row['VehicleNumber']);
+			if(!$vehicle_info){
+				continue;
+			}
+			echo 'CheZhuRfid = '.$row['VehicleNumber'].', rssi = '.$row['rssi'].', time:'.date('Y-m-d H:i:s',$row['Timestamp']).PHP_EOL;
+			//如果基站时间不准确,用服务器时间
+			if($row['Timestamp'] < time()-3600){
+				$row['Timestamp'] = time();
+			}
+			//获取最近一段时间内所有rssi,求均值
+			$avgRssi = $this->getAvgRssi($row);
+			if($avgRssi === false){
+				echo 'getAvgRssi failed! CheZhuRfid = '.$row['VehicleNumber'].PHP_EOL;
+				continue;
+			}
+			//检查车主标签rssi,过滤掉弱信号
+			if ($avgRssi > C('rcgjfw.车主标签_rssi阀值')){
+				echo 'avgRssi > '.C('rcgjfw.车主标签_rssi阀值').', avgRssi = '.$avgRssi.', CheZhuRfid = '.$row['VehicleNumber'].PHP_EOL;
+				continue;
+			}
+			echo 'avgRssi = '.$avgRssi.', CheZhuRfid = '.$row['VehicleNumber'].PHP_EOL;
+			
+			$info = array(
+				'Timestamp' => $row['Timestamp'],
+				'StationCode' => $row['StationCode'],
+				'Rssi' => $row['rssi']
+			);
+			//更新车主RFID上报信息
+			 S('last_chezhu_rfid_'.$row['VehicleNumber'], $info, 60*60*24);
+			//echo 'last_chezhu_rfid_'.$row['VehicleNumber'] .PHP_EOL;
+			//var_dump(S('last_chezhu_rfid_'.$row['VehicleNumber']));
+			
+			//echo '更新车主标签上报信息成功,CheZhuRfid: '.$row['VehicleNumber'].', 上报时间:'.date('Y-m-d H:i:s',$row['Timestamp']).', rssi: '.$row['rssi'].PHP_EOL;
+			
+		}
+	}
+	
+ 
+	private  function checkVehicleStatus( $chezhu_rfid, $row ){
+		if(!$chezhu_rfid){
+			echo 'checkVehicleStatus failed, chezhu_rfid is empty!'.PHP_EOL;
+			return false;
+		}
+		if(!$row){
+			echo 'checkVehicleStatus failed, row is empty!'.PHP_EOL;
+			return false;
+		}
+		
+		//获取车辆已授权车主标签
+		$all_rfids = \Rchy\Cache\RedisCache::getVehicleAuthors($row['VehicleNumber']);
+		if(!$all_rfids){
+			$all_rfids = array();
+		}
+		//加入车主标签
+		if(!in_array($chezhu_rfid,$all_rfids)){
+			array_push($all_rfids,$chezhu_rfid);
+		}
+		
+		$is_stolen = true;
+		foreach ($all_rfids as $rfid){
+			//获取授权车主标签上报信息
+			$last_rfid_info = S('last_chezhu_rfid_'.$rfid);
+			//var_dump($last_rfid_info);
+			$last_rfid_time = $last_rfid_info['Timestamp'];
+			$rfid_pass_station = $last_rfid_info['StationCode'];
+			echo 'chezhu_rfid = '.$rfid.', now = '.$row['Timestamp'].', last_rfid_time = '.$last_rfid_time.', rfid_pass_station = '. $rfid_pass_station . PHP_EOL;
+			//车辆和授权车主同时出现在同一个小区门口,则认为是过车告警
+			if ( $last_rfid_time && abs($row['Timestamp']-$last_rfid_time) < C('rcgjfw.人车标签_允许的最大时间差') && $rfid_pass_station == $row['StationCode']){
+				$is_stolen = false;
+				break;
+			}
+		}
+		echo 'all_rfids:'.PHP_EOL;
+		var_dump($all_rfids);
+		
+		return $is_stolen;
+	}
+	
+ 
+	private  function checkVehicleDirection( $vehicle_no, $now ){
+		//开发中。。。
+		return false;
+		if(!$vehicle_no){
+			echo 'checkVehicleDirection failed, vehicle_no is empty!'.PHP_EOL;
+			return false;
+		}
+		if(!$now){
+			echo 'checkVehicleDirection failed, now is empty!'.PHP_EOL;
+			return false;
+		}
+		//获取最近一段时间内的信号
+		$cond = array($now-60,$now);
+		$signal_list = $this->redis_vsignal->table($vehicle_no)->where($cond)->select();
+		//查询结果默认是按时间升序,这里翻转一下
+		$signal_list = array_reverse($signal_list);
+		if(!$signal_list){
+			echo 'checkVehicleDirection failed,signal_list is empty!'.PHP_EOL;
+			return false;
+		}
+		
+		//最后一个信号点所在的基站
+		$last_station = $signal_list[0]['StationCode'];
+		if(!$last_station){
+			echo 'checkVehicleDirection failed,last_station is empty!'.PHP_EOL;
+			return false;
+		}
+		//获取一段时间内同一基站连续的几个信号强度点
+		$rssi_list = array();
+		foreach($signal_list as $key=>$row){
+			$signal = json_decode($key,true);
+			if(!$signal){
+				echo 'json_decode error'.PHP_EOL;
+				return false;
+			}
+			if($last_station != $signal['StationCode']){
+				echo 'is not the last_station rssi,stop push! last_station = '.$last_station.', cur_station = '.$signal['StationCode'].PHP_EOL;
+				break;
+			}
+			$rssi_list[] = $signal['rssi'];
+		}
+		
+		if( count($rssi_list)<3 ){
+			echo 'checkVehicleDirection failed,count($rssi_list)<3 !! count($rssi_list) = '.count($rssi_list).PHP_EOL;
+			return false;
+		}
+		
+		//<信号一直递增或者一直递减的,车辆方向暂时不好判断>
+		//判断车辆rssi信号强度是否递减(此时,时间是降序)
+		$is_rssi_decrease = $this->isRssiDecrease($rssi_list, count($rssi_list));
+		if($is_rssi_decrease){
+			echo '检测到车辆rssi信号强度递增,车辆正在靠近基站!StationCode = '.$last_station.PHP_EOL;
+			return false;
+		}
+		//如果rssi先逐渐增强,后逐渐减弱,且峰值右侧第一个信号大于峰值左侧第一个信号,则认为是进入小区
+		
+		//如果rssi先逐渐增强,后逐渐减弱,且峰值右侧第一个信号小于峰值左侧第一个信号,则认为是走出小区
+		
+		echo '判断不出车辆运动方向!VehicleNumber = '. $vehicle_no .PHP_EOL;
+		return false;
+	}
+	
+ 
+	private  function isRssiDecrease( $arr, $len ){
+		if ($len == 1)
+		    return true;
+		
+		return ($arr[$len - 2] >= $arr[$len - 1]) && $this->isRssiDecrease($arr, $len-1);
+	}
+	
+ 
+	private  function isRssiDrab( $arr, $len ){
+		if(!$arr){
+			echo 'isRssiDrab failed, arr is empty!!'.PHP_EOL;
+			return false;
+		}
+		
+		if(!$len){
+			echo 'isRssiDrab failed, len is empty!!'.PHP_EOL;
+			return false;
+		}
+		
+		if($this->isRssiIncrease($arr, $len)){
+			echo 'rssi increase'.PHP_EOL;
+			return true;
+		}
+		
+		if($this->isRssiDecrease($arr, $len)){
+			echo 'rssi decrease'.PHP_EOL;
+			return true;
+		}
+		
+		echo 'rssi is not drab '.PHP_EOL;
+		return false;
+	}
+	
+ 
+	private  function isRssiIncrease( $arr, $len ){
+		if ($len == 1)
+		    return true;
+		
+		return ($arr[$len - 2] <= $arr[$len - 1]) && $this->isRssiIncrease($arr, $len-1);
+	}
+	
+ 
+	private  function getAvgRssi( $data ){
+		$rfid = $data['VehicleNumber'];
+		if(!$rfid){
+			echo 'getAvgRssi failed ,VehicleNumber not existed!'.PHP_EOL;
+			return false;
+		}
+		$station_code = $data['StationCode'];
+		if(!$station_code){
+			echo 'getAvgRssi failed ,StationCode not existed!'.PHP_EOL;
+			return false;
+		}
+		
+		$key = $station_code.'_'.$rfid;
+		//添加新车辆轨迹点
+		$route_point = array('Rfid'=>$rfid,'Time'=>$data['Timestamp'],'Rssi'=>$data['rssi']);
+		$signal_data = array(json_encode($route_point) => $data['Timestamp']);
+		$this->redis_vsignal->table($key)->add($signal_data);
+		//查询时间窗内,所有rssi
+		$interval = C('rcgjfw.信号均值_采集时间窗');
+		if(!$interval){
+			$interval = 4;
+		}
+		$start = $data['Timestamp']-$interval;
+		$where = array( $start,$data['Timestamp']);
+		$list = $this->redis_vsignal->table($key)->where($where)->select();
+		
+		//计算平均值
+		$sum = 0;
+		$count = 0;
+		$info = array();
+		foreach($list as $signal => $time){
+			$signal = json_decode($signal,true);
+			$sum += $signal['Rssi'];
+			$count++;
+			$info[$time] = $signal['Rssi'];
+		}
+		echo 'key:'.$key.', rssi_pool:'.json_encode($info).PHP_EOL;
+		
+		if(!$count){
+			echo 'getAvgRssi failed ,rssi_pool empty!'.PHP_EOL;
+			return false;
+		}
+		$avgRssi = round($sum/$count);
+		echo 'rfid: '.$rfid.', station_code: '.$station_code.', avgRssi = '.$avgRssi.PHP_EOL;
+		return $avgRssi;
+	}
+	
+
+}

+ 118 - 0
Home/Lib/Class/ClientSend.php

@@ -0,0 +1,118 @@
+<?php
+namespace Jiaruan;
+
+class ClientSend {
+			
+
+	private $host ;
+	
+	private $port ;
+	
+	private $socket ;
+	
+	private $error ;
+	
+	private $result ;
+	
+
+ 
+	public  function __construct( $host, $port ){
+		$test = '';
+		$this->host = $host;
+		$this->port = $port;
+	}
+	
+ 
+	public  function send( $msg ){
+		$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
+		if ($this->socket === FALSE) {
+			return array('success' => false,
+						 'message' => "socket创建失败: " . socket_strerror(socket_last_error()));
+		}
+		socket_set_option($this->socket, SOL_SOCKET, SO_SNDTIMEO, array('sec' => 3, 'usec' => 0));
+		socket_set_option($this->socket, SOL_SOCKET, SO_RCVTIMEO, array('sec' => 3, 'usec' => 0));
+		
+		if (!$this->conn()) {
+			socket_close($this->socket);
+			return array('success' => false, 'message' => $this->error);
+		}
+		
+		if (!$this->wri($msg . chr(0), strlen($msg) + 1)) {
+			socket_close($this->socket);
+			return array('success' => false, 'message' => $this->error);
+		}
+		if (!$this->red()) {
+			socket_close($this->socket);
+			return array('success' => false, 'message' => $this->error);
+		}
+		$ret = json_decode($this->result, true);
+		if (is_array($ret))
+			return $ret;
+		return array('success' => false, 'message' => '返回数据格式错误:' .$this->result);
+	}
+	
+ 
+	private  function conn(  ){
+		$timeout = 5;
+		socket_set_nonblock($this->socket);
+		$time = time();
+		while (!@socket_connect($this->socket, $this->host, $this->port)) {
+			$err = socket_last_error($this->socket);
+			if($err === 56) {
+				//echo 'connected ok';
+				break;
+			}
+			if ((time() - $time) >= $timeout) {
+				$this->error = '连接超时';
+				return false;
+			}
+			usleep(250000);
+		}
+		socket_set_block($this->socket);
+		return true;
+	}
+	
+ 
+	private  function wri( $msg, $length ){
+		while(true) {
+			$sent = socket_write($this->socket, $msg, $length);
+			if($sent === false) {
+				$this->error = "发送失败" . socket_strerror(socket_last_error());;
+				return false;
+			}
+			if($sent < $length) {
+				$msg = substr($msg, $sent);
+				$length -= $sent;
+				//echo "这些字节没法送 :$msg:重新发送";
+				$this->error = "发送失败,信息发送不完整";
+				return false;
+			} else {
+				return true;
+			}
+		}
+	}
+	
+ 
+	private  function red(  ){
+		socket_clear_error();
+		while (true) {
+			$out = socket_read($this->socket, 2048);
+		
+			if ($out == false)
+				break;
+			$this->result .= $out;
+		}
+		$errorCode = socket_last_error($this->socket);
+		if ($errorCode == 115 || $errorCode == 114 || $errorCode == 104) {
+			//            echo "\n连接关闭\n";
+			return true;
+		} else if ($errorCode == 11) { //读取超时
+			$this->error = "读取超时";
+		} else {
+			$this->error = "读取失败:" . socket_strerror($errorCode);
+		}
+		return false;
+	}
+	
+
+}

+ 142 - 0
Home/Lib/Class/ConnectionManager.php

@@ -0,0 +1,142 @@
+<?php
+namespace jiaruan;
+
+class ConnectionManager {
+			
+
+	private $connections = array();
+	
+	private $city_stations = array();
+	
+
+ 
+	public  function add( $mac, $connection ){
+		//获取基站城市id
+		//$cityid = get_station_cityid($mac);
+		//log_debug('cityid:'. $cityid . '-' . $mac);
+		$cityid=1;
+		//添加在所有链接变量
+		$connection->online_time = time();
+		$connection->cityid = $cityid;
+		$connection->station_mac = $mac;
+		$this->connections[$mac] = $connection;
+		
+		//更新到县区的基站信息
+		$this->city_stations[$cityid][$mac] = 1;
+		
+	}
+	
+ 
+	public  function remove( $connection ){
+		//获取连接
+		$mac = $connection->station_mac;
+		$cityid = $connection->cityid;
+		
+		//从城市基站中移除该基站
+		unset( $this->city_stations[$cityid][$mac]);
+		
+		//从所有连接中移除连接
+		unset( $this->connections[$mac]);
+		
+	}
+	
+ 
+	public  function heartbeat( $connection ){
+		//添加在所有链接变量
+		$connection->online_time = time();
+		$mac = $connection->station_mac;
+		$cityid = $connection->cityid;
+		$this->connections[$mac] = $connection;
+		
+		//更新到县区的基站信息
+		$this->city_stations[$cityid][$mac] = 1;
+		
+	}
+	
+ 
+	public  function send_one( $mac, $cmd ){
+		//检查长连接是否存在
+		$conn =  $this->connections[$mac];
+		if (!$conn) {
+			log_debug('基站mac不在线:' . $mac);
+			return array('success'=>false, 'message'=>"基站不在线");
+		}
+		$cmd['mac'] = $conn->station_mac;
+		//发送命令
+		if (false === $conn->send(json_encode($cmd))) {
+			log_debug('发送命令失败基站mac:' . $mac.json_encode($cmd));
+			return array('success'=>false, 'message'=>"命令发送失败,基站不在线");
+		} else {
+			log_debug('发送命令成功基站mac:' . $mac.json_encode($cmd));
+			return array('success'=>true, 'message'=>"命令发送已成功");
+		}
+		
+	}
+	
+ 
+	public  function send_city( $cityid, $cmd ){
+		//遍历所有基站并发送命令
+		$stations = array_keys($this->city_stations[$cityid]);
+		if (count($stations) == 0) {
+			log_debug('当前城市的基站是0');
+			return array('success' => true, 'message' => '当前城市没有在线基站');
+		}
+		$fails = [];
+		foreach($stations as $mac){
+			$cmd['mac'] = $mac;
+			$conn = $this->connections[$mac];
+			if($conn){
+				$ret = $conn->send(json_encode($cmd));
+				if ($ret === false){
+					log_debug('所在城市发送命令失败,基站mac: ' . $mac . json_encode($cmd) );
+					$fails[] = $mac;
+				}
+				else {
+					log_debug('所在城市发送命令成功,基站mac: ' . $mac . json_encode($cmd) );
+				}
+			}
+			else{
+				$fails[] = $mac;
+				log_debug('找不到基站长连接,基站mac: ' . $mac . json_encode($cmd) );
+			}
+		}
+		if (count($fails) != 0)
+			return array('success'=>false, 'message'=>'发送命令失败的基站mac:' . json_encode($fails));
+		else
+			return array('success'=>true, 'message'=>'发送命令成功');
+	}
+	
+ 
+	public  function send_multi( $macs ){
+		foreach($macs as $mac){
+			//检查长连接是否存在
+			$conn =  $this->connections[$mac];
+			if ($conn) {
+				$cmd['mac'] = $mac;
+				$result = $conn->send(json_encode($cmd));
+			}
+			else{
+				log_debug('找不到基站长连接,基站mac: ' . $mac . json_encode($cmd) );
+			}
+		}
+		return array('success'=>true, 'message'=>"命令发送已成功");
+	}
+	
+ 
+	public  function send_all( $cmd ){
+		$fails = array();
+		foreach ($this->connections as $conn) {
+			$cmd['msgid'] = $conn->request->msgid;
+			$cmd['mac'] = $conn->request->mac;
+			if (false === $conn->send(json_encode($cmd))) {
+				$fails[] = $conn->request->mac;
+				log_debug('发送升级命令失败基站mac:' . $conn->request->mac);
+			} else {
+				log_debug('发送升级命令成功基站mac:' . json_encode($cmd));
+			}
+		}
+		return array('success'=>true, 'message'=>"命令发送已成功,失败的为" . json_encode($fails));
+	}
+	
+
+}

+ 61 - 0
Home/Lib/Class/EventLog.php

@@ -0,0 +1,61 @@
+<?php
+namespace Jiaruan;
+
+class EventLog {
+			
+
+
+ 
+	public  function onLoginFailed( $req, $resp, $errorMsg ){
+		log_debug("onLoginFailed" . $req->mac);
+	}
+	
+ 
+	public  function onLoginSuccess( $req, $resp ){
+		$req->server_time = time();
+		$req->device_time = time();
+		$msg =  '['.$req->mac.'] onLoginSuccess, time: '.date('Y-m-d H:i:s', $req->server_time);
+		log_debug($msg);
+	}
+	
+ 
+	public  function onMonitSuccess(  ){
+		
+	}
+	
+ 
+	public  function onMonitFailed(  ){
+		
+	}
+	
+ 
+	public  function onUnmonitSuccess(  ){
+		
+	}
+	
+ 
+	public  function onUnmonitFailed(  ){
+		
+	}
+	
+ 
+	public  function onHeartbeatSuccess( $req, $resp ){
+		$req->server_time = time();
+		$req->device_time = $req->realtime;
+		echo '['.$req->mac.'] heartbeat, time: '.date('Y-m-d H:i:s', $req->server_time).PHP_EOL;
+	}
+	
+ 
+	public  function onUserMonitSuccess( $req, $resp ){
+		$msg =  '['.$req->mac.'] onUserMonitSuccess';
+		log_debug($msg);
+	}
+	
+ 
+	public  function onUserMonitFailed( $req, $resp, $error ){
+		$msg =  '['.$req->mac.'] onUserMonitFailed';
+		log_debug($msg);
+	}
+	
+
+}

+ 216 - 0
Home/Lib/Class/JzglPhpClient.php

@@ -0,0 +1,216 @@
+<?php
+namespace Jiaruan;
+
+class JzglPhpClient {
+			
+
+
+ 
+	public  function monit( $vehicle_number ){
+		if (!is_array($vehicle_number) || count($vehicle_number) == 0) {
+			return array('success'=>false, 'message'=>'车辆参数必须是数组');
+		}
+		//生成下发命令
+		$cmd = array(
+			"type"=> C('协议类型_用户布控车辆'),
+			"cityid"=>get_vehicle_cityid($vehicle_number[0]),
+			"vehicle_number"=>$vehicle_number,
+			"sign"=> ""
+		);
+		$cmd['sign'] = $this->sign_monit($cmd);
+		//发送命令
+		return $this->send($cmd);
+	}
+	
+ 
+	public  function unmonit( $vehicle_number ){
+		if (!is_array($vehicle_number) || count($vehicle_number) == 0) {
+			return array('success'=>false, 'message'=>'车辆参数必须是数组');
+		}
+		//生成下发命令
+		$cmd = array(
+			"type"=> C('协议类型_用户撤控车辆'),
+			"cityid"=>get_vehicle_cityid($vehicle_number[0]),
+			"vehicle_number"=>$vehicle_number,
+			"sign"=> ""
+		);
+		$cmd['sign'] = $this->sign_unmonit($cmd);
+		//发送命令
+		return $this->send($cmd);
+	}
+	
+ 
+	public  function upgrade( $station_number, $data ){
+		//生成下发命令
+		$cmd = array(
+			"type"=> C('协议类型_用户升级基站'),
+			"cityid"=>get_station_cityid($station_number),
+			"station_number"=>$station_number,
+			"version" => $data["Version"],
+			"ftpip" => $data["FtpHost"],
+			"ftpport" => $data["FtpPort"],
+			"name" => $data["FtpUsername"],
+			"password" => $data["FtpPassword"],
+			"filename" =>$data["FileName"],
+			"filesize" => $data["FileSize"],
+			"sign"=> ""
+		);
+		$cmd['sign'] = $this->sign_upgrade($cmd);
+		//发送命令
+		return $this->send($cmd);
+	}
+	
+ 
+	public  function reboot( $station_number ){
+		//生成下发命令
+		$cmd = array(
+			"type"=> C('协议类型_用户重启基站'),
+			"cityid"=>get_station_cityid($station_number),
+			"station_number"=>$station_number,
+			"sign"=> ""
+		);
+		$cmd['sign'] = $this->sign_reboot($cmd);
+		//发送命令
+		return $this->send($cmd);
+	}
+	
+ 
+	public  function cmdline( $station_number, $cmdline ){
+		//生成下发命令
+		$cmd = array(
+			"type"=> C('协议类型_用户下发命令行'),
+			"cityid"=>get_station_cityid($station_number),
+			"station_number"=>$station_number,
+			"cmdline"=>$cmdline,	
+			"sign"=> ""
+		);
+		$cmd['sign'] = $this->sign_cmdline($cmd);
+		//发送命令
+		return $this->send($cmd);
+	}
+	
+ 
+	private  function send( $cmd ){
+		//主机、端口配置  
+		$remote_host = '127.0.0.1';
+		$service_port = 20161;
+		
+		$client = new ClientSend($remote_host, $service_port);
+		return $client->send(json_encode($cmd));
+		/*
+		//创建 TCP/IP socket
+		$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);  
+		if ($socket < 0) {  
+			return array('success'=>false,'message'=>'创建tcp连接失败');
+		}
+		//连接基站管理服务
+		$result = socket_connect($socket, $remote_host, $service_port);  
+		if ($result < 0) {  
+			return array('success'=>false,'message'=>'连接基站管理服务失败。' . socket_strerror($result) );
+		}  
+		//向基站管理发送命令
+		$text = json_encode($cmd) . chr(0);
+		$result = socket_write($socket, $text, strlen($text));
+		if ($result < 0) {
+			socket_close($socket);
+			return array('success'=>false,'message'=>'发送命令字失败1。' . socket_strerror($result) );
+		} 
+		
+		//接收回应信息
+		$out = '';
+		$start_time = time();
+		while ($buf = socket_read($socket, 2048)) {
+		   $out .= $buf;
+			$result = json_decode($out,true);
+			if($result){
+				socket_close($socket);
+				return $result;
+			}
+			if( time() - $start_time >= 1){
+				socket_close($socket);
+				return array('success'=>false,'message'=>'接收回应信息超时 ' . $out);
+			}
+		}
+		socket_close($socket);
+		return array('success'=>false,'message'=>'不应该走到这里。接收回应信息失败。');
+		*/
+	}
+	
+ 
+	private  function sign_reboot( $cmd ){
+		$str = $cmd['type'];
+		$str.= $cmd['cityid'];
+		$str.= $cmd['station_number'];
+		$str.= C('协议签名密钥');
+		return strtoupper(md5(urlencode($str)));
+	}
+	
+ 
+	private  function sign_cmdline( $cmd ){
+		$str = $cmd['type'];
+		$str.= $cmd['cityid'];
+		$str.= $cmd['station_number'];
+		$str.= $cmd['cmdline'];
+		$str.= C('协议签名密钥');
+		return strtoupper(md5(urlencode($str)));
+	}
+	
+ 
+	private  function sign_monit( $cmd ){
+		$str = $cmd['type'];
+		$str.= $cmd['cityid'];
+		$str.= json_encode($cmd['vehicle_number']);
+		$str.= C('协议签名密钥');
+		return strtoupper(md5(urlencode($str)));
+	}
+	
+ 
+	private  function sign_upgrade( $cmd ){
+		$str = $cmd['type'];
+		$str.= $cmd['cityid'];
+		$str.= $cmd['station_number'];
+		$str.= $cmd['version'];
+		$str.= $cmd['ftpip'];
+		$str.= $cmd['ftpport'];
+		$str.= $cmd['name'];
+		$str.= $cmd['password'];
+		$str.= $cmd['filename'];
+		$str.= $cmd['filesize'];
+		$str.= C('协议签名密钥');
+		return strtoupper(md5(urlencode($str)));
+	}
+	
+ 
+	private  function sign_unmonit( $cmd ){
+		$str = $cmd['type'];
+		$str.= $cmd['cityid'];
+		$str.= json_encode($cmd['vehicle_number']);
+		$str.= C('协议签名密钥');
+		return strtoupper(md5(urlencode($str)));
+	}
+	
+ 
+	public  function check( $station_number ){
+		//生成下发命令
+		$cmd = array(
+			"type"=> C('协议类型_用户巡检基站'),
+			"cityid"=>get_station_cityid($station_number),
+			"station_number"=>$station_number,
+			"sign"=> ""
+		);
+		$cmd['sign'] = $this->sign_check($cmd);
+		//发送命令
+		return $this->send($cmd);
+	}
+	
+ 
+	private  function sign_check( $cmd ){
+		$str = $cmd['type'];
+		$str.= $cmd['cityid'];
+		$str.= $cmd['station_number'];
+		$str.= C('协议签名密钥');
+		return strtoupper(md5(urlencode($str)));
+	}
+	
+
+}

+ 14 - 0
Home/Lib/Class/ProtoCheck.php

@@ -0,0 +1,14 @@
+<?php
+namespace Jiaruan;
+
+class ProtoCheck {
+			
+
+
+ 
+	public  function proc(  ){
+		
+	}
+	
+
+}

+ 61 - 0
Home/Lib/Class/ProtoCmdLine.php

@@ -0,0 +1,61 @@
+<?php
+namespace Jiaruan;
+
+class ProtoCmdLine {
+			
+
+
+ 
+	public  function proc( $req, $connmgr ){
+		//检查签名
+		if($req->sign != $this->sign_req($req) ) {
+			log_error('check sign failed '  . json_encode($req));
+			return $this->create_resp($req, false, "检查签名失败");
+		}
+		
+		//生成下发基站的命令
+		$cmd = $this->create_cmd($req);
+		
+		//下发命令给单个基站
+		$result = $connmgr->send_one($req->station_number,$cmd);
+		return $this->create_resp($req, $result['success'], $result['message']);
+		
+	}
+	
+ 
+	public  function create_cmd( $req ){
+		$config = array(
+			"subtype" => 1,
+			"cmd" => $req->cmdline,
+		);
+		$cmd = array(
+			'type' => 4,
+			'mac' => '??', //?? 表示由连接管理池发送时自动填充
+			'msgid' => 0,
+			'num' => 1,
+			'config' => array($config),
+		);
+		return $cmd;
+	}
+	
+ 
+	public  function create_resp( $req, $isSuccess, $message ){
+		$resp = array(
+			'success' => $isSuccess,
+			'message' => $message
+		);
+		return $resp;
+	}
+	
+ 
+	public  function sign_req( $req ){
+		$str = $req->type;
+		$str.= $req->cityid;
+		$str.= $req->station_number;
+		$str.= $req->cmdline;
+		$str.= C('协议签名密钥');
+		return strtoupper(md5(urlencode($str)));
+	}
+	
+
+}

+ 337 - 0
Home/Lib/Class/ProtoHeartbeat.php

@@ -0,0 +1,337 @@
+<?php
+namespace Jiaruan;
+
+class ProtoHeartbeat {
+			
+
+
+ 
+	public  function resp_black1( $req ){
+		$lock_cfg = array(
+			"subtype" => 5,
+			"mac"=> substr($req->mac,-6),
+			"elable" => "411681111138",
+			"op" => 1   //1-添加黑车 2-删除黑车
+		);
+		$item = array(
+			"subtype" => 5,
+			"elable" => '',
+			"op" => 1   //1-添加黑车 2-删除黑车
+		);
+		
+		$lock_cfg = array(
+		);
+		for($i=138;$i<338;$i++){
+			$item['elable'] = "411681111" . $i;
+			array_push($lock_cfg,$item);
+		}
+		
+		return $this->create_resp( $req,$lock_cfg );
+	}
+	
+ 
+	public  function resp_black2( $req ){
+		$lock_cfg = array(
+		);
+		$item = array( 
+			"subtype" => 5, 
+			"elable" => '',
+			"op" => 2   //1-添加黑车 2-删除黑车
+		);
+		for($i=138;$i<238;$i++){
+			$item['elable'] = "411681111" . $i;
+			array_push($lock_cfg,$item);
+		}
+		return $this->create_resp( $req,$lock_cfg );
+	}
+	
+ 
+	private  function resp_all_blacklist( $req ){
+		$cityId = get_station_cityid($req->mac);
+		if ($cityId == 0) {
+			log_debug("第一次心跳布控,没有查询到对应的城市id, 基站MAC = " . $req->mac);
+			return $this->create_resp($req, array() );
+		}
+		
+		//
+		$model = M("jzgl_blacklist");
+		$where = array(
+			'CityId' => $cityId,
+			'MonitorStatus' => C("布控状态_布控")
+		);
+		$data = $model->field("DeviceNumber")->where($where)->select();
+		
+		if ($data) {
+			log_debug("第一次心跳布控车辆为" . json_encode($data));
+		} else {
+			log_debug("第一次心跳布控车辆为0");
+		}
+		$monit_cfg = array();
+		$item = array( 
+			"subtype" => 5,
+			"elable" => '',
+			"op" => 1   //1-添加黑车 2-删除黑车
+		);
+		foreach ($data as $value) {
+			$item['elable'] = $value['DeviceNumber'];
+			array_push($monit_cfg, $item);
+		}
+		return $this->create_resp($req, $monit_cfg);
+	}
+	
+ 
+	public  function proc( $req, $param ){
+		//第一次心跳下发所有的布控车辆
+		if($param['heartbeat'] == 1){
+			return $this->resp_all_blacklist($req);
+		}
+		
+		
+		//返回被盗配置
+		/*
+		static $i = 0;
+		if($i == 0){
+			$resp = $this->resp_black1($req);
+			$i = 1;
+		}
+		else{
+			$resp = $this->resp_black2($req);
+			$i = 0;
+		}
+		return $resp;
+		*/
+		/*
+				上锁解锁配置
+				$resp = $this->resp_lockddc($req);
+				if($resp)
+					return $resp;
+				*/
+				/*
+				//重启配置
+				$resp = $this->resp_reboot($req);
+				if($resp)
+					return $resp;
+				//升级配置
+				$resp = $this->resp_upgrade($req);
+				if($resp)
+					return $resp;
+				//命令配置
+				$resp = $this->resp_cmd($req);
+				if($resp)
+					return $resp;
+				//上锁配置
+				$resp = $this->resp_lockddc($req);
+				if($resp)
+					return $resp;
+				//实时上报配置
+				$resp = $this->resp_rtreport($req);
+				if($resp)
+					return $resp;
+				return null;
+				*/
+				//返回被盗配置
+				//$resp = $this->resp_black($req);
+				//return $resp;
+				//返回空配置包
+				$resp = $this->resp_empty($req);
+				return $resp;
+	}
+	
+ 
+	public  function sign_req( $req ){
+		$str = $req->type;
+		$str.= $req->msgid;
+		$str.= $req->mac;
+		$str.= $req->interval;
+		$str.= $req->uptime;
+		$str.= C('协议签名密钥');
+		return strtoupper(md5(urlencode($str)));
+	}
+	
+ 
+	public  function resp_reboot( $req ){
+				$reboot_cfg = array(
+					"subtype"=> 1,
+					"cmd"=> "reboot"
+				);
+				return $this->create_resp( $req,array($reboot_cfg) );
+	}
+	
+ 
+	public  function resp_upgrade(  ){
+				return null;
+				$upgrade_cfg = array(
+					"subtype"=> 3,
+					"version"=> "",
+					"ftpip"=> "",
+					"ftpport"=> "",
+					"name"=> "",
+					"pssword"=> "",
+					"filename"=> "",
+					"filesize"=> "",
+				);
+				return $this->create_resp( $req,array($upgrade_cfg) );
+	}
+	
+ 
+	public  function resp_lockddc( $req ){
+		/*$lock_cfg1 = array(
+					"subtype" => 4,
+					"base_mac"=> substr($req->mac,-6),
+					"elable" => "411681111114",
+					"lock" => 0
+				);
+				$lock_cfg2 = array(
+					"subtype" => 4,
+					"base_mac"=> substr($req->mac,-6),
+					"elable" => "411681111115",
+					"lock" => 0
+				);*/
+				//获取基站待下发配置数据
+				$cond = array(
+					'StationMac' => $req->mac,
+					'IsSent' => 0,
+					'ConfigType' => C('配置类型_解锁或锁车')
+				);
+				$list = M('sc_station_cfg')->where($cond)->select();
+				$conf_arr = array();
+				foreach($list as $key=>$row){
+					$conf_data = json_decode($row['ConfigData'],true);
+					if($conf_data){
+						array_push($conf_arr,$conf_data);
+						//变更待下发基站状态
+						$cond = array('ID'=>$row['ID']);
+						$data = array('IsSent'=>1,'SendTime'=>date('Y-m-d H:i:s'));
+						if(!M('sc_station_cfg')->createSave($cond,$data)){
+							echo '变更基站配置发送状态失败'. PHP_EOL;
+							return;
+						}
+						//变更车辆锁定状态
+						if(!$conf_data['lock']){
+							//解锁车辆
+							$LockStatus = C('车辆锁定状态_待解锁');
+							$cond = array('VehicleLabel'=>$row['VehicleLabel'],'LockStationMac'=>$row['StationMac'],'LockStatus'=>$LockStatus);
+							$lock_info = M('sc_lock_vehicle')->where($cond)->find();
+							if(!$lock_info){
+								echo '未找到待解锁车辆记录'. PHP_EOL;
+								return;
+							}
+							$cond = array('ID'=>$lock_info['ID']);
+							$data = array('LockStatus'=>C('车辆锁定状态_已解锁'),'UnlockTime'=>date('Y-m-d H:i:s'));
+							if(!M('sc_lock_vehicle')->createSave($cond,$data)){
+								echo '设置锁定状态为已解锁失败'. PHP_EOL;
+								return;
+							}
+						}else{
+							//锁定车辆
+							$LockStatus = C('车辆锁定状态_待锁定');
+							$cond = array('VehicleLabel'=>$row['VehicleLabel'],'LockStationMac'=>$row['StationMac'],'LockStatus'=>$LockStatus);
+							$lock_info = M('sc_lock_vehicle')->where($cond)->find();
+							if(!$lock_info){
+								echo '未找到待锁定车辆记录'. PHP_EOL;
+								return;
+							}
+							$cond = array('ID'=>$lock_info['ID']);
+							$data = array('LockStatus'=>C('车辆锁定状态_已锁定'),'LockTime'=>date('Y-m-d H:i:s'));
+							if(!M('sc_lock_vehicle')->createSave($cond,$data)){
+								echo '设置锁定状态为已锁定失败'. PHP_EOL;
+								return;
+							}
+						}
+					}
+				}
+				return $this->create_resp( $req,$conf_arr );
+				
+	}
+	
+ 
+	public  function resp_rtreport( $req ){
+				$rtreport_cfg1 = array(
+					"subtype"=> 5,
+					"base_mac"=> substr($req->mac,-6),
+					"elable" => "411681111114",
+					"op"=> 2
+				);
+				$rtreport_cfg2 = array(
+					"subtype"=> 5,
+					"base_mac"=> substr($req->mac,-6),
+					"elable" => "411681111115",
+					"op"=> 2
+				);
+				return $this->create_resp( $req,array($rtreport_cfg1,$rtreport_cfg2) );
+	}
+	
+ 
+	public  function resp_cmd( $req ){
+				$cmd_cfg = array(
+					"subtype"=> 1,
+					"cmd"=> "echo 123 >> /tmp/1.txt"
+				);
+				return $this->create_resp( $req,array($cmd_cfg) );
+	}
+	
+ 
+	public  function sign_resp( $resp ){
+			$str = $resp['type'];
+				$str.= $resp['msgid'];
+				$str.= $resp['num'];
+				foreach($resp['config'] as $cfg){
+					if( $cfg['subtype'] == C('配置类型_执行命令') ){
+						$str.= $cfg['version'];
+						$str.= $cfg['ftpip'];
+						$str.= $cfg['ftpport'];
+						$str.= $cfg['name'];
+						$str.= $cfg['password'];
+						$str.= $cfg['filename'];
+						$str.= $cfg['filesize'];
+					}
+					else if( $cfg['subtype'] == C('配置类型_升级基站') ){
+						$str.= $cfg['subtype'];
+						$str.= $cfg['cmd'];
+					}
+					else if( $cfg['subtype'] == C('配置类型_重启基站') ){
+						$str.= $cfg['subtype'];
+						$str.= $cfg['cmd'];
+					}
+					else if( $cfg['subtype'] == C('配置类型_解锁或锁车') ){
+					}
+					else if( $cfg['subtype'] == C('配置类型_被盗实时上报') ){
+					}
+					else{
+					}
+				}
+				$str.= C('协议签名密钥');
+				return strtoupper(md5(urlencode($str)));
+	}
+	
+ 
+	public  function create_resp( $req, $config ){
+		$resp = array(
+			'type' => 4,
+			'msgid' => $req->msgid,
+			'mac' => $req->mac,
+			'num' => count($config),
+			'config' => $config,
+		);
+		return $resp;
+	}
+	
+ 
+	public  function resp_black( $req ){
+				$lock_cfg = array(
+					"subtype" => 5,
+					"mac"=> substr($req->mac,-6),
+					"elable" => "411681111138",
+					"op" => 1   //1-添加黑车 2-删除黑车
+				);
+				return $this->create_resp( $req,array($lock_cfg) );
+	}
+	
+ 
+	public  function resp_empty( $req ){
+		$empty_cfg = array();
+		return $this->create_resp( $req,$empty_cfg );
+	}
+	
+
+}

+ 61 - 0
Home/Lib/Class/ProtoLogin.php

@@ -0,0 +1,61 @@
+<?php
+namespace Jiaruan;
+
+class ProtoLogin {
+			
+
+
+ 
+	public  function proc( $req ){
+		//检查签名
+		if($req->sign != $this->sign_req($req) ){
+			log_error('check sign failed '  . json_encode($req));
+			return $this->create_resp($req,1);
+		}
+		else{
+			log_debug('check sign success '  . json_encode($req));
+			return $this->create_resp($req,0);
+		}
+	}
+	
+ 
+	private  function sign_req( $req ){
+		$str = $req->type;
+		$str.= $req->msgid;
+		$str.= $req->model;
+		$str.= $req->mac;
+		$str.= $req->sn;
+		$str.= $req->version;
+		$str.= $req->wip;
+		$str.= $req->nmode;
+		$str.= $req->rssi;
+		$str.= $req->serialnum;
+		$str.= $req->uptime;
+		$str.= C('协议签名密钥');
+		return strtoupper(md5(urlencode($str)));
+	}
+	
+ 
+	private  function sign_resp( $resp ){
+		$str = $resp['type'];
+		$str.= $resp['msgid'];
+		//$str.= $resp['result'];
+		$str.= C('协议签名密钥');
+		return strtoupper(md5(urlencode($str)));
+	}
+	
+ 
+	public  function create_resp( $req, $result ){
+		$resp = array(
+			'type' => 2,
+			'msgid' => $req->msgid,
+			'mac' => $req->mac,
+			'sign' => '',
+			'result' => $result,
+		);
+		$resp['sign'] = $this->sign_resp($resp);
+		return $resp;
+	}
+	
+
+}

+ 7 - 0
changelog.md

@@ -0,0 +1,7 @@
+
+# 1.0.0
+完成日期:2018-04-11
+设计人员:吕智民
+开发人员:吕智民
+完成功能:
+	1.新增:轨迹服务1.0.0版本,信号解码,自动添加告警

+ 18 - 0
composer.json

@@ -0,0 +1,18 @@
+{
+    "name": "rlfd/router-server",
+    "type": "project",
+	"repositories": [
+		{ "type": "composer", "url": "http://satis.jrcad.com/" }
+    ],
+    "require": {
+        "php": ">=5.6.0",
+		"elvisszhang/php-tp313": "dev-master",
+		"elvisszhang/php-workerman352": "dev-master",
+		"rlfd/workerman-proto": "dev-master",
+		"jrtk/aliyun-ots-sdk-php":"2.1.1",
+		"jrtk/jr-phpcls2": "dev-master"
+    },
+	"config": {
+		"secure-http": false
+	}
+}

+ 6 - 0
index.php

@@ -0,0 +1,6 @@
+<?php
+defined('SCRIPT_FILENAME') or define('SCRIPT_FILENAME',__FILE__);
+define('PHPCLS_NAMESPACE','Rlgs,Rchy');
+require 'stub.php';
+require( dirname(__DIR__) . '/jrtp/stub.php');
+//require('phar://'. dirname(__DIR__). '/jrtp/jrtp_v2.0.30.phar/bootstrap.php');

+ 84 - 0
plugin.json

@@ -0,0 +1,84 @@
+{
+  "id": "b4b2a6a6aec3d176f740d61d166fb2be",
+  "name": "rchy-gjfw",
+  "title": "人车轨迹服务",
+  "prefix": "rcgjfw",
+  "version": "1.1.1",
+  "icon": "rcgjfw.png",
+  "description": "基站数据解码,人车标签信号检测,生成告警信息",
+  "homepage": "",
+  "license": "",
+  "repository": {
+    "type": "git",
+    "url": ""
+  },
+  "author": {
+    "name": "elviss",
+  "mobile": "", 
+    "email": "elvisszhang@icloud.com",
+  "company": ""
+  },
+  "dependencies": {
+    
+  },
+  "devDependencies": {
+    "thinkphp": "3.1.3"
+  },
+  "environment":[
+    {
+        "name":"DB_DSN",
+        "title":"MySQL数据库配置",
+        "description":"",
+        "type":"text"
+    },
+	{
+        "name":"REDIS_DSN",
+        "title":"redis配置",
+        "description":"",
+        "type":"text"
+    },
+	{
+        "name":"人车标签_允许的最大时间差",
+        "title":"",
+        "description":"单位:秒",
+        "type":"text"
+    },
+	{
+        "name":"被盗告警_间隔时间",
+        "title":"",
+        "description":"单位:秒",
+        "type":"text"
+    },
+	{
+        "name":"车辆标签_rssi阀值",
+        "title":"",
+        "description":"数值越小,信号越强",
+        "type":"text"
+    },
+	{
+        "name":"车主标签_rssi阀值",
+        "title":"",
+        "description":"数值越小,信号越强",
+        "type":"text"
+    }
+  ],
+  "crontab":[
+  ],
+  "daemon":[
+	{
+        "name":"rchy-gjfw",
+        "title":"人车轨迹服务",
+        "description":"监听20160端口,检测到车辆、车主信号时,推送告警信息",
+        "startcmd":"{php} {plugin.dir}/index.php start -d rcgjfw_index/index ",
+        "stopcmd":"{php} {plugin.dir}/index.php stop"
+    }
+  ],
+  "pointcuts":[
+  ],
+  "advices":[
+  ],
+  "pcmenu":[
+  ],
+  "rights":[
+  ]
+}

+ 1 - 0
start.sh

@@ -0,0 +1 @@
+php index.php start rcgjfw_index/index

+ 5 - 0
stub.php

@@ -0,0 +1,5 @@
+<?php
+define('APP_ID','b4b2a6a6aec3d176f740d61d166fb2be');
+define('APP_PREFIX','rcgjfw');
+define('APP_VERSION','1.1.1');
+define('APP_FILENAME', __FILE__);