Dijital ikiz durum senkronizasyonu; bağlantı kopmaları, gecikmeler ve çelişen komutlar olsa bile ikizin inandığı şey ile varlığın gerçekte olduğu şeyi uyum içinde tutma problemidir. Bu makale bildirilen ve istenen (reported vs desired) durum modelini, sürümlemeyi ve çakışma yönetimini ve bunların retained MQTT topic'leriyle nasıl uygulanacağını açıklar; sonunda da istenen duruma yakınsayan çalışan bir Python cihazı bulacaksınız.
Genel bağlam için dijital ikiz teknolojisi rehberiyle başlayın.
Senkronizasyon Neden Zordur
Bir ikiz yalnızca doğruysa işe yarar. Pratikte buna karşı çalışan birkaç etken vardır:
- Gecikme: ikiz bir değeri, değiştikten birkaç milisaniye veya saniye sonra görür.
- Bağlantı kopmaları: bir cihaz çevrimdışı olur ve farklı bir durumla geri gelir.
- Eşzamanlı yazıcılar: bir operatör ve bir otomasyon aynı ayar noktasını değiştirir.
- Sıralama: farklı yollardan gelen mesajlar sırası bozuk ulaşır.
- Kısmi hata: bir komut iletilir ama onay mesajı kaybolur.
İyi bir senkronizasyon tasarımı bu sorunları ortadan kaldırmaz; onları görünür ve kurtarılabilir kılar.
Bildirilen ve İstenen Durum Modeli
Device shadow servisleri ve Eclipse Ditto gibi ikiz çerçeveleriyle yaygınlaşan en çok kullanılan kalıp, durumu iki belgeye ayırır:
| Belge | Yazan | Anlamı |
|---|---|---|
| Bildirilen (reported) | Yalnızca cihaz | Varlığın şu anda gerçekte ne olduğu |
| İstenen (desired) | Uygulamalar, operatörler, otomasyonlar | Varlığın ne olması gerektiği |
| Fark (delta, türetilmiş) | Hesaplanır | İstenenin bildirilenden farklı olduğu alanlar |
Altın kural: bildirilen durumu yalnızca cihaz yazar. Bir pompanın 1.450 rpm'de çalışmasını isteyen bir uygulama desired.speed = 1450 yazar. Cihaz farkı görür, aksiyon alır ve ardından reported.speed = 1450 bildirir. İkiz arayüzü her ikisini de gösterir; böylece operatörler "istenen" ile "ulaşılan"ı ayırt edebilir.
Bu ayrım, arayüzün makinenin aslında hiç ulaşmadığı bir değeri gösterdiği bütün bir hata sınıfını ortadan kaldırır. Hafif shadow'ların ve tam ikizlerin bunu nasıl uyguladığını device shadow ve dijital ikiz karşılaştırmasında karşılaştırın.
MQTT ile Dijital İkiz Durum Senkronizasyonunu Uygulamak
MQTT, retained topic'ler kullanarak bu modele temiz bir şekilde eşlenir:
connectmq/demo/<id>/plant1/pump7/state/reported retained, written by device
connectmq/demo/<id>/plant1/pump7/state/desired retained, written by apps
connectmq/demo/<id>/plant1/pump7/status retained, last will "offline"
Her iki durum topic'i de retained olduğundan broker en son sürümü abone olan herkese iletir:
- İkizi açan bir görüntüleyici bildirilen ve istenen durumu hemen alır.
- Bir kesintiden sonra yeniden bağlanan bir cihaz, istenen değişiklik o çevrimdışıyken yapılmış olsa bile en son istenen durumu hemen alır ve ona yakınsayabilir.
Bu ikinci özellik sağlam senkronizasyonun anahtarıdır. Ayar noktası türündeki değişiklikler için ayrı bir "bekleyen komutlar" deposuna ihtiyacınız yoktur; retained istenen belge, bekleyen komutun kendisidir. Retained ikiz durumu üzerine eksiksiz bir eğitim için retained MQTT ile dijital ikiz yazısına bakın.
İstenen Durum ve Komutlar
İstenen durumu idempotent hedefler için kullanın: ayar noktaları, modlar, yapılandırma, aç/kapat. Bunları iki kez uygulamak, bir kez uygulamakla aynı etkiye sahiptir. Aksiyonlar için retained olmayan komutlar kullanın: bir sayacı sıfırlamak, bir kapıyı beş saniyeliğine açmak, bir kalibrasyon başlatmak. Bir aksiyon komutunu retained yapmak, onu her yeniden bağlanmadan sonra tekrar oynatır; bu da tehlikelidir.
Sürümleme ve Çakışma Yönetimi
Her durum belgesi bir sürüm ve bir zaman damgası taşımalıdır:
{
"version": 42,
"ts": 1792152000000,
"source": "operator:maria",
"speed_rpm": 1450,
"mode": "AUTO"
}
Bunları çakışmaları yönetmek için kullanın:
- İstenen durumda monoton artan sürümler. Yazıcılar güncel istenen belgeyi okur, sürümü artırır ve yayınlar. Tüketiciler, zaten uygulanmış olandan daha düşük sürüme sahip her istenen mesajı yok sayar.
- Bildirilen durum, uygulanan istenen sürümü geri yansıtır. Cihaz bildirilen belgesine
desired_version: 42ekler; böylece ikiz hangi isteğin karşılandığını bilir. - Son yazan kazanır, ama açıkça. Çoğu ayar noktası için en son sürüm kazanır. Arayüzün değişikliği kimin yaptığını gösterebilmesi için
sourcebilgisini kaydedin. - Gerektiğinde alan düzeyinde birleştirme. Bir uygulama
speedalanının, bir diğerimodealanının sahibiyse, tüm belgeyi değiştirmek yerine alan düzeyinde birleştirin ya da bunları ayrı retained topic'lere bölün. - Cihaz tarafı korumalar. Cihaz güvenli limitlerin dışındaki değerleri sessizce yok saymak yerine reddeder ve reddi bir gerekçeyle bildirir.
Broker'lar sürümleri sizin için zorunlu kılmaz; bu, yayıncılarınızdaki ve cihazdaki mantıktır. MQTT 5 user property'leri, payload'a dokunmadan sürüm meta verisi taşımak için kullanışlı bir yerdir.
Yeniden Bağlanmaları ve Bayat Durumu Yönetmek
Bir cihaz yeniden bağlandığında sabit bir sırayı izleyin:
Durum topic'inde bir Last Will ile bağlanın.
Durum topic'ine retained olarak
onlineyayınlayın.Tam güncel bildirilen durumu, en son ne gönderdiğine dair bellekten değil, yerel sensörlerden okuyarak retained olarak yayınlayın.
İstenen topic'ine abone olun ve retained istenen belgeyi alın.
Farkı hesaplayın, uygulayın ve güncellenmiş bildirilen durumu yayınlayın.
adımın 4. adımdan önce gelmesi önemlidir: ikiz önce gerçeği öğrenir, ardından cihaz hedefe yakınsar. Sırayı tersine çevirirseniz bir görüntüleyici kısa bir süre için bayat bildirilen durumu güncelmiş gibi görebilir.
İkiz tarafında, durum topic'i offline dediğinde bildirilen durumu bayat kabul edin. Eski değerleri canlıymış gibi göstermek yerine griye çevirin.
İstenen Duruma Yakınsayan Çalışan Bir Cihaz
Bu Python cihazı herkese açık broker'ı ve paho-mqtt 2.x'i kullanır. 9c4e değerini kendi rastgele önekinizle değiştirin.
import json
import time
import paho.mqtt.client as mqtt
BASE = "connectmq/demo/9c4e/plant1/pump7"
LIMITS = {"speed_rpm": (0, 3000)}
reported = {"version": 0, "desired_version": 0, "speed_rpm": 0, "mode": "MANUAL"}
def publish_reported(client):
reported["version"] += 1
reported["ts"] = int(time.time() * 1000)
client.publish(f"{BASE}/state/reported", json.dumps(reported), qos=1, retain=True)
def on_connect(client, userdata, flags, reason_code, properties):
client.publish(f"{BASE}/status", "online", qos=1, retain=True)
publish_reported(client)
client.subscribe(f"{BASE}/state/desired", qos=1)
def on_message(client, userdata, msg):
if not msg.payload:
return
desired = json.loads(msg.payload)
if desired.get("version", 0) <= reported["desired_version"]:
return
errors = []
for key, value in desired.items():
if key in ("version", "ts", "source"):
continue
low, high = LIMITS.get(key, (None, None))
if low is not None and not (low <= value <= high):
errors.append(f"{key} out of range")
continue
reported[key] = value
reported["desired_version"] = desired["version"]
reported["errors"] = errors
publish_reported(client)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="pump7-9c4e")
client.will_set(f"{BASE}/status", "offline", qos=1, retain=True)
client.on_connect = on_connect
client.on_message = on_message
client.connect("broker.connectmq.com", 1883)
client.loop_forever()
Başka bir terminalden istenen bir durum ayarlayın:
mosquitto_pub -h broker.connectmq.com -p 1883 -i pump7-ops-9c4e -q 1 -r \
-t "connectmq/demo/9c4e/plant1/pump7/state/desired" \
-m '{"version": 1, "source": "operator", "speed_rpm": 1450, "mode": "AUTO"}'
Cihazı durdurun, yeni bir hızla 2. sürümü yayınlayın, ardından cihazı yeniden başlatın. Cihaz abone olduğunda retained istenen belgeyi alır ve hemen yakınsar. Her şeyi mosquitto_sub -t "connectmq/demo/9c4e/plant1/pump7/#" -v ile izleyin.
Senkronizasyon Durumunu İkiz Arayüzünde Göstermek
Senkronizasyon mantığı işin yalnızca yarısıdır; operatörlerin bunu görmesi gerekir. İyi bir ikiz arayüzü senkronizasyon durumunu gizlemek yerine açıkça gösterir:
- Beklemede: istenen değer bildirilenden farklı ve cihaz çevrimiçi. Hedef değeri gerçek değerin yanında gösterin; örneğin "1.450 rpm istendi, gerçek 1.200 rpm".
- Uygulandı: bildirilen durum en son istenen sürümü yansıtıyor. Yalnızca gerçek değeri gösterin.
- Reddedildi: cihaz o sürüm için bir hata bildirdi. Gerekçeyi gösterin ve önceki değeri koruyun.
- Bayat: durum topic'i çevrimdışı diyor. Değerleri griye çevirin ve en son ne zaman güncellendiklerini gösterin.
Bir zaman aşımı da ekleyin. İstenen bir değişiklik, cihazın normalde ihtiyaç duyduğundan daha uzun süre beklemede kalırsa, makinenin komuta uyduğunu varsaymak yerine birinin incelemesi için bunu işaretleyin. Bu durumlar doğrudan yukarıda anlatılan sürümlü bildirilen ve istenen belgelerden çıkar; bu yüzden ek altyapı gerektirmez.
Durum Senkronizasyonu Kontrol Listesi
- Bildirilen durumu yalnızca cihaz, istenen durumu yalnızca uygulamalar yazar.
- Her iki belge de sürüm ve zaman damgasıyla retained tutulur.
- Bildirilen durum, en son uygulanan istenen sürümü yansıtır.
- Aksiyonlar, onay mesajlı ve retained olmayan komutlar olarak gönderilir.
- Retained bir durum topic'inde Last Will bulunur.
- Cihaz yeniden bağlanırken istenen durumu uygulamadan önce bildirilen durumu yayınlar.
- Cihaz limitleri doğrular ve retleri bildirir.
- Arayüz istenen ve bildirilen durumu gösterir, çevrimdışı ikizleri bayat olarak işaretler.
Dijital İkizlerinizi ConnectMQ Üzerinde Senkronize Edin
ConnectMQ; retained mesajları, Last Will'i, plana bağlı olarak QoS 0 ila 2'yi ve user property, message expiry ve session expiry gibi MQTT 5 özelliklerini destekleyen tam yönetilen bir MQTT servisidir. Portalın canlı topic gezgini, bildirilen ve istenen belgeleri yan yana incelemeyi kolaylaştırır. Senkronizasyon mantığınız normal MQTT istemcileri olarak bağlanan cihazlarda ve uygulamalarda yaşarken teslimatı buluttaki MQTT broker üstlenir. Kredi kartı gerekmeden app.connectmq.com adresinde Serverless planla ücretsiz başlayın.
Sık Sorulan Sorular
Bildirilen ve istenen durum arasındaki fark nedir?
Bildirilen durum, cihazın gerçekte ne olduğudur ve yalnızca cihaz tarafından yazılır. İstenen durum ise uygulamaların cihazın ne olmasını istediğidir. Aralarındaki fark, cihazın üzerinde aksiyon alması gereken delta'dır.
İstenen durum için neden retained mesajlar kullanılmalı?
Retained bir istenen belge, cihaz abone olur olmaz ona iletilir; böylece cihaz çevrimdışıyken yapılan değişiklikler yeniden bağlandığında otomatik olarak uygulanır.
Bir dijital ikize gelen çelişen güncellemeleri nasıl çözerim?
İstenen belgelere sürüm ve kaynak ekleyin, eski sürümleri yok sayın, sahipliğin farklı olduğu yerlerde alan düzeyinde birleştirin ve cihazın doğrulama yapıp gerçekte neyi uyguladığını bildirmesini sağlayın.
Komutlar retained yapılmalı mı?
Hayır. Yalnızca idempotent istenen durumu retained yapın. Aksiyon komutları retained olmamalı; ideal olarak message expiry ve bir onay mesajıyla birlikte gönderilmelidir.



