845 lines
31 KiB
PHP
845 lines
31 KiB
PHP
<?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\Http\Resources\WormPhotoResource;
|
||
use App\Iot\MoJing\HttpClient as MoJingHttpClient;
|
||
use App\Iot\Qly\HttpClient as QlyHttpClient;
|
||
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\Models\WormPhoto;
|
||
use App\Models\WormReport;
|
||
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] = 10.00;
|
||
}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(950, 1050) / 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' => 10.00,
|
||
'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(900, 1100) / 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(950, 1050) / 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 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' => ['开始时间不能大于结束时间'],
|
||
]);
|
||
}
|
||
|
||
$wormReports = WormReport::where('device_id', $id)
|
||
->whereBetween('reported_at', [$startTime->toDateString(), $endTime->toDateString()])
|
||
->pluck('worm_num', 'reported_at');
|
||
|
||
$data = [];
|
||
|
||
if ($startTime->lte($endTime)) {
|
||
do {
|
||
// 日期
|
||
$date = $startTime->toDateString();
|
||
|
||
$data[$date] = $wormReports->get($date);
|
||
|
||
$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(),
|
||
);
|
||
|
||
$wormPhotos = WormPhoto::where('device_id', $id)
|
||
->whereBetween('uploaded_at', [$startTime, $endTime])
|
||
->latest('uploaded_at')
|
||
->paginate($request->input('per_page', 20));
|
||
|
||
return WormPhotoResource::collection($wormPhotos);
|
||
}
|
||
|
||
/**
|
||
* 监控设备直播地址
|
||
*/
|
||
public function live(int $id, BiAngDeviceService $biangDeviceService)
|
||
{
|
||
$device = Device::where('type', DeviceType::Monitor)->findOrFail($id);
|
||
|
||
switch ($device->supplier_key) {
|
||
case 'device-supplier-biang':
|
||
$client = $biangDeviceService->buildHttpClient($device);
|
||
|
||
// 直播地址
|
||
$address = $client->getMonitorPalyAddress($device->sn);
|
||
|
||
return [
|
||
'type' => 'm3u8',
|
||
'address' => (string) $address,
|
||
// 有效期 60 分钟
|
||
'expires' => 3540,
|
||
];
|
||
|
||
// 中国移动千里眼
|
||
case 'device-supplier-yidong':
|
||
$client = new QlyHttpClient(
|
||
config('services.ydqly.appid'),
|
||
config('services.ydqly.secret'),
|
||
config('services.ydqly.rsa'),
|
||
);
|
||
$result = $client->post(
|
||
'/v3/open/api/websdk/player',
|
||
[
|
||
'deviceId' => $device->sn,
|
||
],
|
||
);
|
||
|
||
$address = (string) data_get($result, 'data.url');
|
||
if (config('services.ydqly.play_proxy')) {
|
||
$address = str_replace('open.andmu.cn', 'lcyd.peidikeji.cn', $address);
|
||
}
|
||
|
||
return [
|
||
'type' => 'iframe',
|
||
'address' => $address,
|
||
'expires' => data_get($result, 'data.expiresIn'),
|
||
];
|
||
|
||
// 中国电信魔镜
|
||
case 'device-supplier-dianxin':
|
||
$address = '';
|
||
|
||
if ($channelId = data_get($device->extends, 'passage')) {
|
||
$client = new MoJingHttpClient(
|
||
config('services.dxmj.app_id'),
|
||
config('services.dxmj.app_secret'),
|
||
);
|
||
$result = $client->get(
|
||
"/api/v3/channel/RealTimeVideo/{$channelId}",
|
||
[
|
||
'transType' => 4,
|
||
'natType' => 1,
|
||
'nAudioType' => 1,
|
||
],
|
||
);
|
||
$address = data_get($result, 'data.playUrl');
|
||
}
|
||
|
||
return [
|
||
'type' => 'flv',
|
||
'address' => (string) $address,
|
||
// 有效期 29 分钟
|
||
'expires' => 1740,
|
||
];
|
||
}
|
||
|
||
// 直播地址
|
||
$address = '';
|
||
|
||
if ($rtspUrl = data_get($device->extends, 'rtsp_url')) {
|
||
$value = Setting::where('slug', 'ffmpeg_websocket_ip')->value('value');
|
||
|
||
$wsConfig = $value ? json_decode($value, true) : [];
|
||
if (! is_array($wsConfig)) {
|
||
$wsConfig = [];
|
||
}
|
||
$wsConfig = array_merge([
|
||
'host' => '',
|
||
'ip' => '127.0.0.1',
|
||
'port'=> '80',
|
||
'ssl' => false,
|
||
], $wsConfig);
|
||
|
||
if ($wsConfig['ssl'] && $wsConfig['host']) {
|
||
$address = sprintf(
|
||
'wss://%s/rtsp?url=%s',
|
||
$wsConfig['host'],
|
||
base64_encode($rtspUrl),
|
||
);
|
||
} else {
|
||
$address = sprintf(
|
||
'ws://%s:%s/rtsp?url=%s',
|
||
$wsConfig['ip'],
|
||
$wsConfig['port'],
|
||
base64_encode($rtspUrl),
|
||
);
|
||
}
|
||
}
|
||
|
||
return [
|
||
'type' => 'flv',
|
||
'address' => $address,
|
||
'expires' => 0,
|
||
];
|
||
}
|
||
}
|