生成土壤监控报告
parent
6654d00ee3
commit
5b69d6758a
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
];
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ class SoilReport extends Model
|
|||
'p',
|
||||
'k',
|
||||
'conductivity',
|
||||
'moisture',
|
||||
'reported_at',
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -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');
|
||||
}
|
||||
};
|
||||
Loading…
Reference in New Issue