Sahada OCPP 1.6: Şarj İstasyonu Simülatöründen Dersler

OCPP 1.6 okunması kolay bir spesifikasyon. Bir şarj oturumunu tamamlayacak kadarını birkaç günde uygulayabilirsiniz. Bununla, gerçek bir arka uca karşı, filo ölçeğinde ve haftalar boyunca doğru davranan bir şarj istasyonu arasındaki mesafe ise mühendisliğin tamamının yaşadığı yer.

SimIt'i geliştiriyoruz: fiziksel şarj istasyonlarının yerine geçerek işletmecilerin merkezi sistemlerini — CSMS'i — donanım satın almadan test etmesini sağlayan bir simülatör. OCPP 1.6 ve 2.0.1 konuşuyor ve gerçek arka uçlara karşı aynı anda çok sayıda istasyon çalıştırıyor. Bu bileşim belirli bir problem sınıfını ortaya çıkarıyor: "mesaj ayrıştırılıyor mu" değil, "dört yüzüncü istasyonda, üçüncü günde ne oluyor".

Aşağıdakiler bize en pahalıya öğrenilen dersler.

1.6 ve 2.0.1 konnektörün ne olduğu konusunda anlaşamıyor

OCPP 1.6'da bir şarj istasyonunun düz bir konnektör listesi vardır; tek bir tam sayı olan connectorId ile adreslenir ve 0 "istasyonun bütünü" anlamına gelir. 2.0.1'de model iki katmanlıdır: istasyon EVSE'ler içerir, her EVSE kendi içinde yerel olarak numaralanmış konnektörler içerir.

İki sürümü birlikte destekliyorsanız — ve bu alanda araç geliştiren herkes desteklemek zorundadır — tek bir dahili modele ve protokol sınırında bir çeviriye ihtiyacınız var. Biz dahilî model olarak 2.0.1'in şeklini seçtik, çünkü bir hiyerarşiyi her zaman düz listeye indirebilirsiniz ama düz tam sayılardan hiyerarşiyi güvenilir biçimde geri kuramazsınız; 1.6 işleyicileri için de her iki yöne açık dönüşümler yazdık.

Cazip alternatif, düz modeli koruyup 2.0.1'i özel durum olarak ele almaktır. EVSE başına birden fazla konnektörü olan bir istasyona rastlayana kadar işe yarar — tek güç katı üzerinde CCS ve CHAdeMO taşıyan, akım limitini paylaşan bir DC ünite gibi. Düz numaralandırma bunların bir şeyi paylaştığını ifade edemez, dolayısıyla üzerine kurulan her güç paylaştırma kararı sinsice yanlış olur. Bugün konuştuğunuz protokol ihtiyaç duymasa bile zengin modeli seçin.

Şarj profilleri: limitin kime ait olduğunu bilmek

Akıllı şarj, 1.6'nın uygulamaların en sık yanlış yaptığı kısmı — çünkü "bu konnektörün akım limiti nedir" sorusunun cevabı hiçbir zaman saklanmış tek bir sayı değildir. Aynı anda birden fazla profil geçerli olabilir ve spesifikasyon bunların nasıl birleştiğini tanımlar.

Profillerin bir amacı vardır — istasyon geneli bir üst sınır, işlemler için bir varsayılan ya da tek bir işleme bağlı bir limit — ve bir yığın seviyesi. Çözümleme her amaç içindeki en yüksek yığın seviyesini alır, ardından spesifikasyonun akıllı şarj bölümündeki amaç önceliğini uygular ve etkin limit en kısıtlayıcı sonuçtur. İşleme özgü bir profil istasyon üst sınırının yerine geçmez; onunla sınırlanır.

Gözden kaçması kolay iki ayrıntı var ve ikisi de makul görünen yanlış sayılar üretiyor:

  • Profiller bir değer değil, bir çizelge taşır. Limit, çizelge başlangıcından itibaren geçen süreye bağlı, ayrık dönemlerden oluşan bir fonksiyondur. "Limiti okumak", bir alanı okumak değil çizelgeyi bir zaman noktasında değerlendirmek demektir.
  • Birim profil başınadır. Bir profil hızını amper ya da watt olarak bildirir. İki profili karşılaştırmak önce normalize etmeyi gerektirir; amperi watta çevirmek ise besleme gerilimi ve faz sayısını gerektirir — bunlar profilin parçası değil, istasyon yapılandırmasıdır. Birimleri farklı profillerin ham sayılarını karşılaştırırsanız, yüzlerce kat sapmış bir limiti kendinizden emin biçimde uygularsınız.

Profil deposunu kendi doğal biriminde tutuyoruz ve dönüşümü yalnızca karşılaştırma noktasında yapıyoruz; böylece dönüşüm varsayımları gözden geçirilebilecekleri tek bir yerde duruyor.

Çevrimdışı yetkilendirme tek bayrak değil, üç bayrağın etkileşimi

Yalnızca bağlıyken çalışan bir şarj istasyonu, şarj istasyonu değildir. Spesifikasyonun kopan bağlantıya cevabı bir dizi yapılandırma anahtarıdır ve gerçek davranış bunların etkileşimidir:

  • LocalAuthListEnabled — istasyon, bildiği kimliklerin yerel önbelleğine bakabilir mi?
  • LocalPreAuthorize — CSMS onayını beklemeden bu yerel listeye dayanarak işlem başlatabilir mi?
  • AllowOfflineTxForUnknownId — çevrimdışıyken, hiç görmediği bir kimlik için işlem başlatabilir mi?

Tek tek okununca her biri apaçık. Bir arada ise bir karar tablosu oluşturuyorlar ve uygulamaların birbirinden ayrıştığı yer bu kombinasyonlar: çevrimiçi ama yavaş bir istasyon, çevrimdışı olandan farklı davranır ve yerel listede bulunmayan bir kimlik, listede olup süresi geçmiş bir kimlikle aynı şey değildir.

Bunu, işlem işleyicilerinin içine if çevrimdışı kontrolleri dağıtmak yerine, kimliği ve o anki bağlantı durumunu alıp bir karar döndüren tek bir yetkilendirme fonksiyonu olarak uyguladık. Bu biçim düzenlilikten öte bir sebeple önemli: kapsamlı biçimde test edilebilen tek sürüm bu. Üç bayrağın, iki bağlantı durumunun ve üç kimlik koşulunun her bileşimi sayılabilir bir tablodur — ama yalnızca mantık tek bir yerde duruyorsa.

İstasyonun kendi saatine asla güvenmeyin

Zaman damgası taşıyan her OCPP mesajı, bir faturalandırma anlaşmazlığında delildir. Bir istasyonun saati yanlışsa, sayaç değerleri de yanlıştır — ve bu, biri faturaya itiraz edene kadar görünmez bir yanlışlıktır.

İstasyon saatleri kayar ve sahada kimi zaman fena hâlde yanlıştır; elektriği kesilip saati epoch'a dönmüş bir ünite olağandışı değildir. Bu yüzden protokol zaman damgalarında yerel saati kullanmıyoruz. CSMS kendi saatini BootNotification yanıtında ve heartbeat yanıtlarında bildiriyor; biz bununla yerel saat arasındaki farkı saklıyor ve giden her zaman damgasını yerel saat artı bu fark olarak türetiyoruz.

Saati kurmak yerine bir fark tutmak buradaki asıl ayrıntı. Ucuz, yetki gerektirmiyor, CSMS'in kısa süre erişilemez olmasına dayanıyor ve arka ucun saati zıplasa bile bizim kendi olaylarımız arasındaki aralıkları monoton tutuyor. İstasyon, kendisini bir zaman otoritesi gibi göstermeden arka uçla mutlak zaman konusunda anlaşıyor.

Filolar yeniden bağlanma döngüsünde ölür

Pahalı olan bu ve pahalı olmasının sebebi, hatanın kimsenin önemli saymadığı bir kod parçasında olması: başarısız bir WebSocket bağlantısını yeniden deneyen döngü.

Bizimki yeniden deneme aralığını yalnızca el sıkışma zaman aşımına uğradığında artırıyordu. Başka her başarısızlık — arka ucun doğrudan reddetmesi dahil — sayacı sıfırlıyor ve temel aralıkta, birkaç saniyede bir, süresiz olarak yeniden deniyordu.

Bu ayrım, bir CSMS hız sınırlaması uygulamaya başlayana kadar zararsız görünür. Arka uç her bağlantı denemesine zaman aşımı yerine anında bir ret döndürdüğünde, filodaki her istasyon temel yeniden deneme bandında kalıp arka uca birkaç saniyede bir vurmaya devam etti ve hiç geri çekilmedi. Filo kalıcı bir bağlan-reddedil döngüsüne girdi.

Ardından gelen arıza, tahmin edeceğimiz arıza değildi. Reddedilen bir el sıkışma hiç oturum kurmaz, dolayısıyla tek bir bağlantı fazla bir şey sızdırmadı. Ama bu çalkantı içindeki her başarılı yeniden bağlanma bir istasyonun tüm nesne grafiğini yeniden inşa ediyor ve süreç belleği bu tekrarlanan inşa altında istikrarlı biçimde büyüyerek süreçler bellek sınırlarını aştıkları için sonlandırılana kadar devam etti. Bir yeniden deneme politikası kusuru, sebebinden birkaç katman uzakta, bellek yetersizliği olayı olarak göründü.

Bu yüzden tek işi bir reddi sınıflandırıp bir bekleme süresi üretmek olan küçük bir modül yazdık. Ayaktayım ama şu an seni alamıyorum anlamına gelen retler — hız sınırı ve hizmet kullanılamaz durumları — bir üst sınıra kadar, üzerine gürültü eklenmiş bir eğri boyunca artıyor.

İki kez yanlış yaptığımız devam kararı

Kalıcı retleri — hatalı kimlik bilgisi, yanlış URL yolu — başta backoff'un dışında tuttuk; sağlam görünen iki gerekçeyle: yanlış yapılandırılmış bir istasyonu yeniden denemek ucuzdur ve operatörün hatayı hemen görmesi gerekir.

İkisi de yanlıştı ve kayıtlara sonradan bakmak nedenini gösterdi. Bir avuç yanlış yapılandırılmış istasyon, filo genelinde kaydedilen tüm uyarıların ezici çoğunluğunu üretiyordu; her biri saatler boyunca, hiç artmayan birkaç saniyelik aralıkla yeniden deniyordu. Ucuz değil: bu, daha önceki bellek olayına yol açan bağlan-reddedil çalkantısının tam kendisi. Hemen görünür de değil — sinyal tek bir belirgin satır değil, birbirinin aynısı binlerce satırdı ve aynı anda yaşanan diğer, gerçekten farklı arızaları gömüyordu. Ayrıca başkasının arka ucuna karşı sürekli akan başarısız kimlik doğrulama denemeleri, çıkış adresinizi engellemeye götüren iyi bir yol.

Kalıcı retler artık aynı eğri boyunca artıyor; hız sınırı sınıfından ayrı izleniyor, çünkü aynı şekli paylaşıyorlar ama aynı anlamı paylaşmıyorlar. Artırmak yanlış yapılandırmayı gizlemiyor: her deneme kendi durum kodunu yine kaydediyor, sadece azalan bir hızda.

Asla vazgeçmemek

Baştan sona koruduğumuz bir kural: döngü aralığı artırır ama asla vazgeçmez.

Gerekçe, arızayı kimin düzelttiğiyle ilgili. Hız sınırında arka uç her an toparlanabilir. Kalıcı retde ise kurtarma olayı, bir insanın kimlik bilgisini ya da URL'yi düzeltmesidir — ve sonrasında kimse istasyona yürüyüp elektriğini kesip açmayacak. Kalıcı olarak vazgeçen bir istasyonun kurtarılması saha ziyareti gerektirirdi; yavaşça yeniden deneyen istasyon ise düzeltmeyi üst sınır aralığı içinde kendi başına fark eder. Yavaş bir istasyon, ölü bir istasyondan çok daha iyi bir arızadır.

Buradan çıkarılacaklar

  • Zengin alan modelini dahilî tutun, protokol sınırında çevirin. Düzleştirmek sonradan ihtiyaç duyacağınız ilişkileri yok eder.
  • Akıllı şarjda limit, saklanmış bir sayı değil; bildirilmiş bir birimde, bir zaman noktasında değerlendirilen bir çizelgedir.
  • Çok bayraklı kararları tek bir fonksiyona koyun. Dağıtılmış koşullar sayılamaz, dolayısıyla test edilemez.
  • Protokol zaman damgalarını yerel saatten değil, arka ucun saatine göre tutulan bir farktan türetin.
  • Yeniden deneme döngüleri hata sınıflarını ayırmalıdır. Zaman aşımı olmayan her şeyi geçici saymak, bir arka ucun hız sınırını sizin kesintinize çevirir.
  • Kurtarma olayı birinin uzaktan yapılandırma düzeltmesiyse, aralığı artırın ama asla vazgeçmeyin.
  • Gösterişsiz bir koddaki kusur, birkaç katman uzakta alakasız bir belirti olarak görünebilir. Hata mesajını değil çalkantıyı izleyin.

SimIt'e simit.ddosoft.com adresinden ulaşabilirsiniz; bir CSMS'i simüle edilmiş istasyonlara karşı test etmek için ücretsiz planı var. OCPP 1.6 ya da 2.0.1 entegrasyonu yapıyorsanız ve bu dersleri bizim öğrendiğimiz yolla öğrenmek istemiyorsanız, bizimle iletişime geçin.