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.8+ |
| JavaScript | ironflock on npm | Node.js 18+ veya modern tarayıcı |
| Go | github.com/RecordEvolution/ironflock-go | Go 1.27.2+ |
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 a table operation rides out a platform restart (optional)
)| Parametre | Açıklama |
|---|---|
mainFunc | Bağlantı kurulduktan sonra çalışan bir async fonksiyon |
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 | Bir tablo işleminin, bir yeniden başlatmanın ardından hata yükseltmeden önce platformun geri gelmesini beklediği saniye sayısı. Varsayılan 60; 0 beklemeyi kapatır (bkz. Bağlantı Güvenilirliği) |
Veri Yayımlama
publishToTable / publish_to_table
Bir filo tablosuna veri kaydı yayımlar. 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.
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 yayımlanmış bir satırı düşürür ve bunun tek izi uygulamanın error-logs tablosundaki bir girdidir — onaylanan yayımlama başarılı görünür. Depolama yetersizliği nedeniyle reddettiği bir satır ise bu iz bile olmadan düşürülür. Bir satırın saklanıp saklanmadığını bilmeniz gerektiğinde appendToTable / append_to_table kullanın.
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
Yayımla/abone ol yerine uzak prosedür çağrısı kullanarak filo tablosuna veri ekler. Verinin kalıcı hale getirildiğine dair onay gerektiğinde bunu kullanın: reddedilen bir satır bir hatadı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) yayımlar. 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. publishToTable gibi bu da gönder-unut prensibiyle çalışır — onay, veritabanı eklemesini değil, yönlendiriciye teslimi doğrular.
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 — publishToTable’da olduğu gibi yayımlayana tek söz söylenmeden düşürülür.
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. Ekleme sonucuna ihtiyacınız olduğunda bunu publishRowsToTable / publish_rows_to_table yerine tercih edin. 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
# Fire-and-forget (default): publishes to the error-logs table
await ironflock.report_error("Sensor read timed out", level="warn")
# Pass an exception to capture its traceback (falls back to the message)
try:
risky_operation()
except Exception as err:
await ironflock.report_error(err)
# Use the append RPC when you want to await the insert outcome, and show the
# operator a friendlier line than the raw error
await ironflock.report_error(
"Calibration failed: offset 0.42 out of range",
append=True,
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", "warn", "info" veya "debug". Varsayılan "error" |
append | bool / boolean, isteğe bağlı | true olduğunda append RPC kullanılır (ekleme sonucunu döndürür). Varsayılan false (fire-and-forget yayımlama) |
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,LevelWarn,LevelInfo,LevelDebugdeğerlerinden biridir ve döndürülen*Result,Appendayarlanmadıkçanil’dir.
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.
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 |
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
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. 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)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. 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)
await ironflock.subscribe("com.myapp.alerts", on_alert)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 mesaj yönlendiricisi üzerinden gelen diğer her şey | SECRET_PLACEHOLDER olarak dışa aktarılan __secret__ yer tutucusu (Go’da ironflock.SecretPlaceholder) |
| SQL erişimi (FleetDB Access Postgres oturumu) | 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.
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, tek bir farkla: 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.
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. Gizli sütunu olmayan bir tablo URI_NOT_A_SECRET_COLUMN ile, bir gizli sütun üzerindeki filtre ise URI_SECRET_COLUMN ile reddedilir.
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.
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 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.
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).
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.
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ı ile ç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(); Start’tan önce de alınabilen bir *filestore.FileStore) üzerinden erişilen özel bir nesne depolama alanı edinir. 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 yeniden denetler. 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 |
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. 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 yeniden denetlenir. 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. 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 ü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ı bekler (Python ve Go’da en fazla 10, JavaScript’te 6 saniye) 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, DEV veya PROD olur. 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). Kayıt her yeniden bağlanmadan sonra geri yüklenir.
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.
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 (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 | publishToTable / publish_to_table, reportError / report_error, publishRowsToTable / publish_rows_to_table |
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.
Değerler /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) |
| Metot | Açıklama |
|---|---|
run() | Bağlantıyı başlatır ve mainFunc’ı çalıştırır (engelleme) |
await start() | Bağlantıyı asenkron olarak başlatır |
await stop() | Bağlantıyı ve tüketilen uygulama bağlantılarını durdurur ve çalışan görevleri iptal eder. Bağlantılar realm’lerinden ayrılır ve sonrasında yeniden bağlanmaz; start() yeniden bağlanır. Hâlâ bekleyen bir start() veya connect_to_app başarısız olur (bkz. Bağlantı Güvenilirliği) |
await run_async() | Bağlantıyı başlatır ve asenkron olarak çalıştırmaya devam eder |
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. 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 — yazmalar, geçmiş, seri ve gizli değer okumaları ile tüketilen bir uygulamanın geçmiş 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 ve 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. Dosya çağrıları yeniden denenmez (bkz. Dosya Depolama Hataları). 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. Go’da böyle bir istek — ve beklemesi içinde bağlantı bulamayan her işlem — wamp.ErrNotConnected’ı (Stop’tan sonra wamp.ErrStopped’u) saran bir hatayla başarısız olur. Go’nun diğer işlemleri (Publish, Subscribe, Call, cihaz fonksiyonları, uygulamalar arası keşif) bir bağlantı için en fazla 10 saniye bekler ve yeniden denenmez.
Ö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(), bir bağlantı için süren her beklemeyi sona erdirir. Ö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. 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. stop() ile yarışan bir ret, kendi CrossAppAccessError hatasını korur. stop() döndüğünde bağlanmaya çalışan hiçbir şey kalmaz; ayrılma isteğine yanıt vermeyen bir router’ın bağlantısı 5 saniye sonra kesilir.
Go’da Stop(ctx) de bu beklemeleri wamp.ErrStopped’u saran hatalarla sonlandırır: hâlâ bekleyen bir Start bu hatayı döndürür, hâlâ açılmakta olan bir ConnectToApp ise ironflock: connection to app '<app>' (<stage>) aborted by Stop ile (bağlantı yine de katıldığında closed by Stop ile) başarısız olur. Stop, ayrılmayı ctx içinde bekler ve ardından soketi kapatır; Run ona 10 saniye tanır.
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
Her SDK metodu hatayı açıkça bildirir: 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). Hiçbir şey sessizce yutulmaz; bu nedenle hayatta kalmasını istediğiniz çağrıları bir try bloğuna sarın — Go’da döndürülen hatayı denetleyin.
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 platformdan kaynaklanan hatalar WampError örnekleridir — 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ı. 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’tan sonra).
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. Veri arka ucunun reddettiği yayımlanmış bir satır hatasız düşürülür — bunu yalnızca append metotları bildirir.
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 veya gizli sütunu olmayan bir tablo üzerinde revealSecrets / reveal_secrets |
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 | Kimlik doğrulama için cihaz anahtarı |
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 |
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 önce burayı okur, böylece çalışma zamanında değişen değerler, örneğin bir uzaktan erişim portu, çalışan bir uygulamaya ulaşır. Uygulama başına kimlik bilgisi her bağlantı denemesinde yeniden okunur, böylece ajanın uygulama çalışırken rotasyonla yenilediği bir kimlik bilgisi bir sonraki yeniden bağlanmada sunulur. 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.