# Fonksiyon yazın

> Merkez sunucuda bir C# fonksiyonu yazın, derleyin, Edge Gateway'lere dağıtın ve günlüklerini okuyun. Hazır bir şablonla ya da kendi kodunuzla başlayın.

Kaynak: https://proxus.io/tr/docs/automate/functions

Bu kılavuzda merkez sunucuda küçük bir C# fonksiyonu yazacak ve Edge Gateway'de çalıştıracaksınız. Fonksiyon, bir tünel
fırınından gelen her mesajı okur. Fırının 1. bölgesi 180 °C'nin üzerindeyse günlüğe en fazla dakikada bir uyarı
yazar. Yazacağınız her fonksiyon için adımlar aynıdır.

Fonksiyon, Edge Gateway'in kendi cihazlarının yanında çalıştırdığı bir C# sınıfıdır. İhtiyacınızı bir
[işlem akışıyla](https://proxus.io/tr/docs/automate/pipelines) ya da [alarm kuralıyla](https://proxus.io/tr/docs/automate/rules) karşılayamadığınızda
fonksiyon kullanın. Kullanabileceğiniz sınıflar ve yardımcı yöntemler [Functions SDK](https://proxus.io/tr/docs/develop/functions-sdk)
sayfasında anlatılır.

## Başlamadan önce

Şunlar gerekir:

- Durumu **Bağlı** olan bir Edge Gateway.
- Temel düzeyde C# bilgisi. Düzenleyici, siz yazarken kodu tamamlar ve hataları işaretler.
- Fonksiyon oluşturma ve dağıtma izni olan bir kullanıcı rolü.

## Adım 1: Fonksiyonu oluşturun

**Kurallar ve akışlar > Fonksiyonlar** ekranını açın ve **Yeni** (1) düğmesini seçin.

*Ekran görüntüsü: Araç çubuğunda Yeni düğmesi işaretli fonksiyon listesi*

Yeni fonksiyon boş bir düzenleyiciyle açılır. Düzenleyicinin altında **Yapılandırma** bölümü bulunur.

## Adım 2: Şablonla başlayın ya da kodu yazın

Bir örnekten başlamak için araç çubuğundaki **Diğer öğeler** menüsünü (**...**) açın ve **Şablonlar** komutunu
seçin. Listeden bir şablon seçin ve **Seç** düğmesini seçin. Düzenleyicideki kodun yerini şablon alır.

*Ekran görüntüsü: Dokuz şablon ve açıklamalarıyla Fonksiyon şablonu listesi*

| Şablon (`Product.<name>.cs` adıyla listelenir) | Yaptığı iş |
| --- | --- |
| DerivedMetricsFunction | Gelen cihaz verisine hesaplanmış değerler ekler. |
| DeviceHealthMonitorFunction | Cihazları, son değerlerinin geldiği zamana göre güncel ya da eskimiş olarak işaretler. |
| HistoricalJsonImportFunction | Geçmiş değerleri, kendi zaman damgalarıyla birlikte JSON'dan içe aktarır. |
| HttpJsonPollingFunction | Bir REST uç noktasını sorgular ve seçilen JSON alanlarını değer olarak kaydeder. |
| MqttJsonIngestionFunction | JSON gönderen bir MQTT konusuna abone olur ve özelliklerini değer olarak kaydeder. |
| OeeTrackerFunction | Yaygın üretim sayaçlarından OEE değerlerini hesaplar. |
| PayloadNormalizerFunction | Veri saklanmadan önce değerleri yeniden adlandırır ve birimlerini dönüştürür. |
| SqlPollingIngestionFunction | Bir SQL kaynağını sorgular ve sonucu değer olarak kaydeder. |
| ThresholdAlarmFunction | Bekleme süresi ve ölü bant uygulayarak eşik alarmları üretir. |

Her şablonun sınıf adına kısa, rastgele bir son ek eklenir. Böylece aynı şablonu birden çok kez kullanabilirsiniz.

Bu kılavuzda şablon yerine aşağıdaki kodu düzenleyiciye yapıştırın:

```csharp
using System;
using Proxus.Common;
using Proxus.Common.Messages;
using Proxus.SDK.BaseFunctions;

namespace Plant {

public sealed class OvenZoneWatch(object _, object __, object ___) : FunctionBase(_, __, ___) {

    private const string Device = "MFB_LYS_TUNNEL_OVEN";
    private const double Limit = 180;
    private static readonly TimeSpan Quiet = TimeSpan.FromMinutes(1);
    private DateTime _lastWarning = DateTime.MinValue;

    protected override void OnStarted() {
        // Receive every message of tag values the gateway reads.
        Subscriptions?.Add(new SubscriptionContext {
            Type = typeof(TransportData),
            Topics = ["*"]
        });

        base.OnStarted();
    }

    protected override void OnMessageReceive(FunctionContext ctx) {
        if (ctx.Message is TransportData data && data.GetMetaDataValueByKey("DeviceName") == Device) {
            var zone1 = data.GetPayloadValueByName<double>("Zone1Temp");
            if (zone1 > Limit && DateTime.UtcNow - _lastWarning > Quiet) {
                _lastWarning = DateTime.UtcNow;
                LogWarning($"{Device} zone 1 is {zone1:0.0} degC, above {Limit} degC.");
            }
        }

        base.OnMessageReceive(ctx);
    }
}
}
```

Her fonksiyon aynı yapıdadır: `FunctionBase` sınıfından türeyen ve yukarıdaki gibi üç parametreli kurucusu olan tek
bir sınıf. `OnStarted` yöntemi fonksiyonun hangi mesajları dinleyeceğini belirler. `OnMessageReceive` yöntemi gelen
her mesajı işler.

Fonksiyonun adı, dağıtım sırasında koddan alınır ve C# ad alanı (namespace) ile sınıf adından oluşur. Bu örnekte
ad `Plant.OvenZoneWatch` olur (ekran görüntülerinde `DOCS` ad alanındaki bir kopya görünür). Ad alanının
algılanması için onu blok biçiminde yazın (`namespace Plant { ... }`). Fonksiyon adları benzersiz olmalıdır.

## Adım 3: Edge Gateway'leri seçin

Düzenleyicinin altındaki **Yapılandırma** bölümünü açın. **Edge Gateway'ler** listesi, fonksiyonun çalışacağı uç
cihazları gösterir. Yeni fonksiyonlarda listede **SYSTEM** bulunur. Edge Gateway eklemek ya da çıkarmak için bu listeyi
kullanın. Fonksiyon dağıtıldıktan sonra liste kilitlenir. Değiştirmek için önce dağıtımı kaldırmanız gerekir.

## Adım 4: Derleyin

**Derle** (1) düğmesini seçin.

*Ekran görüntüsü: Derle ve Dağıt düğmeleri ve düzenleyicideki koduyla dağıtılmış bir fonksiyon*

1. **Derle**, kodu merkez sunucuda derler. "Derleme başarılı." iletisi kodun hazır olduğunu gösterir. Derleme
   başarısız olursa iletide hatalar satır numaralarıyla listelenir.
2. **Dağıt**, fonksiyonu Edge Gateway'lere gönderir.
3. Düzenleyici, siz yazarken hataları ve uyarıları işaretler. Kodu biçimlendirmek için Ctrl+Shift+F (Mac'te
   Cmd+Shift+F) tuşlarına basın. Sağ üst köşedeki iki düğmeden biri kodu kopyalar, diğeri tam ekrana geçer.

## Adım 5: Dağıtın

**Dağıt** düğmesini seçin ve işlemi onaylayın. Merkez sunucu fonksiyonu kaydeder, derlenip başlatılabildiğini
kontrol eder ve **Edge Gateway'ler** listesindeki her Edge Gateway'e gönderir. Her Edge Gateway fonksiyonu yeniden derler ve
başlatır. Sonuç iletisi, dağıtımı onaylayan ve yanıt vermeyen Edge Gateway'leri listeler.

Fonksiyon listede artık **Dağıtıldı** olarak görünür. **Çalıştığı yer** sütunu, fonksiyonun hangi Edge Gateway'lerde
çalıştığını gösterir.

*Ekran görüntüsü: OvenZoneWatch fonksiyonunun SYSTEM Edge Gateway'ine dağıtıldığını gösteren fonksiyon listesi*

Yeniden başlatılan bir Edge Gateway, kendisine dağıtılmış fonksiyonları merkez sunucudan yeniden alır.

## Adım 6: Günlük kayıtlarını okuyun

Fonksiyonu açın, **Diğer öğeler** (**...**) menüsünü açın ve **Günlükler** komutunu seçin. Listede yalnızca bu
fonksiyonun günlük kayıtları görünür. **Sorunlar**, **Hatalar** ya da **Tüm günlükler** seçeneklerinden birini
seçin. Yeni kayıtları görmek için **Yenile** düğmesini seçin.

*Ekran görüntüsü: Fonksiyonun Günlükler penceresi: fırının 1. bölgesinin 180 °C'nin üzerinde olduğunu bildiren iki uyarı*

Kodunuzdan günlüğe yazmak için `LogInformation`, `LogWarning`, `LogError` ve diğer `Log...` yöntemlerini kullanın.

## Fonksiyonu değiştirin ya da durdurun

- **Değiştirmek için** kodu düzenleyin, **Derle** düğmesini, ardından yeniden **Dağıt** düğmesini seçin. Yalnızca
  kaydetmek, Edge Gateway'lerde çalışan kodu değiştirmez.
- **Durdurmak için** **Dağıtımı kaldır** komutunu seçin. Fonksiyon durur ve Edge Gateway'lerden kaldırılır. Dağıtılmış
  bir fonksiyon silinemez. Silmeden önce dağıtımını kaldırın.

## Sınırlar

- Bazı .NET ad alanları kullanılamaz. Bunlar arasında `System.IO`, `System.Diagnostics`, `System.Reflection`,
  `System.Data`, `System.Text.RegularExpressions` ve `Microsoft.Win32` bulunur. Bunlardan birini kullanırsanız
  derleme hatası bu ad alanının adını gösterir. Veritabanlarına okuma ve yazma için `FunctionBase` sınıfının yardımcı
  yöntemlerini kullanın.
- Edge Gateway, çok sayıda işlenmemiş hata (exception) fırlatan bir fonksiyonun dağıtımını kaldırır. Sınırı, yapılandırma
  dosyasının `[UserFunctions]` bölümündeki `ExceptionThresholdPerFunction` ayarı belirler (varsayılan: 10).
- Günlüğün dolmasını önlemek için her fonksiyon dakikada yalnızca birkaç hata ve kritik kaydı yazabilir. Bu sayıyı
  `[UserFunctions]` bölümündeki `LogLevelPolicy` ayarı belirler (varsayılan: her biri için dakikada iki).

```toml
[UserFunctions]
ExceptionThresholdPerFunction = 10
LogLevelPolicy = ["Error:2 1m", "Critical:2 1m"]
```

## Sonraki adımlar

- [Functions SDK](https://proxus.io/tr/docs/develop/functions-sdk): temel sınıf, yardımcı yöntemler ve daha fazla örnek.
- [Yapılandırma anahtarları](https://proxus.io/tr/docs/reference/configuration): `[UserFunctions]` bölümündeki ayarlar.
- [Otomasyon sorunlarını giderme](https://proxus.io/tr/docs/troubleshoot/automation): derlenmeyen ya da çalışmayan fonksiyonlar için çözümler.
