Lab3 Tiago Porto

aluno: Tiago Porto Barbosa
ano/sem: 2008/2o.
data do laboratório (2) : 08/10/2008 (2)

Introdução

O laboratório 3 tratou de criar uma maquina de estados finita, deterministica, movida a eventos. Uma máquia pode possuir vários estados, cada um com um nome único. Só sendo possível estar em um estado por vez, com o estado inicial determinado no começo do processo. Para mudar de estado, uma máquina também transições, que são direcionadas. A máquina muda de estado quando recebe um evento, que também possui um nome (também único para a máquina). Por ser determinista, de cada estado de uma máquina só pode sair no máximo uma transição que responde a um determinado evento.

Após o desenvolvimento da maquina, testou com uma simulaçao de uma turma e um professor, cada um com seus estados, suas transacoes e seus eventos. Aproveitou-se para implementar o padrão Observer (professor observa a turma).

Enfim, modelou-se um processo de senha sincronisada para um banco com um segurança com observador.

Desenvolvimento

Parte 1: Desenvolvimento da biblioteca State Machine

Classe StateMachine

A classe StateMachine é a principal classe, pois ela define como registra e atualiza os obeservadores, cria estados, cria eventos, cria mais transicoes, define as ações de guarda, define o estado inicial e dispara eventos;

No metodo fireEvent, foi utilizado o tratamento de exceçoes para evitar a inexistencia de transações.

import java.util.*;
 
public class StateMachine 
{
    State currentState;    
    public Vector<State> states = new Vector<State>();
    public Vector<Event> events = new Vector<Event>();
    public Vector<Observer> observers = new Vector<Observer>();
    State fromState_, toState_;
 
    public void registerObserver(Observer observer)
    {
        observers.add(observer);
    }
 
    protected void notifyObservers()
    {
        for(Observer obs : observers){
            obs.update(this, currentState.name);
        }
    }
 
    public void createState(String name, StateCommand onEnter)
    {
        this.createState(name);
        getState(name).stateCommand = onEnter;
    }  
 
    public void createState(String name)
    {
        State state = new State(name, this);
        states.add(state);
    }
 
    public void setInitialState(String name) throws UndefinedStateException
    {
        State state_ = getState(name);
        if(state_ == null) throw new UndefinedStateException();
        this.currentState = state_;
        if(currentState.stateCommand != null)
        {
            currentState.stateCommand.execute();
        }
    }
 
    public void createEvent(String name, String[] from, String to, EventCommand onSuccess)
    {
        Event event_;
        for(String from_ : from)
        {
            event_ = getEvent(name);
            if(event_ == null)
            {
                this.createEvent(name, from_, to);
                getEvent(name).eventCommand = onSuccess;
            }
            else 
            { 
                event_.addTransition(getState(from_), getState(to));
                event_.eventCommand = onSuccess;
            }    
        } 
    }
 
    public void createEvent(String name, String[] from, String to)
    {
        Event event_;
        for(String from_ : from)
        {
            event_ = getEvent(name);
            if(event_ == null)
            {
                this.createEvent(name, from_, to);
            }
            else 
                event_.addTransition(getState(from_), getState(to));         
        }
    }
 
    public void createEvent(String name, String from, String to,  EventCommand onSuccess) 
    {
        this.createEvent(name, from, to);
        getEvent(name).eventCommand = onSuccess;
    }
    public void createEvent(String name, String from, String to) throws UndefinedStateException
    {
        if(getState(from) == null) throw new UndefinedStateException();
        if(getState(to) == null) throw new UndefinedStateException();
        Event event = new Event(name, getState(from), getState(to), this);
        events.add(event);
    }
 
    public void appendTransitionToEvent(String event, String fromState, String toState)
    {
        Event event_ = this.getEvent(event);
        event_.addTransition(this.getState(fromState), this.getState(toState));
    }
 
    public void appendTransitionToEvent(String event, String[] fromState, String toState)
    {
        for(String state : fromState)
        {
            this.appendTransitionToEvent(event, state, toState);
        }
    }
 
    public void setGuardToEvent(String event,EventGuard g)
    {
        Event event_ = getEvent(event);
        event_.eventGuard = g;
    }
 
    public boolean fireEvent(String name) throws UndefinedTransitionException
    {
        Event event_ = getEvent(name);
        if(event_ == null) throw new UndefinedEventException();
        if((event_.eventGuard != null && event_.eventGuard.shouldPerform()) || event_.eventGuard == null)
        {
            for(TransitionToEvent transitionToEvent_ : event_.transitionsToEvent)
            {
                if(transitionToEvent_.from == null || transitionToEvent_.to == null) throw new UndefinedStateException();
                if(transitionToEvent_.from.name == currentState.name)
                {
                    State previousState = currentState;
                    currentState = transitionToEvent_.to;
                    if(event_.eventCommand != null)
                    {
                        event_.eventCommand.execute(previousState.name);
                    }
                    if(currentState.stateCommand != null)
                    {
                        currentState.stateCommand.execute();
                    }
                    return true;
                }
                else continue;
            }
        } 
        return false;  
    }
 
    public String getCurrentStateName()
    {
        return this.currentState.name;
    }
 
    public Event getEvent(String event)
    {
        for(Event event_ : events)
        {
            if(event_.name == event)
            {
                return event_;
            }
        }
        return null;
    }
 
    public State getState(String state)
    { 
        for(State state_ : states)
        {
            if(state_.name == state) 
            {
                return state_;
            }
        }
        return null;       
    }
}

Classe StateCommand, classe EventCommand, interface EventGuard e interface Observer
Permaneceram idênticos a proposta de laboratório.

Classe State

A classe State é uma subclasse de StateCommand. Cada objeto de State tem um nome que o identifica e um objeto StateCommad que define o método execute()

public class State extends StateCommand
{
    String name;
    StateCommand stateCommand;
    public State(String name, StateMachine machine)
    {
        super(machine);
        this.name = name;
    }
    public void execute() { }
}

Classe Event

A classe Event é subclasse de EventCommand.
Ela tem um nome que a identifica, um objeto de EventCommand que define o método execute(String previousState) , e um vetor de TransitionToEvent que armazena todas as transições que podem ser executadas quando o evento é disparado.

import java.util.*;
 
public class Event extends EventCommand
{
    String name;
    Vector<TransitionToEvent> transitionsToEvent = new Vector<TransitionToEvent>();
    EventGuard eventGuard;
    EventCommand eventCommand;
    public Event(String name, State from, State to, StateMachine machine)
    {
        super(machine);
        this.name = name;
        addTransition(from, to);        
    }
    public void addTransition(State from, State to)
    {        
        transitionsToEvent.add(new TransitionToEvent(this, from, to));
    }
    public void execute(String previousState) { }
}

Classe TransitionToEvent

A classe TransitionToEvent serve para definir os estados de partido e chegada que uma transação realiza quando um evento é disparado.

public class TransitionToEvent
{
    State from, to;
    Event event;
    public TransitionToEvent(Event event, State from, State to)
    {
        this.event = event;
        this.from = from;
        this.to = to;
    } 
}

Classe UndefinedEventException

A classe UndefinedEventException é uma subclasse de RuntimeException, para tratar de um caso de evento inexistente.

public class UndefinedEventException extends RuntimeException
{
    public UndefinedEventException()
    {
       System.out.println("Evento não existe");
    }
}

Classe UndefinedStateException

A classe UndefinedStateException é uma subclasse de RuntimeException, para tratar de um caso de estado inexistente.

public class UndefinedStateException extends RuntimeException
{
    public UndefinedStateException()
    {
        System.out.println("Estado não existe");
    }
}

Classe UndefinedTransitionException

A classe UndefinedStateException é uma subclasse de RuntimeException, para tratar de um caso de uma transação inexistente.

public class UndefinedTransitionException extends Exception
{   
    public UndefinedTransitionException()
    {
        System.out.println("Transicao não existe");
    }
}

Parte 2: Testando a biblioteca

Método testStateMachineSimple()

lab311.jpg

Método testArrayInFrom()

lab312.jpg

Método testMultipleTransitions()

lab313.jpg

Método testStateCommand()

lab314.jpg

Método testEventCommand()

lab315.jpg

Método testGuard()

Não mostra resultados.

Método testShouldReturnErrorIfEventDoesntExist()

lab316.jpglab317.jpg

Método testShouldReturnErrorIfStateDoesntExist()

lab318.jpglab319.jpg

Diagrama de classe

No final, o diagram de classe ficou assim:

lab320.jpg
lab330.jpg

Parte 3: Modelando um processo

Além do exemplo dado em sala, modelou-se um processo que podesse usar a maquina virtual.
É a digitação de uma senha sincronizada de um banco que é viagiado por um segurança, cada um com seus estados, transições e eventos.

Classe WithSM

A classe WithSM é identica ao modelo mostrado em aula da turma e professor.

Classe Banco

Para abrir o banco, precisa da senha 1289. Caso contrário erre 3 vezes, o alarm do banco é acionado e o segurança é acionado.
Cada nova ação nas teclas do banco, é atualizado os observadores.

import stateMachine.*;
import java.util.Vector;
public class Banco extends WithSM
{
    private Vector<BancoObserver> observers = new Vector<BancoObserver>();
    boolean alarm = false;
 
    public Banco() {
         super();
         sm.createState("in1");  
         sm.createState("in2");
         sm.createState("in3");
         sm.createState("open");
         sm.createState("...0");
         sm.createState("...1");
         sm.createState("...2");
         sm.createState("alarm", new StateCommand(sm){
             public void execute(){
                 alarm = true;
             }
         });
         sm.setInitialState("...0");
         sm.createEvent("close", "open", "...0");
         sm.createEvent("alarm", "...2", "alarm");
         sm.createEvent("1", "...0", "in1");
         sm.appendTransitionToEvent("1", "in1", "...0");
         sm.appendTransitionToEvent("1", "in2", "...0");
         sm.appendTransitionToEvent("1", "in3", "...");
         sm.appendTransitionToEvent("1", "...1", "...0");
         sm.appendTransitionToEvent("1", "...2", "...0");
         sm.createEvent("2", "...0", "...1");
         sm.appendTransitionToEvent("2", "in1", "in2");
         sm.appendTransitionToEvent("2", "in2", "...0");
         sm.appendTransitionToEvent("2", "in3", "...");
         sm.appendTransitionToEvent("2", "...1", "...2");
         sm.appendTransitionToEvent("2", "...2", "alarm");
         sm.createEvent("8", "...0", "...1");
         sm.appendTransitionToEvent("8", "in1", "...0");
         sm.appendTransitionToEvent("8", "in2", "in3");
         sm.appendTransitionToEvent("8", "in3", "...0");
         sm.appendTransitionToEvent("2", "...1", "...2");
         sm.appendTransitionToEvent("2", "...2", "alarm");
         sm.createEvent("9", "...0", "...1");
         sm.appendTransitionToEvent("9", "in1", "...0");
         sm.appendTransitionToEvent("9", "in2", "...0");
         sm.appendTransitionToEvent("9", "in3", "open");
         sm.appendTransitionToEvent("9", "...1", "...2");
         sm.appendTransitionToEvent("9", "...2", "alarm");
         String froms[] = {"2", "3", "4", "5", "6", "7", "0"};
         for(String event : froms)
         {
                sm.createEvent(event, "...0", "...1");
                sm.appendTransitionToEvent(event, "in1", "...0");
                sm.appendTransitionToEvent(event, "in2", "...0");
                sm.appendTransitionToEvent(event, "in3", "...0");
                sm.appendTransitionToEvent(event, "...1", "...2");
                sm.appendTransitionToEvent(event, "...2", "alarm");
         }
         String from[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"};
         for(String event : from)
         {
            sm.setGuardToEvent(event, new EventGuard() {
                public boolean shouldPerform() {
                    return !alarm;
                }
            });
        }
    }
    public void registerObserver(BancoObserver observer){
        observers.add(observer);
    }
    private void notifyObservers() {
        for(BancoObserver obs : observers) {
            obs.updateFromBanco(this);
        }
    }
    public void fire(String event) {
        super.fire(event);
        notifyObservers();
    }
}

Interface BancoObserver

Serve para implementar o padrao Observer

public interface BancoObserver
{
    void updateFromBanco(Banco t);
}

Classe Seguranca

O segurança observa o que está acontecendo no banco.

public class Seguranca extends WithSM implements stateMachine.Observer, BancoObserver
{
    boolean assalto = false;
    public Seguranca() {
        super();
        setUpSM();
        sm.registerObserver(this);
    }    
    public String getAtencao() {
        if(getState() == "dormindo")
            return "zzzZZZzzz (Seguranca)";
        else if(getState() == "comendo_rosquinha")
            return "Segurancao comendo rosquinha";
        else if(getState() == "atento")
            return "Seguranca ligou para voce para saber se esta tudo ok";
        else
            return "A caminho";
    }
    public void updateFromBanco(Banco t) {
        if (t.getState() == "in1" || t.getState() == "in2"|| t.getState() == "in3")
            this.fire("dormir");
        else if(t.getState() == "...0")
            this.fire("comer");
        else if(t.getState() == "...1" || t.getState() == "...2")
            this.fire("assistir");
        else if(t.getState() == "alarm")
            this.fire("correr");
        else 
            this.fire("comer");
    }
    public void update(stateMachine.StateMachine sm, String newStateName) {
        System.out.println("Seguranca: " + newStateName);
    }
    private void setUpSM() {
        sm.createState("dormindo", new stateMachine.StateCommand(sm){
            public void execute(){
                System.out.println("Seguranca: Que sono... vou dormir");
            }
        });
        sm.createState("comendo_rosquinha", new stateMachine.StateCommand(sm){
            public void execute(){
                System.out.println("Seguranca: Tou com fome");
            }
        });
        sm.createState("assistindo", new stateMachine.StateCommand(sm){
            public void execute(){
                System.out.println("Seguranca: O que esta acontecendo?");
            }
        });
        sm.createState("correndo", new stateMachine.StateCommand(sm) {
            public void execute() {
                System.out.println("ATENCAO!! Policia!");
            }
        } );
        sm.setInitialState("comendo_rosquinha");
        sm.createEvent("dormir", "comendo_rosquinha", "dormindo");
        sm.createEvent("comer", "dormindo", "comendo_rosquinha");
        sm.createEvent("assistir", "comendo_rosquinha", "assistindo");
        sm.createEvent("correr", "assistindo", "correndo", new stateMachine.EventCommand(sm){
            public void execute(String m){
                assalto = true;
            }
        });
        sm.setGuardToEvent("dormir", new stateMachine.EventGuard() {
            public boolean shouldPerform(){
               return !assalto;  //nunca fica desmotivado se ganha milhoes
            }
        });
        sm.setGuardToEvent("comer", new stateMachine.EventGuard() {
            public boolean shouldPerform(){
               return !assalto;  //nunca fica desmotivado se ganha milhoes
            }
        });
        sm.setGuardToEvent("assistir", new stateMachine.EventGuard() {
            public boolean shouldPerform(){
               return !assalto;  //nunca fica desmotivado se ganha milhoes
            }
        });
    }
}

Package stataMachine

É o pacote com o que foi feito na primeira parte: a Maquina de Estados.

Testando…
Os seguintes passos foram feitos manualmente:

Criou-se um seguranca Bernardo.

lab331.jpg

Criou-se um banco CES22 e registrou o observador Bernardo.

Entrou-se com a sequencia "1", "2", "8" e "9" (a senha).

lab332.jpg

Fechou-se o banco com "close"

Entrou-se com a sequencia "1", "9", "2", "8" e "2" (3 erros).

lab333.jpg

+++Arquivo anexado
lab3.rar

Conclusão

Muito interessante a prática no sentido de abstração.
Foi também muito instrutiva pois exercitou modelagem OO para o desenvolvimento de uma aplicação com fins práticos, exercitou o uso do padrão Observer, serviu para conhecer o padrão Command (EventCommand e StateCommand) e colocar em pratica as classes anônimas que são muito importantes.

O tempo total para realização foi superior a 10 horas.
As possiveis melhorias são definições das subclasses de Exception e RumtimeException, pois, da forma que estão, não tratam das exceções para concerta-las.
Os pontos positivos foi o desacoplamento da encapsulamento do resto do código , pois permite usar a maquina de estados para diversos fins práticos.

Essa prática foi muito importante pois minha equipe usará o princípio da maquina de estados para implementar o projeto final dessa materia (CES22).

Add a New Comment
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License