# ارتباط سرویس‌ها با 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`