osamabyc86 commited on
Commit
9463778
·
verified ·
1 Parent(s): 7ca8135

Upload peer_discovery.py

Browse files
Files changed (1) hide show
  1. peer_discovery.py +1341 -68
peer_discovery.py CHANGED
@@ -1,96 +1,1369 @@
1
  #!/usr/bin/env python3
 
 
 
 
 
2
  import os
3
  import socket
4
  import threading
5
  import time
6
  import logging
7
  import requests
8
- from zeroconf import Zeroconf, ServiceInfo, ServiceBrowser
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  # إعداد السجلات
11
- logging.basicConfig(level=logging.INFO)
 
 
 
12
  logger = logging.getLogger(__name__)
13
 
14
- # منفذ الخدمة (بدءاً من 1000 مع زيادة متسلسلة)
15
- current_port = 1000
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  def get_sequential_port():
18
- global current_port
19
- port = current_port
20
- current_port += 1
21
- if current_port > 9999:
22
- current_port = 1000
23
- return port
24
-
25
- PORT = "7520" and int(os.getenv("CPU_PORT", get_sequential_port()))
26
- SERVICE = "_tasknode._tcp.local."
27
- PEERS = set()
28
- PEERS_INFO = {}
29
-
30
- CENTRAL_REGISTRY_SERVERS = [
31
- "https://cv4790811.regru.cloud",
32
- "https://amaloffload.onrender.com",
33
- "https://osamabyc86-offload.hf.space",
34
- "https://huggingface.co/spaces/osamabyc19866/omsd",
35
- "https://huggingface.co/spaces/osamabyc86/offload",
36
- "https://176.28.159.79"
37
- ]
38
-
39
- def get_local_ip():
40
- try:
41
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
42
- s.connect(("8.8.8.8", 80))
43
- ip = s.getsockname()[0]
44
- s.close()
45
- return ip
46
- except Exception:
47
- return "127.0.0.1"
48
 
49
  def register_peer(ip, port):
50
- peer_url = f"http://{ip}:{port}/run"
51
- if peer_url not in PEERS:
52
- PEERS.add(peer_url)
53
- logger.info(f"تم تسجيل قرين جديد: {peer_url}")
 
 
 
54
 
55
  def discover_lan_peers():
56
- class Listener:
57
- def add_service(self, zc, type_, name):
58
- info = zc.get_service_info(type_, name)
59
- if info:
60
- ip = socket.inet_ntoa(info.addresses[0])
61
- register_peer(ip, info.port)
62
-
63
- zeroconf = Zeroconf()
64
- ServiceBrowser(zeroconf, SERVICE, Listener())
65
- return zeroconf
66
 
67
  def main():
68
- logger.info("🚀 بدء نظام اكتشاف الأقران...")
69
-
70
- # تسجيل الخدمة المحلية
71
- zeroconf = Zeroconf()
72
- info = ServiceInfo(
73
- type_=SERVICE,
74
- name=f"{socket.gethostname()}.{SERVICE}",
75
- addresses=[socket.inet_aton(get_local_ip())],
76
- port=int(PORT),
77
- properties={b'version': b'1.0'},
78
- server=f"{socket.gethostname()}.local."
79
- )
80
- zeroconf.register_service(info)
81
-
82
- # بدء اكتشاف الأقران
83
- discover_lan_peers()
84
-
85
  try:
 
 
 
 
 
86
  while True:
87
- logger.info(f"عدد الأقران المكتشفين: {len(PEERS)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  time.sleep(10)
 
89
  except KeyboardInterrupt:
90
  logger.info("🛑 إيقاف النظام...")
91
  finally:
92
- zeroconf.unregister_service(info)
93
- zeroconf.close()
94
 
95
  if __name__ == "__main__":
96
  main()
 
1
  #!/usr/bin/env python3
2
+ """
3
+ نظام اكتشاف أقران محسن - الإصدار 2.3
4
+ اكتشاف متكامل مع أولوية المنافذ وتحسينات الأداء
5
+ """
6
+
7
  import os
8
  import socket
9
  import threading
10
  import time
11
  import logging
12
  import requests
13
+ import json
14
+ import subprocess
15
+ import platform
16
+ import re
17
+ from urllib.parse import urljoin
18
+ from zeroconf import Zeroconf, ServiceInfo, ServiceBrowser, ServiceListener
19
+ from concurrent.futures import ThreadPoolExecutor, as_completed
20
+ import ipaddress
21
+ from dataclasses import dataclass
22
+ from typing import Set, Dict, List, Optional, Tuple
23
+ from datetime import datetime, timedelta
24
+ from port_manager import port_manager
25
+
26
+ # استيراد مدير المنافذ
27
+ try:
28
+ from port_manager import PortManager
29
+ port_manager = PortManager()
30
+ DISCOVERY_PORT = port_manager.get_available_port()
31
+ except:
32
+ DISCOVERY_PORT = 7520
33
+ PORT = DISCOVERY_PORT
34
 
35
  # إعداد السجلات
36
+ logging.basicConfig(
37
+ level=logging.INFO,
38
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
39
+ )
40
  logger = logging.getLogger(__name__)
41
 
42
+ # إعدادات النظام
43
+ SERVICE_TYPE = "_http._tcp.local."
44
+ DISCOVERY_PORT = int(os.getenv("OFFLOAD_PORT", "7520"))
45
+ DISCOVERY_INTERVAL = 30
46
+ HEALTH_CHECK_INTERVAL = 60
47
+ NETWORK_SCAN_INTERVAL = 120 # كل دقيقتين
48
+
49
+ # إعدادات أولوية المنافذ
50
+ PRIORITY_PORT = 72500 # المنفذ ذو الأولوية القصوى
51
+ FALLBACK_PORT = 7520 # المنفذ الاحتياطي
52
+
53
+ @dataclass
54
+ class PeerInfo:
55
+ """معلومات شاملة عن الجهاز الشريك"""
56
+ url: str
57
+ ip: str
58
+ port: int
59
+ hostname: str
60
+ network_type: str # lan, wan, internet
61
+ last_seen: datetime
62
+ last_health_check: datetime
63
+ is_active: bool = True
64
+ response_time: float = 0.0
65
+ cpu_usage: float = 0.0
66
+ memory_available: float = 0.0
67
+ capabilities: List[str] = None
68
+ discovery_method: str = "unknown" # إضافة طريقة الاكتشاف
69
+
70
+ def __post_init__(self):
71
+ if self.capabilities is None:
72
+ self.capabilities = []
73
+
74
+ @dataclass
75
+ class ResourceInfo:
76
+ """معلومات عن الموارد المساعدة"""
77
+ device_id: str
78
+ name: str
79
+ type: str # storage, camera, sensor
80
+ connection_type: str # usb, network, bluetooth
81
+ capabilities: List[str]
82
+ status: str
83
+ paired_at: datetime
84
+ details: Dict = None
85
+
86
+ def __post_init__(self):
87
+ if self.details is None:
88
+ self.details = {}
89
+
90
+ class NetworkScanner:
91
+ """مسح الشبكة المتقدم"""
92
+
93
+ def __init__(self):
94
+ self.scan_results: Dict[str, dict] = {}
95
+
96
+ def ping_host(self, ip: str, timeout: int = 2) -> bool:
97
+ """فحص إذا كان الجهاز متجاوب"""
98
+ try:
99
+ if platform.system().lower() == "windows":
100
+ cmd = f"ping -n 1 -w {timeout * 1000} {ip}"
101
+ else:
102
+ cmd = f"ping -c 1 -W {timeout} {ip}"
103
+
104
+ result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
105
+ return result.returncode == 0
106
+ except Exception:
107
+ return False
108
+
109
+ def check_port(self, ip: str, port: int, timeout: int = 3) -> bool:
110
+ """فحص إذا كان المنفذ مفتوحاً"""
111
+ try:
112
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
113
+ sock.settimeout(timeout)
114
+ result = sock.connect_ex((ip, port))
115
+ return result == 0
116
+ except Exception:
117
+ return False
118
+
119
+ def scan_ports_prioritized(self, ip: str) -> List[int]:
120
+ """مسح منافذ مع أولوية 72500 أولاً"""
121
+ open_ports = []
122
+
123
+ # أولاً: فحص المنفذ 72500
124
+ logger.info(f"🎯 فحص المنفذ المفضل {PRIORITY_PORT} على {ip}")
125
+ if self.check_port(ip, PRIORITY_PORT, timeout=3):
126
+ open_ports.append(PRIORITY_PORT)
127
+ logger.info(f"✅ وجد خدمة على المنفذ المفضل {PRIORITY_PORT}")
128
+ return open_ports
129
+
130
+ # ثانياً: فحص منافذ مدير المنافذ (إذا كان معروفاً)
131
+ try:
132
+ from port_manager import PortManager
133
+ port_manager = PortManager()
134
+ dynamic_port = port_manager.get_available_port()
135
+ if dynamic_port and self.check_port(ip, dynamic_port, timeout=2):
136
+ open_ports.append(dynamic_port)
137
+ logger.info(f"📍 وجد خدمة على منفذ المدير {dynamic_port}")
138
+ return open_ports
139
+ except:
140
+ pass
141
+
142
+ # ثالثاً: فحص المنافذ الثانوية
143
+ secondary_ports = [7520, 9630, 5297, 80, 443, 8000, 8080]
144
+ logger.info(f"🔍 مسح المنافذ الثانوية على {ip}")
145
+
146
+ with ThreadPoolExecutor(max_workers=10) as executor:
147
+ futures = {
148
+ executor.submit(self.check_port, ip, port, 2): port
149
+ for port in secondary_ports
150
+ }
151
+ for future in as_completed(futures):
152
+ port = futures[future]
153
+ if future.result():
154
+ open_ports.append(port)
155
+ logger.info(f"📍 وجد خدمة على المنفذ {port}")
156
+
157
+ return open_ports
158
+
159
+ def scan_ports(self, ip: str, ports: List[int]) -> List[int]:
160
+ """مسح منافذ متعددة على جهاز"""
161
+ open_ports = []
162
+ with ThreadPoolExecutor(max_workers=20) as executor:
163
+ futures = {executor.submit(self.check_port, ip, port): port for port in ports}
164
+ for future in as_completed(futures):
165
+ port = futures[future]
166
+ if future.result():
167
+ open_ports.append(port)
168
+ return open_ports
169
+
170
+ def get_hostname(self, ip: str) -> str:
171
+ """الحصول على اسم المضيف"""
172
+ try:
173
+ return socket.gethostbyaddr(ip)[0]
174
+ except (socket.herror, socket.gaierror):
175
+ return "unknown"
176
+
177
+ def scan_subnet_prioritized(self, subnet_base: str) -> List[dict]:
178
+ """مسح شبكة فرعية مع أولوية المنفذ المفضل"""
179
+ discovered_hosts = []
180
+
181
+ def scan_single_host_prioritized(i):
182
+ ip = f"{subnet_base}.{i}"
183
+ if self.ping_host(ip):
184
+ # استخدام المسح المُفضل بدلاً من العادي
185
+ open_ports = self.scan_ports_prioritized(ip)
186
+ if open_ports:
187
+ return {
188
+ 'ip': ip,
189
+ 'open_ports': open_ports,
190
+ 'hostname': self.get_hostname(ip),
191
+ 'discovery_method': 'network_scan_prioritized',
192
+ 'priority_port_found': PRIORITY_PORT in open_ports,
193
+ 'last_seen': datetime.now()
194
+ }
195
+ return None
196
+
197
+ with ThreadPoolExecutor(max_workers=50) as executor:
198
+ futures = [executor.submit(scan_single_host_prioritized, i) for i in range(1, 255)]
199
+ for future in as_completed(futures):
200
+ result = future.result()
201
+ if result:
202
+ discovered_hosts.append(result)
203
+ if result['priority_port_found']:
204
+ logger.info(f"🎯 جهاز بالمنفذ المفضل: {result['ip']}")
205
+
206
+ return discovered_hosts
207
+
208
+ def scan_subnet(self, subnet_base: str) -> List[dict]:
209
+ """مسح شبكة فرعية كاملة"""
210
+ discovered_hosts = []
211
+ target_ports = [7520, 9630, 5297, 80, 443, 8000, 8080] # منافذ الخدمة المحتملة
212
+
213
+ def scan_single_host(i):
214
+ ip = f"{subnet_base}.{i}"
215
+ if self.ping_host(ip):
216
+ open_ports = self.scan_ports(ip, target_ports)
217
+ if open_ports:
218
+ return {
219
+ 'ip': ip,
220
+ 'open_ports': open_ports,
221
+ 'hostname': self.get_hostname(ip),
222
+ 'discovery_method': 'network_scan',
223
+ 'last_seen': datetime.now()
224
+ }
225
+ return None
226
+
227
+ with ThreadPoolExecutor(max_workers=50) as executor:
228
+ futures = [executor.submit(scan_single_host, i) for i in range(1, 255)]
229
+ for future in as_completed(futures):
230
+ result = future.result()
231
+ if result:
232
+ discovered_hosts.append(result)
233
+
234
+ return discovered_hosts
235
+
236
+ class HardwareDiscoverer:
237
+ """مكتشف الأجهزة والموارد المساعدة"""
238
+
239
+ def __init__(self):
240
+ self.supported_types = ['storage', 'network_camera']
241
+
242
+ def discover_storage_devices(self) -> List[dict]:
243
+ """اكتشاف أجهزة التخزين المتصلة"""
244
+ storage_devices = []
245
+
246
+ try:
247
+ if platform.system() == "Windows":
248
+ # استخدام wmic في Windows
249
+ cmd = "wmic logicaldisk where drivetype=2 get deviceid,size,freespace,description /format:csv"
250
+ result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
251
+ storage_devices = self.parse_windows_storage(result.stdout)
252
+
253
+ elif platform.system() == "Linux":
254
+ # استخدام lsblk في Linux
255
+ cmd = "lsblk -J -o NAME,SIZE,TYPE,MOUNTPOINT"
256
+ result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
257
+ storage_devices = self.parse_linux_storage(result.stdout)
258
+
259
+ elif platform.system() == "Darwin":
260
+ # استخدام diskutil في macOS
261
+ cmd = "diskutil list -plist"
262
+ result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
263
+ storage_devices = self.parse_macos_storage(result.stdout)
264
+
265
+ except Exception as e:
266
+ logger.error(f"خطأ في اكتشاف أجهزة التخزين: {e}")
267
+
268
+ return storage_devices
269
+
270
+ def parse_windows_storage(self, output: str) -> List[dict]:
271
+ """تحليل مخرجات أجهزة التخزين في Windows"""
272
+ devices = []
273
+ lines = output.strip().split('\n')[1:] # تخطي العنوان
274
+
275
+ for line in lines:
276
+ if line.strip():
277
+ parts = line.split(',')
278
+ if len(parts) >= 4:
279
+ devices.append({
280
+ 'device_id': f"storage_{parts[1]}",
281
+ 'name': parts[4] if len(parts) > 4 else 'USB Storage',
282
+ 'type': 'storage',
283
+ 'connection_type': 'usb',
284
+ 'capacity': parts[2] if len(parts) > 2 else 'unknown',
285
+ 'free_space': parts[3] if len(parts) > 3 else 'unknown'
286
+ })
287
+ return devices
288
+
289
+ def parse_linux_storage(self, output: str) -> List[dict]:
290
+ """تحليل مخرجات أجهزة التخزين في Linux"""
291
+ try:
292
+ data = json.loads(output)
293
+ devices = []
294
+
295
+ for device in data.get('blockdevices', []):
296
+ if device.get('type') == 'disk' and device.get('name', '').startswith('sd'):
297
+ devices.append({
298
+ 'device_id': f"storage_{device['name']}",
299
+ 'name': device.get('name', 'Storage Device'),
300
+ 'type': 'storage',
301
+ 'connection_type': 'usb',
302
+ 'size': device.get('size', 'unknown'),
303
+ 'mountpoint': device.get('mountpoint', '')
304
+ })
305
+ return devices
306
+ except json.JSONDecodeError:
307
+ return []
308
+
309
+ def parse_macos_storage(self, output: str) -> List[dict]:
310
+ """تحليل مخرجات أجهزة التخزين في macOS"""
311
+ devices = []
312
+ # تحليل بسيط لمخرجات diskutil
313
+ lines = output.split('\n')
314
+ current_device = {}
315
+
316
+ for line in lines:
317
+ if 'Device Identifier' in line:
318
+ if current_device:
319
+ devices.append(current_device)
320
+ current_device = {'type': 'storage', 'connection_type': 'usb'}
321
+
322
+ if 'Device Identifier' in line:
323
+ current_device['device_id'] = f"storage_{line.split()[-1]}"
324
+ elif 'Volume Name' in line:
325
+ current_device['name'] = line.split('Volume Name:')[-1].strip()
326
+ elif 'Size' in line and 'Disk Size' not in line:
327
+ current_device['size'] = line.split('Size:')[-1].strip()
328
+
329
+ if current_device:
330
+ devices.append(current_device)
331
+
332
+ return devices
333
+
334
+ def discover_network_cameras(self) -> List[dict]:
335
+ """اكتشاف كاميرات الشبكة"""
336
+ cameras = []
337
+
338
+ try:
339
+ # مسح الشبكة للعثور على كاميرات
340
+ local_ip = self.get_local_ip()
341
+ network_base = ".".join(local_ip.split(".")[:3])
342
+
343
+ common_camera_ports = [80, 81, 82, 83, 84, 85, 86, 87, 88, 443, 554, 1935, 8000, 8080, 8081]
344
+
345
+ for i in range(1, 50): # مسح نطاق محدود للأداء
346
+ ip = f"{network_base}.{i}"
347
+ open_ports = []
348
+
349
+ for port in common_camera_ports:
350
+ if self.check_port(ip, port, timeout=1):
351
+ open_ports.append(port)
352
+
353
+ if open_ports:
354
+ cameras.append({
355
+ 'device_id': f"camera_{ip}",
356
+ 'name': f'Network Camera {ip}',
357
+ 'type': 'camera',
358
+ 'connection_type': 'network',
359
+ 'ip': ip,
360
+ 'open_ports': open_ports,
361
+ 'stream_urls': [f"http://{ip}:{port}" for port in open_ports]
362
+ })
363
+
364
+ except Exception as e:
365
+ logger.error(f"خطأ في اكتشاف كاميرات الشبكة: {e}")
366
+
367
+ return cameras
368
+
369
+ def get_local_ip(self) -> str:
370
+ """الحصول على IP المحلي"""
371
+ try:
372
+ with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
373
+ s.connect(("8.8.8.8", 80))
374
+ return s.getsockname()[0]
375
+ except Exception:
376
+ return "127.0.0.1"
377
+
378
+ def check_port(self, ip: str, port: int, timeout: int = 2) -> bool:
379
+ """فحص المنفذ"""
380
+ try:
381
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
382
+ sock.settimeout(timeout)
383
+ result = sock.connect_ex((ip, port))
384
+ return result == 0
385
+ except Exception:
386
+ return False
387
+
388
+ class EnhancedPeerDiscovery:
389
+ """نظام اكتشاف أقران محسن مع أولوية المنافذ"""
390
+
391
+ def __init__(self):
392
+ self.peers: Dict[str, PeerInfo] = {}
393
+ self.resources: Dict[str, ResourceInfo] = {} # الموارد المساعدة
394
+ self.zeroconf: Optional[Zeroconf] = None
395
+ self.service_info: Optional[ServiceInfo] = None
396
+ self._lock = threading.RLock()
397
+ self._running = False
398
+ self._discovery_thread: Optional[threading.Thread] = None
399
+ self._health_check_thread: Optional[threading.Thread] = None
400
+ self._network_scan_thread: Optional[threading.Thread] = None
401
+ self._resource_discovery_thread: Optional[threading.Thread] = None
402
+
403
+ # إعدادات أولوية المنافذ
404
+ self.current_discovery_port = None
405
+ self.port_priority_index = 0
406
+ self.port_strategy = "priority"
407
+
408
+ # أدوات الاكتشاف
409
+ self.network_scanner = NetworkScanner()
410
+ self.hardware_discoverer = HardwareDiscoverer()
411
+
412
+ # سيرفرات التسجيل المركزية - محدثة ومختبرة
413
+ self.central_servers = [
414
+ "http://localhost:", # السيرفر المحلي للاختبار
415
+ "http://127.0.0.1:", # localhost بديل
416
+ "https://offloadhelper.onrender.com",
417
+ "http://cv5303201.regru.cloud",
418
+ "https://amaloffload.onrender.com",
419
+ "https://huggingface.co/spaces/mrwabnalas40/Ranoosh",
420
+ "https://huggingface.co/spaces/mrwabnalas40/Tafreegh",
421
+ "https://huggingface.co/spaces/mrwabnalas40/Offloadv3",
422
+ "https://mrwabnalas40-gameplayergamer.hf.space",
423
+ "https://mrwabnalas40-because.hf.space",
424
+ "https://huggingface.co/spaces/mrwabnalas40/Tafreegh",
425
+ "https://geregesdodi-offloadv2.hf.space",
426
+ "https://geregesdodi-offload.hf.space"
427
+ ]
428
+
429
+ # سيرفرات الاكتشاف الاحتياطية
430
+ self.backup_discovery_servers = [
431
+ "https://discovery.offload-network.com",
432
+ "https://peer-registry.fly.dev",
433
+ ]
434
+
435
+ # إحصائيات الاكتشاف
436
+ self.discovery_stats = {
437
+ 'central_servers_working': 0,
438
+ 'central_servers_failed': 0,
439
+ 'last_central_discovery': None,
440
+ 'peers_found': 0
441
+ }
442
+
443
+ # سيرفرات التسجيل المركزية الصالحة فقط
444
+ self.central_servers = [
445
+ "https://offloadhelper.onrender.com",
446
+ "http://cv5303201.regru.cloud",
447
+ "https://amaloffload.onrender.com",
448
+ "https://osamabyc86-offload.hf.space",
449
+ "https://huggingface.co/spaces/osamabyc19866/omsd",
450
+ "https://huggingface.co/spaces/osamabyc86/offload",
451
+ "https://176.28.159.79",
452
+ "https://167.28.156.149",
453
+ "https://youtu.be/yj9x-2IbQ-Y?si=suBc9zTTAoLQm82r",
454
+ "https://huggingface.co/spaces/mrwabnalas40/Ranoosh/discussions:7860",
455
+ "https://omsdmail.gumroad.com/l/amaloffloadhelper",
456
+ "https://mrwabnalas40-Offloadv3.hf.space:7861"
457
+ ]
458
+
459
+ def get_discovery_port_prioritized(self) -> int:
460
+ """الحصول على منفذ الاكتشاف مع الأولوية"""
461
+ strategies = [
462
+ self._get_priority_port, # 72500 أولاً
463
+ self._get_dynamic_port, # مدير المنافذ ثانياً
464
+ self._get_fallback_port # 7520 أخيراً
465
+ ]
466
+
467
+ for i, strategy in enumerate(strategies):
468
+ try:
469
+ port = strategy()
470
+ if port:
471
+ self.port_priority_index = i
472
+ strategy_name = ["72500", "dynamic", "7520"][i]
473
+ logger.info(f"🎯 استخدام منفذ الاكتشاف: {port} (استراتيجية: {strategy_name})")
474
+ return port
475
+ except Exception as e:
476
+ logger.warning(f"⚠️ فشل استراتيجية المنفذ {i}: {e}")
477
+ continue
478
+
479
+ # إذا فشلت جميع الاستراتيجيات
480
+ logger.error("💥 فشلت جميع استراتيجيات المنفذ، استخدام 7520 افتراضي")
481
+ return FALLBACK_PORT
482
+
483
+ def _get_priority_port(self) -> int:
484
+ """المنفذ ذو الأولوية القصوى 72500"""
485
+ port = PRIORITY_PORT
486
+ if self.is_port_available(port):
487
+ logger.info(f"✅ المنفذ {PRIORITY_PORT} متاح للاستخدام")
488
+ return port
489
+ else:
490
+ raise Exception(f"المنفذ {PRIORITY_PORT} غير متاح")
491
+
492
+ def _get_dynamic_port(self) -> int:
493
+ """الحصول على منفذ ديناميكي من المدير"""
494
+ try:
495
+ from port_manager import PortManager
496
+ port_manager = PortManager()
497
+ dynamic_port = port_manager.get_available_port()
498
+ if dynamic_port and self.is_port_available(dynamic_port):
499
+ logger.info(f"✅ حصل على منفذ ديناميكي: {dynamic_port}")
500
+ return dynamic_port
501
+ else:
502
+ raise Exception("فشل في الحصول على منفذ ديناميكي")
503
+ except Exception as e:
504
+ raise Exception(f"مدير المنافذ غير متاح: {e}")
505
+
506
+ def _get_fallback_port(self) -> int:
507
+ """المنفذ الاحتياطي 7520"""
508
+ port = FALLBACK_PORT
509
+ if self.is_port_available(port):
510
+ logger.info(f"🔄 استخدام المنفذ الاحتياطي {FALLBACK_PORT}")
511
+ return port
512
+ else:
513
+ raise Exception(f"المنفذ {FALLBACK_PORT} غير متاح")
514
+
515
+ def is_port_available(self, port: int) -> bool:
516
+ """فحص إذا كان المنفذ متاح للاستخدام محلياً"""
517
+ try:
518
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
519
+ s.bind(('0.0.0.0', port))
520
+ return True
521
+ except socket.error:
522
+ return False
523
+
524
+ def get_local_ip(self) -> str:
525
+ """الحصول على IP المحلي"""
526
+ try:
527
+ with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
528
+ s.connect(("8.8.8.8", 80))
529
+ return s.getsockname()[0]
530
+ except Exception:
531
+ return "127.0.0.1"
532
+
533
+ def is_local_network(self, ip: str) -> bool:
534
+ """فحص إذا كان IP في الشبكة المحلية"""
535
+ try:
536
+ addr = ipaddress.ip_address(ip)
537
+ return addr.is_private
538
+ except Exception:
539
+ return False
540
+
541
+ def register_local_service_prioritized(self):
542
+ """تسجيل الخدمة مع أولوية المنافذ"""
543
+ discovery_port = self.get_discovery_port_prioritized()
544
+ self.current_discovery_port = discovery_port
545
+
546
+ try:
547
+ local_ip = self.get_local_ip()
548
+ hostname = socket.gethostname()
549
+
550
+ self.service_info = ServiceInfo(
551
+ type_=SERVICE_TYPE,
552
+ name=f"{hostname} Offload Service.{SERVICE_TYPE}",
553
+ addresses=[socket.inet_aton(local_ip)],
554
+ port=discovery_port,
555
+ properties={
556
+ b'version': b'2.3',
557
+ b'hostname': hostname.encode(),
558
+ b'service': b'offload',
559
+ b'port': str(discovery_port).encode(),
560
+ b'network_scan': b'enabled',
561
+ b'discovery_strategy': self.port_strategy.encode(),
562
+ b'priority_index': str(self.port_priority_index).encode()
563
+ },
564
+ server=f"{hostname}.local."
565
+ )
566
+
567
+ self.zeroconf = Zeroconf()
568
+ self.zeroconf.register_service(self.service_info)
569
+ logger.info(f"✅ الخدمة مسجلة على المنفذ {discovery_port} (استراتيجية: {self.get_strategy_name()})")
570
+
571
+ except Exception as e:
572
+ logger.error(f"❌ فشل تسجيل الخدمة على المنفذ {discovery_port}: {e}")
573
+ # محاولة التراجع للمنفذ 7520 مباشرة
574
+ self._fallback_to_default_port()
575
+
576
+ def get_strategy_name(self) -> str:
577
+ """اسم استراتيجية المنفذ الحالية"""
578
+ strategies = ["72500", "مدير المنافذ", "7520 احتياطي"]
579
+ return strategies[self.port_priority_index] if self.port_priority_index < len(strategies) else "غير معروف"
580
+
581
+ def _fallback_to_default_port(self):
582
+ """التراجع للمنفذ الافتراضي 7520"""
583
+ try:
584
+ self.current_discovery_port = FALLBACK_PORT
585
+ local_ip = self.get_local_ip()
586
+ hostname = socket.gethostname()
587
+
588
+ self.service_info = ServiceInfo(
589
+ type_=SERVICE_TYPE,
590
+ name=f"{hostname} Offload Service.{SERVICE_TYPE}",
591
+ addresses=[socket.inet_aton(local_ip)],
592
+ port=FALLBACK_PORT,
593
+ properties={b'version': b'2.3', b'hostname': hostname.encode()},
594
+ server=f"{hostname}.local."
595
+ )
596
+
597
+ if self.zeroconf:
598
+ self.zeroconf.unregister_service(self.service_info)
599
+ self.zeroconf.register_service(self.service_info)
600
+ logger.info(f"🔄 تم التراجع للمنفذ الافتراضي {FALLBACK_PORT}")
601
+
602
+ except Exception as e:
603
+ logger.error(f"💥 فشل كامل في تسجيل الخدمة: {e}")
604
+
605
+ def register_local_service(self):
606
+ """وظيفة التوافق - استخدام النظام الجديد"""
607
+ self.register_local_service_prioritized()
608
+
609
+ class PeerListener(ServiceListener):
610
+ """مستمع لاكتشاف خدمات Zeroconf محسن"""
611
+
612
+ def __init__(self, discovery_manager):
613
+ self.manager = discovery_manager
614
+
615
+ def add_service(self, zc: Zeroconf, type_: str, name: str) -> None:
616
+ """اكتشاف خدمة جديدة"""
617
+ try:
618
+ info = zc.get_service_info(type_, name)
619
+ if info and info.addresses:
620
+ ip = socket.inet_ntoa(info.addresses[0])
621
+ port = info.port
622
+ hostname = info.server[:-1] if info.server.endswith('.') else info.server
623
+
624
+ peer_url = f"http://{ip}:{port}"
625
+ network_type = "lan"
626
+
627
+ # فحص سريع للمنفذ قبل الإضافة
628
+ if self.manager.check_port_quick(ip, port):
629
+ peer_info = PeerInfo(
630
+ url=peer_url,
631
+ ip=ip,
632
+ port=port,
633
+ hostname=hostname,
634
+ network_type=network_type,
635
+ last_seen=datetime.now(),
636
+ last_health_check=datetime.now(),
637
+ discovery_method="zeroconf"
638
+ )
639
+
640
+ # إضافة فورية مع فحص صحة
641
+ self.manager.add_peer_immediate(peer_info)
642
+ logger.info(f"🔍 اكتشاف جهاز عبر Zeroconf: {hostname} ({ip}:{port})")
643
+
644
+ except Exception as e:
645
+ logger.debug(f"خطأ في معالجة الخدمة {name}: {e}")
646
+
647
+ def remove_service(self, zc: Zeroconf, type_: str, name: str) -> None:
648
+ """إزالة خدمة"""
649
+ logger.info(f"خدمة تمت إزالتها: {name}")
650
+
651
+ def update_service(self, zc: Zeroconf, type_: str, name: str) -> None:
652
+ """تحديث خدمة"""
653
+ logger.debug(f"خدمة محدثة: {name}")
654
+
655
+ def check_port_quick(self, ip: str, port: int, timeout: int = 2) -> bool:
656
+ """فحص سريع للمنفذ"""
657
+ try:
658
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
659
+ sock.settimeout(timeout)
660
+ result = sock.connect_ex((ip, port))
661
+ return result == 0
662
+ except Exception:
663
+ return False
664
+
665
+ def add_peer_immediate(self, peer_info: PeerInfo):
666
+ """إضافة قرين مع فحص صحة فوري"""
667
+ with self._lock:
668
+ if peer_info.url not in self.peers:
669
+ self.peers[peer_info.url] = peer_info
670
+
671
+ # فحص الصحة فوري وغير متزامن
672
+ threading.Thread(
673
+ target=self.check_peer_health_prioritized,
674
+ args=(peer_info,),
675
+ daemon=True
676
+ ).start()
677
+
678
+ def start_lan_discovery(self):
679
+ """بدء اكتشاف الأجهزة على الشبكة المحلية"""
680
+ try:
681
+ if self.zeroconf:
682
+ listener = self.PeerListener(self)
683
+ ServiceBrowser(self.zeroconf, SERVICE_TYPE, listener)
684
+ logger.info("📡 بدء اكتشاف الأجهزة على LAN")
685
+ except Exception as e:
686
+ logger.error(f"فشل بدء اكتشاف LAN: {e}")
687
+
688
+ def start_network_scanning_prioritized(self):
689
+ """بدء مسح الشبكة المُفضل"""
690
+ def network_scan_worker():
691
+ while self._running:
692
+ try:
693
+ logger.info(f"🎯 بدء المسح المُفضل (المنفذ {PRIORITY_PORT} أولاً)...")
694
+
695
+ local_ip = self.get_local_ip()
696
+ network_base = ".".join(local_ip.split(".")[:3])
697
+
698
+ # استخدام المسح المُفضل
699
+ discovered_hosts = self.network_scanner.scan_subnet_prioritized(network_base)
700
+
701
+ for host in discovered_hosts:
702
+ self.process_scanned_host_prioritized(host)
703
+
704
+ # إحصائيات الأولوية
705
+ preferred_count = sum(1 for h in discovered_hosts if h['priority_port_found'])
706
+ logger.info(f"✅ المسح المُفضل مكتمل: {preferred_count} جهاز بالمنفذ المفضل")
707
+
708
+ except Exception as e:
709
+ logger.error(f"خطأ في المسح المُفضل: {e}")
710
+
711
+ time.sleep(NETWORK_SCAN_INTERVAL)
712
+
713
+ self._network_scan_thread = threading.Thread(
714
+ target=network_scan_worker,
715
+ daemon=True
716
+ )
717
+ self._network_scan_thread.start()
718
+
719
+ def start_network_scanning(self):
720
+ """وظيفة التوافق - استخدام النظام الجديد"""
721
+ self.start_network_scanning_prioritized()
722
+
723
+ def process_scanned_host_prioritized(self, host: dict):
724
+ """معالجة الجهاز المكتشف من المسح المُفضل"""
725
+ try:
726
+ ip = host['ip']
727
+ open_ports = host['open_ports']
728
+
729
+ # استخدام أول منفذ مفتوح (مع الأولوية لـ 72500)
730
+ if open_ports:
731
+ port = open_ports[0] # أول منفذ مفتوح (مع الأولوية)
732
+ peer_url = f"http://{ip}:{port}"
733
+
734
+ with self._lock:
735
+ if peer_url not in self.peers:
736
+ peer_info = PeerInfo(
737
+ url=peer_url,
738
+ ip=ip,
739
+ port=port,
740
+ hostname=host['hostname'],
741
+ network_type="lan",
742
+ last_seen=datetime.now(),
743
+ last_health_check=datetime.now(),
744
+ capabilities=['network_scan_discovered'],
745
+ discovery_method="network_scan_prioritized"
746
+ )
747
+ self.peers[peer_url] = peer_info
748
 
749
+ # فحص الصحة غير متزامن
750
+ threading.Thread(
751
+ target=self.check_peer_health_prioritized,
752
+ args=(peer_info,),
753
+ daemon=True
754
+ ).start()
755
+
756
+ port_type = "مفضل" if port == PRIORITY_PORT else "ثانوي"
757
+ logger.info(f"➕ قرين جديد من المسح ({port_type}): {ip}:{port}")
758
+
759
+ except Exception as e:
760
+ logger.debug(f"خطأ في معالجة الجهاز المسح: {e}")
761
+
762
+ def process_scanned_host(self, host: dict):
763
+ """وظيفة التوافق - استخدام النظام الجديد"""
764
+ self.process_scanned_host_prioritized(host)
765
+
766
+ def start_resource_discovery(self):
767
+ """بدء اكتشاف الموارد المساعدة"""
768
+ def resource_discovery_worker():
769
+ while self._running:
770
+ try:
771
+ # اكتشاف أجهزة التخزين
772
+ storage_devices = self.hardware_discoverer.discover_storage_devices()
773
+ for device in storage_devices:
774
+ self.add_storage_resource(device)
775
+
776
+ # اكتشاف كاميرات الشبكة
777
+ cameras = self.hardware_discoverer.discover_network_cameras()
778
+ for camera in cameras:
779
+ self.add_camera_resource(camera)
780
+
781
+ logger.info(f"📦 اكتشاف الموارد: {len(storage_devices)} تخزين, {len(cameras)} كاميرا")
782
+
783
+ except Exception as e:
784
+ logger.error(f"خطأ في اكتشاف الموارد: {e}")
785
+
786
+ time.sleep(300) # كل 5 دقائق
787
+
788
+ self._resource_discovery_thread = threading.Thread(
789
+ target=resource_discovery_worker,
790
+ daemon=True
791
+ )
792
+ self._resource_discovery_thread.start()
793
+
794
+ def add_storage_resource(self, device: dict):
795
+ """إضافة مورد تخزين"""
796
+ resource_id = device['device_id']
797
+
798
+ with self._lock:
799
+ if resource_id not in self.resources:
800
+ resource_info = ResourceInfo(
801
+ device_id=resource_id,
802
+ name=device['name'],
803
+ type='storage',
804
+ connection_type=device['connection_type'],
805
+ capabilities=['file_storage', 'backup', 'cache'],
806
+ status='available',
807
+ paired_at=datetime.now(),
808
+ details={
809
+ 'capacity': device.get('capacity', 'unknown'),
810
+ 'free_space': device.get('free_space', 'unknown'),
811
+ 'mountpoint': device.get('mountpoint', '')
812
+ }
813
+ )
814
+ self.resources[resource_id] = resource_info
815
+ logger.info(f"💾 مورد تخزين مضاف: {device['name']}")
816
+
817
+ def add_camera_resource(self, camera: dict):
818
+ """إضافة مورد كاميرا"""
819
+ resource_id = camera['device_id']
820
+
821
+ with self._lock:
822
+ if resource_id not in self.resources:
823
+ resource_info = ResourceInfo(
824
+ device_id=resource_id,
825
+ name=camera['name'],
826
+ type='camera',
827
+ connection_type='network',
828
+ capabilities=['video_stream', 'monitoring'],
829
+ status='available',
830
+ paired_at=datetime.now(),
831
+ details={
832
+ 'ip': camera['ip'],
833
+ 'ports': camera['open_ports'],
834
+ 'stream_urls': camera['stream_urls']
835
+ }
836
+ )
837
+ self.resources[resource_id] = resource_info
838
+ logger.info(f"📷 مورد كاميرا مضاف: {camera['name']}")
839
+
840
+ def discover_central_peers_enhanced(self):
841
+ """اكتشاف محسن من السيرفرات المركزية"""
842
+ logger.info("🌍 بدء اكتشاف السيرفرات المركزية...")
843
+
844
+ working_servers = 0
845
+ total_peers_found = 0
846
+
847
+ for server_url in self.central_servers:
848
+ try:
849
+ logger.info(f"🔗 محاولة الاتصال بـ: {server_url}")
850
+
851
+ # محاولة endpoints مختلفة
852
+ endpoints = [
853
+ "/api/peers",
854
+ "/peers",
855
+ "/discovery/peers",
856
+ "/nodes",
857
+ "/health" # قد يعطينا معلومات عن الأقران
858
+ ]
859
+
860
+ for endpoint in endpoints:
861
+ discovery_url = urljoin(server_url, endpoint)
862
+ try:
863
+ # تعطيل التحقق من SSL للسيرفرات المحلية والاختبار
864
+ verify_ssl = not any(domain in server_url for domain in ['localhost', '127.0.0.1', '192.168.'])
865
+ response = requests.get(discovery_url, timeout=10, verify=verify_ssl)
866
+ logger.info(f"📡 استجابة من {discovery_url}: {response.status_code}")
867
+
868
+ if response.status_code == 200:
869
+ peers_data = response.json()
870
+ peers_list = peers_data.get('peers', []) or peers_data.get('nodes', []) or []
871
+
872
+ if peers_list:
873
+ for peer_data in peers_list:
874
+ self.add_peer_from_discovery_enhanced(peer_data, server_url)
875
+ total_peers_found += 1
876
+
877
+ working_servers += 1
878
+ logger.info(f"✅ {server_url} يعمل - وجد {len(peers_list)} جهاز")
879
+ break # خروج عند النجاح
880
+
881
+ except requests.exceptions.RequestException as e:
882
+ logger.debug(f"❌ فشل {endpoint} على {server_url}: {e}")
883
+ continue
884
+ except json.JSONDecodeError as e:
885
+ logger.debug(f"❌ خطأ في JSON من {server_url}: {e}")
886
+ continue
887
+
888
+ except Exception as e:
889
+ logger.warning(f"❌ فشل كامل مع {server_url}: {e}")
890
+
891
+ # تحديث الإحصائيات
892
+ self.discovery_stats.update({
893
+ 'central_servers_working': working_servers,
894
+ 'central_servers_failed': len(self.central_servers) - working_servers,
895
+ 'last_central_discovery': datetime.now(),
896
+ 'peers_found': total_peers_found
897
+ })
898
+
899
+ logger.info(f"📊 نتائج الاكتشاف المركزي: {working_servers}/{len(self.central_servers)} سيرفرات تعمل، {total_peers_found} جهاز مكتشف")
900
+
901
+ def add_peer_from_discovery_enhanced(self, peer_data: dict, source_server: str):
902
+ """إضافة جهاز من بيانات الاكتشاف مع تحسينات"""
903
+ try:
904
+ ip = peer_data.get('ip') or peer_data.get('address') or peer_data.get('host')
905
+ port = peer_data.get('port', self.current_discovery_port or FALLBACK_PORT)
906
+ hostname = peer_data.get('hostname') or peer_data.get('name', f'peer_from_{source_server}')
907
+
908
+ if not ip:
909
+ logger.debug("❌ بيانات جهاز بدون IP - تخطي")
910
+ return
911
+
912
+ # تنظيف الـ IP
913
+ ip = str(ip).strip()
914
+ if ip.startswith('http://'):
915
+ ip = ip[7:]
916
+ if ip.startswith('https://'):
917
+ ip = ip[8:]
918
+ if ':' in ip:
919
+ ip = ip.split(':')[0]
920
+
921
+ peer_url = f"http://{ip}:{port}"
922
+
923
+ with self._lock:
924
+ if peer_url not in self.peers:
925
+ peer_info = PeerInfo(
926
+ url=peer_url,
927
+ ip=ip,
928
+ port=port,
929
+ hostname=hostname,
930
+ network_type="wan",
931
+ last_seen=datetime.now(),
932
+ last_health_check=datetime.now(),
933
+ discovery_method=f"central_server_{source_server}"
934
+ )
935
+ self.peers[peer_url] = peer_info
936
+
937
+ # فحص الصحة غير متزامن
938
+ threading.Thread(
939
+ target=self.check_peer_health_prioritized,
940
+ args=(peer_info,),
941
+ daemon=True
942
+ ).start()
943
+
944
+ logger.info(f"🌍 قرين مركزي مضاف: {hostname} ({ip}:{port}) من {source_server}")
945
+
946
+ except Exception as e:
947
+ logger.error(f"❌ خطأ في إضافة جهاز من الاكتشاف: {e}")
948
+
949
+ def check_peer_health_prioritized(self, peer_info: PeerInfo):
950
+ """فحص صحة مع أولوية للمنفذ المفضل"""
951
+
952
+ # أولاً: محاولة المنفذ المفضل 72500
953
+ preferred_health_url = f"http://{peer_info.ip}:{PRIORITY_PORT}/health"
954
+
955
+ try:
956
+ start_time = time.time()
957
+ verify_ssl = not self.is_local_network(peer_info.ip)
958
+ response = requests.get(preferred_health_url, timeout=3, verify=verify_ssl)
959
+ response_time = time.time() - start_time
960
+
961
+ if response.status_code == 200:
962
+ self._update_peer_health(peer_info, response, response_time, "preferred_port")
963
+ logger.info(f"✅ {peer_info.hostname} صحي عبر المنفذ المفضل ({response_time:.2f}s)")
964
+ return True
965
+ except Exception as e:
966
+ logger.debug(f"⏰ فشل المنفذ المفضل لـ {peer_info.hostname}: {e}")
967
+
968
+ # ثانياً: إذا فشل، المحاولة بالمنافذ الأخرى
969
+ return self.check_peer_health_enhanced(peer_info)
970
+
971
+ def _update_peer_health(self, peer_info: PeerInfo, response, response_time: float, method: str):
972
+ """تحديث صحة القرين"""
973
+ try:
974
+ health_data = response.json()
975
+ except:
976
+ health_data = {}
977
+
978
+ with self._lock:
979
+ peer_info.is_active = True
980
+ peer_info.response_time = response_time
981
+ peer_info.last_health_check = datetime.now()
982
+ peer_info.cpu_usage = health_data.get('cpu_usage', 0)
983
+ peer_info.memory_available = health_data.get('memory_available', 0)
984
+ peer_info.capabilities = health_data.get('capabilities', [])
985
+
986
+ def check_peer_health_enhanced(self, peer_info: PeerInfo):
987
+ """فحص صحة محسن مع endpoints متعددة"""
988
+ health_endpoints = [
989
+ "/health",
990
+ "/api/health",
991
+ "/status",
992
+ "/api/status",
993
+ "/", # الصفحة الرئيسية قد تعطي معلومات
994
+ ]
995
+
996
+ for endpoint in health_endpoints:
997
+ try:
998
+ health_url = f"{peer_info.url}{endpoint}"
999
+ start_time = time.time()
1000
+
1001
+ # تعطيل SSL verification للأجهزة المحلية
1002
+ verify_ssl = not any(domain in peer_info.url for domain in ['localhost', '127.0.0.1', '192.168.'])
1003
+ response = requests.get(health_url, timeout=5, verify=verify_ssl)
1004
+ response_time = time.time() - start_time
1005
+
1006
+ if response.status_code == 200:
1007
+ self._update_peer_health(peer_info, response, response_time, endpoint)
1008
+ logger.info(f"✅ {peer_info.hostname} صحي ({response_time:.2f}s) عبر {endpoint}")
1009
+ return True
1010
+
1011
+ except requests.exceptions.Timeout:
1012
+ logger.debug(f"⏰ انتهت مهلة {endpoint} لـ {peer_info.hostname}")
1013
+ continue
1014
+ except requests.exceptions.ConnectionError:
1015
+ logger.debug(f"🔌 فشل اتصال {endpoint} لـ {peer_info.hostname}")
1016
+ continue
1017
+ except Exception as e:
1018
+ logger.debug(f"⚠️ خطأ في {endpoint} لـ {peer_info.hostname}: {e}")
1019
+ continue
1020
+
1021
+ # إذا فشلت جميع المحاولات
1022
+ self.mark_peer_inactive(peer_info)
1023
+ logger.warning(f"❌ {peer_info.hostname} غير متجاوب بعد كل المحاولات")
1024
+ return False
1025
+
1026
+ def mark_peer_inactive(self, peer_info: PeerInfo):
1027
+ """تعليم الجهاز كغير نشط"""
1028
+ with self._lock:
1029
+ peer_info.is_active = False
1030
+ peer_info.last_health_check = datetime.now()
1031
+ logger.info(f"🔴 {peer_info.hostname} معلم كغير نشط")
1032
+
1033
+ def health_check_worker(self):
1034
+ """عامل فحص الصحة الدوري"""
1035
+ while self._running:
1036
+ try:
1037
+ current_peers = self.get_active_peers()
1038
+
1039
+ if current_peers:
1040
+ with ThreadPoolExecutor(max_workers=5) as executor:
1041
+ futures = [
1042
+ executor.submit(self.check_peer_health_prioritized, peer_info)
1043
+ for peer_info in current_peers
1044
+ ]
1045
+
1046
+ for future in as_completed(futures):
1047
+ try:
1048
+ future.result(timeout=15)
1049
+ except Exception:
1050
+ continue
1051
+
1052
+ logger.info(f"❤️ فحص صحة مكتمل - الأجهزة النشطة: {len(self.get_active_peers())}")
1053
+
1054
+ except Exception as e:
1055
+ logger.error(f"💥 خطأ في فحص الصحة: {e}")
1056
+
1057
+ time.sleep(HEALTH_CHECK_INTERVAL)
1058
+
1059
+ def discovery_worker(self):
1060
+ """عامل الاكتشاف الدوري المحسن"""
1061
+ while self._running:
1062
+ try:
1063
+ # الاكتشاف من السيرفرات المركزية
1064
+ self.discover_central_peers_enhanced()
1065
+
1066
+ # تنظيف الأجهزة القديمة
1067
+ self.cleanup_old_peers()
1068
+
1069
+ # تسجيل الإحصائيات
1070
+ active_peers = len(self.get_active_peers())
1071
+ total_peers = len(self.peers)
1072
+
1073
+ logger.info(
1074
+ f"🔄 الاكتشاف الدوري - "
1075
+ f"نشط: {active_peers}/{total_peers}, "
1076
+ f"سيرفرات عاملة: {self.discovery_stats['central_servers_working']}"
1077
+ )
1078
+
1079
+ except Exception as e:
1080
+ logger.error(f"💥 خطأ في الاكتشاف الدوري: {e}")
1081
+
1082
+ time.sleep(DISCOVERY_INTERVAL)
1083
+
1084
+ def cleanup_old_peers(self):
1085
+ """تنظيف الأجهزة القديمة"""
1086
+ cutoff_time = datetime.now() - timedelta(minutes=10)
1087
+
1088
+ with self._lock:
1089
+ to_remove = [
1090
+ url for url, peer in self.peers.items()
1091
+ if peer.last_seen < cutoff_time and not peer.is_active
1092
+ ]
1093
+
1094
+ for url in to_remove:
1095
+ del self.peers[url]
1096
+ logger.info(f"🧹 تنظيف جهاز قديم: {url}")
1097
+
1098
+ def get_active_peers(self) -> List[PeerInfo]:
1099
+ """الحصول على قائمة الأجهزة النشطة"""
1100
+ with self._lock:
1101
+ return [
1102
+ peer for peer in self.peers.values()
1103
+ if peer.is_active
1104
+ ]
1105
+
1106
+ def get_peers_by_network(self, network_type: str) -> List[PeerInfo]:
1107
+ """الحصول على الأجهزة حسب نوع الشبكة"""
1108
+ with self._lock:
1109
+ return [
1110
+ peer for peer in self.peers.values()
1111
+ if peer.network_type == network_type and peer.is_active
1112
+ ]
1113
+
1114
+ def get_resources_by_type(self, resource_type: str) -> List[ResourceInfo]:
1115
+ """الحصول على الموارد حسب النوع"""
1116
+ with self._lock:
1117
+ return [
1118
+ resource for resource in self.resources.values()
1119
+ if resource.type == resource_type and resource.status == 'available'
1120
+ ]
1121
+
1122
+ def start_enhanced_discovery(self):
1123
+ """بدء الاكتشاف المحسن مع أولوية المنافذ"""
1124
+ if self._running:
1125
+ return
1126
+
1127
+ self._running = True
1128
+
1129
+ # 1. تسجيل الخدمة مع الأولوية
1130
+ self.register_local_service_prioritized()
1131
+
1132
+ # 2. بدء الاكتشاف المحلي
1133
+ self.start_lan_discovery()
1134
+
1135
+ # 3. الاكتشاف الفوري مع الأولوية
1136
+ logger.info("🚀 بدء الاكتشاف الفوري مع أولوية المنفذ 72500...")
1137
+ self.discover_central_peers_enhanced()
1138
+
1139
+ # 4. بدء المسح المُفضل
1140
+ self.start_network_scanning_prioritized()
1141
+
1142
+ # 5. بدء اكتشاف الموارد
1143
+ self.start_resource_discovery()
1144
+
1145
+ # 6. بدء الخيوط الدورية
1146
+ self._start_periodic_threads()
1147
+
1148
+ logger.info(f"🎯 نظام الاكتشاف المحسن v2.3 يعمل على المنفذ {self.current_discovery_port}")
1149
+
1150
+ def _start_periodic_threads(self):
1151
+ """بدء جميع الخيوط الدورية"""
1152
+
1153
+ # الاكتشاف الدوري
1154
+ self._discovery_thread = threading.Thread(
1155
+ target=self.discovery_worker,
1156
+ daemon=True
1157
+ )
1158
+ self._discovery_thread.start()
1159
+
1160
+ # فحص الصحة الدوري
1161
+ self._health_check_thread = threading.Thread(
1162
+ target=self.health_check_worker,
1163
+ daemon=True
1164
+ )
1165
+ self._health_check_thread.start()
1166
+
1167
+ def start(self):
1168
+ """وظيفة البدء المتوافقة"""
1169
+ self.start_enhanced_discovery()
1170
+
1171
+ def stop(self):
1172
+ """إيقاف نظام الاكتشاف"""
1173
+ self._running = False
1174
+
1175
+ if self.zeroconf and self.service_info:
1176
+ self.zeroconf.unregister_service(self.service_info)
1177
+ self.zeroconf.close()
1178
+
1179
+ logger.info("🛑 نظام اكتشاف الأقران متوقف")
1180
+
1181
+ def get_port_strategy_report(self) -> dict:
1182
+ """تقرير استراتيجية المنافذ"""
1183
+ active_peers = self.get_active_peers()
1184
+ port_stats = {}
1185
+
1186
+ for peer in active_peers:
1187
+ port = peer.port
1188
+ if port not in port_stats:
1189
+ port_stats[port] = 0
1190
+ port_stats[port] += 1
1191
+
1192
+ return {
1193
+ 'current_port': self.current_discovery_port,
1194
+ 'current_strategy': self.get_strategy_name(),
1195
+ 'priority_index': self.port_priority_index,
1196
+ 'active_peers_by_port': port_stats,
1197
+ 'total_peers': len(active_peers),
1198
+ 'efficiency': self.calculate_port_efficiency()
1199
+ }
1200
+
1201
+ def calculate_port_efficiency(self) -> float:
1202
+ """حساب كفاءة استراتيجية المنفذ"""
1203
+ active_peers = self.get_active_peers()
1204
+ if not active_peers:
1205
+ return 0.0
1206
+
1207
+ # عدد الأقران على المنفذ المفضل 72500
1208
+ preferred_peers = sum(1 for p in active_peers if p.port == PRIORITY_PORT)
1209
+ return preferred_peers / len(active_peers)
1210
+
1211
+ def set_preferred_port(self, port: int):
1212
+ """تعيين منفذ مفضل جديد"""
1213
+ global PRIORITY_PORT
1214
+ PRIORITY_PORT = port
1215
+ logger.info(f"🔄 تغيير المنفذ المفضل إلى: {port}")
1216
+
1217
+ @property
1218
+ def PEERS(self) -> List[str]:
1219
+ """واجهة توافقية للحصول على روابط الأقران"""
1220
+ active_peers = self.get_active_peers()
1221
+ return [peer.url for peer in active_peers]
1222
+
1223
+ @property
1224
+ def PEERS_INFO(self) -> Dict[str, dict]:
1225
+ """معلومات مفصلة عن الأقران"""
1226
+ with self._lock:
1227
+ return {
1228
+ url: {
1229
+ 'ip': peer.ip,
1230
+ 'port': peer.port,
1231
+ 'hostname': peer.hostname,
1232
+ 'network_type': peer.network_type,
1233
+ 'is_active': peer.is_active,
1234
+ 'response_time': peer.response_time,
1235
+ 'cpu_usage': peer.cpu_usage,
1236
+ 'memory_available': peer.memory_available,
1237
+ 'last_seen': peer.last_seen.isoformat(),
1238
+ 'capabilities': peer.capabilities,
1239
+ 'discovery_method': peer.discovery_method
1240
+ }
1241
+ for url, peer in self.peers.items()
1242
+ }
1243
+
1244
+ @property
1245
+ def RESOURCES_INFO(self) -> Dict[str, dict]:
1246
+ """معلومات عن الموارد المساعدة"""
1247
+ with self._lock:
1248
+ return {
1249
+ resource_id: {
1250
+ 'name': resource.name,
1251
+ 'type': resource.type,
1252
+ 'connection_type': resource.connection_type,
1253
+ 'capabilities': resource.capabilities,
1254
+ 'status': resource.status,
1255
+ 'paired_at': resource.paired_at.isoformat(),
1256
+ 'details': resource.details
1257
+ }
1258
+ for resource_id, resource in self.resources.items()
1259
+ }
1260
+
1261
+ def get_discovery_report(self) -> dict:
1262
+ """تقرير مفصل عن حالة الاكتشاف"""
1263
+ active_peers = self.get_active_peers()
1264
+
1265
+ return {
1266
+ 'total_peers': len(self.peers),
1267
+ 'active_peers': len(active_peers),
1268
+ 'discovery_stats': self.discovery_stats,
1269
+ 'peers_by_method': self._get_peers_by_discovery_method(),
1270
+ 'central_servers_status': self._test_central_servers(),
1271
+ 'port_strategy': self.get_port_strategy_report()
1272
+ }
1273
+
1274
+ def _get_peers_by_discovery_method(self) -> dict:
1275
+ """تصنيف الأقران حسب طريقة الاكتشاف"""
1276
+ methods = {}
1277
+ with self._lock:
1278
+ for peer in self.peers.values():
1279
+ method = peer.discovery_method
1280
+ if method not in methods:
1281
+ methods[method] = []
1282
+ methods[method].append({
1283
+ 'hostname': peer.hostname,
1284
+ 'url': peer.url,
1285
+ 'is_active': peer.is_active
1286
+ })
1287
+ return methods
1288
+
1289
+ def _test_central_servers(self) -> dict:
1290
+ """فحص حالة السيرفرات المركزية"""
1291
+ results = {}
1292
+ for server in self.central_servers:
1293
+ try:
1294
+ verify_ssl = not any(domain in server for domain in ['localhost', '127.0.0.1', '192.168.'])
1295
+ response = requests.get(server, timeout=5, verify=verify_ssl)
1296
+ results[server] = {
1297
+ 'status': 'working' if response.status_code == 200 else 'failed',
1298
+ 'status_code': response.status_code
1299
+ }
1300
+ except Exception as e:
1301
+ results[server] = {
1302
+ 'status': 'failed',
1303
+ 'error': str(e)
1304
+ }
1305
+ return results
1306
+
1307
+ # نسخة عالمية للاستيراد
1308
+ discovery_manager = EnhancedPeerDiscovery()
1309
+
1310
+ # دوال التوافق مع الإصدار القديم
1311
  def get_sequential_port():
1312
+ """وظيفة التوافق - استخدام المنفذ الثابت"""
1313
+ return discovery_manager.current_discovery_port or FALLBACK_PORT
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1314
 
1315
  def register_peer(ip, port):
1316
+ """وظيفة التوافق"""
1317
+ peer_url = f"http://{ip}:{port}"
1318
+ discovery_manager.add_peer_from_discovery_enhanced({
1319
+ 'ip': ip,
1320
+ 'port': port,
1321
+ 'hostname': 'manual'
1322
+ }, "manual_registration")
1323
 
1324
  def discover_lan_peers():
1325
+ """وظيفة التواسب - بدلاً من إرجاع Zeroconf"""
1326
+ discovery_manager.start_lan_discovery()
1327
+ return discovery_manager.zeroconf
 
 
 
 
 
 
 
1328
 
1329
  def main():
1330
+ """الدالة الرئيسية المحسنة"""
1331
+ logger.info("🚀 بدء نظام اكتشاف الأقران المحسن v2.3...")
1332
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1333
  try:
1334
+ # بدء النظام المحسن
1335
+ discovery_manager.start_enhanced_discovery()
1336
+
1337
+ # حلقة العرض المحسنة
1338
+ counter = 0
1339
  while True:
1340
+ counter += 1
1341
+
1342
+ # كل 5 دورات عرض تقرير مفصل
1343
+ if counter % 5 == 0:
1344
+ report = discovery_manager.get_discovery_report()
1345
+ port_report = report['port_strategy']
1346
+ logger.info(f"📊 تقرير مفصل - الأقران: {report['active_peers']}/{report['total_peers']} نشط")
1347
+ logger.info(f"🎯 استراتيجية المنافذ: {port_report['current_strategy']} - الكفاءة: {port_report['efficiency']:.1%}")
1348
+ else:
1349
+ # عرض إحصائيات سريعة
1350
+ active_peers = discovery_manager.get_active_peers()
1351
+ stats = discovery_manager.discovery_stats
1352
+ port_stats = discovery_manager.get_port_strategy_report()
1353
+
1354
+ logger.info(
1355
+ f"📈 إحصائيات سريعة - "
1356
+ f"أقران: {len(active_peers)} نشط, "
1357
+ f"سيرفرات: {stats['central_servers_working']} تعمل, "
1358
+ f"استراتيجية: {port_stats['current_strategy']}"
1359
+ )
1360
+
1361
  time.sleep(10)
1362
+
1363
  except KeyboardInterrupt:
1364
  logger.info("🛑 إيقاف النظام...")
1365
  finally:
1366
+ discovery_manager.stop()
 
1367
 
1368
  if __name__ == "__main__":
1369
  main()