5.1. Test správnosti adresovania RPC alebo SBA

Volanie RPC v D2000 obsahuje viacero úrovní adresácie:

  • HOBJ procesu, na ktorý sa správa doručí (EVH, HIP, JAPI Session),
  • HOBJ Event-u, na ktorom je RPC definovaná (v prípade JAPI Session má hodnotu 0),
  • Číslo inštancie Event-u v prípade inštančného Event-u alebo 0,
  • „use Java" – logická hodnota rozlišujúca, či je RPC definovaná v D2000 Java Environment,
  • HOBJ ESL Interface objektu, v ktorom je RPC deklarovaná v prípade implementácie interface procedúry alebo 0 v prípade, že je RPC deklarovaná priamo,
  • meno RPC.

Zoznam parametrov nie je súčasťou adresy, ale pokiaľ sa RPC zavolá s iným počtom alebo typmi parametrov, ako je deklarovaná, ide o chybu.

V prípade volania SBA je adresácia jednoduchšia (vypadli „use Java", HOBJ ESL Interface a parametre volania majú implicitnú štruktúru).

Vzhľadom na dynamickú povahu konfigurácie aplikácie v D2000 nie je možné zaručiť, že bude RPC vždy dostupná na rovnakom mieste. Aktuálny stav je však možné zistiť volaním metód D2Session.testRPCD2Session.testSBA.

Pozor: Metódy zisťujú stav v momente zavolania (resp. doručenia žiadosti na príslušný proces) a tento stav sa môže v reálnom čase zmeniť. V prípade kladnej odpovede sa preto na túto skutočnosť do budúcnosti nedá celkom spoľahnúť. Negatívna odpoveď je však dobrý mechanizmus ako detegovať, že klientská aplikácia nemá uspokojené všetky svoje požiadavky voči D2000 aplikácii.

Nasledujúci príklad demonštruje test, či je možné zavolať:

  • RPC s názvom setData,
  • s parametrami:
    1. vstupný parameter typu INT (meno parametra nie je dôležité),
    2. vstupný parameter typu RECORD NOALIAS(SD.Data),
    3. vstupno-výstupný parameter typu BOOL,
  • implementovaný v ESL časti Event-u E.Service,
  • bežiaci v procese SELF.EVH,
  • nie je to inštančný Event, ani RPC neimplementuje ESL Interface.

Budúci výsledok vo forme Future je uložený v premennej upcomingResult.

FutureEvent<Integer> upcomingProcessHobj =
    D2SessionUtils.getFutureObjectHobjByName(session, "SELF.EVH");

FutureEvent<Integer> upcomingEventHobj =
    D2SessionUtils.getFutureObjectHobjByName(session, "E.Service");

int structureDefinitionHobj = session.getStructDefInfo("SD.Data").getHobj();

FutureEvent<Boolean> upcomingResult = session.testRPC(
    false,
    upcomingProcessHobj.get(),
    upcomingEventHobj.get(),
    0, // eventInstanceId
    0, // ESL interface HOBJ
    "SetData",
    new RpcParameterType[]
    {
        RpcParameterTypeFactory.createParameter(false, UnivalType.integer),
        RpcParameterTypeFactory.createParameter(false, structureDefinitionHobj),
        RpcParameterTypeFactory.createParameter(true, UnivalType.bool)
    });

5.2. Čítanie dát z archívu

V nasledovnom príklade je uvedená statická metóda, ktorá umožní asynchrónne čítanie dát z archívu. Pre jednoduchosť sú vypustené niektoré parametre prístupu do archívu. Maximálny počet prečítaných záznamov z archívu je možné limitovať parametrom limitDataLength volania getArchiveValues. Hodnota parametra -1 znamená, že čítanie je bez obmedzenia. Nie je však možné prečítať viac záznamov, ako sa zmestí do pamäte procesu arc.exe.

V príklade je názorne použitá trieda AdjustableFuture, ktorá implementuje rozhranie java.util.concurrent.Future a zároveň umožňuje asynchrónne nastaviť hodnotu ako aj pridať asynchrónneho spracovateľa hodnoty. Taktiež je v príklade použitý objekt typu ObjectInfoCache, ktorý po zvolenú dobu udržiava preklady názvov objektov na ich HOBJ.

public static FutureEvent<ArchiveResult> loadArchiveData(
        final D2Session session,
        final IndexedObjectName indexedObjectName,
        final Date beginTime,
        final Date endTime,
        final int limitDataLength) {

    final AdjustableFuture<ArchiveResult> result = new AdjustableFuture<>();

    final ObjectInfoCache cache = session.getConnector().getDefaultObjectInfoCache();

    final FutureEvent<Integer> futureHobj
            = cache.getHobj(indexedObjectName.getObjectName(), session);

    futureHobj.addEventHandler(
            new HobjHandler(result, session, indexedObjectName, beginTime, endTime,
                    limitDataLength));

    return result;
}

Trieda ArchiveResult obaľuje prečítané hodnoty a popis archívneho objektu.

public class ArchiveResult {

    private final ArchiveObjectDescription description;
    private final List<UnivalValue<?>> values;

    private ArchiveResult(
            List<UnivalValue<?>> values,
            ArchiveObjectDescription description) {
        this.values = values;
        this.description = description;
    }

    public ArchiveObjectDescription getDescription() {
        return description;
    }

    public List<UnivalValue<?>> getValues() {
        return values;
    }
}

Trieda HobjHandler spracuje udalosť získania HOBJ na základe mena objektu. V prípade neúspechu indikuje neúspech aj do výsledku pôvodného volania – objekt result. V prípade úspechu spustí čítanie dát z archívu so zvolenými parametrami.

private static class HobjHandler
        implements FutureEventHandler<Integer> {

    private final Date beginTime;
    private final Date endTime;
    private Integer hobj;
    private final IndexedObjectName indexedObjectName;
    private final int limitDataLength;
    private final AdjustableFuture<ArchiveResult> result;
    private final D2Session session;

    public HobjHandler(
            AdjustableFuture<ArchiveResult> result,
            D2Session session,
            IndexedObjectName indexedObjectName,
            Date beginTime,
            Date endTime,
            int limitDataLength) {
        this.result = result;
        this.session = session;
        this.indexedObjectName = indexedObjectName;
        this.beginTime = beginTime;
        this.endTime = endTime;
        this.limitDataLength = limitDataLength;
    }

    @Override
    public void onException(Throwable error) {
        result.setException(error);
    }

    @Override
    public void onResult(Integer hobj) {
        this.hobj = hobj;
        session.getArchiveValues(hobj,
                indexedObjectName.getRowIndex(),
                indexedObjectName.getColumnIndex(),
                beginTime,
                endTime,
                0,
                limitDataLength,
                new DataListener());
    }

Spracovanie dát z archívu je implementované v triede DataListener, ktorá je vnorenou triedou triedy HobjHandler. Implementuje rozhranie ArchiveDataListener2, ktoré je rozšírením rozhrania ArchiveDataListener.

Archív ako prvé pošle popis archívneho objektu. Následne posiela načítané dáta v balíkoch maximálne po 1000 záznamoch, ktoré sú zoradené vzostupne podľa časovej značky. Posledný balík dát je indikovaný hodnotou true parametra noMoreData. Vtedy je možné čítanie ukončiť a výsledok uložiť do premennej result.

Prípadnú chybu indikuje archív chybovým kódom. Chyba môže nastať kedykoľvek pred doručením posledného balíka dát – t.j. na začiatku aj po prijatí jedného alebo viacerých balíkov.

V prípade, že zoznam prijatých hodnôt z archívu bol orezaný podľa parametra limitDataLength – v archívnej databáze sa v časovom intervale od beginTime po endTime nachádzajú ešte nejaké ďalšie hodnoty – tak bude pred posledným balíkom dát zavolaná metóda onMoreDataInArchive. Vtedy je možné v čítaní pokračovať nadväzujúcou požiadavkou na archív.

private class DataListener
        implements ArchiveDataListener2 {

    private ArchiveObjectDescription description = null;
    private boolean moreDataInArchive = false;
    private boolean skipFirst = false;
    private final List<UnivalValue<?>> values = new ArrayList<>();

    @Override
    public void onArchiveData(UnivalValue<?>[] data, boolean noMoreData) {
        List<UnivalValue<?>> list = Arrays.asList(data);
        if (this.skipFirst) {
            list = list.subList(1, data.length);
            this.skipFirst = false;
        }
        values.addAll(list);
        if (noMoreData) {
            if (this.moreDataInArchive) {
                this.moreDataInArchive = false;
                this.skipFirst = true;
                session.getArchiveValues(hobj,
                        indexedObjectName.getRowIndex(),
                        indexedObjectName.getColumnIndex(),
                        data[data.length - 1].getValueTime(),
                        endTime,
                        0,
                        limitDataLength,
                        this);
            } else {
                result.setValue(new ArchiveResult(values, description));
            }
        }
    }

    @Override
    public void onArchiveObjectDescription(ArchiveObjectDescription description) {
        this.description = description;
    }

    @Override
    public void onError(D2JapiErrorCode errorCode) {
        result.setException(new D2JapiException(errorCode));
    }

    @Override
    public void onMoreDataInArchive() {
        this.moreDataInArchive = true;
    }
}



Napíšte komentár