Thanks to visit codestin.com
Credit goes to engineeringvolkan.wordpress.com

Volkan's Space

Blog

  • 7 Yilda Ogrendigim 7 Sey

    7 Yilda Ogrendigim 7 Sey


    Uzatmalari da tamamlayarak 2019 Mart ayinda mezuniyetimi tamamladim. Gectigimiz haftalarda kaybettigimiz Prof. Dr. Turgut Ikiz hocamiza Allah’tan rahmet dileyerek bir giris yapmak benim icin odemem gereken bir vefa borcunun … Okulu bitirebilmis olmam konusunda emegi gozardi edilemez.

    7 senede 5 farkli is yeri, farkli calisma kulturleri gordum ve bunlardan cikarimlarimi 7 maddede toplamaya karar verdim.


    1. Calisilan Domain

    Hangi domain icin kod yazdiginiz uretkenliginiz uzerinde onemli bir rol oynuyor. Her domainin kendine ozgu dinamikleri, surecleri ve market regulasyonlari farkli oldugu icin kisiliginiz bu noktada devreye giriyor. Bazi domainler icin bazi insanlar uygun degil. Bu noktada kisa surede fazlaca is degistirerek sevdiginiz domaini, is yapilis bicimini bulmak kariyerinizin erken doneminde yapabileceginiz iyi bir yatirim olabilir. Uzun sure ayni domainde kalan insanlarin domain gecisi yaptiklarinda adapte olamadiklarini ve bir cogunun eski sirketine tekrar donus yaptiklarini gozlemledim.

    2. Farkli Ozgecmise Sahip Yoneticinin Olmasi

    Kurumsal sirketlerin aci gerceklerinden birisi de People Manager ve Technical Manager tarafini iyi yonetmekte zorlandiklari. En tehlikesi de People Manager’in Technical Manager rolunu ustleniyor olmasi olarak cikabiliyor karsimiza. Takim icin oldukca aci verici bir surec bu. Ayni dili konusamadigin bir yoneticinin olmasi bir sure sonra cok fazla yoruyor. Ozellikle sirket icerisinden degil, ithal edilmis bir People Manager’in Technical Manager rolunu almasi + zorlu bir domain catisi altindaysaniz sizi is degistirmeye zorlayabilir. Mutlu bir mentali olmayan yoneticilerin toksik kultur olusturduklarini gozlemledim.

    3. Ofis Politikleri

    Hayir, ofis politiklerinden kacmak sizi yonetim gozunde daha iyi bir yere koymayacak. Ofis politikleri konusunda iyi olmanin cok onemli yetenek oldugunu gozlemledim. Kulakligimi taktim, islerimi tamamliyorum, sprintlerimi kosuyorum sizi iyi politik yapan birisinden daha ozel kilmayacak. Politik belki kotu bir kelime gibi duruyor ama semsiye bir terim. Burada sirket ici pozisyonlari kovalama, alinan insiyatifler icin yeri geldiginde konusabilmek, sirket kulturune katki saglamak ve degistirmek istediginiz seyler icin uygun zemin hazirlamak. Bazen de disini gostermek olabilir. Dedikodu yapmaktan bahsetmiyoruz. Goze batmadan yapilan iyi politikanin para, kariyer olarak geri dondugunu gozlemledim.

    4. Is Ilanlarini Okuyabilmek

    Bir cok is ilani calisacaginiz ekip icerisinden birisi tarafindan ya da yoneticiniz tarafindan direkt olarak hazirlaniyor. Ilan icerisinde gereksinimlerin nasil ifade edildigi size bazi konularda ipucu verebilir. Ornegin ilanda (standart pozisyon) bu pozisyon direkt olarak yoneticiye rapor ediyor diyorsa, takim hiyerarsinin olmadigi, orada bir seylerin yanlis oldugunu unutmamakta fayda var. Eger ilanda kullanilan tech stack hakkinda versiyon bilgisi var ise calisacaginiz projenin legacy ya da modern bir sistem olduguna dair ipucu alabilirsiniz. Gorusmelerden once glassdoor, indeed gibi yerlerden sirket hakkinda bilgi almakta fayda var. Kurumsal yapi icerisinde startup kulturu ile calisan yerlerin hep sikintili oldugunu gozlemledim.

    5. Kodu Herkes Yaziyor

    Kod yazmak bir yere kadar, belli yil tecrubeden sonra uc assagi bes yukari herkes bir sistemi ayaga kaldiriyor ya da bir problemi cozuyor. Fakat nasil ayaga kaldirdigin, o problemi nasil cozdugun ve bunu nasil sundugun daha onemli hale geliyor. Bu yuzden yaptiginiz cozum uzerine bir kez daha dusunmenin iyi bir gelisim yolu oldugunu gozlemledim.

    6. Yurt Disi Tecrubesi

    14 ay once yurtdisina yerlestim, keske kariyerimin cok daha erken doneminde gelip deneyimleseydim diyorum. Iyi, kotu, ortalama bir bir cok yanlari var ve herkesin deneyimi kendince. Ancak her deneyimin ortak noktasi farkli bir vizyon kattigi. Teknik anlamda atom parcalanmiyor yurtdisinda calisinca ama farkli kulturlerden onlarca farkli insan ile calismak farkli bir soft skill kapisini aciyor. Bu yuzden yurtdisi tecrubesine sahip insanlarin takim yonetimi, insan yonetimi, teknik anlamda bakis acilarinin farkli oldugunu gozlemledim.

    7. Kendini Aktif Tutmak

    Herkes modern sistemlerde, guzel kod altyapilarinda calismiyor. Sirket size kendinizi gelistirmeniz icin maas odemiyor. Eger calisirken yeni seyler ogreniyorsaniz bu cok guzel bir sey. Fakat cogu zaman cok gercekci olmuyor. Teslim etmeniz gereken bir is var. Sirket icin calistiginiz zamaninin gun icerisinde en az %10’luk diliminde bir konuda makale okuyabilir, kendinizi gelistirebileceginizi dusunduguz kisisel bir yan projeye zaman ayirmanizi oneririm. Eger gun icerisinde bunu yapamiyorsaniz, haftasonu ya da aksamlari mutlaka bu zamani kendinize ayirmalisiniz. Ozellikle son zamanlarda sikca duydugumuz isten cikarmalarin oldugu bir ortamda kendimizi guncel tutmak, firsatlara acik olmak her zaman avantaj. Sirketler ailemiz degil, is cikarabildigimiz kadar iyiyiz, is cikaramadigimizda en kotusu siz olursunuz. Bu mentale sahip bireylerin daha mutlu oldugunu gozlemledim.

  • Ve Irlanda’da 1 yıl dolarken..

    Ve Irlanda’da 1 yıl dolarken..


    Huhh, 6 Ekim 2024, saat 04.15 sularında hayatımda ilk defa yurtdışına çıkmak üzere Istanbul Havalimanina varmamın üzerinden neredeyse 1 yıl olacak.

    Yurtdışına taşınmamın en önemli faktörlerinden birisi TR’de çalıştığım yerin fiyat/performans açısından iyi bir olmasıydı. Bu durum epey bir konfor alanı oluşturmuştu. Evet, rahatlık battı diyebiliriz bir noktada. Nedense o dönem attan inmek istemedim TR içerisinde, denemedim değil ama başka at bulamadım/bulduklarımdan ret yedim. Diğer bir tetiklenme noktam ise; geçmişte ve mevcutta çalıştığım/tanıdığım bir çok arkadaşımın yurtdışına göçlerine tanık olmaktı. Bir gün uyanıyorsun A kişisi yeni bir teklif almış, diğer bir gün B kişisi “Lan bu hafta görüşelim, haftaya Hollanda’ya taşınıyorum…”. O dönem Instagram hesabım olmadığı için, kim ne yapıyor onu da bilemiyordum. Kimse Facebook’ta paylaşım da yapmıyor artık. 😦

    Yurtdışı muhabbetleri 2023’ün 3.çeyreğinde askere gitmeden önce iyice ağır bastı. Markete bakmıyorum tabi de, Hollanda’ya giderim bir şekilde kafasındayım, askerliği tamamladıktan sonra. Peygamber ocağına gittim geldim, markete bir baktım Türkiye’nin teknoloji başkenti Hollanda için ilan neredeyse yok. Referans üzerinden bir kaç firma denedik, yok dönüş yok hiç yok. Biz yolumuzda olalım da Allah muhakkak bir kapı açar kafası, seri başvurudayım ama sadece LinkedIn üzerinden +130 başvuru atmışımdır 2024 ilk çeyreğinde. Vizyonum yok Almanya, Polonya, Brezilya, Ingiltere, denk geldikçe Hollanda, harita da yerini henüz bilmediğim ‘relocation’ verdiğini gördüğüm bazı yerler. Amacım yurtdışı mülakat tecrübesi kazanmak olursa da ‘kurulu düzenimizi’ bırakıp ufak ufak ilerlemek. Çünkü mülakatı vermek ayrı bir disiplin istiyor. 6-7 firmadan mülakat alma şansım oldu ve 2’sinden teklif alabildim. Bir tanesi tamamen uzaktan ve Türkiye’den çalışmalı, diğeri ise Almanya’da orta ölçekli bir firmaydı. Uzaktan çalışmalı işin parası o dönemde kazandığım paranın oldukça üzerindeydi fakat prosedürsel şeyler ve tamamen uzaktan olması, 2 yıllık kontratlı olması gibi nedenlerden ötürü kendimi koyamadım oraya. Almanya’da ki firmanın ‘relocation’ veremeyeceğini, vize süreçleri de dahil kendim yönetmem gerektiğini belirtmesi, verdikleri maaşı bir arkadaşıma sorduğumda, ‘yaşarsın ama paylaşımlı dairelerde kalman gerekir’ demesi üzerine kabul etmedim. Hali hazırda bir konforum var yaşamımda, belki bunu tamamen koruyamayacağım illa ki ama en azından o konforu koruyabileceğim kadar korumalıyım noktasına evrildim işler ciddiye binice.

    Mülakatlardan çıkarımlarım ve yurtdışı mentaline iyice büründükten sonra şunları anladım; Çok ciddi bir dil bariyerim var ve bu durum mülakat özgüvenimi tamamen kırıyor, diğer nokta ise oturup hackerrank, leetcode vs gibi ‘production’ da oldukça anlamsız bulduğum ama üzerine çalışmam gereken platformlar. Ve artık maaş bazında ortalama Avrupa değerimi ve ne istediğimi bir nebze olsun bilir durumdaydım. Ne yazık ki ilk durum olan dil bariyerimi en azından kısa sürede çözemeyeceğimi anladım, fakat bunun için yapabileceklerim vardı, sunum hazırlamak ve bu sunuma çalışmak. Ben kimim, hangi konularda çalıştım, neleri biliyorum üzerine sunum hazırladım ve bu sunum üzerine çalıştım. Anlattıklarımdan olası gelecek sorulara karşı da hazırlık yaptım. Çünkü henüz gerçekten teknik aşamaya gelmeden, dil bariyerimden ötürü karşı tarafta oluşan algıyı kırmak istiyordum. Ve benim ana dili Ingilizce olan ülkelerde şansımın daha fazla olduğunu anladım. Elbette mülakatı yapan kişinin ana dili ingilizce de denk gelmeyebilir ama kem-küm ettiğimde dahi karşı taraf ne demek istediğimi, sorduğu soruyu cevaplayabilecek kapasitede olup olmadığımı mülakatı yapan kişi anlayabiliyordu. 6-7 firmadan birisi Ingiltere merkezli ve dil bariyerim konusunda geri bildirim almıştım. Süreçler iyi gitmiş fakat onlar için riskliydim, ama kem-kümlerinden bir şeyler bildiğimi anlayabilmişlerdi. Tüm bunların yanında +100 kolay-orta hackerrank tamamladım. Yazı ile yazmak kolay ama bunların hepsi gerçekten ekstra ciddi eforlar. Bu süreçte iş spamlayı da bıraktım.

    Bu noktadan sonra odağımız temiz, gençliğimiz var artık. Ingiltere, Irlanda, Kanada, Singapur benim için çalışır. Öncesinde FAANG firmalara başvurum çok minumum iken, hazır hackerrank sonrası elim sıcakken bunlara da ağırlık verdim. Tekrar başvuru döngüsündeyim ve FAANG’lardan birisiyle görüşme yakaladım, offline hackerrank tokatladım ve 2 Hintli ile teknik mülakata girdim. Hoş geldim, Namaste hocam derken, sunumumu çok kısa sürede kestiler ve live coding sürecinde 1.5/3 gibi bir performans sergiledim. Offline olsa yapabileceğim soruları o anki heyecan ve mülakatı yapan hintli arkadaşların etkisi ile tamamlayamadım.(Birilerine suç atmam gerekiyor). Mülakattan sonra tek hatırladığım ciddi anlamda terlediğim ve çok keyifsiz oluşum.

    Hupps, Bir mail İrlanda’da başvurduğum firma PPA ve GIA testleri yollamış. Takip eden günlerde WP üzerinden +353 alan koduna sahip bir arama, hepimizin başına geldiği gibi Nijeryalı ya da Pakistanlı bir dolandırıcıdır diye telefonu açmadım. Sonra +353 nerenin diye baktım ve bir saniye bir saniye İrlanda 🙂 Tekrardan aradım ve görüşme için ne zaman müsait olduğumu sordular, Irish insanların aksanları inanılmaz farklı, öyle ki başka bir bölgeden bir irish başka bölgeden bir irish’i anlamakta zorluk çekiyor. Ben telefon görüşmesinde zorlandığım için mail üzerinden müsait zamanımı ileteceğimi söyledim ve 1 hafta sonrasına bir zaman verdim. Bu süreçte soluğu reddit’te alarak maaş skalası, irlanda özelinde araştırmalar yaptım ve tek bir ortak nokta ev krizi. Ev krizinin ne demek olduğunu buraya gelmeden önce anlam veremiyordum. Cebinizde paranızın olması sizi kurtarmıyor. Aynı eve sizin gibi parası olan +20 (iyimser bir rakam olabilir) kiralamak için aynı gün sıraya giriyor ve evi görüyor. Ya da paylaşımlı evlere mahkumsunuz. (Saving açısından çok iyi olabilir ama mentaliniz ile bunun karşılığını ödeyebilirsiniz, bankada daha çok param olmasını anlamsızca gülmeye tercih etmek istemiyordum.)

    HR görüşmesi öncesi Notepad’de tüm sorularımı listeledim ve ‘relocation’ kapsamını detaylıca sordum, ev bulma konusunda destek veriyor musunuz ? Evet, veriyoruz seni lokal bir ajans ile iletişime geçiriyoruz onlar sana yardımcı oluyor, başlayacağın zaman hala bulamaz isen AirBnb’ye yerleştiriyoruz ve faturasını bize yansıtıyorsun cevabı beni aşırı rahatlattı. Maaş konusunda müzakere olmadığını, kendilerinin bir excel’i olduğu konusu soru işaretleri arasında yerini aldı geçen görüşme içerisinde. Yinede beklentimi belirttim. Görüşme sonrasında mail ile tüm anladıklarımı özetleyip gönderdim yazılı olması açısından.

    İlk teknik görüşmede sunum çok güzel çalıştı, 15dk kadar sunum yaptım ve anlattıklarım üzerinden sorular sordular ve cevapladım. Elbette fazlaca kem-küm ettim. Teknik olarak görece basit sorular sordular ve kem-küm misali cevapladım. 1 saatlik görüşmenin ardından, aynı gün içerisinde dönüş sağlandı ve 2. Teknik görüşme için gün istediler. Görüşmede live-coding olup olmadığını, beni nelerin beklediğini sordum. Üstü kapalı bir cevap aldım ve live-coding olmayacağı belliydi. 2.Teknik görüşme de bir Irish, Software-Arch rolünde çalışan birisi ve Portekizli Senior Software rolünde çalışan başka bir arkadaş geldi. Mülakat güzel gidiyordu ta ki Irish birey sorular sormaya başlayalı, ne sorduğunu tam anlamıyor, tekrar tekrar sorduruyor ve hatta chat’e yazmasını istiyordum. Sonra sorduğu sorulardan bazıları için ekran paylaşıp kod yazarak anlatmaya başladım. Sanıyorum ki hoşuna gitti de geldim buraya. Mülakat sonunda iyi mi geçti kötü mü geçti çok arafta kalmıştım. Benim tarafım için iyiydi fakat iletişim için red-flag gelir diye düşünüyordum.

    Hey, how you doin ? Aradan bir kaç saat sonrasında bir telefon aldım ve Good news kelimelerini içeriyordu, yöneticinin görüşme yapmak istediğini belirttiler, izne çıkmadan görüşme yapmak istemesi üzerine ertesi güne görüşme yapmak zorunda kaldım. Ve o noktada anlıyorsunuz bir şeyler başardığınızı (yok yok). O noktada anladım olumlu olduğunu ve görüşmenin prosedürsel olacağını. 30dk kadar davranışsal sorular üzerine görüşme gerçekleştirdik ve en büyük korkumun dil bariyerim olduğunu belirttim.

    … vize süreçleri, çalışma izni derken 3 ay kadar bekledim.

    Yurtdışına taşınmak benim için hayatımın dönüm noktalarından birisi oldu, kendimi aştım, tazelendim. Korkularımla yüzleştim, sabretmeyi öğrendim, yavaşlığın içerisinde zamanı kendim için hızlandırmayı öğrendim. Hayatıma major değişiklikler ekledim, dilimi çok iyi ölçüde geliştirdim. TR’de hiç arabam olmamıştı, araba kullanmayı bilmiyordum burada ilk arabamı aldım ve kullanmayı öğreniyorum. Sigara kullanıyordum, çok büyük ölçüde azalttım ve bırakma noktasındayım. Haftada 2-3 gün spora gitmeye başladım. Hiking olayına aşık oldum. 20-25km’yi gören patikası olmayan yerlerde yürümeye aşık oldum. Aynı dili değil, aynı duygu, heyecanı paylaşmanın üstün olduğunu öğrendim. Yağmurun altında, yağmur damlalarını öperek başka bir dudakla birleştim. Coğrafya kader midir sorusunun cevabını gördüm. Dezavantajlarımı ? Elbette var ama şuan benim için çalışıyor burası. İlla ki bir noktada çalışmayacak ve döneceğim bunu da biliyorum.

    Şimdi mi ? Çalışma hayatım çok yolunda gitmedi maalesef ve bunun finansal/mental anlamda da bazı sonuçları oldu. Sonraki 1 yıl için dileğim üzerine koyarak toparlanmak. Şu an iş değiştirme sürecindeyim, bakalım nasıl olacak… Bunlardan bahsettiğim sonraki içerikte görüşmek üzere. Ve her şey için bin ŞÜKÜR..

  • Neden override sözcüğünü kullanmak zorundasın ? Paranormal Kod

    Neden override sözcüğünü kullanmak zorundasın ? Paranormal Kod


    Derleyicileri kızdırmak, şikayetlenmelerini sağlamak bir çok durum için run-time sırasında ağlamaktan daha iyi bir alternatif. Aynı code-base içerisinde çalıştığınız diğer kişiler içinse, haberin olsun şu an bir şeyler yaptığın satıra ait fonksiyonun bulunduğu sınıfın bir atası var ve sahip olduğu bu atanın içerisinde bu fonksiyonun bir imzası var. Bu imzayı koruman gerekiyor.

    Base ve Derived hiyerarşisine sahip bir yapımız olduğunu düşünelim. Fakat Derived sınıf içerisinde bulunan msgHandle fonksiyonun imzası Base sınıftan daha farklı olsun ve override niteleyicisini de eklemeyelim. nobody's gonna know how they gonna know yaklaşımı ile kod yazıyoruz.

    struct Base
    {
      virtual void msgHandle(uint64_t type)
      {
        std::cout << "Handled from Base" << std::endl;
      }
    };
    
    struct Derived : Base
    {
      virtual void msgHandle(uint32_t type)
      {
        std::cout <<  "Handled from Derived" << std::endl;
      }
    };
    
    

    x86-64 gcc 14.2 ile derleme yapıyoruz ve vtable‘ı görebilmek için dump alalım.

    ...
    ...
    ...
    
    vtable for Derived:
            .quad   0
            .quad   typeinfo for Derived
            .quad   Base::msgHandle(unsigned long)
            .quad   Derived::msgHandle(unsigned int)
    vtable for Base:
            .quad   0
            .quad   typeinfo for Base
            .quad   Base::msgHandle(unsigned long)
    typeinfo for Derived:
            .quad   vtable for __cxxabiv1::__si_class_type_info+16
            .quad   typeinfo name for Derived
            .quad   typeinfo for Base
    typeinfo name for Derived:
            .string &quot;7Derived&quot;
    typeinfo for Base:
            .quad   vtable for __cxxabiv1::__class_type_info+16
            .quad   typeinfo name for Base
    typeinfo name for Base:
            .string &quot;4Base&quot;
    

    Derived sınıfının 2 farklı fonksiyon gösterecisine sahip olduğunu görebiliriz. Peki sorun nerede başlıyor ?

    virtual olarak nitelendirilmiş bir fonksiyonu farklı imza yapısına sahip bir şekilde fakat doğru şekilde çağrılmasını sağlamak yukardaki yaklaşım ile mümkün değil. Bunun için belki CRTP‘den destek alabilirsin. Ama burada bulunan problem bundan daha fazlası.

    Çoğu zaman çalıştığımız şirketlerde sıfırdan bir şeyler inşa etmiyoruz. Bazen kullandığımız ara katmanda güvenlik açığından dolayı güncel versiyonu port etmemiz gerekiyor, bazen varolan tasarımı kabul edip onun üzerine yeni kat çıkıyoruz. Bazen ihtiyaca yönelik mevcut ko vdu güncelliyoruze en önemlisi kod-base’e gerçekten hakim olamıyoruz. Çünkü onlarca farklı komponent farklı uzmanlık alanları olan kişiler tarafından geliştiriliyor. Hakim olmaya başladığında da yeni bir iş aramaya başlıyorsun :sweat_smile:

    Gerçek bir senaryodan bahsedeyim, kullanılan bir komponentin yeni bir versiyonuna geçiş kararı veriliyor. Ekip bir araya geliyor ve port işlemini gerçekleştiyor. Güncellenen kütüphanede fonksiyonların parametrik yapısında UNSIGNED ifadesinden UINT64_t bir geçiş olduğu için, üst katmanda bulunan bazı fonksiyonlarında buna adapte edilmesi gerekiyor. İşte tam da burada sorun başlıyor, çünkü sınıf hiyerarşileri çok katmanlı ve bazı yerlerde override niteliyicisi kullanılmamış. Yani yapılması gereken bazı değişiklikler atlanıyor, çünkü o yükü derleyiciden alıp kullanıcıya vermiş oluyorsun. Kod derleniyor ve durum halting.

    ... 
    ... 
    ...
    struct Derived : Base
    {
    - virtual void msgHandle(uint32_t type) 
    + virtual void msgHandle(uint32_t type) override
      {
        std::cout <<"Handled from Derived" << std::endl;
      }
    };
    
    */
    

    Yukarda bulunan kod için override niteleyicisi eklendiği takdirde derleyici carlayacaktı. Çünkü Derived sınıf kendi atasından olmayan bir şeyleri override etmeye çalışıyor.

    Best Practice

    commercial or professional procedures that are accepted or prescribed as being correct or most effective.

    Buy Me A Coffee

  • İrlanda’ya Geldikten Sonra Yapmanız Gereken ilk 3 Adım [ IRP, PPS, Banka İşlemleri ]


    Hoşgeldiniz, kendimde ülkeye çok taze birisi olarak böyle bir yazının faydalı olacağını düşünüyorum.


    İrlanda’ya adım attınız, sakin bir zaman dilimi bulup hızlıca halletmeniz gereken ve ne kadar erken yaparsanız bir o kadar erken tarihte çözüme kavuşabileceğiniz 3 konuya ilişkin detayları bu yazı içerisinde bulabilirsiniz. Her şeyin yolunda gitmesi durumunda toplamda 30dk’nızı almayacaktır.

    IRP Kart İşlemleri

    İnternette araştırma yaparken GNIB terimine de denk gelmiş olabilirsiniz. Güncel adı IRP (Irish Residence Permit) diye geçiyor. Eğer Dublin, Cork, Kildare, Limerick, Meath ya da Wicklow,bölgesi içerisinde yaşıyorsanız telefon ile arayıp randevu alabilirsiniz. 1800 800 630 bu numarayı arayak randevu alabilirsiniz. Randevu almak için size sorulacak sorular;

    • Ad-Soyad
    • Doğum Tarihiniz
    • Pasaport Numaranız
    • Mail Adresiniz
    • Neden geldiğiniz

    Telefonun diğer ucunda bulunan kişi sizin için bir tarih söyleyecek ve tarafınıza 1 saat içerisinde bir mail geleceğini belirttikten randevu işi tamamdır. Maili almanız önemli o mailin çıktısını da randevu günü yanınızda götürmenüz gerekiyor. Randevu tarihi benim için 2 ay sonrasına verildi ve sonrasında ertesi gün ve takip eden günlerde de bir kaç defa daha arayarak erken tarih için randevu dilendim fakat olmadığını söylediler. Sizde deneyebilirsiniz belki sizde çalışır.

    Randevu tarihinde yanınızda götürmeniz gereken belgeler


    Mandatory Items: Principal Worker
    Printed copy of the automated appointment confirmation email, sent to you after booking your appointment (if applying in Dublin)
    Original current passport
    Original passport used to enter Ireland (if different)
    Copy of your employment permit
    Copy of your employment contract, or a reference letter confirming the terms of your employment
    Proof of medical insurance
    €300 application fee

    Items That May Be Requested:
    Proof of address in Ireland (e.g., utility bill, lease)
    2x most recent payslips
    Eircode (Irish postcode) of your address (www.eircode.ie)

    PPS İşlemleri

    PPS numaranızı maaş döngünüzden önce almanız bir tık önemli, çünkü bu numaraya sahip olmadığınız sürece devlet sizden emergency tax adı altında bir vergi kesintisi yapıyor ve şu an %40. Yani 6.000£ maaşınız var ise bunun %40’ını kesiyor. Ama endişe etmeyin, PPS numaranızı maaş döngünüz sonrasında alırsanız dahi, geriye dönük kesintiler hesabınıza iade edilmekte. Bunun başvurusu için MyWelfare isimli siteye giderek üyelik oluşturup PPS’e başvuru yapabilirsiniz, yüklemeniz gereken dökümanlar için size yönlendirecek sistem. Ardından Under Review'a çekilecek başvurunuz ve 2-3 hafta sonrasında size bir mail ile randevu tarihi verilecek.

    Randevu tarihinde yanınızda götürmeniz gereken belgeler


    • Employment Letter
    • Passport
    • IRP Card (if applicable)
    • Proof of Address

    Burada dikkat etmeniz gereken en önemli madde Employment Letter, Kontratınız değil. İşe başladıktan sonra HR’ınızı bilgilendirirseniz size gerekli yazıyı verecektir.

    Banka İşlemleri

    Ben internet yaptığım araştırmalarda online olduğu için herkes AIB bankasını kullanıyordu. Bu bankanın uygulamasını telefonunuza indirip, yeni müşteri seçeneğinden yönlendirmeleri devam ederek formu doldurun. Sonra canlı bir birisine bağlayacak sistem sizi ve onunla görüşüp hesabınızı açabilirsiniz. Görüşmede sizden istenecekler.

    • Elinizi yüzünüzde hareket ettirmenizi isteyecek
    • Ad-Soyad bilgilerinizi soracak
    • Pasaport Numaranızı okumanızı isteyecek
    • Doğum Tarihinizi söylemenizi isteyecek
    • Telefonun flash’ı açılacak ve pasaport sağa sola oynatarak bandrolün gerçek olduğunu görmek isteyecek.
    • Pasaportunuz fotoğrafını çekecek
    • Size sorduğu onay cümlelerinde(ses ve video kaydı) YES şeklinde ifade etmenizi isteyecek onayınızı 🙂

    Bu canlı görüşmede problem yaşayanların sayısı az değil anladığım kadarıyla, benim ilk görüşmede pasaportum güncel olmasına rağmen pasaportunuz versiyonu uyuşmuyor diyerek görüşmeyi sonlandırdılar, sonra tavsiye üzerine tekrar doldurdum ve işlemleri tamamlayabildim. Her canlı bağlantıda farklı bir kişi geldiği için hepsinin algısı farklı oluyor galiba 🙂

    Banka işlemlerinizi tamamladıktan 1 hafta sonra evinize bir posta gelecek ve posta içerisinde gelen şifreyi uygulamaya girerek hesabınızı aktif edebilirsiniz. Aradan 2-3 gün sonra yeni bir posta gelecek bu da fiziksel kartınızın şifresi, 5 gün sonrasında ise fiziksel kartınız gelecek. Evet 3 farklı posta alacaksınız…

    Eğer bu yazı size faydalı oldu ise bana bir kahve borçlusunuz demektir. Görüşmek üzere…

  • Bir Satır Arduıno Kodunu Derleyerek Neler Öğrenebilirsin ?

    Bir Satır Arduıno Kodunu Derleyerek Neler Öğrenebilirsin ?


    Kit fiyatları, öğrenci arkadaşların bütçelerini bir süredir zorluyor. Nispeten Arduino Uno gibi geliştirme kartları çok daha rahat erişilebilir hale gelmiştir. Kimi kesim tarafından oyuncak ya da hobi olarak görülen bu kart ile ilerde gömülü yazılım alanında çalışmayı düşünen arkadaşların güzel temeller atabileceğini göstermek amacıyla bu yazıyı yazıyorum.

    void setup() 
    {
      Serial.begin(9600);
      Serial.println(RAMEND, HEX);
    }
    
    void loop() 
    {
       
    }
    

    Yukarıda bulunan kodu Arduino IDE’si aracılığıyla derlediğinizde Uno modeli için şöyle bir çıktı muhtemelen sizi IDE’nin derleme alanında karşılayacaktır.

    Sketch uses 1570 bytes (4%) of program storage space. Maximum is 32256 bytes.
    Global variables use 188 bytes (9%) of dynamic memory, leaving 1860 bytes for local variables. Maximum is 2048 bytes.
    
    

    Burada bazı rakamlar görüyoruz, bu kartı çoğu zaman hobi amaçlı kullanan kişiler için kodun derlenmesi ve beklediği gibi çalışması elbette çoğu zaman yeterli olacaktır.

    Bu rakamların analizi için avr-size isimli bir araç kullanacağım, bu araç Arduino IDE’sini yüklediğinizde arduino-x.y.z/hardware/tools/avr/bin dizini altında bulunmakta. Yukarda bulunan kodun derlenmesi sonrasında oluşan .elf uzantılı dosyayı bu araca veriyorum.

    avr-size.exe -A <file.elf>
    

    .data alanı ilklendirilmesi yapılmış global/statik veri tiplerinin değerlerinin tutulduğu bir alan.
    .text programın yürütülebilir makine kodlarını içeren bellek bölümü diyebiliriz.
    .bss .data alanının aksine ilklendirilmesi yani bir değer atanması yapılmamış değişkenlerin tutulduğu bir bellek alanı.

    Ne kadar flash alanı tükettiğinizi .data ve .text alanlarını toplayarak elde etmeniz mümkün. Yukarıda bulunan çıktıya bakarsanız 1570 byte (1548 + 22) bir alanın kullanıldığını görebilirsiniz. Tüketilen RAM miktarını ise .data ve .bss alanlarını toplayarak elde edebiliriz. Çıktımıza göre 188 (166 + 22) byte bir RAM alanını tüketmekteyiz. RAM volatile bir bellek alanı ve bir donanım için run-time durumundayken işlevsel. Farkettiyseniz tüketilen program alanı ve ram alanını hesaplarken .data bölümü ikisi içinde ortak. Data bölümünde tutulan verilen aynı zaman programın saklama alanında bir yere sahipler ve kodun startup rutininde ilgili değerler bu şekilde RAM bölgesine kopyalanıyorlar. Gelin .data, .text, .bss’i daha iyi anlayalım.

    char foo [1000];
    void setup() 
    {
      Serial.begin(9600);
      Serial.println("foo");
      Serial.print (foo);
    }
    
    void loop() 
    {
    }
    

    Yukarda bulunan kod parçası Arduino Uno kartı için şu çıktıyı veriyor.

    Sketch uses 1498 bytes (4%) of program storage space. Maximum is 32256 bytes.
    Global variables use 1192 bytes (58%) of dynamic memory, leaving 856 bytes for local variables. Maximum is 2048 bytes.
    

    Eğer foo isimli dizinin elemanları için değer ataması yaparsak;

    char foo [1000] = { 1, 2, 3, 4, 5 };
    void setup() 
    {
      Serial.begin(9600);
      Serial.println("foo");
      Serial.print (foo);
    }
    
    void loop() 
    { 
    }
    

    1000-byte kadar daha fazla flash alanı tüketildiğini söylüyor.

    Sketch uses 2498 bytes (7%) of program storage space. Maximum is 32256 bytes.
    Global variables use 1192 bytes (58%) of dynamic memory, leaving 856 bytes for local variables. Maximum is 2048 bytes.
    

    Değer ataması yaptığımızda artık .data belleğine taşımış olduğumuzu görebilirsiniz. Atama gerçekleşmediği durumda .bss belleğinde tutuluyordu.

    Bir çok kavramı öğrenmek için Arduino’nun iyi bir araç olduğunu düşünüyorum ben açıkcası, eğer farklı düşünüyorsanız konuşabiliriz. 🙂

  • Teknik Kırgınlık

    Teknik Kırgınlık


    Ne zamandır bu konuda bir yazı yazmak istiyordum. Vakit bulamadığımdan değil, kırgın olduğumdan yazmaya fırsatım olmadı açıkcası. Bir süre öncesine kadar daha ve daha deparlı olduğunu olduğumu bildiğim işlerde, daha sakin davranmamın altında yatan hatta sadece yatmakla kalmayıp uyuyan bu hissi tam tarif edemediğim için adına Teknik Kırgınlık dedim. Henüz burnout diyecek kadar beyaz yakalı da hissetmiyorum kendimi. Kabullenemediğim bir gerçek olan; yazılımın nankör oluşu ve mesleğim için aslında kod yazmanın buz dağının görünen yüzü olması bu hissi sanki biraz daha tetikliyor.

    Mesleğimin 4.yılını doldurdum, mesleğimi seviyorum. Bu süreci çocukların gelişimine benzetiyorum ve çocuk gelişiminde en kritik dönemin 0-6 yaş arasında olduğunu söylüyor uzmanlar ve beslenme çantamda otlu peynir kokusuydu babam. (bunu yazmadan geçemeyeceğim bir andı özür dilerim, yazıyı baltaladığım için) En kritik dönemi geride bırakacağım bu süre için sadece 2 yıl kalması beni daha huzursuz ediyor sanki. Tuvalet eğitimi tecrübemi alırken daha iyi değerlendirebilirdim diyorum kendime. Korktuğum şeyin yeni şeyler öğrenmek olmadığını, öğrendiğim şeyleri kullanamadan unutmaktan olduğunu farkettiğimden beri bir süredir yeni şeyler öğrenmiyorum. Bazen reddediyorum. Bazen zorunda kalıyorum ve öğreneceğim şeye/lere olan ilişkimi tek gecelik olarak görüyorum ve tutkulu sevişemiyorum onlarla. Ama hâlâ heyecanlıyım ve bu beni ciddi manada ayakta tutuyor. Zaten heyecanlı olmasam kırgın değil küs olurdum diyorum kendime. Kafamda rutin ve konfor alanını kafes dövüşüne çıkarıyorum zaman zaman. Bir taraf nakovt oluyor son zamanlarda. Daha iyi uyuyorum fakat daha iyi uyanamıyorum. Sonra biraz Ayfer Tunç okuyarak olası bir depreme karşı yönünü değiştirdiğim yatağımı topluyorum.

    Bilginin güç olduğunu düşünürsek, kendimi güçsüz hissediyorum. Yani 4.yılımda kendimi görmek istediğim noktadan uzakta görüyorum. Hoş 28 yaşında kendimi görmek istediğim noktadan da uzakta görüyorum. Ama ALLAH var, GAM yok bunlara gram üzülmüyorum. Üzüldüğüm ince nokta şurası; 28 yaşında kendimi görmek istediğimi düşündüğüm o yaşlarda neden böyle düşündüm. Mesala 22’mde. Nispeten 25’imde ?. Böyle konularda kendimle konuşmayı ve farkındalık sahibi olduğumu düşününce de epey mutlu oluyorum. Teknik Kırgınlık ifadesini de ilk kendimle konuşurken bulmuştum Moda iskelesinde. Dün gece kendi reçetemi hazırladım ve koştur koştur nöbetçi eczaneye giderek ilaçlarımı aldım. Teknik kırgınlığımı bir nokta azaltmak için yeni hedefler de koymayı ihmal etmedim. Sana söz Teknik Kırgınlığım, yeneceğiz.

    Ya yazıyı geri dönüp okuduğumda, çok karamsar durmuş ama hayatımda da en iyi dönemlerden birinde olduğumu söylemeden geçmeyim bari ortalık biraz yumuşasın.

  • Nıxıe Tüplerin Kullanımı – Nıxıe Pomodoro Saat Yapımı

    Nıxıe Tüplerin Kullanımı – Nıxıe Pomodoro Saat Yapımı


    Bir süredir nixie tüpler ile bir şeyler yapmak istiyordum ve klasik saat fikri bana çok çekici gelmiyordu. Akşamları lambader ışığında okuma yapmak ya da çalışmaktan keyif aldığım için bu süreci biraz daha disiplinli ve sağlıklı hale getirmek istedim. O yüzden pomodoro tekniğini denemeye çalışmaya başladım. Tabi biraz kendimce yorumlayarak. Bu tekniği uygularken daha motive olabilmek adına Nixie Tüpleri kullanarak kendime bir adet Pomodoro Saati yapmaya karar verdim. Bu yazı serisinde bu süreci anlatmak istiyorum.


    1. Adım Nixie Tüplerini Bulabilmek

    Bu konuda en güvenilir sitelerin başında bence tindie geliyor. Bilmeyenler için tindie maker-market bir alışveriş sitesi. Site üzerinde arama yaparak nixie tüp satın alımı gerçekleştirebilirsiniz. Kargo ücreti satıcının bulunduğu lokasyon, çalıştığı firmaya göre farklılıklar gösterebiliyor. Ürün ülkeye girdiğinde ekstra bir gümrük vergisi de ödemeniz gerekiyor. Bu siteye alternatif olarak Etsy de kullanılabilir, en son çare olarak Aliexpress’i öneriyorum bu tarz ürünlerde açıkcası. Bu adımda benim izlediğim strateji şu şekilde oldu, öncelikle Reddit platformunda Nixie satıcısının olup olmadığını sordum ve paralelde etsy ve tindie üzerinde fiyat araştırması yaptım. Reddit üzerinden görüştüğüm bir arkadaş, etsy’de bulduğum satıcıdan daha uygun fiyata veremeyeceğini ifade etti. Bir umut picproje forumuna yazarak nixie tüp elinde olan var ise satın almak istediğimi belirttim. Şanslıyım ki TR’de elinde 4 adet nixie tüp olan birisiyle tanıştım ve ondan satın aldım. 4 x IN12B Nixie Tüp için kendisine $32 ödedim. Kişi kendi kullanımı için nixie için bir taban kartı yapmıştı ve taban kartının da olması işimi oldukça kolaylaştırdı. Aşağıda bulunan görselde aldığım nixie tüpünü görebilirsiniz.

    2.Adım Uygun Güç Kaynağını Bulmak

    Nixie tüpler çalışması için yüksek gerilime ihtiyaç duyuyorlar. Bu yüzden çalışma yaparken dikkatli olmanız gerekiyor. İnternette çeşitli açık kaynak devreler mevcut bunlardan ürettirebilir ya da kendi devrenizi de hazırlayabilirsiniz. Nixie tüpleri aldığım kişide uygun besleme kaynağı olduğu için kendisinden onu da satın aldım. Oldukça verimli bir devre ve zamanında https://nixieclock.org sitesi üzerinden satın alınmış. Kendim 12V çıkış veren bir adaptörü giriş gerilimi olarak aldığım modüle verdim ve modülün üzerinde bulunan pot aracılığı ile 170V çıkış gerilimi aldım. Ve Nixie Tüplerimin ilk testlerini gerçekleştirdim. Modülü 13$’a satın aldım.

    3.Nixie Tüplerini Sürmek

    Aldığım nixie tüpün modelinin 12B olduğunu daha önce söylemiştim. 12A modeline göre tek fark 12B modelinde nokta işareti de bulunmakta. Nixie tüpümde 0-9 ve nokta digit’i ile birlikte toplamda 11 adet çıkış bulunuyor. Her pin için mikrodenetleyicinin bir pinini kullandığımızda çok fazla sayıda pine ihtiyaç olacağı için alternatifleri araştırmaya başladım. İnternette genelde 4141/SN74141 ya da K155ID1 (rus muadili) entegreler ile kullanımına rastladım. Entegreler BCD mantığında çalıştığı için mikrodenetleyicinin 4 pinini işgal edeceklerdi. Entegreler oldukça eski ve üretimlerinin olmadığını öğrendim. O yüzden çalışır bir entegre bulmak çok mümkün değildi ama şansımı Aliexpress üzerinde denedim ve bir satıcıdan 10 adet entegre satın aldım. Forumlardan okuduğum kadarıyla entegrelerin patates çıkacağını biliyordum ve öyle oldu 🙂 Entegreler geldikten sonra hemen bir devre kurdum ve entegreleri test ettim ne yazık ki hepsi patates çıktı ve 14$’m çöp oldu. Sonrasında 74HC595 entegresini kullanmayı düşündüm. 3 giriş kullanarak paralel bir şekilde 8 adet çıkış almak mümkündü. Bir tüp için 11 adet çıkış olduğu için bir tüpün tüm digitlerini sürmek için 2 adet 74HC595’e ihtiyaç olacaktı. Elimde 1 adet 74HC595 vardı ve bir tane nixie tüp üzerinde çalışma yaptım. Tabi giriş çıkışlar da arttığı için ortalık kablo çöplüğüne dönüyor ister istemez. Bir devre hazırlamadan önce internette sürücü olup olmadığını araştırdım ve tindie üzerinde tam da istediğim gibi bir sürücü vardı. Bu sürücüden 2 adet satın aldım, hem smd malzemeler ile dizilmişti hemde fiyatı kendi yapacağımdan uyguna geliyordu. 2 sürücü için 11$ ödedim. Şevkime yenik düştüğüm için ürün gönderiminde UPS seçtim ve kargo ücreti olarak 18$ ödedim, gümrük vergisi içinde yaklaşık 5$ ödedim. Ürün 4 gün içerisinde Polonyadan geldi. Aldığım ürünle hızlıca bir sayıcı yaptım.

    Ürün kullanımı oldukça pratik ve kolay. Satıcı çok güzel bir şekilde ürünü paketlemişti.

    4.Yazılım

    Ben dört adet tüp satın aldım ancak yapacağım saat için iki tanesi yeterli olmakta. Pomodoro tekniğini bir yorumladım ve çalışma süresi için 25, 50, 60, 70, 80, 90 şeklinde dakika cinsinden periyotlar belirledim. Iki tane tüp ile bunları göstermem ve saydırmam mümkün. MCU olarak Arduino Uno kullandım, belki internet tabanlı yapmak adına ESP32’ye de geçebilirim. Periyotu saydırmak için harici bir saat devresi kullanmak istemedim. Daha önce blogumda anlatımı yaptığım kütüphaneyi kullandım. Yazılımsal kısımları github üzerinde inceleyebilirsiniz.

    5.Masam için Ürün

    Henüz ürün halinde kutulamayı başaramadım. Öncelikte tüm elektronik aksam için delikli pertinaks yerine kullanmak için bir PCB hazırlıyorum.(PCB’yi doğruladıktan sonra github reposuna ekleyeceğim.) Kafadam kutu için fikirler mevcut ancak nasıl yaparım nasıl ederim bilmiyorum deri işçiliğine daha önce hiç girişmedim. Bakalım süreç nasıl olacak 🙂 Tekrardan yazıyı güncelleyeceğim.

  • Hayır, Çürümeye Değil, Olgunlaşmaya Bıraktım Kendimi


    Yeni yüzler görmeyi reddettiğim dönemin bana getirisi olsa gerek hem sigaramın o dumanı evde kalsın istemiyorum hem de camdan dışarı üflemeye cesaretim yok. Gecenin, tutamadığım o karanlığında uyanıp her an kağıdı terkedecek gibi duran harflerle şöyle yazmışım; Mutluluklarımızı paylaşmakta aceleci olduğumuz kadar üzüntülerimizi paylaşmakta bir o kadar erteleyici davranıyoruz. O gün, o yoğun bakım ünitesinin önünde, dünyanın en iyi orkestrasını yönetebilirdim. Nasıl yapardım bilmiyorum ama fırsat verilseydi yönetebilirdim bundan eminim. Halbuki flüt bile çalmayı becerememiştim ilkokulda. Ama eminim, yönetebilirdim. Ben hep küllüğün boş tarafına bakarım biliyor musun arkadaşım ? Oraya bir tane daha öfkemi, kızgınlığımı, yere düştüğümde kalkarken pislenen ellerimden bırakabilmek için. Boş yer kalmadığı zamanlarda ise buraya yazarım. Çok yazmışım buraya…

    Her şey ‘L’ harfini andırır oldu bana bir süredir. Sokak başları, duvar köşeleri, masalar. Hatta uyurken bile L şekline girmeye çalışıyorum. Gideceğim bir yere ‘L’ şekline uygun bir şekilde varmayı arzuluyorum. Daha önce ‘F’ harfi için böyle bir şey olmuştu. Az dolu olan bir otobüse binip oturan insanlara göre yerimi değiştirmek suretiyle ‘F’ harfini oluşturmaya çalıştım. Valla ‘L’ harfine okeyim arkadaşım, her şeye uyuyor.

    Kalkıp silkelenmek istemiyorum ki ben arkadaşım, Hayır, çürümeye değil, olgunlaşmaya bıraktım kendimi. Neden mi ? Bunun tek bir cevabı yok ama küllüğümde boş yer açmam gerekti bir şekilde.

    Arşivden…

  • Git Clone’un Kardeşi Git Sparse


    Büyük codebase içerisinde çalışırken kimi zaman başka bir bölüm bizim bölüme dahi hit etmeyebiliyor. Tüm repoyu çekmenin ise getirdiği bazı maliyetler mevcut. Diskte çok fazla yer kaplaması gibi. Böyle bir durumda sadece ihtiyacımız olan klasörleri çekerek onlar özelinde çalışma yapmak isteyebiliriz. Bunun yöntemlerinden birisi ise git sparse tekniğini kullanmak. Elbette büyük organizasyonda bu yönetimi sizin yapmanız mümkün olmayabilir ama SPARSE tekniğinin ne olduğunu bilmek fayda sağlayabilir.

    Bu partial-clone yapısını şu görsel üzerinden inceleyelim.

    Geliştirme tarafında sadece win_project altında bulunan yapıya ihtiyacımız olduğunu düşünelim. Repoyu çekeceğimiz klasör içerisine gelerek;

    git init
    
    git config core.sparsecheckout true
    
    

    Şimdi ise Git’e hangi dosyalara ihtiyacımız olduğunu belirtmemiz gerekiyor.

    echo win_project >> .git/info/sparse-checkout
    
    git remote add -f origin <REPO_URL>
    

    Belirttiğimiz dosyaları çekelim

    git pull origin master
    
    

    İlerleyen dönemlerde diğer dosyalara da ihtiyacımız olduğunda şu komut ile diğer dosyaları çekebiliriz.

    git sparse-checkout disable
    

  • KitimSende – Üniversite Öğrencileri İçin Ücretsiz Elektronik Kit Desteği Sağlayan Girişim


    Uzun süredir amatörce gerçekleştirdiğimiz ve kısa sürede geniş kitlelere ulaşmak için Seckin GULEVIZ ve Berat Yıldız ile birlikte #KitimSende ismini verdiğimiz projemizi yayına aldık.

    Gün geçtikçe öğrencilerin kendilerini geliştirebilmesi için ihtiyaç duyduğu fiziksel donanımlara erişiminin ekonomik olarak zorlayıcı olduğunun farkındayız. 

    KitimSende, gömülü yazılım alanında çalışan üniversite öğrencilerinin elektronik kitlere ulaşmasını kolaylaştırmak amacıyla kurulmuş bir platformdur. Bu bağlamda kit bağışlamak ve kit sahiplenmek isteyen kişileri bir araya getirmeyi hedeflemektedir. KitimSende, karşılıklı güven ortamını sağlamak amacıyla 
    kullanıcların sadece LinkedIn hesabıyla giriş yaptıkları bir platformdur. Platformun akışı 4 ana adımdan oluşmaktadır.

    1 – Bağışçılar ve öğrenciler platforma giriş yaparlar.
    2 – Bağışcılar ilanlarını oluşturur.
    3 – Öğrenciler ihtiyaçları doğrultusunda tercih ettikleri ilan için ‘Kit Mektubu’ alanını doldurarak başvurularını tamamlarlar.
    4 – Bağışçı tarafından seçilen öğrenciye kit bağışçı tarafından gönderilir. 

    Daha fazla kişinin KitimSende diyebilmesi için etkileşim desteklerinizi bekliyoruz.

    Sitede şuanda bağışçılar tarafından bırakılmış çeşitli ürünler bulunmakta, girip incelemek isterseniz

    https://kitimsende.com/

    *Masaüstünden giriş yapmanızı tavsiye ediyoruz.

  • RTI CONNEXT DDS

    RTI CONNEXT DDS


    Dökümanın Amacı

    Bu dökümanın amacı DriverAlerts_subscriber.cxx dosyası üzerinden bir componentin yazılımını incelemek ve bir yayın yapan modülün yazılma sürecini anlatmak amacıyla hazırlanmıştır. İlgili dosya (DriverAlerts_subscriber.cxx) code base içerisinde bulunan HMI klasörünün altında bulunmaktadır. Bu modülün amacı, Alert topic’e abonelik sağlamak ve kullancıya bir pop-up çıkarmaktır. Bu dökümana geçmeden önce mevcut haliyle sistemin ayağa kaldırıldığından emin olunması gerekmektedir.

    Repo : https://github.com/rticommunity/rticonnextdds-usecases-automotive

    Bilinmesi Gereken Terminolojiler

    • Domain → Birbiriyle iletişimde bulunan uygulamaları birbirine bağlayan yapı. (Sisteme yeni eklenecek modülün diğer uygulamalar arasında konuşması domainId’nin uyumu ile mümkündür.)
    • Domain Participant Uygulamaların domaine olan üyeliğini temsil eder.
    • Topic → İletişim kurmak için tanımlı alanlar ve ilgili servis kalitelerini içerir.

    Bir Modülün Kod Akışı

    1. Öncelikle DomainParticipant nesnesi oluşturulur.
    2. Oluşturulan nesne üzerinden publisher ve subscriber nesneleri hayata getirilir.
    3. Subscriber ve publisher nesneleri üzerinden DataWriter ve DataReader nesneleri hayata getirilir. Bir modül içerisinde birden fazla publisher-subscriber olacağı için böyle bir durumda ilgili her farklı component için DataWriter/DataReader nesneleri oluşturulur.

    Yukarda bulunan adımları kod üzerinde (DriverAlerts_subscriber.cxx) görelim;

    Line 124

    Line 134

    Line 172

    Sisteme Yeni Bir Modül Ekleyelim

    • Eğer sisteme ekleyeceğiniz modül varolan sistem içerisinde ki herhangi bir topic’e abonelik yapacaksa .idl dosyası içerisine yeni bir type eklemenize ihtiyacınız yok. Ancak bugün modülümüz bir mesaj yayınlacağı için, ilgili mesajın domain içerisinde bulunan diğer modüller tarafından da istenildiği zaman anlaşılması için .idl dosyasına ekleme yapılması gerekmektedir. Bunun için .idl dosyasına yaptığım eklemeyi aşağıda görebilirsiniz.
    module Helloworld {
        struct HelloWordMessage {
            string<256> msg;
        };
    };
    

    Yukarda bulunan module anahtar kelimesi hakkında detaylı bilgiye şuradan ulaşabilirsiniz. Bu işlemden sonra ilgili type için daha sonra kullanacağımız sınıfların oluşturulması amacıyla idl generator tool’unun tekrar çalıştırılması gerekmektedir. Parametre sisteminize göre değişiklik gösterebilir.

    make -f make/Makefile_x64Linux3gcc5.4.0
    
    • Sisteme yeni modül eklenirken ilgili modülün konfigürasyonlarının daha yönetilebilir ve parametrik hale getirebilmek adına .properties dosyası ekleyebiliriz. Bu kısım aslında opsiyonel bir süreç ancak bu şekilde daha rahat bakımı yapılabilir bir modül elde ediyoruz. Bunun için resource klasörü altına yeni bir dosyayı .properties uzantısı ile ekliyoruz. Bu dosyanın içine hangi topic’e sub olacağımızı hangi topic’e veri göndereceğimizi, hangi sıklıklarla veriyi göndereceğimiz gibi çeşitli parametreleri girebiliyoruz. Ben şu an için aşağıda bulunan parametreleri girdim. (3.adım içerisinde verilmiş XML kodları ile aşağıda bulunan parametrelerini tekrar incelemekte fayda var.)
    //hello_world.properties
    topic.Hello=Hello
    qos.Library=Demo_Library
    qos.Hello.Profile=Hello_World_Profile
    
    config.domainId=0
    config.pubInterval=1000
    
    • Bir DDS sistemini XML dosyası üzerinden ilgili tagleri parse ederek oluşturmak mümkün. Bu kısım aslında zorunlu bir kısım değil ancak sistem büyüdükçe, bu sistemin bakımının daha rahat yapılabilmesi gibi özellikler gözönüne alındığında büyük sistemler için mutlaka kullanılması gereken bir yapı. XML dosyasında çeşitli tagler arasına yazacağınız ifadeler ile birlikte modülünüz DomainParticipant, DataWriter, DataReader ismi gibi bir çok parametrenin isimlendirilmesi yapılabilir. Bu tagleri ezberlemek zaten çok mümkün değil, ihtiyaç oldukça öğrenilebilir. Şuradan konu ile ilgili okuma yapabilirsiniz.
    <qos_profile name="Hello_World_Profile" base_name="BuiltinQosLibExp::Generic.BestEffort" is_default_qos="false">
          <!-- QoS used to configure the Vehicle Platform data reader and writer created in the example code 
               The data is periodic so it will be sent best effort. A deadline has been added
               to make sure that an error is reported when no data is received for a certian time. -->
          <participant_qos>
            <!--
              The participant name, if it is set, will be displayed in the
              RTI tools, making it easier for you to tell one
              application from another when you're debugging.
            -->
            <participant_name>
              <name>Hello World Platform</name>
            </participant_name>
            <transport_builtin>
              <mask>UDPv4</mask>
            </transport_builtin>        
            <discovery>
              <initial_peers>
                <!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
                <!-- Insert addresses here of machines you want     -->
                <!-- to contact                                     -->
                <!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
                <element>127.0.0.1</element>
                <!-- <element>192.168.1.2</element>-->
              </initial_peers>
            </discovery>
          </participant_qos>
          <datareader_qos>
            <subscription_name>
              <name>Hello World Reader</name>
            </subscription_name>
            <deadline>
              <period>
                <nanosec>0</nanosec>
                <sec>11</sec>
              </period>
            </deadline>
            <history>
              <depth>100</depth>
            </history>
          </datareader_qos>
          <datawriter_qos>
            <publication_name>
              <name>Hello World Publisher</name>
            </publication_name>
            <deadline>
              <period>
                <nanosec>0</nanosec>
                <sec>10</sec>
              </period>
            </deadline>
          </datawriter_qos>
        </qos_profile>
    
    • Artık sisteme ekleyeceğimiz modül için src dizini altına kendi klasörümüzü oluşturarak, .cpp uzantılı dosyamızı klasörümüz altına ekleyebiliriz. (Benim klasörüm ExternalHW isimli klasör.)
    • Bu kısım önemli bir kısım, eklediğiniz modülün derlenmesi, link edilmesi gibi süreçlerin yapılabilmesi için make dizini altında bulunan Makefile.common klasörü altında düzenlemeler yapılması gerekmektedir.
      • Değişkenler set edilir. (Eklediğiniz dizin ve dosya adına göre değişiklik gösterecektir.)
    ###############################################################################
    # Hello World
    ###############################################################################
    
    SOURCES_HELLO        = src/ExternalHW/external_hardware.cxx
    
    SOURCES_HELLO_NODIR  = $(notdir $(SOURCES_HELLO))
    HELLO_OBJS           = $(SOURCES_HELLO_NODIR:%.cxx=objs/$(ARCH)/%.o)
    HELLO_EXE            = objs/$(ARCH)/HelloWorld
    
    • Build Rules içerisine modülünüz eklenir. (ExternalHW isimli kısımlar yeni eklenen kısımlardır.)
    all: $(DIRECTORIES) Vision Collision_Avoidance HMI Lidar CameraImageDataSub CameraImageDataPub Sensor_Fusion Vehicle_Platform ExternalHW
    
    Vision:			$(DIRECTORIES) $(IDL_OBJS) $(DATA_OBJS) \
    			$(PROP_OBJS) $(VISION_OBJS)
    			$(LINKER) $(LINKER_FLAGS)   -o $(VISION_EXE) $(IDL_OBJS) \
                            $(DATA_OBJS) $(PROP_OBJS) $(VISION_OBJS) $(LIBS)
    
    Collision_Avoidance:	$(DIRECTORIES) $(IDL_OBJS) $(PROP_OBJS) \
    			$(CA_OBJS)
    			$(LINKER) $(LINKER_FLAGS)   -o $(CA_EXE) $(IDL_OBJS) \
    			$(PROP_OBJS) $(CA_OBJS) $(LIBS)
    
    HMI:			$(DIRECTORIES) $(IDL_OBJS) $(PROP_OBJS) \
    			$(HMI_OBJS)
    			$(LINKER) $(LINKER_FLAGS)   -o $(HMI_EXE) $(IDL_OBJS) \
    			$(PROP_OBJS) $(HMI_OBJS) $(LIBS)
    
    Lidar:			$(DIRECTORIES) $(IDL_OBJS) $(PROP_OBJS) \
    			$(LIDAR_OBJS)
    			$(LINKER) $(LINKER_FLAGS)   -o $(LIDAR_EXE) $(IDL_OBJS) \
    			$(PROP_OBJS) $(LIDAR_OBJS) $(LIBS)
    
    CameraImageDataSub: $(DIRECTORIES) $(IDL_OBJS) $(PROP_OBJS) \
    			$(CAMDATASUB_OBJS)
    			$(LINKER) $(LINKER_FLAGS)   -o $(CAMDATASUB_EXE) $(IDL_OBJS) \
    			$(PROP_OBJS) $(CAMDATASUB_OBJS) $(LIBS)
    
    CameraImageDataPub: $(DIRECTORIES) $(IDL_OBJS) $(PROP_OBJS) \
    			$(CAMDATAPUB_OBJS)
    			$(LINKER) $(LINKER_FLAGS)   -o $(CAMDATAPUB_EXE) $(IDL_OBJS) \
    			$(PROP_OBJS) $(CAMDATAPUB_OBJS) $(LIBS)
    
    Sensor_Fusion:		$(DIRECTORIES) $(IDL_OBJS) $(PROP_OBJS) \
    			$(SF_OBJS)
    			$(LINKER) $(LINKER_FLAGS)   -o $(SF_EXE) $(IDL_OBJS) \
    			$(PROP_OBJS) $(SF_OBJS) $(LIBS)
    
    Vehicle_Platform:	$(DIRECTORIES) $(IDL_OBJS) $(DATA_OBJS) \
    			$(PROP_OBJS) $(VP_OBJS)
    			$(LINKER) $(LINKER_FLAGS)   -o $(VP_EXE) $(IDL_OBJS) \
    			$(DATA_OBJS) $(PROP_OBJS) $(VP_OBJS) $(LIBS)
    
    Vehicle_Platform:	$(DIRECTORIES) $(IDL_OBJS) $(DATA_OBJS) \
    			$(PROP_OBJS) $(VP_OBJS)
    			$(LINKER) $(LINKER_FLAGS)   -o $(VP_EXE) $(IDL_OBJS) \
    			$(DATA_OBJS) $(PROP_OBJS) $(VP_OBJS) $(LIBS)
    
    ExternalHW:		$(DIRECTORIES) $(IDL_OBJS) $(PROP_OBJS) \
    			$(HELLO_OBJS)
    			$(LINKER) $(LINKER_FLAGS)   -o $(HELLO_EXE) $(IDL_OBJS) \
    			$(PROP_OBJS) $(HELLO_OBJS) $(LIBS)
    
    • Son olarak objs klasörü altına çıktının oluşturulması için aşağıda bulunan kısım eklenir.
    objs/$(ARCH)/%.o : src/ExternalHW/%.cxx
    		$(COMPILER) $(COMPILER_FLAGS)  -o $@ $(DEFINES) $(INCLUDES) -c $<
    
    • Artık eklediğimiz dosyaya gelerek kodlarımızı yazabiliriz.(Bir Modülün Akışı kısmını kafada oturmakta fayda var.) Kodun akışı ile ilgili anlaşılmayan yer var ise birlikte bakabiliriz.)
    #include "Utils.h" // Dosya işlemleri
    #include "automotive.h" //IDL üzerinden generate edilen ortak arayüz ile ilgili
    #include "automotiveSupport.h" //IDL üzerinden generate edilen ortak arayüz ile ilgili
    #include "ndds/ndds_cpp.h" //Standart kütüphanelerimiz
    
    static int shutdown(
        DDSDomainParticipant *participant)
    {
        DDS_ReturnCode_t retcode;
        int status = 0;
    
        if (participant != NULL) {
            retcode = participant->delete_contained_entities();
            if (retcode != DDS_RETCODE_OK) {
                printf("delete_contained_entities error %d\n", retcode);
                status = -1;
            }
    
            retcode = DDSTheParticipantFactory->delete_participant(participant);
            if (retcode != DDS_RETCODE_OK) {
                printf("delete_participant error %d\n", retcode);
                status = -1;
            }
        }
    
        return status;
    }
    
    extern "C" int publisher_main(int sample_count)
    {
        DDSDomainParticipant *participant = NULL;
        DDSPublisher *publisher = NULL;
        DDSTopic *topic = NULL;
        DDSDataWriter *writer = NULL;
    
        Helloworld_HelloWordMessageDataWriter* Hello_WorldMessage_writer = NULL;
        Helloworld_HelloWordMessage* helloworld_instance = NULL;
        
        DDS_InstanceHandle_t instance_handle = DDS_HANDLE_NIL;
        int domainId = 0;
        const char *type_name = NULL;
        DDS_ReturnCode_t retcode;
        DDS_Duration_t send_period = {20,0};
    
    
        PropertyUtil* prop = new PropertyUtil("hello_world.properties");
        domainId = prop -> getLongProperty("config.domainId");
        
        long time = prop->getLongProperty("config.pubInterval");
        send_period.sec = time / 1000;
        send_period.nanosec = (time % 1000) * 1000 * 1000;
    
        std::string platformTopicName = prop->getStringProperty("topic.Hello");
        if (platformTopicName == "") {
            printf("No platform topic name specified\n");
            return -1;
        }
        //Qos Parametrelerinin Olduğu Dosya Adi Alinir.
        std::string qosLibrary = prop->getStringProperty("qos.Library");
        if (qosLibrary == "") {
            printf("No QoS Library specified\n");
            return -1;
        }
    
        //İlgili Component İçin Gerekli kütüphane detaylari alinir.
        std::string HelloWorldQosProfile = prop->getStringProperty("qos.Hello.Profile");
        if (HelloWorldQosProfile == "") {
            printf("No Planning QoS Profile specified\n");
            return -1;
        }
    
        //Participant oluşturuldu.
        participant = DDSTheParticipantFactory->create_participant_with_profile(
            domainId, qosLibrary.c_str(), HelloWorldQosProfile.c_str(),
            NULL /* listener */, DDS_STATUS_MASK_NONE);
        if (participant == NULL) {
            printf("create_participant error\n");
            shutdown(participant);
            return -1;
        }
    
        //Publisher oluşturuldu
        publisher = participant->create_publisher_with_profile(
            qosLibrary.c_str(), HelloWorldQosProfile.c_str(), NULL /* listener */, DDS_STATUS_MASK_NONE);
        if (publisher == NULL) {
            printf("create_publisher error\n");
            shutdown(participant);
            return -1;
        }
    
        type_name = Helloworld_HelloWordMessageTypeSupport::get_type_name();
        retcode = Helloworld_HelloWordMessageTypeSupport::register_type(
            participant, type_name);
        if (retcode != DDS_RETCODE_OK) {
            printf("register_type error %d\n", retcode);
            shutdown(participant);
            return -1;
        }
    
        topic = participant->create_topic_with_profile(
            platformTopicName.c_str(),
            type_name, qosLibrary.c_str(), HelloWorldQosProfile.c_str(), NULL /* listener */,
            DDS_STATUS_MASK_NONE);
        if (topic == NULL) {
            printf("create_topic error\n");
            shutdown(participant);
            return -1;
        }
    
        writer = publisher->create_datawriter_with_profile(
            topic, qosLibrary.c_str(), HelloWorldQosProfile.c_str(), NULL /* listener */,
            DDS_STATUS_MASK_NONE);
        if (writer == NULL) {
            printf("create_datawriter error\n");
            shutdown(participant);
            return -1;
        }
    	//Neden bunu yapıyoruz diye merak ederseniz.
    	//https://community.rti.com/kb/what-purpose-narrow-method-created-type-rtiddsgen
        Hello_WorldMessage_writer = Helloworld_HelloWordMessageDataWriter::narrow(writer);
        if (Hello_WorldMessage_writer == NULL) {
            printf("DataWriter narrow error\n");
            shutdown(participant);
            return -1;
        }
    
        helloworld_instance = Helloworld_HelloWordMessageTypeSupport::create_data();
        if (helloworld_instance == NULL) {
            printf("Platform_PlatformStatusTypeSupport::create_data error\n");
            shutdown(participant);
            return -1;
        }
    
        for (int count=0; (sample_count == 0) || (count < sample_count); ++count) {
    
            printf("\r\n Hello World Message Sended");
            
            if (count % 2 == 0) {
                helloworld_instance->msg = (char*)"Test";
            }
    
            else {
                helloworld_instance->msg = (char*)"Hello World";
            }
          
            retcode = Hello_WorldMessage_writer->write(*helloworld_instance, instance_handle);
            if (retcode != DDS_RETCODE_OK) {
                printf("write error %d\n", retcode);
            }
    
            NDDSUtility::sleep(send_period);
        }
    
        retcode = Helloworld_HelloWordMessageTypeSupport::delete_data(helloworld_instance);
        if (retcode != DDS_RETCODE_OK) {
            printf("Helloworld_HelloWordMessageTypeSupport::delete_data error %d\n", retcode);
        }
    
        /* Delete all entities */
        return shutdown(participant);
    }
    
    int main(int argc, char *argv[])
    {
        int sample_count = 0;
    
        if (argc >= 2) {
            sample_count = atoi(argv[1]);
        }
        
        return publisher_main(sample_count);
    }
    

    Modülümüzü RTI Admin Konsolu üzerinden inceleyelim.

    • Yapılan tüm değişiklikler sonrasında programı derleyip çalıştırın ve RTI kurulumu ile birlikte Launcher üzerinden Admin Console çalıştırın.

    Sistemimiz içerisine Hello isimli bir modül gelmiş bulunmakta. Sağ click yaparak ilgili modüle abonelik sağlayalım. Fotoğraflarda görüldüğü gibi msg içeriğinin yukarda yazdığımız count değişkeninin tek ya da çift olma durumuna göre değiştiğini görebiliriz.

    Eklediğimiz Bir Modülün Yaptığı Yayına Başka Bir Modül Abone Olalım.

    Az önce geliştirdiğimiz modülü RTI’n bize sağlamış olduğu bir tool üzerinden inceleyebildik. Bu bölümde ise yayına abone olalım ve dataları ekrana basalım. Bunun için HMI Modülünü seçtim ve HMI modülü üzerinden Hello ismini verdiğimiz topic’e abone olacağız.

    • Öncelikle hmi.properties dosyasında çeşitli değişikler yaparak, abone olacağımız topic ismi gibi detayları ekleyeceğim. Eklenen değişiklikler aşağıda verilmiştir.
    topic.Hello=Hello
    qos.Hello.Profile=Hello_World_Profile
    
    • DriverAlerts_subscriber.cxx dosyasına DDSDataReaderListener isimli classtan türetilerek yeni bir listener sınıfı yazılır.(Bir Modülün Akışı kısmını kafada oturmakta fayda var.). Aşağıda verilen kodlarda /// <yapılan değişikler> /// şeklinde sonradan eklenen kısımlar görülebilir
    /****************************************************************************
    (c) 2005-2017 Copyright, Real-Time Innovations, Inc.  All rights reserved.                                     
    RTI grants Licensee a license to use, modify, compile, and create derivative 
    works of the Software.  Licensee has the right to distribute object form 
    only for use with RTI products.  The Software is provided 'as is', with no
    arranty of any type, including any warranty for fitness for any purpose. RTI
    is under no obligation to maintain or support the Software.  RTI shall not
    be liable for any incidental or consequential damages arising out of the 
    use or inability to use the software.
    *****************************************************************************/
    
    #include <stdio.h>
    #include <stdlib.h>
    #include "Utils.h"
    
    #include "automotive.h"
    #include "automotiveSupport.h"
    #include "ndds/ndds_cpp.h"
    
    ////////////////////////////////////////////////////////////////////////////////
    class Hello_HelloWorldListenerClass : public DDSDataReaderListener {
        public:
        virtual void on_requested_deadline_missed(
            DDSDataReader* /*reader*/,
            const DDS_RequestedDeadlineMissedStatus& /*status*/) {
            printf("on_requested_deadline_missed\n");
        }
    
        virtual void on_requested_incompatible_qos(
            DDSDataReader* /*reader*/,
            const DDS_RequestedIncompatibleQosStatus& /*status*/) {
            printf("on_requested_incompatible_qos\n");
        }
    
        virtual void on_sample_rejected(
            DDSDataReader* /*reader*/,
            const DDS_SampleRejectedStatus& /*status*/) {
            printf("on_sample_rejected\n");
        }
    
        virtual void on_liveliness_changed(
            DDSDataReader* /*reader*/,
            const DDS_LivelinessChangedStatus& /*status*/) {
            printf("on_liveliness_changed\n");
        }
    
        virtual void on_sample_lost(
            DDSDataReader* /*reader*/,
            const DDS_SampleLostStatus& /*status*/) {
            printf("on_sample_lost\n");
        }
    
        virtual void on_subscription_matched(
            DDSDataReader* /*reader*/,
            const DDS_SubscriptionMatchedStatus& /*status*/) {
            printf("on_subscription_matched\n");
        }
    
        virtual void on_data_available(DDSDataReader* reader);
    };
    
    void Hello_HelloWorldListenerClass::on_data_available(DDSDataReader* reader)
    {
        Helloworld_HelloWordMessageDataReader *HelloWorldMessage_reader = NULL;
        Helloworld_HelloWordMessageSeq data_seq;
        DDS_SampleInfoSeq info_seq;
        DDS_ReturnCode_t retcode;
        int i;
    
        HelloWorldMessage_reader = Helloworld_HelloWordMessageDataReader::narrow(reader);
        if (HelloWorldMessage_reader == NULL) {
            printf("DataReader narrow error\n");
            return;
        }
    
        /* Read all samples */
        retcode = HelloWorldMessage_reader->take(
            data_seq, info_seq, DDS_LENGTH_UNLIMITED,
            DDS_ANY_SAMPLE_STATE, DDS_ANY_VIEW_STATE, DDS_ANY_INSTANCE_STATE);
    
        if (retcode == DDS_RETCODE_NO_DATA) {
            return;
        }
        else if (retcode != DDS_RETCODE_OK) {
            printf("take error %d\n", retcode);
            return;
        }
    
        for (i = 0; i < data_seq.length(); ++i) {
            
            if (info_seq[i].valid_data) {
                /* Print the data and store some data for use in the status message*/
                Helloworld_HelloWordMessageTypeSupport::print_data(&data_seq[i]);
                
            }
        }
    
        retcode = HelloWorldMessage_reader->return_loan(data_seq, info_seq);
        if (retcode != DDS_RETCODE_OK) {
            printf("return loan error %d\n", retcode);
        }
    }
    ////////////////////////////////////////////////////////////////////////////////
    
    class Alerts_DriverAlertsListener : public DDSDataReaderListener {
      public:
        virtual void on_requested_deadline_missed(
            DDSDataReader* /*reader*/,
            const DDS_RequestedDeadlineMissedStatus& /*status*/) {
            printf("on_requested_deadline_missed\n");
        }
    
        virtual void on_requested_incompatible_qos(
            DDSDataReader* /*reader*/,
            const DDS_RequestedIncompatibleQosStatus& /*status*/) {
            printf("on_requested_incompatible_qos\n");
        }
    
        virtual void on_sample_rejected(
            DDSDataReader* /*reader*/,
            const DDS_SampleRejectedStatus& /*status*/) {
            printf("on_sample_rejected\n");
        }
    
        virtual void on_liveliness_changed(
            DDSDataReader* /*reader*/,
            const DDS_LivelinessChangedStatus& /*status*/) {
            printf("on_liveliness_changed\n");
        }
    
        virtual void on_sample_lost(
            DDSDataReader* /*reader*/,
            const DDS_SampleLostStatus& /*status*/) {
            printf("on_sample_lost\n");
        }
    
        virtual void on_subscription_matched(
            DDSDataReader* /*reader*/,
            const DDS_SubscriptionMatchedStatus& /*status*/) {
            printf("on_subscription_matched\n");
        }
    
        virtual void on_data_available(DDSDataReader* reader) {}
    };
    
    
    /* Delete all entities */
    static int subscriber_shutdown(
        DDSDomainParticipant *participant)
    {
        DDS_ReturnCode_t retcode;
        int status = 0;
    
        if (participant != NULL) {
            retcode = participant->delete_contained_entities();
            if (retcode != DDS_RETCODE_OK) {
                printf("delete_contained_entities error %d\n", retcode);
                status = -1;
            }
    
            retcode = DDSTheParticipantFactory->delete_participant(participant);
            if (retcode != DDS_RETCODE_OK) {
                printf("delete_participant error %d\n", retcode);
                status = -1;
            }
        }
    
        return status;
    }
    
    
    extern "C" int subscriber_main(int sample_count)
    {
        DDSDomainParticipant *participant = NULL;
        DDSSubscriber *subscriber = NULL;
        DDSTopic *topic = NULL;
        Alerts_DriverAlertsListener *reader_listener = NULL;     
        DDSDataReader *reader = NULL;
        DDS_ReturnCode_t retcode;
        const char *type_name = NULL;
        int count = 0;
        int status = 0;
        int domainId = 0;
        DDSWaitSet *waitset = NULL;
        Alerts_DriverAlertsDataReader *Alerts_DriverAlerts_reader = NULL;
        DDS_Duration_t timeout = { 10, 0 };
    
        /////////////////////////////////////////////////
        Hello_HelloWorldListenerClass* hello_reader_listener = NULL;
        /////////////////////////////////////////////////
    
        /* Read the properties and configure */
        PropertyUtil* prop = new PropertyUtil("hmi.properties");
        domainId = prop->getLongProperty("config.domainId");
        
        std::string topicName = prop->getStringProperty("topic.Alerts");
        if (topicName == "") {
            printf("No topic name specified\n");
            return -1;
        }
    
        std::string HelloTopicName = prop->getStringProperty("topic.Hello");
        if (HelloTopicName == "") {
            printf("No planning topic name specified\n");
            return -1;
        }
    
        std::string HelloQosProfile = prop->getStringProperty("qos.Hello.Profile");
        if (HelloQosProfile == "") {
            printf("No Planning QoS Profile specified\n");
            return -1;
        }
    
        std::string qosLibrary = prop->getStringProperty("qos.Library");
        if (qosLibrary == "") {
            printf("No QoS Library specified\n");
            return -1;
        }
    
    
        std::string qosProfile = prop->getStringProperty("qos.Profile");
        if (qosProfile == "") {
            printf("No QoS Profile specified\n");
            return -1;
        }
    
        /* Create the participant */
        participant = DDSTheParticipantFactory->create_participant_with_profile(
            domainId, qosLibrary.c_str(), qosProfile.c_str(),
            NULL /* listener */, DDS_STATUS_MASK_NONE);
        if (participant == NULL) {
            printf("create_participant error\n");
            subscriber_shutdown(participant);
            return -1;
        }
    
        /* Create the subscriber */
        subscriber = participant->create_subscriber_with_profile(
            qosLibrary.c_str(), qosProfile.c_str(), NULL /* listener */, DDS_STATUS_MASK_NONE);
        if (subscriber == NULL) {
            printf("create_subscriber error\n");
            subscriber_shutdown(participant);
            return -1;
        }
    
        /* Register the type before creating the topic */
        type_name = Alerts_DriverAlertsTypeSupport::get_type_name();
        retcode = Alerts_DriverAlertsTypeSupport::register_type(
            participant, type_name);
        if (retcode != DDS_RETCODE_OK) {
            printf("register_type error %d\n", retcode);
            subscriber_shutdown(participant);
            return -1;
        }
    
        /* Create the alert topic */
        topic = participant->create_topic_with_profile(
            topicName.c_str(),
            type_name, qosLibrary.c_str(), qosProfile.c_str(), NULL /* listener */,
            DDS_STATUS_MASK_NONE);
        if (topic == NULL) {
            printf("create_topic error\n");
            subscriber_shutdown(participant);
            return -1;
        }
    
        /* Create a data reader listener */
        reader_listener = new Alerts_DriverAlertsListener();
    
        /* The listener is used for any events other than on data
           available. Since the alerts will pop up a message box
           it is handled as waitset in the main loop using on data 
           available listener would block the receive which we 
           don't want 
         */
        reader = subscriber->create_datareader_with_profile(
            topic, qosLibrary.c_str(), qosProfile.c_str(), reader_listener,
            DDS_STATUS_MASK_ALL & ~DDS_DATA_AVAILABLE_STATUS);
        if (reader == NULL) {
            printf("create_datareader error\n");
            subscriber_shutdown(participant);
            delete reader_listener;
            return -1;
        }
    
        /* Create status condition
        * ---------------------
        */
        DDSStatusCondition* status_condition = reader->get_statuscondition();
        if (status_condition == NULL) {
            printf("get_statuscondition error\n");
            subscriber_shutdown(participant);
            return -1;
        }
        /* All we are interessted is the on data available*/
        retcode = status_condition->set_enabled_statuses(
            DDS_DATA_AVAILABLE_STATUS);
        if (retcode != DDS_RETCODE_OK) {
            printf("set_enabled_statuses error\n");
            subscriber_shutdown(participant);
            return -1;
        }
    
    
        /* Attach condition to waitset
        * ---------------------------
        */
        waitset = new DDSWaitSet();
        if (waitset == NULL) {
            printf("waitset error\n");
            subscriber_shutdown(participant);
            return -1;
        }
    
        /* attach status condition to waitset */
        retcode = waitset->attach_condition(status_condition);
        if (retcode != DDS_RETCODE_OK) {
            printf("attach_condition error\n");
            subscriber_shutdown(participant);
            delete waitset;
            return -1;
        }
    
        /* Narrow data reader to specific type */
        Alerts_DriverAlerts_reader = Alerts_DriverAlertsDataReader::narrow(reader);
        if (Alerts_DriverAlerts_reader == NULL) {
            printf("DataReader narrow error\n");
            subscriber_shutdown(participant);
            delete waitset;
            return -1;
        }
    
    /////////////////////////////////////////////////////////////////////////////////////////////////////
    
        type_name = Helloworld_HelloWordMessageTypeSupport::get_type_name();
        retcode = Helloworld_HelloWordMessageTypeSupport::register_type(
            participant, type_name);
        if (retcode != DDS_RETCODE_OK) {
            printf("register_type error %d\n", retcode);
            subscriber_shutdown(participant);
            return -1;
        }
    
    
        topic = participant->create_topic_with_profile(
            HelloTopicName.c_str(),
            type_name, qosLibrary.c_str(), HelloQosProfile.c_str(), NULL /* listener */,
            DDS_STATUS_MASK_NONE);
        if (topic == NULL) {
            printf("create_topic error\n");
            subscriber_shutdown(participant);
            return -1;
        }
    
        hello_reader_listener = new Hello_HelloWorldListenerClass();
    
        reader = subscriber->create_datareader_with_profile(
            topic, qosLibrary.c_str(), HelloQosProfile.c_str(), hello_reader_listener,
            DDS_STATUS_MASK_ALL);
        if (reader == NULL) {
            printf("create_datareader error\n");
            subscriber_shutdown(participant);
            return -1;
        }
        ///////////////////////////////////////////////////////////////////////////////////////
    
    
        /* Main loop */
        for (count=0; (sample_count == 0) || (count < sample_count); ++count) {
    
            Alerts_DriverAlertsSeq data_seq;
            DDS_SampleInfoSeq info_seq;
            DDSConditionSeq active_conditions_seq;
    
            /* wait() blocks executione until condition becomes ture or timeout */
            retcode = waitset->wait(active_conditions_seq, timeout);
    
            if (retcode == DDS_RETCODE_TIMEOUT) {
                continue;
            }
            else if (retcode != DDS_RETCODE_OK) {
                printf("wait returned error: %d", retcode);
                break;
            }
            /* Check what caused the wait to return. It can really
               on be the on data available for the alert topic
             */
            int active_conditions = active_conditions_seq.length();
    
            for (int i = 0; i < active_conditions; i++) {
                if (active_conditions_seq[i] == status_condition) {
                    /* Take the data */
                    retcode = Alerts_DriverAlerts_reader->take(
                        data_seq, info_seq, DDS_LENGTH_UNLIMITED,
                        DDS_ANY_SAMPLE_STATE, DDS_ANY_VIEW_STATE, DDS_ANY_INSTANCE_STATE);
    
                    if (retcode == DDS_RETCODE_NO_DATA) {
                        continue;
                    }
                    else if (retcode != DDS_RETCODE_OK) {
                        printf("take error %d\n", retcode);
                    }
    
                    /* If we have valid data process it */
                    for (i = 0; i < data_seq.length(); ++i) {
                        if (info_seq[i].valid_data) {
                            /* Print the sample for information purpose*/
                            Alerts_DriverAlertsTypeSupport::print_data(&data_seq[i]);
    
                            /* Pop-up the right message box. On Linux we use SDL2 */
                            if (data_seq[i].backCollision) {
                                MessageBoxUtil::PopUp((char *)"Back Collision Warning", MSGBOX_WARNING);
                            }
                            if (data_seq[i].blindSpotDriver) {
                                MessageBoxUtil::PopUp((char *)"Car in blind spot on driver side", MSGBOX_INFO);
                            }
                            if (data_seq[i].blindSpotPassenger) {
                                MessageBoxUtil::PopUp((char *)"Car in blind spot on passanger side", MSGBOX_INFO);
                            }
                            if (data_seq[i].driverAttention) {
                                MessageBoxUtil::PopUp((char *)"Driver Attention", MSGBOX_ATTENTION);
                            }
                            if (data_seq[i].frontCollision) {
                                MessageBoxUtil::PopUp((char *)"Front Collision Warning", MSGBOX_ATTENTION);
                            }
                            if (data_seq[i].parkingCollision) {
                                MessageBoxUtil::PopUp((char *)"Parking Collision Warning", MSGBOX_WARNING);
                            }
                        }
                    }
                }
    
                retcode = Alerts_DriverAlerts_reader->return_loan(data_seq, info_seq);
                if (retcode != DDS_RETCODE_OK) {
                    printf("return loan error %d\n", retcode);
                }
    
            }
        }
    
        /* Delete all entities */
        status = subscriber_shutdown(participant);
        delete reader_listener;
        delete waitset;
        return status;
    }
    
    int main(int argc, char *argv[])
    {
        int sample_count = 0; /* infinite loop */
    
    
        if (argc >= 2) {
            sample_count = atoi(argv[1]);
        }
    
        /* Uncomment this to turn on additional logging
        NDDSConfigLogger::get_instance()->
        set_verbosity_by_category(NDDS_CONFIG_LOG_CATEGORY_API, 
        NDDS_CONFIG_LOG_VERBOSITY_STATUS_ALL);
        */
    
        return subscriber_main(sample_count);
    }
    

    Yapılan değişikliklerden sonra objs klasörü tamamen silinerek, tekrar derleme işlemi yapılması gerekmektedir. Sistemi çalıştırdığımızda HMI terminalimizde Alarm oluşmadan önce verilerin geldiği görülebilir.

    Alarm oluştuğunda ise alarm oluştuğuna dair veri ve abone olduğumuz Hello topic’e ait veriler gözlenebilir.

    RTI Launcher üzerinden incelendiğinde HMI modülünün artık 2 farklı topic’e abone olduğu görülebilir.

    Buraya kadar bizimle olduğun için teşekkür ederim 🙂

  • Bağlı Listeler, Manzara, Tımer, Onur Saylak

    Bağlı Listeler, Manzara, Tımer, Onur Saylak


    Veri yapılarına yönelik içeriklerin bir çoğu herhangi bir kullanım şekli (use-case) senaryosuna değinmek yerine sadece ne olduğuna dair genel bir bilgi veriyor. Bu tarz öğrenme şekli benimle çok uyuşmadığı ve öğrenme metodu benim gibi olan bireylerin varlığı için temel veri yapılarından olan bağlı listeyi (linked list) kendimce bir tane kullanım senaryosuna yedirerek bir yazı yazmak istedim.

    https://www.reddit.com/r/ProgrammerHumor/comments/p8d9iu/linked_list/

    Yukarda bulunan görsel üzerinden bağlı liste için genel terminolojiyi konuşabiliriz.

    1. Hepsi kendi başına bir birey (node <-> düğüm)
    2. En solda bulunan arkadaş baş düğümümüz (head, root node),
    3. En sağda duran ve halinden çok memnun gözükmeyen arkadaşımız ise son düğümümüz (tail node).
    4. Bu 4 arkadaşın bir araya gelmesi ile oluşan yapıyı ise bağlı liste (linked list) olarak adlandırabiliriz. Daha spesifik isimlendirecek olursak bu tek yönlü bir bağlı listedir(single linked list). (Çift taraflı bağlı listenin (double linked list) bu görsele göre nasıl olacağını bir canlandırabilirsiniz.)

    Gelelim bir kullanım senaryosuna, donanımın bize sağladığı bir timer mekanizmasını kullanarak kendi yazılımsal timer modülümüzü yazmaya çalışacağız. Parkura çıktığımızı ve kronometremizi tetiklediğimizi düşünelim(System tick olarak düşünebiliriz.) Yaldır yaldır koşmaya başladık (askeri lise mülakatında öyle koşmuştum, tam tamına 400 metre) bu sürecinin sonunda neyi hedeflediğimizi bir çıkaralım.

    1. Bitirmek istediğimiz bir süre olması gerekiyor, [Interval]
    2. Hedeflediğimiz sürede parkuru bitirdiğimizde devam mı edeceğiz yoksa tamam kardeşim diyerek yere mi yığılıyoruz [Single Shot or Auto Reloaded Timer]
    3. Sürenin sonunda almamız gereken bir aksiyon var mıdır ? Bir su içelim ? [Callback when the timer expired]
    4. Koşunun ortasında bakmak isteyeceğimiz diğer bir olay ise kalan zaman. [remaining time]
    5. Koşuya ne zaman başladığımız [starting timestamp]

    Koda dökmek istersek;

    typedef enum timer_type
    {
      SHOT,
      LOOP
    }timer_type_e;
    
    typedef struct timer_s
    {   
      struct timer_s* timer_list; ///< Next entry in the single linked list
      timer_type_e type; ///< Single Shot or auto - reloaled
      void *arg; ///< Placeholder for argument will pass when the timer is expired.
      void (*exp_func)(void *); ///< Call the callback function When the timer is expired
      ut32_timer remaining_time_to_expire; ///< Remaining time to the fire!
      ut32_timer starting_timestamp; ///<Placeholder for starting timestamp
      ut32_timer interval; ///< milisecond
    }timer_t;
    

    Parkurda koşmaya devam ediyoruz ve sen yaparsın diyen çocuğun(bkz) babasıda koşmaya başladı. Koşmaya yeni başlayan bireyi tutmak ve halihazırda koşan birisine bağlamak için kullandığımız nokta ise tam olarak şurası.

    struct timer_s* timer_list;
    

    İlk koşmaya başlayan kişiyi referans alarak diğer koşan bireyleri de tutmaya başladık. Peki ilk koşan kişiyi kim tutacak ? Hadi gelin;

    typedef struct timer_mngmnt_s
    {
      timer_t* root; ///< Handle root
      ut8_timer count_of_active_timers; ///< Total number of the running timers;
    }timer_mngmnt_t;
    
    static timer_mngmnt_t tmr_mngmnt = {.root = NULL, .count_of_active_timers = 0};
    

    Şimdi parkur başına bir tane koşucuyu alalım ve yukarda bahsettiğimiz hedeflerini bize bir söylesin.

    void init_timer(timer_t *tmr, timer_type_e type, void *arg, void(*exp_func)(void*), ut32_timer interval)
    {
      tmr -> timer_list = NULL;
      tmr -> type = type;
      tmr -> exp_func = exp_func;
      tmr -> arg = arg;
      tmr -> remaining_time_to_expire = 0;
      tmr -> interval = interval;
      tmr -> starting_timestamp = 0;
    }
    

    Koşmaya başlasın.

    static void timer_add(timer_t *tmr)
    {
      if (NULL == tmr_mngmnt.root)
      {
        tmr_mngmnt.root = tmr;
      }
      else 
      {
        timer_t *pos = tmr_mngmnt.root;
        
        while (NULL != pos -> timer_list)
        {      
          pos = pos -> timer_list;
        }
        
        pos -> timer_list = tmr;
      }
    
      ++tmr_mngmnt.count_of_active_timers;
    }
    

    Eğer parkur boş ise koşan ilk kişiyi baş düğüm olarak belirledik. Şayet halihazırda parkurda koşan diğer kişiler var ise parkura ilk çıkan kişi üzerinden en son çıkanı bulduk ve onaşiii (onunla ilişkilendirdik yazmak istedim ama kedi klavyeye bastı ve anı kalması adına silmiyorum.)

    Tabi koşmaya başlayan bireyler hayatlarının sonlarına kadar koşmayacaklar ve burada iki senaryo mevcut.

    1. Kişi istediği zaman koşmayı bırakabilir.
    2. Kişi zaten parkuru tek sefer koşmak için gelmiştir.

    Şimdi şayet bu kişiyle ilişkilendirilmiş bir kişi var ise bu adam parkurdan ayrılırsa ben onu sonra nereden bulacağım ? O yüzden bu kişi parkurdan ayrılmadan yapmam gerekenler var.

    Diyelim parkurda 3 kişi var. (1 -> 2 -> 3) Ve 2. arkadaşımız ayrılmak istiyor. Bu durumda 1 -> 3 arasında ilişkiyi kurmalıyım. Nasıl mı ?

    static void unlink_timer(timer_t *tmr)
    {
      //Check timer is root ?
      //Ex: root (t1) -> t2 -> t3
      // (t1) == (unlinked_timer)
      // root -> t2 -> t3
      if (tmr_mngmnt.root == tmr)
      {
        tmr_mngmnt.root = tmr -> timer_list;
        --tmr_mngmnt.count_of_active_timers;
        return;
      }
    
      timer_t *prev = tmr_mngmnt.root;
    
      for (timer_t* pos = tmr_mngmnt.root; NULL != pos; pos = pos -> timer_list)
      {
        // Ex; root (t1) -> t2 -> t3
        // (t1 -- t2) == unlinked_timer
        // t1 -> t3
        if (prev -> timer_list == tmr)
        {
          prev -> timer_list = tmr -> timer_list;
          --tmr_mngmnt.count_of_active_timers;
          break;
        }    
        //Update to previous one
        prev = pos;
      }  
    }
    

    Koşan bireylerin parkura çıkmadan önce belirledikleri hedefleri bir göz gezdirelim.(linked list traverse) Ve bu hedefler doğrultusunda aksiyonlar alalım.

    void timer_pool(void)
    {
      for (timer_t* pos = tmr_mngmnt.root; NULL != pos; pos = pos -> timer_list)
      {
        if ((get_systick() - pos -> starting_timestamp) >= pos -> interval)
        {
          /* Call expire func */
          if (pos -> exp_func != NULL)
          {
            pos -> exp_func(pos -> arg);
          }
    
          if (pos -> type == SHOT)
          {
            unlink_timer(pos);
          }
          /* Reload the timer */
          else if (pos -> type == LOOP)
          {
            pos -> starting_timestamp = get_systick();
          }
        }
      }
    }
    

    Projenin Atmega328p üzerinde örnek çalışmasını görmek için github reposuna gelebilirsiniz.

    Bu yazının ilhamı olan manzarama teşekkür ederek yazıyı sonlandırmak isterim.

WordPress.com ile böyle bir site tasarlayın
Başlayın