生成土壤监控报告
parent
6654d00ee3
commit
5b69d6758a
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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',
|
'p',
|
||||||
'k',
|
'k',
|
||||||
'conductivity',
|
'conductivity',
|
||||||
|
'moisture',
|
||||||
'reported_at',
|
'reported_at',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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