Reputation: 767
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;
Upvotes: 1
Views: 249
Reputation: 11261
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...]
}
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
.
Upvotes: 1
Reputation: 8452
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.
Upvotes: 0