IronFlock SDK
IronFlock SDK, edge uygulamalarınızın IronFlock platformuyla etkileşim kurmasını sağlar. Aynı işlevselliğe sahip üç dilde sunulur — Python, JavaScript ve Go — ve aşağıda yan yana belgelenmiştir. Kayıtlı bir cihazda çalışırken kimlik doğrulamayı otomatik olarak yönetir; veri yayımlama, geçmiş sorgulama, cihazlar arası uzak prosedür çağırma ve cihaz meta verisini güncelleme işlevleri sunar.
| SDK | Paket | Gereklilik |
|---|---|---|
| Python | ironflock on PyPI | Python 3.11+ |
| JavaScript | ironflock on npm | Node.js 22+ veya modern tarayıcı |
| Go | github.com/ironflock/sdk-go | Go 1.27.2+ |
Bu sayfa, üç SDK’nın kılavuzudur. Her SDK’nın eksiksiz referansı, GitHub’daki README dosyasıdır: IronFlock/sdk-py, IronFlock/sdk-js ve IronFlock/sdk-go.
Kurulum
Python
pip install ironflockYa da uygulamanızın requirements.txt dosyasına ironflock ekleyin.
Hızlı Başlangıç
Python
import asyncio
from datetime import datetime, timezone
from ironflock import IronFlock
async def main():
while True:
await ironflock.publish_to_table("sensordata", {
"tsp": datetime.now(timezone.utc).isoformat(),
"temperature": 22.5,
"humidity": 60
})
await asyncio.sleep(5)
ironflock = IronFlock(mainFunc=main)
ironflock.run()Bir IronFlock uygulama container’ı içinde kullanıldığında SDK, bağlantı kimlik bilgilerini ortamdan otomatik olarak okur — manuel yapılandırma gerekmez. Bir tabloya yazdığınız her satır, zaman damgası olan bir tsp taşır (bkz. publishToTable).
Yapıcı Seçenekleri
Python
ironflock = IronFlock(
mainFunc=main, # async function to run after connecting
serial_number="abc123", # override device serial (optional)
reconnect_window=60, # seconds an operation rides out a platform restart (optional)
)| Parametre | Açıklama |
|---|---|
mainFunc | Bağlantı kurulduktan sonra çalışan bir async fonksiyon (async def). Fonksiyon döndüğünde veya bir istisna yükselttiğinde run() ve run_async() bağlantıyı durdurur (bkz. Bağlantı Özellikleri & Yaşam Döngüsü) |
ironFlockUrl | Router’ın WebSocket URL’si, ör. wss://cbw.ironflock.com/ws-ua-usr. Verilmezse SDK, URL’yi ortamdan alır (bkz. Ortam Değişkenleri) |
serial_number | Yayımlarla ve tablo yazmalarıyla gönderilen cihaz seri numarasını geçersiz kılar. Varsayılan: DEVICE_SERIAL_NUMBER ortam değişkeni. Cihaz ajanı uygulama başına bir kimlik bilgisi enjekte ettiği sürece, bağlantının kimlik doğrularken kullandığı kimliği değiştirmez |
reconnect_window | Tablo işlemlerinin, publish, subscribe ve register_device_function’ın bir yeniden başlatmanın ardından hata yükseltmeden önce platformun geri gelmesini bekledikleri saniye sayısı. Varsayılan 60; 0 beklemeyi kapatır (bkz. Bağlantı Güvenilirliği). IronFlock(), negatif bir değer, NaN veya sonsuz bir değer için ValueError, sayı olmayan bir değer için ise TypeError yükseltir |
Veri Yayımlama
publishToTable / publish_to_table
Bir filo tablosuna veri kaydı yazar. Tablo adı, uygulamanızın data-template.yml dosyasında tanımlanan bir tabloyla eşleşmelidir. SDK, veriyi doğru proje veritabanına otomatik olarak yönlendirir.
appendToTable / append_to_table ile aynıdır: tablonun ekleme prosedürünü çağırır; bu yüzden bir platform yeniden başlatmasını atlatır — bağlantı için yeniden bağlanma penceresi boyunca bekler ve veri arka ucu tabloyu henüz sunmadığı sürece yeniden dener — ve veri arka ucu satırı reddettiğinde hata yükseltir (reddeder, bir hata döndürür). Platform yeniden başlarken hiçbir satır kaybolmaz.
Üç SDK’nın 1.9.1 ve önceki sürümleri satırı bunun yerine tablonun yazma konusuna yayımlıyordu: veri arka ucu yeniden başlarken yayımlanan bir satır kayboluyor, reddedilen bir satır ise hata vermeden düşürülüyordu. Artık böyle bir satır çağrıyı başarısız kılar — örneğin
tspiçermeyen bir satır ya da veri şablonunda bulunmayan bir tablo (yeniden bağlanma penceresinden sonra).
Python
from datetime import datetime, timezone
await ironflock.publish_to_table("sensordata", {
"tsp": datetime.now(timezone.utc).isoformat(),
"temperature": 22.5,
"humidity": 60,
"device_id": "sensor-001"
})Veri arka ucu satırı ilk argümandan okur: Python’da bir dict, JavaScript’te dizinin ilk öğesi, Go’da ilk konumsal argüman (bir map veya bir struct). Ek anahtar kelime argümanları — Go’da argümanlar arasındaki bir ironflock.Kwargs değeri — yalnızca veri şablonunun onlara eşlediği sütunlar için okunur (kwargs.<name>).
Her satırın bir tsp’ye ihtiyacı vardır. Her tablonun zorunlu bir tsp sütunu vardır: satırın zaman damgası — ör. bir ISO 8601 dizesi ya da Go’da UTC’de RFC 3339 olarak gönderilen bir time.Time —; veri şablonu onu başka bir yere eşlemedikçe veri arka ucu bunu satırdan okur. tsp içermeyen bir satırı, appendToTable / append_to_table altında anlatıldığı gibi reddeder.
Varlık tablolarında kısmi satırlar. Varlık anahtarı olan bir tabloda (maintainLatestFlagFor) bir satır yalnızca varlık anahtarını, tsp’yi ve değişen sütunları taşıyabilir. Satırın dışarıda bıraktığı bir sütun varlığın önceki değerini korur; None / null / nil olarak gönderilen bir sütun temizlenir. Bir alanın dışarıda bırakılmış sayılıp sayılmayacağı satırı nasıl oluşturduğunuza bağlıdır:
- Python: değeri
Noneolan bir anahtar null olarak gönderilir ve sütunu temizler. Eski değeri korumak için anahtarı dict’ten kaldırın. - JavaScript: değeri
undefinedolan bir özellik veri yükünden düşer, bu yüzden eski değeri korur. Sütunu temizlemek için onunullyapın. - Go:
omitemptyveyaomitzeroetiketli bir struct alanı boş olduğunda dışarıda bırakılır, bu yüzden eski değeri korur. Aksi haldenilbir işaretçi alanı, birironflock.Rowiçindekinildeğer gibi, null olarak gönderilir ve sütunu temizler.
Aynısı dört yazma yönteminin tümü için geçerlidir. Varlık anahtarı olmayan bir tabloda, satırın dışarıda bıraktığı bir sütun NULL olarak saklanır.
appendToTable / append_to_table
Uzak prosedür çağrısı kullanarak filo tablosuna veri ekler: reddedilen bir satır bir hatadır. publishToTable / publish_to_table de aynısını yapar; tek fark, hata mesajının işleme verdiği addır.
Python
result = await ironflock.append_to_table("sensordata", {
"tsp": datetime.now(timezone.utc).isoformat(),
"temperature": 22.5,
"humidity": 60
})Satırın, publishToTable’da olduğu gibi bir tsp’ye ihtiyacı vardır. Veri arka ucu tsp içermeyen bir satırı wamp.error.runtime_error ile ve bir neden belirtmeden reddeder; SDK’nın hatası bu durumda şunu ekler: “the data backend refused the row without a reason — a missing tsp is the usual cause; see the error-logs table”. Bir appliance’ta dolan disk gibi türü belirli retler Veri Arka Ucu Retleri altında listelenir.
publishRowsToTable / publish_rows_to_table
Bir filo tablosuna tek bir mesajda birden çok satır (toplu ekleme) yazar. Platform, tüm yığını tek bir işlemde atomik olarak (ya hep ya hiç) ekler. Satır başına bir gidiş-dönüşün çok maliyetli olacağı yüksek frekanslı veriler için bunu kullanın. appendRowsToTable / append_rows_to_table ile aynıdır: toplu ekleme prosedürünü çağırır, publishToTable gibi bir platform yeniden başlatmasını atlatır ve veri arka ucu yığını reddettiğinde başarısız olur.
Python
await ironflock.publish_rows_to_table("sensordata", [
{"tsp": "2024-01-15T10:30:00.000Z", "temperature": 22.5},
{"tsp": "2024-01-15T10:30:01.000Z", "temperature": 22.7},
])İkinci argüman, her biri kendi tsp’sine sahip, eklenecek satır nesnelerinden oluşan boş olmayan bir listedir — Go’da bir satır slice’ı ([]ironflock.Row, []map[string]any veya bir struct slice’ı). Veri arka ucunun reddettiği bir yığın — örneğin bir satırın tsp’si eksik olduğu için — çağrıyı başarısız kılar ve yığının hiçbir satırı saklanmaz. (SDK’ların 1.9.1 ve önceki sürümleri yığını tablonun toplu (bulk) konusuna yayımlıyordu; reddedilen bir yığın orada hata vermeden düşürülüyordu.)
appendRowsToTable / append_rows_to_table
Bir filo tablosuna tek bir uzak prosedür çağrısında birden çok satır (toplu ekleme) ekler. Platform, tüm yığını atomik olarak (ya hep ya hiç) ekler: herhangi bir satır geçersizse tüm yığın reddedilir ve hiçbir şey kalıcı hale getirilmez. publishRowsToTable / publish_rows_to_table de aynısını yapar. Her satırın kendi tsp’sine ihtiyacı vardır; retler, nedensiz reddedilen bir yığın için verilen ipucu dahil, appendToTable ile aynıdır.
Python
result = await ironflock.append_rows_to_table("sensordata", [
{"tsp": "2024-01-15T10:30:00.000Z", "temperature": 22.5},
{"tsp": "2024-01-15T10:30:01.000Z", "temperature": 22.7},
])
# result -> {"success": True, "count": 2}reportError / report_error
Bir uygulama hatasını filonuzun error-logs tablosuna raporlar. Bu, publishToTable / appendToTable üzerine kullanışlı bir sarmalayıcıdır: satırı source: "app", bir önem level değeri ve bir zaman damgasıyla işaretler, ardından onu herhangi bir normal tablo satırı gibi yazar. Hata, fleetdb sistem hatalarının kullandığı (source: "system" olarak etiketlenmiş) aynı error-logs tablosuna düşer; bu nedenle getHistory ile sorgulanabilir, subscribeToTable / subscribe_to_table ile akışa alınabilir, board-template’lerde kullanılabilir ve transformed.error-logs üzerinde gerçek zamanlı olarak teslim edilir — platformun sistem hatası bildirimini tetiklemeden.
Python
# Writes a row to the error-logs table
await ironflock.report_error("Sensor read timed out", level="warning")
# Pass an exception to capture its traceback (falls back to the message)
try:
risky_operation()
except Exception as err:
await ironflock.report_error(err)
# Show the operator a friendlier line than the raw error
await ironflock.report_error(
"Calibration failed: offset 0.42 out of range",
user_message="Please recalibrate the sensor",
)Satır { tsp, msg, user_message, source: "app", level } biçimindedir: msg teknik metni (bir traceback ya da stack) tutar; user_message ise board’un hata bildiriminin (toast) gösterdiği metni: ilettiğiniz kullanıcı mesajını ya da, verilmediyse, mesajın kendisini.
Parametreler:
| Parametre | Tür | Açıklama |
|---|---|---|
error | str / string veya istisna / Error | Hata mesajı veya traceback/stack (ya da mesajı) kaydedilen bir istisna |
level | str / string, isteğe bağlı | Önem düzeyi: "error", "warning", "info" veya "debug"; panonun bildirim (toast) widget’ının gösterdiği türler bunlardır. Varsayılan "error". SDK 1.9.1 ve öncesinin yazımı olan "warn", "warning" olarak gönderilir |
append | bool / boolean, isteğe bağlı | Uyumluluk için korunur: publishToTable ve appendToTable ikisi de ekleme yaptığından satır her durumda eklenir. Yalnızca hata mesajının ikisinden hangisini adlandıracağını seçer |
tsp | str / string, isteğe bağlı | ISO-8601 zaman damgası geçersiz kılması. Varsayılan olarak geçerli zaman |
user_message / userMessage | str / string, isteğe bağlı | Operatöre yönelik metin, user_message olarak saklanır. Varsayılan olarak kaydedilen mesaj |
Python’da seçenekler anahtar kelime argümanlarıdır (
report_error(error, level=..., append=..., tsp=..., user_message=...)); JavaScript’te bir seçenekler nesnesi aracılığıyla geçilir (reportError(error, { level, append, tsp, userMessage })); Go’da ise bir struct aracılığıyla (ReportError(ctx, errOrMsg, ironflock.ReportErrorOptions{Level, Append, Tsp, UserMessage})). BuradaerrOrMsgbirerrorveya bir dizedir,Levelironflock.LevelError,LevelWarning,LevelInfo,LevelDebugdeğerlerinden biridir ve döndürülen*Resultekleme sonucudur.
publish
Kendinize ait bir WAMP konusuna mesaj yayımlar. Veritabanı tablosuna eşlenmeyen özel mesajlaşma veya olaylar için bunu kullanın. Yönlendirici bazı adları platform için ayırır — bkz. Bir Uygulamanın Kullanabileceği URI’ler.
Tablo yazmaları gibi publish de cihaz meta verisini anahtar kelime argümanlarına ekler: DEVICE_SERIAL_NUMBER, DEVICE_KEY ve DEVICE_NAME. Bu adlardan birini taşıyan kendi anahtar kelime argümanınız önceliklidir.
Bağlantı yokken publish, bir bağlantı için yeniden bağlanma penceresi boyunca (varsayılan olarak 60 saniye, bkz. Bağlantı Güvenilirliği) bekler; böylece bir yayımlama kısa bir kesintiyi atlatır. Ardından bir kez gönderilir, asla yeniden denenmez. Yönlendirici, kimsenin abone olmadığı bir mesajı da onaylar; bu yüzden bu arada kapalı olan bir abone onu kaçırır.
Python
await ironflock.publish("com.myapp.alerts", {
"level": "warning",
"message": "Temperature threshold exceeded"
})Geçmiş Veri Sorgulama
getHistory
Bir filo tablosundan veya transform’dan geçmiş verileri getirir: eşleşen en yeni satırları, artan tsp sırasıyla. Filtreleme, zaman aralığı ve sayfalama desteklenir.
Python
# Simple query
data = await ironflock.getHistory("sensordata", {"limit": 100})
# Query with time range and filters
data = await ironflock.getHistory("sensordata", {
"limit": 500,
"offset": 0,
"timeRange": ["2026-01-01T00:00:00Z", "2026-03-01T00:00:00Z"],
"filterAnd": [
{"column": "temperature", "operator": ">", "value": 20},
{"column": "humidity", "operator": "<=", "value": 80}
]
})
# Current value(s) only: the "latest" marker returns the newest row per entity
current = await ironflock.getHistory("sensordata", {
"limit": 100,
"filterAnd": [{"latest": True}]
})
# One field of a large json column: the column comes back pruned to its paths
readings = await ironflock.getHistory("sensordata", {
"limit": 100,
"columns": ["json_data"],
"columnPaths": ["json_data.sensor.temp"],
})Sorgu parametreleri:
| Alan | Tür | Açıklama |
|---|---|---|
limit | int / number | Döndürülecek maksimum satır sayısı (1–10.000, zorunlu; bir transform için 1–3.000) |
offset | int / number | Sayfalama için kaydırma |
timeRange | list / array | [start, end] — ISO tarih-saat dizeleri veya epoch milisaniye sayıları, asla karışık değil; açık bir uç için None / null. Eski bir {"start": ..., "end": ...} nesnesi bu ikiliye dönüştürülür |
filterAnd | list / array | AND ile birleştirilen filtre girdileri: koşullar, gruplar ve/veya latest işareti (aşağıya bakın) |
columns | list / array | Döndürülecek sütunlar (isteğe bağlı). tsp, device_key ve authid her zaman dahil edilir; tüm sütunlar için bu alanı atlayın |
columnPaths | list / array | json sütunlarını budar (isteğe bağlı): filtrelerin kullandığı gösterimde JSON yolları ("json_data.a.b", "json_data['key']"). columns içinde listelenen bir json sütunu yalnızca yollarının birleşimini içerecek şekilde döner; burada yolu olmayan bir sütun bütün olarak döner. Transform’lar ve fleetdb v1.0.52’den eski veri arka uçları tarafından yok sayılır |
Sorgu parametreleri geçtiğinizde limit zorunludur; hiç parametre geçmezseniz (Go’da nil), getHistory en yeni 10 satırı okur.
Go’da bunlar ironflock.TableQueryParams türünün Limit, Offset, TimeRange, FilterAnd, Columns ve ColumnPaths alanlarıdır. TimeRange bir *ironflock.TimeRange{Start, End} değeridir: her sınır bir time.Time, bir ISO 8601 dizesi, bir epoch milisaniye sayısı ya da açık bir uç için nil olabilir (ironflock.Between, Since ve Until, time.Time değerlerinden bir aralık oluşturur). FilterAnd bir []ironflock.Filter değeridir. Eski nesne biçimi yoktur.
Filtre operatörleri: =, !=, <>, >, <, >=, <=, LIKE, ILIKE, NOT LIKE, NOT ILIKE, IN, NOT IN, IS NULL, IS NOT NULL. JavaScript bu listeyi SQL_OPERATORS olarak dışa aktarır; Go’da ironflock.SQLOperators() onu döndürür.
Her filtre koşulu column, operator ve value anahtarlarına sahip bir nesnedir — Go’da ironflock.Where(column, operator, value), ironflock.IsNull(column) ve ironflock.IsNotNull(column) ile oluşturulur. Operatörler büyük/küçük harf fark etmeksizin, çevresindeki boşluklar yok sayılarak eşleştirilir ve yukarıda listelenen biçimde — veri arka ucunun kabul ettiği tek biçimde — gönderilir; böylece "like", gruplar içinde de "LIKE" olarak gider. Listenin dışındaki bir operatör, bir uyarıyla birlikte verildiği gibi gönderilir. IS NULL ve IS NOT NULL bir value almaz ve NULL’ı sorgulamanın tek yoludur: diğer her operatör, sütunu NULL olan bir satırı tam SQL’deki gibi dışarıda bırakır. IN ve NOT IN bir liste alır (Go’da bir slice). LIKE büyük/küçük harfe duyarlıdır; duyarsız eşleştirme için ILIKE kullanın.
OR ile birleştirme. filterAnd girdileri AND ile birleştirilir. Bir grup girdisi kendi girdilerini — koşulları, latest işaretini veya başka grupları — tek bir operatörle, "AND" ya da "OR" ile birleştirir:
Python
# Rows that are not soft-deleted: deleted IS NULL OR deleted = false
"filterAnd": [
{
"combinator": "OR",
"filters": [
{"column": "deleted", "operator": "IS NULL"},
{"column": "deleted", "operator": "=", "value": False},
],
}
]Gruplardan önceki bir veri arka ucu (v1.2.0 öncesi fleetdb) bu yapının bir kısmını uygulamak yerine onu reddeder; böylece filtrenin dışarıda bıraktığı satırları sessizce döndürmek yerine açıkça başarısız olur.
Güncel değerleri okuma. filterAnd içindeki bir {"latest": true} girdisi (Go’da ironflock.Latest()) bir filtre koşulu değil, bir mod anahtarıdır: veri arka ucu yalnızca varlık başına en yeni satırı döndürür; bu satır, tablonun maintainLatestFlagFor ile bildirdiği varlık anahtarından SQL’de türetilir. Varlık anahtarı olmayan bir tablo, yalnızca en son tek satırını döndürür.
Diğer koşullar bu işaretle beklediğiniz gibi birleşir: varlık anahtarı sütunları üzerindeki koşullar hangi varlıkların döndürüleceğini daraltır; diğer tüm koşullar ve timeRange ise elde edilen en son satırlara uygulanır. Yani {"latest": true} ile bir deleted filtresini birleştirmek, silinen varlıkların önceki satırını yeniden ortaya çıkarmak yerine bu varlıkları gizler.
IronFlock’un önceki sürümleri fiziksel bir latest_flag sütunu saklıyordu. Bu sütun artık mevcut değil — eski bir latest_flag = true filtresi hâlâ kabul edilir ve bu işaret gibi ele alınır, ancak yeni kodlar {"latest": true} kullanmalıdır. latest işareti getSeriesHistory içinde kullanılamaz.
Zaman aralıkları. Veri arka ucu, dize olarak verilen bir sınırı JavaScript’in Date nesnesiyle okur; bu yüzden bir ISO 8601 tarihi veya tarih-saati yazın (2026-07-01, 2026-07-01T12:30:15.250Z) ve tarih ile saati T ile ayırın. SDK’lar, Date’in yanlış okuduğu 0–99 yıllarında tarih ile saat arasındaki boşluğu reddeder: "0001-01-01 00:00:00" — Python’da str(datetime.min) ifadesinin verdiği değer — 2001 olarak okunurdu. Python SDK’sı, datetime.fromisoformat ile okunamayan bir dizeyi de reddeder. Saat dilimi olmayan bir saat UTC olarak okunur. Sayısal bir sınır epoch milisaniyesidir ve sonlu olmalıdır: NaN ve ±Infinity veri arka ucuna açık bir sınır olarak ulaşır ve zaman filtresini sessizce düşürürdü; bu yüzden SDK’lar bunları göndermeden önce reddeder. Go’da bir time.Time sınırı UTC’de RFC 3339 olarak gönderilir; bir dize sınırı ise Date’in doğru okuduğu ISO 8601 biçimlerinden biri olmalıdır — bir tarih ya da isteğe bağlı bir Z veya UTC farkı (+02:00 veya +0200) içeren bir tarih ve günün saati; temel biçim 20260701T123000Z, yalnızca saatten oluşan bir fark veya ayda bulunmayan bir gün (2026-02-30) gibi diğer dizeler, hiçbir şey gönderilmeden önce ironflock.ErrInvalidArgument ile başarısız olur.
Transform’lar. Bir transform (bir veri şablonunun SQL görünümü, bkz. Dönüşüm Tabloları) bir tablodan farklı okunur. Veri arka ucu yalnızca limit — en fazla 3000; daha büyüğü URI_INVALID_LIMIT ile reddedilir —, offset ve filterAnd uygular. timeRange, columns, columnPaths ve latest işaretini yok sayar; transform’un tanımlamadığı bir sütun üzerindeki filtreyi düşürür (bir transform’un örtük tsp, device_key veya authid sütunu yoktur), bu da sonucu genişletir. Satırları, görünümün kendi sırasının tersiyle gelir: en yeni satırları en eskiden başlayarak yalnızca görünüm en yeniden başlayarak sıraladığında alırsınız. Bir transform’un zaman aralığını SQL’inde sınırlayın.
Büyük sonuçlar. Veri arka ucunun tek bir mesajına (8 MiB JSON) sığmayacak kadar büyük bir sonuç parçalar halinde gelir ve SDK bunları bellekte yeniden birleştirir: tek bir okumanın satırlarını aynı sırayla alırsınız. Tutmayan parçalar — biri eksik veya yinelenmiş ya da satır sayısı veri arka ucunun özetinden farklı — satırların bir kısmını döndürmek yerine okumayı başarısız kılar. 8 MiB’ı aşan tek bir satır veya veri arka ucunun 1 GiB korumasını aşan bir sonuç URI_RESULT_TOO_LARGE ile reddedilir: limit değerini düşürün, timeRange aralığını daraltın, daha az columns seçin veya columnPaths ile budayın.
getSeriesHistory / get_series_history
Bir filo tablosundan alt örneklenmiş zaman serisi verileri alır: zaman kovası başına toplanan sütunlar (ör. saatlik ortalamalar). Uzun zaman aralıklarını kapsayan grafikler için idealdir. Tablolar için (transform’lar için değil), fleetdb v1.0.58 ve sonrasındaki veri arka uçlarında kullanılabilir.
Python
from datetime import datetime, timedelta, timezone
from ironflock import SeriesMetric
now = datetime.now(timezone.utc)
avg = SeriesMetric(ref="temperature", method="AVG") # AVG, SUM, COUNT, MIN, MAX, FIRST, LAST
series = await ironflock.get_series_history("sensordata", {
"metrics": [avg, {"ref": "humidity", "method": "MAX"}],
"bucketMs": 3_600_000, # one-hour buckets; omit to divide the range into `limit` buckets
"limit": 24, # the bucket budget, 1–10000
"timeRange": [(now - timedelta(hours=24)).isoformat(), now.isoformat()], # required, with a start
"groupBy": ["device_key"],
})
for row in series:
print(row["tsp"], row["device_key"], row[avg.column]) # avg.column is "AVG:temperature"Sorgu parametreleri:
| Alan | Tür | Açıklama |
|---|---|---|
metrics | list / array | Kova başına hesaplanan toplamlar, en az bir tane: {"ref": ..., "method": ...}. ref bir sütun veya bir json sütununun içine giden bir JSON yoludur ("json_data.temp"); method ise "AVG", "SUM", "COUNT", "MIN", "MAX", "FIRST" veya "LAST" olur. Her metrik "<METHOD>:<ref>" adı altında döner ve bu ad 63 bayta sığmalıdır |
bucketMs | int / number | Tam milisaniye cinsinden kova genişliği, en az 1000 (isteğe bağlı). Zaman aralığını limit kovaya bölmek için atlayın |
limit | int / number | Kova bütçesi (1–10.000) — satır limiti değildir |
timeRange | list / array | [start, end] — ISO tarih-saat dizeleri veya epoch-ms sayıları. Zorunludur ve bir başlangıç içermelidir; bitiş olarak None / null şimdi anlamına gelir. Bitiş başlangıçtan sonra olmalıdır |
groupBy | list / array | Her kovayı grup başına bir satıra bölen sütunlar (isteğe bağlı) |
filterAnd | list / array | getHistory’deki gibi AND ile birleştirilen koşullar ve gruplar (isteğe bağlı). latest işareti burada, bir grup içinde de reddedilir; güncel değerleri okumak için getHistory kullanın |
Satırlar. Her satır bir kovadır — groupBy ile, bir grubun kovası. tsp, kovanın epoch milisaniye cinsinden başlangıcıdır; her groupBy sütunu kendi adıyla, her metrik ise "<METHOD>:<ref>" altında döner (Python’da SeriesMetric.column, JavaScript’te seriesColumn(metric), Go’da SeriesMetric.Column()), ör. row["AVG:temperature"]. Satırlar tsp değerine göre artan sıradadır; satırı olmayan bir kova sonuçta yer almaz (boşluk doldurma yoktur). AVG ve SUM sayısal sütunları alır, MIN ve MAX dizeleri ve zaman damgalarını da; FIRST ve LAST kovanın en erken ve en geç satırının değerini verir; COUNT null olmayan değerleri sayar (tsp için: satırları). Büyük bir sonuç, getHistory’deki gibi parçalardan yeniden birleştirilir.
Kovalar ve açık bir uç. limit satırları değil kovaları sayar. bucketMs olmadan zaman aralığı limit kovaya bölünür. bucketMs verildiğinde, aralık limit değerinden fazla kova içeriyorsa veri arka ucu kovaları bucketMs değerinin bir katına genişletir — ve açık bir uç, veri arka ucunun kendi “şimdi”sine, yani sizinkinden biraz sonrasına kadar uzanır. Açık uçlu son 24 saat 25 adet bir saatlik kova içerir, bu yüzden limit: 24 iki saatlik kovalar alır. Örnekteki gibi bitişi sınırlayın veya bir kova fazlasına izin verin: bucketMs ve açık bir uçla limit + 1 kova dönebilir.
SDK’lar, veri arka ucunun reddedeceği şeyleri göndermeden önce denetler: en az bir metrik, boş olmayan ref’ler, bilinen metotlar, 63 baytı aşmayan sütun adları, bir başlangıç, başlangıçtan sonra gelen bir bitiş ve en az 1000 olan bir tamsayı olarak bucketMs (Go’da tam milisaniye cinsinden, en az 1 saniyelik bir Bucket). Veri arka ucu, yanıtlayamadığı şeyi URI_INVALID_TIME_RANGE, URI_INVALID_METRIC, URI_INVALID_GROUP_BY veya URI_SERIES_TOO_MANY_GROUPS (50.000’den fazla satır) ile reddeder — bkz. Veri Arka Ucu Retleri.
Kullanımdan kaldırılan biçim. 1.9.0’a kadarki SDK sürümleri
metricsalanını sütun adları artı üst düzey birmethodolarak gönderiyordu ({"metrics": ["temperature"], "method": "AVG", ...}); veri arka uçları bu biçimi fleetdb v1.0.58’den beri reddediyor. Bu biçim hâlâ çalışır: SDK sütun başına bir{ref, method}metriği gönderir, uyarır (Python’da birDeprecationWarning, JavaScript’te süreç başına bir kez konsol uyarısı) ve sonuç sütunlarını yeniden sütun adlarıyla adlandırır; böylece eski kodrow["temperature"]okumaya devam eder. Tek istisna, satırda o adda bir sütunun zaten bulunmasıdır; örneğintsp’ninCOUNT’u içintsp.{ref, method}metriklerine geçin verow["AVG:temperature"]okuyun. Go SDK’sında yalnızca güncel biçim vardır: metrik başına bir metot (SeriesMetric).
Veriye Abone Olma
subscribeToTable / subscribe_to_table
Bir filo tablosundaki gerçek zamanlı güncellemelere abone olur. Veri arka ucu yeni bir satır sakladığında işleyici çağrılır; ilk argümanı, saklandığı haliyle satırdır — değerler veri şablonunun sütun türlerine göre tiplendirilmiş, gizli sütunlar maskelenmiş olarak. Toplu ekleme yolu (publishRowsToTable / appendRowsToTable) üzerinden yazılan satırlar işleyicinize birer birer teslim edilir, böylece verinin nasıl yazıldığından bağımsız olarak işleyici kodu aynı kalır.
Python
def on_sensor_data(*args, **kwargs):
row = args[0]
print("New reading:", row["tsp"], row["temperature"])
await ironflock.subscribe_to_table("sensordata", on_sensor_data)subscribeToTable / subscribe_to_table, iki beslemenin de tanıtıcısı olan bir TableSubscription döndürür: onun unsubscribe() metodu iki beslemeyi de kaldırır ve aşağıda açıklanan kaçırılan satırların teslimini sona erdirir (bkz. unsubscribe / unregister).
Bağlantı kopukken saklanan satırlar, yeniden bağlanmanın ardından gelir. Yönlendirici, bağlantı kopukken yayımlananları yeniden göndermez; bu yüzden SDK her yeniden bağlanmadan sonra bu arada saklanan satırları okur ve bunları, canlı gelen satırlardan önce, en eskiden başlayarak işleyicinize teslim eder: tsp değeri işleyicinizin aldığı en yeni satırınkinden daha geç olan her satırı. İşleyiciniz henüz hiç satır almadıysa başlangıç noktası, abone olduğunuz anda saklanmış en yeni satırdır: SDK bu satırı abone olduktan hemen sonra okur; bu okuma başarısız olursa abone olduğunuz andan (cihazın saatine göre) başlar; o sırada boş olan bir tablo en baştan okunur. Bu arada daha eski bir tsp ile yazılan bir satır (örneğin bir cihazın arabelleğe aldığı ölçümleri göndermesi) sonradan teslim edilmez. Kaçırılan satırların tek bir teslimi en fazla en yeni 10.000 satırı kapsar ve bu kadar satır okunduğunda günlüğe bir uyarı kaydedilir. Kaçırılan satırları getiren okuma, her geçmiş okuması gibi en fazla yeniden bağlanma penceresi süresince veri arka ucunu bekler ve canlı satırlar da bu okumayı bekler; okuma başarısız olursa bu arada saklanan satırlar teslim edilmez ve günlüğe bir uyarı kaydedilir. Kaçırılan satırlar olaylardan değil bir okumadan geldiği için işleyici her birini olay ayrıntıları olmadan alır: Python’da yalnızca satırı, JavaScript’te ([row], {}) biçiminde, Go’da ise Topic ve Args içeren ve aboneliğin DeliveryGroup’unda sırayla teslim edilen bir Event olarak. Kaçırılan satırların teslimi SDK’da 1.10.0 sürümünden itibaren bulunur.
Bir transform’un kendine ait satırları yoktur: zamanlamasının her tetiklenişinde veri arka ucu görünümün tamamını — en fazla 3000 satır, görünümün sırasının tersiyle — ilk argümanı satır listesi olan tek bir olay olarak yayımlar. Bir transform’un kaçırılan satırların teslimine ihtiyacı yoktur: yeniden bağlanmanın ardından gelen bir sonraki tetiklenişi, görünümün tamamını yeniden yayımlar. Python’da event_rows(args[0]), JavaScript’te eventRows(args) ve Go’da ev.Rows() her iki biçimi de okur: bir tablonun satırı [row] verir, bir transform’un listesi listenin kendisini (boş bir görünüm için []), başka her şey ise None / undefined / nil verir. Go’da bir transform olayı için ev.Row() nil’dir.
Python
from ironflock import event_rows
def on_hourly(*args, **kwargs):
for row in event_rows(args[0]) or []:
print(row)
await ironflock.subscribe_to_table("hourly_averages", on_hourly)subscribe
Özel gerçek zamanlı mesajlaşma için kendinize ait bir WAMP konusuna abone olur. Tablo satırları için subscribeToTable / subscribe_to_table kullanın: yönlendirici, ham tablo yazma konularına yapılan abonelikleri reddeder (bkz. Bir Uygulamanın Kullanabileceği URI’ler).
Python
def on_alert(*args, **kwargs):
print("Alert received:", args, kwargs)
alerts = await ironflock.subscribe("com.myapp.alerts", on_alert)
# Later: the handler is called for no further event
await alerts.unsubscribe()subscribe, aboneliğin tanıtıcısı olan bir Subscription döndürür (bkz. unsubscribe / unregister). Her çağrı, zaten abone olunmuş bir konu için de kendine ait bir tanıtıcı döndürür.
Bağlantı yokken subscribe, publish gibi bir bağlantı için yeniden bağlanma penceresi boyunca (varsayılan olarak 60 saniye) bekler; subscribeToTable / subscribe_to_table de öyle (bkz. Bağlantı Güvenilirliği).
Konu birebir eşleştirilir. İşleyiciden sonra verilen isteğe bağlı bir argüman olan abonelik seçenekleri bunu değiştirebilir: "prefix" değerinde bir match, verilen konuyla başlayan her konuya abone olur. Seçenekler Python’da autobahn’ın SubscribeOptions sınıfıdır (SubscribeOptions(match="prefix"), autobahn.wamp.types içinden), JavaScript’te bir nesnedir ({ match: "prefix" }), Go’da ise ironflock.SubscribeOptions{Match: "prefix"}. Abonelik her yeniden bağlanmadan sonra aynı seçeneklerle geri yüklenir. subscribeToTable / subscribe_to_table aynı seçenekleri alır ve iki beslemesine de bunlarla abone olur.
unsubscribe / unregister
subscribe, subscribeToTable / subscribe_to_table ve registerDeviceFunction / register_device_function her biri bir tanıtıcı döndürür. Bir tanıtıcı, siz onu kaldırana kadar yeniden bağlanmalar boyunca geçerli kalır: SDK, tanıtıcının temsil ettiği şeyi her yeniden bağlanmadan sonra geri yükler.
| Tanıtıcı | Döndüren | Üyeler |
|---|---|---|
Subscription | subscribe | unsubscribe(), topic, active |
TableSubscription | Kendi uygulamanızın ve tüketilen bir uygulamanın subscribeToTable / subscribe_to_table metodu | unsubscribe(), table, active |
Registration | registerDeviceFunction / register_device_function ve takma adları | unregister(), procedure, active |
topic, table ve procedure, tanıtıcının temsil ettiği şeyi adlandırır; procedure tam WAMP URI’sidir. active, aboneliğin veya kaydın şu anda yönlendiricide etkin olup olmadığını söyler: bağlantı kopukken, başarısız bir geri yüklemeden sonra bir yeniden deneme başarılı olana kadar ve tanıtıcı kaldırıldıktan sonra false olur. Bir TableSubscription, iki beslemesi de etkin olduğu sürece etkindir. ironflock.unsubscribe(handle) bir Subscription veya bir TableSubscription, ironflock.unregister(registration) ise bir Registration alır; her biri, tanıtıcının kendi metoduyla aynı işi yapar.
Python
status = await ironflock.subscribe("com.myapp.status", on_status)
readings = await ironflock.subscribe_to_table("sensordata", on_row)
add = await ironflock.register_device_function("com.myapp.add", add_numbers)
print(readings.table, readings.active) # sensordata True
await status.unsubscribe() # the same as: await ironflock.unsubscribe(status)
await ironflock.unsubscribe(readings) # both feeds of the table, and its catch-up
await ironflock.unregister(add) # the same as: await add.unregister()unsubscribe() ve unregister(), çağrıldıkları anda etki eder ve bir awaitable döndürür: yönlendiricinin yanıt verdiğini bilmek için onu await edin; bir ret orada yükseltilir. async olmayan düz bir fonksiyon olan bir işleyici, subscription.unsubscribe() metodunu await etmeden çağırabilir. Üç tanıtıcı sınıfı ironflock paketinden dışa aktarılır.
Bir kaldırma işlemi üç SDK’da da aynı kurallara uyar:
- Hemen etkili olur. İşleyici artık hiçbir olay için çağrılmaz: ne hâlâ kuyrukta bekleyen bir olay için, ne bir toplu eklemenin kalan satırları için, ne de kaçırılan satırların teslimiyle gelecek satırlar için. Kaldırılan bir fonksiyon artık çağrılmaz; yönlendiricinin kaydı düşürmeden önce hâlâ ona yönlendirdiği bir çağrı
wamp.error.no_such_procedureile reddedilir. Hâlihazırda süren bir çağrı beklenmez; bu yüzden bir işleyici kendi aboneliğini kaldırabilir. Kaldırdığınız şey bir yeniden bağlanmadan sonra asla geri yüklenmez. - Bir bağlantıyı beklemez. Bağlantı kopukken abonelik veya kayıt yerel olarak kaldırılır, çünkü yönlendirici onu oturumla birlikte düşürmüştür; çağrı da hemen döner. Bağlantı açıkken yönlendiriciye bildirilir ve çağrı, yönlendirici yanıt verdiğinde tamamlanır. Bir ret yükseltilir (Python, bir
RuntimeError), fırlatılır (JavaScript, birWampError) ya da döndürülür (Go), ancak kaldırma yine de geçerli kalır. - İdempotenttir. İkinci bir çağrı hiçbir şey yapmaz; tanıtıcı olmadan yapılan bir çağrı da (
None,nullveyaundefined,nil) öyle.stop()/Stopsonrasındaki bir çağrı, aboneliği veya kaydı yalnızca yerel olarak kaldırır: Python ve JavaScript’te bir sonrakistart()onu bu durumda geri yüklemez. - Tanıtıcılar birbirinden bağımsızdır. Bir tanıtıcıyı kaldırmak, aynı konu veya tablo üzerinde olsa bile, başka bir tanıtıcının işleyicisini asla kaldırmaz. Yönlendiricideki abonelik, konusunun son tanıtıcısıyla birlikte sona erer.
- Bir
TableSubscriptioniki beslemesini de kaldırır:transformed.<table>vetransformed.bulk.<table>; ayrıca kaçırılan satırların teslimini sona erdirir.
Yükseltme: 1.10.0 sürümünden önce Python ve JavaScript SDK’ları autobahn’ın abonelik ve kayıt nesnelerini döndürüyordu;
subscribe_to_table/subscribeToTableise yalnızca satır beslemesinin aboneliğini döndürüyordu, bu yüzden onu kaldırmak toplu beslemeyi abone hâlde bırakıyordu. Döndürülen değer üzerindeunsubscribe()veyaunregister()çağıran kod çalışmaya devam eder ve kaldırdığı şey artık bir yeniden bağlanmadan sonra geri yüklenmez.idgibi, autobahn nesnelerinin diğer üyeleri artık yoktur.
Gizli Sütunlar
Uygulamanın veri şablonunda secret: true olarak bildirilen bir sütun şifrelenmiş olarak saklanır. Hiçbir olağan okuma yolu düz metni döndürmez, onu yazan uygulamaya bile:
| Okuma yolu | Bir gizli sütunun görünümü |
|---|---|
getHistory, subscribeToTable / subscribe_to_table ve tüketilen bir uygulamanın okumaları (panoların da kullandığı geçmiş okumaları ve gerçek zamanlı satırlar) | SECRET_PLACEHOLDER olarak dışa aktarılan __secret__ yer tutucusu (Go’da ironflock.SecretPlaceholder) |
| SQL (SQL sorgu konsolu, veri dışa aktarımları ve FleetDB Access Postgres oturumu) ve veritabanı yedekleri | saklanan şifreli metin, ifsec:1:… |
revealSecrets / reveal_secrets | şifresi çözülmüş düz metin |
NULL, her yolda NULL olarak geçirilir; böylece “atanmış ama gizli” ile “hiç yazılmamış” birbirinden ayırt edilebilir kalır.
Bir varlık tablosunda oku-değiştir-yaz güvenlidir: hâlâ __secret__ yer tutucusunu taşıyan bir satırı yeniden yayımlamak — ya da gizli sütunu tamamen atlamak — saklanan sırrın üzerine yazmak yerine onu korur; açıkça gönderilen null ise sırrı temizler. Kesin kurallar için veri arka ucu kılavuzundaki Gizli Sütunlar bölümüne bakın.
Yalnızca string türünde bir sütun gizli olabilir; ne tsp ne de bir varlık anahtarı gizli olabilir. Veri arka ucu her değeri AES-256-GCM ve o değere ait rastgele bir IV ile şifreler; bu yüzden aynı sır her satırda farklı bir şifreli metin olarak saklanır: gizli bir sütun filtrelenemez, gruplanamaz veya sıralanamaz. Böyle bir sütun üzerindeki filtre URI_SECRET_COLUMN ile reddedilir; bir değeri karşılaştırmak için verifySecret kullanın.
Python
from datetime import datetime, timezone
# Written like any other column — the data backend encrypts it on insert
await ironflock.publish_to_table("credentials", {
"tsp": datetime.now(timezone.utc).isoformat(),
"device_id": "d-1",
"api_token": "hunter2",
})
# A normal read redacts it
rows = await ironflock.getHistory("credentials", {"limit": 1})
# [{"tsp": "...", "device_id": "d-1", "api_token": "__secret__"}]Aşağıdaki iki fonksiyon yalnızca uygulamanın kendi kapsayıcılarından çağrılabilir. Mesaj yönlendiricisi bunları her tarayıcı rolüne ve tüketen uygulamalara reddeder; bir ConsumedApp tanıtıcısının bunların karşılığını sunmamasının nedeni de budur: başka bir uygulamanın gizli sütunları size sansürlenmiş olarak ulaşır ve öyle kalır.
revealSecrets / reveal_secrets
Kendi tablolarınızdan birinin satırlarını, gizli sütunlarının şifresi çözülmüş halde okur.
Python
rows = await ironflock.reveal_secrets("credentials", {
"limit": 1,
"filterAnd": [{"column": "device_id", "operator": "=", "value": "d-1"}],
})
# [{"tsp": "...", "device_id": "d-1", "api_token": "hunter2"}]Sorgu parametreleri getHistory ile aynıdır; ancak limit, 100’ü aşamaz. Bu üst sınır, tek bir çağrının koca bir tablonun şifresini çözmesini engellemek için vardır ve veri arka ucu daha büyük bir değeri sessizce kırpmak yerine reddeder. Sorgu parametreleri verilmezse çağrı en yeni 10 satırı okur. getHistory’den farklı olarak satırları en yeniden başlayarak döndürür.
Veri arka ucu, bir cihazdaki uygulama kimlik bilgisi başına dakikada en fazla 30 açığa çıkarma isteğini yanıtlar; bunun ötesinde çağrı URI_RATE_LIMITED ile başarısız olur. Bir sırrı her kullanımda değil, bir kez açığa çıkarın ve saklayın. Bir gizli sütun üzerindeki filtre URI_SECRET_COLUMN ile reddedilir. Veri arka ucu bu fonksiyonu yalnızca gizli sütun tanımlayan tablolar için sunar: diğer her tablo için yönlendirici wamp.error.no_such_procedure yanıtını verir; SDK bunu yeniden bağlanma penceresi boyunca yeniden dener ve ardından çağrı, prosedürün kayıtlı olmadığını belirten bir hatayla başarısız olur. Hata, en olası olanı başta olmak üzere nedenleri sıralar: tablo hiçbir gizli sütun tanımlamıyordur (veri şablonunda secret: true), tablo tanımlanmamıştır ya da veri arka ucu gizli sütunlardan önceki bir sürümdür.
verifySecret / verify_secret
Bir aday değeri, gizli sütunu ifşa etmeden onunla karşılaştırır — açığa çıkarma fonksiyonunun parola denetimi karşılığıdır. Karşılaştırma veri arka ucunun içinde, sabit zamanda çalışır ve geriye yalnızca yanıt döner.
Python
ok = await ironflock.verify_secret(
"credentials", "api_token", submitted_token,
{"limit": 1, "filterAnd": [{"column": "device_id", "operator": "=", "value": "d-1"}]},
)
if ok:
...Parametreler:
| Parametre | Tür | Açıklama |
|---|---|---|
tablename | str / string | Karşılaştırmanın yapılacağı tablo |
column | str / string | Karşılaştırılacak gizli sütun |
candidate | str / string | Sınanan düz metin. Sunucu tarafında karşılaştırılır ve asla tabloya yazılmaz |
query_params / queryParams | SecretQueryParams | Hangi satırların dahil olacağı. Varsayılan olarak en son satır; limit 100’ü aşamaz |
Go’da parametreler context’ten sonra gelen konumsal dizelerdir; sorgu ise bir *ironflock.TableQueryParams değeridir (en son satır için nil).
JavaScript { match, checked } döndürür, Go ise Match ve Checked alanlarına sahip bir *ironflock.SecretVerifyResult; Python düz bir bool döndürür. checked elinizde olduğunda bunu kullanmaya değer: yanlış bir adayı (checked > 0), hiçbir satırla eşleşmeyen bir seçiciden (checked === 0) ayırır — bunlar kullanıcıya bildirilmesi gereken birbirinden çok farklı hatalardır.
Her satır kendi rastgele değeriyle şifrelendiği için, aynı sırrı tutan iki satır farklı şifreli metin saklar. Bir gizli sütun üzerinde = filtresinin çalışamamasının — veri arka ucu böyle bir sütun üzerindeki her filtreyi URI_SECRET_COLUMN ile reddeder — ve bunun yerine bu fonksiyonun var olmasının nedeni budur.
Veri arka ucu, bir cihazdaki uygulama kimlik bilgisi başına dakikada en fazla 120 denetimi yanıtlar; bunun ötesinde çağrı URI_RATE_LIMITED ile başarısız olur. Her istekte yapılan bir denetim için (ör. bir API anahtarı) olumlu bir sonucu kısa süreliğine önbelleğe alın. Gizli sütun olmayan bir sütun URI_NOT_A_SECRET_COLUMN ile reddedilir; hiç gizli sütunu olmayan bir tabloda ise çağrı, revealSecrets / reveal_secrets çağrısının orada başarısız olduğu gibi başarısız olur.
Uygulamalar Arası Veri Erişimi
Başka bir uygulamanın filo verilerini, aynı proje içinde kendi uygulamanızın içinden okuyun. Uygulamanız, sağlayıcı uygulamayı kendi data-template.yml dosyasının consumes: bölümünde belirtmeli (veya aşağıda açıklanan joker karakter iznine sahip olmalı) ve proje kullanıcısı erişim izni vermelidir. Erişim salt okunurdur: sağlayıcının paylaştığı tabloların ve transform’ların geçmişini sorgulayabilir ve satırlarına gerçek zamanlı olarak abone olabilirsiniz, ancak bunlara yazamazsınız. Tüketilen uygulamalara yapılan bağlantılar uygulama ve aşama başına önbelleğe alınır ve örneğiniz durduğunda otomatik olarak kapatılır.
Bağlantı, cihaz ajanının enjekte ettiği uygulama başına kimlik bilgisini (APP_AUTH_ID, APP_AUTH_SECRET) gerektirir. Platform, eski cihaz kimlik bilgisini — uygulama başına kimlik bilgisi almamış bir uygulamanın, ör. eski bir cihaz ajanı altında, geri dönüş olarak kullandığı seri numarasını — başka bir uygulamanın realm’inde reddeder (NOT_AUTHORIZED, bunu belirten bir mesajla). Sağlayıcı uygulama aynı cihazda çalışıyorsa durum farklıdır: bu kimlik bilgisi o uygulamanın realm’ine salt okunur olarak değil, o uygulamanın kendi tam yetkileriyle katılır.
Uygulamanız joker karakter (wildcard) iznine (consumes: [{ app: "*" }]) sahipse, sağlayıcıları listConsumableApps / list_consumable_apps ve connectToAllApps / connect_to_all_apps (aşağıda) ile dinamik olarak keşfedebilir ve açabilirsiniz.
connectToApp / connect_to_app
Başka bir uygulamanın veri arka ucuna salt okunur bir bağlantı açar ve bir tanıtıcı (handle) döndürür. Tanıtıcı getHistory / get_history, subscribeToTable / subscribe_to_table ve getSeriesHistory / get_series_history (yalnızca tablolar) işlevlerini sunar — kendi tablolarınızda kullandığınız sorgulama ve abone olma işlemlerinin aynısı, aynı parametreler ve sonuçlarla — ayrıca close ile paylaşılan tables / transforms kataloglarını da sağlar. Tanıtıcının subscribeToTable / subscribe_to_table metodu, kendi tablolarınızdakiyle aynı TableSubscription tanıtıcısını döndürür; ironflock.unsubscribe de bunu kabul eder (bkz. unsubscribe / unregister). Bu tablo abonelikleri de yeniden bağlanmanın ardından kaçırılan satırları aynı şekilde alır (bkz. subscribeToTable).
Tanıtıcı ayrıca sağlayıcının adını ve aşamasını (app ve stage, Go’da App ve Stage) ve sağlayıcının realm’ine olan bağlantısını taşır: is_connected / isConnected / IsConnected() bağlantının açık olup olmadığını söyler, connection / Connection() ise bağlantının kendisidir. JavaScript’te close(), bağlantı kapandığında, en geç 5 saniye içinde çözülür (resolve).
Python
# Open a read-only handle on another app's data backend
weather = await ironflock.connect_to_app("weather-app")
# Inspect what the provider shares
print([t["tablename"] for t in weather.tables])
# Query history and subscribe, just like your own tables
rows = await weather.get_history("forecasts", {"limit": 100})
def on_forecast(*args, **kwargs):
print("New forecast:", args)
await weather.subscribe_to_table("forecasts", on_forecast)Parametreler:
| Parametre | Tür | Açıklama |
|---|---|---|
app_name / appName | str / string | consumes: bölümünüzde belirtildiği gibi sağlayıcı uygulamanın adı |
stage | str / string, isteğe bağlı | Sağlayıcı aşaması (stage): "dev" veya "prod". Varsayılan olarak kendi uygulamanızın aşaması |
on_error / onError | callable, isteğe bağlı | Açık bağlantı kalıcı olarak reddedildiğinde (ör. izin daha sonra iptal edildiğinde — bir reddin ne zaman kesin sayıldığı için aşağıya bakın) bir CrossAppAccessError ile çağrılır |
Erişim reddedilir veya yanlış kullanılırsa, code alanına (Go’da ironflock.CodeNoGrant gibi sabitlerle birlikte Code) sahip bir CrossAppAccessError yükseltilir (Python) / fırlatılır (JavaScript) / döndürülür (Go; errors.As ile sınanacak bir *ironflock.CrossAppAccessError): NO_GRANT, PROVIDER_NOT_INSTALLED, UNKNOWN_APP, PRIVATE_TABLE, SECRET_COLUMN (sağlayıcının gizli olarak işaretlediği bir sütuna — bir grup içinde de — filtre uygulayan veya o sütunu seçen bir sorgu) ya da NOT_AUTHORIZED (izin iptal edildi veya o anda doğrulanamadı — ya da uygulamanız eski cihaz kimlik bilgisini sundu; bu durumda mesaj bunu belirtir). Sağlayıcının yönlendiricisinin reddettiği (wamp.error.not_authorized, wamp.error.authorization_failed veya wamp.error.authentication_failed), tüketilen bir uygulamaya ait bir subscribeToTable / subscribe_to_table çağrısı da, reddedilen bir okuma gibi, NOT_AUTHORIZED koduna sahip bir CrossAppAccessError ile başarısız olur.
connectToApp / connect_to_app beklerken gelen bir ret, çağrıyı hemen başarısız kılar. Bir tanıtıcı açıldıktan sonra ret ancak kalıcı olduğunda kesin sayılır — bağlantının en son ayakta olduğu andan bu yana 3 veya daha fazla ret (yönlendiricinin oturumu bir kimlik doğrulama nedeniyle kapatması da, reddedilen her yeniden bağlanma da bir ret sayılır), ilki en az 60 saniye önce —, çünkü platform erişimi kısa bir süre doğrulayamadığında da aynı şekilde reddeder. O zamana kadar tanıtıcı yeniden bağlanmayı sürdürür ve bir yeniden bağlanma başarılı olursa toparlanır. Ret kesinleştiğinde on_error / onError / OnError çağrılır ve tanıtıcı önbellekten çıkarılır: bir sonraki connectToApp / connect_to_app / ConnectToApp yeni bir bağlantı açar. O andan itibaren tanıtıcının okumaları ve abonelikleri, on_error / onError hangi hatayla çağrıldıysa o hatayla hemen başarısız olur — Go’da wamp.ErrStopped’u saran bir hatayla.
Python’da
stageveon_erroranahtar kelime argümanlarıdır; JavaScript’te bir seçenekler nesnesi aracılığıyla geçilir (connectToApp(appName, { stage, onError })); Go’da ise bir struct aracılığıyla (ConnectToApp(ctx, appName, ironflock.ConnectToAppOptions{Stage, OnError})); buradaOnErrorbirfunc(*ironflock.CrossAppAccessError)’dır. Go’dactxyalnızca çağıranın beklemesini sınırlar: aynı uygulama için eşzamanlı çağrılar tek bir denemeyi paylaşır veClose(ctx)bir tanıtıcıyı kapatır. Go, uygulama adlarını büyük/küçük harf fark etmeksizin eşleştirir;ConsumedApp.Stageiseifl.Stage()değerinin aksine küçük harfli aşamadır ("dev"veya"prod").
listConsumableApps / list_consumable_apps
Projedeki her özel olmayan sağlayıcıyı listeler — bu, joker karakter tüketim iznine (data-template.yml dosyanızdaki consumes: [{ app: "*" }], proje kullanıcısı tarafından verilir) sahip uygulamalar için temel keşif işlevidir. Tek bir çağrı yapar ve hiçbir bağlantı açmaz: döndürülen katalogları bir seçicide gösterin, ardından istediğiniz sağlayıcılar için connectToApp / connect_to_app çağırın — veya hepsini tek seferde açmak için connectToAllApps / connect_to_all_apps çağırın.
Not: İzni uygulamanızın
data-template.ymldosyasında belirtin ve*işaretini tırnak içine alın — tırnaksız bir*, bir YAML takma adıdır ve ayrıştırılamaz:
consumes:
- app: "*"Python
providers = await ironflock.list_consumable_apps()
for p in providers:
print(p["app"], list(p["stages"].keys())) # e.g. "weather-app" ["dev", "prod"]Her giriş bir sağlayıcıyı tanımlar:
| Alan | Tür | Açıklama |
|---|---|---|
app | str / string | Sağlayıcı uygulama adı |
provider_app_key | int / number | Sağlayıcının uygulama anahtarı |
stages | dict / object | Aşama başına katalog { dev?, prod? }; bir aşama yalnızca sağlayıcının o aşama için bir veri arka ucu varsa mevcuttur. Her katalog, paylaştığı özel olmayan tables ve transforms öğelerini içerir |
Go’da her girdi; App, ProviderAppKey ve Stages.Dev / Stages.Prod (Tables ve Transforms içeren bir *StageCatalog, eksik bir aşama için nil) alanlarına sahip bir ironflock.ConsumedAppInfo’dur; Catalog(stage) adıyla birini döndürür.
Uygulamanızın hiçbir joker karakter izni yoksa, code: NO_GRANT içeren bir CrossAppAccessError yükseltilir (Python) / fırlatılır (JavaScript) / döndürülür (Go).
connectToAllApps / connect_to_all_apps
Projedeki her özel olmayan sağlayıcıya tek bir çağrıda salt okunur tanıtıcılar (handle) açar (yalnızca joker karakter tüketicileri). Sağlayıcıları listConsumableApps / list_consumable_apps aracılığıyla numaralandırır ve her birini açar; istenen aşama için veri arka ucu bulunmayanları atlar. Her tanıtıcı, connectToApp / connect_to_app ile aynı anahtar altında önbelleğe alınır, böylece daha sonraki bir connectToApp(name) çağrısı zaten hazırlanmış tanıtıcıyı döndürür. Döndürülen tanıtıcılar, örneğiniz durduğunda birlikte kapatılır.
Python
apps = await ironflock.connect_to_all_apps(
on_error=lambda err: print("Provider skipped:", err)
)
for app in apps:
rows = await app.get_history(app.tables[0]["tablename"], {"limit": 10})
print(app.app, rows)Parametreler:
| Parametre | Tür | Açıklama |
|---|---|---|
stage | str / string, isteğe bağlı | Sağlayıcı aşaması (stage): "dev" veya "prod". Varsayılan olarak kendi uygulamanızın aşaması |
continue_on_error / continueOnError | bool / boolean, isteğe bağlı | true olduğunda (varsayılan), açılamayan bir sağlayıcı on_error / onError’a bildirilir ve sonuçtan çıkarılır. false olduğunda, ilk hata yükseltilir/fırlatılır |
on_error / onError | callable, isteğe bağlı | Açılamayan her sağlayıcının hatasıyla çağrılır (continue_on_error / continueOnError true iken) ve zaten açılmış bir bağlantı daha sonra kalıcı olarak reddedilirse (ör. izin iptal edilirse) bir CrossAppAccessError ile çağrılır |
Başarıyla açılan sağlayıcı tanıtıcılarını döndürür (connectToApp / connect_to_app ile aynı tanıtıcı türü). Uygulamanızın hiçbir joker karakter izni yoksa, code: NO_GRANT içeren bir CrossAppAccessError yükseltilir (Python) / fırlatılır (JavaScript) / döndürülür (Go).
Python’da
stage,on_errorvecontinue_on_erroranahtar kelime argümanlarıdır; JavaScript’te bir seçenekler nesnesi aracılığıyla geçilir (connectToAllApps({ stage, onError, continueOnError })); Go’da ise bir struct aracılığıyla (ConnectToAllApps(ctx, ironflock.ConnectToAllAppsOptions{Stage, OnError, StopOnError})); buradaOnErrorbirfunc(error)’dır veStopOnError: true,continueOnError: false’a karşılık gelir.
Yönetilen Dosya Depolama
Her uygulama veri arka ucu, tablolarının yanı sıra files özelliği (Go’da ifl.Files(), bir *filestore.FileStore) üzerinden erişilen özel bir nesne depolama alanı edinir. Bu özellik hiçbir G/Ç (I/O) işlemi yapmaz; bu yüzden üç SDK’nın hepsinde bağlantı başlatılmadan önce de okunabilir. Bunu görüntüler, PDF’ler, kamera kareleri, firmware blob’ları — kısacası bir tablo satırına ait olmayan her şey — için kullanın. Kurulum gerekmez: veri şablonunda files: bölümü bulunmayan bir uygulama bile default adında bir ad alanı edinir (kendi ad alanlarını tanımlayan bir şablon onun yerini alır — bkz. Ad Alanları).
Bu bölüm istemci tarafı referansıdır. Platform tarafı için — ad alanlarını, kotaları ve saklamayı tanımlama, uygulamalar arasında dosya paylaşma, uygulama başına S3 kimlik bilgileri ve yerinde (on-premises) davranış — bkz. Dosya Depolama.
Temel fikir şudur: bir nesneyi sakladığınızda size aynı çağrıda doğrudan bir tablo sütununa yazabileceğiniz kalıcı bir URL döner; böylece bir pano widget’ı onu başka hiçbir iş yapmadan görüntüleyebilir:
Python
from datetime import datetime, timezone
# Store an object and get a permanent URL back in the same call
info = await ironflock.files.put("part-1.jpg", jpeg_bytes, content_type="image/jpeg")
# The URL is safe to store in a table column — a dashboard widget can then
# render <img src="{{photo_url}}"> without any extra round trip
await ironflock.publish_to_table("inspections", {
"tsp": datetime.now(timezone.utc).isoformat(),
"part_id": "1",
"photo_url": info.url,
})
# Read it back
data = await ironflock.files.get("part-1.jpg")
# Every object in the folder 2026/, in its subfolders too (pages are fetched for you)
async for obj in ironflock.files.iter(prefix="2026/"):
print(obj.key, obj.size)Bu URL hiçbir zaman sona ermez, ancak herkese açık bir bağlantı değildir: yalnızca bu veri arka ucu üzerinde READ erişimine sahip, kimliği doğrulanmış bir istek sahibi tarafından okunabilir kalır ve bir kimlik doğrulama proxy’si bunu her istekte denetler; kararını en fazla 5 dakika boyunca önbellekte tutar. Bu nedenle veritabanında saklanması güvenlidir.
Ad Alanları
Bir ad alanı (namespace), politika taşıyan bir anahtar önekidir — saklama süresi, paylaşım kuralları, izin verilen içerik türleri. Ayrı bir bucket değildir; bir uygulamanın her ad alanı, o uygulamanın tek depolama alanının içinde yer alır. Yalnızca bir nesne kümesinin farklı kurallara ihtiyacı olduğunda yeni bir tane tanımlayın; aksi halde default içinde kalın ve nesnelerinizi 2026/03/part-1.jpg gibi anahtar yollarıyla düzenleyin.
Ad alanlarını data-template.yml dosyasında tanımlayın. Herhangi birini tanımlamak örtük default’un yerini alır: default adında bir tane de listeleyin, aksi halde ad alanı belirtilmeyen her çağrı NO_SUCH_NAMESPACE ile başarısız olur.
files:
# Storage budget the app suggests for itself. The project user can change it,
# and their setting is the one that gets enforced.
quotaBytes: 5368709120
namespaces:
# Keep default: declaring namespaces replaces the implicit one.
- name: default
- name: frames
description: Raw camera frames, one JPEG per inspected part.
contentTypes: ["image/jpeg"]
maxObjectBytes: 20971520
retention: { deleteAfter: 30 days }Bütçenin ad alanı başına değil, uygulamanın tamamı için bir kez tanımlandığına dikkat edin. Bir ad alanı yalnızca uygulamanın tek depolama alanı içindeki bir anahtar önekidir, dolayısıyla önek başına bir bütçenin uygulanabileceği bir şey yoktur. maxObjectBytes ise ad alanı başınadır — bir toplamı değil, tek bir nesneyi sınırlar: ayarlanmadıysa 100 MiB, en fazla 5 GiB (üstünde OBJECT_TOO_LARGE).
Aşağıdaki her metot ad alanını isteğe bağlı bir argüman olarak alır ve varsayılan olarak default kullanır; boş bir ad alanı ("") da default anlamına gelir.
Nesneleri Saklama ve Okuma
Python
# Bytes in, bytes out
info = await ironflock.files.put("reports/march.pdf", pdf_bytes, content_type="application/pdf")
data = await ironflock.files.get("reports/march.pdf")
# Or straight from/to a local file — these stream on the large-object path,
# so a multi-gigabyte file never has to fit in memory
await ironflock.files.put_file("firmware/v2.bin", "/data/build/v2.bin")
await ironflock.files.get_to_file("firmware/v2.bin", "/tmp/v2.bin")| Metot | Açıklama |
|---|---|
put(key, data, …) | Bir nesneyi saklar (Python’da bytes, JavaScript’te Uint8Array, Go’da []byte). Nesnenin url değeri dahil meta verilerini döndürür |
get(key, namespace?) | Nesnenin içeriğini döndürür. Satır içi limiti aşan bir nesne doğrudan nesne depolamadan gelir; bunun mümkün olmadığı yerlerde ise yönlendirici üzerinden bayt aralıkları halinde gelir (bkz. Büyük Nesneler) |
put_file(key, path, …) / get_to_file(key, path, …) | Python ve Go (PutFile / GetToFile; Go ayrıca PutReader ile bir io.Reader’dan ve GetTo ile bir io.Writer’a akış olarak aktarır). Yerel bir dosyadan saklar ya da yerel bir dosyaya yazar. Büyük nesne yolunda akış (stream) olarak aktarır |
delete(key, namespace?) | Bir nesneyi siler |
copy(key, to, …) | Bir nesneyi, isteğe bağlı olarak başka bir ad alanına kopyalar |
move(key, to, …) | Önce kopyalar, sonra siler. Atomik değildir — serviste bir taşıma fiili yoktur, bu nedenle başarısız bir silme her iki kopyayı da bırakır |
put ve get nesnenin tamamını bellekte tutar. Büyük bir nesne için akış (stream) olarak aktaran dosya varyantlarını kullanın (Go’da ayrıca PutReader ve GetTo). JavaScript’te dosya yolu yardımcıları yoktur, çünkü paket hem Node hem de tarayıcı için tek bir derleme yayımlar — yerel dosyaları fs ile kendiniz okuyup yazın.
Python’da put_file ve get_to_file metotlarının dosya işleri olay döngüsünün dışında çalışır. get_to_file, eksik üst dizinleri oluşturur ve nesnenin ilk baytları elde edilir edilmez dosyayı yerinde yazar: reddedilen bir okuma (böyle bir nesne yok, yetki yok) yola hiç dokunmaz; yarıda başarısız olan bir okuma ise o ana kadar yazılanı bırakır. Bir FIFO, nesne akış halinde geldikçe yazılır ve onu açmak bir okuyucu bekler; bu yüzden kimsenin okumadığı bir FIFO çağrıyı (olay döngüsünü değil) engeller; asyncio.wait_for ondan vazgeçebilir. put_file, bir FIFO’yu saklamadan önce sonuna kadar okur.
Go’da GetToFile da eksik üst dizinleri oluşturur, ancak hedefin yanındaki geçici bir dosyaya indirir ve indirme tamamlanınca onu yeniden adlandırarak yerine koyar: başarısız olan veya iptal edilen bir indirme hiçbir zaman yarım bir dosya bırakmaz ve önceki dosyayı korur. Sembolik bağlantılar izlenir (gösterdikleri dosya yazılır, bağlantılar yerinde kalır); yeni dosya, sürecin izin verdiği ölçüde eski dosyanın sahibini, grubunu ve izin bitlerini alır. Bir FIFO veya bir aygıt, nesne akarken yerinde yazılır. Yanına yeni bir dosya konamayan bir hedef için — örneğin salt okunur veya sözde (pseudo) bir dosya sistemindeki bir hedef — kendiniz açtığınız bir dosyayla GetTo kullanın.
put şunları kabul eder: content_type / contentType (MIME türü; ad alanı hangilerine izin verildiğini kısıtlayabilir) ve namespace. Python’da bunlar anahtar kelime argümanlarıdır; JavaScript’te bir seçenekler nesnesinin içine girer; Go’da seçeneklerdir (filestore.ContentType(…), filestore.Namespace(…)).
Listeleme ve İnceleme
| Metot | Açıklama |
|---|---|
list(…) | Dosya servisinin listelemesinin tek bir sayfası, varsayılan olarak klasör tarzında: objects, anahtarları öneki / içermeden sürdüren nesneleri, prefixes ise altındaki klasörleri tutar. "" değerinde bir delimiter düz listeler — önek altındaki her nesneyi, alt klasörlerdekiler dahil. Başka bir limit geçmediğiniz sürece bir sayfa en fazla 200 nesne içerir. objects, prefixes, is_truncated / isTruncated / IsTruncated ve sonraki sayfa için geri geçirilecek bir cursor döndürür |
iter(…) / iterate(…) | Bir önek altındaki her nesne üzerinde, alt klasörlerdekiler dahil, sayfalamayı otomatik yaparak ilerleyen asenkron yineleyici. Python’da iter, JavaScript’te iterate, Go’da Iter (bir range döngüsü için bir iter.Seq2[*ObjectInfo, error]) olarak adlandırılır |
stat(key, namespace?) | İçeriğini aktarmadan tek bir nesnenin meta verileri |
exists(key, namespace?) | Bir nesnenin var olup olmadığı |
namespaces() | Bu uygulamanın kullanabileceği ad alanları |
usage(…) | Uygulamanın ne kadar depolama alanı kullandığı — aşağıya bakın |
catalog() | Ad alanları ile sunucunun bildirdiği limitler ve kotalar. Bir kez getirilir ve önbelleğe alınır: catalog(refresh=True) / catalog(true) / RefreshCatalog(ctx) onu yeniden getirir |
Klasörler. / ile biten bir önek bir klasörü adlandırır: iter(prefix="2026/") / iterate({ prefix: "2026/" }) / Iter(ctx, filestore.Prefix("2026/")), 2026/a.txt ve 2026/sub/c.txt öğelerini verir, ancak 2026-notes.txt öğesini vermez. list bir klasörü adıyla açamaz, çünkü dosya servisi / ile biten bir listeleme önekini reddeder (FleetFiles v0.2 INTERNAL yanıtını verir): 2026 önekiyle çağrılan list, nesneler arasında 2026-notes.txt öğesini, önekler arasında ise 2026/ klasörünü döndürür. Servis anahtarları Unicode normalleştirme biçimi C’de saklar; bu yüzden klasör önekini de NFC olarak verin. Önekin başka bir biçimi altındaki bir anahtarla karşılaşan bir yineleme, her anahtarı atlamak yerine başarısız olur.
Nesneler üç SDK’da da aynı alanlarla, her dilin adlandırma stiline uygun olarak tanımlanır: namespace, key, size, etag, content_type / contentType, last_modified / lastModified, checksum_sha256 / checksumSha256 ve url — Go’da filestore.ObjectInfo türünün Namespace, Key, Size, ETag, ContentType, LastModified, ChecksumSHA256 ve URL alanları.
Depolama Kullanımı ve Kota
usage, tek bir çağrıyla doğrudan nesne deposundan yanıt alır; bu nedenle toplamlar SDK tarafından toplanmış değil, kesindir:
Python
u = await ironflock.files.usage()
print(u.size_bytes, u.object_count, u.quota_bytes, u.free_bytes)
# Break the total down per namespace (costs one listing per namespace)
detailed = await ironflock.files.usage(detail=True)
print(detailed.per_namespace) # {"default": 1048576, "frames": 73400320}Go’da alanlar filestore.StorageUsage türünün SizeBytes, ObjectCount, QuotaBytes, FreeBytes ve PerNamespace alanlarıdır.
| Alan | Anlamı |
|---|---|
size_bytes / sizeBytes | Şu anda saklanan bayt miktarı |
object_count / objectCount | Saklanan nesne sayısı |
quota_bytes / quotaBytes | Uygulanan bütçe. 0 sınırsız anlamına gelir |
free_bytes / freeBytes | Kalan bayt miktarı. -1 sınırsız anlamına gelir — burada 0 bildirmek “dolu” gibi okunurdu |
per_namespace / perNamespace | Ad alanı başına bayt miktarı. Yalnızca ayrıntılı dökümü istediğinizde bulunur |
Ad alanı başına döküm varsayılan olarak kapalıdır, çünkü depo bunu doğrudan yanıtlayamaz: hesabı depolama alanı başına tutar ve bir ad alanı yalnızca bir önektir, dolayısıyla SDK’nın boyutları toplamak için her ad alanını listelemesi gerekir. Bunu istediğinizde isteyin, sıcak bir kod yolunda değil.
Birbirinden ayrı tutulması gereken iki farklı kota vardır. catalog() her ikisini de bildirir:
| Alan | Anlamı |
|---|---|
quota_bytes / quotaBytes | Gerçekte uygulanan değer; nesne deposundan okunur — proje kullanıcısının ayarı |
suggested_quota_bytes / suggestedQuotaBytes | Uygulamanın veri şablonunun istediği değer. Hiçbir şey istemediyse 0 |
Go’da Catalog(ctx), QuotaBytes ve SuggestedQuotaBytes alanlarına sahip bir *filestore.Catalog döndürür.
Bir kullanıcı uygulamanın bütçesini yükselttiğinde ya da düşürdüğünde bu iki değer birbirinden ayrılır; uygulanan değerin şablondan değil depodan okunmasının nedeni de budur — uygulamanın yeniden dağıtılması, kullanıcının seçimini sessizce sıfırlamamalıdır. Bir arayüz her ikisini de gösterebilir (“uygulama X öneriyor, siz Y ayarladınız”). Uygulama her zaman ilkini kullanır. Kullanıcı bütçeyi istediği zaman değiştirebilir ve catalog() onu son getirildiği andaki haliyle bildirir: güncel değeri usage() ile okuyun.
Nesneleri Paylaşma
İki tür bağlantı vardır ve aradaki fark önemlidir:
| Metot | Ömür | Kimler okuyabilir |
|---|---|---|
url(key, …) | Kalıcı | Yalnızca bu veri arka ucu üzerinde READ yetkisine sahip, kimliği doğrulanmış bir istek sahibi — her istekte denetlenir (bir karar en fazla 5 dakika önbellekte tutulur). Bir tablo sütununda saklanması güvenlidir |
share_url / shareUrl / ShareURL | Süreli (varsayılan 15 dakika, sunucu tarafından sınırlanır) | Bağlantıyı elinde tutan herkes. Kullanıldığında yetkilendirmeyi yeniden denetleyen hiçbir şey yoktur |
share_url / shareUrl bir taşıyıcı (bearer) yetkidir: geçici erişime ihtiyacı olan bir kişiye verin ve veritabanında saklamayın. Bir panonun görüntülediği her şey için url kullanın.
url, dağıtımın bir HTTP kenarı bulunmadığı durumlarda (örneğin düz HTTP çalışan bir appliance) None / undefined / "" (Go) döndürür — bu, get’e geri dönmeniz gerektiğinin işaretidir. share: none olarak tanımlanmış bir ad alanında kalıcı URL de çalışmaz, ancak katalog bir ad alanının nasıl paylaşıldığını bildirmediği için url bunu ayırt edemez: URL’yi yine de döndürür ve dosya kenarı (file edge) ona 404 ile yanıt verir. Bu tür nesneleri get ile okuyun; share_url / shareUrl / ShareURL bağlantıları share ayarına bağlı değildir. Nesnenin etag değerini version argümanı olarak (Go’da filestore.Version(etag)) geçmek, tarayıcıların yanıtı değişmez (immutable) biçimde önbelleğe almasını sağlar.
Python ve Go’da cloud_url(key, …) / CloudURL(ctx, key, …), kalıcı URL’yi bir appliance’ın bulut dosya tüneli üzerinden döndürür (https://i<instanceKey>-files.<cloud-edge>): appliance çevrimiçi olduğu sürece internetten erişilebilir ve url ile aynı yetkilendirmeye tabidir. Bulutta barındırılan dağıtımlarda (url değerinin zaten internetten erişilebildiği yerlerde), HTTP kenarının bulunmadığı yerlerde ve veri arka ucunun bulut paylaşımı kapalıyken None (Go’da "") döndürür. Kullanıcı bunu istediği zaman açıp kapatabilir; bu nedenle cloud_url / CloudURL, önbellekteki katalog 30 saniyeden eskiyse kataloğu yeniden okur.
upload_url / uploadUrl / UploadURL, doğrudan yükleme kabul eden süreli bir URL (varsayılan olarak 1 saat geçerli) üretir ve url, method, headers ile expires_in / expiresIn döndürür (Go’da URL, Method, Headers ve ExpiresIn alanlarına sahip bir *filestore.UploadTarget). Nesnenin URL üretilirken belirlenen içerik türüyle saklanması için başlıkları verildiği gibi gönderin. Ad alanının içerik türleri ve boyut sınırı, bildirilen bir size ve kota, URL kullanıldığında değil üretildiğinde denetlenir: URL yalnızca ana makine (host) için imzalanır; bu nedenle onu elinde tutan herkes, süresi dolana kadar anahtarına herhangi bir içerik yazabilir. share_url / shareUrl gibi bu da bir taşıyıcı (bearer) yetkidir — yazan türden.
Büyük Nesneler
SDK aktarım yolunu boyuta göre otomatik olarak seçer — yapılandırılacak bir şey yoktur:
| Nesne boyutu | Nasıl aktarılır |
|---|---|
| Satır içi limite kadar (şu anda 6 MiB) | Mesaj yönlendiricisi üzerinden tek bir çağrı |
| Daha büyük | Yönlendiriciyi atlayarak, HTTPS üzerinden doğrudan nesne depolamaya |
| Daha büyük, doğrudan yolun bulunmadığı yerlerde okuma | Mesaj yönlendiricisi üzerinden bayt aralıkları halinde |
Kesin limit, sunucu tarafından çalışma zamanında catalog() içinde inline_max_bytes / inlineMaxBytes / InlineMaxBytes olarak bildirilir; böylece yeni bir SDK sürümü çıkmadan yükseltilebilir.
Doğrudan yol, URL’leri önceden imzalayabilen (presign) bir dağıtım ve yalnızca yönlendiriciye değil nesne depolama sunucusuna da erişebilen bir cihaz gerektirir. Bunlardan biri eksik olduğunda — katalog dağıtımın önceden imzalayamadığını söylediğinde (varsayılan olarak bir yerinde (on-premises) appliance), okuma URL’sinin üretilmesi NOT_SUPPORTED yanıtını verdiğinde veya herhangi bir bayt gelmeden önce nesne depolamaya erişilemediğinde — get (ve Python’da get_to_file, Go’da GetTo ile GetToFile) bunun yerine nesneyi yönlendirici üzerinden bayt aralıkları halinde okur (FleetFiles v0.1.33 ve sonrası); her aralık en fazla satır içi limit kadardır. Her aralık ayrı bir okumadır: böyle bir okuma sırasında değiştirilen bir nesne okumayı INTERNAL ile başarısız kılar (“object changed while it was read”). Yazmaların böyle bir geri dönüş yolu yoktur.
Yazmalar için geriye iki tavan kalır ve her ikisi de hangisine takıldığınızı belirten bir nedenle TOO_LARGE bildirir:
- 5 GiB — nesne deposunun tek seferlik yükleme limiti. Çok parçalı (multipart) yükleme henüz uygulanmadı.
- Doğrudan uç noktanın bulunmadığı yerlerde satır içi limit — önceden imzalayamayan bir dağıtım (varsayılan olarak bir appliance) daha büyük bir nesneyi hiç saklayamaz. Hiçbir yeniden deneme ya da daha küçük parça işe yaramaz ve mesaj bunu açıkça söyler.
Doğrudan yolun sahada sık görülen iki arızası, yetkilendirme sorunu gibi görünmek yerine kendi kodlarını alır: PRESIGN_UNREACHABLE (yalnızca yönlendiriciye izin veren bir proxy — bir okuma aralıklara geri döner, bir yazma başarısız olur) ve CLOCK_SKEW (nesne deposu, 15 dakikadan fazla sapmış istekleri reddeder — cihazdaki NTP’yi kontrol edin).
Dosya Depolama Hataları
Dosya servisinin veya doğrudan bir aktarımın başarısızlığı, kararlı bir code ve insan tarafından okunabilir bir reason taşıyan bir FileStoreError yükseltir (Python) / fırlatır (JavaScript) / döndürür (Go). reason üzerinde değil, her zaman code üzerinde dallanın. Yerel dosya hataları, iptal edilen bir çağrı ve aşağıda eşlenenler dışındaki yönlendirici düzeyindeki WAMP hataları olduğu gibi yükseltilir.
Python
from ironflock.filestore import FileStoreError
try:
await ironflock.files.put("huge.bin", payload)
except FileStoreError as e:
if e.code == "QUOTA_EXCEEDED":
print("Filestore is full:", e.reason)
else:
raise| Kod | Anlamı |
|---|---|
NOT_AUTHORIZED | Çağıran taraf bu işlemi gerçekleştiremez |
NO_SUCH_NAMESPACE | Ad alanı veri şablonunda tanımlı değil |
NO_SUCH_OBJECT | Anahtar mevcut değil |
TOO_LARGE | Tek çağrılık aktarım limitini aşıyor |
OBJECT_TOO_LARGE | Ad alanının kendi maxObjectBytes değerini aşıyor |
QUOTA_EXCEEDED | Dosya deposu dolu |
CONTENT_TYPE_NOT_ALLOWED | Ad alanı contentTypes değerini kısıtlıyor |
INVALID_KEY | Hatalı biçimlendirilmiş bir anahtar veya önek (aşağıya bakın; FleetFiles v0.2 bunu hâlâ INTERNAL olarak bildirir) |
INVALID_RANGE | Nesnenin dışında kalan bir bayt aralığı; SDK’lar asla böyle bir aralık istemez |
NOT_SUPPORTED | Arka uç bunu yapamıyor |
NOT_AVAILABLE | Bu dağıtımda dosya servisi yok — ya da henüz yok: bir platform yeniden başlatmasından sonra veya dosya servisi prosedürlerini kaydederken. Çağrı hiçbir yerde çalışmadı ve tekrarlanabilir |
PRESIGN_UNREACHABLE | Nesne depolamaya doğrudan erişilemiyor (bir proxy mi?) |
CLOCK_SKEW | Cihaz saati fazlaca sapmış |
INTERNAL | Bunların dışındaki her şey — aralıklı bir okuma sırasında değişen bir nesne de |
Daha yeni bir sunucu, bu SDK sürümünün bilmediği kodlar sunabilir. Bunlar tek bir kategoriye indirgenmeden code olarak geçirilir; bu nedenle tanınmayan bir değeri genel bir hata olarak ele alın.
Dosya çağrıları bağlantıyı en fazla 10 saniye bekler (start() / Start öncesinde yapılan bir çağrı, aynı süre içinde onu da bekler) ve yeniden denenmez: tablo işlemlerinden farklı olarak bir platform yeniden başlatmasını atlatamazlar; bu yüzden böyle bir yeniden başlatma sırasında bir çağrı, dosya servisi yeniden kaydolana kadar NOT_AVAILABLE ile başarısız olabilir. Böyle bir çağrı hiçbir yerde çalışmamıştır, bu nedenle uygulamanız onu tekrarlayabilir.
Anahtarlar. Anahtarlar konusunda yetkili olan dosya servisidir; SDK’lar anahtarları ve önekleri verildiği gibi gönderir. Hatalı biçimlendirilmiş bir anahtar veya önek — boş; başında veya sonunda / bulunan (iter / iterate için bir klasör öneki hariç); boş, . veya .. segmenti ya da . ile biten veya boşlukla başlayan ya da biten bir segmenti olan; \ : * ? " < > | ^ ~ karakterlerinden birini, bir kontrol karakterini veya bir Unicode biçim karakterini içeren; 900 baytı, 20 segmenti veya segment başına 255 baytı aşan; ya da ayrılmış _sys/ öneki altındaki — anahtar hatalarını sınıflandıran bir dosya servisi tarafından INVALID_KEY ile, FleetFiles v0.2 tarafından ise INTERNAL ile reddedilir. Zaman damgalarına dikkat edin: 2026-10-09T12:00:00Z, : nedeniyle geçerli bir anahtar segmenti değildir.
Python’da
FileStoreError,ironflock.filestoreiçinden içe aktarılır; JavaScript’te ise paket kökünden dışa aktarılır (import { FileStoreError } from "ironflock"); Go’da*filestore.Error’dır (ayrıcaironflock.FileStoreError) ve kodlarfilestore.CodeQuotaExceededgibi sabitlerdir.
Cihazlar Arası İletişim
registerDeviceFunction / register_device_function
Aynı uygulamayı (aynı aşamada) çalıştıran diğer cihazların ve pano widget eylemlerinin çağırabileceği bir prosedür kaydeder. SDK tam URI’yi, <SWARM_KEY>.<DEVICE_KEY>.<APP_KEY>.<STAGE>.<topic>, sizin için oluşturur; burada STAGE, ENV değeri PROD olduğunda (büyük/küçük harf fark etmeksizin) PROD, aksi halde DEV olur. SDK, herhangi bir şey göndermeden önce boş bir konuyu ve eksik ya da 0 olan bir cihaz, proje veya uygulama anahtarını reddeder; callDeviceFunction / call_device_function de öyle. Yönlendirici bir uygulamadan başka hiçbir kaydı kabul etmez ve yalnızca tekli (single) kayıtları kabul eder (bkz. Bir Uygulamanın Kullanabileceği URI’ler). Bağlantı yokken metot, bir bağlantı için yeniden bağlanma penceresi boyunca (varsayılan olarak 60 saniye, bkz. Bağlantı Güvenilirliği) bekler. Kayıt her yeniden bağlanmadan sonra geri yüklenir. Metot, kaydın tanıtıcısı olan bir Registration döndürür: onun unregister() metodu kaydı kaldırır (bkz. unsubscribe / unregister).
Python
def add(a, b):
return a + b
await ironflock.register_device_function("com.myapp.add", add)
register(),register_device_function()için bir takma addır.
Eski adlar olan register_function / registerFunction / RegisterFunction ve call_function / callFunction / CallFunction hâlâ çalışır, ancak kullanımdan kaldırılmıştır.
ENVdeğerini geçersiz kılan uygulamalar: 1.8.6’ya (Python) ve 1.8.4’e (JavaScript) kadarki SDK sürümleri, hamENVdeğerini aşama segmentine koyuyordu. Bu yalnızca bir uygulamaENVdeğerini kendisiprodgibi farklı bir yazımla ayarladığında fark yaratır: bu durumda o sürümlerdeki cihazlar ile daha yeni sürümlerdeki cihazlar birbirlerinin fonksiyonlarını çağıramaz.
callDeviceFunction / call_device_function
Aynı uygulamanın başka bir cihazı tarafından kaydedilmiş bir prosedürü çağırır. SDK, hedef cihazın anahtarını kullanarak tam WAMP konusunu otomatik olarak oluşturur: <SWARM_KEY>.<device_key>.<APP_KEY>.<STAGE>.<topic>; bu, registerDeviceFunction / register_device_function metodunun hedef cihazda kaydettiği URI’nin aynısıdır.
Python
result = await ironflock.call_device_function(
42, # target device key
"com.myapp.add", # procedure name
args=[3, 5] # arguments
)
print(result) # 8call
Tam bir WAMP URI kullanarak uzak prosedür çağırır. Bir uygulama some.full.wamp.topic gibi bir adı kaydedemez; bu yüzden böyle bir ad yalnızca platform servislerinin uygulamanın veri realm’inde kaydettiği prosedürlere, örneğin veri arka ucunun ve dosya depolamanın prosedürlerine ulaşır. Uygulamanızın başka bir cihazının kaydettiği bir fonksiyonu çağırmak için callDeviceFunction / call_device_function kullanın.
Python
result = await ironflock.call("some.full.wamp.topic", args=[42])Bir Uygulamanın Kullanabileceği URI’ler
Uygulama container’ınız kendi veri realm’ine, realm-<SWARM_KEY>-<APP_KEY>-<dev|prod>, app yönlendirici rolüyle katılır. Yönlendirici oradaki her yayımlama, abonelik, çağrı ve kaydı bu rolün izin listesine göre denetler. SDK’nın kendi metotları bu listenin içinde kalır; kurallar, publish, subscribe veya call metotlarına kendi URI’lerinizi verdiğinizde ya da connection nesnesini (Go’da Connection()) doğrudan kullandığınızda önem kazanır. Reddedilen bir eylem wamp.error.not_authorized ile başarısız olur. Python’da metot bir RuntimeError yükseltir ve wamp_error_uri(err) URI’yi döndürür; JavaScript’te hata, error alanı URI olan bir WampError nesnesidir; Go’da ironflock.WampURI(err) bunu döndürür.
| Kendi realm’inizdeki URI | İzin verilen | Kullanan |
|---|---|---|
Kendi adlarınız, ör. com.myapp.status | yayımlama, abonelik (önek abonelikleri de), çağrı — asla kayıt | publish, subscribe, call |
<SWARM_KEY>.<APP_KEY>.<table>, bulk.<SWARM_KEY>.<APP_KEY>.<table> | yayımlama — asla abonelik | – (tablo yazma konuları; SDK’nın tablo yazmaları bunun yerine ekleme yapar) |
transformed.<table>, transformed.bulk.<table> | yalnızca abonelik | subscribeToTable / subscribe_to_table |
databackend.errors.…, files.events… | yalnızca abonelik | Veri arka ucunun hata ve dosya olayları |
<SWARM_KEY>.<DEVICE_KEY>.<APP_KEY>.<STAGE>.<name> | kayıt (single), çağrı, yayımlama — asla abonelik | registerDeviceFunction / register_device_function, callDeviceFunction / call_device_function |
append.…, appendBulk.…, history.transformed.…, secret.reveal.…, secret.verify.…, files.read.…, files.write.… | çağrı (SDK bunları çağırır) | Tablo, geçmiş, gizli değer ve dosya metotları |
sys.appaccess.list, sys.appaccess.resolve | çağrı | listConsumableApps / list_consumable_apps, connectToApp / connect_to_app, connectToAllApps / connect_to_all_apps |
wamp.session.get | çağrı | JavaScript SDK’sının heartbeat’i |
Kimliği denetlenen adlar. Bir rakamla başlayan adlar — tablo yazma konuları ve cihaz fonksiyonu URI’leri — ayrıca platformun kimlik denetiminden geçer; bu denetim onları realm’inizin SWARM_KEY değerine bağlar. Bir kayıt yalnızca kendi cihazınıza ait, realm’inizin SWARM_KEY, APP_KEY ve aşama (stage) değerlerini taşıyan bir fonksiyon URI’si için kabul edilir; önek veya joker karakterli bir kayıt ise yalnızca bu dört segmenti sabitlediğinde kabul edilir. Yayımlama ve çağrı yalnızca realm’inizin SWARM_KEY değerini gerektirir: başka bir APP_KEY içeren bir tablo konusuna yayımlanan satır kabul edilir, ancak hiçbir veri arka ucuna ulaşmaz ve kaybolur. Bu URI’leri SDK’nın oluşturmasına bırakın.
Ayrılmış adlar. Yönlendirici, yukarıdaki iki uygulamalar arası prosedür dışında sys. adlarını ve wamp.session.get dışında wamp. adlarını reddeder. Tablodaki diğer adlar platforma aittir: kendi adlarınızı bir rakamla, bulk., transformed., databackend.errors., files., append., appendBulk., history. veya secret. ile başlatmayın. Diğer her ilk segment, uygulamanızın kendi konu alanıdır.
Ham tablo konularına kimse abone olmaz. Bu konular satırı tam olarak gönderildiği gibi, gizli sütunların düz metni dahil taşır; veri arka ucu gizli sütunları ancak satırı saklarken şifreler. subscribeToTable / subscribe_to_table bunun yerine saklanan satırları transformed.<table> konusundan, tiplendirilmiş ve gizli sütunları maskelenmiş olarak okur.
Diğer uygulamaların verileri salt okunurdur. Bir sağlayıcının realm’inde uygulamanız (app_reader yönlendirici rolü), heartbeat için kullanılan wamp.session.get dışında yalnızca transformed.… ve files.events… konularına abone olabilir ve history.transformed.… ile files.read.… prosedürlerini çağırabilir — yayımlama, kayıt, yazma ve gizli değer prosedürleri yoktur.
Cihaz Meta Verisi
setDeviceLocation / set_device_location
Platformdan cihazın GPS konumunu güncellemesini ister; IronFlock haritaları bu konumu gerçek zamanlı olarak gösterir.
Şu anda kullanılamıyor: uygulamanın veri realm’inde bu çağrıyı yanıtlayan bir platform servisi henüz yok, bu yüzden çağrı
wamp.error.no_such_procedureile başarısız olur. O zamana kadar konumları kendi tablonuzda tutun.
Python
await ironflock.set_device_location(long=8.6821, lat=50.1109)| Parametre | Aralık |
|---|---|
long | -180 ile 180 |
lat | -90 ile 90 |
Konum geçmişi saklanmaz. Konumu zaman içinde takip etmek için ayrı bir tablo oluşturun ve
publish_to_table/publishToTablekullanın.
getRemoteAccessUrlForPort
Uygulamanın port-template.yml dosyasında tanımlanan bir portun, tüneli etkinleştiğinde erişilebilir olduğu URL’yi döndürür.
Python
url = ironflock.getRemoteAccessUrlForPort(8080)
# "https://<device_key>-<app_name>-8080.app.ironflock.com"
vnc = ironflock.getRemoteAccessUrlForPort(5900, protocol="tcp")
# "tcp://app.ironflock.com:<the tunnel's remote port>"| Parametre | Tür | Açıklama |
|---|---|---|
port | int / number | port-template.yml dosyasında tanımlandığı şekliyle port |
protocol | str / string, isteğe bağlı | Portun protokolü: "http" (varsayılan), "https", "tcp" veya "udp", büyük/küçük harf fark etmeksizin; çevresindeki boşluklar yok sayılır ve boş bir protokol "http" anlamına gelir |
remote_port_environment / remotePortEnvironment | str / string, isteğe bağlı | Yalnızca tcp/udp: REMOTE_PORT_FOR_<port> yerine uzak portu tutan değişken — şablondaki portun kendi remote_port_environment değeri; 0.19.8 öncesi cihaz ajanları buna ihtiyaç duyar |
http ve https portları, cihaz ajanının enjekte ettiği tünel alan adında (TUNNEL_DOMAIN) <device_key>-<app name, lower case>-<port> tünel etiketi altında (https portları platformun secure- önekiyle) her zaman bir https:// URL’si olarak sunulur. Yerinde (on-premises) bir appliance’a ait bir cihazda (ajan INSTANCE_KEY enjekte eder) bunun yerine bulut üzerinden yönlendirilen https://i<INSTANCE_KEY>-<label>.<CLOUD_TUNNEL_DOMAIN> yolu döndürülür.
Bir tcp veya udp URL’si, tünelin atadığı ve cihaz ajanının REMOTE_PORT_FOR_<port> olarak duyurduğu (ajan 0.19.8 ve sonrası) genel portu taşır: <protocol>://<TUNNEL_DOMAIN>:<remote port>. Bir appliance’ın cihazında, ajan duyurduğu sürece internete açık port olan REMOTE_PORT_FOR_<port>_CLOUD tercih edilir; bu port görünmeden önce ve ajan onu kaldırdıktan sonra yeniden (bulut yönlendirmesi kapatıldığında) appliance’ın yerel URL’si döndürülür. 0 değerindeki bir port henüz atanmamıştır.
Port değerleri /data/env dizininden canlı olarak okunur; bu nedenle sonucu önbelleğe almak yerine metodu yeniden çağırın. URL’nin karşılayamadığı bazı durumlar:
- Cihaz ajanı yalnızca üretim (PROD) kurulumlarının portları için tünel açar. Bir geliştirme (DEV) container’ında http(s) URL’si, bu cihazdaki aynı uygulamanın PROD kurulumunun URL’sidir; tcp/udp ise hiçbir URL vermez. Bir DEV uygulamasına bunun yerine cihazın LAN adresi üzerinden ulaşın (
DEVICE_LAN_IP,DEVICE_PORT_FOR_<port>). - Alan adı olmadan (düz mod) kurulmuş bir appliance’ın uç cihazında ajan
TUNNEL_DOMAINdeğerinilocalhostolarak ayarlar; bu yüzden orada oluşturulan URL’ler localhost’u gösterir: böyle bir appliance, uygulama portlarını kendi ana makine portlarında sunar ve bunu bir tünel URL’si ifade edemez. - Ayarlanmamış veya boş tünel alan adları varsayılan olarak
app.ironflock.comolur. Platform bugünCLOUD_TUNNEL_DOMAINdeğerini ayarlamaz: uygulama bu değişkeni ortam ayarlarında kendisi ayarlamadıkça bir appliance’ın URL’leriapp.ironflock.comkullanır. - Root dışında bir kullanıcıyla çalışan bir container’da değerler, container’ın başladığı andaki değerlerdir (bkz. Ortam Değişkenleri).
URL oluşturulamadığında metot None / null (Go’da "" ve false) döndürür: cihaz anahtarı veya uygulama adı eksikse, bir tcp/udp portu için henüz uzak port duyurulmamışsa (veya değeri 0 ise), protokol bilinmiyorsa ya da etiket geçerli bir DNS etiketi değilse (63 karakterden uzun veya noktalı bir uygulama adı). Tarayıcıda ortam yoktur: http(s) URL’si deviceKey ve appName seçeneklerinden app.ironflock.com üzerinde oluşturulur.
Bağlantı Özellikleri & Yaşam Döngüsü
Python
| Özellik | Tür | Açıklama |
|---|---|---|
is_connected | bool | Platform bağlantısının aktif olup olmadığı |
connection | CrossbarConnection | Altta yatan bağlantı örneği (gelişmiş kullanım) |
files | FileStore | Uygulamanın yönetilen dosya depolaması (bkz. Yönetilen Dosya Depolama) |
| Metot | Açıklama |
|---|---|
run() | Bağlantıyı başlatır, mainFunc’ı çalıştırır (engelleme), ardından bağlantıyı durdurur |
await start() | Bağlantıyı asenkron olarak başlatır. Bundan önce yapılan işlemler, kendi bekleme süreleri içinde onu bekler (bkz. Bağlantı Güvenilirliği) |
await stop() | Bağlantıyı ve tüketilen uygulama bağlantılarını durdurur ve ana görevi (mainFunc) iptal eder. Bağlantılar realm’lerinden ayrılır ve sonrasında yeniden bağlanmaz; start() yeniden bağlanır. Hâlâ bir bağlantıyı veya start() çağrısını bekleyen her işlem hemen başarısız olur; hâlâ bekleyen bir start() veya connect_to_app da öyle. stop() sonrasında yapılan bir işlem bir sonraki start() çağrısını bekler (bkz. Bağlantı Güvenilirliği) |
await run_async() | Bağlantıyı başlatır ve asenkron olarak çalıştırmaya devam eder |
run() ve run_async(), mainFunc döndüğü veya bir istisna yükselttiği anda bağlantıyı durdurur; yükseltilen istisna, durdurmanın ardından yeniden yükseltilir. Bu nedenle yalnızca abone olan veya cihaz fonksiyonları kaydeden bir mainFunc, örneğin await asyncio.Event().wait() ile çalışmaya devam etmelidir. mainFunc bir async fonksiyon (async def) olmalıdır. mainFunc verilmezse run() ve run_async() bağlantıyı süreç sona erene kadar açık tutar.
Bağlantı Güvenilirliği
SDK kendiliğinden yeniden bağlanır. Bağlantı koptuğunda router geri gelene kadar yeniden dener, ardından her aboneliği ve kayıtlı her cihaz fonksiyonunu geri yükler — yeniden bağlandıktan sonra tekrar abone olmanız gerekmez ve elinizdeki tanıtıcılar geçerli kalır: her birinin active değeri, şu anda router’da etkin olup olmadığını söyler (bkz. unsubscribe / unregister). Tablo abonelikleri, bağlantı kopukken saklanan satırları da teslim eder (bkz. subscribeToTable); kendi konularınızdaki mesajlar ise saklanmaz, bu yüzden bu arada yayımlananlar kaybolur. Biri geri yüklenemezse diğerleri yine de geri yüklenir; başarısız olan ise oturum sürdüğü müddetçe yeniden denenir — 1 saniye sonra, 30 saniyeye kadar ikiye katlanarak ve her bekleme %15’e kadar değiştirilerek — ve her yeniden bağlanmadan sonra tekrar denenir: router onu daha önce kabul etmişti, dolayısıyla reddi geçicidir; örneğin platformun bir cihaz fonksiyonu için yaptığı kimlik denetiminin bir anlığına güvenli tarafta kalarak reddetmesi (fail closed) gibi. İlk başarısızlık bir uyarı olarak kaydedilir; geri yükleme başarılı olduğunda da kaydedilir.
Tablo işlemleri — tablo yazmaları, geçmiş, seri ve gizli değer okumaları ile tüketilen bir uygulamanın geçmiş ve seri okumaları — router veya platform güncellemesi gibi bir platform yeniden başlatmasını da atlatır. Bağlantının geri gelmesi için reconnect_window (Python, saniye cinsinden) / reconnectWindowMs (JavaScript) / WithReconnectWindow (Go, bir time.Duration) kadar, varsayılan olarak 60 saniye beklerler; start() / Start öncesinde yapılan bir işlem de aynı pencere içinde onu bekler. Ayrıca veri arka ucu prosedürlerini henüz sunmadığı sürece yeniden denerler: router wamp.error.no_such_procedure yanıtını verir ya da veri arka ucu, kaydının önüne geçen bir çağrıyı “client has no handler for registration” ile reddeder. Böyle bir çağrı hiçbir zaman bir işleyiciye ulaşmamıştır; bu yüzden bir yeniden deneme bir satırı asla iki kez yazmaz; diğer her hata geldiği gibi yükseltilir. Bir tablo aboneliğinin kaçırılan satırları getiren okuması da bu okumalardan biridir.
Tablo yazmalarına publishToTable / publish_to_table, publishRowsToTable / publish_rows_to_table ve reportError / report_error de dahildir: appendToTable ve appendRowsToTable ile aynı ekleme prosedürlerini çağırırlar; bu yüzden veri arka ucu yeniden başlarken yazılan bir satır kaybolmaz.
publish, subscribe, subscribeToTable / subscribe_to_table (tüketilen bir uygulamanınki de) ve registerDeviceFunction / register_device_function de yeniden bağlanma penceresi içinde bir bağlantı bekler; böylece kısa bir kesinti onları başarısız kılmaz. Yeniden denenmezler; bir abonelik veya kayıt sonraki her yeniden bağlanmadan sonra geri yüklenir.
Diğer her işlem — call, callDeviceFunction / call_device_function, setDeviceLocation / set_device_location, dosya çağrıları ve uygulamalar arası sorgulamalar ile bağlantı açma işlemleri — bir bağlantı için en fazla 10 saniye bekler ve yeniden denenmez. start() / Start öncesinde yapılan bir işlem, bu 10 saniye içinde onu bekler. Beklemesi içinde bağlantı bulamayan bir işlem, bekleme süresini saniye cinsinden belirten bir hatayla başarısız olur: Python’da IronFlock metotları nedeni bir TimeoutError olan bir RuntimeError yükseltir; JavaScript’te bu düz bir Error’dır; Go’da ise wamp.ErrNotConnected’ı saran bir hata. unsubscribe ve unregister hiç beklemez: bağlantı kopukken de hemen etkili olurlar (bkz. unsubscribe / unregister).
0 pencereyi kapatır: bu işlemler bu durumda diğer her işlem gibi davranır. Bir bağlantı için 10 saniye bekler ve yeniden denenmezler; bu yüzden bir platform yeniden başlatmasının hemen ardından kaçırılan satırların teslimi de yeniden denenmez. Negatif bir pencere ve Python ile JavaScript’te ayrıca NaN veya sonsuz bir değer, IronFlock nesnesi oluşturulurken reddedilir (bkz. Yapıcı Seçenekleri).
Oturumu kapandığında (router onu kapattığında, bağlantı koptuğunda ya da stop() çağrıldığında) zaten gönderilmiş olan bir istek, yanıt beklemek yerine hemen başarısız olur ve yeniden denenmez: işleyicisine ulaşmış olabilir. Python’da hatanın nedeni (cause), kapanış nedenini (ör. wamp.close.system_shutdown) taşıyan autobahn ApplicationError hatası ya da bağlantı koptuysa TransportLost’tur. JavaScript’te hata, error alanı kapanış nedeni olan bir WampError nesnesidir: router’ın bildirdiği neden, stop() sonrasında wamp.close.goodbye_and_out ya da bağlantı koptuğunda wamp.close.transport_lost. Go’da böyle bir istek, wamp.ErrNotConnected’ı (Stop’tan sonra wamp.ErrStopped’u) saran bir hatayla başarısız olur.
Özel bir durum, erişilebilir olan ancak uygulama için henüz bir realm’i bulunmayan router’dır. Eşzamanlı kurulum yarışında uygulama konteyneri, veri arka ucu realm’ini hazırlamadan önce başlar; bu yüzden ilk katılma denemeleri wamp.error.no_such_realm ile reddedilir. SDK birkaç saniyede bir yeniden dener ve realm oluştuğu anda uygulama ayağa kalkar. Bir dakika sonra hâlâ eksik olan bir realm büyük olasılıkla hiç oluşmayacaktır — uygulama silinmiştir ya da bu stage için bir veri arka ucu yoktur —, bu nedenle SDK o andan itibaren router’a sonsuza kadar her saniye istek göndermek yerine iki dakikada bir denemeye yavaşlar. Realm ortaya çıktığında ya da router’ın kendisi gidip geri geldiğinde hızlı tempo geri döner.
stop() / Stop her beklemeyi hemen sona erdirir. Hâlâ bir bağlantıyı — ya da start() / Start öncesinde yapılmışsa onu — bekleyen bir işlem, SDK’nın durdurulma hatasıyla başarısız olur; iki yeniden deneme arasında bekleyen bir tablo çağrısı da öyle: Python’da IronFlock bağlantısının durdurulduğunu belirten bir ConnectionError (IronFlock metotları bunu RuntimeError hatalarının nedeni olarak yükseltir), JavaScript’te bir ConnectionStoppedError, Go’da ise wamp.ErrStopped’u saran bir hata. Örneğin henüz var olmayan bir realm’e ilk katılmasını hâlâ bekleyen bir start(), Connection to realm <realm> stopped before it opened ile başarısız olur; Go’da Start, wamp.ErrStopped’u saran bir hata döndürür. Bir sağlayıcının bağlantısını hâlâ açmakta olan bir connect_to_app / connectToApp, Connection to app '<app>' (<stage>) aborted by stop() ile başarısız olur; bağlantı yine de katılırsa kapatılır ve çağrı Connection to app '<app>' (<stage>) closed by stop() ile başarısız olur. Go’da ConnectToApp, ironflock: connection to app '<app>' (<stage>) aborted by Stop ile (ya da closed by Stop ile) başarısız olur. stop() ile yarışan bir ret, kendi CrossAppAccessError hatasını korur. Python ve JavaScript’te stop() döndüğünde bağlanmaya çalışan hiçbir şey kalmaz ve ayrılma isteğine yanıt vermeyen bir router’ın bağlantısı 5 saniye sonra kesilir; Go’nun Stop metodu ayrılmayı ctx içinde bekler ve ardından soketi kapatır, Run ise ona 10 saniye tanır.
Python ve JavaScript’te örnek, stop() sonrasında yeniden başlatılabilir: start() yeniden bağlanır ve stop() sonrasında yapılan bir işlem, ilk start() öncesinde yapılan bir işlem gibi bir sonraki start() çağrısını bekler. Go’nun Stop metodu kalıcıdır: ondan sonra yapılan bir işlem hemen wamp.ErrStopped ile başarısız olur.
Daha zor durum, sessizce ölen bir bağlantıdır: boştayken bir NAT ya da proxy iki tarafa da haber vermeden bağlantıyı keser veya bir yük dengeleyicinin arkasındaki router yeniden başlar. Kaybı bildiren hiçbir şey gelmez. Yalnızca veri yayımlayan bir uygulama bunu bir sonraki başarısız yazma işleminde fark eder; ancak yalnızca abone olan bir uygulama hiç yazmaz, dolayısıyla yardım almadan konteyner yeniden başlayana kadar ölü kalır.
Üç SDK da bunu tespit eder. Tespit varsayılan olarak etkindir ve yapılandırma gerektirmez.
Python
Python SDK’sı WebSocket keepalive kullanır: bağlantı boştayken her 30 saniyede bir router’a ping gönderir ve 10 saniye içinde pong bekler. Yanıt gelmezse bağlantı düşürülüp yeniden kurulur. Gelen trafik yaşam kanıtı sayılır, bu yüzden yoğun bir bağlantı hiç yoklanmaz — ping yalnızca bağlantı gerçekten sessizleştiğinde gider.
connectToApp/connect_to_appile açılan uygulamalar arası bağlantılar genellikle yalnızca abone olur; bu da tam olarak burada korunan durumdur. Otomatik olarak kapsanırlar ve router onları reddettikten sonra da, ret kalıcı hale gelene kadar yeniden bağlanırlar (bkz.connectToApp).
Hata Yönetimi
Başarısız olan bir SDK metodu — geçersiz argümanlarda, kopan bir bağlantıda veya platformdan gelen bir reddetmede — işlemi, konuyu ve nedeni belirten bir mesajla bir istisna yükseltir (Python), reddeder (JavaScript) ya da bir hata döndürür (Go); bu nedenle hayatta kalmasını istediğiniz çağrıları bir try bloğuna sarın — Go’da döndürülen hatayı denetleyin. Bazı başarısızlıklar kodunuza ulaşmaz. Kendi konularınızdan birine yayımlanan bir mesaj, bu arada kapalı olan hiçbir aboneye ulaşmaz. Yeniden bağlanmanın ardından kaçırılan satırları getiren okuma başarısız olursa bu yalnızca günlüğe kaydedilir (bkz. subscribeToTable).
Python
try:
rows = await ironflock.getHistory("sensordata", {"limit": 100})
except ValueError as e:
# Invalid parameters — e.g. limit out of range, or a malformed filter
print(f"Bad query: {e}")
except RuntimeError as e:
# Not connected, table not in the data-template, or the platform rejected the call
print(f"Query failed: {e}")JavaScript’te router’ın veya çağrılan tarafın bir reddi genellikle bir WampError’dır — ek olarak WAMP hata URI’sini error içinde, hata yükünü ise args / kwargs içinde taşıyan normal bir Error alt sınıfı. Üç tür çağrı farklı şekilde başarısız olur: prosedürü kayıtlı olmayan bir geçmiş, seri, açığa çıkarma veya doğrulama çağrısı bunu belirten ve olası nedenleri sıralayan düz bir Error ile ve ancak yeniden bağlanma penceresinin tamamı dolduktan sonra başarısız olur, çünkü wamp.error.no_such_procedure yeniden denenir; bir dosya çağrısı bir FileStoreError ile başarısız olur (bkz. Dosya Depolama Hataları); reddedilen bir uygulamalar arası çağrı ise, tüketilen bir uygulamanın subscribeToTable çağrısı dahil, bir CrossAppAccessError ile başarısız olur. Bağlantıyı beklerken stop() tarafından sonlandırılan bir işlem, dışa aktarılan bir Error alt sınıfı olan ConnectionStoppedError ile başarısız olur. Bunun dışındaki her şey (hatalı parametreler, bağlantı olmaması) düz bir Error’dır.
Go’da başarısız bir işlem, mesajı işlemi ve nedeni belirten ve bu nedeni saran bir *ironflock.OperationError döndürür; errors.Is ve errors.As onun içini görür. Router’dan veya çağrılan taraftan gelen bir ret, URI, Args ve Kwargs alanlarına sahip bir *ironflock.WampError’dır (*wamp.Error); ironflock.WampURI(err) URI’yi ya da "" döndürür. Diğer sentinel hatalar ironflock.ErrMissingConfig (işlemin ihtiyaç duyduğu kimlik bilgisi eksik), ironflock.ErrAlreadyStarted ve wamp.ErrStopped’tur (Stop sonrasındaki bir işlem ya da Stop tarafından yarıda kesilen bir işlem).
Yükseltme: Eski Python ve JavaScript SDK sürümleri bir çağrı başarısız olduğunda bir mesaj kaydedip
None/nulldöndürüyordu. Artık bunun yerine hata yükseltiyorlar; bu nedenleif result is None:biçimindeki kodlar hataları artık yakalamaz —try/except(ya datry/catch) kullanın.
Veri Arka Ucu Retleri
Veri arka ucunun reddettiği bir işlem, aşağıdaki WAMP hata URI’lerinden biriyle başarısız olur; neden ilk argümandadır. Üç SDK da bunları sabitler olarak dışa aktarır; Go bunları Go stilinde adlandırır (ironflock.URIRateLimited, ironflock.URIStorageFull, …). Python’da IronFlock metotları, nedeni autobahn’ın ApplicationError hatası olan bir RuntimeError yükseltir (tüketilen bir uygulamanın okumaları doğrudan ApplicationError yükseltir) ve wamp_error_uri(err) URI’yi her ikisinden de okur. JavaScript’te hata, error alanı URI olan bir WampError nesnesidir. Go’da bir *ironflock.WampError’ı saran bir *ironflock.OperationError’dır ve ironflock.WampURI(err) URI’yi okur. SDK’lar bunların hiçbirini yeniden denemez. Yayımlama metotları dahil her tablo yazması bunları bildirir: yayımlama metotları da ekleme yapar.
Python
from datetime import datetime, timezone
from ironflock import URI_STORAGE_FULL, wamp_error_uri
row = {"tsp": datetime.now(timezone.utc).isoformat(), "temperature": 22.5}
try:
await ironflock.append_to_table("sensordata", row)
except RuntimeError as err:
if wamp_error_uri(err) == URI_STORAGE_FULL:
pending.append(row) # the appliance's disk is nearly full: append the row later
else:
raise| Sabit | Reddedilen |
|---|---|
URI_RATE_LIMITED | Bir cihazdaki uygulama kimlik bilgisi başına dakikada 30’u aşan revealSecrets / reveal_secrets ve 120’yi aşan verifySecret / verify_secret çağrıları |
URI_RESULT_TOO_LARGE | 8 MiB’lık mesaj bütçesini aşan tek bir satır içeren bir okuma veya 1 GiB korumasını aşan bir sonuç (diğer büyük sonuçlar parçalar halinde gelir ve SDK bunları yeniden birleştirir). Anahtar kelime argümanları en büyük sütunları adlandırır: limit değerini düşürün, timeRange aralığını daraltın, daha az columns seçin veya columnPaths ile budayın |
URI_INVALID_LIMIT | limit değeri 3000’i aşan bir transform okuması |
URI_INVALID_TIME_RANGE, URI_INVALID_METRIC, URI_INVALID_GROUP_BY, URI_SERIES_TOO_MANY_GROUPS | Tablonun yanıtlayamadığı seri sorguları: başlangıç yok veya bitiş başlangıçtan sonra değil; tablonun toplayamadığı bir metrik (böyle bir sütun yok, gizli bir sütun, sütun türünün kabul etmediği bir metot, COUNT dışında bir metotla tsp); tablonun gruplayamadığı bir groupBy sütunu; 50.000’den fazla satır |
URI_SECRET_COLUMN, URI_NOT_A_SECRET_COLUMN | Gizli bir sütun üzerinde filtre; gizli olmayan bir sütun üzerinde verifySecret / verify_secret. Gizli sütunu olmayan bir tablonun açığa çıkarma veya doğrulama fonksiyonu hiç yoktur (bkz. revealSecrets) |
URI_STORAGE_FULL | Appliance’ın diski neredeyse doluyken yapılan bir ekleme (yaklaşık 3 GiB’ın altında boş alan; 4 GiB’tan itibaren yeniden kabul edilir) — geçicidir: satırları daha sonra ekleyin |
URI_STORAGE_OVERUSAGE | Hesabın depolama hakkını aşan bir ekleme; kısa süre sonra yeniden denemek işe yaramaz |
URI_ENTITY_KEY_CONFLICT | Varlık anahtarı (maintainLatestFlagFor) ve tsp değeri, yığında veya saklanmış başka bir satırda zaten bulunan bir satır: her satıra kendi tsp değerini verin |
URI_SECRET_SENTINEL_UNRESOLVABLE, URI_SECRET_CIPHERTEXT_REJECTED | Bir sırrın önceki değerini (__secret__) onu bulacak bir varlık anahtarı olmadan koruyan bir yazma; zaten şifrelenmiş bir değerin yazılması |
Veri arka ucunun neden belirtmeden reddettiği bir ekleme — wamp.error.runtime_error, tsp içermeyen bir satıra verdiği yanıt — türü belirli bir ret değildir: SDK’nın hata mesajı eksik tsp değerine ve veri arka ucunun nedeni kaydettiği, uygulamanın error-logs tablosuna işaret eder.
Tarayıcı Kullanımı (Yalnızca JavaScript)
JavaScript SDK modern tarayıcılarda çalışır. Tarayıcılarda ortam değişkenleri bulunmadığından, tüm yapılandırmayı yapıcı aracılığıyla geçin:
import { IronFlock } from "ironflock";
const ironflock = new IronFlock({
serialNumber: "device-serial-from-server",
deviceKey: "12",
appName: "MyWebApp",
swarmKey: 10,
appKey: 20,
env: "PROD",
});
await ironflock.start();
await ironflock.publishToTable("sensordata", [{ tsp: new Date().toISOString(), temperature: 22 }]);Kimlik bilgilerini sabit kodlamak yerine arka ucunuzdan yapılandırma almak için IronFlock.fromServer()’ı kullanın:
const ironflock = await IronFlock.fromServer("/api/ironflock-config");
await ironflock.start();Arka uç uç noktanız, bağlantı seçenekleriyle (serialNumber, deviceKey, appName, swarmKey, appKey, env) bir JSON nesnesi döndürmelidir.
AI Ajan Fonksiyonlarını Kaydetme
SDK, AI ajanları tarafından çağrılabilecek fonksiyonlar kaydedebilir. Bir prosedür kaydedin ve ai-template.yml dosyanızdaki konusunu referans alın:
Python
def get_sensor_reading(sensor_id):
"""Returns the latest reading from a sensor."""
reading = read_from_hardware(sensor_id)
return {
"sensor_id": sensor_id,
"temperature": reading.temp,
"humidity": reading.hum,
"timestamp": reading.ts
}
await ironflock.register_device_function("sensors.get_latest", get_sensor_reading)AI ajanı, kullanıcı canlı sensör verisi gerektiren bir soru sorduğunda bu fonksiyonu çağırabilir.
Kayıtlı WAMP konusunu bir AI ajanına bağlamak için, uygulamanızın .ironflock/ai-template.yml dosyasında referans alın:
sensor_agent:
tool_description: |
Delegate to this agent when the user asks about sensor readings,
live device data, or current environmental conditions.
system_prompt: |
You are a sensor data specialist. Use get_current to retrieve
the latest reading from any sensor. Always include the unit in
your response.
main: true
max_context_tokens: 30000
max_iterations: 5
tools:
get_current:
description: Returns the latest reading from a sensor.
topic: sensors.get_latest
parameters:
sensor_id:
type: string
description: The sensor identifier to query.
required: truetopic değeri (sensors.get_latest), edge kodunuzdaki register_device_function / registerDeviceFunction / RegisterDeviceFunction’a geçirilen adla eşleşmelidir. IronFlock, çağrıyı otomatik olarak fonksiyonun kaydedildiği cihaza yönlendirir.
Tam ai-template.yml referansı için bkz. Ajan & Araç Tanımlama.
Ortam Değişkenleri
Bu değişkenler IronFlock runtime tarafından uygulama container’ı içinde otomatik olarak ayarlanır:
| Değişken | Açıklama |
|---|---|
DEVICE_NAME | Cihaz görüntülenme adı |
DEVICE_SERIAL_NUMBER | Benzersiz, değişmez cihaz tanımlayıcısı |
DEVICE_KEY | Cihazın sayısal anahtarı. SDK, cihaz fonksiyonu URI’lerini ve uzaktan erişim tüneli etiketlerini bundan oluşturur ve onu yayımlarla ve tablo yazmalarıyla birlikte meta veri olarak gönderir; kimlik doğrulamak için onu kullanmaz |
SWARM_KEY | Proje tanımlayıcısı |
APP_KEY | Uygulama tanımlayıcısı |
APP_NAME | Uygulama adı |
ENV | Ortam: DEV veya PROD |
APP_AUTH_ID, APP_AUTH_SECRET | SDK’nın yönlendiriciye sunduğu, uygulamanın kendi kimlik bilgisi. Uygulamalar arası erişim bunu gerektirir; bu olmadan (eski bir cihaz ajanında) SDK cihaz seri numarasına geri döner |
DEVICE_ENDPOINT_URL | SDK’nın bağlandığı yönlendirici URL’si; yolu /ws-ua-usr ile değiştirilir. RESWARM_URL’ye tercih edilir; seçenek olarak geçilen bir yönlendirici URL’si (ironFlockUrl, Go’da WithURL) ikisini de geçersiz kılar |
RESWARM_URL | Studio URL’si; DEVICE_ENDPOINT_URL ayarlanmamışsa SDK yönlendirici URL’sini bundan türetir. SDK’nın tanımadığı bir studio URL’si start() / Start çağrısının başarısız olmasına yol açar. İki değişken de yoksa SDK wss://cbw.ironflock.com/ws-ua-usr adresine bağlanır |
Python
import os
device_name = os.environ.get("DEVICE_NAME")
serial = os.environ.get("DEVICE_SERIAL_NUMBER")
project_key = os.environ.get("SWARM_KEY")Uzaktan erişim değişkenleri (TUNNEL_DOMAIN, DEVICE_LAN_IP, DEVICE_PORT_FOR_<port>, REMOTE_PORT_FOR_<port>, …) Port Yapılandırma altında listelenmiştir.
Cihaz ajanı bu değerleri ayrıca /data/env altına, her biri için bir <NAME>.txt dosyası olacak şekilde yansıtır ve uygulama çalışırken bu yansımayı güncel tutar. SDK’lar, bir değişikliğin çalışan bir uygulamaya ulaşması için iki tür değeri önce dosyadan okur: her bağlantı denemesinde yeniden okunan uygulama başına kimlik bilgisi (APP_AUTH_ID, APP_AUTH_SECRET) — böylece ajanın uygulama çalışırken rotasyonla yenilediği bir kimlik bilgisi bir sonraki yeniden bağlanmada sunulur — ve getRemoteAccessUrlForPort her çağrıldığında okunan uzak portlar (REMOTE_PORT_FOR_<port> ve REMOTE_PORT_FOR_<port>_CLOUD). Diğer tüm değerler — kimlik (DEVICE_KEY, DEVICE_NAME, …), ENV ve tünel alan adları — container’ın başladığı andaki ortamdan gelir (Go, kimliği ve ENV değerini New içinde bir kez okur). Ajan /data/env dizinini yalnızca root tarafından okunabilir yapar: başka bir kullanıcı olarak çalışan bir container’da SDK, container’ın başladığı andaki değerleri görür ve bir kez uyarı kaydeder. JavaScript’te yansımayı okumak Node.js 22.3 veya üstünü gerektirir; daha eski bir Node.js yalnızca ortamı görür.