• Ei tuloksia

Laskennan ohj aus - j ärj estelmän dynaaminen malli

5 TOTEUTETTU JÄRJESTELMÄ

5.2 Funktionaalinen laskentalogiikka

5.2.6 Laskennan ohj aus - j ärj estelmän dynaaminen malli

if (valueStatus == MOSAttribute::CIRCULAR_CALCULATION) return;

if (valueStatus == MOSAttribute::UNDER_CALCULATION) { setValueStatus(MOSAttribute: :CIRCULAR_CALCULATION);

Freturn;

}

int iterations = getType()->getRange();

setValueStatus(MOSAttribute: :UNDER_CALCULATION) ; while (1) {

turn--;

if (myRule->calculate(this) == FALSE) { setValueStatus(MOSAttribute: :RULE_FAILED) ; Freturn;

}

if ((valueStatus == MOSAttribute::CIRCULAR_CALCULATION) &&

(turn > 0)) {

Rivillä 14 käynnistetään kappaleessa 5.2.3 kuvatun laskentasäännöstä muodostetun jäsennyspuun evaluointi, jonka tulos asetetaan parametrina olevan attribuutin arvoksi.

Laskennan ohjausta varten asetetaan ennen varsinaista laskentaa rivillä 10 attribuutin tilamuuttujan arvoksi UNDER CALCULATION. Mikäli laskettavana olevan attribuutin arvoa tarvitaan laskennan aikana, asetetaan rivillä 6 ensimmäisellä kerralla tilamuuttujan arvoksi CIRCULARCALCULATION. Seuraavalla kerralla suoritus palaa välittömästi, rivi 3. Laskennan onnistuttua tarkistetaan rivillä 18 syklisen laskennan mahdollisuus.

Mikäli tilamuuttujan arvoksi oli tullut CIRCULAR CALCULATION, pitää laskenta suorittaa uudelleen. Ennen uutta laskentaa invalidoidaan kaikki riippuvat attribuutit, jotta syklisesti toisistaan riippuvat attribuutit laskettaisiin oikein. Iteraatiokierrosten maksimimäärä on lukulistan pituus, joka määräytyy attribuutin tyypin perusteella.

Laskennan jälkeen attribuutin tilamuuttujan arvoksi asetetaan VALUE OK, rivi 25.

5.2.6 Laskennan ohjaus - järjestelmän dynaaminen malli

MUST-sovelluksen suoritus etenee järjestelmään rakennetun dynaamisen mallin mukaisesti. Suoritusjärjestykseen voidaan vaikuttaa joko taulukossa 8 esitetyillä laskentasääntöjen ohj ausparametreilla tai sivuvaikutuksellisilla laskentasääntö- lausekkeilla.

Taulukko 8 Laskentasäännön ohjaus vaihtoehdot

Ohj ausvaihtoehto Selitys

WHENNEEDED Normaali käyttäytyminen, attribuutin arvo

invalidoidaan aina ja laskenta suoritetaan viivästetysti sitten, kun arvoa tarvitaan.

AFTERTRANSACTION Normaali invalidointi, mutta uusi arvo lasketaan jokaisen käyttäjän tekemän transaktion jälkeen.

BEFOREINV ALID ATION Attribuutin arvo lasketaan jokaisen invalidoinnin yhteydessä. Mikäli saatu tulos poikkeaa puskuroidusta, invalidoidaan attribuutti ja kaikki siitä riippuvat muut attribuutit. Uusi laskenta suoritetaan viivästetysti tarpeen mukaan.

WHEN CALLED NO INVALI DATION

Attribuutin arvoa ei koskaan invalidoida. Laskenta voidaan käynnistää vain funktionkutsua vastaavalla ca//-laskentasäännöllä.

MUST-kehittimellä tehty sovellus on aina täysin interaktiivinen. Järjestelmä odottaa käyttäjän käskyä, suorittaa tarvittavat toimenpiteet ja jää odottamaan seuraavaa käskyä.

Seuraavassa on käyty läpi koko toimenpidelista käyttäjän tekemästä muutoksesta näkymien päivittymiseen. C++-toteutuksesta on esitetty toimenpidelistan jälkeen tärkeimmät funktiot riisuttuina versioina.

1. Muutetaan mielivaltaisen attribuutin arvoa (esimerkiksi asetetaan instanssin Nokia tietoalkion NetSales arvoksi luku 123).

2. Attribuutin tilamuuttujan arvoksi asetetaan VALUE_OK.

3. Invalidoidaan kaikki tästä attribuutista riippuvat muut attribuutit (MOSObject::

dataChanged).

3.1 Käynnistetään takaperininvalidointi kaikissa laskentasääntöjen jäsennyspuissa alkaen niistä solmuista, joissa tämän attribuutin arvo saattoi esiintyä.

3.2 Jokainen mahdollinen reitti, jonka mukaan jokin jäsennyspuu voidaan takaperinedetä, johtaa yksikäsitteiseen laskentasääntöön ja attribuuttiin.

Käsketään saatua laskentasääntöä invalidoimaan saatu attribuutti (MOSRule::

invalidate Attribute). Seuraavat kohdat toistetaan kaikille yhdistelmille:

3.2.1 Mikäli attribuutin arvo oli jo invalidoitu tai attribuutti ei käytä invalidointia aiheuttavaa laskentasääntöä, palataan kohtaan 3.2 ja käsitellään seuraava mahdollinen attribuutti.

3.2.2 Mikäli laskentasäännön ohjaustyyli on WHEN CALLED_NO INVAL1DATION, palataan ja käsitellään seuraava.

3.2.3 Mikäli laskentasäännön ohjaustyyli on BEFORE-INVALIDATION, suoritetaan laskenta ja tarkistetaan, muuttuiko arvo. Mikäli arvo ei muuttunnut, palataan seuraavaan attribuuttiin.

3.2.5 Invalidoidaan attribuutti.

3.2.5.1 Asetetaan attribuutin tilamuuttujan arvoksi VALUE INVALID.

3.2.5.2 Kutsutaan globaalia metodia Calculator::addChangedAttribute(this), joka ylläpitää listaa invalidoiduista attribuuteista laskentaa varten.

3.2.5.3 Käynnistetään rekursiivinen invalidointi juuri muuttuneelle attribuutille, eli suoritetaan kohta 3 kokonaisuudessaan ennenkuin jatketaan.

3.3 Asetetaan invalidoituneen tai muuttuneen olion tilamuuttujan changed arvoksi TRUE. Mikäli olio esiintyy jossain näkymässä, jolloin näkymä on tehnyt attribuuttiin tarkkailuunkin, kutsutaan globaalia metodia Updator::

addChangedObject(this), joka ylläpitää listaa päivitettävistä olioista.

4. Malliin voi tulla useita muutoksia esimerkiksi tiedostojen sisäänlukemisen yhteydessä. Jokainen muutos käsitellään samalla tavalla, eli suoritetaan kohdat 1-3.

5. Käyttäjän aloittama transaktio loppuu, suoritetaan globaali metodi Updator::

Update

Views0-5.1 Kerrotaan järjestelmän osille, että päivitysrutiini on käynnistetty kutsumalla globaalia metodia EventLink::makeEvent(GLOBAL_UPDATE_STARTED).

5.2 Käynnistetään tarvittavien attribuuttien laskenta. Normaalitilanteessa kutsutaan globaalia metodia Calculator::calculateAllImmediateAttributesO, joka laskee kaikkien niiden invalidoituneiden attribuuttien arvot, joiden säännön laskentaparametri on AFTER TRANSACTION. Mikäli viivästetty laskenta on kytketty pois päältä, lasketaan lisäksi kaikkien muuttuneiden attribuuttien arvot metodin calculateAllInvalidAttributesO. Viivästettyä laskentaa ei ole kytketty pois päältä yhdessäkään asiakassovelluksessa.

5.3 Suoritetaan näkymien päivityssilmukka.

5.3.1 Kopioidaan kaikki viittaukset muuttuneisiin olioihin uuteen listaan ja tyhjennetään vanha lista.

5.3.2 Käsketään jokaista muuttunutta oliota päivittämään omat näkymänsä.

5.3.2.1 Muuttunut olio käskee jokaista näkymälinkkiään päivittämään siihen liittyneen näkymän. Näkymä pyytää oliolta uuden, muuttuneen arvon.

Mikäli arvo oli vain invalidoitu, laskee attribuutti tässä vaiheessa itselleen uuden arvon ja palauttaa sen. Laskenta voi luonnollisesti vaatia useiden välitulosten rekursiivisen laskennan. Uusia invalidointeja voi tapahtua vain siinä tilanteessa, että laskentasäännöissä on käytetty sivuvaikutuksellisia sijoituslauseita.

5.3.2.2 Olio palauttaa kohdassa 3.3 asetetun tilamuuttujansa Changed arvoksi FALSE.

5.3.3 Tuhotaan kaikki laskentasääntöjen sivuvaikutuksien perusteella tuhottaviksi tuomitut instanssit.

5.3.4 Mikäli sivuvaikutukselleen laskentasääntöjen takia uusia olioita ja näkymiä pitää päivittää, suoritetaan silmukka uudelleen palaamalla kohtaan 5.3.1.

5.4 Kerrotaan järjestelmän osille, että päivitysrutiini on ohi kutsumalla globaalia metodia EventLink::makeEvent(GLOBAL_UPDATE_OVER). Kutsu aiheuttaa tarvittaessa hierarkianäkymien päivityksen ja päivityspaketin lähettämisen Excel-käyttöliittymäsovellukselle.

6. Käyttäjän transaktio on käsitelty ja järjestelmä jää odottamaan seuraavaa käskyä.

void MOSObj ect: :dataChanged() {

if ( views.empty() == FALSE ) Updator: :addChangedObject(this);

} }

void MOSObject;;updateViews() { DO(views, ViewLink, vl)

vl->updateData();

END

changed = FALSE;

}

void MOSRule:;invalidateAttribute(MOSAttribute *dcAttr) { if ( (dcAttr->getValueStatus() == MOSAttribute;:VALUE_OK) ||

(dcAttr->getValueStatus() == MOSAttribute::RULE_FAILED) ) { if (dcAttr->getRule() == this) {

if (getCalculationMode() = = WHEN_CALLED_NO_INVALIDATION)) { return;

} else if (getCalculationMode() == BEFORE_INVALIDATION) { CalcValue *old_val = new CalcValue(dcAttr->getParent());

CalcValue *new_val = new CalcValue(dcAttr->getParent());

dcAttr->makeCalcValue(*old_val,0,0);

dcAttr->calculate() ;

dcAttr->makeCalcValue(*new_val,0,0);

boolean value_did_not_change =

((old_val->applyDoubleOperator(CalcValue::EQ, *new_val)) &&

(old_val->getType() == cv_number) &&

(old_val->val.num == 1));

delete old_val;

delete new_val;

if (value_did_not_change) return;

}

dcAttr->setValueStatus(MOSAttribute: : CALCULATE);

dcAttr->invalidateDependents();

} } }

void Updatori:updateViews() {

EventLink::makeEvent(EventLink::GLOBAL_UPDATE_STARTED);

if (prefs->useDelayedCalculations() == TRUE) { Calculator::calculateAllInvalidAttributes();

} else {

Calculator::calculateAlllmmediateAttributes();

}

while (changedObjects->count() + doomedObjects->count() > 0) { SymbList previousChanged(changedObjects);

changedObjects->reset();

L_DO(previousChanged,MOSObject,nObj)

nObj->updateViews();

L_END

L_FOR_FIRST_DO(doomedlnstances, MOSInstance, inst) inst->free();

doomedlnstances->removeO(inst);

L_END

}

updateTurnNumber++;

updateClassBrowser();

EventLink : :makeEvent(EventLink: :GLOBAL_UPDATE_OVER) ;

}