Veri Arka Ucu
IronFlock, TimescaleDB tarafından desteklenen her proje için özel bir veritabanı sağlar. Uygulamanız veri şemasını tanımlar; IronFlock tabloları oluşturur ve uygulamaya bir cihaz eklenir eklenmez veri toplamaya başlar.
Nasıl Çalışır
- Veri şemanızı
.ironflock/data-template.ymliçinde tanımlayın. - Edge kodunuzdan veri yayımlamak için IronFlock SDK’sını kullanın.
- IronFlock, uygulamanın kurulu olduğu her projedeki veritabanı tablolarını otomatik olarak ayarlar.
- Veriler cihazlardan mesajlaşma sistemi aracılığıyla proje veritabanına akar.
Her proje kendi fiziksel veritabanını alır — projeler arasında veri paylaşımı yoktur.
Kullanıcı, projelerinde uygulamanız tarafından toplanan veriler üzerinde tam kontrole sahiptir. Geliştirici olarak bu verilere erişiminiz yoktur.
Veri Şemasını Tanımlama
.ironflock/ dizininde bir data-template.yml dosyası oluşturun:
data:
tables:
- tablename: sensordata
columns:
- id: tsp
name: Timestamp
description: Timestamp of measurement
path: args[0].timestamp
dataType: timestamp
- id: temperature
name: Temperature
description: Temperature reading in Celsius
path: args[0].temperature
dataType: numeric
- id: humidity
name: Humidity
description: Relative humidity percentage
path: args[0].humidity
dataType: numeric
- id: device_id
name: Device ID
description: Source device identifier
path: args[0].device_id
dataType: stringSütun Seçenekleri
| Alan | Açıklama |
|---|---|
id | İç sütun tanımlayıcısı (zaman damgası sütunları için tsp kullanın) |
name | Panolarda gösterilen, okunabilir sütun adı |
description | İsteğe bağlı açıklama |
path | Yayımlanan veri nesnesindeki değerin yolu (örn. args[0].temperature) |
dataType | Şunlardan biri: timestamp, numeric, string, boolean |
secret | Bu sütunu şifrelenmiş olarak saklar ve normal bir okumada asla açık şekilde döndürmez — aşağıdaki Gizli Sütunlar bölümüne bakın |
Gizli Sütunlar
Bazı değerlerin saklanması gerekir ama asla gösterilmemelidir: bir API belirteci, bir cihaz parolası, bir lisans anahtarı. Sütunu secret: true ile işaretleyin; veri arka ucu onu ekleme sırasında şifreler:
- tablename: credentials
columns:
- id: tsp
dataType: timestamp
- id: device_id
dataType: string
- id: api_token
dataType: string
secret: trueBundan sonra hiçbir olağan okuma düz metni döndürmez — onu yazan uygulamaya bile:
| Okuma yolu | Geri aldığınız şey |
|---|---|
| Panolar, widget’lar ve mesaj yönlendiricisi üzerinden gelen diğer her şey | __secret__ yer tutucusu |
| SQL erişimi (FleetDB Access Postgres oturumu) | saklanan şifreli metin, ifsec:1:… |
| SDK’nın açığa çıkarma fonksiyonu | şifresi çözülmüş değer |
NULL her yerde NULL olarak kalır; böylece “atanmış ama gizli” ile “hiç yazılmamış” birbirinden ayırt edilebilir kalır.
Bir sırrı açık şekilde geri okumak yalnızca uygulamanın kendi kapsayıcılarından, SDK aracılığıyla mümkündür — SDK referansındaki Gizli Sütunlar bölümüne bakın. Panolar ve diğer uygulamalar bu değeri hiçbir şekilde elde edemez; “asla açık şekilde gösterilmez” ifadesini yalnızca göstermelik değil de gerçek kılan budur.
Bir satırı sırrını kaybetmeden güncelleme. Bir varlık tablosunda, bir satırı düzenleyen istemci gerçek sırrı asla geri gönderemez — okumalar ona yalnızca yer tutucuyu vermiştir. Bu nedenle veri arka ucu, yazma sırasında __secret__ yer tutucusunu (ve panoların gösterdiği •••••••• maskesini) önceki değeri koru olarak yorumlar; atlanan bir gizli sütun da değeri korur. Açıkça null göndermek sırrı temizler. Dolayısıyla bir pano formunda bir makinenin açıklamasını düzenlemek, erişim kodunu asla silmez.
Bundan iki sonuç çıkar: __secret__ ve •••••••• sabit dizeleri kendileri gizli değer olarak saklanamaz (ve ham ifsec:… şifreli metni girdi olarak reddedilir) ve varlık anahtarı olmayan bir tabloda yer tutucu yazımı reddedilir — korunacak önceki bir satır yoktur.
Baştan planlamaya değer üç sonuç vardır, çünkü bunlar birer öneri değil kesin sınırlardır:
- Gizli sütunlar yalnızca dize (
string) olabilir. Şifreleme metin ürettiği içinnumeric,booleanvetimestampsütunları gizli olamaz. Zorunlutspsütunu da gizli olamaz. - Bir gizli sütuna göre filtreleme, gruplama veya sıralama yapamazsınız. Her satır kendi rastgele değeriyle şifrelenir; dolayısıyla aynı sırrı tutan iki satır farklı şifreli metin saklar. Bir gizli sütun üzerinde eşitlik filtreleri,
GROUP BY,ORDER BYveDISTINCTdüpedüz çalışamaz. “Bu değer eşleşiyor mu?” sorusunu yanıtlamak için, herhangi bir şey döndürmek yerine karşılaştırmayı veri arka ucunun içinde yapan SDK’nın doğrulama fonksiyonunu kullanın. - Bir gizli sütun varlık anahtarı olamaz. Her satırın şifreli metni farklı olduğundan, varlık başına en son satırı veren okumalar her satırı ayrı bir varlık sayardı; bu nedenle böyle bir sütunun
maintainLatestFlagForiçinde adlandırılması doğrudan reddedilir.
Son iki kısıtlama, veri şablonunuz doğrulanırken denetlenir; dolayısıyla bunları ihlal eden bir tablo, sonradan hatalı davranmak yerine yayımlama sırasında başarısız olur.
Tablo Seçenekleri
columns dışında bir tablo, nasıl tanımlandığını ve verisinin nasıl yaşlandığını denetleyen birkaç isteğe bağlı anahtar kabul eder:
data:
tables:
- tablename: sensordata
description: Üretim sahasından ortam ölçümleri
chunkTimeInterval: 1 hour
dropAfter: 30 days
columns:
# ...| Alan | Açıklama |
|---|---|
tablename | Tablonun adı |
description | İsteğe bağlı açıklama; arayüzde gösterilir ve yapay zekâ ajanları tarafından tabloyu anlamak için kullanılır |
chunkTimeInterval | Tablonun bölündüğü zaman bölümlerinin boyutu. Varsayılan 7 days |
dropAfter | Saklama penceresi — bundan eski bölümler otomatik olarak silinir |
downsample | Uzun pencereli grafiklerin hızlı çalışması için önceden toplulaştırılmış bir kopya tutar — aşağıdaki Sürekli Aşağı Örnekleme bölümüne bakın |
maintainLatestFlagFor | Benzersiz bir varlığı tanımlayan sütunlar — aşağıdaki Bir Varlığın En Son Durumunu Takip Etme bölümüne bakın |
private | Bu tabloyu diğer uygulamalardan gizler — aşağıdaki Verileri Diğer Uygulamalarla Paylaşma bölümüne bakın |
chunkTimeInterval, zaman serisi verisinin diskte nasıl bölümleneceğini belirler. Bir bölümün, tek seferde sorguladığınız veri miktarına yaklaşık olarak denk gelmesini sağlayacak bir değer seçin: saniyede bir toplanan yüksek frekanslı veriler küçük parçalardan (dakikalar–saatler), yavaş değişen veriler ise büyük parçalardan (haftalar) fayda görür. Bu yalnızca uygulamanın varsayılanıdır — proje sahibi daha sonra kendi data backend’inde bunu değiştirebilir.
dropAfter, tabloyu kayan bir pencereye dönüştürür. Belirtilen aralıktan eski bölümler bütün olarak silinir; bu, satırları tek tek silmekten çok daha ucuzdur. Temizleme işi dropAfter / 4 aralığıyla çalışır, dolayısıyla bir kayıt bölümü kaldırılana kadar süresini aralığın dörtte biri kadar aşabilir. Verileri süresiz saklamak için dropAfter’ı belirtmeyin.
Her ikisi de PostgreSQL aralık ifadeleri alır — 30 minutes, 1 hour, 7 days, 6 months.
Sürekli Aşağı Örnekleme
Panolar, veritabanından veriyi toplulaştırmasını isteyebilir — saatlik ortalamalar, günlük toplamlar, makine başına sayımlar. Bunları ham kayıtlardan hesaplamak bir gün için sorunsuzdur, bir yıl için pahalıdır. Bir tabloya downsample ekleyin; platform o tablonun sürekli güncellenen, önceden toplulaştırılmış bir kopyasını tutar ve uzun pencereli sorguları bunun yerine o kopyadan yanıtlar:
data:
tables:
- tablename: sensordata
dropAfter: 30 days
downsample:
bucket: 1 minute
keepFor: 2 years
paths:
- payload.temperature
columns:
# ...| Alan | Açıklama |
|---|---|
bucket | Önceden toplulaştırılmış kopyanın çözünürlüğü. Varsayılan 1 minute |
keepFor | Aşağı örneklenmiş geçmişin ne kadar saklanacağı. Süresiz saklamak için belirtmeyin |
paths | Dahil edilecek JSON alan yolları; panoların kullandığı gösterimin aynısı |
bucket, bir grafiğin sunulabileceği en ince çözünürlüktür — bundan çok daha ince aralıklar isteyen bir grafik onun yerine ham tabloyu okur. Bir günü tam bölen, 1 second ile 1 day arasındaki sabit genişlikli aralıkları kabul eder (1 minute, 5 minutes, 1 hour). Varsayılan 1 minute pratikte her panoya uygundur; daha kaba bir aralık daha az depolama ve daha az yazma yükü demektir.
keepFor, uzun geçmişleri en baştan mümkün kılan şeydir. Ham kayıtlar dropAfter ile silinir, ancak aşağı örneklenmiş kopyanın kendi saklama süresi vardır: ham veriyi 30 gün, aşağı örneklenmiş veriyi 2 yıl saklayın; bir pano depolamanın küçük bir bölümüyle yine de iki yıllık saatlik ortalamaları grafikleyebilir. Bunu dropAfter’dan daha uzun ayarlayın — platform tersini hatalı yapılandırma sayarak reddeder.
paths, aşağı örneklemeyi JSON sütunlarının içindeki değerlere de genişletir. Sayısal sütunlar otomatik olarak dahil edilir; JSON alanlarının ise açıkça adlandırılması gerekir, çünkü bir JSON sütununun sabit bir anahtar kümesi yoktur. Bildirilmemiş alanlar panolarda yine de çalışır — yalnızca ham tablodan hesaplanırlar.
Geri kalan her şey otomatiktir. Her sayısal sütunun istatistikleri (ortalama, toplam, minimum, maksimum, ilk, son ve kayıt sayısı), tablonun varlık anahtarına (maintainLatestFlagFor ya da yayımlayan cihaz) göre gruplanarak sürdürülür. Panoların ne bir yapılandırmaya ne de bundan haberdar olmaya ihtiyacı vardır: widget her zamanki gibi sorgular ve platform her sorguda önceden toplulaştırılmış kopyanın yanıt verip veremeyeceğine karar verir — veremediğinde, örneğin bir filtre kopyanın gruplamadığı bir sütuna atıfta bulunduğunda, şeffaf biçimde ham tabloya geri düşer.
Şema değişiklikleri kopyayı yeniden oluşturur. Aşağı örneklenmiş bir tabloya sütun eklemek, sütun kaldırmak veya bir sütunun tipini değiştirmek — ya da
downsamplebloğunun kendisini düzenlemek — önceden toplulaştırılmış kopyayı ham tablodan yeniden oluşturur.dropAfter’dan eski olan hiçbir şey yeniden kurulamaz ve kaybolur. Bu bloğu mümkün olduğunca tabloyla birlikte kurun ve uzun ömürlü tablolardaki sonraki şema değişikliklerini bilinçli bir karar olarak ele alın.
Edge Kodundan Veri Yayımlama
Uygulamanızdan veri göndermek için IronFlock SDK’sını kullanın:
Python
from ironflock import IronFlock
flock = IronFlock()
flock.publish_to_table("sensordata", {
"timestamp": "2025-01-15T10:30:00Z",
"temperature": 23.5,
"humidity": 62.1,
"device_id": "sensor-001"
})Yüksek frekanslı veriler için, satır başına bir gidiş-dönüş yerine publish_rows_to_table / publishRowsToTable (gönder-unut) ya da append_rows_to_table / appendRowsToTable (ekleme sonucunu döndürür) kullanarak tek bir mesajda birden çok satır gönderin. Her yığın atomik olarak eklenir — ya hep ya hiç. Ayrıntılar için bkz. SDK referansı.
Dönüşüm Tabloları
Ham verilerinizi otomatik olarak toplayan veya işleyen SQL dönüşümleri tanımlayabilirsiniz:
data:
tables:
- tablename: sensordata
columns:
# ... ham veri sütunları ...
transforms:
- tablename: hourly_averages
materialize: true
schedule: "0 * * * *"
sql: >
SELECT
time_bucket('1 hour', tsp) AS hour,
avg(temperature) AS avg_temp,
avg(humidity) AS avg_humidity
FROM sensordata
GROUP BY hour
columns:
- id: hour
name: Hour
dataType: timestamp
- id: avg_temp
name: Average Temperature
dataType: numeric
- id: avg_humidity
name: Average Humidity
dataType: numeric| Alan | Açıklama |
|---|---|
tablename | Türetilmiş tablonun adı |
materialize | true ise sonuçlar tablo olarak kalıcılaştırılır |
schedule | Dönüşümün ne zaman çalıştırılacağı için cron ifadesi |
sql | Dönüşümü hesaplayan SQL sorgusu |
columns | Çıktı için sütun tanımları |
Dönüşüm tabloları, tıpkı normal tablolar gibi panolarda ve SDK aracılığıyla erişilebilir.
Bir dönüşüm tam olarak kendi SQL’inin döndürdüğü gibi okunur: widget zaman pencereleri, takvim filtreleri ve latest modu tablolar için geçerlidir ve bir dönüşüm için yok sayılır. Bundan üç kural çıkar. Zaman aralığını sorgunun içinde sınırlayın (WHERE tsp > now() - interval '7 days'), bir zaman serisini en yeniden en eskiye sıralayın (ORDER BY <time column> DESC) ki bir satır sınırı en yeni satırları korusun ve bir panonun çizmesi veya filtrelemesi gereken her sütunu seçin — bir dönüşümün örtük zaman damgası veya cihaz sütunları yoktur. Tek bir okuma en fazla 3000 satır döndürür; bu nedenle toplulaştırmayı sorguda yapın.
Bir dönüşüm elde etmenin tek yolu onu uygulamayla birlikte göndermek değildir: Veri Erişimi yetkisine sahip bir proje üyesi aynı türden bir dönüşümü, hiçbir uygulama olmadan projenin veri görünümünden kaydedebilir; AI asistanı da bunu yapabilir. Bkz. Özel Dönüşümler.
Bir Varlığın En Son Durumunu Takip Etme
Gerçek dünya varlıklarının mevcut durumunu temsil eden tablolar için — makineler, varlıklar, üretim emirleri — IronFlock en son durum takibi adı verilen bir deseni destekler.
Bir şey değiştiğinde satırın üzerine yazmak yerine, her zaman yeni bir satır eklersiniz. Benzersiz bir varlığı hangi sütunların tanımladığını siz bildirirsiniz; IronFlock da tablo her okunduğunda varlık başına en son satırı türetir. Bu, her değişikliğin tam geçmişini sağlarken yalnızca mevcut durumu sorgulamayı da kolaylaştırır.
maintainLatestFlagFor ile bir tabloda etkinleştirin:
- tablename: machineform
maintainLatestFlagFor: ['machinename']
columns:
- id: tsp
dataType: timestamp
- id: machinename
dataType: string
- id: machinetype
dataType: string
- id: active
dataType: boolean
- id: description
dataType: stringmaintainLatestFlagFor, birlikte benzersiz bir varlığı tanımlayan sütunların listesini alır. Satırın kendisine hiçbir şey yazılmaz: IronFlock tabloyu bu varlık anahtarına ve zaman damgasına göre indeksler ve sorgu anında varlık başına en yeni satırı seçer. Bu nedenle geç ya da sırasız gelen bir satır, geride asla eskimiş bir işaret bırakamaz.
Bir Varlığı Kısmi Bir Satırla Güncelleme
Bir varlık tablosuna eklenen satır, o varlığın yeni bir sürümüdür — ve eksiksiz olmak zorunda değildir. Satırın sağlamadığı sütunlar, varlığın önceki en son satırından devralınır; dolayısıyla tek bir alanı güncellemek, yalnızca varlık anahtarını, bir zaman damgasını ve o alanı yayımlamak anlamına gelir:
| Yeni satırın sütunu | Saklanan sürümün tuttuğu |
|---|---|
sağlanmışsa (0, false ve "" sağlanmış sayılır) | sağlanan değer |
açıkça null ise | NULL — sütun temizlenir |
| yoksa | önceki en son satırdaki değer |
Bilinmeye değer iki ayrıntı vardır:
- Tüm sütunları sağlayan bir satır, önceki satırı arama adımını tamamen atlar; dolayısıyla eksiksiz satırlar her zaman olduğu kadar ucuz kalır — yüksek frekanslı yollarda eksiksiz satırlar göndermeye devam edin.
- Devralma zamana saygı gösterir: daha eski bir zaman damgasıyla gelen bir satır (geriye dönük bir doldurma), yalnızca kendi
tspdeğerine eşit ya da ondan önceki satırlardan devralır, asla daha yeni olanlardan değil.
maintainLatestFlagFor içermeyen tablolar düz ekleme semantiğini korur: satırın sağlamadığı bir sütun NULL olarak saklanır.
Yalnızca mevcut makine durumlarını sorgulamak için:
SELECT DISTINCT ON (machinename) *
FROM machineform
ORDER BY machinename, tsp DESCBelirli bir makinenin tam geçmişini görüntülemek için:
SELECT * FROM machineform WHERE machinename = 'Assembly-Line-01' ORDER BY tspBu sorguyu elle yazmanız nadiren gerekir. Bu tabloya bağlanan panodaki widget’ların filtre ayarlarında bir latest anahtarı bulunur; böylece kullanıcılar herhangi bir ek çalışma olmadan her zaman güncel değerleri görür. SDK’dan aynı modu, filterAnd içine {"latest": true} ekleyerek isteyebilirsiniz — bkz. getHistory.
latest_flag’ten yükseltme: IronFlock’un önceki sürümlerilatest_flagadında fiziksel bir boolean sütunu saklıyordu. Bu sütun artık mevcut değil — mevcut durum bunun yerine SQL’de türetiliyor; bu da satırlar sırasız geldiğinde bile sonucun doğru kalmasını sağlıyor.latest_flag = trueile filtreleyen mevcut panolar ve SDK çağrıları çalışmaya devam eder: IronFlock bunları tanır ve en son durum modunu uygular. Yeni kodlar latest anahtarını veya{"latest": true}filtre girdisini kullanmalıdır.
Kayıtları Yumuşak Silme
IronFlock’un yalnızca ekleme modeli, kayıtların fiziksel olarak hiçbir zaman silinmediği anlamına gelir. Bunun yerine, bir kaydı kaldırılmış olarak işaretlemek için deleted boolean sütununu kullanın. Bu, tam denetim izini korurken silinen kayıtları dashboard’lardan gizler.
Herhangi bir varlık tablosuna deleted sütunu ekleyin:
- id: deleted
name: Deleted
dataType: booleanBir kullanıcı bir kaydı sildiğinde (örneğin panodaki bir form aracılığıyla), uygulamanız o varlık için deleted: true ile yeni bir satır yayımlar. maintainLatestFlagFor ile birleştiğinde, bu yeni satır en son durum olur. Sonraki kısmi satırlar, deleted işaretini diğer her sütun gibi devralır; dolayısıyla deleted sütununa değinmeyen bir güncelleme, varlığı ne yeniden ortaya çıkarır ne de gizler.
Yalnızca aktif (silinmemiş) mevcut kayıtları sorgulamak için:
SELECT * FROM (
SELECT DISTINCT ON (machinename) *
FROM machineform
ORDER BY machinename, tsp DESC
) latest
WHERE deleted IS NULL OR deleted = falsedeleted denetimi, makine başına en son satır seçildikten sonra çalışır. Bu sıra önemlidir: silinen satırları önce ayıklamak, önceki silinmemiş satırın mevcut durum olarak yeniden ortaya çıkmasına yol açar.
Pano widget’ları ve SDK aynı sırayı otomatik olarak uygular — latest anahtarını (veya {"latest": true}) bir deleted filtresiyle birleştirdiğinizde tam olarak bu davranışı elde edersiniz. Silinen kayıtlar form gönderilir gönderilmez dashboard’dan kaybolur, ancak geçmiş ve denetim amacıyla veritabanında kalır.
Verileri Diğer Uygulamalarla Paylaşma
Data backend’iniz yalnızca sizin uygulamanıza aittir: projede kurulu başka hiçbir uygulama tablolarınızı göremez. data-template.yml içindeki iki isteğe bağlı anahtar bunu değiştirir.
Başka bir uygulamanın verilerini okumak için, okumak istediğiniz uygulamaları üst düzey bir consumes: bölümünde listeleyin — data: bölümünün içinde değil, yanında:
consumes:
- app: machine-monitor
reason: "Monitörün makine durumu ve sayaç akışlarından OEE hesaplar"
data:
tables:
- tablename: oee_results
columns:
# ... her zamanki gibi uygulamanızın kendi tablolarıapp, veriyi sağlayan uygulamanın teknik adıdır; projedeki tüm uygulamalar için "*" (tırnak işaretleri zorunludur) kullanılır. reason, onay penceresinde kullanıcıya gösterilir — bildirimin kendisi, kullanıcı onaylamadıkça hiçbir erişim vermez.
Belirli tabloları paylaşım dışında tutmak için bunları private: true ile işaretleyin. Tanımladığınız her şey varsayılan olarak paylaşılabilirdir; özel bir tablo veya dönüşüm, diğer uygulamaların gördüğü katalogda hiç görünmez.
data:
tables:
- tablename: measurements # paylaşılır (varsayılan)
columns: [ ... ]
- tablename: calibration_state # dahili — diğer uygulamalara asla görünmez
private: true
columns: [ ... ]Erişim salt okunurdur, kullanıcı tarafından proje bazında verilir ve istendiği zaman geri alınabilir. Modelin tamamı ve sağlayıcı uygulamanın geçmiş ile canlı akışlarını okuyan SDK çağrıları için bkz. Diğer Uygulamaların Verilerini Kullanma.