From 261997d65153754c8722b916ed46599838ef55d8 Mon Sep 17 00:00:00 2001 From: Jing Li Date: Wed, 30 Aug 2023 20:08:00 +0800 Subject: [PATCH] Update --- .../Commands/BiAng/DeviceLogSyncCommand.php | 2 +- .../Commands/Linkos/WormReportCommand.php | 158 ++++++++++++++ .../LinkosDeviceLogArchiveCommand.php | 74 ------- .../Commands/LinkosDeviceLogSyncCommand.php | 197 ------------------ .../Commands/YunFei/WormReportCommand.php | 2 +- app/Console/Kernel.php | 4 + app/Exceptions/LinkosFarmException.php | 9 + app/Iot/Linkos/FarmClient.php | 153 ++++++++++++++ 8 files changed, 326 insertions(+), 273 deletions(-) create mode 100644 app/Console/Commands/Linkos/WormReportCommand.php delete mode 100644 app/Console/Commands/LinkosDeviceLogArchiveCommand.php delete mode 100644 app/Console/Commands/LinkosDeviceLogSyncCommand.php create mode 100644 app/Exceptions/LinkosFarmException.php create mode 100644 app/Iot/Linkos/FarmClient.php diff --git a/app/Console/Commands/BiAng/DeviceLogSyncCommand.php b/app/Console/Commands/BiAng/DeviceLogSyncCommand.php index 8a03bc7..3573ee9 100644 --- a/app/Console/Commands/BiAng/DeviceLogSyncCommand.php +++ b/app/Console/Commands/BiAng/DeviceLogSyncCommand.php @@ -156,7 +156,7 @@ class DeviceLogSyncCommand extends Command $disk->put($path, file_get_contents($item['url'])); } - WormPhoto::firstOrCreate([ + WormPhoto::updateOrCreate([ 'device_id' => $device->id, 'uploaded_at' => $item['time'], ], [ diff --git a/app/Console/Commands/Linkos/WormReportCommand.php b/app/Console/Commands/Linkos/WormReportCommand.php new file mode 100644 index 0000000..80cd289 --- /dev/null +++ b/app/Console/Commands/Linkos/WormReportCommand.php @@ -0,0 +1,158 @@ +info('------------------------------------------'); + $this->info(now()); + try { + $this->sync(); + } catch (Throwable $e) { + report($e); + } + $this->info('------------------------------------------'); + } + + protected function sync(): void + { + // 接口接口限制,每分钟最多访问6次,因此每次访问后需休眠10秒 + $client = new FarmClient('xunwang', 'qwer1234'); + + $now = now(); + + /** @var \Illuminate\Database\Eloquent\Collection */ + $devices = Device::supplierBy('device-supplier-linkos') + ->where('type', DeviceType::Worm) + ->whereIn('status', [DeviceStatus::Online, DeviceStatus::Offline]) + ->get(); + + if ($devices->isEmpty()) { + $this->warn('没有找到虫情设备'); + return; + } + + $this->info('=================================='); + $this->info('尝试更新设备状态...'); + $realTimeData = $client->realTimeData($devices->pluck('sn')->all()); + foreach ($realTimeData as $item) { + foreach ($devices as $device) { + if ($item['deviceAddr'] != $device->sn) { + continue; + } + // 更新设备状态 + $device->update([ + 'state' => $item['status'] === 'online' ? DeviceStatus::Online : DeviceStatus::Offline, + ]); + } + } + $this->info("设备状态更新完成"); + + $this->info('=================================='); + + $this->info('尝试同步虫情区域统计...'); + for ($i=2; $i > 0; $i--) { + $reportedAt = $now->copy()->subDays($i); + + $statistics = collect( + $client->wormStatistics( + 'E05F10DAIB6F4I4977IB95FI82554A48DE7C', + $reportedAt->copy()->startOfDay(), + $reportedAt->copy()->endOfDay(), + ) + )->mapWithKeys(fn ($item) => [$item['deviceAddr'] => $item['wornData']]); + + foreach ($devices as $device) { + $data = $statistics[$device->sn] ?? []; + + WormReport::updateOrCreate([ + 'device_id' => $device->id, + 'reported_at' => $reportedAt->toDateString(), + ], [ + 'agricultural_base_id' => $device->agricultural_base_id, + 'worm_num' => collect($data)->sum('num'), + 'data' => $data, + ]); + } + } + $this->info("同步虫情区域统计完成"); + + $this->info('=================================='); + + // 接口请求次数 + $requests = 4; + + // 同步最近7天的分析报表记录 + $this->info('尝试同步分析报表记录...'); + foreach ($devices->pluck('sn') as $sn) { + $data = $client->wormAnalyseData($sn, $now->copy()->subDays(7), $now, 1, 100); + + foreach ($data['rows'] as $item) { + foreach ($devices as $device) { + if ($item['deviceAddr'] != $device->sn) { + continue; + } + + $url = $item['analyseCoordUrl'] ?: $item['imagesUrl']; + + // 下载图片 + $name = md5($url); + if ($ext = pathinfo($url, PATHINFO_EXTENSION)) { + $name .= ".{$ext}"; + } + + $path = "worm-photos/{$device->id}/{$name}"; + + $disk = Storage::disk('public'); + if (! $disk->exists($path)) { + $disk->put($path, file_get_contents($url)); + } + + WormPhoto::updateOrCreate([ + 'device_id' => $device->id, + 'uploaded_at' => $item['createTime'], + ], [ + 'url' => $path, + ]); + } + } + + $requests++; + // 接口请求频率: 每分钟6次 + if ($requests == 6) { + $requests = 0; + sleep(61); + } + } + $this->info("同步分析报表记录完成"); + } +} diff --git a/app/Console/Commands/LinkosDeviceLogArchiveCommand.php b/app/Console/Commands/LinkosDeviceLogArchiveCommand.php deleted file mode 100644 index ed87457..0000000 --- a/app/Console/Commands/LinkosDeviceLogArchiveCommand.php +++ /dev/null @@ -1,74 +0,0 @@ -get(); - - // 物联平台目前只有水质监测设备和气象监测设备 - LinkosDeviceLog::orderBy('reported_at', 'asc')->lazy()->each(function ($log) use ($devices, $linkosDeviceLogService) { - if (empty($log->data)) { - return; - } - - foreach ($devices as $device) { - if ($device->sn !== $log->device_id) { - continue; - } - - match ($device->type) { - DeviceType::Soil => $linkosDeviceLogService->handleSoilMonitoringLog($device, $log->data, $log->reported_at), - DeviceType::Meteorological => $linkosDeviceLogService->handleMeteorologicalMonitoringLog($device, $log->data, $log->reported_at), - DeviceType::WaterQuality => $linkosDeviceLogService->handleWaterQualityMonitoringLog($device, $log->data, $log->reported_at), - }; - } - }); - - $now = now(); - $date = Carbon::parse('2022-06-01'); - - while ($date->lt($now)) { - foreach ($devices as $device) { - match ($device->type) { - DeviceType::Soil => $linkosDeviceLogService->handleSoilMonitoringDailyLog($device, $date), - DeviceType::Meteorological => $linkosDeviceLogService->handleMeteorologicalMonitoringDailyLog($device, $date), - DeviceType::WaterQuality => $linkosDeviceLogService->handleWaterQualityMonitoringDailyLog($device, $date), - }; - } - - $date->addDay(); - } - - return Command::SUCCESS; - } -} diff --git a/app/Console/Commands/LinkosDeviceLogSyncCommand.php b/app/Console/Commands/LinkosDeviceLogSyncCommand.php deleted file mode 100644 index 6e59909..0000000 --- a/app/Console/Commands/LinkosDeviceLogSyncCommand.php +++ /dev/null @@ -1,197 +0,0 @@ -argument('device'); - - // 最近同步时间 - $lastDate = $this->getLastDate($device); - - do { - if ($lastDate === null) { - $lastDate = Carbon::parse('2022-06-01'); - } else { - $lastDate->addDay(); - } - - $start = $lastDate->copy()->startOfDay(); - - if ($start->gt($now)) { - throw new BizException('开始时间大约当前时间'); - } - - $end = $lastDate->copy()->endOfDay(); - - if ($end->gt($now)) { - $end = $now; - } - - $this->info('----------------------------------'); - $this->info('设备编号: '.$device); - $this->info('开始时间: '.$start->toDateTimeString()); - $this->info('结束时间: '.$end->toDateTimeString()); - $this->info('开始同步'); - $this->info('...'); - $this->synchronize($device, $start, $end); - $this->info('Done!'); - $this->info('----------------------------------'); - - if ($now->isSameDay($lastDate)) { - break; - } - - $this->setLastDate($device, $lastDate); - } while (true); - - return Command::SUCCESS; - } - - /** - * 同步设备历史数据 - * - * @param string $device - * @param \Carbon\Carbon $start - * @param \Carbon\Carbon $end - * @return void - */ - protected function synchronize(string $device, Carbon $start, Carbon $end) - { - // 分页页码 - $page = 0; - // 每页条数 - $size = 50; - // 开始时间戳 - $startTime = $start->unix() * 1000; - // 结束时间戳 - $endTime = $end->unix() * 1000; - - LinkosDeviceLog::where('device_id', $device)->whereBetween('reported_at', [$start, $end])->delete(); - - do { - $result = retry(5, function () use ($device, $page, $size, $startTime, $endTime) { - return $this->linkosService()->post('/deviceFlow/v1/list', [ - 'device_id' => $device, - 'start_time' => $startTime, - 'end_time' => $endTime, - 'pageable' => [ - 'page' => $page, - 'size' => $size, - ], - ]); - }, 100); - - $data = collect($result['data']['content']); - - $count = $data->count(); - - if ($count === 0) { - break; - } - - $time = now(); - - LinkosDeviceLog::insert( - $data->map(function ($item) use ($time) { - return [ - 'device_id' => $item['device_id'], - 'device_unit' => $item['device_unit'], - 'device_category' => $item['device_category'], - 'data' => ! empty($item['data']) ? json_encode($item['data']) : '{}', - 'reported_at' => $item['createTime'], - 'created_at' => $time->toDateTimeString(), - 'updated_at' => $time->toDateTimeString(), - ]; - })->toArray() - ); - - unset($result, $data); - - $page++; - } while ($count === $size); - } - - /** - * @return \App\Services\LinkosService - */ - protected function linkosService(): LinkosService - { - if ($this->linkosService === null) { - $this->linkosService = app(LinkosService::class); - } - - return $this->linkosService; - } - - /** - * 获取设备最后同步日期 - * - * @param string $device - * @return \Carbon\Carbon|null - */ - protected function getLastDate(string $device): ?Carbon - { - if (is_null($date = Cache::get($this->generateKey($device)))) { - return null; - } - - return Carbon::parse($date); - } - - /** - * 设置设备最后同步日期 - * - * @param string $device - * @param \Carbon\Carbon $date - * @return void - */ - protected function setLastDate(string $device, Carbon $date): void - { - Cache::put($this->generateKey($device), $date->toDateString(), 86400); - } - - /** - * @param string $device - * @return string - */ - protected function generateKey(string $device): string - { - return 'linkos_device_log:'.$device.'_last_sync_date'; - } -} diff --git a/app/Console/Commands/YunFei/WormReportCommand.php b/app/Console/Commands/YunFei/WormReportCommand.php index 2b16c86..27927fb 100644 --- a/app/Console/Commands/YunFei/WormReportCommand.php +++ b/app/Console/Commands/YunFei/WormReportCommand.php @@ -171,7 +171,7 @@ class WormReportCommand extends Command $disk->put($path, file_get_contents($url)); } - WormPhoto::firstOrCreate([ + WormPhoto::updateOrCreate([ 'device_id' => $log->device_id, 'uploaded_at' => $log->reported_at, ], [ diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 4e8a7ea..ebb58f0 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -18,6 +18,10 @@ class Kernel extends ConsoleKernel $schedule->command(Commands\BiAng\WormStatisticsSyncCommand::class) ->hourly() ->runInBackground(); + + $schedule->command(Commands\Linkos\WormReportCommand::class) + ->hourlyAt(15) + ->runInBackground(); } /** diff --git a/app/Exceptions/LinkosFarmException.php b/app/Exceptions/LinkosFarmException.php new file mode 100644 index 0000000..7c51807 --- /dev/null +++ b/app/Exceptions/LinkosFarmException.php @@ -0,0 +1,9 @@ +get('/api/v2.0/entrance/device/getsysUserDevice', [ + 'groupId' => $groupId, + 'deviceType' => $deviceType, + ]); + + return $result['data']; + } + + /** + * 获取用户区域 + */ + public function groups(?string $groupName = null): array + { + $result = $this->get('/api/v2.0/entrance/group/getsysUserGroup', [ + 'groupName' => $groupName, + ]); + + return $result['data']; + } + + /** + * 获取设备实时数据 + */ + public function realTimeData(array $devices = []): array + { + $result = $this->get('/api/v2.0/entrance/device/getRealTimeData', [ + 'deviceAddrs' => implode(',', $devices), + ]); + + return $result['data']; + } + + /** + * 虫情区域统计 + */ + public function wormStatistics(string $groupId, Carbon $beginTime, Carbon $endTime): array + { + $result = $this->get('/api/v2.0/worm/deviceData/getWormStatisticsByGroup', [ + 'groupId' => $groupId, + 'beginTime' => $beginTime->toDateTimeString(), + 'endTime' => $endTime->toDateTimeString(), + ]); + + return $result['data']; + } + + /** + * 虫情设备分析报表 + */ + public function wormAnalyseData(string $device, Carbon $beginTime, Carbon $endTime, int $pages, int $limit = 100): array + { + $result = $this->get('/api/v2.0/worm/deviceData/getWormDataList', [ + 'deviceAddr' => $device, + 'beginTime' => $beginTime->toDateTimeString(), + 'endTime' => $endTime->toDateTimeString(), + 'pages' => $pages, + 'limit' => $limit, + ]); + + return $result['data']; + } + + public function get(string $url, array $query = []): array + { + return $this->request('GET', $url, [ + 'query' => $query, + ]); + } + + public function post(string $url, array $data = []): array + { + return $this->request('POST', $url, [ + 'json' => $data, + ]); + } + + protected function request(string $method, string $url, array $options = []): array + { + $headers = [ + 'Content-Type' => 'application/json', + ]; + if ($url !== '/api/v2.0/entrance/user/userLogin') { + $headers['token'] = $this->token(); + } + + /** @var \Illuminate\Http\Client\Response */ + $response = Http::withHeaders($headers)->baseUrl(self::ENDPOINT_URL)->send($method, $url, $options); + + $json = $response->throw()->json(); + + if (data_get($json, 'code') === 1000) { + return $json; + } + + throw new LinkosFarmException( + data_get($json, 'message', '出错啦'), + data_get($json, 'code', 0), + ); + } + + protected function token(): string + { + if ($this->token && $this->expires > now()->unix() + 30) { + return $this->token; + } + + $result = $this->post('/api/v2.0/entrance/user/userLogin', [ + 'loginName' => $this->loginName, + 'loginPwd' => $this->loginPwd, + ]); + + $this->expires = $result['data']['expDate']; + + return $this->token = $result['data']['token']; + } +}