osamabyc86 commited on
Commit
eef2c3e
·
verified ·
1 Parent(s): 2b689e4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +819 -0
app.py CHANGED
@@ -116,6 +116,825 @@ def run_fastapi():
116
  """تشغيل FastAPI في thread منفصل"""
117
  threading.Thread(target=health_check_loop, daemon=True).start()
118
  uvicorn.run(app, host="0.0.0.0", port=PORT)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
 
120
  if __name__ == "__main__":
121
  # تشغيل FastAPI في thread منفصل
 
116
  """تشغيل FastAPI في thread منفصل"""
117
  threading.Thread(target=health_check_loop, daemon=True).start()
118
  uvicorn.run(app, host="0.0.0.0", port=PORT)
119
+ #!/usr/bin/env python3
120
+ # -*- coding: utf-8 -*-
121
+ """
122
+ نظام توزيع المهام الذكي - الخادم المركزي على Hugging Face
123
+ يجمع بين واجهة Gradio وخادم التوزيع المركزي
124
+ """
125
+
126
+ import os
127
+ import sys
128
+ import time
129
+ import json
130
+ import logging
131
+ import threading
132
+ import random
133
+ import queue
134
+ from datetime import datetime
135
+ from typing import Dict, List, Optional, Any
136
+ from dataclasses import dataclass, field, asdict
137
+
138
+ import gradio as gr
139
+ from flask import Flask, request, jsonify
140
+ from flask_cors import CORS
141
+ import requests
142
+
143
+ # ─────────────── إعداد السجلات ───────────────
144
+ logging.basicConfig(
145
+ level=logging.INFO,
146
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
147
+ )
148
+ logger = logging.getLogger("CentralServerHF")
149
+
150
+ # ─────────────── هياكل البيانات ───────────────
151
+ @dataclass
152
+ class NodeInfo:
153
+ """معلومات العقدة المتصلة"""
154
+ node_id: str
155
+ ip: str
156
+ port: int
157
+ url: str
158
+ capabilities: List[str]
159
+ cpu_usage: float = 0.0
160
+ memory_usage: float = 0.0
161
+ status: str = "online"
162
+ last_seen: datetime = field(default_factory=datetime.now)
163
+ success_rate: float = 1.0
164
+ response_time: float = 0.0
165
+ tasks_completed: int = 0
166
+ tasks_failed: int = 0
167
+ current_tasks: int = 0
168
+ weight: float = 1.0 # للأوزان الذكية
169
+
170
+ @property
171
+ def score(self) -> float:
172
+ """حساب درجة العقدة"""
173
+ health = 0.4 * (100 - (self.cpu_usage + self.memory_usage) / 2) / 100
174
+ performance = 0.3 * self.success_rate
175
+ availability = 0.2 * (1.0 - self.current_tasks / 5)
176
+ speed = 0.1 * max(0, 1 - self.response_time / 10)
177
+
178
+ return (health + performance + availability + speed) * self.weight
179
+
180
+ @dataclass
181
+ class TaskInfo:
182
+ """معلومات المهمة"""
183
+ task_id: str
184
+ task_type: str
185
+ params: Dict[str, Any]
186
+ sender: str
187
+ status: str = "pending" # pending, processing, completed, failed
188
+ assigned_to: Optional[str] = None
189
+ result: Optional[Any] = None
190
+ error: Optional[str] = None
191
+ created_at: datetime = field(default_factory=datetime.now)
192
+ started_at: Optional[datetime] = None
193
+ completed_at: Optional[datetime] = None
194
+ priority: int = 2 # 1=high, 2=medium, 3=low
195
+
196
+ # ─────────────── فئة الخادم المركزي ───────────────
197
+ class CentralServer:
198
+ """الخادم المركزي الذكي لتوزيع المهام"""
199
+
200
+ def __init__(self):
201
+ self.nodes: Dict[str, NodeInfo] = {}
202
+ self.tasks: Dict[str, TaskInfo] = {}
203
+ self.task_queue = queue.PriorityQueue()
204
+ self.lock = threading.RLock()
205
+ self.is_running = True
206
+
207
+ # إحصائيات النظام
208
+ self.metrics = {
209
+ "total_nodes": 0,
210
+ "online_nodes": 0,
211
+ "total_tasks": 0,
212
+ "completed_tasks": 0,
213
+ "failed_tasks": 0,
214
+ "avg_response_time": 0.0,
215
+ "system_uptime": time.time()
216
+ }
217
+
218
+ # بدء الخدمات الخلفية
219
+ self._start_background_services()
220
+
221
+ logger.info("🚀 بدء تشغيل الخادم المركزي على Hugging Face")
222
+
223
+ def _start_background_services(self):
224
+ """بدء الخدمات الخلفية"""
225
+ # خيط توزيع المهام
226
+ self.dispatcher = threading.Thread(target=self._dispatch_loop, daemon=True)
227
+ self.dispatcher.start()
228
+
229
+ # خيط فحص صحة العقد
230
+ self.health_checker = threading.Thread(target=self._health_check_loop, daemon=True)
231
+ self.health_checker.start()
232
+
233
+ # خيط محاكاة العقد (للتجربة)
234
+ self.simulator = threading.Thread(target=self._simulate_nodes, daemon=True)
235
+ self.simulator.start()
236
+
237
+ def register_node(self, node_data: Dict) -> Dict:
238
+ """تسجيل عقدة جديدة"""
239
+ with self.lock:
240
+ node_id = node_data.get('node_id', f"node_{len(self.nodes)+1}")
241
+
242
+ node = NodeInfo(
243
+ node_id=node_id,
244
+ ip=node_data.get('ip', '127.0.0.1'),
245
+ port=node_data.get('port', 0),
246
+ url=node_data.get('url', ''),
247
+ capabilities=node_data.get('capabilities', ['general']),
248
+ cpu_usage=node_data.get('cpu_usage', 0.0),
249
+ memory_usage=node_data.get('memory_usage', 0.0),
250
+ status='online'
251
+ )
252
+
253
+ self.nodes[node_id] = node
254
+ self.metrics["total_nodes"] = len(self.nodes)
255
+ self.metrics["online_nodes"] = len([n for n in self.nodes.values() if n.status == 'online'])
256
+
257
+ logger.info(f"✅ عقدة مسجلة: {node_id}")
258
+
259
+ return {
260
+ "status": "success",
261
+ "node_id": node_id,
262
+ "message": "تم التسجيل بنجاح",
263
+ "server_time": datetime.now().isoformat()
264
+ }
265
+
266
+ def update_node_status(self, node_id: str, metrics: Dict):
267
+ """تحديث حالة العقدة"""
268
+ with self.lock:
269
+ if node_id in self.nodes:
270
+ node = self.nodes[node_id]
271
+
272
+ if 'cpu_usage' in metrics:
273
+ node.cpu_usage = metrics['cpu_usage']
274
+ if 'memory_usage' in metrics:
275
+ node.memory_usage = metrics['memory_usage']
276
+ if 'current_tasks' in metrics:
277
+ node.current_tasks = metrics['current_tasks']
278
+ if 'status' in metrics:
279
+ node.status = metrics['status']
280
+
281
+ node.last_seen = datetime.now()
282
+
283
+ # تحديث معدل النجاح
284
+ total = node.tasks_completed + node.tasks_failed
285
+ if total > 0:
286
+ node.success_rate = node.tasks_completed / total
287
+
288
+ return {"status": "updated"}
289
+
290
+ return {"error": "العقدة غير موجودة"}
291
+
292
+ def submit_task(self, task_data: Dict) -> Dict:
293
+ """إرسال مهمة جديدة"""
294
+ task_id = task_data.get('task_id', f"task_{int(time.time())}_{random.randint(1000,9999)}")
295
+
296
+ task = TaskInfo(
297
+ task_id=task_id,
298
+ task_type=task_data.get('task_type', 'general'),
299
+ params=task_data.get('params', {}),
300
+ sender=task_data.get('sender', 'unknown'),
301
+ priority=task_data.get('priority', 2)
302
+ )
303
+
304
+ with self.lock:
305
+ self.tasks[task_id] = task
306
+ self.metrics["total_tasks"] = len(self.tasks)
307
+
308
+ # إضافة إلى قائمة الانتظار
309
+ self.task_queue.put((task.priority, task_id))
310
+
311
+ logger.info(f"📨 مهمة مستلمة: {task_id} ({task.task_type})")
312
+
313
+ return {
314
+ "status": "accepted",
315
+ "task_id": task_id,
316
+ "queue_position": self.task_queue.qsize(),
317
+ "estimated_wait": self.task_queue.qsize() * 2 # ثانيتين لكل مهمة
318
+ }
319
+
320
+ def _dispatch_loop(self):
321
+ """حلقة توزيع المهام"""
322
+ while self.is_running:
323
+ try:
324
+ # انتظار مهمة
325
+ priority, task_id = self.task_queue.get(timeout=1.0)
326
+
327
+ with self.lock:
328
+ task = self.tasks.get(task_id)
329
+ if not task or task.status != "pending":
330
+ continue
331
+
332
+ # إيجاد أفضل عقدة
333
+ best_node = self._select_best_node(task.task_type)
334
+
335
+ if best_node:
336
+ # تعيين المهمة
337
+ self._assign_task(task, best_node)
338
+ else:
339
+ # إعادة المحاولة
340
+ time.sleep(3)
341
+ self.task_queue.put((priority, task_id))
342
+
343
+ except queue.Empty:
344
+ continue
345
+ except Exception as e:
346
+ logger.error(f"❌ خطأ في التوزيع: {e}")
347
+ time.sleep(5)
348
+
349
+ def _select_best_node(self, task_type: str) -> Optional[NodeInfo]:
350
+ """اختيار أفضل عقدة للمهمة"""
351
+ with self.lock:
352
+ # فلترة العقد المتاحة
353
+ available = []
354
+ for node in self.nodes.values():
355
+ if node.status != 'online':
356
+ continue
357
+ if node.cpu_usage > 85 or node.memory_usage > 85:
358
+ continue
359
+ if node.current_tasks >= 3:
360
+ continue
361
+
362
+ # التحقق من القدرات
363
+ capabilities_needed = []
364
+ if task_type in ['matrix', 'fibonacci']:
365
+ capabilities_needed = ['cpu_intensive']
366
+ elif task_type in ['data', 'processing']:
367
+ capabilities_needed = ['memory']
368
+
369
+ if capabilities_needed:
370
+ if not any(cap in node.capabilities for cap in capabilities_needed):
371
+ continue
372
+
373
+ available.append(node)
374
+
375
+ if not available:
376
+ return None
377
+
378
+ # اختيار الأعلى درجة
379
+ return max(available, key=lambda n: n.score)
380
+
381
+ def _assign_task(self, task: TaskInfo, node: NodeInfo):
382
+ """تعيين مهمة لعقدة"""
383
+ try:
384
+ # تحديث حالة المهمة
385
+ with self.lock:
386
+ task.status = "processing"
387
+ task.assigned_to = node.node_id
388
+ task.started_at = datetime.now()
389
+ node.current_tasks += 1
390
+
391
+ logger.info(f"🎯 تعيين {task.task_id} → {node.node_id}")
392
+
393
+ # محاكاة التنفيذ
394
+ execution_time = self._simulate_task_execution(task, node)
395
+
396
+ # تحديث النتيجة
397
+ with self.lock:
398
+ task.status = "completed"
399
+ task.completed_at = datetime.now()
400
+ task.result = {
401
+ "executed_on": node.node_id,
402
+ "execution_time": execution_time,
403
+ "task_type": task.task_type,
404
+ "result": f"نتيجة محاكاة لـ {task.task_type}"
405
+ }
406
+
407
+ node.current_tasks -= 1
408
+ node.tasks_completed += 1
409
+ node.response_time = execution_time
410
+
411
+ self.metrics["completed_tasks"] += 1
412
+
413
+ logger.info(f"✅ مكتملة: {task.task_id} في {execution_time:.2f}s")
414
+
415
+ except Exception as e:
416
+ logger.error(f"❌ فشلت المهمة {task.task_id}: {e}")
417
+
418
+ with self.lock:
419
+ task.status = "failed"
420
+ task.error = str(e)
421
+ self.metrics["failed_tasks"] += 1
422
+
423
+ if node.node_id in self.nodes:
424
+ self.nodes[node.node_id].tasks_failed += 1
425
+ self.nodes[node.node_id].current_tasks -= 1
426
+
427
+ def _simulate_task_execution(self, task: TaskInfo, node: NodeInfo) -> float:
428
+ """محاكاة تنفيذ المهمة"""
429
+ # أوقات تنفيذ مختلفة حسب نوع المهمة
430
+ base_times = {
431
+ 'matrix': 1.5,
432
+ 'fibonacci': 0.8,
433
+ 'primes': 1.2,
434
+ 'data': 0.5,
435
+ 'image': 2.0,
436
+ 'general': 0.3
437
+ }
438
+
439
+ base_time = base_times.get(task.task_type, 0.5)
440
+
441
+ # تأثير حمل العقدة
442
+ load_factor = 1 + (node.cpu_usage + node.memory_usage) / 200
443
+
444
+ # تأثير حجم المهمة
445
+ size = task.params.get('size', 10) if isinstance(task.params, dict) else 10
446
+ size_factor = 1 + (size / 1000)
447
+
448
+ # وقت تنفيذ محاكي
449
+ execution_time = base_time * load_factor * size_factor
450
+
451
+ # محاكاة الانتظار
452
+ time.sleep(min(execution_time, 3.0))
453
+
454
+ return execution_time
455
+
456
+ def _health_check_loop(self):
457
+ """فحص صحة العقد"""
458
+ while self.is_running:
459
+ try:
460
+ with self.lock:
461
+ now = datetime.now()
462
+ for node in self.nodes.values():
463
+ # إذا مر أكثر من 2 دقيقة دون تحديث
464
+ if (now - node.last_seen).total_seconds() > 120:
465
+ node.status = "offline"
466
+
467
+ # تحديث الإحصائيات
468
+ self.metrics["online_nodes"] = len(
469
+ [n for n in self.nodes.values() if n.status == 'online']
470
+ )
471
+
472
+ time.sleep(30)
473
+
474
+ except Exception as e:
475
+ logger.error(f"❌ خطأ في فحص الصحة: {e}")
476
+ time.sleep(60)
477
+
478
+ def _simulate_nodes(self):
479
+ """محاكاة عقد افتراضية (للتجربة)"""
480
+ # إنشاء عقد افتراضية
481
+ virtual_nodes = [
482
+ {"id": "node_cpu1", "capabilities": ["cpu_intensive"], "cpu": 20, "memory": 40},
483
+ {"id": "node_mem1", "capabilities": ["memory"], "cpu": 30, "memory": 25},
484
+ {"id": "node_gpu1", "capabilities": ["gpu", "image"], "cpu": 25, "memory": 50},
485
+ {"id": "node_gen1", "capabilities": ["general"], "cpu": 15, "memory": 35}
486
+ ]
487
+
488
+ for vnode in virtual_nodes:
489
+ self.register_node({
490
+ "node_id": vnode["id"],
491
+ "ip": "192.168.1." + str(random.randint(100, 200)),
492
+ "port": random.randint(5000, 6000),
493
+ "url": f"http://192.168.1.{random.randint(100,200)}:{random.randint(5000,6000)}",
494
+ "capabilities": vnode["capabilities"],
495
+ "cpu_usage": vnode["cpu"],
496
+ "memory_usage": vnode["memory"]
497
+ })
498
+
499
+ # محاكاة تحديثات دورية
500
+ while self.is_running:
501
+ try:
502
+ with self.lock:
503
+ for node_id in list(self.nodes.keys())[:4]: # أول 4 عقد (الافتراضية)
504
+ if node_id in self.nodes:
505
+ node = self.nodes[node_id]
506
+ node.cpu_usage = max(5, min(80, node.cpu_usage + random.uniform(-5, 5)))
507
+ node.memory_usage = max(10, min(70, node.memory_usage + random.uniform(-3, 3)))
508
+ node.current_tasks = random.randint(0, 2)
509
+ node.last_seen = datetime.now()
510
+
511
+ time.sleep(10)
512
+
513
+ except Exception as e:
514
+ logger.error(f"❌ خطأ في محاكاة العقد: {e}")
515
+ time.sleep(30)
516
+
517
+ def get_system_overview(self) -> Dict:
518
+ """نظرة عامة على النظام"""
519
+ with self.lock:
520
+ now = datetime.now()
521
+
522
+ # حساب متوسط وقت الاستجابة
523
+ response_times = [n.response_time for n in self.nodes.values() if n.response_time > 0]
524
+ avg_response = sum(response_times) / len(response_times) if response_times else 0
525
+
526
+ # المهام الأخيرة
527
+ recent_tasks = list(self.tasks.values())[-10:] # آخر 10 مهام
528
+
529
+ return {
530
+ "metrics": {
531
+ **self.metrics,
532
+ "uptime": time.time() - self.metrics["system_uptime"],
533
+ "avg_response_time": avg_response,
534
+ "queue_size": self.task_queue.qsize(),
535
+ "timestamp": now.isoformat()
536
+ },
537
+ "nodes": [
538
+ {
539
+ "id": n.node_id,
540
+ "status": n.status,
541
+ "score": round(n.score, 3),
542
+ "cpu": n.cpu_usage,
543
+ "memory": n.memory_usage,
544
+ "tasks": n.current_tasks,
545
+ "success_rate": round(n.success_rate, 2),
546
+ "last_seen": (now - n.last_seen).total_seconds()
547
+ }
548
+ for n in self.nodes.values()
549
+ ],
550
+ "recent_tasks": [
551
+ {
552
+ "id": t.task_id,
553
+ "type": t.task_type,
554
+ "status": t.status,
555
+ "assigned_to": t.assigned_to,
556
+ "created": t.created_at.strftime("%H:%M:%S")
557
+ }
558
+ for t in recent_tasks
559
+ ]
560
+ }
561
+
562
+ # ─────────────── إنشاء الخادم المركزي ───────────────
563
+ central_server = CentralServer()
564
+
565
+ # ─────────────── دوال Gradio ───────────────
566
+ def get_system_status():
567
+ """الحصول على حالة النظام"""
568
+ overview = central_server.get_system_overview()
569
+
570
+ metrics = overview["metrics"]
571
+ nodes = overview["nodes"]
572
+ tasks = overview["recent_tasks"]
573
+
574
+ # تنسيق النص
575
+ status_text = f"""
576
+ # 🌐 الخادم المركزي لتوزيع المهام
577
+
578
+ ## 📊 إحصائيات النظام
579
+ - **الوقت:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
580
+ - **مدة التشغيل:** {metrics['uptime']/3600:.1f} ساعة
581
+ - **العقد الإجمالية:** {metrics['total_nodes']}
582
+ - **العقد النشطة:** {metrics['online_nodes']}
583
+ - **المهام الإجمالية:** {metrics['total_tasks']}
584
+ - **المهام المكتملة:** {metrics['completed_tasks']}
585
+ - **المهام الفاشلة:** {metrics['failed_tasks']}
586
+ - **متوسط وقت الاستجابة:** {metrics['avg_response_time']:.2f} ثانية
587
+ - **المهام في الانتظار:** {metrics['queue_size']}
588
+
589
+ ## 👥 العقد المتصلة ({len(nodes)})
590
+ """
591
+
592
+ for node in nodes:
593
+ status_emoji = "🟢" if node['status'] == 'online' else "🔴" if node['status'] == 'offline' else "🟡"
594
+ status_text += f"\n**{status_emoji} {node['id']}**"
595
+ status_text += f"\n - النقاط: {node['score']:.3f}"
596
+ status_text += f"\n - CPU: {node['cpu']:.1f}% | ذاكرة: {node['memory']:.1f}%"
597
+ status_text += f"\n - المهام النشطة: {node['tasks']}"
598
+ status_text += f"\n - معدل النجاح: {node['success_rate']*100:.1f}%"
599
+ status_text += f"\n - آخر ظهور: {node['last_seen']:.0f} ثانية مضت"
600
+
601
+ if tasks:
602
+ status_text += f"\n\n## 📋 آخر المهام ({len(tasks)})"
603
+ for task in tasks[-5:]: # آخر 5 مهام
604
+ status_emoji = "🟢" if task['status'] == 'completed' else "🟡" if task['status'] == 'processing' else "🔴"
605
+ status_text += f"\n{status_emoji} **{task['id']}** ({task['type']}) → {task['assigned_to'] or 'في الانتظار'}"
606
+
607
+ return status_text
608
+
609
+ def submit_task_ui(task_type, params_json):
610
+ """إرسال مهمة جديدة"""
611
+ try:
612
+ params = json.loads(params_json) if params_json else {}
613
+ except:
614
+ params = {"size": 10}
615
+
616
+ task_data = {
617
+ "task_type": task_type,
618
+ "params": params,
619
+ "sender": "gradio_ui",
620
+ "priority": 2
621
+ }
622
+
623
+ result = central_server.submit_task(task_data)
624
+
625
+ if "error" in result:
626
+ return f"## ❌ خطأ\n{result['error']}"
627
+
628
+ return f"""
629
+ ## ✅ تم قبول المهمة
630
+
631
+ ### 📝 معلومات المهمة
632
+ - **معرف المهمة:** {result['task_id']}
633
+ - **نوع المهمة:** {task_type}
634
+ - **الحالة:** في قائمة الانتظار
635
+ - **الموقع في الطابور:** {result['queue_position']}
636
+ - **الوقت المتوقع:** {result['estimated_wait']} ثانية
637
+ - **الوقت:** {datetime.now().strftime('%H:%M:%S')}
638
+
639
+ ### 📊 تتبع المهمة
640
+ سيتم تعيين المهمة لأفضل عقدة متاحة تلقائياً.
641
+ """
642
+
643
+ def get_node_details():
644
+ """الحصول على تفاصيل العقد"""
645
+ overview = central_server.get_system_overview()
646
+ nodes = overview["nodes"]
647
+
648
+ headers = ["العقدة", "الحالة", "النقاط", "CPU%", "الذاكرة%", "المهام", "معدل النجاح", "آخر ظهور"]
649
+ data = []
650
+
651
+ for node in nodes:
652
+ status_emoji = "🟢" if node['status'] == 'online' else "🔴"
653
+ data.append([
654
+ node['id'],
655
+ f"{status_emoji} {node['status']}",
656
+ f"{node['score']:.3f}",
657
+ f"{node['cpu']:.1f}",
658
+ f"{node['memory']:.1f}",
659
+ str(node['tasks']),
660
+ f"{node['success_rate']*100:.1f}%",
661
+ f"{node['last_seen']:.0f}s"
662
+ ])
663
+
664
+ return data
665
+
666
+ def simulate_new_node():
667
+ """محاكاة عقدة جديدة"""
668
+ node_id = f"node_{int(time.time())}"
669
+ caps = random.choice([['cpu_intensive'], ['memory'], ['general'], ['gpu', 'image']])
670
+
671
+ result = central_server.register_node({
672
+ "node_id": node_id,
673
+ "ip": f"10.0.0.{random.randint(1, 255)}",
674
+ "port": random.randint(5000, 6000),
675
+ "url": f"http://10.0.0.{random.randint(1,255)}:{random.randint(5000,6000)}",
676
+ "capabilities": caps,
677
+ "cpu_usage": random.uniform(10, 40),
678
+ "memory_usage": random.uniform(20, 60)
679
+ })
680
+
681
+ return f"## 🆕 عقدة محاكاة\n**{node_id}** - {', '.join(caps)}\n\nتم التسجيل بنجاح!"
682
+
683
+ def simulate_task_load(count: int):
684
+ """محاكاة حمل مهام"""
685
+ task_types = ['matrix', 'fibonacci', 'primes', 'data', 'image', 'general']
686
+
687
+ for i in range(min(count, 10)): # حد أقصى 10 مهام
688
+ task_type = random.choice(task_types)
689
+ central_server.submit_task({
690
+ "task_type": task_type,
691
+ "params": {"size": random.randint(10, 1000)},
692
+ "sender": "simulation",
693
+ "priority": random.randint(1, 3)
694
+ })
695
+ time.sleep(0.1)
696
+
697
+ return f"## 📨 محاكاة حمل\nتم إرسال {min(count, 10)} مهام عشوائية!"
698
+
699
+ # ─────────────── واجهة Gradio ───────────────
700
+ def create_interface():
701
+ """إنشاء واجهة Gradio"""
702
+
703
+ with gr.Blocks(
704
+ title="الخادم المركزي لتوزيع المهام",
705
+ theme=gr.themes.Soft(primary_hue="blue", secondary_hue="teal")
706
+ ) as demo:
707
+ gr.Markdown("# 🌐 الخادم المركزي الذكي لتوزيع المهام")
708
+ gr.Markdown("### نسخة Hugging Face Spaces - خادم حقيقي")
709
+
710
+ with gr.Tabs():
711
+ # تبويب حالة النظام
712
+ with gr.TabItem("📊 لوحة التحكم"):
713
+ status_output = gr.Markdown()
714
+ refresh_btn = gr.Button("🔄 تحديث الحالة", variant="primary")
715
+
716
+ refresh_btn.click(get_system_status, outputs=status_output)
717
+ demo.load(get_system_status, outputs=status_output)
718
+
719
+ # تبويب إرسال المهام
720
+ with gr.TabItem("🚀 إرسال المهام"):
721
+ with gr.Row():
722
+ with gr.Column():
723
+ gr.Markdown("### إرسال مهمة جديدة")
724
+
725
+ task_type = gr.Dropdown(
726
+ choices=[
727
+ ("ضرب المصفوفات (CPU)", "matrix"),
728
+ ("متسلسلة فيبوناتشي", "fibonacci"),
729
+ ("الأعداد الأولية", "primes"),
730
+ ("معالجة البيانات", "data"),
731
+ ("محاكاة معالجة الصور", "image"),
732
+ ("مهمة عامة", "general")
733
+ ],
734
+ label="نوع المهمة",
735
+ value="matrix"
736
+ )
737
+
738
+ params_input = gr.Textbox(
739
+ label="معاملات المهمة (JSON)",
740
+ value='{"size": 100}',
741
+ placeholder='{"size": 100} أو {"limit": 1000}'
742
+ )
743
+
744
+ submit_btn = gr.Button("📨 إرسال المهمة", variant="primary")
745
+
746
+ with gr.Column():
747
+ gr.Markdown("### نتيجة الإرسال")
748
+ task_result = gr.Markdown()
749
+
750
+ submit_btn.click(submit_task_ui, [task_type, params_input], task_result)
751
+
752
+ # تبويب العقد
753
+ with gr.TabItem("👥 إدارة العقد"):
754
+ gr.Markdown("### العقد المتصلة")
755
+ nodes_table = gr.Dataframe(
756
+ headers=["العقدة", "الحالة", "النقاط", "CPU%", "الذاكرة%", "المهام", "معدل النجاح", "آخر ظهور"],
757
+ interactive=False,
758
+ datatype=["str", "str", "str", "str", "str", "str", "str", "str"]
759
+ )
760
+
761
+ with gr.Row():
762
+ refresh_nodes = gr.Button("🔄 تحديث قائمة العقد")
763
+ sim_node = gr.Button("➕ محاكاة عقدة جديدة")
764
+ sim_tasks = gr.Button("📨 محاكاة حمل مهام")
765
+ task_count = gr.Slider(1, 10, value=3, label="عدد المهام")
766
+
767
+ sim_result = gr.Markdown()
768
+
769
+ refresh_nodes.click(get_node_details, outputs=nodes_table)
770
+ sim_node.click(simulate_new_node, outputs=sim_result)
771
+ sim_tasks.click(simulate_task_load, task_count, sim_result)
772
+ demo.load(get_node_details, outputs=nodes_table)
773
+
774
+ # تبويب المعلومات
775
+ with gr.TabItem("ℹ️ معلومات النظام"):
776
+ gr.Markdown("""
777
+ ## 📖 عن الخادم المركزي
778
+
779
+ هذا تطبيق **خادم مركزي حقيقي** يعمل على Hugging Face Spaces.
780
+
781
+ ### ✨ المميزات:
782
+ - **توزيع ذكي للمهام** على أفضل عقدة متاحة
783
+ - **مراقبة في الوقت الفعلي** لجميع العقد
784
+ - **محاكاة متقدمة** للعقد والمهام
785
+ - **إحصائيات مفصلة** عن أداء النظام
786
+ - **واجهة تحكم كاملة** عبر Gradio
787
+
788
+ ### 🏗️ كيفية العمل:
789
+ 1. **العقد تتصل** بالخادم وتسجل نفسها
790
+ 2. **المهام ترسل** للخادم المركزي
791
+ 3. **الخادم يختار** أفضل عقدة بناءً على:
792
+ - استخدام CPU والذاكرة
793
+ - معدل النجاح السابق
794
+ - عدد المهام النشطة
795
+ - وقت الاستجابة
796
+ 4. **المهام تنفذ** على العقد المختارة
797
+ 5. **النتائج ترجع** للخادم
798
+
799
+ ### 🌐 API المتاح:
800
+ يمكن للعقد الحقيقية الاتصال عبر:
801
+ - `POST /register` - تسجيل عقدة
802
+ - `POST /task/submit` - إرسال مهمة
803
+ - `POST /status/update` - تحديث حالة
804
+ - `GET /nodes` - قائمة العقد
805
+ - `GET /tasks` - قائمة المهام
806
+
807
+ ### 🔧 التقنيات:
808
+ - Python 3 مع معالجة متعددة الخيوط
809
+ - Gradio للواجهة الأمامية
810
+ - خوارزميات توزيع ذكية
811
+ - نظام محاكاة متكامل
812
+ """)
813
+
814
+ gr.Markdown("---\n*الخادم المركزي لتوزيع المهام الذكي - الإصدار 3.0.0*")
815
+
816
+ return demo
817
+
818
+ # ─────────────── API Flask (للعقد الحقيقية) ───────────────
819
+ flask_app = Flask(__name__)
820
+ CORS(flask_app)
821
+
822
+ @flask_app.route('/')
823
+ def api_home():
824
+ return jsonify({
825
+ "message": "الخادم المركزي لتوزيع المهام",
826
+ "version": "3.0.0",
827
+ "status": "running",
828
+ "gradio_url": "https://huggingface.co/spaces/your-username/your-space"
829
+ })
830
+
831
+ @flask_app.route('/register', methods=['POST'])
832
+ def api_register():
833
+ try:
834
+ data = request.get_json()
835
+ result = central_server.register_node(data)
836
+ return jsonify(result)
837
+ except Exception as e:
838
+ return jsonify({"error": str(e)}), 500
839
+
840
+ @flask_app.route('/task/submit', methods=['POST'])
841
+ def api_submit_task():
842
+ try:
843
+ data = request.get_json()
844
+ result = central_server.submit_task(data)
845
+ return jsonify(result)
846
+ except Exception as e:
847
+ return jsonify({"error": str(e)}), 500
848
+
849
+ @flask_app.route('/status/update', methods=['POST'])
850
+ def api_update_status():
851
+ try:
852
+ data = request.get_json()
853
+ node_id = data.get('node_id')
854
+ if not node_id:
855
+ return jsonify({"error": "معرف العقدة مطلوب"}), 400
856
+
857
+ result = central_server.update_node_status(node_id, data)
858
+ return jsonify(result)
859
+ except Exception as e:
860
+ return jsonify({"error": str(e)}), 500
861
+
862
+ @flask_app.route('/nodes', methods=['GET'])
863
+ def api_get_nodes():
864
+ try:
865
+ overview = central_server.get_system_overview()
866
+ return jsonify({
867
+ "status": "success",
868
+ "count": len(overview["nodes"]),
869
+ "nodes": overview["nodes"]
870
+ })
871
+ except Exception as e:
872
+ return jsonify({"error": str(e)}), 500
873
+
874
+ @flask_app.route('/tasks', methods=['GET'])
875
+ def api_get_tasks():
876
+ try:
877
+ with central_server.lock:
878
+ tasks = list(central_server.tasks.values())[-50:]
879
+ tasks_data = []
880
+ for task in tasks:
881
+ task_dict = asdict(task)
882
+ for field in ["created_at", "started_at", "completed_at"]:
883
+ if task_dict[field]:
884
+ task_dict[field] = task_dict[field].isoformat()
885
+ tasks_data.append(task_dict)
886
+
887
+ return jsonify({
888
+ "status": "success",
889
+ "count": len(tasks_data),
890
+ "tasks": tasks_data
891
+ })
892
+ except Exception as e:
893
+ return jsonify({"error": str(e)}), 500
894
+
895
+ @flask_app.route('/stats', methods=['GET'])
896
+ def api_get_stats():
897
+ try:
898
+ overview = central_server.get_system_overview()
899
+ return jsonify({
900
+ "status": "success",
901
+ "stats": overview["metrics"]
902
+ })
903
+ except Exception as e:
904
+ return jsonify({"error": str(e)}), 500
905
+
906
+ # ─────────────── بدء Flask في خيط منفصل ───────────────
907
+ def start_flask_server():
908
+ """بدء خادم Flask للعقد الحقيقية"""
909
+ port = 7861 # منفذ مختلف عن Gradio
910
+ flask_app.run(
911
+ host="0.0.0.0",
912
+ port=port,
913
+ debug=False,
914
+ threaded=True,
915
+ use_reloader=False
916
+ )
917
+
918
+ # ─────────────── الدالة الرئيسية ───────────────
919
+ def main():
920
+ """الدالة الرئيسية"""
921
+ logger.info("🚀 بدء تشغيل تطبيق Hugging Face كخادم مركزي")
922
+
923
+ # بدء خادم Flask للAPI في خيط منفصل
924
+ flask_thread = threading.Thread(target=start_flask_server, daemon=True)
925
+ flask_thread.start()
926
+ logger.info("✅ بدء خادم Flask API على المنفذ 7861")
927
+
928
+ # إنشاء واجهة Gradio
929
+ demo = create_interface()
930
+
931
+ # تشغيل Gradio
932
+ demo.launch(
933
+ server_name="0.0.0.0",
934
+ server_port=7860,
935
+ share=False,
936
+ debug=False
937
+ )
938
 
939
  if __name__ == "__main__":
940
  # تشغيل FastAPI في thread منفصل