Pages

Search

Wednesday, January 30, 2013

Finite State Machine – Beverage Vending Machine


 Finite state machine is a mathematical computation model.

Considering a machine which has finite number of possible states, and there will be only one state in which the machine is said to be in.
Ex:- Vending machine could be in sleep mode or requesting coins from user etc…
The machine may change its state, which could depend upon the triggers.
Ex:- User inserting coins into the machine, might change the machine state from sleep to accepting the coins.
Each state would have next possible states which are associated.

State Machine Diagram :

Below is the sample code, which works using state machine algorithm.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StateMachine_BeverageVendingMachine
{
    class Program
    {
        static void Main(string[] args)
        {
            //creating vending machine instance and setting default product cost
            BeverageVendingMachine machine = new BeverageVendingMachine() { Cost = 10 };

            while (true)
            {
                try
                {
                    Console.WriteLine("Current State : " + machine.ToString());

                    //if machine state is sleep, then asking user to enter coins
                    if (machine.CurrentState == MachineState.Sleep)
                    {
                        Console.WriteLine("Please enter coins for amount : " + (machine.Cost - machine.Amount));
                        Int32 coin = GetValidOption((i) =>i>0 && i <= (machine.Cost - machine.Amount), "Please enter coins for amount : " + (machine.Cost - machine.Amount));
                        machine.AddCoins(coin);
                    }

                    //if machine state is coins status, then asking for the remaining amount or either giving a cancel option
                    if (machine.CurrentState == MachineState.Coin_Status)
                    {
                        Console.WriteLine("Please enter coins for amount : " + (machine.Cost - machine.Amount) + " or hit -1 to cancel");
                        Int32 coin = GetValidOption((i) => i > 0 && i <= (machine.Cost - machine.Amount) || i == -1, "Please enter coins for amount : " + (machine.Cost - machine.Amount) + " or hit -1 to cancel");
                        if (coin == -1)
                            machine.Cancel();
                        else
                            machine.AddCoins(coin);
                    }

                    //if machine state is active, then asking user to select beverage option
                    if (machine.CurrentState == MachineState.Active)
                    {
                        Console.WriteLine("Please select menu \n 1. coffee\n 2. Tea");
                        Int32 coin = GetValidOption((i) => i == 1 || i == 2, "Please select menu \n 1. coffee\n 2. Tea");
                        if (coin == 1)
                            machine.Product = "Coffee";
                        if (coin == 2)
                            machine.Product = "Tea";

                        //supplyin the selected product
                        machine.Supply();
                    }
                }
                catch (Exception exp)
                {
                    Console.WriteLine("Error : " + exp.Message);
                }
            }
        }


        public static Int32 GetValidOption(Func<int, bool> validate, string msg)
        {
            while (true)
            {
                Int32 option = 0;
                if (int.TryParse(Console.ReadLine(), out option))
                {
                    if (validate(option))
                        return option;
                    else
                        Console.WriteLine(msg);
                }
                else
                    Console.WriteLine("Please enter valid number");
            }
        }
    }

    public class BeverageVendingMachine
    {
        Dictionary<StateTransition, MachineState> machineTransitions = null;

        public MachineState CurrentState { get; set; }

        public int Amount { get; set; }

        public int Cost { get; set; }

        public string Product { get; set; }

        public BeverageVendingMachine()
        {
            //adding each possible statetransition for a given state to dictionary.
            machineTransitions = new Dictionary<StateTransition, MachineState>()
            {
                { new StateTransition(MachineState.Sleep,MachineCommand.Sleep2CoinCheck), MachineState.Coin_Check},
                { new StateTransition(MachineState.Coin_Status,MachineCommand.CoinStatus2Cancel), MachineState.Cancel},
                { new StateTransition(MachineState.Coin_Check,MachineCommand.CoinCheck2CointStatus), MachineState.Coin_Status},
                { new StateTransition(MachineState.Coin_Status,MachineCommand.CoinStatus2Active), MachineState.Active},
                { new StateTransition(MachineState.Active,MachineCommand.Active2Supply), MachineState.Supply},
                { new StateTransition(MachineState.Supply,MachineCommand.Supply2Sleep), MachineState.Sleep},
                { new StateTransition(MachineState.Cancel,MachineCommand.Cancel2Sleep), MachineState.Sleep}
            };
            this.CurrentState = MachineState.Sleep;
        }

        //when user inserts coins
        public void AddCoins(Int32 coin)
        {

            //In sleep state the amount is 0, hence changing the state to coin check state
            if (Amount == 0)
                GetNextState(MachineCommand.Sleep2CoinCheck);

            //adding the total amount inserted so far
            Amount += coin;

            //if amount collected is same as cost
            if (Amount == Cost)
            {
                //changing the coin check to coin status and then coin status to active
                if (CurrentState == MachineState.Coin_Check)
                    GetNextState(MachineCommand.CoinCheck2CointStatus);
                GetNextState(MachineCommand.CoinStatus2Active);
            }
            else
                //changing coin check to coin status, when user enters coins, but the amount is less than the actual cost
                if (CurrentState == MachineState.Coin_Check)
                    GetNextState(MachineCommand.CoinCheck2CointStatus);
        }

        //If user selects cancel operation, after entering few coins
        public void Cancel()
        {
            //changing the state to cancel and sleep
            GetNextState(MachineCommand.CoinStatus2Cancel);
            GetNextState(MachineCommand.Cancel2Sleep);

            //handovering the amout collected before cancel
            if (Amount > 0)
                Console.WriteLine("Please collect your amount : {0}", Amount);
            Amount = 0;
        }

        //after user selects product from menu
        public void Supply()
        {
            //supplying the product
            GetNextState(MachineCommand.Active2Supply);
            Console.WriteLine("Supplying {0} ...", Product);

            //going to sleep mode
            GetNextState(MachineCommand.Supply2Sleep);
            Console.WriteLine("Thanks for choosing this vending machine");
            Amount = 0;
        }

        //setting the next state based on current state and command issued
        public void GetNextState(MachineCommandcommand)
        {
            StateTransition trans = new StateTransition(CurrentState, command);
            MachineState nextState;
            if (machineTransitions.TryGetValue(trans, out nextState))
            {
                CurrentState = nextState;
            }
            else
                throw new Exception(string.Format("No valid state transition for state : {0}, command : {1}", CurrentState, command));
        }

        public override string ToString()
        {
            return CurrentState.ToString();
        }
    }


    //each state transition object is the reference of StateTransition class

    public class StateTransition
    {
        readonly MachineState CurrentState;
        readonly MachineCommandCommand;
        public StateTransition(MachineState state, MachineCommand command)
        {
            this.CurrentState = state;
            this.Command = command;
        }
        public override bool Equals(object obj1)
        {
            StateTransition obj = obj1 as StateTransition;
            return (obj.Command == this.Command && obj.CurrentState == this.CurrentState);
        }
        public override int GetHashCode()
        {
            return (10 * CurrentState.GetHashCode()) + (10 * Command.GetHashCode());
        }
    }

    //Vending machine, finite possible states
    public enum MachineState
    {
        Sleep = 1,
        Coin_Check = 2,
        Cancel = 3,
        Coin_Status = 4,
        Active = 5,
        Supply = 6
    }

    //Vending machine, possible state transitions
    public enum MachineCommand
    {
        Sleep2CoinCheck = 1,
        CoinStatus2Cancel = 2,
        Cancel2Sleep = 3,
        CoinCheck2CointStatus = 4,
        CoinStatus2Active = 5,
        Active2Supply = 6,
        Supply2Sleep = 7
    }
}

Output :

12 comments:

  1. I like the helpful info you provide in your articles.I am quite certain I’ll learn lots of new stuff right here!
    Recommended Little Susitna Salmon Fishing Alaska Guides, IFishAlaska

    ReplyDelete
  2. So this is how it works. Thanks for the info. www.seattleseo.biz

    ReplyDelete
  3. Terrific job, this information is exactly what I was looking for. Thank you very much.
    Best Spicewood landscape lighting click here

    ReplyDelete
  4. there will be only one state in which the machine is said to be in.
    Great data for Dallas Apartments for Rent

    ReplyDelete
  5. User inserting coins into the machine, might change the machine state from sleep to accepting the coins.
    Get Best Info for Thailand Yoga Teacher Training School

    ReplyDelete
  6. Great and useful article. Creating content regularly is very tough. Your points are motivated me to move on


    Digital Marketing Company in Chennai

    ReplyDelete
  7. this is very nice blog this studying course information very useful to everyone who have learning this information.this education information is very helpful to start my carrier with technology.

    Base SAS Training in chennai

    ReplyDelete