MODSIM II
- procesowy język symulacyjny zorientowany obiektowo
Krystyna KOLEŚNIK
Streszczenie. W artykule przedstawiono język MODSIM przeznaczony do programowania symulacji i animacji. Język jest zorientowany na programowanie obiektowe i symulację procesową. Posiada bogatą bibliotekę obiektów symulacyjnych i graficznych. Opisano cechy języka MODSIM jako języka algorytmicznego ogólnego przeznaczenia oraz jego udogodnienia wspomagające wykonywanie standardowych funkcji programów symulacyjnych i animację modelu. Opisano także możliwości języka i zasady programowania symulacji procesowej. Przedstawiono sposób definiowania graficznej postaci modelu oraz zasady łączenia jej z programem symulacyjnym. Opis języka uzupełniono uwagami o wykorzystywaniu systemu do zajęć dydaktycznych z programowania symulacji.
Stowa kluczowe: symulacja, animacja, programowanie obiektowe, MODSIM
1. Wstęp
Programowanie zorientowane obiektowo oparte na definicji klas obiektów, dziedziczeniu, hermetyzacji obiektów i polimorfizmie wprowadzono po raz pierwszy w procesowym języku symulacyjnym Simula ponad 25 lat temu (1967) [8]. Jednak dopiero w ostatnich latach mechanizmy programowania zorientowanego obiektowo (OOP) zaczęto szeroko wprowadzać w językach ogólnego przeznaczenia (Pascal, C++, CLOSS, EIFELL - już w ponad stu językach). Rozwijane są także metody obiektowo zorientowanej analizy (OOA) oraz obiektowo zorientowanych: analizy i projektowania systemów informatycznych (OOAD) [2]. Wegner [10] przewiduje, że metodologia projektowania i programowania zorientowanego obiektowo stanie się dominującą metodą lat dziewięćdziesiątych.
Powstał również nowy język symulacyjny MODSIM, zorientowany na symulację procesową;
opartą na programowaniu zorientowanym obiektowo, wyprodukowany przez CACI Products Company - firmę specjalizującą się w tworzeniu oprogramowania przeznaczonego do programowania i definiowania symulacyjnych modeli systemów rzeczywistych. MODSIM jest uniwersalnym językiem do programowania zorientowanego obiektowo i może być stosowany nie tylko do zastosowań symulacyjnych (4, 5]. Na system obsługi języka MODSIM II składają się:
~ program zarządzający systemem (MODBENCH) zawierający edytor tekstu,
~ kompilator języka MODSIM wykorzystujący jezyk C++ do zapisu postaci pośredniej programu,
2
. edytor graficzny SIMDRAW (9] służący do definiowania elementów graficznych wykorzystywanych przez program symulacyjny,
~ pakiet graficzny SIMGRAPHICS II obsługujący animację on-lute pracy modelu symulacyjnego oraz prezentację graficzną wyników pracy modelu,
v pakiet SIMVIDEO służący do rejestracji on-line i odtwarzania animacji off lirce, ~ pakiet uruchamiający OBJECT DEBUGGER.
System MODSIM zaimplementowano dla systemów operacyjnych OS2 i UNIX. Najnowsza wersja dla komputerów IBM PC jest zrealizowana dla środowiska Windows. Oprócz podstawowych struktur danych i instrukcji algorytmicznego języka programo
wania wysokiego poziomu, język symulacyjny dostarcza udogodnień, które wspomagają wykonywanie standardowych funkcji programów symulacyjnych. Należą do nich według Naylora [7):
~ możliwość opisu statycznej i dynamicznej struktury modelu, v ustalanie stanu początkowego modelu,
~ mechanizm upływu czasu, ~ organizacja grup obiektów,
~ generowanie liczb pseudolosowych,
~ przeprowadzanie analizy statystycznej wyników,
~ wyprowadzanie wyników według żądanych formatów, ~ mechanizmy do wykrywania błędów.
Współczesny język symulacyjny powinien umożliwiać budowę modeli modularnych oraz dostarczać dodatkowo:
~ mocnych standardowych typów obiektów, posiadających standardowe atrybuty aktualizowane automatycznie,
v mechanizmów upraszczających programowanie komunikacji użytkownika z programem, zbliżonych do stosowanych w innych językach ogólnego przeznaczenia,
~ udogodnień do tworzenia graficznej postaci modelu, przeznaczonej do animacji pracy modelu symulacyjnego na bieżąco,
~ łatwych mechanizmów do łączenia modelu symulacyjnego systemu z jego modelem graficznym,
~ udogodnień do analizy i uruchamiania programów.
Dodatkowo język symulacyjny powinien wymuszać na programiście stosowanie systematycznej metody budowy modelu, której stosowanie przyczynia się do szybszego i łatwiejszego konstruowania poprawnych programów symulacyjnych.
Język MODSIM II spełnia prawie wszystkie wymienione powyżej wymagania. W kolejnych punktach opisano:
~ charakterystykę języka MODSIM (w zakresie cech języka programowania ogólnego przeznaczenia),
~ udogodnienia do tworzenia modeli symulacyjnych dostarczane przez język MODSIM. ~ udogodnienia do komunikacji z użytkownikiem i tworzenia graficznej postaci modelu.
2. MODSIM jako język programowania ogólnego przeznaczenia
Język MODSIM dostarcza wszystkich mechanizmów znanych z języków algorytmicznych ogólnego przeznaczenia. Jego składnia wzorowana jest na składni języków, takich jak: C, Modula i Pascal. Poniżej opisano elementy języka przez porównanie ich z odpowiednikami w języku Pascal.
W identyfikatorach języka, tworzonych według składni języka Pascal, są rozróżniane małe i wielkie litery. Słowa kluczowe są pisane wielkimi literami. Komentarze mogą być umieszczane pomiędzy parami nawiasów: { ~ i (* *) i mogą być zagnieżdżone. Stałe w jęryku MODSIM mogą być określane przez wyrażenia, w których skład wchodzą stałe wcześniej zdefiniowane.
Język MODSIM dostarcza standardowych typów, takich jak: INTEGER, REAL, CHAR, BOOLEAN i STRING. Typ INTEGER jest zgodny z paskalowym typem LONGINT. Typ STRING jest dynamiczny. W język MODSIM wbudowano wiele procedur i funkcji dla standardowych typów (w tym dotyczących konwersji typów). W wyrażeniach arytmetycznych i logicznych można używać standardowych operatorów arytmetycznych i logicznych według zasad obowiązujących w Pascalu.
Użytkownik może definiować proste typy własne, takie jak: typ okrojony (do 256 wartości) i typ wyliczeniowy oraz typy strukturalne (typ tablicowy, rekordowy i obiektowy), a także typy proceduralne. Aby przy wywołaniach procedur ułatwić zgodność typów, zdefiniowano typy referencyjne ANYREC i ANYOBJ, które mogą zastępować odpowiednio dowolny typ rekordowy i dowolny typ obiektowy. Przy deklaracji typu tablicowego określa się wymiarowość tablicy (przez podanie typu wymiaru tablicy) oraz typ bazowy. Tablice można deklarować jako tablice wielowymiarowe lub jako tablice tablic. Wymiary tablic są określane przy generowaniu egzemplarza tablicy. Dla tablicy tablic można wygenerować tablicę, której elementy strukturalne różnią się liczbą elementów bazowych (tablica powycinana). Tablice i rekordy statyczne w sensie Pascala są deklarowane jako FIXED ARRAY i FIXED RECORD. Zasady tworzenia selektorów elementów tablicy i pól rekordu są takie same, jak w Pascalu dla statycznych tablic i rekordów. Zmienne typów rekordowych mogą być porównywane ze względu na równość i nierówność.
Zmiennej typu proceduralnego można przypisać identyfikator procedury, a następnie wywoływać tę procedurę instrukcją CALL z tą zmienną w roli argumentu.
Zbiór instrukcji języka obejmuje pełny zbiór instrukcji języka Pascal, rozszerzony o pewne instrukcje z języka Modula, z nieco zmienioną składnią. Instrukcja przypisania zmiennych typów dynamicznych ma znaczenie przypisania referencji. Każda instrukcja strukturalna (z wyjątkiem REPEAT) ma ograniczony zakres działania przez kończące ją słowo END ze słowem kluczowym instrukcji lub bloku (na przykład END IF).
W instrukcjach warunkowych pominięto słowo THEN. Instrukcja warunkowa występuje w wersji niepełnej, pełnej (ze słowem ELSE) oraz pełnej z warunkiem zagnieżdżonym (ELSIF) z dowolną liczbą zagnieżdżeń. Instrukcja CASE pomija słowo OF i używa słowa kluczowego WHEN poprzedzającego listę etykiet i słowa OTHERWISE zastępującego słowo ELSE. Selektorem instrukcji CASE może być także, oprócz typów porządkowych, napis typu STRING.
W języku MODSIM występują cztery instrukcje iteracyjne: WHILE, REPEAT, FOR i LOOP. Instrukcja FOR (w której pominięto słowo DO) zawiera dodatkowo wielkość kroku (BY step), o który modyfikuje się zmienną sterującą pętli. Instrukcja pętli LOOP pozwala na konstruowanie pętli nieskończonej. Z zakresu tej pętli, a także z innych pętli, można jednak wyjść przez zawieszenie wykonywania i ponownie powrócić do miejsca zawieszenia. Przy zawieszeniu stan atrybutów obiektu pozostaje zachowany do czasu powrotu z zawieszenia lub dopóki nie zostanie zmieniony przez inną metodę tego samego obiektu. Instrukcja WAIT, powodująca zawieszenie, realizuje upływ czasu i umożliwia symulację procesów obiektów, których fragmenty są wykonywane naprzemiennie.
Parametry procedur i funkcji mają określony rodzaj. Rozróżnia się trzy rodzaje parametrów poprzedzane w wykazie parametrów procedury odpowiednim słowem kluczowym: IN parametry wejściowe, OUT parametry wyjściowe, INOUT parametry wejściowo-uryjściowe. Powrót z procedury na żądanie wykonuje się przy użyciu instrukcji RETURN. Za pomocą instrukcji RETURN następuje też przypisanie wartości w procedurach funkcyjnych.
W języku MODSIM możliwa jest też pełna obsługa operacji wejścia-wyjścia (I/O). Posiada on wbudowane nieobiektowe procedury do bezformatowego I/O oraz procedury formatowanego wyprowadzania danych tekstowych. Strumienie I/O są obsługiwane przez typ obiektowy StreamObj zawierający metody do otwierania i zamykania pliku skojarzonego z obiektem oraz odrębne dla każdego typu zmiennej, jednoargumentowe procedury czytania i pisania. W procedurach pisania, podobnie jak w Pascalu, można podać format wyprowadzanych liczb. Dostępne są też standardowe procedury dotyczące operacji na plikach umożliwiające otwarcie, zamknięcie, skasowanie, sprawdzenie istnienia i podanie rozmiaru pliku. Każdy obiekt typu StreamObj posiada dwa pola: eof sygnalizujące koniec pliku i ioResult sygnalizujące stan wykonania ostatniej operacji I/O. Na podstawie typu StreamObj zdefiniowano typ FileObj do obsługi indeksowanych sekwencyjnych plików opierających się na kluczu określonym w rekordzie.
Programy w języku MODSIM mogą być podzielone na moduły umieszczane w odrębnych plikach. Rozróżnia się dwa typy modułów: moduły definicyjne zawierające definicje stałych, typów i zmiennych oraz moduły implementacyjne zawierające treść metod, zdefiniowanych dla typów obiektowych w module definicyjnym. Program główny oraz moduły muszą być umieszczane w plikach o nazwach modułu z dodaną odpowiednią pierwszą literą: D - dla modułów definicyjnych, I - dla modułów implementacyjnych i M - dla programu głównego. Nazwy modułów implementacyjnych muszą być zgodne z nazwami modułów definicyjnych. Wszystkie pliki programu symulacyjnego mają rozszerzenie .MOD.
Program główny i moduły są blokami ograniczonymi przez słowa kluczowe określające typ modułu (MAIN, DEFINITION lub IMPLEMENTATION MODULE) i słowa END MODULE.
W module definicyjnym można definiować dowolną liczbę typów obiektowych. Wszystkie metody jednego obiektu, zadeklarowane w module definicyjnym, w odpowiednim module implementacyjnym są zawarte w bloku ograniczonym przez słowa kluczowe OBJECT i END OBJECT.
Zarówno program główny, jak i moduły, muszą zawierać jawną specyfikację elementów importowanych i modułów, w których te elementy zostały zdefiniowane - w dyrektywie IMPORT. Obiekt importowany udostępnia wszystkie atrybuty (z wyjątkiem prywatnych)
oraz typ referencyjny. Cykliczne wykorzystywanie typów w modułach jest możliwe dzięki wprowadzeniu w modułach definicyjnych dodatkowej sekcji typów eksportowanych stanowiącej pewien rodzaj zapowiedzi obiektu.
Kolejność sekcji programu i modułu jest analogiczna do programu paskalowego. Oddzielenie definicji obiektów od ich implementacji pozwala na ukrywanie sposobu realizacji metod obiektu, dostarczając u'zytkownikowi - w postaci źródłowej - wyłącznie modułów definiujących obiekty standardowe.
3. Opis statycznej i dynamicznej struktury modelu
Strukturę statyczną i dynamiczną modelu opisuje się w języku MODSIM wykorzystując definicje typów obiektowych. Pola typu obiektowego określają atrybuty obiektów danej klasy i pozwalają na przechowywanie ich parametrów wraz z danymi opisującymi ich stan. Metody obiektów opisują ich działania i współdziałania z innymi obiektami. Typy obiektowe mogą mieć zdefiniowane pola i metody prywatne, które nie będą dostępne poza obiektem. Deklaracje zmiennych typów obiektowych określają strukturę statyczną modelu.
Typ obiektowy języka MODSIM posiada wszystkie cechy obiektów występujące w programowaniu zorientowanym obiektowo [10], tj.: dziedziczenie, hermetyczność, polimorfizm i przesyłanie wiadomości. Definicje typów obiektowych w języku MODSIM buduje się wykorzystując wielodziedziczenie, tzn. możliwość dziedziczenia po wielu przodkach. W obiektach potomnych można definiować nowe pola, ale pola dziedziczy się w całości i nie można ich przesłaniać. Przewidziano zasady postępowania związane z niedeterminizmem wynikającym z powtarzania się nazw pól i metod w obiektach różnych przodków. Można też budować obiekty hierarchiczne. W obiektach tego rodzaju typy pól mogą należeć do zdefiniowanych w języku, standardowych typów obiektowych, a także wcześniej zdefiniowanych typów użytkownika. Metody odziedziczone mogą być rozszerzane lub przedefiniowywane w typach potomnych dzięki wykorzystaniu przesłaniania, obsługiwanego przez polimorfizm. W treści metody przesłaniającej można wywołać przesłanianą metodę przodka przed lub po dodatkowych działaniach metody przesłaniającej. Metody nie przesłonięte są pobierane z najbliższego przodka.
Przesłanianie i wielodziedziczenie pozwalają na przypisywanie obiektom złożonych cech i skomplikowanych zachowań. Ułatwiają też przystosowywanie bibliotek obiektów do bieżących potrzeb - przez pokrywanie metod ich nowymi wersjami, bez potrzeby ingerowania w starą treść biblioteki.
Typy obiektowe języka MODSIM nie tylko definiują strukturę i zachowanie egzemplarzy obiektów generowanych na podstawie definicji i stanowiących klasę obiektów, ale mogą także zawierać pola charakteryzujące klasę, stanowiące zmienne tej klasy oraz metody dotyczące tych pól. Dla określonej klasy obiektów istnieje jeden egzemplarz zmiennych klasy o nazwie typu obiektu oraz wiele egzemplarzy pól obiektów, odrębnych dla wszystkich obiektów wygenerowanych na podstawie wzorca klasy. Pola klasy są dostępne przez kwalifikowanie ich nazwą typu obiektowego.
Wszystkie egzemplarze klas języka MODSIM są generowane dynamicznie. Dynamiczne generowanie egzemplarzy obiektów dotyczy zatem zarówno obiektów tymczasowych, jak i obiektów trwałych modelu, i pociąga za sobą konieczność kasowania obiektów, gdy
7
nie są już w modelu potrzebne. Wygenerowanie obiektu automatycznie wywołuje metodę inicjującą obiektu (jeśli jest ona zdefiniowana). W celu zainicjowania pól przodków, w metodach inicjujących, zdefiniowanych w klasach potomnych, muszą być jawnie wywołane metody inicjujące klas przodków, poprzedzające wszystkie instrukcje metody przesłaniającej. Procedura zwalniająca obiekt automatycznie wywołuje metodę kończącą obiektu, (zanim egzemplarz obiektu zostanie zwolniony).
Deklaracja typu obiektowego definiuje nowy typ o tej samej nazwie, znany jako typ referencyjny, podobny do typu wskaźnikowego. Typ referencyjny odnoszący się do typu obiektowego jest jednak "mocniejszy" niż typ wskaźnikowy, ponieważ nie tylko wskazuje na strukturę danych, ale pamięta o dynamicznej alokacji metod związanych z przesłanianiem metod w obiektach potomnych. Zmienne typów obiektowych są zmiennymi referencyjnymi. Egzemplarze obiektów są więc identyfikowane przez zmienne referencyjne. Zmienna referencyjna typu przodka może wskazywać na potomka, ale nie odwrotnie. Zdefiniowano typ referencyjny ANYOBJ, który może zastępować dowolny typ obiektowy przy wywołaniach pewnych metod.
Przypisanie zmiennych obiektowych jest przydzieleniem dodatkowej referencji do egzemplarza obiektu. Powielenie treści obiektu jest realizowane przez instrukcję klonowania (CLONE). Tablice o elementach typu obiektowego są tablicami zmiennych referencyjnych. Każda tablica obiektów musi być wygenerowana z podaniem jej rozmiaru. Dodatkowo musi być wygenerowany każdy egzemplarz obiektu stanowiący element tej tablicy.
Atrybuty egzemplarza obiektu, zarówno własne, jak i odziedziczone, są dostępne w metodach obiektu i są traktowane tak, jak zmienne globalne obiektu, tzn. mogą być one odczytywane i modyfikowane. Atrybuty innych obiektów są dostępne tylko do odczytu (poprzez udostępniające je metody obiektu). Każde wywołanie metody jest wykonywane jako przesłanie komunikatu do obiektu, którego metoda ma być wykonana. Każda metoda ma niejawny parametr (SELF), będący odwołaniem do struktury danych egzemplarza obiektu, dla którego została ona wywołana.
W języku MODSIM rozróżnia się trzy typy metod: metody ASK, metody TELL i metody WAITFOR. Definicje i wywołania tych metod zawierają odpowiednie słowa kluczowe (ASK, TELL lub WAIT FOR). Bezpośrednie wywołanie metody może dotyczyć metody własnej obiektu, która ma być wykonana w bieżącej chwili czasu symulacji (dotyczy to metod ASK i TELL).
Metody ASK są metodami synchronicznymi i są wykonywane w chwili wywołania tak, jak zwykłe procedury. Metody te po zakończeniu przekazują sterowanie do metody wywołującej i mogą także przekazać wartość.
Metody TELL są synchronizowane w czasie i można określić czas symulacji, w którym mają być wykonane. Wywołania metod TELL są rejestrowane, zaś same metody są wykonywane asynchroniczne przez mechanizm upływu czasu. Wywołanie metody powoduje wyłącznie zaplanowanie jej wykonania w harmonogramie wykonywania metod, po czym jest kontynuowane wykonywanie metody wywołującej (bez przekazywania sterowania). Wykonanie metody zaplanowanej będzie zrealizowane, gdy po przekazaniu sterowania do obiektu zarządzającego metodami, metoda znajdzie się na pierwszej pozycji harmonogramu wykonywania metod i czas symulacji będzie równy czasowi, na który metoda została zaplanowana. Jeżeli przy wywołaniu metody TELL nie podano opóźnienia, zaplanowana
metoda zostanie wykonana po zakończeniu metody planującej (w tym samym czasie symulacji). W metodzie TELL, podczas wykonywania metod ASK obiektu oraz zwykłych procedur i instrukcji (z wyjątkiem WAIT), czas symulacji nie płynie. Metody TELL nie mogą być wywoływane w metodach inicjujących.
Metoda TELL nie musi być wykonywana w całości w danej chwili czasu symulacji. Wykonanie części metody może zostać opóźnione, jeżeli w metodzie zostanie wykonana instrukcja WAIT. Po wykonaniu tej instrukcji metoda TELL przekazuje sterowanie do procedury zarządzającej synchronizacją metod i jej wykonanie zostanie wznowione od miejsca zawieszenia (tj. od następnej instrukcji po instrukcji WAIT), gdy warunek wznowienia metody będzie spełniony. Podczas realizowania instrukcji WAIT czas symulacji może płynąć, co oznacza, że różne fragmenty metody TELL rozdzielone instrukcją WAIT są wykonywane w różnym czasie symulacji, a tym samym, przy innym stanie obiektu i modelu.
Warunek wznowienia metody określany w instrukcji WAIT może być zdefiniowany przez podanie czasu opóźnienia wykonania metody (WAIT DURATION), podanie nazwy metody (WAIT FOR ObjMethld), po wykonaniu której metoda opóźniana może zostać wznowiona, lub przez podanie nazwy przerzutnika (WAIT FOR Thggerld), który musi zostać włączony przed wznowieniem metody. Efektem wykonania instrukcji WAIT FOR ObjMethld jest umieszczenie metody ObjMethld w harmonogramie metod i jej asynchroniczne wykonanie. Wykonanie instrukcji WAIT jest traktowane jako wykonywanie pewnej czynności przez obiekt. Czynność ta może zostać przerwana przez inne metody danego obiektu lub innych obiektów, jeśli wykonają one procedurę Interrupt należącą do obsługi upływu czasu. Po działaniach przewidzianych do wykonania po wznowieniu obiektu, w zakresie instrukcji WAIT umieszcza się klauzulę ON INTERRUPT, po której określa się działania obiektu wykonywane w przypadku przerwania "wykonywanej czynnośći" obiektu. Wykonywanie metody TELL może zostać arbitralnie przerwane instrukcją TERMINATE. Instrukcja ta kończy też wykonanie innych metod czekających na metodę, w której ją wykonano. Metody TELL nie mogą mieć parametrów wyspecyfikowanych jako OUT i INOUT i nie mogą być definiowane jako metody funkcyjne. Instrukcje WAIT i TERMINATE mogą być wykonywane wyłącznie w metodach TELL lub WAITFOR.
Skomplikowane zachowanie obiektu może być opisane przez wiele oddzielnych metod TELL, które wzajemnie się planują, bądź też jedna metoda TELL może opisywać wiele sekwencyjnych faz procesu obiektu, rozdzielonych instrukcjami WAIT. Jeżeli jednak obiekt może jednocześnie wykonywać wiele różnych czynności, to powinny być one obsługiwane przez odrębne metody TELL, a ich wykonanie synchronizowane odpowiednio do systemu obsługi prowadzonego przez obiekt.
W języku MODSIM zdefiniowano biblioteki standardowych obiektów wspomagających programowanie symulacji. Użytkownik może używać zdefiniowanych w nich typów bezpośrednio do deklarowania zmiennych lub używać ich jako typów przodków do definiowania własnych typów obiektowych o składowych standardowych typów. Niektóre z typów obiektowych są zdefiniowane jako obiekty prototypowe (PROTO OBJECT). Obiekty prototypowe mają określany typ parametru, dotyczący typu obiektowego, jako #ANYOBJ. Użytkownik może ograniczyć typ obiektów, które mogą z tego typu obiektu korzystać, określając jaki typ obiektu będzie zastępował parametry typu #ANYOBJ.
4. Ustalanie stanu początkowego modelu
Zmienne i pola rekordów i obiektów typów standardowych są inicjowane automatycznie wartościami początkowymi tych typów (INTEGER - 0, REAL - 0.0, BOOLEAN - FALSE, CIIAR - znak kodu ASCII o numerze 0, STR1NG - ", typ wyliczeniowy - pierwsza wartość z listy wartości).
Zmiennym typów referencyjnych przypisuje się wartość początkową sygnalizującą, że obiekt tego typu nie istnieje. Wartości te są określone przez stałe odpowiednie do typu danych: NILREC, NILARRAY, NILOBJ. Automatycznie są inicjowane pola standardowych typów obiektów standardowych zawartych w bibliotekach obiektów języka.
5. Organizacja upływu czasu
Organizacja upływu czasu opiera się na harmonogramie wykonywania metod stanowiącym kalendarz zdarzeń. Jest on zrealizowany jako lista opisów zdarzeń. Opis zdarzenia pozwala zidentyfikować egzemplarz obiektu i jego metodę oraz określa czas symulacji, w którym metoda ma być wykonana lub wznowiona. Mechanizm upływu czasu jest zdefiniowany i zaimplementowany w module SimMod, w którym zadeklarowano zmienną reprezentującą czas symulacji i funkcję SimTime(), która ten czas odczytuje. Lista opisów zdarzeń jest dwupoziomowa. Na pierwszym poziomie są umieszczone opisy najbliższych zdarzeń w różnych obiektach. Do opisu zdarzenia z pierwszego poziomu dla każdego obiektu jest dołączona lista czynności, czyli zdarzeń zaplanowanych dla tego obiektu. Wstawianie nowych opisów do kalendarza zdarzeń jest wykonywane niejawnie jako skutek wywołania metody TELL lub metody WAITFOR. Zdarzenie umieszczone w kalendarzu może zostać usunięte na skutek wykonania procedury Interrupt w metodzie TELL. Parametry procedury Interrupt identyfikują obiekt i metodę, czyli zaplanowane zdarzenie, które nie zajdzie. Dla tego obiektu, po wznowieniu, zostaną wykonane działania przewidziane w metodzie zawieszonej na wypadek przerwania. Jeżeli w metodzie jest kilka instrukcji WAIT, wykonuje się działania po tej instrukcji WAIT, która spowodowała ostatnie zawieszenie metody TELL. Procedura InterruptAll pozwala przerwać wszystkie czynności prowadzone przez wskazany obiekt. Jest to realizowane przez usunięcie z kalendarza zdarzeń wszystkich zdarzeń zaplanowanych dla tego obiektu oraz wykonanie działań przewidzianych na wypadek przerwania w metodach kolejno wznawianych (na podstawie listy czynności obiektu). Rekord czynności zawiera wszystkie niezbędne informacje potrzebne do kontynuowania metody po zakończeniu zawieszenia metody.
Przekazanie sterowania przez metodę TELL do obiektu zarządzającego wykonywaniem metod następuje w wyniku zawieszenia wykonywania przez instrukcję WAIT, zakończenia wykonywania przez instrukcję TERMINATE lub zwykłe wykonanie metody do końca.
Niejawnie wywołana procedura zarządzająca metodami odłącza pierwszy element z listy pierwszego poziomu kalendarza i odłącza od niego opis aktualnego zdarzenia. Wykonuje ona także przeorganizowanie kalendarza zdarzeń - przez aktualizację elementu pierwszego poziomu pobranego z kalendarza i włączenie go w nowe miejsce, zależne od czasu następnego zdarzenia w tym obiekcie. Jeżeli zdarzenie aktualne nie jest zdarzeniem
równoczesnym ze zdarzeniami już obsłużonymi, tj. jego obsługa nie jest przewidziana na bieżący czas symulacji, czas symulacji jest przesuwany do czasu z opisu aktualnego zdarzenia.
Na podstawie opisu aktualnego zdarzenia pobranego z kalendarza, procedura zarządzająca wykonywaniem metod przekazuje komunikat do metody asynchronicznej obiektu, opisanej przez to zdarzenie i w zależności od sposobu zaplanowania metody, następuje rozpoczęcie lub wznowienie jej wykonywania. Dla jednego egzemplarza obiektu może być równocześnie zaplanowanych wiele metod, co oznacza, że w czasie symulacji może on jednocześnie "wykonywać" wiele czynności. Czynności te mogą być synchronizowane przez użycie mechanizmu oczekiwania na zakończenie wykonywania metody własnej lub innego obiektu, lub też włączenie przerzutnika.
Typ obiektowy przerzutnika (TriggerObj) został zdefiniowany w module organizacji symulacji SimMod. Instrukcja WAIT FOR Tiiggerld powoduje automatyczne dołączenie referencji obiektu oczekującego do listy obiektów oczekujących, która jest atrybutem przerzutnika. Długość listy nie jest ograniczona. Oznacza to, że na włączenie jednego przerzutnika może czekać dowolna liczba metod. Można ją odczytać za pomocą metody ASK NumWaiting. Dwie inne metody TELL tego obiektu - Fire i InterruptTiigger - także mogą być jawnie wykorzystywane. Metoda Fire automatycznie spowoduje wznowienie wszystkich metod obiektów oczekujących na włączenie, które są zarejestrowane w liście obiektów oczekujących przerzutnika i wykona w nich działania przewidziane po efektywnym "wykonaniu" czynności. Metoda InterruptTrigger spowoduje wznowienie wszystkich metod obiektów oczekujących na włączenie i wykona w nich działania wyspecyfikowane w klauzuli ON INTERRUPT.
Dla wielu egzemplarzy tego samego typu obiektowego mogą być równocześnie zaplanowane te same lub różne metody TELL przewidziane dla tego typu.
Czas symulacji jest dostępny w każdym miejscu programu symulacyjnego przez wywołanie funkcji SimTime typu REAL z pustym parametrem ();. Jednostka czasu symulacji jest bezwymiarowa. Pozostawia się użytkownikowi konwersję wszystkich wartości reprezentujących w modelu atrybuty czasu do wybranej jednostki czasu modelu.
Przed rozpoczęciem symulacji w głównym programie musi zostać zaplanowana i umieszczona w kalendarzu zdarzeń co najmniej jedna metoda TELL. Rozpoczęcie symulacji przez wykonanie procedury StartSimulation ustala czas symulacji równy 0.0 i rozpoczyna przebieg symulacji przez uruchomienie mechanizmu upływu czasu. Symulacja kończy się, gdy kalendarz zdarzeń stanie się pusty.
6. Grupowanie obiektów tymczasowych
Korzystając z bibliotecznych typów obiektów zdefiniowanych w module GrpMod można w programie deklarować i generować egzemplarze obiektów następujących uporządkowanych grup obiektów:
· kolejki (typu QueueObj) tworzone i obsługiwąne według regulaminu FIFO, · stosy (typu StackObj) tworzone i obsługiwane według regulaminu LIFO,
· listy (typu RankedObj) uporządkowane według jednego lub wielu atrybutów obiektu,
~ drzewa (typu BTreeObj) obiektów uporządkowanych według klucza typu STRING Metody typu QueueObj pozwalają:
v dołączyć nowy obiekt na koniec kolejki (Add),
~ dołączyć nowy obiekt przed wskazanym obiektem grupy (AddBefore), v dołączyć nowy obiekt po wskazanym obiekcie grupy (AddAfter),
v usunąć obiekt z początku kolejki (Remove),
~ usunąć obiekt z grupy bez względu na miejsce w którym się znajduje (RemoveThis), v odczytać wartość referencji do obiektu, który znajduje się na pierwszej (First) lub ostatniej (Last) pozycji w grupie albo do obiektu następnego po (Next) lub poprzedniego przed (Prev) wskazanym obiektem grupy,
v stwierdzić, czy wskazany obiekt jest członkiem grupy (Includes).
Odczyt pola numberln pozwala określić liczbę członków grupy.
W typie StackObj przedefiniowano metodę Add tak, aby dołączała nowe elementy na początku grupy. Przesłonięta metoda umieszczająca obiekty w grupie (Add) dla typu RankedObj, przy określaniu miejsca wstawienia nowego elementu do grupy wykorzystuje wirtualną metodę funkcyjną (Rank), definiowaną przez użytkownika. Metoda Rank porównuje pola obiektu wstawianego (a) i obiektu bieżącego grupy (b) oraz określa ich kolejność w grupie, dając wartość odpowiednią do wyniku porównania, tj. -1, gdy a<b, 1, gdy a>b i 0, gdy a=b.
Dla wszystkich typów grup obiektów, zdefiniowano dodatkowe typy grup statystycznych, które oprócz pól i metod grupy zawierają obiekt typu Statlnteger, posiadający pola i metody pozwalające na wyznaczenie statystyk grupy dotyczących liczby obiektów w grupie. Do wyznaczanych uogólnionych parametrów statystycznych miary grupy, jaką jest liczba elementów w grupie, należą: wartości minimalna i maksymalna, średnie zwykła i ważona oraz wariancje zwykła i ważona. Wyznaczone statystyki grupy można odczytywać osobno za pomocą metod typu ASK.
Przeglądanie grupy, w celu znalezienia elementu lub zbioru elementów grupy, można prowadzić zarówno od początku, jak i od końca grupy, wykorzystując instrukcję WHILE i metody obiektów grup.
7. Generowanie liczb pseudolosowych
Typ obiektowy generatora liczb pseudolosowych o rozkładzie równomiernym RandomObj zdefiniowano w module bibliotecznym RandMod. Generowane są liczby z przedziału (0.0, 1.0). Każde wygenerowanie zmiennej typu RandomObj ustala domyślną wartośe początkową dla generatora - tzw. ziarno (originalseed). Pociąga to za sobą generowanie zawsze takich samych ciągów liczb pseudolosowych. Użytkownik może wybrać i ustalić inną z 10 wartości początkowych dla generatora, wykonując metodę funkcyjną FetchSeed z parametrem określającym numer ziarna, a następnie wykorzystać podaną wartość w metodzie SetSeed generatora.
W języku MODSIM jest też dostępna standardowa procedura nieobiektowa, która generuje liczby wykorzystując generator pseudolosowy Random dostępny na danym komputerze.
W module RandMod zdefiniowano też metody generujące liczby pseudolosowe o następujących rozkładach:
~ równomiernym z określonego przedziału - wartością odpowiedniej metody jest wartość typu INTEGER lub typu REAL (metody Uniformlnt lub UniformReal),
~ wykładniczym (Exponential), ~ normalnym (Normal),
v Gamma (Gamma), ~ Beta (Beta),
v trójkątnym (Triangular).
8. Wyznaczanie miar pracy systemu przeprowadzanie analizy statystycznej wyników
Oprócz automatycznego wyznaczania statystyk grup dla grupowych obiektów statystycznych, w języku MODSIM zdefiniowano (w module StatMod) typy obiektowe statystyczne i statystyczne monitorowane. Wszystkie typy statystyczne są typami potomnymi typu StatObj, który posiada pola i metody pozwalające na wyznaczenie uogólnionych parametrów miar statystycznych, będących zmiennymi losowymi. Pola akumulują sumę, sumę kwadratów, liczbę aktualizacji i histogram oraz rejestrują dane histogramu.
Za pomocą metod obiektu ustala się stan początkowy miary (Reset), parametry histogramu, tj, dolną i górną granicę oraz szerokość przedziału (SetHistogram), wyznacza się średnią (Mean), kwadrat średniej (Mean Square), odchylenie standardowe (StDev) i wariancję (Variance). Typ potomny o nazwie TimedStatObj posiada dodatkowe pola i metody, które pozwalają na wyznaczenie uogólnionych parametrów statystycznych dla miar typu proces stacjonarny, tj. średnich i odchyleń standardowych ważonych.
W module StatMod zdefiniowano także typy potomne, które służą do wyznaczania wartości minimalnych i maksymalnych oraz uogólnionych parametrów statystycznych zwykłych i ważonych dla miar typów INTEGER i REAL. Te z kolei są typami przodków dla odpowiednich monitorów statystyk. Rozważany moduł zawiera też definicje typu histogramu używanego przez obiekty statystyczne dla zdefiniowania pola do zbierania histogramu wyznaczanej miary. Jego aktualizacja jest realizowana za pomocą metody UpdateHistogram.
9. Zarządzanie zasobami
W module ResMod biblioteki obiektów standardowych języka MODSIM zdefiniowano mocny typ reprezentujący zasób (ResourceObj). Zasób stanowi pulę ekwiwalentnych elementów, które mogą być przydzielane na żądanie egzemplarzom obiektów obsługiwanych na określony czas. Pojemność zasobu jest ograniczona (pole MaxResources) i określa ją użytkownik (za pomocą metody Create). Wartość początkowa jest ustawiana na 0 przy generowaniu obiektu. Pojemność zasobu może być zmieniona podczas symulacji (za pomocą metod InerementResourcesBy i DecrementResourcesBy). Przydzielenie części zasobu jest realizowane na skutek wykonania żądania przydzielenia zasobu. Obsługiwane są cztery typy żądań:
~ przydzielenie określonej liczby elementów zasobu (Give),
~ priorytetowe przydzielenie określonej liczby elementów zasobu (PriorityGive),
~ przydzielenie określonej liczby elementów zasobu, zrealizowane w określonym czasie (TimedGive),
~ priorytetowe przydzielenie określonej liczby elementów zasobu, zrealizowane w określonym czasie (GetResource).
Priorytet jest wartością rzeczywistą - większa wartość oznacza wyższy priorytet żądania i powoduje wcześniejszą realizację. Priorytet nie ma znaczenia, jeżeli żądanie może być zrealizowane natychmiast. Żądania bezpriorytetowe mają priorytet 0.0. Żądanie priorytetowe nie ma priorytetu przywłaszczającego w stosunku do elementów zasobu przydzielonych wcześniej. Jeżeli rzeczywisty zasób ma takie możliwości, użytkownik musi to sam zaprogramować definiując własny typ potomny typu ResourceObj.
Wszystkie żądania muszą być zamawiane przez wykonanie instrukcji WAIT FOR. Żądania zamawiane bez określenia czasu, w którym mają być zrealizowane, oczekują na zasób aż do jego przydzielenia. Natomiast żądania określające limit czasowy realizacji są odrzucane, jeżeli nie mogły być zrealizowane w terminie. Żądania te powinny mieć przewidzianą klauzulę ON INTERRUPT w zakresie instrukcji WAIT oczekującej na przydzielenie zasobu. Pominięcie działań wykonywanych na skutek przerwania metody może prowadzić do powstania błędu wykonania (w przypadku przekroczenia czasu oczekiwania na przydzielenie zasobu).
Przydzielony element puli zasobu jest niedostępny dla innych obiektów do czasu jego zwolnienia i zwrócenia do puli. Żądania nie obsłużone są przez zasób ustawiane w kolejkę. Kolejka jest uporządkowana według wartości priorytetu, a dla tej samej wartości priorytetu - w kolejności zgłoszeń. Żądania obsłużone są rejestrowane w celu sprawdzenia liczby przydzielanych i zwracanych elementów zasobu. Próba zwrócenia większej liczby elementów niż liczba elementów przydzielonych powoduje wystąpienie błędu wykonania. Obiekt obsłużony przez przydzielenie mu części zasobu ma nad nim kontrolę do czasu zwrócenia go do puli (za pomocą metody TakeBack). Działania dotyczące wykonywania przez użytkownika operacji na przydzielonym zasobie nie należą do działań obiektu ResourceObj. Użytkownik zasobu może też sam zdecydować, komu przekaże zasób, zamiast zwrócić go do puli (Transfer). Możliwa jest rezygnacja z oczekiwania na zasób lub zmiana liczby żądanych elementów zasobu (Cancel).
Obiekt ResourceObj automatycznie akumuluje statystyki dotyczące zajętości zasobu oraz długości kolejki i czasów oczekiwania w kolejce po zasób. Zbieranie statystyk zajętości zasobu może być włączone lub wyłączone (SetAllocStats). Podobnie można włączyć lub wyłączyć (SetPendStats) zbieranie statystyk związanych z kolejką po zasób. Dla zbieranych statystyk można włączyć lub wyłączyć wyznaczanie histogramu statystyk (SetAllocHistogram i SetPendingHistogram). Przy generowaniu obiektu pole sterujące zbieraniem statystyk ma domyślną wartość FALSE.
Typ ResourceObj jest zdefiniowany jako PROTO OBJECT, dzięki czemu użytkownik może ograniczyć typ obiektów ubiegających się o przydzielenie zasobu.
10. Śledzenie stanu systemu podczas przebiegu symulacji
Zmienne programu lub pola obiektów mogą być wyświetlane w czasie przebiegu symulacji za pomocą obiektów monitorujących. W deklaracji typu obiektu monitorującego słowo OBJECT jest poprzedzone słowem kluczowym MONITOR i nazwą typu monitorowanego. Przy deklaracji zmiennych i pól, które mają być monitorowane, określa się sposób monitorowania, typ monitorowany (INTEGER, REAL) i typ monitorujący zdefiniowany przez użytkownika. W zależności od sposobu monitorowania, wartości zmiennych monitorowanych są wyświetlane podczas symulacji, gdy zmienna jest aktualizowana (LMONITORED), gdy zmienna jest odczytywana (RMONITORED) lub w obydwu przypadkach (LRMONITORED). Monitorowanie może być włączane i wyłączane.
Standardowe metody monitorowania udostępniają:
~ ostatnią wartość zmiennej lub pola,
v nową wartość, która ma być przypisana zmiennej monitorowanej (dla LMONITORED), oraz umożliwiają nadanie zmiennej monitorowanej wartości wyrażenia.
Oprócz monitorowania wartości zmiennych programu i atrybutów obiektów w postaci liczbowej, ich stan może być pokazywany na pewnych elementach graficznych, tj. miernikach i wykresach, dla których zdefiniowano standardowe rysunki w bibliotece obiektów graficznych. Wymagane jest wówczas powiązanie zmiennej monitorowanej z monitorującym ją miernikiem lub wykresem (za pomocą metody SetGraph). Zmiana wartości monitorowanej zmiennej powoduje automatyczną aktualizację stanu elementu graficznego. Zasady importowania i wyświetlanie rysunków są opisane w p. 14.
Czas symulacji, wyspecyfikowany w godzinach, minutach i sekundach, może być pokazywany na zegarze (typ potomny typu ClockVObj). Zegar jest opisany przez tytuł, zakres wyświetlanych wartości, graficzny i liczbowy przedział taktu zegara. Są dostępne dwie formy graficzne zegara reprezentowane przez różne obiekty: zegar cyfrowy lub zegar analogowy. Dla zegara ustala się programowo skalę, specyfikującą liczbę rzeczywistych sekund na jednostkę czasu symulacji. Inne dane zegara (zakres zegara, tj. maksymalną liczbę godzin na tarczy, liczbę minut na godzinę, liczbę sekund na minutę) można zdefiniować w edytorze graficznym. Podany czas można wyświetlić metodą DisplayTime. Zegar uruchomiony na początku symulacji metodą StartMotion automatycznie wskazuje bieżący czas symulacji.
Stan pewnych zmiennych reprezentujących wartości skalarne może być pokazywany na miernikach różnych typów (potomnych w stosunku do typu MeterVObj) takich, jak: mierniki cyfrowe, analogowe i wyświetlacze tekstu. Mierniki analogowe są dwóch typów: mierniki wychylne wskazówkowe i wskaźniki poziomu. Mierniki cyfrowe (typu DigitalDisplayObj) mają określony tytuł, szerokość pola w cyfrach i dokładność. Wychylne mierniki wskazówkowe (DialObj), na przykład szybkościomierz, i mierniki poziomu (LevelMeterObj), na przykład termometr, są zdefiniowane przez tytuł, zakres wyświetlanych wartości, tj. minimum i maksimum, graficzną i liczbową wartość jednostki poziomu oraz współczynnik skali. Aktualną wartość wyświetla metoda DisplayValue. Dla miernika wychylnego określa się w stopniach, położenie minimalnej i maksymalnej wartości na liczniku. Oznaczenia liczbowe jednostek na mierniku mogą być umieszczone wewnątrz lub na zewnątrz tarczy.
Na wyświetlaczu tekstu (TextDisplayObj) można wyświetlać łańcuch znaków graficznie. Wyprowadzane napisy są obcinane do ustalonej szerokości pola. Napis jest wyświetlany metodą DisplayTezt i jest umieszczany w ramce z wyrównywaniem do prawej strony.
Mierniki muszą być importowane z odpowiedniego modułu, zadeklarowane, wygenerowane, ich rysunki pobrane z biblioteki, a monitorowana zmienna musi zostać jawnie powiązana z wyświetlanym miernikiem. Wskazania mierników monitorowanych są uaktualniane automatycznie przy zmianie stanu zmiennej.
Zmienna skalarna może być także monitorowam na wykresie w postaci histogramu ChartObj) lub wykresu śledzącego (Trace Plot). Czas pokazywany jest na osi x, zaś warość zmiennej na osi y. Jeżeli zakres czasu przekracza zakres wartości osi czasu, wykres est kompresowany lub przesuwany. Na jednym wykresie można monitorować więcej niż winą zmienną. Pojedyncza zmienna może być także pokazana jako słupek lub wycinek koła. W tym przypadku na jednym wykresie każdy słupek lub wycinek koła może reprezentować inną zmienną, której przypisuje się numer reprezentującego ją słupka. Słupki ą numerowane od lewej do prawej.
Tablice wartości skalarnych mogą być monitorowane przez wykres dwuwymiarowy lub kołowy (zob. p. 12). Słupki odpowiadają kolejnym elementom tablicy. Także w tym przypadku jest wymagane powiązanie zmiennej lub tablicy monitorowanej z wykresem za ponocą metody SetGraph. Nadawanie wartości zmiennej lub elementom tablicy, związanym wykresem, automatycznie modyfikuje stan wykresu (przez umieszczanie punktu na wy;resie).
Obiekt reprezentujący wykres musi być zdefiniowany w edytorze SIMDRAW (określa się typ wykresu) i zapamiętany w bibliotece graficznej. Sposób definiowania wykresu jest taki sam, jak dla wykresów, na których przedstawia się wyniki symulacji (zob. p. 12).
Obiekty graficzne służące do śledzenia stanu atrybutów elementów modelu podczas symulacji są potomkami obiektu GraphicVObj, który jest scharakteryzowany w p. 11. Wszystkie te obiekty są traktowane jako wykresy, ponieważ mają pewne cechy odziedziczone po obiekcie GraphObj, wspólne z innymi wykresami przeznaczonymi do prezentowania wyników symulacji (zob. p. 12).
11. Wspomaganie komunikacji użytkownika z programem
W pakiecie SIMGRAPHICS II [9] znajduje się szereg modułów bibliotecznych wspomagających programowanie komunikacji użytkownika z programem w zakresie prezentacji oferty programu, wyboru opcji oferty i wprowadzania danych. Podstawową cechą tej komunikacji jest możliwość zainicjowania jej, nie tylko na początku przebiegu symulacji, v celu ustalenia parametrów pracy modelu, ale także w dowolnej chwili pracy programu, v celu ich zweryfikowania.
Komunikacja z użytkownikiem może być włączona do programu na dwa sposoby:
~ przez programowanie poszczególnych części ekranu w sposób podobny, jak przy użyciu biblioteki Turbo Vision pakietu Turbo Pascal 7.0,
przez definiowanie elementów graficznych w edytorze graficznym SIMDRAW, składowanie ich w pliku stanowiącym bibliotekę obrazów i importowanie tych elementów z biblioteki podczas pracy programu.
Obydwie metody określania postaci elementów graficznych, umieszczanych przez program na ekranie, wykorzystują standardowe obiekty z biblioteki obiektów języka MODSIM.
j Obiekty te są używane także do programowania, definiowania i animacji elementów obrazu ekranu komputera stanowiących graficzny model systemu. Ich zastosowanie do tego celu jest opisane w p. 13, zaś zasady konstruowania obrazu w edytorze SIMDRAW są opisane w [9].
1 Wszystkie obiekty graficzne są dynamiczne. Podstawowym elementem graficznym jest GraphicVObj. Każdy obiekt potomny ma zdolność umieszczania (DisplayAt), rysowania (Draw) i wybierania go (BeSelected). Obiekty potomne typu GraphicVObj mają zdolność grupowania podobną do obiektów typu QueueObj. Obiekty graficzne mogą być dodawane do grupy (AddChild) i usuwane z niej (RemoveChild). Mogą być składowane (SaveToLib
ł raty) i odzyskiwane z biblioteki obiektów graficznych (LoadFromLibrary). Elementy grupy j obiektów graficznych mogą być udostępniane (Child i Descendant).
Każdy obiekt potomny typu GraphicVObj może zawierać elementy składowe, które podlegają działaniom wykonywanym na obiekcie jako całości, ale można też działać na nich indywidualnie. Ponieważ elementy składowe mogą również zawierać obiekty graficzne, hierarchia obiektów graficznych jest grupą reprezentowaną przez drzewo obiektów graficznych.
Obiekt graficzny zawierający zagnieżdżone obiekty graficzne jest nazywany ojcem. Obiekty graficzne wewnętrzne są nazywane dziećmi. Zbiór wszystkich dzieci jest nazywany zbiorem-potomkiem, a zbiór wszystkich ojców zbiorem-przodkiem. Drzewo obiektów graficznych jest tworzone przez metody .4ddGraphic i Remove77eisGraphic typu potomnego typu GraphicVObj. Po dodaniu dzieci obiekt ojca powinien zostać narysowany (Draw) w aktualnym oknie. Rysowanie dzieci bez narysowania ojca nie przynosi efektu. Każdy obiekt graficzny zdefiniowany w edytorze SIMDRAW ma przydzieloną nazwę referencyjną, rejestrowaną w polu ReferenceName, typu napis oraz identyfikator całkowity Id. Pozwalają one na zidentyfikowanie obiektu w grupie. Dostęp do składowych obiektu graficznego
' umożliwia metoda Child obiektu stanowiącego grupę. Jej wartością jest referencja do obiektu graficznego określana na podstawie nazwy referencyjnej i identyfikatora, znanych
użytkownikowi definiującemu obiekt graficzny w edytorze. Metoda Child jako metoda funkcyjna musi być wywołana w instrukcji przypisania, nadając wartość zmiennej referencyjnej zgodnej z typem obiektu, którego te identyfikatory dotyczą.
Z typu GraphicVObj wyprowadzono cztery typy potomne o następującym przeznaczeniu:
~ WindowObj do definiowania okna,
' ~ ImageObj do definiowania obrazów i grup obrazów reprezentujących elementy symulowanego systemu,
~ ControlVObj do definiowania elementów nazwanych sterowaniami, służących do wprowadzania danych do programu,
v FormObj do definiowania elementów nazwanych formularzami, stanowiącymi grupy · obiektów graficznych.
Okno (WindowObj) jest podstawowym elementem reprezentującym część ekranu monitora komputera, w której umieszcza się obiekty graficzne. W odróżnieniu od okien biblioteki Turbo Vision jest to okno graficzne. Okno może mieć określony tytuł (SetTitle), może zmieniać kolor (SetColor), położenie (SetTranslation) i rozmiary (SetSize), może być rysowane (Draw) i kasowane (Erase). Efekt wykonania na obiektach graficznych metod zaczynających sie od słowa Set jest widoczny dopiero po narysowaniu okna. Efekt wykonania innych metod jest natychmiastowy. Przy zmianie wielkości okna jego elementy są przeskalowywane. Współrzędne (0, 0) określają lewy dolny, a współrzędne (100, 100) - prawy górny narożnik pełnego okna. Położenie okna jest określone przez położenie jego lewego dolnego narożnika. System współrzędnych ustalony dla okna (SetWorld) obowiązuje dla wszystkich zagnieżdżonych obiektów okna i może być udostępniony (ShowWorld).
Obraz (ImageObj) jest przodkiem wszystkich typów obrazów reprezentujących elementy symulowanego systemu w jego modelu graficznym i jest opisany szczegółowo w p. 13. Każde okno komunikacji z użytkownikiem może zawierać ofertę programu z podofer
tami lub opcjami do wyboru i okienka dialogowe do wprowadzania danych różnych typów. Okno oferty programu (MenuBarObj) i okienko dialogowe (DialogBoxObj) są obiektami graficznymi wyprowadzonymi z obiektu typu FormVObj.
Obiekty do konstruowania oferty programu
Okno oferty programu zawiera elementy oferty, które mogą być podofertami. Oferta programu (MenuBarObj) może być utworzona w edytorze SIMDRAW. Obiekt oferty musi zostać wygenerowany i w tym przypadku jego obraz jest odtwarzany z biblioteki i dołączany do okna tak, jak inne elementy graficzne. Aby pojawił się na ekranie, musi zostać narysowany. Okno oferty staje się aktywne po zainicjowaniu symulacji w wyniku wykonania procedury Start Simulation.
Elementom oferty lub podoferty (MenultemObj) można przypisać jeden klawisz ze znaków nazwy elementu (na ogół zaznaczony w nazwie wielką literą i podświetlony), który stanowi mnemonik danego elementu. Gdy dana oferta jest widoczna, wyboru elementu dokonuje się przez naciśnięcie klawisza znaku mnemonika łącznie z klawiszem Alt. Mnemonik dla elementu oferty może zostać ustalony w edytorze (przy definiowaniu ramki oferty) lub programowo (za pomocą metody SetMnemonic) poprzez przypisanie go obiektowi stanowiącemu element oferty lub podoferty. Elementowi oferty lub podoferty można też przypisać tzw. klawisz aktywny (ang. accelerator), który powoduje wybranie danego elementu, nawet jeśli okienko oferty lub podoferty nie jest aktywne. Nazwa klawisza aktywnego jest zwykle wyświetlana w oferćie z prawej strony danego elementu. Klawiszem aktywnym może być kombinacja klawiszy Alt i Crtl oraz dowolnego znaku lub jeden z klawiszy funkcyjnych FI - F12. Podobnie jak mnemonik, klawisz aktywny może być przypisany elementowi oferty programowo (SetAccelerator).
Umieszczone w oknie elementy obrazu mogą być wybierane za pomocą myszki. Okno musi więc być informowane o ruchach myszki i naciskanych przyciskach. Wymaga to ustawienia monitorowania myszki (za pomocą metod SetMoveMonitoring i SetClickMonitoring). Ustawienie monitorowań spowoduje wywołanie odpowiednich metod (MouseMove, MouseClick) przy ruchach myszką. W ramach tych metod można badać inne pola obiektu okna - dla uzyskania bardziej szczegółowych informacji o stanie myszki. Metody informujące
o ruchu myszki są zwykle przesłaniane w obiektach potomnych typu WindowObj. Metody przesłaniające (OVERRIDEN) wywołują przesłanianą metodę przodka. Wybranie myszką może także dotyczyć opcji, tj. elementu oferty. Bez względu na sposób wybrania (myszką, klawiszem mnemonika lub klawiszem aktywnym), rozpoznanie wybranego elementu oferty i podoferty wymaga w przesłaniającej metodzie BeSelected:
v przekazania wybranego obiektu elementu oferty i jego nazwy referencyjnej,
v w zależności od wybranej podoferty, odczytania nazwy referencyjnej wybranego elementu podoferty z ostatnio wybranego elementu LastPicked
i wykonania działania odpowiedniego do dokonanego wyboru.
Obiekty używane w okienku dialogowym
Okienka dialogowe mogą pobierać swój obraz z biblioteki obrazów graficznych i są dołączane do okna tak, jak inne obiekty graficzne. Okienko jest wyświetlane centralnie w oknie przodka i może zawierać tytuł wyświetlany na górze ramki okienka. Dane wprowadzane poprzez okienko dialogowe są nazywane sterowaniami i są potomkami obiektu ControlVObj.
Każde sterowanie jest na ekranie reprezentowane przez ramkę-prostokąt i może być użyte do ustalenia wartości domyślnej lub wartości określonej przez użytkownika. Każde sterowanie, podobnie jak i inne obrazy zdefiniowane w edytorze SIMDRAW, ma przydzieloną nazwę referencyjną (RefName), za pomocą której jest identyfikowane w grupie obrazów oraz identyfikator Id (IdValue) pozwalający na zidentyfikowanie sterowania wybranego za pomocą myszki. Numer sterowania Id określa położenie sterowania w zbiorze sterowań okienka dialogowego. Użytkownik musi jawnie używać tych atrybutów sterowania w programie, jeżeli chce: I
~ odzyskać z biblioteki element graficzny sterowania zdefiniowany w SIMDRAW~ ~ wyprowadzić na ekran aktualną wartość sterowania,
~ pobrać ze sterowania daną do zmiennej programu.
Różne typy sterowań służą do ustalania wartości, wybranych ze zbioru dopuszczalnych wartości, lub do wprowadzania danych z klawiatury. Sterowania są identyfikowane w okienku dialogowym przez przypisanie im etykiet (napisów identyfikujących). Są dostępne sterowania takich typów, jak: przycisk, przełącznik dwupołożeniowy (parametr), przełącznik wielopołożeniowy (opcja), lista wyboru, napis, wartość.
Przycisk (ButtonObj) służy do określenia zewnętrznego zdarzenia okienka dialogowego. Zbiór nazwanych przycisków okienka reprezentuje zbiór możliwych zdarzeń odenka. Przyciski mogą być weryfikujące lub kończące. Przycisk weryfikujący powoduje weryfikację wartości wprowadzanych przez sterowania (ValueBoxObj), a przycisk kończący Zamyka okienko dialogowe ojca. Wybrany przycisk definiuje także rodzaj operacji, która Zostanie wykonana
po zamknięciu okienka, na przykład anulowanie lub zaakceptowanie wprowadzonych
danych i uruchomienie działania, dla którego zostały wprowadzone dane Jeżeli dla sterowań okienka przewidziano kontrolę wartości wprowadzonych do okienek;~ to naciśnięcie
przycisku akceptacji danych powoduje weryfikację danych oraz sygnalizacji dźwiękową i graficzną, gdy wprowadzone dane nie mieszczą się w zakresie dopuszczalnych wartości
Wybór aplikacji, a także zamknięcie okienka przez użytkownika, automatycznie wywołuje metodę BeClosed, która także może być przedefiniowana.
Przełącznik dwupołożeniowy - parametr (CheckBoxObj) - pozwala na wybór jednej z dwu przypisywanych mu wartości. Stan parametru można ustalić programowo (SetCheck, DisplayCheck).
Przełącznik wielopołożeniowy - ramka opcji (RadioBoxObj) - pozwala na wybór jednej ze skończonego zbioru wartości, z których każda jest reprezentowana przez przełącznik (RadioButtonObj). Zbiór przełączników opcji wyprowadzany jest w ramce opcji - każdy przełącznik w osobnym wierszu. Wybrany przełącznik może być odczytany z ramki opcji lub bezpośrednio z przełącznika. Wybrany przełącznik w ramce opcji może zostać ustalony za pomocą metody SetSelected Button.
Wybór wartości ze zbioru dopuszczalnych wartości o zmiennej liczbie wartości jest realizowany przez listę elementów do wyboru. Są dwa rodzaje list wyboru i odpowiednio do nich dwa typy obiektów list. Pierwszy z nich - ListBoxMultObj - pozwala na wybranie dowolnej liczby elementów ze zbioru. Drugi typ - ListBoxObj - pozwala na wybranie jednego elementu. Ramka wyboru ma szerokość i wysokość określaną w znakach. Elementy listy o znanym identyfikatorze są dodawane do listy i usuwane z niej za pomocą metod AddGraphic i RemoveGraphic, odziedziczonych z typu GraphicVObj. Kolejność elementów w liście wyboru jest określona przez porządek w zbiorze elementów graficznych. Elementy listy wyboru mogą być uporządkowane alfabetycznie. Wymaga to dodawania ich do listy metodą AddAlpha. Wszystkie elementy listy i lista wyboru muszą być wygenerowane. Każdemu elementowi należy przypisać etykietę i dodać go do obiektu listy, a następnie wykonać rysowanie obiektu listy. Można jednocześnie usunąć wszystkie elementy listy (RemoveAll ).
Stan wyboru może zostać zmieniony przez element, który był wybrany, lub przez listę wyboru z podaniem referencji elementu wybranego. Jeżeli stan wyboru elementów z listy zostanie zmieniony z programu, tzn. wybór jednego z elementów zostanie skasowany, a innego określony, uwidocznienie tego na ekranie wymaga wykonania aktualizacji listy wyboru (Update). Można zidentyfikować, który z elementów listy został wybrany - przez przeglądanie elementów listy przy użyciu metod FirstGraphic i NextGraphic oraz badanie ich pola IsSelected lub przez odczytanie wybranego elementu (Selectedltem) z listy.
Dane tekstowe są wprowadzane do programu za pomocą ramki napisu typu TextBoxObj. Ma ona określoną etykietę i długość. Zawartość ramki napisu (TextBoxld) może zostać zmieniona za pomocą metod Set i Draw.
Dane liczbowe są wprowadzane za pomocą ramki wartości (YalueBoxObj). Określa się ukres dopuszczalnych wartości, sposób formatowania i zasady zaokrąglania. Wprowadzeaie do ramki wartości z poza dopuszczalnego zakresu, przy ustawionej weryfikacji, powoduje sygnalizację dźwiękową i wyprowadzenie znaku < przy niewłaściwej wartości. War:ością metody Value(), odczytującej wartość z ramki, jest liczba rzeczywista. Zawartość ramki wartości można ustalić wykonując metody SetValue i Draw lub DisplayValue.
Sterowania mogą być aktywne lub pasywne, co jest sygnalizowane użytkownikowi przez odpowiednie narysowanie sterowania po wybraniu. Sterowanie pasywne nie przyjmuje darych od użytkownika.
Wyprowadzenie wartości sterowania na ekranie wymaga znajomości referencji wybranego obiektu, która musi być odczytana z drzewa obrazu okienka dialogowego do zmiennej typu sterowania. Pobiera się ją na podstawie znanych atrybutów sterowania: nazwy referencyjnej RefName i identyfikatora IdValue. Następnie wartość pobrana z określonej zmiennej programu zostaje wprowadzona do obiektu sterowania metodami SetValue lub SetText odpowiednio dla wartości lub dla napisu. Niekiedy wymaga to zamiany typu wyprowadzanej wartości (aby dopasować typ zmiennej programu do typu sterowania). W celu odczytania wybranej lub wprowadzonej przez użytkownika wartości sterowania należy, podobnie jak dla odczytu wybranego elementu oferty, wywołać w programie metodę Acceptlnput(). Jest to metoda funkcyjna, której wartością jest referencja do wybranego przycisku (ButtonObj). Po wykonaniu metody Acceptlnput() program zostanie zatrzymany do chwili naciśnięcia przycisku okienka dialogowego. Odczytanie wartości, wprowadzonych przez użytkownika do sterowań okienka, odbywa się po naciśnięciu w okienku przycisku o nazwie referencyjnej OK. Ustalenie wartości zmiennych, według wartości odczytanej z obiektu sterowania, wymaga wykonania przypisania odpowiedniej wartości do każdej zmiennej, której wartość ma być ustalona poprzez dialog z użytkownikiem.
Sterowanie zdefiniowane w SIMDRAW może zostać zmodyfikowane w programie. Modyfikacja polega na przesłonieniu pewnych metod i zastąpieniu ich innymi. Użycie metody Dispose dla egzemplarza typu DialogBoxObj zwalnia wszystkie sterowania z tego okienka.
12. Wspomaganie prezentacji wyników pracy modelu
Wyniki uzyskane podczas przebiegu symulacji, stanowiące zbiory danych, można przedstawiać na wykresach. W języku MODSIM dostępne są obiekty do tworzenia wykresu dwuwymiarowego (ChartObj) i kołowego (PieChartObj). Wykres dwuwymiarowy jest używany do przedstawiania pewnej liczby zbiorów danych. Wykresy dwuwymiarowe mają różną postać, w zależności od typu wykresu (histogram, wykres słupkowy, wykres powierzchniowy, wykres stosowy). Typ wykresu ustala się w edytorze SIMDRAW podczas jego definiowania.
Dla wykresu pokazującego osie (dwuwymiarowy i słupkowy) należy w edytorze zdefiniować nazwę wykresu, opis osi, czynnik skali i sposób przedstawiania zbiorów danych (legendę). Dodatkowo w programie można zdefiniować: minimalną i maksymalną wartość dla osi (SetRanges), przedział jednostki (Setlntervals), sposób numerowania przedziałów (SetNumlntervals) i punkt przecięcia osi (Setlntercepts). Jeieli w edytorze graficznym nie zostały ustalone rozmiary i położenie wykresu, można je określić w programie, podając odpowiednio szerokość i wysokość w układzie współrzędnych obiektu ojca (SetViewbox) lub pozycję lewego górnego rogu wykresu (SetTranslation). Punkt jest umieszczany na wykresie za pomocą metody Plot. Dla wykresów przewijanych, używanych do śledzenia stanu zmiennych podczas przebiegu symulacji, osie są automatycznie przeskalowywane, gdy wartość na wykresie nie mieści się w zakresie osi y. Na wykresach można umieszczać etykiety (PIotLabelObj) zdefiniowane przez obiekt TextObj. Mogą być one umieszczane pod kątem w stosunku do osi x.
Wykres kołowy (PiechartObj) zawiera tytuł, wycinki koła i ich opis. Każdy wycinek pokazuje wartość w procentach sumy wszystkich wartości. Wycinki koła są numerowane kolejno, począwszy od wycinka zaczynającego się na godzinie 3:00 zegara, w kierunku od
wrotnym do kierunku ruchu wskazówek zegara. Wartości odpowiadające wycinkom koła można wyświetlić metodą DisplaySlice. Wartości procentowe są wyliczane automatycznie i wyświetlane w legendzie.
13. Animacja pracy modelu symulacyjnego na bieżąco
Podczas przebiegu symulacji, na ekranie komputera jest prezentowany graficzny model symulowanego systemu. Wszystkie obiekty modelu symulacyjnego, których obrazy tworzą ten model, są obiektami potomnymi typu ImageObj. Mogą być one rysowane, skalowane, umieszczane w określonym miejscu, obracane i kasowane. Obraz, tak jak każdy obiekt graficzny, może zawierać elementy składowe, które podlegają działaniom wykonywanym na całym obrazie, ale można też działać na nich indywidualnie. Hierarchia obrazów jest więc także reprezentowana przez drzewo obrazów.
Obrazy wykorzystywane do animacji, podobnie jak inne obiekty graficzne wykorzystywane do komunikacji z użytkownikiem, mogą być przygotowywane w edytorze graficznym i łączone z obiektami modelu według zasad opisanych w p. 14 lub też programowane (zob. p. 15).
Zachowanie się elementów modelu jest pokazywane na ekranie przez powiązanie każdej zmiany atrybutu elementu modelu ze zmianą stanu odpowiadającego mu obrazu. Wymaga to wykonania zmiany stanu atrybutów graficznych obiektu po zmianie stanu jego atrybutów zwykłych. Sama zmiana stanu atrybutów graficznych obiektu, ustalana przez metody Set*, jeszcze nie powoduje zmiany obrazu i wymaga dodatkowo narysowania obrazu obiektu. Pewne metody mają działania wykonujące zmianę stanu atrybutów graficznych z jednoczesną zmianą stanu ekranu (DisplayAt(x,y)). Jeżeli obraz, na którym jest wykonywana operacja (na przykład rysowanie lub kasowanie), jest obiektem złożonym, to operacja jest wykonywana także na jego potomkach.
Obrazy można tworzyć programowo przez generowanie egzemplarzy i ustawianie ich atrybutów. Jest to jednak czasochłonne i efekt widać dopiero po uruchomieniu programu. Znacznie wygodniej jest korzystać z edytora graficznego SIMDRAW, który pozwala na projektowanie obrazów, łączenie ich w grupy i zapamiętywanie w bibliotece obiektów graficznych. Utworzenie grupy obrazów w edytorze wymaga, aby program komunikował się z obrazami składowymi grupy poprzez ojca grupy.
Szczególnie interesujące jest pokazywanie obiektów przemieszczających się po obrazie modelu. Obiekty ruchome modelu są potomkami typuDynlmageObj, wyprowadzonymi pośrednio z typu DynamicObj. Obiekt dynamiczny może zostać uruchomiony (StartMotion), uaktualniony (DynamicUpdate) i zatrzymany (StopMotion). Aktualizacja obiektu uruchomionego, realizowana periodycznie przez procedurę upływu czasu, może być przedefiniowana, aby wykonać odpowiednie działania. Typ DynlmageObj dziedziczy zdolność ruchu po obiektach typów MovingObj, RotatingObj i ScalingObj. Typ MovingObj obsługuje ruch obiektu według parametrów ruchu, tj. jego kierunek i szybkość. Metoda SetCourse ustala kierunek ruchu przeciwny do ruchu wskazówek zegara (w stosunku do dodatniej osi x) i układu współrzędnych świata (kierunek ten jest podawany w radianach). Szybkość, podawana w jednostkach współrzędnych świata na jednostkę czasu, jest ustalana przez metodę SetSpeed. Obiekt może wykonać ruch przez przesunięcie go do określonego punktu (Move
To) lub wzdłuż ścieżki zdefiniowanej przez tablicę punktów (FollowPath). Ruch wzdłuż ścieżki może zostać przerwany przez procedurę Interrupt. Poruszanie się obiektów po określonym odcinku lub drodze na ekranie jest uruchamiane tylko jawnie. Przemieszczanie się obiektu z punktu, w którym znajduje się on na ekranie, do określonego punktu docelowego nie wymaga już wykonywania w programie jawnych działań. Jeżeli jednak poszczególne odcinki drogi poruszającego się obiektu są przebywane w różnych warunkach, na przykład z różnymi szybkościami, z badaniem możliwości przemieszczenia się do kolejnego położenia lub ze zmianą kierunku, pokonanie każdego fragmentu drogi musi być inicjowane osobno.
Typ RotatingObj obsługuje obracanie obiektów. Należy najpierw ustalić szybkość obracania podawaną w radianach na sekundę (SetRotationSpeed), a następnie wykonać obrót (Rotate7b).
Skalowanie obiektów obsługuje typ ScalingObj. Podobnie jak w przypadku typu RotatingObj, najpierw należy ustalić szybkość skalowania (SetScaleSpeed), a następnie wykonać skalowanie (ScaleTo).
Poruszanie się obiektu można zrealizować na dwa sposoby:
~ ustalić kierunek i szybkość, po czym zainicjować ruch (StartMotion),
v wykonać metodę TELL lub instrukcję WAIT FOR na zakończenie wykonywania metod realizujących ruch (MoveTo, FollowPath, RotateTo lub ScaleTo).
Wykonywanie metod realizujących ruch powoduje powrót do metody zamawiającej ruch (po czasie wykonania ruchu).
Obiekty graficzne ekranu podlegają wybieraniu za pomocą myszki. Wybrany obiekt można zidentyfikować, podobnie jak wybraną opcję oferty lub wybrane sterowanie. Przy wybieraniu obrazków za pomocą myszki obiekty, które mogą być wybrane, powinny mieć zdefiniowane własne metody przewidziane na wypadek wybrania, na przykład pokazujące szczegółowo atrybuty obiektu. Referencja do wybranego obiektu jest wartością metody Acceptlnput. Dodatkowo można zidentyfikować wybraną część składową obrazu przez wykorzystanie pola LastPicked wybranego obiektu. Obiekt podlegający wybieraniu musi mieć przesłoniętą metodę BeSelected, w której za pomocą metod myszki o nazwach ClickButton i ClickIsSecond można zidentyfikować użyty przycisk. Metoda będzie wywoływana automatycznie, gdy użytkownik naciśnie na dowolny przycisk myszki.
14. Łączenie modelu symulacyjnego systemu z jego modelem graficznym
Wykorzystywanie obrazów przygotowanych w edytorze graficznym wymaga jawnego powiązania (w programie) elementów programu z elementami obrazu. Składa się na nie szereg działań, różniących się nieco dla elementów obrazu zapamiętanych pojedynczo lub zapamiętanych jako grupa. Jeżeli przewiduje się wykonywanie działań dla pojedynczych obrazów zapamiętanych jako grupa, to należy:
a) zadeklarować obiekt okna typu WindowObj,
b) zadeklarować obiekt biblioteki typu GraphicLibObj, c) zadeklarować obraz ojca i obrazy potomków,
d) wygenerować obraz okna,
e) narysować obraz okna,
f) wygenerować egzemplarz biblioteki,
g) wygenerować egzemplarz ojca i egzemplarze potomków, h) odczytać bibliotekę z pliku,
i) wczytać obraz ojca z biblioteki,
j) pobrać obrazy dzieci z obiektu ojca i przypisać je obiektom dzieci, k) dodać obraz ojca do okna,
1) narysować obraz ojca lub okno.
Dla obrazów zapamiętanych pojedynczo operacje i) oraz k) należy wykonać dla każdego elementu osobno zapamiętanego w bibliotece, a operacje j) i inne, dotyczące obiektu ojca, są pomijane.
Aby powiązać obrazy przygotowane w edytorze z obiektami modelu symulowanego systemu, należy zatem wykonać w programie szereg jawnych działań inicjujących obraz oraz działań, które będą nanosiły na obraz modyfikacje wynikające ze zmiany stanu jawnie określanych w programie nowych wartości atrybutów graficznych obiektu.
Lokalizacja obrazów na ekranie i ich przemieszczanie wymaga znajomości układu współrzędnych, w którym obraz jest umieszczony. Pozycja dowolnego obrazu jest wyspecyfikowana jako współrzędne jego środkowego punktu w układzie współrzędnych ojca. Środkiem dowolnego obrazu jest punkt o współrzędnych (0.0, 0.0). Układ współrzędnych jest określany metodą SetWorld. Obraz, który nie ma określonego układu współrzędnych, używa układu współrzędnych ojca. Jeżeli żaden z przodków nie ma ustawionego układu współrzędnych, to jest używany układ domyślny (0..32767, 0..32767). Jeśli jakiś obraz składowy okna ma być umieszczony centralnie, określa się jego położenie jako (0.0, 0.0). Pozostałe obiekty muszą być umieszczane w położeniach uwzględniających ich fizyczne odległości od obiektu centralnego w symulowanym systemie.
W pakiecie SIMGRAPHICS znajdują się udogodnienia do wykorzystywania grafiki wektorowej i rastrowej. Obrazy (mapy) bitowe przygotowane w formacie rastrowym mogą być dostarczane w plikach i mogą być wykorzystywane jako obrazy tła i rysunków lub być używane w kombinacji z obrazami wektorowymi. Wykorzystywanie mapy bitowej, jako statycznego tła modelu, powoduje szybsze rysowanie okna, niż dla okna przygotowanego w grafice wektorowej. Dla współpracy ze środowiskiem Microsoft Windows dostarczono format BME Do importowania do programu i wyświetlania map bitowych służy obiekt typu SnapShotObj. Nazwa pliku, w którym znajduje się mapa bitowa, jest podawana w jego polu File. Mapy bitowe nie nadają się do przedstawiania obrazów, które często muszą być skalowane i obracane.
Dla mapy bitowej można zdefiniować (w obiekcie typu SnapShotObj) maskę, która określi, że część obrazu tła może przeświecać. Maska stanowi drugą wersję mapy bitowej i jest zamieszczona w pliku o nazwie pliku mapy bitowej z końcową literą "M". Skalowanie obrazu, jakkolwiek dopuszczalne, często może prowadzić do jego zniekształcenia i nie powinnno być stosowane. Obraz tego typu nie podlega też obracaniu. Może być jednak dodawany do hierarchii obrazów, łącznie z obrazami wektorowymi. Przy przemieszczaniu (SetTranslation) należy podawać współrzędne lewego dolnego narożnika. Obraz w postaci mapy bitowej może być ponadto podświetlany (HighlightedColor).
15. Programowanie obrazów
Niekiedy istnieje potrzeba używania obrazów tworzonych dynamicznie, tj. podczas pracy programu symulacyjnego. Obrazy tego typu muszą być generowane, mieć programowo określane atrybuty, a następnie być rysowane. Wszystkie elementarne obrazy, zdefiniowane w module GTypes, są potomkami obiektu typu PrimitiveVObj. Atrybutem tego obiektu jest jednowymiarowa tablica punktów typu PointArrayType złożona z rekordów typu PointType. Punkty obrazu elementarnego definiują jego kształt. Liczba punktów jest określona przez wymiar tablicy, a położenia punktów określa się metodą SetPoints. Do elementarnych obrazów programowanych należą: linia łamana (PolylineObj), wielobok (PolygonObj), linia punktowa (PolyMarkObj), tekst (TexżObj), łuk (ArcObj), koło (CircleObj) i wycinek koła (SectorObj).
Linia łamana jest zbiorem odcinków łączących punkty z tablicy. Atrybutami linii są: ~ rodzaj (Style) - ze zbioru ośmiu dopuszczalnych rodzajów linii zdefiniowanych przez typ wyliczeniowy LineStyleType (definicja tego typu znajduje się także w module G7ypes),
v szerokość linii (Width) - określona w wymiarach układu współrzędnych świata rzeczywistego, skalowana przy skalowaniu obrazu przodka,
v szerokość linii (PctWith) - określona w procentach rozmiarów okna, nie podlegająca skalowaniu przy skalowaniu linii lub obrazu przodka.
Łuk posiada te same atrybuty, co linia łamana. Określają one rodzaj i szerokość linii. Parametry łuku są przechowywane w trzypunktowej tablicy. Pierwszy punkt określa położenie środka łuku, drugi - współrzędne początku, a trzeci - współrzędne jego końca, przy czym łuk jest rysowany przeciwnie do ruchu wskazówek zegara.
Wielobok określa granice przestrzeni wypełnianej według rodzaju wypełnienia (Style) i typu FillStyleType (osiem wartości).
Koło jest opisane przez dwa punkty: współrzędne środka i współrzędne dowolnego punktu na okręgu i jest obrazem wypełnianym tak, jak wielobok. Wycinek kołowy jest obrazem wypełnianym, a jego pozostałe atrybuty są określane tak samo, jak dla łuku.
Tekst zdefiniowany przez typ TextObj jest wyświetlany graficznie. Jego atrybuty określają jego rozmiar, krój (spośród ośmiu dostępnych), napis i ustawienie. Metoda SetAlignment pozwala określić ustawienie poziome lub pionowe. Położenie pionowe opiera się na komórce znakowej, która może być położona nad lub pod aktualnym znakiem. W położeniu poziomym tekst może być centrowany lub wyrównywany lewo- lub prawostronnie. W położeniu pionowym tekst może być wyrównywany od dołu, od góry, centrowany lub umieszczany w komórce, która ma określone położenie góry lub dołu. Rozmiar tekstu może być określony przez rozmiary prostokąta, w którym tekst ma być umieszczony (SetSize), podawane w wymiarach współrzędnych świata (może wówczas podlegać skalowaniu). Przy podawaniu rozmiarów tekstu w procentach ekranu (SetPctHeight) tekst ma zawsze ten sam rozmiar i nie podlega skalowaniu. Tekst może być obracany (za pomocą metody SetRotation).
Linia punktowa jest zbiorem znaczników punktów, rysowanych według położeń podanych w tablicy punktów. Znacznik punktu ma także rozmiar i rodzaj, który określa kształt
znacznika (sześć możliwych wartości). Zasady określania wymiarów znaczników punktów są takie same, jak dla tekstu (SetSize, SetR;tSize).
Wszystkie części składowe obrazu muszą być zadeklarowane jako egzemplarze obiektów typu odpowiedniego do kształtu części składowej obrazu. Każda z części musi być wygenerowana i musi mieć określone atrybuty polożenia, koloru, rodzaju i kształtu. Obrazy składowe muszą być dodane do obrazu głównego (AddGraphic), który następnie musi być wyświetlony we wskazanym położeniu. Rysowanie w programie jest więc uciążliwe, ale wykorzystywanie 'obiektów elementarnych obrazów jest niezbędne, jeźeli uźytkownik musi je wprowadzać jako dane do programu.
16. Wykrywanie błędów w programach symulacyjnych
Kompilator języka wykrywa błędy składniowe, podobnie jak inne kompilatory języków algorytmicznych. W wykazie błędów wymieniono 187 błędów wykrywanych na etapie kompilacji. Niestety znaczna część błędów jest wykrywana dopiero na etapie łączenia. Podczas pracy programu wykrywane są błędy wykonania, związane z niewłaściwymi operacjami na strukturach danych (przy danym stanie struktury), niewłaściwymi wartościami argumentów instrukcji lub parametrów metod i przekroczeniem rozmiarów pamięci przeznaczonej na zmienne dynamiczne. Łącznie w wykazie błędów wymieniono 47 przyczyn błędów wykonania.
Rozróżnialność dużych i małych liter, a także pewna niejednolitość w nazewnictwie, na przykład słowa kluczowe pisane wielkimi literami, standardowe procedury, pola obiektów i ich metody często opisane literami o różnych rozmiarach, są przyczynami wielu błędów popełnianych przez użytkownika.
17. Uwagi końcowe
Przedstawiony język MODSIM II umożliwia programowanie strukturalne powiązane z programowaniem zorientowanym obiektowo. Tworzone programy korzystają z bogatych bibliotek obiektów standardowych języka i są modularne. Na podstawie obiektów standardowych, wykorzystując wielodziedziczenie, użytkownik może tworzyć definicje nowych typów obiektowych przez rozszerzanie listy atrybutów odziedziczonych i rozszerzanie lub przesłanianie odziedziczonych metod.
Instrukcje synchronizacji wykonywania metod obiektów pozwalają zarówno na stosowanie metody programowania symulacji zorientowanej na procesy, jak i symulacji zorientowanej na obiekty (3], wykorzystującej do synchronizacji działań obiektów wyłącznie metody TELL. Przy zastosowaniu do programowania modelu symulacji zorientowanej obiektowo działania obiektu reprezentowane przez metody planujące zdarzenia są związane z obiektami obsługującymi.
Przy programowaniu symulacji procesowej podział działań obiektu na metody nie wymusza na użytkowniku wiązania procesów z określonymi typami obiektów, na przykład z obiektami obsługiwanymi lub obsługującymi. Metody obiektów można synchronizować czasowo albo też wznawiać ich wykonywanie po zakończeniu wykonywania jakiejś zaplanowanej metody lub zmianie stanu przerzutnika. W jednej metodzie TELL można zasto
sować wiele instrukcji WAIT, odpowiadających kolejnym fazom obsługi obiektu obsługiwanego, lub też każdą fazę obsługi prowadzoną przez obiekt obsługujący można opisać jako odrębną metodę. Pozostawia się użytkownikowi pełną dowolność. Jest to jednocześnie zaletą języka i jego wadą, gdyż nie wymusza on na użytkowniku jakiegoś systematycznego podejścia do programowania modelu. Takie podejście jest wymagane przy zastosowaniu symulacji zorientowanej na obiekty. W tym przypadku jedna metoda obiektu inicjuje jedną fazę obsługi prowadzoną przez obiekt, odmierzaną przez zaplanowane zdarzenie zakończenia tej fazy.
W roku akademickim 1993/94 system MODSIM był wykorzystywany przez studentów III roku Wydziału Informatyki i Zarządzania Politechniki Wrocławskiej w ramach laboratorium towarzyszącego wykładowi z metod symulacyjnych. System zainstalowano w Centrum Informatycznym na serwerze obsługującym laboratorium sieci Novell wyposażone w 10 komputerów PC 386 z 8 MB pamięci RAM. Ponieważ do pracy w języku MODSIM jest wymagane korzystanie z kompilatora języka C++ lub Visual C, na cały system trzeba przeznaczyć na dysku stałym około 70 MB. Jest to minimalna konfiguracja zalecana przez producenta do współpracy ze środowiskiem Microsoft Windows. Według opinii studentów praca systemu przy tej konfiguracji jest bardzo wolna. Długo czeka się na na przeprowadzenie dwustopniowej kompilacji: z języka MODSIM na język C++ i z języka C++ na program wynikowy oraz łączenie z bibliotekami obydwu języków. Również znaczne pogorszenie jakości pracy obserwuje się przy obsłudze, przez program symulacyjny, większej liczby obiektów przemieszczających się po ekranie (ruchy tracą na płynności). W celu polepszenia warunków pracy z systemem należałoby wyposażyć laboratorium w komputery wyższej klasy.
Mimo pewnych innych niedogodności w pracy z systemem, zaobserwowanych przez studentów, w ramach 15 godzin laboratorium i wykonania pracy zaliczeniowej, zdołali oni opanować system oraz wykonać szereg interesujących programów symulacyjnych z animacją, ciekawszych i obszerniejszych niż standardowe programy przykładowe dostarczone przez producenta (chociaż były to modele dość proste). Opracowane modele potwierdzają wyższość modeli z animacją pracy modelu na bieżąco nad modelami symulacyjnymi śledzącymi stan modelu na wykresach i wytwarzającymi ilościowe raporty końcowe o pracy modelu [3]. Wyniki pracy studentów świadczą o mocy języka i jego przydatności do nauczania programowania symulacji, a także metodologii badań przeprowadzanych na modelu.
Dużą wadą systemów symulacyjnych, w tym także systemu MODSIM, jest ich wysoka cena (1700 funtów dla wyższych uczelni i 20-krotnie wyższa dla zastosowań komercyjnych). System MODSIM został zakupiony niezależnie przez kilka wyższych uczelni, w tym oprócz Politechniki Wrocławskiej przez Politechnikę Krakowską, Poznańską i Warszawską, przy czym zakupu dokonywano bezpośrednio u producenta - w firmie CACI. Firma ta jest także producentem systemów definiowania modeli symulacyjnych, takich jak NET WORK, COMNET, SIMFACTORY, SIMMOD. Korzystanie z tych systemów do tworzenia modeli symulacyjnych wymaga już tylko znajomości symulowanego systemu rzeczywistego i nie wymaga znajomości programowania.
Wydaje się, że spopularyzowanie metody badań systemów rzeczywistych na ich modelach symulacyjnych przy dostępności tak mocnych narzędzi, jakim jest niewątpliwie system MODSIM, byłoby celowe i korzystne nie tylko dla nauki, ale także dla gospodarki.
Literatura
(1] Belanger, R., MODSIM ll, The High Level Object Ońented Language, CACI Products Company. (2] Coad, E, Object Oriented Patterns, Communication of the ACM, VoI. 35, 9 (1992).
(3] Koleśnik, K., Koncepcja symulacji obiektowej na bazie języka lLrbo Pascal 5.5, Informatyka 4 (1992). (4] Koleśnik, K., MODSIM II, Procesowy język symulacyjny zorientowany obiektowo, Część I: Programowanie symulacji i animacji, Centrum Informatyczne Politechniki Wrocławskiej, Raport Serii SPR 13 ( 1994).
[5] MODSIM II, The Language for Object-Oriented Programmin~ Reference Manual, CACI Products Company, 1991
(6] MODSIM Tutońal, CACI Products Company, 1991.
(7] Naylor, T H., Modelowanie cyfrowe systemów ekonomicznych, Pafistwowe Wydawnictwo Naukowe, Warszawa 1975.
(8] Oktaba, H., Ratajczak, W, SZMULA 67, Wydawnictwa Naukowo-'Ibchniczne, Warszawa 1980. (9] SIMGRAPHICS II, User ś Manual for MODSIM II, CACI Ptoducts Company, 1993.
(10] Wegner, E, Learning the language, Byte, March 1989, 245-253.