diff --git a/app/Admin/Controllers/DeviceController.php b/app/Admin/Controllers/DeviceController.php index bdb9745..8abac77 100644 --- a/app/Admin/Controllers/DeviceController.php +++ b/app/Admin/Controllers/DeviceController.php @@ -13,6 +13,7 @@ use App\Models\MeteorologicalReport; use App\Models\MonitorMode; use App\Models\Region; use Illuminate\Http\Request; +use App\Services\MqttService; class DeviceController extends AdminController { @@ -279,7 +280,6 @@ class DeviceController extends AdminController amisMake()->Wrapper()->sm(6)->body([ amisMake()->Panel()->title('智能开关设置') ->labelWidth(100) - ->className('Panel--success') ->body([ \amisMake()->Form()->title('')->mode('horizontal')->body([ amisMake()->FieldSetControl()->title('智能开启')->body([ @@ -325,7 +325,6 @@ class DeviceController extends AdminController ]), amisMake()->Wrapper()->sm(6)->body([ amisMake()->Panel()->title('开关记录') - ->className('Panel--success') ->body([ \amisMake()->Table()->title('') ->data([ @@ -348,23 +347,79 @@ class DeviceController extends AdminController } /** - * 喷灌 + * 喷雾 */ public function atomizingDetail(){ + $regionId = request()->input('region_id', 0); + $config = null; + $statusStr = '未知'; + if($regionId){ + $region = Region::find($regionId); + if($region){ + $monitorMode = $region->monitorModes()->where('type', MonitorMode::TYPE_ATOMIZING)->first(); + $config = $monitorMode?->pivot->config ?? null; + + if($monitorMode){ + //判断设备状态是否离线; + $device = $monitorMode->devices()->first(); + if($device->state == Device::STATE_ONLINE){ + $res = (new MqttService())->getStatus(); + if($res['error']){ + switch($res['error']){ + case 1: + $statusStr = '急停'; + break; + case 2: + $statusStr = '低水位报警'; + break; + } + }else{ + if($res['is_running']){ + if($res['yv1']){ + $statusStr = '区域A运行中,喷雾量:'.$res['speed1'].'%'; + }elseif(['yv2']){ + $statusStr = '区域B运行中,喷雾量:'.$res['speed2'].'%'; + } + }else{ + $statusStr = '未运行'; + } + } + }else{ + $statusStr = Device::stateMap()[$device->state]; + } + } + } + } + return amisMake()->Grid()->columns([ amisMake()->Wrapper()->sm(6)->body([ amisMake()->Panel()->title('智能喷灌设置') ->subFormMode('horizontal') ->labelWidth(80) - ->className('Panel--success') ->body([ - \amisMake()->Form()->title('')->mode('horizontal')->body([ - amisMake()->FieldSetControl()->title('定时喷灌')->body([ + \amisMake()->Form()->title('')->mode('horizontal') + ->api([ + 'method'=>'post', + 'url'=>admin_url('save-region-config').'/'.$regionId, + 'data'=>[ + 'is_enable' => '${is_enable}', + 'config' => '${config}' + ] + ]) + ->data($config ? json_decode($config) : []) + ->body([ + amisMake()->FieldSetControl()->title('当前状态')->body([ + amisMake()->TextControl('status', '状态')->value($statusStr), + ])->static(), + amisMake()->FieldSetControl()->className('mt-10')->title('定时喷灌')->body([ \amisMake()->SwitchControl()->name('is_enable')->label('开关'), \amisMake()->ArrayControl()->name('config')->label('定时')->items([ amisMake()->ComboControl()->items([ - \amisMake()->InputTimeRange(), - \amisMake()->TextControl()->name('input')->label('喷灌量')->labelWidth(30), + amisMake()->SelectControl('value')->options([ + 'a'=>'区域A','b'=>'区域B' + ]), + \amisMake()->InputTimeRange()->name('time_zone'), + Components::make()->decimalControl('input', '喷雾量%')->value(70)->percision(0)->step(1)->max(100) ]), ]), ]), @@ -373,7 +428,6 @@ class DeviceController extends AdminController ]), amisMake()->Wrapper()->sm(6)->body([ amisMake()->Panel()->title('开关记录') - ->className('Panel--success') ->body([ \amisMake()->Table()->title('') ->data([ @@ -394,4 +448,20 @@ class DeviceController extends AdminController ]), ]); } + + public function saveRegionConfig($id, Request $request) + { + if($id){ + $region = Region::find($id); + if($region){ + $monitorMode = $region->monitorModes()->where('type', MonitorMode::TYPE_ATOMIZING)->first(); + $config = $request->input() ?? null; + $res = $region->monitorModes()->updateExistingPivot($monitorMode->id, [ + 'config' => $config, + ]); + } + } + + return $this->autoResponse($res, __('admin.save')); + } } diff --git a/app/Admin/Controllers/MonitorModeController.php b/app/Admin/Controllers/MonitorModeController.php index dc7e648..4a16d94 100644 --- a/app/Admin/Controllers/MonitorModeController.php +++ b/app/Admin/Controllers/MonitorModeController.php @@ -168,7 +168,7 @@ class MonitorModeController extends AdminController amisMake()->PickerControl('picker_devices', '喷雾设备')->visibleOn('data.type == '.MonitorMode::TYPE_ATOMIZING) ->valueField('id') ->labelField('name') - ->multiple(true) + ->multiple(false)//喷雾设备只能选择一个 ->size('lg') ->source([ 'method' => 'get', diff --git a/app/Admin/routes.php b/app/Admin/routes.php index ceb4338..d320bf8 100644 --- a/app/Admin/routes.php +++ b/app/Admin/routes.php @@ -91,6 +91,8 @@ Route::group([ $router->post('custom-region-atomizing', '\App\Admin\Controllers\DeviceController@atomizingDetail'); + $router->post('save-region-config/{id}', '\App\Admin\Controllers\DeviceController@saveRegionConfig'); + //种植记录 $router->resource('crop-plants', \App\Admin\Controllers\CropPlantController::class)->only(['index','store', 'edit', 'update', 'destroy']); $router->resource('crop-harvestes', \App\Admin\Controllers\CropHarvestController::class)->only(['index','store', 'edit', 'update', 'destroy']); diff --git a/app/Console/Commands/DeviceLogSyncCommand.php b/app/Console/Commands/DeviceLogSyncCommand.php index 8a900ea..02bc61c 100644 --- a/app/Console/Commands/DeviceLogSyncCommand.php +++ b/app/Console/Commands/DeviceLogSyncCommand.php @@ -5,7 +5,6 @@ namespace App\Console\Commands; use App\Models\Device; use App\Services\DeviceLogService; use Illuminate\Console\Command; -use Illuminate\Support\Carbon; use Throwable; class DeviceLogSyncCommand extends Command @@ -64,7 +63,13 @@ class DeviceLogSyncCommand extends Command /** @var \Illuminate\Database\Eloquent\Collection */ $devices = Device::with(['factory'])->poweredBy($factory)->get(); + /** @var \App\Models\Device */ foreach ($devices as $device) { + // 忽略通风设备 + if ($device->isTypeAir()) { + continue; + } + $this->info('=========================================='); $this->info('设备编号: ' . $device->sn); $this->info('设备名称: ' . $device->name); diff --git a/app/Console/Commands/MqttPenwuPlan.php b/app/Console/Commands/MqttPenwuPlan.php new file mode 100644 index 0000000..886462d --- /dev/null +++ b/app/Console/Commands/MqttPenwuPlan.php @@ -0,0 +1,37 @@ +info('linkos callback parameters -------->', $request->input()); + logger()->debug('linkos callback parameters -------->', $request->input()); if ($request->filled('notify_type')) { switch ($request->notify_type) { diff --git a/app/Iot/Linkos/HttpClient.php b/app/Iot/Linkos/HttpClient.php index 1b01273..7900a4e 100644 --- a/app/Iot/Linkos/HttpClient.php +++ b/app/Iot/Linkos/HttpClient.php @@ -26,7 +26,7 @@ class HttpClient * @param int $perPage * @return array */ - public function getDeviceFlowList(string $deviceId, Carbon $start, Carbon $end, int $page = 1, int $perPage = 50): 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, @@ -38,9 +38,60 @@ class HttpClient ], ]); + 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 @@ -77,13 +128,7 @@ class HttpClient 'Signature' => $this->sign(compact('nonce', 'timestamp')), ])->baseUrl(self::ENDPOINT_URL)->send($method, $url, $options); - $result = $response->throw()->json(); - - if (data_get($result, 'success') !== true) { - throw new RuntimeException(data_get($result, 'msg', '出错啦!')); - } - - return $result; + return $response->throw()->json(); } /** diff --git a/app/Models/Device.php b/app/Models/Device.php index 0afe60b..4ed8252 100644 --- a/app/Models/Device.php +++ b/app/Models/Device.php @@ -87,4 +87,9 @@ class Device extends Model { return $this->type === static::TYPE_METEOROLOGICAL; } + + public function isTypeAir(): bool + { + return $this->type === static::TYPE_AIR; + } } diff --git a/app/Models/Region.php b/app/Models/Region.php index d236ab9..03087a5 100644 --- a/app/Models/Region.php +++ b/app/Models/Region.php @@ -45,7 +45,7 @@ class Region extends Model } public function monitorModes(){ - return $this->belongsToMany(MonitorMode::class, RegionMonitor::class, 'region_id', 'monitor_id')->withTimestamps(); + return $this->belongsToMany(MonitorMode::class, RegionMonitor::class, 'region_id', 'monitor_id')->withTimestamps()->withPivot('config'); } // 种植记录 diff --git a/app/Services/DeviceLogService.php b/app/Services/DeviceLogService.php index 478f7cd..e3c35d2 100644 --- a/app/Services/DeviceLogService.php +++ b/app/Services/DeviceLogService.php @@ -41,7 +41,7 @@ class DeviceLogService $perPage = 50; do { - $data = $httpClient->getDeviceFlowList( + $data = $httpClient->deviceFlowList( $device->sn, $start, $end, $page, $perPage ); @@ -56,74 +56,25 @@ class DeviceLogService continue; } - if ($device->isTypeSoil()) { - // 如果包含气象设备监测字段,则跳过 - if (Arr::hasAny($item['data'], [ - 'day_rainfall', - 'accumulate_rainfall', - 'potassium_content', - 'moment_rainfall', - 'pm10_concentration', - 'pm25_concentration', - 'box_noise', - 'box_carbon', - 'box_humidity', - 'box_pressure', - 'box_temperature', - 'box_illumination', - 'wind_power', - 'wind_speed', - 'wind_degree', - 'wind_direction', - ])) { - continue; - } - } elseif ($device->isTypeMeteorological()) { - // 如果包含土壤设备监测字段,则跳过 - if (Arr::hasAny($item['data'], [ - 'nitrogen_content', - 'phosphorus_content', - 'potassium_content', - 'conductivity', - 'soil_humidity', - 'soil_temperature', - ])) { - continue; - } - } - - if ( - $device->isTypeSoil() && Arr::hasAny($item['data'], [ - - ]) - ) { - - } elseif ( - $device->isTypeMeteorological() && - Arr::hasAny( - $item['data'], - [ - 'nitrogen_content', - 'phosphorus_content', - 'potassium_content', - 'conductivity', - 'soil_humidity', - 'soil_temperature', - ] - ) - ) { - continue; - } - - $isSoilMonitoring = Arr::hasAny($item['data'], [ - 'soil_humidity', - 'soil_temperature', - 'nitrogen_content', - 'potassium_content', - 'phosphorus_content', - ]); - - if (($device->isTypeSoil() && ! $isSoilMonitoring) || (! $device->isTypeSoil() && $isSoilMonitoring)) { + // 如果多合一气象监测器包含土壤监控时,需过滤掉气象监控的数据 + if ($device->isTypeSoil() && Arr::hasAny($item['data'], [ + 'current_rainfall', + 'day_rainfall', + 'accumulate_rainfall', + 'moment_rainfall', + 'pm10_concentration', + 'pm25_concentration', + 'box_illumination', + 'box_pressure', + 'box_carbon', + 'box_temperature', + 'box_humidity', + 'box_noise', + 'wind_degree', + 'wind_direction', + 'wind_power', + 'wind_speed', + ])) { continue; } @@ -497,6 +448,10 @@ class DeviceLogService switch ($key) { case 'wind_samples': $attributes['wind_degree'] = value(function (array $windSamples) { + if (empty($windSamples)) { + return null; + } + $x = 0; $y = 0; @@ -526,6 +481,10 @@ class DeviceLogService }, $item); $attributes['wind_direction'] = value(function ($windDegree) { + if (is_null($windDegree)) { + return null; + } + if ($windDegree >= 22.5 && $windDegree < 67.5) { return MeteorologicalDailyReport::WIND_DIRECTION_NORTHEAST; } elseif ($windDegree >= 67.5 && $windDegree < 112.5) { diff --git a/app/Services/MqttService.php b/app/Services/MqttService.php new file mode 100644 index 0000000..c517c6d --- /dev/null +++ b/app/Services/MqttService.php @@ -0,0 +1,59 @@ + 'application/json;charset=UTF-8', + ])->get('http://36.133.205.221:92/status'); + return $res->json(); + } + + public function mqttDo($parms) + { + $res = Http::withHeaders([ + 'Content-Type' => 'application/json;charset=UTF-8', + ])->post('http://36.133.205.221:92/mqtt', $parms); + return $res->json(); + } + + /** + * 开启哪个区域,多大量 + */ + public function open($quyu, $liang) + { + $parms = [ + 'speed1' => 0, + 'speed2' => 0, + 'yv1' => 0, + 'yv2' => 0, + ]; + switch($quyu){ + case 'a': + $parms['speed1'] = $liang; + $parms['yv1'] = 1; + break; + case 'b': + $parms['speed2'] = $liang; + $parms['yv2'] = 1; + break; + } + return $this->mqttDo($parms); + } + + public function close(){ + $parms = [ + 'speed1' => 0, + 'speed2' => 0, + 'yv1' => 0, + 'yv2' => 0, + ]; + return $this->mqttDo($parms); + } +} \ No newline at end of file