# ارتباط سرویس‌ها با Plant و گیاهان

این سند توضیح می‌دهد که در پروژه، داده‌ی گیاه از کجا می‌آید، چطور در `farm_data` نگه‌داری می‌شود، چگونه به سرویس‌های `crop_simulation` می‌رسد و در نهایت چطور در `location_data` برای پیشنهاد گیاه هر کلاستر استفاده می‌شود.

## نمای کلی

در این پروژه سه لایه اصلی برای کار با گیاه وجود دارد:

1. `plant`
2. `farm_data`
3. `crop_simulation` و `location_data`

نقش هر لایه:

- `plant`: مرجع canonical نام گیاه و aliasها است.
- `farm_data`: نسخه snapshot شده‌ی گیاهان Backend و assignment هر مزرعه به گیاه‌ها را نگه می‌دارد.
- `crop_simulation`: از گیاه انتخاب‌شده برای ساخت ورودی شبیه‌سازی استفاده می‌کند.
- `location_data`: داده‌ی کلاسترهای KMeans را با گیاه‌های مزرعه ترکیب می‌کند و پیشنهاد گیاه می‌سازد.

## 1) لایه plant

اپ `plant` مرجع اصلی برای resolve کردن نام گیاه است.

فایل مهم:

- `plant/apps.py`

تابع مهم:

- `resolve_plant_name`

رفتار این تابع:

- نام ورودی را می‌گیرد.
- اگر همان نام در جدول `plant.Plant` وجود داشته باشد، همان را برمی‌گرداند.
- اگر alias برای آن تعریف شده باشد، alias را به نام canonical تبدیل می‌کند.
- اگر از نظر نرمال‌سازی متنی با یک گیاه match شود، همان نام canonical را برمی‌گرداند.

در نتیجه:

- ورودی‌هایی مثل `plant_name`، `crop` یا `crop_name` قبل از ورود به شبیه‌سازی، به نام استاندارد تبدیل می‌شوند.

## 2) لایه farm_data

اپ `farm_data` مدل read-model مربوط به گیاهان هر مزرعه را نگه می‌دارد.

مدل‌های اصلی:

- `farm_data.models.PlantCatalogSnapshot`
- `farm_data.models.FarmPlantAssignment`
- `farm_data.models.SensorData`

### PlantCatalogSnapshot

این مدل کپی محلی و خواندنی از کاتالوگ گیاه Backend است.

اطلاعاتی که در آن نگه‌داری می‌شود:

- نام گیاه
- توضیحات
- `growth_profile`
- `irrigation_profile`
- `health_profile`
- فصل کاشت، زمان برداشت، فاصله کاشت، کود و ...

این مدل منبع اصلی هوش مصنوعی برای خواندن پروفایل گیاه است، نه relation قدیمی `SensorData.plants`.

### FarmPlantAssignment

این مدل مشخص می‌کند هر مزرعه چه گیاه‌هایی دارد.

فیلدهای مهم:

- `farm`
- `plant`
- `position`
- `stage`
- `metadata`

یعنی هر مزرعه می‌تواند چند گیاه داشته باشد و ترتیب و مرحله رشد هرکدام هم ثبت می‌شود.

### توابع مهم در farm_data/services.py

فایل مهم:

- `farm_data/services.py`

توابع کلیدی:

- `sync_plant_catalog_from_backend`
- `assign_farm_plants_from_backend_ids`
- `get_farm_plant_assignments`
- `get_farm_plant_snapshots`
- `get_primary_plant_snapshot`
- `get_farm_plant_snapshot_by_name`
- `clone_snapshot_as_runtime_plant`
- `get_runtime_plant_for_farm`
- `list_runtime_plants_for_farm`

### جریان داده در farm_data

1. کاتالوگ گیاه از Backend خوانده می‌شود و داخل `PlantCatalogSnapshot` ذخیره می‌شود.
2. گیاه‌های انتخاب‌شده‌ی هر مزرعه با `FarmPlantAssignment` ثبت می‌شوند.
3. اگر سرویس شبیه‌سازی یک `plant_name` مشخص بگیرد، همان گیاه از assignmentها پیدا می‌شود.
4. اگر `plant_name` ارسال نشود، گیاه اول مزرعه به عنوان پیش‌فرض انتخاب می‌شود.

### Runtime Plant

تابع `get_runtime_plant_for_farm` یک snapshot را به یک object سبک runtime تبدیل می‌کند تا downstream serviceها بدون وابستگی مستقیم به مدل DB از آن استفاده کنند.

این object شامل فیلدهایی مثل:

- `name`
- `growth_profile`
- `irrigation_profile`
- `health_profile`
- `planting_season`
- `harvest_time`

است.

## 3) ورود گیاه به crop_simulation

فایل‌های مهم:

- `crop_simulation/services.py`
- `crop_simulation/growth_simulation.py`
- `crop_simulation/harvest_prediction.py`
- `crop_simulation/yield_prediction.py`

### build_simulation_payload_from_farm

مهم‌ترین نقطه اتصال بین `farm_data` و `crop_simulation` این تابع است:

- `crop_simulation.services.build_simulation_payload_from_farm`

این تابع:

1. مزرعه را با `get_canonical_farm_record` پیدا می‌کند.
2. گیاه را با `get_runtime_plant_for_farm` resolve می‌کند.
3. snapshot هوش مصنوعی مزرعه را می‌خواند.
4. weather, soil, site_parameters را می‌سازد.
5. از پروفایل گیاه، `crop_parameters` و در صورت وجود `agromanagement` پیش‌فرض را استخراج می‌کند.

خروجی این تابع شامل این بخش‌هاست:

- `plant`
- `runtime_plants`
- `weather`
- `soil`
- `site_parameters`
- `crop_parameters`
- `agromanagement`

یعنی تمام چیزی که موتور شبیه‌سازی لازم دارد.

### استفاده در Growth Simulation

در `crop_simulation/growth_simulation.py` اگر `farm_uuid` داده شود:

- `build_growth_context` از `build_simulation_payload_from_farm` استفاده می‌کند.
- گیاه انتخاب‌شده وارد context می‌شود.
- سپس شبیه‌سازی PCSE یا fallback projection روی همان گیاه اجرا می‌شود.

### استفاده در Harvest Prediction

در `crop_simulation/harvest_prediction.py` اگر `plant_name` ارسال نشود:

- سرویس با `get_runtime_plant_for_farm` گیاه پیش‌فرض مزرعه را پیدا می‌کند.
- سپس از همان گیاه برای محاسبه‌ی GDD و پیش‌بینی برداشت استفاده می‌شود.

### استفاده در Yield Prediction

در `crop_simulation/yield_prediction.py`:

- سرویس chart فعلی مزرعه را صدا می‌زند.
- chart هم قبلاً گیاه را از مسیر canonical مزرعه resolve کرده است.
- بنابراین yield همیشه روی یک گیاه مشخص از assignmentهای مزرعه محاسبه می‌شود.

## 4) نقش serializerها در resolve کردن نام گیاه

فایل مهم:

- `crop_simulation/serializers.py`

کلاس مهم:

- `PlantNameAliasMixin`

این mixin:

- `plant_name`
- `crop`
- `crop_name`

را قبول می‌کند و با `apps.get_app_config("plant").resolve_plant_name(...)` آن را canonical می‌کند.

پس حتی اگر کلاینت نام گیاه را با alias بفرستد، سرویس شبیه‌سازی با نام استاندارد کار می‌کند.

## 5) ارتباط location_data با گیاه‌ها

فایل مهم:

- `location_data/cluster_recommendation.py`

تابع اصلی:

- `build_cluster_crop_recommendations`

این تابع ارتباط بین کلاسترهای KMeans و گیاه‌های مزرعه را می‌سازد.

### ورودی

- `farm_uuid`

### کارهایی که انجام می‌دهد

1. مزرعه را از `farm_data` پیدا می‌کند.
2. لیست گیاه‌های ثبت‌شده را با `get_farm_plant_assignments` می‌خواند.
3. snapshot کلاسترهای `location_data` را می‌گیرد.
4. برای هر گیاه ثبت‌شده، با `build_simulation_payload_from_farm` یک payload پایه می‌سازد.
5. برای هر کلاستر:
   - متریک‌های همان کلاستر مثل `ndvi`, `ndwi`, `soil_vv`, `soil_vv_db` را جمع می‌کند.
   - پارامترهای soil/site را با داده همان کلاستر override می‌کند.
   - برای تک‌تک گیاه‌های مزرعه شبیه‌سازی اجرا می‌کند.
   - خروجی‌ها را بر اساس `yield_estimate` رتبه‌بندی می‌کند.
6. بهترین گیاه را به عنوان `suggested_plant` برمی‌گرداند.

### نتیجه

`location_data` خودش مرجع گیاه نیست؛ فقط:

- گیاه‌ها را از `farm_data`
- نام canonical را از `plant`
- منطق شبیه‌سازی را از `crop_simulation`

می‌گیرد و روی داده‌های کلاستر اعمال می‌کند.

## 6) ترتیب مسئولیت‌ها

برای جلوگیری از ابهام، مسئولیت هر بخش این است:

- `plant`:
  - canonical name
  - alias resolving

- `farm_data`:
  - snapshot گیاه
  - assignment گیاه به مزرعه
  - تبدیل snapshot به runtime plant

- `crop_simulation`:
  - ساخت payload شبیه‌سازی از مزرعه و گیاه
  - اجرای شبیه‌سازی رشد، عملکرد و برداشت

- `location_data`:
  - خواندن کلاسترهای KMeans
  - مقایسه گیاه‌های مزرعه برای هر کلاستر
  - پیشنهاد گیاه برای sub-block

## 7) نکات مهم طراحی

- relation قدیمی `SensorData.plants` مسیر legacy است و منبع canonical نیست.
- مسیر canonical برای گیاه‌های مزرعه، `FarmPlantAssignment` و `PlantCatalogSnapshot` است.
- سرویس‌های شبیه‌سازی نباید مستقیم از `plant.Plant` برای گیاه مزرعه استفاده کنند؛ مسیر درست، `farm_data.services.get_runtime_plant_for_farm` است.
- اگر `plant_name` صریح داده نشود، معمولاً گیاه اول assignmentهای مزرعه انتخاب می‌شود.
- اگر چند گیاه برای مزرعه ثبت شده باشد، endpoint پیشنهاد کلاستر همه‌ی آن‌ها را compare می‌کند.

## 8) خلاصه جریان end-to-end

جریان کامل به این صورت است:

1. Backend plant catalog -> `PlantCatalogSnapshot`
2. farm selected plants -> `FarmPlantAssignment`
3. client request with `farm_uuid`
4. farm -> runtime plant resolution
5. runtime plant + farm metrics -> simulation payload
6. simulation payload -> PCSE/projection
7. cluster metrics + plant candidates -> recommended crop per cluster

## 9) فایل‌های کلیدی برای مرور سریع

- `plant/apps.py`
- `farm_data/models.py`
- `farm_data/services.py`
- `crop_simulation/services.py`
- `crop_simulation/growth_simulation.py`
- `crop_simulation/harvest_prediction.py`
- `crop_simulation/yield_prediction.py`
- `location_data/cluster_recommendation.py`

