From 5b69d6758a8e6290636104efadbdae3b3dcc6e82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9D=99?= Date: Tue, 16 May 2023 11:53:48 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=9F=E6=88=90=E5=9C=9F=E5=A3=A4=E7=9B=91?= =?UTF-8?q?=E6=8E=A7=E6=8A=A5=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Commands/DeviceLogDailyReportCommand.php | 88 ++++++----- .../Commands/DeviceLogReportCommand.php | 2 + app/Models/SoilDailyReport.php | 27 ++++ app/Models/SoilReport.php | 1 + app/Services/DeviceLogService.php | 146 ++++++++++++++++-- ...340_add_moisture_to_soil_reports_table.php | 32 ++++ ...103057_create_soil_daily_reports_table.php | 42 +++++ 7 files changed, 292 insertions(+), 46 deletions(-) create mode 100644 app/Models/SoilDailyReport.php create mode 100644 database/migrations/2023_05_15_100340_add_moisture_to_soil_reports_table.php create mode 100644 database/migrations/2023_05_16_103057_create_soil_daily_reports_table.php diff --git a/app/Console/Commands/DeviceLogDailyReportCommand.php b/app/Console/Commands/DeviceLogDailyReportCommand.php index d9609aa..58559d6 100644 --- a/app/Console/Commands/DeviceLogDailyReportCommand.php +++ b/app/Console/Commands/DeviceLogDailyReportCommand.php @@ -5,6 +5,8 @@ namespace App\Console\Commands; use App\Models\Device; use App\Models\MeteorologicalDailyReport; use App\Models\MeteorologicalReport; +use App\Models\SoilDailyReport; +use App\Models\SoilReport; use App\Models\WaterQualityDailyReport; use App\Models\WaterQualityReport; use App\Services\DeviceLogService; @@ -65,51 +67,65 @@ class DeviceLogDailyReportCommand extends Command */ protected function createReportToLinkosDevice(Device $device): void { - $lastReportedAt = null; + [$lastReportedAt, $latestReportedAt] = value(function (Device $device) { + $lastReportedAt = null; - switch ($device->type) { - case Device::TYPE_WATER_QUALITY: - $lastReportedAt = WaterQualityDailyReport::where('device_id', $device->id) - ->latest('reported_at') - ->value('reported_at'); + $latestReportedAt = null; - $lastReportedAt ??= WaterQualityReport::where('device_id', $device->id) - ->oldest('reported_at') - ->value('reported_at'); - break; + switch ($device->type) { + case Device::TYPE_WATER_QUALITY: + $lastReportedAt = WaterQualityDailyReport::where('device_id', $device->id) + ->latest('reported_at') + ->value('reported_at'); - case Device::TYPE_METEOROLOGICAL: - $lastReportedAt = MeteorologicalDailyReport::where('device_id', $device->id) - ->latest('reported_at') - ->value('reported_at'); + $lastReportedAt ??= WaterQualityReport::where('device_id', $device->id) + ->oldest('reported_at') + ->value('reported_at'); - $lastReportedAt ??= MeteorologicalReport::where('device_id', $device->id) - ->oldest('reported_at') - ->value('reported_at'); - break; - } + if ($lastReportedAt) { + $latestReportedAt = WaterQualityReport::where('device_id', $device->id) + ->latest('reported_at') + ->value('reported_at'); + } + break; - if ($lastReportedAt === null) { - return; - } + case Device::TYPE_METEOROLOGICAL: + $lastReportedAt = MeteorologicalDailyReport::where('device_id', $device->id) + ->latest('reported_at') + ->value('reported_at'); - $latestReportedAt = null; + $lastReportedAt ??= MeteorologicalReport::where('device_id', $device->id) + ->oldest('reported_at') + ->value('reported_at'); - switch ($device->type) { - case Device::TYPE_WATER_QUALITY: - $latestReportedAt = WaterQualityReport::where('device_id', $device->id) - ->latest('reported_at') - ->value('reported_at'); - break; + if ($lastReportedAt) { + $latestReportedAt = MeteorologicalReport::where('device_id', $device->id) + ->latest('reported_at') + ->value('reported_at'); + } + break; - case Device::TYPE_METEOROLOGICAL: - $latestReportedAt = MeteorologicalReport::where('device_id', $device->id) - ->latest('reported_at') - ->value('reported_at'); - break; - } + case Device::TYPE_SOIL: + $lastReportedAt = SoilDailyReport::where('device_id', $device->id) + ->latest('reported_at') + ->value('reported_at'); - if ($latestReportedAt === null) { + $lastReportedAt ??= SoilReport::where('device_id', $device->id) + ->oldest('reported_at') + ->value('reported_at'); + + if ($lastReportedAt) { + $latestReportedAt = SoilReport::where('device_id', $device->id) + ->latest('reported_at') + ->value('reported_at'); + } + break; + } + + return [$lastReportedAt, $latestReportedAt]; + }, $device); + + if ($lastReportedAt === null || $latestReportedAt === null) { return; } diff --git a/app/Console/Commands/DeviceLogReportCommand.php b/app/Console/Commands/DeviceLogReportCommand.php index fe1b8fd..6b9a4ef 100644 --- a/app/Console/Commands/DeviceLogReportCommand.php +++ b/app/Console/Commands/DeviceLogReportCommand.php @@ -4,6 +4,7 @@ namespace App\Console\Commands; use App\Models\Device; use App\Models\MeteorologicalReport; +use App\Models\SoilReport; use App\Models\WaterQualityReport; use App\Services\DeviceLogService; use Illuminate\Console\Command; @@ -64,6 +65,7 @@ class DeviceLogReportCommand extends Command protected function createReportToLinkosDevice(Device $device): void { $lastReportedAt = match ($device->type) { + Device::TYPE_SOIL => SoilReport::where('device_id', $device->id)->latest('reported_at')->value('reported_at'), Device::TYPE_WATER_QUALITY => WaterQualityReport::where('device_id', $device->id)->latest('reported_at')->value('reported_at'), Device::TYPE_METEOROLOGICAL => MeteorologicalReport::where('device_id', $device->id)->latest('reported_at')->value('reported_at'), default => null, diff --git a/app/Models/SoilDailyReport.php b/app/Models/SoilDailyReport.php new file mode 100644 index 0000000..b45ad22 --- /dev/null +++ b/app/Models/SoilDailyReport.php @@ -0,0 +1,27 @@ + 'date', + ]; + + protected $fillable = [ + 'device_id', + 'temperature', + 'humidity', + 'n', + 'p', + 'k', + 'conductivity', + 'moisture', + 'reported_at', + ]; +} diff --git a/app/Models/SoilReport.php b/app/Models/SoilReport.php index c68a725..83babbc 100644 --- a/app/Models/SoilReport.php +++ b/app/Models/SoilReport.php @@ -21,6 +21,7 @@ class SoilReport extends Model 'p', 'k', 'conductivity', + 'moisture', 'reported_at', ]; } diff --git a/app/Services/DeviceLogService.php b/app/Services/DeviceLogService.php index 7e89f20..da93993 100644 --- a/app/Services/DeviceLogService.php +++ b/app/Services/DeviceLogService.php @@ -7,6 +7,8 @@ use App\Models\Device; use App\Models\DeviceLog; use App\Models\MeteorologicalDailyReport; use App\Models\MeteorologicalReport; +use App\Models\SoilDailyReport; +use App\Models\SoilReport; use App\Models\WaterQualityDailyReport; use App\Models\WaterQualityReport; use Illuminate\Support\Arr; @@ -144,6 +146,10 @@ class DeviceLogService public function createReportToLinkosDevice(Device $device, Carbon $time): void { switch ($device->type) { + case Device::TYPE_SOIL: + $this->createReportToLinkosSoilDevice($device, $time); + break; + case Device::TYPE_METEOROLOGICAL: $this->createReportToLinkosMeteorologicalDevice($device, $time); break; @@ -154,6 +160,70 @@ class DeviceLogService } } + /** + * 创建 linkos 土壤设备报告 + */ + protected function createReportToLinkosSoilDevice(Device $device, Carbon $time): void + { + $reportedAt = $time->copy()->startOfHour(); + + /** @var \Illuminate\Database\Eloquent\Collection */ + $logs = $device->logs() + ->whereBetween('reported_at', [$reportedAt, $reportedAt->copy()->endOfHour()]) + ->oldest('reported_at') + ->get(); + + if ($logs->isEmpty()) { + return; + } + + $attributes = $logs->reduce(function (array $attributes, DeviceLog $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 = SoilReport::where([ + 'device_id' => $device->id, + 'reported_at' => $reportedAt, + ])->first(); + + if ($soilReport === null) { + $lastSoilReport = SoilReport::where([ + 'device_id' => $device->id, + 'reported_at' => $reportedAt->copy()->subHour(), + ])->first(); + + $soilReport = $lastSoilReport?->replicate() ?: new SoilReport(); + + $soilReport->fill([ + 'device_id' => $device->id, + 'reported_at' => $reportedAt, + ]); + } + + $soilReport->fill($attributes)->save(); + } + /** * 创建 linkos 气象设备报告 */ @@ -299,9 +369,69 @@ class DeviceLogService case Device::TYPE_WATER_QUALITY: $this->createDailyReportToLinkosWaterQualityDevice($device, $time); break; + + case Device::TYPE_SOIL: + $this->createDailyReportToLinkosSoilDevice($device, $time); + break; } } + /** + * 创建 linkos 土壤设备每日报告 + */ + protected function createDailyReportToLinkosSoilDevice(Device $device, Carbon $date): void + { + /** @var \Illuminate\Database\Eloquent\Collection */ + $soilReports = SoilReport::where('device_id', $device->id) + ->whereDate('reported_at', $date) + ->oldest('reported_at') + ->get(); + + if ($soilReports->isEmpty()) { + return; + } + + $attributes = value(function ($soilReports) { + $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 ($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 = SoilDailyReport::firstOrNew([ + 'device_id' => $device->id, + 'reported_at' => $date->format('Y-m-d'), + ]); + + $soilDailyReport->fill($attributes)->save(); + } + /** * 创建 linkos 气象设备每日报告 */ @@ -419,14 +549,12 @@ class DeviceLogService }, $meteorologicalReports); /** @var \App\Models\MeteorologicalDailyReport */ - $meteorologicalDailyReport = MeteorologicalDailyReport::firstOrCreate([ + $meteorologicalDailyReport = MeteorologicalDailyReport::firstOrNew([ 'device_id' => $device->id, 'reported_at' => $date, - ], $attributes); + ]); - if (! $meteorologicalDailyReport->wasRecentlyCreated) { - $meteorologicalDailyReport->update($attributes); - } + $meteorologicalDailyReport->fill($attributes)->save(); } /** @@ -477,13 +605,11 @@ class DeviceLogService }, $waterQualityReports); /** @var \App\Models\WaterQualityDailyReport */ - $waterQualityDailyReport = WaterQualityDailyReport::firstOrCreate([ + $waterQualityDailyReport = WaterQualityDailyReport::firstOrNew([ 'device_id' => $device->id, 'reported_at' => $date->format('Y-m-d'), - ], $attributes); + ]); - if (! $waterQualityDailyReport->wasRecentlyCreated) { - $waterQualityDailyReport->update($attributes); - } + $waterQualityDailyReport->fill($attributes)->save(); } } diff --git a/database/migrations/2023_05_15_100340_add_moisture_to_soil_reports_table.php b/database/migrations/2023_05_15_100340_add_moisture_to_soil_reports_table.php new file mode 100644 index 0000000..a816088 --- /dev/null +++ b/database/migrations/2023_05_15_100340_add_moisture_to_soil_reports_table.php @@ -0,0 +1,32 @@ +decimal('moisture', 8, 2)->nullable()->comment('含水率(单位: %)'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('soil_reports', function (Blueprint $table) { + $table->dropColumn('moisture'); + }); + } +}; diff --git a/database/migrations/2023_05_16_103057_create_soil_daily_reports_table.php b/database/migrations/2023_05_16_103057_create_soil_daily_reports_table.php new file mode 100644 index 0000000..7ac5ef5 --- /dev/null +++ b/database/migrations/2023_05_16_103057_create_soil_daily_reports_table.php @@ -0,0 +1,42 @@ +id(); + $table->unsignedBigInteger('device_id')->comment('设备ID'); + $table->decimal('temperature', 8, 2)->nullable()->comment('温度(单位: ℃)'); + $table->decimal('humidity', 8, 2)->nullable()->comment('湿度(单位: %RH)'); + $table->integer('n')->nullable()->comment('氮 (单位: mg/kg)'); + $table->integer('p')->nullable()->comment('磷 (单位: mg/kg)'); + $table->integer('k')->nullable()->comment('钾 (单位: mg/kg)'); + $table->decimal('conductivity', 8, 2)->nullable()->comment('电导率(单位: us/cm)'); + $table->decimal('moisture', 8, 2)->nullable()->comment('含水率(单位: %)'); + $table->date('reported_at')->comment('报告日期,示例: 2023-05-01'); + $table->timestamps(); + + $table->unique(['device_id', 'reported_at']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('soil_soil_reports'); + } +};