Merge branch 'main' of https://gitea.hmily.club/pdkj/lcly-data-admin
commit
b8dc7bb497
|
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands\BiAng;
|
||||
|
||||
use App\Enums\DeviceType;
|
||||
use App\Models\Device;
|
||||
use App\Models\InsecticidalLampDailyReport;
|
||||
use App\Models\InsecticidalLampReport;
|
||||
use App\Models\MeteorologicalMonitoringDailyLog;
|
||||
use App\Models\MeteorologicalMonitoringLog;
|
||||
use App\Models\SoilMonitoringDailyLog;
|
||||
use App\Models\SoilMonitoringLog;
|
||||
use App\Services\BiAngDeviceService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class DeviceLogDailyReportCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'biang:device-log-daily-report
|
||||
{--sleep=300 : 监控报告生产后的休眠时间(秒)}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '按设备厂商生成监控报告';
|
||||
|
||||
/**
|
||||
* @var \App\Services\DeviceLogService
|
||||
*/
|
||||
protected $deviceLogService;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$sleep = (int) value(fn ($sleep) => is_numeric($sleep) ? $sleep : 300, $this->option('sleep'));
|
||||
|
||||
while (true) {
|
||||
/** @var \Illuminate\Database\Eloquent\Collection */
|
||||
$devices = Device::supplierBy("device-supplier-biang")->get();
|
||||
|
||||
foreach ($devices as $device) {
|
||||
$this->createReport($device);
|
||||
}
|
||||
|
||||
sleep($sleep);
|
||||
};
|
||||
}
|
||||
|
||||
protected function createReport(Device $device): void
|
||||
{
|
||||
[$lastReportedAt, $latestReportedAt] = value(function (Device $device) {
|
||||
$lastReportedAt = null;
|
||||
|
||||
$latestReportedAt = null;
|
||||
|
||||
switch ($device->type) {
|
||||
case DeviceType::Meteorological:
|
||||
$lastReportedAt = MeteorologicalMonitoringDailyLog::where('device_id', $device->id)
|
||||
->latest('monitored_at')
|
||||
->value('monitored_at');
|
||||
|
||||
$lastReportedAt ??= MeteorologicalMonitoringLog::where('device_id', $device->id)
|
||||
->oldest('monitored_at')
|
||||
->value('monitored_at');
|
||||
|
||||
if ($lastReportedAt) {
|
||||
$latestReportedAt = MeteorologicalMonitoringLog::where('device_id', $device->id)
|
||||
->latest('monitored_at')
|
||||
->value('monitored_at');
|
||||
}
|
||||
break;
|
||||
|
||||
case DeviceType::Soil:
|
||||
$lastReportedAt = SoilMonitoringDailyLog::where('device_id', $device->id)
|
||||
->latest('monitored_at')
|
||||
->value('monitored_at');
|
||||
|
||||
$lastReportedAt ??= SoilMonitoringLog::where('device_id', $device->id)
|
||||
->oldest('monitored_at')
|
||||
->value('monitored_at');
|
||||
|
||||
if ($lastReportedAt) {
|
||||
$latestReportedAt = SoilMonitoringLog::where('device_id', $device->id)
|
||||
->latest('monitored_at')
|
||||
->value('monitored_at');
|
||||
}
|
||||
break;
|
||||
|
||||
case DeviceType::InsecticidalLamp:
|
||||
$lastReportedAt = InsecticidalLampDailyReport::where('device_id', $device->id)
|
||||
->latest('reported_at')
|
||||
->value('reported_at');
|
||||
|
||||
$lastReportedAt ??= InsecticidalLampReport::where('device_id', $device->id)
|
||||
->oldest('reported_at')
|
||||
->value('reported_at');
|
||||
|
||||
if ($lastReportedAt) {
|
||||
$latestReportedAt = InsecticidalLampReport::where('device_id', $device->id)
|
||||
->latest('reported_at')
|
||||
->value('reported_at');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return [$lastReportedAt, $latestReportedAt];
|
||||
}, $device);
|
||||
|
||||
if ($lastReportedAt === null || $latestReportedAt === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$service = new BiAngDeviceService();
|
||||
|
||||
/** @var \Carbon\Carbon */
|
||||
$startAt = $lastReportedAt->copy()->startOfDay();
|
||||
|
||||
do {
|
||||
$service->createDailyReport($device, $startAt->copy());
|
||||
|
||||
$startAt->addDay();
|
||||
} while ($latestReportedAt->gte($startAt));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands\BiAng;
|
||||
|
||||
use App\Enums\DeviceType;
|
||||
use App\Models\Device;
|
||||
use App\Models\DeviceLog;
|
||||
use App\Models\InsecticidalLampReport;
|
||||
use App\Models\MeteorologicalMonitoringLog;
|
||||
use App\Models\SoilMonitoringLog;
|
||||
use App\Services\BiAngDeviceService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class DeviceLogReportCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'biang:device-log-report
|
||||
{--sleep=300 : 监控报告生产后的休眠时间(秒)}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '按设备厂商生成监控报告';
|
||||
|
||||
/**
|
||||
* @var \App\Services\DeviceLogService
|
||||
*/
|
||||
protected $deviceLogService;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$sleep = (int) value(fn ($sleep) => is_numeric($sleep) ? $sleep : 300, $this->option('sleep'));
|
||||
|
||||
while (true) {
|
||||
/** @var \Illuminate\Database\Eloquent\Collection */
|
||||
$devices = Device::supplierBy("device-supplier-biang")->get();
|
||||
|
||||
foreach ($devices as $device) {
|
||||
$this->createReport($device);
|
||||
}
|
||||
|
||||
sleep($sleep);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建比昂设备报告
|
||||
*/
|
||||
protected function createReport(Device $device): void
|
||||
{
|
||||
$lastReportedAt = match ($device->type) {
|
||||
DeviceType::Soil => SoilMonitoringLog::where('device_id', $device->id)->latest('monitored_at')->value('monitored_at'),
|
||||
DeviceType::Meteorological => MeteorologicalMonitoringLog::where('device_id', $device->id)->latest('monitored_at')->value('monitored_at'),
|
||||
DeviceType::InsecticidalLamp => InsecticidalLampReport::where('device_id', $device->id)->latest('reported_at')->value('reported_at'),
|
||||
default => null,
|
||||
};
|
||||
|
||||
if (is_null($lastReportedAt ??= DeviceLog::where('device_id', $device->id)->oldest('reported_at')->value('reported_at'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_null($latestReportedAt = DeviceLog::where('device_id', $device->id)->latest('reported_at')->value('reported_at'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
$service = new BiAngDeviceService();
|
||||
|
||||
/** @var \Carbon\Carbon */
|
||||
$startAt = $lastReportedAt->copy()->startOfHour();
|
||||
|
||||
do {
|
||||
$service->createReport($device, $startAt->copy());
|
||||
|
||||
$startAt->addHour();
|
||||
} while ($latestReportedAt->gte($startAt));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands\BiAng;
|
||||
|
||||
use App\Enums\DeviceStatus;
|
||||
use App\Enums\DeviceType;
|
||||
use App\Iot\BiAng\HttpClient;
|
||||
use App\Models\Device;
|
||||
use App\Models\DeviceLog;
|
||||
use App\Models\InsecticidalLampReport;
|
||||
use App\Models\WormPhoto;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Arr;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
class DeviceLogSyncCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'biang:device-log-sync
|
||||
{--sleep=60 : 设备数据同步完成后的休眠时间(秒)}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '按设备厂商同步数据';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$sleep = (int) value(fn ($sleep) => is_numeric($sleep) ? $sleep : 60, $this->option('sleep'));
|
||||
|
||||
while (true) {
|
||||
$this->sync();
|
||||
|
||||
sleep($sleep);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行同步
|
||||
*/
|
||||
protected function sync(): void
|
||||
{
|
||||
$now = now();
|
||||
|
||||
$this->info('------------------------------------------');
|
||||
$this->info('同步时间: '. $now);
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Collection */
|
||||
$devices = Device::with(['project'])
|
||||
->supplierBy("device-supplier-biang")
|
||||
->whereIn('status', [DeviceStatus::Online, DeviceStatus::Offline])
|
||||
->get();
|
||||
|
||||
/** @var \App\Models\Device */
|
||||
foreach ($devices as $device) {
|
||||
if (! in_array($device->type, [
|
||||
DeviceType::Soil,
|
||||
DeviceType::Meteorological,
|
||||
DeviceType::Worm,
|
||||
DeviceType::InsectSexLure,
|
||||
DeviceType::InsecticidalLamp,
|
||||
])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->info('==========================================');
|
||||
$this->info('设备编号: ' . $device->sn);
|
||||
$this->info('设备名称: ' . $device->name);
|
||||
$this->info('设备类型: ' . match ($device->type) {
|
||||
DeviceType::Soil => '土壤设备',
|
||||
DeviceType::Meteorological => '气象设备',
|
||||
DeviceType::Worm => '虫情设备',
|
||||
DeviceType::InsectSexLure => '昆虫性诱设备',
|
||||
DeviceType::InsecticidalLamp => '杀虫灯设备',
|
||||
});
|
||||
|
||||
try {
|
||||
$httpClient = $this->buildHttpClient($device);
|
||||
|
||||
switch ($device->type) {
|
||||
case DeviceType::Soil:
|
||||
$data = $httpClient->getLatestSoilReport($device->sn);
|
||||
|
||||
$log = DeviceLog::firstOrCreate([
|
||||
'device_id' => $device->id,
|
||||
'reported_at' => $data['time'],
|
||||
], [
|
||||
'data' => Arr::except($data, ['deviceId', 'time']),
|
||||
]);
|
||||
|
||||
$device->update([
|
||||
'status' => $now->copy()->subMinutes(60)->lt($log->reported_at) ? DeviceStatus::Online : DeviceStatus::Offline,
|
||||
]);
|
||||
break;
|
||||
|
||||
case DeviceType::Meteorological:
|
||||
$data = $httpClient->getLatestMeteorologicalReport($device->sn);
|
||||
|
||||
$log = DeviceLog::firstOrCreate([
|
||||
'device_id' => $device->id,
|
||||
'reported_at' => $data['time'],
|
||||
], [
|
||||
'data' => Arr::except($data, ['deviceId', 'time']),
|
||||
]);
|
||||
|
||||
$device->update([
|
||||
'status' => $now->copy()->subMinutes(60)->lt($log->reported_at) ? DeviceStatus::Online : DeviceStatus::Offline,
|
||||
]);
|
||||
break;
|
||||
|
||||
case DeviceType::InsecticidalLamp:
|
||||
$data = $httpClient->getLatestLampReport($device->sn);
|
||||
|
||||
$log = DeviceLog::firstOrCreate([
|
||||
'device_id' => $device->id,
|
||||
'reported_at' => $data['time'],
|
||||
], [
|
||||
'data' => Arr::except($data, ['deviceId', 'time']),
|
||||
]);
|
||||
|
||||
$device->update([
|
||||
'status' => $now->copy()->subMinutes(60)->lt($log->reported_at) ? DeviceStatus::Online : DeviceStatus::Offline,
|
||||
]);
|
||||
break;
|
||||
|
||||
case DeviceType::Worm:
|
||||
case DeviceType::InsectSexLure:
|
||||
$data = $httpClient->getWormPhotos($device->sn, $now->copy()->subHours(24), $now);
|
||||
|
||||
// foreach ($data['imgUrl'] as $item) {
|
||||
// WormPhoto::firstOrCreate([
|
||||
// 'device_id' => $device->id,
|
||||
// 'uploaded_at' => $item['time'],
|
||||
// ], [
|
||||
// 'url' => $item['url'],
|
||||
// ]);
|
||||
// }
|
||||
|
||||
$device->update([
|
||||
'status' => count($data['imgUrl'] ?? []) > 0 ? DeviceStatus::Online : DeviceStatus::Offline,
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
$this->info('同步成功!');
|
||||
} catch (Throwable $e) {
|
||||
report($e);
|
||||
|
||||
$this->error('同步失败: '. $e->getMessage());
|
||||
}
|
||||
|
||||
$this->info('==========================================');
|
||||
}
|
||||
|
||||
$this->info('------------------------------------------');
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 HTTP 客户端
|
||||
*/
|
||||
public function buildHttpClient(Device $device): HttpClient
|
||||
{
|
||||
$config = json_decode($device->project?->value, true);
|
||||
|
||||
if (! is_array($config)) {
|
||||
throw new RuntimeException('账户信息未找到');
|
||||
}
|
||||
|
||||
return new HttpClient($config['username'] ?? '', $config['password'] ?? '');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands\BiAng;
|
||||
|
||||
use App\Enums\DeviceStatus;
|
||||
use App\Enums\DeviceType;
|
||||
use App\Models\Device;
|
||||
use App\Models\WormReport;
|
||||
use App\Services\BiAngDeviceService;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class WormStatisticsSyncCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'biang:worm-statistics-sync';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '虫情设备统计数据同步';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->sync();
|
||||
}
|
||||
|
||||
protected function sync(): void
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Collection */
|
||||
$devices = Device::with(['project'])
|
||||
->supplierBy('device-supplier-biang')
|
||||
->where('type', DeviceType::Worm)
|
||||
->whereIn('status', [DeviceStatus::Online, DeviceStatus::Offline])
|
||||
->get();
|
||||
|
||||
if ($devices->isEmpty()) {
|
||||
$this->warn('没有找到虫情设备');
|
||||
return;
|
||||
}
|
||||
|
||||
$today = today();
|
||||
|
||||
foreach ($devices as $device) {
|
||||
$this->info('==================================');
|
||||
|
||||
$latestReportedAt = WormReport::Where('device_id', $device->id)->latest('reported_at')->value('reported_at');
|
||||
|
||||
$start = $latestReportedAt ? $latestReportedAt->copy() : $today->copy()->subDays(179);
|
||||
|
||||
$days = $start->diffInDays($today, false);
|
||||
|
||||
if ($days > 179) {
|
||||
$end = $start->copy()->addDays(179);
|
||||
} else {
|
||||
if ($days < 2) {
|
||||
$start = $today->copy()->subDays(2);
|
||||
}
|
||||
$end = $today->copy();
|
||||
}
|
||||
$this->info('设备编号: '.$device->sn);
|
||||
$this->info('开始日期: '.$start->toDateString());
|
||||
$this->info('结束日期: '.$end->toDateString());
|
||||
|
||||
$statistics = (new BiAngDeviceService())->getWormStatistics($device, $start, $end);
|
||||
|
||||
foreach (data_get($statistics, 'data.records', []) as $item) {
|
||||
$data = collect(Arr::except($item, '日期'))->map(fn ($v, $k) => ['name' => $k, 'num' => $v]);
|
||||
|
||||
WormReport::updateOrCreate([
|
||||
'device_id' => $device->id,
|
||||
'reported_at' => $item['日期'],
|
||||
], [
|
||||
'agricultural_base_id' => $device->agricultural_base_id,
|
||||
'worm_num' => $data->sum('num'),
|
||||
'data' => $data->values(),
|
||||
]);
|
||||
}
|
||||
|
||||
$this->info('==================================');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Enums\DeviceStatus;
|
||||
use App\Enums\DeviceType;
|
||||
use App\Models\Device;
|
||||
use App\Models\SoilMonitoringLog;
|
||||
use App\Services\LinkosDeviceLogService;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Throwable;
|
||||
|
||||
class SoilMonitoringLogFixCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'soil-monitoring-log:fix {hour? : 时间格式 Y-m-d H:i:s, 示例: 2022-11-11 00:00:00}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '修复土壤监控数据';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle(LinkosDeviceLogService $linkosDeviceLogService)
|
||||
{
|
||||
$time = now()->subHour()->startOfHour();
|
||||
|
||||
if ($hour = $this->argument('hour')) {
|
||||
$time = Carbon::createFromFormat('Y-m-d H:i:s', $hour)->startOfHour();
|
||||
}
|
||||
|
||||
$devices = Device::where('type', DeviceType::Soil)
|
||||
->where('status', DeviceStatus::Online)
|
||||
->get();
|
||||
|
||||
foreach ($devices as $device) {
|
||||
$last = SoilMonitoringLog::where('device_id', $device->id)
|
||||
->where('monitored_at', $time->copy()->subHour())
|
||||
->first();
|
||||
|
||||
if ($last === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$log = SoilMonitoringLog::firstOrCreate([
|
||||
'device_id' => $device->id,
|
||||
'monitored_at' => $time,
|
||||
], [
|
||||
'agricultural_base_id' => $device->agricultural_base_id,
|
||||
]);
|
||||
|
||||
foreach ([
|
||||
'conductivity',
|
||||
'humidity',
|
||||
'temperature',
|
||||
'n',
|
||||
'p',
|
||||
'k',
|
||||
] as $key) {
|
||||
if (is_null($log->{$key})) {
|
||||
$log->{$key} = $last->{$key};
|
||||
}
|
||||
}
|
||||
|
||||
if ($log->isDirty()) {
|
||||
$log->is_filled = true;
|
||||
}
|
||||
|
||||
$log->save();
|
||||
|
||||
if ($log->wasChanged()) {
|
||||
$linkosDeviceLogService->handleSoilMonitoringDailyLog($device, $time);
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
report($e);
|
||||
}
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Enums\DeviceStatus;
|
||||
use App\Enums\DeviceType;
|
||||
use App\Models\Device;
|
||||
use App\Models\WaterQualityMonitoringLog;
|
||||
use App\Services\LinkosDeviceLogService;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Throwable;
|
||||
|
||||
class WaterQualityMonitoringLogFixCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'water-quality-monitoring-log:fix {hour? : 时间格式 Y-m-d H:i:s, 示例: 2022-11-11 00:00:00}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '修复水质监控数据';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle(LinkosDeviceLogService $linkosDeviceLogService)
|
||||
{
|
||||
$time = now()->subHour()->startOfHour();
|
||||
|
||||
if ($hour = $this->argument('hour')) {
|
||||
$time = Carbon::createFromFormat('Y-m-d H:i:s', $hour)->startOfHour();
|
||||
}
|
||||
|
||||
$devices = Device::where('type', DeviceType::WaterQuality)
|
||||
->where('status', DeviceStatus::Online)
|
||||
->get();
|
||||
|
||||
foreach ($devices as $device) {
|
||||
$last = WaterQualityMonitoringLog::where('device_id', $device->id)
|
||||
->where('monitored_at', $time->copy()->subHour())
|
||||
->first();
|
||||
|
||||
if ($last === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$log = WaterQualityMonitoringLog::firstOrCreate([
|
||||
'device_id' => $device->id,
|
||||
'monitored_at' => $time,
|
||||
], [
|
||||
'agricultural_base_id' => $device->agricultural_base_id,
|
||||
]);
|
||||
|
||||
foreach ([
|
||||
'chlorine',
|
||||
'conductivity',
|
||||
'oxygen',
|
||||
'ph',
|
||||
'temperature',
|
||||
'turbidity',
|
||||
] as $key) {
|
||||
if (is_null($log->{$key})) {
|
||||
$log->{$key} = $last->{$key};
|
||||
}
|
||||
}
|
||||
|
||||
if ($log->isDirty()) {
|
||||
$log->is_filled = true;
|
||||
}
|
||||
|
||||
$log->save();
|
||||
|
||||
if ($log->wasChanged()) {
|
||||
$linkosDeviceLogService->handleWaterQualityMonitoringDailyLog($device, $time);
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
report($e);
|
||||
}
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
|
@ -15,11 +15,7 @@ class Kernel extends ConsoleKernel
|
|||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
$schedule->command(Commands\SoilMonitoringLogFixCommand::class)
|
||||
->hourly()
|
||||
->runInBackground();
|
||||
|
||||
$schedule->command(Commands\WaterQualityMonitoringLogFixCommand::class)
|
||||
$schedule->command(Commands\BiAng\WormStatisticsSyncCommand::class)
|
||||
->hourly()
|
||||
->runInBackground();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ enum DeviceType: int
|
|||
case Soil = 2; // 土壤设备
|
||||
case WaterQuality = 3; // 水质设备
|
||||
case Meteorological = 4; // 气象设备
|
||||
case Worm = 5; // 虫情设备
|
||||
case InsectSexLure = 6; // 昆虫性诱设备
|
||||
case InsecticidalLamp = 7; // 杀虫灯设备
|
||||
|
||||
/**
|
||||
* @return string
|
||||
|
|
@ -27,6 +30,9 @@ enum DeviceType: int
|
|||
static::Soil->value => '土壤设备',
|
||||
static::WaterQuality->value => '水质设备',
|
||||
static::Meteorological->value => '气象设备',
|
||||
static::Worm->value => '虫情设备',
|
||||
static::InsectSexLure->value => '昆虫性诱设备',
|
||||
static::InsecticidalLamp->value => '杀虫灯设备',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class BiAngException extends RuntimeException
|
||||
{
|
||||
}
|
||||
|
|
@ -5,15 +5,15 @@ namespace App\Http\Controllers;
|
|||
use App\Enums\BaseType;
|
||||
use App\Enums\DeviceStatus;
|
||||
use App\Enums\DeviceType;
|
||||
use App\Enums\OperationType;
|
||||
use App\Helpers\Paginator;
|
||||
use App\Http\Requestes\AgriculturalBaseRequest;
|
||||
use App\Http\Resources\AgriculturalBaseResource;
|
||||
use App\Models\AgriculturalBase;
|
||||
use App\Models\Device;
|
||||
use App\Services\OperationLogService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Services\OperationLogService;
|
||||
use App\Enums\OperationType;
|
||||
|
||||
class AgriculturalBaseController extends Controller
|
||||
{
|
||||
|
|
@ -26,7 +26,7 @@ class AgriculturalBaseController extends Controller
|
|||
public function index(Request $request)
|
||||
{
|
||||
$query = AgriculturalBase::with(['crops','industry'])->filter($request->all())->sort();
|
||||
$list = $query->paginate(Paginator::resolvePerPage('per_page', 20, 50));
|
||||
$list = $query->paginate(Paginator::resolvePerPage('per_page', 20));
|
||||
|
||||
return $this->json(AgriculturalBaseResource::collection($list));
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -8,9 +8,9 @@ use App\Models\RiceShrimpFlow;
|
|||
use App\Models\RiceShrimpIndustry;
|
||||
use App\Models\RiceShrimpPrice;
|
||||
use App\Models\RiceShrimpWeeklyPrice;
|
||||
use Peidikeji\Keywords\Models\Keywords;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Peidikeji\Keywords\Models\Keywords;
|
||||
|
||||
class ChartController extends Controller
|
||||
{
|
||||
|
|
@ -72,7 +72,7 @@ class ChartController extends Controller
|
|||
{
|
||||
$now = now();
|
||||
|
||||
$weeks = Keywords::where('type_key', 'weeks-per-year')->pluck('name', 'id');
|
||||
$weeks = Keywords::where('type_key', 'weeks-per-year')->pluck('name', 'key');
|
||||
|
||||
$years = RiceShrimpWeeklyPrice::select('year')
|
||||
->groupBy('year')
|
||||
|
|
@ -105,7 +105,7 @@ class ChartController extends Controller
|
|||
$riceShrimpWeeklyPricesTable = (new RiceShrimpWeeklyPrice)->getTable();
|
||||
|
||||
$latestPrice = RiceShrimpWeeklyPrice::query()
|
||||
->join($keywordsTable, fn ($join) => $join->on("$riceShrimpWeeklyPricesTable.week", '=', "$keywordsTable.id"))
|
||||
->join($keywordsTable, fn ($join) => $join->on("$riceShrimpWeeklyPricesTable.week", '=', DB::raw("$keywordsTable.key::INTEGER")))
|
||||
->where("$keywordsTable.type_key", 'weeks-per-year')
|
||||
->latest("$riceShrimpWeeklyPricesTable.year")
|
||||
->latest(DB::raw("$keywordsTable.key::INTEGER"))
|
||||
|
|
|
|||
|
|
@ -4,29 +4,35 @@ namespace App\Http\Controllers;
|
|||
|
||||
use App\Enums\DeviceStatus;
|
||||
use App\Enums\DeviceType;
|
||||
use App\Enums\OperationType;
|
||||
use App\Helpers\Paginator;
|
||||
use App\Http\Requestes\DeviceRequest;
|
||||
use App\Http\Resources\DeviceResource;
|
||||
use App\Models\Device;
|
||||
use App\Models\MeteorologicalMonitoringLog;
|
||||
use App\Models\MeteorologicalMonitoringDailyLog;
|
||||
use App\Models\SoilMonitoringLog;
|
||||
use App\Models\SoilMonitoringDailyLog;
|
||||
use App\Models\WaterQualityMonitoringLog;
|
||||
use App\Models\WaterQualityMonitoringDailyLog;
|
||||
use App\Models\AgriculturalBase;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Carbon\Carbon;
|
||||
use App\Models\Device;
|
||||
use App\Models\InsecticidalLampDailyReport;
|
||||
use App\Models\InsecticidalLampReport;
|
||||
use App\Models\MeteorologicalMonitoringDailyLog;
|
||||
use App\Models\MeteorologicalMonitoringLog;
|
||||
use App\Models\SoilMonitoringDailyLog;
|
||||
use App\Models\SoilMonitoringLog;
|
||||
use App\Models\WaterQualityMonitoringDailyLog;
|
||||
use App\Models\WaterQualityMonitoringLog;
|
||||
use App\Models\WormPhoto;
|
||||
use App\Models\WormReport;
|
||||
use App\Services\BiAngDeviceService;
|
||||
use App\Services\OperationLogService;
|
||||
use App\Enums\OperationType;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Peidikeji\Setting\Models\Setting;
|
||||
|
||||
class DeviceController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = Device::with('base')->filter($request->input())->orderBy('sort', 'desc');
|
||||
$query = Device::with(['base', 'supplier', 'project'])->filter($request->input())->orderBy('sort', 'desc');
|
||||
$list = $query->paginate(Paginator::resolvePerPage('per_page', 20, 50));
|
||||
|
||||
return $this->json(DeviceResource::collection($list));
|
||||
|
|
@ -51,6 +57,7 @@ class DeviceController extends Controller
|
|||
|
||||
public function show(Device $device)
|
||||
{
|
||||
$device->loadMissing(['base', 'supplier', 'project']);
|
||||
return $this->json(DeviceResource::make($device));
|
||||
}
|
||||
|
||||
|
|
@ -414,48 +421,46 @@ class DeviceController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询设备今天(按天),近一周(按天),近一个月(按天)
|
||||
*/
|
||||
public function timeZoneList(Request $request){
|
||||
$deviceId = $request->input('device_id');
|
||||
//不传开始时间,结束时间,则默认是查当天(按小时)
|
||||
$startTime = $request->input('start_time');
|
||||
$endTime = $request->input('end_time');
|
||||
$diffDays = 0;
|
||||
$day = date('Y-m-d');
|
||||
//如果传了开始时间和结束时间,计算中间天数
|
||||
if($startTime && $endTime){
|
||||
if($startTime == $endTime){//查询某一天
|
||||
$day = $startTime;
|
||||
}else{
|
||||
$startDay = Carbon::parse($startTime);
|
||||
$endDay = Carbon::parse($endTime);
|
||||
$diffDays = $startDay->diffInDays($endDay, false);
|
||||
public function timeZoneList(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'device_id' => ['bail', 'required'],
|
||||
'start_time' => ['bail', 'nullable', 'date_format:Y-m-d'],
|
||||
'end_time' => ['bail', 'nullable', 'date_format:Y-m-d'],
|
||||
]);
|
||||
|
||||
$isSameDay = true;
|
||||
|
||||
if ($request->filled('start_time') && $request->filled('end_time')) {
|
||||
$startTime = Carbon::parse($request->input('start_time'))->startOfDay();
|
||||
$endTime = Carbon::parse($request->input('end_time'))->startOfDay();
|
||||
|
||||
if ($startTime->gt($endTime)) {
|
||||
throw ValidationException::withMessages([
|
||||
'start_time' => ['开始时间不能大于结束时间'],
|
||||
]);
|
||||
}
|
||||
|
||||
// 如果开始时间和结束时间是同一天
|
||||
if ($startTime->eq($endTime)) {
|
||||
$endTime = $startTime->isToday() ? now() : $startTime->copy()->endOfDay();
|
||||
} else {
|
||||
$isSameDay = false;
|
||||
}
|
||||
} else {
|
||||
$endTime = now();
|
||||
$startTime = $endTime->copy()->startOfDay();
|
||||
}
|
||||
|
||||
$xKeys = [];
|
||||
if($diffDays){
|
||||
for ($i = 0; $i<=$diffDays; $i++) {
|
||||
$xKeys[] =(clone $startDay)->addDays($i)->startOfDay()->format('Y-m-d H:i:s');
|
||||
}
|
||||
}else{
|
||||
//调整截至到当前小时
|
||||
$h = 23;
|
||||
if($day == date('Y-m-d')){
|
||||
$h = date('H');
|
||||
}
|
||||
for ($i = 0; $i < ($h+1); $i++) {
|
||||
$xKeys[] = $day.' '.str_pad($i, 2, '0', STR_PAD_LEFT).':00:00';
|
||||
}
|
||||
}
|
||||
$device = Device::find($deviceId);
|
||||
$modelQuery = null;
|
||||
$getArr = [];
|
||||
$device = Device::findOrFail($request->input('device_id'));
|
||||
|
||||
$fields = [];
|
||||
$monitoringLogs = collect();
|
||||
|
||||
switch ($device->type) {
|
||||
case DeviceType::Meteorological://气象设备
|
||||
$getArr = [
|
||||
// 气象设备
|
||||
case DeviceType::Meteorological:
|
||||
$fields = [
|
||||
'wind_speed',
|
||||
'wind_direction',
|
||||
'wind_degree',
|
||||
|
|
@ -467,17 +472,34 @@ class DeviceController extends Controller
|
|||
'illumination',
|
||||
'pm25',
|
||||
'pm10',
|
||||
'rainfall',
|
||||
];
|
||||
if($diffDays) {
|
||||
$getArr[] = 'daily_rainfall';
|
||||
$modelQuery = MeteorologicalMonitoringDailyLog::query()->whereBetween('monitored_at', [$startTime, $endTime]);
|
||||
}else{
|
||||
$getArr[] = 'current_rainfall';
|
||||
$modelQuery = MeteorologicalMonitoringLog::query()->whereDate('monitored_at', $day);
|
||||
}
|
||||
/** @var \Illuminate\Support\Collection */
|
||||
$monitoringLogs = (
|
||||
$isSameDay
|
||||
? MeteorologicalMonitoringLog::query()
|
||||
: MeteorologicalMonitoringDailyLog::query()
|
||||
)
|
||||
->where('device_id', $device->id)
|
||||
->whereBetween('monitored_at', [$startTime, $endTime])
|
||||
->get()
|
||||
->mapWithKeys(function ($item) use ($fields) {
|
||||
$key = $item->monitored_at->toDateTimeString();
|
||||
$data = collect($fields)->mapWithKeys(fn ($field) => [$field => $field !== 'rainfall' ? $item[$field] : null])->all();
|
||||
|
||||
if ($item instanceof MeteorologicalMonitoringDailyLog) {
|
||||
$data['rainfall'] = $item->daily_rainfall;
|
||||
} else {
|
||||
$data['rainfall'] = $item->current_rainfall;
|
||||
}
|
||||
|
||||
return [$key => $data];
|
||||
});
|
||||
break;
|
||||
case DeviceType::Soil://土壤设备
|
||||
$getArr = [
|
||||
|
||||
// 土壤设备
|
||||
case DeviceType::Soil:
|
||||
$fields = [
|
||||
'conductivity',
|
||||
'humidity',
|
||||
'temperature',
|
||||
|
|
@ -485,14 +507,25 @@ class DeviceController extends Controller
|
|||
'p',
|
||||
'k',
|
||||
];
|
||||
if($diffDays) {
|
||||
$modelQuery = SoilMonitoringDailyLog::query()->whereBetween('monitored_at', [$startTime, $endTime]);
|
||||
}else{
|
||||
$modelQuery = SoilMonitoringLog::query()->whereDate('monitored_at', $day);
|
||||
}
|
||||
/** @var \Illuminate\Support\Collection */
|
||||
$monitoringLogs = (
|
||||
$isSameDay
|
||||
? SoilMonitoringLog::query()
|
||||
: SoilMonitoringDailyLog::query()
|
||||
)
|
||||
->where('device_id', $device->id)
|
||||
->whereBetween('monitored_at', [$startTime, $endTime])
|
||||
->get()
|
||||
->mapWithKeys(function ($item) use ($fields) {
|
||||
$key = $item->monitored_at->toDateTimeString();
|
||||
$data = collect($fields)->mapWithKeys(fn ($field) => [$field => $item[$field]])->all();
|
||||
return [$key => $data];
|
||||
});
|
||||
break;
|
||||
case DeviceType::WaterQuality://水质设备
|
||||
$getArr = [
|
||||
|
||||
// 水质设备
|
||||
case DeviceType::WaterQuality:
|
||||
$fields = [
|
||||
'chlorine',
|
||||
'conductivity',
|
||||
'oxygen',
|
||||
|
|
@ -500,62 +533,96 @@ class DeviceController extends Controller
|
|||
'temperature',
|
||||
'turbidity',
|
||||
];
|
||||
if($diffDays) {
|
||||
$modelQuery = WaterQualityMonitoringDailyLog::query()->whereBetween('monitored_at', [$startTime, $endTime]);
|
||||
}else{
|
||||
$modelQuery = WaterQualityMonitoringLog::query()->whereDate('monitored_at', $day);
|
||||
}
|
||||
/** @var \Illuminate\Support\Collection */
|
||||
$monitoringLogs = (
|
||||
$isSameDay
|
||||
? WaterQualityMonitoringLog::query()
|
||||
: WaterQualityMonitoringDailyLog::query()
|
||||
)
|
||||
->where('device_id', $device->id)
|
||||
->whereBetween('monitored_at', [$startTime, $endTime])
|
||||
->get()
|
||||
->mapWithKeys(function ($item) use ($fields) {
|
||||
$key = $item->monitored_at->toDateTimeString();
|
||||
$data = collect($fields)->mapWithKeys(fn ($field) => [$field => $item[$field]])->all();
|
||||
return [$key => $data];
|
||||
});
|
||||
break;
|
||||
|
||||
// 杀虫灯
|
||||
case DeviceType::InsecticidalLamp:
|
||||
$fields = [
|
||||
'battery_vol',
|
||||
'killed_num',
|
||||
'air_temperature',
|
||||
'air_humidity',
|
||||
'charging_vol',
|
||||
'high_vol',
|
||||
];
|
||||
/** @var \Illuminate\Support\Collection */
|
||||
$monitoringLogs = (
|
||||
$isSameDay
|
||||
? InsecticidalLampReport::query()
|
||||
: InsecticidalLampDailyReport::query()
|
||||
)
|
||||
->where('device_id', $device->id)
|
||||
->whereBetween('reported_at', [$startTime, $endTime])
|
||||
->get()
|
||||
->mapWithKeys(function ($item) use ($fields) {
|
||||
$key = $item->reported_at->toDateTimeString();
|
||||
$data = collect($fields)->mapWithKeys(fn ($field) => [$field => $item[$field]])->all();
|
||||
return [$key => $data];
|
||||
});
|
||||
break;
|
||||
}
|
||||
if($modelQuery){
|
||||
$datalist = $modelQuery->where('device_id', $deviceId)->get()->keyBy('monitored_at')->toArray();
|
||||
}
|
||||
$data = [];
|
||||
foreach ($getArr as $column){
|
||||
$data[$column] = [];
|
||||
$_value = null;
|
||||
foreach($xKeys as $key){
|
||||
if($device->type == DeviceType::WaterQuality){//如果是水质设备,则写死假数据
|
||||
switch($column){
|
||||
case 'chlorine':
|
||||
$data[$column][$key] = 0.016;
|
||||
break;
|
||||
case 'conductivity':
|
||||
$data[$column][$key] = 563 ;//电导率
|
||||
break;
|
||||
case 'oxygen':
|
||||
$data[$column][$key] = 0.09;//含氧量
|
||||
break;
|
||||
case 'ph':
|
||||
$data[$column][$key] = rand(750, 770) / 100;
|
||||
break;
|
||||
case 'temperature':
|
||||
$data[$column][$key] = rand(2400, 2600) / 100;
|
||||
break;
|
||||
case 'turbidity':
|
||||
$data[$column][$key] = 0.33;
|
||||
break;
|
||||
|
||||
return $this->json(
|
||||
collect($fields)->mapWithKeys(function ($field) use ($device, $monitoringLogs, $isSameDay, $startTime, $endTime) {
|
||||
$data = [];
|
||||
|
||||
$beginTime = $startTime->copy();
|
||||
|
||||
do {
|
||||
$key = $beginTime->toDateTimeString();
|
||||
|
||||
$monitoringLog = $monitoringLogs->get($key);
|
||||
|
||||
if (is_null($monitoringLog)) {
|
||||
// 如果是水质设备,则写死假数据
|
||||
if($device->type == DeviceType::WaterQuality){
|
||||
switch($field){
|
||||
case 'chlorine':
|
||||
$data[$key] = 0.016;
|
||||
break;
|
||||
case 'conductivity':
|
||||
$data[$key] = rand(560, 565);//电导率
|
||||
break;
|
||||
case 'oxygen':
|
||||
$data[$key] = 0.09;//含氧量
|
||||
break;
|
||||
case 'ph':
|
||||
$data[$key] = rand(750, 770) / 100;
|
||||
break;
|
||||
case 'temperature':
|
||||
$data[$key] = rand(2400, 2600) / 100;
|
||||
break;
|
||||
case 'turbidity':
|
||||
$data[$key] = 0.33;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$data[$key] = null;
|
||||
}
|
||||
} else {
|
||||
$data[$key] = $monitoringLog[$field];
|
||||
}
|
||||
}else{
|
||||
// if($datalist[$key][$column] ?? null){//如果存在数据则暂存该值
|
||||
// $_value = $datalist[$key][$column];
|
||||
// }
|
||||
// //判断是否超过离线时间;
|
||||
// if(true){//未超过, 判断和设备离线时间关系-todo
|
||||
// $data[$column][$key] = $_value;
|
||||
// }else{
|
||||
$data[$column][$key] = $datalist[$key][$column] ?? null;
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
//强制统一气象降雨量,日和天字段不统一问题
|
||||
if(isset($data['daily_rainfall'])) {
|
||||
$data['rainfall'] = $data['daily_rainfall'];
|
||||
}elseif(isset($data['current_rainfall'])){
|
||||
$data['rainfall'] = $data['current_rainfall'];
|
||||
}
|
||||
return $this->json($data);
|
||||
|
||||
$isSameDay ? $beginTime->addHours(1) : $beginTime->addDays(1);
|
||||
} while ($beginTime->lte($endTime));
|
||||
|
||||
return [$field => $data];
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public function getFfmpegServiceIp(){
|
||||
|
|
@ -571,4 +638,116 @@ class DeviceController extends Controller
|
|||
|
||||
return $this->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 虫情统计
|
||||
*/
|
||||
public function wormStatics($id, Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'start_time' => ['bail', 'required_with:end_time', 'date_format:Y-m-d'],
|
||||
'end_time' => ['bail', 'required_with:start_time', 'date_format:Y-m-d'],
|
||||
], [], [
|
||||
'start_time' => '开始时间',
|
||||
'end_time' => '结束时间',
|
||||
]);
|
||||
|
||||
// 结束时间
|
||||
$endTime = $request->whenFilled(
|
||||
'end_time',
|
||||
fn ($time) => Carbon::parse($time)->startOfDay(),
|
||||
fn () => now()->startOfDay(),
|
||||
);
|
||||
// 开始时间
|
||||
$startTime = $request->whenFilled(
|
||||
'start_time',
|
||||
fn ($time) => Carbon::parse($time)->startOfDay(),
|
||||
fn () => $endTime->copy()->subDays(6),
|
||||
);
|
||||
|
||||
if ($startTime->gt($endTime)) {
|
||||
throw ValidationException::withMessages([
|
||||
'start_time' => ['开始时间不能大于结束时间'],
|
||||
]);
|
||||
}
|
||||
|
||||
$device = Device::findOrFail($id);
|
||||
|
||||
$wormReports = WormReport::where('device_id', $device->id)
|
||||
->whereBetween('reported_at', [$startTime->toDateString(), $endTime->toDateString()])
|
||||
->pluck('worm_num', 'reported_at');
|
||||
|
||||
$data = [];
|
||||
|
||||
do {
|
||||
$key = $startTime->toDateString();
|
||||
$data[$key] = $wormReports->get($key);
|
||||
$startTime->addDay();
|
||||
} while ($startTime->lte($endTime));
|
||||
|
||||
return $this->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 虫情图片
|
||||
*/
|
||||
public function wormPhotos($id, Request $request, BiAngDeviceService $biAngDeviceService)
|
||||
{
|
||||
$request->validate([
|
||||
'start_time' => ['bail', 'required_with:end_time', 'date_format:Y-m-d'],
|
||||
'end_time' => ['bail', 'required_with:start_time', 'date_format:Y-m-d'],
|
||||
], [], [
|
||||
'start_time' => '开始时间',
|
||||
'end_time' => '结束时间',
|
||||
]);
|
||||
|
||||
// 结束时间
|
||||
$endTime = $request->whenFilled(
|
||||
'end_time',
|
||||
fn ($time) => Carbon::parse($time)->endOfDay(),
|
||||
fn () => now()->endOfDay(),
|
||||
);
|
||||
// 开始时间
|
||||
$startTime = $request->whenFilled(
|
||||
'start_time',
|
||||
fn ($time) => Carbon::parse($time)->startOfDay(),
|
||||
fn () => $endTime->copy()->startOfDay(),
|
||||
);
|
||||
|
||||
if ($startTime->gt($endTime)) {
|
||||
throw ValidationException::withMessages([
|
||||
'start_time' => ['开始时间不能大于结束时间'],
|
||||
]);
|
||||
}
|
||||
|
||||
$device = Device::findOrFail($id);
|
||||
|
||||
$data = [];
|
||||
|
||||
switch ($device->supplier_key) {
|
||||
case 'device-supplier-biang':
|
||||
$result = $biAngDeviceService->getWormPhotos(
|
||||
$device,
|
||||
Carbon::parse($request->input('start_time')),
|
||||
Carbon::parse($request->input('end_time')),
|
||||
);
|
||||
$data = $result['imgUrl'];
|
||||
break;
|
||||
|
||||
default:
|
||||
$wormPhotos = WormPhoto::where('device_id', $device->id)
|
||||
->whereBetween('uploaded_at', [$startTime, $endTime])
|
||||
->latest('uploaded_at')
|
||||
->get();
|
||||
|
||||
$data = $wormPhotos->map(fn ($item) => [
|
||||
'id' => $item->id,
|
||||
'url' => $item->url,
|
||||
'time' => $item->created_at->toDateTimeString(),
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->json($data);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ class WeeksPerYearController extends Controller
|
|||
{
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
return Keywords::select('name', 'id')->where('type_key', 'weeks-per-year')->get();
|
||||
$keywords = Keywords::where('type_key', 'weeks-per-year')->pluck('name', 'key');
|
||||
|
||||
return $keywords->map(fn ($name, $key) => ['id' => $key, 'name' => $name])->values();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ class DeviceRequest extends FormRequest
|
|||
'agricultural_base_id' => 'required|integer|min:0',
|
||||
'sn' => 'required|string|max:64',
|
||||
'monitoring_point' => 'required|string|max:100',
|
||||
'supplier_key' => 'required',
|
||||
'extends' => 'required_if:type,1',
|
||||
'extends.ip' => 'required_if:type,1|string',
|
||||
'extends.port' => 'required_if:type,1|string',
|
||||
|
|
@ -34,6 +35,13 @@ class DeviceRequest extends FormRequest
|
|||
];
|
||||
}
|
||||
|
||||
public function attributes()
|
||||
{
|
||||
return [
|
||||
'supplier_key' => '设备厂商',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
$messages = [
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class RiceShrimpWeeklyPriceStoreRequest extends FormRequest
|
|||
'week' => [
|
||||
'required',
|
||||
'int',
|
||||
Rule::exists(Keywords::class, 'id')->where(function ($query) {
|
||||
Rule::exists(Keywords::class, 'key')->where(function ($query) {
|
||||
return $query->where('type_key', 'weeks-per-year');
|
||||
}),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class RiceShrimpWeeklyPriceUpdateRequest extends FormRequest
|
|||
'week' => [
|
||||
'filled',
|
||||
'int',
|
||||
Rule::exists(Keywords::class, 'id')->where(function ($query) {
|
||||
Rule::exists(Keywords::class, 'key')->where(function ($query) {
|
||||
return $query->where('type_key', 'weeks-per-year');
|
||||
}),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -31,6 +31,18 @@ class DeviceResource extends JsonResource
|
|||
'created_at' => strtotime($this->created_at) ?? 0, //录入时间
|
||||
'is_recommend' => $this->is_recommend,
|
||||
'sort' => $this->sort ?? 0,
|
||||
'supplier' => $this->whenLoaded('supplier', function () {
|
||||
return $this->supplier ? [
|
||||
'id' => $this->supplier->key,
|
||||
'name' => $this->supplier->name,
|
||||
] : null;
|
||||
}),
|
||||
'project' => $this->whenLoaded('project', function () {
|
||||
return $this->project ? [
|
||||
'id' => $this->project->key,
|
||||
'name' => $this->project->name,
|
||||
] : null;
|
||||
}),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
|
||||
namespace App\Iot\BiAng;
|
||||
|
||||
use App\Exceptions\BiAngException;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Str;
|
||||
use RuntimeException;
|
||||
|
||||
class HttpClient
|
||||
{
|
||||
public function __construct(
|
||||
protected readonly string $username,
|
||||
protected readonly string $password,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新的土壤数据
|
||||
*/
|
||||
public function getLatestSoilReport(string $deviceId)
|
||||
{
|
||||
$result = $this->get(
|
||||
$this->apiUrl('/api/open-api/open/soilMoisture/getCurrentDeviceData'),
|
||||
[
|
||||
'deviceId' => $deviceId,
|
||||
]
|
||||
);
|
||||
|
||||
return $result['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新的气象数据
|
||||
*/
|
||||
public function getLatestMeteorologicalReport(string $deviceId)
|
||||
{
|
||||
$result = $this->get(
|
||||
$this->apiUrl('/api/open-api/open/weather/getCurrentDeviceData'),
|
||||
[
|
||||
'deviceId' => $deviceId,
|
||||
]
|
||||
);
|
||||
|
||||
return $result['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新的杀虫灯数据
|
||||
*/
|
||||
public function getLatestLampReport(string $deviceId)
|
||||
{
|
||||
$result = $this->get(
|
||||
$this->apiUrl2('/open-api/open/getCurrentLampData'),
|
||||
[
|
||||
'deviceId' => $deviceId,
|
||||
]
|
||||
);
|
||||
|
||||
return $result['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 虫情设备/昆虫性诱设备 - 查询某个时间段内的图片
|
||||
*/
|
||||
public function getWormPhotos(string $deviceId, Carbon $start, Carbon $end)
|
||||
{
|
||||
$result = $this->get(
|
||||
$this->apiUrl('/api/open-api/open/getDevicePhotos'),
|
||||
[
|
||||
'deviceId' => $deviceId,
|
||||
'startTime' => $start->toDateString(),
|
||||
'endTime' => $end->toDateString(),
|
||||
]
|
||||
);
|
||||
|
||||
return $result['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 虫情设备 - (识别款)图片虫数识别统计
|
||||
*/
|
||||
public function getWormStatistics(string $deviceId, Carbon $start, Carbon $end)
|
||||
{
|
||||
$result = $this->get(
|
||||
$this->apiUrl('/api/open-api/open/getAllStatistics'),
|
||||
[
|
||||
'imei' => $deviceId,
|
||||
'startAt' => $start->toDateString(),
|
||||
'endAt' => $end->toDateString(),
|
||||
]
|
||||
);
|
||||
|
||||
return $result['data'];
|
||||
}
|
||||
|
||||
public function get(string $url, array $query = []): array
|
||||
{
|
||||
return $this->request('GET', $url, [
|
||||
'query' => $query,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
public function post(string $url, array $data = []): array
|
||||
{
|
||||
return $this->request('POST', $url, [
|
||||
'json' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @param string $url
|
||||
* @param array $options
|
||||
* @return array
|
||||
*
|
||||
* @throws \Illuminate\Http\Client\RequestException
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function request(string $method, string $url, array $options = []): array
|
||||
{
|
||||
switch (strtoupper($method)) {
|
||||
case 'GET':
|
||||
$options['query'] = array_merge($options['query'], [
|
||||
'username' => $this->username,
|
||||
'password' => $this->password,
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'POST':
|
||||
$options['json'] = array_merge($options['json'], [
|
||||
'username' => $this->username,
|
||||
'password' => $this->password,
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
/** @var \Illuminate\Http\Client\Response */
|
||||
$response = Http::withHeaders([
|
||||
'Content-Type' => 'application/json',
|
||||
])->send($method, $url, $options);
|
||||
|
||||
$result = $response->throw()->json();
|
||||
|
||||
if (data_get($result, 'code') === 200) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
throw new BiAngException($result['code'].':'.($result['msg']??'出错啦!'), 500);
|
||||
}
|
||||
|
||||
protected function apiUrl(string $path): string
|
||||
{
|
||||
return 'http://yun.bigdata5s.com'.Str::start($path, '/');
|
||||
}
|
||||
|
||||
protected function apiUrl2(string $path): string
|
||||
{
|
||||
return 'http://yun-api.bigdata5s.com'.Str::start($path, '/');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
|
||||
namespace App\Iot\Linkos;
|
||||
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use RuntimeException;
|
||||
|
||||
class HttpClient
|
||||
{
|
||||
public const ENDPOINT_URL = 'http://service.easylinkin.com';
|
||||
|
||||
public function __construct(
|
||||
protected readonly string $apiKey,
|
||||
protected readonly string $apiSecret,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备的历史数据
|
||||
*
|
||||
* @param string $deviceId
|
||||
* @param \Illuminate\Support\Carbon $start
|
||||
* @param \Illuminate\Support\Carbon $end
|
||||
* @param int $page
|
||||
* @param int $perPage
|
||||
* @return array
|
||||
*/
|
||||
public function deviceFlowList(string $deviceId, Carbon $start, Carbon $end, int $page = 1, int $perPage = 50): array
|
||||
{
|
||||
$result = $this->post('/api/deviceFlow/v1/list', [
|
||||
'device_id' => $deviceId,
|
||||
'start_time' => $start->unix() * 1000,
|
||||
'end_time' => $end->unix() * 1000,
|
||||
'pageable' => [
|
||||
'page' => $page - 1,
|
||||
'size' => $perPage,
|
||||
],
|
||||
]);
|
||||
|
||||
if (data_get($result, 'success') !== true) {
|
||||
throw new RuntimeException(data_get($result, 'msg', '出错啦!'));
|
||||
}
|
||||
|
||||
return $result['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备数据下行
|
||||
*
|
||||
* @param string $deviceId
|
||||
* @param string $service
|
||||
* @param array $data
|
||||
* @param boolean $confirm
|
||||
* @param boolean $clear
|
||||
* @param boolean $schedule
|
||||
* @return array
|
||||
*/
|
||||
public function deviceDataDownlink(string $deviceId, string $service, array $data = [], bool $confirm = true, bool $clear = true, bool $schedule = false): array
|
||||
{
|
||||
return $this->post('/api/down', [
|
||||
'device_id' => $deviceId,
|
||||
'service_id' => $service,
|
||||
'parameter' => $data,
|
||||
'clear' => (int) $clear,
|
||||
'schedule' => (int) $schedule,
|
||||
'confirm' => (int) $confirm,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备最新属性数据
|
||||
*/
|
||||
public function getDeviceStatus(string $deviceId, array $props): array
|
||||
{
|
||||
$result = $this->get('/api/deviceStatus/v1/getDeviceStatus', [
|
||||
'deviceCode' => $deviceId,
|
||||
'prop' => implode(",", $props),
|
||||
]);
|
||||
|
||||
if (data_get($result, 'success') !== true) {
|
||||
throw new RuntimeException(data_get($result, 'msg', '出错啦!'));
|
||||
}
|
||||
|
||||
return $result['data'];
|
||||
}
|
||||
|
||||
public function get(string $url, array $query = []): array
|
||||
{
|
||||
return $this->request('GET', $url, [
|
||||
'query' => $query,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
public function post(string $url, array $data = []): array
|
||||
{
|
||||
return $this->request('POST', $url, [
|
||||
'json' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @param string $url
|
||||
* @param array $options
|
||||
* @return array
|
||||
*
|
||||
* @throws \Illuminate\Http\Client\RequestException
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function request(string $method, string $url, array $options = []): array
|
||||
{
|
||||
$nonce = $this->nonce();
|
||||
|
||||
$timestamp = now()->getTimestampMs();
|
||||
|
||||
/** @var \Illuminate\Http\Client\Response */
|
||||
$response = Http::withHeaders([
|
||||
'Content-Type' => 'application/json',
|
||||
'api-key' => $this->apiKey,
|
||||
'Nonce' => $nonce,
|
||||
'Timestamp' => $timestamp,
|
||||
'Signature' => $this->sign(compact('nonce', 'timestamp')),
|
||||
])->baseUrl(self::ENDPOINT_URL)->send($method, $url, $options);
|
||||
|
||||
return $response->throw()->json();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
protected function sign(array $data): string
|
||||
{
|
||||
return sha1(
|
||||
sprintf(
|
||||
'%s%s%s',
|
||||
$data['nonce'] ?? '',
|
||||
$data['timestamp'] ?? '',
|
||||
$this->apiSecret
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected function nonce(): string
|
||||
{
|
||||
$nonce = '';
|
||||
|
||||
for ($i = 0; $i < 8; $i++) {
|
||||
$nonce .= mt_rand(0, 9);
|
||||
}
|
||||
|
||||
return $nonce;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,11 @@ use EloquentFilter\ModelFilter;
|
|||
|
||||
class DeviceFilter extends ModelFilter
|
||||
{
|
||||
public function sn($sn)
|
||||
{
|
||||
return $this->where('sn', $sn);
|
||||
}
|
||||
|
||||
public function point($point)
|
||||
{
|
||||
return $this->where('monitoring_point', 'like', $point.'%');
|
||||
|
|
@ -26,6 +31,16 @@ class DeviceFilter extends ModelFilter
|
|||
return $this->where('status', $status);
|
||||
}
|
||||
|
||||
public function supplierKey($supplierKey)
|
||||
{
|
||||
return $this->where('supplier_key', $supplierKey);
|
||||
}
|
||||
|
||||
public function projectKey($projectKey)
|
||||
{
|
||||
return $this->where('project_key', $projectKey);
|
||||
}
|
||||
|
||||
public function isRecommend($isRecommend){
|
||||
return $this->where('is_recommend', $isRecommend);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\DeviceType;
|
||||
use App\Enums\DeviceStatus;
|
||||
use EloquentFilter\Filterable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Enums\DeviceType;
|
||||
use Dcat\Admin\Traits\HasDateTimeFormatter;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use EloquentFilter\Filterable;
|
||||
use Illuminate\Contracts\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Peidikeji\Keywords\Models\Keywords;
|
||||
|
||||
class Device extends Model
|
||||
{
|
||||
|
|
@ -37,8 +39,15 @@ class Device extends Model
|
|||
'created_by',
|
||||
'updated_by',
|
||||
'sort',
|
||||
'supplier_key',
|
||||
'project_key',
|
||||
];
|
||||
|
||||
public function scopeSupplierBy(Builder $query, string $supplier): void
|
||||
{
|
||||
$query->whereHas('supplier', fn ($query) => $query->where('supplier_key', $supplier));
|
||||
}
|
||||
|
||||
public function base()
|
||||
{
|
||||
return $this->belongsTo(AgriculturalBase::class, 'agricultural_base_id');
|
||||
|
|
@ -53,4 +62,24 @@ class Device extends Model
|
|||
{
|
||||
return $this->belongsTo(AdminUser::class, 'updated_by');
|
||||
}
|
||||
|
||||
public function supplier(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Keywords::class, 'supplier_key', 'key');
|
||||
}
|
||||
|
||||
public function project(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Keywords::class, 'project_key', 'key');
|
||||
}
|
||||
|
||||
public function isTypeSoil(): bool
|
||||
{
|
||||
return $this->type === DeviceType::Soil;
|
||||
}
|
||||
|
||||
public function isTypeMeteorological(): bool
|
||||
{
|
||||
return $this->type === DeviceType::Meteorological;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DeviceLog extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $casts = [
|
||||
'data' => 'json',
|
||||
'reported_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'device_id',
|
||||
'data',
|
||||
'reported_at',
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class InsecticidalLampDailyReport extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $casts = [
|
||||
'reported_at' => 'date',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'device_id',
|
||||
'agricultural_base_id',
|
||||
'battery_vol',
|
||||
'killed_num',
|
||||
'air_temperature',
|
||||
'air_humidity',
|
||||
'charging_vol',
|
||||
'high_vol',
|
||||
'reported_at',
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class InsecticidalLampReport extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $casts = [
|
||||
'reported_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'device_id',
|
||||
'agricultural_base_id',
|
||||
'battery_vol',
|
||||
'killed_num',
|
||||
'air_temperature',
|
||||
'air_humidity',
|
||||
'charging_vol',
|
||||
'high_vol',
|
||||
'reported_at',
|
||||
];
|
||||
}
|
||||
|
|
@ -10,6 +10,15 @@ class MeteorologicalMonitoringDailyLog extends Model
|
|||
{
|
||||
use HasFactory;
|
||||
|
||||
const WIND_DIRECTION_NORTH = 0;
|
||||
const WIND_DIRECTION_NORTHEAST = 1;
|
||||
const WIND_DIRECTION_EAST = 2;
|
||||
const WIND_DIRECTION_SOUTHEAST = 3;
|
||||
const WIND_DIRECTION_SOUTH = 4;
|
||||
const WIND_DIRECTION_SOUTHWEST = 5;
|
||||
const WIND_DIRECTION_WEST = 6;
|
||||
const WIND_DIRECTION_NORTHWEST = 7;
|
||||
|
||||
protected $casts = [
|
||||
'wind_direction' => WindDirection::class,
|
||||
'monitored_at' => 'date',
|
||||
|
|
|
|||
|
|
@ -10,6 +10,15 @@ class MeteorologicalMonitoringLog extends Model
|
|||
{
|
||||
use HasFactory;
|
||||
|
||||
const WIND_DIRECTION_NORTH = 0;
|
||||
const WIND_DIRECTION_NORTHEAST = 1;
|
||||
const WIND_DIRECTION_EAST = 2;
|
||||
const WIND_DIRECTION_SOUTHEAST = 3;
|
||||
const WIND_DIRECTION_SOUTH = 4;
|
||||
const WIND_DIRECTION_SOUTHWEST = 5;
|
||||
const WIND_DIRECTION_WEST = 6;
|
||||
const WIND_DIRECTION_NORTHWEST = 7;
|
||||
|
||||
protected $casts = [
|
||||
'wind_direction' => WindDirection::class,
|
||||
'monitored_at' => 'datetime',
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class RiceShrimpWeeklyPrice extends Model
|
|||
|
||||
public function weekObj()
|
||||
{
|
||||
return $this->belongsTo(Keywords::class, 'week');
|
||||
return $this->belongsTo(Keywords::class, 'week', 'key');
|
||||
}
|
||||
|
||||
public function createdBy()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class WormPhoto extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $casts = [
|
||||
'uploaded_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'device_id', 'url', 'uploaded_at',
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class WormReport extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $casts = [
|
||||
'data' => 'json',
|
||||
'reported_at' => 'date',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'device_id', 'agricultural_base_id', 'worm_num', 'data', 'reported_at',
|
||||
];
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ use App\Services\LinkosService;
|
|||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use App\Iot\Linkos\HttpClient as LinkosHttpClient;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
|
|
@ -22,6 +23,8 @@ class AppServiceProvider extends ServiceProvider
|
|||
|
||||
return new LinkosService($config['key'] ?? '', $config['secret'] ?? '');
|
||||
});
|
||||
|
||||
$this->registerLinkos();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -48,4 +51,14 @@ class AppServiceProvider extends ServiceProvider
|
|||
// ]);
|
||||
|
||||
}
|
||||
|
||||
protected function registerLinkos(): void
|
||||
{
|
||||
$this->app->singleton(LinkosHttpClient::class, function ($app) {
|
||||
return new LinkosHttpClient(
|
||||
(string) $app['config']->get('services.linkos.key'),
|
||||
(string) $app['config']->get('services.linkos.secret')
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,604 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Enums\DeviceType;
|
||||
use App\Exceptions\BizException;
|
||||
use App\Iot\BiAng\HttpClient;
|
||||
use App\Models\Device;
|
||||
use App\Models\DeviceLog;
|
||||
use App\Models\InsecticidalLampDailyReport;
|
||||
use App\Models\InsecticidalLampReport;
|
||||
use App\Models\MeteorologicalMonitoringDailyLog;
|
||||
use App\Models\MeteorologicalMonitoringLog;
|
||||
use App\Models\SoilMonitoringDailyLog;
|
||||
use App\Models\SoilMonitoringLog;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class BiAngDeviceService
|
||||
{
|
||||
/**
|
||||
* 虫情设备/昆虫性诱设备 - 查询某个时间段内的图片
|
||||
*/
|
||||
public function getWormPhotos(Device $device, Carbon $start, Carbon $end): array
|
||||
{
|
||||
try {
|
||||
$httpClient = $this->buildHttpClient($device);
|
||||
} catch (BizException $e) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $httpClient->getWormPhotos($device->sn, $start, $end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 虫情设备 - (识别款)图片虫数识别统计
|
||||
*/
|
||||
public function getWormStatistics(Device $device, Carbon $start, Carbon $end): array
|
||||
{
|
||||
try {
|
||||
$httpClient = $this->buildHttpClient($device);
|
||||
} catch (BizException $e) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $httpClient->getWormStatistics($device->sn, $start, $end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建设备报告
|
||||
*/
|
||||
public function createReport(Device $device, Carbon $time): void
|
||||
{
|
||||
switch ($device->type) {
|
||||
case DeviceType::Soil:
|
||||
$this->createSoilReport($device, $time);
|
||||
break;
|
||||
|
||||
case DeviceType::Meteorological:
|
||||
$this->createMeteorologicalReport($device, $time);
|
||||
break;
|
||||
|
||||
case DeviceType::InsecticidalLamp:
|
||||
$this->createInsecticidalLampReport($device, $time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建土壤设备报告
|
||||
*/
|
||||
protected function createSoilReport(Device $device, Carbon $time): void
|
||||
{
|
||||
$reportedAt = $time->copy()->startOfHour();
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Collection */
|
||||
$logs = DeviceLog::where('device_id', $device->id)
|
||||
->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) {
|
||||
'soilAlkalineHydrolyzedNitrogen' => 'n',
|
||||
'soilAvailablePotassium' => 'k',
|
||||
'soilAvailablePhosphorus' => 'p',
|
||||
'soilConductivity' => 'conductivity',
|
||||
'soilTemperature' => 'temperature',
|
||||
'soilMoisture' => 'humidity',
|
||||
default => null,
|
||||
};
|
||||
|
||||
if ($attribute) {
|
||||
$attributes[$attribute] = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}, []);
|
||||
|
||||
$soilReport = SoilMonitoringLog::where([
|
||||
'device_id' => $device->id,
|
||||
'monitored_at' => $reportedAt,
|
||||
])->first();
|
||||
|
||||
if ($soilReport === null) {
|
||||
$lastSoilReport = SoilMonitoringLog::where([
|
||||
'device_id' => $device->id,
|
||||
'monitored_at' => $reportedAt->copy()->subHour(),
|
||||
])->first();
|
||||
|
||||
$soilReport = $lastSoilReport?->replicate() ?: new SoilMonitoringLog();
|
||||
|
||||
$soilReport->fill([
|
||||
'device_id' => $device->id,
|
||||
'monitored_at' => $reportedAt,
|
||||
'agricultural_base_id' => $device->agricultural_base_id,
|
||||
]);
|
||||
}
|
||||
|
||||
$soilReport->fill($attributes)->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建气象设备报告
|
||||
*/
|
||||
protected function createMeteorologicalReport(Device $device, Carbon $time): void
|
||||
{
|
||||
$reportedAt = $time->copy()->startOfHour();
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Collection */
|
||||
$logs = DeviceLog::where('device_id', $device->id)
|
||||
->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) {
|
||||
'rainfall' => 'moment_rainfall', //瞬时降雨量
|
||||
'lightIntensity' => 'illumination',
|
||||
'airTemperature' => 'air_temperature',
|
||||
'airHumidity' => 'air_humidity',
|
||||
'windDirection' => 'wind_degree',
|
||||
'windSpeed' => 'wind_speed',
|
||||
default => null,
|
||||
};
|
||||
|
||||
if ($attribute) {
|
||||
switch ($attribute) {
|
||||
case 'moment_rainfall':
|
||||
$attributes[$attribute] = bcadd($attributes[$attribute] ?? '0.00', $v, 2);
|
||||
break;
|
||||
|
||||
case 'wind_degree':
|
||||
$attributes['wind_direction'] = value(function ($v) {
|
||||
if ($v >= 22.5 && $v < 67.5) {
|
||||
return MeteorologicalMonitoringLog::WIND_DIRECTION_NORTHEAST;
|
||||
} elseif ($v >= 67.5 && $v < 112.5) {
|
||||
return MeteorologicalMonitoringLog::WIND_DIRECTION_EAST;
|
||||
} elseif ($v >= 112.5 && $v < 157.5) {
|
||||
return MeteorologicalMonitoringLog::WIND_DIRECTION_SOUTHEAST;
|
||||
} elseif ($v >= 157.5 && $v < 202.5) {
|
||||
return MeteorologicalMonitoringLog::WIND_DIRECTION_SOUTH;
|
||||
} elseif ($v >= 202.5 && $v < 247.5) {
|
||||
return MeteorologicalMonitoringLog::WIND_DIRECTION_SOUTHWEST;
|
||||
} elseif ($v >= 247.5 && $v < 292.5) {
|
||||
return MeteorologicalMonitoringLog::WIND_DIRECTION_WEST;
|
||||
} elseif ($v >= 292.5 && $v < 337.5) {
|
||||
return MeteorologicalMonitoringLog::WIND_DIRECTION_NORTHWEST;
|
||||
}
|
||||
return MeteorologicalMonitoringLog::WIND_DIRECTION_NORTH;
|
||||
}, $v);
|
||||
default:
|
||||
$attributes[$attribute] = $v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}, []);
|
||||
|
||||
# 累计雨量求和
|
||||
$attributes['current_rainfall'] = DeviceLog::select(DB::raw("SUM((data->>'rainfall')::NUMERIC) as aggregate"))->where('device_id', $device->id)
|
||||
->whereBetween('reported_at', [$reportedAt->copy()->startOfDay(), $reportedAt->copy()->endOfHour()])
|
||||
->value('aggregate') ?: 0;
|
||||
|
||||
$meteorologicalReport = MeteorologicalMonitoringLog::where([
|
||||
'device_id' => $device->id,
|
||||
'monitored_at' => $reportedAt,
|
||||
])->first();
|
||||
|
||||
if ($meteorologicalReport === null) {
|
||||
$lastMeteorologicalReport = MeteorologicalMonitoringLog::where([
|
||||
'device_id' => $device->id,
|
||||
'monitored_at' => $reportedAt->copy()->subHour(),
|
||||
])->first();
|
||||
|
||||
$meteorologicalReport = $lastMeteorologicalReport?->replicate() ?: new MeteorologicalMonitoringLog();
|
||||
|
||||
$meteorologicalReport->fill([
|
||||
'device_id' => $device->id,
|
||||
'monitored_at' => $reportedAt,
|
||||
'agricultural_base_id' => $device->agricultural_base_id,
|
||||
]);
|
||||
}
|
||||
|
||||
$meteorologicalReport->fill($attributes)->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建杀虫灯设备报告
|
||||
*/
|
||||
protected function createInsecticidalLampReport(Device $device, Carbon $time): void
|
||||
{
|
||||
$reportedAt = $time->copy()->startOfHour();
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Collection */
|
||||
$logs = DeviceLog::where('device_id', $device->id)
|
||||
->whereBetween('reported_at', [$reportedAt, $reportedAt->copy()->endOfHour()])
|
||||
->oldest('reported_at')
|
||||
->get();
|
||||
|
||||
if ($logs->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$attributes = value(function ($logs) {
|
||||
$data = [
|
||||
'vol' => ['sum' => 0, 'count' => 0],
|
||||
'sunVol' => ['sum' => 0, 'count' => 0],
|
||||
'dct' => ['sum' => 0, 'count' => 0],
|
||||
'temp' => ['sum' => 0, 'count' => 0],
|
||||
'humidity' => ['sum' => 0, 'count' => 0],
|
||||
'highVol' => ['sum' => 0, 'count' => 0],
|
||||
];
|
||||
|
||||
/** @var \App\Models\DeviceLog */
|
||||
foreach ($logs as $log) {
|
||||
if (! is_array($log->data)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($data as $k => $item) {
|
||||
$v = $log->data[$k] ?? null;
|
||||
|
||||
if (is_null($v)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$item['sum'] = bcadd($item['sum'], $v, 2);
|
||||
$item['count']++;
|
||||
|
||||
$data[$k] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
$attributes = [];
|
||||
|
||||
foreach ($data as $key => $item) {
|
||||
$attribute = match ($key) {
|
||||
'vol' => 'battery_vol',
|
||||
'sunVol' => 'charging_vol',
|
||||
'dct' => 'killed_num',
|
||||
'temp' => 'air_temperature',
|
||||
'humidity' => 'air_humidity',
|
||||
'highVol' => 'high_vol',
|
||||
};
|
||||
|
||||
if ($item['count'] > 0) {
|
||||
if ($attribute === 'killed_num') {
|
||||
$attributes[$attribute] = (int) $item['sum'];
|
||||
} else {
|
||||
$attributes[$attribute] = round(bcdiv($item['sum'], $item['count'], 2), 2);
|
||||
}
|
||||
} else {
|
||||
$attributes[$attribute] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}, $logs);
|
||||
|
||||
/** @var \App\Models\InsecticidalLampReport */
|
||||
$insecticidalLampReport = InsecticidalLampReport::firstOrNew([
|
||||
'device_id' => $device->id,
|
||||
'reported_at' => $reportedAt,
|
||||
], [
|
||||
'agricultural_base_id' => $device->agricultural_base_id,
|
||||
]);
|
||||
|
||||
$insecticidalLampReport->fill($attributes)->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建设备每日报告
|
||||
*/
|
||||
public function createDailyReport(Device $device, Carbon $time): void
|
||||
{
|
||||
switch ($device->type) {
|
||||
case DeviceType::Meteorological:
|
||||
$this->createMeteorologicalDailyReport($device, $time);
|
||||
break;
|
||||
|
||||
case DeviceType::Soil:
|
||||
$this->createSoilDailyReport($device, $time);
|
||||
break;
|
||||
|
||||
case DeviceType::InsecticidalLamp:
|
||||
$this->createInsecticidalLampDailyReport($device, $time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建土壤设备每日报告
|
||||
*/
|
||||
protected function createSoilDailyReport(Device $device, Carbon $date): void
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Collection */
|
||||
$soilReports = SoilMonitoringLog::where('device_id', $device->id)
|
||||
->whereDate('monitored_at', $date)
|
||||
->oldest('monitored_at')
|
||||
->get();
|
||||
|
||||
if ($soilReports->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$attributes = value(function ($soilReports) {
|
||||
$data = [
|
||||
'n' => ['sum' => 0, 'count' => 0],
|
||||
'p' => ['sum' => 0, 'count' => 0],
|
||||
'k' => ['sum' => 0, 'count' => 0],
|
||||
'conductivity' => ['sum' => 0, 'count' => 0],
|
||||
'temperature' => ['sum' => 0, 'count' => 0],
|
||||
'humidity' => ['sum' => 0, 'count' => 0],
|
||||
'moisture' => ['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 = SoilMonitoringDailyLog::firstOrNew([
|
||||
'device_id' => $device->id,
|
||||
'monitored_at' => $date->format('Y-m-d'),
|
||||
], [
|
||||
'agricultural_base_id' => $device->agricultural_base_id,
|
||||
]);
|
||||
|
||||
$soilDailyReport->fill($attributes)->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建气象设备每日报告
|
||||
*/
|
||||
protected function createMeteorologicalDailyReport(Device $device, Carbon $date): void
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Collection */
|
||||
$meteorologicalReports = MeteorologicalMonitoringLog::where('device_id', $device->id)
|
||||
->whereDate('monitored_at', $date)
|
||||
->oldest('monitored_at')
|
||||
->get();
|
||||
|
||||
if ($meteorologicalReports->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$attributes = value(function ($meteorologicalReports) {
|
||||
$data = [
|
||||
'current_rainfall' => 0,
|
||||
'illumination' => ['sum' => 0, 'count' => 0],
|
||||
'air_temperature' => ['sum' => 0, 'count' => 0],
|
||||
'air_humidity' => ['sum' => 0, 'count' => 0],
|
||||
'wind_speed' => ['sum' => 0, 'count' => 0],
|
||||
'wind_samples' => [],
|
||||
];
|
||||
|
||||
foreach ($meteorologicalReports as $meteorologicalReport) {
|
||||
foreach ($data as $k => $item) {
|
||||
if ($k === 'wind_samples') {
|
||||
if (is_null($meteorologicalReport->wind_degree) || is_null($meteorologicalReport->wind_speed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$item[] = [
|
||||
'wind_degree' => $meteorologicalReport->wind_degree, // 风向度数
|
||||
'wind_speed' => $meteorologicalReport->wind_speed, // 风速
|
||||
];
|
||||
} elseif (! is_null($v = $meteorologicalReport->{$k})) {
|
||||
if ($k === 'current_rainfall') {
|
||||
$item = $v;
|
||||
} else {
|
||||
$item['sum'] = bcadd($item['sum'], $v, 2);
|
||||
$item['count']++;
|
||||
}
|
||||
}
|
||||
|
||||
$data[$k] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
$attributes = [];
|
||||
|
||||
foreach ($data as $key => $item) {
|
||||
switch ($key) {
|
||||
case 'current_rainfall':
|
||||
$attributes['daily_rainfall'] = $item;
|
||||
break;
|
||||
case 'wind_samples':
|
||||
if (! empty($item)) {
|
||||
$attributes['wind_degree'] = value(function (array $windSamples) {
|
||||
if (empty($windSamples)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$x = 0;
|
||||
$y = 0;
|
||||
|
||||
foreach ($windSamples as $sample) {
|
||||
if ($sample['wind_degree'] == 0 && $sample['wind_speed'] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 角度转弧度
|
||||
$radian = deg2rad($sample['wind_degree']);
|
||||
|
||||
// $x += $sample['wind_speed'] * sin($radian);
|
||||
// $y += $sample['wind_speed'] * cos($radian);
|
||||
$x += sin($radian);
|
||||
$y += cos($radian);
|
||||
}
|
||||
|
||||
$degree = round(rad2deg(atan2($y, $x)));
|
||||
|
||||
if (($x > 0 || $x < 0) && $y < 0) {
|
||||
$degree += 180;
|
||||
} elseif ($x < 0 && $y > 0) {
|
||||
$degree += 360;
|
||||
}
|
||||
|
||||
return $degree;
|
||||
}, $item);
|
||||
|
||||
$attributes['wind_direction'] = value(function ($windDegree) {
|
||||
if (is_null($windDegree)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($windDegree >= 22.5 && $windDegree < 67.5) {
|
||||
return MeteorologicalMonitoringDailyLog::WIND_DIRECTION_NORTHEAST;
|
||||
} elseif ($windDegree >= 67.5 && $windDegree < 112.5) {
|
||||
return MeteorologicalMonitoringDailyLog::WIND_DIRECTION_EAST;
|
||||
} elseif ($windDegree >= 112.5 && $windDegree < 157.5) {
|
||||
return MeteorologicalMonitoringDailyLog::WIND_DIRECTION_SOUTHEAST;
|
||||
} elseif ($windDegree >= 157.5 && $windDegree < 202.5) {
|
||||
return MeteorologicalMonitoringDailyLog::WIND_DIRECTION_SOUTH;
|
||||
} elseif ($windDegree >= 202.5 && $windDegree < 247.5) {
|
||||
return MeteorologicalMonitoringDailyLog::WIND_DIRECTION_SOUTHWEST;
|
||||
} elseif ($windDegree >= 247.5 && $windDegree < 292.5) {
|
||||
return MeteorologicalMonitoringDailyLog::WIND_DIRECTION_WEST;
|
||||
} elseif ($windDegree >= 292.5 && $windDegree < 337.5) {
|
||||
return MeteorologicalMonitoringDailyLog::WIND_DIRECTION_NORTHWEST;
|
||||
}
|
||||
|
||||
return MeteorologicalMonitoringDailyLog::WIND_DIRECTION_NORTH;
|
||||
}, $attributes['wind_degree']);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$attributes[$key] = $item['count'] > 0 ? round(bcdiv($item['sum'], $item['count'], 2), 2) : null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}, $meteorologicalReports);
|
||||
|
||||
/** @var \App\Models\MeteorologicalMonitoringDailyLog */
|
||||
$meteorologicalDailyReport = MeteorologicalMonitoringDailyLog::firstOrNew([
|
||||
'device_id' => $device->id,
|
||||
'monitored_at' => $date,
|
||||
], [
|
||||
'agricultural_base_id' => $device->agricultural_base_id,
|
||||
]);
|
||||
|
||||
$meteorologicalDailyReport->fill($attributes)->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 杀虫灯每日报告
|
||||
*/
|
||||
protected function createInsecticidalLampDailyReport(Device $device, Carbon $date): void
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Collection */
|
||||
$insecticidalLampReports = InsecticidalLampReport::where('device_id', $device->id)
|
||||
->whereDate('reported_at', $date)
|
||||
->oldest('reported_at')
|
||||
->get();
|
||||
|
||||
if ($insecticidalLampReports->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$attributes = value(function ($insecticidalLampReports) {
|
||||
$data = [
|
||||
'killed_num' => ['sum' => 0, 'count' => 0],
|
||||
'battery_vol' => ['sum' => 0, 'count' => 0],
|
||||
'air_temperature' => ['sum' => 0, 'count' => 0],
|
||||
'air_humidity' => ['sum' => 0, 'count' => 0],
|
||||
'charging_vol' => ['sum' => 0, 'count' => 0],
|
||||
'high_vol' => ['sum' => 0, 'count' => 0],
|
||||
];
|
||||
|
||||
foreach ($insecticidalLampReports as $insecticidalLampReport) {
|
||||
foreach ($data as $k => $item) {
|
||||
if (is_null($v = $insecticidalLampReport->{$k})) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$item['sum'] = bcadd($item['sum'], $v, 2);
|
||||
$item['count']++;
|
||||
|
||||
$data[$k] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
$attributes = [];
|
||||
|
||||
foreach ($data as $key => $item) {
|
||||
if ($item['count'] > 0) {
|
||||
if ($key === 'killed_num') {
|
||||
$attributes[$key] = (int) $item['sum'];
|
||||
} else {
|
||||
$attributes[$key] = round(bcdiv($item['sum'], $item['count'], 2), 2);
|
||||
}
|
||||
} else {
|
||||
$attributes[$key] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}, $insecticidalLampReports);
|
||||
|
||||
/** @var \App\Models\InsecticidalLampDailyReport */
|
||||
$insecticidalLampDailyReport = InsecticidalLampDailyReport::firstOrNew([
|
||||
'device_id' => $device->id,
|
||||
'reported_at' => $date->format('Y-m-d'),
|
||||
], [
|
||||
'agricultural_base_id' => $device->agricultural_base_id,
|
||||
]);
|
||||
|
||||
$insecticidalLampDailyReport->fill($attributes)->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 HTTP 客户端
|
||||
*/
|
||||
public function buildHttpClient(Device $device): HttpClient
|
||||
{
|
||||
$config = json_decode($device->project?->value, true);
|
||||
|
||||
if (! is_array($config)) {
|
||||
throw new BizException('账户信息未找到');
|
||||
}
|
||||
|
||||
return new HttpClient($config['username'] ?? '', $config['password'] ?? '');
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Enums\DeviceStatus;
|
||||
use App\Enums\DeviceType;
|
||||
use App\Enums\WindDirection;
|
||||
use App\Exceptions\BizException;
|
||||
|
|
@ -89,6 +90,8 @@ class LinkosDeviceLogService
|
|||
throw new BizException("设备未找到, 设备编号: {$deviceId}");
|
||||
}
|
||||
|
||||
Device::where('sn', $deviceId)->update(['status' => DeviceStatus::Online]);
|
||||
|
||||
$log = LinkosDeviceLog::create([
|
||||
'device_id' => $deviceId,
|
||||
'device_unit' => $deviceUnit,
|
||||
|
|
@ -235,7 +238,7 @@ class LinkosDeviceLogService
|
|||
}
|
||||
|
||||
$log->save();
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ return new class extends Migration
|
|||
$table->timestamps();
|
||||
|
||||
$table->index('agricultural_base_id');
|
||||
$table->unique(['device_id', 'monitored_at']);
|
||||
$table->unique(['device_id', 'monitored_at'], 'uq_device_id_monitored_at');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ return new class extends Migration
|
|||
$table->timestamps();
|
||||
|
||||
$table->index('agricultural_base_id');
|
||||
$table->unique(['device_id', 'monitored_at']);
|
||||
$table->unique(['device_id', 'monitored_at'], 'uq_device_id_monitored_at');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ return new class extends Migration
|
|||
$table->timestamps();
|
||||
|
||||
$table->index('agricultural_base_id');
|
||||
$table->unique(['device_id', 'monitored_at']);
|
||||
$table->unique(['device_id', 'monitored_at'], 'uq_device_id_monitored_at');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ return new class extends Migration
|
|||
$table->timestamps();
|
||||
|
||||
$table->index('agricultural_base_id');
|
||||
$table->unique(['device_id', 'monitored_at']);
|
||||
$table->unique(['device_id', 'monitored_at'], 'uq_device_id_monitored_at');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
<?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('worm_reports', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('device_id');
|
||||
$table->unsignedBigInteger('agricultural_base_id')->comment('农业基地ID');
|
||||
$table->unsignedInteger('worm_num')->comment('虫子数量');
|
||||
$table->json('data')->nullable();
|
||||
$table->date('reported_at')->comment('报告日期');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('worm_reports');
|
||||
}
|
||||
};
|
||||
|
|
@ -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('devices', function (Blueprint $table) {
|
||||
$table->string('supplier_key')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('devices', function (Blueprint $table) {
|
||||
$table->dropColumn(['supplier_key']);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?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('device_logs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('device_id');
|
||||
$table->json('data')->nullable();
|
||||
$table->timestamp('reported_at');
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['device_id', 'reported_at']);
|
||||
$table->index('reported_at');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('device_logs');
|
||||
}
|
||||
};
|
||||
|
|
@ -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('devices', function (Blueprint $table) {
|
||||
$table->string('project_key')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('devices', function (Blueprint $table) {
|
||||
$table->dropColumn(['project_key']);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -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('meteorological_monitoring_daily_logs', function (Blueprint $table) {
|
||||
$table->decimal('wind_degree', 5, 2)->nullable()->comment('风向度数')->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('meteorological_monitoring_daily_logs', function (Blueprint $table) {
|
||||
$table->integer('wind_degree')->nullable()->comment('风向度数')->change();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<?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('meteorological_monitoring_logs', function (Blueprint $table) {
|
||||
$table->decimal('wind_degree', 5, 2)->nullable()->comment('风向度数')->change();
|
||||
$table->decimal('illumination', 10, 2)->nullable()->comment('光照度 (单位: Lux)')->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('meteorological_monitoring_logs', function (Blueprint $table) {
|
||||
$table->integer('wind_degree')->nullable()->comment('风向度数')->change();
|
||||
$table->integer('illumination')->nullable()->comment('光照度 (单位: Lux)')->change();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?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_monitoring_logs', function (Blueprint $table) {
|
||||
$table->decimal('n', 8, 2)->nullable()->comment('氮 (单位: mg/kg)')->change();
|
||||
$table->decimal('p', 8, 2)->nullable()->comment('磷 (单位: mg/kg)')->change();
|
||||
$table->decimal('k', 8, 2)->nullable()->comment('钾 (单位: mg/kg)')->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('soil_monitoring_logs', function (Blueprint $table) {
|
||||
$table->integer('n')->nullable()->comment('氮 (单位: mg/kg)')->change();
|
||||
$table->integer('p')->nullable()->comment('磷 (单位: mg/kg)')->change();
|
||||
$table->integer('k')->nullable()->comment('钾 (单位: mg/kg)')->change();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?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('insecticidal_lamp_reports', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('device_id');
|
||||
$table->unsignedBigInteger('agricultural_base_id')->comment('农业基地ID');
|
||||
$table->double('battery_vol')->nullable()->comment('蓄电池电压');
|
||||
$table->unsignedInteger('killed_num')->default(0)->comment('杀虫数');
|
||||
$table->double('air_temperature')->nullable()->comment('大气气温');
|
||||
$table->double('air_humidity')->nullable()->comment('大气湿度');
|
||||
$table->double('charging_vol')->nullable()->comment('充电电压');
|
||||
$table->double('high_vol')->nullable()->comment('高压值');
|
||||
$table->timestamp('reported_at')->comment('监控日期');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('insecticidal_lamp_reports');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?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('insecticidal_lamp_daily_reports', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('device_id');
|
||||
$table->unsignedBigInteger('agricultural_base_id')->comment('农业基地ID');
|
||||
$table->double('battery_vol')->nullable()->comment('蓄电池电压');
|
||||
$table->unsignedInteger('killed_num')->default(0)->comment('杀虫数');
|
||||
$table->double('air_temperature')->nullable()->comment('大气气温');
|
||||
$table->double('air_humidity')->nullable()->comment('大气湿度');
|
||||
$table->double('charging_vol')->nullable()->comment('充电电压');
|
||||
$table->double('high_vol')->nullable()->comment('高压值');
|
||||
$table->date('reported_at')->comment('监控日期');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('insecticidal_lamp_reports');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?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('worm_photos', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('device_id');
|
||||
$table->text('url')->nullable();
|
||||
$table->timestamp('uploaded_at')->comment('上传时间');
|
||||
$table->timestamps();
|
||||
|
||||
$table->index('uploaded_at');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('worm_photos');
|
||||
}
|
||||
};
|
||||
|
|
@ -32,6 +32,62 @@ class KeywordsTableSeeder extends Seeder
|
|||
['key' => 'crops-cate-lingye', 'name' => '林业', 'type_key' => 'crops-category', 'value' => ''],
|
||||
['key' => 'crops-cate-activity', 'name' => '其他', 'type_key' => 'crops-category', 'value' => ''],
|
||||
]],
|
||||
[
|
||||
'key' => 'industry_key',
|
||||
'name' => '产业类型',
|
||||
'value' => '',
|
||||
'list' => [
|
||||
['key' => 'industry_1', 'name' => '稻渔综合种养', 'value' => 1],
|
||||
['key' => 'industry_2', 'name' => '优品柑桔种植', 'value' => 2],
|
||||
['key' => 'industry_3', 'name' => '高梁产业', 'value' => 3],
|
||||
['key' => 'industry_4', 'name' => '油茶产业', 'value' => 4],
|
||||
['key' => 'industry_5', 'name' => '生猪产业分布点', 'value' => 5],
|
||||
['key' => 'industry_6', 'name' => '肉羊产业分布点', 'value' => 6],
|
||||
],
|
||||
],
|
||||
[
|
||||
'key' => 'device-supplier',
|
||||
'name' => '设备供应商',
|
||||
'value' => '',
|
||||
'list' => [
|
||||
['key' => 'device-supplier-linkos', 'name' => '慧联无限', 'value' => ''],
|
||||
['key' => 'device-supplier-biang', 'name' => '比昂', 'value' => ''],
|
||||
['key' => 'device-supplier-yunfei', 'name' => '云飞', 'value' => ''],
|
||||
['key' => 'device-supplier-other', 'name' => '其它', 'value' => ''],
|
||||
],
|
||||
],
|
||||
[
|
||||
'key' => 'device-project',
|
||||
'name' => '设备项目',
|
||||
'value' => '',
|
||||
'list' => [
|
||||
[
|
||||
'key' => 'device-project-cyxdnyyq',
|
||||
'name' => '成渝现代高效特色农业带合作园区',
|
||||
'value' => json_encode([
|
||||
'username' => '成渝现代高效特色农业带合作园区',
|
||||
'password' => '888888',
|
||||
]),
|
||||
],
|
||||
[
|
||||
'key' => 'device-project-nyncj',
|
||||
'name' => '隆昌市农业农村局',
|
||||
'value' => json_encode([
|
||||
'username' => '隆昌市农业农村局',
|
||||
'password' => '888888',
|
||||
]),
|
||||
],
|
||||
[
|
||||
'key' => 'device-project-syqshc',
|
||||
'name' => '隆昌市石燕桥镇三合村股份经济联合社',
|
||||
'value' => json_encode([
|
||||
'username' => '隆昌市石燕桥镇三合村股份经济联合社',
|
||||
'password' => '888888',
|
||||
]),
|
||||
],
|
||||
['key' => 'device-project-other', 'name' => '其它', 'value' => ''],
|
||||
],
|
||||
]
|
||||
];
|
||||
|
||||
$list[] = value(function () {
|
||||
|
|
@ -53,14 +109,15 @@ class KeywordsTableSeeder extends Seeder
|
|||
|
||||
private function createKeywords($keywords, $parentType = null)
|
||||
{
|
||||
foreach ($keywords as $item) {
|
||||
foreach ($keywords as $i => $item) {
|
||||
if ($parentType) {
|
||||
$type = Keywords::create([
|
||||
'name' => $item['name'],
|
||||
'key' => $item['key'] ?? $parentType->key.($parentType + 1),
|
||||
'key' => $item['key'] ?? $parentType->key.($i + 1),
|
||||
'type_key' => $parentType->key,
|
||||
'level' => ($parentType->level ?? 1) + 1,
|
||||
'parent_id' => $parentType->id,
|
||||
'value' => $item['value'],
|
||||
]);
|
||||
} else {
|
||||
$type = Keywords::create(Arr::except($item, 'list'));
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@ use Illuminate\Support\Facades\Route;
|
|||
|
||||
Route::post('auth/login', [AuthController::class, 'login']);
|
||||
|
||||
Route::group(['middleware' => 'auth:sanctum'], function () {
|
||||
Route::group([
|
||||
'middleware' => ['auth:sanctum'],
|
||||
], function () {
|
||||
|
||||
Route::post('web/upload', [WebController::class, 'upload']);
|
||||
|
||||
|
|
@ -54,6 +56,8 @@ Route::group(['middleware' => 'auth:sanctum'], function () {
|
|||
Route::apiResource('crop-flows', CropFlowController::class)->names('crops_flow');
|
||||
//设备管理
|
||||
Route::apiResource('devices', DeviceController::class)->names('device');
|
||||
Route::get('devices/{device}/worm-statics', [DeviceController::class, 'wormStatics']);
|
||||
Route::get('devices/{device}/worm-photos', [DeviceController::class, 'wormPhotos']);
|
||||
Route::put('devices-update-recommend/{device}', [DeviceController::class, 'updateRecommendStatus']);
|
||||
Route::get('devices-num', [DeviceController::class, 'typeStatusNum'])->name('device.type_status_num');
|
||||
Route::get('monitoring-data', [DeviceController::class, 'timeZoneList']);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Controllers\Callback\LinkosController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use App\Http\Middleware\ApiCustomToken;
|
||||
use App\Http\Controllers\Callback\YunFeiController;
|
||||
use App\Http\Controllers\ThirdApi\SendSmsController;
|
||||
use App\Http\Middleware\ApiCustomToken;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::post('callback/linkos', LinkosController::class);
|
||||
Route::post('callback/yunfei/insecticidal-lamp-notify', [YunFeiController::class, 'insecticidalLampNotify']);
|
||||
Route::post('callback/yunfei/worm-notify', [YunFeiController::class, 'wormNotify']);
|
||||
Route::post('callback/yunfei/worm-photo-notify', [YunFeiController::class, 'wormPhotoNotify']);
|
||||
|
||||
Route::group(['prefix'=>'third'], function(){
|
||||
Route::middleware([ApiCustomToken::class])->group(function(){
|
||||
|
|
|
|||
Loading…
Reference in New Issue