File size: 12,352 Bytes
ef36b58
2bc171c
58f0060
5587234
58f0060
117be98
5587234
117be98
0de68dd
5587234
58f0060
840aca1
58f0060
856d037
117be98
 
 
20bf6fe
117be98
20bf6fe
5a6c071
0de68dd
5a6c071
d962918
117be98
 
 
 
f1c93d1
117be98
 
 
f1c93d1
117be98
0de68dd
 
f1c93d1
0de68dd
 
117be98
 
 
 
8a9570d
 
 
0de68dd
117be98
 
 
 
2bc171c
0de68dd
 
117be98
0de68dd
b598ef9
0de68dd
 
 
 
8a9570d
 
0de68dd
 
 
 
 
 
 
ef36b58
 
0de68dd
 
117be98
 
 
f1c93d1
117be98
 
 
ef36b58
 
 
117be98
840aca1
 
117be98
ef36b58
117be98
ef36b58
 
117be98
 
ef36b58
0de68dd
 
117be98
 
 
 
 
 
 
 
 
 
58f0060
117be98
 
2bc171c
0de68dd
2bc171c
5a6c071
46fdcef
117be98
30e282e
117be98
 
5a6c071
ef36b58
8a9570d
0de68dd
 
 
 
 
 
 
ef36b58
8a9570d
0de68dd
 
 
ef36b58
0de68dd
8a9570d
a933af2
0de68dd
ef36b58
 
 
0de68dd
6709cb4
ef36b58
8a9570d
51ac048
0de68dd
 
51ac048
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
840aca1
 
51ac048
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
840aca1
6709cb4
840aca1
6709cb4
51ac048
6709cb4
0de68dd
ef36b58
0de68dd
8a9570d
 
0de68dd
 
840aca1
0de68dd
 
 
 
 
5a6c071
0de68dd
 
 
 
 
 
ef36b58
cb510da
117be98
ef36b58
d962918
cb510da
8a9570d
5587234
117be98
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# app.py - الكود النهائي المعدل ليتوافق مع بنية measurements.json الحقيقية

import os
import json
import gradio as gr
import base64
from PIL import Image
import io
import requests

# ==============================================================================
#           1. إعدادات الـ API
# ==============================================================================
API_URL_BASE = "https://BaseerAI-baseer-server.hf.space"
API_START_SESSION_URL = f"{API_URL_BASE}/start_session"
API_RUN_STEP_URL = f"{API_URL_BASE}/run_step"
API_END_SESSION_URL = f"{API_URL_BASE}/end_session"

EXAMPLES_DIR = "examples" 

# ==============================================================================
#           2. الدوال المساعدة والدوال التي تتصل بالـ API
# ==============================================================================

def image_to_base64(pil_image):
    buffered = io.BytesIO()
    pil_image.save(buffered, format="JPEG")
    return base64.b64encode(buffered.getvalue()).decode('utf-8')

def base64_to_image(b64_string):
    img_data = base64.b64decode(b64_string)
    return Image.open(io.BytesIO(img_data))

def start_new_session():
    """يبدأ جلسة جديدة على الخادم ويعيد معرف الجلسة وأمر إظهار الواجهة."""
    print("Requesting to start a new session...")
    try:
        response = requests.post(API_START_SESSION_URL, timeout=30)
        response.raise_for_status()
        session_data = response.json()
        session_id = session_data.get("session_id")
        if not session_id:
            raise gr.Error("لم يتمكن الخادم من بدء جلسة جديدة.")
        
        status_message = f"✅ تم بدء جلسة جديدة بنجاح. أنت الآن متصل بالخادم."
        print(f"Session started successfully: {session_id}")
        return session_id, status_message, gr.update(visible=True)
    except requests.exceptions.RequestException as e:
        error_message = f"فشل الاتصال بخادم الـ API: {e}"
        print(error_message)
        raise gr.Error(error_message)

def handle_end_session(session_id):
    """ينهي الجلسة الحالية على الخادم."""
    if not session_id:
        return "لا توجد جلسة نشطة لإنهائها.", None, gr.update(visible=False)
    
    print(f"Requesting to end session: {session_id}")
    try:
        response = requests.post(f"{API_END_SESSION_URL}?session_id={session_id}", timeout=30)
        response.raise_for_status()
        
        status_message = f"🔌 تم إنهاء الجلسة بنجاح. انقطع الاتصال بالخادم."
        print(status_message)
        return status_message, None, gr.update(visible=False)
    except requests.exceptions.RequestException as e:
        error_message = f"حدث خطأ أثناء إنهاء الجلسة: {e}"
        print(error_message)
        raise gr.Error(error_message)

# --- [تم تعديل هذه الدالة] ---
def run_single_frame_via_api(session_id, rgb_image_path, measurements_path):
    if not session_id:
        raise gr.Error("لا توجد جلسة نشطة. يرجى بدء جلسة جديدة أولاً.")
    if not (rgb_image_path and measurements_path):
        raise gr.Error("الرجاء توفير الصورة الأمامية وملف القياسات على الأقل.")
    try:
        rgb_image_pil = Image.open(rgb_image_path).convert("RGB")
        with open(measurements_path, 'r') as f:
            measurements_dict = json.load(f)
        image_b64 = image_to_base64(rgb_image_pil)
        
        # --- [التعديل الرئيسي هنا] ---
        # بناء الحمولة (Payload) بناءً على بنية الملف الحقيقية
        payload = {
            "session_id": session_id,
            "image_b64": image_b64,
            "measurements": {
                "pos_global": measurements_dict.get('pos_global', [0.0, 0.0]),
                "theta": measurements_dict.get('theta', 0.0),
                "speed": measurements_dict.get('speed', 0.0),
                "target_point": measurements_dict.get('target_point', [0.0, 100.0])
            }
        }
        
        print(f"Sending run_step request for session: {session_id}")
        response = requests.post(API_RUN_STEP_URL, json=payload, timeout=60)
        response.raise_for_status()
        api_result = response.json()
        dashboard_b64 = api_result.get("dashboard_b64")
        control_commands = api_result.get("control_commands", {})
        if not dashboard_b64:
            raise gr.Error("لم يتم إرجاع صورة لوحة التحكم من الـ API.")
        output_image = base64_to_image(dashboard_b64)
        return output_image, control_commands
    except requests.exceptions.RequestException as e:
        raise gr.Error(f"حدث خطأ أثناء الاتصال بالـ API: {e}")
    except Exception as e:
        raise gr.Error(f"حدث خطأ غير متوقع: {e}")

# ==============================================================================
#           4. تعريف واجهة Gradio
# ==============================================================================

with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"), css=".gradio-container {max-width: 100% !important;}") as demo:
    session_id_state = gr.State(value=None)
    
    gr.Markdown("# 🚗 واجهة التحكم لنظام Baseer للقيادة الذاتية")
    gr.Markdown("هذه الواجهة تعمل كـ **عميل (Client)** يتصل بـ **[Baseer Self-Driving API](https://huggingface.co/spaces/BaseerAI/baseer-server)** لمعالجة البيانات.")

    gr.Markdown("---")

    with gr.Group():
        gr.Markdown("## ⚙️ التحكم بالجلسة")
        with gr.Row():
            start_session_button = gr.Button("🟢 بدء جلسة جديدة", variant="primary")
            end_session_button = gr.Button("🔴 إنهاء الجلسة الحالية")
        status_textbox = gr.Textbox(label="حالة الجلسة", interactive=False, value="في انتظار بدء الجلسة...")

    gr.Markdown("")

    with gr.Group(visible=False) as main_controls_group:
        with gr.Row():
            # -- العمود الأيسر: الإعدادات والمدخلات --
            with gr.Column(scale=2):
                with gr.Group():
                    gr.Markdown("### 🗂️ ارفع ملفات السيناريو")
                    api_rgb_image_path = gr.Image(type="filepath", label="صورة الكاميرا الأمامية (RGB)")
                    api_measurements_path = gr.File(label="ملف القياسات (JSON)", type="filepath")
                    
                    # --- [تم حذف gr.JSON من هنا] ---
                    
                    api_run_button = gr.Button("🚀 أرسل البيانات للمعالجة", variant="primary")

                gr.Markdown("")
                
# ... (داخل with gr.Group(visible=False) as main_controls_group:)
                with gr.Group():
                    gr.Markdown("### ✨ أمثلة جاهزة")
                    
                    # التحقق من وجود مجلد الأمثلة لتجنب الأخطاء
                    if os.path.isdir(EXAMPLES_DIR) and \
                       os.path.exists(os.path.join(EXAMPLES_DIR, "sample1", "measurements.json")) and \
                       os.path.exists(os.path.join(EXAMPLES_DIR, "sample2", "measurements.json")):
                        
                        # قراءة بيانات المثال الأول
                        with open(os.path.join(EXAMPLES_DIR, "sample1", "measurements.json"), 'r') as f:
                            data1 = json.load(f)
                        
                        # قراءة بيانات المثال الثاني
                        with open(os.path.join(EXAMPLES_DIR, "sample2", "measurements.json"), 'r') as f:
                            data2 = json.load(f)
                        
                        # --- [تعديل] 1: إنشاء مكونات جديدة لكل البيانات المستخدمة ---
                        # سيتم استخدام عناوينها كعناوين للأعمدة في الجدول
                        with gr.Column(visible=False):
                            example_speed = gr.Textbox(label="السرعة (m/s)")
                            example_theta = gr.Textbox(label="الاتجاه (Theta)")
                            example_pos = gr.Textbox(label="الموقع العام")
                            example_target = gr.Textbox(label="النقطة المستهدفة")

                        gr.Examples(
                            examples=[
                                # --- [تعديل] 2: بناء البيانات للأعمدة الجديدة ---
                                [
                                    # المدخلات الأساسية
                                    os.path.join(EXAMPLES_DIR, "sample1", "rgb.jpg"), 
                                    os.path.join(EXAMPLES_DIR, "sample1", "measurements.json"),
                                    # البيانات للعرض فقط
                                    f"{data1.get('speed', 0.0):.2f}",
                                    f"{data1.get('theta', 0.0):.2f}",
                                    str(data1.get('pos_global', 'N/A')),
                                    str(data1.get('target_point', 'N/A'))
                                ],
                                [
                                    # المدخلات الأساسية
                                    os.path.join(EXAMPLES_DIR, "sample2", "rgb.jpg"),
                                    os.path.join(EXAMPLES_DIR, "sample2", "measurements.json"),
                                    # البيانات للعرض فقط
                                    f"{data2.get('speed', 0.0):.2f}",
                                    f"{data2.get('theta', 0.0):.2f}",
                                    str(data2.get('pos_global', 'N/A')),
                                    str(data2.get('target_point', 'N/A'))
                                ]
                            ],
                            # --- [تعديل] 3: تحديث المدخلات لتشمل كل الأعمدة ---
                            inputs=[
                                api_rgb_image_path,
                                api_measurements_path,
                                example_speed,
                                example_theta,
                                example_pos,
                                example_target
                            ],
                            label="اختر سيناريو اختبار",
                        )
                    else:
                        gr.Markdown("لم يتم العثور على مجلد الأمثلة (`examples`) أو محتوياته.")

            # -- العمود الأيمن: المخرجات --
            with gr.Column(scale=3):
                with gr.Group():
                    gr.Markdown("### 📊 النتائج من الخادم")
                    api_output_image = gr.Image(label="لوحة التحكم المرئية (من الـ API)", type="pil", interactive=False, height=600)
                    api_control_json = gr.JSON(label="أوامر التحكم (من الـ API)")

    # --- ربط منطق الواجهة ---
    start_session_button.click(
        fn=start_new_session,
        inputs=None,
        outputs=[session_id_state, status_textbox, main_controls_group]
    )
    
    end_session_button.click(
        fn=handle_end_session,
        inputs=[session_id_state],
        outputs=[status_textbox, session_id_state, main_controls_group]
    )

    # --- [تم تعديل هذا الجزء] ---
    api_run_button.click(
        fn=run_single_frame_via_api,
        inputs=[session_id_state, api_rgb_image_path, api_measurements_path], # تم حذف target_point_list
        outputs=[api_output_image, api_control_json],
    )

if __name__ == "__main__":
    demo.queue().launch(debug=True)