StateMachine // finite state machine, FSM

Markus Freitag 3,791 Reputation points
2023-07-06T16:40:02.4466667+00:00

Hello, I am still looking for a good concept. At the moment I have kept it simple.

I have three buttons that can signal a state. Only when I am in my process state I have to evaluate them, otherwise ignore them. Without task, without cancellation. I consider the states and the sensors, which then define the working path.

Can you please give me tips and examples so that I can finally reach my goal. Thanks in advance.

How could I optimize the working function? public void Working() With best regards Markus



//// Code
////////////////////  WinForms /////////////////////////
StateContext SC;
private async void btnStateMachine_Click(object sender, EventArgs e)
{
	try
	{
		int counter = 0;
		CurrentPrinter = new PrinterProcess();
		await Task.Run(() =>
		{
		   
			//StateContext sc = new StateContext(new State1(), counter);
			SC = new StateContext(counter);

			for (int i = 0; i < 30; i++)
			{
				SC.Working();
				Trace.WriteLine($"--------------- {i} ---");
				Thread.Sleep(1000);
			}
		});
		Trace.WriteLine($"Counter= '{counter}'     ");
	}
	catch (System.OperationCanceledException ex)
	{
		Trace.WriteLine("Cancelled");
	}
	Trace.WriteLine("Done");         
	return;
}
private void btnIN_Click(object sender, EventArgs e)
{	
	SC.CurrentSensorState = StateContext.SensorState.IN;
}

private void btnSTART_Click(object sender, EventArgs e)
{
	SC.CurrentSensorState = StateContext.SensorState.START;
}

private void btnOUT_Click(object sender, EventArgs e)
{
	SC.CurrentSensorState = StateContext.SensorState.OUT;
}



//////////////////// MODEL /////////////////////////////////

		
public class StateContext
{
	public enum SensorState
	{
		NotDefine = -1,
		IN,
		START,
		OUT
	}

	private IState Actual;
	private int CurrentCounter;

	public SensorState CurrentSensorState { set; get; }
	private State1 S1;// = new State1(this);
	private State2 S2;// = new State2(this);
	private State3 S3;// = new State3(this);

	public StateContext(int counter)
	{
		S1 = new State1(this);
		S2 = new State2(this);
		S3 = new State3(this);

		Actual = S1;
		CurrentSensorState = SensorState.NotDefine;
		CurrentCounter = counter;
	}

	public void Working()
	{
		switch (CurrentSensorState)
		{
			default:
				break;
			case SensorState.IN:
				if (Actual == S1 ) 
				{
					++CurrentCounter;
					Actual.Do();
					ChangeState();
				}
				break;
			case SensorState.START:
				if (Actual == S2) 
				{
					++CurrentCounter;
					Actual.Do();
					ChangeState();
				}
				break;
			case SensorState.OUT:
				if (Actual == S3) 
				{
					++CurrentCounter;
					Actual.Do();
					ChangeState();
				}
				break;
		}            
	}

	public void ChangeState()
	{
		Actual.Exit();

		if (Actual is State1)
		{
			Actual = S2;
		}
		else if (Actual is State2 ) //&& CurrentCounter > 20)
		{
			Actual = S3;
		}
		else if (Actual is State3)
		{
			Actual = S1;
		}
		else
		{

		}
		Actual.Entry();
	}
}


interface IState
{
	void Entry();
	void Exit();
	void Do();
}

public abstract class StateBase : IState
{
	private readonly  StateContext SC;

	public StateBase(StateContext sC)
	{
		SC = sC;
	}

	public virtual void Do()
	{
		Trace.WriteLine("StateBase : Do");

		Trace.WriteLine("StateBase : DataFromServer");
		Trace.WriteLine("StateBase : CheckIsMarkingpossible");
	}
	public virtual void Entry()
	{
		Trace.WriteLine("StateBase : Entry");
	}
	public virtual void Exit()
	{
		Trace.WriteLine("StateBase : Exit");
	}
}

public class State1 : StateBase
{
	public State1(StateContext sC) : base(sC)
	{
		
	}

	public override void Do()
	{
		base.Do();

		Trace.WriteLine("State 1: Do");

		Trace.WriteLine("State 1: DataFromServer");
		Trace.WriteLine("State 1: CheckIsMarkingpossible");


		Trace.WriteLine("State 1: ServerData1 and ServerData1 are available!");


		
	}

	public override void Entry()
	{
		base.Entry();
		Trace.WriteLine("State 1: Entry");

		
	}

	public override void Exit()
	{
		base.Exit();
		Trace.WriteLine("State 1: Exit");

		
	}
}

public class State2 : StateBase
{
	public State2(StateContext sC) : base(sC)
	{

	}

	public override void Do()
	{
		base.Do();
		Trace.WriteLine("State 2: Do");
		Trace.WriteLine("State 2: Marking Ink");
	}

	public override void Entry()
	{
		base.Entry();
		Trace.WriteLine("State 2: Entry");            
	}

	public override void Exit()
	{
		base.Exit();
		Trace.WriteLine("State 2: Exit");            
	}
}

public class State3 : StateBase
{
	public State3(StateContext sC) : base(sC)
	{

	}

	public override void Do()
	{
		Trace.WriteLine("State 3: Do");
		Trace.WriteLine("State 3: Send result to server");
	}

	public override void Entry()
	{
		Trace.WriteLine("State 3: Entry");
	}

	public override void Exit()
	{
		Trace.WriteLine("State 3: Exit");
	}
}



Developer technologies | C#
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Anonymous
    2023-07-07T09:23:15.6133333+00:00

    Hi @Markus Freitag , Welcome to Microsoft Q&A.

    Optimizing code is more subjective, and I will give two suggestions from my own perspective.

    Create event handlers for button click events: Instead of handling the state directly in the button click events, bind each button's click event to a corresponding event handler. This improves code readability and maintainability, and separates the state logic from the UI logic.

    private void btnIN_Click(object sender, EventArgs e)
    {
        HandleSensorStateChange(StateContext.SensorState.IN);
    }
    
    private void btnSTART_Click(object sender, EventArgs e)
    {
        HandleSensorStateChange(StateContext.SensorState.START);
    }
    
    private void btnOUT_Click(object sender, EventArgs e)
    {
        HandleSensorStateChange(StateContext.SensorState.OUT);
    }
    
    private void HandleSensorStateChange(StateContext.SensorState newState)
    {
        if (SC.CurrentSensorState == StateContext.SensorState.Working)
        {
            SC.CurrentSensorState = newState;
            SC.Working();
        }
    }
    

    Use events instead of polling: You can use events to handle changes in the sensor state. Define a sensor state changed event in the StateContext class and raise the event when the state changes. Subscribe to this event in the main form to perform the necessary actions based on the state change.

    public class StateContext
    {
        public event EventHandler<SensorStateChangedEventArgs> SensorStateChanged;
    
        // ...
    
        public void Working()
        {
            // ...
    
            // Raise the sensor state changed event
            SensorStateChanged?.Invoke(this, new SensorStateChangedEventArgs(CurrentSensorState));
    
            // ...
        }
    }
    
    public class SensorStateChangedEventArgs : EventArgs
    {
        public StateContext.SensorState NewState { get; }
    
        public SensorStateChangedEventArgs(StateContext.SensorState newState)
        {
            NewState = newState;
        }
    }
    
    // Subscribe to the sensor state changed event in the main form
    private void Form_Load(object sender, EventArgs e)
    {
        SC.SensorStateChanged += SC_SensorStateChanged;
    }
    
    private void SC_SensorStateChanged(object sender, SensorStateChangedEventArgs e)
    {
        if (SC.CurrentSensorState == StateContext.SensorState.Working)
        {
            if (e.NewState == StateContext.SensorState.IN || e.NewState == StateContext.SensorState.START || e.NewState == StateContext.SensorState.OUT)
            {
                SC.Working();
            }
        }
    }
    

    Best Regards,

    Jiale


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment". 

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.