Állapotgép
Az állapotgép az objektum működésének szemléltetésére, illetve a tervezés segítésére van.
Megvalósítása külön nem szükséges, érvényesülni a metódusok hívásain keresztül fog történni!
Objektum életciklusa
Életciklus lehetséges jelenségei
Az objektum
- Létrejön
- Példányosodik (Konstruktor)
- Változik
- Metódusait hívják, adattagjait megváltoztatják
- Megszűnik
- Megsemmisül (Destruktor)
Objektum különböző állapotai (state)
- Fizikai állapot
- Az objektum adatai által felvett értékek együttese
- Logikai állapot
- Valamilyen szempont szerint közös tulajdonságú fizikai állapotok összessége(halmaza)
Az objektum állapota valamilyen esemény hatására változhat meg
Esemény (event)
- Üzenet (trigger)
- Tevékenység befejezője
- Őrfeltétel (guard) teljesülése
- Logikai állítás
- When
- Időhöz kötött várakozás
- After
- Logikai állítás
Állapot-átmenet gráf
Az objektum életciklusát, logikai állapotának változásait, azokra épülő működését lehet állapot-átmenet gráffal ábrázolni, ez az objektum állapotgépe.
Leírása egy olyan irányított gráffal lehetséges, ahol a csúcsok a logikai állapotok, az élek az állapot-átmenetek, s mind az állapothoz, mind az átmenetekhez tevékenységek tartoznak.

Az új aktív állapot egyértelműen választódik ki a régiből kivezető élek által mutatott állapotok közül.
Állapotok
- Jelzése a kerekített sarkú téglalap, ami kaphat nevet, de maradhat anonim is
- Az állapot neve mellett
[]-közé írható az objektum fizikai állapotára tett logikai állítás- Egy állapotgép állapotainak feltételei teljesen diszjunkt rendszert kell, hogy alkossanak
- Az állapottot jelző téglalapvan felsorolhatók az állapothoz rendelt különvéle tevékenységek
Állapotok tevékenységei
<állapot> |
|---|
enter / <belépési tevékenység> |
do / <belső tevékenység> |
exit / <kilépési tevékenység> |
<üzenet>(<paraméterek>)/<tevékenység> |
Ezek a folyamatok az aktív állapotban hajtódnak végre.
Fontos megjegyezni, hogy kizárólag egy aktív állapot lehet!
Belépési tevékenység
Az állapot aktivációjával hajtódik végre (és mindíg be is fejeződik)
Belső tevékenység
Lehet végtelenített, vagy befejeződő, amely legfeljebb addig tart, amíg az állapot aktív
Kilépési tevékenység
Akkor hajtódik végre(és mindíg be is fejeződik), amikor az állapot passzívvá válik
Triggerelt tevékenységek
Olyan üzenet váltja ki, amely során az állapot nem változik, így sem a ki-, sem a belépési feltétel nem hajtódik végre
Szokás még belső átmeneti tevékenységnek is nevezni
Tevékenységek sorrendje
Állapotváltásnál a sorrend rendre
- Az aktív állapot kilépési tevékenysége
- Az átmenethez tartozó tevékenység (ha van ilyen)
- Az új állapot belépési tevékenysége
- Az új állapot belső tevékenysége
Az átmenet lehet reflexív is, tehát visszavezethet önmagába
Pl, az állapot inaktívvá válik, amíg a másik állapotban a belső tevékenység lefut, majd visszatér az előző állapotba.
Hierarhikus állapotok
Ilyenkor az állapot már annyira összetett, hogy azt egy saját állapotgéppel reprezentálunk
Jelölni kell a be- és kilépési pontokat, a kezdő és befejező állapotot is

Pszeudo állapotok

A kezdő állapotból csak egy átmenet indulhat, ez szimbolizálja az objektum létrejöttét
A végállapot jelöli az objektum megszűnését
Állapot átmenet

- Egy állapotok közti átmenet mindíg egy esemény hatására következik be
- Ha az esemény egy üzenet(szignál, vagy metódus hívás), akkor nevét az átmenet nyilára tesszük
- Az üzenetnek lehetnek paramérerei, kapcsolódhatnak hozzá őrfeltétel is
- Ha az őrfeltétel logikai, akkor az a paraméterektől és az objektum adattagjaitól függ
- Az átmenethez rendelt tevékenység az objektum adattagjaival és az esetleges kiváltó üzenet paramétereivel operáló program
Állapot átmenet tábla
A diagram működése táblázattal is leírható (jelentésük ekvivalens)
\startjelöli a kezdő állapotot

Üzenet vezérelt állapot-átmenetek

Akkor következik be, amikor az átmenetet egy üzenet váltja ki.
Bekövetkezése:
- Őrfeltétel nélkül azonnal
- Ha volt eddig belső tevékenység, az megszakad
- Ugyanabból az állapotból egy üzenethez csak egy őrfeltétel nélküli él vezethet ki!
- Őrfeltétel esetén csak annak teljesülésével
- Több él csak akkor lehetséges, ha diszjunktak
- (Nem következhet be mind a kettő egyszerre, hiszen csak egy aktív állapota lehet az objektumnak)
- Több él csak akkor lehetséges, ha diszjunktak

Üzenet nélüli állapot-átmenetek

A belső tevékenységek befejeződésekor következik be, nem üzenet váltja ki
Bekövetkezése
- Őrfeltétel nélkül azonnal
- Ebből csak egy lehet
- Őrfeltételnél az átmenet a teljesülésre vár
- A várakozás elévül, ha másik őrfeltétel teljesül
- Ekkor a másik átmenet bekövetkezik
- Az állapotból kivezető üzenetek nélküli átmenetek diszjunktak
- (Továbbra is azért, hogy minden időben egy aktív állapot létezzen)
- A várakozás elévül, ha másik őrfeltétel teljesül
Feladat - Korlátos sor
Korlátos sor - Elképzelés
FIFO elvű homogén tároló meghatározott maximális elemszámmal
Enqueue()ésDequeue()metódusokkal rendelkezik- Rögzített
nméretű tömbbel lehet megvalósítaniinésoutpozíciók, valamint az aktuális elmeszám külön tárolva van

Korlátos sor - Állapotgép

Korlátos sor - Osztály diagram

Feladat - Garázskapu vezérlés
Leírás
- A garázskaput egy távirányító vezérli
- Parancsai:
- Nyílj!
- Záródj!
- Állj meg!
- Parancsai:
- A motor a legutoljára kiadott parancs szerint mozhatja a kaput
- A kapu automatikusan megáll, ha:
- Teljesen kinyílt
- Teljesen becsukódott
- A szenzor érzékel valamit az útban
Tervezési szint diagramjai
Használati eset

Kommunikációs diagram

Szekvencia diagram

Osztály diagram

Állapotgépek
- Az objektumok egy része (motor, szenzor) aszinkron dolgozza fel a nekik küldött szignálokat, mások (kapu) szinkron metódus-hívásokat fogad.
- a motor, a vezérlőtől és a szenzortól kapott szignálokat a beérkezésük sorrendjében, a saját működésével aszinkron módon dolgozza fel egy külön szálon megvalósított állapotgéppel
- a szenzor külön szálon futó állapotgépe figyel az akadályokra, ha az állapota aktív, amelyet a motor állít be.
- a kaput szinkron hívásokkal vezérli a motor; ezért nem készítünk hozzá külön szálon futó állapotgépet.
- a vezérlő egység csak a felhasználó jelzéseit továbbítja a motor és a szenzor felé; nincsenek megkülönböztethető állapotai.
Szenzor állapotgépe

Motor állapotgépe


Aszinkron üzenetekkel vezérelt állapotgép megvalósítása
- Az aszinkron érkező üzenetet a küldő száltól eltérő szálon kell futtatni
- Az objektum a hozzá több irányból érkező szignálokat egy eseménysorban gyűjti.
- A küldő objektumok a fogadó objektum
Küld()metódusának hívásával tehetnek bele szignált a fogadó eseménysorába annakSorba()metódusával. Ezek a lépések a küldő objektum szálán hajtódnak végre. - A fogadó objektum saját szálán futó állapotgépe vesz ki egy szignált az eseménysorból annak
Sorból()metódusával. - A
Sorba()ésSorból()metódusokat szinkronizálni kell: ezek kölcsönösen kizárásos módon használhatják a sort, továbbá üres sor esetén aVesz()műveletet várakozó utasítással blokkolni kell.
- A küldő objektumok a fogadó objektum
Megvalósítási szint diagramjai
Megvalósítási szint - Kommunikációs diagram

Megvalósítási szint - Osztály diagram

Garázs megvalósítása kód
Rendszer osztálya
class Garagegate
{
public readonly Engine engine;
public readonly Sensor sensor;
public readonly Gate gate;
private readonly Controller controller;
public Garagegate()
{
controller = new Controller(this);
gate = new Gate(this, 5);
sensor = new Sensor(this);
engine = new Engine(this);
}
public void Process()
{
controller.Control();
}
}
Vezérlő osztály
public enum Signal
{
up, down, stop,
unrolled, coiled,
activate, deactivate,
blockage, none, final
};
class Controller
{
private readonly Garagegate sys;
public Controller(Garagegate s) { sys = s; }
public void Control() {
MenuWrite();
int v;
do
{
v = int.Parse(Console.ReadLine());
switch (v)
{
case 0: system.sensor.Send(Signal.final);
system.engine.Send(Signal.final); break;
case 1: system.engine.Send(Signal.up); break;
case 2: system.engine.Send(Signal.down); break;
case 3: system.engine.Send(Signal.stop); break;
}
} while (v != 0);
}
private void MenuWrite()
{
Console.WriteLine("Menupoints:");
Console.WriteLine("0 - exit");
Console.WriteLine("1 - up");
Console.WriteLine("2 - down");
Console.WriteLine("3 - stop");
}
}
Kapu osztály
class Gate
{
public readonly Garagegate system;
private readonly int maxLength;
public int CurrentLength { get; private set; }
public Gate(Garagegate s, int m)
{
system = s; maxLength = m; CurrentLength = 0;
}
public void Up()
{
if (currentLength > 0) --CurrentLength;
if (0 == CurrentLength) system.engine.Send(Signal.coiled);
}
public void Down()
{
if (CurrentLength < maxLength) ++CurrentLength;
if (maxLength == CurrentLength) system.engine.Send(Signal.unrolled);
}
}
Szálbiztos eseménysor
Ahhoz, hogy az
Enqueue()és aDequeue()ne okozzon hibát a külön szálon futásukkal, szinkronizálni kell őketFontos, hogy a Kölcsönös kizárás elve alapján működjenek.
class MyQueue <Signal>
{
private readonly Queue<Signal> queue = new ();
private readonly object criticalSection = new ();
private readonly Signal noneSignal;
public MyQueue(Signal none) { noneSignal = none; }
public bool Empty() { return queue.Count == 0; }
public void Enqueue(Item e)
{
Monitor.Enter(criticalSection);
queue.Enqueue(e);
Monitor.Exit(criticalSection);
}
public Signal Dequeue()
{
Signal e;
Monitor.Enter(criticalSection);
if (!Empty())
{
e = queue.Peek();
queue.Dequeue();
}
else e = noneSignal;
Monitor.Exit(criticalSection);
return e;
}
}
Állapotgép osztály
public abstract class StateMachine<Signal>
{
private readonly Thread thread;
private readonly MyQueue<Signal> eventQueue;
private readonly Signal finalSignal;
public StateMachine(Signal none, Signal final)
{
finalSignal = final;
eventQueue = new MyQueue<Signal>(none);
thread = new Thread(new ThreadStart(StateMachineProcess));
thread.Start();
}
public void Send(Signal signal) { eventQueue.Enqueue(signal); }
private bool inprocess;
private void StateMachineProcess()
{
inprocess = true;
while (inprocess)
{
Signal signal = eventQueue.Dequeue();
if (signal.Equals(finalSignal)) inprocess = false;
else Transition(signal);
}
}
protected abstract void Transition(Signal signal);
}
Szenzor osztály
Ez az implementáció javítható SensorState ősosztállyal, vagy Singleton-nal
class Sensor : StateMachine<Signal>
{
public readonly Garagegate system;
private SensorState currentState;
public string CurrentState { get { return currentState.ToString(); } }
public Sensor(Garagegate sys) : base(Signal.none, Signal.final)
{
system = sys;
currentState = SensorState.passive;
}
protected override void Transition(Signal signal) { … }
protected override void Transition(Signal signal)
{
switch (currentState)
{
case SensorState.passive:
switch (signal)
{
case Signal.activate: currentState = SensorState.active; break;
case Signal.deactivate: case Signal.none: break;
default: throw new UnknownSignalException();
}
break;
case SensorState.active:
switch (signal)
{
case Signal.activate: break;
case Signal.deactivate: currentState = SensorState.passive; break;
case Signal.none: Observe(); break;
default: throw new UnknownSignalException();
}
break;
}
}
private readonly Random rand = new();
private void Observe()
{
if (rand.Next() % 100 < 1)
{
system.engine.Send(Signal.blockage);
}
}
}
Motor osztály
class Engine : StateMachine<Signal>
{
public readonly Garagegate system;
private EngineState currentState;
public string CurrentState { get { return currentState.ToString(); } }
public Engine(Garagegate sys) : base(Signal.none, Signal.final)
{
system = sys;
currentState = … ;
}
protected override void Transition(Signal signal) { … }
}