This commit is contained in:
2026-05-11 03:27:21 +03:30
parent cf7cbb937c
commit d0e68a1a56
854 changed files with 102985 additions and 76 deletions
+499
View File
@@ -0,0 +1,499 @@
# پیشنهاد معماری سنسور برای Backend و AI
## مسئله اصلی
تو سناریوی شما ممکن است:
- یک مزرعه چندین سنسور داشته باشد
- سنسورها از vendorهای مختلف باشند
- هر سنسور payload متفاوتی بفرستد
- بعضی سنسورها یک metric مشترک مثل `soil_moisture` را هم‌زمان گزارش کنند
- AI مجبور باشد هم raw data را بفهمد و هم یک view تجمیع‌شده برای recommendation داشته باشد
پس معماری سنسور باید این سه ویژگی را هم‌زمان داشته باشد:
1. **انعطاف‌پذیری در نوع سنسور**
2. **قابلیت نگهداری چند سنسور برای یک farm**
3. **قابلیت تبدیل raw sensor data به metricهای استاندارد برای AI**
---
## چیزی که الان در پروژه دارید
### در Backend
- `Backend/farm_hub/models.py` مدل `FarmSensor` را دارد برای register کردن سنسورهای هر مزرعه
- `Backend/sensor_catalog/models.py` مدل `SensorCatalog` را دارد برای تعریف نوع/کاتالوگ سنسور
- `Backend/sensor_external_api/models.py` لاگ raw requestهای دستگاه‌ها را نگه می‌دارد
یعنی Backend الان تا حد خوبی نقش **device registry + ingestion gateway + audit log** را دارد.
### در AI
- `Ai/farm_data/models.py::SensorData` داده سنسورها را در `sensor_payload` نگه می‌دارد
- `Ai/farm_data/models.py::SensorParameter` پارامترهای قابل پشتیبانی هر `sensor_key` را نگه می‌دارد
- `Ai/farm_data/services.py` روی payloadهای چند سنسوره metric resolve می‌کند
یعنی AI الان نقش **farm context store + normalized sensor context** را دارد.
---
## جمع‌بندی نقش‌ها
به نظر من بهترین تفکیک این است:
### Backend = owner دستگاه و جریان ورود داده
Backend باید مسئول این‌ها باشد:
- ثبت سنسور برای مزرعه
- نگهداری metadata دستگاه
- احراز هویت request دستگاه
- ثبت raw payload
- forward کردن داده به AI
### AI = owner داده تحلیلی و context مزرعه
AI باید مسئول این‌ها باشد:
- نگهداری normalized view از داده سنسورها برای هر farm
- استانداردسازی metricها
- merge / aggregation / conflict resolution
- feed کردن irrigation / fertilization / crop simulation / alerts / RAG
---
## پیشنهاد معماری نهایی
## 1) در Backend فقط registry و ingestion را canonical نگه دار
### `SensorCatalog`
این جدول باید تعریف‌کننده template سنسور باشد:
- `code`
- `name`
- `description`
- `returned_data_fields`
- `sample_payload`
- `customizable_fields`
- `supported_power_sources`
ولی بهتر است بعداً این metadataها را قوی‌تر کنی:
- `measurement_schema`
- `supported_metrics`
- `payload_mapping`
- `transport_type` مثل `http`, `mqtt`, `lorawan`
- `vendor`
- `model`
- `firmware_constraints`
### `FarmSensor`
این جدول باید instance واقعی دستگاه روی مزرعه باشد.
پیشنهاد فیلدهای مفهومی:
- `farm`
- `sensor_catalog`
- `physical_device_uuid`
- `name`
- `sensor_type`
- `installation_zone`
- `depth_cm`
- `position`
- `status`
- `last_seen_at`
- `calibration`
- `specifications`
- `power_source`
- `metadata`
یعنی `FarmSensor` باید بگوید:
- این دستگاه چیست
- کجا نصب شده
- چه زمانی آخرین بار data فرستاده
- به چه farm تعلق دارد
---
## 2) در Backend raw event را جدا از registry نگه دار
الان `SensorExternalRequestLog` فقط لاگ request است. این خوب است، ولی برای مقیاس‌پذیری بهتر است دو لایه داشته باشی:
### لایه اول: audit log
همان چیزی که الان داری:
- raw payload
- request time
- physical device uuid
- farm uuid
### لایه دوم: normalized ingestion event
اگر بخواهی معماری تمیزتر شود، بهتر است یک مدل جدا هم داشته باشی، مثلاً:
- `SensorReadingEvent`
که در آن این‌ها ذخیره شود:
- `farm_sensor`
- `recorded_at`
- `received_at`
- `payload_raw`
- `payload_normalized`
- `ingestion_status`
- `validation_errors`
این مدل لازم نیست الان فوراً ساخته شود، ولی اگر سنسورها زیاد شوند خیلی کمک می‌کند.
---
## 3) در AI داده سنسورها را به شکل sensor-centric نگه دار، نه فقط metric-centric
الان `Ai/farm_data/models.py::SensorData` این ساختار را دارد:
```json
{
"sensor-7-1": {
"soil_moisture": 22.4,
"soil_temperature": 18.1
},
"leaf-sensor": {
"leaf_wetness": 11
}
}
```
این از نظر انعطاف‌پذیری خوب است، ولی یک ضعف دارد:
- `sensor_key` بیشتر نوع سنسور را نشان می‌دهد، نه instance سنسور
اگر یک farm دو سنسور از یک type داشته باشد، این ساختار collision می‌دهد.
### پیشنهاد بهتر
در AI payload را بر اساس **sensor instance** نگه دار، نه فقط type:
```json
{
"device:8c1e...": {
"sensor_key": "sensor-7-1",
"sensor_type": "soil_probe",
"recorded_at": "2026-05-02T10:15:00Z",
"metrics": {
"soil_moisture": 22.4,
"soil_temperature": 18.1
},
"metadata": {
"depth_cm": 20,
"zone": "north-1"
}
},
"device:91af...": {
"sensor_key": "sensor-7-1",
"sensor_type": "soil_probe",
"recorded_at": "2026-05-02T10:15:30Z",
"metrics": {
"soil_moisture": 24.1,
"soil_temperature": 17.9
},
"metadata": {
"depth_cm": 40,
"zone": "north-1"
}
}
}
```
یعنی key اصلی بهتر است یکی از این‌ها باشد:
- `physical_device_uuid`
- یا `farm_sensor.uuid`
نه فقط `sensor-7-1`.
---
## 4) در AI یک لایه normalized metrics جدا بساز
AI برای recommendation نباید هر بار raw payload را از صفر تفسیر کند.
بهترین مدل ذهنی این است:
### لایه A: raw sensor context
- هر sensor instance چه چیزی فرستاده؟
### لایه B: resolved farm metrics
- برای farm در این لحظه `soil_moisture` نهایی چقدر است؟
- source این metric کدام sensorها بوده؟
- strategy حل conflict چه بوده؟
الان `Ai/farm_data/services.py` بخشی از این کار را انجام می‌دهد. این مسیر درست است.
پیشنهاد من:
- `sensor_payload` = raw/near-raw normalized by device
- `resolved_metrics` = خروجی استاندارد شده برای AI
- `metric_sources` = توضیح اینکه هر metric از کجا آمده
این‌ها لازم نیست حتماً همگی DB column جدا باشند؛ فعلاً می‌توانند در service layer ساخته شوند. ولی از نظر معماری باید explicit باشند.
---
## 5) resolution strategy باید قابل تنظیم باشد
وقتی چند سنسور یک metric مشترک دارند، AI باید بداند چطور resolve کند.
مثلاً برای `soil_moisture`:
- اگر چند سنسور هم‌عمق و هم‌زون باشند → average
- اگر depth فرق دارد → shallow و deep را جدا نگه دار
- اگر یکی unhealthy باشد → ignore
- اگر یکی stale باشد → وزن کمتر بگیرد یا حذف شود
### بنابراین برای هر metric این چیزها مهم می‌شوند:
- `recorded_at`
- `depth_cm`
- `zone`
- `sensor_health`
- `priority`
- `confidence`
الان average ساده خوب است برای شروع، ولی برای طراحی نهایی کافی نیست.
---
## 6) schema mapping را از business logic جدا کن
Backend و AI نباید به aliasهای vendor-specific وابسته بمانند.
مثلاً:
- `moisture_percent`
- `soilMoisture`
- `moisture`
- `soil_moisture`
همه باید به یک metric استاندارد map شوند:
- `soil_moisture`
### بهترین محل این mapping
به نظر من mapping باید در یک لایه canonical تعریف شود:
- در `SensorCatalog`
- یا در AI داخل `SensorParameter.metadata`
مثلاً:
```json
{
"code": "soil_moisture",
"metadata": {
"aliases": ["moisture_percent", "soilMoisture", "moisture"],
"unit": "%",
"valid_range": [0, 100]
}
}
```
---
## پیشنهاد عملی برای Backend
## Backend چه چیزی نگه دارد؟
### مدل‌های اصلی
- `SensorCatalog` = تعریف نوع سنسور
- `FarmSensor` = دستگاه نصب‌شده روی مزرعه
- `SensorExternalRequestLog` = raw ingress log
### مسئولیت‌ها
- ثبت sensor inventory
- validate کردن physical device
- ثبت raw payload برای audit
- attach کردن payload به farm درست
- forward کردن payload به AI
### چیزی که Backend نباید owner آن باشد
- منطق aggregation نهایی برای recommendation
- conflict resolution تخصصی برای سنسورهای متعدد
- semantic interpretation نهایی برای AI outputs
---
## پیشنهاد عملی برای AI
## AI چه چیزی نگه دارد؟
### `SensorData`
برای هر `farm_uuid`:
- `sensor_payload` بر اساس device instance
- `plants`
- `irrigation_method`
- `center_location`
- `weather_forecast`
### `SensorParameter`
برای تعریف metricهای استاندارد:
- `sensor_key`
- `code`
- `name_fa`
- `unit`
- `data_type`
- `metadata`
ولی پیشنهاد می‌کنم `metadata` را برای این موارد غنی‌تر کنی:
- `aliases`
- `valid_range`
- `aggregation_strategy`
- `semantic_group`
- `recommended_for`
---
## ساختار پیشنهادی payload بین Backend و AI
به‌جای فرستادن فقط این:
```json
{
"sensor-7-1": {
"soil_moisture": 45.2
}
}
```
بهتر است به این سمت بروی:
```json
{
"device:22222222-2222-2222-2222-222222222222": {
"sensor_key": "sensor-7-1",
"sensor_type": "soil_probe",
"recorded_at": "2026-05-02T10:15:00Z",
"metrics": {
"soil_moisture": 45.2,
"soil_temperature": 22.5
},
"metadata": {
"depth_cm": 20,
"zone": "zone-a"
}
}
}
```
اگر فعلاً نمی‌خواهی API را بشکنی، حداقل این migration path را برو:
1. فعلاً `sensor_payload` فعلی را نگه دار
2. اجازه بده علاوه بر `sensor_key`، `physical_device_uuid` هم به AI برسد
3. در AI key داخلی را با device uuid بساز
4. بعداً schema را کامل migrate کن
---
## الگوی تصمیم‌گیری برای چند سنسور
برای AI پیشنهاد می‌کنم سه خروجی داشته باشی:
### 1) `raw_sensor_payload`
همه داده‌های هر device بدون از دست رفتن context
### 2) `resolved_metrics`
مثلاً:
```json
{
"soil_moisture": 23.2,
"soil_temperature": 18.5
}
```
### 3) `metric_sources`
مثلاً:
```json
{
"soil_moisture": {
"strategy": "weighted_average",
"sensor_keys": ["device:a", "device:b"],
"depths": [20, 40],
"conflict": true
}
}
```
این ساختار برای explainability خیلی مهم است.
---
## چیزی که من پیشنهاد می‌کنم همین الان تغییر بدهی
### تغییرات سریع و پرارزش
#### در Backend
- به `FarmSensor` این فیلدها را اضافه کن:
- `metadata`
- `installation_zone`
- `depth_cm`
- `last_seen_at`
- `status`
#### در AI
- `sensor_payload` را به‌سمت instance-based key ببر
- `SensorParameter.metadata` را برای alias و aggregation strategy غنی کن
- resolver فعلی را از `average ساده` به strategy-based resolver ارتقا بده
#### در API بین Backend و AI
- همراه payload این اطلاعات را هم بفرست:
- `physical_device_uuid`
- `sensor_catalog_uuid`
- `sensor_type`
- `recorded_at`
- `depth_cm` یا `zone`
---
## پیشنهاد naming
برای شفافیت بیشتر:
- `SensorCatalog` = نوع سنسور
- `FarmSensor` = سنسور نصب‌شده
- `SensorExternalRequestLog` = raw ingest log
- `SensorData` = farm sensor context
اگر بعداً مدل event اضافه کردی:
- `SensorReadingEvent` = reading normalized per device
---
## تصمیم نهایی من
اگر بخواهم خیلی خلاصه بگویم:
- **Backend** باید registry, ingestion, audit را handle کند
- **AI** باید normalization, aggregation, context-building, recommendation input را handle کند
- key اصلی داده سنسور باید **sensor instance** باشد، نه فقط `sensor_key`
- raw sensor data و resolved farm metrics باید از هم جدا باشند
---
## نتیجه یک‌خطی
برای سنسورهای انعطاف‌پذیر و چندتایی، Backend را به‌عنوان لایه ثبت دستگاه و ورود raw data نگه دار و AI را به‌عنوان لایه استانداردسازی و تجمیع metricها؛ و مهم‌تر از همه، داده‌ها را بر اساس **device instance** مدل کن نه فقط نوع سنسور.