RcgjfwXqIndexAction.class.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. <?php
  2. class RcgjfwXqIndexAction extends Action {
  3. public function index( ){
  4. if (!C('rcgjfw.人车标签_允许的最大时间差')){
  5. echo '常量【人车标签_允许的最大时间差】未定义'.PHP_EOL;
  6. return;
  7. }
  8. if (!C('rcgjfw.被盗告警_间隔时间')){
  9. echo '常量【被盗告警_间隔时间】未定义'.PHP_EOL;
  10. return;
  11. }
  12. if (!C('rcgjfw.车辆标签_rssi阀值')){
  13. echo '常量【车辆标签_rssi阀值】未定义'.PHP_EOL;
  14. return;
  15. }
  16. if (!C('rcgjfw.车主标签_rssi阀值')){
  17. echo '常量【车辆标签_rssi阀值】未定义'.PHP_EOL;
  18. return;
  19. }
  20. Vendor('Workerman352.Autoloader');
  21. $tcp_worker = new Workerman\Worker("tcp://0.0.0.0:20160");
  22. $tcp_worker->count = 1;
  23. $tcp_worker->onWorkerStart = function($worker){
  24. };
  25. $tcp_worker->onConnect = function($connection){
  26. $this->onConnect($connection);
  27. };
  28. $tcp_worker->onMessage = function($connection,$data){
  29. $this->onMessage($connection,$data);
  30. };
  31. $tcp_worker->onClose = function($connection){
  32. $this->onClose($connection);
  33. };
  34. //实例化信号处理类
  35. $this->rfid_proto = new Rlgs\Proto\RfidStation2();
  36. $this->rchy_alarm_list = Redis("rchy_alarm_list","queue");
  37. $this->redis_vsignal = Redis("rchy_vsignal_{no}","zset")->prefix('rchy_vsignal_');
  38. //$this->cache = \Rchy\Cache\RedisCache::getInstance();
  39. Workerman\Worker::runAll();//保持运行
  40. }
  41. private function onMessage( $connection, $data ){
  42. $connection->m_packet .= $data;
  43. }
  44. private function onConnect( $connection ){
  45. echo PHP_EOL . $connection->id . ' connected'. PHP_EOL;
  46. }
  47. private function onClose( $connection ){
  48. //记录数据包文本
  49. $packet_text = $this->rfid_proto->tcp_hex2str($connection->m_packet,true) ;
  50. //$this->rfid_proto->tcp_log($connection->id . ' closed ' . $packet_text);
  51. //echo 'packet_text => '.$packet_text.PHP_EOL;
  52. //解析消息体
  53. $packet = $this->rfid_proto->tcp_str2bin($packet_text);
  54. $result = $this->rfid_proto->tcp_unpack_msg($packet);
  55. if(! $result['Success'] ){
  56. echo 'unpack packet failed'.PHP_EOL;
  57. $this->rfid_proto->decode_error_log('unpack_fail','unpack packet failed ,result : '.json_encode($result));
  58. return;
  59. }
  60. //echo 'decode_data => '.json_encode($result).PHP_EOL;
  61. //记录基站数据包
  62. //$this->rfid_proto->decode_packet_log($result['StationCode'],$result['StationCode'].' => '.$packet_text);
  63. if (!$result['StationCode']){
  64. echo 'StationCode is empty!'.PHP_EOL;
  65. $this->rfid_proto->decode_error_log('unpack_fail','StationCode is empty! ');
  66. return false;
  67. }
  68. if (!$result['VehicleList']){
  69. echo 'VehicleList is empty!'.PHP_EOL;
  70. return ;
  71. }
  72. //检查是否有效基站
  73. $station_info = \Rchy\Cache\RedisCache::getStationInfo($result['StationCode']);
  74. if(!$station_info){
  75. $this->rfid_proto->decode_error_log('unpack_fail','getStationInfo failed ,StationCode : '.$result['StationCode']);
  76. return false;
  77. }
  78. //更新车主标签时间
  79. echo '======= updateRfidTime start ======='.$connection->id.PHP_EOL;
  80. $this->updateRfidTime($result);
  81. echo '======= updateRfidTime over ======='.$connection->id.PHP_EOL;
  82. //添加报警
  83. echo '======= addAlarm start ======='.$connection->id.PHP_EOL;
  84. $this->addAlarm($result);
  85. echo '======= addAlarm over ======='.$connection->id.PHP_EOL;
  86. /*
  87. //轨迹包redis主库
  88. $redis_raw_packet = Redis("fdqu_raw_packet","queue");
  89. $redis_raw_packet->push($packet_text);
  90. //数据解包
  91. $result = $this->unpackMsg($packet_text);
  92. //生成车辆轨迹日志
  93. $this->addTcpLog($result);
  94. */
  95. }
  96. private function addTcpLog( $result ){
  97. //一个基站一个日志文件
  98. $this->rfid_proto->station_route_log('tcp',$result);
  99. //一个车辆一个日志文件
  100. $this->rfid_proto->vehicle_route_log('tcp',$result);
  101. }
  102. private function unpackMsg( $packet_text ){
  103. $packet = $this->rfid_proto->tcp_str2bin($packet_text);
  104. if(!$packet){
  105. echo 'parse packet failed.'.PHP_EOL;
  106. $this->rfid_proto->tcp_error_log('parse_fail','parse packet failed ,packet_text : '.$packet_text);
  107. return false;
  108. }
  109. $result = $this->rfid_proto->tcp_unpack_msg($packet);
  110. if(! $result['Success'] ){
  111. echo 'unpack fail: ' . $result['Message'].PHP_EOL;
  112. $this->rfid_proto->tcp_error_log('unpack_fail',$result);
  113. return false;
  114. }
  115. return $result;
  116. }
  117. private function addAlarm( $result ){
  118. //获取当前所有车主rfid的rssi数据
  119. $chezhu_rfids = array();
  120. foreach ($result['VehicleList'] as $key => $row){
  121. $chezhu_info = \Rchy\Cache\RedisCache::getCheZhuInfo($row['VehicleNumber']);
  122. if(!$chezhu_info){
  123. continue;
  124. }
  125. $chezhu_rfids[$row['VehicleNumber']]['Rssi'] = $row['rssi'];
  126. $chezhu_rfids[$row['VehicleNumber']]['SignalCount'] = $row['SignalCount'];
  127. }
  128. //var_dump($result['VehicleList']);
  129. foreach ($result['VehicleList'] as $key => $row){
  130. $row['StationCode'] = $result['StationCode'];
  131. if (!$row['VehicleNumber']){
  132. echo 'VehicleNumber empty!'.PHP_EOL;
  133. continue;
  134. }
  135. //获取车辆信息
  136. $vehicle_info = \Rchy\Cache\RedisCache::getVehicleInfo($row['VehicleNumber']);
  137. //车辆不存在跳过
  138. if (!$vehicle_info){
  139. //echo 'vehicle not existed, vehilce_number = '.$row['VehicleNumber'].', rssi = '.$row['rssi'].PHP_EOL;
  140. continue;
  141. }
  142. //未绑定车主rfid跳过
  143. if (!$vehicle_info['CheZhuRfid']){
  144. echo 'vehicle not kaihu, vehilce_number: '.$row['VehicleNumber'].', rssi = '.$row['rssi'].PHP_EOL;
  145. continue;
  146. }
  147. echo 'VehicleNumber: '.$row['VehicleNumber'].',rssi = '.$row['rssi'].PHP_EOL;
  148. //车辆标签
  149. $vehicle_no = strtolower($row['VehicleNumber']);
  150. //末次信号(改用本地缓存了)
  151. //$last_signal_info = $this->redis_vsignal->table($vehicle_no)->max();
  152. //获取最近一段时间内所有rssi,求均值
  153. $avgRssi = $this->getAvgRssi($row);
  154. if($avgRssi === false){
  155. echo 'getAvgRssi failed! VehicleNumber = '.$row['VehicleNumber'].PHP_EOL;
  156. continue;
  157. }
  158. //车辆rssi信号弱的,过滤掉,防止收到车信号的时候,没有收到车主信号,从而误报
  159. if ($avgRssi > C('rcgjfw.车辆标签_rssi阀值')){
  160. echo 'avgRssi > '.C('rcgjfw.车辆标签_rssi阀值').', avgRssi = '.$avgRssi.', VehicleNumber = '.$row['VehicleNumber'].PHP_EOL;
  161. continue;
  162. }
  163. //有效信号加入到 rchy_vsignal_{no} 表
  164. $signal = array(
  165. 'StationCode' => $row['StationCode'],
  166. 'Rssi' => $row['rssi'],
  167. 'Timestamp' => $row['Timestamp']
  168. );
  169. //获取最近一次的信号信息
  170. $last_signal_info = S('last_signal_info'.$row['VehicleNumber']);
  171. //更新最近一次的信号信息
  172. S('last_signal_info'.$row['VehicleNumber'], $signal, 24*60*60);
  173. $alarm_info = array();
  174. $alarm_info['VehicleNumber'] = $row['VehicleNumber'];
  175. $alarm_info['AlarmTime'] = time();
  176. $alarm_info['OnlineTime'] = $row['OnlineTime'];
  177. $alarm_info['VehicleRssi'] = $row['rssi'];
  178. $alarm_info['VehicleSignalCount'] = $row['SignalCount'];
  179. $alarm_info['CheZhuRssi'] = $chezhu_rfids[$vehicle_info['CheZhuRfid']]['Rssi'];//车主或者授权车主信号强度,这里还需要完善
  180. $alarm_info['CheZhuSignalCount'] = $chezhu_rfids[$vehicle_info['CheZhuRfid']]['SignalCount'];
  181. $alarm_info['StationCode'] = $row['StationCode'];
  182. //检查车辆经过时,在限定的时间内,是否有接收到任一授权标签信号
  183. $is_stolen = $this->checkVehicleStatus($vehicle_info['CheZhuRfid'], $row);
  184. $cur_time = time();
  185. //检查是否被盗报警
  186. if ( $is_stolen ){
  187. $alarm_info['AlarmType'] = \Rchy\Enum\AlarmType::STOLEN;
  188. //上次被盗告警时间
  189. $latest_stolen_alarm_time = S('latest_stolen_alarm_time_'.$row['VehicleNumber']);
  190. //避免频繁告警检测
  191. if ($latest_stolen_alarm_time && $cur_time - $latest_stolen_alarm_time <= C('rcgjfw.被盗告警_间隔时间')){
  192. echo '车辆'.$row['VehicleNumber'].'刚被盗告警过,跳过告警! latest_stolen_alarm_time = '.$latest_stolen_alarm_time.', cur_time = '.$cur_time.PHP_EOL;
  193. return ;
  194. }
  195. echo 'VehicleNumber:'.$row['VehicleNumber'].', 告警类型: 被盗告警'.PHP_EOL;
  196. //更新本次被盗告警时间
  197. S('latest_stolen_alarm_time_'.$row['VehicleNumber'], $cur_time);
  198. }else{
  199. //上次告警时间
  200. $latest_pass_alarm_time = S('latest_pass_alarm_time_'.$row['VehicleNumber']);
  201. //避免频繁告警检测
  202. if ($latest_pass_alarm_time && $cur_time - $latest_pass_alarm_time <= C('rcgjfw.过车告警_间隔时间')){
  203. echo '车辆'.$row['VehicleNumber'].'刚过车告警过,跳过! 已过车告警'.$pass_alarm_count.'次, latest_pass_alarm_time = '.$latest_pass_alarm_time.', cur_time = '.$cur_time.PHP_EOL;
  204. return ;
  205. }
  206. $alarm_info['AlarmType'] = \Rchy\Enum\AlarmType::PASS;
  207. echo 'pass_alarm_count = '.$pass_alarm_count.', last_chezhu_signal_time = '.$last_signal_info['Timestamp'].', now = '.$row['Timestamp']. PHP_EOL;
  208. echo 'VehicleNumber:'.$row['VehicleNumber'].', 告警类型: 过车告警'.PHP_EOL;
  209. //记录本次过车告警时间
  210. S('latest_pass_alarm_time_'.$row['VehicleNumber'], $cur_time);
  211. }
  212. $this->rchy_alarm_list->push(json_encode($alarm_info));
  213. echo '添加告警成功, VehicleNumber:'.$row['VehicleNumber'].', AlarmType: '.$alarm_info['AlarmType'].PHP_EOL;
  214. //echo 'alarm_info: '.json_encode($alarm_info).PHP_EOL;
  215. }
  216. }
  217. private function updateRfidTime( $result ){
  218. foreach($result['VehicleList'] as $key => $row){
  219. $row['StationCode'] = $result['StationCode'];
  220. if(!$row['VehicleNumber']){
  221. //echo 'VehicleNumber not existed, VehicleNumber: '.$row['VehicleNumber'].PHP_EOL;
  222. continue;
  223. }
  224. //检查车主标签是否有效,无效跳过,有效的更新上报信息
  225. $vehicle_info = \Rchy\Cache\RedisCache::getCheZhuInfo($row['VehicleNumber']);
  226. if(!$vehicle_info){
  227. continue;
  228. }
  229. echo 'CheZhuRfid = '.$row['VehicleNumber'].', rssi = '.$row['rssi'].', time:'.date('Y-m-d H:i:s',$row['Timestamp']).PHP_EOL;
  230. //如果基站时间不准确,用服务器时间
  231. if($row['Timestamp'] < time()-3600){
  232. $row['Timestamp'] = time();
  233. }
  234. //获取最近一段时间内所有rssi,求均值
  235. $avgRssi = $this->getAvgRssi($row);
  236. if($avgRssi === false){
  237. echo 'getAvgRssi failed! CheZhuRfid = '.$row['VehicleNumber'].PHP_EOL;
  238. continue;
  239. }
  240. //检查车主标签rssi,过滤掉弱信号
  241. if ($avgRssi > C('rcgjfw.车主标签_rssi阀值')){
  242. echo 'avgRssi > '.C('rcgjfw.车主标签_rssi阀值').', avgRssi = '.$avgRssi.', CheZhuRfid = '.$row['VehicleNumber'].PHP_EOL;
  243. continue;
  244. }
  245. echo 'avgRssi = '.$avgRssi.', CheZhuRfid = '.$row['VehicleNumber'].PHP_EOL;
  246. $info = array(
  247. 'Timestamp' => $row['Timestamp'],
  248. 'StationCode' => $row['StationCode'],
  249. 'Rssi' => $row['rssi']
  250. );
  251. //更新车主RFID上报信息
  252. S('last_chezhu_rfid_'.$row['VehicleNumber'], $info, 60*60*24);
  253. //echo 'last_chezhu_rfid_'.$row['VehicleNumber'] .PHP_EOL;
  254. //var_dump(S('last_chezhu_rfid_'.$row['VehicleNumber']));
  255. //echo '更新车主标签上报信息成功,CheZhuRfid: '.$row['VehicleNumber'].', 上报时间:'.date('Y-m-d H:i:s',$row['Timestamp']).', rssi: '.$row['rssi'].PHP_EOL;
  256. }
  257. }
  258. private function checkVehicleStatus( $chezhu_rfid, $row ){
  259. if(!$chezhu_rfid){
  260. echo 'checkVehicleStatus failed, chezhu_rfid is empty!'.PHP_EOL;
  261. return false;
  262. }
  263. if(!$row){
  264. echo 'checkVehicleStatus failed, row is empty!'.PHP_EOL;
  265. return false;
  266. }
  267. //获取车辆已授权车主标签
  268. $all_rfids = \Rchy\Cache\RedisCache::getVehicleAuthors($row['VehicleNumber']);
  269. if(!$all_rfids){
  270. $all_rfids = array();
  271. }
  272. //加入车主标签
  273. if(!in_array($chezhu_rfid,$all_rfids)){
  274. array_push($all_rfids,$chezhu_rfid);
  275. }
  276. $is_stolen = true;
  277. foreach ($all_rfids as $rfid){
  278. //获取授权车主标签上报信息
  279. $last_rfid_info = S('last_chezhu_rfid_'.$rfid);
  280. //var_dump($last_rfid_info);
  281. $last_rfid_time = $last_rfid_info['Timestamp'];
  282. $rfid_pass_station = $last_rfid_info['StationCode'];
  283. echo 'chezhu_rfid = '.$rfid.', now = '.$row['Timestamp'].', last_rfid_time = '.$last_rfid_time.', rfid_pass_station = '. $rfid_pass_station . PHP_EOL;
  284. //车辆和授权车主同时出现在同一个小区门口,则认为是过车告警
  285. if ( $last_rfid_time && abs($row['Timestamp']-$last_rfid_time) < C('rcgjfw.人车标签_允许的最大时间差') && $rfid_pass_station == $row['StationCode']){
  286. $is_stolen = false;
  287. break;
  288. }
  289. }
  290. echo 'all_rfids:'.PHP_EOL;
  291. var_dump($all_rfids);
  292. return $is_stolen;
  293. }
  294. private function checkVehicleDirection( $vehicle_no, $now ){
  295. //开发中。。。
  296. return false;
  297. if(!$vehicle_no){
  298. echo 'checkVehicleDirection failed, vehicle_no is empty!'.PHP_EOL;
  299. return false;
  300. }
  301. if(!$now){
  302. echo 'checkVehicleDirection failed, now is empty!'.PHP_EOL;
  303. return false;
  304. }
  305. //获取最近一段时间内的信号
  306. $cond = array($now-60,$now);
  307. $signal_list = $this->redis_vsignal->table($vehicle_no)->where($cond)->select();
  308. //查询结果默认是按时间升序,这里翻转一下
  309. $signal_list = array_reverse($signal_list);
  310. if(!$signal_list){
  311. echo 'checkVehicleDirection failed,signal_list is empty!'.PHP_EOL;
  312. return false;
  313. }
  314. //最后一个信号点所在的基站
  315. $last_station = $signal_list[0]['StationCode'];
  316. if(!$last_station){
  317. echo 'checkVehicleDirection failed,last_station is empty!'.PHP_EOL;
  318. return false;
  319. }
  320. //获取一段时间内同一基站连续的几个信号强度点
  321. $rssi_list = array();
  322. foreach($signal_list as $key=>$row){
  323. $signal = json_decode($key,true);
  324. if(!$signal){
  325. echo 'json_decode error'.PHP_EOL;
  326. return false;
  327. }
  328. if($last_station != $signal['StationCode']){
  329. echo 'is not the last_station rssi,stop push! last_station = '.$last_station.', cur_station = '.$signal['StationCode'].PHP_EOL;
  330. break;
  331. }
  332. $rssi_list[] = $signal['rssi'];
  333. }
  334. if( count($rssi_list)<3 ){
  335. echo 'checkVehicleDirection failed,count($rssi_list)<3 !! count($rssi_list) = '.count($rssi_list).PHP_EOL;
  336. return false;
  337. }
  338. //<信号一直递增或者一直递减的,车辆方向暂时不好判断>
  339. //判断车辆rssi信号强度是否递减(此时,时间是降序)
  340. $is_rssi_decrease = $this->isRssiDecrease($rssi_list, count($rssi_list));
  341. if($is_rssi_decrease){
  342. echo '检测到车辆rssi信号强度递增,车辆正在靠近基站!StationCode = '.$last_station.PHP_EOL;
  343. return false;
  344. }
  345. //如果rssi先逐渐增强,后逐渐减弱,且峰值右侧第一个信号大于峰值左侧第一个信号,则认为是进入小区
  346. //如果rssi先逐渐增强,后逐渐减弱,且峰值右侧第一个信号小于峰值左侧第一个信号,则认为是走出小区
  347. echo '判断不出车辆运动方向!VehicleNumber = '. $vehicle_no .PHP_EOL;
  348. return false;
  349. }
  350. private function isRssiDecrease( $arr, $len ){
  351. if ($len == 1)
  352. return true;
  353. return ($arr[$len - 2] >= $arr[$len - 1]) && $this->isRssiDecrease($arr, $len-1);
  354. }
  355. private function isRssiDrab( $arr, $len ){
  356. if(!$arr){
  357. echo 'isRssiDrab failed, arr is empty!!'.PHP_EOL;
  358. return false;
  359. }
  360. if(!$len){
  361. echo 'isRssiDrab failed, len is empty!!'.PHP_EOL;
  362. return false;
  363. }
  364. if($this->isRssiIncrease($arr, $len)){
  365. echo 'rssi increase'.PHP_EOL;
  366. return true;
  367. }
  368. if($this->isRssiDecrease($arr, $len)){
  369. echo 'rssi decrease'.PHP_EOL;
  370. return true;
  371. }
  372. echo 'rssi is not drab '.PHP_EOL;
  373. return false;
  374. }
  375. private function isRssiIncrease( $arr, $len ){
  376. if ($len == 1)
  377. return true;
  378. return ($arr[$len - 2] <= $arr[$len - 1]) && $this->isRssiIncrease($arr, $len-1);
  379. }
  380. private function getAvgRssi( $data ){
  381. $rfid = $data['VehicleNumber'];
  382. if(!$rfid){
  383. echo 'getAvgRssi failed ,VehicleNumber not existed!'.PHP_EOL;
  384. return false;
  385. }
  386. $station_code = $data['StationCode'];
  387. if(!$station_code){
  388. echo 'getAvgRssi failed ,StationCode not existed!'.PHP_EOL;
  389. return false;
  390. }
  391. $key = $station_code.'_'.$rfid;
  392. //添加新车辆轨迹点
  393. $route_point = array('Rfid'=>$rfid,'Time'=>$data['Timestamp'],'Rssi'=>$data['rssi']);
  394. $signal_data = array(json_encode($route_point) => $data['Timestamp']);
  395. $this->redis_vsignal->table($key)->add($signal_data);
  396. //查询时间窗内,所有rssi
  397. $interval = C('rcgjfw.信号均值_采集时间窗');
  398. if(!$interval){
  399. $interval = 4;
  400. }
  401. $start = $data['Timestamp']-$interval;
  402. $where = array( $start,$data['Timestamp']);
  403. $list = $this->redis_vsignal->table($key)->where($where)->select();
  404. //计算平均值
  405. $sum = 0;
  406. $count = 0;
  407. $info = array();
  408. foreach($list as $signal => $time){
  409. $signal = json_decode($signal,true);
  410. $sum += $signal['Rssi'];
  411. $count++;
  412. $info[$time] = $signal['Rssi'];
  413. }
  414. echo 'key:'.$key.', rssi_pool:'.json_encode($info).PHP_EOL;
  415. if(!$count){
  416. echo 'getAvgRssi failed ,rssi_pool empty!'.PHP_EOL;
  417. return false;
  418. }
  419. $avgRssi = round($sum/$count);
  420. echo 'rfid: '.$rfid.', station_code: '.$station_code.', avgRssi = '.$avgRssi.PHP_EOL;
  421. return $avgRssi;
  422. }
  423. }