I have a hardware component that I use with TCP communication. The class that control it has 3 jobs: Trigger the device, listen to incoming messages and raise an event
when a message is received:
public class Hardware
{
public event Action<string> OnHardwareMessage;
private NetworkStream stream = new NetworkStream();
public Hardware()
{
Task.Factory.StartNew(() => { Listen(); });
}
private void Listen()
{
//listen to TCP port and raise an event when a message is received
byte[] bytes = new byte[1024];
int bytesRead = stream.Read(bytes, 0, bytes.Length);
string response = Encoding.ASCII.GetString(bytes, 0, bytesRead);
if (OnHardwareMessage != null)
OnHardwareMessage(response);
}
public void Trigger()
{
//Trigger the hardware component
//the response usually takes up to 5 seconds to arrive
}
}
This class is used in a loop inside a view-model
:
public class MainViewModel
{
private static EventWaitHandle hardwareWaiter =
new EventWaitHandle(false, EventResetMode.AutoReset);
private Hardware hardware = new Hardware();
//what i'm doing now is holding a field for incoming event results
private string hardwareResult;
public MainViewModel()
{
hardware.OnHardwareMessage += hardware_OnHardwareMessage;
while (true)
{
hardware.Trigger();
if (hardwareWaiter.WaitOne(10000))
{
//is it possible to read the event argument here?
//do something with the event argument
someObservableCollection.Add(hardwareResult);
//clear the value
hardwareResult = string.Empty;
}
else
{
throw new Exception("Hardware did not respond on time");
}
}
}
//event listener for hardware events
private void hardware_OnHardwareMessage(string message)
{
hardwareResult = message;
hardwareWaiter.Set();
}
}
What I do is trigger the device and wait for up to 10 seconds for a response. What I'm doing now is holding a class scoped field, assign the message received inside the event listener, read it in the inside the loop and clear it.
My question is if there's any built in mechanism that could let me read the event argument directly after the EventWaitHandle
was signaled (outside the event listener).
I would not base this on events. Maybe your listener should expose a
BlockingCollection
that represents the incoming stream of messages. Readers can then take from that collection with a timeout. If the timeout hits no message is taken and lost (this is a race condition that you have right now).If you insist on using events use a closure to thread the message state:
That way you only need a local variable which is scoped more tightly than a field.
There's the usual TCP bug as well: You assume to read an entire message. You could read one byte at a time instead.