# ارتباط `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 داخلی` باشد.