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); if ($data['status'] === 'online') { InsecticidalLampReport::updateOrCreate([ 'device_id' => $device->id, 'reported_at' => $now->copy()->startOfHour(), ], [ 'agricultural_base_id' => $device->agricultural_base_id, 'battery_vol' => $data['vol'], 'killed_num' => $data['dct'], 'air_temperature' => $data['temp'], 'air_humidity' => $data['humidity'], 'solar_panel_vol' => $data['sunVol'], 'high_vol' => $data['highVol'], ]); } $device->update([ 'status' => $data['status'] === 'online' ? DeviceStatus::Online : DeviceStatus::Offline, ]); break; case DeviceType::Worm: case DeviceType::InsectSexLure: $data = $httpClient->getWormPhotos($device->sn, $now->copy()->subHours(24), $now); $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'] ?? ''); } }