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 |
|---|---|
| Geçmiş okumaları ve gerçek zamanlı satırlar: panoların, widget’ların, SDK’nın ve tabloyu okuyan diğer uygulamaların aldığı şey | __secret__ yer tutucusu |
| SQL (SQL sorgu konsolu, veri dışa aktarımları ve FleetDB Access Postgres oturumu) ve veritabanı yedekleri | 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()
await flock.start()
await flock.publish_to_table("sensordata", {
"timestamp": "2025-01-15T10:30:00Z",
"temperature": 23.5,
"humidity": 62.1,
"device_id": "sensor-001"
})Her satırın zorunlu tsp sütunu için bir değere ihtiyacı vardır: veri arka ucu bu değeri içermeyen bir satırı reddeder. SDK’nın tablo yazması bir hatayla başarısız olur; bir panonun yayımladığı satır ise atılır ve uygulamanın error-logs tablosunda yalnızca bir kayıt bırakır. Yukarıdaki sensordata tanımı bu sütunu path: args[0].timestamp ile eşler; bu nedenle tsp, satırın timestamp alanından gelir. path içermeyen bir sütun, satırdaki aynı adlı alanı (args[0].<id>) okur: bu eşleme olmasaydı her satırın kendi tsp alanına ihtiyacı olurdu.
Yüksek frekanslı veriler için, satır başına bir gidiş-dönüş yerine publish_rows_to_table / publishRowsToTable / PublishRowsToTable ya da append_rows_to_table / appendRowsToTable / AppendRowsToTable kullanarak tek bir mesajda birden çok satır gönderin — ikisi aynıdır ve ekleme sonucunu bildirir. 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ç saklanır ve zamanlamaya göre tazelenir. false ise dönüşüm, her okumada hesaplanan bir görünümdür |
schedule | Dönüşümün çalıştırmaları için cron ifadesi: beş alan (dakika, saat, ayın günü, ay, haftanın günü) ya da başına saniye alanı eklenmiş altı alan. */5 * * * * her 5 dakikada, */10 * * * * * her 10 saniyede bir çalışır. Her çalıştırma kalıcılaştırılmış sonucu tazeler ve güncel sonucu dönüşüme bağlı panolara gönderir. Uygulamanın kodu böyle bir çalıştırmayı talep üzerine de yapabilir, bkz. Talep Üzerine Tazeleme |
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, bir tablo okumasının ifade edemediği şeyler içindir: bir join, bir oran, birkaç tablonun mevcut satırları üzerinde bir toplulaştırma. Varlıkların mevcut durumunu göstermenin veya silinen kayıtları gizlemenin yolu değildir. Bunlar tablonun kendisinin okuma modlarıdır — latest anahtarı ve bir deleted sütunu üzerindeki bir filtre (aşağıya bakın) — ve canlı çalışırlar: tabloya bağlı bir widget her yeni satırı bir saniye içinde izler. Bir dönüşüm panoya yalnızca zamanlamasına göre ulaşır (ya da uygulamanın kodu onu kendi yazmalarından sonra tazelediğinde — bir panonun kaydetmesi bunu asla tetiklemez); bu nedenle varlık başına en yeni satırı seçen veya silinen satırları ayıklayan bir görünüm aynı sonucu geç gösterir. Bir dönüşümün bir varlık tablosunun mevcut durumuna gerçekten ihtiyacı olduğunda, örneğin onu başka bir tabloyla birleştirmek için, bunu kendi SQL’inde DISTINCT ON ile seçin ve deleted denetimini, Kayıtları Yumuşak Silme altında gösterilen sırayla orada uygulayı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.
Kalıcılaştırılmış Bir Dönüşüm Ne Zaman Tazelenir
Zamanlanmış bir çalıştırma, kalıcılaştırılmış bir dönüşümü yalnızca okuduğu tablolardan birine son tazelemesinden bu yana yazılmışsa tazeler; bundan bağımsız olarak da en az 10 dakikada bir tazeler. Okuyacak yeni bir şeyi olmayan bir çalıştırma, saklanan sonucu olduğu gibi bırakır: saatte bir satır alan bir tablo üzerinde her dakika çalışacak şekilde zamanlanmış bir dönüşüm, o satırdan sonra, bunun dışında da 10 dakikada bir tazelenir.
Kalıcılaştırılmış bir dönüşüm, sonucu bir yazma olmadan değişebiliyorsa her çalıştırmada tazelenir:
- SQL’i geçerli zamanı veya rastgeleliği kullanıyorsa, örneğin
now(),CURRENT_DATEveyarandom(); - aynı girdi için farklı bir sonuç döndürebilen bir fonksiyon çağırıyorsa: volatile (değişken) bir fonksiyon ya da PostgreSQL’de yerleşik olmayan stable (kararlı) bir fonksiyon;
- uygulama tabloları ve kalıcılaştırılmış dönüşümler dışında bir şey okuyorsa, örneğin kalıcılaştırılmamış bir dönüşüm, aşağı örneklenmiş bir kopya ya da platformun kendi tablolarından biri.
Aynı anda çok sayıda dönüşümün çalışma zamanı geldiğinde platform her seferinde birkaçını tazeler, diğerleri sıralarını bekler; bu nedenle bir tazeleme, planlanandan daha geç bitebilir. Aynı dönüşümün önceki tazelemesi hâlâ çalışırken veya beklerken zamanı gelen bir çalıştırma atlanır.
Talep Üzerine Tazeleme
Uygulamanın kendi kodu, zamanlamasını beklemeden bir dönüşümün çalıştırmasını yapabilir: SDK’nın refresh_transform / refreshTransform / RefreshTransform metodu kalıcılaştırılmış sonucu tazeler (sade bir görünüm yalnızca okunur) ve güncel sonucu dönüşüme bağlı panolara gönderir; böylece bir pano, uygulamanın az önce yazdığı bir yığını hemen gösterir. Zamanlanmış bir çalıştırma için geçerli olan kurallar aynen geçerlidir: dönüşümün okuduğu tablolardan hiçbirine son tazelemeden bu yana yazılmamışsa tazeleme atlanır (ve atlandığı bildirilir), aynı dönüşümün bir tazelemesi çalışırken gelen bir çağrı onun arkasında sıraya alınmış olan tazelemeyi bekler ve dönüşüm başına saniyede en fazla bir tazeleme başlar. SDK’nın tablo yazmaları geri dönmeden önce commit edilir; bu nedenle bir yazmanın ardından gelen bir tazeleme o yazmayı asla kaçırmaz. Bir uygulamanın dönüşümlerini yalnızca kendi kapsayıcıları tazeleyebilir; bir pano satırları alır ve başka bir uygulamanın verilerini tüketen bir uygulama onun dönüşümlerini okur ama tazeleyemez. Projenin veri görünümünden kaydedilen dönüşümler hiçbir uygulamaya ait değildir ve yalnızca zamanlamalarına göre çalışır. Bkz. refreshTransform / refresh_transform.
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 tablosu, varlık ve zaman damgası başına tek bir satır tutar. Saklanan bir satırın hem varlık anahtarını hem de tsp değerini tekrarlayan bir satır reddedilir; iki satırın bunu yaptığı bir yığın da reddedilir: SDK’nın yazması URI_ENTITY_KEY_CONFLICT hatasıyla başarısız olur ve uygulamanın error-logs tablosu reddi kaydeder. Bir varlığın her sürümüne kendi tsp değerini verin. Varlık anahtarı sütunu boş (NULL) olan bir satır hiçbir varlığa ait değildir: asla tekrar olarak reddedilmez ve en son durum okumaları onu dışarıda bırakır.
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 |
Bir sütunu boşaltmak için onu null olarak gönderin (Python’da None, Go’da nil). Sütunu dışarıda bırakmak önceki değeri korur. Bir alanı istemeden dışarıda bırakan koda dikkat edin: örneğin değeri undefined olan bir JavaScript özelliği veri yükünden düşer. SDK başvurusu bu durumları dile göre listeler.
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 ve onu bir dönüşüm olarak saklamamalısınız. 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. Anahtar canlıdır: widget, tablonun her yeni satırını geldiği anda alır ve varlığın önceki satırını onunla değiştirir; böylece bir değişiklik yazıldıktan sonra bir saniye içinde görünür, tablo ise her sürümü saklar. Hiçbir şey bir zamanlamaya göre yeniden hesaplanmaz — bir dönüşümün ekleyeceği tek şey de bu olurdu. 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.
Varlık Anahtarını Değiştirme
Bir uygulama sürümü yayımlandıktan sonra, tablolarının her birinin varlık anahtarı sabitlenir. Bulut IDE’nin yan panelindeki Veri Yapısını Güncelle ve yeni bir sürümün derlemesi, uygulamanın en yeni sürümünde bulunan bir tablonun varlık anahtarını değiştiren bir data-template.yml dosyasını reddeder:
maintainLatestFlagForlistesine bir sütun eklemek, birini kaldırmak, birini başka bir sütunla değiştirmek veya sütunların sırasını değiştirmek;- daha önce
maintainLatestFlagForiçermeyen bir tabloya onu eklemek ya da onu kaldırmak.
Böyle bir değişiklik, uygulamanın kurulu olduğu her projede tablonun döndürdüğü şeyi sessizce değiştirirdi. Anahtara yeni eklenen bir sütun mevcut her satırda boştur ve en son durum okumaları anahtar sütunu boş olan satırları dışarıda bırakır; dolayısıyla mevcut tüm varlıklar kaybolurdu. Anahtardan kaldırılan bir sütun, satırları aynı zaman damgasını paylaşan varlıkları birleştirebilir; oysa tablo varlık ve zaman damgası başına yalnızca bir satır tutabilir.
Farklı bir varlık anahtarı kullanmak için yeni anahtarla yeni bir tablo oluşturun ve verileri, örneğin uygulamanızın kodundan, bu tabloya taşıyın. Verileri taşımak uygulamanın işidir. Henüz hiçbir sürümde bulunmayan bir tablonun varlık anahtarı, geliştirme sırasında hâlâ değiştirilebilir. Bu kural yürürlüğe girmeden önce yayımlanmış bir uygulama sürümü bir varlık anahtarını hâlâ değiştirebilir: o sürüme güncellenen bir proje veri yapısı güncellemesini reddeder ve öncekini korur.
Varlığı ve Zaman Damgasını Tekrarlayan Satırlar
IronFlock varlık ve zaman damgası başına tek satır kuralını uygulamaya başlamadan önce yazılmış tablolar hâlâ tekrarlar içerebilir; varlık anahtarını değiştirdiğiniz bir geliştirme tablosu da öyle. Veri hizmeti böyle bir tabloyu kurarken, yani başlangıcında veya bir veri yapısı güncellemesinden sonra, tekrarlanan her gruptaki tüm satırları aynı veri arka ucundaki <table>__entitykey_dupes adlı bir tabloya taşır ve her gruptan bir satırı geri koyar. Bundan sonra tablo tekrarları reddeder. Uygulamanın error-logs tablosundaki bir kayıt, kaç satırın taşındığını belirtir.
Yeni tablo, taşınan her satırı olduğu gibi, iki ek sütunla birlikte saklar: taşıma zamanı olan quarantined_at ve geri konan satırı işaretleyen restored. Hangi satırın geri konduğu, en son durum okumalarında zaten olduğu gibi keyfidir. Tabloyu projenin veri görünümündeki sorgu konsolunda databackend_<key>.<table>__entitykey_dupes olarak sorgulayın. Özgün tablo özel ise yeni tablo da özeldir. Satırlar taşınamazsa error-logs kaydı nedenini belirtir ve veri hizmeti bir sonraki başlangıcında yeniden dener.
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.
Panoda bir form, bir kaydı deleted sütununu adlandıran Delete-Flag Column ayarı aracılığıyla siler: form silme sırasında true, her kaydetmede ise false yazar; böylece silinmiş bir kaydı kaydetmek onu geri getirir (bkz. Veri Silme). Kayıtları listeleyen widget’lar tablonun kendisini filtreler. Listeyi temizlemek için hiçbir görünüm veya dönüşüm gerekmez; böyle bir şey bir silmeyi ancak bir sonraki zamanlanmış çalıştırmasında gösterirdi.
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.
Ayrıcalıklı Erişim
Bir uygulamanın container’ları cihazdan yalıtılmış olarak, Docker’ın varsayılan haklarıyla çalışır. Bazı uygulamalar daha fazlasına ihtiyaç duyar: bir seri port veya USB adaptörü, GPIO, bir CAN veya Bluetooth arayüzü, host ağı. Buna ayrıcalıklı erişim denir. Ayrıcalıklı erişime sahip bir uygulama, diğer uygulamalar ve verileri dahil olmak üzere cihazın tamamını kontrol edebilir; bu yüzden uygulamanızın bunu talep etmesi gerekir ve verilip verilmeyeceğine kullanıcı karar verir.
Talep etmek için, üst düzeye reason içeren bir privileged: bölümü ekleyin — data: bölümünün içine değil, yanına:
privileged:
reason: "PLC'yi /dev/ttyUSB0 seri portu üzerinden okur"
data:
tables:
# ... her zamanki gibi uygulamanızın kendi tablolarıreason zorunludur. Onay penceresinde kullanıcıya gösterilir; bu yüzden onu, uygulamanıza güvenip güvenmeyeceğine karar veren biri için yazın.
Tablosu olmayan bir uygulama privileged yanına data: {} yazar: data: anahtarı olmayan bir data-template.yml yayımlanamaz.
Kullanıcının neye izin verdiği, uygulamanın nasıl oluşturulduğuna bağlıdır:
- Tek imajlı bir uygulama (yalnızca bir
Dockerfile) kullanıcı izin verdiğinde ayrıcalıklı modda çalışır: tüm Linux yetenekleri (capabilities), cihazın/devdizini ve host’un D-Bus’ı ile. Bildirim olmadan host’un cihazları, ek yetenekler ve host’un D-Bus’ı olmadan çalışır; sürüm yine de yayımlanır ve derleme günlüğü bu konuda uyarır. - Bir Docker Compose uygulaması tam olarak
docker-compose.ymldosyasının istediği ayarları alır ve onay penceresi bunları listeler. Compose dosyası aşağıdaki ayarlardan herhangi birini kullanıyorsa uygulamaprivilegedbildirmelidir; aksi halde sürümün yayımlanması, servisi ve ayarı belirten bir hatayla başarısız olur. Compose dosyası bunların hiçbirini kullanmıyorkenprivilegedbildirmek de başarısız olur.
Şu compose ayarları ayrıcalıklı sayılır:
| Ayar | Ayrıcalıklı sayıldığı durum |
|---|---|
privileged | false dışındaki her değer |
cap_add, devices, device_cgroup_rules | bir girdisi olduğunda |
security_opt | no-new-privileges dışında bir girdisi olduğunda |
network_mode, pid, ipc, uts, userns_mode, cgroup | host veya container:… olarak ayarlandığında |
cgroup_parent | bir değer atandığında |
bir servisin volumes ayarı | bir host yolunu (/…, ~/…, C:\…), uygulama klasörü dışındaki bir yolu (../…) veya bir named pipe’ı bağladığında. Salt okunur /etc/localtime ve /etc/timezone sorun değildir. |
volumes_from | container:… belirttiğinde |
üst düzey volumes | bir volume’ün driver_opts, external veya sabit bir name değeri olduğunda |
üst düzey networks | bir ağ bridge dışında bir driver (örneğin macvlan, ipvlan veya host) ya da driver_opts belirlediğinde, external olduğunda veya sabit bir name değeri olduğunda |
üst düzey secrets, configs | bir girdi uygulama klasörü dışındaki bir file dosyasını okuduğunda |
Adlandırılmış volume’ler, varsayılan bridge sürücüsünü kullanan ağlar, uygulama klasörü içindeki bind mount’lar (./config:/config), yayımlanan portlar ve GPU rezervasyonları (gpus, deploy.resources.reservations.devices) ayrıcalıklı değildir.
Bu ayarlardan birinde ${VAR} yer tutucusu kullanmak serbesttir ve ayrıcalıklı sayılır; yani bunun için de bildirim gerekir. Bir uygulama parametresi kullanıcıların seri portu seçmesini böyle sağlayabilir:
services:
plc:
build: .
devices:
- "${SERIAL_PORT}:/dev/ttyS0"include veya file içeren extends kullanan bir compose dosyası, bildirim olsun ya da olmasın yayımlanamaz: bir sürüm yalnızca docker-compose.yml dosyasını taşır. Diğer dosyaları bu dosyada birleştirin.
Uygulamanın ihtiyacından fazlasını talep etmeyin. Uygulama tek bir seri porta ihtiyaç duyuyorsa, tek imaj veya privileged: true yerine tek bir servis ve devices: [/dev/ttyUSB0] içeren bir compose dosyası yazın. Kullanıcı uygulamanın tam olarak ne aldığını görür ve uygulama bundan fazlasını yapamaz.
Onay ve Geri Alma
- Kurulumda kullanıcı
reasonmetninizi ve compose uygulamaları için ayarların listesini görür, ayrıcalıklı erişimi açar veya kapatır. Reddetmek kurulumu asla engellemez. - Bir güncellemede, yeni sürüm ayrıcalıklı erişim talep ediyorsa ve proje buna izin vermemişse güncelleme penceresi sorar. Reddedilse bile uygulama güncellenir; yeni sürüm bu durumda ayrıcalıklı erişim olmadan çalışır.
- Sonrasında ayrıcalıklı erişim, projenin uygulama ayarlarında bir anahtardır; kullanıcılar onu orada istedikleri zaman geri alabilir veya verebilir. Çalışan cihazlar uygulamayı yeni ayarla yeniden başlatır.
Cloud IDE’den başlatılan geliştirme modu bildirime uyar ve bir anahtarı yoktur: .ironflock/data-template.yml privileged bildiriyorsa geliştirme derlemesi ayrıcalıklı çalışır, bildirmiyorsa ayrıcalıklı erişim olmadan çalışır. Compose dosyası bildirim olmadan ayrıcalıklı ayarlar kullanıyorsa geliştirme derlemesi, yayımlamadaki hatanın aynısıyla başarısız olur.
İzin Verilmediğinde
Uygulama yine de başlar. Tek imajlı bir uygulama Docker’ın varsayılanlarıyla çalışır: /dev yok, ek yetenek yok, host’un D-Bus’ı yok. Bir compose uygulamasında cihaz ajanı, uygulamayı başlatmadan önce ayrıcalıklı ayarları compose dosyasından kaldırır ve hangi servisten hangi ayarları kaldırdığını uygulamanın loguna yazar. Kodunuzun, erişemediği şeyleri (örneğin açamadığı bir seri portu) çökmek yerine bildirmesini sağlayın.
Önceki Sürümler
Uygulamaların ayrıcalıklı erişimi bildirmesi zorunlu olmadan önce yayımlanan sürümler, bu erişimi talep ediyormuş gibi sayılır ve mevcut kurulumlar bu erişimi korur; böylece bu uygulamalar eskisi gibi çalışır. Kullanıcılar, diğer tüm uygulamalarda olduğu gibi, projenin uygulama ayarlarından bu erişimi geri alabilir. Bir sonraki sürümünüzden itibaren, uygulamanın ihtiyacı varsa privileged bildirin.