Files

292 lines
11 KiB
Markdown
Raw Permalink Normal View History

2026-05-13 22:28:56 +03:30
# ارتباط سرویس‌ها با 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`