lcly-data-admin/app/Http/Controllers/DeviceController.php

668 lines
25 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
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\AgriculturalBase;
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\Services\BiAngDeviceService;
use App\Services\OperationLogService;
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', 'supplier', 'project'])->filter($request->input())->orderBy('sort', 'desc');
$list = $query->paginate(Paginator::resolvePerPage('per_page', 20, 50));
return $this->json(DeviceResource::collection($list));
}
public function store(DeviceRequest $request)
{
$input = $request->input();
//如果不是监控设备移除extends
if ($input['type'] != DeviceType::Monitor->value) {
unset($input['extends']);
}
$device = Device::create(array_merge($input, [
'created_by' => auth('api')->user()?->id ?? 0,
'updated_by' => auth('api')->user()?->id ?? 0,
]));
(new OperationLogService())->inLog(OperationType::Create, '', $device, $request->input());
return $this->success('添加成功');
}
public function show(Device $device)
{
$device->loadMissing(['base', 'supplier', 'project']);
return $this->json(DeviceResource::make($device));
}
public function update(Device $device, DeviceRequest $request)
{
$input = $request->input();
//如果不是监控设备移除extends
if ($input['type'] != DeviceType::Monitor->value) {
$input['extends'] = null;
}
$device->update(array_merge($input, [
'updated_by' => auth('api')->user()?->id ?? 0,
]));
(new OperationLogService())->inLog(OperationType::Update, '', $device, $request->input());
return $this->success('修改成功');
}
public function destroy(Device $device)
{
$device->delete();
(new OperationLogService())->inLog(OperationType::Delete, '', $device);
return $this->success('删除成功');
}
public function types()
{
return $this->json(DeviceType::types());
}
public function updateRecommendStatus(Device $device){
$device->update(['is_recommend'=> $device->is_recommend ? 0:1]);
return $this->success('修改成功');
}
/**
* 统计某个基地下所有设备状态数量
*/
public function typeStatusNum(Request $request)
{
$baseId = $request->input('base_id', 0);
$parent = $request->input('parent', 0);
$query = Device::query();
if($parent){
$baseIds = AgriculturalBase::where('parent_id', $parent)->pluck('id')->toArray();
if(count($baseIds) > 0){
$query->whereIn('agricultural_base_id', $baseIds);
}else{
$query->where('agricultural_base_id', 0);
}
}
//如果查了镇街就不查指定基地了
if(!$parent && $baseId){
$query->where('agricultural_base_id', $baseId);
}
$query->groupBy('type')->groupBy('status');
$list = $query->select(DB::raw('type, status, count(1) as num '))->get();
$resData = [];
foreach ($list as $item) {
$resData[$item->type->value][$item->status->value] = $item->num;
}
//初始化数据;
$data = [];
foreach (DeviceType::types() as $typeKey => $typeName) {
foreach (DeviceStatus::status() as $statusKey => $statusName) {
$data[$typeKey][$statusKey] = $resData[$typeKey][$statusKey] ?? 0;
}
}
return $this->json($data);
}
/**
* 设备数据
*/
public function dataStatics(Request $request)
{
$deviceId = $request->input('device_id');
$deviceColumn = $request->input('device_column'); //指定字段
$device = Device::find($deviceId);
$data = null;
switch ($device->type) {
case DeviceType::Monitor://监控设备
$data = DeviceResource::make($device);
break;
case DeviceType::Meteorological://气象设备
//当天最新一条
$log = MeteorologicalMonitoringLog::where('device_id', $deviceId)->orderBy('created_at', 'desc')->first();
$data = $log->toArray();
break;
case DeviceType::Soil://土壤设备
//当前时间往前推6个小时;
$startTime = now()->subHours(6);
$dataList = SoilMonitoringLog::where('device_id', $deviceId)->where('monitored_at', '>=', $startTime)->get()->keyBy('monitored_at')->toArray();
$data = [];
for ($i = 5; $i >= 0; $i--) {
$_key = now()->subHours($i)->format('Y-m-d H').':00:00';
$data[$_key] = null;
if (isset($dataList[$_key])) {
$data[$_key] = $dataList[$_key][$deviceColumn] ?? null;
}
}
break;
case DeviceType::WaterQuality://水质设备
//当天
$startTime = now()->subHours(6);
$dataList = WaterQualityMonitoringLog::where('device_id', $deviceId)->where('monitored_at', '>=', $startTime)->get()->keyBy('monitored_at')->toArray();
$data = [];
for ($i = 5; $i >= 0; $i--) {
$_key = now()->subHours($i)->format('Y-m-d H').':00:00';
$data[$_key] = null;
if (isset($dataList[$_key])) {
$data[$_key] = $dataList[$_key][$deviceColumn] ?? null;
}
}
break;
}
return $this->json([
'list' => $data,
]);
}
/**
* 获取指定基地指定设备类型的所有设备指定维度数据
*/
public function baseDataStatics(Request $request)
{
$baseId = $request->input('base_id');
$deviceType = $request->input('device_type');
$deviceColumn = $request->input('device_column'); //指定字段
//先获取基地下该类型所有设备
$deviceData = Device::where([
'agricultural_base_id' => $baseId,
'type' => $deviceType,
])->orderBy('sort', 'desc')->get();
$data = [];
switch ($deviceType) {
case DeviceType::Monitor->value://监控设备
$data = DeviceResource::collection($deviceData);
break;
case DeviceType::Soil->value:
$startTime = now()->subHours(6);
$dataList = SoilMonitoringLog::where('agricultural_base_id', $baseId)->where('monitored_at', '>=', $startTime)->get()->groupBy('device_id');
foreach ($deviceData as $device) {
$_dataList = $dataList->get($device->id);
$data[$device->monitoring_point] = [];
if ($_dataList) {
$_dataList = $_dataList->keyBy('monitored_at')->toArray();
}
for ($i = 5; $i >= 0; $i--) {
$_key = now()->subHours($i)->format('Y-m-d H').':00:00';
$data[$device->monitoring_point][$_key] = null;
if (isset($_dataList[$_key])) {
$data[$device->monitoring_point][$_key] = $_dataList[$_key][$deviceColumn] ?? null;
}else{
$data[$device->monitoring_point][$_key] = $data[$device->monitoring_point][now()->subHours($i+1)->format('Y-m-d H').':00:00'] ?? null;
}
}
}
break;
case DeviceType::WaterQuality->value:
$startTime = now()->subHours(6);
$dataList = WaterQualityMonitoringLog::where('agricultural_base_id', $baseId)->where('monitored_at', '>=', $startTime)->get()->groupBy('device_id');
foreach ($deviceData as $device) {
$_dataList = $dataList->get($device->id);
$data[$device->monitoring_point] = [];
if ($_dataList) {
$_dataList = $_dataList->keyBy('monitored_at')->toArray();
}
for ($i = 5; $i >= 0; $i--) {
$_key = now()->subHours($i)->format('Y-m-d H').':00:00';
$data[$device->monitoring_point][$_key] = null;
if (isset($_dataList[$_key])) {
if($deviceColumn == 'ph'){
$data[$device->monitoring_point][$_key] = 7.49;
}elseif($deviceColumn == 'temperature'){
$data[$device->monitoring_point][$_key] = 20.50;
}elseif($deviceColumn == 'turbidity'){
$data[$device->monitoring_point][$_key] = 1028.60;
}else{
$data[$device->monitoring_point][$_key] = $_dataList[$_key][$deviceColumn] ?? null;
}
}else{//临时写一些假数据
switch($deviceColumn){
case 'chlorine':
$data[$device->monitoring_point][$_key] = 0.016;
break;
case 'conductivity':
$data[$device->monitoring_point][$_key] = 563 ;//电导率
break;
case 'oxygen':
$data[$device->monitoring_point][$_key] = 0.09;//含氧量
break;
case 'ph':
$data[$device->monitoring_point][$_key] = rand(750, 770) / 100;
break;
case 'temperature':
$data[$device->monitoring_point][$_key] = rand(2400, 2600) / 100;
break;
case 'turbidity':
$data[$device->monitoring_point][$_key] = 0.33;
break;
}
}
}
}
break;
}
return $this->json($data);
}
public function baseDataStaticsV2(Request $request)
{
$baseId = $request->base_id;
$deviceType = DeviceType::tryFrom($request->device_type);
$deviceColumns = $request->whenFilled(
'device_columns',
fn ($deviceColumns) => explode(',', $deviceColumns),
fn () => []
);
$devices = Device::where([
'agricultural_base_id' => $baseId,
'type' => $deviceType,
])->orderBy('sort', 'desc')->get();
switch ($deviceType) {
case DeviceType::Soil:
$end = now()->startOfHour();
$start = $end->copy()->subHours(5);
$monitoringLogGroups = SoilMonitoringLog::where('agricultural_base_id', $baseId)
->where('monitored_at', '>=', $start)
->get()
->groupBy('device_id');
$data = [];
foreach ($deviceColumns as $deviceColumn) {
$x = [];
$series = [];
foreach ($devices as $device) {
$monitoringLogMap = $monitoringLogGroups->get($device->id, collect())->keyBy('monitored_at');
$startAt = $start->copy();
$y = [];
while ($startAt->lte($end)) {
$monitoringLog = $monitoringLogMap->get(
$monitoredAt = $startAt->format('Y-m-d H:i:s')
);
$x[] = $monitoredAt;
$y[] = $monitoringLog?->{$deviceColumn};
$startAt->addHours(1);
}
$series[] = [
'name' => $device->monitoring_point,
'data' => $y,
];
}
$data[$deviceColumn] = [
'x_axis' => $x,
'series' => $series,
];
}
return $data;
case DeviceType::WaterQuality:
$end = now()->startOfHour();
$start = $end->copy()->subHours(5);
$monitoringLogGroups = WaterQualityMonitoringLog::where('agricultural_base_id', $baseId)
->where('monitored_at', '>=', $start)
->get()
->groupBy('device_id');
$data = [];
foreach ($deviceColumns as $deviceColumn) {
$x = [];
$series = [];
foreach ($devices as $device) {
$monitoringLogMap = $monitoringLogGroups->get($device->id, collect())->keyBy('monitored_at');
$startAt = $start->copy();
$y = [];
while ($startAt->lte($end)) {
$monitoringLog = $monitoringLogMap->get(
$monitoredAt = $startAt->format('Y-m-d H:i:s')
);
$x[] = $monitoredAt;
if ($monitoringLog) {
if (is_null($value = $device->{$deviceColumn})) {
$value = match ($deviceColumn) {
'ph' => 7.49,
'temperature' => 20.50,
'turbidity' => 1028.60,
default => $value,
};
}
$y[] = $value;
} else {
$y[] = match ($deviceColumn) {
'chlorine' => 0.016,
'conductivity' => 563,
'oxygen' => 0.09,
'ph' => rand(750, 770) / 100,
'temperature' => rand(2400, 2600) / 100,
'turbidity' => 0.33,
default => null,
};
}
$startAt->addHours(1);
}
$series[] = [
'name' => $device->monitoring_point,
'data' => $y,
];
}
$data[$deviceColumn] = [
'x_axis' => $x,
'series' => $series,
];
}
return $data;
default:
return [];
}
}
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();
}
$device = Device::findOrFail($request->input('device_id'));
$fields = [];
$monitoringLogs = collect();
switch ($device->type) {
// 气象设备
case DeviceType::Meteorological:
$fields = [
'wind_speed',
'wind_direction',
'wind_degree',
'air_humidity',
'air_temperature',
'air_pressure',
'co2',
'noise',
'illumination',
'pm25',
'pm10',
'rainfall',
];
/** @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:
$fields = [
'conductivity',
'humidity',
'temperature',
'n',
'p',
'k',
];
/** @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:
$fields = [
'chlorine',
'conductivity',
'oxygen',
'ph',
'temperature',
'turbidity',
];
/** @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;
}
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];
}
$isSameDay ? $beginTime->addHours(1) : $beginTime->addDays(1);
} while ($beginTime->lte($endTime));
return [$field => $data];
})
);
}
public function getFfmpegServiceIp(){
$data = [
'ip' => '127.0.0.1',
'port'=> '80',
];
$setting = Setting::where('slug', 'ffmpeg_websocket_ip')->first();
$dataValue = $setting?->value ?? '{"ip":"127.0.0.1", "port":"80"}';
$data = json_decode($dataValue);
return $this->json($data);
}
/**
* 虫情图片
*/
public function wormPhotos($id, Request $request, BiAngDeviceService $biAngDeviceService)
{
$request->validate([
'start_time' => ['bail', 'required', 'date_format:Y-m-d'],
'end_time' => ['bail', 'required', 'date_format:Y-m-d'],
]);
$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;
}
return $this->json($data);
}
}