1
0
Fork 0

生成土壤监控报告

develop
李静 2023-05-16 11:53:48 +08:00
parent 6654d00ee3
commit 5b69d6758a
7 changed files with 292 additions and 46 deletions

View File

@ -5,6 +5,8 @@ namespace App\Console\Commands;
use App\Models\Device; use App\Models\Device;
use App\Models\MeteorologicalDailyReport; use App\Models\MeteorologicalDailyReport;
use App\Models\MeteorologicalReport; use App\Models\MeteorologicalReport;
use App\Models\SoilDailyReport;
use App\Models\SoilReport;
use App\Models\WaterQualityDailyReport; use App\Models\WaterQualityDailyReport;
use App\Models\WaterQualityReport; use App\Models\WaterQualityReport;
use App\Services\DeviceLogService; use App\Services\DeviceLogService;
@ -65,51 +67,65 @@ class DeviceLogDailyReportCommand extends Command
*/ */
protected function createReportToLinkosDevice(Device $device): void protected function createReportToLinkosDevice(Device $device): void
{ {
$lastReportedAt = null; [$lastReportedAt, $latestReportedAt] = value(function (Device $device) {
$lastReportedAt = null;
switch ($device->type) { $latestReportedAt = null;
case Device::TYPE_WATER_QUALITY:
$lastReportedAt = WaterQualityDailyReport::where('device_id', $device->id)
->latest('reported_at')
->value('reported_at');
$lastReportedAt ??= WaterQualityReport::where('device_id', $device->id) switch ($device->type) {
->oldest('reported_at') case Device::TYPE_WATER_QUALITY:
->value('reported_at'); $lastReportedAt = WaterQualityDailyReport::where('device_id', $device->id)
break; ->latest('reported_at')
->value('reported_at');
case Device::TYPE_METEOROLOGICAL: $lastReportedAt ??= WaterQualityReport::where('device_id', $device->id)
$lastReportedAt = MeteorologicalDailyReport::where('device_id', $device->id) ->oldest('reported_at')
->latest('reported_at') ->value('reported_at');
->value('reported_at');
$lastReportedAt ??= MeteorologicalReport::where('device_id', $device->id) if ($lastReportedAt) {
->oldest('reported_at') $latestReportedAt = WaterQualityReport::where('device_id', $device->id)
->value('reported_at'); ->latest('reported_at')
break; ->value('reported_at');
} }
break;
if ($lastReportedAt === null) { case Device::TYPE_METEOROLOGICAL:
return; $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) { if ($lastReportedAt) {
case Device::TYPE_WATER_QUALITY: $latestReportedAt = MeteorologicalReport::where('device_id', $device->id)
$latestReportedAt = WaterQualityReport::where('device_id', $device->id) ->latest('reported_at')
->latest('reported_at') ->value('reported_at');
->value('reported_at'); }
break; break;
case Device::TYPE_METEOROLOGICAL: case Device::TYPE_SOIL:
$latestReportedAt = MeteorologicalReport::where('device_id', $device->id) $lastReportedAt = SoilDailyReport::where('device_id', $device->id)
->latest('reported_at') ->latest('reported_at')
->value('reported_at'); ->value('reported_at');
break;
}
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; return;
} }

View File

@ -4,6 +4,7 @@ namespace App\Console\Commands;
use App\Models\Device; use App\Models\Device;
use App\Models\MeteorologicalReport; use App\Models\MeteorologicalReport;
use App\Models\SoilReport;
use App\Models\WaterQualityReport; use App\Models\WaterQualityReport;
use App\Services\DeviceLogService; use App\Services\DeviceLogService;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@ -64,6 +65,7 @@ class DeviceLogReportCommand extends Command
protected function createReportToLinkosDevice(Device $device): void protected function createReportToLinkosDevice(Device $device): void
{ {
$lastReportedAt = match ($device->type) { $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_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'), Device::TYPE_METEOROLOGICAL => MeteorologicalReport::where('device_id', $device->id)->latest('reported_at')->value('reported_at'),
default => null, default => null,

View File

@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class SoilDailyReport extends Model
{
use HasFactory;
protected $casts = [
'reported_at' => 'date',
];
protected $fillable = [
'device_id',
'temperature',
'humidity',
'n',
'p',
'k',
'conductivity',
'moisture',
'reported_at',
];
}

View File

@ -21,6 +21,7 @@ class SoilReport extends Model
'p', 'p',
'k', 'k',
'conductivity', 'conductivity',
'moisture',
'reported_at', 'reported_at',
]; ];
} }

View File

@ -7,6 +7,8 @@ use App\Models\Device;
use App\Models\DeviceLog; use App\Models\DeviceLog;
use App\Models\MeteorologicalDailyReport; use App\Models\MeteorologicalDailyReport;
use App\Models\MeteorologicalReport; use App\Models\MeteorologicalReport;
use App\Models\SoilDailyReport;
use App\Models\SoilReport;
use App\Models\WaterQualityDailyReport; use App\Models\WaterQualityDailyReport;
use App\Models\WaterQualityReport; use App\Models\WaterQualityReport;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
@ -144,6 +146,10 @@ class DeviceLogService
public function createReportToLinkosDevice(Device $device, Carbon $time): void public function createReportToLinkosDevice(Device $device, Carbon $time): void
{ {
switch ($device->type) { switch ($device->type) {
case Device::TYPE_SOIL:
$this->createReportToLinkosSoilDevice($device, $time);
break;
case Device::TYPE_METEOROLOGICAL: case Device::TYPE_METEOROLOGICAL:
$this->createReportToLinkosMeteorologicalDevice($device, $time); $this->createReportToLinkosMeteorologicalDevice($device, $time);
break; 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 气象设备报告 * 创建 linkos 气象设备报告
*/ */
@ -299,9 +369,69 @@ class DeviceLogService
case Device::TYPE_WATER_QUALITY: case Device::TYPE_WATER_QUALITY:
$this->createDailyReportToLinkosWaterQualityDevice($device, $time); $this->createDailyReportToLinkosWaterQualityDevice($device, $time);
break; 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 气象设备每日报告 * 创建 linkos 气象设备每日报告
*/ */
@ -419,14 +549,12 @@ class DeviceLogService
}, $meteorologicalReports); }, $meteorologicalReports);
/** @var \App\Models\MeteorologicalDailyReport */ /** @var \App\Models\MeteorologicalDailyReport */
$meteorologicalDailyReport = MeteorologicalDailyReport::firstOrCreate([ $meteorologicalDailyReport = MeteorologicalDailyReport::firstOrNew([
'device_id' => $device->id, 'device_id' => $device->id,
'reported_at' => $date, 'reported_at' => $date,
], $attributes); ]);
if (! $meteorologicalDailyReport->wasRecentlyCreated) { $meteorologicalDailyReport->fill($attributes)->save();
$meteorologicalDailyReport->update($attributes);
}
} }
/** /**
@ -477,13 +605,11 @@ class DeviceLogService
}, $waterQualityReports); }, $waterQualityReports);
/** @var \App\Models\WaterQualityDailyReport */ /** @var \App\Models\WaterQualityDailyReport */
$waterQualityDailyReport = WaterQualityDailyReport::firstOrCreate([ $waterQualityDailyReport = WaterQualityDailyReport::firstOrNew([
'device_id' => $device->id, 'device_id' => $device->id,
'reported_at' => $date->format('Y-m-d'), 'reported_at' => $date->format('Y-m-d'),
], $attributes); ]);
if (! $waterQualityDailyReport->wasRecentlyCreated) { $waterQualityDailyReport->fill($attributes)->save();
$waterQualityDailyReport->update($attributes);
}
} }
} }

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('soil_reports', function (Blueprint $table) {
$table->decimal('moisture', 8, 2)->nullable()->comment('含水率(单位: %');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('soil_reports', function (Blueprint $table) {
$table->dropColumn('moisture');
});
}
};

View File

@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('soil_soil_reports', function (Blueprint $table) {
$table->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');
}
};