Blog

UMT Modelinden C++ Koduna: Bir Kapı Kontrolcüsü 30 Dakikada

Yayınlanma tarihi 2026-05-25

UMT Modelinden C++ Koduna: Bir Kapı Kontrolcüsü 30 Dakikada

Durum makinesi tabanlı gömülü yazılım geliştirirken çoğu ekip aynı soruyla karşılaşır: modeli kim yazacak, kodu kim yazacak ve ikisi nasıl senkronize kalacak?

Bu yazıda UMTSM'in bu soruya verdiği cevabı somut bir örnekle gösteriyorum. Bir kapı kontrolcüsünü .umt dosyasından başlayarak üretime hazır C++ koduna dönüştürüyoruz — adım adım.


Problem: Otomatik ve Manuel Modda Çalışan Bir Kapı

Sistem şu gereksinimleri karşılamalı:

  • Kapı otomatik modda açıldığında belirli bir süre bekledikten sonra kendiliğinden kapanır.
  • Manuel modda kapı yalnızca butona basıldığında hareket eder.
  • İki mod arasında çalışma sırasında geçiş yapılabilir.
  • Kapı açılırken ya da kapanırken mod değişikliği hareketi kesmez.
  • Sistem kapandıktan sonra son durumunu hatırlar — cihaz yeniden başlatıldığında kaldığı yerden devam eder.

Bunlar gerçek bir ürün gereksinimi. Elle yazılmış bir FSM'de bu beş maddeyi temiz tutmak haftalar sürebilir. Eğer sistemi kabaca bir UML diagramı olarak ifade ecek olursak

Çift Kullanımlı Kapı Durum Makinesi Diagramı
Çift Kullanımlı Kapı Durum Makinesi Diagramı

Bu durum makinesini UMTSM'de ifade edersek, .umt dosyası şöyle olur:


Adım 1: Durum Makinesini Modellemek

type sm Engine;

sm Door
{
    persistent deep history -> ManualMode_Open;

    state ManualMode
    {
        Open:
            ButtonPressed -> Closing / engineRunACCW;

        Closing:
            entry / engineRunACCW;
            DoorClosed / engineStop -> Close;
            ButtonPressed / engineStop -> Opening;

        Close:
            ButtonPressed -> Opening;

        Opening:
            entry / engineRunCCW;
            DoorOpen / engineStop -> Open;
            ButtonPressed / engineStop -> Closing;

        Automatic -> AutomaticMode;
    }

    state AutomaticMode
    {
        Open:
            entry / resetWaitingTime;
            do    / wait;
            ButtonPressed / resetWaitingTime;
            -> Closing;

        Closing:
            entry / engineRunACCW;
            DoorClosed / engineStop -> Close;
            ButtonPressed / engineStop -> Opening;

        Close:
            ButtonPressed -> Opening;

        Opening:
            entry / engineRunCCW;
            DoorOpen / engineStop -> Open;

        Manual -> ManualMode;
    }
}

Birkaç satırda neler tanımladık:

  • İki üst seviye bileşik durum: ManualMode ve AutomaticMode
  • Her birinde dört alt durum: Open, Closing, Close, Opening
  • Otomatik modda do / wait ile zamanlayıcı tabanlı kapanma
  • persistent deep history ile güç kesintisinden kurtarma
  • type sm Engine ile Engine durum makinesine bağımlılık bildirimi

Adım 2: Kod Üretimi

UMTSM modeli parse edip hiyerarşik bir iç temsile (IR) dönüştürür. CppGen bu IR'dan aşağıdaki dosyaları üretir:

Üretilen Dosyalar
Üretilen Dosyalar
DosyaAmacı
Door.hhDurum Makinesi sınıf bildirimi
Door.cppDurum Mekinesi
Door_DataType.hhDuurum Makinesi veri yapısı
Door_Types.hhTüretilmiş tip bildirimleri
Door_Auxilary.cpp.templateKullanıcı yazacağı kısıt ve eylem fonksiyonları için şablonlar
Door_DataType.cpp.templateVeri yapılarının devreye alınma ve devre dışı bırakılması için şablon
Door_UserTypes.hh.templateKullanıcı tanımladığı harici veri tip tanımları dosyası için şablon

Üretilen dosyalar dokunulmaz — bir sonraki üretimde yeniden yazılır. Kullanıcıya ait dosyalar src/door/ altında yaşar ve üretimden etkilenmez.


Adım 3: Aksiyonları İmplemente Etmek

Üretilen şablona bakarak src/door/Door_Auxilary.cpp dosyasını oluşturuyoruz:

void Door::engineRunCCW([[maybe_unused]] Door_DataType const& input)
{
    instanceData.doorActionTimeStart = std::chrono::system_clock::now();
    instanceData.pEngine->trigger_runCCW();
}

void Door::engineRunACCW([[maybe_unused]] Door_DataType const& input)
{
    instanceData.doorActionTimeStart = std::chrono::system_clock::now();
    instanceData.pEngine->trigger_runACCW();
}

void Door::engineStop([[maybe_unused]] Door_DataType const& input)
{
    instanceData.pEngine->trigger_stop();
}

void Door::resetWaitingTime([[maybe_unused]] Door_DataType const& input)
{
    instanceData.waitUntil =
        std::chrono::system_clock::now() + DOOR_AUTO_CLOSE_DELAY;
}

void Door::wait([[maybe_unused]] Door_DataType const& input)
{
    std::this_thread::sleep_until(instanceData.waitUntil);
}

Aksiyonlar virtual metodlardır — SM iskeletini değiştirmeden farklı donanım için farklı implementasyon sağlanabilir. Simülasyonda motor thread'e uyuyarak taklit edilir; gerçek donanımda GPIO çıkışı sürer.


Adım 4: Sistemi Birleştirmek

src/main/main.cpp içinde tüm durum makineleri örneklenir ve birbirine bağlanır:

Door     door;
Engine   engine;
Button   button;
// ...

door.instanceData.pEngine   = &engine;
button.instanceData.pDoor   = &door;

engine.start();
door.start();      // persistent history burada yüklenir
button.start();
// ...

door.start() çağrıldığında load_Deep_Main() kalıcı depolamadan son durumu okur. İlk açılışta varsayılan olarak ManualMode::Open'dan başlar. Sonraki açılışlarda son aktif durum geri yüklenir.


Adım 5: Çalıştırmak

cmake -S . -B build/Release -DCMAKE_BUILD_TYPE=Release
cmake --build build/Release
./build/Release/src/main/door

Uygulama tam ekran ncurses arayüzüyle açılır:

Simülasyon Kontrol Paneli
Simülasyon Kontrol Paneli

A tuşuna basıldığında sistem otomatik moda geçer. Kapı açılır, zamanlayıcı başlar, süre dolunca kapanma başlar. Bu arada M tuşuna basıldığında kapı mevcut hareketini sürdürür — motor durmaz — ama artık manuel modun kuralları geçerlidir.


Ne Elde Ettik?

GereksinimYaklaşım
Otomatik kapanmado / wait + completion transition
Manuel modButon eventi ile hareket
Mod geçişiManual / Automatic eventi, simetrik geçişler
Hareket sırasında mod geçişiGeçiş entry action'ı yok, motor devam eder
Güç kesintisi sonrası kurtarmapersistent deep history + store_Deep_Main

Toplam elle yazılan kod: ~150 satır aksiyon implementasyonu ve ~50 satır main.cpp. Geri kalan her şey üretildi.


Sonuç

UMTSM'in bu örnekte sağladığı şeyin özü şu: modeli ve kodu aynı kaynak dosyasından türetmek. .umt dosyası değiştiğinde üretilen iskelet güncellenir, kullanıcı implementasyonları korunur. Model ile kod hiçbir zaman senkronizasyonu kaybetmez.

Örnek kodun tamamı GitHub'da: https://github.com/demiralp/umtsm-examples-cpp

UMTSM ile ilgili sorularınız veya projeleriniz için iletişime geçin.