> For the complete documentation index, see [llms.txt](https://docs.augelab.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.augelab.com/arabic/almyzat-alreysyh/headless/scripting-reference.md).

# مرجع السكربتات

تُوثّق هذه الصفحة واجهة برمجة التطبيقات العامة (public headless scripting API) المعروضة عبر حزمة Python المسماة `studio`.

تركز الوثائق على الرموز العامة التالية:

* `StudioScenario`
* `install_logging_event`
* `enable_logging_stdout`
* `disable_logging_stdout`

إذا لم تقُم بتشغيل سيناريو في الوضع headless مسبقًا، فاقرأ قسم Headless أولاً ثم عد إلى هنا للحصول على تفاصيل الـ API.

***

## الاستيراد

كل واجهات الـ API الموثقة هنا يتم تصديرها من الحزمة العلوية `studio`:

```python
from studio import (
	StudioScenario,
	install_logging_event,
	enable_logging_stdout,
	disable_logging_stdout,
)
```

***

## تعريفات الأنواع (Type Stubs)

السطح العام للعمل في الوضع headless يمكن إيجاده كالتالي:

```python
from __future__ import annotations

from typing import Any, Callable, Optional


def enable_logging_stdout() -> None: ...
def disable_logging_stdout() -> None: ...
def install_logging_event(event: Callable[[str], None]) -> None: ...


class StudioScenario:
	def __init__(self, *, verification_code: str = "") -> None: ...

	def load_scenario(self, path: str) -> Optional[StudioScenario]: ...
	def load_scenario_raw(self, content: str) -> bool: ...
	def save_scenario(self, path: str) -> bool: ...
	def clear_scenario(self) -> bool: ...
	def disable_load_errors(self) -> StudioScenario: ...
	def load_custom_nodes(self, path: str) -> bool: ...

	def get_ports(
		self,
		input_op_title: str = "Subsystem In",
		output_op_title: str = "Subsystem Out",
	) -> dict[str, list[str]]: ...
	def get_name(self) -> str: ...

	def run(self, args: tuple[Any, ...] = ()) -> tuple[list[Any], ...]: ...
	def run_server(
		self,
		inputs: tuple[Any, ...] = (),
		host: str = "127.0.0.1",
		port: int = 8080,
		display_controls: bool = False,
		header: str = "Scenario Server",
	) -> None: ...
	def cleanup(self) -> bool: ...

	def enable_logging_stdout(self) -> StudioScenario: ...
	def disable_logging_stdout(self) -> StudioScenario: ...
	def install_logging_hook(self, hook: Callable[[str, int], None], level: int = 0) -> StudioScenario: ...

	def installStartEvent(self, event: Callable[..., None]) -> StudioScenario: ...
	def installEndEvent(self, event: Callable[..., None]) -> StudioScenario: ...
	def installStepStartEvent(self, event: Callable[..., None]) -> StudioScenario: ...
	def installStepEndEvent(self, event: Callable[..., None]) -> StudioScenario: ...
```

***

## StudioScenario

`StudioScenario` هو نقطة الدخول الرئيسية لعمليات تحميل وتشغيل ملف `.pmod` بدون واجهة سطح المكتب (desktop UI).

### المُنشئ

```python
scenario = StudioScenario(verification_code="...")
```

#### `verification_code`

تُستخدم هذه القيمة لتكوين الترخيص (licensing) للتنفيذ في الوضع headless.

الأنماط الشائعة:

* جهاز محلي / خادم: مرّر سلسلة رمز التحقق (verification code).
* Docker / CI: قم بتركيب ملف الترخيص داخل الحاوية ومرّر مسار الملف (path) كـ `verification_code`.

{% hint style="warning" %}
بالنسبة للبيئات غير التفاعلية (مثل Docker/CI/الخدمات)، مرّر دائمًا `verification_code` بشكل صريح.
{% endhint %}

***

### دورة الحياة النموذجية

في معظم السكربتات ستقوم بالخطوات التالية:

1. إنشاء السيناريو (`StudioScenario(...)`)
2. تحميل ملف `.pmod` (`load_scenario(...)`)
3. تشغيله (`run(...)`)
4. التنظيف (`cleanup()`)

```python
import os

from studio import StudioScenario, enable_logging_stdout


def main() -> None:
	# اختياري: يعرض سجلات التشغيل إلى stdout (مفيد عند التصحيح).
	enable_logging_stdout()

	verification_code = os.environ.get("AUGELAB_VERIFICATION_CODE", "")
	scenario = StudioScenario(verification_code=verification_code)

	try:
		loaded = scenario.load_scenario("your_scenario.pmod")
		if loaded is None:
			raise FileNotFoundError("Could not load .pmod file")

		# If your scenario has N input ports, you must pass exactly N args.
		result = scenario.run(args=())
		print("Scenario result:", result)
	finally:
		scenario.cleanup()


if __name__ == "__main__":
	main()
```

{% hint style="info" %}
`cleanup()` مهم في العمليات طويلة التشغيل (خدمات، مهام دفعية) لتحرير الذاكرة وإعادة ضبط حالة وقت التشغيل.
{% endhint %}

***

### تحميل ملف `.pmod`

```python
scenario.load_scenario("path/to/scenario.pmod")
```

* تُرجع `StudioScenario` عند النجاح.
* تُرجع `None` إذا كان المسار غير موجود.

إذا كان السيناريو يشير إلى موارد خارجية، فاحفظ نفس هيكل المجلدات المستخدم على سطح المكتب (راجع قسم “Transferring .pmod files”).

***

### تشغيل سيناريو

```python
outputs = scenario.run(args=(... ,))
```

يتحقق وقت التشغيل من أن عدد الوسائط (arguments) يطابق عدد مداخل السيناريو.

إذا كان ملف `.pmod` يحتوي على:

* 0 مدخلات: استدعِ `scenario.run()` أو `scenario.run(())`
* مدخل واحد: استدعِ `scenario.run((value,))`
* مدخلان: استدعِ `scenario.run((value1, value2))`

{% hint style="warning" %}
يجب أن يكون `args` عبارة عن tuple. للمُدخل الواحد، تذكّر الفاصلة في النهاية: `(value,)`.
{% endhint %}

***

## مزيد من أساليب `StudioScenario`

يغطي هذا القسم طرقًا عامة إضافية يمكن استخدامها للأتمتة والتصحيح.

### التحميل بدون الفشل عند عدم وجود موارد

إذا كنت تنقل سيناريوهات بين آلات/حاويات وبعض الموارد قد تكون غير متاحة، يمكنك التحميل بتسامح أكبر:

```python
from studio import StudioScenario

scenario = StudioScenario(verification_code="YOUR_CODE")
scenario.disable_load_errors()
scenario.load_scenario("scenario.pmod")
```

### التحميل / الحفظ

```python
scenario.load_scenario_raw(content="...")
scenario.save_scenario("out.pmod")
scenario.clear_scenario()
```

### العقد المخصصة (Custom nodes)

تحميل وحدات Python للعقد المخصصة من مجلد:

```python
scenario.load_custom_nodes("path/to/custom_nodes")
```

{% hint style="warning" %}
هذا يستورد ملفات Python ديناميكيًا. قم بتحميل كود موثوق فقط.
{% endhint %}

### اسم السيناريو

```python
name = scenario.get_name()
```

### المنافذ (مدخلات/مخرجات السيناريو)

يمكنك استعلام أسماء منافذ الإدخال/الإخراج:

```python
ports = scenario.get_ports()
print("inputs:", ports["inputs"])
print("outputs:", ports["outputs"])
```

### تشغيل خادم ويب (اختياري)

إذا رغبت في كشف مشغل السيناريو عبر HTTP:

```python
scenario.run_server(host="0.0.0.0", port=8080)
```

### الأحداث (Events)

تثبيت ردود نداء (callbacks) حول دورة حياة تنفيذ السيناريو:

```python
def on_start() -> None:
	print("Scenario started")


scenario.installStartEvent(on_start)
```

***

## مساعدات السجل (Logging Helpers)

يمتلك AugeLab Studio نظام سجل وقت تشغيل يُستخدم بواسطة البلوكات والسيناريوهات. في الوضع headless عادةً تريد أحد الخيارات التالية:

* طباعة السجلات إلى stdout أثناء التطوير
* إعادة توجيه السجلات إلى نظام التسجيل الخاص بك

تلك المساعدات عامة (global) وتؤثر على مسجل وقت التشغيل المستخدم بواسطة سيناريوهاتك في الوضع headless.

***

### enable\_logging\_stdout()

يفعّل طباعة سجلات وقت التشغيل بشكل "موجَّه" إلى stdout.

استخدم هذا عندما تريد رؤية سجلات البلوكات/السيناريو في الكونسول:

```python
from studio import enable_logging_stdout

enable_logging_stdout()
```

***

### disable\_logging\_stdout()

يعطّل طباعة سجلات وقت التشغيل إلى stdout.

مفيد عندما:

* تستخدم `install_logging_event(...)` وتريد تجنب تكرار السجلات
* تريد إخراجًا نظيفًا (فقط مخرجات `print(...)` أو سجلك الخاص)

```python
from studio import disable_logging_stdout

disable_logging_stdout()
```

***

### install\_logging\_event(event: Callable\[\[str], None])

يثبت رد نداء يستقبل رسائل السجل في وقت التشغيل.

هذا أسهل طريقة لدمج سجلات AugeLab في إطار تسجيل الخاص بك.

```python
import logging

from studio import install_logging_event, disable_logging_stdout

logger = logging.getLogger("augelab")


def forward_to_python_logging(msg: str) -> None:
	logger.info(msg)


disable_logging_stdout()
install_logging_event(forward_to_python_logging)
```

### StudioScenario.install\_logging\_hook(hook, level=0)

إذا فضّلت تثبيت hook على مستوى السيناريو مع تصفية بمستوى محدد، استخدم:

```python
def hook(msg: str, level: int) -> None:
	print(level, msg)


scenario.install_logging_hook(hook, level=20)
```

{% hint style="warning" %}
اجعل رد النداء سريعًا. إذا كنت بحاجة لإجراء عمل ثقيل (I/O، شبكة)، فكّر في تخزين الرسائل مؤقتًا ومعالجتها في خيط/عملية أخرى.
{% endhint %}

***

## أنماط موصى بها

### 1) متغير بيئي لترخيص التشغيل

بالنسبة للسكربتات التي تعمل في بيئات مختلفة (محلي مقابل CI مقابل Docker)، تمرير `verification_code` عبر متغير بيئي يجعل الشيفرة قابلة للنقل:

```python
import os
from studio import StudioScenario

scenario = StudioScenario(verification_code=os.environ["AUGELAB_VERIFICATION_CODE"])
```

### 2) التنظيف دائمًا باستخدام `try/finally`

```python
from studio import StudioScenario

scenario = StudioScenario(verification_code="YOUR_CODE")
try:
	scenario.load_scenario("scenario.pmod")
	scenario.run(())
finally:
	scenario.cleanup()
```
