# Functions SDK

> Edge Gateway'de çalışan fonksiyonlar için C# temel sınıfı ve yardımcı yöntemler. Hesaplanan veri noktası ekleyen tam bir örnek içerir.

Kaynak: https://proxus.io/tr/docs/develop/functions-sdk

Fonksiyon, Proxus'un derleyip seçtiğiniz Edge Gateway'lerde çalıştırdığı bir C# sınıfıdır. Edge Gateway'in okuduğu değerleri
alır, bu değerleri değiştirebilir veya yeni değerler ekleyebilir. Ayrıca değer kaydedebilir, başka sistemleri
çağırabilir ve bildirim gönderebilir. Bu sayfa programlama arayüzünü anlatır. Merkez sunucuda fonksiyon oluşturma,
derleme ve dağıtma adımları için [Fonksiyonlar](https://proxus.io/tr/docs/automate/functions) sayfasına bakın.

## Bir fonksiyonun yapısı

Fonksiyon, `FunctionBase` sınıfından türetilen tek bir sınıftır. Kurucusundaki üç parametreyi olduğu gibi temel
sınıfa aktarır. Edge Gateway, fonksiyon dağıtıldığında sınıftan bir kez örnek oluşturur. Fonksiyon kaldırıldığında veya
yeniden dağıtıldığında bu örneği durdurur.

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

namespace Proxus.Functions {

// Koli paketleme makinelerinin her okumasına hesaplanan bir veri noktası ekler ve motor akımı yüksek olduğunda uyarır.
public sealed class PackerPowerFunction(object sys, object log, object config) : FunctionBase(sys, log, config) {

    private const double CurrentLimit = 45.0;          // A
    private const string ChannelName = "Shift supervisors";
    private DateTime _lastWarning = DateTime.MinValue;

    protected override void OnStarted() {
        // Bu Edge Gateway'deki tüm cihazların okumalarını alır. Daraltmak için "*" yerine cihaz ID'leri yazın.
        Subscriptions?.Add(new SubscriptionContext {
            Type = typeof(TransportData),
            Topics = ["*"]
        });

        // Her dakika fonksiyon günlüğüne bir yaşam sinyali yazar.
        ExecuteScheduledTask(TimeSpan.FromMinutes(1), () => LogInformation("PackerPowerFunction is running"));

        base.OnStarted();
    }

    protected override void OnMessageReceive(FunctionContext ctx) {
        if (ctx.Message is not TransportData data) return;

        var deviceName = data.GetMetaDataValueByKey("DeviceName") ?? ctx.Topic;
        if (!deviceName.StartsWith("LYS_PACKER_", StringComparison.Ordinal)) return;

        var current = data.GetPayloadValueByName<double>("MotorCurrent");
        if (current <= 0) return;

        // Aynı okumaya yeni bir veri noktası eklenir. Bu değer, makineden okunan bir değer gibi saklanır ve yayınlanır.
        var powerKw = Math.Round(400 * current * Math.Sqrt(3) * 0.85 / 1000, 2);
        data.SetPayloadValue("MotorPower", powerKw, "kW");

        if (current > CurrentLimit && DateTime.UtcNow - _lastWarning > TimeSpan.FromMinutes(10)) {
            _lastWarning = DateTime.UtcNow;
            LogWarning($"{deviceName}: motor current {current} A is above {CurrentLimit} A");
            SendNotification(ChannelName, $"{deviceName}: motor current {current} A is above {CurrentLimit} A");
        }
    }
}
}
```

Edge Gateway'in fonksiyonları çalıştırma biçimi nedeniyle şu kurallara uyun:

- Aboneliklerinizi `OnStarted` içinde ekleyin ve ardından `base.OnStarted()` yöntemini çağırın. Abonelikleri temel
  yöntem bağlar. Bu çağrıdan sonra eklenen abonelikler veri almaz.
- `OnMessageReceive` her okuma için çağrılır. Bu çağrı okuma saklanmadan, veri hedeflerine gönderilmeden ve işlem
  akışlarından geçmeden önce yapılır. `TransportData` nesnesine eklediğiniz veya nesnede değiştirdiğiniz veri
  içeriği, okumayla birlikte işlenmeye devam eder.
- Fonksiyonun kendi ürettiği okumalar (`BackToPipeline`, `Save`) fonksiyonlara tekrar gönderilmez. Böylece bir
  fonksiyon kendi çıktısıyla sonsuz döngüye girmez.
- `OnMessageReceive` yöntemini kısa tutun. HTTP çağrısı veya veritabanı sorgusu gibi uzun işleri ayrı bir görev
  olarak başlatın ve sonucunu beklemeyin.

## Yaşam döngüsü

| Üye | Ne zaman çalışır | Burada ne yapılır |
| --- | --- | --- |
| `protected virtual void OnStarted()` | Dağıtımdan sonra bir kez. | Abonelikleri ekleyin, zamanlayıcıları başlatın, ardından `base.OnStarted()` yöntemini çağırın. |
| `protected virtual void OnMessageReceive(FunctionContext ctx)` | Aboneliklerden biriyle eşleşen her mesajda. | Fonksiyonun asıl işini yapın. |
| `protected virtual void OnStopping()` | Fonksiyonun dağıtımı kaldırıldığında veya yeni sürümüyle değiştirildiğinde. | Zamanlayıcıları durdurun, istemcileri serbest bırakın, ardından `base.OnStopping()` yöntemini çağırın. |
| `public string? GatewayId`, `GatewayName` | Her zaman erişilebilir. | Fonksiyonun çalıştığı Edge Gateway'i verir. |

`FunctionContext` iki alan içerir: `Topic` (cihaz verisinde cihazın ID'si) ve `Message` (alınan nesne, genellikle
bir `TransportData`).

## Abonelikler

```csharp
Subscriptions?.Add(new SubscriptionContext {
    Type = typeof(TransportData),
    Topics = ["69", "70"],       // cihaz ID'leri veya Edge Gateway'deki her cihaz için "*"
    EdgeGateways = ["2"]         // isteğe bağlı: 2 numaralı Edge Gateway'in PublishToEdgeGateways ile yayınladıklarını da al
});
```

| Alan | Anlamı |
| --- | --- |
| `Type` | Cihaz okumaları için `typeof(TransportData)`. `PublishLocal` veya `Publish` ile yayınlanan mesajlar için `typeof(GenericMessage)`. |
| `Topics` | Okumalar için cihaz ID'leri (cihazın `Oid` değeri) veya `"*"`. Genel mesajlar için mesaj konuları. |
| `EdgeGateways` | `PublishToEdgeGateways` çıktısı bu fonksiyona gelecek diğer Edge Gateway'lerin ID'leri. |

## Okumadaki değerleri alma ve değiştirme

Her okuma bir `TransportData` nesnesidir. İçinde iki liste bulunur: her veri noktası için bir `Payload` ve okumaya
ait bilgileri taşıyan `MetaData`. `DeviceId`, `DeviceName`, `Enterprise`, `Site`, `Area`, `ProcessCell` ve
`EquipmentModule` bu bilgilere örnektir.

| Yöntem (`Proxus.Common` namespace'i) | İşlevi |
| --- | --- |
| `T? GetPayloadValueByName<T>(string payloadName)` | Veri noktasının `T` türüne çevrilmiş değerini verir. Veri noktası yoksa `default` döner. |
| `object? GetPayloadValueByName(string payloadName)` | Veri noktasının değerini verir. |
| `string? GetMetaDataValueByKey(string key)` | Bir meta veri değerini verir, örneğin `"DeviceName"`. |
| `void SetPayloadValue<T>(string payloadName, T? value, string unit = "")` | Veri noktasının değerini ayarlar. Veri noktası yoksa ekler. |
| `void AddPayload(string key, object? value, string? type, string unit = "")` | Yeni bir veri noktası ekler. `type`, `"Double"` gibi bir veri türü adıdır. |
| `void AddMetaData(string key, string value)` | Yeni bir meta veri kaydı ekler. |

`Payload` nesnesi `Key`, `Value` (metin), `NumericValue`, `Type`, `Unit` ve `Time` alanlarını içerir.

## Veriyi gönderme

| Yöntem | İşlevi |
| --- | --- |
| `Task Save(TransportData data, bool usePipeline = true)` | Var olan bir cihazın okumasını saklar. Okumada sayısal bir `DeviceId` meta veri kaydı bulunmalıdır. Bulunmazsa okuma `Save skipped: missing or invalid DeviceId in MetaData.` uyarısıyla atlanır. |
| `Task Save(List<Payload> payloads, string deviceName, bool usePipeline = true)` | Değerleri bir cihaz adıyla saklar. Bu adda bir cihaz yoksa Proxus cihazı bu Edge Gateway'de oluşturur. |
| `void BackToPipeline(TransportData data)` | Okumayı Edge Gateway'in olağan veri yoluna (saklama, veri hedefleri, işlem akışları) geri gönderir. Okumada `DeviceId` meta veri kaydı bulunmalıdır. Bulunmazsa günlüğe `Error in BackToPipeline! DeviceId is null! Check your code.` yazılır. |
| `void PublishLocal(object message)` | Aynı Edge Gateway'deki diğer fonksiyonlara mesaj yayınlar. |
| `Task PublishToEdgeGateways(TransportData data)` | Okumayı, `EdgeGateways` listesinde bu Edge Gateway'i belirten diğer Edge Gateway'lerdeki fonksiyonlara yayınlar. |
| `void PublishMqttMessage(string topic, string payload)` | Bir MQTT sunucusuna mesaj yayınlar. |
| `void SubscribeToMqttTopic(string topic)` | Bir MQTT konusuna abone olur. Gelen mesajları almak için `HandleMqttMessage(MqttApplicationMessage message)` yöntemini geçersiz kılın (override). |

MQTT yardımcıları, geçersiz kılınabilen dört özellikte tanımlı MQTT sunucusuna bağlanır: `MqttBrokerHost`
(varsayılan `"localhost"`), `MqttBrokerPort` (`1883`), `MqttBrokerUser` ve `MqttBrokerPassword` (boş). Başka bir
MQTT sunucusuna bağlanmak için bu özellikleri geçersiz kılın. Edge Gateway'in yerleşik MQTT sunucusunu kullanmak için
`MqttBrokerUser` özelliğine Edge Gateway'in `MasterToken` değerini verin.

## Cihazlar, bildirimler ve veritabanları

| Yöntem | İşlevi |
| --- | --- |
| `void SendNotification(string channelName, string message)` | Mesajı, bu adı taşıyan [bildirim kanalı](https://proxus.io/tr/docs/connect/notification-channels) üzerinden gönderir. |
| `Task UpdateDeviceStatusAsync(int deviceId, string status)` | Cihaz için gösterilen durum metnini değiştirir. |
| `Task SwitchDeviceEnabledAsync(int deviceId, bool isEnabled)` | Cihazı etkinleştirir veya devre dışı bırakır. |
| `SelectStatementResult[]? ExecuteQuery(TargetType dbType, Dictionary<string, string> parameters, string query)` | Bir veritabanında sorgu çalıştırır. Veritabanı, türüyle ve aynı türdeki bir [veri hedefinin](https://proxus.io/tr/docs/reference/outbound-targets) kullandığı parametrelerle belirtilir. `TargetType.SystemDB` ve `null` parametrelerle Proxus veritabanı okunur. |
| `int? ExecuteNonQuery(TargetType dbType, Dictionary<string, string> parameters, string nonQuery)` | Bir SQL ifadesi çalıştırır ve etkilenen satır sayısını döndürür. |
| `TransportData ExecuteQueryAndTransformResult(TargetType dbType, Dictionary<string, string> parameters, string query)` | Sorgu çalıştırır ve sonuç satırlarını veri içeriği (payload) olarak döndürür. |

## Zamanlayıcılar

| Yöntem | İşlevi |
| --- | --- |
| `CancellationTokenSource? ExecuteScheduledTask(TimeSpan period, Action action)` | `action` işini her `period` süresinde bir çalıştırır. Durdurmak için dönen nesneyi iptal edin. |
| `CancellationTokenSource? ExecuteScheduledTask(TimeSpan period, Action action, bool align, bool strict = false)` | `align: true` verilirse iş `period` süresinin tam katlarında çalışır (her tam dakikada, her tam saatte gibi). |
| `CancellationTokenSource? SendRepeatedly(TimeSpan period, object message)` | `message` nesnesini her `period` süresinde fonksiyonun kendisine gönderir. |
| `TimeSpan? EnableCacheWithExpirationTime` | Gelen okumaları bu süre boyunca `Cache` içinde tutar. `base.OnStarted()` çağrısından önce ayarlayın. |

Bu yöntemlerle başlatılan zamanlayıcılar, fonksiyon durduğunda otomatik olarak durdurulur.

## Günlükler ve hatalar

`LogTrace`, `LogDebug`, `LogInformation`, `LogWarning`, `LogError` ve `LogCritical` yöntemlerinin tümü
`(object message, bool asJson = false)` parametrelerini alır. Yazılan satırlar, fonksiyonun **Günlükler** bölümünde ve
[Sistem günlükleri](https://proxus.io/tr/docs/administer/system-logs) ekranında, başında sınıf adıyla görünür.

Edge Gateway'in yapılandırmasında `[UserFunctions]` bölümündeki ayarlar, hatalı çalışan bir fonksiyonun etkisini sınırlar:

| Anahtar | Varsayılan | Anlamı |
| --- | --- | --- |
| `ExceptionThresholdPerFunction` | `10` | Yakalanmamış istisna sayısı bu değere ulaşınca Edge Gateway fonksiyonun dağıtımını kaldırır ve fonksiyonu dağıtılmamış olarak işaretler. `0` sınır olmadığını gösterir. |
| `LogLevelPolicy` | `["Error:2 1m", "Critical:2 1m"]` | Her seviye için belirli bir süre içinde yazılabilecek en fazla günlük satırı sayısı, örneğin `"Warning:100 1h"`. Sınıra ulaşıldığında bunu bildiren tek bir uyarı yazılır. Süre dolana kadar o seviyedeki diğer satırlar yazılmaz. |
| `UseAutoTryCatchInjector` | `true` | Derleyici yöntemlerinizi hata yakalama koduyla sarar. Böylece bir istisna Edge Gateway'i durdurmaz, günlüğe yazılır ve sayılır. |

Çağrı başına süre veya bellek sınırı yoktur. Takılan ya da döngüye giren bir fonksiyon, abone olduğu cihazların verisinin
Edge Gateway'de işlenmesini bekletir. Bu nedenle `OnMessageReceive` içindeki işi küçük tutun.

## Derleyicinin sundukları

Kodunuzda .NET temel kütüphanesini (koleksiyonlar, LINQ, görevler, `HttpClient`, soketler, JSON, XML), yukarıda
anlatılan Proxus türlerini, MQTT istemci türlerini ve ML.NET'i kullanabilirsiniz. Paket yöneticisi yoktur. Bu
kümenin dışındaki kütüphanelere başvuru eklenemez.

Güvenlik nedeniyle aşağıdaki namespace'leri kullanan kod derlenmez ve
`Blacklisted namespace used: <name>. Reason: ...` mesajıyla reddedilir:

| Namespace | Belirtilen neden |
| --- | --- |
| `System.IO` | Doğrudan dosya erişimine izin verilmez. |
| `System.Diagnostics` | Süreçlere ve sistem tanılama araçlarına erişime izin verilmez. |
| `System.Reflection` | Reflection kullanılamaz. |
| `System.Data`, `System.Data.Sql` | Doğrudan veritabanı erişimine izin verilmez. Bunun yerine `ExecuteQuery` ve `ExecuteNonQuery` kullanın. |
| `System.Text.RegularExpressions` | Düzenli ifadeler kullanılamaz. |
| `System.Web`, `System.Drawing`, `System.Management`, `Microsoft.Win32`, `Microsoft.AspNetCore`, `Microsoft.VisualStudio` | Fonksiyonlarda kullanılamaz. |

Derleme hataları `<code> (line <n>, column <n>): <message>` biçiminde listelenir.

## Şablonlar

Fonksiyon ekranındaki **Şablonlar** ile yeni bir fonksiyonu aşağıdaki şablonlardan biriyle başlatabilirsiniz:

| Şablon | İşlevi |
| --- | --- |
| Türetilmiş ölçümler | Gelen okumalara hesaplanmış değerler ekler: gerilim ve akımdan güç, sayaç farkı ve artış hızı gibi. |
| Cihaz sağlığı izleyici | Cihazları son okuma zamanına göre sağlıklı veya güncel değil olarak işaretler. |
| Geçmiş JSON aktarımı | Zaman damgalı JSON değerlerini geçmiş veri deposuna aktarır. |
| HTTP JSON sorgulama | Bir REST uç noktasını belirli aralıklarla sorgular ve seçilen JSON alanlarını veri noktası olarak kaydeder. |
| MQTT JSON alımı | Bir MQTT konusuna abone olur ve JSON özelliklerini adı verilen cihazın veri noktaları olarak kaydeder. |
| OEE izleyici | Üretim sayaçlarından kullanılabilirlik, performans, kalite ve OEE değerlerini hesaplar. |
| Veri içeriği düzenleyici | Değerler saklanmadan önce veri noktalarını yeniden adlandırır, birimleri dönüştürür ve değerleri ölçekler. |
| SQL sorgulama ile alım | Bir veritabanı tablosunu veya görünümünü belirli aralıklarla okur ve sonucu veri noktası olarak kaydeder. |
| Eşik alarmı | Bekleme süresi ve histerezis uygulayarak alarm üretir ve bildirim gönderir. |

## Sonraki adımlar

- [Fonksiyonlar](https://proxus.io/tr/docs/automate/functions): merkez sunucuda fonksiyon oluşturun, derleyin, dağıtın ve günlüklerini inceleyin.
- [İşlem akışları ve alarm kuralları](https://proxus.io/tr/docs/concepts/pipelines-and-rules): hangi durumda fonksiyon, hangi durumda işlem akışı veya alarm kuralı kullanılacağını öğrenin.
- [Otomasyon sorunlarını giderme](https://proxus.io/tr/docs/troubleshoot/automation): derleme ve dağıtım hatalarını çözün.
