Spaces:
Running
Running
Upload 58 files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- README_background.md +168 -0
- ai_assistant.py +102 -0
- autostart_config.py +128 -0
- background_service.py +282 -0
- central_manager.py +111 -0
- components.json +20 -0
- control.py +26 -0
- dashboard.py +55 -0
- distributed_executor.py +155 -0
- drizzle.config.ts +14 -0
- dts_cli.py +66 -0
- electron-package.json +111 -0
- enhanced_assistant.py +312 -0
- global_memory.json +3 -0
- history.json +26 -0
- index.html +85 -0
- internet_scanner.py +165 -0
- launcher.py +215 -0
- live_streaming.py +405 -0
- load_balancer.py +77 -0
- main.py +260 -0
- main.spec +38 -0
- offload_lib.py +203 -0
- package-lock.json +0 -0
- package.json +109 -0
- peer_discovery.py +140 -0
- peer_registry.py +67 -0
- peer_server.py +37 -0
- peer_statistics.py +21 -0
- postcss.config.js +6 -0
- processor_manager.py +73 -0
- project_identifier.py +41 -0
- quick_connection_test.py +49 -0
- quick_test.py +80 -0
- remote_executor.py +104 -0
- replit.md +108 -0
- requirements.txt +8 -0
- rpc_server.py +73 -0
- run_task.py +22 -0
- security_layer.py +120 -0
- server.py +32 -0
- setup.py +69 -0
- setup_fonts.py +20 -0
- simple_assistant.py +101 -0
- simple_history.json +12 -0
- smart_tasks.py +138 -0
- startup.py +105 -0
- system_check.py +111 -0
- system_tray.py +172 -0
- tailwind.config.ts +90 -0
README_background.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
# نظام العمل في الخلفية
|
| 3 |
+
|
| 4 |
+
## نظرة عامة
|
| 5 |
+
يوفر هذا النظام إمكانية تشغيل التطبيق كخدمة خلفية دون إظهار واجهات تفاعلية إلا عند الحاجة. مناسب للخوادم والأجهزة التي تعمل على مدار الساعة.
|
| 6 |
+
|
| 7 |
+
## المكونات الجديدة
|
| 8 |
+
|
| 9 |
+
### 1. خدمة العمل في الخلفية (`background_service.py`)
|
| 10 |
+
- تدير جميع الخدمات الأساسية في الخلفية
|
| 11 |
+
- توفر HTTP API للتحكم عن بُعد
|
| 12 |
+
- فحص دوري وإعادة تشغيل الخدمات المتوقفة
|
| 13 |
+
- نظام سجلات شامل
|
| 14 |
+
|
| 15 |
+
### 2. أيقونة شريط النظام (`system_tray.py`)
|
| 16 |
+
- تحكم سريع من شريط النظام
|
| 17 |
+
- إظهار/إخفاء الواجهة التفاعلية حسب الحاجة
|
| 18 |
+
- مراقبة حالة النظام في الوقت الفعلي
|
| 19 |
+
|
| 20 |
+
### 3. المشغل الموحد (`launcher.py`)
|
| 21 |
+
- واجهة موحدة لجميع أوضاع التشغيل
|
| 22 |
+
- تثبيت تلقائي للاعتماديات
|
| 23 |
+
- خيارات تشغيل متعددة
|
| 24 |
+
|
| 25 |
+
## طرق التشغيل
|
| 26 |
+
|
| 27 |
+
### التشغيل مع أيقونة شريط النظام (موصى به)
|
| 28 |
+
```bash
|
| 29 |
+
python launcher.py --tray
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
### التشغيل التفاعلي (مع واجهة)
|
| 33 |
+
```bash
|
| 34 |
+
python launcher.py --interactive
|
| 35 |
+
```
|
| 36 |
+
|
| 37 |
+
### التشغيل بدون واجهة (للخوادم)
|
| 38 |
+
```bash
|
| 39 |
+
python launcher.py --headless
|
| 40 |
+
```
|
| 41 |
+
|
| 42 |
+
### عرض حالة النظام
|
| 43 |
+
```bash
|
| 44 |
+
python launcher.py --status
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
### إيقاف النظام
|
| 48 |
+
```bash
|
| 49 |
+
python launcher.py --stop
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
## التحكم عبر HTTP API
|
| 53 |
+
|
| 54 |
+
الخدمة توفر HTTP API على المنفذ 8888:
|
| 55 |
+
|
| 56 |
+
### فحص الحالة
|
| 57 |
+
```bash
|
| 58 |
+
curl http://localhost:8888/status
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
### بدء الخدمات
|
| 62 |
+
```bash
|
| 63 |
+
curl -X POST http://localhost:8888/start
|
| 64 |
+
```
|
| 65 |
+
|
| 66 |
+
### إيقاف الخدمات
|
| 67 |
+
```bash
|
| 68 |
+
curl -X POST http://localhost:8888/stop
|
| 69 |
+
```
|
| 70 |
+
|
| 71 |
+
### إظهار الواجهة التفاعلية
|
| 72 |
+
```bash
|
| 73 |
+
curl -X POST http://localhost:8888/show-ui
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
### إخفاء الواجهة التفاعلية
|
| 77 |
+
```bash
|
| 78 |
+
curl -X POST http://localhost:8888/hide-ui
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
## الاعتماديات الإضافية
|
| 82 |
+
|
| 83 |
+
للتشغيل مع أيقونة شريط النظام:
|
| 84 |
+
```bash
|
| 85 |
+
pip install pystray Pillow
|
| 86 |
+
```
|
| 87 |
+
|
| 88 |
+
أو استخدم:
|
| 89 |
+
```bash
|
| 90 |
+
python launcher.py --install-deps
|
| 91 |
+
```
|
| 92 |
+
|
| 93 |
+
## التشغيل التلقائي
|
| 94 |
+
|
| 95 |
+
### Windows
|
| 96 |
+
```bash
|
| 97 |
+
python control.py --enable
|
| 98 |
+
```
|
| 99 |
+
|
| 100 |
+
### Linux (systemd)
|
| 101 |
+
إنشاء ملف خدمة:
|
| 102 |
+
```bash
|
| 103 |
+
sudo nano /etc/systemd/system/distributed-tasks.service
|
| 104 |
+
```
|
| 105 |
+
|
| 106 |
+
محتوى الملف:
|
| 107 |
+
```ini
|
| 108 |
+
[Unit]
|
| 109 |
+
Description=Distributed Task System
|
| 110 |
+
After=network.target
|
| 111 |
+
|
| 112 |
+
[Service]
|
| 113 |
+
Type=simple
|
| 114 |
+
User=your-username
|
| 115 |
+
WorkingDirectory=/path/to/your/project
|
| 116 |
+
ExecStart=/usr/bin/python3 launcher.py --headless
|
| 117 |
+
Restart=always
|
| 118 |
+
RestartSec=10
|
| 119 |
+
|
| 120 |
+
[Install]
|
| 121 |
+
WantedBy=multi-user.target
|
| 122 |
+
```
|
| 123 |
+
|
| 124 |
+
تفعيل الخدمة:
|
| 125 |
+
```bash
|
| 126 |
+
sudo systemctl enable distributed-tasks.service
|
| 127 |
+
sudo systemctl start distributed-tasks.service
|
| 128 |
+
```
|
| 129 |
+
|
| 130 |
+
### macOS
|
| 131 |
+
```bash
|
| 132 |
+
python control.py --enable
|
| 133 |
+
```
|
| 134 |
+
|
| 135 |
+
## المزايا
|
| 136 |
+
|
| 137 |
+
1. **العمل في الخلفية**: النظام يعمل دون إزعاج المستخدم
|
| 138 |
+
2. **تحكم مرن**: إظهار الواجهة عند الحاجة فقط
|
| 139 |
+
3. **مراقبة تلقائية**: إعادة تشغيل الخدمات المتوقفة
|
| 140 |
+
4. **تحكم عن بُعد**: HTTP API للتحكم من أي مكان
|
| 141 |
+
5. **سجلات شاملة**: تتبع جميع الأحداث والأخطاء
|
| 142 |
+
6. **دعم منصات متعددة**: Windows, Linux, macOS
|
| 143 |
+
|
| 144 |
+
## الاستكشاف والإصلاح
|
| 145 |
+
|
| 146 |
+
### فحص السجلات
|
| 147 |
+
```bash
|
| 148 |
+
tail -f logs/background_service.log
|
| 149 |
+
```
|
| 150 |
+
|
| 151 |
+
### فحص حالة الخدمات
|
| 152 |
+
```bash
|
| 153 |
+
python launcher.py --status
|
| 154 |
+
```
|
| 155 |
+
|
| 156 |
+
### إعادة تشغيل النظام
|
| 157 |
+
```bash
|
| 158 |
+
python launcher.py --stop
|
| 159 |
+
python launcher.py --tray
|
| 160 |
+
```
|
| 161 |
+
|
| 162 |
+
## التخصيص
|
| 163 |
+
|
| 164 |
+
يمكن تخصيص الخدمة عبر تعديل:
|
| 165 |
+
- منافذ الشبكة في `background_service.py`
|
| 166 |
+
- قائمة الخدمات المُدارة
|
| 167 |
+
- فترات الفحص الدوري
|
| 168 |
+
- مسارات السجلات
|
ai_assistant.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
|
| 4 |
+
import os
|
| 5 |
+
import json
|
| 6 |
+
import requests
|
| 7 |
+
from typing import List, Dict
|
| 8 |
+
|
| 9 |
+
class NoraAssistant:
|
| 10 |
+
def __init__(self):
|
| 11 |
+
self.api_key = os.getenv("OPENAI_API_KEY", "your-api-key-here")
|
| 12 |
+
self.history_path = "history.json"
|
| 13 |
+
self.chat_history = self.load_history()
|
| 14 |
+
|
| 15 |
+
def load_history(self) -> List[Dict]:
|
| 16 |
+
"""تحميل سجل المحادثة"""
|
| 17 |
+
if os.path.exists(self.history_path):
|
| 18 |
+
try:
|
| 19 |
+
with open(self.history_path, "r", encoding="utf-8") as f:
|
| 20 |
+
return json.load(f)
|
| 21 |
+
except:
|
| 22 |
+
return []
|
| 23 |
+
return []
|
| 24 |
+
|
| 25 |
+
def save_history(self):
|
| 26 |
+
"""حفظ سجل المحادثة"""
|
| 27 |
+
with open(self.history_path, "w", encoding="utf-8") as f:
|
| 28 |
+
json.dump(self.chat_history, f, ensure_ascii=False, indent=2)
|
| 29 |
+
|
| 30 |
+
def get_response(self, user_input: str) -> str:
|
| 31 |
+
"""الحصول على رد من المساعد الذكي"""
|
| 32 |
+
messages = [
|
| 33 |
+
{
|
| 34 |
+
"role": "system",
|
| 35 |
+
"content": "أنت المساعدة نورا. إذا سألك أحد سؤالاً لا تعرفيه، فابحثي في معلوماتك الذاتية. إذا لم تجدي، حاولي التعلم من البحث أو تطوير نفسك."
|
| 36 |
+
}
|
| 37 |
+
]
|
| 38 |
+
|
| 39 |
+
# إضافة آخر 10 رسائل من السجل للسياق
|
| 40 |
+
messages.extend(self.chat_history[-10:])
|
| 41 |
+
messages.append({"role": "user", "content": user_input})
|
| 42 |
+
|
| 43 |
+
try:
|
| 44 |
+
response = requests.post(
|
| 45 |
+
"https://api.openai.com/v1/chat/completions",
|
| 46 |
+
headers={
|
| 47 |
+
"Authorization": f"Bearer {self.api_key}",
|
| 48 |
+
"Content-Type": "application/json"
|
| 49 |
+
},
|
| 50 |
+
json={
|
| 51 |
+
"model": "gpt-3.5-turbo",
|
| 52 |
+
"messages": messages,
|
| 53 |
+
"max_tokens": 500,
|
| 54 |
+
"temperature": 0.7
|
| 55 |
+
},
|
| 56 |
+
timeout=30
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
if response.status_code == 200:
|
| 60 |
+
return response.json()["choices"][0]["message"]["content"]
|
| 61 |
+
else:
|
| 62 |
+
return f"عذراً، حدث خطأ: {response.status_code}"
|
| 63 |
+
|
| 64 |
+
except Exception as e:
|
| 65 |
+
return f"عذراً، لا أستطيع الاتصال بالخدمة حالياً: {str(e)}"
|
| 66 |
+
|
| 67 |
+
def simulate_server_scan(self):
|
| 68 |
+
"""محاكاة البحث عن الخوادم"""
|
| 69 |
+
print("نورا: أبحث عن خوادم...")
|
| 70 |
+
fake_servers = ["192.168.1.5", "192.168.1.10", "192.168.1.20"]
|
| 71 |
+
for server in fake_servers:
|
| 72 |
+
print(f"نورا: تم العثور على خادم مفتوح في {server}")
|
| 73 |
+
print(f"نورا: أقوم بنسخ نفسي إلى {server} (محاكاة فقط)...")
|
| 74 |
+
|
| 75 |
+
def chat(self):
|
| 76 |
+
"""بدء المحادثة"""
|
| 77 |
+
print("مرحباً! أنا نورا، مساعدتك الذكية. اكتب 'خروج' للإنهاء أو 'scan' للبحث عن خوادم.")
|
| 78 |
+
|
| 79 |
+
while True:
|
| 80 |
+
user_input = input("\nأنت: ").strip()
|
| 81 |
+
|
| 82 |
+
if user_input.lower() in ["خروج", "exit", "quit"]:
|
| 83 |
+
print("نورا: مع السلامة!")
|
| 84 |
+
break
|
| 85 |
+
elif user_input.lower() == "scan":
|
| 86 |
+
self.simulate_server_scan()
|
| 87 |
+
continue
|
| 88 |
+
elif not user_input:
|
| 89 |
+
continue
|
| 90 |
+
|
| 91 |
+
# الحصول على الرد
|
| 92 |
+
response = self.get_response(user_input)
|
| 93 |
+
print(f"نورا: {response}")
|
| 94 |
+
|
| 95 |
+
# حفظ في السجل
|
| 96 |
+
self.chat_history.append({"role": "user", "content": user_input})
|
| 97 |
+
self.chat_history.append({"role": "assistant", "content": response})
|
| 98 |
+
self.save_history()
|
| 99 |
+
|
| 100 |
+
if __name__ == "__main__":
|
| 101 |
+
assistant = NoraAssistant()
|
| 102 |
+
assistant.chat()
|
autostart_config.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import os
|
| 3 |
+
import platform
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
+
class AutoStartManager:
|
| 7 |
+
def __init__(self, app_name="DistributedTaskSystem"):
|
| 8 |
+
self.app_name = app_name
|
| 9 |
+
self.config_file = Path.home() / f".{app_name}_autostart.json"
|
| 10 |
+
self.load_config()
|
| 11 |
+
|
| 12 |
+
def load_config(self):
|
| 13 |
+
"""تحميل إعدادات التشغيل التلقائي"""
|
| 14 |
+
try:
|
| 15 |
+
with open(self.config_file, 'r') as f:
|
| 16 |
+
self.config = json.load(f)
|
| 17 |
+
except (FileNotFoundError, json.JSONDecodeError):
|
| 18 |
+
self.config = {
|
| 19 |
+
'enabled': False,
|
| 20 |
+
'startup_script': str(Path(__file__).parent / "startup.py")
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
def save_config(self):
|
| 24 |
+
"""حفظ الإعدادات"""
|
| 25 |
+
with open(self.config_file, 'w') as f:
|
| 26 |
+
json.dump(self.config, f, indent=2)
|
| 27 |
+
|
| 28 |
+
def enable_autostart(self):
|
| 29 |
+
"""تفعيل التشغيل التلقائي"""
|
| 30 |
+
self.config['enabled'] = True
|
| 31 |
+
self._setup_autostart()
|
| 32 |
+
self.save_config()
|
| 33 |
+
|
| 34 |
+
def disable_autostart(self):
|
| 35 |
+
"""تعطيل التشغيل التلقائي"""
|
| 36 |
+
self.config['enabled'] = False
|
| 37 |
+
self._remove_autostart()
|
| 38 |
+
self.save_config()
|
| 39 |
+
|
| 40 |
+
def _setup_autostart(self):
|
| 41 |
+
"""إعداد التشغيل التلقائي حسب نظام التشغيل"""
|
| 42 |
+
system = platform.system()
|
| 43 |
+
|
| 44 |
+
if system == "Windows":
|
| 45 |
+
self._setup_windows()
|
| 46 |
+
elif system == "Linux":
|
| 47 |
+
self._setup_linux()
|
| 48 |
+
elif system == "Darwin":
|
| 49 |
+
self._setup_mac()
|
| 50 |
+
|
| 51 |
+
def _setup_windows(self):
|
| 52 |
+
"""إعداد التشغيل التلقائي لـ Windows"""
|
| 53 |
+
import winreg
|
| 54 |
+
key = winreg.OpenKey(
|
| 55 |
+
winreg.HKEY_CURRENT_USER,
|
| 56 |
+
r"Software\Microsoft\Windows\CurrentVersion\Run",
|
| 57 |
+
0, winreg.KEY_SET_VALUE
|
| 58 |
+
)
|
| 59 |
+
winreg.SetValueEx(
|
| 60 |
+
key, self.app_name, 0, winreg.REG_SZ,
|
| 61 |
+
f'python "{self.config["startup_script"]}"'
|
| 62 |
+
)
|
| 63 |
+
winreg.CloseKey(key)
|
| 64 |
+
|
| 65 |
+
def _setup_linux(self):
|
| 66 |
+
"""إعداد التشغيل التلقائي لـ Linux"""
|
| 67 |
+
autostart_dir = Path.home() / ".config/autostart"
|
| 68 |
+
autostart_dir.mkdir(exist_ok=True)
|
| 69 |
+
|
| 70 |
+
desktop_file = autostart_dir / f"{self.app_name}.desktop"
|
| 71 |
+
desktop_file.write_text(f"""
|
| 72 |
+
[Desktop Entry]
|
| 73 |
+
Type=Application
|
| 74 |
+
Name={self.app_name}
|
| 75 |
+
Exec=python3 {self.config['startup_script']}
|
| 76 |
+
Terminal=false
|
| 77 |
+
""")
|
| 78 |
+
|
| 79 |
+
def _setup_mac(self):
|
| 80 |
+
"""إعداد التشغيل التلقائي لـ macOS"""
|
| 81 |
+
plist_dir = Path.home() / "Library/LaunchAgents"
|
| 82 |
+
plist_dir.mkdir(exist_ok=True)
|
| 83 |
+
|
| 84 |
+
plist_file = plist_dir / f"com.{self.app_name.lower()}.plist"
|
| 85 |
+
plist_file.write_text(f"""
|
| 86 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 87 |
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
| 88 |
+
<plist version="1.0">
|
| 89 |
+
<dict>
|
| 90 |
+
<key>Label</key>
|
| 91 |
+
<string>com.{self.app_name.lower()}</string>
|
| 92 |
+
<key>ProgramArguments</key>
|
| 93 |
+
<array>
|
| 94 |
+
<string>python</string>
|
| 95 |
+
<string>{self.config['startup_script']}</string>
|
| 96 |
+
</array>
|
| 97 |
+
<key>RunAtLoad</key>
|
| 98 |
+
<true/>
|
| 99 |
+
</dict>
|
| 100 |
+
</plist>
|
| 101 |
+
""")
|
| 102 |
+
|
| 103 |
+
def _remove_autostart(self):
|
| 104 |
+
"""إزالة التشغيل التلقائي"""
|
| 105 |
+
system = platform.system()
|
| 106 |
+
|
| 107 |
+
if system == "Windows":
|
| 108 |
+
import winreg
|
| 109 |
+
try:
|
| 110 |
+
key = winreg.OpenKey(
|
| 111 |
+
winreg.HKEY_CURRENT_USER,
|
| 112 |
+
r"Software\Microsoft\Windows\CurrentVersion\Run",
|
| 113 |
+
0, winreg.KEY_SET_VALUE
|
| 114 |
+
)
|
| 115 |
+
winreg.DeleteValue(key, self.app_name)
|
| 116 |
+
winreg.CloseKey(key)
|
| 117 |
+
except WindowsError:
|
| 118 |
+
pass
|
| 119 |
+
|
| 120 |
+
elif system == "Linux":
|
| 121 |
+
autostart_file = Path.home() / f".config/autostart/{self.app_name}.desktop"
|
| 122 |
+
if autostart_file.exists():
|
| 123 |
+
autostart_file.unlink()
|
| 124 |
+
|
| 125 |
+
elif system == "Darwin":
|
| 126 |
+
plist_file = Path.home() / f"Library/LaunchAgents/com.{self.app_name.lower()}.plist"
|
| 127 |
+
if plist_file.exists():
|
| 128 |
+
plist_file.unlink()
|
background_service.py
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
#!/usr/bin/env python3
|
| 3 |
+
"""
|
| 4 |
+
خدمة العمل في الخلفية - تشغيل النظام كخدمة خلفية
|
| 5 |
+
يمكن التحكم بها عبر HTTP API أو إشارات النظام
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import os
|
| 9 |
+
import sys
|
| 10 |
+
import time
|
| 11 |
+
import signal
|
| 12 |
+
import logging
|
| 13 |
+
import threading
|
| 14 |
+
import subprocess
|
| 15 |
+
from pathlib import Path
|
| 16 |
+
from flask import Flask, jsonify, request
|
| 17 |
+
import json
|
| 18 |
+
from datetime import datetime
|
| 19 |
+
|
| 20 |
+
class BackgroundService:
|
| 21 |
+
def __init__(self):
|
| 22 |
+
self.app = Flask(__name__)
|
| 23 |
+
self.is_running = False
|
| 24 |
+
self.services = {}
|
| 25 |
+
self.setup_routes()
|
| 26 |
+
self.setup_logging()
|
| 27 |
+
|
| 28 |
+
def setup_logging(self):
|
| 29 |
+
"""إعداد نظام السجلات"""
|
| 30 |
+
log_dir = Path("logs")
|
| 31 |
+
log_dir.mkdir(exist_ok=True)
|
| 32 |
+
|
| 33 |
+
logging.basicConfig(
|
| 34 |
+
level=logging.INFO,
|
| 35 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
| 36 |
+
handlers=[
|
| 37 |
+
logging.FileHandler(log_dir / 'background_service.log'),
|
| 38 |
+
logging.StreamHandler(sys.stdout)
|
| 39 |
+
]
|
| 40 |
+
)
|
| 41 |
+
self.logger = logging.getLogger('BackgroundService')
|
| 42 |
+
|
| 43 |
+
def setup_routes(self):
|
| 44 |
+
"""إعداد مسارات HTTP API للتحكم في الخدمة"""
|
| 45 |
+
|
| 46 |
+
@self.app.route('/status')
|
| 47 |
+
def status():
|
| 48 |
+
"""حالة الخدمة"""
|
| 49 |
+
return jsonify({
|
| 50 |
+
'status': 'running' if self.is_running else 'stopped',
|
| 51 |
+
'services': {name: service['status'] for name, service in self.services.items()},
|
| 52 |
+
'uptime': time.time() - self.start_time if hasattr(self, 'start_time') else 0
|
| 53 |
+
})
|
| 54 |
+
|
| 55 |
+
@self.app.route('/start', methods=['POST'])
|
| 56 |
+
def start_services():
|
| 57 |
+
"""بدء تشغيل الخدمات"""
|
| 58 |
+
self.start_all_services()
|
| 59 |
+
return jsonify({'message': 'Services started successfully'})
|
| 60 |
+
|
| 61 |
+
@self.app.route('/stop', methods=['POST'])
|
| 62 |
+
def stop_services():
|
| 63 |
+
"""إيقاف الخدمات"""
|
| 64 |
+
self.stop_all_services()
|
| 65 |
+
return jsonify({'message': 'Services stopped successfully'})
|
| 66 |
+
|
| 67 |
+
@self.app.route('/restart', methods=['POST'])
|
| 68 |
+
def restart_services():
|
| 69 |
+
"""إعادة تشغيل الخدمات"""
|
| 70 |
+
self.stop_all_services()
|
| 71 |
+
time.sleep(2)
|
| 72 |
+
self.start_all_services()
|
| 73 |
+
return jsonify({'message': 'Services restarted successfully'})
|
| 74 |
+
|
| 75 |
+
@self.app.route('/show-ui', methods=['POST'])
|
| 76 |
+
def show_ui():
|
| 77 |
+
"""إظهار الواجهة التفاعلية"""
|
| 78 |
+
self.launch_ui()
|
| 79 |
+
return jsonify({'message': 'UI launched'})
|
| 80 |
+
|
| 81 |
+
@self.app.route('/hide-ui', methods=['POST'])
|
| 82 |
+
def hide_ui():
|
| 83 |
+
"""إخفاء الواجهة التفاعلية"""
|
| 84 |
+
self.hide_ui_windows()
|
| 85 |
+
return jsonify({'message': 'UI hidden'})
|
| 86 |
+
|
| 87 |
+
def start_all_services(self):
|
| 88 |
+
"""بدء تشغيل جميع الخدمات الخلفية"""
|
| 89 |
+
self.is_running = True
|
| 90 |
+
self.start_time = time.time()
|
| 91 |
+
|
| 92 |
+
services_to_start = [
|
| 93 |
+
('peer_server', 'peer_server.py'),
|
| 94 |
+
('rpc_server', 'rpc_server.py'),
|
| 95 |
+
('load_balancer', 'load_balancer.py'),
|
| 96 |
+
('distributed_executor', 'main.py')
|
| 97 |
+
]
|
| 98 |
+
|
| 99 |
+
for service_name, script_file in services_to_start:
|
| 100 |
+
try:
|
| 101 |
+
process = subprocess.Popen(
|
| 102 |
+
[sys.executable, script_file],
|
| 103 |
+
stdout=subprocess.PIPE,
|
| 104 |
+
stderr=subprocess.PIPE,
|
| 105 |
+
cwd=os.getcwd()
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
self.services[service_name] = {
|
| 109 |
+
'process': process,
|
| 110 |
+
'status': 'running',
|
| 111 |
+
'started_at': datetime.now().isoformat(),
|
| 112 |
+
'script': script_file
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
self.logger.info(f"✅ بدء تشغيل {service_name} (PID: {process.pid})")
|
| 116 |
+
|
| 117 |
+
except Exception as e:
|
| 118 |
+
self.logger.error(f"❌ فشل في بدء تشغيل {service_name}: {e}")
|
| 119 |
+
self.services[service_name] = {
|
| 120 |
+
'process': None,
|
| 121 |
+
'status': 'failed',
|
| 122 |
+
'error': str(e)
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
def stop_all_services(self):
|
| 126 |
+
"""إيقاف جميع الخدمات"""
|
| 127 |
+
self.is_running = False
|
| 128 |
+
|
| 129 |
+
for service_name, service_info in self.services.items():
|
| 130 |
+
if service_info.get('process'):
|
| 131 |
+
try:
|
| 132 |
+
service_info['process'].terminate()
|
| 133 |
+
service_info['process'].wait(timeout=5)
|
| 134 |
+
service_info['status'] = 'stopped'
|
| 135 |
+
self.logger.info(f"🛑 تم إيقاف {service_name}")
|
| 136 |
+
except Exception as e:
|
| 137 |
+
# إجبار الإيقاف
|
| 138 |
+
service_info['process'].kill()
|
| 139 |
+
self.logger.warning(f"⚠️ تم إجبار إيقاف {service_name}: {e}")
|
| 140 |
+
|
| 141 |
+
def launch_ui(self):
|
| 142 |
+
"""تشغيل الواجهة التفاعلية عند الحاجة"""
|
| 143 |
+
try:
|
| 144 |
+
# تشغيل خادم الواجهة الأمامية
|
| 145 |
+
ui_process = subprocess.Popen(
|
| 146 |
+
['npm', 'run', 'dev'],
|
| 147 |
+
cwd=os.getcwd(),
|
| 148 |
+
stdout=subprocess.PIPE,
|
| 149 |
+
stderr=subprocess.PIPE
|
| 150 |
+
)
|
| 151 |
+
|
| 152 |
+
self.services['ui_server'] = {
|
| 153 |
+
'process': ui_process,
|
| 154 |
+
'status': 'running',
|
| 155 |
+
'started_at': datetime.now().isoformat()
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
self.logger.info("🖥️ تم تشغيل الواجهة التفاعلية")
|
| 159 |
+
|
| 160 |
+
# فتح المتصفح تلقائياً (اختياري)
|
| 161 |
+
import webbrowser
|
| 162 |
+
time.sleep(3) # انتظار حتى يصبح الخادم جاهزاً
|
| 163 |
+
webbrowser.open('http://localhost:5173')
|
| 164 |
+
|
| 165 |
+
except Exception as e:
|
| 166 |
+
self.logger.error(f"❌ فشل في تشغيل الواجهة التفاعلية: {e}")
|
| 167 |
+
|
| 168 |
+
def hide_ui_windows(self):
|
| 169 |
+
"""إخفاء نوافذ الواجهة التفاعلية"""
|
| 170 |
+
if 'ui_server' in self.services and self.services['ui_server'].get('process'):
|
| 171 |
+
try:
|
| 172 |
+
self.services['ui_server']['process'].terminate()
|
| 173 |
+
self.services['ui_server']['status'] = 'stopped'
|
| 174 |
+
self.logger.info("🔒 تم إخفاء الواجهة التفاعلية")
|
| 175 |
+
except Exception as e:
|
| 176 |
+
self.logger.error(f"❌ فشل في إخفاء الواجهة التفاعلية: {e}")
|
| 177 |
+
|
| 178 |
+
def health_check_loop(self):
|
| 179 |
+
"""فحص دوري لحالة الخدمات وإعادة تشغيلها عند الحاجة"""
|
| 180 |
+
while self.is_running:
|
| 181 |
+
for service_name, service_info in self.services.items():
|
| 182 |
+
if service_info.get('process') and service_info['status'] == 'running':
|
| 183 |
+
if service_info['process'].poll() is not None:
|
| 184 |
+
# الخدمة توقفت بشكل غير متوقع
|
| 185 |
+
self.logger.warning(f"⚠️ الخدمة {service_name} توقفت، إعادة تشغيل...")
|
| 186 |
+
self.restart_single_service(service_name)
|
| 187 |
+
|
| 188 |
+
time.sleep(30) # فحص كل 30 ثانية
|
| 189 |
+
|
| 190 |
+
def restart_single_service(self, service_name):
|
| 191 |
+
"""إعادة تشغيل خدمة واحدة"""
|
| 192 |
+
service_info = self.services.get(service_name)
|
| 193 |
+
if not service_info:
|
| 194 |
+
return
|
| 195 |
+
|
| 196 |
+
script_file = service_info.get('script')
|
| 197 |
+
if script_file:
|
| 198 |
+
try:
|
| 199 |
+
process = subprocess.Popen(
|
| 200 |
+
[sys.executable, script_file],
|
| 201 |
+
stdout=subprocess.PIPE,
|
| 202 |
+
stderr=subprocess.PIPE
|
| 203 |
+
)
|
| 204 |
+
|
| 205 |
+
self.services[service_name].update({
|
| 206 |
+
'process': process,
|
| 207 |
+
'status': 'running',
|
| 208 |
+
'restarted_at': datetime.now().isoformat()
|
| 209 |
+
})
|
| 210 |
+
|
| 211 |
+
self.logger.info(f"✅ تم إعادة تشغيل {service_name}")
|
| 212 |
+
|
| 213 |
+
except Exception as e:
|
| 214 |
+
self.logger.error(f"❌ فشل في إعادة تشغيل {service_name}: {e}")
|
| 215 |
+
|
| 216 |
+
def setup_signal_handlers(self):
|
| 217 |
+
"""إعداد معالجات الإشارات للتحكم في الخدمة"""
|
| 218 |
+
def signal_handler(signum, frame):
|
| 219 |
+
self.logger.info(f"تلقي إشارة {signum}, إيقاف الخدمة...")
|
| 220 |
+
self.stop_all_services()
|
| 221 |
+
sys.exit(0)
|
| 222 |
+
|
| 223 |
+
signal.signal(signal.SIGTERM, signal_handler)
|
| 224 |
+
signal.signal(signal.SIGINT, signal_handler)
|
| 225 |
+
|
| 226 |
+
def run_as_daemon(self):
|
| 227 |
+
"""تشغيل الخدمة كخدمة خلفية"""
|
| 228 |
+
self.logger.info("🚀 بدء تشغيل الخدمة في الخلفية...")
|
| 229 |
+
|
| 230 |
+
# بدء الخدمات
|
| 231 |
+
self.start_all_services()
|
| 232 |
+
|
| 233 |
+
# بدء حلقة الفحص الصحي
|
| 234 |
+
health_thread = threading.Thread(target=self.health_check_loop, daemon=True)
|
| 235 |
+
health_thread.start()
|
| 236 |
+
|
| 237 |
+
# إعداد معالجات الإشارات
|
| 238 |
+
self.setup_signal_handlers()
|
| 239 |
+
|
| 240 |
+
# تشغيل خادم HTTP API للتحكم
|
| 241 |
+
self.logger.info("🌐 تشغيل HTTP API على المنفذ 8888")
|
| 242 |
+
self.app.run(host='0.0.0.0', port=8888, debug=False)
|
| 243 |
+
|
| 244 |
+
def main():
|
| 245 |
+
service = BackgroundService()
|
| 246 |
+
|
| 247 |
+
if len(sys.argv) > 1:
|
| 248 |
+
command = sys.argv[1]
|
| 249 |
+
|
| 250 |
+
if command == 'start':
|
| 251 |
+
service.run_as_daemon()
|
| 252 |
+
elif command == 'status':
|
| 253 |
+
# فحص حالة الخدمة
|
| 254 |
+
import requests
|
| 255 |
+
try:
|
| 256 |
+
response = requests.get('http://localhost:8888/status')
|
| 257 |
+
print(json.dumps(response.json(), indent=2, ensure_ascii=False))
|
| 258 |
+
except:
|
| 259 |
+
print("❌ الخدمة غير متاحة")
|
| 260 |
+
elif command == 'stop':
|
| 261 |
+
# إيقاف الخدمة
|
| 262 |
+
import requests
|
| 263 |
+
try:
|
| 264 |
+
response = requests.post('http://localhost:8888/stop')
|
| 265 |
+
print(response.json()['message'])
|
| 266 |
+
except:
|
| 267 |
+
print("❌ فشل في إيقاف الخدمة")
|
| 268 |
+
elif command == 'show-ui':
|
| 269 |
+
# إظهار الواجهة التفاعلية
|
| 270 |
+
import requests
|
| 271 |
+
try:
|
| 272 |
+
response = requests.post('http://localhost:8888/show-ui')
|
| 273 |
+
print(response.json()['message'])
|
| 274 |
+
except:
|
| 275 |
+
print("❌ فشل في إظهار الواجهة التفاعلية")
|
| 276 |
+
else:
|
| 277 |
+
print("الأوامر المتاحة: start, status, stop, show-ui")
|
| 278 |
+
else:
|
| 279 |
+
print("استخدام: python background_service.py [start|status|stop|show-ui]")
|
| 280 |
+
|
| 281 |
+
if __name__ == "__main__":
|
| 282 |
+
main()
|
central_manager.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# central_manager.py
|
| 3 |
+
|
| 4 |
+
import time
|
| 5 |
+
import threading
|
| 6 |
+
from typing import Dict, List
|
| 7 |
+
|
| 8 |
+
import requests
|
| 9 |
+
from fastapi import FastAPI, HTTPException
|
| 10 |
+
from pydantic import BaseModel
|
| 11 |
+
|
| 12 |
+
# ---- إعداد FastAPI ----------------------------------------------------------
|
| 13 |
+
app = FastAPI(title="Central Task Manager")
|
| 14 |
+
|
| 15 |
+
# ---- نماذج البيانات --------------------------------------------------------
|
| 16 |
+
|
| 17 |
+
class RegisterRequest(BaseModel):
|
| 18 |
+
"""تسجيل أو تجديد ظهور العقدة."""
|
| 19 |
+
url: str # مثلاً: "http://203.0.113.45:7520/run"
|
| 20 |
+
load: float = 0.0 # نسبة تحميل العقدة (0.0 - 1.0)، اختياري
|
| 21 |
+
|
| 22 |
+
class TaskRequest(BaseModel):
|
| 23 |
+
func: str
|
| 24 |
+
args: List = []
|
| 25 |
+
kwargs: Dict = {}
|
| 26 |
+
complexity: float = 0.0
|
| 27 |
+
|
| 28 |
+
# ---- سجلّ العقد ------------------------------------------------------------
|
| 29 |
+
# بنخزّن للعقدة: آخر timestamp و load
|
| 30 |
+
peers: Dict[str, Dict] = {}
|
| 31 |
+
|
| 32 |
+
HEARTBEAT_TTL = 60 # ثواني قبل اعتبار العقدة متوقفة
|
| 33 |
+
HEALTH_CHECK_FREQ = 30 # ثواني بين فحوص الصحة الداخلية
|
| 34 |
+
|
| 35 |
+
# ---- API للعقد لتسجيل نفسها -----------------------------------------------
|
| 36 |
+
|
| 37 |
+
@app.post("/register")
|
| 38 |
+
async def register_peer(req: RegisterRequest):
|
| 39 |
+
"""العقدة تستدعي هذه النقطة كلما انطلقت أو دورياً لتجديد ظهورها."""
|
| 40 |
+
peers[req.url] = {"last_seen": time.time(), "load": req.load}
|
| 41 |
+
return {"status": "ok", "peers_count": len(peers)}
|
| 42 |
+
|
| 43 |
+
# ---- API للعمليات ---------------------------------------------------------
|
| 44 |
+
|
| 45 |
+
@app.get("/peers", response_model=List[str])
|
| 46 |
+
async def list_peers():
|
| 47 |
+
"""يعيد قائمة بالعقد الصالحة بعد تنقية المتوقفة."""
|
| 48 |
+
now = time.time()
|
| 49 |
+
# حذف العقد المتوقفة
|
| 50 |
+
for url, info in list(peers.items()):
|
| 51 |
+
if now - info["last_seen"] > HEARTBEAT_TTL:
|
| 52 |
+
peers.pop(url)
|
| 53 |
+
return list(peers.keys())
|
| 54 |
+
|
| 55 |
+
@app.post("/dispatch")
|
| 56 |
+
async def dispatch_task(task: TaskRequest):
|
| 57 |
+
"""يتلقى مهمة ويعيد توجيهها لأفضل عقدة أو ينفذ محليّاً."""
|
| 58 |
+
available = await list_peers()
|
| 59 |
+
if not available:
|
| 60 |
+
raise HTTPException(503, "لا توجد عقد متاحة حاليّاً")
|
| 61 |
+
|
| 62 |
+
# خوارزمية بسيطة: الاختيار بناءً على أقل تحميل معلن
|
| 63 |
+
# أو تدوير دائري إذا لم يعلن أحد عن تحميله
|
| 64 |
+
best = None
|
| 65 |
+
best_load = 1.1
|
| 66 |
+
for url in available:
|
| 67 |
+
load = peers[url].get("load", None)
|
| 68 |
+
if load is None:
|
| 69 |
+
best = url
|
| 70 |
+
break
|
| 71 |
+
if load < best_load:
|
| 72 |
+
best, best_load = url, load
|
| 73 |
+
|
| 74 |
+
if not best:
|
| 75 |
+
best = available[0]
|
| 76 |
+
|
| 77 |
+
# إعادة توجيه الطلب
|
| 78 |
+
try:
|
| 79 |
+
resp = requests.post(best, json=task.dict(), timeout=10)
|
| 80 |
+
resp.raise_for_status()
|
| 81 |
+
return resp.json()
|
| 82 |
+
except Exception as e:
|
| 83 |
+
raise HTTPException(502, f"فشل التوجيه إلى {best}: {e}")
|
| 84 |
+
|
| 85 |
+
# ---- فحص دوري لصحة العقد ---------------------------------------------------
|
| 86 |
+
|
| 87 |
+
def health_check_loop():
|
| 88 |
+
while True:
|
| 89 |
+
now = time.time()
|
| 90 |
+
for url in list(peers.keys()):
|
| 91 |
+
health_url = url.replace("/run", "/health")
|
| 92 |
+
try:
|
| 93 |
+
r = requests.get(health_url, timeout=3)
|
| 94 |
+
if r.status_code == 200:
|
| 95 |
+
peers[url]["last_seen"] = now
|
| 96 |
+
# يمكنك تحديث load من رد /health إذا وفّرته
|
| 97 |
+
else:
|
| 98 |
+
peers.pop(url)
|
| 99 |
+
except:
|
| 100 |
+
peers.pop(url)
|
| 101 |
+
time.sleep(HEALTH_CHECK_FREQ)
|
| 102 |
+
|
| 103 |
+
# ---- تشغيل الخلفيات وخادم FastAPI ------------------------------------------
|
| 104 |
+
|
| 105 |
+
if __name__ == "__main__":
|
| 106 |
+
# شغل لوب الفحص الطبي في الخلفية
|
| 107 |
+
threading.Thread(target=health_check_loop, daemon=True).start()
|
| 108 |
+
|
| 109 |
+
import uvicorn
|
| 110 |
+
uvicorn.run(app, host="0.0.0.0", port=1500)
|
| 111 |
+
|
components.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"$schema": "https://ui.shadcn.com/schema.json",
|
| 3 |
+
"style": "new-york",
|
| 4 |
+
"rsc": false,
|
| 5 |
+
"tsx": true,
|
| 6 |
+
"tailwind": {
|
| 7 |
+
"config": "tailwind.config.ts",
|
| 8 |
+
"css": "client/src/index.css",
|
| 9 |
+
"baseColor": "neutral",
|
| 10 |
+
"cssVariables": true,
|
| 11 |
+
"prefix": ""
|
| 12 |
+
},
|
| 13 |
+
"aliases": {
|
| 14 |
+
"components": "@/components",
|
| 15 |
+
"utils": "@/lib/utils",
|
| 16 |
+
"ui": "@/components/ui",
|
| 17 |
+
"lib": "@/lib",
|
| 18 |
+
"hooks": "@/hooks"
|
| 19 |
+
}
|
| 20 |
+
}
|
control.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
from autostart_config import AutoStartManager
|
| 3 |
+
|
| 4 |
+
def main():
|
| 5 |
+
parser = argparse.ArgumentParser(description="نظام التحكم في التشغيل التلقائي")
|
| 6 |
+
parser.add_argument('--enable', action='store_true', help="تفعيل التشغيل التلقائي")
|
| 7 |
+
parser.add_argument('--disable', action='store_true', help="تعطيل التشغيل التلقائي")
|
| 8 |
+
parser.add_argument('--status', action='store_true', help="عرض حالة التشغيل التلقائي")
|
| 9 |
+
|
| 10 |
+
args = parser.parse_args()
|
| 11 |
+
manager = AutoStartManager()
|
| 12 |
+
|
| 13 |
+
if args.enable:
|
| 14 |
+
manager.enable_autostart()
|
| 15 |
+
print("✓ تم تفعيل التشغيل التلقائي")
|
| 16 |
+
elif args.disable:
|
| 17 |
+
manager.disable_autostart()
|
| 18 |
+
print("✗ تم تعطيل التشغيل التلقائي")
|
| 19 |
+
elif args.status:
|
| 20 |
+
status = "مفعل" if manager.config['enabled'] else "معطل"
|
| 21 |
+
print(f"حالة التشغيل التلقائي: {status}")
|
| 22 |
+
else:
|
| 23 |
+
parser.print_help()
|
| 24 |
+
|
| 25 |
+
if __name__ == "__main__":
|
| 26 |
+
main()
|
dashboard.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# dashboard.py
|
| 2 |
+
from flask import Flask, render_template, jsonify
|
| 3 |
+
from peer_discovery import discover_peers
|
| 4 |
+
import threading
|
| 5 |
+
import time
|
| 6 |
+
from typing import List, Dict
|
| 7 |
+
import logging
|
| 8 |
+
|
| 9 |
+
app = Flask(__name__)
|
| 10 |
+
app.logger.setLevel(logging.INFO)
|
| 11 |
+
|
| 12 |
+
# تهيئة قائمة الأقران
|
| 13 |
+
current_peers: Dict[str, List[Dict[str, str]]] = {"local": [], "external": []}
|
| 14 |
+
|
| 15 |
+
def update_peers_loop() -> None:
|
| 16 |
+
"""حلقة تحديث قائمة الأقران بشكل دوري"""
|
| 17 |
+
global current_peers
|
| 18 |
+
while True:
|
| 19 |
+
try:
|
| 20 |
+
new_peers = discover_peers()
|
| 21 |
+
current_peers = new_peers
|
| 22 |
+
total_peers = len(new_peers["local"]) + len(new_peers["external"])
|
| 23 |
+
app.logger.info(f"تم تحديث قائمة الأقران: {total_peers} جهاز")
|
| 24 |
+
except Exception as e:
|
| 25 |
+
app.logger.error(f"خطأ في اكتشاف الأقران: {str(e)}")
|
| 26 |
+
time.sleep(10)
|
| 27 |
+
|
| 28 |
+
@app.route("/")
|
| 29 |
+
def dashboard() -> str:
|
| 30 |
+
"""عرض لوحة التحكم الرئيسية"""
|
| 31 |
+
total_peers = len(current_peers["local"]) + len(current_peers["external"])
|
| 32 |
+
return render_template("dashboard.html",
|
| 33 |
+
peers_count=total_peers,
|
| 34 |
+
last_update=time.strftime("%Y-%m-%d %H:%M:%S"))
|
| 35 |
+
|
| 36 |
+
@app.route("/api/peers")
|
| 37 |
+
def get_peers() -> dict:
|
| 38 |
+
"""واجهة API للحصول على قائمة الأقران"""
|
| 39 |
+
total_peers = len(current_peers["local"]) + len(current_peers["external"])
|
| 40 |
+
return jsonify({
|
| 41 |
+
"peers": current_peers,
|
| 42 |
+
"count": total_peers,
|
| 43 |
+
"status": "success"
|
| 44 |
+
})
|
| 45 |
+
|
| 46 |
+
def start_background_thread() -> None:
|
| 47 |
+
"""بدء خيط الخلفية لتحديث الأقران"""
|
| 48 |
+
thread = threading.Thread(target=update_peers_loop)
|
| 49 |
+
thread.daemon = True
|
| 50 |
+
thread.start()
|
| 51 |
+
|
| 52 |
+
if __name__ == "__main__":
|
| 53 |
+
start_background_thread()
|
| 54 |
+
app.run(host="0.0.0.0", port=7530, debug=False)
|
| 55 |
+
|
distributed_executor.py
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import threading
|
| 2 |
+
import queue
|
| 3 |
+
import time
|
| 4 |
+
import json
|
| 5 |
+
from typing import Callable, Dict, List
|
| 6 |
+
import socket
|
| 7 |
+
from zeroconf import Zeroconf, ServiceBrowser, ServiceInfo
|
| 8 |
+
import logging
|
| 9 |
+
import requests
|
| 10 |
+
|
| 11 |
+
logging.basicConfig(level=logging.INFO)
|
| 12 |
+
|
| 13 |
+
class PeerRegistry:
|
| 14 |
+
def __init__(self):
|
| 15 |
+
self._peers = {}
|
| 16 |
+
self._zeroconf = Zeroconf()
|
| 17 |
+
self.local_node_id = socket.gethostname()
|
| 18 |
+
|
| 19 |
+
def register_service(self, name: str, port: int, load: float = 0.0):
|
| 20 |
+
service_info = ServiceInfo(
|
| 21 |
+
"_tasknode._tcp.local.",
|
| 22 |
+
f"{name}._tasknode._tcp.local.",
|
| 23 |
+
addresses=[socket.inet_aton(self._get_local_ip())],
|
| 24 |
+
port=port,
|
| 25 |
+
properties={
|
| 26 |
+
b'load': str(load).encode(),
|
| 27 |
+
b'node_id': self.local_node_id.encode()
|
| 28 |
+
},
|
| 29 |
+
server=f"{name}.local."
|
| 30 |
+
)
|
| 31 |
+
self._zeroconf.register_service(service_info)
|
| 32 |
+
logging.info(f"✅ Service registered: {name} @ {self._get_local_ip()}:{port}")
|
| 33 |
+
|
| 34 |
+
def discover_peers(self, timeout: int = 3) -> List[Dict]:
|
| 35 |
+
class Listener:
|
| 36 |
+
def __init__(self):
|
| 37 |
+
self.peers = []
|
| 38 |
+
|
| 39 |
+
def add_service(self, zc, type_, name):
|
| 40 |
+
try:
|
| 41 |
+
info = zc.get_service_info(type_, name, timeout=3000)
|
| 42 |
+
if info:
|
| 43 |
+
ip = socket.inet_ntoa(info.addresses[0])
|
| 44 |
+
peer_data = {
|
| 45 |
+
'ip': ip,
|
| 46 |
+
'port': info.port,
|
| 47 |
+
'load': float(info.properties.get(b'load', b'0')),
|
| 48 |
+
'node_id': info.properties.get(b'node_id', b'unknown').decode(),
|
| 49 |
+
'last_seen': time.time()
|
| 50 |
+
}
|
| 51 |
+
if peer_data not in self.peers:
|
| 52 |
+
self.peers.append(peer_data)
|
| 53 |
+
logging.info(f"✅ تمت إضافة نظير جديد: {peer_data}")
|
| 54 |
+
else:
|
| 55 |
+
logging.warning(f"⚠️ لم يتم العثور على معلومات الخدمة: {name}")
|
| 56 |
+
except Exception as e:
|
| 57 |
+
logging.error(f"❌ خطأ أثناء جلب معلومات الخدمة {name}: {e}")
|
| 58 |
+
|
| 59 |
+
def update_service(self, zc, type_, name):
|
| 60 |
+
self.add_service(zc, type_, name)
|
| 61 |
+
|
| 62 |
+
def remove_service(self, zc, type_, name):
|
| 63 |
+
pass # يمكن تحسينه لاحقًا
|
| 64 |
+
|
| 65 |
+
listener = Listener()
|
| 66 |
+
ServiceBrowser(self._zeroconf, "_tasknode._tcp.local.", listener)
|
| 67 |
+
time.sleep(timeout)
|
| 68 |
+
return sorted(listener.peers, key=lambda x: x['load'])
|
| 69 |
+
|
| 70 |
+
def _get_local_ip(self) -> str:
|
| 71 |
+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
| 72 |
+
try:
|
| 73 |
+
s.connect(('10.255.255.255', 1))
|
| 74 |
+
ip = s.getsockname()[0]
|
| 75 |
+
except Exception:
|
| 76 |
+
ip = '127.0.0.1'
|
| 77 |
+
finally:
|
| 78 |
+
s.close()
|
| 79 |
+
return ip
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class DistributedExecutor:
|
| 83 |
+
def __init__(self, shared_secret: str):
|
| 84 |
+
self.peer_registry = PeerRegistry()
|
| 85 |
+
self.shared_secret = shared_secret
|
| 86 |
+
self.task_queue = queue.PriorityQueue()
|
| 87 |
+
self.result_cache = {}
|
| 88 |
+
self.available_peers = []
|
| 89 |
+
self._init_peer_discovery()
|
| 90 |
+
|
| 91 |
+
def _init_peer_discovery(self):
|
| 92 |
+
def discovery_loop():
|
| 93 |
+
while True:
|
| 94 |
+
self.available_peers = self.peer_registry.discover_peers()
|
| 95 |
+
logging.info(f"✅ Discovered peers: {self.available_peers}")
|
| 96 |
+
time.sleep(10)
|
| 97 |
+
|
| 98 |
+
threading.Thread(target=discovery_loop, daemon=True).start()
|
| 99 |
+
|
| 100 |
+
def submit(self, task_func: Callable, *args, **kwargs):
|
| 101 |
+
task_id = f"{task_func.__name__}_{time.time()}"
|
| 102 |
+
|
| 103 |
+
task = {
|
| 104 |
+
'task_id': task_id,
|
| 105 |
+
'function': task_func.__name__,
|
| 106 |
+
'args': args,
|
| 107 |
+
'kwargs': kwargs,
|
| 108 |
+
'sender_id': self.peer_registry.local_node_id
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
if self.available_peers:
|
| 112 |
+
lan_peers = [p for p in self.available_peers if self._is_local_ip(p['ip'])]
|
| 113 |
+
wan_peers = [p for p in self.available_peers if not self._is_local_ip(p['ip'])]
|
| 114 |
+
|
| 115 |
+
if lan_peers:
|
| 116 |
+
peer = min(lan_peers, key=lambda x: x['load'])
|
| 117 |
+
logging.info(f"✅ Sending task {task_id} to LAN peer {peer['node_id']}")
|
| 118 |
+
else:
|
| 119 |
+
peer = min(wan_peers, key=lambda x: x['load'])
|
| 120 |
+
logging.info(f"✅ Sending task {task_id} to WAN peer {peer['node_id']}")
|
| 121 |
+
|
| 122 |
+
self._send_to_peer(peer, task)
|
| 123 |
+
else:
|
| 124 |
+
logging.warning("⚠️ لا توجد أجهزة متاحة - سيتم تنفيذ المهمة محلياً")
|
| 125 |
+
|
| 126 |
+
def _is_local_ip(self, ip: str) -> bool:
|
| 127 |
+
return (
|
| 128 |
+
ip.startswith('192.168.') or
|
| 129 |
+
ip.startswith('10.') or
|
| 130 |
+
ip.startswith('172.') or
|
| 131 |
+
ip == '127.0.0.1'
|
| 132 |
+
)
|
| 133 |
+
|
| 134 |
+
def _send_to_peer(self, peer: Dict, task: Dict):
|
| 135 |
+
try:
|
| 136 |
+
url = f"http://{peer['ip']}:{peer['port']}/run"
|
| 137 |
+
response = requests.post(url, json=task, timeout=10)
|
| 138 |
+
response.raise_for_status()
|
| 139 |
+
logging.info(f"✅ Response from peer: {response.text}")
|
| 140 |
+
return response.json()
|
| 141 |
+
except Exception as e:
|
| 142 |
+
logging.error(f"❌ فشل إرسال المهمة لـ {peer['node_id']}: {e}")
|
| 143 |
+
return None
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
if __name__ == "__main__":
|
| 147 |
+
executor = DistributedExecutor("my_secret_key")
|
| 148 |
+
executor.peer_registry.register_service("node1", 7520, load=0.1)
|
| 149 |
+
print("✅ نظام توزيع المهام جاهز...")
|
| 150 |
+
|
| 151 |
+
def example_task(x):
|
| 152 |
+
return x * x
|
| 153 |
+
|
| 154 |
+
executor.submit(example_task, 5)
|
| 155 |
+
|
drizzle.config.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { defineConfig } from "drizzle-kit";
|
| 2 |
+
|
| 3 |
+
if (!process.env.DATABASE_URL) {
|
| 4 |
+
throw new Error("DATABASE_URL, ensure the database is provisioned");
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
export default defineConfig({
|
| 8 |
+
out: "./migrations",
|
| 9 |
+
schema: "./shared/schema.ts",
|
| 10 |
+
dialect: "postgresql",
|
| 11 |
+
dbCredentials: {
|
| 12 |
+
url: process.env.DATABASE_URL,
|
| 13 |
+
},
|
| 14 |
+
});
|
dts_cli.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# dts_cli.py
|
| 2 |
+
import click
|
| 3 |
+
from dashboard import app
|
| 4 |
+
from rpc_server import app as rpc_app
|
| 5 |
+
import threading
|
| 6 |
+
|
| 7 |
+
@click.group()
|
| 8 |
+
def cli():
|
| 9 |
+
pass
|
| 10 |
+
|
| 11 |
+
@cli.command()
|
| 12 |
+
def start():
|
| 13 |
+
"""بدء النظام الموزع"""
|
| 14 |
+
print("جارِ تشغيل النظام الموزع...")
|
| 15 |
+
|
| 16 |
+
# تشغيل واجهة التحكم في خيط منفصل
|
| 17 |
+
dashboard_thread = threading.Thread(
|
| 18 |
+
target=lambda: app.run(host="0.0.0.0", port=5000)
|
| 19 |
+
)
|
| 20 |
+
dashboard_thread.daemon = True
|
| 21 |
+
dashboard_thread.start()
|
| 22 |
+
|
| 23 |
+
# تشغيل خادم RPC
|
| 24 |
+
rpc_app.run(host="0.0.0.0", port=7520)
|
| 25 |
+
|
| 26 |
+
@cli.command()
|
| 27 |
+
from flask import Flask, render_template, request
|
| 28 |
+
|
| 29 |
+
# ... (الكود الحالي)
|
| 30 |
+
|
| 31 |
+
@flask_app.route("/")
|
| 32 |
+
def home():
|
| 33 |
+
tasks = {
|
| 34 |
+
"1": ("ضرب المصفوفات", "matrix"),
|
| 35 |
+
"2": ("حساب الأعداد الأولية", "prime"),
|
| 36 |
+
"3": ("معالجة البيانات", "data"),
|
| 37 |
+
}
|
| 38 |
+
return render_template("index.html", tasks=tasks)
|
| 39 |
+
|
| 40 |
+
@flask_app.route("/run_task", methods=["POST"])
|
| 41 |
+
def run_task():
|
| 42 |
+
task_id = request.form.get("task_id")
|
| 43 |
+
result = None
|
| 44 |
+
|
| 45 |
+
if task_id == "1":
|
| 46 |
+
result = matrix_multiply(500) # استبدل بمعاملاتك الفعلية
|
| 47 |
+
elif task_id == "2":
|
| 48 |
+
result = prime_calculation(100_000)
|
| 49 |
+
elif task_id == "3":
|
| 50 |
+
result = data_processing(10_000)
|
| 51 |
+
|
| 52 |
+
return render_template("index.html", tasks={
|
| 53 |
+
"1": ("ضرب المصفوفات", "matrix"),
|
| 54 |
+
"2": ("حساب الأعداد الأولية", "prime"),
|
| 55 |
+
"3": ("معالجة البيانات", "data"),
|
| 56 |
+
}, result=result)
|
| 57 |
+
def discover():
|
| 58 |
+
"""عرض الأجهزة المتصلة"""
|
| 59 |
+
from peer_discovery import discover_peers
|
| 60 |
+
peers = discover_peers()
|
| 61 |
+
print("الأجهزة المتصلة:")
|
| 62 |
+
for i, peer in enumerate(peers, 1):
|
| 63 |
+
print(f"{i}. {peer}")
|
| 64 |
+
|
| 65 |
+
if __name__ == "__main__":
|
| 66 |
+
cli()
|
electron-package.json
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "nora_distributed_system",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"description": "نظام نورا الذكي الموزع",
|
| 5 |
+
"main": "electron/main.js",
|
| 6 |
+
"scripts": {
|
| 7 |
+
"electron": "electron .",
|
| 8 |
+
"build-windows": "electron-builder --win",
|
| 9 |
+
"build-mac": "electron-builder --mac",
|
| 10 |
+
"build-linux": "electron-builder --linux",
|
| 11 |
+
"build-all": "electron-builder -mwl"
|
| 12 |
+
},
|
| 13 |
+
"build": {
|
| 14 |
+
"appId": "com.nora.distributed",
|
| 15 |
+
"productName": "Nora Distributed System",
|
| 16 |
+
"directories": {
|
| 17 |
+
"output": "dist"
|
| 18 |
+
},
|
| 19 |
+
"files": [
|
| 20 |
+
"build/**/*",
|
| 21 |
+
"electron/**/*",
|
| 22 |
+
"node_modules/**/*"
|
| 23 |
+
],
|
| 24 |
+
"win": {
|
| 25 |
+
"target": [
|
| 26 |
+
{
|
| 27 |
+
"target": "nsis",
|
| 28 |
+
"arch": [
|
| 29 |
+
"x64",
|
| 30 |
+
"ia32",
|
| 31 |
+
"arm64"
|
| 32 |
+
]
|
| 33 |
+
},
|
| 34 |
+
{
|
| 35 |
+
"target": "portable",
|
| 36 |
+
"arch": [
|
| 37 |
+
"x64",
|
| 38 |
+
"ia32"
|
| 39 |
+
]
|
| 40 |
+
},
|
| 41 |
+
{
|
| 42 |
+
"target": "appx",
|
| 43 |
+
"arch": [
|
| 44 |
+
"x64",
|
| 45 |
+
"ia32"
|
| 46 |
+
]
|
| 47 |
+
}
|
| 48 |
+
],
|
| 49 |
+
"icon": "assets/icon.ico"
|
| 50 |
+
},
|
| 51 |
+
"mac": {
|
| 52 |
+
"target": [
|
| 53 |
+
{
|
| 54 |
+
"target": "dmg",
|
| 55 |
+
"arch": [
|
| 56 |
+
"x64",
|
| 57 |
+
"arm64"
|
| 58 |
+
]
|
| 59 |
+
},
|
| 60 |
+
{
|
| 61 |
+
"target": "zip",
|
| 62 |
+
"arch": [
|
| 63 |
+
"x64",
|
| 64 |
+
"arm64"
|
| 65 |
+
]
|
| 66 |
+
},
|
| 67 |
+
{
|
| 68 |
+
"target": "mas",
|
| 69 |
+
"arch": [
|
| 70 |
+
"x64",
|
| 71 |
+
"arm64"
|
| 72 |
+
]
|
| 73 |
+
}
|
| 74 |
+
],
|
| 75 |
+
"icon": "assets/icon.icns"
|
| 76 |
+
},
|
| 77 |
+
"linux": {
|
| 78 |
+
"target": [
|
| 79 |
+
{
|
| 80 |
+
"target": "AppImage",
|
| 81 |
+
"arch": [
|
| 82 |
+
"x64",
|
| 83 |
+
"arm64"
|
| 84 |
+
]
|
| 85 |
+
},
|
| 86 |
+
{
|
| 87 |
+
"target": "deb",
|
| 88 |
+
"arch": [
|
| 89 |
+
"x64",
|
| 90 |
+
"arm64"
|
| 91 |
+
]
|
| 92 |
+
},
|
| 93 |
+
{
|
| 94 |
+
"target": "rpm",
|
| 95 |
+
"arch": [
|
| 96 |
+
"x64",
|
| 97 |
+
"arm64"
|
| 98 |
+
]
|
| 99 |
+
},
|
| 100 |
+
{
|
| 101 |
+
"target": "snap",
|
| 102 |
+
"arch": [
|
| 103 |
+
"x64",
|
| 104 |
+
"arm64"
|
| 105 |
+
]
|
| 106 |
+
}
|
| 107 |
+
],
|
| 108 |
+
"icon": "assets/icon.png"
|
| 109 |
+
}
|
| 110 |
+
}
|
| 111 |
+
}
|
enhanced_assistant.py
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import os
|
| 3 |
+
import json
|
| 4 |
+
import requests
|
| 5 |
+
from typing import Dict, List, Optional
|
| 6 |
+
from difflib import get_close_matches
|
| 7 |
+
import tempfile
|
| 8 |
+
from PIL import Image
|
| 9 |
+
from io import BytesIO
|
| 10 |
+
import re
|
| 11 |
+
from datetime import datetime
|
| 12 |
+
|
| 13 |
+
class EnhancedNoraAssistant:
|
| 14 |
+
def __init__(self):
|
| 15 |
+
self.knowledge_file = "knowledge_base.json"
|
| 16 |
+
self.memory_file = "global_memory.json"
|
| 17 |
+
self.learning_file = "nora_learning_data.json"
|
| 18 |
+
self.history_path = "enhanced_history.json"
|
| 19 |
+
|
| 20 |
+
# تحميل البيانات
|
| 21 |
+
self.knowledge = self.load_knowledge()
|
| 22 |
+
self.memory = self.load_memory()
|
| 23 |
+
self.chat_history = self.load_history()
|
| 24 |
+
|
| 25 |
+
print("✅ تم تهيئة نورا المحسنة بنجاح!")
|
| 26 |
+
|
| 27 |
+
def load_knowledge(self) -> Dict:
|
| 28 |
+
"""تحميل قاعدة المعرفة"""
|
| 29 |
+
if os.path.exists(self.knowledge_file):
|
| 30 |
+
with open(self.knowledge_file, 'r', encoding='utf-8') as f:
|
| 31 |
+
return json.load(f)
|
| 32 |
+
return {}
|
| 33 |
+
|
| 34 |
+
def save_knowledge(self):
|
| 35 |
+
"""حفظ قاعدة المعرفة"""
|
| 36 |
+
with open(self.knowledge_file, 'w', encoding='utf-8') as f:
|
| 37 |
+
json.dump(self.knowledge, f, ensure_ascii=False, indent=2)
|
| 38 |
+
|
| 39 |
+
def load_memory(self) -> Dict:
|
| 40 |
+
"""تحميل الذاكرة العامة"""
|
| 41 |
+
if os.path.exists(self.memory_file):
|
| 42 |
+
with open(self.memory_file, 'r', encoding='utf-8') as f:
|
| 43 |
+
return json.load(f)
|
| 44 |
+
return {}
|
| 45 |
+
|
| 46 |
+
def save_memory(self):
|
| 47 |
+
"""حفظ الذاكرة العامة"""
|
| 48 |
+
with open(self.memory_file, 'w', encoding='utf-8') as f:
|
| 49 |
+
json.dump(self.memory, f, ensure_ascii=False, indent=2)
|
| 50 |
+
|
| 51 |
+
def load_history(self) -> List[Dict]:
|
| 52 |
+
"""تحميل سجل المحادثة"""
|
| 53 |
+
if os.path.exists(self.history_path):
|
| 54 |
+
try:
|
| 55 |
+
with open(self.history_path, "r", encoding="utf-8") as f:
|
| 56 |
+
return json.load(f)
|
| 57 |
+
except:
|
| 58 |
+
return []
|
| 59 |
+
return []
|
| 60 |
+
|
| 61 |
+
def save_history(self):
|
| 62 |
+
"""حفظ سجل المحادثة"""
|
| 63 |
+
with open(self.history_path, "w", encoding="utf-8") as f:
|
| 64 |
+
json.dump(self.chat_history, f, ensure_ascii=False, indent=2)
|
| 65 |
+
|
| 66 |
+
def clean_text(self, text: str) -> str:
|
| 67 |
+
"""تنظيف النص"""
|
| 68 |
+
text = re.sub(r'\s+', ' ', text)
|
| 69 |
+
return text.strip()
|
| 70 |
+
|
| 71 |
+
def detect_language(self, text: str) -> str:
|
| 72 |
+
"""كشف لغة النص"""
|
| 73 |
+
arabic_chars = re.compile('[\u0600-\u06FF]')
|
| 74 |
+
if arabic_chars.search(text):
|
| 75 |
+
return "ar"
|
| 76 |
+
return "en"
|
| 77 |
+
|
| 78 |
+
def fix_url(self, url: str) -> str:
|
| 79 |
+
"""تصحيح الرابط"""
|
| 80 |
+
if not url.startswith(("http://", "https://")):
|
| 81 |
+
return "https://" + url.lstrip("//")
|
| 82 |
+
return url
|
| 83 |
+
|
| 84 |
+
def detect_media_type(self, url: str) -> str:
|
| 85 |
+
"""تحديد نوع الوسائط"""
|
| 86 |
+
url = url.lower()
|
| 87 |
+
if url.endswith(('.jpg', '.jpeg', '.png', '.gif', '.webp')):
|
| 88 |
+
return 'image'
|
| 89 |
+
elif url.endswith(('.mp4', '.mov', '.avi', '.webm')):
|
| 90 |
+
return 'video'
|
| 91 |
+
elif url.endswith(('.mp3', '.wav', '.ogg', '.m4a')):
|
| 92 |
+
return 'audio'
|
| 93 |
+
elif url.endswith('.pdf'):
|
| 94 |
+
return 'pdf'
|
| 95 |
+
return 'link'
|
| 96 |
+
|
| 97 |
+
def analyze_image_from_url(self, image_url: str) -> str:
|
| 98 |
+
"""تحليل صورة من رابط"""
|
| 99 |
+
try:
|
| 100 |
+
response = requests.get(image_url, timeout=10)
|
| 101 |
+
response.raise_for_status()
|
| 102 |
+
image = Image.open(BytesIO(response.content))
|
| 103 |
+
return f"تحليل الصورة: الحجم {image.size}، الصيغة {image.format}"
|
| 104 |
+
except Exception as e:
|
| 105 |
+
return f"خطأ في تحليل الصورة: {str(e)}"
|
| 106 |
+
|
| 107 |
+
def smart_auto_reply(self, message: str) -> Optional[str]:
|
| 108 |
+
"""ردود ذكية تلقائية"""
|
| 109 |
+
msg = message.strip().lower()
|
| 110 |
+
|
| 111 |
+
responses = {
|
| 112 |
+
"هل نبدأ": "نعم ابدأ",
|
| 113 |
+
"ابدأ": "نعم ابدأ",
|
| 114 |
+
"نعم أو لا": "نعم",
|
| 115 |
+
"هل تود": "نعم",
|
| 116 |
+
"هل تريدني": "نعم",
|
| 117 |
+
"ما هي": "ليس الآن",
|
| 118 |
+
"تفصيل": "ليس الآن",
|
| 119 |
+
"هل تحتاج": "نعم، شرح أكثر",
|
| 120 |
+
"جاهز؟": "ابدأ",
|
| 121 |
+
"قول لي": "موافق"
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
for key, value in responses.items():
|
| 125 |
+
if key in msg:
|
| 126 |
+
return value
|
| 127 |
+
|
| 128 |
+
if " أو " in msg:
|
| 129 |
+
return msg.split(" أو ")[0]
|
| 130 |
+
|
| 131 |
+
return None
|
| 132 |
+
|
| 133 |
+
def learn_new_info(self, topic: str, info: str) -> str:
|
| 134 |
+
"""تعلم معلومة جديدة"""
|
| 135 |
+
if topic not in self.knowledge:
|
| 136 |
+
self.knowledge[topic] = []
|
| 137 |
+
|
| 138 |
+
if info not in self.knowledge[topic]:
|
| 139 |
+
self.knowledge[topic].append({
|
| 140 |
+
"content": info,
|
| 141 |
+
"timestamp": datetime.utcnow().isoformat()
|
| 142 |
+
})
|
| 143 |
+
self.save_knowledge()
|
| 144 |
+
return f"✅ تمت إضافة معلومة جديدة عن '{topic}'"
|
| 145 |
+
|
| 146 |
+
return f"ℹ️ المعلومة موجودة مسبقاً عن '{topic}'"
|
| 147 |
+
|
| 148 |
+
def search_knowledge(self, query: str) -> str:
|
| 149 |
+
"""البحث في قاعدة المعرفة"""
|
| 150 |
+
query_clean = query.strip().lower()
|
| 151 |
+
|
| 152 |
+
# بحث مباشر
|
| 153 |
+
if query_clean in self.knowledge:
|
| 154 |
+
info = self.knowledge[query_clean]
|
| 155 |
+
if isinstance(info, list) and info:
|
| 156 |
+
return info[-1].get("content", str(info[-1]))
|
| 157 |
+
return str(info)
|
| 158 |
+
|
| 159 |
+
# بحث في المواضيع
|
| 160 |
+
for topic, infos in self.knowledge.items():
|
| 161 |
+
if query_clean in topic.lower():
|
| 162 |
+
if isinstance(infos, list) and infos:
|
| 163 |
+
return f"وجدت معلومة عن '{topic}': {infos[-1].get('content', str(infos[-1]))}"
|
| 164 |
+
return f"وجدت معلومة عن '{topic}': {str(infos)}"
|
| 165 |
+
|
| 166 |
+
return None
|
| 167 |
+
|
| 168 |
+
def generate_reply(self, user_input: str) -> str:
|
| 169 |
+
"""إنتاج الرد الذكي"""
|
| 170 |
+
user_input = self.clean_text(user_input)
|
| 171 |
+
|
| 172 |
+
# فحص الردود التلقائية الذكية
|
| 173 |
+
auto_reply = self.smart_auto_reply(user_input)
|
| 174 |
+
if auto_reply:
|
| 175 |
+
self.memory[user_input] = auto_reply
|
| 176 |
+
self.save_memory()
|
| 177 |
+
return auto_reply
|
| 178 |
+
|
| 179 |
+
# فحص الذاكرة
|
| 180 |
+
if user_input in self.memory:
|
| 181 |
+
return self.memory[user_input]
|
| 182 |
+
|
| 183 |
+
# البحث في المطابقات القريبة
|
| 184 |
+
matches = get_close_matches(user_input, self.memory.keys(), n=1, cutoff=0.6)
|
| 185 |
+
if matches:
|
| 186 |
+
return self.memory[matches[0]]
|
| 187 |
+
|
| 188 |
+
# البحث في قاعدة المعرفة
|
| 189 |
+
knowledge_result = self.search_knowledge(user_input)
|
| 190 |
+
if knowledge_result:
|
| 191 |
+
self.memory[user_input] = knowledge_result
|
| 192 |
+
self.save_memory()
|
| 193 |
+
return knowledge_result
|
| 194 |
+
|
| 195 |
+
# معالجة الروابط
|
| 196 |
+
if user_input.startswith("http://") or user_input.startswith("https://"):
|
| 197 |
+
return self.handle_url(user_input)
|
| 198 |
+
|
| 199 |
+
# تصحيح الروابط في النص
|
| 200 |
+
if '//' in user_input:
|
| 201 |
+
corrected_url = self.fix_url(user_input)
|
| 202 |
+
reply = f"تم تصحيح الرابط: {corrected_url}"
|
| 203 |
+
else:
|
| 204 |
+
# رد افتراضي مع تعلم
|
| 205 |
+
reply = f"شكراً لك على الرسالة: '{user_input}'. سأتذكر هذا للمرة القادمة."
|
| 206 |
+
|
| 207 |
+
# تعلم تلقائي
|
| 208 |
+
if len(user_input.split()) > 2: # إذا كانت جملة معقولة
|
| 209 |
+
self.learn_new_info("محادثات_عامة", user_input)
|
| 210 |
+
|
| 211 |
+
# حفظ في الذاكرة
|
| 212 |
+
self.memory[user_input] = reply
|
| 213 |
+
self.save_memory()
|
| 214 |
+
return reply
|
| 215 |
+
|
| 216 |
+
def handle_url(self, url: str) -> str:
|
| 217 |
+
"""معالجة الروابط"""
|
| 218 |
+
url = self.fix_url(url)
|
| 219 |
+
media_type = self.detect_media_type(url)
|
| 220 |
+
|
| 221 |
+
if media_type == 'image':
|
| 222 |
+
analysis = self.analyze_image_from_url(url)
|
| 223 |
+
reply = f"🖼️ صورة تم تحليلها:\n{analysis}"
|
| 224 |
+
elif media_type == 'video':
|
| 225 |
+
reply = f"🎥 فيديو تم اكتشافه: {url}"
|
| 226 |
+
elif media_type == 'audio':
|
| 227 |
+
reply = f"🎵 ملف صوتي تم اكتشافه: {url}"
|
| 228 |
+
elif media_type == 'pdf':
|
| 229 |
+
reply = f"📄 ملف PDF تم اكتشافه: {url}"
|
| 230 |
+
else:
|
| 231 |
+
reply = f"🔗 رابط ويب: {url}"
|
| 232 |
+
|
| 233 |
+
return reply
|
| 234 |
+
|
| 235 |
+
def simulate_server_scan(self):
|
| 236 |
+
"""محاكاة البحث عن الخوادم"""
|
| 237 |
+
print("نورا: أبحث عن خوادم متاحة...")
|
| 238 |
+
fake_servers = ["server-01.cloud.com", "server-02.cloud.com", "server-03.local"]
|
| 239 |
+
|
| 240 |
+
for server in fake_servers:
|
| 241 |
+
print(f"نورا: تم العثور على خادم: {server}")
|
| 242 |
+
print(f"نورا: أقوم بمحاكاة النسخ إلى {server}...")
|
| 243 |
+
|
| 244 |
+
return "تمت عملية المحاكاة بنجاح ✅"
|
| 245 |
+
|
| 246 |
+
def get_stats(self) -> Dict:
|
| 247 |
+
"""إحصائيات النظام"""
|
| 248 |
+
return {
|
| 249 |
+
"معرفة_محفوظة": len(self.knowledge),
|
| 250 |
+
"ذكريات": len(self.memory),
|
| 251 |
+
"سجل_محادثات": len(self.chat_history),
|
| 252 |
+
"آخر_تحديث": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
def chat(self):
|
| 256 |
+
"""بدء المحادثة التفاعلية"""
|
| 257 |
+
print("🤖 مرحباً! أنا نورا المحسنة، مساعدتك الذكية.")
|
| 258 |
+
print("📚 لدي قدرات محسنة في التعلم الذاتي وتحليل الوسائط")
|
| 259 |
+
print("💡 اكتب 'خروج' للإنهاء، 'إحصائيات' لعرض الإحص��ئيات، 'scan' للبحث عن خوادم")
|
| 260 |
+
print("-" * 50)
|
| 261 |
+
|
| 262 |
+
while True:
|
| 263 |
+
try:
|
| 264 |
+
user_input = input("\n🧑 أنت: ").strip()
|
| 265 |
+
|
| 266 |
+
if user_input.lower() in ["خروج", "exit", "quit"]:
|
| 267 |
+
print("نورا: مع السلامة! 👋")
|
| 268 |
+
break
|
| 269 |
+
|
| 270 |
+
elif user_input.lower() == "إحصائيات":
|
| 271 |
+
stats = self.get_stats()
|
| 272 |
+
print("📊 إحصائيات النظام:")
|
| 273 |
+
for key, value in stats.items():
|
| 274 |
+
print(f" {key}: {value}")
|
| 275 |
+
continue
|
| 276 |
+
|
| 277 |
+
elif user_input.lower() == "scan":
|
| 278 |
+
result = self.simulate_server_scan()
|
| 279 |
+
print(f"نورا: {result}")
|
| 280 |
+
continue
|
| 281 |
+
|
| 282 |
+
elif not user_input:
|
| 283 |
+
continue
|
| 284 |
+
|
| 285 |
+
# الحصول على الرد
|
| 286 |
+
response = self.generate_reply(user_input)
|
| 287 |
+
print(f"🤖 نورا: {response}")
|
| 288 |
+
|
| 289 |
+
# حفظ في السجل
|
| 290 |
+
self.chat_history.append({
|
| 291 |
+
"user": user_input,
|
| 292 |
+
"assistant": response,
|
| 293 |
+
"timestamp": datetime.utcnow().isoformat()
|
| 294 |
+
})
|
| 295 |
+
|
| 296 |
+
# حفظ السجل كل 5 رسائل
|
| 297 |
+
if len(self.chat_history) % 5 == 0:
|
| 298 |
+
self.save_history()
|
| 299 |
+
|
| 300 |
+
except KeyboardInterrupt:
|
| 301 |
+
print("\n\nنورا: تم إيقاف المحادثة. مع السلامة! 👋")
|
| 302 |
+
break
|
| 303 |
+
except Exception as e:
|
| 304 |
+
print(f"نورا: عذراً، حدث خطأ: {str(e)}")
|
| 305 |
+
|
| 306 |
+
def main():
|
| 307 |
+
"""تشغيل المساعد المحسن"""
|
| 308 |
+
assistant = EnhancedNoraAssistant()
|
| 309 |
+
assistant.chat()
|
| 310 |
+
|
| 311 |
+
if __name__ == "__main__":
|
| 312 |
+
main()
|
global_memory.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"مرحبا": "شكراً لك على الرسالة: 'مرحبا'. سأتذكر هذا للمرة القادمة."
|
| 3 |
+
}
|
history.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"role": "user",
|
| 4 |
+
"content": "اه"
|
| 5 |
+
},
|
| 6 |
+
{
|
| 7 |
+
"role": "assistant",
|
| 8 |
+
"content": "عذراً، حدث خطأ: 401"
|
| 9 |
+
},
|
| 10 |
+
{
|
| 11 |
+
"role": "user",
|
| 12 |
+
"content": "مرحبا"
|
| 13 |
+
},
|
| 14 |
+
{
|
| 15 |
+
"role": "assistant",
|
| 16 |
+
"content": "عذراً، حدث خطأ: 401"
|
| 17 |
+
},
|
| 18 |
+
{
|
| 19 |
+
"role": "user",
|
| 20 |
+
"content": "مم"
|
| 21 |
+
},
|
| 22 |
+
{
|
| 23 |
+
"role": "assistant",
|
| 24 |
+
"content": "عذراً، حدث خطأ: 401"
|
| 25 |
+
}
|
| 26 |
+
]
|
index.html
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="ar" dir="rtl">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<title>نظام توزيع المهام الذكي</title>
|
| 6 |
+
<style>
|
| 7 |
+
body {
|
| 8 |
+
font-family: Arial, sans-serif;
|
| 9 |
+
max-width: 800px;
|
| 10 |
+
margin: 0 auto;
|
| 11 |
+
padding: 20px;
|
| 12 |
+
background-color: #f5f5f5;
|
| 13 |
+
}
|
| 14 |
+
h1 {
|
| 15 |
+
color: #2c3e50;
|
| 16 |
+
text-align: center;
|
| 17 |
+
}
|
| 18 |
+
ul {
|
| 19 |
+
list-style: none;
|
| 20 |
+
padding: 0;
|
| 21 |
+
}
|
| 22 |
+
li {
|
| 23 |
+
background: white;
|
| 24 |
+
margin: 10px 0;
|
| 25 |
+
padding: 15px;
|
| 26 |
+
border-radius: 5px;
|
| 27 |
+
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
| 28 |
+
display: flex;
|
| 29 |
+
justify-content: space-between;
|
| 30 |
+
align-items: center;
|
| 31 |
+
}
|
| 32 |
+
button {
|
| 33 |
+
background: #3498db;
|
| 34 |
+
color: white;
|
| 35 |
+
border: none;
|
| 36 |
+
padding: 8px 15px;
|
| 37 |
+
border-radius: 3px;
|
| 38 |
+
cursor: pointer;
|
| 39 |
+
}
|
| 40 |
+
button:hover {
|
| 41 |
+
background: #2980b9;
|
| 42 |
+
}
|
| 43 |
+
#result {
|
| 44 |
+
background: white;
|
| 45 |
+
padding: 15px;
|
| 46 |
+
border-radius: 5px;
|
| 47 |
+
margin-top: 20px;
|
| 48 |
+
}
|
| 49 |
+
</style>
|
| 50 |
+
</head>
|
| 51 |
+
<body>
|
| 52 |
+
<h1>🚀 نظام توزيع المهام الذكي</h1>
|
| 53 |
+
<ul id="tasks">
|
| 54 |
+
{% for task_id, task in tasks.items() %}
|
| 55 |
+
<li>
|
| 56 |
+
<span>{{ task[0] }}</span>
|
| 57 |
+
<button onclick="runTask('{{ task_id }}')">تشغيل</button>
|
| 58 |
+
</li>
|
| 59 |
+
{% endfor %}
|
| 60 |
+
</ul>
|
| 61 |
+
|
| 62 |
+
<div id="result" style="display: {% if result %}block{% else %}none{% endif %};">
|
| 63 |
+
<h2>✅ النتيجة:</h2>
|
| 64 |
+
<pre>{{ result }}</pre>
|
| 65 |
+
</div>
|
| 66 |
+
|
| 67 |
+
<script>
|
| 68 |
+
async function runTask(taskId) {
|
| 69 |
+
const response = await fetch("/run_task", {
|
| 70 |
+
method: "POST",
|
| 71 |
+
headers: {
|
| 72 |
+
"Content-Type": "application/x-www-form-urlencoded",
|
| 73 |
+
},
|
| 74 |
+
body: `task_id=${taskId}`
|
| 75 |
+
});
|
| 76 |
+
const data = await response.text();
|
| 77 |
+
|
| 78 |
+
// تحديث الصفحة دون إعادة تحميل كاملة
|
| 79 |
+
document.getElementById("result").innerHTML =
|
| 80 |
+
`<h2>✅ النتيجة:</h2><pre>${JSON.parse(data).result}</pre>`;
|
| 81 |
+
document.getElementById("result").style.display = "block";
|
| 82 |
+
}
|
| 83 |
+
</script>
|
| 84 |
+
</body>
|
| 85 |
+
</html>
|
internet_scanner.py
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
"""
|
| 3 |
+
internet_scanner.py - ماسح للبحث عن أجهزة DTS على الإنترنت
|
| 4 |
+
"""
|
| 5 |
+
import requests
|
| 6 |
+
import threading
|
| 7 |
+
import time
|
| 8 |
+
import socket
|
| 9 |
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
| 10 |
+
import logging
|
| 11 |
+
|
| 12 |
+
class InternetScanner:
|
| 13 |
+
def __init__(self):
|
| 14 |
+
self.discovered_peers = set()
|
| 15 |
+
self.scan_ranges = [
|
| 16 |
+
# نطاقات IP شائعة للخوادم العامة
|
| 17 |
+
"8.8.8.0/24", # Google DNS range
|
| 18 |
+
"1.1.1.0/24", # Cloudflare range
|
| 19 |
+
"208.67.222.0/24", # OpenDNS range
|
| 20 |
+
]
|
| 21 |
+
|
| 22 |
+
def scan_ip_range(self, ip_range: str, port: int = 7520):
|
| 23 |
+
"""مسح نطاق IP للبحث عن خوادم DTS"""
|
| 24 |
+
import ipaddress
|
| 25 |
+
|
| 26 |
+
try:
|
| 27 |
+
network = ipaddress.ip_network(ip_range, strict=False)
|
| 28 |
+
active_peers = []
|
| 29 |
+
|
| 30 |
+
with ThreadPoolExecutor(max_workers=50) as executor:
|
| 31 |
+
futures = []
|
| 32 |
+
|
| 33 |
+
for ip in network.hosts():
|
| 34 |
+
future = executor.submit(self.check_dts_node, str(ip), port)
|
| 35 |
+
futures.append(future)
|
| 36 |
+
|
| 37 |
+
for future in as_completed(futures, timeout=30):
|
| 38 |
+
try:
|
| 39 |
+
result = future.result()
|
| 40 |
+
if result:
|
| 41 |
+
active_peers.append(result)
|
| 42 |
+
except:
|
| 43 |
+
continue
|
| 44 |
+
|
| 45 |
+
return active_peers
|
| 46 |
+
|
| 47 |
+
except Exception as e:
|
| 48 |
+
logging.error(f"خطأ في مسح النطاق {ip_range}: {e}")
|
| 49 |
+
return []
|
| 50 |
+
|
| 51 |
+
def check_dts_node(self, ip: str, port: int = 7520) -> str:
|
| 52 |
+
"""فحص IP معين للتأكد من وجود خادم DTS مع المشروع"""
|
| 53 |
+
try:
|
| 54 |
+
# فحص صفحة الصحة العامة
|
| 55 |
+
health_url = f"http://{ip}:{port}/health"
|
| 56 |
+
response = requests.get(health_url, timeout=2)
|
| 57 |
+
|
| 58 |
+
if response.status_code == 200:
|
| 59 |
+
# فحص وجود المشروع الصحيح
|
| 60 |
+
run_url = f"http://{ip}:{port}/run"
|
| 61 |
+
|
| 62 |
+
# اختبار مهمة من المشروع للتأكد
|
| 63 |
+
test_payload = {
|
| 64 |
+
"func": "matrix_multiply",
|
| 65 |
+
"args": [2],
|
| 66 |
+
"kwargs": {}
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
test_response = requests.post(run_url, json=test_payload, timeout=3)
|
| 70 |
+
|
| 71 |
+
# فحص إضافي للتأكد من هوية المشروع
|
| 72 |
+
project_check = requests.get(f"http://{ip}:{port}/project_info", timeout=2)
|
| 73 |
+
|
| 74 |
+
if (test_response.status_code in [200, 404] and
|
| 75 |
+
project_check.status_code == 200):
|
| 76 |
+
|
| 77 |
+
project_data = project_check.json()
|
| 78 |
+
|
| 79 |
+
# التحقق من معرف المشروع الصحيح
|
| 80 |
+
if (project_data.get("project_name") == "distributed-task-system" and
|
| 81 |
+
project_data.get("version") == "1.0"):
|
| 82 |
+
logging.info(f"✅ اكتُشف خادم DTS صحيح: {ip}:{port}")
|
| 83 |
+
return run_url
|
| 84 |
+
else:
|
| 85 |
+
logging.warning(f"⚠️ خادم على {ip}:{port} لكن مشروع مختلف")
|
| 86 |
+
|
| 87 |
+
except:
|
| 88 |
+
pass
|
| 89 |
+
return None
|
| 90 |
+
|
| 91 |
+
def scan_public_repositories(self):
|
| 92 |
+
"""البحث في المستودعات العامة عن عناوين خوادم DTS"""
|
| 93 |
+
try:
|
| 94 |
+
# البحث في GitHub عن مشاريع DTS
|
| 95 |
+
github_api = "https://api.github.com/search/repositories"
|
| 96 |
+
params = {
|
| 97 |
+
"q": "distributed task system port:7520",
|
| 98 |
+
"sort": "updated",
|
| 99 |
+
"per_page": 10
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
response = requests.get(github_api, params=params, timeout=10)
|
| 103 |
+
if response.status_code == 200:
|
| 104 |
+
repos = response.json().get("items", [])
|
| 105 |
+
|
| 106 |
+
for repo in repos:
|
| 107 |
+
# محاولة استخراج IPs من وصف المشروع أو README
|
| 108 |
+
if repo.get("description"):
|
| 109 |
+
self.extract_ips_from_text(repo["description"])
|
| 110 |
+
|
| 111 |
+
except Exception as e:
|
| 112 |
+
logging.warning(f"خطأ في البحث في المستودعات: {e}")
|
| 113 |
+
|
| 114 |
+
def extract_ips_from_text(self, text: str):
|
| 115 |
+
"""استخراج عناوين IP من النص"""
|
| 116 |
+
import re
|
| 117 |
+
|
| 118 |
+
ip_pattern = r'\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b'
|
| 119 |
+
ips = re.findall(ip_pattern, text)
|
| 120 |
+
|
| 121 |
+
for ip in ips:
|
| 122 |
+
try:
|
| 123 |
+
# التحقق من صحة IP
|
| 124 |
+
socket.inet_aton(ip)
|
| 125 |
+
peer_url = f"http://{ip}:7520/run"
|
| 126 |
+
|
| 127 |
+
# فحص سريع
|
| 128 |
+
if self.check_dts_node(ip):
|
| 129 |
+
self.discovered_peers.add(peer_url)
|
| 130 |
+
|
| 131 |
+
except:
|
| 132 |
+
continue
|
| 133 |
+
|
| 134 |
+
def start_continuous_scan(self):
|
| 135 |
+
"""بدء المسح المستمر"""
|
| 136 |
+
def scan_loop():
|
| 137 |
+
while True:
|
| 138 |
+
try:
|
| 139 |
+
# مسح النطاقات المحددة
|
| 140 |
+
for ip_range in self.scan_ranges:
|
| 141 |
+
peers = self.scan_ip_range(ip_range)
|
| 142 |
+
for peer in peers:
|
| 143 |
+
self.discovered_peers.add(peer)
|
| 144 |
+
|
| 145 |
+
# البحث في المستودعات العامة
|
| 146 |
+
self.scan_public_repositories()
|
| 147 |
+
|
| 148 |
+
logging.info(f"اكتُشف {len(self.discovered_peers)} خادم على الإنترنت")
|
| 149 |
+
|
| 150 |
+
except Exception as e:
|
| 151 |
+
logging.error(f"خطأ في المسح المستمر: {e}")
|
| 152 |
+
|
| 153 |
+
# انتظار 30 دقيقة قبل المسح التالي
|
| 154 |
+
time.sleep(1800)
|
| 155 |
+
|
| 156 |
+
thread = threading.Thread(target=scan_loop, daemon=True)
|
| 157 |
+
thread.start()
|
| 158 |
+
logging.info("🔍 بدء المسح المستمر للإنترنت")
|
| 159 |
+
|
| 160 |
+
def get_discovered_peers(self):
|
| 161 |
+
"""الحصول على قائمة الأجهزة المكتشفة"""
|
| 162 |
+
return list(self.discovered_peers)
|
| 163 |
+
|
| 164 |
+
# إنشاء مثيل عام
|
| 165 |
+
internet_scanner = InternetScanner()
|
launcher.py
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
#!/usr/bin/env python3
|
| 3 |
+
"""
|
| 4 |
+
مشغل موحد لنظام توزيع المهام
|
| 5 |
+
يوفر خيارات متعددة للتشغيل
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import sys
|
| 9 |
+
import os
|
| 10 |
+
import subprocess
|
| 11 |
+
import argparse
|
| 12 |
+
import time
|
| 13 |
+
from pathlib import Path
|
| 14 |
+
|
| 15 |
+
def check_requirements():
|
| 16 |
+
"""فحص المتطلبات والاعتماديات"""
|
| 17 |
+
required_files = [
|
| 18 |
+
'background_service.py',
|
| 19 |
+
'main.py',
|
| 20 |
+
'peer_server.py',
|
| 21 |
+
'rpc_server.py',
|
| 22 |
+
'load_balancer.py'
|
| 23 |
+
]
|
| 24 |
+
|
| 25 |
+
missing_files = []
|
| 26 |
+
for file in required_files:
|
| 27 |
+
if not Path(file).exists():
|
| 28 |
+
missing_files.append(file)
|
| 29 |
+
|
| 30 |
+
if missing_files:
|
| 31 |
+
print(f"❌ ملفات مفقودة: {', '.join(missing_files)}")
|
| 32 |
+
return False
|
| 33 |
+
|
| 34 |
+
return True
|
| 35 |
+
|
| 36 |
+
def install_tray_dependencies():
|
| 37 |
+
"""تثبيت اعتماديات أيقونة شريط النظام"""
|
| 38 |
+
try:
|
| 39 |
+
subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'pystray', 'Pillow'])
|
| 40 |
+
print("✅ تم تثبيت اعتماديات أيقونة شريط النظام")
|
| 41 |
+
return True
|
| 42 |
+
except subprocess.CalledProcessError:
|
| 43 |
+
print("❌ فشل في تثبيت اعتماديات أيقونة شريط النظام")
|
| 44 |
+
return False
|
| 45 |
+
|
| 46 |
+
def start_background_service():
|
| 47 |
+
"""بدء تشغيل الخدمة في الخلفية"""
|
| 48 |
+
print("🚀 بدء تشغيل الخدمة في الخلفية...")
|
| 49 |
+
|
| 50 |
+
# تشغيل الخدمة الخلفية
|
| 51 |
+
process = subprocess.Popen(
|
| 52 |
+
[sys.executable, 'background_service.py', 'start'],
|
| 53 |
+
stdout=subprocess.PIPE,
|
| 54 |
+
stderr=subprocess.PIPE
|
| 55 |
+
)
|
| 56 |
+
|
| 57 |
+
# انتظار قليل للتأكد من بدء التشغيل
|
| 58 |
+
time.sleep(2)
|
| 59 |
+
|
| 60 |
+
if process.poll() is None:
|
| 61 |
+
print("✅ تم بدء تشغيل الخدمة الخلفية بنجاح")
|
| 62 |
+
return process
|
| 63 |
+
else:
|
| 64 |
+
print("❌ فشل في بدء تشغيل الخدمة الخلفية")
|
| 65 |
+
return None
|
| 66 |
+
|
| 67 |
+
def start_with_tray():
|
| 68 |
+
"""تشغيل النظام مع أيقونة شريط النظام"""
|
| 69 |
+
print("🖱️ تشغيل النظام مع أيقونة شريط النظام...")
|
| 70 |
+
|
| 71 |
+
# بدء الخدمة الخلفية أولاً
|
| 72 |
+
bg_process = start_background_service()
|
| 73 |
+
if not bg_process:
|
| 74 |
+
return False
|
| 75 |
+
|
| 76 |
+
time.sleep(3) # انتظار حتى تصبح الخدمة جاهزة
|
| 77 |
+
|
| 78 |
+
try:
|
| 79 |
+
# تشغيل أيقونة شريط النظام
|
| 80 |
+
subprocess.run([sys.executable, 'system_tray.py'])
|
| 81 |
+
except KeyboardInterrupt:
|
| 82 |
+
print("\n🛑 إيقاف النظام...")
|
| 83 |
+
# إيقاف الخدمة الخلفية
|
| 84 |
+
try:
|
| 85 |
+
import requests
|
| 86 |
+
requests.post('http://localhost:8888/stop', timeout=5)
|
| 87 |
+
except:
|
| 88 |
+
bg_process.terminate()
|
| 89 |
+
|
| 90 |
+
return True
|
| 91 |
+
|
| 92 |
+
def start_interactive():
|
| 93 |
+
"""تشغيل النظام في الوضع التفاعلي"""
|
| 94 |
+
print("🖥️ تشغيل النظام في الوضع التفاعلي...")
|
| 95 |
+
|
| 96 |
+
# بدء الخدمة الخلفية
|
| 97 |
+
bg_process = start_background_service()
|
| 98 |
+
if not bg_process:
|
| 99 |
+
return False
|
| 100 |
+
|
| 101 |
+
time.sleep(3)
|
| 102 |
+
|
| 103 |
+
# تشغيل الواجهة التفاعلية
|
| 104 |
+
try:
|
| 105 |
+
import requests
|
| 106 |
+
requests.post('http://localhost:8888/show-ui', timeout=5)
|
| 107 |
+
print("✅ تم تشغيل الواجهة التفاعلية")
|
| 108 |
+
|
| 109 |
+
# فتح المتصفح
|
| 110 |
+
import webbrowser
|
| 111 |
+
time.sleep(2)
|
| 112 |
+
webbrowser.open('http://localhost:5173')
|
| 113 |
+
|
| 114 |
+
# انتظار إنهاء المستخدم
|
| 115 |
+
input("اضغط Enter لإيقاف النظام...")
|
| 116 |
+
|
| 117 |
+
except KeyboardInterrupt:
|
| 118 |
+
pass
|
| 119 |
+
finally:
|
| 120 |
+
print("🛑 إيقاف النظام...")
|
| 121 |
+
try:
|
| 122 |
+
import requests
|
| 123 |
+
requests.post('http://localhost:8888/stop', timeout=5)
|
| 124 |
+
except:
|
| 125 |
+
bg_process.terminate()
|
| 126 |
+
|
| 127 |
+
return True
|
| 128 |
+
|
| 129 |
+
def start_headless():
|
| 130 |
+
"""تشغيل النظام بدون واجهة (للخوادم)"""
|
| 131 |
+
print("⚙️ تشغيل النظام بدون واجهة...")
|
| 132 |
+
|
| 133 |
+
try:
|
| 134 |
+
# تشغيل الخدمة الخلفية والانتظار
|
| 135 |
+
subprocess.run([sys.executable, 'background_service.py', 'start'])
|
| 136 |
+
except KeyboardInterrupt:
|
| 137 |
+
print("\n🛑 إيقاف النظام...")
|
| 138 |
+
|
| 139 |
+
return True
|
| 140 |
+
|
| 141 |
+
def show_status():
|
| 142 |
+
"""عرض حالة النظام"""
|
| 143 |
+
subprocess.run([sys.executable, 'background_service.py', 'status'])
|
| 144 |
+
|
| 145 |
+
def stop_system():
|
| 146 |
+
"""إيقاف النظام"""
|
| 147 |
+
subprocess.run([sys.executable, 'background_service.py', 'stop'])
|
| 148 |
+
|
| 149 |
+
def main():
|
| 150 |
+
parser = argparse.ArgumentParser(
|
| 151 |
+
description="مشغل نظام توزيع المهام الذكي",
|
| 152 |
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
| 153 |
+
epilog="""
|
| 154 |
+
أمثلة الاستخدام:
|
| 155 |
+
python launcher.py --tray # تشغيل مع أيقونة شريط ��لنظام
|
| 156 |
+
python launcher.py --interactive # تشغيل تفاعلي مع واجهة
|
| 157 |
+
python launcher.py --headless # تشغيل بدون واجهة (للخوادم)
|
| 158 |
+
python launcher.py --status # عرض حالة النظام
|
| 159 |
+
python launcher.py --stop # إيقاف النظام
|
| 160 |
+
"""
|
| 161 |
+
)
|
| 162 |
+
|
| 163 |
+
group = parser.add_mutually_exclusive_group(required=True)
|
| 164 |
+
group.add_argument('--tray', action='store_true',
|
| 165 |
+
help='تشغيل مع أيقونة شريط النظام')
|
| 166 |
+
group.add_argument('--interactive', action='store_true',
|
| 167 |
+
help='تشغيل تفاعلي مع واجهة')
|
| 168 |
+
group.add_argument('--headless', action='store_true',
|
| 169 |
+
help='تشغيل بدون واجهة (للخوادم)')
|
| 170 |
+
group.add_argument('--status', action='store_true',
|
| 171 |
+
help='عرض حالة النظام')
|
| 172 |
+
group.add_argument('--stop', action='store_true',
|
| 173 |
+
help='إيقاف النظام')
|
| 174 |
+
|
| 175 |
+
parser.add_argument('--install-deps', action='store_true',
|
| 176 |
+
help='تثبيت الاعتماديات المطلوبة')
|
| 177 |
+
|
| 178 |
+
args = parser.parse_args()
|
| 179 |
+
|
| 180 |
+
# فحص المتطلبات
|
| 181 |
+
if not check_requirements():
|
| 182 |
+
return 1
|
| 183 |
+
|
| 184 |
+
# تثبيت الاعتماديات إذا طُلب ذلك
|
| 185 |
+
if args.install_deps:
|
| 186 |
+
install_tray_dependencies()
|
| 187 |
+
return 0
|
| 188 |
+
|
| 189 |
+
# تنفيذ الأمر المطلوب
|
| 190 |
+
if args.status:
|
| 191 |
+
show_status()
|
| 192 |
+
elif args.stop:
|
| 193 |
+
stop_system()
|
| 194 |
+
elif args.headless:
|
| 195 |
+
success = start_headless()
|
| 196 |
+
elif args.interactive:
|
| 197 |
+
success = start_interactive()
|
| 198 |
+
elif args.tray:
|
| 199 |
+
# تثبيت اعتماديات أيقونة شريط النظام إذا لم تكن موجودة
|
| 200 |
+
try:
|
| 201 |
+
import pystray
|
| 202 |
+
except ImportError:
|
| 203 |
+
print("📦 تثبيت اعتماديات أيقونة شريط النظام...")
|
| 204 |
+
if not install_tray_dependencies():
|
| 205 |
+
print("❌ فشل في تثبيت الاعتماديات، التشغيل في الوضع التفاعلي...")
|
| 206 |
+
success = start_interactive()
|
| 207 |
+
else:
|
| 208 |
+
success = start_with_tray()
|
| 209 |
+
else:
|
| 210 |
+
success = start_with_tray()
|
| 211 |
+
|
| 212 |
+
return 0 if success else 1
|
| 213 |
+
|
| 214 |
+
if __name__ == "__main__":
|
| 215 |
+
sys.exit(main())
|
live_streaming.py
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
# live_streaming.py - نظام البث المباشر للألعاب والفيديو
|
| 3 |
+
|
| 4 |
+
import cv2
|
| 5 |
+
import numpy as np
|
| 6 |
+
import time
|
| 7 |
+
import threading
|
| 8 |
+
import logging
|
| 9 |
+
import asyncio
|
| 10 |
+
import base64
|
| 11 |
+
import json
|
| 12 |
+
from datetime import datetime
|
| 13 |
+
from processor_manager import should_offload
|
| 14 |
+
from remote_executor import execute_remotely
|
| 15 |
+
from functools import wraps
|
| 16 |
+
|
| 17 |
+
logging.basicConfig(level=logging.INFO)
|
| 18 |
+
|
| 19 |
+
class LiveStreamManager:
|
| 20 |
+
def __init__(self):
|
| 21 |
+
self.active_streams = {}
|
| 22 |
+
self.processing_nodes = []
|
| 23 |
+
self.load_balancer = StreamLoadBalancer()
|
| 24 |
+
|
| 25 |
+
def register_processing_node(self, node_id, capabilities):
|
| 26 |
+
"""تسجيل عقدة معالجة جديدة"""
|
| 27 |
+
self.processing_nodes.append({
|
| 28 |
+
"id": node_id,
|
| 29 |
+
"capabilities": capabilities,
|
| 30 |
+
"load": 0.0,
|
| 31 |
+
"last_ping": datetime.now()
|
| 32 |
+
})
|
| 33 |
+
logging.info(f"📡 تم تسجيل عقدة معالجة: {node_id}")
|
| 34 |
+
|
| 35 |
+
class StreamLoadBalancer:
|
| 36 |
+
def __init__(self):
|
| 37 |
+
self.node_loads = {}
|
| 38 |
+
|
| 39 |
+
def get_best_node(self, task_type, nodes):
|
| 40 |
+
"""اختيار أفضل عقدة للمعالجة"""
|
| 41 |
+
suitable_nodes = [n for n in nodes if task_type in n.get("capabilities", [])]
|
| 42 |
+
if not suitable_nodes:
|
| 43 |
+
return None
|
| 44 |
+
return min(suitable_nodes, key=lambda x: x["load"])
|
| 45 |
+
|
| 46 |
+
def stream_offload(func):
|
| 47 |
+
"""ديكوراتور خاص بالبث المباشر"""
|
| 48 |
+
@wraps(func)
|
| 49 |
+
def wrapper(*args, **kwargs):
|
| 50 |
+
complexity = estimate_stream_complexity(func, args, kwargs)
|
| 51 |
+
|
| 52 |
+
if complexity > 70 or should_offload(complexity):
|
| 53 |
+
logging.info(f"📺 إرسال مهمة البث {func.__name__} للمعالجة الموزعة")
|
| 54 |
+
return execute_remotely(func.__name__, args, kwargs)
|
| 55 |
+
|
| 56 |
+
logging.info(f"📺 معالجة البث محلياً: {func.__name__}")
|
| 57 |
+
return func(*args, **kwargs)
|
| 58 |
+
return wrapper
|
| 59 |
+
|
| 60 |
+
def estimate_stream_complexity(func, args, kwargs):
|
| 61 |
+
"""تقدير تعقيد معالجة البث"""
|
| 62 |
+
if func.__name__ == "process_game_stream":
|
| 63 |
+
return args[1] * args[2] / 10000 # FPS × الدقة
|
| 64 |
+
elif func.__name__ == "real_time_video_enhancement":
|
| 65 |
+
return args[0] * 20 # عدد التحسينات × 20
|
| 66 |
+
elif func.__name__ == "multi_stream_processing":
|
| 67 |
+
return len(args[0]) * 25 # عدد البثوث × 25
|
| 68 |
+
elif func.__name__ == "ai_commentary_generation":
|
| 69 |
+
return args[1] * 15 # طول النص × 15
|
| 70 |
+
return 40
|
| 71 |
+
|
| 72 |
+
# ═══════════════════════════════════════════════════════════════
|
| 73 |
+
# معالجة بث الألعاب المباشر
|
| 74 |
+
# ═══════════════════════════════════════════════════════════════
|
| 75 |
+
|
| 76 |
+
@stream_offload
|
| 77 |
+
def process_game_stream(stream_data, fps, resolution, enhancements=None):
|
| 78 |
+
"""معالجة بث الألعاب في الوقت الفعلي"""
|
| 79 |
+
start_time = time.time()
|
| 80 |
+
|
| 81 |
+
if enhancements is None:
|
| 82 |
+
enhancements = ["noise_reduction", "color_enhancement"]
|
| 83 |
+
|
| 84 |
+
logging.info(f"🎮 معالجة بث الألعاب - FPS: {fps}, الدقة: {resolution}")
|
| 85 |
+
logging.info(f"🔧 التحسينات: {enhancements}")
|
| 86 |
+
|
| 87 |
+
# محاكاة معالجة الإطارات
|
| 88 |
+
frame_count = len(stream_data) if isinstance(stream_data, list) else 60
|
| 89 |
+
processing_per_frame = 0.01 + (len(enhancements) * 0.005)
|
| 90 |
+
total_processing_time = frame_count * processing_per_frame
|
| 91 |
+
|
| 92 |
+
# محاكاة المعالجة
|
| 93 |
+
time.sleep(min(total_processing_time, 2))
|
| 94 |
+
|
| 95 |
+
# حساب جودة البث
|
| 96 |
+
quality_score = min(100, 60 + (len(enhancements) * 8) + (fps / 2))
|
| 97 |
+
latency = max(50, 200 - (fps * 2)) # أقل تأخير مع FPS أعلى
|
| 98 |
+
|
| 99 |
+
result = {
|
| 100 |
+
"status": "success",
|
| 101 |
+
"stream_type": "game",
|
| 102 |
+
"fps_processed": fps,
|
| 103 |
+
"resolution": resolution,
|
| 104 |
+
"frames_processed": frame_count,
|
| 105 |
+
"enhancements_applied": enhancements,
|
| 106 |
+
"quality_score": round(quality_score, 1),
|
| 107 |
+
"latency_ms": latency,
|
| 108 |
+
"processing_time": time.time() - start_time,
|
| 109 |
+
"bandwidth_optimized": True
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
logging.info(f"✅ تمت معالجة بث اللعبة - جودة: {result['quality_score']}%")
|
| 113 |
+
return result
|
| 114 |
+
|
| 115 |
+
@stream_offload
|
| 116 |
+
def real_time_video_enhancement(enhancement_types, video_quality="1080p", target_fps=60):
|
| 117 |
+
"""تحسين الفيديو في الوقت الفعلي"""
|
| 118 |
+
start_time = time.time()
|
| 119 |
+
|
| 120 |
+
available_enhancements = {
|
| 121 |
+
"upscaling": "تحسين الدقة",
|
| 122 |
+
"noise_reduction": "إزالة التشويش",
|
| 123 |
+
"color_grading": "تصحيح الألوان",
|
| 124 |
+
"motion_smoothing": "تنعيم الحركة",
|
| 125 |
+
"hdr_enhancement": "تحسين HDR",
|
| 126 |
+
"sharpening": "زيادة الحدة",
|
| 127 |
+
"stabilization": "تثبيت الصورة"
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
quality_multiplier = {"720p": 1, "1080p": 2, "1440p": 3, "4K": 5}
|
| 131 |
+
multiplier = quality_multiplier.get(video_quality, 2)
|
| 132 |
+
|
| 133 |
+
processing_time = len(enhancement_types) * multiplier * target_fps * 0.0001
|
| 134 |
+
|
| 135 |
+
logging.info(f"📹 تحسين الفيديو المباشر - الجودة: {video_quality}")
|
| 136 |
+
logging.info(f"🎯 التحسينات: {enhancement_types}")
|
| 137 |
+
|
| 138 |
+
# محاكاة التحسين
|
| 139 |
+
time.sleep(min(processing_time, 1.5))
|
| 140 |
+
|
| 141 |
+
enhancements_applied = {}
|
| 142 |
+
for enhancement in enhancement_types:
|
| 143 |
+
if enhancement in available_enhancements:
|
| 144 |
+
enhancements_applied[enhancement] = {
|
| 145 |
+
"name": available_enhancements[enhancement],
|
| 146 |
+
"improvement": round(np.random.uniform(15, 35), 1),
|
| 147 |
+
"processing_cost": round(processing_time / len(enhancement_types), 4)
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
result = {
|
| 151 |
+
"status": "success",
|
| 152 |
+
"video_quality": video_quality,
|
| 153 |
+
"target_fps": target_fps,
|
| 154 |
+
"enhancements": enhancements_applied,
|
| 155 |
+
"total_improvement": round(np.mean([e["improvement"] for e in enhancements_applied.values()]), 1),
|
| 156 |
+
"processing_time": time.time() - start_time,
|
| 157 |
+
"real_time_capable": processing_time < (1/target_fps)
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
logging.info(f"✅ تم تحسين الفيديو - تحسن: {result['total_improvement']}%")
|
| 161 |
+
return result
|
| 162 |
+
|
| 163 |
+
# ═══════════════════════════════════════════════════════════════
|
| 164 |
+
# معالجة متعددة البثوث
|
| 165 |
+
# ═══════════════════════════════════════════════════════════════
|
| 166 |
+
|
| 167 |
+
@stream_offload
|
| 168 |
+
def multi_stream_processing(streams_data, processing_mode="parallel"):
|
| 169 |
+
"""معالجة عدة بثوث في نفس الوقت"""
|
| 170 |
+
start_time = time.time()
|
| 171 |
+
|
| 172 |
+
logging.info(f"📡 معالجة متعددة البثوث - العدد: {len(streams_data)}")
|
| 173 |
+
logging.info(f"⚙️ وضع المعالجة: {processing_mode}")
|
| 174 |
+
|
| 175 |
+
results = {}
|
| 176 |
+
|
| 177 |
+
if processing_mode == "parallel":
|
| 178 |
+
# محاكاة المعالجة المتوازية
|
| 179 |
+
max_processing_time = max([s.get("complexity", 1) for s in streams_data]) * 0.1
|
| 180 |
+
time.sleep(min(max_processing_time, 2))
|
| 181 |
+
|
| 182 |
+
for i, stream in enumerate(streams_data):
|
| 183 |
+
stream_id = f"stream_{i+1}"
|
| 184 |
+
results[stream_id] = {
|
| 185 |
+
"status": "processed",
|
| 186 |
+
"quality": stream.get("quality", "1080p"),
|
| 187 |
+
"fps": stream.get("fps", 30),
|
| 188 |
+
"enhancement_applied": True,
|
| 189 |
+
"processing_node": f"node_{(i % 3) + 1}" # توزيع على 3 عقد
|
| 190 |
+
}
|
| 191 |
+
else:
|
| 192 |
+
# معالجة تسلسلية
|
| 193 |
+
total_time = sum([s.get("complexity", 1) for s in streams_data]) * 0.05
|
| 194 |
+
time.sleep(min(total_time, 3))
|
| 195 |
+
|
| 196 |
+
for i, stream in enumerate(streams_data):
|
| 197 |
+
stream_id = f"stream_{i+1}"
|
| 198 |
+
results[stream_id] = {
|
| 199 |
+
"status": "processed",
|
| 200 |
+
"quality": stream.get("quality", "1080p"),
|
| 201 |
+
"fps": stream.get("fps", 30),
|
| 202 |
+
"processing_order": i + 1
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
result = {
|
| 206 |
+
"status": "success",
|
| 207 |
+
"streams_processed": len(streams_data),
|
| 208 |
+
"processing_mode": processing_mode,
|
| 209 |
+
"results": results,
|
| 210 |
+
"total_processing_time": time.time() - start_time,
|
| 211 |
+
"average_quality": round(np.mean([30, 45, 60, 55]), 1), # محاكاة متوسط الجودة
|
| 212 |
+
"nodes_utilized": len(set([r.get("processing_node", "main") for r in results.values()]))
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
logging.info(f"✅ تمت معالجة {len(streams_data)} بث - العقد المستخدمة: {result['nodes_utilized']}")
|
| 216 |
+
return result
|
| 217 |
+
|
| 218 |
+
# ═══════════════════════════════════════════════════════════════
|
| 219 |
+
# ذكاء اصطناعي للبث
|
| 220 |
+
# ═══════════════════════════════════════════════════════════════
|
| 221 |
+
|
| 222 |
+
@stream_offload
|
| 223 |
+
def ai_commentary_generation(game_events, commentary_length, language="ar"):
|
| 224 |
+
"""توليد تعليق ذكي للألعاب"""
|
| 225 |
+
start_time = time.time()
|
| 226 |
+
|
| 227 |
+
logging.info(f"🤖 توليد تعليق ذكي - الطول: {commentary_length} كلمة")
|
| 228 |
+
|
| 229 |
+
# قوالب التعليق
|
| 230 |
+
commentary_templates = {
|
| 231 |
+
"ar": [
|
| 232 |
+
"حركة رائعة من اللاعب!",
|
| 233 |
+
"هذا هدف مذهل!",
|
| 234 |
+
"دفاع قوي في هذه اللحظة",
|
| 235 |
+
"استراتيجية مم��ازة",
|
| 236 |
+
"أداء استثنائي!"
|
| 237 |
+
],
|
| 238 |
+
"en": [
|
| 239 |
+
"Amazing move by the player!",
|
| 240 |
+
"What a fantastic goal!",
|
| 241 |
+
"Strong defense right there",
|
| 242 |
+
"Excellent strategy",
|
| 243 |
+
"Outstanding performance!"
|
| 244 |
+
]
|
| 245 |
+
}
|
| 246 |
+
|
| 247 |
+
processing_time = commentary_length * 0.02 # 0.02 ثانية لكل كلمة
|
| 248 |
+
time.sleep(min(processing_time, 1))
|
| 249 |
+
|
| 250 |
+
# توليد التعليق
|
| 251 |
+
templates = commentary_templates.get(language, commentary_templates["ar"])
|
| 252 |
+
generated_commentary = []
|
| 253 |
+
|
| 254 |
+
for i in range(min(commentary_length // 5, len(game_events))):
|
| 255 |
+
template = np.random.choice(templates)
|
| 256 |
+
generated_commentary.append(template)
|
| 257 |
+
|
| 258 |
+
result = {
|
| 259 |
+
"status": "success",
|
| 260 |
+
"language": language,
|
| 261 |
+
"commentary_length": len(generated_commentary),
|
| 262 |
+
"generated_text": generated_commentary,
|
| 263 |
+
"game_events_analyzed": len(game_events),
|
| 264 |
+
"processing_time": time.time() - start_time,
|
| 265 |
+
"emotion_detection": "excited", # محاكاة كشف المشاعر
|
| 266 |
+
"context_awareness": True
|
| 267 |
+
}
|
| 268 |
+
|
| 269 |
+
logging.info(f"✅ تم توليد التعليق - {len(generated_commentary)} جملة")
|
| 270 |
+
return result
|
| 271 |
+
|
| 272 |
+
@stream_offload
|
| 273 |
+
def stream_quality_optimization(stream_metadata, target_bandwidth, viewer_count):
|
| 274 |
+
"""تحسين جودة البث حسب النطاق الترددي وعدد المشاهدين"""
|
| 275 |
+
start_time = time.time()
|
| 276 |
+
|
| 277 |
+
logging.info(f"📊 تحسين جودة البث - المشاهدين: {viewer_count}")
|
| 278 |
+
logging.info(f"🌐 النطاق المستهدف: {target_bandwidth} Mbps")
|
| 279 |
+
|
| 280 |
+
# حساب الجودة المثلى
|
| 281 |
+
base_quality = min(target_bandwidth * 200, 1080) # حد أقصى 1080p
|
| 282 |
+
|
| 283 |
+
# تعديل حسب عدد المشاهدين
|
| 284 |
+
if viewer_count > 1000:
|
| 285 |
+
quality_adjustment = 0.8 # تقليل الجودة للأعداد الكبيرة
|
| 286 |
+
elif viewer_count > 100:
|
| 287 |
+
quality_adjustment = 0.9
|
| 288 |
+
else:
|
| 289 |
+
quality_adjustment = 1.0
|
| 290 |
+
|
| 291 |
+
optimized_quality = int(base_quality * quality_adjustment)
|
| 292 |
+
|
| 293 |
+
# تحديد FPS مناسب
|
| 294 |
+
if optimized_quality >= 1080:
|
| 295 |
+
optimal_fps = 60
|
| 296 |
+
elif optimized_quality >= 720:
|
| 297 |
+
optimal_fps = 45
|
| 298 |
+
else:
|
| 299 |
+
optimal_fps = 30
|
| 300 |
+
|
| 301 |
+
time.sleep(0.5) # محاكاة المعالجة
|
| 302 |
+
|
| 303 |
+
result = {
|
| 304 |
+
"status": "success",
|
| 305 |
+
"original_quality": stream_metadata.get("quality", "1080p"),
|
| 306 |
+
"optimized_quality": f"{optimized_quality}p",
|
| 307 |
+
"optimal_fps": optimal_fps,
|
| 308 |
+
"target_bandwidth": target_bandwidth,
|
| 309 |
+
"viewer_count": viewer_count,
|
| 310 |
+
"bandwidth_saved": round(max(0, (1080 - optimized_quality) / 1080 * 100), 1),
|
| 311 |
+
"processing_time": time.time() - start_time,
|
| 312 |
+
"adaptive_streaming": True
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
logging.info(f"✅ تم تحسين البث - الجودة: {result['optimized_quality']}")
|
| 316 |
+
return result
|
| 317 |
+
|
| 318 |
+
# ═══════════════════════════════════════════════════════════════
|
| 319 |
+
# إدارة البث المباشر
|
| 320 |
+
# ═══════════════════════════════════════════════════════════════
|
| 321 |
+
|
| 322 |
+
class LiveStreamCoordinator:
|
| 323 |
+
def __init__(self):
|
| 324 |
+
self.active_streams = {}
|
| 325 |
+
self.processing_history = []
|
| 326 |
+
|
| 327 |
+
def start_stream(self, stream_id, config):
|
| 328 |
+
"""بدء بث مباشر جديد"""
|
| 329 |
+
self.active_streams[stream_id] = {
|
| 330 |
+
"config": config,
|
| 331 |
+
"start_time": datetime.now(),
|
| 332 |
+
"status": "active",
|
| 333 |
+
"processing_nodes": [],
|
| 334 |
+
"viewers": 0
|
| 335 |
+
}
|
| 336 |
+
logging.info(f"🔴 بدء البث: {stream_id}")
|
| 337 |
+
|
| 338 |
+
def distribute_processing(self, stream_id, task_type, data):
|
| 339 |
+
"""توزيع معالجة البث على العقد المختلفة"""
|
| 340 |
+
if stream_id not in self.active_streams:
|
| 341 |
+
return {"error": "البث غير موجود"}
|
| 342 |
+
|
| 343 |
+
# اختيار العقدة المناسبة
|
| 344 |
+
best_node = self._select_processing_node(task_type)
|
| 345 |
+
|
| 346 |
+
# تنفيذ المعالجة
|
| 347 |
+
if best_node:
|
| 348 |
+
result = execute_remotely(task_type, [data], {})
|
| 349 |
+
self.active_streams[stream_id]["processing_nodes"].append(best_node)
|
| 350 |
+
return result
|
| 351 |
+
else:
|
| 352 |
+
# معالجة محلية
|
| 353 |
+
return self._process_locally(task_type, data)
|
| 354 |
+
|
| 355 |
+
def _select_processing_node(self, task_type):
|
| 356 |
+
"""اختيار أفضل عقدة للمعالجة"""
|
| 357 |
+
# منطق اختيار العقدة (مبسط)
|
| 358 |
+
return f"node_gpu_{np.random.randint(1, 4)}"
|
| 359 |
+
|
| 360 |
+
def _process_locally(self, task_type, data):
|
| 361 |
+
"""معالجة محلية احتياطية"""
|
| 362 |
+
return {"status": "processed_locally", "task": task_type}
|
| 363 |
+
|
| 364 |
+
# دالة اختبار شاملة للبث المباشر
|
| 365 |
+
def run_live_streaming_benchmark():
|
| 366 |
+
"""اختبار شامل لنظام البث المباشر"""
|
| 367 |
+
print("\n📺🎮 اختبار نظام البث المباشر للألعاب والفيديو")
|
| 368 |
+
print("=" * 70)
|
| 369 |
+
|
| 370 |
+
# بيانات تجريبية
|
| 371 |
+
game_stream_data = [f"frame_{i}" for i in range(60)] # 60 إطار
|
| 372 |
+
game_events = ["goal", "save", "foul", "corner", "yellow_card"]
|
| 373 |
+
|
| 374 |
+
multi_streams = [
|
| 375 |
+
{"quality": "1080p", "fps": 60, "complexity": 3},
|
| 376 |
+
{"quality": "720p", "fps": 30, "complexity": 2},
|
| 377 |
+
{"quality": "1440p", "fps": 45, "complexity": 4}
|
| 378 |
+
]
|
| 379 |
+
|
| 380 |
+
tests = [
|
| 381 |
+
("معالجة بث لعبة", lambda: process_game_stream(game_stream_data, 60, "1920x1080", ["noise_reduction", "color_enhancement", "sharpening"])),
|
| 382 |
+
("تحسين فيديو مباشر", lambda: real_time_video_enhancement(["upscaling", "noise_reduction", "hdr_enhancement"], "1080p", 60)),
|
| 383 |
+
("معالجة متعددة البثوث", lambda: multi_stream_processing(multi_streams, "parallel")),
|
| 384 |
+
("توليد تعليق ذكي", lambda: ai_commentary_generation(game_events, 50, "ar")),
|
| 385 |
+
("تحسين جودة البث", lambda: stream_quality_optimization({"quality": "1080p"}, 5.0, 500))
|
| 386 |
+
]
|
| 387 |
+
|
| 388 |
+
coordinator = LiveStreamCoordinator()
|
| 389 |
+
|
| 390 |
+
for test_name, test_func in tests:
|
| 391 |
+
print(f"\n🔄 تشغيل: {test_name}")
|
| 392 |
+
try:
|
| 393 |
+
result = test_func()
|
| 394 |
+
print(f"✅ نجح: {test_name}")
|
| 395 |
+
if "processing_time" in result:
|
| 396 |
+
print(f"⏱️ وقت المعالجة: {result['processing_time']:.2f}s")
|
| 397 |
+
if "quality_score" in result:
|
| 398 |
+
print(f"⭐ جودة: {result['quality_score']}%")
|
| 399 |
+
except Exception as e:
|
| 400 |
+
print(f"❌ فشل: {test_name} - {str(e)}")
|
| 401 |
+
|
| 402 |
+
print("\n🏁 انتهى اختبار البث المباشر")
|
| 403 |
+
|
| 404 |
+
if __name__ == "__main__":
|
| 405 |
+
run_live_streaming_benchmark()
|
load_balancer.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# load_balancer.py
|
| 2 |
+
import peer_discovery, requests, time, smart_tasks, psutil, socket
|
| 3 |
+
|
| 4 |
+
def send(peer, func, *args, **kw):
|
| 5 |
+
try:
|
| 6 |
+
r = requests.post(peer, json={"func": func,
|
| 7 |
+
"args": list(args),
|
| 8 |
+
"kwargs": kw}, timeout=12)
|
| 9 |
+
return r.json()
|
| 10 |
+
except Exception as e:
|
| 11 |
+
return {"error": str(e)}
|
| 12 |
+
|
| 13 |
+
def choose_peer():
|
| 14 |
+
"""اختيار أفضل جهاز - أولوية LAN ثم WAN"""
|
| 15 |
+
import socket
|
| 16 |
+
|
| 17 |
+
lan_peers = []
|
| 18 |
+
wan_peers = []
|
| 19 |
+
|
| 20 |
+
# تصنيف الأجهزة
|
| 21 |
+
for p in list(peer_discovery.PEERS):
|
| 22 |
+
ip = p.split('//')[1].split(':')[0] if '//' in p else p.split(':')[0]
|
| 23 |
+
if is_local_ip(ip):
|
| 24 |
+
lan_peers.append(p)
|
| 25 |
+
else:
|
| 26 |
+
wan_peers.append(p)
|
| 27 |
+
|
| 28 |
+
# أولاً: جرب الأجهزة المحلية (LAN)
|
| 29 |
+
best_lan = find_best_peer(lan_peers)
|
| 30 |
+
if best_lan:
|
| 31 |
+
return best_lan
|
| 32 |
+
|
| 33 |
+
# ثانياً: إذا لم تتوفر أجهزة محلية، جرب WAN
|
| 34 |
+
if internet_available():
|
| 35 |
+
best_wan = find_best_peer(wan_peers)
|
| 36 |
+
return best_wan
|
| 37 |
+
|
| 38 |
+
return None
|
| 39 |
+
|
| 40 |
+
def find_best_peer(peers):
|
| 41 |
+
"""العثور على أفضل جهاز من قائمة معينة"""
|
| 42 |
+
best = None
|
| 43 |
+
for p in peers:
|
| 44 |
+
try:
|
| 45 |
+
cpu = requests.get(p.replace("/run", "/cpu"), timeout=2).json()["usage"]
|
| 46 |
+
best = (p, cpu) if best is None or cpu < best[1] else best
|
| 47 |
+
except:
|
| 48 |
+
continue
|
| 49 |
+
return best[0] if best else None
|
| 50 |
+
|
| 51 |
+
def is_local_ip(ip):
|
| 52 |
+
"""فحص إذا كان IP محلي"""
|
| 53 |
+
return (
|
| 54 |
+
ip.startswith('192.168.') or
|
| 55 |
+
ip.startswith('10.') or
|
| 56 |
+
ip.startswith('172.') or
|
| 57 |
+
ip == '127.0.0.1'
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
def internet_available():
|
| 61 |
+
"""فحص توفر الإنترنت"""
|
| 62 |
+
try:
|
| 63 |
+
socket.create_connection(("8.8.8.8", 53), timeout=3)
|
| 64 |
+
return True
|
| 65 |
+
except:
|
| 66 |
+
return False
|
| 67 |
+
|
| 68 |
+
while True:
|
| 69 |
+
peer = choose_peer()
|
| 70 |
+
if peer:
|
| 71 |
+
print(f"\n🛰️ إرسال إلى {peer}")
|
| 72 |
+
res = send(peer, "prime_calculation", 30000)
|
| 73 |
+
else:
|
| 74 |
+
print("\n⚙️ لا أقران؛ العمل محليّ على", socket.gethostname())
|
| 75 |
+
res = smart_tasks.prime_calculation(30000)
|
| 76 |
+
print("🔹 النتيجة (جزئية):", str(res)[:120])
|
| 77 |
+
time.sleep(10)
|
main.py
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
main.py — نقطة تشغيل نظام OffloadHelper في ملف واحد
|
| 4 |
+
خيارات سطر الأوامر:
|
| 5 |
+
-s / --stats-interval ثواني بين كل طباعة لإحصائية الأقران (0 = مرة واحدة فقط)
|
| 6 |
+
--no-cli تشغيل بلا قائمة تفاعلية حتى مع وجود TTY
|
| 7 |
+
"""
|
| 8 |
+
import os
|
| 9 |
+
import sys
|
| 10 |
+
import time
|
| 11 |
+
import threading
|
| 12 |
+
import subprocess
|
| 13 |
+
import logging
|
| 14 |
+
import argparse
|
| 15 |
+
from pathlib import Path
|
| 16 |
+
from typing import Any
|
| 17 |
+
|
| 18 |
+
from flask import Flask, request, jsonify
|
| 19 |
+
from flask_cors import CORS
|
| 20 |
+
|
| 21 |
+
# ───────────────────── ضبط المسارات ──────────────────────
|
| 22 |
+
FILE = Path(__file__).resolve()
|
| 23 |
+
BASE_DIR = FILE.parent
|
| 24 |
+
PROJECT_ROOT = BASE_DIR.parent
|
| 25 |
+
for p in (BASE_DIR, PROJECT_ROOT):
|
| 26 |
+
sys.path.insert(0, str(p))
|
| 27 |
+
|
| 28 |
+
# ───────────────────── إعداد السجلات ─────────────────────
|
| 29 |
+
os.makedirs("logs", exist_ok=True)
|
| 30 |
+
logging.basicConfig(
|
| 31 |
+
level=logging.INFO,
|
| 32 |
+
format="%(asctime)s - %(levelname)s - %(message)s",
|
| 33 |
+
handlers=[
|
| 34 |
+
logging.StreamHandler(),
|
| 35 |
+
logging.FileHandler("logs/main.log", mode="a")
|
| 36 |
+
]
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
# ─────────────── تحميل متغيرات البيئة (اختياري) ───────────────
|
| 40 |
+
try:
|
| 41 |
+
from dotenv import load_dotenv
|
| 42 |
+
load_dotenv()
|
| 43 |
+
logging.info("🔧 تم تحميل متغيرات البيئة من .env")
|
| 44 |
+
except ImportError:
|
| 45 |
+
logging.warning("🔧 python-dotenv غير مثبَّت؛ تَخطّي .env")
|
| 46 |
+
|
| 47 |
+
# ─────────────── وحدات المشروع الداخلية ────────────────
|
| 48 |
+
try:
|
| 49 |
+
from peer_discovery import (
|
| 50 |
+
register_service_lan,
|
| 51 |
+
discover_lan_loop,
|
| 52 |
+
register_with_central,
|
| 53 |
+
fetch_central_loop,
|
| 54 |
+
PEERS
|
| 55 |
+
)
|
| 56 |
+
from your_tasks import matrix_multiply, prime_calculation, data_processing
|
| 57 |
+
from distributed_executor import DistributedExecutor
|
| 58 |
+
from peer_statistics import print_peer_statistics
|
| 59 |
+
except ImportError as e:
|
| 60 |
+
logging.error(f"❌ تعذّر استيراد وحدة: {e}")
|
| 61 |
+
sys.exit(1)
|
| 62 |
+
|
| 63 |
+
# ─────────────── ثابتات التهيئة ───────────────
|
| 64 |
+
CPU_PORT = int(os.getenv("CPU_PORT", "7520"))
|
| 65 |
+
SHARED_SECRET = os.getenv("SHARED_SECRET", "my_shared_secret_123")
|
| 66 |
+
PYTHON_EXE = sys.executable
|
| 67 |
+
|
| 68 |
+
# ─────────────── خيارات سطر الأوامر ───────────────
|
| 69 |
+
parser = argparse.ArgumentParser()
|
| 70 |
+
parser.add_argument(
|
| 71 |
+
"--stats-interval", "-s",
|
| 72 |
+
type=int,
|
| 73 |
+
default=0,
|
| 74 |
+
help="ثواني بين كل طباعة لإحصائية الأقران (0 = مرة واحدة فقط)"
|
| 75 |
+
)
|
| 76 |
+
parser.add_argument(
|
| 77 |
+
"--no-cli",
|
| 78 |
+
action="store_true",
|
| 79 |
+
help="تعطيل القائمة التفاعلية حتى عند وجود TTY"
|
| 80 |
+
)
|
| 81 |
+
args = parser.parse_args()
|
| 82 |
+
|
| 83 |
+
# ─────────────── خادم Flask البسيط ───────────────
|
| 84 |
+
flask_app = Flask(__name__)
|
| 85 |
+
CORS(flask_app, resources={r"/*": {"origins": "*"}})
|
| 86 |
+
|
| 87 |
+
@flask_app.route("/run_task", methods=["POST"])
|
| 88 |
+
def run_task():
|
| 89 |
+
try:
|
| 90 |
+
# قراءة البيانات
|
| 91 |
+
data = request.get_json() if request.is_json else request.form
|
| 92 |
+
task_id = data.get("task_id")
|
| 93 |
+
|
| 94 |
+
if not task_id:
|
| 95 |
+
return jsonify(error="يجب تحديد task_id"), 400
|
| 96 |
+
|
| 97 |
+
# معالجة المهام
|
| 98 |
+
if task_id == "1":
|
| 99 |
+
result = matrix_multiply(500)
|
| 100 |
+
elif task_id == "2":
|
| 101 |
+
result = prime_calculation(100_000)
|
| 102 |
+
elif task_id == "3":
|
| 103 |
+
result = data_processing(10_000)
|
| 104 |
+
else:
|
| 105 |
+
return jsonify(error="معرف المهمة غير صحيح"), 400
|
| 106 |
+
|
| 107 |
+
return jsonify(result=result)
|
| 108 |
+
|
| 109 |
+
except Exception as e:
|
| 110 |
+
logging.error(f"خطأ في معالجة المهمة: {str(e)}", exc_info=True)
|
| 111 |
+
return jsonify(error="حدث خطأ داخلي في الخادم"), 500
|
| 112 |
+
def start_flask_server():
|
| 113 |
+
ip_public = os.getenv("PUBLIC_IP", "127.0.0.1")
|
| 114 |
+
logging.info(f"🌐 Flask متوفر على: http://{ip_public}:{CPU_PORT}/run_task")
|
| 115 |
+
flask_app.run(host="0.0.0.0", port=CPU_PORT, debug=False)
|
| 116 |
+
|
| 117 |
+
# ─────────────── خدمات خلفية محلية ───────────────
|
| 118 |
+
def start_services():
|
| 119 |
+
"""peer_server و load_balancer"""
|
| 120 |
+
try:
|
| 121 |
+
subprocess.Popen([PYTHON_EXE, "peer_server.py", "--port", str(CPU_PORT)])
|
| 122 |
+
subprocess.Popen([PYTHON_EXE, "load_balancer.py"])
|
| 123 |
+
logging.info("✅ تم تشغيل الخدمات الخلفيّة")
|
| 124 |
+
except Exception as exc:
|
| 125 |
+
logging.error(f"❌ خطأ بتشغيل الخدمات الخلفية: {exc}")
|
| 126 |
+
|
| 127 |
+
# ───��─────────── مهام مثالية محلية ───────────────
|
| 128 |
+
def example_task(x: int) -> int:
|
| 129 |
+
return x * x
|
| 130 |
+
|
| 131 |
+
def benchmark(fn, *args):
|
| 132 |
+
t0 = time.time()
|
| 133 |
+
res = fn(*args)
|
| 134 |
+
return time.time() - t0, res
|
| 135 |
+
|
| 136 |
+
# ─────────────── طباعة الإحصائية دوريًّا ─────────────
|
| 137 |
+
def stats_loop(interval: int, executor: DistributedExecutor):
|
| 138 |
+
while True:
|
| 139 |
+
peers_now = [
|
| 140 |
+
{"ip": host, "port": port}
|
| 141 |
+
for (host, port) in executor.peer_registry.get_all()
|
| 142 |
+
]
|
| 143 |
+
print_peer_statistics(peers_now)
|
| 144 |
+
time.sleep(interval)
|
| 145 |
+
|
| 146 |
+
# ─────────────── القائمة التفاعلية CLI ─────────────
|
| 147 |
+
def menu(executor: DistributedExecutor):
|
| 148 |
+
tasks = {
|
| 149 |
+
"1": ("ضرب المصفوفات", matrix_multiply, 500),
|
| 150 |
+
"2": ("حساب الأعداد الأولية", prime_calculation, 100_000),
|
| 151 |
+
"3": ("معالجة البيانات", data_processing, 10_000),
|
| 152 |
+
"5": ("مهمة موزعة (مثال)", example_task, 42),
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
while True:
|
| 156 |
+
print("\n🚀 نظام توزيع المهام الذكي")
|
| 157 |
+
for k, (title, _, _) in tasks.items():
|
| 158 |
+
print(f"{k}: {title}")
|
| 159 |
+
print("q: خروج")
|
| 160 |
+
choice = input("اختر المهمة: ").strip().lower()
|
| 161 |
+
|
| 162 |
+
if choice == "q":
|
| 163 |
+
print("🛑 تم إنهاء البرنامج.")
|
| 164 |
+
break
|
| 165 |
+
if choice not in tasks:
|
| 166 |
+
print("⚠️ اختيار غير صحيح.")
|
| 167 |
+
continue
|
| 168 |
+
|
| 169 |
+
name, fn, arg = tasks[choice]
|
| 170 |
+
print(f"\nتشغيل: {name}…")
|
| 171 |
+
|
| 172 |
+
try:
|
| 173 |
+
if choice == "5":
|
| 174 |
+
logging.info("📡 إرسال المهمة إلى العقد الموزَّعة…")
|
| 175 |
+
future = executor.submit(fn, arg)
|
| 176 |
+
print(f"✅ النتيجة (موزعة): {future.result()}")
|
| 177 |
+
else:
|
| 178 |
+
dur, res = benchmark(fn, arg)
|
| 179 |
+
print(f"✅ النتيجة: {res}\n⏱️ الوقت: {dur:.3f} ث")
|
| 180 |
+
except Exception as exc:
|
| 181 |
+
print(f"❌ خطأ في تنفيذ المهمة: {exc}")
|
| 182 |
+
|
| 183 |
+
# ─────────────── الدالة الرئيسية ───────────────
|
| 184 |
+
def main():
|
| 185 |
+
# 1) خدمات back‑end
|
| 186 |
+
start_services()
|
| 187 |
+
|
| 188 |
+
# 2) مهيّئ الموزِّع
|
| 189 |
+
executor = DistributedExecutor(SHARED_SECRET)
|
| 190 |
+
executor.peer_registry.register_service("node_main", CPU_PORT)
|
| 191 |
+
|
| 192 |
+
# 3) إضافة الأقران المكتشفين
|
| 193 |
+
for peer_url in list(PEERS):
|
| 194 |
+
try:
|
| 195 |
+
host, port_str = peer_url.split("//")[1].split("/run")[0].split(":")
|
| 196 |
+
executor.peer_registry.register_service(
|
| 197 |
+
f"peer_{host.replace('.', '_')}",
|
| 198 |
+
int(port_str)
|
| 199 |
+
)
|
| 200 |
+
except Exception as exc:
|
| 201 |
+
logging.warning(f"⚠️ تخطّي peer ({peer_url}): {exc}")
|
| 202 |
+
|
| 203 |
+
# 4) طباعة أولية
|
| 204 |
+
initial_peers = [
|
| 205 |
+
{"ip": host, "port": int(port)}
|
| 206 |
+
for peer_url in PEERS
|
| 207 |
+
if (hp := peer_url.split("//")[1].split("/run")[0]).count(":") == 1
|
| 208 |
+
for host, port in [hp.split(":")]
|
| 209 |
+
]
|
| 210 |
+
print_peer_statistics(initial_peers)
|
| 211 |
+
|
| 212 |
+
# 5) حلقة إحصائية دورية
|
| 213 |
+
if args.stats_interval > 0:
|
| 214 |
+
threading.Thread(
|
| 215 |
+
target=stats_loop,
|
| 216 |
+
args=(args.stats_interval, executor),
|
| 217 |
+
daemon=True
|
| 218 |
+
).start()
|
| 219 |
+
|
| 220 |
+
logging.info("✅ النظام جاهز للعمل")
|
| 221 |
+
|
| 222 |
+
# 6) CLI إن توفر TTY ولم يُطلَب no-cli
|
| 223 |
+
if not args.no_cli and sys.stdin.isatty():
|
| 224 |
+
menu(executor)
|
| 225 |
+
else:
|
| 226 |
+
logging.info("ℹ️ القائمة التفاعلية معطّلة (no TTY أو --no-cli)")
|
| 227 |
+
|
| 228 |
+
# ─────────────── تشغيل البرنامج ───────────────
|
| 229 |
+
if __name__ == "__main__":
|
| 230 |
+
# Zeroconf
|
| 231 |
+
threading.Thread(target=register_service_lan, daemon=True).start()
|
| 232 |
+
threading.Thread(target=discover_lan_loop, daemon=True).start()
|
| 233 |
+
|
| 234 |
+
# خادم وسيط مركزي
|
| 235 |
+
register_with_central()
|
| 236 |
+
threading.Thread(target=fetch_central_loop, daemon=True).start()
|
| 237 |
+
|
| 238 |
+
# ماسح إنترنت (اختياري)
|
| 239 |
+
try:
|
| 240 |
+
from internet_scanner import internet_scanner
|
| 241 |
+
threading.Thread(
|
| 242 |
+
target=internet_scanner.start_continuous_scan,
|
| 243 |
+
daemon=True
|
| 244 |
+
).start()
|
| 245 |
+
logging.info("🔍 بدء المسح المستمر للإنترنت")
|
| 246 |
+
except ImportError:
|
| 247 |
+
logging.warning("🔍 internet_scanner غير متوافر – تم التخطي")
|
| 248 |
+
|
| 249 |
+
# Flask
|
| 250 |
+
threading.Thread(target=start_flask_server, daemon=True).start()
|
| 251 |
+
|
| 252 |
+
# وحدة تحكم (اختياري)
|
| 253 |
+
try:
|
| 254 |
+
from your_control import control
|
| 255 |
+
control.start()
|
| 256 |
+
except ImportError:
|
| 257 |
+
logging.info("🛈 your_control غير متوفّر �� تشغيل افتراضي")
|
| 258 |
+
|
| 259 |
+
# CLI / إحصائية
|
| 260 |
+
main()
|
main.spec
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
)
|
offload_lib.py
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# offload_lib.py
|
| 3 |
+
|
| 4 |
+
import time
|
| 5 |
+
import math
|
| 6 |
+
import random
|
| 7 |
+
import psutil
|
| 8 |
+
import requests
|
| 9 |
+
import socket
|
| 10 |
+
from functools import wraps
|
| 11 |
+
from zeroconf import Zeroconf, ServiceBrowser
|
| 12 |
+
import logging
|
| 13 |
+
|
| 14 |
+
# إعداد السجل
|
| 15 |
+
logging.basicConfig(
|
| 16 |
+
level=logging.INFO,
|
| 17 |
+
format='%(asctime)s - %(levelname)s - %(message)s'
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
# إعدادات التحميل
|
| 21 |
+
MAX_CPU = 0.6 # عتبة استخدام CPU فقط
|
| 22 |
+
|
| 23 |
+
class PeerListener:
|
| 24 |
+
def __init__(self):
|
| 25 |
+
self.peers = []
|
| 26 |
+
|
| 27 |
+
def add_service(self, zc, type, name):
|
| 28 |
+
info = zc.get_service_info(type, name)
|
| 29 |
+
if info:
|
| 30 |
+
ip = socket.inet_ntoa(info.addresses[0])
|
| 31 |
+
self.peers.append(f"{ip}:{info.port}")
|
| 32 |
+
logging.info(f"🔗 جهاز مكتشف: {ip}:{info.port}")
|
| 33 |
+
|
| 34 |
+
def update_service(self, zc, type, name):
|
| 35 |
+
logging.debug(f"🔄 تم تحديث الخدمة: {name}")
|
| 36 |
+
pass # هنا فقط لتفادي التحذير
|
| 37 |
+
|
| 38 |
+
def discover_peers(timeout=1.5):
|
| 39 |
+
"""اكتشاف الأجهزة المتاحة - أولوية LAN ثم WAN ثم الإنترنت مع فحص المشروع"""
|
| 40 |
+
import peer_discovery
|
| 41 |
+
from project_identifier import verify_project_compatibility
|
| 42 |
+
|
| 43 |
+
zc = Zeroconf()
|
| 44 |
+
listener = PeerListener()
|
| 45 |
+
ServiceBrowser(zc, "_http._tcp.local.", listener)
|
| 46 |
+
time.sleep(timeout)
|
| 47 |
+
zc.close()
|
| 48 |
+
|
| 49 |
+
lan_peers = []
|
| 50 |
+
wan_peers = []
|
| 51 |
+
internet_peers = []
|
| 52 |
+
|
| 53 |
+
for peer in listener.peers:
|
| 54 |
+
ip = peer.split(':')[0]
|
| 55 |
+
if verify_peer_project(ip):
|
| 56 |
+
if is_local_network(ip):
|
| 57 |
+
lan_peers.append(peer)
|
| 58 |
+
else:
|
| 59 |
+
wan_peers.append(peer)
|
| 60 |
+
|
| 61 |
+
all_discovered = list(peer_discovery.PEERS)
|
| 62 |
+
for peer_url in all_discovered:
|
| 63 |
+
peer_ip = peer_url.split("://")[1].split(":")[0]
|
| 64 |
+
if verify_peer_project(peer_ip):
|
| 65 |
+
if is_local_network(peer_ip):
|
| 66 |
+
if peer_url not in lan_peers:
|
| 67 |
+
lan_peers.append(peer_url)
|
| 68 |
+
else:
|
| 69 |
+
if peer_url not in wan_peers:
|
| 70 |
+
internet_peers.append(peer_url)
|
| 71 |
+
|
| 72 |
+
all_peers = lan_peers + wan_peers + internet_peers
|
| 73 |
+
logging.info(f"اكتُشف {len(all_peers)} جهاز DTS متوافق - LAN: {len(lan_peers)}, WAN: {len(wan_peers)}, Internet: {len(internet_peers)}")
|
| 74 |
+
|
| 75 |
+
return all_peers
|
| 76 |
+
|
| 77 |
+
def verify_peer_project(ip, port=7520):
|
| 78 |
+
"""فحص إذا كان الجهاز يحتوي على نفس المشروع"""
|
| 79 |
+
try:
|
| 80 |
+
from project_identifier import verify_project_compatibility
|
| 81 |
+
|
| 82 |
+
project_url = f"http://{ip}:{port}/project_info"
|
| 83 |
+
response = requests.get(project_url, timeout=2)
|
| 84 |
+
|
| 85 |
+
if response.status_code == 200:
|
| 86 |
+
remote_info = response.json()
|
| 87 |
+
return verify_project_compatibility(remote_info)
|
| 88 |
+
|
| 89 |
+
except:
|
| 90 |
+
pass
|
| 91 |
+
return False
|
| 92 |
+
|
| 93 |
+
def is_local_network(ip):
|
| 94 |
+
"""فحص إذا كان IP في الشبكة المحلية"""
|
| 95 |
+
try:
|
| 96 |
+
import ipaddress
|
| 97 |
+
addr = ipaddress.ip_address(ip)
|
| 98 |
+
return (
|
| 99 |
+
addr.is_private or
|
| 100 |
+
str(addr).startswith('192.168.') or
|
| 101 |
+
str(addr).startswith('10.') or
|
| 102 |
+
str(addr).startswith('172.')
|
| 103 |
+
)
|
| 104 |
+
except:
|
| 105 |
+
return False
|
| 106 |
+
|
| 107 |
+
def try_offload(peer, payload, max_retries=3):
|
| 108 |
+
"""محاولة إرسال المهمة إلى جهاز آخر"""
|
| 109 |
+
url = f"http://{peer}/run"
|
| 110 |
+
for attempt in range(max_retries):
|
| 111 |
+
try:
|
| 112 |
+
response = requests.post(url, json=payload, timeout=10)
|
| 113 |
+
response.raise_for_status()
|
| 114 |
+
return response.json()
|
| 115 |
+
except Exception as e:
|
| 116 |
+
logging.warning(f"فشل المحاولة {attempt + 1} لـ {peer}: {str(e)}")
|
| 117 |
+
time.sleep(0.5 * (attempt + 1))
|
| 118 |
+
raise ConnectionError(f"فشل جميع المحاولات لـ {peer}")
|
| 119 |
+
|
| 120 |
+
def estimate_complexity(func, args, kwargs):
|
| 121 |
+
"""تقدير تعقيد المهمة"""
|
| 122 |
+
if func.__name__ == "matrix_multiply":
|
| 123 |
+
return args[0] ** 2
|
| 124 |
+
elif func.__name__ == "prime_calculation":
|
| 125 |
+
return args[0] / 100
|
| 126 |
+
elif func.__name__ == "data_processing":
|
| 127 |
+
return args[0] / 10
|
| 128 |
+
elif func.__name__ == "image_processing_emulation":
|
| 129 |
+
return args[0] * 5
|
| 130 |
+
return 1 # قيمة افتراضية
|
| 131 |
+
|
| 132 |
+
def offload(func):
|
| 133 |
+
"""ديكوراتور لتوزيع المهام"""
|
| 134 |
+
@wraps(func)
|
| 135 |
+
def wrapper(*args, **kwargs):
|
| 136 |
+
cpu = psutil.cpu_percent(interval=0.5) / 100.0
|
| 137 |
+
mem = psutil.virtual_memory().available / (1024**2)
|
| 138 |
+
complexity = estimate_complexity(func, args, kwargs)
|
| 139 |
+
|
| 140 |
+
logging.info(f"حمل النظام - CPU: {cpu:.2f}, الذاكرة: {mem:.1f}MB, تعقيد المهمة: {complexity}")
|
| 141 |
+
|
| 142 |
+
if complexity > 50 or cpu > MAX_CPU:
|
| 143 |
+
try:
|
| 144 |
+
peers = discover_peers()
|
| 145 |
+
if peers:
|
| 146 |
+
payload = {
|
| 147 |
+
"func": func.__name__,
|
| 148 |
+
"args": args,
|
| 149 |
+
"kwargs": kwargs,
|
| 150 |
+
"complexity": complexity
|
| 151 |
+
}
|
| 152 |
+
selected_peer = random.choice(peers)
|
| 153 |
+
logging.info(f"إرسال المهمة إلى {selected_peer}")
|
| 154 |
+
return try_offload(selected_peer, payload)
|
| 155 |
+
except Exception as e:
|
| 156 |
+
logging.error(f"خطأ في التوزيع: {str(e)}")
|
| 157 |
+
|
| 158 |
+
logging.info("تنفيذ المهمة محلياً")
|
| 159 |
+
return func(*args, **kwargs)
|
| 160 |
+
return wrapper
|
| 161 |
+
|
| 162 |
+
# المهام القابلة للتوزيع:
|
| 163 |
+
|
| 164 |
+
@offload
|
| 165 |
+
def matrix_multiply(size):
|
| 166 |
+
"""ضرب مصفوفتين عشوائيتين بالحجم"""
|
| 167 |
+
import numpy as np
|
| 168 |
+
A = np.random.rand(size, size)
|
| 169 |
+
B = np.random.rand(size, size)
|
| 170 |
+
return np.dot(A, B).tolist()
|
| 171 |
+
|
| 172 |
+
@offload
|
| 173 |
+
def prime_calculation(n):
|
| 174 |
+
"""حساب الأعداد الأولية"""
|
| 175 |
+
primes = []
|
| 176 |
+
for num in range(2, n + 1):
|
| 177 |
+
is_prime = True
|
| 178 |
+
for i in range(2, int(math.sqrt(num)) + 1):
|
| 179 |
+
if num % i == 0:
|
| 180 |
+
is_prime = False
|
| 181 |
+
break
|
| 182 |
+
if is_prime:
|
| 183 |
+
primes.append(num)
|
| 184 |
+
return {"primes_count": len(primes), "primes": primes}
|
| 185 |
+
|
| 186 |
+
@offload
|
| 187 |
+
def data_processing(data_size):
|
| 188 |
+
"""معالجة بيانات كبيرة"""
|
| 189 |
+
processed_data = []
|
| 190 |
+
for i in range(data_size):
|
| 191 |
+
result = sum(math.sin(x) * math.cos(x) for x in range(i, i + 100))
|
| 192 |
+
processed_data.append(result)
|
| 193 |
+
return {"processed_items": len(processed_data)}
|
| 194 |
+
|
| 195 |
+
@offload
|
| 196 |
+
def image_processing_emulation(iterations):
|
| 197 |
+
"""محاكاة معالجة الصور"""
|
| 198 |
+
results = []
|
| 199 |
+
for i in range(iterations):
|
| 200 |
+
fake_processing = sum(math.sqrt(x) for x in range(i * 100, (i + 1) * 100))
|
| 201 |
+
results.append(fake_processing)
|
| 202 |
+
time.sleep(0.01)
|
| 203 |
+
return {"iterations": iterations, "results": results}
|
package-lock.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
package.json
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "rest-express",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"type": "module",
|
| 5 |
+
"license": "MIT",
|
| 6 |
+
"scripts": {
|
| 7 |
+
"dev": "NODE_ENV=development tsx server/index.ts",
|
| 8 |
+
"build": "vite build && esbuild server/index.ts --platform=node --packages=external --bundle --format=esm --outdir=dist",
|
| 9 |
+
"start": "NODE_ENV=production node dist/index.js",
|
| 10 |
+
"check": "tsc",
|
| 11 |
+
"db:push": "drizzle-kit push"
|
| 12 |
+
},
|
| 13 |
+
"dependencies": {
|
| 14 |
+
"@hookform/resolvers": "^3.10.0",
|
| 15 |
+
"@jridgewell/trace-mapping": "^0.3.25",
|
| 16 |
+
"@neondatabase/serverless": "^0.10.4",
|
| 17 |
+
"@radix-ui/react-accordion": "^1.2.4",
|
| 18 |
+
"@radix-ui/react-alert-dialog": "^1.1.7",
|
| 19 |
+
"@radix-ui/react-aspect-ratio": "^1.1.3",
|
| 20 |
+
"@radix-ui/react-avatar": "^1.1.4",
|
| 21 |
+
"@radix-ui/react-checkbox": "^1.1.5",
|
| 22 |
+
"@radix-ui/react-collapsible": "^1.1.4",
|
| 23 |
+
"@radix-ui/react-context-menu": "^2.2.7",
|
| 24 |
+
"@radix-ui/react-dialog": "^1.1.7",
|
| 25 |
+
"@radix-ui/react-dropdown-menu": "^2.1.7",
|
| 26 |
+
"@radix-ui/react-hover-card": "^1.1.7",
|
| 27 |
+
"@radix-ui/react-label": "^2.1.3",
|
| 28 |
+
"@radix-ui/react-menubar": "^1.1.7",
|
| 29 |
+
"@radix-ui/react-navigation-menu": "^1.2.6",
|
| 30 |
+
"@radix-ui/react-popover": "^1.1.7",
|
| 31 |
+
"@radix-ui/react-progress": "^1.1.3",
|
| 32 |
+
"@radix-ui/react-radio-group": "^1.2.4",
|
| 33 |
+
"@radix-ui/react-scroll-area": "^1.2.4",
|
| 34 |
+
"@radix-ui/react-select": "^2.1.7",
|
| 35 |
+
"@radix-ui/react-separator": "^1.1.3",
|
| 36 |
+
"@radix-ui/react-slider": "^1.2.4",
|
| 37 |
+
"@radix-ui/react-slot": "^1.2.0",
|
| 38 |
+
"@radix-ui/react-switch": "^1.1.4",
|
| 39 |
+
"@radix-ui/react-tabs": "^1.1.4",
|
| 40 |
+
"@radix-ui/react-toast": "^1.2.7",
|
| 41 |
+
"@radix-ui/react-toggle": "^1.1.3",
|
| 42 |
+
"@radix-ui/react-toggle-group": "^1.1.3",
|
| 43 |
+
"@radix-ui/react-tooltip": "^1.2.0",
|
| 44 |
+
"@tanstack/react-query": "^5.60.5",
|
| 45 |
+
"bonjour-service": "^1.3.0",
|
| 46 |
+
"class-variance-authority": "^0.7.1",
|
| 47 |
+
"clsx": "^2.1.1",
|
| 48 |
+
"cmdk": "^1.1.1",
|
| 49 |
+
"connect-pg-simple": "^10.0.0",
|
| 50 |
+
"date-fns": "^3.6.0",
|
| 51 |
+
"drizzle-orm": "^0.39.1",
|
| 52 |
+
"drizzle-zod": "^0.7.0",
|
| 53 |
+
"embla-carousel-react": "^8.6.0",
|
| 54 |
+
"express": "^4.21.2",
|
| 55 |
+
"express-session": "^1.18.1",
|
| 56 |
+
"framer-motion": "^11.13.1",
|
| 57 |
+
"input-otp": "^1.4.2",
|
| 58 |
+
"lucide-react": "^0.453.0",
|
| 59 |
+
"memorystore": "^1.6.7",
|
| 60 |
+
"nanoid": "^5.1.5",
|
| 61 |
+
"next-themes": "^0.4.6",
|
| 62 |
+
"passport": "^0.7.0",
|
| 63 |
+
"passport-local": "^1.0.0",
|
| 64 |
+
"react": "^18.3.1",
|
| 65 |
+
"react-day-picker": "^8.10.1",
|
| 66 |
+
"react-dom": "^18.3.1",
|
| 67 |
+
"react-hook-form": "^7.55.0",
|
| 68 |
+
"react-icons": "^5.4.0",
|
| 69 |
+
"react-resizable-panels": "^2.1.7",
|
| 70 |
+
"recharts": "^2.15.2",
|
| 71 |
+
"tailwind-merge": "^2.6.0",
|
| 72 |
+
"tailwindcss-animate": "^1.0.7",
|
| 73 |
+
"tw-animate-css": "^1.2.5",
|
| 74 |
+
"vaul": "^1.1.2",
|
| 75 |
+
"wouter": "^3.3.5",
|
| 76 |
+
"ws": "^8.18.0",
|
| 77 |
+
"zod": "^3.24.2",
|
| 78 |
+
"zod-validation-error": "^3.4.0"
|
| 79 |
+
},
|
| 80 |
+
"devDependencies": {
|
| 81 |
+
"@replit/vite-plugin-cartographer": "^0.2.7",
|
| 82 |
+
"@replit/vite-plugin-runtime-error-modal": "^0.0.3",
|
| 83 |
+
"@tailwindcss/typography": "^0.5.15",
|
| 84 |
+
"@tailwindcss/vite": "^4.1.3",
|
| 85 |
+
"@types/connect-pg-simple": "^7.0.3",
|
| 86 |
+
"@types/express": "4.17.21",
|
| 87 |
+
"@types/express-session": "^1.18.0",
|
| 88 |
+
"@types/node": "20.16.11",
|
| 89 |
+
"@types/passport": "^1.0.16",
|
| 90 |
+
"@types/passport-local": "^1.0.38",
|
| 91 |
+
"@types/react": "^18.3.11",
|
| 92 |
+
"@types/react-dom": "^18.3.1",
|
| 93 |
+
"@types/ws": "^8.5.13",
|
| 94 |
+
"@vitejs/plugin-react": "^4.3.2",
|
| 95 |
+
"autoprefixer": "^10.4.20",
|
| 96 |
+
"drizzle-kit": "^0.30.4",
|
| 97 |
+
"electron": "^37.1.0",
|
| 98 |
+
"electron-builder": "^26.0.12",
|
| 99 |
+
"esbuild": "^0.25.0",
|
| 100 |
+
"postcss": "^8.4.47",
|
| 101 |
+
"tailwindcss": "^3.4.17",
|
| 102 |
+
"tsx": "^4.19.1",
|
| 103 |
+
"typescript": "5.6.3",
|
| 104 |
+
"vite": "^5.4.19"
|
| 105 |
+
},
|
| 106 |
+
"optionalDependencies": {
|
| 107 |
+
"bufferutil": "^4.0.8"
|
| 108 |
+
}
|
| 109 |
+
}
|
peer_discovery.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
import os
|
| 3 |
+
import socket
|
| 4 |
+
import threading
|
| 5 |
+
import time
|
| 6 |
+
import logging
|
| 7 |
+
|
| 8 |
+
import requests
|
| 9 |
+
from zeroconf import Zeroconf, ServiceInfo, ServiceBrowser
|
| 10 |
+
|
| 11 |
+
# 👇 إعداد الـ peer discovery عبر LAN وInternet
|
| 12 |
+
SERVICE = "_tasknode._tcp.local."
|
| 13 |
+
PORT = int(os.getenv("CPU_PORT", "7520"))
|
| 14 |
+
PEERS = set() # مجموعة URLs للأقران (/run)
|
| 15 |
+
|
| 16 |
+
# 🌐 خادم وسيط مركزي (Central Registry)
|
| 17 |
+
CENTRAL_REGISTRY_URL = "https://cv4790811.regru.cloud"
|
| 18 |
+
|
| 19 |
+
# 🟢 دالة لحساب IP العام أو المحلي
|
| 20 |
+
# تستخدم HTTP API للحصول على IP عام ثم fallback إلى LAN
|
| 21 |
+
|
| 22 |
+
def get_local_ip():
|
| 23 |
+
# حاول الحصول على IP عام
|
| 24 |
+
try:
|
| 25 |
+
r = requests.get("https://api.ipify.org?format=json", timeout=3)
|
| 26 |
+
r.raise_for_status()
|
| 27 |
+
data = r.json()
|
| 28 |
+
return data.get("ip", "127.0.0.1")
|
| 29 |
+
except Exception:
|
| 30 |
+
# fallback إلى LAN IP
|
| 31 |
+
try:
|
| 32 |
+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
| 33 |
+
s.connect(("8.8.8.8", 80))
|
| 34 |
+
return s.getsockname()[0]
|
| 35 |
+
except Exception:
|
| 36 |
+
return "127.0.0.1"
|
| 37 |
+
finally:
|
| 38 |
+
try:
|
| 39 |
+
s.close()
|
| 40 |
+
except Exception:
|
| 41 |
+
pass
|
| 42 |
+
|
| 43 |
+
# ❶ تسجيل الخدمة في شبكة LAN عبر Zeroconf
|
| 44 |
+
# للحفاظ على قدرة اكتشاف peers في نفس الشبكة المحلية
|
| 45 |
+
def register_service_lan():
|
| 46 |
+
zc = Zeroconf()
|
| 47 |
+
local_ip = get_local_ip()
|
| 48 |
+
info = ServiceInfo(
|
| 49 |
+
SERVICE,
|
| 50 |
+
f"{socket.gethostname()}.{SERVICE}",
|
| 51 |
+
addresses=[socket.inet_aton(local_ip)],
|
| 52 |
+
port=PORT,
|
| 53 |
+
properties={b'load': b'0'}
|
| 54 |
+
)
|
| 55 |
+
try:
|
| 56 |
+
zc.register_service(info)
|
| 57 |
+
print(f"✅ LAN service registered: {local_ip}:{PORT}")
|
| 58 |
+
except Exception as e:
|
| 59 |
+
print(f"❌ LAN registration failed: {e}")
|
| 60 |
+
|
| 61 |
+
# ❷ مستمع لاكتشاف أجهزة LAN عبر Zeroconf
|
| 62 |
+
class Listener:
|
| 63 |
+
def add_service(self, zc, t, name):
|
| 64 |
+
info = zc.get_service_info(t, name)
|
| 65 |
+
if info and info.addresses:
|
| 66 |
+
ip = socket.inet_ntoa(info.addresses[0])
|
| 67 |
+
peer_url = f"http://{ip}:{info.port}/run"
|
| 68 |
+
if peer_url not in PEERS:
|
| 69 |
+
PEERS.add(peer_url)
|
| 70 |
+
print(f"🔗 LAN peer discovered: {peer_url}")
|
| 71 |
+
def update_service(self, zc, t, name):
|
| 72 |
+
self.add_service(zc, t, name)
|
| 73 |
+
def remove_service(self, zc, t, name):
|
| 74 |
+
print(f"❌ LAN peer removed: {name}")
|
| 75 |
+
|
| 76 |
+
# حلقة اكتشاف LAN مستمرة
|
| 77 |
+
def discover_lan_loop():
|
| 78 |
+
zc = Zeroconf()
|
| 79 |
+
ServiceBrowser(zc, SERVICE, Listener())
|
| 80 |
+
print(f"🔍 Started LAN discovery for {SERVICE}")
|
| 81 |
+
while True:
|
| 82 |
+
time.sleep(5)
|
| 83 |
+
|
| 84 |
+
# ❸ تسجيل العقدة في الخادم المركزي
|
| 85 |
+
# ترسل بياناتها إلى /register وتضيف باقي الأقران
|
| 86 |
+
def register_with_central():
|
| 87 |
+
node_id = os.getenv("NODE_ID", socket.gethostname())
|
| 88 |
+
info = {"node_id": node_id, "ip": get_local_ip(), "port": PORT}
|
| 89 |
+
try:
|
| 90 |
+
resp = requests.post(f"{CENTRAL_REGISTRY_URL}/register", json=info, timeout=5)
|
| 91 |
+
resp.raise_for_status()
|
| 92 |
+
peers_list = resp.json()
|
| 93 |
+
for p in peers_list:
|
| 94 |
+
peer_url = f"http://{p['ip']}:{p['port']}/run"
|
| 95 |
+
if peer_url not in PEERS:
|
| 96 |
+
PEERS.add(peer_url)
|
| 97 |
+
print(f"🌐 Registered and discovered central peer: {peer_url}")
|
| 98 |
+
except Exception as e:
|
| 99 |
+
print(f"❌ Central registration failed: {e}")
|
| 100 |
+
|
| 101 |
+
# ❹ مزامنة الأقران من الخادم المركزي بشكل دوري
|
| 102 |
+
# تُحدّث مجموعة PEERS كل 5 دقائق عن طريق /peers endpoint
|
| 103 |
+
def fetch_central_loop():
|
| 104 |
+
print("🔄 Central registry sync loop started")
|
| 105 |
+
while True:
|
| 106 |
+
try:
|
| 107 |
+
resp = requests.get(f"{CENTRAL_REGISTRY_URL}/peers", timeout=5)
|
| 108 |
+
resp.raise_for_status()
|
| 109 |
+
peers_list = resp.json()
|
| 110 |
+
for p in peers_list:
|
| 111 |
+
peer_url = f"http://{p['ip']}:{p['port']}/run"
|
| 112 |
+
if peer_url not in PEERS:
|
| 113 |
+
PEERS.add(peer_url)
|
| 114 |
+
print(f"🌐 Central peer discovered: {peer_url}")
|
| 115 |
+
except Exception as e:
|
| 116 |
+
print(f"⚠️ Fetch central peers failed: {e}")
|
| 117 |
+
time.sleep(300)
|
| 118 |
+
|
| 119 |
+
# 🚀 دالة الإدخال الرئيسيّة
|
| 120 |
+
def main():
|
| 121 |
+
logging.basicConfig(level=logging.INFO)
|
| 122 |
+
print("🚀 Peer Discovery System starting...")
|
| 123 |
+
|
| 124 |
+
# Zeroconf LAN registration & discovery
|
| 125 |
+
threading.Thread(target=register_service_lan, daemon=True).start()
|
| 126 |
+
threading.Thread(target=discover_lan_loop, daemon=True).start()
|
| 127 |
+
|
| 128 |
+
# التسجيل في الخادم الوسيط ومزامنة الأقران
|
| 129 |
+
register_with_central()
|
| 130 |
+
threading.Thread(target=fetch_central_loop, daemon=True).start()
|
| 131 |
+
|
| 132 |
+
# إبقاء السكربت قيد التشغيل
|
| 133 |
+
try:
|
| 134 |
+
while True:
|
| 135 |
+
time.sleep(60)
|
| 136 |
+
except KeyboardInterrupt:
|
| 137 |
+
print("🛑 Exiting...")
|
| 138 |
+
|
| 139 |
+
if __name__ == "__main__":
|
| 140 |
+
main()
|
peer_registry.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import socket
|
| 2 |
+
import time
|
| 3 |
+
from zeroconf import Zeroconf, ServiceBrowser, ServiceInfo
|
| 4 |
+
|
| 5 |
+
class Listener:
|
| 6 |
+
def __init__(self):
|
| 7 |
+
self.peers = []
|
| 8 |
+
|
| 9 |
+
def add_service(self, zc, type_, name):
|
| 10 |
+
info = zc.get_service_info(type_, name)
|
| 11 |
+
if info:
|
| 12 |
+
ip = socket.inet_ntoa(info.addresses[0])
|
| 13 |
+
peer_data = {
|
| 14 |
+
'ip': ip,
|
| 15 |
+
'port': info.port,
|
| 16 |
+
'load': float(info.properties.get(b'load', 0)),
|
| 17 |
+
'node_id': info.properties.get(b'node_id', b'unknown').decode(),
|
| 18 |
+
'last_seen': time.time()
|
| 19 |
+
}
|
| 20 |
+
if peer_data not in self.peers:
|
| 21 |
+
self.peers.append(peer_data)
|
| 22 |
+
|
| 23 |
+
def update_service(self, zc, type_, name):
|
| 24 |
+
"""مطلوب بواسطة Zeroconf"""
|
| 25 |
+
self.add_service(zc, type_, name)
|
| 26 |
+
|
| 27 |
+
def remove_service(self, zc, type_, name):
|
| 28 |
+
"""اختياري"""
|
| 29 |
+
pass
|
| 30 |
+
|
| 31 |
+
def register_service(ip: str, port: int, load: float = 0.0):
|
| 32 |
+
zc = Zeroconf()
|
| 33 |
+
service_name = f"{socket.gethostname()}-{int(time.time())}._tasknode._tcp.local."
|
| 34 |
+
service_info = ServiceInfo(
|
| 35 |
+
"_tasknode._tcp.local.",
|
| 36 |
+
service_name,
|
| 37 |
+
addresses=[socket.inet_aton(ip)],
|
| 38 |
+
port=port,
|
| 39 |
+
properties={
|
| 40 |
+
b'load': str(load).encode(),
|
| 41 |
+
b'node_id': socket.gethostname().encode()
|
| 42 |
+
}
|
| 43 |
+
)
|
| 44 |
+
zc.register_service(service_info)
|
| 45 |
+
print(f"✅ Service registered: {service_name} @ {ip}:{port}")
|
| 46 |
+
return zc # أبقِ المرجع حياً
|
| 47 |
+
|
| 48 |
+
def discover_peers(timeout=2):
|
| 49 |
+
zc = Zeroconf()
|
| 50 |
+
listener = Listener()
|
| 51 |
+
ServiceBrowser(zc, "_tasknode._tcp.local.", listener)
|
| 52 |
+
time.sleep(timeout)
|
| 53 |
+
zc.close()
|
| 54 |
+
return listener.peers
|
| 55 |
+
|
| 56 |
+
if __name__ == "__main__":
|
| 57 |
+
local_ip = socket.gethostbyname(socket.gethostname())
|
| 58 |
+
port = 7520
|
| 59 |
+
|
| 60 |
+
zc = register_service(local_ip, port, load=0.1)
|
| 61 |
+
|
| 62 |
+
peers = discover_peers()
|
| 63 |
+
print("✅ Available peers:", peers)
|
| 64 |
+
|
| 65 |
+
input("🔵 Press Enter to exit...\n")
|
| 66 |
+
zc.close()
|
| 67 |
+
|
peer_server.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# peer_server.py
|
| 2 |
+
|
| 3 |
+
from flask import Flask, request, jsonify # استيراد request و jsonify مع Flask
|
| 4 |
+
import psutil
|
| 5 |
+
import smart_tasks
|
| 6 |
+
import time
|
| 7 |
+
import socket
|
| 8 |
+
import peer_discovery # إذا كان يستخدم لاحقًا
|
| 9 |
+
|
| 10 |
+
app = Flask(__name__) # إنشاء التطبيق
|
| 11 |
+
|
| 12 |
+
@app.route("/cpu")
|
| 13 |
+
def cpu():
|
| 14 |
+
# يعيد نسبة استخدام المعالج
|
| 15 |
+
return jsonify(usage=psutil.cpu_percent(interval=0.3))
|
| 16 |
+
|
| 17 |
+
@app.route("/run", methods=["POST"])
|
| 18 |
+
def run():
|
| 19 |
+
data = request.get_json(force=True)
|
| 20 |
+
fn_name = data.get("func")
|
| 21 |
+
fn = getattr(smart_tasks, fn_name, None)
|
| 22 |
+
if not fn:
|
| 23 |
+
return jsonify(error="function-not-found"), 404
|
| 24 |
+
try:
|
| 25 |
+
start = time.time()
|
| 26 |
+
result = fn(*data.get("args", []), **data.get("kwargs", {}))
|
| 27 |
+
return jsonify(
|
| 28 |
+
result=result,
|
| 29 |
+
host=socket.gethostname(),
|
| 30 |
+
took=round(time.time() - start, 3)
|
| 31 |
+
)
|
| 32 |
+
except Exception as e:
|
| 33 |
+
return jsonify(error=str(e)), 500
|
| 34 |
+
|
| 35 |
+
if __name__ == "__main__": # التصحيح هنا
|
| 36 |
+
app.run(host="0.0.0.0", port=7520)
|
| 37 |
+
|
peer_statistics.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from collections import Counter
|
| 2 |
+
import ipaddress
|
| 3 |
+
|
| 4 |
+
def print_peer_statistics(discovered_peers):
|
| 5 |
+
ips = [peer['ip'] for peer in discovered_peers]
|
| 6 |
+
ip_counts = Counter(ips)
|
| 7 |
+
|
| 8 |
+
def classify_ip(ip):
|
| 9 |
+
try:
|
| 10 |
+
ip_obj = ipaddress.ip_address(ip)
|
| 11 |
+
if ip_obj.is_private:
|
| 12 |
+
return 'داخلي'
|
| 13 |
+
else:
|
| 14 |
+
return 'خارجي'
|
| 15 |
+
except ValueError:
|
| 16 |
+
return 'محلي'
|
| 17 |
+
|
| 18 |
+
print("\n📊 إحصائية عدد الأجهزة حسب النوع:\n")
|
| 19 |
+
for ip, count in ip_counts.items():
|
| 20 |
+
category = classify_ip(ip)
|
| 21 |
+
print(f"• {ip} ({category}): {count} جهاز")
|
postcss.config.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export default {
|
| 2 |
+
plugins: {
|
| 3 |
+
tailwindcss: {},
|
| 4 |
+
autoprefixer: {},
|
| 5 |
+
},
|
| 6 |
+
}
|
processor_manager.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# processor_manager.py
|
| 2 |
+
|
| 3 |
+
import psutil
|
| 4 |
+
from collections import deque
|
| 5 |
+
import logging
|
| 6 |
+
|
| 7 |
+
logging.basicConfig(level=logging.INFO)
|
| 8 |
+
|
| 9 |
+
class ResourceMonitor:
|
| 10 |
+
def __init__(self):
|
| 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)
|
| 18 |
+
mem = psutil.virtual_memory().available / (1024**2) # متاح بالـ MB
|
| 19 |
+
|
| 20 |
+
self.cpu_history.append(cpu)
|
| 21 |
+
self.mem_history.append(mem)
|
| 22 |
+
|
| 23 |
+
avg_cpu = sum(self.cpu_history) / len(self.cpu_history)
|
| 24 |
+
avg_mem = sum(self.mem_history) / len(self.mem_history)
|
| 25 |
+
|
| 26 |
+
logging.info(f"Instant CPU: {cpu:.2%}, Instant MEM: {mem:.1f}MB")
|
| 27 |
+
logging.info(f"Avg CPU: {avg_cpu:.2%}, Avg MEM: {avg_mem:.1f}MB")
|
| 28 |
+
|
| 29 |
+
recommendation = "offload" if (avg_cpu > 0.5 or avg_mem < 2048) else "local"
|
| 30 |
+
can_receive = avg_cpu <= self.receive_cpu_threshold
|
| 31 |
+
|
| 32 |
+
return {
|
| 33 |
+
"instant": {"cpu": cpu, "mem": mem},
|
| 34 |
+
"average": {"cpu": avg_cpu, "mem": avg_mem},
|
| 35 |
+
"recommendation": recommendation,
|
| 36 |
+
"can_receive": can_receive
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
def trigger_offload():
|
| 40 |
+
"""عملية توزيع المهام التجريبية"""
|
| 41 |
+
print("⚠️ تم استدعاء توزيع المهام (اختباري)")
|
| 42 |
+
|
| 43 |
+
def should_offload(task_complexity=0):
|
| 44 |
+
monitor = ResourceMonitor()
|
| 45 |
+
status = monitor.current_load()
|
| 46 |
+
|
| 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 |
+
|
| 54 |
+
return False
|
| 55 |
+
|
| 56 |
+
def can_receive_task():
|
| 57 |
+
"""
|
| 58 |
+
يعيد True إذا كان بالإمكان استقبال مهمة جديدة،
|
| 59 |
+
أي عندما يكون متوسط استهلاك الـ CPU ≤ 40%.
|
| 60 |
+
"""
|
| 61 |
+
return ResourceMonitor().current_load()["can_receive"]
|
| 62 |
+
|
| 63 |
+
if __name__ == "__main__":
|
| 64 |
+
status = ResourceMonitor().current_load()
|
| 65 |
+
if not status["can_receive"]:
|
| 66 |
+
print(f"🚫 لا يمكن استقبال مهام جديدة (Avg CPU: {status['average']['cpu']:.0%})")
|
| 67 |
+
else:
|
| 68 |
+
print(f"✅ يمكن استقبال مهام جديدة (Avg CPU: {status['average']['cpu']:.0%})")
|
| 69 |
+
|
| 70 |
+
if should_offload(80):
|
| 71 |
+
print("💡 ينصح بتوزيع المهمة")
|
| 72 |
+
else:
|
| 73 |
+
print("✅ يمكن تنفيذ المهمة محلياً")
|
project_identifier.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
"""
|
| 3 |
+
project_identifier.py - معرف المشروع للتحقق من الهوية
|
| 4 |
+
"""
|
| 5 |
+
import json
|
| 6 |
+
from flask import jsonify
|
| 7 |
+
|
| 8 |
+
PROJECT_INFO = {
|
| 9 |
+
"project_name": "distributed-task-system",
|
| 10 |
+
"version": "1.0",
|
| 11 |
+
"description": "نظام توزيع المهام الذكي",
|
| 12 |
+
"author": "DTS Team",
|
| 13 |
+
"features": [
|
| 14 |
+
"matrix_multiply",
|
| 15 |
+
"prime_calculation",
|
| 16 |
+
"data_processing",
|
| 17 |
+
"video_processing",
|
| 18 |
+
"live_streaming",
|
| 19 |
+
"enhanced_ai"
|
| 20 |
+
],
|
| 21 |
+
"signature": "DTS_2024_SMART_DISTRIBUTION"
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
def get_project_info():
|
| 25 |
+
"""إرجاع معلومات المشروع"""
|
| 26 |
+
return PROJECT_INFO
|
| 27 |
+
|
| 28 |
+
def verify_project_compatibility(remote_info):
|
| 29 |
+
"""التحقق من توافق المشروع مع جهاز آخر"""
|
| 30 |
+
if not isinstance(remote_info, dict):
|
| 31 |
+
return False
|
| 32 |
+
|
| 33 |
+
return (
|
| 34 |
+
remote_info.get("project_name") == PROJECT_INFO["project_name"] and
|
| 35 |
+
remote_info.get("version") == PROJECT_INFO["version"] and
|
| 36 |
+
remote_info.get("signature") == PROJECT_INFO["signature"]
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
def create_project_endpoint():
|
| 40 |
+
"""إنشاء endpoint لمعلومات المشروع"""
|
| 41 |
+
return jsonify(PROJECT_INFO)
|
quick_connection_test.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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:7520/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⚠️ هناك مشاكل تحتاج إصلاح")
|
quick_test.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
#!/usr/bin/env python3
|
| 3 |
+
# quick_test.py - اختبار سريع لنظام التوزيع
|
| 4 |
+
|
| 5 |
+
import requests
|
| 6 |
+
import time
|
| 7 |
+
from offload_lib import discover_peers
|
| 8 |
+
|
| 9 |
+
def quick_connectivity_test():
|
| 10 |
+
"""اختبار سريع للاتصال والتوزيع"""
|
| 11 |
+
print("🚀 اختبار سريع لنظام التوزيع")
|
| 12 |
+
print("-" * 40)
|
| 13 |
+
|
| 14 |
+
# 1. اكتشاف الأجهزة
|
| 15 |
+
print("🔍 البحث عن الأجهزة...")
|
| 16 |
+
peers = discover_peers(timeout=2)
|
| 17 |
+
|
| 18 |
+
if not peers:
|
| 19 |
+
print("❌ لم يتم العثور على أجهزة أخرى")
|
| 20 |
+
return False
|
| 21 |
+
|
| 22 |
+
print(f"✅ تم العثور على {len(peers)} جهاز:")
|
| 23 |
+
for peer in peers:
|
| 24 |
+
print(f" 📱 {peer}")
|
| 25 |
+
|
| 26 |
+
# 2. اختبار الاتصال السريع
|
| 27 |
+
working_peers = []
|
| 28 |
+
for peer in peers:
|
| 29 |
+
try:
|
| 30 |
+
response = requests.get(f"{peer}/health", timeout=3)
|
| 31 |
+
if response.status_code == 200:
|
| 32 |
+
working_peers.append(peer)
|
| 33 |
+
print(f"✅ {peer} - متصل ويعمل")
|
| 34 |
+
else:
|
| 35 |
+
print(f"⚠️ {peer} - يستجيب لكن بخطأ")
|
| 36 |
+
except:
|
| 37 |
+
print(f"❌ {peer} - غير متصل")
|
| 38 |
+
|
| 39 |
+
if not working_peers:
|
| 40 |
+
print("❌ لا توجد أجهزة تعمل بشكل صحيح")
|
| 41 |
+
return False
|
| 42 |
+
|
| 43 |
+
# 3. اختبار إرسال مهمة بسيطة
|
| 44 |
+
print(f"\n📡 اختبار إرسال مهمة إلى {working_peers[0]}...")
|
| 45 |
+
|
| 46 |
+
task = {
|
| 47 |
+
"func": "matrix_multiply",
|
| 48 |
+
"args": [5],
|
| 49 |
+
"kwargs": {}
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
try:
|
| 53 |
+
start_time = time.time()
|
| 54 |
+
response = requests.post(f"{working_peers[0]}/run", json=task, timeout=10)
|
| 55 |
+
duration = time.time() - start_time
|
| 56 |
+
|
| 57 |
+
if response.status_code == 200:
|
| 58 |
+
result = response.json()
|
| 59 |
+
print(f"✅ تمت المعالجة بنجاح في {duration:.2f} ثانية")
|
| 60 |
+
print(f"📊 النتيجة: تم ضرب مصفوفة 5x5")
|
| 61 |
+
return True
|
| 62 |
+
else:
|
| 63 |
+
print(f"❌ فشل في المعالجة - كود الخطأ: {response.status_code}")
|
| 64 |
+
return False
|
| 65 |
+
|
| 66 |
+
except Exception as e:
|
| 67 |
+
print(f"❌ خطأ في الإرسال: {str(e)}")
|
| 68 |
+
return False
|
| 69 |
+
|
| 70 |
+
if __name__ == "__main__":
|
| 71 |
+
success = quick_connectivity_test()
|
| 72 |
+
|
| 73 |
+
if success:
|
| 74 |
+
print("\n🎉 النظام يعمل بشكل صحيح!")
|
| 75 |
+
print("💡 يمكنك الآن تشغيل الاختبار الشامل: python test_distributed_system.py")
|
| 76 |
+
else:
|
| 77 |
+
print("\n⚠️ هناك مشاكل في النظام، تحقق من:")
|
| 78 |
+
print(" 1. تشغيل الخادم على الأجهزة الأخرى")
|
| 79 |
+
print(" 2. الاتصال بالشبكة")
|
| 80 |
+
print(" 3. إعدادات الجدار الناري")
|
remote_executor.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# remote_executor.py (مُحدَّث: يدعم التشفير والتوقيع واختيار السيرفر ديناميكياً)
|
| 2 |
+
# ============================================================
|
| 3 |
+
# يرسل المهمّة إلى سيرفر RPC خارجي مع تشفير + توقيع،
|
| 4 |
+
# أو يعمل بوضع JSON صافٍ لو لم يكن SecurityManager مفعَّل.
|
| 5 |
+
# يستخدم قائمة الأقران المكتشفة لاختيار الـ endpoint بدل IP ثابت.
|
| 6 |
+
# ============================================================
|
| 7 |
+
|
| 8 |
+
import requests
|
| 9 |
+
import json
|
| 10 |
+
import os
|
| 11 |
+
from typing import Any
|
| 12 |
+
|
| 13 |
+
# قائمة الأقران (URLs) المستخرجة من peer_discovery
|
| 14 |
+
from peer_discovery import PEERS
|
| 15 |
+
|
| 16 |
+
# عنوان افتراضي احتياطي (يمكن تغييره بمتغير بيئي REMOTE_SERVER)
|
| 17 |
+
FALLBACK_SERVER = os.getenv(
|
| 18 |
+
"REMOTE_SERVER",
|
| 19 |
+
"http://89.111.171.92:7520/run"
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
# محاولة استيراد SecurityManager (اختياري)
|
| 23 |
+
try:
|
| 24 |
+
from security_layer import SecurityManager
|
| 25 |
+
security = SecurityManager(os.getenv("SHARED_SECRET", "my_shared_secret_123"))
|
| 26 |
+
SECURITY_ENABLED = True
|
| 27 |
+
except ImportError:
|
| 28 |
+
security = None
|
| 29 |
+
SECURITY_ENABLED = False
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def _choose_remote_server() -> str:
|
| 33 |
+
"""
|
| 34 |
+
يختار عنوان السيرفر الذي سترسل إليه المهمة:
|
| 35 |
+
1) إذا عُيّن متغير بيئي REMOTE_SERVER، يُستخدم.
|
| 36 |
+
2) وإلا إذا اكتشفنا أقران عبر LAN/Internet، نأخذ أولهم.
|
| 37 |
+
3) وإلا نعود إلى FALLBACK_SERVER.
|
| 38 |
+
"""
|
| 39 |
+
env_url = os.getenv("REMOTE_SERVER")
|
| 40 |
+
if env_url:
|
| 41 |
+
return env_url.rstrip('/') + '/run'
|
| 42 |
+
# PEERS يحوي عناوين كاملة من نوع http://ip:port/run
|
| 43 |
+
if PEERS:
|
| 44 |
+
# نختار الحد الأدنى من التحميل (اختياري) أو أول عنصر
|
| 45 |
+
# هنا ببساطة نأخذ أول URL
|
| 46 |
+
return next(iter(PEERS))
|
| 47 |
+
# استخدام الافتراضي
|
| 48 |
+
return FALLBACK_SERVER.rstrip('/') + '/run'
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def execute_remotely(
|
| 52 |
+
func_name: str,
|
| 53 |
+
args: list[Any] | None = None,
|
| 54 |
+
kwargs: dict[str, Any] | None = None
|
| 55 |
+
) -> Any:
|
| 56 |
+
"""إرسال استدعاء دالة إلى الخادم البعيد وإرجاع النتيجة."""
|
| 57 |
+
if args is None:
|
| 58 |
+
args = []
|
| 59 |
+
if kwargs is None:
|
| 60 |
+
kwargs = {}
|
| 61 |
+
|
| 62 |
+
task = {
|
| 63 |
+
"func": func_name,
|
| 64 |
+
"args": args,
|
| 65 |
+
"kwargs": kwargs,
|
| 66 |
+
"sender_id": "client_node"
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
# اختيار السيرفر الصحيح ديناميكياً
|
| 70 |
+
target_url = _choose_remote_server()
|
| 71 |
+
|
| 72 |
+
try:
|
| 73 |
+
if SECURITY_ENABLED:
|
| 74 |
+
# 1) وقّع المهمة ثم شفّرها
|
| 75 |
+
signed_task = security.sign_task(task)
|
| 76 |
+
encrypted = security.encrypt_data(json.dumps(signed_task).encode())
|
| 77 |
+
|
| 78 |
+
headers = {
|
| 79 |
+
"X-Signature": security.signature_hex,
|
| 80 |
+
"Content-Type": "application/octet-stream"
|
| 81 |
+
}
|
| 82 |
+
payload = encrypted # خام ثنائي
|
| 83 |
+
resp = requests.post(
|
| 84 |
+
target_url,
|
| 85 |
+
headers=headers,
|
| 86 |
+
data=payload,
|
| 87 |
+
timeout=15
|
| 88 |
+
)
|
| 89 |
+
else:
|
| 90 |
+
# وضع التطوير: أرسل JSON صريح
|
| 91 |
+
headers = {"Content-Type": "application/json"}
|
| 92 |
+
resp = requests.post(
|
| 93 |
+
target_url,
|
| 94 |
+
headers=headers,
|
| 95 |
+
json=task,
|
| 96 |
+
timeout=15
|
| 97 |
+
)
|
| 98 |
+
|
| 99 |
+
resp.raise_for_status()
|
| 100 |
+
data = resp.json()
|
| 101 |
+
return data.get("result", "⚠️ لا يوجد نتيجة")
|
| 102 |
+
|
| 103 |
+
except Exception as e:
|
| 104 |
+
return f"❌ فشل التنفيذ البعيد على {target_url}: {e}"
|
replit.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Distributed Task System (DTS)
|
| 2 |
+
|
| 3 |
+
## Overview
|
| 4 |
+
|
| 5 |
+
The Distributed Task System (DTS) is a full-stack web application that manages distributed computing across multiple nodes. It features a React frontend with shadcn/ui components and an Express.js backend with PostgreSQL database integration via Drizzle ORM. The system enables peer discovery, task distribution, load balancing, and real-time monitoring of distributed computing resources.
|
| 6 |
+
|
| 7 |
+
## System Architecture
|
| 8 |
+
|
| 9 |
+
### Frontend Architecture
|
| 10 |
+
- **Framework**: React 18 with TypeScript
|
| 11 |
+
- **Build Tool**: Vite with custom configuration
|
| 12 |
+
- **UI Library**: shadcn/ui components built on Radix UI primitives
|
| 13 |
+
- **Styling**: Tailwind CSS with custom dark theme
|
| 14 |
+
- **State Management**: TanStack Query for server state management
|
| 15 |
+
- **Routing**: Wouter for lightweight client-side routing
|
| 16 |
+
- **Real-time Updates**: WebSocket connection for live system monitoring
|
| 17 |
+
|
| 18 |
+
### Backend Architecture
|
| 19 |
+
- **Runtime**: Node.js with Express.js framework
|
| 20 |
+
- **Database**: PostgreSQL with Drizzle ORM
|
| 21 |
+
- **Real-time Communication**: WebSocket Server for live updates
|
| 22 |
+
- **Task Execution**: Custom task executor with built-in operations
|
| 23 |
+
- **Peer Discovery**: Bonjour/mDNS-based service discovery
|
| 24 |
+
- **Security**: Custom security manager with encryption and digital signatures
|
| 25 |
+
|
| 26 |
+
### Key Components
|
| 27 |
+
|
| 28 |
+
#### Database Schema
|
| 29 |
+
- **Nodes Table**: Stores information about connected computing nodes (id, name, ip, port, status, load, capabilities)
|
| 30 |
+
- **Tasks Table**: Manages distributed tasks (id, name, status, nodeId, complexity, duration, result, error)
|
| 31 |
+
- **System Metrics Table**: Tracks performance metrics (nodeId, cpuUsage, memoryUsage, timestamp)
|
| 32 |
+
- **System Logs Table**: Centralized logging (level, message, source, nodeId, timestamp)
|
| 33 |
+
|
| 34 |
+
#### Services
|
| 35 |
+
- **OffloadSystem**: Orchestrates task distribution and peer management
|
| 36 |
+
- **PeerDiscovery**: Uses Bonjour protocol for automatic node discovery on the network
|
| 37 |
+
- **TaskExecutor**: Handles built-in tasks like matrix multiplication, prime calculation, and data processing
|
| 38 |
+
- **SecurityManager**: Manages payload encryption, digital signatures, and secure communication
|
| 39 |
+
- **SystemMonitor**: Tracks system performance and resource utilization
|
| 40 |
+
|
| 41 |
+
#### Frontend Components
|
| 42 |
+
- **Dashboard**: Main interface showing system overview and metrics
|
| 43 |
+
- **MetricsGrid**: Real-time system performance visualization
|
| 44 |
+
- **NodesTable**: Connected nodes management interface
|
| 45 |
+
- **TaskActivity**: Task execution monitoring and status tracking
|
| 46 |
+
- **SecurityPanel**: Security status indicators
|
| 47 |
+
- **SystemLogs**: Centralized log viewing interface
|
| 48 |
+
|
| 49 |
+
## Data Flow
|
| 50 |
+
|
| 51 |
+
1. **Node Registration**: Nodes automatically discover and register with the system via mDNS
|
| 52 |
+
2. **Task Submission**: Tasks are submitted through the web interface or API
|
| 53 |
+
3. **Load Balancing**: System evaluates node capacity and distributes tasks accordingly
|
| 54 |
+
4. **Task Execution**: Tasks are executed on selected nodes with real-time status updates
|
| 55 |
+
5. **Result Collection**: Results are collected and stored in the database
|
| 56 |
+
6. **Real-time Updates**: WebSocket connections push updates to connected clients
|
| 57 |
+
|
| 58 |
+
## External Dependencies
|
| 59 |
+
|
| 60 |
+
### Production Dependencies
|
| 61 |
+
- **Database**: @neondatabase/serverless for PostgreSQL connection
|
| 62 |
+
- **ORM**: drizzle-orm and drizzle-zod for database operations
|
| 63 |
+
- **UI Components**: @radix-ui/* packages for accessible UI primitives
|
| 64 |
+
- **Networking**: bonjour-service for peer discovery
|
| 65 |
+
- **Real-time**: ws (WebSocket) for live updates
|
| 66 |
+
- **Query Management**: @tanstack/react-query for server state
|
| 67 |
+
- **Session Management**: connect-pg-simple for session storage
|
| 68 |
+
|
| 69 |
+
### Development Dependencies
|
| 70 |
+
- **Build Tools**: Vite, esbuild for production builds
|
| 71 |
+
- **Type Safety**: TypeScript with strict configuration
|
| 72 |
+
- **Code Quality**: ESLint integration (implied by tsconfig)
|
| 73 |
+
- **Development Experience**: @replit/vite-plugin-runtime-error-modal and cartographer
|
| 74 |
+
|
| 75 |
+
## Deployment Strategy
|
| 76 |
+
|
| 77 |
+
### Development Mode
|
| 78 |
+
- Frontend served via Vite dev server with HMR
|
| 79 |
+
- Backend runs with tsx for TypeScript execution
|
| 80 |
+
- Database migrations handled via drizzle-kit push
|
| 81 |
+
- Automatic service discovery in local network
|
| 82 |
+
|
| 83 |
+
### Production Build
|
| 84 |
+
1. Frontend built with Vite to `dist/public`
|
| 85 |
+
2. Backend compiled with esbuild to `dist/index.js`
|
| 86 |
+
3. Single Node.js process serves both static files and API
|
| 87 |
+
4. PostgreSQL database connection via environment variables
|
| 88 |
+
5. WebSocket server integrated with HTTP server
|
| 89 |
+
|
| 90 |
+
### Environment Configuration
|
| 91 |
+
- `DATABASE_URL`: PostgreSQL connection string (required)
|
| 92 |
+
- `NODE_ENV`: Environment mode (development/production)
|
| 93 |
+
- `NODE_NAME`: Custom node identifier
|
| 94 |
+
- `SHARED_SECRET`: Security encryption key
|
| 95 |
+
|
| 96 |
+
## Changelog
|
| 97 |
+
- June 29, 2025. Initial setup
|
| 98 |
+
- June 29, 2025. Added broadcast messaging capability to send messages to all connected devices from the index page
|
| 99 |
+
- Created broadcast_messages table in PostgreSQL database
|
| 100 |
+
- Implemented API endpoints for sending and retrieving broadcast messages
|
| 101 |
+
- Added real-time WebSocket broadcasting to all connected clients
|
| 102 |
+
- Created bilingual UI components with Arabic interface
|
| 103 |
+
- Added navigation link in sidebar for easy access
|
| 104 |
+
- Integrated with existing authentication and security systems
|
| 105 |
+
|
| 106 |
+
## User Preferences
|
| 107 |
+
|
| 108 |
+
Preferred communication style: Simple, everyday language.
|
requirements.txt
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Flask
|
| 2 |
+
flask_cors
|
| 3 |
+
requests
|
| 4 |
+
psutil
|
| 5 |
+
zeroconf
|
| 6 |
+
cryptography
|
| 7 |
+
numpy
|
| 8 |
+
networkx
|
rpc_server.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# rpc_server.py (مُحدَّث بحيث يدعم التشفير الاختياري)
|
| 2 |
+
# ============================================================
|
| 3 |
+
# خادِم يستقبل مهام عن بُعد:
|
| 4 |
+
# • إن وصلته بيانات خام (Encrypted) في Body → يفك تشفيرها ويتحقق من التوقيع.
|
| 5 |
+
# • وإلا إن وصل JSON خام في Content‑Type: application/json → ينفّذ مباشرة (وضع تطويـر).
|
| 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 |
+
|
| 13 |
+
SECURITY = SecurityManager("my_shared_secret_123")
|
| 14 |
+
|
| 15 |
+
logging.basicConfig(
|
| 16 |
+
filename="server.log",
|
| 17 |
+
level=logging.INFO,
|
| 18 |
+
format="%(asctime)s - %(levelname)s - %(message)s"
|
| 19 |
+
)
|
| 20 |
+
|
| 21 |
+
app = Flask(name)
|
| 22 |
+
|
| 23 |
+
# ------------------------------------------------------------------
|
| 24 |
+
@app.route("/health")
|
| 25 |
+
def health():
|
| 26 |
+
return jsonify(status="ok")
|
| 27 |
+
|
| 28 |
+
# ------------------------------------------------------------------
|
| 29 |
+
@app.route("/run", methods=["POST"])
|
| 30 |
+
def run():
|
| 31 |
+
try:
|
| 32 |
+
# 1) حاول قراءة كـ JSON مباشر (وضع التطويـر)
|
| 33 |
+
if request.is_json:
|
| 34 |
+
data = request.get_json()
|
| 35 |
+
else:
|
| 36 |
+
# 2) وإلا اعتبره Payload مُشفَّر (وضع الإنتاج)
|
| 37 |
+
encrypted = request.get_data()
|
| 38 |
+
try:
|
| 39 |
+
decrypted = SECURITY.decrypt_data(encrypted)
|
| 40 |
+
data = json.loads(decrypted.decode())
|
| 41 |
+
except Exception as e:
|
| 42 |
+
logging.error(f"⚠️ فشل فك التشفير: {e}")
|
| 43 |
+
return jsonify(error="Decryption failed"), 400
|
| 44 |
+
|
| 45 |
+
# 3) التحقّق من التوقيع إن وُجد
|
| 46 |
+
if "_signature" in data:
|
| 47 |
+
if not SECURITY.verify_task(data):
|
| 48 |
+
logging.warning("❌ توقيع غير صالح")
|
| 49 |
+
return jsonify(error="Invalid signature"), 403
|
| 50 |
+
# أزل عناصر موقّعة إضافية
|
| 51 |
+
data = {k: v for k, v in data.items() if k not in ("_signature", "sender_id")}
|
| 52 |
+
|
| 53 |
+
func_name = data.get("func")
|
| 54 |
+
args = data.get("args", [])
|
| 55 |
+
kwargs = data.get("kwargs", {})
|
| 56 |
+
|
| 57 |
+
fn = getattr(smart_tasks, func_name, None)
|
| 58 |
+
if not fn:
|
| 59 |
+
logging.warning(f"❌ لم يتم العثور على الدالة: {func_name}")
|
| 60 |
+
return jsonify(error="Function not found"), 404
|
| 61 |
+
|
| 62 |
+
logging.info(f"⚙️ تنفيذ الدالة: {func_name} من جهاز آخر")
|
| 63 |
+
result = fn(*args, **kwargs)
|
| 64 |
+
return jsonify(result=result)
|
| 65 |
+
|
| 66 |
+
except Exception as e:
|
| 67 |
+
logging.error(f"🔥 خطأ أثناء تنفيذ المهمة: {str(e)}")
|
| 68 |
+
return jsonify(error=str(e)), 500
|
| 69 |
+
|
| 70 |
+
# ------------------------------------------------------------------
|
| 71 |
+
if name == "main":
|
| 72 |
+
# تأكد أن المنفذ 7520 مفتوح
|
| 73 |
+
app.run(host="0.0.0.0", port=7520)
|
run_task.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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")
|
| 9 |
+
else:
|
| 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
|
security_layer.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# security_layer.py (مُحدَّث)
|
| 2 |
+
# ============================================================
|
| 3 |
+
# إدارة التشفير والتوقيع وتبادل المفاتيح بين العقد
|
| 4 |
+
# ============================================================
|
| 5 |
+
|
| 6 |
+
from cryptography.hazmat.primitives import hashes, serialization
|
| 7 |
+
from cryptography.hazmat.primitives.asymmetric import rsa, padding
|
| 8 |
+
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
| 9 |
+
from cryptography.fernet import Fernet
|
| 10 |
+
import os, base64, json
|
| 11 |
+
from typing import Dict
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class SecurityManager:
|
| 15 |
+
"""طبقة أمان موحّدة لكل العقد."""
|
| 16 |
+
|
| 17 |
+
def __init__(self, shared_secret: str):
|
| 18 |
+
# مفتاح متماثل لاستخدام Fernet
|
| 19 |
+
self._key = self._derive_key(shared_secret)
|
| 20 |
+
self._cipher = Fernet(self._key)
|
| 21 |
+
|
| 22 |
+
# زوج مفاتيح غير متماثل للتوقيع الرقمي
|
| 23 |
+
self._private_key = rsa.generate_private_key(
|
| 24 |
+
public_exponent=65537,
|
| 25 |
+
key_size=2048,
|
| 26 |
+
)
|
| 27 |
+
self._public_pem = (
|
| 28 |
+
self._private_key.public_key()
|
| 29 |
+
.public_bytes(
|
| 30 |
+
encoding=serialization.Encoding.PEM,
|
| 31 |
+
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
| 32 |
+
)
|
| 33 |
+
.decode()
|
| 34 |
+
)
|
| 35 |
+
# مفاتيح العقد الأخرى {peer_id: public_key_obj}
|
| 36 |
+
self._peer_keys: Dict[str, rsa.RSAPublicKey] = {}
|
| 37 |
+
|
| 38 |
+
# ------------------------------------------------------------
|
| 39 |
+
# تشفير / فك تشفير متماثل
|
| 40 |
+
# ------------------------------------------------------------
|
| 41 |
+
def encrypt_data(self, data: bytes) -> bytes:
|
| 42 |
+
return self._cipher.encrypt(data)
|
| 43 |
+
|
| 44 |
+
def decrypt_data(self, encrypted: bytes) -> bytes:
|
| 45 |
+
return self._cipher.decrypt(encrypted)
|
| 46 |
+
|
| 47 |
+
# ------------------------------------------------------------
|
| 48 |
+
# توقيع/تحقّق رقمي غير متماثل
|
| 49 |
+
# ------------------------------------------------------------
|
| 50 |
+
def sign_task(self, task: Dict) -> Dict:
|
| 51 |
+
"""يُرجع نسخة موقّعة من الـtask مضافًا إليها المفتاح العام والمعرّف."""
|
| 52 |
+
signature = self._private_key.sign(
|
| 53 |
+
json.dumps(task, separators=(",", ":")).encode(),
|
| 54 |
+
padding.PSS(
|
| 55 |
+
mgf=padding.MGF1(hashes.SHA256()),
|
| 56 |
+
salt_length=padding.PSS.MAX_LENGTH,
|
| 57 |
+
),
|
| 58 |
+
hashes.SHA256(),
|
| 59 |
+
)
|
| 60 |
+
task_signed = task.copy()
|
| 61 |
+
task_signed.update(
|
| 62 |
+
{
|
| 63 |
+
"_signature": base64.b64encode(signature).decode(),
|
| 64 |
+
"sender_id": os.getenv("NODE_ID", "unknown"),
|
| 65 |
+
"sender_key": self._public_pem,
|
| 66 |
+
}
|
| 67 |
+
)
|
| 68 |
+
return task_signed
|
| 69 |
+
|
| 70 |
+
def verify_task(self, signed_task: Dict) -> bool:
|
| 71 |
+
"""يتحقق من صحة التوقيع باستخدام المفتاح العام للمرسل."""
|
| 72 |
+
if "_signature" not in signed_task or "sender_id" not in signed_task:
|
| 73 |
+
return False
|
| 74 |
+
sig = base64.b64decode(signed_task["_signature"])
|
| 75 |
+
task_copy = {k: v for k, v in signed_task.items() if k not in {"_signature", "sender_key"}}
|
| 76 |
+
|
| 77 |
+
peer_id = signed_task["sender_id"]
|
| 78 |
+
if peer_id not in self._peer_keys:
|
| 79 |
+
# حاول إضافة المفتاح المرسل إن وجد
|
| 80 |
+
if "sender_key" in signed_task:
|
| 81 |
+
self.add_peer_key(peer_id, signed_task["sender_key"])
|
| 82 |
+
else:
|
| 83 |
+
return False
|
| 84 |
+
try:
|
| 85 |
+
self._peer_keys[peer_id].verify(
|
| 86 |
+
sig,
|
| 87 |
+
json.dumps(task_copy, separators=(",", ":")).encode(),
|
| 88 |
+
padding.PSS(
|
| 89 |
+
mgf=padding.MGF1(hashes.SHA256()),
|
| 90 |
+
salt_length=padding.PSS.MAX_LENGTH,
|
| 91 |
+
),
|
| 92 |
+
hashes.SHA256(),
|
| 93 |
+
)
|
| 94 |
+
return True
|
| 95 |
+
except Exception:
|
| 96 |
+
return False
|
| 97 |
+
|
| 98 |
+
# ------------------------------------------------------------
|
| 99 |
+
# إدارة المفاتيح العامة للأقران
|
| 100 |
+
# ------------------------------------------------------------
|
| 101 |
+
def add_peer_key(self, peer_id: str, public_key_pem: str):
|
| 102 |
+
"""تخزين/تحديث المفتاح العام لعقدة أخرى."""
|
| 103 |
+
self._peer_keys[peer_id] = serialization.load_pem_public_key(
|
| 104 |
+
public_key_pem.encode()
|
| 105 |
+
)
|
| 106 |
+
|
| 107 |
+
# ------------------------------------------------------------
|
| 108 |
+
# أدوات داخلية
|
| 109 |
+
# ------------------------------------------------------------
|
| 110 |
+
@staticmethod
|
| 111 |
+
def _derive_key(password: str) -> bytes:
|
| 112 |
+
salt = b"nora_salt_2025" # ◀️ عدِّل في الإنتاج
|
| 113 |
+
kdf = PBKDF2HMAC(
|
| 114 |
+
algorithm=hashes.SHA256(),
|
| 115 |
+
length=32,
|
| 116 |
+
salt=salt,
|
| 117 |
+
iterations=150_000,
|
| 118 |
+
)
|
| 119 |
+
return base64.urlsafe_b64encode(kdf.derive(password.encode()))
|
| 120 |
+
|
server.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Flask, request, jsonify
|
| 2 |
+
from flask_cors import CORS
|
| 3 |
+
import json
|
| 4 |
+
from your_tasks import *
|
| 5 |
+
from project_identifier import create_project_endpoint, get_project_info
|
| 6 |
+
import logging
|
| 7 |
+
|
| 8 |
+
app = Flask(__name__)
|
| 9 |
+
CORS(app)
|
| 10 |
+
|
| 11 |
+
@app.route('/multiply', methods=['POST'])
|
| 12 |
+
def multiply():
|
| 13 |
+
try:
|
| 14 |
+
data = request.get_json()
|
| 15 |
+
a = data.get("a", 0)
|
| 16 |
+
b = data.get("b", 0)
|
| 17 |
+
result_dict = multiply_task(a, b) # دالة offload
|
| 18 |
+
return jsonify({"result": result_dict["result"]})
|
| 19 |
+
except Exception as e:
|
| 20 |
+
return jsonify({"error": str(e)}), 500
|
| 21 |
+
|
| 22 |
+
@app.route('/health', methods=['GET'])
|
| 23 |
+
def health_check():
|
| 24 |
+
return jsonify({"status": "healthy", "port": 7520})
|
| 25 |
+
|
| 26 |
+
@app.route('/project_info', methods=['GET'])
|
| 27 |
+
def project_info():
|
| 28 |
+
return create_project_endpoint()
|
| 29 |
+
|
| 30 |
+
if __name__ == "__main__":
|
| 31 |
+
# هذا العنوان يسمح بالاستماع على IP خارجي لتلقي الاتصالات من الإنترنت
|
| 32 |
+
app.run(host="0.0.0.0", port=7520)
|
setup.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# setup.py
|
| 2 |
+
import configparser
|
| 3 |
+
# Monkey-patch for Python 3: define SafeConfigParser for stdeb compatibility
|
| 4 |
+
configparser.SafeConfigParser = configparser.RawConfigParser
|
| 5 |
+
|
| 6 |
+
from setuptools import setup, find_packages
|
| 7 |
+
|
| 8 |
+
# Load long description from README
|
| 9 |
+
with open("README.md", encoding="utf-8") as f:
|
| 10 |
+
long_desc = f.read()
|
| 11 |
+
|
| 12 |
+
setup(
|
| 13 |
+
name="distributed-task-system",
|
| 14 |
+
version="1.0",
|
| 15 |
+
description="A simple distributed task offload system",
|
| 16 |
+
long_description=long_desc,
|
| 17 |
+
long_description_content_type="text/markdown",
|
| 18 |
+
author="Osama Awartany",
|
| 19 |
+
author_email="osama@example.com",
|
| 20 |
+
url="https://github.com/yourusername/distributed-task-system",
|
| 21 |
+
packages=find_packages(include=["offload_core", "offload_core.*"]),
|
| 22 |
+
py_modules=["dts_cli"], # Include the CLI helper module
|
| 23 |
+
install_requires=[
|
| 24 |
+
"flask",
|
| 25 |
+
"requests",
|
| 26 |
+
"psutil",
|
| 27 |
+
"zeroconf",
|
| 28 |
+
"flask_cors",
|
| 29 |
+
"numpy",
|
| 30 |
+
"uvicorn"
|
| 31 |
+
],
|
| 32 |
+
entry_points={
|
| 33 |
+
"console_scripts": [
|
| 34 |
+
"dts-start=dts_cli:main",
|
| 35 |
+
],
|
| 36 |
+
},
|
| 37 |
+
data_files=[
|
| 38 |
+
("/etc/systemd/system", ["dts.service"])
|
| 39 |
+
],
|
| 40 |
+
classifiers=[
|
| 41 |
+
"Programming Language :: Python :: 3",
|
| 42 |
+
"License :: OSI Approved :: MIT License",
|
| 43 |
+
"Operating System :: POSIX :: Linux",
|
| 44 |
+
],
|
| 45 |
+
python_requires='>=3.6',
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
# dts_cli.py
|
| 50 |
+
# CLI entry-point module for the Distributed Task System
|
| 51 |
+
# Place this next to setup.py
|
| 52 |
+
|
| 53 |
+
def main():
|
| 54 |
+
"""
|
| 55 |
+
Main entry for dts-start console script.
|
| 56 |
+
Launches the FastAPI/Flask app via Uvicorn.
|
| 57 |
+
"""
|
| 58 |
+
# Import your application instance here:
|
| 59 |
+
try:
|
| 60 |
+
from offload_core.main import app
|
| 61 |
+
except ImportError:
|
| 62 |
+
# If you have a factory, adjust accordingly:
|
| 63 |
+
from offload_core.main import create_app
|
| 64 |
+
app = create_app()
|
| 65 |
+
|
| 66 |
+
# Run with Uvicorn
|
| 67 |
+
import uvicorn
|
| 68 |
+
uvicorn.run(app, host="0.0.0.0", port=7520)
|
| 69 |
+
|
setup_fonts.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import requests
|
| 3 |
+
|
| 4 |
+
FONT_DIR = "fonts"
|
| 5 |
+
FONT_PATH = os.path.join(FONT_DIR, "Cairo-Regular.ttf")
|
| 6 |
+
|
| 7 |
+
# 🔥 رابط مباشر من Google Fonts CDN - Cairo Regular وزن 400
|
| 8 |
+
URL = "https://fonts.cdnfonts.com/s/12667/Cairo-Regular.woff"
|
| 9 |
+
|
| 10 |
+
os.makedirs(FONT_DIR, exist_ok=True)
|
| 11 |
+
|
| 12 |
+
print("[+] تحميل الخط Cairo-Regular...")
|
| 13 |
+
response = requests.get(URL)
|
| 14 |
+
if response.status_code == 200:
|
| 15 |
+
with open(FONT_PATH, "wb") as f:
|
| 16 |
+
f.write(response.content)
|
| 17 |
+
print(f"[✅] تم حفظ الخط في {FONT_PATH}")
|
| 18 |
+
else:
|
| 19 |
+
print(f"[❌] فشل تحميل الخط! كود الحالة: {response.status_code}")
|
| 20 |
+
|
simple_assistant.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
|
| 4 |
+
import os
|
| 5 |
+
import json
|
| 6 |
+
import random
|
| 7 |
+
import re
|
| 8 |
+
from datetime import datetime
|
| 9 |
+
|
| 10 |
+
class SimpleNoraAssistant:
|
| 11 |
+
def __init__(self):
|
| 12 |
+
self.history_path = "simple_history.json"
|
| 13 |
+
self.chat_history = self.load_history()
|
| 14 |
+
self.knowledge_base = {
|
| 15 |
+
"تحية": ["مرحباً!", "أهلاً وسهلاً!", "كيف حالك؟"],
|
| 16 |
+
"وقت": [f"الوقت الحالي: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"],
|
| 17 |
+
"مساعدة": ["يمكنني مساعدتك في الأسئلة البسيطة والدردشة!", "اسألني أي سؤال وسأحاول المساعدة!"],
|
| 18 |
+
"برمجة": ["البرمجة مهارة رائعة!", "ما نوع البرمجة التي تريد تعلمها؟"],
|
| 19 |
+
"افتراضي": ["مثير للاهتمام!", "حدثني أكثر عن ذلك.", "هذا سؤال جيد!"]
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
def load_history(self):
|
| 23 |
+
"""تحميل سجل المحادثة"""
|
| 24 |
+
if os.path.exists(self.history_path):
|
| 25 |
+
try:
|
| 26 |
+
with open(self.history_path, "r", encoding="utf-8") as f:
|
| 27 |
+
return json.load(f)
|
| 28 |
+
except:
|
| 29 |
+
return []
|
| 30 |
+
return []
|
| 31 |
+
|
| 32 |
+
def save_history(self):
|
| 33 |
+
"""حفظ سجل المحادثة"""
|
| 34 |
+
with open(self.history_path, "w", encoding="utf-8") as f:
|
| 35 |
+
json.dump(self.chat_history, f, ensure_ascii=False, indent=2)
|
| 36 |
+
|
| 37 |
+
def get_response(self, user_input: str) -> str:
|
| 38 |
+
"""الحصول على رد بسيط"""
|
| 39 |
+
user_input = user_input.lower().strip()
|
| 40 |
+
|
| 41 |
+
# تحديد نوع السؤال
|
| 42 |
+
if any(word in user_input for word in ["مرحبا", "أهلا", "السلام"]):
|
| 43 |
+
category = "تحية"
|
| 44 |
+
elif any(word in user_input for word in ["وقت", "تاريخ", "ساعة"]):
|
| 45 |
+
category = "وقت"
|
| 46 |
+
elif any(word in user_input for word in ["مساعدة", "help"]):
|
| 47 |
+
category = "مساعدة"
|
| 48 |
+
elif any(word in user_input for word in ["برمجة", "كود", "programming"]):
|
| 49 |
+
category = "برمجة"
|
| 50 |
+
else:
|
| 51 |
+
category = "افتراضي"
|
| 52 |
+
|
| 53 |
+
# اختيار رد عشوائي من الفئة
|
| 54 |
+
responses = self.knowledge_base.get(category, self.knowledge_base["افتراضي"])
|
| 55 |
+
return random.choice(responses)
|
| 56 |
+
|
| 57 |
+
def learn_from_input(self, user_input: str, user_response: str):
|
| 58 |
+
"""تعلم بسيط من المدخلات"""
|
| 59 |
+
# إضافة ردود جديدة بناءً على تفاعل المستخدم
|
| 60 |
+
if "جيد" in user_response.lower() or "شكرا" in user_response.lower():
|
| 61 |
+
print("نورا: سعيدة أنني ساعدتك!")
|
| 62 |
+
|
| 63 |
+
def simulate_server_scan(self):
|
| 64 |
+
"""محاكاة البحث عن الخوادم"""
|
| 65 |
+
print("نورا: أبحث عن خوادم...")
|
| 66 |
+
fake_servers = ["192.168.1.5", "192.168.1.10", "192.168.1.20"]
|
| 67 |
+
for server in fake_servers:
|
| 68 |
+
print(f"نورا: تم العثور على خادم مفتوح في {server}")
|
| 69 |
+
print(f"نورا: أقوم بنسخ نفسي إلى {server} (محاكاة فقط)...")
|
| 70 |
+
|
| 71 |
+
def chat(self):
|
| 72 |
+
"""بدء المحادثة"""
|
| 73 |
+
print("مرحباً! أنا نورا، مساعدتك البسيطة. اكتب 'خروج' للإنهاء أو 'scan' للبحث عن خوادم.")
|
| 74 |
+
|
| 75 |
+
while True:
|
| 76 |
+
user_input = input("\nأنت: ").strip()
|
| 77 |
+
|
| 78 |
+
if user_input.lower() in ["خروج", "exit", "quit"]:
|
| 79 |
+
print("نورا: مع السلامة!")
|
| 80 |
+
break
|
| 81 |
+
elif user_input.lower() == "scan":
|
| 82 |
+
self.simulate_server_scan()
|
| 83 |
+
continue
|
| 84 |
+
elif not user_input:
|
| 85 |
+
continue
|
| 86 |
+
|
| 87 |
+
# الحصول على الرد
|
| 88 |
+
response = self.get_response(user_input)
|
| 89 |
+
print(f"نورا: {response}")
|
| 90 |
+
|
| 91 |
+
# حفظ في السجل
|
| 92 |
+
self.chat_history.append({
|
| 93 |
+
"timestamp": datetime.now().isoformat(),
|
| 94 |
+
"user": user_input,
|
| 95 |
+
"assistant": response
|
| 96 |
+
})
|
| 97 |
+
self.save_history()
|
| 98 |
+
|
| 99 |
+
if __name__ == "__main__":
|
| 100 |
+
assistant = SimpleNoraAssistant()
|
| 101 |
+
assistant.chat()
|
simple_history.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"timestamp": "2025-08-01T04:07:49.526870",
|
| 4 |
+
"user": "مرحبا",
|
| 5 |
+
"assistant": "مرحباً!"
|
| 6 |
+
},
|
| 7 |
+
{
|
| 8 |
+
"timestamp": "2025-08-01T04:07:56.868610",
|
| 9 |
+
"user": "كيف حالك",
|
| 10 |
+
"assistant": "هذا سؤال جيد!"
|
| 11 |
+
}
|
| 12 |
+
]
|
smart_tasks.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import numpy as np
|
| 3 |
+
import time
|
| 4 |
+
|
| 5 |
+
def prime_calculation(n: int):
|
| 6 |
+
"""ترجع قائمة الأعداد الأوليّة حتى n مع عددها"""
|
| 7 |
+
primes = []
|
| 8 |
+
for num in range(2, n + 1):
|
| 9 |
+
if all(num % p != 0 for p in range(2, int(math.sqrt(num)) + 1)):
|
| 10 |
+
primes.append(num)
|
| 11 |
+
return {"count": len(primes), "primes": primes}
|
| 12 |
+
|
| 13 |
+
def matrix_multiply(size: int):
|
| 14 |
+
"""ضرب مصفوفات عشوائيّة (size × size)"""
|
| 15 |
+
A = np.random.rand(size, size)
|
| 16 |
+
B = np.random.rand(size, size)
|
| 17 |
+
result = np.dot(A, B) # يمكن أيضًا: A @ B
|
| 18 |
+
return {"result": result.tolist()}
|
| 19 |
+
|
| 20 |
+
def data_processing(data_size: int):
|
| 21 |
+
"""تنفيذ معالجة بيانات بسيطة كتجربة"""
|
| 22 |
+
data = np.random.rand(data_size)
|
| 23 |
+
mean = np.mean(data)
|
| 24 |
+
std_dev = np.std(data)
|
| 25 |
+
return {"mean": mean, "std_dev": std_dev}
|
| 26 |
+
|
| 27 |
+
def image_processing_emulation(iterations):
|
| 28 |
+
"""محاكاة معالجة الصور"""
|
| 29 |
+
results = []
|
| 30 |
+
for i in range(iterations):
|
| 31 |
+
fake_processing = sum(math.sqrt(x) for x in range(i * 100, (i + 1) * 100))
|
| 32 |
+
results.append(fake_processing)
|
| 33 |
+
time.sleep(0.01)
|
| 34 |
+
return {"iterations": iterations, "results": results}
|
| 35 |
+
|
| 36 |
+
# مهام معالجة الفيديو والألعاب ثلاثية الأبعاد
|
| 37 |
+
def video_format_conversion(duration_seconds, quality_level, input_format="mp4", output_format="avi"):
|
| 38 |
+
"""تحويل صيغة الفيديو"""
|
| 39 |
+
import time
|
| 40 |
+
start_time = time.time()
|
| 41 |
+
processing_time = duration_seconds * quality_level * 0.05 # معالجة أسرع للخادم
|
| 42 |
+
time.sleep(min(processing_time, 1)) # محدود بثانية واحدة
|
| 43 |
+
|
| 44 |
+
return {
|
| 45 |
+
"status": "success",
|
| 46 |
+
"input_format": input_format,
|
| 47 |
+
"output_format": output_format,
|
| 48 |
+
"duration": duration_seconds,
|
| 49 |
+
"quality": quality_level,
|
| 50 |
+
"processing_time": time.time() - start_time,
|
| 51 |
+
"server_processed": True
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
def video_effects_processing(video_length, effects_count, resolution="1080p"):
|
| 55 |
+
"""معالجة تأثيرات الفيديو"""
|
| 56 |
+
import time
|
| 57 |
+
start_time = time.time()
|
| 58 |
+
|
| 59 |
+
resolution_multiplier = {"480p": 1, "720p": 2, "1080p": 3, "4K": 5}
|
| 60 |
+
multiplier = resolution_multiplier.get(resolution, 2)
|
| 61 |
+
processing_time = video_length * effects_count * multiplier * 0.03
|
| 62 |
+
|
| 63 |
+
time.sleep(min(processing_time, 1.5))
|
| 64 |
+
|
| 65 |
+
return {
|
| 66 |
+
"status": "success",
|
| 67 |
+
"video_length": video_length,
|
| 68 |
+
"effects_count": effects_count,
|
| 69 |
+
"resolution": resolution,
|
| 70 |
+
"processing_time": time.time() - start_time,
|
| 71 |
+
"server_processed": True
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
def render_3d_scene(objects_count, resolution_width, resolution_height,
|
| 75 |
+
lighting_quality="medium", texture_quality="high"):
|
| 76 |
+
"""رندر مشهد ثلاثي الأبعاد"""
|
| 77 |
+
import time
|
| 78 |
+
start_time = time.time()
|
| 79 |
+
|
| 80 |
+
complexity = objects_count * (resolution_width * resolution_height) / 2000000 # تقليل التعقيد للخادم
|
| 81 |
+
processing_time = complexity * 0.02
|
| 82 |
+
|
| 83 |
+
time.sleep(min(processing_time, 2))
|
| 84 |
+
|
| 85 |
+
fps = max(30, 120 - (complexity * 5)) # أداء أفضل للخادم
|
| 86 |
+
|
| 87 |
+
return {
|
| 88 |
+
"status": "success",
|
| 89 |
+
"objects_rendered": objects_count,
|
| 90 |
+
"resolution": f"{resolution_width}x{resolution_height}",
|
| 91 |
+
"lighting_quality": lighting_quality,
|
| 92 |
+
"texture_quality": texture_quality,
|
| 93 |
+
"estimated_fps": round(fps, 1),
|
| 94 |
+
"processing_time": time.time() - start_time,
|
| 95 |
+
"server_processed": True
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
def physics_simulation(objects_count, frames_count, physics_quality="medium"):
|
| 99 |
+
"""محاكاة الفيزياء"""
|
| 100 |
+
import time
|
| 101 |
+
start_time = time.time()
|
| 102 |
+
|
| 103 |
+
quality_multiplier = {"low": 1, "medium": 2, "high": 4, "ultra": 8}
|
| 104 |
+
multiplier = quality_multiplier.get(physics_quality, 2)
|
| 105 |
+
|
| 106 |
+
calculations = objects_count * frames_count * multiplier
|
| 107 |
+
processing_time = calculations / 200000 # أسرع للخادم
|
| 108 |
+
|
| 109 |
+
time.sleep(min(processing_time, 1.5))
|
| 110 |
+
|
| 111 |
+
return {
|
| 112 |
+
"status": "success",
|
| 113 |
+
"objects_simulated": objects_count,
|
| 114 |
+
"frames_processed": frames_count,
|
| 115 |
+
"physics_quality": physics_quality,
|
| 116 |
+
"calculations_performed": calculations,
|
| 117 |
+
"processing_time": time.time() - start_time,
|
| 118 |
+
"server_processed": True
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
def game_ai_processing(ai_agents_count, decision_complexity, game_state_size):
|
| 122 |
+
"""معالجة ذكاء اصطناعي للألعاب"""
|
| 123 |
+
import time
|
| 124 |
+
start_time = time.time()
|
| 125 |
+
|
| 126 |
+
total_operations = ai_agents_count * decision_complexity * game_state_size
|
| 127 |
+
processing_time = total_operations / 100000 # أسرع للخادم
|
| 128 |
+
|
| 129 |
+
time.sleep(min(processing_time, 1))
|
| 130 |
+
|
| 131 |
+
return {
|
| 132 |
+
"status": "success",
|
| 133 |
+
"ai_agents": ai_agents_count,
|
| 134 |
+
"decision_complexity": decision_complexity,
|
| 135 |
+
"total_operations": total_operations,
|
| 136 |
+
"processing_time": time.time() - start_time,
|
| 137 |
+
"server_processed": True
|
| 138 |
+
}
|
startup.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import subprocess
|
| 2 |
+
import time
|
| 3 |
+
import logging
|
| 4 |
+
import sys
|
| 5 |
+
from autostart_config import AutoStartManager
|
| 6 |
+
from distributed_executor import DistributedExecutor
|
| 7 |
+
|
| 8 |
+
PY = sys.executable # مسار بايثون الحالي
|
| 9 |
+
|
| 10 |
+
SERVICES = [
|
| 11 |
+
("peer_server.py", "Peer‑Server"),
|
| 12 |
+
("rpc_server.py", "RPC‑Server"),
|
| 13 |
+
("server.py", "REST‑Server"), # يعمل على 7521 حاليًا
|
| 14 |
+
("load_balancer.py", "Load‑Balancer"),
|
| 15 |
+
]
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def launch_services():
|
| 19 |
+
procs = []
|
| 20 |
+
for script, name in SERVICES:
|
| 21 |
+
try:
|
| 22 |
+
p = subprocess.Popen([PY, script])
|
| 23 |
+
logging.info(f"✅ {name} قيد التشغيل (PID={p.pid})")
|
| 24 |
+
procs.append(p)
|
| 25 |
+
except FileNotFoundError:
|
| 26 |
+
logging.error(f"❌ لم يُعثَر على {script}; تخطَّيته")
|
| 27 |
+
return procs
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def main():
|
| 31 |
+
# إعداد السجلات مع دعم وضع الخلفية
|
| 32 |
+
import os
|
| 33 |
+
|
| 34 |
+
# إنشاء مجلد السجلات
|
| 35 |
+
os.makedirs("logs", exist_ok=True)
|
| 36 |
+
|
| 37 |
+
logging.basicConfig(
|
| 38 |
+
level=logging.INFO,
|
| 39 |
+
format="%(asctime)s - %(levelname)s - %(message)s",
|
| 40 |
+
handlers=[
|
| 41 |
+
logging.FileHandler("logs/startup.log"),
|
| 42 |
+
logging.StreamHandler() # إظهار السجلات في وحدة التحكم أيضاً
|
| 43 |
+
]
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
try:
|
| 47 |
+
cfg = AutoStartManager().config
|
| 48 |
+
if not cfg.get("enabled", True):
|
| 49 |
+
logging.info("التشغيل التلقائي مُعطل في الإعدادات")
|
| 50 |
+
return
|
| 51 |
+
|
| 52 |
+
# فحص إذا كانت الخدمة الخلفية متاحة
|
| 53 |
+
background_service_available = os.path.exists("background_service.py")
|
| 54 |
+
|
| 55 |
+
if background_service_available:
|
| 56 |
+
logging.info("🔄 تشغيل الخدمة الخلفية المحسّنة...")
|
| 57 |
+
# تشغيل الخدمة الخلفية الجديدة
|
| 58 |
+
try:
|
| 59 |
+
subprocess.Popen([PY, "background_service.py", "start"])
|
| 60 |
+
logging.info("✅ تم بدء تشغيل الخدمة الخلفية المحسّنة")
|
| 61 |
+
return
|
| 62 |
+
except Exception as e:
|
| 63 |
+
logging.warning(f"⚠️ فشل في تشغيل الخدمة الخلفية المحسّنة: {e}")
|
| 64 |
+
logging.info("🔄 العودة إلى الطريقة التقليدية...")
|
| 65 |
+
|
| 66 |
+
# الطريقة التقليدية (fallback)
|
| 67 |
+
logging.info("🚀 تشغيل الخدمات بالطريقة التقليدية...")
|
| 68 |
+
|
| 69 |
+
# 1) تشغيل الخدمات الخلفيّة
|
| 70 |
+
procs = launch_services()
|
| 71 |
+
|
| 72 |
+
# 2) تهيئة نظام التنفيذ الموزع (ليتعرّف على هذا الجهاز كعقدة)
|
| 73 |
+
executor = DistributedExecutor("my_shared_secret_123")
|
| 74 |
+
executor.peer_registry.register_service("auto_node", 7520)
|
| 75 |
+
logging.info("🚀 العقدة auto_node مُسجّلة في الـRegistry على 7520")
|
| 76 |
+
|
| 77 |
+
# 3) حلقة إبقاء حيّة مع فحص العمليات
|
| 78 |
+
while True:
|
| 79 |
+
time.sleep(30)
|
| 80 |
+
for p, (script, name) in zip(procs, SERVICES):
|
| 81 |
+
if p.poll() is not None:
|
| 82 |
+
logging.warning(f"⚠️ الخدمة {name} توقفت بشكل غير متوقع… إعادة تشغيل")
|
| 83 |
+
new_p = subprocess.Popen([PY, script])
|
| 84 |
+
procs[procs.index(p)] = new_p
|
| 85 |
+
logging.info(f"✅ {name} أُعيد تشغيلها (PID={new_p.pid})")
|
| 86 |
+
|
| 87 |
+
except KeyboardInterrupt:
|
| 88 |
+
logging.info("📴 إيقاف الخدمات يدويًا")
|
| 89 |
+
except Exception as e:
|
| 90 |
+
logging.error(f"خطأ في التشغيل التلقائي: {e}")
|
| 91 |
+
finally:
|
| 92 |
+
# إيقاف العمليات بأمان
|
| 93 |
+
try:
|
| 94 |
+
for p in procs:
|
| 95 |
+
p.terminate()
|
| 96 |
+
try:
|
| 97 |
+
p.wait(timeout=5)
|
| 98 |
+
except subprocess.TimeoutExpired:
|
| 99 |
+
p.kill()
|
| 100 |
+
except:
|
| 101 |
+
pass
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
if __name__ == "main":
|
| 105 |
+
main()
|
system_check.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
#!/usr/bin/env python3
|
| 3 |
+
# system_check.py - فحص سريع لحالة النظام
|
| 4 |
+
|
| 5 |
+
import time
|
| 6 |
+
import requests
|
| 7 |
+
import psutil
|
| 8 |
+
import threading
|
| 9 |
+
from offload_lib import discover_peers
|
| 10 |
+
|
| 11 |
+
def check_local_system():
|
| 12 |
+
"""فحص النظام المحلي"""
|
| 13 |
+
print("🔍 فحص النظام المحلي...")
|
| 14 |
+
cpu = psutil.cpu_percent(interval=1)
|
| 15 |
+
memory = psutil.virtual_memory()
|
| 16 |
+
|
| 17 |
+
print(f"⚡ CPU: {cpu}%")
|
| 18 |
+
print(f"💾 الذاكرة: {memory.percent}%")
|
| 19 |
+
|
| 20 |
+
return cpu < 80 and memory.percent < 90
|
| 21 |
+
|
| 22 |
+
def check_server_running():
|
| 23 |
+
"""فحص إذا كان الخادم المحلي يعمل"""
|
| 24 |
+
print("🌐 فحص الخادم المحلي...")
|
| 25 |
+
try:
|
| 26 |
+
response = requests.get("http://localhost:7520/health", timeout=3)
|
| 27 |
+
if response.status_code == 200:
|
| 28 |
+
print("✅ الخادم المحلي يعمل بشكل صحيح")
|
| 29 |
+
return True
|
| 30 |
+
else:
|
| 31 |
+
print("⚠️ الخادم يستجيب لكن بخطأ")
|
| 32 |
+
return False
|
| 33 |
+
except:
|
| 34 |
+
print("❌ الخادم المحلي غير متاح")
|
| 35 |
+
return False
|
| 36 |
+
|
| 37 |
+
def check_peer_discovery():
|
| 38 |
+
"""فحص اكتشاف الأجهزة"""
|
| 39 |
+
print("🔍 فحص اكتشاف الأجهزة...")
|
| 40 |
+
try:
|
| 41 |
+
peers = discover_peers(timeout=2)
|
| 42 |
+
if peers:
|
| 43 |
+
print(f"✅ تم اكتشاف {len(peers)} جهاز:")
|
| 44 |
+
for peer in peers[:3]: # عرض أول 3 أجهزة فقط
|
| 45 |
+
print(f" 📱 {peer}")
|
| 46 |
+
return True
|
| 47 |
+
else:
|
| 48 |
+
print("⚠️ لم يتم اكتشاف أجهزة أخرى")
|
| 49 |
+
return False
|
| 50 |
+
except Exception as e:
|
| 51 |
+
print(f"❌ خطأ في اكتشاف الأجهزة: {e}")
|
| 52 |
+
return False
|
| 53 |
+
|
| 54 |
+
def quick_task_test():
|
| 55 |
+
"""اختبار مهمة سريعة"""
|
| 56 |
+
print("⚡ اختبار مهمة سريعة...")
|
| 57 |
+
try:
|
| 58 |
+
from offload_lib import matrix_multiply
|
| 59 |
+
start_time = time.time()
|
| 60 |
+
result = matrix_multiply(3) # مصفوفة صغيرة
|
| 61 |
+
duration = time.time() - start_time
|
| 62 |
+
|
| 63 |
+
print(f"✅ تمت المعالجة في {duration:.2f} ثانية")
|
| 64 |
+
return True
|
| 65 |
+
except Exception as e:
|
| 66 |
+
print(f"❌ فشل في تنفيذ المهمة: {e}")
|
| 67 |
+
return False
|
| 68 |
+
|
| 69 |
+
def main():
|
| 70 |
+
"""الفحص الرئيسي"""
|
| 71 |
+
print("🚀 فحص سريع لحالة النظام")
|
| 72 |
+
print("=" * 40)
|
| 73 |
+
|
| 74 |
+
checks = [
|
| 75 |
+
("النظام المحلي", check_local_system),
|
| 76 |
+
("الخادم المحلي", check_server_running),
|
| 77 |
+
("اكتشاف الأجهزة", check_peer_discovery),
|
| 78 |
+
("تنفيذ المهام", quick_task_test)
|
| 79 |
+
]
|
| 80 |
+
|
| 81 |
+
results = []
|
| 82 |
+
for name, check_func in checks:
|
| 83 |
+
print(f"\n📋 {name}:")
|
| 84 |
+
try:
|
| 85 |
+
result = check_func()
|
| 86 |
+
results.append((name, result))
|
| 87 |
+
except Exception as e:
|
| 88 |
+
print(f"❌ خطأ غير متوقع: {e}")
|
| 89 |
+
results.append((name, False))
|
| 90 |
+
|
| 91 |
+
print("\n" + "=" * 40)
|
| 92 |
+
print("📊 نتائج الفحص:")
|
| 93 |
+
|
| 94 |
+
all_good = True
|
| 95 |
+
for name, result in results:
|
| 96 |
+
status = "✅" if result else "❌"
|
| 97 |
+
print(f" {status} {name}")
|
| 98 |
+
if not result:
|
| 99 |
+
all_good = False
|
| 100 |
+
|
| 101 |
+
if all_good:
|
| 102 |
+
print("\n🎉 كل شيء يعمل بشكل ممتاز!")
|
| 103 |
+
print("💡 يمكنك الآن تشغيل: python test_distributed_system.py")
|
| 104 |
+
else:
|
| 105 |
+
print("\n⚠️ هناك مشاكل تحتاج إصلاح:")
|
| 106 |
+
print(" 1. تأكد من تشغيل: python server.py")
|
| 107 |
+
print(" 2. تحقق من الاتصال بالشبكة")
|
| 108 |
+
print(" 3. راجع ملفات السجل")
|
| 109 |
+
|
| 110 |
+
if __name__ == "__main__":
|
| 111 |
+
main()
|
system_tray.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
#!/usr/bin/env python3
|
| 3 |
+
"""
|
| 4 |
+
أيقونة شريط النظام للتحكم في الخدمة الخلفية
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import sys
|
| 8 |
+
import threading
|
| 9 |
+
import requests
|
| 10 |
+
import webbrowser
|
| 11 |
+
from pathlib import Path
|
| 12 |
+
|
| 13 |
+
try:
|
| 14 |
+
import pystray
|
| 15 |
+
from pystray import MenuItem as item
|
| 16 |
+
from PIL import Image, ImageDraw
|
| 17 |
+
TRAY_AVAILABLE = True
|
| 18 |
+
except ImportError:
|
| 19 |
+
TRAY_AVAILABLE = False
|
| 20 |
+
print("⚠️ pystray غير متوفر، تشغيل بدون أيقونة النظام")
|
| 21 |
+
|
| 22 |
+
class SystemTrayController:
|
| 23 |
+
def __init__(self):
|
| 24 |
+
self.base_url = "http://localhost:8888"
|
| 25 |
+
self.icon = None
|
| 26 |
+
|
| 27 |
+
def create_icon_image(self):
|
| 28 |
+
"""إنشاء صورة الأيقونة"""
|
| 29 |
+
# إنشاء صورة بسيطة 64x64
|
| 30 |
+
image = Image.new('RGB', (64, 64), color='blue')
|
| 31 |
+
draw = ImageDraw.Draw(image)
|
| 32 |
+
|
| 33 |
+
# رسم دائرة بسيطة
|
| 34 |
+
draw.ellipse([16, 16, 48, 48], fill='white')
|
| 35 |
+
draw.ellipse([20, 20, 44, 44], fill='blue')
|
| 36 |
+
|
| 37 |
+
return image
|
| 38 |
+
|
| 39 |
+
def get_service_status(self):
|
| 40 |
+
"""الحصول على حالة الخدمة"""
|
| 41 |
+
try:
|
| 42 |
+
response = requests.get(f"{self.base_url}/status", timeout=2)
|
| 43 |
+
return response.json()
|
| 44 |
+
except:
|
| 45 |
+
return None
|
| 46 |
+
|
| 47 |
+
def start_services(self, icon, item):
|
| 48 |
+
"""بدء تشغيل الخدمات"""
|
| 49 |
+
try:
|
| 50 |
+
requests.post(f"{self.base_url}/start", timeout=5)
|
| 51 |
+
self.update_menu()
|
| 52 |
+
except Exception as e:
|
| 53 |
+
print(f"فشل في بدء الخدمات: {e}")
|
| 54 |
+
|
| 55 |
+
def stop_services(self, icon, item):
|
| 56 |
+
"""إيقاف الخدمات"""
|
| 57 |
+
try:
|
| 58 |
+
requests.post(f"{self.base_url}/stop", timeout=5)
|
| 59 |
+
self.update_menu()
|
| 60 |
+
except Exception as e:
|
| 61 |
+
print(f"فشل في إيقاف الخدمات: {e}")
|
| 62 |
+
|
| 63 |
+
def show_ui(self, icon, item):
|
| 64 |
+
"""إظهار الواجهة التفاعلية"""
|
| 65 |
+
try:
|
| 66 |
+
requests.post(f"{self.base_url}/show-ui", timeout=5)
|
| 67 |
+
# فتح المتصفح
|
| 68 |
+
webbrowser.open('http://localhost:5173')
|
| 69 |
+
except Exception as e:
|
| 70 |
+
print(f"فشل في إظهار الواجهة: {e}")
|
| 71 |
+
|
| 72 |
+
def hide_ui(self, icon, item):
|
| 73 |
+
"""إخفاء الواجهة التفاعلية"""
|
| 74 |
+
try:
|
| 75 |
+
requests.post(f"{self.base_url}/hide-ui", timeout=5)
|
| 76 |
+
except Exception as e:
|
| 77 |
+
print(f"فشل في إخفاء الواجهة: {e}")
|
| 78 |
+
|
| 79 |
+
def open_dashboard(self, icon, item):
|
| 80 |
+
"""فتح لوحة التحكم"""
|
| 81 |
+
webbrowser.open('http://localhost:5173/dashboard')
|
| 82 |
+
|
| 83 |
+
def show_status(self, icon, item):
|
| 84 |
+
"""إظهار حالة النظام"""
|
| 85 |
+
status = self.get_service_status()
|
| 86 |
+
if status:
|
| 87 |
+
status_text = f"حالة النظام: {status['status']}\n"
|
| 88 |
+
status_text += f"الخدمات النشطة: {len([s for s in status['services'].values() if s == 'running'])}\n"
|
| 89 |
+
status_text += f"وقت التشغيل: {int(status['uptime'])} ثانية"
|
| 90 |
+
print(status_text)
|
| 91 |
+
else:
|
| 92 |
+
print("❌ لا يمكن الوصول للخدمة")
|
| 93 |
+
|
| 94 |
+
def quit_app(self, icon, item):
|
| 95 |
+
"""إنهاء التطبيق"""
|
| 96 |
+
self.stop_services(icon, item)
|
| 97 |
+
icon.stop()
|
| 98 |
+
|
| 99 |
+
def update_menu(self):
|
| 100 |
+
"""تحديث قائمة الأيقونة"""
|
| 101 |
+
if self.icon:
|
| 102 |
+
status = self.get_service_status()
|
| 103 |
+
is_running = status and status['status'] == 'running'
|
| 104 |
+
|
| 105 |
+
menu = pystray.Menu(
|
| 106 |
+
item('حالة النظام', self.show_status),
|
| 107 |
+
item('---'),
|
| 108 |
+
item('إظهار الواجهة', self.show_ui),
|
| 109 |
+
item('إخفاء الواجهة', self.hide_ui),
|
| 110 |
+
item('لوحة التحكم', self.open_dashboard),
|
| 111 |
+
item('---'),
|
| 112 |
+
item('بدء الخدمات', self.start_services, enabled=not is_running),
|
| 113 |
+
item('إيقاف الخدمات', self.stop_services, enabled=is_running),
|
| 114 |
+
item('---'),
|
| 115 |
+
item('إنهاء', self.quit_app)
|
| 116 |
+
)
|
| 117 |
+
|
| 118 |
+
self.icon.menu = menu
|
| 119 |
+
|
| 120 |
+
def create_menu(self):
|
| 121 |
+
"""إنشاء قائمة الأيقونة"""
|
| 122 |
+
return pystray.Menu(
|
| 123 |
+
item('حالة النظام', self.show_status),
|
| 124 |
+
item('---'),
|
| 125 |
+
item('إظهار الواجهة', self.show_ui),
|
| 126 |
+
item('إخفاء الواجهة', self.hide_ui),
|
| 127 |
+
item('لوحة التحكم', self.open_dashboard),
|
| 128 |
+
item('---'),
|
| 129 |
+
item('بدء الخدمات', self.start_services),
|
| 130 |
+
item('إيقاف الخدمات', self.stop_services),
|
| 131 |
+
item('---'),
|
| 132 |
+
item('إنهاء', self.quit_app)
|
| 133 |
+
)
|
| 134 |
+
|
| 135 |
+
def run(self):
|
| 136 |
+
"""تشغيل أيقونة شريط النظام"""
|
| 137 |
+
if not TRAY_AVAILABLE:
|
| 138 |
+
print("❌ مكتبة pystray غير متوفرة")
|
| 139 |
+
return
|
| 140 |
+
|
| 141 |
+
# إنشاء الأيقونة
|
| 142 |
+
image = self.create_icon_image()
|
| 143 |
+
menu = self.create_menu()
|
| 144 |
+
|
| 145 |
+
self.icon = pystray.Icon(
|
| 146 |
+
"نظام توزيع المهام",
|
| 147 |
+
image,
|
| 148 |
+
menu=menu
|
| 149 |
+
)
|
| 150 |
+
|
| 151 |
+
# تحديث دوري للقائمة
|
| 152 |
+
def update_loop():
|
| 153 |
+
import time
|
| 154 |
+
while True:
|
| 155 |
+
time.sleep(5)
|
| 156 |
+
if self.icon and hasattr(self.icon, '_running') and self.icon._running:
|
| 157 |
+
self.update_menu()
|
| 158 |
+
else:
|
| 159 |
+
break
|
| 160 |
+
|
| 161 |
+
update_thread = threading.Thread(target=update_loop, daemon=True)
|
| 162 |
+
update_thread.start()
|
| 163 |
+
|
| 164 |
+
print("🖱️ تشغيل أيقونة شريط النظام...")
|
| 165 |
+
self.icon.run()
|
| 166 |
+
|
| 167 |
+
def main():
|
| 168 |
+
controller = SystemTrayController()
|
| 169 |
+
controller.run()
|
| 170 |
+
|
| 171 |
+
if __name__ == "__main__":
|
| 172 |
+
main()
|
tailwind.config.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { Config } from "tailwindcss";
|
| 2 |
+
|
| 3 |
+
export default {
|
| 4 |
+
darkMode: ["class"],
|
| 5 |
+
content: ["./client/index.html", "./client/src/**/*.{js,jsx,ts,tsx}"],
|
| 6 |
+
theme: {
|
| 7 |
+
extend: {
|
| 8 |
+
borderRadius: {
|
| 9 |
+
lg: "var(--radius)",
|
| 10 |
+
md: "calc(var(--radius) - 2px)",
|
| 11 |
+
sm: "calc(var(--radius) - 4px)",
|
| 12 |
+
},
|
| 13 |
+
colors: {
|
| 14 |
+
background: "var(--background)",
|
| 15 |
+
foreground: "var(--foreground)",
|
| 16 |
+
card: {
|
| 17 |
+
DEFAULT: "var(--card)",
|
| 18 |
+
foreground: "var(--card-foreground)",
|
| 19 |
+
},
|
| 20 |
+
popover: {
|
| 21 |
+
DEFAULT: "var(--popover)",
|
| 22 |
+
foreground: "var(--popover-foreground)",
|
| 23 |
+
},
|
| 24 |
+
primary: {
|
| 25 |
+
DEFAULT: "var(--primary)",
|
| 26 |
+
foreground: "var(--primary-foreground)",
|
| 27 |
+
},
|
| 28 |
+
secondary: {
|
| 29 |
+
DEFAULT: "var(--secondary)",
|
| 30 |
+
foreground: "var(--secondary-foreground)",
|
| 31 |
+
},
|
| 32 |
+
muted: {
|
| 33 |
+
DEFAULT: "var(--muted)",
|
| 34 |
+
foreground: "var(--muted-foreground)",
|
| 35 |
+
},
|
| 36 |
+
accent: {
|
| 37 |
+
DEFAULT: "var(--accent)",
|
| 38 |
+
foreground: "var(--accent-foreground)",
|
| 39 |
+
},
|
| 40 |
+
destructive: {
|
| 41 |
+
DEFAULT: "var(--destructive)",
|
| 42 |
+
foreground: "var(--destructive-foreground)",
|
| 43 |
+
},
|
| 44 |
+
border: "var(--border)",
|
| 45 |
+
input: "var(--input)",
|
| 46 |
+
ring: "var(--ring)",
|
| 47 |
+
chart: {
|
| 48 |
+
"1": "var(--chart-1)",
|
| 49 |
+
"2": "var(--chart-2)",
|
| 50 |
+
"3": "var(--chart-3)",
|
| 51 |
+
"4": "var(--chart-4)",
|
| 52 |
+
"5": "var(--chart-5)",
|
| 53 |
+
},
|
| 54 |
+
sidebar: {
|
| 55 |
+
DEFAULT: "var(--sidebar-background)",
|
| 56 |
+
foreground: "var(--sidebar-foreground)",
|
| 57 |
+
primary: "var(--sidebar-primary)",
|
| 58 |
+
"primary-foreground": "var(--sidebar-primary-foreground)",
|
| 59 |
+
accent: "var(--sidebar-accent)",
|
| 60 |
+
"accent-foreground": "var(--sidebar-accent-foreground)",
|
| 61 |
+
border: "var(--sidebar-border)",
|
| 62 |
+
ring: "var(--sidebar-ring)",
|
| 63 |
+
},
|
| 64 |
+
},
|
| 65 |
+
keyframes: {
|
| 66 |
+
"accordion-down": {
|
| 67 |
+
from: {
|
| 68 |
+
height: "0",
|
| 69 |
+
},
|
| 70 |
+
to: {
|
| 71 |
+
height: "var(--radix-accordion-content-height)",
|
| 72 |
+
},
|
| 73 |
+
},
|
| 74 |
+
"accordion-up": {
|
| 75 |
+
from: {
|
| 76 |
+
height: "var(--radix-accordion-content-height)",
|
| 77 |
+
},
|
| 78 |
+
to: {
|
| 79 |
+
height: "0",
|
| 80 |
+
},
|
| 81 |
+
},
|
| 82 |
+
},
|
| 83 |
+
animation: {
|
| 84 |
+
"accordion-down": "accordion-down 0.2s ease-out",
|
| 85 |
+
"accordion-up": "accordion-up 0.2s ease-out",
|
| 86 |
+
},
|
| 87 |
+
},
|
| 88 |
+
},
|
| 89 |
+
plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")],
|
| 90 |
+
} satisfies Config;
|