Parquet'tan Gömülü Cihazlara Config Kayıtlarını İdempotent Şekilde Senkronize Etme

Filo cihazları genellikle bağlı düğüm/sensörlerin yerel bir kaydını tutar (seri numarası, bus adresi, isim, bölge, kalibrasyon değerleri). Yetkili veri bir veritabanında yaşadığında temiz aktarım şudur: Parquet’e dışa aktar → her cihazın HTTP API’sine karşı idempotent bir senkronizasyon script’i çalıştır. Bu yazı, ikinci adım için yeniden kullanılabilir bir desen ve pratikte her zaman yaşanan kısmi hataların nasıl ele alınacağını gösteriyor.

Tasarım kuralları

  1. İdempotent: Script’i iki kez çalıştırmak aynı duruma yakınsamalı — “create or update”, asla “create or fail” değil.
  2. Adres-farkında oluşturma: Geçerli bus adresi olmayan kayıtlar zorla oluşturulamaz — çalışmayı iptal etmek yerine net bir raporla atlanmalı.
  3. Alan-bazlı güncellemeler: Her özniteliği (isim, bölge, setpoint’ler) kendi endpoint’i üzerinden gönder, böylece kısmi kayıt girdileri bile tamamen dolar.
  4. Doğrulama: Sonrasında, cihazın bildirdiği kayıt sayısını Parquet satır sayısıyla karşılaştır.

Senkronizasyon script’i

sync_devices.py
#!/usr/bin/env python3
"""Bir Parquet dosyasındaki düğüm config kayıtlarını cihaz API'sine idempotent şekilde senkronize et."""
import sys
import pandas as pd
import requests

HOST = sys.argv[1]          # örn. 10.0.0.42
PARQUET = sys.argv[2]
BASE = f"http://{HOST}/api"
TIMEOUT = 10

df = pd.read_parquet(PARQUET)
ok, skipped, failed = 0, [], []

def post(path, payload):
    r = requests.post(f"{BASE}{path}", json=payload, timeout=TIMEOUT)
    return r

for _, row in df.iterrows():
    serial = str(row["SerialNo"])
    addr = row.get("CANAddress")

    # Bus'ta adreslenemeyen kayıtları atla —
    # ancak cihaz kendisi keşfettiğinde ortaya çıkabilirler.
    if pd.isna(addr) or int(addr) == 0:
        skipped.append(serial)
        continue

    try:
        # Oluştur-veya-birleştir kimliği: seri numarası zaten var
        # olsa da adrese yeni bağlansa da endpoint 200 döner.
        r = post("/nodes/merge", {
            "serial": serial,
            "address": int(addr),
        })
        if r.status_code == 404:
            skipped.append(f"{serial} (not in registry, address {int(addr)})")
            continue
        r.raise_for_status()

        # Alan-bazlı güncellemeler — her biri idempotent.
        post("/nodes/set-type",  {"serial": serial, "type": row["Type"]}).raise_for_status()
        for field, ep in [("Name", "name"), ("Name2", "name2"),
                          ("Name3", "name3"), ("Name4", "name4"),
                          ("ZoneName", "zone")]:
            val = row.get(field)
            if pd.notna(val) and str(val):
                post(f"/nodes/set-{ep}", {"serial": serial, "value": str(val)}).raise_for_status()
        if pd.notna(row.get("SetVoltage")):
            post("/nodes/set-voltage", {"serial": serial,
                                        "value": float(row["SetVoltage"])}).raise_for_status()
        if pd.notna(row.get("SetCurrent")):
            post("/nodes/set-current", {"serial": serial,
                                        "value": float(row["SetCurrent"])}).raise_for_status()
        ok += 1
    except requests.RequestException as e:
        failed.append((serial, str(e)))

print(f"OK: {ok}/{len(df)}  skipped: {len(skipped)}  failed: {len(failed)}")
for s in skipped:
    print(f"  skip {s}")
for s, e in failed:
    print(f"  FAIL {s}: {e}")

Doğrulama

verify.py
import requests, pandas as pd
device = requests.get(f"http://{HOST}/api/nodes", timeout=10).json()
expected = len(pd.read_parquet(PARQUET))
print(f"device: {len(device)} records, parquet: {expected}")

Sayılar meşru şekilde farklı olabilir: cihaz ekstra otomatik-keşfedilmiş düğümler tutabilir ve atlanan kayıtlar (geçerli adres yok) ancak bus onları bulduğunda ortaya çıkar.

Kısmi hataları ele alma

BelirtiAnlamEylem
Merge’de 404/ESP_ERR_NOT_FOUNDSeri numarası henüz kayıtta değil VE adres eksik/0Bus keşfini bekle, yeniden çalıştır — geri kalanı zaten deploy edildi
Bir kayıtta HTTP 400Bozuk alan değeriSatırı düzelt, yeniden çalıştır (idempotent)
Cihaza ulaşılamıyorAğ/VPN sorunuYine de Parquet’i filo dizinine deploy et; ulaşılabilir olunca senkronize et

Önemli özellik: keşiften sonra script’i yeniden çalıştırmak, cihazın kendi bulduğu düğümlerde isimleri/bölgeleri/setpoint’leri doldurur — hiçbir şey kaybolmaz, hiçbir şey çoğaltılmaz.

İlgili


Check out similar posts by category: Gömülü Python