diff --git a/app/Console/Commands/DeviceLogReportCommand.php b/app/Console/Commands/DeviceLogReportCommand.php index 6689010..35e961c 100644 --- a/app/Console/Commands/DeviceLogReportCommand.php +++ b/app/Console/Commands/DeviceLogReportCommand.php @@ -3,9 +3,9 @@ namespace App\Console\Commands; use App\Models\Device; -use App\Models\MeteorologicalMonitoringReport; -use App\Models\SoilMonitoringReport; -use App\Models\WaterQualityMonitoringReport; +use App\Models\MeteorologicalReport; +use App\Models\SoilReport; +use App\Models\WaterQualityReport; use Illuminate\Console\Command; use Illuminate\Support\Arr; @@ -18,14 +18,14 @@ class DeviceLogReportCommand extends Command */ protected $signature = 'device-log:report {factory} - {--sleep=300 : 监控报告生成后的休眠时间(秒)}'; + {--sleep=300 : 监控报告生产后的休眠时间(秒)}'; /** * The console command description. * * @var string */ - protected $description = '按厂家生成设备监控报告'; + protected $description = '按设备厂商生成监控报告'; /** * Execute the console command. @@ -41,157 +41,236 @@ class DeviceLogReportCommand extends Command $devices = Device::with(['factory'])->poweredBy($factory)->get(); foreach ($devices as $device) { - $this->createMonitoringReport($device); + switch ($device->factory?->key) { + case 'link-os': + $this->createReportToLinkosDevice($device); + break; + } } sleep($sleep); }; } - protected function createMonitoringReport(Device $device) - { - switch ($device->factory?->key) { - case 'link-os': - $this->createLinkosDeviceMonitoringReport($device); - break; - } - } - - protected function createLinkosDeviceMonitoringReport(Device $device) + protected function createReportToLinkosDevice(Device $device): void { switch ($device->type) { case Device::TYPE_SOIL: - $lastMonitoringReport = SoilMonitoringReport::where('device_id', $device->id) - ->latest('reported_at') - ->first(); - - $lastReportedAt = $lastMonitoringReport?->reported_at; - - $device->logs() - ->when($lastReportedAt, fn ($query, $lastReportedAt) => $query->where('reported_at', '>=', $lastReportedAt)) - ->oldest('reported_at') - ->chunkById(500, function ($deviceLogs) use ($device, &$lastMonitoringReport) { - /** @var \App\Models\DeviceLog */ - foreach ($deviceLogs as $deviceLog) { - $monitoringReport = SoilMonitoringReport::firstOrCreate( - [ - 'device_id' => $device->id, - 'reported_at' => $deviceLog->reported_at->startOfHour(), - ], - Arr::except($lastMonitoringReport?->setHidden([])?->attributesToArray() ?: [], ['reported_at']) - ); - - foreach ([ - 'conductivity' => 'conductivity', - 'soil_humidity' => 'humidity', - 'soil_temperature' => 'temperature', - 'nitrogen_content' => 'n', - 'potassium_content' => 'k', - 'phosphorus_content' => 'p', - ] as $key => $attribute) { - if (! is_array($deviceLog->data) || ! array_key_exists($key, $deviceLog->data)) { - continue; - } - - $monitoringReport->{$attribute} = $deviceLog->data[$key]; - } - - $lastMonitoringReport = tap($monitoringReport)->save(); - } - }); - break; - - case Device::TYPE_WATER_QUALITY: - $lastMonitoringReport = WaterQualityMonitoringReport::where('device_id', $device->id) - ->latest('reported_at') - ->first(); - - $lastReportedAt = $lastMonitoringReport?->reported_at; - - $device->logs() - ->when($lastReportedAt, fn ($query, $lastReportedAt) => $query->where('reported_at', '>=', $lastReportedAt)) - ->oldest('reported_at') - ->chunkById(500, function ($deviceLogs) use ($device, &$lastMonitoringReport) { - /** @var \App\Models\DeviceLog */ - foreach ($deviceLogs as $deviceLog) { - $monitoringReport = WaterQualityMonitoringReport::firstOrCreate( - [ - 'device_id' => $device->id, - 'reported_at' => $deviceLog->reported_at->startOfHour(), - ], - Arr::except($lastMonitoringReport?->setHidden([])?->attributesToArray() ?: [], ['reported_at']) - ); - - foreach ([ - 'chlorine' => 'chlorine', - 'conductivity' => 'conductivity', - 'oxygen' => 'oxygen', - 'ph' => 'ph', - 'temp' => 'temperature', - 'turbidity' => 'turbidity', - ] as $key => $attribute) { - if (! is_array($deviceLog->data) || ! array_key_exists($key, $deviceLog->data)) { - continue; - } - - $monitoringReport->{$attribute} = $deviceLog->data[$key]; - } - - $lastMonitoringReport = tap($monitoringReport)->save(); - } - }); + $this->createReportToLinkosSoilDevice($device); break; case Device::TYPE_METEOROLOGICAL: - $lastMonitoringReport = MeteorologicalMonitoringReport::where('device_id', $device->id) - ->latest('reported_at') - ->first(); + $this->createReportToLinkosMeteorologicalDevice($device); + break; - $lastReportedAt = $lastMonitoringReport?->reported_at; - - $device->logs() - ->when($lastReportedAt, fn ($query, $lastReportedAt) => $query->where('reported_at', '>=', $lastReportedAt)) - ->oldest('reported_at') - ->chunkById(500, function ($deviceLogs) use ($device, &$lastMonitoringReport) { - /** @var \App\Models\DeviceLog */ - foreach ($deviceLogs as $deviceLog) { - $monitoringReport = MeteorologicalMonitoringReport::firstOrCreate( - [ - 'device_id' => $device->id, - 'reported_at' => $deviceLog->reported_at->startOfHour(), - ], - Arr::except($lastMonitoringReport?->setHidden([])?->attributesToArray() ?: [], ['reported_at']) - ); - - foreach ([ - 'current_rainfall' => 'today_rainfall', - 'day_rainfall' => 'yesterday_rainfall', - 'accumulate_rainfall' => 'accumulate_rainfall', - 'moment_rainfall' => 'moment_rainfall', - 'pm10_concentration' => 'pm10', - 'pm25_concentration' => 'pm25', - 'box_illumination' => 'box_illumination', - 'box_pressure' => 'box_pressure', - 'box_carbon' => 'box_co2', - 'box_temperature' => 'box_temperature', - 'box_humidity' => 'box_humidity', - 'box_noise' => 'box_noise', - 'wind_degree' => 'wind_degree', - 'wind_direction' => 'wind_direction', - 'wind_power' => 'wind_power', - 'wind_speed' => 'wind_speed', - ] as $key => $attribute) { - if (! is_array($deviceLog->data) || ! array_key_exists($key, $deviceLog->data)) { - continue; - } - - $monitoringReport->{$attribute} = $deviceLog->data[$key]; - } - - $lastMonitoringReport = tap($monitoringReport)->save(); - } - }); + case Device::TYPE_WATER_QUALITY: + $this->createReportToLinkosWaterQualityDevice($device); break; } } + + protected function createReportToLinkosSoilDevice(Device $device): void + { + $lastSoilReport = SoilReport::where('device_id', $device->id) + ->latest('reported_at') + ->first(); + + $lastReportedAt = $lastSoilReport?->reported_at + ?: $device->logs()->oldest('reported_at')->value('reported_at'); + + if ($lastReportedAt === null) { + return; + } + + $latestReportedAt = $device->logs()->latest('reported_at')->value('reported_at'); + + if ($latestReportedAt === null) { + return; + } + + /** @var \Carbon\Carbon */ + $startAt = $lastReportedAt->copy()->startOfHour(); + /** @var \Carbon\Carbon */ + $endAt = $latestReportedAt->copy()->startOfHour(); + + do { + /** @var \Illuminate\Database\Eloquent\Collection */ + $logs = $device->logs() + ->whereBetween('reported_at', [$startAt, $startAt->copy()->endOfHour()]) + ->oldest('reported_at') + ->get(); + + if ($logs->isNotEmpty()) { + $soilReport = SoilReport::firstOrCreate( + [ + 'device_id' => $device->id, + 'reported_at' => $startAt, + ], + Arr::except($lastSoilReport?->setHidden([])?->attributesToArray() ?: [], ['reported_at']) + ); + + /** @var \App\Models\DeviceLog */ + foreach ($logs as $log) { + foreach ([ + 'conductivity' => 'conductivity', + 'soil_humidity' => 'humidity', + 'soil_temperature' => 'temperature', + 'nitrogen_content' => 'n', + 'potassium_content' => 'k', + 'phosphorus_content' => 'p', + ] as $key => $attribute) { + if (! is_array($log->data) || ! array_key_exists($key, $log->data)) { + continue; + } + + $soilReport->{$attribute} = $log->data[$key]; + } + + $lastSoilReport = tap($soilReport)->save(); + } + } + + $startAt->addHour(); + } while ($endAt->gte($startAt)); + } + + protected function createReportToLinkosMeteorologicalDevice(Device $device): void + { + $lastMeteorologicalReport = MeteorologicalReport::where('device_id', $device->id) + ->latest('reported_at') + ->first(); + + $lastReportedAt = $lastMeteorologicalReport?->reported_at + ?: $device->logs()->oldest('reported_at')->value('reported_at'); + + if ($lastReportedAt === null) { + return; + } + + $latestReportedAt = $device->logs()->latest('reported_at')->value('reported_at'); + + if ($latestReportedAt === null) { + return; + } + + /** @var \Carbon\Carbon */ + $startAt = $lastReportedAt->copy()->startOfHour(); + /** @var \Carbon\Carbon */ + $endAt = $latestReportedAt->copy()->startOfHour(); + + do { + /** @var \Illuminate\Database\Eloquent\Collection */ + $logs = $device->logs() + ->whereBetween('reported_at', [$startAt, $startAt->copy()->endOfHour()]) + ->oldest('reported_at') + ->get(); + + if ($logs->isNotEmpty()) { + $meteorologicalReport = MeteorologicalReport::firstOrCreate( + [ + 'device_id' => $device->id, + 'reported_at' => $startAt, + ], + Arr::except($lastMeteorologicalReport?->setHidden([])?->attributesToArray() ?: [], ['reported_at']) + ); + + /** @var \App\Models\DeviceLog */ + foreach ($logs as $log) { + foreach ([ + 'current_rainfall' => 'today_rainfall', + 'day_rainfall' => 'yesterday_rainfall', + 'accumulate_rainfall' => 'accumulate_rainfall', + 'moment_rainfall' => 'moment_rainfall', + 'pm10_concentration' => 'pm10', + 'pm25_concentration' => 'pm25', + 'box_illumination' => 'box_illumination', + 'box_pressure' => 'box_pressure', + 'box_carbon' => 'box_co2', + 'box_temperature' => 'box_temperature', + 'box_humidity' => 'box_humidity', + 'box_noise' => 'box_noise', + 'wind_degree' => 'wind_degree', + 'wind_direction' => 'wind_direction', + 'wind_power' => 'wind_power', + 'wind_speed' => 'wind_speed', + ] as $key => $attribute) { + if (! is_array($log->data) || ! array_key_exists($key, $log->data)) { + continue; + } + + $meteorologicalReport->{$attribute} = $log->data[$key]; + } + + $lastMeteorologicalReport = tap($meteorologicalReport)->save(); + } + } + + $startAt->addHour(); + } while ($endAt->gte($startAt)); + } + + protected function createReportToLinkosWaterQualityDevice(Device $device): void + { + $lastWaterQualityReport = WaterQualityReport::where('device_id', $device->id) + ->latest('reported_at') + ->first(); + + $lastReportedAt = $lastWaterQualityReport?->reported_at + ?: $device->logs()->oldest('reported_at')->value('reported_at'); + + if ($lastReportedAt === null) { + return; + } + + $latestReportedAt = $device->logs()->latest('reported_at')->value('reported_at'); + + if ($latestReportedAt === null) { + return; + } + + /** @var \Carbon\Carbon */ + $startAt = $lastReportedAt->copy()->startOfHour(); + /** @var \Carbon\Carbon */ + $endAt = $latestReportedAt->copy()->startOfHour(); + + do { + /** @var \Illuminate\Database\Eloquent\Collection */ + $logs = $device->logs() + ->whereBetween('reported_at', [$startAt, $startAt->copy()->endOfHour()]) + ->oldest('reported_at') + ->get(); + + if ($logs->isNotEmpty()) { + $waterQualityReport = WaterQualityReport::firstOrCreate( + [ + 'device_id' => $device->id, + 'reported_at' => $startAt, + ], + Arr::except($lastWaterQualityReport?->setHidden([])?->attributesToArray() ?: [], ['reported_at']) + ); + + /** @var \App\Models\DeviceLog */ + foreach ($logs as $log) { + foreach ([ + 'chlorine' => 'chlorine', + 'conductivity' => 'conductivity', + 'oxygen' => 'oxygen', + 'ph' => 'ph', + 'temp' => 'temperature', + 'turbidity' => 'turbidity', + ] as $key => $attribute) { + if (! is_array($log->data) || ! array_key_exists($key, $log->data)) { + continue; + } + + $waterQualityReport->{$attribute} = $log->data[$key]; + } + + $lastWaterQualityReport = tap($waterQualityReport)->save(); + } + } + + $startAt->addHour(); + } while ($endAt->gte($startAt)); + } } diff --git a/app/Models/MeteorologicalMonitoringReport.php b/app/Models/MeteorologicalReport.php similarity index 93% rename from app/Models/MeteorologicalMonitoringReport.php rename to app/Models/MeteorologicalReport.php index 904425f..1830f27 100644 --- a/app/Models/MeteorologicalMonitoringReport.php +++ b/app/Models/MeteorologicalReport.php @@ -5,7 +5,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; -class MeteorologicalMonitoringReport extends Model +class MeteorologicalReport extends Model { use HasFactory; diff --git a/app/Models/SoilMonitoringReport.php b/app/Models/SoilReport.php similarity index 90% rename from app/Models/SoilMonitoringReport.php rename to app/Models/SoilReport.php index bdba671..c68a725 100644 --- a/app/Models/SoilMonitoringReport.php +++ b/app/Models/SoilReport.php @@ -5,7 +5,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; -class SoilMonitoringReport extends Model +class SoilReport extends Model { use HasFactory; diff --git a/app/Models/WaterQualityMonitoringReport.php b/app/Models/WaterQualityReport.php similarity index 89% rename from app/Models/WaterQualityMonitoringReport.php rename to app/Models/WaterQualityReport.php index b943b45..4f2ee44 100644 --- a/app/Models/WaterQualityMonitoringReport.php +++ b/app/Models/WaterQualityReport.php @@ -5,7 +5,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; -class WaterQualityMonitoringReport extends Model +class WaterQualityReport extends Model { use HasFactory; diff --git a/database/migrations/2023_05_06_103057_create_soil_monitoring_reports_table.php b/database/migrations/2023_05_06_103057_create_soil_reports_table.php similarity index 90% rename from database/migrations/2023_05_06_103057_create_soil_monitoring_reports_table.php rename to database/migrations/2023_05_06_103057_create_soil_reports_table.php index daee1b2..be40d9d 100644 --- a/database/migrations/2023_05_06_103057_create_soil_monitoring_reports_table.php +++ b/database/migrations/2023_05_06_103057_create_soil_reports_table.php @@ -13,7 +13,7 @@ return new class extends Migration */ public function up() { - Schema::create('soil_monitoring_reports', function (Blueprint $table) { + Schema::create('soil_reports', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('device_id')->comment('设备ID'); $table->decimal('temperature', 8, 2)->nullable()->comment('温度(单位: ℃)'); @@ -36,6 +36,6 @@ return new class extends Migration */ public function down() { - Schema::dropIfExists('soil_monitoring_reports'); + Schema::dropIfExists('soil_reports'); } }; diff --git a/database/migrations/2023_05_06_103152_create_water_quality_monitoring_reports_table.php b/database/migrations/2023_05_06_103152_create_water_quality_reports_table.php similarity index 89% rename from database/migrations/2023_05_06_103152_create_water_quality_monitoring_reports_table.php rename to database/migrations/2023_05_06_103152_create_water_quality_reports_table.php index ac51ed3..43bf7a2 100644 --- a/database/migrations/2023_05_06_103152_create_water_quality_monitoring_reports_table.php +++ b/database/migrations/2023_05_06_103152_create_water_quality_reports_table.php @@ -13,7 +13,7 @@ return new class extends Migration */ public function up() { - Schema::create('water_quality_monitoring_reports', function (Blueprint $table) { + Schema::create('water_quality_reports', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('device_id')->comment('设备ID'); $table->decimal('chlorine', 8, 2)->nullable()->comment('余氯(单位: mg/L)'); @@ -36,6 +36,6 @@ return new class extends Migration */ public function down() { - Schema::dropIfExists('water_quality_monitoring_reports'); + Schema::dropIfExists('water_quality_reports'); } }; diff --git a/database/migrations/2023_05_06_103216_create_meteorological_monitoring_reports_table.php b/database/migrations/2023_05_06_103216_create_meteorological_reports_table.php similarity index 93% rename from database/migrations/2023_05_06_103216_create_meteorological_monitoring_reports_table.php rename to database/migrations/2023_05_06_103216_create_meteorological_reports_table.php index 1c46fc3..ecfaf54 100644 --- a/database/migrations/2023_05_06_103216_create_meteorological_monitoring_reports_table.php +++ b/database/migrations/2023_05_06_103216_create_meteorological_reports_table.php @@ -13,7 +13,7 @@ return new class extends Migration */ public function up() { - Schema::create('meteorological_monitoring_reports', function (Blueprint $table) { + Schema::create('meteorological_reports', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('device_id'); $table->decimal('today_rainfall', 8, 2)->nullable()->comment('今日积雨量 (单位: mm)'); @@ -46,6 +46,6 @@ return new class extends Migration */ public function down() { - Schema::dropIfExists('meteorological_monitoring_reports'); + Schema::dropIfExists('meteorological_reports'); } };