diff --git a/app/Console/Commands/DeviceLogSyncCommand.php b/app/Console/Commands/DeviceLogSyncCommand.php index 8ab58b0..6ccaaae 100644 --- a/app/Console/Commands/DeviceLogSyncCommand.php +++ b/app/Console/Commands/DeviceLogSyncCommand.php @@ -3,7 +3,9 @@ namespace App\Console\Commands; use App\Enums\DeviceType; +use App\Iot\BiAng\HttpClient; use App\Models\Device; +use App\Services\BiAngDeviceLogService; use App\Services\DeviceLogService; use Illuminate\Console\Command; use Throwable; @@ -26,18 +28,11 @@ class DeviceLogSyncCommand extends Command */ protected $description = '按设备厂商同步数据'; - /** - * @var \App\Services\DeviceLogService - */ - protected $deviceLogService; - /** * Execute the console command. */ - public function handle(DeviceLogService $deviceLogService) + public function handle() { - $this->deviceLogService = $deviceLogService; - $factory = $this->argument('factory'); $sleep = (int) value(fn ($sleep) => is_numeric($sleep) ? $sleep : 60, $this->option('sleep')); @@ -77,7 +72,11 @@ class DeviceLogSyncCommand extends Command }); try { - $this->deviceLogService->sync($device, $start, $end); + switch ($factory) { + case 'biang': + (new BiAngDeviceLogService())->sync($device); + break; + } $this->info('同步成功!'); } catch (Throwable $e) { diff --git a/app/Iot/BiAng/HttpClient.php b/app/Iot/BiAng/HttpClient.php new file mode 100644 index 0000000..f415ff3 --- /dev/null +++ b/app/Iot/BiAng/HttpClient.php @@ -0,0 +1,123 @@ +get( + $this->apiUrl('/api/open-api/open/soilMoisture/getCurrentDeviceData'), + [ + 'deviceId' => $deviceId, + ] + ); + + if (data_get($result, 'code') === 200) { + return $result['data']; + } + + throw new RuntimeException( + sprintf("%s:%s", data_get($result, 'code', 500), data_get($result, 'msg', '出错啦!')) + ); + } + + /** + * 获取最新的气象数据 + */ + public function getLatestMeteorologicalReport(string $deviceId) + { + $result = $this->get( + $this->apiUrl('/api/open-api/open/weather/getCurrentDeviceData'), + [ + 'deviceId' => $deviceId, + ] + ); + + if (data_get($result, 'code') === 200) { + return $result['data']; + } + + throw new RuntimeException( + sprintf("%s:%s", data_get($result, 'code', 500), data_get($result, 'msg', '出错啦!')) + ); + } + + public function get(string $url, array $query = []): array + { + return $this->request('GET', $url, [ + 'query' => $query, + ]); + } + + /** + * @param string $url + * @param array $data + * @return array + */ + public function post(string $url, array $data = []): array + { + return $this->request('POST', $url, [ + 'json' => $data, + ]); + } + + /** + * @param string $method + * @param string $url + * @param array $options + * @return array + * + * @throws \Illuminate\Http\Client\RequestException + * @throws \RuntimeException + */ + public function request(string $method, string $url, array $options = []): array + { + switch (strtoupper($method)) { + case 'GET': + $options['query'] = array_merge($options['query'], [ + 'username' => $this->username, + 'password' => $this->password, + ]); + break; + + case 'POST': + $options['json'] = array_merge($options['json'], [ + 'username' => $this->username, + 'password' => $this->password, + ]); + break; + } + + /** @var \Illuminate\Http\Client\Response */ + $response = Http::withHeaders([ + 'Content-Type' => 'application/json', + ])->send($method, $url, $options); + + return $response->throw()->json(); + } + + protected function apiUrl(string $path): string + { + return 'http://yun.bigdata5s.com'.Str::start($path, '/'); + } + + protected function apiUrl2(string $path): string + { + return 'http://yun-api.bigdata5s.com'.Str::start($path, '/'); + } +} diff --git a/app/Services/DeviceLogService.php b/app/Services/DeviceLogService.php deleted file mode 100644 index 6fec821..0000000 --- a/app/Services/DeviceLogService.php +++ /dev/null @@ -1,599 +0,0 @@ -supplier?->key) { - case 'linkos': - $this->syncLinkosDeviceLogs($device, $start, $end); - break; - case 'biang': - // $this->syncLinkosDeviceLogs($device, $start, $end); - break; - } - } - - /** - * 同步 Linkos 设备历史流水 - */ - protected function syncLinkosDeviceLogs(Device $device, Carbon $start, Carbon $end): void - { - /** @var \App\Iot\Linkos\HttpClient */ - $httpClient = app(LinkosHttpClient::class); - - $page = 1; - - $perPage = 50; - - do { - $data = $httpClient->deviceFlowList( - $device->sn, $start, $end, $page, $perPage - ); - - $countResults = count($data['content']); - - if ($countResults === 0) { - break; - } - - foreach ($data['content'] as $item) { - if (! isset($item['data'])) { - continue; - } - - // 如果多合一气象监测器包含土壤监控时,需过滤掉气象监控的数据 - if ($device->isTypeSoil() && Arr::hasAny($item['data'], [ - 'current_rainfall', - 'day_rainfall', - 'accumulate_rainfall', - 'moment_rainfall', - 'pm10_concentration', - 'pm25_concentration', - 'box_illumination', - 'box_pressure', - 'box_carbon', - 'box_temperature', - 'box_humidity', - 'box_noise', - 'wind_degree', - 'wind_direction', - 'wind_power', - 'wind_speed', - ])) { - continue; - } - - LinkosDeviceLog::firstOrCreate([ - 'device_id' => $device->sn, - 'reported_at' => $item['createTime'], - ], [ - 'device_unit' => $device->model, - 'data' => empty($item['data']) ? (new \stdClass) : $item['data'], - ]); - } - - unset($data); - - $page++; - } while ($countResults === $perPage); - } - - /** - * 创建 linkos 设备报告 - */ - public function createReportToLinkosDevice(Device $device, Carbon $time): void - { - switch ($device->type) { - case DeviceType::Soil: - $this->createReportToLinkosSoilDevice($device, $time); - break; - - case DeviceType::Meteorological: - $this->createReportToLinkosMeteorologicalDevice($device, $time); - break; - - case DeviceType::WaterQuality: - $this->createReportToLinkosWaterQualityDevice($device, $time); - break; - } - } - - /** - * 创建 linkos 土壤设备报告 - */ - protected function createReportToLinkosSoilDevice(Device $device, Carbon $time): void - { - $reportedAt = $time->copy()->startOfHour(); - - /** @var \Illuminate\Database\Eloquent\Collection */ - $logs = LinkosDeviceLog::where('device_id', $device->sn) - ->whereBetween('reported_at', [$reportedAt, $reportedAt->copy()->endOfHour()]) - ->oldest('reported_at') - ->get(); - - if ($logs->isEmpty()) { - return; - } - - $attributes = $logs->reduce(function (array $attributes, LinkosDeviceLog $log) { - if (is_array($data = $log->data)) { - foreach ($data as $k => $v) { - $attribute = match ($k) { - 'nitrogen_content' => 'n', - 'potassium_content' => 'k', - 'phosphorus_content' => 'p', - 'electroconductibility' => 'conductivity', - 'temperature' => 'temperature', - 'moisture_content' => 'moisture', - 'conductivity' => 'conductivity', - 'soil_humidity' => 'humidity', - 'soil_temperature' => 'temperature', - default => null, - }; - - if ($attribute) { - $attributes[$attribute] = $v; - } - } - } - - return $attributes; - }, []); - - $soilReport = SoilMonitoringLog::where([ - 'device_id' => $device->id, - 'monitored_at' => $reportedAt, - ])->first(); - - if ($soilReport === null) { - $lastSoilReport = SoilMonitoringLog::where([ - 'device_id' => $device->id, - 'monitored_at' => $reportedAt->copy()->subHour(), - ])->first(); - - $soilReport = $lastSoilReport?->replicate() ?: new SoilMonitoringLog(); - - $soilReport->fill([ - 'device_id' => $device->id, - 'monitored_at' => $reportedAt, - 'agricultural_base_id' => $device->agricultural_base_id, - ]); - } - - $soilReport->fill($attributes)->save(); - } - - /** - * 创建 linkos 气象设备报告 - */ - protected function createReportToLinkosMeteorologicalDevice(Device $device, Carbon $time): void - { - $reportedAt = $time->copy()->startOfHour(); - - /** @var \Illuminate\Database\Eloquent\Collection */ - $logs = LinkosDeviceLog::where('device_id', $device->sn) - ->whereBetween('reported_at', [$reportedAt, $reportedAt->copy()->endOfHour()]) - ->oldest('reported_at') - ->get(); - - if ($logs->isEmpty()) { - return; - } - - $attributes = $logs->reduce(function (array $attributes, LinkosDeviceLog $log) { - if (is_array($data = $log->data)) { - foreach ($data as $k => $v) { - $attribute = match ($k) { - // 'day_rainfall' => 'yesterday_rainfall', - 'current_rainfall' => 'current_rainfall', - 'accumulate_rainfall' => 'accumulated_rainfall', - 'moment_rainfall' => 'moment_rainfall', - 'pm10_concentration' => 'pm10', - 'pm25_concentration' => 'pm25', - 'box_illumination' => 'illumination', - 'box_pressure' => 'air_pressure', - 'box_carbon' => 'co2', - 'box_temperature' => 'air_temperature', - 'box_humidity' => 'air_humidity', - 'box_noise' => 'noise', - 'wind_degree' => 'wind_degree', - 'wind_direction' => 'wind_direction', - 'wind_power' => 'wind_power', - 'wind_speed' => 'wind_speed', - default => null, - }; - - if ($attribute) { - $attributes[$attribute] = $v; - } - } - } - - return $attributes; - }, []); - - $meteorologicalReport = MeteorologicalMonitoringLog::where([ - 'device_id' => $device->id, - 'monitored_at' => $reportedAt, - ])->first(); - - if ($meteorologicalReport === null) { - $lastMeteorologicalReport = MeteorologicalMonitoringLog::where([ - 'device_id' => $device->id, - 'monitored_at' => $reportedAt->copy()->subHour(), - ])->first(); - - $meteorologicalReport = $lastMeteorologicalReport?->replicate() ?: new MeteorologicalMonitoringLog(); - - $meteorologicalReport->fill([ - 'device_id' => $device->id, - 'monitored_at' => $reportedAt, - 'agricultural_base_id' => $device->agricultural_base_id, - ]); - } - - $meteorologicalReport->fill($attributes)->save(); - } - - /** - * 创建 linkos 水质设备报告 - */ - protected function createReportToLinkosWaterQualityDevice(Device $device, Carbon $time): void - { - $reportedAt = $time->copy()->startOfHour(); - - /** @var \Illuminate\Database\Eloquent\Collection */ - $logs = LinkosDeviceLog::where('device_id', $device->sn) - ->whereBetween('reported_at', [$reportedAt, $reportedAt->copy()->endOfHour()]) - ->oldest('reported_at') - ->get(); - - if ($logs->isEmpty()) { - return; - } - - $attributes = $logs->reduce(function (array $attributes, LinkosDeviceLog $log) { - if (is_array($data = $log->data)) { - foreach ($data as $k => $v) { - $attribute = match ($k) { - 'chlorine' => 'chlorine', - 'conductivity' => 'conductivity', - 'oxygen' => 'oxygen', - 'ph' => 'ph', - 'temp' => 'temperature', - 'turbidity' => 'turbidity', - default => null, - }; - - if ($attribute) { - $attributes[$attribute] = $v; - } - } - } - - return $attributes; - }, []); - - $waterQualityReport = WaterQualityMonitoringLog::where([ - 'device_id' => $device->id, - 'monitored_at' => $reportedAt, - ])->first(); - - if ($waterQualityReport === null) { - $lastWaterQualityReport = WaterQualityMonitoringLog::where([ - 'device_id' => $device->id, - 'monitored_at' => $reportedAt->copy()->subHour(), - ])->first(); - - $waterQualityReport = $lastWaterQualityReport?->replicate() ?: new WaterQualityMonitoringLog(); - - $waterQualityReport->fill([ - 'device_id' => $device->id, - 'monitored_at' => $reportedAt, - 'agricultural_base_id' => $device->agricultural_base_id, - ]); - } - - $waterQualityReport->fill($attributes)->save(); - } - - /** - * 创建 linkos 设备每日报告 - */ - public function createDailyReportToLinkosDevice(Device $device, Carbon $time): void - { - switch ($device->type) { - case DeviceType::Meteorological: - $this->createDailyReportToLinkosMeteorologicalDevice($device, $time); - break; - - case DeviceType::WaterQuality: - $this->createDailyReportToLinkosWaterQualityDevice($device, $time); - break; - - case DeviceType::Soil: - $this->createDailyReportToLinkosSoilDevice($device, $time); - break; - } - } - - /** - * 创建 linkos 土壤设备每日报告 - */ - protected function createDailyReportToLinkosSoilDevice(Device $device, Carbon $date): void - { - /** @var \Illuminate\Database\Eloquent\Collection */ - $soilReports = SoilMonitoringLog::where('device_id', $device->id) - ->whereDate('monitored_at', $date) - ->oldest('monitored_at') - ->get(); - - if ($soilReports->isEmpty()) { - return; - } - - $attributes = value(function ($soilReports) { - $data = [ - 'n' => ['sum' => 0, 'count' => 0], - 'p' => ['sum' => 0, 'count' => 0], - 'k' => ['sum' => 0, 'count' => 0], - 'conductivity' => ['sum' => 0, 'count' => 0], - 'temperature' => ['sum' => 0, 'count' => 0], - 'humidity' => ['sum' => 0, 'count' => 0], - 'moisture' => ['sum' => 0, 'count' => 0], - ]; - - foreach ($soilReports as $soilReport) { - foreach ($data as $k => $item) { - if (is_null($v = $soilReport->{$k})) { - continue; - } - - $item['sum'] = bcadd($item['sum'], $v, 2); - $item['count']++; - - $data[$k] = $item; - } - } - - $attributes = []; - - foreach ($data as $key => $item) { - $attributes[$key] = $item['count'] > 0 ? round(bcdiv($item['sum'], $item['count'], 2), 2) : null; - } - - return $attributes; - }, $soilReports); - - /** @var \App\Models\SoilDailyReport */ - $soilDailyReport = SoilMonitoringDailyLog::firstOrNew([ - 'device_id' => $device->id, - 'monitored_at' => $date->format('Y-m-d'), - ], [ - 'agricultural_base_id' => $device->agricultural_base_id, - ]); - - $soilDailyReport->fill($attributes)->save(); - } - - /** - * 创建 linkos 气象设备每日报告 - */ - protected function createDailyReportToLinkosMeteorologicalDevice(Device $device, Carbon $date): void - { - /** @var \Illuminate\Database\Eloquent\Collection */ - $meteorologicalReports = MeteorologicalMonitoringLog::where('device_id', $device->id) - ->whereDate('monitored_at', $date) - ->oldest('monitored_at') - ->get(); - - if ($meteorologicalReports->isEmpty()) { - return; - } - - $attributes = value(function ($meteorologicalReports) { - $data = [ - 'current_rainfall' => 0, - 'accumulated_rainfall' => ['sum' => 0, 'count' => 0], - 'moment_rainfall' => ['sum' => 0, 'count' => 0], - 'pm10' => ['sum' => 0, 'count' => 0], - 'pm25' => ['sum' => 0, 'count' => 0], - 'illumination' => ['sum' => 0, 'count' => 0], - 'air_pressure' => ['sum' => 0, 'count' => 0], - 'co2' => ['sum' => 0, 'count' => 0], - 'air_temperature' => ['sum' => 0, 'count' => 0], - 'air_humidity' => ['sum' => 0, 'count' => 0], - 'noise' => ['sum' => 0, 'count' => 0], - 'wind_speed' => ['sum' => 0, 'count' => 0], - 'wind_samples' => [], - ]; - - foreach ($meteorologicalReports as $meteorologicalReport) { - foreach ($data as $k => $item) { - if ($k === 'wind_samples') { - if (is_null($meteorologicalReport->wind_degree) || is_null($meteorologicalReport->wind_speed)) { - continue; - } - - $item[] = [ - 'wind_degree' => $meteorologicalReport->wind_degree, // 风向度数 - 'wind_speed' => $meteorologicalReport->wind_speed, // 风速 - ]; - } elseif (! is_null($v = $meteorologicalReport->{$k})) { - if ($k === 'current_rainfall') { - $item = $v; - } else { - $item['sum'] = bcadd($item['sum'], $v, 2); - $item['count']++; - } - } - - $data[$k] = $item; - } - } - - $attributes = []; - - foreach ($data as $key => $item) { - switch ($key) { - case 'current_rainfall': - $attributes['daily_rainfall'] = $item; - break; - case 'wind_samples': - if (! empty($item)) { - $attributes['wind_degree'] = value(function (array $windSamples) { - if (empty($windSamples)) { - return null; - } - - $x = 0; - $y = 0; - - foreach ($windSamples as $sample) { - if ($sample['wind_degree'] == 0 && $sample['wind_speed'] == 0) { - continue; - } - - // 角度转弧度 - $radian = deg2rad($sample['wind_degree']); - - // $x += $sample['wind_speed'] * sin($radian); - // $y += $sample['wind_speed'] * cos($radian); - $x += sin($radian); - $y += cos($radian); - } - - $degree = round(rad2deg(atan2($y, $x))); - - if (($x > 0 || $x < 0) && $y < 0) { - $degree += 180; - } elseif ($x < 0 && $y > 0) { - $degree += 360; - } - - return $degree; - }, $item); - - $attributes['wind_direction'] = value(function ($windDegree) { - if (is_null($windDegree)) { - return null; - } - - if ($windDegree >= 22.5 && $windDegree < 67.5) { - return MeteorologicalMonitoringDailyLog::WIND_DIRECTION_NORTHEAST; - } elseif ($windDegree >= 67.5 && $windDegree < 112.5) { - return MeteorologicalMonitoringDailyLog::WIND_DIRECTION_EAST; - } elseif ($windDegree >= 112.5 && $windDegree < 157.5) { - return MeteorologicalMonitoringDailyLog::WIND_DIRECTION_SOUTHEAST; - } elseif ($windDegree >= 157.5 && $windDegree < 202.5) { - return MeteorologicalMonitoringDailyLog::WIND_DIRECTION_SOUTH; - } elseif ($windDegree >= 202.5 && $windDegree < 247.5) { - return MeteorologicalMonitoringDailyLog::WIND_DIRECTION_SOUTHWEST; - } elseif ($windDegree >= 247.5 && $windDegree < 292.5) { - return MeteorologicalMonitoringDailyLog::WIND_DIRECTION_WEST; - } elseif ($windDegree >= 292.5 && $windDegree < 337.5) { - return MeteorologicalMonitoringDailyLog::WIND_DIRECTION_NORTHWEST; - } - - return MeteorologicalMonitoringDailyLog::WIND_DIRECTION_NORTH; - }, $attributes['wind_degree']); - } - break; - - default: - $attributes[$key] = $item['count'] > 0 ? round(bcdiv($item['sum'], $item['count'], 2), 2) : null; - break; - } - } - - return $attributes; - }, $meteorologicalReports); - - /** @var \App\Models\MeteorologicalMonitoringDailyLog */ - $meteorologicalDailyReport = MeteorologicalMonitoringDailyLog::firstOrNew([ - 'device_id' => $device->id, - 'monitored_at' => $date, - ], [ - 'agricultural_base_id' => $device->agricultural_base_id, - ]); - - $meteorologicalDailyReport->fill($attributes)->save(); - } - - /** - * 创建 linkos 水质设备每日报告 - */ - protected function createDailyReportToLinkosWaterQualityDevice(Device $device, Carbon $date): void - { - /** @var \Illuminate\Database\Eloquent\Collection */ - $waterQualityReports = WaterQualityMonitoringLog::where('device_id', $device->id) - ->whereDate('monitored_at', $date) - ->oldest('monitored_at') - ->get(); - - if ($waterQualityReports->isEmpty()) { - return; - } - - $attributes = value(function ($waterQualityReports) { - $data = [ - 'chlorine' => ['sum' => 0, 'count' => 0], - 'conductivity' => ['sum' => 0, 'count' => 0], - 'oxygen' => ['sum' => 0, 'count' => 0], - 'ph' => ['sum' => 0, 'count' => 0], - 'temperature' => ['sum' => 0, 'count' => 0], - 'turbidity' => ['sum' => 0, 'count' => 0], - ]; - - foreach ($waterQualityReports as $waterQualityReport) { - foreach ($data as $k => $item) { - if (is_null($v = $waterQualityReport->{$k})) { - continue; - } - - $item['sum'] = bcadd($item['sum'], $v, 2); - $item['count']++; - - $data[$k] = $item; - } - } - - $attributes = []; - - foreach ($data as $key => $item) { - $attributes[$key] = $item['count'] > 0 ? round(bcdiv($item['sum'], $item['count'], 2), 2) : null; - } - - return $attributes; - }, $waterQualityReports); - - /** @var \App\Models\WaterQualityMonitoringDailyLog */ - $WaterQualityMonitoringDailyLog = WaterQualityMonitoringDailyLog::firstOrNew([ - 'device_id' => $device->id, - 'monitored_at' => $date->format('Y-m-d'), - ], [ - 'agricultural_base_id' => $device->agricultural_base_id, - ]); - - $WaterQualityMonitoringDailyLog->fill($attributes)->save(); - } -} diff --git a/database/seeders/KeywordsTableSeeder.php b/database/seeders/KeywordsTableSeeder.php index d79e179..a55c009 100644 --- a/database/seeders/KeywordsTableSeeder.php +++ b/database/seeders/KeywordsTableSeeder.php @@ -41,7 +41,16 @@ class KeywordsTableSeeder extends Seeder ['key' => 'biang', 'name' => '比昂', 'value' => ''], ['key' => 'other', 'name' => '其它', 'value' => ''], ], - ] + ], + [ + 'key' => 'application', + 'name' => '应用', + 'value' => '', + 'list' => [ + ['key' => 'linkos', 'name' => '成渝现代高效特色农业带合作园区', 'value' => ''], + ['key' => 'other', 'name' => '其它', 'value' => ''], + ], + ], ]; $list[] = value(function () {