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\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;
}

View File

@ -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,

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',
'k',
'conductivity',
'moisture',
'reported_at',
];
}

View File

@ -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();
}
}

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');
}
};