Tüm yazılar
Programlama İpucu

Geçiş Koşulu mu, Hedef Durum mu? İki Adım Sıçrayan Dolum Hattı

Üç gün sorunsuz dönen bir şişe dolum hattı, hızı artırdığımız gün şişeyi görmeden kapak basmaya başladı. Adım merdiveninde geçiş koşulunu 'hedef durum'la karıştırdığım hatayı ve 'her geçiş önce şu an hangi adımdayım diye sormalı' kuralını kendi vaka üzerinden anlatıyorum.

Geçiş Koşulu mu, Hedef Durum mu? İki Adım Sıçrayan Dolum Hattı

Cumartesi, öğleden sonra. Şişe dolum hattı. Üç gündür sorunsuz dönüyordu, ritmi oturmuştu, operatör çay molasına çıkıp güvenle arkasını dönüyordu. Sonra dördüncü gün öğlen, büyük bir sipariş geldi, hattın hızını artırdık. O gün, makine şişeyi görmeden kapak basmaya başladı. “Hızlı dönmeye başladığında” diyecek bir şey yoktu aslında; sorun hızda değildi. Sorun bendeydi, üç gün önceki bir satırdı.

Operatör dedi ki, “abi makine şişeyi görmüyor, direkt kapağı basıyor.” Ben de içimden “imkânsız” dedim, çünkü adım merdiveninde (state machine) geçiş 3’ten 4’e “şişe sensörü ON” olmadan verilemez diye yazmıştım. Ama makine öyle yazmadım. Makine, benim yazdığımı yapıyordu. Yalnız ben yanlış yazmıştım.

Sorun: “Şu Adımda mıyım?” Sorusunu Sormamak

Adım merdiveni şu işe yarar: makinenin “şu an hangi adımda olduğunu” tek bir değişkende tutar, adim. Ve her geçiş, “önce şu an hangi adımda mıyım, sonra geçme koşulu sağlandı mı” diye iki şeyi birden sorar. Doğru yazım şu:

if (adim == 3 and sise_sensor)  adim := 4;
if (adim == 4 and kalk_sensor)  adim := 5;
if (adim == 5 and bas_sensor)   adim := 6;

Ben ne yazmıştım? Şunu:

if (sise_sensor)  adim := 3;
if (kalk_sensor)  adim := 4;
if (bas_sensor)   adim := 5;

Fark küçük görünüyor: adim == 3 and kısmını kaldırdım çünkü “zaten o adımda olmalıyordum” diye düşünmüştüm. Yavaş çalışırken öyleydi de. Her sensör, tur başına sadece bir anlık ON oluyor, üst üste binmüyordu. Ama hızı artırdığımızda şişe, 3. adımın penceresi hâlâ açıkken 4. adımın sensörüne kadar ilerlemişti. O tek scan içinde hem kalk_sensor hem bas_sensor bir anlık OK düştü. Makine 3’ten 5’e sıçradı. 4’ü, yani “şişeyi yerleştir”i, atladı. Kapağı, boş bir konuma bastı.

Üç gün boyunca yavaş tempoda o and’ın eksikliği hiçbir şey göstermedi. Dördüncü gün hız, hatayı gün yüzüne çıkardı. Bu, sahanın en sinsi türüdür: kodun doğru çalışıp da girdi hızlandıkça yanlışlaşması.

Masadaki maliyeti on dakika. Simülatörde iki adımı hızlı üst üste ver, sıçramayı üçüncü turda görürsün. Sahadaki maliyeti dört saat. Çünkü “program mı yanlış, sensör mü, ayar mı” diye saatlerce döndük.

Geçiş Koşulu Nedir, Hedef Durum Değildir

Aslına bakarsan yazdığım hatanın adı var: geçiş koşulunu, hedef durumla karıştırmak. Geçiş koşulu, “şu an bulunduğum adımdayken, ileri gitmemi sağlayan anlık olay”dır. Hedef durum ise “varacağım adımın özelliğidir.” Ben bas_sensoru bir geçiş koşulu gibi kullandım, oysa o, 5. adımın sonucudu. Sonucu, tetikleyici yaptım.

Kural şu: her geçiş satırı, if (adim == i and gecis_kosulu_i) adim := i+1; formunda olmalı. adim == i kısmı, merdiveni “tek kuralık” yapar: aynı scan’da birden fazla geçiş yan yana olsa da, yalnızca mevcut adımınki tetiklenebilir. Geri kalanı o turda sessiz kalır. O küçük and, iki adım sıçramayı, üç adım sıçramayı, “nasıl oldu bu” sorusunu hepsini keser.

Bir de ek kural var, onu da ekleyeyim. Geçiş koşulunu nedenden yaz, sonuçtan değil. “Şişe geldi” bir nedendir. “Kapak basıldı” bir sonuçtur. Sonucu tetikleyici yaparsan, makine kendi kendini itmiş olur.

Bu kural, adım merdiveni mi, durum otomatı mı, “şu adımda mı” diye sormadan yazılan büyük if zincirleri mi — hangisiyle çalışırsan çalış, aynı. Fark etmez. Fark etsin.

İpucu serisinin bir sonraki halkasında, “37 zamanlayıcım vardı, tek bir %T yazdım ve makine garip garip dönmeye başladı” diye başlayan hikâyeyi anlatıyorum. Find-replace’in %T1’le %T10’u nasıl karıştırdığı, ve neden numara değil isim kullanıyorum.