Reakcia na zmenu v prúde údajov

Page

Stránky / Nezaradené stránky /

Predstavte si, že z nejakého zdroja dostávate kontinuálny prúd údajov, ktoré sa menia len občas. Ako zabezpečiť reakciu len vtedy, keď došlo ku zmene údajov? Riešenie je jednoduchšie, než by ste čakali. Vystačíte si s jednou premennou, ktorá ukladá históriu a tú premennú porovnávate s prúdom dát.

Algoritmus

Najprv je potrebné definovať premennú, ktorá bude odkladať poslednú načítanú hodnotu. Ak budeme používať len funkcie, budeme ju definovať ako globálnu premennú. Ak sa rozhodneme pre vytvorenie objektu, definujeme ju v rámci objektu.

Premenná potrebuje nejakú inicializačnú hodnotu, ktorá by sa mala líšiť od možného rozsahu, ktorý budeme dostávať v prúde údajov. Napríklad ak budeme dostávať vstupné údaje v podobe celých čísel a údaje budú obsahovať len kladné hodnoty, môžeme ako inicializačnú hodnotu použiť -1. Potom máme istotu, že pri príchode prvého údaja bude zistený rozdiel a celý algoritmus začne pracovať.

Samotný algoritmus pracuje v niekoľkých jednoduchých krokoch:

  • Načítaj novú hodnotu.
  • Ak sa líši vočí poslednej.
    • Vykonaj akciu.
    • Ulož túto hodnotu ako poslednú hodnotu.

Príklady

Uvedieme si dva príklady. Jeden bude problematiku riešiť iba pomocou funkcii. Druhý príklad použije triedu. V oboch príkladoch sa použijú simulované dáta, ktoré sú v poli example_data.

Funkcie

Ak použijeme len funkcie, musíme si definovať globálnu premennú last_data s hodnotou -1, aby sme mali istotu, že hneď pri prvej hodnote zistí zmenu a začne pracovať. Pre každú hodnotu value zavoláme funkciu nextValue. Vo funkcii je vidno, ako sa obvykle programuje reakcia na zmenu v hodnotách. Ak sme zistili zmenu, zavoláme funkciu doAction s hodnotou a následne si poznačíme poslednú hodnotu. Vo funkcii doAction sa nachádza iba výpis hodnôt na sériový port.

Ak sa pozastavíte nad tým, prečo má funkcia nextValue návratovú hodnotu bool, je zo preto, aby sme mohli prípadne reagovať v programe na stav, kedy bola zaznamenaná zmena a takto si tú informáciu vieme odoslať naspäť do funkcie setup.

int last_value=-1;

const int example_data[]={1,1,1,2,2,2,3,3,3,4,4,4,5,6,7,8,9};
const int example_size=sizeof(example_data)/sizeof(int);

void setup() {
  Serial.begin(9600);
  Serial.println("Data Change Stream Action");

  for(int i=0;i<example_size;i++) {
    int value=example_data[i];
    Serial.print("value=");
    Serial.println(value);
    nextValue(value);
    }
}

void loop() {
}

bool nextValue(int value) {
  if(value!=last_value) {
    doAction(value);
    last_value=value;
    return true;
  }
  return false;
}

void doAction(int value) {
  Serial.print("action=");
  Serial.println(value);
}

Trieda

V tomto príklade sme preprogramovali predchádzajúci kód do triedy DataChangeAction. Z kódu je vidno, že musíme napísať trošku viac riadkov. Prečo by sme to tak mali robiť? V takomto jednoduchom príklade prednosti objektového programovania až tak nevyniknú, ale predstavte si, že by sme mali dátové prúdy dva. Ako by ste to spracovávali pomocou kódu z funkcií? Hneď by sme narazili na problém, čo s globálnou premennou last_value. V prípade objektového programovania ju máme bezpečne v triede a ak vytvoríme dva objekty DataChangeAction, môžeme bez problémov posielať jeden prúd údajov do prvého objektu a druhý do druhého.

class DataChangeAction {
  int last_value;

public:
  DataChangeAction();

  bool NextValue(int value);
  void DoAction(int value);
};

DataChangeAction::DataChangeAction()
  : last_value(-1)
  {}

bool DataChangeAction::NextValue(int value) {
  if(value!=last_value) {
    DoAction(value);
    last_value=value;
    return true;
  }
  return false;
}

void DataChangeAction::DoAction(int value) {
  Serial.print("action=");
  Serial.println(value);
}

const int example_data[]={1,1,1,2,2,2,3,3,3,4,4,4,5,6,7,8,9};
const int example_size=sizeof(example_data)/sizeof(int);

void setup() {
  Serial.begin(9600);
  Serial.println("Data Change Stream Action");

  DataChangeAction dca;
  
  for(int i=0;i<example_size;i++) {
    int value=example_data[i];
    Serial.print("value=");
    Serial.println(value);
    dca.NextValue(value);
    }
}

void loop() {
}

Výsledok

Z oboch programov dostanete identický výsledok, ktorý ukazuje, že program naozaj reaguje iba pri zmene hodnôt.

Data Change Stream Action
value=1
action=1
value=1
value=1
value=2
action=2
value=2
value=2
value=3
action=3
value=3
value=3
value=4
action=4
value=4
value=4
value=5
action=5
value=6
action=6
value=7
action=7
value=8
action=8
value=9
action=9

Zdrojový kód

Ak si chcete programy vyskúšať, oba nájdete na GitHub.


30.06.2016


Menu