LocationServer.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: Mead
  5. * Date: 2019/9/3
  6. * Time: 8:05 PM
  7. */
  8. namespace App\Servers;
  9. use App\Handlers\BikeStatusInfoSyncHandler;
  10. use App\Handlers\ConvertHandler;
  11. use App\Maps\BikeMap;
  12. use App\Maps\VideoMap;
  13. use App\Models\AreaTraitModel;
  14. use App\Models\BatteryTraitModel;
  15. use App\Models\BikeTraitModel;
  16. use App\Models\CacheLogTraitModel;
  17. use App\Models\LocationLosTraitModel;
  18. use App\Models\OrderTraitModel;
  19. use App\Models\WarningLogTraitModel;
  20. /**
  21. * 定位解析包
  22. * Class LocationServer
  23. * @package App\Servers
  24. */
  25. class LocationServer extends BaseServer
  26. {
  27. use BatteryTraitModel, BikeTraitModel, AreaTraitModel, WarningLogTraitModel, LocationLosTraitModel, OrderTraitModel, CacheLogTraitModel;
  28. public function main($body)
  29. {
  30. $data = $this->decode($body);
  31. // self::log($data, 'LocationServer', self::$LOG_MAJOR);
  32. // if ($this->is_ep_sec($data['box_no'], 'reload_location', 10)) {
  33. // return $this->response();
  34. // }
  35. //
  36. // $bike_no = $_SESSION['bike_no'];
  37. // $box_no = $data['box_no'];
  38. // if (!$bike_no) return [];
  39. //过滤同一时候多包
  40. if ($_SESSION['last_box_location_time'] == $data['time']) {
  41. return $this->response();
  42. }
  43. $_SESSION['last_box_location_time'] = $data['time'];
  44. $bike_no = $_SESSION['bike_no'];
  45. $box_no = $_SESSION['box_no'];
  46. //车位位置更新redis
  47. $this->cacheJsonLog("NOW_LOCATION:{$bike_no}", [
  48. 'lat' => $data['location']['lat'],
  49. 'lng' => $data['location']['lng'],
  50. 'mileage' => $data['bike']['bike_single_mileage'],
  51. 'time' => time()
  52. ]);
  53. $cols = [];
  54. // 验证位置是否正常
  55. if ($data['location']['lat'] > 0) {
  56. $cols['last_location'] = json_encode($data['location'], true);
  57. $cols['last_location_time'] = date('Y-m-d H:i:s');
  58. } else {
  59. //位置数据为0,说明车可能在房内
  60. if ($this->is_throw_num_time($data['box_no'], 'location_error', 10, 60)) {
  61. // 发出警告
  62. self::warningLocationError($bike_no, $box_no, $data, $body, "location");
  63. }
  64. return $this->response();
  65. }
  66. // 30 分钟更新一次
  67. // $battery_power = $this->byVoltageGetElectric($data['bike']['battery_voltage']);
  68. $battery_power = $this->byVoltageGetElectric($data['bike']['battery_voltage'], $data['battery']['bike_soc']);
  69. $is_low_battery_power = BikeMap::BATTERY_POWER_OK;
  70. if ($battery_power <= self::$max_ride_v) {
  71. $is_low_battery_power = BikeMap::BATTERY_POWER_LOW;
  72. }
  73. // self::log($box_no . '===|' . $data['bike']['battery_voltage'] . '==>' . $battery_power, 'BP');
  74. $location = [
  75. 'order_id' => 0,
  76. 'bike_id' => 0,
  77. 'area_id' => 0,
  78. 'type' => 'no',
  79. 'is_rent' => 0
  80. ];
  81. // 判断是否接车状态
  82. if ((!$data['bike']['status']['is_huanche'])) {
  83. //车运动状态
  84. // 获取用户信息
  85. $order = (new BikeStatusInfoSyncHandler($this->redis))->getRideBikeOrderInfo($bike_no);
  86. if ($order) {
  87. //是否有电量信息
  88. if (array_key_exists('is_rent', $order)) {
  89. $location['is_rent'] = $order['is_rent'];
  90. }
  91. // 订单信息
  92. if (array_key_exists('id', $order)) {
  93. $location['order_id'] = $order['id'];
  94. $location['area_id'] = $order['area_id'];
  95. }
  96. if (array_key_exists('bike_id', $order)) {
  97. $location['bike_id'] = $order['bike_id'];
  98. }
  99. $location['type'] = BikeStatusInfoSyncHandler::ROLE_USER;
  100. if ($order['role'] === BikeStatusInfoSyncHandler::ROLE_USER) {
  101. if ($location['is_rent']) {
  102. //租车
  103. if ($order['is_close_bike']) {
  104. //关车之后没有关上,强制锁车
  105. self::log($box_no, 'LOCATION_FOUCE_CLOSE_BIKE', self::$LOG_COMMON);
  106. BikeControl::closeLock($data['box_no']);
  107. }
  108. } else {
  109. //临时用车
  110. // 临时停车没有关锁
  111. if ($order['is_temporary_close']) {
  112. self::log($box_no, 'LOCATION_FOUCE_TEMPORARY_CLOSE_BIKE', self::$LOG_COMMON);
  113. BikeControl::temporaryCloseLock($data['box_no']);
  114. }
  115. }
  116. //用户正常骑行订单
  117. if ($data['location']['lat'] > 0) {//判断是否在骑行区域内
  118. // 判断后台是否设置超速区域播报语音
  119. if ($order['is_out_area_lost_electric']) {
  120. $is_out_status = $this->isOutArea($data['location']['lat'], $data['location']['lng'], $order['area_id'], $data['box_no']);
  121. if (!$is_out_status['is_out_area']) {//播报语言
  122. if (!$is_out_status['is_out_area_nearby']) {
  123. //断电播放
  124. BikeControl::playVideo($data['box_no'], VideoMap::VIDEO_OUT_AREA_CLOSE_ELECTRIC);
  125. } else {
  126. //超出运营区
  127. BikeControl::outAreaPlayVideo($data['box_no']);
  128. }
  129. if (!$is_out_status['is_out_area_nearby']) {
  130. //失能
  131. echo '要失能';
  132. if (!$this->redis->exists('bike_out_area_open_electric_' . $bike_no)) {
  133. echo '失能';
  134. self::log($box_no, 'LOCATION_BIKE_OUT_AREA_LOSE', self::$LOG_COMMON);
  135. BikeControl::outAreaLoseElectric($data['box_no']);
  136. $this->redis->set('bike_out_area_' . $bike_no, 1, 60);
  137. }
  138. } else {
  139. //供能
  140. if ($this->redis->exists('bike_out_area_' . $bike_no)) {
  141. self::log($box_no, 'LOCATION_BIKE_OUT_AREA_ADD', self::$LOG_COMMON);
  142. BikeControl::outAreaGetElectric($data['box_no']);
  143. $this->redis->del('bike_out_area' . $bike_no);
  144. $this->redis->set('bike_out_area_open_electric_' . $bike_no, 1, 120);
  145. }
  146. }
  147. } else {
  148. //供能
  149. if ($this->redis->exists('bike_out_area_' . $bike_no)) {
  150. self::log($box_no, 'LOCATION_BIKE_OUT_AREA_ADD', self::$LOG_COMMON);
  151. BikeControl::outAreaGetElectric($data['box_no']);
  152. $this->redis->del('bike_out_area' . $bike_no);
  153. $this->redis->set('bike_out_area_open_electric_' . $bike_no, 1, 120);
  154. }
  155. //在区域内断电
  156. $is_in_parking_status = $this->isOutParkings($data['location']['lat'], $data['location']['lng'], $order['area_id'], $data['box_no']);
  157. // self::log($box_no . "|" . $order['area_id'] . "==>" . ($is_in_parking_status ? 1 : 0), "mead_in_parking", self::$LOG_COMMON);
  158. if ($is_in_parking_status) {//播报语言
  159. //断电播放
  160. BikeControl::playVideo($data['box_no'], VideoMap::VIDEO_OUT_AREA_CLOSE_ELECTRIC);
  161. if (!$this->redis->exists('bike_in_parking_open_electric_' . $bike_no)) {
  162. echo '失能';
  163. self::log($box_no, 'LOCATION_BIKE_IN_Pariking_AREA_LOSE', self::$LOG_COMMON);
  164. BikeControl::outAreaLoseElectric($data['box_no']);
  165. $this->redis->set('bike_in_parking_area_' . $bike_no, 1, 60);
  166. }
  167. } else {
  168. //供能
  169. if ($this->redis->exists('bike_in_parking_area_' . $bike_no)) {
  170. self::log($box_no, 'LOCATION_BIKE_IN_parking_AREA_ADD', self::$LOG_COMMON);
  171. BikeControl::outAreaGetElectric($data['box_no']);
  172. $this->redis->del('bike_in_parking_area_' . $bike_no);
  173. $this->redis->set('bike_in_parking_open_electric_' . $bike_no, 1, 120);
  174. }
  175. }
  176. }
  177. }
  178. }
  179. //电量过低自动关车
  180. //是否低电关电车
  181. if ($order['is_low_electric_close_bike']) {
  182. if ($battery_power <= 10) {
  183. BikeControl::playVideo($data['box_no'], VideoMap::VIDEO_NO_ELECTRIC);
  184. if ($this->is_throw_num_time($data['box_no'], 'battery_low', 50, 10)) {
  185. $status = json_decode(file_get_contents(Config['close_order_api_url'] . "&box_no={$box_no}&bike_no={$bike_no}&type=电量低"), true);
  186. self::log($box_no, 'LOCATION_BATTERY_LOW_AUTO_ORDER', self::$LOG_COMMON);
  187. }
  188. }
  189. }
  190. } else if ($order['role'] === BikeStatusInfoSyncHandler::ROLE_WORKER) {
  191. //运维骑行订单
  192. // 订单信息
  193. $location['type'] = BikeStatusInfoSyncHandler::ROLE_WORKER;
  194. } else if ($order['role'] === BikeStatusInfoSyncHandler::ROLE_BIND) {
  195. $location['type'] = BikeStatusInfoSyncHandler::ROLE_BIND;
  196. } elseif ($location['role'] === BikeStatusInfoSyncHandler::ROLE_SERVER) {
  197. //系统本身操作
  198. $location['type'] = BikeStatusInfoSyncHandler::ROLE_SERVER;
  199. } else {
  200. self::log($box_no, 'LOCATION_CLOSE_BIKE_NO_USER_TYPE', self::$LOG_COMMON);
  201. BikeControl::closeLock($data['box_no']);
  202. }
  203. } else {
  204. if (strtotime($data['time']) >= (time() - 100)) {
  205. $num = $this->redis->incr('bike:illegal:open:' . $bike_no, 1);
  206. $this->redis->expire('bike:illegal:open:' . $bike_no, 120);
  207. self::log($box_no, 'LOCATION_CLOSE_BIKE_NO_ORDER', self::$LOG_COMMON);
  208. BikeControl::closeLock($data['box_no']);
  209. if ($num > 5) {
  210. if (!$this->is_ep_min($box_no, 'illegal_location', 20)) {
  211. $this->warningFF($bike_no, $data['box_no'], $data, $body);
  212. }
  213. }
  214. }
  215. }
  216. if (!$this->is_ep_min($box_no, 'update_battery', 5)) {
  217. $cols['battery_power'] = $battery_power;
  218. $cols['is_low_battery_power'] = BikeMap::BATTERY_POWER_OK;
  219. if ($cols['battery_power'] <= self::$max_ride_v) {
  220. $cols['is_low_battery_power'] = BikeMap::BATTERY_POWER_LOW;
  221. //30分钟插一次报警信息
  222. if (!$this->is_ep_min($box_no, 'warning_log_battery', 30)) {
  223. $this->warningLogBatteryLow($bike_no, $data['box_no'], ['battery_power' => $cols['battery_power']], $body, 'location');
  224. }
  225. }
  226. }
  227. // 更新车的位置信息(非骑行状态)
  228. if (count($cols) && !$this->is_ep_min($box_no, 'update_ride_bike_location', 1)) {
  229. $this->db->update('bikes')->where('box_no = ' . $data['box_no'])->cols($cols)->query();
  230. }
  231. } else {
  232. // 车静止状态
  233. if (!$this->is_ep_min($box_no, 'update_stop_bike_battery', 30)) {
  234. $cols['battery_power'] = $battery_power;
  235. $cols['is_low_battery_power'] = BikeMap::BATTERY_POWER_OK;
  236. if ($cols['battery_power'] <= self::$max_ride_v) {
  237. $cols['is_low_battery_power'] = BikeMap::BATTERY_POWER_LOW;
  238. //30分钟插一次报警信息
  239. // if (!$this->is_ep_min($box_no, 'warning_log_battery', 30)) {
  240. $this->warningLogBatteryLow($bike_no, $data['box_no'], ['battery_power' => $cols['battery_power']], $body, 'location');
  241. // }
  242. }
  243. }
  244. $re = $this->updateBikeData($box_no, 'BikeStatus', [
  245. 'battery_power' => $battery_power,
  246. 'is_low_battery_power' => $is_low_battery_power,
  247. ], $cols);
  248. if (!$re) {
  249. if (!$this->is_ep_min($box_no, 'update_bike_location', 20)) {
  250. $cols['battery_power'] = $battery_power;
  251. $cols['is_low_battery_power'] = $is_low_battery_power;
  252. $this->db->update('bikes')->where("box_no = '{$box_no}'")->cols($cols)->query();
  253. }
  254. } else {
  255. $this->is_ep_min($box_no, 'update_bike_location', 20);
  256. }
  257. // // 更新车的位置信息(非骑行状态)
  258. // if (count($cols) && !$this->is_ep_min($box_no, 'update_bike_location', 20)) {
  259. // $this->db->update('bikes')->where('box_no = ' . $data['box_no'])->cols($cols)->query();
  260. // } else {
  261. // //换电池及时更新电量
  262. // $openBatteryKey = "cache:open_battery:{$box_no}";
  263. // if ($this->redis->exists($openBatteryKey)) {
  264. // $this->db->update('bikes')->where('box_no = ' . $data['box_no'])->cols($cols)->query();
  265. // }
  266. // }
  267. if (($data['location']['lat'] > 0)) {
  268. // 修改车的位置
  269. $is_location_ex = $this->redis->geopos(BikeStatusInfoSyncHandler::REDIS_BIKE_LOCATION_TAG, $bike_no)[0];
  270. if ((count($is_location_ex) !== 0)) {
  271. $this->redis->geoadd(BikeStatusInfoSyncHandler::REDIS_BIKE_LOCATION_TAG, $data['location']['lng'], $data['location']['lat'], $bike_no);
  272. }
  273. }
  274. }
  275. // 位置信息
  276. $this->mongo->location_logs->insertOne([
  277. 'bike_no' => $bike_no,
  278. 'box_no' => $data['box_no'],
  279. 'order_id' => $location['order_id'],
  280. 'bike_id' => $location['bike_id'],
  281. 'area_id' => $location['area_id'],
  282. 'latitude' => $data['location']['lat'],
  283. 'longitude' => $data['location']['lng'],
  284. 'speed' => $data['bike']['speed'],
  285. 'battery_power' => $battery_power,
  286. 'mileage' => $data['bike']['bike_single_mileage'],
  287. 'is_riding' => $data['bike']['status']['is_ride'],
  288. 'is_yundong' => $data['bike']['status']['is_yundong'],
  289. 'type' => $location['type'],
  290. 'is_rent' => $location['is_rent'],
  291. 'created_at' => date('Y-m-d H:i:s'),
  292. 'box_time' => $data['time'],
  293. 'status' => 1,
  294. 'day' => date("Ymd")
  295. ]);
  296. return $this->response();
  297. }
  298. /**
  299. * 判断是否在骑行区
  300. * @param $lat
  301. * @param $lng
  302. * @param $box_no
  303. * User: Mead
  304. */
  305. private function isOutArea($lat, $lng, $area_id = false, $box_no = false)
  306. {
  307. $location = [
  308. 'latitude' => $lat,
  309. 'longitude' => $lng,
  310. ];
  311. if (!$area_id) $area_id = $this->byBoxNoGetAreaId($box_no);
  312. $area = $this->byAreaIdGetArea($area_id);
  313. $fences = $area['area_fence'];
  314. $centre = $area['area_centre'];
  315. $radius = $area['area_radius'];
  316. $area_fushe_fence = $area['area_fushe_fence'];
  317. // 判断是否在骑行区域
  318. $ConvertHandler = (new ConvertHandler());
  319. $is_out_area = $ConvertHandler->is_point_in_polygon($location, $fences);
  320. // 判断是否骑出辐射范围
  321. $is_out_area_nearby = true;
  322. if (!$is_out_area) {
  323. $is_out_area_nearby = $ConvertHandler->is_point_in_polygon($location, $area_fushe_fence);
  324. }
  325. return [
  326. 'is_out_area' => $is_out_area,
  327. 'is_out_area_nearby' => $is_out_area_nearby
  328. ];
  329. }
  330. /**
  331. * 是否在断电区域内
  332. * @param $lat
  333. * @param $lng
  334. * @param $area_id
  335. * @param $box_no
  336. * @return bool
  337. */
  338. private function isOutParkings($lat, $lng, $area_id = false, $box_no = false)
  339. {
  340. $location = [
  341. 'latitude' => $lat,
  342. 'longitude' => $lng,
  343. ];
  344. if (!$area_id) return false;
  345. $parkings = $this->byAreaIdGetDuanParkings($area_id);
  346. if (!is_array($parkings) || !count($parkings)) return false;
  347. // 判断是否在骑行区域
  348. $ConvertHandler = (new ConvertHandler());
  349. $status = false;
  350. foreach ($parkings as $parking) {
  351. $parking = json_decode($parking, true);
  352. $is_out_paring = $ConvertHandler->is_point_in_polygon($location, $parking);
  353. if ($is_out_paring) {
  354. $status = true;
  355. break;
  356. }
  357. }
  358. return $status;
  359. }
  360. /**
  361. * 解析装载的登录数据
  362. * @param $body
  363. * @return array
  364. * User: Mead
  365. */
  366. private function decode($body)
  367. {
  368. $i = 0;
  369. $box_no = $this->decodeBoxNo(self::stitching($body, $i, 5));
  370. $i += 5;
  371. // 设备时间
  372. $box_time = $this->decodeTime(self::stitching($body, $i, 4));
  373. $i += 4;
  374. // 维度
  375. $location_lat = self::stitching($body, $i, 4);
  376. $i += 4;
  377. // 经度
  378. $location_lng = self::stitching($body, $i, 4);
  379. $i += 4;
  380. // 海报高度
  381. // $location_height = self::stitching($body, $i, 2);
  382. $i += 2;
  383. //方位角
  384. // $location_azimuth = self::stitching($body, $i, 1);
  385. $i += 1;
  386. // GPS速度
  387. $location_speed = self::stitching($body, $i, 1);
  388. $i += 1;
  389. //信号状态
  390. // $location_signal_status = self::stitching($body, $i, 2);
  391. $i += 2;
  392. // 基站 MCC
  393. // $station_mcc = self::stitching($body, $i, 2);
  394. $i += 2;
  395. // 基站 MNC
  396. // $station_mnc = self::stitching($body, $i, 2);
  397. $i += 2;
  398. // 基站 LAC
  399. // $station_lac = self::stitching($body, $i, 2);
  400. $i += 2;
  401. // 基站 CELLID
  402. // $station_cellid = self::stitching($body, $i, 2);
  403. $i += 2;
  404. // 车辆 相对SOC
  405. $bike_soc = hexdec(self::stitching($body, $i, 1));
  406. $i += 1;
  407. // 车辆 可用剩余容量
  408. $bike_battery_mb_free = self::stitching($body, $i, 2);
  409. $i += 2;
  410. // 车辆 SOH
  411. // $bike_soh = self::stitching($body, $i, 1);
  412. $i += 1;
  413. // 车辆 重放电次数
  414. // $bike_charge_discharge_num = self::stitching($body, $i, 2);
  415. $i += 2;
  416. // 车辆 骑行电流
  417. // $bike_ride_electric_current = self::stitching($body, $i, 1);
  418. $i += 1;
  419. // 车辆 控制器温度
  420. // $bike_box_temperature = self::stitching($body, $i, 1);
  421. $i += 1;
  422. // 车辆 电瓶电压
  423. $bike_battery_voltage = self::stitching($body, $i, 2);
  424. $i += 2;
  425. // 车辆 总里程
  426. $bike_total_mileage = self::stitching($body, $i, 4);
  427. $i += 4;
  428. // 车辆 单次里程数
  429. $bike_single_mileage = self::stitching($body, $i, 4);
  430. $i += 4;
  431. // 车辆 霍尔速度
  432. $bike_speed = self::stitching($body, $i, 2);
  433. $i += 2;
  434. // 车辆 车辆状态
  435. $bike_status = self::stitching($body, $i, 2);
  436. // 处理经纬度
  437. list($lng, $lat) = $this->decodeLatAndLng($location_lat, $location_lng);
  438. // 处理车的状态
  439. $bike_status = $this->decodeBikeStatus($bike_status);
  440. // 处理电压
  441. $bike_battery_voltage = $this->decodeBatteryVoltage($bike_battery_voltage);
  442. return [
  443. 'box_no' => $box_no,
  444. 'time' => $box_time,
  445. 'location' => [
  446. 'lat' => $lat,
  447. 'lng' => $lng,
  448. 'speed' => $location_speed
  449. ],
  450. 'bike' => [
  451. 'status' => $bike_status,
  452. 'speed' => hexdec($bike_speed),
  453. 'battery_voltage' => $bike_battery_voltage,
  454. 'bike_total_mileage' => hexdec($bike_total_mileage),
  455. 'bike_single_mileage' => hexdec($bike_single_mileage),
  456. ],
  457. 'battery' => [
  458. 'bike_battery_mb_free' => $bike_battery_mb_free,
  459. 'bike_soc' => $bike_soc
  460. ]
  461. ];
  462. }
  463. }