1
0
Fork 0
internet-everythings-agricu.../app/Services/Admin/DeviceService.php

512 lines
20 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\Services\Admin;
use App\Models\Device;
use App\Models\MonitorMode;
use App\Models\{MeteorologicalDailyReport, MeteorologicalReport, SoilDailyReport, SoilReport, AirLog, FarmWormPhoto, MonitorDevice, RegionMonitor, Region, FarmWormReport};
use App\Filters\Admin\DeviceFilter;
use Carbon\Carbon;
use App\Admin\Components;
use App\Iot\Linkos\HttpClient;
/**
* @method Device getModel()
* @method Device|\Illuminate\Database\Query\Builder query()
*/
class DeviceService extends BaseService
{
protected string $modelName = Device::class;
protected array $withRelationships = ['factory'];
protected string $modelFilterName = DeviceFilter::class;
public function list()
{
$query = $this->listQuery();
$items = (clone $query)->paginate(request()->input('perPage', 20))->items();
$base = settings()->get('rtsp_url');
if (request('_type') == Device::TYPE_MONITOR) {
$history = request('_mode') === 'history';
$date = explode(',', request('date'));
$start = data_get($date, 0);
$end = data_get($date, 1);
foreach ($items as &$item) {
$url = data_get($item->extends, $history ? 'rtsp_history' : 'rtsp_url');
$src = null;
if ($base && $url) {
// 查看历史监控 &starttime=2023_02_02_14_00_00&endtime=2023_02_02_15_00_00
if ($history && $start && $end) {
$start_format = Carbon::createFromTimestamp($start)->format('Y_m_d_H_i_s');
$end_format = Carbon::createFromTimestamp($end)->format('Y_m_d_H_i_s');
$url .= (str_contains($url, '?') ? '&' : '?') .'starttime=' . $start_format . '&endtime=' . $end_format;
}
$src = $base . base64_encode($url);
}
$item->src = $src;
}
}
$total = (clone $query)->count();
return compact('items', 'total');
}
/**
* 根据时间处理X轴横坐标
*/
public function makeChartXkeys($startTime = null, $endTime = null){
$diffDays = 0;
$day = date('Y-m-d');
$xKeys = [];
if($startTime && $endTime){
$startDay = Carbon::parse($startTime);
$endDay = Carbon::parse($endTime);
if($startDay->format('Y-m-d') == $endDay->format('Y-m-d')){//判断是否同一天
$day = $startDay->format('Y-m-d');
}else{
$diffDays = $startDay->diffInDays($endDay, false);
}
}
$xKeys = [];
if($diffDays){
for ($i = 0; $i<=$diffDays; $i++) {
$xKeys[] =(clone $startDay)->addDays($i)->startOfDay()->format('Y-m-d');
}
}else{
//调整截至到当前小时
$th = $startDay->format('H');
$eh = $endDay->format('H');;
if($day == date('Y-m-d')){
if($eh > date('H')){
$eh = date('H');
}
}
for ($i = $th; $i < ($eh+1); $i++) {
$xKeys[] = str_pad($i, 2, '0', STR_PAD_LEFT).':00';
}
}
return array($day, $diffDays, $xKeys);
}
public function getMonitorModeDeviceChartConfig($monitorMode, $startTime, $endTime, $columnNum = 3)
{
list($day, $diffDays, $xKeys) = $this->makeChartXkeys($startTime, $endTime);
switch($monitorMode->type){
case MonitorMode::TYPE_METEOROLOGICAL:
$dayliyReportQuery = MeteorologicalDailyReport::query();
$reportQuery = MeteorologicalReport::query();
$fieldNameMap = MonitorMode::fieldMap(MonitorMode::TYPE_METEOROLOGICAL);
$fieldUnitMap = MonitorMode::fieldUnitMap(MonitorMode::TYPE_METEOROLOGICAL);
break;
case MonitorMode::TYPE_SOIL:
$dayliyReportQuery = SoilDailyReport::query();
$reportQuery = SoilReport::query();
$fieldNameMap = MonitorMode::fieldMap(MonitorMode::TYPE_SOIL);
$fieldUnitMap = MonitorMode::fieldUnitMap(MonitorMode::TYPE_SOIL);
break;
case MonitorMode::TYPE_INSECT:
$dayliyReportQuery = FarmWormReport::query();
$reportQuery = FarmWormReport::query();
$fieldNameMap = MonitorMode::fieldMap(MonitorMode::TYPE_INSECT);
$fieldUnitMap = MonitorMode::fieldUnitMap(MonitorMode::TYPE_INSECT);
$columnNum = 1;
break;
}
$monitorMode->load('devices');
$configData = $fieldMap = [];
$deviceIds = [];
foreach($monitorMode->devices as $device){
if($device->type == Device::TYPE_INSECT){
$_fields = [
'worm_num'
];
$deviceIds[] = $device->id;
}else{
$_fields = explode(',', $device->pivot->fields);
}
if($diffDays) {
$modelQuery = $dayliyReportQuery->whereBetween('reported_at', [$startTime, $endTime]);
}else{
$modelQuery = $reportQuery->whereDate('reported_at', $day);
}
if($modelQuery){
$datalist = $modelQuery->where('device_id', $device->id)->get()->keyBy('reported_at')->toArray();
}
//组装数据;
foreach($_fields as $field){
$_data = [];
foreach($xKeys as $key){
if(!$diffDays) {
$key = $day.' '. $key.':00';
}else{
$key .= ' 00:00:00';
}
$_data[] = $datalist[$key][$field] ?? 0;
}
$fieldMap[$field] = [
'name' => $fieldNameMap[$field],
'unit' => $fieldUnitMap[$field],
'data' => $_data
];
}
}
$k = 0;
$chartArr = [];
foreach($fieldMap as $key => $field){
$k++;
//特殊字段,特殊统计图;
switch($key){
//点状图;
case 'wind_direction':
$yData = ['北风','东北风','东风','东南风','南风','西南风','西风','西北风'];
$_chartCard = amisMake()->Card()->body(
amisMake()->Chart()->config(
Components::make()->chartScatterConfig($field['name'], $xKeys,
[
'name'=> $field['name'],
'type' => 'scatter',
'symbolSize' => 15,
'data' => array_map(function($item)use($yData){
return $yData[$item];
},$field['data']),
'color' => '#91CC75',
], $yData
)
)
);
break;
//柱状图;
case 'worm_num':
case 'box_noise':
case 'pm10':
case 'pm25':
case 'box_co2':
$_chartCard = amisMake()->Card()->body(
amisMake()->Chart()->config(
Components::make()->chartLineBarConfig($field['name'], $xKeys, [
[
'name'=> $field['name'],
'type' => 'bar',
'data' => $field['data'],
'color' => '#91CC75',
'unit' => $field['unit']
]
])
)
);
break;
default://折线图
$_chartCard = amisMake()->Card()->body(
amisMake()->Chart()->config(
Components::make()->chartLineBarConfig($field['name'], $xKeys, [
[
'name'=> $field['name'],
'type' => 'line',
'data' => $field['data'],
'color' => '#91CC75',
'unit' => $field['unit']
]
])
)
);
break;
}
if($k%$columnNum != 0){
$_chartCard->className('m-r');
}
$chartArr[] = $_chartCard;
}
$i = 0;
$len = round(count($chartArr)/$columnNum);
for($i; $i<=$len; $i++){
$configData[] = \amisMake()->grid()->columns([
amisMake()->Flex()->items(array_splice($chartArr, 0, $columnNum)),
]);
}
if($monitorMode->type == MonitorMode::TYPE_INSECT){
$configData[] = \amisMake()->grid()->columns([//添加照片
amisMake()->Images()->thumbRatio('16:9')->thumbMode(true)->enlargeAble(true)
->options(FarmWormPhoto::whereIn('device_id', $deviceIds)->whereBetween('uploaded_at', [$startTime, $endTime])->pluck('url')->toArray())
]);
}
return $configData;
}
public function getMonitorModeDeviceData($monitorMode, $startTime, $endTime)
{
list($day, $diffDays, $xKeys) = $this->makeChartXkeys($startTime, $endTime);
switch($monitorMode->type){
case MonitorMode::TYPE_METEOROLOGICAL:
$dayliyReportQuery = MeteorologicalDailyReport::query();
$reportQuery = MeteorologicalReport::query();
$fieldNameMap = MonitorMode::fieldMap(MonitorMode::TYPE_METEOROLOGICAL);
$fieldUnitMap = MonitorMode::fieldUnitMap(MonitorMode::TYPE_METEOROLOGICAL);
break;
case MonitorMode::TYPE_SOIL:
$dayliyReportQuery = SoilDailyReport::query();
$reportQuery = SoilReport::query();
$fieldNameMap = MonitorMode::fieldMap(MonitorMode::TYPE_SOIL);
$fieldUnitMap = MonitorMode::fieldUnitMap(MonitorMode::TYPE_SOIL);
break;
case MonitorMode::TYPE_INSECT:
$dayliyReportQuery = FarmWormReport::query();
$reportQuery = FarmWormReport::query();
$fieldNameMap = MonitorMode::fieldMap(MonitorMode::TYPE_INSECT);
$fieldUnitMap = MonitorMode::fieldUnitMap(MonitorMode::TYPE_INSECT);
break;
}
$monitorMode->load('devices');
$fieldMap = [];
foreach($monitorMode->devices as $device){
if($device->type == Device::TYPE_INSECT){
$_fields = [
'worm_num'
];
$deviceIds[] = $device->id;
}else{
$_fields = explode(',', $device->pivot->fields);
}
if($diffDays) {
$modelQuery = $dayliyReportQuery->whereBetween('reported_at', [$startTime, $endTime]);
}else{
$modelQuery = $reportQuery->whereDate('reported_at', $day);
}
if($modelQuery){
$datalist = $modelQuery->where('device_id', $device->id)->get()->keyBy('reported_at')->toArray();
}
//组装数据;
foreach($_fields as $field){
$_data = [];
foreach($xKeys as $key){
if(!$diffDays) {
$key = date('Y-m-d').' '. $key.':00';
}else{
$key .= ' 00:00:00';
}
$_data[] = $datalist[$key][$field] ?? 0;
}
$fieldMap[$field] = [
'name' => $fieldNameMap[$field],
'unit' => $fieldUnitMap[$field],
'data' => $_data,
'xkeys'=> $xKeys
];
}
}
return $fieldMap;
}
public function meteorologicalControAir(Device $device, MeteorologicalReport $log, Carbon $reportedAt)
{
//如果温度或者湿度发生变化;
if($log->wasChanged('box_temperature') || $log->wasChanged('box_humidity')){
//获取当前设备关联监测点IDs
$monitorIds = MonitorDevice::where('device_id', $device->id)->pluck('monitor_id')->toArray();
$regionIds = RegionMonitor::where('monitor_id', $monitorIds)->pluck('region_id')->toArray();
$regions = Region::whereIn('id', $regionIds)->whereHas('monitorModes', function($q){
return $q->where('type', MonitorMode::TYPE_AIR);
})->get();
//遍历地点,是否有通风设备;
foreach($regions as $region){
$monitorModes = $region->monitorModes()->where('type', MonitorMode::TYPE_AIR)->get();
foreach($monitorModes as $monitor){
$_device = $monitor->devices()->where('type', Device::TYPE_AIR)->first();
$config = $_device?->extends ?? [];
$fieldNameMap = MonitorMode::fieldMap(Device::TYPE_METEOROLOGICAL);
$fieldUnitMap = MonitorMode::fieldUnitMap(Device::TYPE_METEOROLOGICAL);
$airState = $this->getAirStatus($_device);
//开启了自动开启配置
if($config && $config['open_is_enable'] ){
$rule = $config['open_config'];
$res = $this->verifyRule($rule, $log);
if($res['status'] && !$airState){//如果判定成功,且设备当前是关闭状态
$msg = '';
$column = $res['keys'][0];
if(strpos($column, ',')){//看是否是并联条件
$_columns = explode(',',$column);
foreach($_columns as $cc){
$msg.= $fieldNameMap[$cc].'达到'.$log->$cc.$fieldUnitMap[$cc].'值,且';
}
$msg = mb_substr($msg, 0, -2);
}else{
$msg = $fieldNameMap[$column].'达到'.$log->$column.$fieldUnitMap[$column].'值';
}
$this->openAir($_device, $msg);
}
}
if($config && $config['close_is_enable'] ){
$rule = $config['close_config'];
$res = $this->verifyRule($rule, $log);
if($res['status'] && $airState){//如果判定成功,且设备当前是开启状态
$msg = '';
$column = $res['keys'][0];
if(strpos($column, ',')){//看是否是并联条件
$_columns = explode(',',$column);
foreach($_columns as $cc){
$msg.= $fieldNameMap[$cc].'达到'.$log->$cc.$fieldUnitMap[$cc].'值,且';
}
$msg = mb_substr($msg, 0, -2);
}else{
$msg = $fieldNameMap[$column].'达到'.$log->$column.$fieldUnitMap[$column].'值';
}
$this->closeAir($_device, $msg);
}
}
}
}
}
}
public function verifyRule($rule, $log)
{
$res = [
'status' => false,
'keys' => []
];
if(isset($rule['conjunction'])){//多条件
switch($rule['conjunction']){
case 'or':
if(isset($rule['children'])){
foreach($rule['children'] as $child){
$cRes = $this->verifyRule($child, $log);
if($cRes && $cRes['status']){
$res['status'] = true;
$res['keys'] = array_merge($res['keys'], $cRes['keys']);
return $res;
}
}
}
break;
case 'and':
if(isset($rule['children'])){
$_keys = [];
foreach($rule['children'] as $child){
$cRes = $this->verifyRule($child, $log);
if($cRes && $cRes['status']){
$_keys = array_merge($_keys, $cRes['keys']);
continue;
}else{
$res['status'] = false;
return $res;
}
}
$res['status'] = true;
$res['keys'][] = implode(',', $_keys);
}
break;
}
}else{//单条件
$key = $rule['left']['field'] ?? '';
$value = $log->$key ?? null;
switch($rule['op']){
case 'less':
break;
if($value < $rule['right']){
$res['status'] = true;
$res['keys'][] = $key;
}
case 'less_or_equal':
if($value <= $rule['right']){
$res['status'] = true;
$res['keys'][] = $key;
}
break;
case 'greater':
if($value > $rule['right']){
$res['status'] = true;
$res['keys'][] = $key;
}
break;
case 'greater_or_equal':
if($value >= $rule['right']){
$res['status'] = true;
$res['keys'][] = $key;
}
break;
}
}
return $res;
}
/**
* 获取通风设备当前状态
*/
public function getAirStatus(Device $device)
{
if($device->type != Device::TYPE_AIR){
return false;
}
$res = app(HttpClient::class)->getDeviceStatus($device->sn, ["switch_state"]);
if($res && isset($res[0])){
if($res[0]['value'] == 1){
return true;
}
}
return false;
}
/**
* 开启通风设备
*/
public function openAir(Device $device, string $msg)
{
if($device->type != Device::TYPE_AIR){
return ;
}
//记录触发日志
AirLog::create([
'device_id' => $device->id,
'type' => 1,
'content' => $msg,
]);
//编号,-todo
// $httpClient = app(HttpClient::class);
// dd($httpClient->deviceDataDownlink($device->sn, 'switch_control', ['switch_state' => 1]));
return ;
}
/**
* 关闭通风设备
*/
public function closeAir(Device $device, string $msg)
{
if($device->type != Device::TYPE_AIR){
return ;
}
AirLog::create([
'device_id' => $device->id,
'type' => 2,
'content' => $msg,
]);
//编号,
// $httpClient = app(HttpClient::class);
// dd($httpClient->deviceDataDownlink($device->sn, 'switch_control', ['switch_state' => 0]));
return ;
}
}