Uskunani integratsiya qilish — API
Korxonalarning fon monitoring va sanoat manbalari gaz-analizatori uskunalarini EKO-NAZORAT tizimiga ulash bo'yicha texnik qo'llanma. Integratsiya ikki bosqichdan iborat: uskunani sertifikatlash va API orqali ma'lumotlarni uzatish.
1 Uskunani sertifikatlashtirish
Integratsiyadan oldin o'lchov uskunasi (gaz-analizator / fon monitoring stansiyasi) O'zbekiston Respublikasi Ekologiya va iqlim o'zgarishi milliy qo'mitasi huzuridagi "Davlat ekologik sertifikatlashtirish va standartlashtirish markazi" tomonidan sertifikatlanishi shart. Sertifikat uskunaning metrologik yaroqliligini va o'lchovlar haqiqiyligini tasdiqlaydi.
Huquqiy asoslar
Quyidagi normativ-huquqiy hujjatlar talablari ijrosini ta'minlash maqsadida sanoat korxonalarining atrof-muhitga salbiy ta'sirini kamaytirish va aholining xabardorligini oshirish uchun ustuvor manbalarga o'rnatilgan avtomatik monitoring stansiyalari Ekologiya vazirligining Situatsion markaziga integratsiya qilinadi.
Qonunlar
Prezident farmonlari va hukumat qarorlari
- atmosfera havosiga ustuvor turg'un (tashkillashtirilgan) manbalardan chiqadigan tashlanmalarni tahlil qiluvchi avtomatik monitoring stansiyalarini o'rnatish;
- yuqori samaradorlikka ega chang-gaz tozalash uskunalari va lokal suv tozalash inshootlarini o'rnatish hamda mavjudlarini modernizatsiya qilish;
- sanitar himoya zonalarini avtomat statsionar kuzatuv postlari bilan jihozlash.
Standartlar
Natija
- Uskunaga ekologik sertifikat beriladi (raqam va amal muddati bilan).
- Ushbu sertifikat 2-bosqichdagi arizaga biriktiriladi.
2 API orqali integratsiya (PULL)
EKO-NAZORAT tizimi ma'lumotni o'zi tortib oladi (PULL). Korxona o'z tarafida ochiq JSON API endpointini taqdim etadi; qo'mita serveri unga davriy (odatda har 15 daqiqada) murojaat qilib, oxirgi o'lchovlarni o'qiydi va tizimga saqlaydi.
- Korxona ma'lumotlarni API spetsifikatsiyasi bo'yicha JSON qaytaradigan endpoint tayyorlaydi.
- Endpoint manzili va kirish ma'lumotlari center@uznature.uz ga yuboriladi — ariza formasi orqali.
- Qo'mita administratori endpointni tekshirib, tizimga Fetcher sifatida ulaydi.
- Ulangach, uskuna avtomatik ravishda xaritalar, hisobotlar va nazoratda ko'rinadi.
manbaraqami — takrorlanmaydigan davlat manba raqamini — berish tavsiya etiladi.
Aks holda faqat stansiya nomi ishlatiladi va boshqa korxona bilan to'qnashishi mumkin.
So'rov (Request)
Qo'mita serveri korxona endpointiga quyidagicha murojaat qiladi:
| Parametr | Qiymat |
|---|---|
| Metod | GET |
| Javob turi | application/json — massiv (array) bo'lishi shart |
| Timeout | 20 soniya |
| Autentifikatsiya * | Majburiy — Basic Auth yoki Bearer token |
| Davriylik | ~15 daqiqada bir marta |
Autentifikatsiya (majburiy)
Endpoint himoyalangan bo'lishi shart — ochiq (autentifikatsiyasiz) manzillar qabul qilinmaydi. Quyidagi ikki usuldan birini qo'llang:
# Basic Auth Authorization: Basic base64(login:parol) # yoki Bearer token Authorization: Bearer <token>
Javob strukturasi
Endpoint stansiyalar massivini qaytaradi. Har bir element — bitta manba (stansiya):
| Maydon | Turi | Izoh |
|---|---|---|
manbaraqami | string | Tavsiya — unikal manba raqami (serial). Bo'lmasa name ishlatiladi. |
name * | string | Stansiya / manba nomi |
organization_name | string | Korxona (tashkilot) nomi |
tashkilot_inn | string | Tashkilot STIR/INN (9 raqam) |
address | string | Manzil |
latitude / longitude | number | Koordinatalar |
stansiya | string | Stansiya turi (CEMS — sanoat manbasi, AQMS — fon) |
trubadiametri | string | Quvur diametri (masalan "500mm") |
timestamp * | string | O'lchov vaqti, ISO 8601 (2026-07-28T11:00:00+05:00) |
devices[].data[] * | array | O'lchovlar: har biri {"parameter":"...","value":...,"unit":"..."} |
data[].parameter * | string | Modda kaliti (masalan so2) |
data[].value * | number | O'lchangan qiymat |
data[].unit | string | O'lchov birligi (masalan mg/m³, %, °C) |
data[].name | string | Moddaning to'liq nomi — jadvalda yo'q (nostandart) moddalar uchun tavsiya etiladi |
* — majburiy maydonlar.
Parametrlar — PDV asosida
Korxona o'zining PDV (ruxsat etilgan chiqindilar normativi) hujjatidan kelib chiqib, manbada o'lchanadigan barcha ifloslantiruvchi moddalarni yuboradi. Havoga chiqindilar juda ko'p turda bo'lishi mumkin — parametrlar ro'yxati sizning PDV'ingiz bilan belgilanadi, qat'iy chegaralanmaydi.
Har bir modda alohida
{ "parameter": "<kalit>", "value": <son>, "unit": "<birlik>" }
juftligi sifatida beriladi — har bir o'lchovda uning birligi (unit) ham ko'rsatiladi.
Qiymatlar mg/m³ da kutiladi (PM uchun µg/m³ ham sozlanishi mumkin — arizada belgilang).
Standart parametr kalitlari
Keng tarqalgan moddalar uchun quyidagi standart parameter kalitlaridan foydalaning:
pm25PM2.5 chang zarrasi · mg/m³pm10PM10 chang zarrasi · mg/m³dustUmumiy chang · mg/m³so2Oltingugurt dioksidi (SO₂) · mg/m³noAzot oksidi (NO) · mg/m³no2Azot dioksidi (NO₂) · mg/m³coUglerod oksidi (CO) · mg/m³co2Uglerod dioksidi (CO₂) · %o2Kislorod (O₂) · %o3Ozon (O₃) · mg/m³nh3Ammiak (NH₃) · mg/m³h2sVodorod sulfidi (H₂S) · mg/m³hfVodorod ftoridi (HF) · mg/m³temperatureHarorat · °ChumidityNamlik · %pressureAtmosfera bosimi · hPawind_speedShamol tezligi · m/swind_directionShamol yo'nalishi · °gas_flowGaz sarfi · m³/soatgas_speedGaz tezligi · m/sdiff_pressureBosim farqi · Paparameter— modda nomining lotincha, kichik harfli, bo'shliqsiz kaliti (masalanbenzol,fenol,formaldegid);value— o'lchangan qiymat,unit— birligi (majburiy);name— moddaning to'liq nomi (ixtiyoriy, aniqlik uchun);- shu moddalar ro'yxatini arizada ham ko'rsating — qo'mita mutaxassislari kalitni tizimga moslaydi (mapping).
JSON namuna
Endpoint qaytaradigan to'liq javob namunasi:
[
{
"manbaraqami": "TSH-0102-001",
"name": "1-quvur (tutun mo'risi)",
"organization_name": "\"Angren IES\" AJ",
"tashkilot_inn": "301234567",
"address": "Toshkent viloyati, Angren sh.",
"stansiya": "CEMS",
"trubadiametri": "1200mm",
"latitude": 41.0167,
"longitude": 70.1436,
"timestamp": "2026-07-28T11:00:00+05:00",
"devices": [
{
"data": [
{ "parameter": "so2", "value": 0.42, "unit": "mg/m³" },
{ "parameter": "no2", "value": 0.18, "unit": "mg/m³" },
{ "parameter": "co", "value": 3.10, "unit": "mg/m³" },
{ "parameter": "dust", "value": 0.05, "unit": "mg/m³" },
{ "parameter": "o2", "value": 6.5, "unit": "%" },
{ "parameter": "temperature", "value": 145.0, "unit": "°C" },
{ "parameter": "gas_flow", "value": 12000, "unit": "m³/soat" },
{ "parameter": "benzol", "value": 0.002, "unit": "mg/m³", "name": "Benzol" },
{ "parameter": "fenol", "value": 0.001, "unit": "mg/m³", "name": "Fenol" }
]
}
]
}
]
benzol,
fenol) — standart jadvalda yo'q moddalarga misol: ular ham
xuddi shu tarzda parameter + value +
unit (va ixtiyoriy name) bilan yuboriladi.
Bir endpoint bir nechta manbani (massivda bir nechta element) qaytarishi mumkin. Har chaqiruvda
oxirgi (joriy) o'lchovni bering — tizim timestamp bo'yicha takrorlarni o'zi filtrlaydi.
Namunaviy server (Python)
Quyida tayyor namunaviy (reference) dastur keltirilgan. U COM (serial) port orqali
gaz-analizatordan ma'lumot o'qiydi va yuqoridagi formatda GET /api/device-data
endpointini host qilib turadi. Autentifikatsiya majburiy (Basic/Bearer), uskuna ulanmagan
bo'lsa — simulyatsiya rejimi ishlaydi. Kodni o'z uskunangiz protokoliga moslang.
1. O'rnatish
pip install flask pyserial
2. Sozlash
Dasturdagi SOZLAMALAR bo'limini tahrirlang: autentifikatsiya (login/parol yoki token),
manba metadatasi (manbaraqami, INN, koordinatalar), COM port
(COM_PORT, masalan Windows: "COM3",
Linux: "/dev/ttyUSB0") va PARAMETERS ro'yxati.
3. Ishga tushirish
python eco_integration_server.py
4. Sinash
# autentifikatsiyasiz — 401 curl -i http://127.0.0.1:8080/api/device-data # to'g'ri Basic Auth — 200 + JSON curl -u ekonazorat:StrongParol123! http://127.0.0.1:8080/api/device-data
To'liq kod — eco_integration_server.py
Namunaviy server kodini ko'rsatish (Python)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
EKO-NAZORAT — Integratsiya namunaviy serveri (REFERENCE / EXAMPLE)
=================================================================
Ushbu dastur korxona tomonida ishlaydi:
1) COM (serial) port orqali gaz-analizator / monitoring stansiyasidan
o'lchov ma'lumotlarini uzluksiz o'qiydi;
2) o'qilgan oxirgi qiymatlarni EKO-NAZORAT hujjatidagi JSON formatida
`GET /api/device-data` endpointida host qilib turadi;
3) endpoint autentifikatsiya bilan himoyalangan (Basic Auth yoki Bearer).
Ekologiya vazirligining Situatsion markazi shu endpointdan (VPN/HTTPS orqali)
ma'lumotni davriy tortib oladi.
Bu — NAMUNA. `SOZLAMALAR` bo'limini va `parse_serial_line()` funksiyasini
o'z uskunangiz protokoliga moslang.
Talablar: Python 3.8+, pip install flask pyserial
Ishga tushirish: python eco_integration_server.py
"""
import base64
import json
import random
import threading
import time
from datetime import datetime, timezone, timedelta
from functools import wraps
from flask import Flask, Response, jsonify, request
try:
import serial # pyserial
except ImportError:
serial = None # COM port bo'lmasa — simulyatsiya rejimi ishlaydi
# ═══════════════════════════════════════════════════════════════════════
# SOZLAMALAR — shu bo'limni o'zingizga moslang
# ═══════════════════════════════════════════════════════════════════════
# ── Server ──────────────────────────────────────────────────────────────
HOST = "0.0.0.0" # barcha interfeyslar (VPN/tarmoq orqali kirish uchun)
PORT = 8080
# ── Autentifikatsiya (MAJBURIY) ─────────────────────────────────────────
# "basic" → login/parol; "bearer" → token
AUTH_TYPE = "basic"
BASIC_USER = "ekonazorat"
BASIC_PASS = "StrongParol123!" # ← albatta o'zgartiring
BEARER_TOKEN = "almashtiring-uzun-tasodifiy-token"
# ── Vaqt mintaqasi (Toshkent = UTC+5) ───────────────────────────────────
TASHKENT_TZ = timezone(timedelta(hours=5))
# ── Manba (stansiya) metadatasi ─────────────────────────────────────────
STATION = {
"manbaraqami": "TSH-0102-001", # unikal davlat manba raqami
"name": "1-quvur (tutun mo'risi)",
"organization_name": '"Korxona" MChJ',
"tashkilot_inn": "301234567",
"address": "Toshkent viloyati, Angren sh.",
"stansiya": "CEMS", # CEMS=sanoat manbasi, AQMS=fon
"trubadiametri": "1200mm",
"latitude": 41.0167,
"longitude": 70.1436,
}
# ── COM (serial) port sozlamalari ───────────────────────────────────────
# COM_PORT = None bo'lsa yoki port ochilmasa → SIMULYATSIYA rejimi yoqiladi
# Windows: "COM3" Linux: "/dev/ttyUSB0"
COM_PORT = None
BAUDRATE = 9600
SERIAL_TIMEOUT = 2 # soniya
# ── Parametrlar xaritasi ────────────────────────────────────────────────
# (standart_kalit, birlik, analizator_liniyasidagi_xom_nom, toliq_nom)
# - Standart kalitlar uchun `name` ni bo'sh qoldiring.
# - Jadvalda YO'Q (nostandart) moddalar uchun `name` ni to'ldiring
# (masalan benzol, fenol) — qo'mita mapping'ni shunga qarab sozlaydi.
PARAMETERS = [
# kalit birlik xom nom to'liq nom (nostandart uchun)
("so2", "mg/m³", "SO2", ""),
("no2", "mg/m³", "NO2", ""),
("co", "mg/m³", "CO", ""),
("dust", "mg/m³", "DUST", ""),
("o2", "%", "O2", ""),
("temperature", "°C", "T", ""),
("gas_flow", "m³/soat", "FLOW", ""),
# ── nostandart modda misoli (PDV'dan) ──
("benzol", "mg/m³", "BENZOL", "Benzol"),
("fenol", "mg/m³", "FENOL", "Fenol"),
]
# ═══════════════════════════════════════════════════════════════════════
# Oxirgi o'lchov — thread-safe ombor
# ═══════════════════════════════════════════════════════════════════════
_latest_lock = threading.Lock()
_latest_values = {} # {xom_nom: float}
_latest_ts = None # datetime (o'lchov vaqti)
def _set_latest(values: dict):
global _latest_values, _latest_ts
with _latest_lock:
_latest_values = dict(values)
_latest_ts = datetime.now(TASHKENT_TZ)
def _get_latest():
with _latest_lock:
return dict(_latest_values), _latest_ts
# ═══════════════════════════════════════════════════════════════════════
# COM port o'quvchi — fon oqimda uzluksiz ishlaydi
# ═══════════════════════════════════════════════════════════════════════
def parse_serial_line(line: str) -> dict:
"""
Analizatordan kelgan bitta matn qatorini {xom_nom: float} ga aylantiradi.
NAMUNA format (o'zingiznikiga moslang):
SO2:0.42;NO2:0.18;CO:3.10;DUST:0.05;O2:6.5;T:145.0;FLOW:12000
Ko'p analizatorlar Modbus RTU (RS-485) ishlatadi — u holda `pymodbus`
kutubxonasidan foydalanib registrlarni o'qing va shu dict'ni qaytaring.
"""
result = {}
line = line.strip()
if not line:
return result
for token in line.replace(",", ";").split(";"):
if ":" not in token:
continue
raw_name, raw_val = token.split(":", 1)
raw_name = raw_name.strip().upper()
try:
result[raw_name] = float(raw_val.strip().replace(",", "."))
except ValueError:
continue
return result
def serial_reader_loop():
"""COM portni ochib, uzluksiz o'qiydi. Port bo'lmasa — simulyatsiya."""
if serial is None or not COM_PORT:
print("⚠ COM port sozlanmagan yoki pyserial yo'q — SIMULYATSIYA rejimi.")
_simulation_loop()
return
while True:
try:
with serial.Serial(COM_PORT, BAUDRATE, timeout=SERIAL_TIMEOUT) as ser:
print(f"✅ COM port ochildi: {COM_PORT} @ {BAUDRATE}")
while True:
raw = ser.readline().decode("ascii", errors="ignore")
values = parse_serial_line(raw)
if values:
_set_latest(values)
except Exception as e:
print(f"🟥 COM port xatosi: {e} — 5s dan keyin qayta urinaman...")
time.sleep(5)
def _simulation_loop():
"""Uskuna ulanmagan holda API'ni sinash uchun tasodifiy qiymatlar."""
base = {
"SO2": 0.40, "NO2": 0.18, "CO": 3.0, "DUST": 0.05, "O2": 6.5,
"T": 145.0, "FLOW": 12000, "BENZOL": 0.002, "FENOL": 0.001,
}
while True:
values = {k: round(v * random.uniform(0.85, 1.15), 4) for k, v in base.items()}
_set_latest(values)
time.sleep(5)
# ═══════════════════════════════════════════════════════════════════════
# Autentifikatsiya
# ═══════════════════════════════════════════════════════════════════════
def _check_auth() -> bool:
hdr = request.headers.get("Authorization", "")
if AUTH_TYPE == "basic":
if not hdr.startswith("Basic "):
return False
try:
decoded = base64.b64decode(hdr[6:]).decode("utf-8")
user, _, pwd = decoded.partition(":")
except Exception:
return False
return user == BASIC_USER and pwd == BASIC_PASS
elif AUTH_TYPE == "bearer":
return hdr == f"Bearer {BEARER_TOKEN}"
return False
def require_auth(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
if not _check_auth():
resp = Response(
json.dumps({"error": "Autentifikatsiya talab qilinadi"}),
status=401, mimetype="application/json",
)
if AUTH_TYPE == "basic":
resp.headers["WWW-Authenticate"] = 'Basic realm="eko-nazorat"'
return resp
return fn(*args, **kwargs)
return wrapper
# ═══════════════════════════════════════════════════════════════════════
# JSON javobni yig'ish
# ═══════════════════════════════════════════════════════════════════════
def build_payload():
values, ts = _get_latest()
if ts is None:
ts = datetime.now(TASHKENT_TZ)
data = []
for key, unit, raw_name, full_name in PARAMETERS:
if raw_name not in values:
continue
item = {"parameter": key, "value": values[raw_name], "unit": unit}
if full_name: # nostandart modda — to'liq nom
item["name"] = full_name
data.append(item)
station = dict(STATION)
station["timestamp"] = ts.isoformat()
station["devices"] = [{"data": data}]
return [station] # hujjat bo'yicha — massiv (array)
# ═══════════════════════════════════════════════════════════════════════
# Flask ilova
# ═══════════════════════════════════════════════════════════════════════
app = Flask(__name__)
@app.route("/api/device-data", methods=["GET"])
@require_auth
def device_data():
payload = build_payload()
return Response(
json.dumps(payload, ensure_ascii=False),
mimetype="application/json",
)
@app.route("/health", methods=["GET"])
def health():
_, ts = _get_latest()
return jsonify({
"status": "ok",
"last_reading": ts.isoformat() if ts else None,
"mode": "serial" if (serial and COM_PORT) else "simulation",
})
def main():
# COM port o'quvchini fon oqimda ishga tushiramiz
t = threading.Thread(target=serial_reader_loop, daemon=True)
t.start()
print("─" * 60)
print(f" EKO-NAZORAT integratsiya serveri")
print(f" Endpoint : http://{HOST}:{PORT}/api/device-data")
print(f" Auth : {AUTH_TYPE}")
print(f" Manba : {STATION['manbaraqami']} — {STATION['name']}")
print("─" * 60)
# DIQQAT: ishlab chiqarishda HTTPS (TLS) yoki VPN ortidan ishlating.
app.run(host=HOST, port=PORT, threaded=True)
if __name__ == "__main__":
main()
Ariza yuborish
Maydonlarni to'ldiring va "Arizani yuborish" tugmasini bosing. Ma'lumotlar tayyor xat ko'rinishida center@uznature.uz ga yuborish uchun e-pochta ilovangizda ochiladi.