Home > Net >  Wait for method to be finished C#
Wait for method to be finished C#

Time:07-25

I'm communication with an external device (PLC) and he requests data.

I have an event that checks when a value changes in my PLC (for example "NeedNewPosition" or "NeedNewBarValues")

I would like to change my code that it will handle them one by one. Sometimes it seems he's handling 2 of them at the same time. (probably since one takes longer than the other to finish) I've read something about async methods and wait/tasks etc, but that seems a lot of work for something this simple.

The code:

private void PLCValueChanged(object sender, EventArgs e)
{
    bool xDisplayValue = false;
    PLCVar plcvariable = (PLCVar)sender;
    string VarName = plcvariable.DisplayName;

    switch (VarName)
    {
        case "NEEDNEWPOSITION": //Writing required position to PLC
        if (Convert.ToBoolean(plcvariable.Value))
        {
            SearchNewPosition();
            OPCclient.SendVarToPLC(OPCclient.SendPlcAllBarsFinished, "FALSE");
            OPCclient.SendVarToPLC(OPCclient.SendPLCAllMagnetsFinished, "FALSE");

            MagnetsInArea = GetMagnetsInWorkArea();
            SymbolsInArea = GetSymbolsInWorkArea();
            BarsInArea = GetBarsInWorkArea();
        }
        else
        {
            OPCclient.SendVarToPLC(OPCclient.SendPLCNewPositionIsSend, "FALSE");
        }
        break;

        case "NEEDNEWBARVALUES": //Writing Bar Values to PLC
        if (Convert.ToBoolean(plcvariable.Value))
        {
            OPCclient.SendVarToPLC(OPCclient.SendPLCBarStrippedOK, "FALSE");
            OPCclient.SendVarToPLC(OPCclient.SendPLCBarMagnetsOK, "FALSE");
            OPCclient.SendVarToPLC(OPCclient.SendPLCAllBarMagnetsLoose, "FALSE");

            SetFirstBarValues();

            OffsetsCalculated = false;

            StartVisualisation?.Invoke(this, null); //%M59
        }
        else //if (!Convert.ToBoolean(plcvariable.Value))
        {
            OPCclient.SendVarToPLC(OPCclient.SendPLCBarDataIsSend, "FALSE");
        }
        break;

CodePudding user response:

It sounds like you are looking for a Semaphore. Like the like/wiki says:

a semaphore is a variable or abstract data type used to control access to a common resource by multiple threads and avoid critical section problems in a concurrent system such as a multitasking operating system.

I.e. you can use the semaphore to "block" until a resource becomes available again.

You have multiple types of semaphores in C#, but the simplest to use is the SemaphoreSlim.

You can just define a static one for your singleton class instance

private static readonly SemaphoreSlim _semaphore = new(1, 1);

The 1,1 means: "1 is available, and there can only be 1".

Then in your code:

// take a semaphore, or wait until it is available
await _semaphore.WaitAsync(Timeout.Infinite, cancellationToken);
try
{
    [.. your work...]
    // Note, semaphore needs to be released on same thread as it is created!
    // so don't ConfigureAwait(false) or such
}
finally
{
    // give the semaphore back
    _semaphore.Release();
}

Note, I'm using await here, because this means the thread becomes available for other tasks. It will also wait indefinitely until a semaphore is available. The way to stop this is the cancallationToken.

CodePudding user response:

You could wait for the processing of the first event to be finished using an AutoResetEvent:

using System.Threading;
// ...

// declare lock as a static class member
private static AutoResetEvent barsInAreaLoaded = new AutoResetEvent(false);

private void PLCValueChanged(object sender, EventArgs e)
{
    // ...

    switch (VarName)
    {
        case "NEEDNEWPOSITION":
        if (Convert.ToBoolean(plcvariable.Value))
        {
            // ...
            BarsInArea = GetBarsInWorkArea();
            // signal waiting thread that bars are ready
            barsInAreaLoaded.Set();
        }
        // ...
        break;

        case "NEEDNEWBARVALUES":
        if (Convert.ToBoolean(plcvariable.Value))
        {
            // ...
            
            // block until bars are ready
            barsInAreaLoaded.WaitOne();
            SetFirstBarValues();
            
            // ...
        }
        // ...
        break;

Note that this will only work if you are sure that the processing of two corresponding NEEDNEWPOSITION and NEEDNEWBARVALUES messages overlap. If some of those messages actually pile up this won't solve your problem and you should consider implementing some kind of message queue/pipeline.

CodePudding user response:

Not sure if it can solve your question, i am not expert in PLC but about async method is pretty simple to implement.

I suggest a simple example for the async part. Of course is not complete and i suggest you to check out the MSDN page anyway:

  1. declare the function as: private async void ...

  2. run an async function like:

await Task.Run(() => 
{
  // code to run async here 
});

Statement await is one you are looking for (probably)!

  •  Tags:  
  • c#
  • Related