rva (1 days)
Tak snad nějaké závěry pro nás, kteří nemají žádné zkušenosti v jazyce flux (ani jiném podobném skriptovacím jazyce). Bral jsem to jako výzvu, jestli z toho něco dostanu. V kódu je vysvětlení amatéra proč jsem co dělal.
Třeba se někdo najde, kdo nám dá rychlokurz jak vše funguje a jak se to má dělat správně.
// Cílem je předpřipravit si data z BMS tak, aby bylo možno posoudit v jakém stavu jsou články baterie.
// Z toho, co BMS ukládá do influxDB, budu využívat:
// 1. čísla článků s max a min napětím
// 2. balancovací proud
// 3. proud baterii
// Úvod do influxDB je na https://docs.influxdata.com/influxdb/cloud/get-started/
// Do tabulky ClanekMax si chci uložit čísla článků s max napětím - to jsou ty, ze kterých se odebírá balanční proud
// Pod tímto názvem mohu data z tabulky opakovaně používat pro opakované zpracování např. ClanekTemp = ClanekMax
// 1. Data mám v influxDB databázi ha2. Tam začnu - nejprve si do ClanekMax vyberu tuto databázi:
ClanekMax = from(bucket: "ha2/autogen")
// Výběr musím omezit na časový rozsah, symbol |> je pipe-forward operator, který posílá výstup předchozí funkce jako vstup do té následující
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
// čísla článků jsou uložena v měřeních "state", tak si vyfiltruji jenom tato měření
|> filter(fn: (r) => r["_measurement"] == "state")
// v těchto měřeních jsou čísla článků uložena ve "field" ktere se jmenuje "value", tak předchozí výsledek dál filtruji
|> filter(fn: (r) => r["_field"] == "value")
// a opět zužuji výběr na "domain" "sensors"
|> filter(fn: (r) => r["domain"] == "sensor")
// a tam mám čísla článků uložena pod "entity_id" "jk_bms_sklep_max_voltage_cell"
|> filter(fn: (r) => r["entity_id"] == "jk_bms_sklep_max_voltage_cell")
// V influxDB jsou záznamy jako časové řady, pro další zpracování si je převedu do tabulky s časovým oknem po 5s
|> aggregateWindow(every: 5s, fn: distinct)
// ponechám si v tabulce jenom dva sloupce.
|> keep(columns: ["_value", "_time"])
// Pokud si chci prohlédnout tento mezivýsledek, odkomentuji následující řádek, stisknu tlačítko "Run Script"
// a nahoře vpravo si stiskem "View Raw Data" přepínám výsledek mezi grafem a tabulkou (řada dat se třeba ani jako graf nedá zobrazit).
//|> yield(name: "CMax")
// Teď mám tabulku ClanekMax s čísly článků + časem a tato data mohu používat pro opakované zpracování např. ClanekTemp = ClanekMax
// Do tabulky ClanekMin si obdobně uložím články, na kterých bylo min. napětí - do těchto článků tedy teče balanční proud
ClanekMin = from(bucket: "ha2/autogen")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "state")
|> filter(fn: (r) => r["_field"] == "value")
|> filter(fn: (r) => r["domain"] == "sensor")
|> filter(fn: (r) => r["entity_id"] == "jk_bms_sklep_min_voltage_cell")
|> aggregateWindow(every: 5s, fn: distinct)
|> keep(columns: ["_value", "_time"])
//|> yield(name: "CMin")
// Do tabulky Balance si obdobně uložím balancovací proud + čas
Balance = from(bucket: "ha2/autogen")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "A")
|> filter(fn: (r) => r["_field"] == "value")
|> filter(fn: (r) => r["domain"] == "sensor")
|> filter(fn: (r) => r["entity_id"] == "jk_bms_sklep_balancing_current")
|> aggregateWindow(every: 5s, fn: first)
|> keep(columns: ["_value", "_time"])
//|> yield(name: "Balance")
// Do tabulky Current si uložím jaký proud tekl celou baterií
Current = from(bucket: "ha2/autogen")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "A")
|> filter(fn: (r) => r["_field"] == "value")
|> filter(fn: (r) => r["domain"] == "sensor")
|> filter(fn: (r) => r["entity_id"] == "jk_bms_sklep_current")
|> aggregateWindow(every: 5s, fn: distinct, createEmpty: true)
|> keep(columns: ["_value", "_time"])
//|> yield(name: "Current")
C = join(tables: {ClanekMin: ClanekMin, ClanekMax: ClanekMax }, on: ["_time"])
// Ve funkci reduce si zavedu akumulátory C01 - C16, do kterých si ukládám, kolikrát je na daném článku
// ve sloupci _value_ClanekMin naměřeno minimální napětí
// Spočítá, kolikrát je v dané tabulce článek daného čísla 1-16
|> reduce(fn: (r, accumulator) => ({
C01: accumulator.C01 + int(v: (r._value_ClanekMin == 1)),
C02: accumulator.C02 + int(v: (r._value_ClanekMin == 2)),
C03: accumulator.C03 + int(v: (r._value_ClanekMin == 3)),
C04: accumulator.C04 + int(v: (r._value_ClanekMin == 4)),
C05: accumulator.C05 + int(v: (r._value_ClanekMin == 5)),
C06: accumulator.C06 + int(v: (r._value_ClanekMin == 6)),
C07: accumulator.C07 + int(v: (r._value_ClanekMin == 7)),
C08: accumulator.C08 + int(v: (r._value_ClanekMin == 8)),
C09: accumulator.C09 + int(v: (r._value_ClanekMin == 9)),
C10: accumulator.C10 + int(v: (r._value_ClanekMin == 10)),
C11: accumulator.C11 + int(v: (r._value_ClanekMin == 11)),
C12: accumulator.C12 + int(v: (r._value_ClanekMin == 12)),
C13: accumulator.C13 + int(v: (r._value_ClanekMin == 13)),
C14: accumulator.C14 + int(v: (r._value_ClanekMin == 14)),
C15: accumulator.C15 + int(v: (r._value_ClanekMin == 15)),
C16: accumulator.C16 + int(v: (r._value_ClanekMin == 16)),
}),
identity: {C01: 0, C02: 0, C03: 0, C04: 0, C05: 0, C06: 0, C07: 0, C08: 0, C09: 0, C10: 0, C11: 0, C12: 0, C13: 0, C14: 0, C15: 0, C16: 0})
|> yield(name: "Články s minimálním napětím")
// Tím jsem získal první zajímavý výsledek - tabulku s tím, kolikrát bylo na každém článku naměřeno minimální napětí.
// Dále by mě zajímalo, na kterém článku bylo naměřeno minimální napětí při vybíjení baterie (proud je menší než nula)
// Vytvořím si tabulku s články + proudem baterií
C1 = join(tables: {ClanekMin: ClanekMin, ClanekMax: ClanekMax }, on: ["_time"])
CBC1 = join(tables: {Clanek: C1, Current: Current}, on: ["_time"])
//|> yield(name: "CBC1")
// vyberu záznamy s vybíjením baterie (proud <0)
|> filter(fn: (r) => r._value < 0)
//|> yield(name: "CBC1")
// Spočítá, kolikrát je v dané tabulce článek daného čísla 1-16 při vybíjení baterie
|> reduce(fn: (r, accumulator) => ({
C01: accumulator.C01 + int(v: (r._value_ClanekMin == 1)),
C02: accumulator.C02 + int(v: (r._value_ClanekMin == 2)),
C03: accumulator.C03 + int(v: (r._value_ClanekMin == 3)),
C04: accumulator.C04 + int(v: (r._value_ClanekMin == 4)),
C05: accumulator.C05 + int(v: (r._value_ClanekMin == 5)),
C06: accumulator.C06 + int(v: (r._value_ClanekMin == 6)),
C07: accumulator.C07 + int(v: (r._value_ClanekMin == 7)),
C08: accumulator.C08 + int(v: (r._value_ClanekMin == 8)),
C09: accumulator.C09 + int(v: (r._value_ClanekMin == 9)),
C10: accumulator.C10 + int(v: (r._value_ClanekMin == 10)),
C11: accumulator.C11 + int(v: (r._value_ClanekMin == 11)),
C12: accumulator.C12 + int(v: (r._value_ClanekMin == 12)),
C13: accumulator.C13 + int(v: (r._value_ClanekMin == 13)),
C14: accumulator.C14 + int(v: (r._value_ClanekMin == 14)),
C15: accumulator.C15 + int(v: (r._value_ClanekMin == 15)),
C16: accumulator.C16 + int(v: (r._value_ClanekMin == 16)),
}),
identity: {C01: 0, C02: 0, C03: 0, C04: 0, C05: 0, C06: 0, C07: 0, C08: 0, C09: 0, C10: 0, C11: 0, C12: 0, C13: 0, C14: 0, C15: 0, C16: 0})
|> yield(name: "Články s minimálním napětím při vybíjení baterie")
// A získal jsem tabulku s tím, jak často bylo na některém článku minimální napětí když se baterie vybíjela
// Do třetice by mě zajímalo, na jakých článcích je minimální napětí při nabíjení baterie - proud > 0
// Vytvořím si tabulku s články + proudem baterií
CBC2 = join(tables: {Clanek: C1, Current: Current}, on: ["_time"])
//|> yield(name: "CBC2")
// vyberu záznamy s nabíjenímm baterie (proud > 0)
|> filter(fn: (r) => r._value > 0)
//|> yield(name: "CBC2")
// Spočítá, kolikrát je v dané tabulce článek daného čísla 1-16 při nabíjení baterie
|> reduce(fn: (r, accumulator) => ({
C01: accumulator.C01 + int(v: (r._value_ClanekMin == 1)),
C02: accumulator.C02 + int(v: (r._value_ClanekMin == 2)),
C03: accumulator.C03 + int(v: (r._value_ClanekMin == 3)),
C04: accumulator.C04 + int(v: (r._value_ClanekMin == 4)),
C05: accumulator.C05 + int(v: (r._value_ClanekMin == 5)),
C06: accumulator.C06 + int(v: (r._value_ClanekMin == 6)),
C07: accumulator.C07 + int(v: (r._value_ClanekMin == 7)),
C08: accumulator.C08 + int(v: (r._value_ClanekMin == 8)),
C09: accumulator.C09 + int(v: (r._value_ClanekMin == 9)),
C10: accumulator.C10 + int(v: (r._value_ClanekMin == 10)),
C11: accumulator.C11 + int(v: (r._value_ClanekMin == 11)),
C12: accumulator.C12 + int(v: (r._value_ClanekMin == 12)),
C13: accumulator.C13 + int(v: (r._value_ClanekMin == 13)),
C14: accumulator.C14 + int(v: (r._value_ClanekMin == 14)),
C15: accumulator.C15 + int(v: (r._value_ClanekMin == 15)),
C16: accumulator.C16 + int(v: (r._value_ClanekMin == 16)),
}),
identity: {C01: 0, C02: 0, C03: 0, C04: 0, C05: 0, C06: 0, C07: 0, C08: 0, C09: 0, C10: 0, C11: 0, C12: 0, C13: 0, C14: 0, C15: 0, C16: 0})
|> yield(name: "Články s minimálním napětím při nabíjení baterie")
// Tím jsem získal třetí zajímavý výsledek - kde je nejnižší napětí, když se baterie nabíjí
// obdobně mohu získat řadu dalšách dat třeba o článcích s max. napětím, popřípadě s přeléváním energie při balancování
Výsledky jsem exportoval jako csv a graf udělal v excelu.
Vlastní kód je určitě neoptimální, zpracování 24 hodin záznamu z JK-BMS trvalo cca 3 minuty.