5 Wyjątki i podprocesy

Po spotkaniu: zapiszesz w tok zdarzenia graniczne przerywające i nieprzerywające (termin, błąd, eskalacja, wiadomość) · zbudujesz podproces i podproces zdarzeniowy z własnym początkiem i końcem · przeprowadzisz model przez podróż tok → BPMN → Camunda Modeler → tok i ocenisz, co się w niej zmieniło · Czas: Nauka 40 min, Wyzwanie 50 min

5.1 Nauka

Wyjątki w tekście

Na kursie BPMN wyjątki modelowaliśmy zdarzeniami granicznymi (boundary events), zob. rozdział 6 kursu BPMN. Kółko przyczepione do krawędzi zadania mówi: „jeśli w trakcie tej pracy coś się stanie, idź tą ścieżką”. W tok zdarzenie graniczne to osobny wiersz ze słowem boundary. O tym, do której czynności jest przyczepione, decyduje atrybut on=:

user odbior "Odbiór książki"
boundary termin "Minęło 7 dni" on=odbior timer=P7D
boundary przypomnienie-czas "Minęły 3 dni" on=odbior timer=P3D noninterrupting

Zdarzenie graniczne ma trzy części:

Część Przykład Znaczenie
on= on=odbior identyfikator czynności, na której krawędzi wisi zdarzenie
wyzwalacz timer=P7D, error="Błąd zapisu", escalation="Trudna kwerenda", message="Odwołanie", signal="Zamknięcie czytelni" co uruchamia wyjątek
flaga noninterrupting zadanie trwa dalej, a ścieżka wyjątku rusza równolegle

Bez flagi noninterrupting zdarzenie jest przerywające: gdy zajdzie, zadanie zostaje odebrane. To ustawienie domyślne, tak jak w BPMN. Błąd (error=) jest zawsze przerywający. Zapis boundary … error="…" noninterrupting walidator odrzuci kodem E012.

Czas zapisujesz w formacie ISO 8601: P7D to 7 dni, PT2H to 2 godziny, P1M to miesiąc. Wartość z ukośnikiem, np. cykl R3/PT1H, piszesz w cudzysłowie.

Ze zdarzenia granicznego wychodzi zwykły przepływ sekwencji: flow termin -> odeslanie -> zwrocona. Do zdarzenia granicznego nic nie wchodzi, bo uruchamia je czynność, a nie poprzedni krok.

Przykład: odbiór książki z terminami

Książka zamówiona w innej bibliotece dotarła. Bibliotekarz rejestruje ją w systemie. Jeśli zapis się nie uda, rejestruje ją ręcznie. Czytelnik ma 7 dni na odbiór. Po 3 dniach dostaje przypomnienie, ale nadal może odebrać książkę. Po 7 dniach książka wraca do biblioteki udostępniającej.

tok 0.1 "Odbiór książki z wypożyczalni międzybibliotecznej"

pool biblioteka "Biblioteka uczelniana"
  lane bibliotekarz "Bibliotekarz wypożyczalni międzybibliotecznej"
    start dotarla "Książka dotarła"
    service rejestracja "Rejestracja egzemplarza w systemie"
    boundary blad-zapisu "Błąd zapisu" on=rejestracja error="Błąd zapisu"
    user reczna "Rejestracja ręczna"
    user powiadomienie "Powiadomienie czytelnika"
    user odeslanie "Odesłanie książki"
    end zwrocona "Książka zwrócona bibliotece udostępniającej"
  lane czytelnik "Czytelnik"
    user odbior "Odbiór książki"
    boundary przypomnienie-czas "Minęły 3 dni" noninterrupting on=odbior timer=P3D
    boundary termin "Minęło 7 dni" on=odbior timer=P7D
    end odebrana "Książka odebrana"
  lane system "System biblioteczny"
    service przypomnienie "Wysłanie przypomnienia"
    end przypomniano "Przypomnienie wysłane"

flow dotarla -> rejestracja -> powiadomienie -> odbior -> odebrana
flow blad-zapisu -> reczna -> powiadomienie
flow przypomnienie-czas -> przypomnienie -> przypomniano
flow termin -> odeslanie -> zwrocona
Odbiór książki: błąd zapisu oraz terminy przerywający i nieprzerywający.

Rysunek 5.1: Odbiór książki: błąd zapisu oraz terminy przerywający i nieprzerywający.

Pobierz model (.tok) Pobierz eksport BPMN (.bpmn)

Sprawdź, jak opis przekłada się na tekst:

Zdanie z opisu Wiersz tok
„jeśli zapis się nie uda” boundary blad-zapisu … on=rejestracja error="Błąd zapisu"
„po 3 dniach dostaje przypomnienie, ale nadal może odebrać” boundary przypomnienie-czas … on=odbior timer=P3D noninterrupting
„po 7 dniach książka wraca” boundary termin … on=odbior timer=P7D

Zdarzenia graniczne deklarujesz w linii, w której mają być narysowane, zwykle w linii swojej czynności. Ścieżka wyjątku może prowadzić do innej linii, jak tu przypomnienie wysyłane przez system.

Podproces

Podproces (sub-process) to kilka kroków, które tworzą całość. W tok to słowo sub z blokiem. Wszystko, co jest wcięte pod nim, należy do podprocesu:

sub poszukiwania "Poszukiwania w zasobie"
  start p-start
  user inwentarze "Przegląd inwentarzy"
  end p-koniec "Poszukiwania zakończone"

Zasady są te same co w BPMN:

  • podproces ma własny start i własny koniec. Start wewnątrz zwykłego podprocesu nie ma wyzwalacza;
  • przepływy sekwencji nie przekraczają granicy podprocesu. Proces główny łączy się z podprocesem jako całością: flow zakres -> poszukiwania, a nie z jego wnętrzem.

Przepływy wnętrza podprocesu możesz zapisać razem z innymi na końcu dokumentu. tok sam rozpozna, że oba końce leżą w podprocesie. Jeśli połączysz element procesu głównego z elementem wnętrza podprocesu, dostaniesz błąd. Dla modelu, w którym start procesu prowadzi wprost do zadania wewnątrz podprocesu, tok check --lang pl wypisuje:

cross.tok:10: błąd R021: przepływ sekwencji s -> szukaj przekracza granicę procesu lub podprocesu

Na diagramie podproces jest rozwinięty: widać jego wnętrze w ramce. Elementy wnętrza nie należą do linii. Do linii należy cały podproces.

Eskalacja z podprocesu

Eskalacja to sygnał „ta sprawa wymaga decyzji wyżej”, ale bez przerywania pracy. Rzuca ją zdarzenie wewnątrz podprocesu, a łapie zdarzenie graniczne na jego krawędzi. Oba łączy ta sama nazwa eskalacji:

sub poszukiwania "Poszukiwania w zasobie"
  ...
  throw trudnosc "Kwerenda wymaga decyzji kierownika" escalation="Trudna kwerenda"
  ...
boundary eskalacja "Trudna kwerenda" on=poszukiwania escalation="Trudna kwerenda" noninterrupting

Flaga noninterrupting na granicy oznacza, że poszukiwania trwają dalej, a kierownik równolegle podejmuje decyzję. Jeśli rzucisz eskalację, której nic nie łapie, walidator zgłosi ostrzeżenie W053.

Podproces zdarzeniowy

Podproces zdarzeniowy (event sub-process) uruchamia się w dowolnym momencie trwania procesu, gdy zajdzie jego zdarzenie początkowe. W tok to słowo eventsub. Jego blok zaczyna się zdarzeniem początkowym z wyzwalaczem:

eventsub wycofanie "Wycofanie wniosku"
  start w-start "Wniosek wycofany" message="Wycofanie wniosku"
  user zamkniecie "Zamknięcie sprawy"
  end w-koniec "Sprawa zamknięta"

Podproces zdarzeniowy nie ma przepływów wchodzących ani wychodzących. Jego start jest domyślnie przerywający: kończy resztę procesu. Flaga noninterrupting na starcie (start … message="…" noninterrupting) sprawia, że podproces działa obok procesu głównego.

Przykład: kwerenda z podprocesem

Poniższy model łączy wszystkie konstrukcje: podproces z eskalacją, podproces zdarzeniowy wywoływany wiadomością od wnioskodawcy i zdarzenie końcowe terminate.

tok 0.1 "Kwerenda archiwalna z podprocesem"

pool archiwum "Archiwum Państwowe"
  lane kancelaria "Kancelaria"
    start wniosek "Wniosek o kwerendę wpłynął" message="Wniosek o kwerendę"
    user rejestracja "Rejestracja wniosku"
    xor zakres "Czy wniosek dotyczy zasobu archiwum?"
    end bezprzedmiotowy "Wniosek bezprzedmiotowy" terminate
    eventsub wycofanie "Wycofanie wniosku"
      start w-start "Wniosek wycofany" message="Wycofanie wniosku"
      user zamkniecie "Zamknięcie sprawy"
      end w-koniec "Sprawa zamknięta"
  lane archiwista "Archiwista"
    sub poszukiwania "Poszukiwania w zasobie"
      start p-start
      user inwentarze "Przegląd inwentarzy"
      xor znaleziono "Czy znaleziono akta?"
      user akta "Analiza akt"
      throw trudnosc "Kwerenda wymaga decyzji kierownika" escalation="Trudna kwerenda"
      end p-koniec "Poszukiwania zakończone"
    boundary eskalacja "Trudna kwerenda" noninterrupting on=poszukiwania escalation="Trudna kwerenda"
    user odpowiedz "Przygotowanie odpowiedzi"
    end wyslana "Odpowiedź wysłana" message="Odpowiedź na kwerendę"
  lane kierownik "Kierownik oddziału"
    user decyzja "Decyzja o dalszych poszukiwaniach"
    end rozstrzygnieto "Sprawa rozstrzygnięta"

pool wnioskodawca "Wnioskodawca"

flow wniosek -> rejestracja -> zakres
flow zakres -> poszukiwania "tak"
flow zakres -> bezprzedmiotowy "nie"
flow poszukiwania -> odpowiedz -> wyslana
flow p-start -> inwentarze -> znaleziono
flow znaleziono -> akta "tak"
flow znaleziono -> trudnosc "nie"
flow trudnosc -> akta -> p-koniec
flow eskalacja -> decyzja -> rozstrzygnieto
flow w-start -> zamkniecie -> w-koniec

message wnioskodawca -> wniosek "Wniosek o kwerendę"
message wyslana -> wnioskodawca "Odpowiedź na kwerendę"
message wnioskodawca -> w-start "Wycofanie wniosku"
Kwerenda archiwalna: podproces z eskalacją i podproces zdarzeniowy.

Rysunek 5.2: Kwerenda archiwalna: podproces z eskalacją i podproces zdarzeniowy.

Pobierz model (.tok) Pobierz eksport BPMN (.bpmn)

Flaga terminate przy end bezprzedmiotowy oznacza zdarzenie końcowe, które natychmiast kończy cały proces, także ścieżki równoległe. W tym modelu nie ma ich na tej gałęzi. W modelach z bramką równoległą terminate rozstrzyga, że po odrzuceniu wniosku żadna inna ścieżka nie będzie kontynuowana.

Kompensacja i wywołanie procesu

Dwie konstrukcje wystarczy na razie znać z nazwy:

Zapis Znaczenie
boundary cofnij "Cofnięcie rezerwacji" on=rezerwacja compensate zdarzenie graniczne kompensacji na zadaniu
user anulowanie "Anulowanie rezerwacji" compensation czynność kompensacyjna: cofa skutki wykonanej pracy, nie ma przepływów sekwencji
throw kompensacja "Wycofanie zmian" compensate zdarzenie, które uruchamia kompensację
call szkolenie "Organizacja szkolenia" process=organizacja-szkolenia czynność wywołania: uruchamia inny, osobno opisany proces

Kompensacja przydaje się w procesach, w których coś trzeba „odkręcić”, np. anulować rezerwację sali, gdy szkolenie zostaje odwołane. Czynność wywołania pozwala opisać raz proces używany w wielu miejscach, np. obsługę płatności.

Podróż tok → BPMN → tok

Model tok nie zamyka cię w jednym narzędziu. Możesz go wyeksportować, poprawić w Camunda Modeler i wrócić do tekstu. Ta podróż ma trzy etapy:

  1. Eksport: tok bpmn model.tok tworzy model.bpmn z układem diagramu.
  2. Praca w Modelerze: otwierasz plik, zmieniasz model (przenosisz zadanie do innej linii, poprawiasz nazwę, dodajesz krok) i zapisujesz.
  3. Import: tok import model.bpmn -o model2.tok zamienia plik BPMN z powrotem na tekst.

Jeśli w Modelerze nic nie zmienisz, import odtwarza dokładnie ten sam tekst. Sprawdziliśmy to na modelu odbioru książki: plik po podróży nie różni się od oryginału ani jednym znakiem.

Po zmianie w Modelerze porównaj oba pliki tekstowe, np. w VS Code. Zobaczysz tylko wiersze, których dotyczy zmiana. Na modelu odbioru książki przeniesiono w Modelerze zadanie „Powiadomienie czytelnika” do linii systemu i zmieniono jego nazwę. W imporcie:

  • wiersz user powiadomienie … przeszedł z linii bibliotekarza do linii „System biblioteczny” i ma nową etykietę;
  • linia „System biblioteczny” przesunęła się w tekście wyżej. Import układa linie w kolejności, w jakiej pojawiają się ich elementy w procesie. Teraz w tej linii jest zadanie wykonywane wcześnie;
  • przepływy i pozostałe elementy się nie zmieniły.

Dwie rzeczy warto wiedzieć o imporcie:

  • Układ diagramu nie wraca do tekstu. tok nie zapisuje współrzędnych, więc przesunięcie prostokąta w Modelerze nie zmienia modelu. Przy następnym tok bpmn układ zostanie obliczony od nowa. To zamierzone: tekst przechowuje znaczenie, a rysunek jest widokiem.
  • Identyfikatory przepływów z Modelera. Gdy w Modelerze dorysujesz strzałkę, dostanie ona identyfikator typu Flow_0x7k2ab. Import zachowuje go i drukuje przepływ jako osobny wiersz z id=…. Opcja --no-flow-ids pomija identyfikatory przepływów i pozwala złożyć je z powrotem w łańcuchy. Używaj jej, gdy dalej pracujesz na tekście, a identyfikatory przepływów nie są ci potrzebne.

Import przyjmuje też pliki narysowane od zera w Modelerze, np. modele z kursu BPMN. Elementy spoza tok 0.1 (np. bramkę złożoną) pomija i wypisuje o nich ostrzeżenie.

5.2 Wyzwanie

Polecenie: zapisz w tok proces „Digitalizacja na zamówienie w bibliotece cyfrowej” z dwoma basenami, danymi i wyjątkami, a potem przeprowadź go przez podróż do Camunda Modeler i z powrotem.

Dane wejściowe: tabela kroków w opisie zadania Z2 (dodatek C). Proces znasz z kursu BPMN. Tym razem ma dodatkowo zamknięty basen „Zamawiający” i przepływy komunikatów.

Kroki:

  1. Ustal baseny, linie, typy elementów i wyjątki. Zwróć uwagę na trzy różne wyjątki: termin 14 dni, termin 5 dni i awarię skanera. Każdy ma inny charakter.
  2. Napisz z2-digitalizacja.tok, sprawdź go (tok check --lang pl) i uporządkuj (tok fmt -w).
  3. Wyeksportuj BPMN i SVG, otwórz model w Camunda Modeler i wprowadź zmianę opisaną w zadaniu.
  4. Zaimportuj zmieniony plik, porównaj teksty i opisz wynik w z2-podroz.md.

Rezultat: pliki wymienione w opisie zadania Z2.

Praca: indywidualnie, bez narzędzi sztucznej inteligencji.

Czas: 50 minut na zajęciach. Zadanie dokończ przed spotkaniem 6.

Zadanie Z2. Model z basenami, danymi i wyjątkami oraz jego podróż przez BPMN (20%). Oddajesz model z2-digitalizacja.tok z eksportem BPMN i SVG, model po zmianie w Camunda Modeler zaimportowany z powrotem do tok oraz krótki opis tej podróży. Pełne polecenie i kryteria oceny są w dodatku C.

Na co zwrócić uwagę

  • „Konsultacja nie zostaje przerwana” to sygnał zdarzenia nieprzerywającego. „Skanowanie zostaje przerwane” to sygnał zdarzenia przerywającego.
  • Oczekiwanie na akceptację z terminem możesz zapisać jako zadanie odbioru (receive) z przerywającym zdarzeniem granicznym czasowym. Wtedy wiadomość od zamawiającego kierujesz na to zadanie.
  • Po zdarzeniu granicznym błędu ścieżka może wrócić do wcześniejszego kroku. W tok to zwykły flow do identyfikatora tego kroku.
  • Zanim otworzysz plik w Modelerze, zrób kopię .tok. Porównanie dwóch plików tekstowych jest najprostszym sposobem, żeby zobaczyć, co naprawdę zmieniłeś.

5.3 Pytania sprawdzające

  1. Jak zapisać w tok termin 10 dni na zadaniu „Poprawa wniosku”, po którym zadanie zostaje odebrane? Co zmieni dopisanie flagi noninterrupting?
  2. Dlaczego zdarzenie graniczne błędu nie może mieć flagi noninterrupting?
  3. Co oznacza błąd R021 w modelu z podprocesem? Jak go poprawić?
  4. Jak w tok połączyć eskalację rzuconą wewnątrz podprocesu ze zdarzeniem granicznym na jego krawędzi?
  5. Co traci model w podróży tok → BPMN → tok, a co zachowuje? Dlaczego utrata układu diagramu nie jest problemem?