UPDATE
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,291 @@
|
||||
# ارتباط سرویسها با 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`
|
||||
|
||||
Reference in New Issue
Block a user