942 lines
29 KiB
Markdown
942 lines
29 KiB
Markdown
|
|
# ارتباط `farm_data`، `location_data` و فیلدهای وابسته
|
||
|
|
|
||
|
|
این سند ساختار فعلی دادهها در پروژه را توضیح میدهد و همزمان دو قاعده کسبوکاری مورد تایید را بهعنوان مبنای توسعه ثبت میکند:
|
||
|
|
|
||
|
|
1. برای محاسبههای عمومی هوش مصنوعی مثل `RAG` و `crop_simulation` باید از دادههای تجمیعشده کل بلوکهای بزرگِ تعریفشده توسط کشاورز استفاده شود.
|
||
|
|
2. اگر برای یک مزرعه هیچ بلاکی تعریف نشده باشد، حالت پیشفرض باید شامل `1 بلوک بزرگ` و `1 بلوک کوچکتر داخل همان بلوک بزرگ` باشد.
|
||
|
|
|
||
|
|
این سند بر اساس ساختار فعلی کد در `farm_data/`, `location_data/`, `weather/`, `rag/`, `crop_simulation/`, `irrigation/` و `plant/` نوشته شده است.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1) نقش هر app در معماری داده
|
||
|
|
|
||
|
|
### `farm_data`
|
||
|
|
|
||
|
|
این app رکورد canonical مزرعه برای مصرف لایه AI را نگه میدارد. مهمترین رکورد آن `SensorData` است.
|
||
|
|
|
||
|
|
وظیفههای اصلی:
|
||
|
|
|
||
|
|
- نگهداری شناسه مزرعه (`farm_uuid`)
|
||
|
|
- اتصال مزرعه به `SoilLocation`
|
||
|
|
- نگهداری payload سنسورها
|
||
|
|
- نگهداری snapshot گیاهها برای مصرف AI
|
||
|
|
- اتصال به روش آبیاری و آبوهوا
|
||
|
|
- ساخت خروجی تجمیعی مزرعه با `get_farm_details()`
|
||
|
|
|
||
|
|
### `location_data`
|
||
|
|
|
||
|
|
این app مدل مکانی مزرعه و ساختار بلاکها را نگه میدارد.
|
||
|
|
|
||
|
|
وظیفههای اصلی:
|
||
|
|
|
||
|
|
- نگهداری مرکز هندسی مزرعه (`SoilLocation`)
|
||
|
|
- نگهداری مرز کل مزرعه (`farm_boundary`)
|
||
|
|
- نگهداری بلاکهای اصلی کشاورز (`block_layout.blocks`)
|
||
|
|
- نگهداری subdivision هر بلاک (`BlockSubdivision`)
|
||
|
|
- نگهداری grid سلولها و دادههای سنجشازدور
|
||
|
|
- ساخت snapshotهای بلاکی و تجمیعی برای مصرف downstream
|
||
|
|
|
||
|
|
### `weather`
|
||
|
|
|
||
|
|
پیشبینی آبوهوا را برای هر `SoilLocation` نگه میدارد. مزرعه از طریق `center_location` به forecast متصل میشود.
|
||
|
|
|
||
|
|
### `plant`
|
||
|
|
|
||
|
|
مدل اصلی گیاهها را نگه میدارد، اما برای لایه AI در `farm_data` یک read-model به نام `PlantCatalogSnapshot` ساخته شده است.
|
||
|
|
|
||
|
|
### `irrigation`
|
||
|
|
|
||
|
|
جدول مرجع روشهای آبیاری را نگه میدارد و `farm_data.SensorData` به یکی از آنها متصل میشود.
|
||
|
|
|
||
|
|
### `rag` و `crop_simulation`
|
||
|
|
|
||
|
|
این دو app مصرفکننده دادهاند، نه مالک اصلی داده. یعنی داده اصلی را از `farm_data`, `location_data`, `weather`, `plant` و snapshotهای تجمیعی میگیرند.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2) موجودیتهای اصلی و ارتباط بین آنها
|
||
|
|
|
||
|
|
نمای کلی:
|
||
|
|
|
||
|
|
```text
|
||
|
|
Farm (business id = farm_uuid)
|
||
|
|
|
|
||
|
|
v
|
||
|
|
farm_data.SensorData
|
||
|
|
|-- FK --> location_data.SoilLocation
|
||
|
|
|-- FK --> weather.WeatherForecast (optional cached/latest link)
|
||
|
|
|-- FK --> irrigation.IrrigationMethod (optional)
|
||
|
|
|-- JSON --> sensor_payload
|
||
|
|
|-- M2M legacy --> plant.Plant
|
||
|
|
|-- 1:N canonical --> farm_data.FarmPlantAssignment --> farm_data.PlantCatalogSnapshot
|
||
|
|
|
||
|
|
location_data.SoilLocation
|
||
|
|
|-- JSON --> farm_boundary
|
||
|
|
|-- int --> input_block_count
|
||
|
|
|-- JSON --> block_layout
|
||
|
|
|-- 1:N --> BlockSubdivision
|
||
|
|
|-- 1:N --> RemoteSensingRun
|
||
|
|
|-- 1:N --> AnalysisGridCell
|
||
|
|
|-- 1:N --> WeatherForecast
|
||
|
|
|-- 1:N --> NdviObservation
|
||
|
|
|
||
|
|
BlockSubdivision
|
||
|
|
|-- belongs to --> SoilLocation
|
||
|
|
|-- identifies --> one main block (block_code)
|
||
|
|
|-- stores --> source_boundary / centroid_points / subdivision_summary
|
||
|
|
|
||
|
|
RemoteSensingRun
|
||
|
|
|-- belongs to --> SoilLocation
|
||
|
|
|-- scoped by --> block_code
|
||
|
|
|-- produces --> AnalysisGridObservation + RemoteSensingSubdivisionResult
|
||
|
|
|
||
|
|
RemoteSensingSubdivisionResult
|
||
|
|
|-- belongs to --> RemoteSensingRun
|
||
|
|
|-- scoped by --> block_code
|
||
|
|
|-- produces --> RemoteSensingClusterBlock + RemoteSensingClusterAssignment
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3) تعریف دقیق موجودیتها
|
||
|
|
|
||
|
|
## 3.1) `farm_data.SensorData`
|
||
|
|
|
||
|
|
این مدل در عمل رکورد اصلی مزرعه برای مصرف AI است.
|
||
|
|
|
||
|
|
فیلدهای مهم:
|
||
|
|
|
||
|
|
- `farm_uuid`
|
||
|
|
- شناسه یکتای مزرعه
|
||
|
|
- primary key
|
||
|
|
- شناسه business-level بین appها
|
||
|
|
|
||
|
|
- `center_location`
|
||
|
|
- `ForeignKey` به `location_data.SoilLocation`
|
||
|
|
- هر مزرعه دقیقاً به یک location مرکزی وصل است
|
||
|
|
- نام legacy آن در بعضی جاها هنوز بهصورت `location` دیده میشود، ولی canonical همان `center_location` است
|
||
|
|
|
||
|
|
- `weather_forecast`
|
||
|
|
- `ForeignKey` اختیاری به `weather.WeatherForecast`
|
||
|
|
- آخرین forecast مرتبط با location را cache میکند
|
||
|
|
- اگر خالی باشد، سرویسها معمولاً از روی `center_location.weather_forecasts` آخرین رکورد را پیدا میکنند
|
||
|
|
|
||
|
|
- `sensor_payload`
|
||
|
|
- `JSONField`
|
||
|
|
- ساختار چندسنسوری دارد
|
||
|
|
- نمونه:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"sensor-7-1": {
|
||
|
|
"soil_moisture": 22.4,
|
||
|
|
"soil_temperature": 18.1,
|
||
|
|
"nitrogen": 31.0
|
||
|
|
},
|
||
|
|
"leaf-sensor": {
|
||
|
|
"leaf_wetness": 11.0,
|
||
|
|
"leaf_temperature": 19.3
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
- `plants`
|
||
|
|
- relation قدیمی به `plant.Plant`
|
||
|
|
- برای سازگاری عقبرو نگه داشته شده
|
||
|
|
- canonical برای AI نیست
|
||
|
|
|
||
|
|
- `irrigation_method`
|
||
|
|
- `ForeignKey` اختیاری به `irrigation.IrrigationMethod`
|
||
|
|
|
||
|
|
- `created_at`, `updated_at`
|
||
|
|
- زمان ساخت و آخرین بهروزرسانی رکورد مزرعه
|
||
|
|
|
||
|
|
نکته مهم:
|
||
|
|
|
||
|
|
- `SensorData` مالک geometry مزرعه نیست.
|
||
|
|
- geometry و بلاکها در `location_data` نگهداری میشوند.
|
||
|
|
- `SensorData` فقط به `SoilLocation` وصل میشود.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3.2) `farm_data.PlantCatalogSnapshot`
|
||
|
|
|
||
|
|
این مدل snapshot خواندنی از کاتالوگ گیاه Backend است تا سرویسهای AI مستقیم به app اصلی گیاه وابسته نباشند.
|
||
|
|
|
||
|
|
فیلدهای مهم:
|
||
|
|
|
||
|
|
- `backend_plant_id`
|
||
|
|
- `name`
|
||
|
|
- `slug`
|
||
|
|
- `icon`
|
||
|
|
- `description`
|
||
|
|
- `metadata`
|
||
|
|
- `health_profile`
|
||
|
|
- `irrigation_profile`
|
||
|
|
- `growth_profile`
|
||
|
|
- `growth_stage`, `growth_stages`
|
||
|
|
- `is_active`
|
||
|
|
- `source_updated_at`
|
||
|
|
|
||
|
|
این مدل source of truth اصلی گیاه در کل سیستم نیست، ولی source of truth لایه AI برای گیاهِ syncشده است.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3.3) `farm_data.FarmPlantAssignment`
|
||
|
|
|
||
|
|
رابط canonical بین مزرعه و snapshot گیاه است.
|
||
|
|
|
||
|
|
فیلدهای مهم:
|
||
|
|
|
||
|
|
- `farm` -> `SensorData`
|
||
|
|
- `plant` -> `PlantCatalogSnapshot`
|
||
|
|
- `position`
|
||
|
|
- `stage`
|
||
|
|
- `metadata`
|
||
|
|
|
||
|
|
کاربرد:
|
||
|
|
|
||
|
|
- تعیین ترتیب گیاههای مزرعه
|
||
|
|
- تعیین stage اختصاصی برای گیاه در همان مزرعه
|
||
|
|
- حذف وابستگی مستقیم AI به `SensorData.plants`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3.4) `farm_data.SensorParameter`
|
||
|
|
|
||
|
|
تعریف metadata هر پارامتر سنسوری است.
|
||
|
|
|
||
|
|
فیلدهای مهم:
|
||
|
|
|
||
|
|
- `sensor_key`
|
||
|
|
- `code`
|
||
|
|
- `name_fa`
|
||
|
|
- `unit`
|
||
|
|
- `data_type`
|
||
|
|
- `metadata`
|
||
|
|
|
||
|
|
کاربرد:
|
||
|
|
|
||
|
|
- ساخت schema داینامیک برای `sensor_payload`
|
||
|
|
- ثبت سنسورهای جدید بدون migration
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3.5) `location_data.SoilLocation`
|
||
|
|
|
||
|
|
این مدل نقطه مرکزی و ساختار فضایی مزرعه را نگه میدارد.
|
||
|
|
|
||
|
|
فیلدهای مهم:
|
||
|
|
|
||
|
|
- `latitude`
|
||
|
|
- `longitude`
|
||
|
|
- مرکز هندسی مزرعه
|
||
|
|
- روی این دو فیلد constraint یکتایی وجود دارد
|
||
|
|
|
||
|
|
- `task_id`
|
||
|
|
- شناسه taskهای async
|
||
|
|
|
||
|
|
- `farm_boundary`
|
||
|
|
- مرز کل مزرعه
|
||
|
|
- معمولاً به شکل GeoJSON polygon ذخیره میشود
|
||
|
|
|
||
|
|
- `input_block_count`
|
||
|
|
- تعداد بلاکهای اصلی تعریفشده توسط کشاورز
|
||
|
|
|
||
|
|
- `block_layout`
|
||
|
|
- ساختار کامل بلاکهای اصلی و زیربلاکهای داخلی
|
||
|
|
- مهمترین فیلد spatial-read-model برای AI
|
||
|
|
|
||
|
|
- `created_at`, `updated_at`
|
||
|
|
|
||
|
|
نکته مهم:
|
||
|
|
|
||
|
|
- `SoilLocation` خود مزرعه نیست.
|
||
|
|
- `SoilLocation` نمای مکانی مزرعه است.
|
||
|
|
- مزرعه business-level با `farm_uuid` در `SensorData` شناسایی میشود.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3.6) `location_data.BlockSubdivision`
|
||
|
|
|
||
|
|
این مدل subdivision یک بلاک اصلی را نگه میدارد.
|
||
|
|
|
||
|
|
فیلدهای مهم:
|
||
|
|
|
||
|
|
- `soil_location`
|
||
|
|
- `block_code`
|
||
|
|
- `source_boundary`
|
||
|
|
- `chunk_size_sqm`
|
||
|
|
- `grid_points`
|
||
|
|
- `centroid_points`
|
||
|
|
- `grid_point_count`
|
||
|
|
- `centroid_count`
|
||
|
|
- `status`
|
||
|
|
- `metadata`
|
||
|
|
- `elbow_plot`
|
||
|
|
|
||
|
|
تفسیر:
|
||
|
|
|
||
|
|
- هر رکورد `BlockSubdivision` به یک `main block` تعلق دارد.
|
||
|
|
- `block_code` همان شناسه بلاک اصلی کشاورز است.
|
||
|
|
- `centroid_points` معمولاً نماینده زیربلاکهای داخلی است.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3.7) `location_data.RemoteSensingRun`
|
||
|
|
|
||
|
|
هر run سنجشازدور برای یک location و معمولاً برای یک `block_code` اجرا میشود.
|
||
|
|
|
||
|
|
فیلدهای مهم:
|
||
|
|
|
||
|
|
- `soil_location`
|
||
|
|
- `block_subdivision`
|
||
|
|
- `block_code`
|
||
|
|
- `provider`
|
||
|
|
- `chunk_size_sqm`
|
||
|
|
- `temporal_start`
|
||
|
|
- `temporal_end`
|
||
|
|
- `status`
|
||
|
|
- `metadata`
|
||
|
|
- `error_message`
|
||
|
|
|
||
|
|
اگر `block_code` خالی باشد، run در سطح کل farm/location تفسیر میشود.
|
||
|
|
اگر `block_code` مقدار داشته باشد، run مربوط به همان بلاک اصلی است.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3.8) `location_data.AnalysisGridCell`
|
||
|
|
|
||
|
|
سلولهای شبکه تحلیلی برای سنجشازدور هستند.
|
||
|
|
|
||
|
|
فیلدهای مهم:
|
||
|
|
|
||
|
|
- `soil_location`
|
||
|
|
- `block_subdivision`
|
||
|
|
- `block_code`
|
||
|
|
- `cell_code`
|
||
|
|
- `chunk_size_sqm`
|
||
|
|
- `geometry`
|
||
|
|
- `centroid_lat`
|
||
|
|
- `centroid_lon`
|
||
|
|
|
||
|
|
اینها لایه پایینتر از main block هستند و برای محاسبات remote sensing استفاده میشوند.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3.9) `location_data.AnalysisGridObservation`
|
||
|
|
|
||
|
|
خروجی متریکهای سنجشازدور برای هر cell در یک بازه زمانی است.
|
||
|
|
|
||
|
|
فیلدهای مهم:
|
||
|
|
|
||
|
|
- `cell`
|
||
|
|
- `run`
|
||
|
|
- `temporal_start`
|
||
|
|
- `temporal_end`
|
||
|
|
- `ndvi`
|
||
|
|
- `ndwi`
|
||
|
|
- `soil_vv`
|
||
|
|
- `soil_vv_db`
|
||
|
|
- `dem_m`
|
||
|
|
- `slope_deg`
|
||
|
|
- `metadata`
|
||
|
|
|
||
|
|
این دادهها raw یا نیمهتجمیعشدهاند و هنوز در سطح مزرعه نیستند.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3.10) `location_data.RemoteSensingSubdivisionResult`
|
||
|
|
|
||
|
|
نتیجه خوشهبندی دادهمحور برای یک run و یک بلاک است.
|
||
|
|
|
||
|
|
فیلدهای مهم:
|
||
|
|
|
||
|
|
- `run`
|
||
|
|
- `soil_location`
|
||
|
|
- `block_subdivision`
|
||
|
|
- `block_code`
|
||
|
|
- `chunk_size_sqm`
|
||
|
|
- `cluster_count`
|
||
|
|
- `selected_features`
|
||
|
|
- `metadata`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3.11) `location_data.RemoteSensingClusterBlock`
|
||
|
|
|
||
|
|
این مدل زیربلاکهای KMeans/cluster-based را نگه میدارد.
|
||
|
|
|
||
|
|
فیلدهای مهم:
|
||
|
|
|
||
|
|
- `uuid`
|
||
|
|
- `result`
|
||
|
|
- `soil_location`
|
||
|
|
- `block_subdivision`
|
||
|
|
- `block_code`
|
||
|
|
- `sub_block_code`
|
||
|
|
- `cluster_label`
|
||
|
|
- `centroid_lat`
|
||
|
|
- `centroid_lon`
|
||
|
|
- `geometry`
|
||
|
|
- `cell_count`
|
||
|
|
- `cell_codes`
|
||
|
|
- `metadata`
|
||
|
|
|
||
|
|
نکته مهم:
|
||
|
|
|
||
|
|
- این زیربلاکها با `main block` فرق دارند.
|
||
|
|
- `block_code` = بلاک اصلی والد
|
||
|
|
- `sub_block_code` = زیربلاک داخلی ساختهشده با خوشهبندی
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3.12) `weather.WeatherForecast`
|
||
|
|
|
||
|
|
پیشبینی هواشناسی برای یک `SoilLocation` است.
|
||
|
|
|
||
|
|
فیلدهای مهم:
|
||
|
|
|
||
|
|
- `location`
|
||
|
|
- `forecast_date`
|
||
|
|
- `temperature_min`
|
||
|
|
- `temperature_max`
|
||
|
|
- `temperature_mean`
|
||
|
|
- `precipitation`
|
||
|
|
- `precipitation_probability`
|
||
|
|
- `humidity_mean`
|
||
|
|
- `wind_speed_max`
|
||
|
|
- `et0`
|
||
|
|
- `weather_code`
|
||
|
|
- `fetched_at`
|
||
|
|
|
||
|
|
نکته:
|
||
|
|
|
||
|
|
- آبوهوا به location وصل است، نه مستقیم به farm_uuid.
|
||
|
|
- `SensorData.weather_forecast` فقط shortcut/cache است.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3.13) `irrigation.IrrigationMethod`
|
||
|
|
|
||
|
|
مدل مرجع روش آبیاری است.
|
||
|
|
|
||
|
|
فیلدهای مهم:
|
||
|
|
|
||
|
|
- `name`
|
||
|
|
- `category`
|
||
|
|
- `description`
|
||
|
|
- `water_efficiency_percent`
|
||
|
|
- `water_pressure_required`
|
||
|
|
- `flow_rate`
|
||
|
|
- `coverage_area`
|
||
|
|
- `soil_type`
|
||
|
|
- `climate_suitability`
|
||
|
|
|
||
|
|
هر مزرعه میتواند صفر یا یک روش آبیاری انتخابشده داشته باشد.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4) منبع اصلی هر نوع داده
|
||
|
|
|
||
|
|
| نوع داده | مالک اصلی | فیلد/مدل canonical | توضیح |
|
||
|
|
|---|---|---|---|
|
||
|
|
| شناسه مزرعه | `farm_data` | `SensorData.farm_uuid` | شناسه business-level |
|
||
|
|
| مرکز مکانی مزرعه | `location_data` | `SoilLocation.latitude/longitude` | centroid هندسی |
|
||
|
|
| مرز کل مزرعه | `location_data` | `SoilLocation.farm_boundary` | شکل کل زمین |
|
||
|
|
| تعداد بلاکهای اصلی | `location_data` | `SoilLocation.input_block_count` | تعداد بلاکهای کشاورز |
|
||
|
|
| ساختار بلاکها | `location_data` | `SoilLocation.block_layout` | بلاکهای اصلی + sub-block metadata |
|
||
|
|
| تعریف subdivision هر بلاک | `location_data` | `BlockSubdivision` | state و مرز هر بلاک |
|
||
|
|
| داده سنسور | `farm_data` | `SensorData.sensor_payload` | source مستقیم از مزرعه/سنسور |
|
||
|
|
| schema پارامترهای سنسور | `farm_data` | `SensorParameter` | metadata فیلدهای sensor_payload |
|
||
|
|
| گیاههای مزرعه | `farm_data` | `FarmPlantAssignment` | canonical برای AI |
|
||
|
|
| catalog گیاه | `farm_data` | `PlantCatalogSnapshot` | snapshot sync شده |
|
||
|
|
| forecast هوا | `weather` | `WeatherForecast` | در سطح location |
|
||
|
|
| داده سنجشازدور سلولی | `location_data` | `AnalysisGridObservation` | خام/نیمهخام |
|
||
|
|
| تجمیع بلاک اصلی | `location_data` | snapshotهای `satellite_snapshot.py` | برای AI |
|
||
|
|
| روش آبیاری | `irrigation` | `IrrigationMethod` | جدول مرجع |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5) دو سطح بلاک که نباید با هم قاطی شوند
|
||
|
|
|
||
|
|
در این پروژه دو سطح جدا داریم:
|
||
|
|
|
||
|
|
### سطح اول: `main block`
|
||
|
|
|
||
|
|
این همان بلاک بزرگی است که کشاورز تعریف میکند.
|
||
|
|
|
||
|
|
محل نگهداری:
|
||
|
|
|
||
|
|
- `SoilLocation.block_layout["blocks"]`
|
||
|
|
- `BlockSubdivision.block_code`
|
||
|
|
- `RemoteSensingRun.block_code`
|
||
|
|
|
||
|
|
مثال:
|
||
|
|
|
||
|
|
- `block-1`
|
||
|
|
- `north-field`
|
||
|
|
- `greenhouse-a`
|
||
|
|
|
||
|
|
### سطح دوم: `sub block`
|
||
|
|
|
||
|
|
این زیربلاک داخلی است که یا:
|
||
|
|
|
||
|
|
- از subdivision اولیه ساخته میشود
|
||
|
|
- یا از خوشهبندی دادهمحور remote sensing/KMeans ساخته میشود
|
||
|
|
|
||
|
|
محل نگهداری:
|
||
|
|
|
||
|
|
- `BlockSubdivision.centroid_points`
|
||
|
|
- `block_layout["blocks"][i]["sub_blocks"]`
|
||
|
|
- `RemoteSensingClusterBlock`
|
||
|
|
- `satellite_snapshot["satellite_sub_blocks"]`
|
||
|
|
- `satellite_snapshot["sensor_sub_blocks"]`
|
||
|
|
|
||
|
|
مثال:
|
||
|
|
|
||
|
|
- `sub-block-1`
|
||
|
|
- `cluster-0`
|
||
|
|
- `cluster-1`
|
||
|
|
|
||
|
|
قانون مهم:
|
||
|
|
|
||
|
|
- `main block` سطح تصمیمگیری کشاورز است.
|
||
|
|
- `sub block` سطح تحلیل داخلی سیستم است.
|
||
|
|
- برای AI عمومی باید جمعبندی روی `main block`ها انجام شود، نه اینکه مستقیماً یک `sub block` بهعنوان نماینده کل مزرعه در نظر گرفته شود.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 6) ساختار `block_layout`
|
||
|
|
|
||
|
|
`SoilLocation.block_layout` مهمترین read-model فضایی برای کل سیستم است.
|
||
|
|
|
||
|
|
شکل عمومی:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"input_block_count": 1,
|
||
|
|
"default_full_farm": true,
|
||
|
|
"algorithm_status": "pending",
|
||
|
|
"blocks": [
|
||
|
|
{
|
||
|
|
"block_code": "block-1",
|
||
|
|
"order": 1,
|
||
|
|
"source": "default",
|
||
|
|
"boundary": {},
|
||
|
|
"needs_subdivision": null,
|
||
|
|
"sub_blocks": []
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
کلیدهای مهم:
|
||
|
|
|
||
|
|
- `input_block_count`
|
||
|
|
- تعداد بلاکهای اصلی کشاورز
|
||
|
|
|
||
|
|
- `default_full_farm`
|
||
|
|
- اگر فقط یک بلاک اصلی وجود داشته باشد معمولاً `true` است
|
||
|
|
|
||
|
|
- `algorithm_status`
|
||
|
|
- وضعیت محاسبات بعدی روی layout
|
||
|
|
- معمولاً `pending` یا `completed`
|
||
|
|
|
||
|
|
- `blocks`
|
||
|
|
- لیست بلاکهای اصلی
|
||
|
|
|
||
|
|
هر آیتم `blocks`:
|
||
|
|
|
||
|
|
- `block_code`
|
||
|
|
- شناسه یکتای بلاک اصلی
|
||
|
|
|
||
|
|
- `order`
|
||
|
|
- ترتیب نمایش/تحلیل
|
||
|
|
|
||
|
|
- `source`
|
||
|
|
- معمولاً `input` یا `default`
|
||
|
|
|
||
|
|
- `boundary`
|
||
|
|
- مرز همان بلاک اصلی
|
||
|
|
|
||
|
|
- `needs_subdivision`
|
||
|
|
- آیا این بلاک نیاز به subdivision داخلی دارد
|
||
|
|
|
||
|
|
- `sub_blocks`
|
||
|
|
- لیست زیربلاکهای داخلی
|
||
|
|
|
||
|
|
در بعضی مرحلهها این layout با فیلدهای تکمیلی enrich میشود:
|
||
|
|
|
||
|
|
- `subdivision_summary`
|
||
|
|
- `analysis_grid_summary`
|
||
|
|
- `aggregated_metrics`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 7) جریان ساخت و بهروزرسانی داده
|
||
|
|
|
||
|
|
## 7.1) وقتی `POST /api/farm-data/` صدا زده میشود
|
||
|
|
|
||
|
|
این endpoint مزرعه را از دید AI upsert میکند.
|
||
|
|
|
||
|
|
جریان:
|
||
|
|
|
||
|
|
1. `farm_uuid` و `farm_boundary` دریافت میشود.
|
||
|
|
2. در `resolve_center_location_from_boundary()` centroid مزرعه محاسبه میشود.
|
||
|
|
3. یک `SoilLocation` بر اساس centroid ساخته یا پیدا میشود.
|
||
|
|
4. `input_block_count` و `block_layout` اولیه تنظیم میشوند.
|
||
|
|
5. اگر ایجاد جدید باشد و فقط یک بلاک وجود داشته باشد، برای `block-1` یک subdivision اولیه هم میتواند ساخته شود.
|
||
|
|
6. forecast آبوهوا از روی location resolve میشود.
|
||
|
|
7. رکورد `SensorData` ساخته یا آپدیت میشود.
|
||
|
|
8. payload سنسورها merge میشود.
|
||
|
|
9. plant assignmentها و irrigation method در صورت ارسال sync میشوند.
|
||
|
|
|
||
|
|
نکته:
|
||
|
|
|
||
|
|
- این endpoint بیشتر مزرعه را به `SoilLocation` وصل میکند.
|
||
|
|
- تعریف دقیق مرز هر main block معمولاً از مسیر `location_data` میآید، نه از `farm_data`.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 7.2) وقتی `POST /api/location-data/` صدا زده میشود
|
||
|
|
|
||
|
|
این endpoint ساختار مزرعه از دید spatial را ذخیره میکند.
|
||
|
|
|
||
|
|
جریان:
|
||
|
|
|
||
|
|
1. `lat`, `lon`, `farm_boundary`, `blocks` دریافت میشود.
|
||
|
|
2. `SoilLocation` بر اساس همان lat/lon ذخیره یا پیدا میشود.
|
||
|
|
3. `input_block_count` و `block_layout` با لیست `blocks` بهروزرسانی میشوند.
|
||
|
|
4. `_sync_defined_blocks()` برای هر `main block` یک `BlockSubdivision` با `status="defined"` میسازد یا بهروزرسانی میکند.
|
||
|
|
5. اگر بلاکی حذف شده باشد، subdivision و state تحلیل قبلی آن پاک میشود.
|
||
|
|
6. اگر boundary بلاکی تغییر کند، state تحلیل سنجشازدور آن invalidate میشود.
|
||
|
|
|
||
|
|
پس:
|
||
|
|
|
||
|
|
- `location_data` مالک تعریف بلاکهای اصلی است.
|
||
|
|
- `farm_data` مالک رکورد مزرعه برای AI است.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 7.3) وقتی `get_farm_details()` ساخته میشود
|
||
|
|
|
||
|
|
این تابع خروجی canonical مزرعه را برای appهای دیگر تولید میکند.
|
||
|
|
|
||
|
|
خروجی شامل این بخشهاست:
|
||
|
|
|
||
|
|
- `center_location`
|
||
|
|
- `weather`
|
||
|
|
- `sensor_payload`
|
||
|
|
- `sensor_schema`
|
||
|
|
- `soil`
|
||
|
|
- `plant_ids`
|
||
|
|
- `plants`
|
||
|
|
- `plant_assignments`
|
||
|
|
- `irrigation_method`
|
||
|
|
- `created_at`, `updated_at`
|
||
|
|
|
||
|
|
بخش `soil` از ادغام این دو منبع ساخته میشود:
|
||
|
|
|
||
|
|
- snapshotهای سنجشازدور
|
||
|
|
- sensor_payload
|
||
|
|
|
||
|
|
قاعده فعلی merge:
|
||
|
|
|
||
|
|
- اگر برای یک metric داده سنسور وجود داشته باشد، روی داده remote sensing override میشود.
|
||
|
|
- اگر چند سنسور مقدار متعارض بدهند:
|
||
|
|
- برای داده عددی average گرفته میشود
|
||
|
|
- برای داده غیرعددی distinct values برمیگردد
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 8) snapshotهای مکانی و معنای آنها
|
||
|
|
|
||
|
|
در `location_data/satellite_snapshot.py` چند نوع snapshot مهم ساخته میشود:
|
||
|
|
|
||
|
|
### `build_location_satellite_snapshot(location, block_code="")`
|
||
|
|
|
||
|
|
یک snapshot برای یک scope خاص میسازد:
|
||
|
|
|
||
|
|
- اگر `block_code` خالی باشد: snapshot عمومی location/farm
|
||
|
|
- اگر `block_code` پر باشد: snapshot همان main block
|
||
|
|
|
||
|
|
### `build_location_block_satellite_snapshots(location)`
|
||
|
|
|
||
|
|
برای همه `main block`های ثبتشده snapshot میسازد.
|
||
|
|
|
||
|
|
خروجی هر بلاک شامل اینهاست:
|
||
|
|
|
||
|
|
- `resolved_metrics`
|
||
|
|
- `metric_sources`
|
||
|
|
- `satellite_metrics`
|
||
|
|
- `sensor_metrics`
|
||
|
|
- `sensor_sub_blocks`
|
||
|
|
- `satellite_sub_blocks`
|
||
|
|
- `sub_block_count`
|
||
|
|
|
||
|
|
### `build_farmer_block_aggregated_snapshot(location)`
|
||
|
|
|
||
|
|
خروجی تجمیعی سطح مزرعه بر اساس همه `main block`های کشاورز است.
|
||
|
|
|
||
|
|
این مهمترین تابع برای قانون کسبوکاری تو است، چون:
|
||
|
|
|
||
|
|
- اگر چند main block وجود داشته باشد، میانگین آنها را در سطح farmer-block میسازد
|
||
|
|
- `aggregation_strategy` آن `farmer_block_mean` است
|
||
|
|
- برای AI عمومی از نظر مفهومی این همان سطح درست مصرف داده است
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 9) قانون canonical برای محاسبههای عمومی AI
|
||
|
|
|
||
|
|
برای سرویسهای عمومی هوش مصنوعی مثل:
|
||
|
|
|
||
|
|
- `RAG`
|
||
|
|
- `crop_simulation`
|
||
|
|
- `fertilization`
|
||
|
|
- `irrigation`
|
||
|
|
- `farm_alerts`
|
||
|
|
- هر سرویسی که قرار است از کل وضعیت مزرعه حرف بزند
|
||
|
|
|
||
|
|
باید سطح داده canonical این باشد:
|
||
|
|
|
||
|
|
### سطح مجاز
|
||
|
|
|
||
|
|
- کل مزرعه بر اساس تجمیع `main block`های کشاورز
|
||
|
|
|
||
|
|
### تابع پیشنهادی canonical
|
||
|
|
|
||
|
|
- `build_farmer_block_aggregated_snapshot(location, sensor_payload=...)`
|
||
|
|
|
||
|
|
### دلیل
|
||
|
|
|
||
|
|
- این تابع دادهها را از سطح `main block` بالا میآورد
|
||
|
|
- اگر مزرعه چند بلاک اصلی داشته باشد، یک بلاک یا یک sub-block به اشتباه نماینده کل مزرعه نمیشود
|
||
|
|
- با خواسته کسبوکاری تو همراستا است
|
||
|
|
|
||
|
|
### سطحی که نباید مبنای AI عمومی باشد
|
||
|
|
|
||
|
|
- یک `sub_block` تکی
|
||
|
|
- یک `cluster-0` یا `cluster-1` بهتنهایی
|
||
|
|
- snapshot خام location بدون درنظرگرفتن بلاکهای اصلی کشاورز، مگر فقط بهعنوان fallback
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 10) وضعیت پیشفرض وقتی بلاک تعریف نشده است
|
||
|
|
|
||
|
|
قاعده مورد تایید:
|
||
|
|
|
||
|
|
- اگر کشاورز هنوز بلاکها را تعریف نکرده باشد:
|
||
|
|
- یک `main block` پیشفرض وجود دارد
|
||
|
|
- داخل آن هم یک `sub block` پیشفرض وجود دارد
|
||
|
|
|
||
|
|
### نمایش منطقی مورد انتظار
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"input_block_count": 1,
|
||
|
|
"default_full_farm": true,
|
||
|
|
"algorithm_status": "pending",
|
||
|
|
"blocks": [
|
||
|
|
{
|
||
|
|
"block_code": "block-1",
|
||
|
|
"order": 1,
|
||
|
|
"source": "default",
|
||
|
|
"boundary": {},
|
||
|
|
"needs_subdivision": false,
|
||
|
|
"sub_blocks": [
|
||
|
|
{
|
||
|
|
"sub_block_code": "sub-block-1"
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### تفسیر این قانون
|
||
|
|
|
||
|
|
- `block-1` نماینده کل مزرعه است
|
||
|
|
- `sub-block-1` حداقل واحد داخلی برای اینکه downstreamها همیشه ساختار یکنواخت ببینند
|
||
|
|
|
||
|
|
### نکته درباره وضعیت فعلی کد
|
||
|
|
|
||
|
|
کد فعلی بهصورت پیشفرض `1 main block` را بهخوبی میسازد، اما وجود `1 sub-block` پیشفرض باید بهعنوان قانون توسعه حفظ و در همه entry pointها یکدست شود.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 11) ارتباط این دادهها با appهای دیگر
|
||
|
|
|
||
|
|
## 11.1) `rag`
|
||
|
|
|
||
|
|
`rag` معمولاً context مزرعه را از `farm_data` میگیرد.
|
||
|
|
|
||
|
|
نقاط مهم:
|
||
|
|
|
||
|
|
- `rag.chat.build_rag_context()` از `get_farm_details()` استفاده میکند
|
||
|
|
- `rag.user_data.build_user_soil_text()` علاوه بر دادههای مزرعه، از:
|
||
|
|
- `build_farmer_block_aggregated_snapshot()`
|
||
|
|
- `build_location_block_satellite_snapshots()`
|
||
|
|
استفاده میکند
|
||
|
|
|
||
|
|
نتیجه:
|
||
|
|
|
||
|
|
- برای RAG عمومی، سطح درست context باید تجمیع `main block`ها باشد
|
||
|
|
- جزئیات بلاکی و زیربلاکی فقط برای explanation تکمیلی مناسباند
|
||
|
|
|
||
|
|
## 11.2) `crop_simulation`
|
||
|
|
|
||
|
|
`crop_simulation` از این دادهها استفاده میکند:
|
||
|
|
|
||
|
|
- `SensorData`
|
||
|
|
- `center_location`
|
||
|
|
- forecastهای هوا
|
||
|
|
- snapshotهای خاک/سنجشازدور
|
||
|
|
- plant profile
|
||
|
|
- irrigation method
|
||
|
|
|
||
|
|
قاعده مورد انتظار:
|
||
|
|
|
||
|
|
- اگر خروجی برای کل مزرعه است، ورودی خاک/سنسور باید از تجمیع `main block`های کشاورز بیاید
|
||
|
|
- نه از یک location snapshot ساده یا یک sub-block خاص
|
||
|
|
|
||
|
|
## 11.3) `weather`
|
||
|
|
|
||
|
|
سرویسهای هواشناسی به `SensorData.center_location` متکی هستند و forecast را از `WeatherForecast`های همان location میخوانند.
|
||
|
|
|
||
|
|
## 11.4) `soile`
|
||
|
|
|
||
|
|
تحلیلهای خاک و anomaly detection از `load_farm_context()` و snapshotهای location استفاده میکنند. برای use-caseهای farm-wide، باید همان rule تجمیع `main block`ها رعایت شود.
|
||
|
|
|
||
|
|
## 11.5) `farm_alerts`
|
||
|
|
|
||
|
|
این app از `load_farm_context()` و `get_farm_details()` استفاده میکند. بنابراین هر تغییری در canonical farm context مستقیماً روی alertها اثر میگذارد.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 12) تفاوت `farm_boundary` با `block boundary`
|
||
|
|
|
||
|
|
این دو نباید با هم اشتباه شوند:
|
||
|
|
|
||
|
|
### `farm_boundary`
|
||
|
|
|
||
|
|
- مرز کل زمین
|
||
|
|
- در `SoilLocation.farm_boundary`
|
||
|
|
- فقط یکی برای هر location
|
||
|
|
|
||
|
|
### `blocks[i].boundary`
|
||
|
|
|
||
|
|
- مرز هر بلاک اصلی کشاورز
|
||
|
|
- در `SoilLocation.block_layout["blocks"]`
|
||
|
|
- بهازای هر main block یک boundary
|
||
|
|
|
||
|
|
نتیجه:
|
||
|
|
|
||
|
|
- `farm_boundary` = outer shell کل مزرعه
|
||
|
|
- `block boundary` = تقسیم داخلی همان مزرعه
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 13) تفاوت `center_location` با `farm_uuid`
|
||
|
|
|
||
|
|
### `farm_uuid`
|
||
|
|
|
||
|
|
- شناسه business-level مزرعه
|
||
|
|
- در `SensorData`
|
||
|
|
- چیزی است که APIهای AI بیشتر با آن کار میکنند
|
||
|
|
|
||
|
|
### `center_location`
|
||
|
|
|
||
|
|
- شناسه مکانی centroid-based
|
||
|
|
- در `SoilLocation`
|
||
|
|
- چیزی است که weather, remote sensing, block layout و geometry به آن وصلاند
|
||
|
|
|
||
|
|
یک `farm_uuid` به یک `center_location` وصل میشود، اما معنا و مسئولیتشان متفاوت است:
|
||
|
|
|
||
|
|
- `farm_uuid` = هویت مزرعه
|
||
|
|
- `center_location` = هویت مکانی مزرعه
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 14) فیلدهایی که downstreamها باید canonical از آنها بخوانند
|
||
|
|
|
||
|
|
اگر سرویسی بخواهد داده مزرعه را بخواند، اولویت canonical اینطور است:
|
||
|
|
|
||
|
|
### هویت مزرعه
|
||
|
|
|
||
|
|
- `SensorData.farm_uuid`
|
||
|
|
|
||
|
|
### geometry و ساختار بلاک
|
||
|
|
|
||
|
|
- `SensorData.center_location`
|
||
|
|
- `SensorData.center_location.farm_boundary`
|
||
|
|
- `SensorData.center_location.block_layout`
|
||
|
|
|
||
|
|
### داده سنسور
|
||
|
|
|
||
|
|
- `SensorData.sensor_payload`
|
||
|
|
|
||
|
|
### schema سنسور
|
||
|
|
|
||
|
|
- `farm_data.SensorParameter`
|
||
|
|
|
||
|
|
### گیاه
|
||
|
|
|
||
|
|
- `FarmPlantAssignment` + `PlantCatalogSnapshot`
|
||
|
|
|
||
|
|
### آبوهوا
|
||
|
|
|
||
|
|
- `SensorData.weather_forecast` اگر موجود بود
|
||
|
|
- در غیر این صورت `center_location.weather_forecasts`
|
||
|
|
|
||
|
|
### summary خاک/remote sensing برای کل مزرعه
|
||
|
|
|
||
|
|
- `build_farmer_block_aggregated_snapshot(...)`
|
||
|
|
|
||
|
|
### summary برای هر main block
|
||
|
|
|
||
|
|
- `build_location_block_satellite_snapshots(...)`
|
||
|
|
|
||
|
|
### summary برای زیربلاکها
|
||
|
|
|
||
|
|
- `satellite_sub_blocks` و `sensor_sub_blocks`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 15) نمونه خلاصه مفهومی برای یک مزرعه
|
||
|
|
|
||
|
|
```text
|
||
|
|
farm_uuid = شناسه اصلی مزرعه
|
||
|
|
center_location = centroid و ساختار spatial مزرعه
|
||
|
|
farm_boundary = مرز کل زمین
|
||
|
|
block_layout = بلاکهای اصلی تعریفشده توسط کشاورز
|
||
|
|
block_subdivisions = وضعیت subdivision هر بلاک اصلی
|
||
|
|
analysis_grid = سلولهای داخلی برای سنجشازدور
|
||
|
|
remote_sensing = متریکهای سلولی و تجمیعشده
|
||
|
|
sensor_payload = سنسورهای واقعی نصبشده در مزرعه
|
||
|
|
plants = گیاههای sync شده برای AI
|
||
|
|
weather = forecastهای location
|
||
|
|
irrigation_method = روش آبیاری انتخابشده
|
||
|
|
AI general context = farmer-block aggregated snapshot + sensor payload + weather + plant + irrigation
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 16) جمعبندی نهایی
|
||
|
|
|
||
|
|
اگر بخواهیم یک قانون ساده و پایدار برای کل سیستم تعریف کنیم:
|
||
|
|
|
||
|
|
- `farm_data` مالک رکورد AI-facing مزرعه است.
|
||
|
|
- `location_data` مالک geometry، بلاکها، subdivision و remote sensing است.
|
||
|
|
- `weather` مالک forecastهای location است.
|
||
|
|
- `plant` و snapshotهای آن مالک context گیاهی مزرعهاند.
|
||
|
|
- `irrigation` مالک روش آبیاری است.
|
||
|
|
|
||
|
|
و از نظر محاسبات عمومی AI:
|
||
|
|
|
||
|
|
- سطح تصمیمگیری باید `کل main block`های کشاورز باشد.
|
||
|
|
- `sub_block`ها فقط جزئیات داخلی و تحلیلی هستند.
|
||
|
|
- اگر بلاکی تعریف نشده بود، مدل ذهنی و دادهای پیشفرض باید `1 main block + 1 sub-block داخلی` باشد.
|
||
|
|
|