decode($body); // 位置信息 // $bike_no = $_SESSION['bike_no']; // $box_no = $data['box_no']; // if (!$bike_no) return []; $location = [ 'order_id' => 0, 'bike_id' => 0, 'area_id' => 0, 'type' => 'no', 'is_rent' => 0 ]; $battery_power = $data['battery_voltage']; $i = count($data['locations']); $last_location = [ 'lat' => $data['locations'][$i - 1]['location_latitude'], 'lng' => $data['locations'][$i - 1]['location_longitude'], ]; $last_location = (new MapHandler())->wgs84togcj02($last_location['lng'], $last_location['lat']); $last_location = [ 'lat' => $last_location[1], 'lng' => $last_location[0], ]; //过滤同一时候多包 if ($_SESSION['last_box_location_time'] == $data['box_time']) { return $this->response(); } $_SESSION['last_box_location_time'] = $data['box_time']; $bike_no = $_SESSION['bike_no']; $box_no = $_SESSION['box_no']; //车位位置更新redis $this->cacheJsonLog("NOW_LOCATION:{$bike_no}", [ 'lat' => $last_location['lat'], 'lng' => $last_location['lng'], 'mileage' => 0, 'time' => time() ]); $cols = []; // 验证位置是否正常 if ($last_location['lat'] >= 3) { $cols['last_location'] = json_encode($last_location, true); $cols['last_location_time'] = date('Y-m-d H:i:s'); } else { //位置数据为0,说明车可能在房内 if ($this->is_throw_num_time($data['box_no'], 'location_error', 10, 60)) { // 发出警告 self::warningLocationError($bike_no, $box_no, $data, $body, "SatelliteLocation"); } return $this->response(); } if ($data['status']['order_status']) { // 电车打开 // 获取用户信息 $order = (new BikeStatusInfoSyncHandler($this->redis))->getRideBikeOrderInfo($bike_no); if ($order) { //是否有电量信息 if (array_key_exists('is_rent', $order)) { $location['is_rent'] = $order['is_rent']; } // 订单信息 if (array_key_exists('id', $order)) { $location['order_id'] = $order['id']; $location['area_id'] = $order['area_id']; } if (array_key_exists('bike_id', $order)) { $location['bike_id'] = $order['bike_id']; } $location['type'] = BikeStatusInfoSyncHandler::ROLE_USER; if ($order['role'] === BikeStatusInfoSyncHandler::ROLE_USER) { if ($location['is_rent']) { //租车 if ($order['is_close_bike'] && $data['status']['bike_lock'] == 1) { //关车之后没有关上,强制锁车 self::log($box_no, 'LOCATION_FOUCE_CLOSE_BIKE', self::$LOG_COMMON); BikeControl::closeLock($data['box_no']); } } else { // 临时停车没有关锁 if ($order['is_temporary_close'] && $data['status']['bike_lock'] == 1) { self::log($box_no, 'LOCATION_FOUCE_TEMPORARY_CLOSE_BIKE', self::$LOG_COMMON); BikeControl::temporaryCloseLock($data['box_no']); } } //用户正常骑行订单 if ($last_location['lat'] > 0) {//判断是否在骑行区域内 // 判断后台是否设置超速区域播报语音 if ($order['is_out_area_lost_electric']) { $is_out_status = $this->isOutArea($last_location['lat'], $last_location['lng'], $order['area_id'], $data['box_no']); if (!$is_out_status['is_out_area']) {//播报语言 self::log('超区域'); if ($this->is_throw_num_time($data['box_no'], 'is_out_area_nearby', 3, 0.5)) { self::log('播报语音'); if (!$is_out_status['is_out_area_nearby']) { //断电播放 BikeControl::playVoice($data['box_no'], VideoMap::VIDEO_BATTERY_EDGE); } else { //超出运营区 BikeControl::playVoice($data['box_no'], VideoMap::VIDEO_GO_BEYOND); } } if (!$is_out_status['is_out_area_nearby']) { //失能 echo '要失能'; if ($this->is_throw_num_time($data['box_no'], 'is_out_area_nearby', 2, 1)) { if ((!$this->redis->exists('bike_out_area_open_electric_' . $bike_no)) || $data['status']['bike_lock']) { echo '失能'; self::log($box_no, 'LOCATION_BIKE_OUT_AREA_LOSE', self::$LOG_COMMON); BikeControl::outAreaLoseElectric($data['box_no'], VideoMap::VIDEO_POWER_FAILURE); $this->redis->set('bike_out_area_' . $bike_no, 1, 60); } } } else { //供能 if ($this->redis->exists('bike_out_area_' . $bike_no)) { self::log($box_no, 'LOCATION_BIKE_OUT_AREA_ADD', self::$LOG_COMMON); BikeControl::outAreaGetElectric($data['box_no']); $this->redis->del('bike_out_area' . $bike_no); $this->redis->set('bike_out_area_open_electric_' . $bike_no, 1, 120); } } } else { //供能 if ($this->redis->exists('bike_out_area_' . $bike_no)) { self::log($box_no, 'LOCATION_BIKE_OUT_AREA_ADD', self::$LOG_COMMON); BikeControl::outAreaGetElectric($data['box_no']); $this->redis->del('bike_out_area' . $bike_no); $this->redis->set('bike_out_area_open_electric_' . $bike_no, 1, 120); } } } } //电量过低自动关车 //是否低电关电车 if ($order['is_low_electric_close_bike']) { if ($battery_power <= 10) { if ($this->is_throw_num_time($data['box_no'], 'is_low_battery', 3, 0.5)) { self::log('低电语音'); BikeControl::playVoice($data['box_no'], VideoMap::VIDEO_LOW_POWER); } if ($this->is_throw_num_time($data['box_no'], 'battery_low', 40, 5)) { $status = json_decode(file_get_contents(Config['close_order_api_url'] . "&box_no={$box_no}&bike_no={$bike_no}&type=电量低&position=3"), true); self::log($box_no, 'LOCATION_BATTERY_LOW_AUTO_ORDER', self::$LOG_COMMON); } } } } else if ($order['role'] === BikeStatusInfoSyncHandler::ROLE_WORKER) { //运维骑行订单 // 订单信息 $location['type'] = BikeStatusInfoSyncHandler::ROLE_WORKER; } else if ($order['role'] === BikeStatusInfoSyncHandler::ROLE_BIND) { $location['type'] = BikeStatusInfoSyncHandler::ROLE_BIND; } else if ($order['role'] === BikeStatusInfoSyncHandler::ROLE_SERVER) { //系统本身操作 $location['type'] = BikeStatusInfoSyncHandler::ROLE_SERVER; } else { self::log($box_no, 'LOCATION_CLOSE_BIKE_NO_USER_TYPE', self::$LOG_COMMON); BikeControl::closeLock($data['box_no']); } } else { //处理晚到的数据 // $next_log = $this->byTimeGetOrder($data['box_time'], $box_no); // if ($next_log) { // $re = $this->byIdAndIsRentAndTime($next_log['order_id'], $next_log['is_rent'], $data['box_time']); // if ($re) { // $location['is_rent'] = $next_log['is_rent']; // $location['order_id'] = $next_log['order_id']; // $location['area_id'] = $next_log['area_id']; // $location['bike_id'] = $next_log['bike_id']; // $location['type'] = BikeStatusInfoSyncHandler::ROLE_USER; // } // } else { //// //非法骑行 // $num = $this->redis->incr('bike:illegal:open:' . $bike_no, 1); // $this->redis->expire('bike:illegal:open:' . $bike_no, 120); // self::log($box_no, 'LOCATION_CLOSE_BIKE_NO_ORDER', self::$LOG_COMMON); // BikeControl::closeLock($data['box_no']); // // if ($num > 5) { // if (!$this->is_ep_min($box_no, 'illegal_location', 20)) { // $this->warningFF($bike_no, $data['box_no'], $data, $body); // } // } // } } if (!$this->is_ep_min($box_no, 'update_battery', 5)) { $cols['battery_power'] = $battery_power; $cols['is_low_battery_power'] = BikeMap::BATTERY_POWER_OK; if ($cols['battery_power'] <= self::$max_ride_v) { $cols['is_low_battery_power'] = BikeMap::BATTERY_POWER_LOW; //30分钟插一次报警信息 if (!$this->is_ep_min($box_no, 'warning_log_battery', 30)) { $this->warningLogBatteryLow($bike_no, $data['box_no'], ['battery_power' => $cols['battery_power']], $body, 'satelliteLocation'); } } } // 更新车的位置信息(非骑行状态) if (count($cols) && !$this->is_ep_min($box_no, 'update_ride_bike_location', 1)) { // $this->db->update('bikes')->where('box_no = ' . (string)$data['box_no'])->cols($cols)->query(); $this->db->update('bikes')->where("box_no = '{$box_no}'")->cols($cols)->query(); } } else { // 车静止状态 if ($data['status']['bike_lock']) { BikeControl::closeLock($data['box_no']); } if (!$this->is_ep_min($box_no, 'update_stop_bike_battery', 15)) { $cols['battery_power'] = $battery_power; $cols['is_low_battery_power'] = BikeMap::BATTERY_POWER_OK; if ($cols['battery_power'] <= self::$max_ride_v) { $cols['is_low_battery_power'] = BikeMap::BATTERY_POWER_LOW; //30分钟插一次报警信息 // if (!$this->is_ep_min($box_no, 'warning_log_battery', 30)) { $this->warningLogBatteryLow($bike_no, $data['box_no'], ['battery_power' => $cols['battery_power']], $body, 'location'); // } } } else { // $openBatteryKey = "cache:open_battery:{$box_no}"; // if ($this->redis->exists($openBatteryKey)) { // $cols['battery_power'] = $battery_power; // $cols['is_low_battery_power'] = BikeMap::BATTERY_POWER_OK; // if ($cols['battery_power'] <= self::$max_ride_v) { // $cols['is_low_battery_power'] = BikeMap::BATTERY_POWER_LOW; // } // } } // 更新车的位置信息(非骑行状态) // if (count($cols) && !$this->is_ep_min($box_no, 'update_bike_location', 10)) { // $this->db->update('bikes')->where('box_no = ' . $data['box_no'])->cols($cols)->query(); // } else { // //换电池及时更新电量 // $openBatteryKey = "cache:open_battery:{$box_no}"; // if ($this->redis->exists($openBatteryKey)) { // $cols['battery_power'] = $battery_power; // $cols['is_low_battery_power'] = BikeMap::BATTERY_POWER_OK; // if ($cols['battery_power'] <= self::$max_ride_v) { // $cols['is_low_battery_power'] = BikeMap::BATTERY_POWER_LOW; // } // $this->db->update('bikes')->where('box_no = ' . $data['box_no'])->cols($cols)->query(); // } // } // $this->db->update('bikes')->where('box_no = ' . (string)$data['box_no'])->cols($cols)->query(); $this->db->update('bikes')->where("box_no = '{$box_no}'")->cols($cols)->query(); if (($last_location['lat'] > 0)) { // 修改车的位置 $is_location_ex = $this->redis->geopos(BikeStatusInfoSyncHandler::REDIS_BIKE_LOCATION_TAG, $bike_no)[0]; if ((count($is_location_ex) !== 0)) { $this->redis->geoadd(BikeStatusInfoSyncHandler::REDIS_BIKE_LOCATION_TAG, $last_location['lng'], $last_location['lat'], $bike_no); } } } $day = date("Ymd"); foreach ($data['locations'] as $v) { $lnglat = (new MapHandler())->wgs84togcj02($v['location_longitude'], $v['location_latitude']); $this->mongo->location_logs->insertOne([ 'bike_no' => $bike_no, 'box_no' => $data['box_no'], 'order_id' => $location['order_id'], 'bike_id' => $location['bike_id'], 'area_id' => $location['area_id'], 'latitude' => $lnglat[1], 'longitude' => $lnglat[0], 'speed' => $v['location_speed']['speed'], 'battery_power' => $battery_power, 'mileage' => $data['kilometers'] ?? 0, 'is_riding' => $data['status']['order_status'] ?? 0, 'is_yundong' => $data['status']['rear_wheel_motion'] ?? 0, 'type' => $location['type'], 'is_rent' => $location['is_rent'], 'created_at' => date('Y-m-d H:i:s'), 'box_time' => $data['box_time'], 'status' => 1, 'source' => 'satelliteLocation', 'day' => $day ]); break; } return $this->response(); } /** * 解析装载的状态消息 * @param $body * @return array * User: Mead */ private function decode($body) { $top34Data = $this->decodeWeiKeMuTop34($body); $i = 34; // 点类型 与 是否超速 $location_type = self::stitching($body, $i, 1); // i=34 $i += 1; // 信息组数 $message_num = hexdec(self::stitching($body, $i, 1)); // i=35 $i += 1; // 第一组样点对应时间 $first_location_time = self::stitching($body, $i, 4); // i=36 $i += 4; // 第一组样点对应维度 $first_location_latitude = hexdec(self::stitching($body, $i, 4));// i=40 $i += 4; // 第一组样点对应经度 $first_location_longitude = hexdec(self::stitching($body, $i, 4));// i=44 $i += 4; // 第一组样点速度 $first_location_speed = self::stitching($body, $i, 1);// i=48 $i += 1; // 第一组样点ACC状态、方向、卫星数 $first_location_acc = self::stitching($body, $i, 1);// i=49 $i += 1; // 第一个点 $first_location = [ 'location_time' => $this->decodeTime($first_location_time), 'location_latitude' => bcdiv($first_location_latitude, 1000000.0, 8), 'location_longitude' => bcdiv($first_location_longitude, 1000000.0, 8), 'location_speed' => self::handleLocationSpeed($first_location_speed), 'location_acc' => self::handleLocationACC($first_location_acc), ]; // 第x个点的速度 1<= x <= hexdec($message_num) // 假设为第三个点的 $locations[0] = $first_location; // 最后一个点 $last_location = []; if ($message_num > 1) { //最后一组样点与第一个样点时间差值 $last_location_time = self::stitching($body, $i, 2);// i=50 $i += 2; // 最后一组样点与第一个样点维度差值 $last_location_latitude = self::stitching($body, $i, 2);// i=52 $i += 2; // 最后一组样点与第一个样点经度差值 $last_location_longitude = self::stitching($body, $i, 2);// i=54 $i += 2; // 最后一组样点速度 $last_location_speed = self::stitching($body, $i, 1);// i=56 $i += 1; // 最后一组样点ACC $last_location_acc = self::stitching($body, $i, 1);// i=57 $i += 1; $last_location = [ 'location_time' => $this->decodeTime(dechex(hexdec($first_location_time) + self::handleI2($last_location_time))), 'location_latitude' => bcdiv($first_location_latitude + self::handleI2($last_location_latitude), 1000000, 8), 'location_longitude' => bcdiv($first_location_longitude + self::handleI2($last_location_longitude), 1000000.0, 8), 'location_speed' => self::handleLocationSpeed($last_location_speed), 'location_acc' => self::handleLocationACC($last_location_acc), ]; } if ($message_num > 2) { $ylocation = [ 'location_time' => hexdec($first_location_time), 'location_latitude' => $first_location_latitude, 'location_longitude' => $first_location_longitude, 'location_speed' => self::handleLocationSpeed($first_location_speed), 'location_acc' => self::handleLocationACC($first_location_acc), ]; for ($x = 1; $x < ($message_num - 1); $x++) { $x_location_speed = self::stitching($body, $i, 1);// i=58 $i += 1; $x_location_acc = self::stitching($body, $i, 1);// i=59 $i += 1; // n 与 n-1的时间差值 $x_location_time = self::stitching($body, $i, 2);// $i =60 $i += 2; // n与n-1 的维度差值 $x_location_latitude = self::stitching($body, $i, 2);// 62 $i += 2; // n与n-1的经度差值 $x_location_longitude = self::stitching($body, $i, 2);// 64 $i += 2; $time = $ylocation['location_time'] + hexdec($x_location_time); $lat = $ylocation['location_latitude'] + self::handleI2($x_location_latitude); $lng = $ylocation['location_longitude'] + self::handleI2($x_location_longitude); $locations[$x] = [ 'location_time' => date('Y-m-d H:i:s', $time), 'location_latitude' => bcdiv($lat, 1000000, 8), 'location_longitude' => bcdiv($lng, 1000000, 8), 'location_speed' => self::handleLocationSpeed($x_location_speed), 'location_acc' => self::handleLocationACC($x_location_acc), ]; $ylocation = [ 'location_time' => $time, 'location_latitude' => $lat, 'location_longitude' => $lng, ]; } } if ($message_num > 1) { $locations[] = $last_location; } return array_merge($top34Data, [ 'location_type' => self::handleLocationType($location_type), 'message_num' => $message_num, 'first_location' => $first_location, 'last_location' => $last_location, 'locations' => $locations ]); } /** * 解析速度 * * @param $location_speed * @return array * @author Fx * */ private static function handleLocationSpeed($location_speed) { // self::dd('Speed==>'); // self::dd($location_speed); $data = self::handleU1($location_speed); // self::dd($data); $is_speeding = $data[7]; $speed = implode('', array_slice($data, 0, 7)); return [ 'is_speeding' => $is_speeding, //1:超速 0:否 'speed' => bindec($speed) * 2// 量化单位2km/h,范围 0~127 ]; } private static function handleLocationACC($locationACC) { // self::dd('ACC==>'); // self::dd($locationACC); $data = self::handleU1($locationACC); // self::dd($data); // 卫星数 $satellites = bindec(implode('', array_slice($data, 0, 4))); // 方向 $directions = [ '000' => '正北', '001' => '东北', '010' => '正东', '011' => '东南', '100' => '正南', '101' => '西南', '110' => '正西', '111' => '西北', ]; $direction = implode('', array_slice($data, 4, 3)); // ACC状态 1:ACC接通;0:ACC断开 $acc_status = $data[7]; return [ 'satellites' => $satellites, 'direction' => $directions[$direction] ?? '', 'acc_status' => $acc_status, ]; } /** * 解析点类型 * * @param $location_type * @return array|mixed * @author Fx * */ private static function handleLocationType($location_type) { $data = self::handleU1($location_type); return [ 'location_type' => $data[5],// 1.静态位置标示;0.正常的轨迹点 'is_speeding' => $data[4] //超速指示,当该数据包持续6个点速度超速时,该位为1 ]; } /** * 状态响应 * @param $login_type * @return array * User: Mead */ public function response($login_type = '00') { $body = [ '5b', '0000' ]; return $body; } /** * 判断是否在骑行区 * @param $lat * @param $lng * @param $box_no * User: Mead */ private function isOutArea($lat, $lng, $area_id = false, $box_no = false) { $location = [ 'latitude' => $lat, 'longitude' => $lng, ]; if (!$area_id) $area_id = $this->byBoxNoGetAreaId($box_no); $area = $this->byAreaIdGetArea($area_id); $fences = $area['area_fence']; $centre = $area['area_centre']; $radius = $area['area_radius']; $area_fushe_fence = $area['area_fushe_fence']; // 判断是否在骑行区域 $ConvertHandler = (new ConvertHandler()); $is_out_area = $ConvertHandler->is_point_in_polygon($location, $fences); // 判断是否骑出辐射范围 $is_out_area_nearby = true; if (!$is_out_area) { $is_out_area_nearby = $ConvertHandler->is_point_in_polygon($location, $area_fushe_fence); } return [ 'is_out_area' => $is_out_area, 'is_out_area_nearby' => $is_out_area_nearby ]; } }