osamabyc86 commited on
Commit
84c65b6
·
verified ·
1 Parent(s): 8acd0e4

Upload 69 files

Browse files
background_service.py CHANGED
@@ -239,8 +239,8 @@ class BackgroundService:
239
  self.setup_signal_handlers()
240
 
241
  # تشغيل خادم HTTP API للتحكم
242
- self.logger.info("🌐 تشغيل HTTP API على المنفذ 8888")
243
- self.app.run(host='0.0.0.0', port=8888, debug=False)
244
 
245
  def main():
246
  service = BackgroundService()
@@ -254,7 +254,7 @@ def main():
254
  # فحص حالة الخدمة
255
  import requests
256
  try:
257
- response = requests.get('http://localhost:8888/status')
258
  print(json.dumps(response.json(), indent=2, ensure_ascii=False))
259
  except:
260
  print("❌ الخدمة غير متاحة")
@@ -262,7 +262,7 @@ def main():
262
  # إيقاف الخدمة
263
  import requests
264
  try:
265
- response = requests.post('http://localhost:8888/stop')
266
  print(response.json()['message'])
267
  except:
268
  print("❌ فشل في إيقاف الخدمة")
@@ -270,7 +270,7 @@ def main():
270
  # إظهار الواجهة التفاعلية
271
  import requests
272
  try:
273
- response = requests.post('http://localhost:8888/show-ui')
274
  print(response.json()['message'])
275
  except:
276
  print("❌ فشل في إظهار الواجهة التفاعلية")
 
239
  self.setup_signal_handlers()
240
 
241
  # تشغيل خادم HTTP API للتحكم
242
+ self.logger.info("🌐 تشغيل HTTP API على المنفذ51738")
243
+ self.app.run(host='0.0.0.0', port=5173, debug=False)
244
 
245
  def main():
246
  service = BackgroundService()
 
254
  # فحص حالة الخدمة
255
  import requests
256
  try:
257
+ response = requests.get('http://localhost:5173/status')
258
  print(json.dumps(response.json(), indent=2, ensure_ascii=False))
259
  except:
260
  print("❌ الخدمة غير متاحة")
 
262
  # إيقاف الخدمة
263
  import requests
264
  try:
265
+ response = requests.post('http://localhost:5173/stop')
266
  print(response.json()['message'])
267
  except:
268
  print("❌ فشل في إيقاف الخدمة")
 
270
  # إظهار الواجهة التفاعلية
271
  import requests
272
  try:
273
+ response = requests.post('http://localhost:5173/show-ui')
274
  print(response.json()['message'])
275
  except:
276
  print("❌ فشل في إظهار الواجهة التفاعلية")
dashboard.py CHANGED
@@ -34,7 +34,7 @@ def get_system_status():
34
  def broadcast_status():
35
  while True:
36
  status = get_system_status()
37
- socketio.emit("status_update", {node_id: status}, broadcast=True)
38
  socketio.sleep(5)
39
 
40
  # ─────────────── صفحة الواجهة ───────────────
@@ -46,7 +46,7 @@ def index():
46
  @socketio.on("status_update")
47
  def handle_status_update(data):
48
  connected_peers_data.update(data)
49
- emit("update_peers", connected_peers_data, broadcast=True)
50
 
51
  # ─────────────── دردشة ───────────────
52
  @socketio.on("send_message")
@@ -55,6 +55,6 @@ def handle_message(data):
55
 
56
  # ─────────────── تشغيل ───────────────
57
  if __name__ == "__main__":
58
- threading.Thread(target=broadcast_status).start()
59
  logging.info(f"🚀 تشغيل Dashboard على {node_id}")
60
  socketio.run(app, host="0.0.0.0", port=7000)
 
34
  def broadcast_status():
35
  while True:
36
  status = get_system_status()
37
+ socketio.emit("status_update", {node_id: status}) # تم إزالة broadcast=True
38
  socketio.sleep(5)
39
 
40
  # ─────────────── صفحة الواجهة ───────────────
 
46
  @socketio.on("status_update")
47
  def handle_status_update(data):
48
  connected_peers_data.update(data)
49
+ emit("update_peers", connected_peers_data, broadcast=True) # هذا صحيح لأنه داخل معالج حدث
50
 
51
  # ─────────────── دردشة ───────────────
52
  @socketio.on("send_message")
 
55
 
56
  # ─────────────── تشغيل ───────────────
57
  if __name__ == "__main__":
58
+ threading.Thread(target=broadcast_status, daemon=True).start() # أضفت daemon=True
59
  logging.info(f"🚀 تشغيل Dashboard على {node_id}")
60
  socketio.run(app, host="0.0.0.0", port=7000)
external_server.py CHANGED
@@ -4,6 +4,7 @@ external_server.py — سيرفر مركزي لتوزيع المهام + Dashboa
4
  """
5
  import logging
6
  import requests
 
7
  from flask import Flask, request, jsonify, render_template
8
  from flask_cors import CORS
9
  from flask_socketio import SocketIO, emit
@@ -18,29 +19,49 @@ socketio = SocketIO(app, cors_allowed_origins="*")
18
 
19
  connected_peers = {} # {node_id: {"cpu":%, "ram":%, "gpu":%}}
20
 
 
 
 
 
 
 
 
 
 
21
  # ─────────────── اختيار أفضل Peer ───────────────
22
  def select_best_peer():
23
- peers_list = list(PEERS)
24
- if not peers_list:
25
  logging.warning("⚠️ لا توجد أجهزة مسجلة حالياً.")
26
  return None
27
 
28
  try:
29
  peer_loads = []
30
- for peer_url in peers_list:
31
  try:
32
- resp = requests.get(f"{peer_url.replace('/run_task','')}/status", timeout=2)
 
 
 
 
 
33
  if resp.ok:
34
  data = resp.json()
35
- peer_loads.append((peer_url, data.get("cpu_load", 100)))
36
- except:
 
 
 
 
37
  continue
38
 
39
  if not peer_loads:
40
  return None
41
 
42
- peer_loads.sort(key=lambda x: x[1])
43
- return peer_loads[0][0]
 
 
 
44
  except Exception as e:
45
  logging.error(f"❌ خطأ في اختيار الـ Peer: {e}")
46
  return None
@@ -49,21 +70,34 @@ def select_best_peer():
49
  @app.route("/submit_task", methods=["POST"])
50
  def submit_task():
51
  data = request.get_json()
52
- if not data or "task_id" not in data:
53
- return jsonify({"error": "يجب تحديد task_id"}), 400
54
 
55
  peer = select_best_peer()
56
  if not peer:
57
  return jsonify({"error": "لا توجد أجهزة متاحة حالياً"}), 503
58
 
59
  try:
60
- resp = requests.post(peer, json=data, timeout=10)
 
 
 
 
 
 
61
  if resp.ok:
62
- return jsonify({"status": "success", "result": resp.json()})
 
 
63
  else:
64
- return jsonify({"error": "فشل إرسال المهمة"}), 500
 
 
 
 
 
65
  except Exception as e:
66
- logging.error(f"❌ خطأ في إرسال المهمة: {e}")
67
  return jsonify({"error": str(e)}), 500
68
 
69
  # ─────────────── API تحديث حالة الأجهزة ───────────────
@@ -75,24 +109,63 @@ def update_status():
75
  return jsonify({"error": "node_id مطلوب"}), 400
76
 
77
  connected_peers[node_id] = {
78
- "cpu": data.get("cpu"),
79
- "ram": data.get("ram"),
80
- "gpu": data.get("gpu")
 
81
  }
 
82
  socketio.emit("update_peers", connected_peers, broadcast=True)
 
83
  return jsonify({"status": "ok"})
84
 
 
 
 
 
 
 
 
 
 
85
  # ─────────────── صفحة Dashboard ───────────────
86
  @app.route("/")
87
  def index():
88
- return render_template("dashboard.html")
 
 
 
 
 
 
89
 
90
  # ─────────────── دردشة ───────────────
91
  @socketio.on("send_message")
92
  def handle_message(data):
93
- socketio.emit("receive_message", data, broadcast=True)
94
 
95
  # ─────────────── تشغيل السيرفر ───────────────
96
  if __name__ == "__main__":
97
- logging.info("🚀 بدء السيرفر المركزي مع Dashboard ودردشة")
98
- socketio.run(app, host="0.0.0.0", port = PORT)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  """
5
  import logging
6
  import requests
7
+ import socket
8
  from flask import Flask, request, jsonify, render_template
9
  from flask_cors import CORS
10
  from flask_socketio import SocketIO, emit
 
19
 
20
  connected_peers = {} # {node_id: {"cpu":%, "ram":%, "gpu":%}}
21
 
22
+ # ─────────────── التحقق من توفر المنفذ ───────────────
23
+ def is_port_available(port):
24
+ try:
25
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
26
+ s.bind(('0.0.0.0', port))
27
+ return True
28
+ except OSError:
29
+ return False
30
+
31
  # ─────────────── اختيار أفضل Peer ───────────────
32
  def select_best_peer():
33
+ if not PEERS:
 
34
  logging.warning("⚠️ لا توجد أجهزة مسجلة حالياً.")
35
  return None
36
 
37
  try:
38
  peer_loads = []
39
+ for peer_url in PEERS:
40
  try:
41
+ # بناء URL لفحص الحالة
42
+ status_url = peer_url.replace('/run_task', '/health')
43
+ if '/run_task' not in peer_url:
44
+ status_url = f"{peer_url}/health"
45
+
46
+ resp = requests.get(status_url, timeout=2)
47
  if resp.ok:
48
  data = resp.json()
49
+ # افتراض أن الخادم يعيد cpu_load أو استخدام قيمة افتراضية
50
+ cpu_load = data.get("cpu_load", 50)
51
+ peer_loads.append((peer_url, cpu_load))
52
+ logging.info(f"✅ {peer_url} - الحمل: {cpu_load}%")
53
+ except Exception as e:
54
+ logging.warning(f"❌ لا يمكن الوصول إلى {peer_url}: {e}")
55
  continue
56
 
57
  if not peer_loads:
58
  return None
59
 
60
+ # اختيار الأقل حملًا
61
+ best_peer = min(peer_loads, key=lambda x: x[1])
62
+ logging.info(f"🎯 أفضل جهاز: {best_peer[0]} مع حمل {best_peer[1]}%")
63
+ return best_peer[0]
64
+
65
  except Exception as e:
66
  logging.error(f"❌ خطأ في اختيار الـ Peer: {e}")
67
  return None
 
70
  @app.route("/submit_task", methods=["POST"])
71
  def submit_task():
72
  data = request.get_json()
73
+ if not data or "func" not in data:
74
+ return jsonify({"error": "يجب تحديد اسم الدالة (func)"}), 400
75
 
76
  peer = select_best_peer()
77
  if not peer:
78
  return jsonify({"error": "لا توجد أجهزة متاحة حالياً"}), 503
79
 
80
  try:
81
+ # تأكد من أن عنوان الـ Peer صحيح
82
+ if not peer.startswith('http'):
83
+ peer = f"http://{peer}"
84
+
85
+ logging.info(f"📤 إرسال المهمة إلى: {peer}")
86
+ resp = requests.post(peer, json=data, timeout=30)
87
+
88
  if resp.ok:
89
+ result = resp.json()
90
+ logging.info(f"✅ تم تنفيذ المهمة بنجاح على {peer}")
91
+ return jsonify({"status": "success", "result": result, "executed_on": peer})
92
  else:
93
+ logging.error(f" فشل تنفيذ المهمة على {peer}: {resp.status_code}")
94
+ return jsonify({"error": f"فشل إرسال المهمة: {resp.text}"}), 500
95
+
96
+ except requests.exceptions.Timeout:
97
+ logging.error(f"⏰ انتهت المهلة أثناء الاتصال بـ {peer}")
98
+ return jsonify({"error": "انتهت مهلة الاتصال"}), 408
99
  except Exception as e:
100
+ logging.error(f"❌ خطأ في إرسال المهمة إلى {peer}: {e}")
101
  return jsonify({"error": str(e)}), 500
102
 
103
  # ─────────────── API تحديث حالة الأجهزة ───────────────
 
109
  return jsonify({"error": "node_id مطلوب"}), 400
110
 
111
  connected_peers[node_id] = {
112
+ "cpu": data.get("cpu", 0),
113
+ "ram": data.get("ram", 0),
114
+ "gpu": data.get("gpu", 0),
115
+ "last_update": "now" # يمكن إضافة timestamp
116
  }
117
+
118
  socketio.emit("update_peers", connected_peers, broadcast=True)
119
+ logging.info(f"📊 تم تحديث حالة {node_id}")
120
  return jsonify({"status": "ok"})
121
 
122
+ # ─────────────── صفحة الحالة ───────────────
123
+ @app.route("/status")
124
+ def status():
125
+ return jsonify({
126
+ "connected_peers": connected_peers,
127
+ "available_peers": list(PEERS),
128
+ "total_peers": len(PEERS)
129
+ })
130
+
131
  # ─────────────── صفحة Dashboard ───────────────
132
  @app.route("/")
133
  def index():
134
+ return render_template("dashboard.html", peers=connected_peers)
135
+
136
+ # ─────────────── استقبال تحديثات الحالة عبر WebSocket ───────────────
137
+ @socketio.on("status_update")
138
+ def handle_status_update(data):
139
+ connected_peers.update(data)
140
+ emit("update_peers", connected_peers, broadcast=True)
141
 
142
  # ─────────────── دردشة ───────────────
143
  @socketio.on("send_message")
144
  def handle_message(data):
145
+ emit("receive_message", data, broadcast=True)
146
 
147
  # ─────────────── تشغيل السيرفر ───────────────
148
  if __name__ == "__main__":
149
+ # محاولة منافذ مختلفة
150
+ ports_to_try = [5005, 5006, 5007, 5008, 5009]
151
+ selected_port = None
152
+
153
+ for port in ports_to_try:
154
+ if is_port_available(port):
155
+ selected_port = port
156
+ break
157
+
158
+ if selected_port is None:
159
+ logging.error("❌ لا توجد منافذ متاحة. حاول إغلاق التطبيقات الأخرى.")
160
+ exit(1)
161
+
162
+ logging.info(f"🚀 بدء السيرفر المركزي مع Dashboard على المنفذ {selected_port}")
163
+
164
+ try:
165
+ socketio.run(app, host="0.0.0.0", port=selected_port, debug=False)
166
+ except OSError as e:
167
+ logging.error(f"❌ فشل تشغيل السيرفر على المنفذ {selected_port}: {e}")
168
+ except KeyboardInterrupt:
169
+ logging.info("⏹️ إيقاف السيرفر...")
170
+ except Exception as e:
171
+ logging.error(f"❌ خطأ غير متوقع: {e}")
history.json CHANGED
@@ -19,6 +19,14 @@
19
  "role": "user",
20
  "content": "مم"
21
  },
 
 
 
 
 
 
 
 
22
  {
23
  "role": "assistant",
24
  "content": "عذراً، حدث خطأ: 401"
 
19
  "role": "user",
20
  "content": "مم"
21
  },
22
+ {
23
+ "role": "assistant",
24
+ "content": "عذراً، حدث خطأ: 401"
25
+ },
26
+ {
27
+ "role": "user",
28
+ "content": "hi"
29
+ },
30
  {
31
  "role": "assistant",
32
  "content": "عذراً، حدث خطأ: 401"
live_streaming.py CHANGED
@@ -1,4 +1,3 @@
1
-
2
  # live_streaming.py - نظام البث المباشر للألعاب والفيديو
3
 
4
  import cv2
@@ -61,13 +60,35 @@ def stream_offload(func):
61
  def estimate_stream_complexity(func, args, kwargs):
62
  """تقدير تعقيد معالجة البث"""
63
  if func.__name__ == "process_game_stream":
64
- return args[1] * args[2] / 10000 # FPS × الدقة
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  elif func.__name__ == "real_time_video_enhancement":
66
- return args[0] * 20 # عدد التحسينات × 20
 
 
67
  elif func.__name__ == "multi_stream_processing":
68
- return len(args[0]) * 25 # عدد البثوث × 25
 
 
69
  elif func.__name__ == "ai_commentary_generation":
70
- return args[1] * 15 # طول النص × 15
 
 
 
 
 
71
  return 40
72
 
73
  # ═══════════════════════════════════════════════════════════════
@@ -75,7 +96,7 @@ def estimate_stream_complexity(func, args, kwargs):
75
  # ═══════════════════════════════════════════════════════════════
76
 
77
  @stream_offload
78
- def process_game_stream(stream_data, fps, resolution, enhancements=None):
79
  """معالجة بث الألعاب في الوقت الفعلي"""
80
  start_time = time.time()
81
 
@@ -221,7 +242,7 @@ def multi_stream_processing(streams_data, processing_mode="parallel"):
221
  # ═══════════════════════════════════════════════════════════════
222
 
223
  @stream_offload
224
- def ai_commentary_generation(game_events, commentary_length, language="ar"):
225
  """توليد تعليق ذكي للألعاب"""
226
  start_time = time.time()
227
 
 
 
1
  # live_streaming.py - نظام البث المباشر للألعاب والفيديو
2
 
3
  import cv2
 
60
  def estimate_stream_complexity(func, args, kwargs):
61
  """تقدير تعقيد معالجة البث"""
62
  if func.__name__ == "process_game_stream":
63
+ # استخراج القيم الرقمية من الدقة
64
+ resolution = args[2] if len(args) > 2 else "1920x1080"
65
+ if isinstance(resolution, str) and 'x' in resolution:
66
+ try:
67
+ width, height = map(int, resolution.split('x'))
68
+ resolution_factor = width * height / 10000
69
+ except:
70
+ resolution_factor = 1920 * 1080 / 10000 # قيمة افتراضية
71
+ else:
72
+ resolution_factor = 1920 * 1080 / 10000
73
+
74
+ fps = args[1] if len(args) > 1 else 60
75
+ return fps * resolution_factor
76
+
77
  elif func.__name__ == "real_time_video_enhancement":
78
+ enhancements = args[0] if len(args) > 0 else []
79
+ return len(enhancements) * 20 # عدد التحسينات × 20
80
+
81
  elif func.__name__ == "multi_stream_processing":
82
+ streams = args[0] if len(args) > 0 else []
83
+ return len(streams) * 25 # عدد البثوث × 25
84
+
85
  elif func.__name__ == "ai_commentary_generation":
86
+ commentary_length = args[1] if len(args) > 1 else 50
87
+ return commentary_length * 15 # طول النص × 15
88
+
89
+ elif func.__name__ == "stream_quality_optimization":
90
+ return 40 # تعقيد متوسط
91
+
92
  return 40
93
 
94
  # ═══════════════════════════════════════════════════════════════
 
96
  # ═══════════════════════════════════════════════════════════════
97
 
98
  @stream_offload
99
+ def process_game_stream(stream_data, fps=60, resolution="1920x1080", enhancements=None):
100
  """معالجة بث الألعاب في الوقت الفعلي"""
101
  start_time = time.time()
102
 
 
242
  # ═══════════════════════════════════════════════════════════════
243
 
244
  @stream_offload
245
+ def ai_commentary_generation(game_events, commentary_length=50, language="ar"):
246
  """توليد تعليق ذكي للألعاب"""
247
  start_time = time.time()
248
 
load_balancer.py CHANGED
@@ -1,78 +1,278 @@
1
  # load_balancer.py
2
- import peer_discovery, requests, time, smart_tasks, psutil, socket
 
 
 
 
 
 
 
 
3
  from peer_discovery import PORT
4
 
5
- def send(peer, func, *args, **kw):
6
- try:
7
- r = requests.post(peer, json={"func": func,
8
- "args": list(args),
9
- "kwargs": kw}, timeout=12)
10
- return r.json()
11
- except Exception as e:
12
- return {"error": str(e)}
13
 
14
- def choose_peer():
15
- """اختيار أفضل جهاز - أولوية LAN ثم WAN"""
16
- import socket
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
- lan_peers = []
19
- wan_peers = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
- # تصنيف الأجهزة
22
- for p in list(peer_discovery.PEERS):
23
- ip = p.split('//')[1].split(':')[0] if '//' in p else p.split(':')[0]
24
- if is_local_ip(ip):
25
- lan_peers.append(p)
26
- else:
27
- wan_peers.append(p)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
- # أولاً: جرب الأجهزة المحلية (LAN)
30
- best_lan = find_best_peer(lan_peers)
31
- if best_lan:
32
- return best_lan
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
- # ثانياً: إذا لم تتوفر أجهزة محلية، جرب WAN
35
- if internet_available():
36
- best_wan = find_best_peer(wan_peers)
37
- return best_wan
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
- return None
40
-
41
- def find_best_peer(peers):
42
- """العثور على أفضل جهاز من قائمة معينة"""
43
- best = None
44
- for p in peers:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  try:
46
- cpu = requests.get(p.replace("/run", "/cpu"), timeout=2).json()["usage"]
47
- best = (p, cpu) if best is None or cpu < best[1] else best
48
- except:
49
- continue
50
- return best[0] if best else None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
- def is_local_ip(ip):
53
- """فحص إذا كان IP محلي"""
54
- return (
55
- ip.startswith('192.168.') or
56
- ip.startswith('10.') or
57
- ip.startswith('172.') or
58
- ip == '127.0.0.1'
59
- )
60
 
61
- def internet_available():
62
- """فحص توفر الإنترنت"""
 
 
 
 
 
 
 
 
 
63
  try:
64
- socket.create_connection(("8.8.8.8", 53), timeout=3)
65
- return True
66
- except:
67
- return False
 
 
 
 
 
 
 
 
 
 
 
68
 
69
- while True:
70
- peer = choose_peer()
71
- if peer:
72
- print(f"\n🛰️ إرسال إلى {peer}")
73
- res = send(peer, "prime_calculation", 30000)
74
- else:
75
- print("\n⚙️ لا أقران؛ العمل محليّ على", socket.gethostname())
76
- res = smart_tasks.prime_calculation(30000)
77
- print("🔹 النتيجة (جزئية):", str(res)[:120])
78
- time.sleep(10)
 
1
  # load_balancer.py
2
+ import peer_discovery
3
+ import requests
4
+ import time
5
+ import smart_tasks
6
+ import psutil
7
+ import socket
8
+ import threading
9
+ import logging
10
+ from datetime import datetime
11
  from peer_discovery import PORT
12
 
13
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 
 
 
 
 
 
 
14
 
15
+ class SmartLoadBalancer:
16
+ def __init__(self):
17
+ self.peer_stats = {}
18
+ self.local_hostname = socket.gethostname()
19
+ self.local_ip = self.get_local_ip()
20
+ self.running = True
21
+
22
+ def get_local_ip(self):
23
+ """الحصول على IP المحلي"""
24
+ try:
25
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
26
+ s.connect(("8.8.8.8", 80))
27
+ ip = s.getsockname()[0]
28
+ s.close()
29
+ return ip
30
+ except:
31
+ return "127.0.0.1"
32
 
33
+ def send(self, peer, func, *args, **kwargs):
34
+ """إرسال مهمة إلى نظير"""
35
+ try:
36
+ url = f"{peer}/run" if not peer.endswith("/run") else peer
37
+ payload = {
38
+ "func": func,
39
+ "args": list(args),
40
+ "kwargs": kwargs
41
+ }
42
+
43
+ logging.info(f"📤 إرسال مهمة {func} إلى {peer}")
44
+ response = requests.post(url, json=payload, timeout=15)
45
+
46
+ if response.status_code == 200:
47
+ result = response.json()
48
+ logging.info(f"✅ تم استلام النتيجة من {peer}")
49
+ return result
50
+ else:
51
+ logging.error(f"❌ خطأ من {peer}: {response.status_code}")
52
+ return {"error": f"HTTP {response.status_code}"}
53
+
54
+ except requests.exceptions.Timeout:
55
+ logging.error(f"⏰ انتهت المهلة مع {peer}")
56
+ return {"error": "Request timeout"}
57
+ except requests.exceptions.ConnectionError:
58
+ logging.error(f"🔌 تعذر الاتصال بـ {peer}")
59
+ return {"error": "Connection failed"}
60
+ except Exception as e:
61
+ logging.error(f"❌ خطأ غير متوقع مع {peer}: {str(e)}")
62
+ return {"error": str(e)}
63
 
64
+ def choose_peer(self):
65
+ """اختيار أفضل جهاز مع خوارزمية متطورة"""
66
+ available_peers = self.get_available_peers()
67
+
68
+ if not available_peers:
69
+ logging.info("🔍 لم يتم العثور على أقران متاحة")
70
+ return None
71
+
72
+ # تقييم كل نظير
73
+ peer_scores = []
74
+ for peer in available_peers:
75
+ score = self.evaluate_peer(peer)
76
+ if score > 0:
77
+ peer_scores.append((peer, score))
78
+
79
+ if not peer_scores:
80
+ return None
81
+
82
+ # اختيار النظير بأعلى درجة
83
+ best_peer = max(peer_scores, key=lambda x: x[1])[0]
84
+ logging.info(f"🏆 أفضل نظير مختار: {best_peer}")
85
+ return best_peer
86
 
87
+ def get_available_peers(self):
88
+ """الحصول على قائمة الأقران المتاحة"""
89
+ peers = []
90
+
91
+ # اكتشاف الأقران المحليين
92
+ local_peers = self.discover_local_peers()
93
+ peers.extend(local_peers)
94
+
95
+ # إضافة الأقران من peer_discovery
96
+ for peer in list(peer_discovery.PEERS):
97
+ if peer not in peers:
98
+ peers.append(peer)
99
+
100
+ # إزالة التكرارات والنظير المحلي
101
+ unique_peers = []
102
+ for peer in peers:
103
+ if self.is_self_peer(peer):
104
+ continue
105
+ if peer not in unique_peers:
106
+ unique_peers.append(peer)
107
+
108
+ logging.info(f"👥 الأقران المتاحون: {len(unique_peers)}")
109
+ return unique_peers
110
 
111
+ def discover_local_peers(self):
112
+ """اكتشاف الأقران على الشبكة المحلية"""
113
+ local_peers = []
114
+ base_ip = ".".join(self.local_ip.split(".")[:-1]) # الحصول على 192.168.1
115
+
116
+ # فحص نطاق IPs المحلي
117
+ for i in range(1, 255):
118
+ if i == int(self.local_ip.split(".")[-1]):
119
+ continue # تخطي الذات
120
+
121
+ ip = f"{base_ip}.{i}"
122
+ peer_url = f"http://{ip}:{PORT}"
123
+
124
+ # فحص سريع للتوصيل
125
+ if self.check_peer_availability(peer_url):
126
+ local_peers.append(peer_url)
127
+
128
+ return local_peers
129
 
130
+ def check_peer_availability(self, peer_url):
131
+ """فحص توفر النظير"""
132
+ try:
133
+ response = requests.get(f"{peer_url}/status", timeout=2)
134
+ return response.status_code == 200
135
+ except:
136
+ return False
137
+
138
+ def is_self_peer(self, peer_url):
139
+ """فحص إذا كان النظير هو الجهاز الحالي"""
140
+ if f"://{self.local_ip}:" in peer_url:
141
+ return True
142
+ if f"://localhost:" in peer_url:
143
+ return True
144
+ if f"://127.0.0.1:" in peer_url:
145
+ return True
146
+ return False
147
+
148
+ def evaluate_peer(self, peer):
149
+ """تقييم أداء النظير"""
150
  try:
151
+ # الحصول على حالة النظام
152
+ status_url = peer.replace("/run", "/status") if "/run" in peer else f"{peer}/status"
153
+ response = requests.get(status_url, timeout=3)
154
+
155
+ if response.status_code == 200:
156
+ status = response.json()
157
+
158
+ # حساب الدرجة
159
+ score = 100
160
+
161
+ # خصم حسب استخدام CPU
162
+ cpu_usage = status.get("cpu_usage", 50)
163
+ score -= cpu_usage * 0.5
164
+
165
+ # خصم حسب استخدام الذاكرة
166
+ memory_usage = status.get("memory_usage", 50)
167
+ score -= memory_usage * 0.3
168
+
169
+ # مكافأة للاتصالات المحلية
170
+ if self.is_local_peer(peer):
171
+ score += 20
172
+
173
+ # خصم للاتصال البطيء
174
+ response_time = response.elapsed.total_seconds()
175
+ score -= response_time * 10
176
+
177
+ return max(0, score)
178
+
179
+ except Exception as e:
180
+ logging.debug(f"تعذر تقييم {peer}: {str(e)}")
181
+
182
+ return 0
183
+
184
+ def is_local_peer(self, peer):
185
+ """فحص إذا كان النظير محلي"""
186
+ peer_ip = peer.split("://")[1].split(":")[0] if "://" in peer else peer.split(":")[0]
187
+ return self.is_local_ip(peer_ip)
188
+
189
+ def is_local_ip(self, ip):
190
+ """فحص إذا كان IP محلي"""
191
+ return (
192
+ ip.startswith('192.168.') or
193
+ ip.startswith('10.') or
194
+ ip.startswith('172.') or
195
+ ip in ['127.0.0.1', 'localhost', self.local_ip]
196
+ )
197
+
198
+ def execute_task(self, func_name, *args, **kwargs):
199
+ """تنفيذ المهمة إما محلياً أو عن بعد"""
200
+ peer = self.choose_peer()
201
+
202
+ if peer:
203
+ logging.info(f"🛰️ إرسال مهمة {func_name} إلى {peer}")
204
+ result = self.send(peer, func_name, *args, **kwargs)
205
+
206
+ if "error" not in result:
207
+ return result
208
+ else:
209
+ logging.warning(f"❌ فشل الإرسال إلى {peer}: {result['error']}")
210
+
211
+ # التنفيذ المحلي كبديل
212
+ logging.info(f"⚙️ تنفيذ محلي للمهمة {func_name}")
213
+ return self.execute_locally(func_name, *args, **kwargs)
214
+
215
+ def execute_locally(self, func_name, *args, **kwargs):
216
+ """تنفيذ المهمة محلياً"""
217
+ try:
218
+ if hasattr(smart_tasks, func_name):
219
+ func = getattr(smart_tasks, func_name)
220
+ result = func(*args, **kwargs)
221
+ return result
222
+ else:
223
+ return {"error": f"الدالة {func_name} غير موجودة"}
224
+ except Exception as e:
225
+ return {"error": str(e)}
226
+
227
+ def start_monitoring(self):
228
+ """بدء مراقبة الأقران"""
229
+ def monitor_loop():
230
+ while self.running:
231
+ try:
232
+ available_peers = self.get_available_peers()
233
+ logging.info(f"📊 المراقبة: {len(available_peers)} أقران متاحون")
234
+ time.sleep(30)
235
+ except Exception as e:
236
+ logging.error(f"خطأ في المراقبة: {e}")
237
+ time.sleep(60)
238
+
239
+ monitor_thread = threading.Thread(target=monitor_loop, daemon=True)
240
+ monitor_thread.start()
241
+
242
+ def stop(self):
243
+ """إيقاف موازن الحمل"""
244
+ self.running = False
245
 
246
+ # إنشاء نسخة عالمية
247
+ load_balancer = SmartLoadBalancer()
 
 
 
 
 
 
248
 
249
+ def send_task(func_name, *args, **kwargs):
250
+ """وظيفة مساعدة لإرسال المهام"""
251
+ return load_balancer.execute_task(func_name, *args, **kwargs)
252
+
253
+ # التشغيل الرئيسي
254
+ def main():
255
+ logging.info("🚀 بدء تشغيل موازن الحمل الذكي")
256
+
257
+ # بدء المراقبة
258
+ load_balancer.start_monitoring()
259
+
260
  try:
261
+ while load_balancer.running:
262
+ # تنفيذ مهام اختبارية
263
+ result = load_balancer.execute_task("prime_calculation", 20000)
264
+
265
+ if "error" in result:
266
+ logging.error(f"❌ خطأ في التنفيذ: {result['error']}")
267
+ else:
268
+ primes_count = result.get('count', 0)
269
+ logging.info(f"✅ تم تنفيذ المهمة - عدد الأعداد الأولية: {primes_count}")
270
+
271
+ time.sleep(15)
272
+
273
+ except KeyboardInterrupt:
274
+ logging.info("🛑 إيقاف موازن الحمل...")
275
+ load_balancer.stop()
276
 
277
+ if __name__ == "__main__":
278
+ main()
 
 
 
 
 
 
 
 
main.py CHANGED
@@ -20,6 +20,7 @@ from flask import Flask, request, jsonify
20
  from flask_cors import CORS
21
  from peer_discovery import CENTRAL_REGISTRY_SERVERS
22
  from peer_discovery import PORT
 
23
  # ─────────────── إعدادات المسارات ───────────────
24
  FILE = Path(__file__).resolve()
25
  BASE_DIR = FILE.parent
@@ -38,30 +39,6 @@ logging.basicConfig(
38
  ]
39
  )
40
 
41
- import multiprocessing
42
- import time
43
- import sys
44
- import os
45
-
46
- # دالة تشغيل السيرفر الخارجي
47
- def run_external_server():
48
- """تشغيل ملف exexternal_server.py في عملية منفصلة"""
49
- try:
50
- # استيراد وتشغيل الملف
51
- import exexternal_server
52
- exexternal_server.main() # افترض أن الدالة الرئيسية اسمها main
53
- except Exception as e:
54
- print(f"خطأ في تشغيل السيرفر: {e}")
55
-
56
- # في الملف الرئيسي
57
- def start_external_server_process():
58
- """تشغيل السيرفر في عملية منفصلة"""
59
- process = multiprocessing.Process(target=run_external_server)
60
- process.daemon = True # سيتوقف تلقائياً عند إنهاء البرنامج الرئيسي
61
- process.start()
62
- print(f"تم تشغيل السيرفر الخارجي (PID: {process.pid})")
63
- return process
64
-
65
  # ─────────────── تحميل متغيرات البيئة ───────────────
66
  try:
67
  from dotenv import load_dotenv
 
20
  from flask_cors import CORS
21
  from peer_discovery import CENTRAL_REGISTRY_SERVERS
22
  from peer_discovery import PORT
23
+ import external_server
24
  # ─────────────── إعدادات المسارات ───────────────
25
  FILE = Path(__file__).resolve()
26
  BASE_DIR = FILE.parent
 
39
  ]
40
  )
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  # ─────────────── تحميل متغيرات البيئة ───────────────
43
  try:
44
  from dotenv import load_dotenv
main.spec CHANGED
@@ -1,38 +1,44 @@
1
- # -*- mode: python ; coding: utf-8 -*-
2
-
3
-
4
- a = Analysis(
5
- ['main.py'],
6
- pathex=[],
7
- binaries=[],
8
- datas=[],
9
- hiddenimports=[],
10
- hookspath=[],
11
- hooksconfig={},
12
- runtime_hooks=[],
13
- excludes=[],
14
- noarchive=False,
15
- optimize=0,
16
- )
17
- pyz = PYZ(a.pure)
18
-
19
- exe = EXE(
20
- pyz,
21
- a.scripts,
22
- a.binaries,
23
- a.datas,
24
- [],
25
- name='main',
26
- debug=False,
27
- bootloader_ignore_signals=False,
28
- strip=False,
29
- upx=True,
30
- upx_exclude=[],
31
- runtime_tmpdir=None,
32
- console=False,
33
- disable_windowed_traceback=False,
34
- argv_emulation=False,
35
- target_arch=None,
36
- codesign_identity=None,
37
- entitlements_file=None,
38
- )
 
 
 
 
 
 
 
1
+ # -*- mode: python ; coding: utf-8 -*-
2
+
3
+
4
+ block_cipher = None
5
+
6
+
7
+ a = Analysis(
8
+ ['main.py'],
9
+ pathex=[],
10
+ binaries=[],
11
+ datas=[('*', '.')],
12
+ hiddenimports=[],
13
+ hookspath=[],
14
+ hooksconfig={},
15
+ runtime_hooks=[],
16
+ excludes=[],
17
+ win_no_prefer_redirects=False,
18
+ win_private_assemblies=False,
19
+ cipher=block_cipher,
20
+ noarchive=False,
21
+ )
22
+ pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
23
+
24
+ exe = EXE(
25
+ pyz,
26
+ a.scripts,
27
+ a.binaries,
28
+ a.zipfiles,
29
+ a.datas,
30
+ [],
31
+ name='main',
32
+ debug=False,
33
+ bootloader_ignore_signals=False,
34
+ strip=False,
35
+ upx=True,
36
+ upx_exclude=[],
37
+ runtime_tmpdir=None,
38
+ console=True,
39
+ disable_windowed_traceback=False,
40
+ argv_emulation=False,
41
+ target_arch=None,
42
+ codesign_identity=None,
43
+ entitlements_file=None,
44
+ )
node_client.py CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env python3
2
  # ================================================================
3
  # node_client.py – عميل تسجيل العُقدة في نظام AmalOffload
4
  # ---------------------------------------------------------------
@@ -21,15 +21,20 @@ from typing import Iterable, Tuple, List
21
  DEFAULT_PORTS = {
22
  7520, 7384, 9021, 6998, 5810, 9274,
23
  8645, 7329, 7734, 8456, 6173, 7860,
 
24
  }
25
 
26
  # ⬇️ خوادم السجل الاحتياطية بالترتيب المفضَّل
27
  DEFAULT_REGISTRY_SERVERS = [
 
 
28
  "https://cv4790811.regru.cloud",
29
  "https://amaloffload.onrender.com",
30
  "https://osamabyc86-offload.hf.space",
31
  "http://10.229.36.125",
32
  "http://10.229.228.178",
 
 
33
  ]
34
 
35
  logging.basicConfig(
@@ -56,8 +61,10 @@ class NodeClient:
56
  self.node_id = node_id or os.getenv("NODE_ID", socket.gethostname())
57
 
58
  # مبدئيًّا اختَر منفذًا (أولوية للمتغيّر البيئي إن وُجد)
59
- self.port: int = int(os.getenv("CPU_PORT", random.choice(list(self.PORTs))))
60
  self.current_server_index: int | None = None
 
 
61
 
62
  # -------------------------------------------------------------------------
63
  @staticmethod
@@ -66,10 +73,15 @@ class NodeClient:
66
  try:
67
  with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
68
  # لا يهم أن ينجح الاتصال الفعلي، الهدف كشف IP واجهة الخروج
69
- s.connect(("10.255.255.255", 1))
70
  return s.getsockname()[0]
71
  except Exception:
72
- return "127.0.0.1"
 
 
 
 
 
73
 
74
  def _register_once(self, server: str, port: int) -> List[str]:
75
  """مُحاولة واحدة للتسجيل؛ تُعيد peers أو ترفع استثناءً."""
@@ -77,32 +89,106 @@ class NodeClient:
77
  "node_id": self.node_id,
78
  "ip": self.get_local_ip(),
79
  "port": port,
 
 
80
  }
81
- resp = requests.post(f"{server}/register", json=payload, timeout=5)
82
- resp.raise_for_status()
83
- return resp.json() # توقّع أن الخادم يُرجع قائمة أقران
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
  # -------------------------------------------------------------------------
86
- def connect_until_success(self, retry_delay: int = 5) -> Tuple[str, List[str]]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  """
88
  يدور على جميع المنافذ والخوادم حتى ينجح التسجيل.
89
  • عند النجاح يُرجع: (عنوان الخادم، قائمة الأقران)
90
  • لا يرفع استثناءات؛ إمّا ينجح أو يستمر في المحاولة إلى ما لا نهاية.
91
  """
92
  logging.info("🔄 بدء محاولات التسجيل للعقدة '%s'...", self.node_id)
 
 
 
 
93
  while True:
94
- for port in self.PORTs:
95
- for idx, server in enumerate(self.registry_servers):
 
 
 
 
 
 
 
 
 
 
96
  try:
97
  peers = self._register_once(server, port)
98
  # سجّل النجاح واحفظ المعلومات
99
  self.port = port
100
  self.current_server_index = idx
101
- logging.info("✅ متصل: %s على المنفذ %s", server, port)
 
102
  return server, peers
103
  except Exception as e:
104
  logging.debug("❌ %s:%s -> %s", server, port, e)
105
- time.sleep(retry_delay) # انتظر قليلًا ثم أعد المحاولة
 
 
106
 
107
  # -------------------------------------------------------------------------
108
  def run_background(self) -> None:
@@ -110,8 +196,25 @@ class NodeClient:
110
  إطلاق التسجيل في خيط منفصل؛ مفيد إذا كنت تريد
111
  إبقاء Main Thread للمهام الأخرى.
112
  """
113
- import threading # استيراد متأخر لتفادي الحمل الزائد عند import
114
- threading.Thread(target=self.connect_until_success, daemon=True).start()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
 
117
  # -----------------------------------------------------------------------------
@@ -120,6 +223,24 @@ if __name__ == "__main__":
120
  للتجربة المباشرة:
121
  $ python node_client.py
122
  """
123
- client = NodeClient()
124
- server, peer_list = client.connect_until_success()
125
- logging.info("🗂️ الأقران: %s", peer_list)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
  # ================================================================
3
  # node_client.py – عميل تسجيل العُقدة في نظام AmalOffload
4
  # ---------------------------------------------------------------
 
21
  DEFAULT_PORTS = {
22
  7520, 7384, 9021, 6998, 5810, 9274,
23
  8645, 7329, 7734, 8456, 6173, 7860,
24
+ 8080, 8000, 5000, 3000, 8888, 9999
25
  }
26
 
27
  # ⬇️ خوادم السجل الاحتياطية بالترتيب المفضَّل
28
  DEFAULT_REGISTRY_SERVERS = [
29
+ "http://localhost:8888", # خادم محلي أولاً
30
+ "http://127.0.0.1:8888", # خادم محلي بديل
31
  "https://cv4790811.regru.cloud",
32
  "https://amaloffload.onrender.com",
33
  "https://osamabyc86-offload.hf.space",
34
  "http://10.229.36.125",
35
  "http://10.229.228.178",
36
+ "http://192.168.1.1:8888", # راوتر محلي
37
+ "http://192.168.0.1:8888", # راوتر محلي بديل
38
  ]
39
 
40
  logging.basicConfig(
 
61
  self.node_id = node_id or os.getenv("NODE_ID", socket.gethostname())
62
 
63
  # مبدئيًّا اختَر منفذًا (أولوية للمتغيّر البيئي إن وُجد)
64
+ self.port: int = int(os.getenv("CPU_PORT", random.choice(list(self.PORTs)))) # 🔧 تصحيح: PORTs بدلاً من PORTS
65
  self.current_server_index: int | None = None
66
+ self.session = requests.Session()
67
+ self.session.timeout = 10
68
 
69
  # -------------------------------------------------------------------------
70
  @staticmethod
 
73
  try:
74
  with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
75
  # لا يهم أن ينجح الاتصال الفعلي، الهدف كشف IP واجهة الخروج
76
+ s.connect(("8.8.8.8", 53))
77
  return s.getsockname()[0]
78
  except Exception:
79
+ try:
80
+ # محاولة بديلة
81
+ hostname = socket.gethostname()
82
+ return socket.gethostbyname(hostname)
83
+ except Exception:
84
+ return "127.0.0.1"
85
 
86
  def _register_once(self, server: str, port: int) -> List[str]:
87
  """مُحاولة واحدة للتسجيل؛ تُعيد peers أو ترفع استثناءً."""
 
89
  "node_id": self.node_id,
90
  "ip": self.get_local_ip(),
91
  "port": port,
92
+ "hostname": socket.gethostname(),
93
+ "timestamp": time.time()
94
  }
95
+
96
+ # جرب مسارات مختلفة للتسجيل
97
+ endpoints = ["/register", "/api/register", "/nodes/register", "/peer/register"]
98
+
99
+ for endpoint in endpoints:
100
+ try:
101
+ url = f"{server.rstrip('/')}{endpoint}"
102
+ logging.info(f"🔗 محاولة التسجيل في: {url}")
103
+ resp = self.session.post(url, json=payload, timeout=8)
104
+
105
+ if resp.status_code == 200:
106
+ data = resp.json()
107
+ logging.info(f"✅ تسجيل ناجح في {server}")
108
+ return data.get("peers", []) if isinstance(data, dict) else data
109
+ elif resp.status_code == 404:
110
+ continue # جرب endpoint التالي
111
+ else:
112
+ resp.raise_for_status()
113
+
114
+ except requests.exceptions.Timeout:
115
+ logging.warning(f"⏰ انتهت المهلة مع {server}{endpoint}")
116
+ continue
117
+ except requests.exceptions.ConnectionError:
118
+ logging.warning(f"🔌 تعذر الاتصال بـ {server}{endpoint}")
119
+ continue
120
+ except Exception as e:
121
+ logging.warning(f"❌ خطأ مع {server}{endpoint}: {e}")
122
+ continue
123
+
124
+ # إذا وصلنا هنا، فكل المحاولات فشلت
125
+ raise Exception(f"فشل التسجيل في {server} بعد تجربة جميع المسارات")
126
 
127
  # -------------------------------------------------------------------------
128
+ def discover_local_servers(self) -> List[str]:
129
+ """اكتشاف خوادم محلية على الشبكة."""
130
+ local_servers = []
131
+ base_ip = ".".join(self.get_local_ip().split(".")[:-1])
132
+
133
+ # فحص نطاق IPs المحلي
134
+ for i in range(1, 50): # فحص أول 50 عنوان فقط للسرعة
135
+ if i == int(self.get_local_ip().split(".")[-1]):
136
+ continue # تخطي الذات
137
+
138
+ ip = f"{base_ip}.{i}"
139
+ for port in [8888, 8000, 5000, 3000]:
140
+ server_url = f"http://{ip}:{port}"
141
+ if self.check_server_availability(server_url):
142
+ local_servers.append(server_url)
143
+ logging.info(f"🔍 تم اكتشاف خادم محلي: {server_url}")
144
+
145
+ return local_servers
146
+
147
+ def check_server_availability(self, server_url: str) -> bool:
148
+ """فحص توفر الخادم."""
149
+ try:
150
+ resp = self.session.get(f"{server_url}/status", timeout=2)
151
+ return resp.status_code == 200
152
+ except:
153
+ return False
154
+
155
+ def connect_until_success(self, retry_delay: int = 10) -> Tuple[str, List[str]]:
156
  """
157
  يدور على جميع المنافذ والخوادم حتى ينجح التسجيل.
158
  • عند النجاح يُرجع: (عنوان الخادم، قائمة الأقران)
159
  • لا يرفع استثناءات؛ إمّا ينجح أو يستمر في المحاولة إلى ما لا نهاية.
160
  """
161
  logging.info("🔄 بدء محاولات التسجيل للعقدة '%s'...", self.node_id)
162
+ logging.info("🌐 عنوان IP المحلي: %s", self.get_local_ip())
163
+ logging.info("📋 عدد الخوادم المتاحة: %d", len(self.registry_servers))
164
+
165
+ attempt = 0
166
  while True:
167
+ attempt += 1
168
+ logging.info("🔄 محاولة التسجيل رقم %d", attempt)
169
+
170
+ # اكتشاف خوادم محلية أولاً
171
+ if attempt % 3 == 1: # كل 3 محاولات، اكتشف خوادم محلية
172
+ local_servers = self.discover_local_servers()
173
+ all_servers = local_servers + self.registry_servers
174
+ else:
175
+ all_servers = self.registry_servers
176
+
177
+ for port in self.PORTs: # 🔧 تصحيح: PORTs بدلاً من PORTS
178
+ for idx, server in enumerate(all_servers):
179
  try:
180
  peers = self._register_once(server, port)
181
  # سجّل النجاح واحفظ المعلومات
182
  self.port = port
183
  self.current_server_index = idx
184
+ logging.info("✅ تسجيل ناجح: %s على المنفذ %s", server, port)
185
+ logging.info("👥 عدد الأقران المكتشفين: %d", len(peers))
186
  return server, peers
187
  except Exception as e:
188
  logging.debug("❌ %s:%s -> %s", server, port, e)
189
+
190
+ logging.warning("❌ فشلت جميع محاولات التسجيل، إعادة المحاولة بعد %d ثواني", retry_delay)
191
+ time.sleep(retry_delay)
192
 
193
  # -------------------------------------------------------------------------
194
  def run_background(self) -> None:
 
196
  إطلاق التسجيل في خيط منفصل؛ مفيد إذا كنت تريد
197
  إبقاء Main Thread للمهام الأخرى.
198
  """
199
+ import threading
200
+ def background_connect():
201
+ try:
202
+ server, peers = self.connect_until_success()
203
+ logging.info("🎯 التسجيل الخلفي ناجح مع %s", server)
204
+ except Exception as e:
205
+ logging.error("💥 خطأ في التسجيل الخلفي: %s", e)
206
+
207
+ threading.Thread(target=background_connect, daemon=True).start()
208
+
209
+ def get_current_info(self) -> dict:
210
+ """الحصول على معلومات العقدة الحالية."""
211
+ return {
212
+ "node_id": self.node_id,
213
+ "ip": self.get_local_ip(),
214
+ "port": self.port,
215
+ "hostname": socket.gethostname(),
216
+ "current_server": self.registry_servers[self.current_server_index] if self.current_server_index is not None else None
217
+ }
218
 
219
 
220
  # -----------------------------------------------------------------------------
 
223
  للتجربة المباشرة:
224
  $ python node_client.py
225
  """
226
+ try:
227
+ client = NodeClient()
228
+ print("🔍 جاري اكتشاف الخوادم المحلية...")
229
+ local_servers = client.discover_local_servers()
230
+ if local_servers:
231
+ print(f"✅ تم اكتشاف {len(local_servers)} خادم محلي")
232
+
233
+ print("🚀 بدء عملية التسجيل...")
234
+ server, peer_list = client.connect_until_success()
235
+ print(f"✅ تسجيل ناجح مع الخادم: {server}")
236
+ print(f"👥 عدد الأقران: {len(peer_list)}")
237
+ if peer_list:
238
+ print("📋 قائمة الأقران:")
239
+ for i, peer in enumerate(peer_list[:10]): # عرض أول 10 أقران فقط
240
+ print(f" {i+1}. {peer}")
241
+ if len(peer_list) > 10:
242
+ print(f" ... و{len(peer_list) - 10} آخرين")
243
+ except KeyboardInterrupt:
244
+ print("\n🛑 تم إيقاف العميل بواسطة المستخدم")
245
+ except Exception as e:
246
+ print(f"💥 خطأ غير متوقع: {e}")
offload_port.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ 1000
peer_discovery.py CHANGED
@@ -21,20 +21,24 @@ def get_sequential_port():
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://osamabyc19866-omsd.hf.space",
35
  "https://52.13.128.108",
36
- "https://176.28.175.216",
37
- "https://44.229.227.142"
 
38
  ]
39
 
40
  def get_local_ip():
@@ -51,47 +55,192 @@ def register_peer(ip, port):
51
  peer_url = f"http://{ip}:{port}/run"
52
  if peer_url not in PEERS:
53
  PEERS.add(peer_url)
54
- logger.info(f"تم تسجيل قرين جديد: {peer_url}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
  def discover_lan_peers():
57
- class Listener:
 
58
  def add_service(self, zc, type_, name):
59
  info = zc.get_service_info(type_, name)
60
- if info:
61
  ip = socket.inet_ntoa(info.addresses[0])
62
- register_peer(ip, info.port)
 
 
 
 
 
 
 
 
 
 
63
 
64
- zeroconf = Zeroconf()
65
- ServiceBrowser(zeroconf, SERVICE, Listener())
66
- return zeroconf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
  def main():
69
- logger.info("🚀 بدء نظام اكتشاف الأقران...")
70
-
71
- # تسجيل الخدمة المحلية
72
- zeroconf = Zeroconf()
73
- info = ServiceInfo(
74
- type_=SERVICE,
75
- name=f"{socket.gethostname()}.{SERVICE}",
76
- addresses=[socket.inet_aton(get_local_ip())],
77
- port=int(PORT),
78
- properties={b'version': b'1.0'},
79
- server=f"{socket.gethostname()}.local."
80
- )
81
- zeroconf.register_service(info)
82
-
83
- # بدء اكتشاف الأقران
84
- discover_lan_peers()
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
  try:
87
  while True:
88
- logger.info(f"عدد الأقران المكتشفين: {len(PEERS)}")
89
- time.sleep(10)
 
 
 
 
 
 
 
90
  except KeyboardInterrupt:
91
  logger.info("🛑 إيقاف النظام...")
92
  finally:
93
- zeroconf.unregister_service(info)
94
- zeroconf.close()
 
 
 
95
 
96
  if __name__ == "__main__":
97
  main()
 
21
  if current_port > 9999:
22
  current_port = 1000
23
  return port
24
+ PORT = current_port
25
+ with open("offload_port.txt", "w") as f:
26
+ f.write(str(current_port))
27
 
28
+ PORT = int(os.getenv("CPU_PORT", get_sequential_port()))
29
  SERVICE = "_tasknode._tcp.local."
30
  PEERS = set()
31
  PEERS_INFO = {}
32
 
33
  CENTRAL_REGISTRY_SERVERS = [
34
+ "http://cv5303201.regru.cloud",
35
+ "https://amaloffload.onrender.com",
36
  "https://osamabyc86-offload.hf.space",
37
  "https://osamabyc19866-omsd.hf.space",
38
  "https://52.13.128.108",
39
+ "https://176.28.156.149",
40
+ "https://44.229.227.142",
41
+ "https://osamabyc86-amalcoreflow.hf.space"
42
  ]
43
 
44
  def get_local_ip():
 
55
  peer_url = f"http://{ip}:{port}/run"
56
  if peer_url not in PEERS:
57
  PEERS.add(peer_url)
58
+ logger.info(f"تم تسجيل قرين جديد: {peer_url}")
59
+
60
+ def register_with_central_servers():
61
+ """تسجيل هذه العقدة على الخوادم المركزية"""
62
+ local_ip = get_local_ip()
63
+ node_info = {
64
+ "node_id": socket.gethostname(),
65
+ "ip": local_ip,
66
+ "port": PORT,
67
+ "hostname": socket.gethostname(),
68
+ "timestamp": time.time(),
69
+ "capabilities": ["cpu", "gpu", "storage"]
70
+ }
71
+
72
+ successful_registrations = 0
73
+
74
+ for server in CENTRAL_REGISTRY_SERVERS:
75
+ for endpoint in ["/register", "/api/register", "/nodes/register", "/peer/register"]:
76
+ try:
77
+ url = f"{server.rstrip('/')}{endpoint}"
78
+ logger.info(f"🔗 محاولة التسجيل في: {url}")
79
+
80
+ response = requests.post(
81
+ url,
82
+ json=node_info,
83
+ timeout=10,
84
+ headers={"Content-Type": "application/json"}
85
+ )
86
+
87
+ if response.status_code == 200:
88
+ data = response.json()
89
+ successful_registrations += 1
90
+ logger.info(f"✅ تم التسجيل بنجاح في: {server}")
91
+
92
+ # إضافة الأقران من الاستجابة
93
+ if isinstance(data, dict) and "peers" in data:
94
+ for peer in data["peers"]:
95
+ if isinstance(peer, str) and peer.startswith("http"):
96
+ PEERS.add(peer)
97
+ logger.info(f"👥 تمت إضافة قرين من السجل: {peer}")
98
+
99
+ break # توقف عن تجربة endpoints أخرى لهذا الخادم
100
+
101
+ elif response.status_code == 404:
102
+ continue # جرب endpoint التالي
103
+ else:
104
+ logger.warning(f"⚠️ استجابة غير متوقعة من {server}: {response.status_code}")
105
+
106
+ except requests.exceptions.Timeout:
107
+ logger.warning(f"⏰ انتهت المهلة مع {server}")
108
+ continue
109
+ except requests.exceptions.ConnectionError:
110
+ logger.warning(f"🔌 تعذر الاتصال بـ {server}")
111
+ continue
112
+ except Exception as e:
113
+ logger.warning(f"❌ خطأ مع {server}: {str(e)}")
114
+ continue
115
+
116
+ logger.info(f"📊 إجمالي التسجيلات الناجحة: {successful_registrations}/{len(CENTRAL_REGISTRY_SERVERS)}")
117
+ return successful_registrations > 0
118
 
119
  def discover_lan_peers():
120
+ """اكتشاف الأقران على الشبكة المحلية باستخدام Zeroconf"""
121
+ class PeerListener:
122
  def add_service(self, zc, type_, name):
123
  info = zc.get_service_info(type_, name)
124
+ if info and info.addresses:
125
  ip = socket.inet_ntoa(info.addresses[0])
126
+ port = info.port
127
+ register_peer(ip, port)
128
+ logger.info(f"🔍 تم اكتشاف قرين محلي: {ip}:{port}")
129
+
130
+ try:
131
+ zeroconf = Zeroconf()
132
+ ServiceBrowser(zeroconf, SERVICE, PeerListener())
133
+ return zeroconf
134
+ except Exception as e:
135
+ logger.error(f"❌ فشل في بدء اكتشاف LAN: {e}")
136
+ return None
137
 
138
+ def start_periodic_registration(interval=60):
139
+ """بدء التسجيل الدوري مع الخوادم المركزية"""
140
+ def registration_loop():
141
+ while True:
142
+ try:
143
+ logger.info("🔄 بدء التسجيل الدوري مع الخوادم المركزية...")
144
+ register_with_central_servers()
145
+ logger.info(f"📈 عدد الأقران الحالي: {len(PEERS)}")
146
+ time.sleep(interval)
147
+ except Exception as e:
148
+ logger.error(f"💥 خطأ في التسجيل الدوري: {e}")
149
+ time.sleep(interval)
150
+
151
+ thread = threading.Thread(target=registration_loop, daemon=True)
152
+ thread.start()
153
+ return thread
154
+
155
+ def start_periodic_discovery(interval=30):
156
+ """اكتشاف دوري للأقران المحليين"""
157
+ def discovery_loop():
158
+ while True:
159
+ try:
160
+ # فحص نطاق IPs المحلي لاكتشاف الأقران
161
+ local_ip = get_local_ip()
162
+ base_ip = ".".join(local_ip.split(".")[:-1])
163
+
164
+ for i in range(1, 50):
165
+ if i == int(local_ip.split(".")[-1]):
166
+ continue # تخطي الذات
167
+
168
+ ip = f"{base_ip}.{i}"
169
+ for port in [PORT, 8888, 8000, 5000, 3000]:
170
+ peer_url = f"http://{ip}:{port}"
171
+ if check_peer_availability(peer_url):
172
+ register_peer(ip, port)
173
+
174
+ time.sleep(interval)
175
+ except Exception as e:
176
+ logger.error(f"💥 خطأ في الاكتشاف الدوري: {e}")
177
+ time.sleep(interval)
178
+
179
+ thread = threading.Thread(target=discovery_loop, daemon=True)
180
+ thread.start()
181
+ return thread
182
+
183
+ def check_peer_availability(peer_url):
184
+ """فحص توفر القرين"""
185
+ try:
186
+ response = requests.get(f"{peer_url}/status", timeout=3)
187
+ return response.status_code == 200
188
+ except:
189
+ return False
190
+
191
+ def get_peers():
192
+ """الحصول على قائمة الأقران المتاحة"""
193
+ return list(PEERS)
194
 
195
  def main():
196
+ logger.info("🚀 بدء نظام اكتشاف الأقران المتقدم...")
197
+ logger.info(f"🌐 العقدة: {socket.gethostname()} - {get_local_ip()}:{PORT}")
198
+
199
+ # تسجيل الخدمة المحلية باستخدام Zeroconf
200
+ try:
201
+ zeroconf = Zeroconf()
202
+ info = ServiceInfo(
203
+ type_=SERVICE,
204
+ name=f"{socket.gethostname()}.{SERVICE}",
205
+ addresses=[socket.inet_aton(get_local_ip())],
206
+ port=PORT,
207
+ properties={b'version': b'1.0', b'hostname': socket.gethostname().encode()},
208
+ server=f"{socket.gethostname()}.local."
209
+ )
210
+ zeroconf.register_service(info)
211
+ logger.info("✅ تم تسجيل الخدمة المحلية باستخدام Zeroconf")
212
+ except Exception as e:
213
+ logger.error(f"❌ فشل في تسجيل Zeroconf: {e}")
214
+ zeroconf = None
215
+
216
+ # بدء الاكتشاف المحلي
217
+ lan_zeroconf = discover_lan_peers()
218
+
219
+ # بدء التسجيل الدوري مع الخوادم المركزية
220
+ registration_thread = start_periodic_registration(interval=60)
221
+
222
+ # بدء الاكتشاف الدوري
223
+ discovery_thread = start_periodic_discovery(interval=45)
224
 
225
  try:
226
  while True:
227
+ logger.info(f"📊 إحصائيات - الأقران: {len(PEERS)}")
228
+ if PEERS:
229
+ logger.info("📋 قائمة الأقران المتاحة:")
230
+ for i, peer in enumerate(list(PEERS)[:5]): # عرض أول 5 فقط
231
+ logger.info(f" {i+1}. {peer}")
232
+ if len(PEERS) > 5:
233
+ logger.info(f" ... و{len(PEERS) - 5} آخرين")
234
+
235
+ time.sleep(30)
236
  except KeyboardInterrupt:
237
  logger.info("🛑 إيقاف النظام...")
238
  finally:
239
+ if zeroconf:
240
+ zeroconf.unregister_service(info)
241
+ zeroconf.close()
242
+ if lan_zeroconf:
243
+ lan_zeroconf.close()
244
 
245
  if __name__ == "__main__":
246
  main()
processor_manager.py CHANGED
@@ -11,7 +11,7 @@ class ResourceMonitor:
11
  self.cpu_history = deque(maxlen=10)
12
  self.mem_history = deque(maxlen=10)
13
  # حد استقبال المهمات الآن 40% CPU بدل 30%
14
- self.receive_cpu_threshold = 0.40
15
 
16
  def current_load(self):
17
  cpu = psutil.cpu_percent(interval=0.5) / 100.0 # كنسبة (0.0 - 1.0)
@@ -47,7 +47,7 @@ def should_offload(task_complexity=0):
47
  avg_cpu = status['average']['cpu']
48
  avg_mem = status['average']['mem']
49
 
50
- if avg_cpu > 0.6 or avg_mem < 2048 or task_complexity > 75:
51
  trigger_offload()
52
  return True
53
 
 
11
  self.cpu_history = deque(maxlen=10)
12
  self.mem_history = deque(maxlen=10)
13
  # حد استقبال المهمات الآن 40% CPU بدل 30%
14
+ self.receive_cpu_threshold = 0.30
15
 
16
  def current_load(self):
17
  cpu = psutil.cpu_percent(interval=0.5) / 100.0 # كنسبة (0.0 - 1.0)
 
47
  avg_cpu = status['average']['cpu']
48
  avg_mem = status['average']['mem']
49
 
50
+ if avg_cpu > 0.7 or avg_mem < 2048 or task_complexity > 75:
51
  trigger_offload()
52
  return True
53
 
quick_connection_test.py CHANGED
@@ -1,49 +1,206 @@
1
-
2
  #!/usr/bin/env python3
3
  # اختبار سريع للتحقق من حالة النظام
4
 
5
  import requests
6
  import time
7
  import json
8
- from offload_lib import discover_peers, matrix_multiply
 
9
 
10
- def test_connection():
11
- """اختبار سريع للاتصال"""
12
- print("🚀 اختبار اتصال سريع...")
13
-
14
- # 1. فحص الخادم المحلي
 
 
 
 
 
15
  try:
16
- response = requests.get("http://localhost:PORT/health", timeout=3)
17
- if response.status_code == 200:
18
- print("✅ الخادم المحلي يعمل")
19
- else:
20
- print("❌ مشكلة في الخادم المحلي")
21
- return False
22
  except:
23
- print("❌ الخادم المحلي غير متاح")
24
- return False
 
 
 
 
25
 
26
- # 2. اختبار اكتشاف الأجهزة
27
- print("🔍 البحث عن الأجهزة...")
28
- peers = discover_peers(timeout=2)
29
- print(f"📱 تم اكتشاف {len(peers)} جهاز")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
- # 3. اختبار مهمة بسيطة
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  print("⚙️ اختبار مهمة بسيطة...")
33
- start_time = time.time()
34
  try:
35
- result = matrix_multiply(5)
 
 
 
 
36
  duration = time.time() - start_time
 
 
 
 
 
37
  print(f"✅ تمت المعالجة في {duration:.2f} ثانية")
38
- print(f"📊 النتيجة: مصفوفة {len(result)}x{len(result[0])}")
 
39
  return True
 
40
  except Exception as e:
41
  print(f"❌ فشل في المعالجة: {e}")
42
  return False
43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  if __name__ == "__main__":
45
- if test_connection():
46
- print("\n🎉 النظام يعمل بشكل جيد!")
47
- print("💡 يمكنك الآن تشغيل: python test_distributed_system.py")
 
 
 
48
  else:
49
- print("\n⚠️ هناك مشاكل تحتاج إصلاح")
 
 
 
 
 
1
  #!/usr/bin/env python3
2
  # اختبار سريع للتحقق من حالة النظام
3
 
4
  import requests
5
  import time
6
  import json
7
+ import socket
8
+ import os
9
 
10
+ # استيراد الملفات المحلية
11
+ try:
12
+ from peer_discovery import PORT, get_local_ip, get_peers
13
+ from load_balancer import send_task
14
+ from smart_tasks import prime_calculation
15
+ except ImportError:
16
+ print("⚠️ بعض الوحدات غير متوفرة، جرب تشغيل: python launcher.py --tray")
17
+
18
+ def get_available_port():
19
+ """الحصول على منفذ متاح"""
20
  try:
21
+ from peer_discovery import PORT
22
+ return PORT
 
 
 
 
23
  except:
24
+ return 8888 # منفذ افتراضي
25
+
26
+ def test_local_server():
27
+ """فحص الخادم المحلي"""
28
+ port = get_available_port()
29
+ endpoints = ["/health", "/status", "/", "/api/status"]
30
 
31
+ for endpoint in endpoints:
32
+ try:
33
+ url = f"http://localhost:{port}{endpoint}"
34
+ response = requests.get(url, timeout=5)
35
+ if response.status_code == 200:
36
+ print(f"✅ الخادم المحلي يعمل على: {url}")
37
+ try:
38
+ data = response.json()
39
+ print(f"📊 حالة النظام: {data}")
40
+ except:
41
+ print("📄 استجابة نصية")
42
+ return True
43
+ except requests.exceptions.ConnectionError:
44
+ continue
45
+ except requests.exceptions.Timeout:
46
+ print(f"⏰ انتهت المهلة مع: localhost:{port}{endpoint}")
47
+ continue
48
+ except Exception as e:
49
+ print(f"❌ خطأ مع localhost:{port}{endpoint}: {e}")
50
+ continue
51
 
52
+ print("❌ الخادم المحلي غير متاح")
53
+ return False
54
+
55
+ def test_peer_discovery():
56
+ """اختبار اكتشاف الأقران"""
57
+ print("🔍 اختبار اكتشاف الأقران...")
58
+ try:
59
+ # محاولة استيراد واستخدام peer_discovery
60
+ from peer_discovery import PEERS, get_peers
61
+
62
+ local_ip = get_local_ip()
63
+ print(f"🌐 IP المحلي: {local_ip}")
64
+ print(f"🔌 المنفذ: {get_available_port()}")
65
+
66
+ peers = list(PEERS) if PEERS else []
67
+ if hasattr(get_peers, '__call__'):
68
+ additional_peers = get_peers()
69
+ if additional_peers:
70
+ peers.extend(additional_peers)
71
+
72
+ print(f"📱 تم اكتشاف {len(peers)} جهاز")
73
+ for i, peer in enumerate(peers[:5]): # عرض أول 5 أقران فقط
74
+ print(f" {i+1}. {peer}")
75
+
76
+ return len(peers) > 0
77
+ except Exception as e:
78
+ print(f"⚠️ تعذر اكتشاف الأقران: {e}")
79
+ return False
80
+
81
+ def test_basic_task():
82
+ """اختبار مهمة بسيطة"""
83
  print("⚙️ اختبار مهمة بسيطة...")
 
84
  try:
85
+ start_time = time.time()
86
+
87
+ # استخدام prime_calculation مباشرة
88
+ result = prime_calculation(1000) # عدد أصغر للاختبار السريع
89
+
90
  duration = time.time() - start_time
91
+
92
+ if "error" in result:
93
+ print(f"❌ فشل في المعالجة: {result['error']}")
94
+ return False
95
+
96
  print(f"✅ تمت المعالجة في {duration:.2f} ثانية")
97
+ print(f"📊 النتيجة: {result.get('count', 0)} أعداد أولية")
98
+ print(f"🔢 أول 5 أعداد: {result.get('primes', [])[:5]}")
99
  return True
100
+
101
  except Exception as e:
102
  print(f"❌ فشل في المعالجة: {e}")
103
  return False
104
 
105
+ def test_load_balancer():
106
+ """اختبار موزع الحمل"""
107
+ print("⚖️ اختبار موزع الحمل...")
108
+ try:
109
+ from load_balancer import send_task
110
+
111
+ result = send_task("prime_calculation", 500)
112
+
113
+ if "error" in result:
114
+ print(f"⚠️ مو��ع الحمل: {result['error']}")
115
+ return False
116
+ else:
117
+ print(f"✅ موزع الحمل يعمل - {result.get('count', 0)} أعداد أولية")
118
+ return True
119
+ except Exception as e:
120
+ print(f"⚠️ تعذر اختبار موزع الحمل: {e}")
121
+ return False
122
+
123
+ def test_network_connectivity():
124
+ """اختبار اتصال الشبكة"""
125
+ print("🌐 اختبار اتصال الشبكة...")
126
+
127
+ # فحص الاتصال بالإنترنت
128
+ try:
129
+ response = requests.get("http://www.google.com", timeout=5)
130
+ print("✅ الاتصال بالإنترنت متاح")
131
+ internet = True
132
+ except:
133
+ print("❌ لا يوجد اتصال بالإنترنت")
134
+ internet = False
135
+
136
+ # فحص الشبكة المحلية
137
+ try:
138
+ local_ip = socket.gethostbyname(socket.gethostname())
139
+ print(f"🏠 الشبكة المحلية: {local_ip}")
140
+ local_network = True
141
+ except:
142
+ print("❌ مشكلة في الشبكة المحلية")
143
+ local_network = False
144
+
145
+ return internet or local_network
146
+
147
+ def test_connection():
148
+ """اختبار سريع للاتصال"""
149
+ print("=" * 50)
150
+ print("🚀 اختبار اتصال سريع لنظام AmalOffload")
151
+ print("=" * 50)
152
+
153
+ tests = [
154
+ ("🌐 اتصال الشبكة", test_network_connectivity),
155
+ ("🖥️ الخادم المحلي", test_local_server),
156
+ ("🔍 اكتشاف الأقران", test_peer_discovery),
157
+ ("⚙️ المهمة الأساسية", test_basic_task),
158
+ ("⚖️ موزع الحمل", test_load_balancer),
159
+ ]
160
+
161
+ results = []
162
+
163
+ for test_name, test_func in tests:
164
+ print(f"\n{test_name}...")
165
+ try:
166
+ result = test_func()
167
+ results.append(result)
168
+ time.sleep(1) # فاصل بين الاختبارات
169
+ except Exception as e:
170
+ print(f"💥 خطأ غير متوقع: {e}")
171
+ results.append(False)
172
+
173
+ print("\n" + "=" * 50)
174
+ print("📊 نتائج الاختبار:")
175
+ print("=" * 50)
176
+
177
+ success_count = sum(results)
178
+ total_tests = len(results)
179
+
180
+ for i, (test_name, _) in enumerate(tests):
181
+ status = "✅" if results[i] else "❌"
182
+ print(f"{status} {test_name}")
183
+
184
+ print(f"\n🎯 النتيجة النهائية: {success_count}/{total_tests}")
185
+
186
+ if success_count == total_tests:
187
+ print("🎉 النظام يعمل بشكل ممتاز!")
188
+ elif success_count >= 3:
189
+ print("👍 النظام يعمل بشكل جيد")
190
+ else:
191
+ print("⚠️ هناك مشاكل تحتاج إصلاح")
192
+
193
+ return success_count >= 3 # يعتبر ناجحاً إذا نجح 3/5 اختبارات على الأقل
194
+
195
  if __name__ == "__main__":
196
+ success = test_connection()
197
+
198
+ if success:
199
+ print("\n💡 يمكنك الآن تشغيل:")
200
+ print(" python launcher.py --tray (لتشغيل النظام)")
201
+ print(" python test_distributed_system.py (لاختبار متقدم)")
202
  else:
203
+ print("\n🔧 اقتراحات الإصلاح:")
204
+ print(" 1. تأكد من تشغيل: python background_service.py start")
205
+ print(" 2. تحقق من إعدادات الجدار الناري")
206
+ print(" 3. جرب: python peer_discovery.py (لاكتشاف الأقران)")
ram_manager.py CHANGED
@@ -1,35 +1,39 @@
 
1
  """
2
- ram_manager.py – Distributed RAM Offload Agent
3
- ================================================
4
 
5
  ❖ الغرض
6
- ------------
7
- يوفِّر هذا الملف إضافة مستقلة إلى مشروع **AmalOffload** من أجل مشاركة الذاكرة (RAM) بين جميع العُقد التي تشغِّل المشروع. عندما ينخفض مقدار الذاكرة الحرة على إحدى العُقد إلى أقل من 2 جيجابايت (أو أي قيمة تحدّدها)، تُنقل كتل بيانات إلى عُقد أخرى ما تزال تملك ذاكرة حرّة.
8
-
9
- المزايا الرئيسة
10
- ------------------
11
- * يراقب استهلاك الذاكرة محليًّا بانتظام.
12
- * يعلن عن نفسه ويكتشف الأقران (Peers) تلقائيًّا بالاعتماد على `peer_registry` إن وُجد، أو على قائمة في المتغيّر البيئي `CENTRAL_PEERS` كحلّ احتياطي.
13
- * يعرض واجهة HTTP بسيطة (`Flask`) بثلاث مسارات:
14
- * `GET /ram_status` → يُرجِع مقدار الذاكرة الحرّة بالعُقدة.
15
- * `POST /ram_store` → يستقبل كتلة بيانات (Base64) ويحجزها في الذاكرة.
16
- * `GET /ram_fetch/<id>` يُرجِع كتلة بيانات مُخزّنة بحسب معرّفها.
17
-
18
- طريقة التشغيل
19
- ----------------
20
- ```bash
21
- python ram_manager.py --ram-limit 2048 --chunk 64 --interval 5
22
- ```
23
- أو اكتفِ بالتشغيل بدون وسائط واعتمد على القيم الافتراضيّة أو على متغيّرات البيئة:
24
- ```bash
 
 
25
  export RAM_THRESHOLD_MB=2048
26
  export RAM_CHUNK_MB=64
 
 
27
  python ram_manager.py
28
- ```
29
-
30
- ❖ التعليمات البرمجيّة
31
- ----------------------
32
  """
 
 
 
33
  import os
34
  import psutil
35
  import time
@@ -37,125 +41,325 @@ import threading
37
  import socket
38
  import base64
39
  import uuid
40
- from typing import Dict, List
 
 
41
 
42
  try:
43
  from flask import Flask, request, jsonify
44
  except ImportError as exc:
45
  raise RuntimeError("Flask غير مُثبّت. نفِّذ: pip install flask") from exc
46
 
47
- # محاولة استيراد مُسجِّل الأقران الحالي من المشروع
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  try:
49
- import peer_registry # يتوقع أن يحتوي على list_peers()
50
- except ImportError:
51
- peer_registry = None
 
 
52
 
53
- # الإعدادات قابلة ��لتعديل عبر متغيّرات البيئة
54
- RAM_LIMIT_MB = int(os.getenv("RAM_THRESHOLD_MB", "2048")) # الحد الأدنى للرام الحرّة قبل الت offload
55
- CHUNK_MB = int(os.getenv("RAM_CHUNK_MB", "64")) # حجم الكتلة المُرسَلة
56
- CHECK_INTERVAL = int(os.getenv("RAM_CHECK_INTERVAL", "5")) # ثواني بين كل فحص
57
- RAM_PORT = int(os.getenv("RAM_PORT", "8765")) # بورت واجهة الذاكرة
58
 
59
  app = Flask(__name__)
60
 
61
- # مخزن الكتل الواردة
62
- remote_chunks: Dict[str, bytes] = {}
 
 
 
63
 
64
  def get_free_ram_mb() -> int:
65
- """الذاكرة الحرّة بالميغابايت"""
66
- return psutil.virtual_memory().available // (1024 * 1024)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
- # ─────────────────── واجهة HTTP ────────────────────
69
- @app.route("/ram_status", methods=["GET"])
 
70
  def ram_status():
71
- """إرجاع كميّة الذاكرة الحرّة بالعُقدة."""
72
- return jsonify({"free_mb": get_free_ram_mb()})
73
 
74
- @app.route("/ram_store", methods=["POST"])
75
  def ram_store():
76
- """تلقّي كتلة بيانات وتخزينها."""
77
- payload = request.get_json(force=True)
78
- cid = payload["id"]
79
- blob_b64= payload["data"]
80
- remote_chunks[cid] = base64.b64decode(blob_b64)
81
- return jsonify({"status": "stored", "id": cid})
82
-
83
- @app.route("/ram_fetch/<cid>", methods=["GET"])
84
- def ram_fetch(cid):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  blob = remote_chunks.get(cid)
86
  if blob is None:
87
- return jsonify({"error": "not found"}), 404
88
- return jsonify({"id": cid, "data": base64.b64encode(blob).decode()})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
  # ─────────────────── وظائف داخليّة ───────────────────
91
 
92
  def start_api():
93
- """تشغيل خادم Flask في خيط منفصل."""
94
- from werkzeug.serving import make_server
95
- server = make_server("0.0.0.0", RAM_PORT, app)
96
- server.serve_forever()
 
 
 
 
 
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
  def discover_peers() -> List[str]:
100
- """الحصول على قائمة IPs للأقران، باستثناء عنوان الجهاز الحالي."""
101
- peers: List[str] = []
102
 
103
- if peer_registry and hasattr(peer_registry, "list_peers"):
 
104
  try:
105
- peers_info = peer_registry.list_peers() # متوقع: [{"ip": "..."}, ...]
106
- peers = [p["ip"] for p in peers_info if p.get("ip")]
107
- except Exception:
108
- pass
109
- else:
110
- central_env = os.getenv("CENTRAL_PEERS", "")
111
- if central_env:
112
- peers = central_env.split(",")
113
-
114
- # إزالة عنوان الجهاز المحلي
115
- try:
116
- local_ip = socket.gethostbyname(socket.gethostname())
117
- peers = [ip for ip in peers if ip != local_ip]
118
- except Exception:
119
- pass
 
 
 
 
 
 
 
 
 
 
 
120
 
121
- return peers
 
 
122
 
 
 
123
 
124
  def offload_chunk(blob: bytes, peer_ip: str) -> bool:
125
- """إرسال كتلة بيانات إلى peer محدّد."""
126
- import requests
127
  try:
128
- resp = requests.post(
 
 
129
  f"http://{peer_ip}:{RAM_PORT}/ram_store",
130
- json={"id": str(uuid.uuid4()), "data": base64.b64encode(blob).decode()},
131
- timeout=5,
132
  )
133
- return resp.status_code == 200
134
- except Exception:
 
 
 
 
 
135
  return False
136
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
  def monitor_loop():
139
  """مراقبة الذاكرة واستدعاء offload عند الحاجة."""
 
 
 
 
140
  while True:
141
- free_mb = get_free_ram_mb()
142
- if free_mb < RAM_LIMIT_MB:
143
- peers = discover_peers()
144
- if not peers:
145
- print("[RAM] لا يوجد أقران متاحون حاليًا.")
146
- else:
147
- blob = bytes(CHUNK_MB * 1024 * 1024) # كتلة وهميّة – استبدلها ببيانات حقيقيّة عند الدمج
148
- for ip in peers:
149
- if offload_chunk(blob, ip):
150
- print(f"[RAM]‎ أرسلت ‎{CHUNK_MB}‎MB إلى ‎{ip}")
151
- break
 
 
 
152
  else:
153
- print("[RAM] كل الأقران رفضوا التخزين أو حدث خطأ.")
154
- time.sleep(CHECK_INTERVAL)
 
 
 
 
 
 
155
 
 
 
 
 
156
 
157
  def main():
158
- threading.Thread(target=start_api, daemon=True).start()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  monitor_loop()
160
 
161
  if __name__ == "__main__":
 
1
+ # -*- coding: utf-8 -*-
2
  """
3
+ ram_manager.py – Distributed RAM Offload Agent (fixed)
4
+ =====================================================
5
 
6
  ❖ الغرض
7
+ --------
8
+ يوفِّر هذا الملف إضافة مستقلة إلى مشروع **AmalOffload** من أجل مشاركة الذاكرة (RAM)
9
+ بين جميع العُقد التي تشغِّل المشروع. عندما ينخفض مقدار الذاكرة الحرة على إحدى
10
+ العُقد إلى أقل من حدّ معيّن، تُنقل كتل بيانات إلى عُقد أخرى تملك ذاكرة حرّة.
11
+
12
+ المزايا
13
+ ---------
14
+ * مراقبة استهلاك الذاكرة محليًّا.
15
+ * إعلان واكتشاف الأقران (Peers) إن توفّرت وحدات المشروع.
16
+ * واجهة HTTP بسيطة عبر Flask:
17
+ - GET /ram_status حالة الذاكرة.
18
+ - POST /ram_store → استلام كتلة بيانات (Base64) وتخزينها في الذاكرة.
19
+ - GET /ram_fetch/<id> → إرجاع كتلة بيانات محفوظة.
20
+ - GET /ram_info → معلومات عامة عن التخزين.
21
+ - GET /health → فحص الصحة.
22
+
23
+ طريقة التشغيل (أمثلة):
24
+ ---------------------
25
+ python ram_manager.py --ram-limit 2048 --chunk 64 --interval 5 --port 8765
26
+
27
+ أو باستخدام متغيّرات البيئة:
28
  export RAM_THRESHOLD_MB=2048
29
  export RAM_CHUNK_MB=64
30
+ export RAM_CHECK_INTERVAL=10
31
+ export RAM_PORT=8765
32
  python ram_manager.py
 
 
 
 
33
  """
34
+
35
+ from __future__ import annotations
36
+
37
  import os
38
  import psutil
39
  import time
 
41
  import socket
42
  import base64
43
  import uuid
44
+ import argparse
45
+ import logging
46
+ from typing import Dict, List, Optional
47
 
48
  try:
49
  from flask import Flask, request, jsonify
50
  except ImportError as exc:
51
  raise RuntimeError("Flask غير مُثبّت. نفِّذ: pip install flask") from exc
52
 
53
+ # إعداد السجلات
54
+ logging.basicConfig(
55
+ level=logging.INFO,
56
+ format='%(asctime)s - %(levelname)s - %(message)s',
57
+ datefmt='%H:%M:%S'
58
+ )
59
+ logger = logging.getLogger(__name__)
60
+
61
+ # استيراد نظام الاكتشاف من المشروع (اختياري)
62
+ DISCOVERY_AVAILABLE = False
63
+ try:
64
+ from peer_discovery import PEERS, get_local_ip, get_peers # type: ignore
65
+ DISCOVERY_AVAILABLE = True
66
+ logger.info("✅ تم تحميل نظام الاكتشاف الموزع (peer_discovery)")
67
+ except Exception:
68
+ logger.warning("⚠️ نظام peer_discovery غير متوفر، سيتم استخدام بدائل بسيطة")
69
+
70
+ # استيراد peer_registry (Zeroconf) إن وجد
71
+ PEER_REGISTRY_AVAILABLE = False
72
  try:
73
+ from peer_registry import discover_peers as discover_peers_zeroconf # type: ignore
74
+ PEER_REGISTRY_AVAILABLE = True
75
+ logger.info("✅ تم تحميل نظام تسجيل الأقران (peer_registry)")
76
+ except Exception:
77
+ logger.warning("⚠️ نظام peer_registry غير متوفر")
78
 
79
+ # الإعدادات الافتراضية (يمكن تعديلها عبر متغيّرات البيئة أو وسيطات سطر الأوامر)
80
+ RAM_LIMIT_MB = int(os.getenv("RAM_THRESHOLD_MB", "2048")) # حدّ الذاكرة الحرة قبل التفريغ
81
+ CHUNK_MB = int(os.getenv("RAM_CHUNK_MB", "64")) # حجم الكتلة المُرسلة
82
+ CHECK_INTERVAL = int(os.getenv("RAM_CHECK_INTERVAL", "10")) # فترة الفحص بالثواني
83
+ RAM_PORT = int(os.getenv("RAM_PORT", "8765")) # منفذ واجهة خدمة الذاكرة
84
 
85
  app = Flask(__name__)
86
 
87
+ # مخازن الذاكرة
88
+ remote_chunks: Dict[str, bytes] = {} # كتل وصلت من أقران أخرى
89
+ local_chunks: Dict[str, bytes] = {} # تخزين محلي إن أردت استخدامه لاحقًا
90
+
91
+ # ─────────────────── وظائف مساعدة للذاكرة ───────────────────
92
 
93
  def get_free_ram_mb() -> int:
94
+ """إرجاع الذاكرة الحرّة بالميغابايت."""
95
+ return int(psutil.virtual_memory().available // (1024 * 1024))
96
+
97
+ def get_ram_usage_percent() -> float:
98
+ """إرجاع نسبة استخدام الذاكرة."""
99
+ return float(psutil.virtual_memory().percent)
100
+
101
+ def get_system_info() -> Dict[str, int | float]:
102
+ """الحصول على معلومات النظام الحالية الخاصة بالذاكرة."""
103
+ memory = psutil.virtual_memory()
104
+ return {
105
+ "total_mb": int(memory.total // (1024 * 1024)),
106
+ "available_mb": int(memory.available // (1024 * 1024)),
107
+ "used_mb": int(memory.used // (1024 * 1024)),
108
+ "usage_percent": float(memory.percent),
109
+ "threshold_mb": int(RAM_LIMIT_MB),
110
+ }
111
 
112
+ # ─────────────────── واجهة HTTP ───────────────────
113
+
114
+ @app.get("/ram_status")
115
  def ram_status():
116
+ """إرجاع كميّة الذاكرة الحرّة ومعلومات تفصيلية."""
117
+ return jsonify(get_system_info())
118
 
119
+ @app.post("/ram_store")
120
  def ram_store():
121
+ """تلقّي كتلة بيانات وتخزينها في الذاكرة (remote_chunks)."""
122
+ try:
123
+ payload = request.get_json(force=True, silent=False)
124
+ if not payload or not isinstance(payload, dict):
125
+ return jsonify({"error": "بيانات غير صالحة"}), 400
126
+
127
+ cid: Optional[str] = payload.get("id")
128
+ blob_b64: Optional[str] = payload.get("data")
129
+
130
+ if not cid or not blob_b64:
131
+ return jsonify({"error": "المعرّف أو البيانات مفقودة"}), 400
132
+
133
+ # فحص المساحة المتاحة قبل التخزين (شرط بسيط احترازي)
134
+ if get_free_ram_mb() < max(64, RAM_LIMIT_MB // 2):
135
+ return jsonify({"error": "مساحة ذاكرة غير كافية"}), 507
136
+
137
+ remote_chunks[cid] = base64.b64decode(blob_b64.encode())
138
+ logger.info("✅ تم تخزين كتلة بيانات: %s (%d بايت)", cid, len(remote_chunks[cid]))
139
+ return jsonify({"status": "stored", "id": cid, "size_bytes": len(remote_chunks[cid])})
140
+ except Exception as e:
141
+ logger.exception("❌ خطأ في التخزين")
142
+ return jsonify({"error": str(e)}), 500
143
+
144
+ @app.get("/ram_fetch/<cid>")
145
+ def ram_fetch(cid: str):
146
+ """جلب كتلة بيانات مخزنة عبر معرّفها."""
147
  blob = remote_chunks.get(cid)
148
  if blob is None:
149
+ return jsonify({"error": "الكتلة غير موجودة"}), 404
150
+
151
+ return jsonify({
152
+ "id": cid,
153
+ "data": base64.b64encode(blob).decode(),
154
+ "size_bytes": len(blob),
155
+ })
156
+
157
+ @app.get("/ram_info")
158
+ def ram_info():
159
+ """معلومات عامة عن الكتل المخزنة محليًا وبعيدًا."""
160
+ return jsonify({
161
+ "local_chunks": len(local_chunks),
162
+ "remote_chunks": len(remote_chunks),
163
+ "local_size_mb": sum(len(b) for b in local_chunks.values()) // (1024 * 1024),
164
+ "remote_size_mb": sum(len(b) for b in remote_chunks.values()) // (1024 * 1024),
165
+ })
166
+
167
+ @app.get("/health")
168
+ def health():
169
+ """فحص صحة الخدمة."""
170
+ return jsonify({
171
+ "status": "healthy",
172
+ "service": "ram_manager",
173
+ "port": RAM_PORT,
174
+ "free_ram_mb": get_free_ram_mb(),
175
+ })
176
 
177
  # ─────────────────── وظائف داخليّة ───────────────────
178
 
179
  def start_api():
180
+ """تشغيل خادم Flask في خيط منفصل باستخدام Werkzeug server."""
181
+ try:
182
+ from werkzeug.serving import make_server
183
+ host = "0.0.0.0"
184
+ server = make_server(host, RAM_PORT, app)
185
+ logger.info("🚀 بدء خادم الذاكرة الموزعة على %s:%d", host, RAM_PORT)
186
+ server.serve_forever()
187
+ except Exception as e:
188
+ logger.error("❌ فشل في بدء الخادم: %s", e)
189
 
190
+ def check_peer_availability(ip: str) -> bool:
191
+ """فحص توفر قرين عبر endpoint /health."""
192
+ try:
193
+ import requests # محليًا لتفادي كونه تبعية صلبة إن لم تُستخدم
194
+ r = requests.get(f"http://{ip}:{RAM_PORT}/health", timeout=3)
195
+ return r.status_code == 200
196
+ except Exception:
197
+ return False
198
+
199
+ def _discover_peers_basic_scan(limit: int = 20) -> List[str]:
200
+ """اكتشاف بسيط بمسح الشبكة إن لم تتوفر آليات المشروع."""
201
+ try:
202
+ if DISCOVERY_AVAILABLE:
203
+ local_ip = get_local_ip()
204
+ else:
205
+ local_ip = socket.gethostbyname(socket.gethostname())
206
+ base_ip = ".".join(local_ip.split(".")[:-1])
207
+ local_last = int(local_ip.split(".")[-1])
208
+ except Exception:
209
+ return []
210
+
211
+ peers_ips: List[str] = []
212
+ for i in range(1, limit + 1):
213
+ if i == local_last:
214
+ continue
215
+ ip = f"{base_ip}.{i}"
216
+ if check_peer_availability(ip):
217
+ peers_ips.append(ip)
218
+ logger.info("🔍 اكتشف مسح شبكة: %s", ip)
219
+ return peers_ips
220
 
221
  def discover_peers() -> List[str]:
222
+ """الحصول على قائمة IPs للأقران المتاحين، بدون تكرار."""
223
+ peers_ips: List[str] = []
224
 
225
+ # 1) Zeroconf عبر peer_registry إن توفر
226
+ if PEER_REGISTRY_AVAILABLE:
227
  try:
228
+ peers_data = discover_peers_zeroconf(timeout=2) # type: ignore[arg-type]
229
+ for peer in peers_data:
230
+ if isinstance(peer, dict) and 'ip' in peer:
231
+ ip = str(peer['ip'])
232
+ if ip and ip not in peers_ips and check_peer_availability(ip):
233
+ peers_ips.append(ip)
234
+ logger.info("🔍 اكتشف zeroconf: %s", ip)
235
+ except Exception as e:
236
+ logger.warning("⚠️ خطأ في zeroconf: %s", e)
237
+
238
+ # 2) نظام الاكتشاف الداخلي للمشروع إن توفر
239
+ if DISCOVERY_AVAILABLE:
240
+ try:
241
+ plist = get_peers() if callable(get_peers) else [] # type: ignore[misc]
242
+ for p in plist:
243
+ if isinstance(p, str) and p.startswith("http://"):
244
+ # استخراج IP من URL
245
+ try:
246
+ ip = p.split("//", 1)[1].split(":", 1)[0]
247
+ except Exception:
248
+ continue
249
+ if ip and ip not in peers_ips and check_peer_availability(ip):
250
+ peers_ips.append(ip)
251
+ logger.info("🔍 اكتشف peer_discovery: %s", ip)
252
+ except Exception as e:
253
+ logger.warning("⚠️ خطأ في peer_discovery: %s", e)
254
 
255
+ # 3) بديل: مسح بسيط للشبكة
256
+ if not peers_ips:
257
+ peers_ips = _discover_peers_basic_scan(limit=20)
258
 
259
+ logger.info("📊 إجمالي الأقران المكتشفين: %d", len(peers_ips))
260
+ return peers_ips
261
 
262
  def offload_chunk(blob: bytes, peer_ip: str) -> bool:
263
+ """إرسال كتلة بيانات إلى قرين محدّد عبر /ram_store."""
 
264
  try:
265
+ import requests
266
+ chunk_id = str(uuid.uuid4())
267
+ r = requests.post(
268
  f"http://{peer_ip}:{RAM_PORT}/ram_store",
269
+ json={"id": chunk_id, "data": base64.b64encode(blob).decode()},
270
+ timeout=10
271
  )
272
+ if r.status_code == 200:
273
+ logger.info("✅ تم إرسال كتلة %dMB إلى %s", len(blob) // (1024 * 1024), peer_ip)
274
+ return True
275
+ logger.warning("⚠️ رفض التخزين من %s: %s", peer_ip, r.status_code)
276
+ return False
277
+ except Exception as e:
278
+ logger.warning("🔌 فشل الإرسال إلى %s: %s", peer_ip, e)
279
  return False
280
 
281
+ def create_sample_data(size_mb: int) -> bytes:
282
+ """إنشاء بيانات عشوائية للاختبار بعدد ميغابايتات محدّد."""
283
+ return os.urandom(size_mb * 1024 * 1024)
284
+
285
+ def register_with_peer_registry():
286
+ """تسجيل هذه العقدة في نظام Zeroconf (اختياري)."""
287
+ if not PEER_REGISTRY_AVAILABLE:
288
+ return
289
+ try:
290
+ from peer_registry import register_service # type: ignore
291
+ local_ip = get_local_ip() if DISCOVERY_AVAILABLE else socket.gethostbyname(socket.gethostname())
292
+ register_service(local_ip, RAM_PORT, load=0.0)
293
+ logger.info("✅ تم تسجيل العقدة في Zeroconf: %s:%d", local_ip, RAM_PORT)
294
+ except Exception as e:
295
+ logger.warning("⚠️ فشل التسجيل في Zeroconf: %s", e)
296
 
297
  def monitor_loop():
298
  """مراقبة الذاكرة واستدعاء offload عند الحاجة."""
299
+ logger.info("🔍 بدء مراقبة الذاكرة...")
300
+ # تسجيل العقدة إن أمكن
301
+ register_with_peer_registry()
302
+
303
  while True:
304
+ try:
305
+ free_mb = get_free_ram_mb()
306
+ usage_percent = get_ram_usage_percent()
307
+
308
+ # تسجيل الحالة كل ~30 ثانية (تقريبية بدون مؤقّت إضافي)
309
+ if int(time.time()) % 30 == 0:
310
+ logger.info("📊 حالة الذاكرة: %dMB حرّة (%.1f%% مستخدمة)", free_mb, usage_percent)
311
+
312
+ # إن كانت الذاكرة الحرة أقل من الحدّ، حاول التفريغ
313
+ if free_mb < RAM_LIMIT_MB:
314
+ logger.warning("🚨 الذاكرة منخفضة: %dMB (الحد: %dMB)", free_mb, RAM_LIMIT_MB)
315
+ peers = discover_peers()
316
+ if not peers:
317
+ logger.info("👥 لا يوجد أقران متاحون حاليًا.")
318
  else:
319
+ # ⚠️ في التطبيق الفعلي، ينبغي تفريغ بيانات حقيقية من التطبيق
320
+ blob = create_sample_data(CHUNK_MB)
321
+ for ip in peers:
322
+ if offload_chunk(blob, ip):
323
+ logger.info("📤 تم تفريغ %dMB إلى %s", CHUNK_MB, ip)
324
+ break
325
+ else:
326
+ logger.warning("❌ جميع الأقران رفضوا التخزين")
327
 
328
+ time.sleep(CHECK_INTERVAL)
329
+ except Exception as e:
330
+ logger.error("💥 خطأ في مراقبة الذاكرة: %s", e)
331
+ time.sleep(CHECK_INTERVAL)
332
 
333
  def main():
334
+ """الدالة الرئيسية لتشغيل الخدمة."""
335
+ global RAM_LIMIT_MB, CHUNK_MB, CHECK_INTERVAL, RAM_PORT
336
+
337
+ parser = argparse.ArgumentParser(description="مدير الذاكرة الموزعة")
338
+ parser.add_argument("--ram-limit", type=int, default=RAM_LIMIT_MB,
339
+ help="حد الذاكرة الحرّة بالميجابايت")
340
+ parser.add_argument("--chunk", type=int, default=CHUNK_MB,
341
+ help="حجم الكتلة بالميجابايت")
342
+ parser.add_argument("--interval", type=int, default=CHECK_INTERVAL,
343
+ help="فترة المراقبة بالثواني")
344
+ parser.add_argument("--port", type=int, default=RAM_PORT,
345
+ help="منفذ الخدمة")
346
+ args = parser.parse_args()
347
+
348
+ # تحديث الإعدادات من الوسائط
349
+ RAM_LIMIT_MB = int(args.ram_limit)
350
+ CHUNK_MB = int(args.chunk)
351
+ CHECK_INTERVAL = int(args.interval)
352
+ RAM_PORT = int(args.port)
353
+
354
+ logger.info("🚀 بدء تشغيل مدير الذاكرة الموزعة")
355
+ logger.info("⚙️ الإعدادات: الحد %dMB, الكتلة %dMB, الفاصل %ds, المنفذ %d",
356
+ RAM_LIMIT_MB, CHUNK_MB, CHECK_INTERVAL, RAM_PORT)
357
+
358
+ # بدء الخادم في خيط منفصل
359
+ server_thread = threading.Thread(target=start_api, daemon=True)
360
+ server_thread.start()
361
+
362
+ # بدء حلقة المراقبة
363
  monitor_loop()
364
 
365
  if __name__ == "__main__":
requirements.txt CHANGED
@@ -18,7 +18,7 @@ flask
18
  GPUtil
19
  requests
20
  python-dotenv
21
- torch
22
  flask_socketio
23
- #huggingface-hub
24
- gradio>=4.0.0
 
18
  GPUtil
19
  requests
20
  python-dotenv
21
+
22
  flask_socketio
23
+
24
+
rpc_server.py CHANGED
@@ -6,11 +6,21 @@
6
  # ============================================================
7
 
8
  from flask import Flask, request, jsonify
9
- import smart_tasks # «your_tasks» تمّ استيراده تحت هذا الاسم فى main.py
10
  import logging, json
11
  from security_layer import SecurityManager
12
  from peer_discovery import PORT
13
 
 
 
 
 
 
 
 
 
 
 
 
14
  SECURITY = SecurityManager("my_shared_secret_123")
15
 
16
  logging.basicConfig(
@@ -19,7 +29,20 @@ logging.basicConfig(
19
  format="%(asctime)s - %(levelname)s - %(message)s"
20
  )
21
 
22
- app = Flask(name)
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
  # ------------------------------------------------------------------
25
  @app.route("/health")
@@ -30,45 +53,70 @@ def health():
30
  @app.route("/run", methods=["POST"])
31
  def run():
32
  try:
 
 
33
  # 1) حاول قراءة كـ JSON مباشر (وضع التطويـر)
34
  if request.is_json:
35
  data = request.get_json()
 
36
  else:
37
  # 2) وإلا اعتبره Payload مُشفَّر (وضع الإنتاج)
38
  encrypted = request.get_data()
 
39
  try:
40
  decrypted = SECURITY.decrypt_data(encrypted)
41
- data = json.loads(decrypted.decode())
 
42
  except Exception as e:
43
  logging.error(f"⚠️ فشل فك التشفير: {e}")
44
  return jsonify(error="Decryption failed"), 400
45
 
 
 
 
 
 
46
  # 3) التحقّق من التوقيع إن وُجد
47
  if "_signature" in data:
 
48
  if not SECURITY.verify_task(data):
49
  logging.warning("❌ توقيع غير صالح")
50
  return jsonify(error="Invalid signature"), 403
51
  # أزل عناصر موقّعة إضافية
52
  data = {k: v for k, v in data.items() if k not in ("_signature", "sender_id")}
 
53
 
 
54
  func_name = data.get("func")
55
- args = data.get("args", [])
56
- kwargs = data.get("kwargs", {})
 
 
 
 
57
 
 
58
  fn = getattr(smart_tasks, func_name, None)
59
- if not fn:
60
  logging.warning(f"❌ لم يتم العثور على الدالة: {func_name}")
61
- return jsonify(error="Function not found"), 404
62
 
63
- logging.info(f"⚙️ تنفيذ الدالة: {func_name} من جهاز آخر")
 
 
64
  result = fn(*args, **kwargs)
65
- return jsonify(result=result)
 
 
66
 
67
  except Exception as e:
68
- logging.error(f"🔥 خطأ أثناء تنفيذ المهمة: {str(e)}")
69
  return jsonify(error=str(e)), 500
70
 
71
  # ------------------------------------------------------------------
72
- if name == "main":
73
- # تأكد أن المنفذ PORT مفتوح
74
- app.run(host="0.0.0.0", port=PORT)
 
 
 
 
6
  # ============================================================
7
 
8
  from flask import Flask, request, jsonify
 
9
  import logging, json
10
  from security_layer import SecurityManager
11
  from peer_discovery import PORT
12
 
13
+ # استيراد المهام - مع معالجة الخطأ
14
+ try:
15
+ import smart_tasks
16
+ except ImportError:
17
+ # إذا كان اسم الملف مختلف
18
+ try:
19
+ import your_tasks as smart_tasks
20
+ except ImportError:
21
+ logging.error("❌ لم يتم العثور على وحدة المهام")
22
+ raise
23
+
24
  SECURITY = SecurityManager("my_shared_secret_123")
25
 
26
  logging.basicConfig(
 
29
  format="%(asctime)s - %(levelname)s - %(message)s"
30
  )
31
 
32
+ app = Flask(__name__)
33
+
34
+ # ------------------------------------------------------------------
35
+ @app.route("/")
36
+ def index():
37
+ return jsonify({
38
+ "message": "AmalOffload RPC Server",
39
+ "version": "1.0",
40
+ "endpoints": {
41
+ "/health": "GET - فحص صحة الخادم",
42
+ "/run": "POST - تنفيذ المهام (يتطلب JSON)",
43
+ },
44
+ "status": "running"
45
+ })
46
 
47
  # ------------------------------------------------------------------
48
  @app.route("/health")
 
53
  @app.route("/run", methods=["POST"])
54
  def run():
55
  try:
56
+ data = None
57
+
58
  # 1) حاول قراءة كـ JSON مباشر (وضع التطويـر)
59
  if request.is_json:
60
  data = request.get_json()
61
+ logging.info("📥 تم استقبال بيانات JSON مباشرة (وضع التطوير)")
62
  else:
63
  # 2) وإلا اعتبره Payload مُشفَّر (وضع الإنتاج)
64
  encrypted = request.get_data()
65
+ logging.info(f"🔐 تم استقبال بيانات مشفرة بحجم: {len(encrypted)} بايت")
66
  try:
67
  decrypted = SECURITY.decrypt_data(encrypted)
68
+ data = json.loads(decrypted.decode('utf-8'))
69
+ logging.info("✅ تم فك التشفير بنجاح")
70
  except Exception as e:
71
  logging.error(f"⚠️ فشل فك التشفير: {e}")
72
  return jsonify(error="Decryption failed"), 400
73
 
74
+ # التحقق من وجود البيانات
75
+ if not data:
76
+ logging.warning("❌ لا توجد بيانات صالحة")
77
+ return jsonify(error="No valid data received"), 400
78
+
79
  # 3) التحقّق من التوقيع إن وُجد
80
  if "_signature" in data:
81
+ logging.info("🔏 التحقق من التوقيع...")
82
  if not SECURITY.verify_task(data):
83
  logging.warning("❌ توقيع غير صالح")
84
  return jsonify(error="Invalid signature"), 403
85
  # أزل عناصر موقّعة إضافية
86
  data = {k: v for k, v in data.items() if k not in ("_signature", "sender_id")}
87
+ logging.info("✅ التوقيع صالح")
88
 
89
+ # التحقق من وجود اسم الدالة
90
  func_name = data.get("func")
91
+ if not func_name:
92
+ logging.warning("❌ لم يتم تحديد اسم الدالة")
93
+ return jsonify(error="Function name 'func' is required"), 400
94
+
95
+ args = data.get("args", [])
96
+ kwargs = data.get("kwargs", {})
97
 
98
+ # البحث عن الدالة
99
  fn = getattr(smart_tasks, func_name, None)
100
+ if not fn or not callable(fn):
101
  logging.warning(f"❌ لم يتم العثور على الدالة: {func_name}")
102
+ return jsonify(error=f"Function '{func_name}' not found"), 404
103
 
104
+ logging.info(f"⚙️ تنفيذ الدالة: {func_name} مع args: {args}, kwargs: {kwargs}")
105
+
106
+ # تنفيذ الدالة
107
  result = fn(*args, **kwargs)
108
+
109
+ logging.info(f"✅ تم تنفيذ الدالة {func_name} بنجاح")
110
+ return jsonify(result=result, status="success")
111
 
112
  except Exception as e:
113
+ logging.error(f"🔥 خطأ أثناء تنفيذ المهمة: {str(e)}", exc_info=True)
114
  return jsonify(error=str(e)), 500
115
 
116
  # ------------------------------------------------------------------
117
+ if __name__ == "__main__":
118
+ logging.info(f"🚀 بدء تشغيل RPC Server على المنفذ {PORT}")
119
+ try:
120
+ app.run(host="0.0.0.0", port=PORT, debug=False)
121
+ except Exception as e:
122
+ logging.error(f"❌ فشل تشغيل الخادم: {e}")
run_task.py CHANGED
@@ -1,8 +1,87 @@
1
- @flask_app.route("/run_task", methods=["POST"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  def run_task():
3
- task_id = request.form.get("task_id")
4
- result = None
5
- # قبول البيانات سواء كانت JSON أو form-urlencoded
 
 
 
6
  if request.is_json:
7
  data = request.get_json()
8
  task_id = data.get("task_id")
@@ -10,13 +89,103 @@ def run_task():
10
  task_id = request.form.get("task_id")
11
 
12
  if not task_id:
13
- return jsonify(error="Missing task_id"), 400
14
-
15
- if task_id == "1":
16
- result = matrix_multiply(500)
17
- elif task_id == "2":
18
- result = prime_calculation(100_000)
19
- elif task_id == "3":
20
- result = data_processing(10_000)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
- return jsonify(result=result) # إرجاع JSON بدل HTML
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ run_task.py - خادم ويب بسيط لتشغيل المهام
4
+ """
5
+
6
+ from flask import Flask, request, jsonify
7
+ import logging
8
+ import sys
9
+ import os
10
+
11
+ # إضافة المسار الحالي إلى Python path
12
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
13
+
14
+ # استيراد المهام
15
+ try:
16
+ from smart_tasks import matrix_multiply, prime_calculation, data_processing
17
+ TASKS_AVAILABLE = True
18
+ except ImportError as e:
19
+ logging.error(f"❌ لم يتم العثور على وحدة المهام: {e}")
20
+ TASKS_AVAILABLE = False
21
+
22
+ # إعداد السجلات
23
+ logging.basicConfig(
24
+ level=logging.INFO,
25
+ format='%(asctime)s - %(levelname)s - %(message)s',
26
+ datefmt='%H:%M:%S'
27
+ )
28
+
29
+ # إنشاء تطبيق Flask
30
+ app = Flask(__name__)
31
+
32
+ @app.route("/")
33
+ def index():
34
+ """الصفحة الرئيسية"""
35
+ return jsonify({
36
+ "message": "AmalOffload Task Runner",
37
+ "version": "1.0",
38
+ "status": "running",
39
+ "endpoints": {
40
+ "/": "GET - الصفحة الرئيسية",
41
+ "/health": "GET - فحص الصحة",
42
+ "/run_task": "POST - تشغيل المهام",
43
+ "/tasks": "GET - قائمة المهام المتاحة"
44
+ }
45
+ })
46
+
47
+ @app.route("/health")
48
+ def health():
49
+ """فحص صحة الخادم"""
50
+ return jsonify({
51
+ "status": "healthy",
52
+ "tasks_available": TASKS_AVAILABLE
53
+ })
54
+
55
+ @app.route("/tasks")
56
+ def list_tasks():
57
+ """عرض قائمة المهام المتاحة"""
58
+ tasks = {
59
+ "1": {
60
+ "name": "matrix_multiply",
61
+ "description": "ضرب المصفوفات بحجم 500x500",
62
+ "parameters": "matrix_size=500"
63
+ },
64
+ "2": {
65
+ "name": "prime_calculation",
66
+ "description": "حساب الأعداد الأولية حتى 100,000",
67
+ "parameters": "limit=100000"
68
+ },
69
+ "3": {
70
+ "name": "data_processing",
71
+ "description": "معالجة بيانات بحجم 10,000 عنصر",
72
+ "parameters": "size=10000"
73
+ }
74
+ }
75
+ return jsonify(tasks)
76
+
77
+ @app.route("/run_task", methods=["POST"])
78
  def run_task():
79
+ """تشغيل مهمة محددة"""
80
+ if not TASKS_AVAILABLE:
81
+ return jsonify({"error": "المهام غير متاحة"}), 500
82
+
83
+ # قبول البيانات سواء كانت JSON أو form-urlencoded
84
+ task_id = None
85
  if request.is_json:
86
  data = request.get_json()
87
  task_id = data.get("task_id")
 
89
  task_id = request.form.get("task_id")
90
 
91
  if not task_id:
92
+ return jsonify({"error": "معرف المهمة مطلوب (task_id)"}), 400
93
+
94
+ logging.info(f"🎯 تشغيل المهمة: {task_id}")
95
+
96
+ try:
97
+ result = None
98
+ execution_time = None
99
+ import time
100
+
101
+ start_time = time.time()
102
+
103
+ if task_id == "1":
104
+ result = matrix_multiply(500)
105
+ elif task_id == "2":
106
+ result = prime_calculation(100000)
107
+ elif task_id == "3":
108
+ result = data_processing(10000)
109
+ else:
110
+ return jsonify({"error": f"المهمة {task_id} غير معروفة"}), 404
111
+
112
+ execution_time = time.time() - start_time
113
+
114
+ logging.info(f"✅ تم تنفيذ المهمة {task_id} في {execution_time:.2f} ثانية")
115
+
116
+ return jsonify({
117
+ "task_id": task_id,
118
+ "status": "success",
119
+ "execution_time": round(execution_time, 2),
120
+ "result": result
121
+ })
122
+
123
+ except Exception as e:
124
+ logging.error(f"❌ خطأ في تنفيذ المهمة {task_id}: {e}")
125
+ return jsonify({
126
+ "task_id": task_id,
127
+ "status": "error",
128
+ "error": str(e)
129
+ }), 500
130
+
131
+ @app.route("/run_task/<task_id>", methods=["GET"])
132
+ def run_task_get(task_id):
133
+ """تشغيل مهمة عبر GET (للتجربة السريعة)"""
134
+ if not TASKS_AVAILABLE:
135
+ return jsonify({"error": "المهام غير متاحة"}), 500
136
+
137
+ logging.info(f"🎯 تشغيل المهمة عبر GET: {task_id}")
138
+
139
+ try:
140
+ result = None
141
+ execution_time = None
142
+ import time
143
+
144
+ start_time = time.time()
145
+
146
+ if task_id == "1":
147
+ result = matrix_multiply(500)
148
+ elif task_id == "2":
149
+ result = prime_calculation(100000)
150
+ elif task_id == "3":
151
+ result = data_processing(10000)
152
+ else:
153
+ return jsonify({"error": f"المهمة {task_id} غير معروفة"}), 404
154
+
155
+ execution_time = time.time() - start_time
156
+
157
+ logging.info(f"✅ تم تنفيذ المهمة {task_id} في {execution_time:.2f} ثانية")
158
+
159
+ return jsonify({
160
+ "task_id": task_id,
161
+ "status": "success",
162
+ "execution_time": round(execution_time, 2),
163
+ "result": result
164
+ })
165
+
166
+ except Exception as e:
167
+ logging.error(f"❌ خطأ في تنفيذ المهمة {task_id}: {e}")
168
+ return jsonify({
169
+ "task_id": task_id,
170
+ "status": "error",
171
+ "error": str(e)
172
+ }), 500
173
+
174
+ def main():
175
+ """الدالة الرئيسية"""
176
+ port = 5613 # يمكن تغيير المنفذ إذا لزم الأمر
177
 
178
+ logging.info("🚀 بدء تشغيل خادم المهام...")
179
+ logging.info(f"📡 سيعمل الخادم على: http://localhost:{port}")
180
+ logging.info("📋 المهام المتاحة:")
181
+ logging.info(" 1 - ضرب المصفوفات (matrix_multiply)")
182
+ logging.info(" 2 - حساب الأعداد الأولية (prime_calculation)")
183
+ logging.info(" 3 - معالجة البيانات (data_processing)")
184
+
185
+ try:
186
+ app.run(host="0.0.0.0", port=port, debug=False)
187
+ except Exception as e:
188
+ logging.error(f"❌ فشل في تشغيل الخادم: {e}")
189
+
190
+ if __name__ == "__main__":
191
+ main()
simple_history.json CHANGED
@@ -8,5 +8,15 @@
8
  "timestamp": "2025-08-01T04:07:56.868610",
9
  "user": "كيف حالك",
10
  "assistant": "هذا سؤال جيد!"
 
 
 
 
 
 
 
 
 
 
11
  }
12
  ]
 
8
  "timestamp": "2025-08-01T04:07:56.868610",
9
  "user": "كيف حالك",
10
  "assistant": "هذا سؤال جيد!"
11
+ },
12
+ {
13
+ "timestamp": "2025-10-20T01:04:19.278752",
14
+ "user": "h",
15
+ "assistant": "حدثني أكثر عن ذلك."
16
+ },
17
+ {
18
+ "timestamp": "2025-10-20T01:04:25.627297",
19
+ "user": "hi",
20
+ "assistant": "هذا سؤال جيد!"
21
  }
22
  ]
system_tray.py CHANGED
@@ -1,4 +1,3 @@
1
-
2
  #!/usr/bin/env python3
3
  """
4
  أيقونة شريط النظام للتحكم في الخدمة الخلفية
@@ -8,9 +7,11 @@ import sys
8
  import threading
9
  import requests
10
  import webbrowser
 
 
 
11
  from pathlib import Path
12
  from peer_discovery import PORT
13
-
14
  try:
15
  import pystray
16
  from pystray import MenuItem as item
@@ -24,150 +25,294 @@ class SystemTrayController:
24
  def __init__(self):
25
  self.base_url = "http://localhost:8888"
26
  self.icon = None
 
 
27
 
28
- def create_icon_image(self):
29
  """إنشاء صورة الأيقونة"""
30
  # إنشاء صورة بسيطة 64x64
31
- image = Image.new('RGB', (64, 64), color='blue')
32
  draw = ImageDraw.Draw(image)
33
 
34
  # رسم دائرة بسيطة
35
  draw.ellipse([16, 16, 48, 48], fill='white')
36
- draw.ellipse([20, 20, 44, 44], fill='blue')
 
 
 
 
 
 
37
 
38
  return image
39
 
40
- def get_service_status(self):
41
- """الحصول على حالة الخدمة"""
42
  try:
43
  response = requests.get(f"{self.base_url}/status", timeout=2)
44
- return response.json()
 
 
45
  except:
46
- return None
47
-
48
- def start_services(self, icon, item):
49
- """بدء تشغيل الخدمات"""
 
50
  try:
51
- requests.post(f"{self.base_url}/start", timeout=5)
52
- self.update_menu()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  except Exception as e:
54
- print(f"فشل في بدء الخدمات: {e}")
 
55
 
56
- def stop_services(self, icon, item):
57
- """إيقاف الخدمات"""
58
  try:
59
- requests.post(f"{self.base_url}/stop", timeout=5)
60
- self.update_menu()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  except Exception as e:
62
- print(f"فشل في إيقاف الخدمات: {e}")
 
63
 
64
- def show_ui(self, icon, item):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  """إظهار الواجهة التفاعلية"""
66
  try:
67
- requests.post(f"{self.base_url}/show-ui", timeout=5)
68
- # فتح المتصفح
 
 
 
 
69
  webbrowser.open('http://localhost:5173')
 
70
  except Exception as e:
71
- print(f"فشل في إظهار الواجهة: {e}")
72
 
73
- def hide_ui(self, icon, item):
74
- """إخفاء الواجهة التفاعلية"""
75
  try:
76
- requests.post(f"{self.base_url}/hide-ui", timeout=5)
 
77
  except Exception as e:
78
- print(f"فشل في إخفاء الواجهة: {e}")
79
 
80
- def open_dashboard(self, icon, item):
81
- """فتح لوحة التحكم"""
82
- webbrowser.open('http://localhost:5173/dashboard')
 
 
 
 
83
 
84
- def show_status(self, icon, item):
85
  """إظهار حالة النظام"""
86
- status = self.get_service_status()
87
  if status:
88
- status_text = f"حالة النظام: {status['status']}\n"
89
- status_text += f"الخدمات النشطة: {len([s for s in status['services'].values() if s == 'running'])}\n"
90
- status_text += f"وقت التشغيل: {int(status['uptime'])} ثانية"
91
  print(status_text)
92
  else:
93
- print("❌ لا يمكن الوصول للخدمة")
 
 
 
 
 
 
 
94
 
95
- def quit_app(self, icon, item):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  """إنهاء التطبيق"""
97
- self.stop_services(icon, item)
98
- icon.stop()
 
 
 
 
99
 
100
- def update_menu(self):
 
 
 
 
101
  """تحديث قائمة الأيقونة"""
102
  if self.icon:
103
- status = self.get_service_status()
104
- is_running = status and status['status'] == 'running'
 
 
 
 
105
 
106
  menu = pystray.Menu(
107
- item('حالة النظام', self.show_status),
108
- item('---'),
109
- item('إظهار الواجهة', self.show_ui),
110
- item('إخفاء الواجهة', self.hide_ui),
111
- item('لوحة التحكم', self.open_dashboard),
112
- item('---'),
113
- item('بدء الخدمات', self.start_services, enabled=not is_running),
114
- item('إيقاف الخدمات', self.stop_services, enabled=is_running),
115
- item('---'),
116
- item('إنهاء', self.quit_app)
 
 
 
 
117
  )
118
 
119
  self.icon.menu = menu
120
 
121
  def create_menu(self):
122
  """إنشاء قائمة الأيقونة"""
 
 
 
123
  return pystray.Menu(
124
- item('حالة النظام', self.show_status),
125
- item('---'),
126
- item('إظهار الواجهة', self.show_ui),
127
- item('إخفاء الواجهة', self.hide_ui),
128
- item('لوحة التحكم', self.open_dashboard),
129
- item('---'),
130
- item('بدء الخدمات', self.start_services),
131
- item('إيقاف الخدمات', self.stop_services),
132
- item('---'),
133
- item('إنهاء', self.quit_app)
 
 
 
 
134
  )
135
 
136
  def run(self):
137
  """تشغيل أيقونة شريط النظام"""
138
  if not TRAY_AVAILABLE:
139
  print("❌ مكتبة pystray غير متوفرة")
140
- return
 
141
 
142
- # إنشاء الأيقونة
143
- image = self.create_icon_image()
144
- menu = self.create_menu()
145
-
146
- self.icon = pystray.Icon(
147
- "نظام توزيع المهام",
148
- image,
149
- menu=menu
150
- )
151
-
152
- # تحديث دوري للقائمة
153
- def update_loop():
154
- import time
155
- while True:
156
- time.sleep(5)
157
- if self.icon and hasattr(self.icon, '_running') and self.icon._running:
158
- self.update_menu()
159
- else:
160
- break
161
-
162
- update_thread = threading.Thread(target=update_loop, daemon=True)
163
- update_thread.start()
164
-
165
- print("🖱️ تشغيل أيقونة شريط النظام...")
166
- self.icon.run()
 
 
 
 
 
 
 
 
167
 
168
  def main():
 
169
  controller = SystemTrayController()
170
- controller.run()
 
 
 
 
 
 
 
171
 
172
  if __name__ == "__main__":
173
  main()
 
 
1
  #!/usr/bin/env python3
2
  """
3
  أيقونة شريط النظام للتحكم في الخدمة الخلفية
 
7
  import threading
8
  import requests
9
  import webbrowser
10
+ import subprocess
11
+ import os
12
+ import time
13
  from pathlib import Path
14
  from peer_discovery import PORT
 
15
  try:
16
  import pystray
17
  from pystray import MenuItem as item
 
25
  def __init__(self):
26
  self.base_url = "http://localhost:8888"
27
  self.icon = None
28
+ self.background_process = None
29
+ self.services_running = False
30
 
31
+ def create_icon_image(self, color='blue'):
32
  """إنشاء صورة الأيقونة"""
33
  # إنشاء صورة بسيطة 64x64
34
+ image = Image.new('RGB', (64, 64), color=color)
35
  draw = ImageDraw.Draw(image)
36
 
37
  # رسم دائرة بسيطة
38
  draw.ellipse([16, 16, 48, 48], fill='white')
39
+ draw.ellipse([20, 20, 44, 44], fill=color)
40
+
41
+ # إضافة رمز صغير للإشارة للحالة
42
+ if self.services_running:
43
+ draw.rectangle([28, 28, 36, 36], fill='green') # نقطة خضراء عند التشغيل
44
+ else:
45
+ draw.rectangle([28, 28, 36, 36], fill='red') # نقطة حمراء عند التوقف
46
 
47
  return image
48
 
49
+ def check_service_status(self):
50
+ """فحص حالة الخدمة"""
51
  try:
52
  response = requests.get(f"{self.base_url}/status", timeout=2)
53
+ if response.status_code == 200:
54
+ self.services_running = True
55
+ return response.json()
56
  except:
57
+ self.services_running = False
58
+ return None
59
+
60
+ def start_background_service(self):
61
+ """بدء الخدمة الخلفية"""
62
  try:
63
+ if self.background_process and self.background_process.poll() is None:
64
+ print("✅ الخدمة الخلفية تعمل بالفعل")
65
+ return True
66
+
67
+ print("🚀 بدء الخدمة الخلفية...")
68
+ self.background_process = subprocess.Popen(
69
+ [sys.executable, 'background_service.py', 'start'],
70
+ stdout=subprocess.PIPE,
71
+ stderr=subprocess.PIPE,
72
+ creationflags=subprocess.CREATE_NO_WINDOW
73
+ )
74
+
75
+ # انتظار حتى تبدأ الخدمة
76
+ for i in range(10):
77
+ time.sleep(1)
78
+ if self.check_service_status():
79
+ print("✅ تم بدء الخدمة الخلفية بنجاح")
80
+ self.services_running = True
81
+ return True
82
+
83
+ print("⚠️ بدأت الخدمة ولكنها لم تستجب بعد")
84
+ return False
85
+
86
  except Exception as e:
87
+ print(f"فشل في بدء الخدمة الخلفية: {e}")
88
+ return False
89
 
90
+ def stop_background_service(self):
91
+ """إيقاف الخدمة الخلفية"""
92
  try:
93
+ if self.background_process:
94
+ # محاولة إيقاف نظيف عبر HTTP
95
+ try:
96
+ requests.post(f"{self.base_url}/stop", timeout=5)
97
+ time.sleep(2)
98
+ except:
99
+ pass
100
+
101
+ # إجبار الإيقاف إذا لزم
102
+ if self.background_process.poll() is None:
103
+ self.background_process.terminate()
104
+ self.background_process.wait(timeout=5)
105
+
106
+ self.background_process = None
107
+ self.services_running = False
108
+ print("✅ تم إيقاف الخدمة الخلفية")
109
+ return True
110
+ else:
111
+ print("ℹ️ لا توجد خدمة خلفية قيد التشغيل")
112
+ return True
113
+
114
  except Exception as e:
115
+ print(f"فشل في إيقاف الخدمة الخلفية: {e}")
116
+ return False
117
 
118
+ def start_services(self, icon=None, item=None):
119
+ """بدء تشغيل الخدمات"""
120
+ print("🔧 بدء جميع الخدمات...")
121
+ success = self.start_background_service()
122
+ if success:
123
+ self.update_icon_menu()
124
+ print("✅ تم بدء جميع الخدمات")
125
+ else:
126
+ print("❌ فشل في بدء الخدمات")
127
+
128
+ def stop_services(self, icon=None, item=None):
129
+ """إيقاف الخدمات"""
130
+ print("🛑 إيقاف جميع الخدمات...")
131
+ success = self.stop_background_service()
132
+ if success:
133
+ self.update_icon_menu()
134
+ print("✅ تم إيقاف جميع الخدمات")
135
+ else:
136
+ print("❌ فشل في إيقاف الخدمات")
137
+
138
+ def show_ui(self, icon=None, item=None):
139
  """إظهار الواجهة التفاعلية"""
140
  try:
141
+ # بدء الخدمة أولاً إذا لم تكن مشغلة
142
+ if not self.services_running:
143
+ self.start_background_service()
144
+ time.sleep(3)
145
+
146
+ # محاولة فتح الواجهة
147
  webbrowser.open('http://localhost:5173')
148
+ print("🌐 فتح الواجهة في المتصفح...")
149
  except Exception as e:
150
+ print(f"فشل في فتح الواجهة: {e}")
151
 
152
+ def open_dashboard(self, icon=None, item=None):
153
+ """فتح لوحة التحكم"""
154
  try:
155
+ webbrowser.open('http://localhost:5173/dashboard')
156
+ print("📊 فتح لوحة التحكم...")
157
  except Exception as e:
158
+ print(f"فشل في فتح لوحة التحكم: {e}")
159
 
160
+ def open_web_interface(self, icon=None, item=None):
161
+ """فتح واجهة الويب"""
162
+ try:
163
+ webbrowser.open('http://localhost:PORT')
164
+ print("🖥️ فتح واجهة المهام...")
165
+ except Exception as e:
166
+ print(f"❌ فشل في فتح واجهة المهام: {e}")
167
 
168
+ def show_status(self, icon=None, item=None):
169
  """إظهار حالة النظام"""
170
+ status = self.check_service_status()
171
  if status:
172
+ status_text = f"حالة النظام: {status.get('status', 'unknown')}\n"
173
+ status_text += f"📊 الخدمات النشطة: {len([s for s in status.get('services', {}).values() if s == 'running'])}\n"
174
+ status_text += f"⏱️ وقت التشغيل: {int(status.get('uptime', 0))} ثانية"
175
  print(status_text)
176
  else:
177
+ print("❌ الخدمات غير نشطة - استخدم 'بدء الخدمات' لتشغيلها")
178
+
179
+ def show_system_info(self, icon=None, item=None):
180
+ """عرض معلومات النظام"""
181
+ try:
182
+ import psutil
183
+ cpu = psutil.cpu_percent(interval=1)
184
+ memory = psutil.virtual_memory()
185
 
186
+ info_text = f"⚡ استخدام المعالج: {cpu}%\n"
187
+ info_text += f"💾 استخدام الذاكرة: {memory.percent}%\n"
188
+ info_text += f"📡 الخدمات: {'🟢 نشطة' if self.services_running else '🔴 متوقفة'}"
189
+ print(info_text)
190
+ except Exception as e:
191
+ print(f"❌ فشل في الحصول على معلومات النظام: {e}")
192
+
193
+ def restart_services(self, icon=None, item=None):
194
+ """إعادة تشغيل الخدمات"""
195
+ print("🔄 إعادة تشغيل الخدمات...")
196
+ self.stop_services()
197
+ time.sleep(2)
198
+ self.start_services()
199
+
200
+ def quit_app(self, icon=None, item=None):
201
  """إنهاء التطبيق"""
202
+ print("🛑 إيقاف النظام...")
203
+ self.stop_services()
204
+ if icon:
205
+ icon.stop()
206
+ print("✅ تم إنهاء الت��بيق")
207
+ os._exit(0)
208
 
209
+ def dummy_action(self, icon=None, item=None):
210
+ """إجراء افتراضي للفواصل"""
211
+ pass
212
+
213
+ def update_icon_menu(self):
214
  """تحديث قائمة الأيقونة"""
215
  if self.icon:
216
+ # تحديث حالة الخدمات
217
+ self.check_service_status()
218
+
219
+ # تحديث لون الأيقونة
220
+ icon_color = 'green' if self.services_running else 'red'
221
+ self.icon.icon = self.create_icon_image(icon_color)
222
 
223
  menu = pystray.Menu(
224
+ item(f'الحالة: {"🟢 نشط" if self.services_running else "🔴 متوقف"}', self.dummy_action, enabled=False),
225
+ item('---', self.dummy_action),
226
+ item('📊 حالة النظام', self.show_status),
227
+ item('💻 معلومات النظام', self.show_system_info),
228
+ item('---', self.dummy_action),
229
+ item('🖥️ فتح الواجهة الرئيسية', self.show_ui),
230
+ item('📈 لوحة التحكم', self.open_dashboard),
231
+ item('⚙️ واجهة المهام', self.open_web_interface),
232
+ item('---', self.dummy_action),
233
+ item('🚀 بدء الخدمات', self.start_services, enabled=not self.services_running),
234
+ item('🛑 إيقاف الخدمات', self.stop_services, enabled=self.services_running),
235
+ item('🔄 إعادة التشغيل', self.restart_services),
236
+ item('---', self.dummy_action),
237
+ item('❌ إنهاء', self.quit_app)
238
  )
239
 
240
  self.icon.menu = menu
241
 
242
  def create_menu(self):
243
  """إنشاء قائمة الأيقونة"""
244
+ # فحص الحالة الأولية
245
+ self.check_service_status()
246
+
247
  return pystray.Menu(
248
+ item(f'الحالة: {"🟢 نشط" if self.services_running else "🔴 متوقف"}', self.dummy_action, enabled=False),
249
+ item('---', self.dummy_action),
250
+ item('📊 حالة النظام', self.show_status),
251
+ item('💻 معلومات النظام', self.show_system_info),
252
+ item('---', self.dummy_action),
253
+ item('🖥️ فتح الواجهة الرئيسية', self.show_ui),
254
+ item('📈 لوحة التحكم', self.open_dashboard),
255
+ item('⚙️ واجهة المهام', self.open_web_interface),
256
+ item('---', self.dummy_action),
257
+ item('🚀 بدء الخدمات', self.start_services, enabled=not self.services_running),
258
+ item('🛑 إيقاف الخدمات', self.stop_services, enabled=self.services_running),
259
+ item('🔄 إعادة التشغيل', self.restart_services),
260
+ item('---', self.dummy_action),
261
+ item('❌ إنهاء', self.quit_app)
262
  )
263
 
264
  def run(self):
265
  """تشغيل أيقونة شريط النظام"""
266
  if not TRAY_AVAILABLE:
267
  print("❌ مكتبة pystray غير متوفرة")
268
+ print("💡 جرب: pip install pystray Pillow")
269
+ return False
270
 
271
+ try:
272
+ # إنشاء الأيقونة
273
+ icon_color = 'green' if self.services_running else 'red'
274
+ image = self.create_icon_image(icon_color)
275
+ menu = self.create_menu()
276
+
277
+ self.icon = pystray.Icon(
278
+ "نظام توزيع المهام",
279
+ image,
280
+ menu=menu
281
+ )
282
+
283
+ # تحديث دوري للقائمة
284
+ def update_loop():
285
+ while True:
286
+ time.sleep(10) # تحديث كل 10 ثواني
287
+ if self.icon:
288
+ try:
289
+ self.update_icon_menu()
290
+ except:
291
+ break
292
+
293
+ update_thread = threading.Thread(target=update_loop, daemon=True)
294
+ update_thread.start()
295
+
296
+ print("🖱️ تشغيل أيقونة شريط النظام...")
297
+ print("💡 انقر بزر الماوس الأيمن على الأيقونة للخيارات")
298
+ self.icon.run()
299
+ return True
300
+
301
+ except Exception as e:
302
+ print(f"❌ فشل في تشغيل أيقونة النظام: {e}")
303
+ return False
304
 
305
  def main():
306
+ print("🚀 بدء نظام توزيع المهام...")
307
  controller = SystemTrayController()
308
+
309
+ # بدء الخدمات تلقائياً عند التشغيل (اختياري)
310
+ # controller.start_services()
311
+
312
+ success = controller.run()
313
+ if not success:
314
+ print("❌ فشل في تشغيل واجهة النظام")
315
+ input("اضغط Enter للإغلاق...")
316
 
317
  if __name__ == "__main__":
318
  main()