I currently have a partly synchronous (poor) implementation of UDP communication between my android App and a hardware which is broadcasting UDP packets. The App continuously polls the hardware for status information which then is used to update the UI. The App also has various screens, each requesting (only when user switches screens, not continuous) a different set of configuration information. The user can also make changes to the configurations and load them to the hardware. All this while, the status updates keeps running in the background. I am looking for a solution best suited to my scenario.
Here is what I have done so far (simplified to make it more readable)
void InitializeUDP()
{
udpClient = new UdpClient(15001);
sender = default(IPEndPoint);
ThreadPool.QueueUserWorkItem(o => UDP_StatusCommunicator());
udpClient.EnableBroadcast = true;
udpClient.Client.ReceiveTimeout = 500;
}
void UDP_StatusCommunicator()
{
while (true)
{
if (update_flag)
{
try
{
sent_packet = FrameGenerator(frame_Queue[screen], true); //Creates UDP Packet
//CheckQuery(sent_packet);
udpClient.Send(sent_packet, sent_packet.Length,"192.168.4.255", 15000);
received_packet = udpClient.Receive(ref sender);
//CheckResponse(received_packet);
RunOnUiThread(() =>
{
Update_UI(received_packet);
});
}
catch (SocketException e)
{
Console.Writeline("Socket Timeout: " + e);
}
}
Thread.Sleep(update_delay);
}
}
void UDPReadWrite(int screen, bool reading)
{
SelectFunctionQueue(screen); //Select the frames according to the screen selected
//CheckQueue(frame_Queue);
for (int i = 0; i < frame_Queue.Length; i++)
{
try
{
sent_packet = FrameGenerator(frame_Queue[i], reading);
//CheckQuery(sent_packet);
udpClient.Send(sent_packet, sent_packet.Length, "192.168.4.255", 15000);
received_packet = udpClient.Receive(ref sender);
//CheckResponse(received_packet);
if (sent_packet[2] == received_packet[2]) //Verify correct packet received
{
Update_UI(received_packet);
}
else
{
i--; //retry
}
}
catch (SocketException e)
{
Console.WriteLine("Socket Timeout: " e);
i--;
}
}
}
}
void Switch_Screen(int new_screen)
{
update_flag = false;
UDPReadWrite(new_screen, true)
update_flag = true;
}
void User_Config_Write(int screen, byte[] data)
{
update_flag = false;
Update_Payload(data);
UDPReadWrite(screen, false)
update_flag = true;
}
As you would have clearly noticed, this is a very flawed implementation. I keep running into issues like UI freeze, same socket usage being attempted by two threads simultaneously, stuck while waiting for packets. I have tried to use 'async await' but I am not implementing it correctly resulting in race conditions and what not. Any help would be appreciated
Update : After some research and testing I have found the below to be working satisfactorily. However, I would appreciate if someone could just verify whether it has been done correctly
UdpClient udpClient = new UdpClient();
UdpClient r_UdpClient = new UdpClient(15001);
IPEndPoint sender = default(IPEndPoint);
ManualResetEventSlim receive = new ManualResetEventSlim(true);
Task.Run(() => UDP_Transmit());
async void UDP_Transmit()
{
byte[] frame;
SelectFrameQueue(selector);
udpClient = new UdpClient(15001);
udpClient.EnableBroadcast = true;
udpClient.BeginReceive(new AsyncCallback(UDP_Receive), udpClient);
while (true)
{
for (int i = 0; i < frame_Queue.Length; i++)
{
frame = FrameGenerator(frame_Queue[i]); //Generates Frames
try
{
udpClient.Send(frame, frame.Length, "192.168.4.255", 15000);
}
catch (SocketException)
{
Log.Debug("Error", "Socket Exception");
}
if(!receive.Wait(10000)) //Receive Timeout
{
RunOnUiThread(() =>
{
ShowToast("Connection Timeout. Please check device");
});
};
await Task.Delay(update_delay); //To release pressure from H/W
receive.Reset();
}
}
}
void UDP_Receive(IAsyncResult result)
{
receive.Set();
r_UdpClient = result.AsyncState as UdpClient;
data = r_UdpClient.EndReceive(result, ref sender);
RunOnUiThread(() =>
{
Update_UI(data);
});
r_UdpClient.BeginReceive(new AsyncCallback(UDP_Receive), r_UdpClient);
}
I don't know what the intent of this code is:
void InitializeUDP()
{
udpClient = new UdpClient(15001);
sender = default(IPEndPoint);
ThreadPool.QueueUserWorkItem(o => UDP_StatusCommunicator());
udpClient.EnableBroadcast = true;
udpClient.Client.ReceiveTimeout = 500;
}
but it is not guaranteed that
udpClient.EnableBroadcast = true;
udpClient.Client.ReceiveTimeout = 500;
is executed before UDP_StatusCommunicator().
For client UIs like Xamarin Task.Run can be a good option over ThreadPool.QueueUserWorkItem.
You might want to take a look at Dataflow (Task Parallel Library), in particular to the ActionBlock to replace your queue.
You might also want to consider using Progress to report updates to the UI or using Reactive Extensions (Rx) to subscribe to updates from the UI.
I'm trying to port my code from an obsolete library called CastleMQ to NetMQ but I'm running into some problems.
I prefer to using polling with a timeout, for reliability - I just found that it worked best for me from trial and error compared to just sitting blocking the port indefinitely.
here is my CastleMQ code
public int ZeroPort;
private void ThreadProc()
{
var ctx = new Context();
try {
using (var repSocket = ctx.CreateSocket(SocketType.Rep))
{
string bindAddress = "tcp://*:"+ZeroPort;
repSocket.Bind(bindAddress);
print2("*** BINDING on {0} ***", bindAddress);
bool quit = false;
while (!quit) {
try {
var polling = new Polling(PollingEvents.RecvReady, repSocket);
polling.RecvReady += (socket) =>
{ // using socket.Recv() here is guaranted to return stuff
var msg = socket.Recv();
var msgStr = Encoding.UTF8.GetString(msg);
print2("[REP:{0}] {1}", bindAddress, msgStr);
switch (msgStr) {
case "positions": {
StringBuilder csv = new StringBuilder();
print2("csv: {0}", csv.ToString());
socket.Send(csv.ToString());
break;
}
default: {
socket.Send("Unrecognized Command: " + msgStr);
break;
}
}
};
polling.Poll(POLL_TIMEOUT_MS); // this returns once some socket event happens
} catch (Exception e) {
if (e is ThreadAbortException) {
quit = true;
print2("\n*** EXITED ***");
} else print2(e.ToString());
}
}
}
} catch (Exception e) {
print2(e.ToString());
} finally {
ctx.Dispose();
}
}
here is what I tried to do and then got lost with NetMQ
private void ThreadProc()
{
try {
string bindAddress = "#tcp://*:" + ZeroPort;
print2("*** BINDING on {0} ***", bindAddress);
using (var repSocket = new ResponseSocket(bindAddress))
using (var poller = new NetMQPoller { repSocket })
{
// bool quit = false;
// while (!quit)
// these event will be raised by the Poller
repSocket.ReceiveReady += (s, a) =>
{
// receive won't block as a message is ready
string msg = a.Socket.ReceiveString(); // defeinition for ReceiveString() can't be found
// send a response
a.Socket.Send("Response"); // it doesn't like "Response", do I need to wrap it in some object?
I'm especially confused as how to add a timeout so I can poll with a timeout in a loop the way my CastleMQ code does.
Any pointers would be much appreciated, thanks
I am trying to establish a connection to a remote host via IP address and Port number. The connection does get established (even verified using cmd netstat) however when I try to close the connection in code:
clientConnection.Client.Close();
clientConnection.Client.Dispose();
clientConnection.Close();
The program crashes because the socket does not have any available data to be read from the client stream. In my windows application (client) I have a button which I click to call the ConnectToFalcon method and that calls the ReadStream method. Please let me know where have I been going wrong.
public void readStream(object argument)
{
clientConnection = (TcpClient)argument;
//TcpClient client = new TcpClient();
DateTime start_time = DateTime.Now;
TimeSpan delay = new TimeSpan(0, 0, 10);
while (clientConnection.Available == 0)
{
Application.DoEvents();
if (DateTime.Now.Subtract(start_time) > delay)
break;
}
if ((clientConnection != null) && (clientConnection.Available > 0))
{
var message = new byte[1];
Array.Resize(ref message, clientConnection.Available);
//remove below two lines and if-statement block if program crashes
clientConnection.Client.ReceiveTimeout = 20000; //Timeout after 20 seconds
clientConnection.SendTimeout = 20000;
if (clientConnection.Client.ReceiveTimeout <= 20000 || clientConnection.SendTimeout == 20000)
{
clientConnection.Client.Receive(message);
string testResult = System.Text.Encoding.Default.GetString(message);
}
else
{
MessageBox.Show("Time expired before read operation completed.");
}
}
else if (((clientConnection == null) && (clientConnection.Available <= 0)) || (clientConnection.Connected == false))
{
clientConnection.Close();
MessageBox.Show("Closing client connection due to insufficient amount of data available to be read");
}
//clientConnection.Client.Close();
//clientConnection.Client.Dispose();
//clientConnection.Close();
}}
public void ConnectToFalcon(string IPaddress, int port)
{
clientConnection = new TcpClient();
//var result = clientConnection.BeginConnect(IPaddress, port, new AsyncCallback(callback), clientConnection);
var result = clientConnection.BeginConnect(IPaddress, port, null, null);
var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(1));
if (success == false)
{
MessageBox.Show("Failed to connect");
}
else
{
MessageBox.Show("Client connected...");
while (true)
{
Thread t = new Thread(readStream);
t.Start(clientConnection); //A new thread is spawned
//t.Start();
}
}
}
According to #Luaan, You must call EndConnect on the IAsyncResult in the callback function in order to acknowledge the client request.`
public void callback(IAsyncResult ar)
{
this.clientConnection.EndConnect(ar);
}
var result = clientConnection.BeginConnect(ip, port, new AsyncCallback(callback), clientConnection);
It's a lot of irrelevant code to look through.. but pretty much it sends a packet and listens for a packet in return
if i comment the part out where it calls the ReceiveAuthPacket() method at the end of sending a packet, it will work and the label will turn blue.. but otherwise it will never activate turning the label blue and will instead turn the label red or green (depending on the returned packet).
basically im just using the label as an indicator of the status.. and no matter what i try i can't get it to turn blue because it seems to be waiting for all the code to be finished executing and it just won't work..
i even tried using data triggers in WPF and it still won't work.
any work arounds? i just don't get it..
private readonly UdpMessageAuthentication _msgAuth;
private void Button_Authenticate_OnClick(object sender, RoutedEventArgs e)
{
Label_Authentication.Content = "Attempting Authentication";
Label_Authentication.Foreground = new SolidColorBrush(Colors.Blue);
_msgAuth.SendAuthPacket(IPAddress.Parse(TextBox_IP.Text), TextBox_ClientID.Text);
}
public void SendAuthPacket(IPAddress ip, string userID)
{
_ip = ip;
_userID = userID;
if (_udpClient.Client == null)
_udpClient = new UdpClient();
//GSISClockRegRequest,<Client Id>,,1
string msg = string.Format("GSISClockRegRequest,{0},,1", _userID);
byte[] sendBytes = Encoding.ASCII.GetBytes(msg);
bool sent = false;
try
{
_label.Content = "Attempting Authentication";
_label.Foreground = new SolidColorBrush(Colors.Blue);
while (_label.Content != "Attempting Authentication")
{
//loop
}
_udpClient.Connect(_ip, 5001);
_udpClient.Send(sendBytes, sendBytes.Length);
Console.WriteLine("Sending {0} bytes. Message: {1}", sendBytes.Length, msg);
sent = true;
}
catch (Exception)
{
Console.WriteLine("UDP Auth Packet Failed to Send");
}
_udpClient.Close();
if (sent)
ReceiveAuthPacket(); //IF I COMMENT THIS OUT IT'LL WORK
}
private void ReceiveAuthPacket()
{
IPEndPoint e = new IPEndPoint(IPAddress.Any, 5001);
UdpClient u = new UdpClient(e);
u.Client.ReceiveTimeout = 3000;
Console.WriteLine("Listening for Messages: ");
try
{
Byte[] receiveBytes = u.Receive(ref e);
string receiveString = Encoding.ASCII.GetString(receiveBytes);
Console.WriteLine("Received: {0}", receiveString);
string errMsg = "";
if (AuthMessageParser.ParseMessage(receiveString, ref errMsg))
{
_label.Content = "Authentication Successful!";
_label.Foreground = new SolidColorBrush(Colors.Green);
}
else
{
_label.Content = "Authentication Unsuccessful: " + errMsg;
_label.Foreground = new SolidColorBrush(Colors.Red);
}
}
catch (Exception)
{
_label.Content = "Authentication Unsuccessful";
_label.Foreground = new SolidColorBrush(Colors.Red);
Console.WriteLine("UDP Auth Packet was NOT Received.");
}
u.Close();
}
Your UI thread is blocked by calls to things like _udpClient.Connect() and _udpClient.Send() (and the receives, too)
A workaround would be to leverage the task parallel library and perform communications asynchronously to avoid blocking the UI thread.
It will manage threads for you as long as you define tasks properly. Holler if you need an example.
protected void SomeButton_Click(Object sender, EventArgs e)
{
// Task off the work and do not wait, no blocking here.
Task.Run(PerformConnection);
}
private async Task PerformConnection()
{
// This method acts the way a thread should. We await the result of async comms.
// This will not block the UI but also may or may not run on its own thread.
// You don't need to care about the threading much.
var conn = await ListenerOrSomething.AwaitConnectionsAsync( /* ... */ );
// Now you have your result because it awaited.
using(var newClient = conn.Client())
{
var buffer = new byte[];
var recv = await newClient.ReceiveAsyncOrSomething(out buffer);
// Data received is not zero, process it or return
if(recv > 0)
newClient.Response = await ProcessRequest(buffer);
else
return;
}
}
i have a program that's reading from serial port in c#. i need to quickly write to a port, read from it, then close it. i cannot leave it open. i understand that serial ports read and write slowly, I've tried to set the ReadTimeout and WriteTimeout properties high, and added a thread.Sleep to try to drag the read and write times out for the devices. here's a little bit of code:
my method to write to port:
private void CheckPorts(string testMessage)
{
foreach (string s in SerialPort.GetPortNames())
{
portNumber = Int32.Parse(s.Remove(0, 3));
testSerial = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
if (testSerial.IsOpen)
{
testSerial.Close();
}
testSerial.ReadTimeout = 2000;
testSerial.WriteTimeout = 1000;
testSerial.Open();
if (testSerial.IsOpen)
{
string received;
testSerial.DiscardInBuffer();
try
{
//testSerial.DataReceived += new SerialDataReceivedEventHandler(testSerialPort_DataReceived);
testSerial.Write(testMessage);
System.Threading.Thread.Sleep(2000);
received = testSerial.ReadExisting(); //EITHER I USE THIS OR EVENT HANDLER, NOT BOTH
}
catch (TimeoutException e)
{
testSerial.Close();
continue;
}
if (received.Length > 0)
{
MessageReceived(received);
}
testSerial.Close();
}
}
}
private void testSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string received = testSerial.ReadExisting();
int y = received.IndexOf("\r");
while (y == -1)
{
received = received + testSerial.ReadExisting();
y = received.IndexOf("\r");
}
if (testSerial.IsOpen)
{
testSerial.Close();
}
}
i'm wondering, if i absolutely have to use datahandler, how do i keep the serial port open long enough to read from it, but close the serialport before the next port needs to be opened?
see, the first method gets called a few times, and it iterates through a foreach loop, trying a message on a few ports, then trying to read a response. so, at some point i have to close the ports, or else the next time it goes through it, it doesn't work properly because the port is still open
HERE'S MY UPDATED CODE (still not working):
private void CheckPorts(string testMessage, int baudRate)
{
foreach (string s in SerialPort.GetPortNames())
{
var interval = 3000; // ms
var timer = new System.Timers.Timer(interval);
timer.Elapsed += (o, e) =>
{
timer.Enabled = false;
if (testSerial.IsOpen)
testSerial.Close(); // may not be necessary with Dispose?
testSerial.Dispose();
timer.Dispose();
};
portNumber = Int32.Parse(s.Remove(0, 3));
testSerial = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
testSerial.ReadTimeout = 2000;
testSerial.WriteTimeout = 2000;
if (testSerial.IsOpen)
{
testSerial.Close();
}
testSerial.Open();
timer.Enabled = true;
if (testSerial.IsOpen)
{
string received;
//testSerial.DiscardInBuffer();
//autoEvent = new AutoResetEvent(false);
try
{
// testSerial.DataReceived += new SerialDataReceivedEventHandler(testSerialPort_DataReceived);
// autoEvent.Reset();
lblPortNum.Content = s;
lblPortNum.Refresh();
testSerial.Write(testMessage);
//System.Threading.Thread.Sleep(2000);
//testSerial.NewLine = "\r\n";
byte[] rBuff = new byte[2];
int rCnt = testSerial.Read(rBuff, 0, 2);
System.Text.Encoding enc = System.Text.Encoding.ASCII;
received = enc.GetString(rBuff);
//received = testSerial.ReadLine();
}
catch (TimeoutException e)
{
testSerial.Close();
continue;
}
if (received.Length > 0)
{
MessageReceived(received, Int16.Parse(s.Remove(0, 3)));
}
/*
if (autoEvent.WaitOne(2000))
{
// the port responded
// testSerial.Close();
autoEvent.Dispose();
lblPortNum.Content = "HEY I RESPONDED";
}
else
{
testSerial.Close();
autoEvent.Dispose();
continue;
// port did not respond within 2 seconds
}*/
//testSerial.Close();
}
}
}
UPDATED AGAIN (still not working properly)
private void CheckPorts(string testMessage, int baudRate)
{
foreach (string s in SerialPort.GetPortNames())
{
portNumber = Int32.Parse(s.Remove(0, 3));
// MUST BE LOCAL
var serialOneOfMany = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
serialOneOfMany.ReadTimeout = 2000;
serialOneOfMany.WriteTimeout = 2000;
if (serialOneOfMany.IsOpen)
{
serialOneOfMany.Close();
}
// timer must be defined _after_ serialOneOfMany
var interval = 3000; // ms
var timer = new System.Timers.Timer(interval);
timer.Elapsed += (o, e) =>
{
timer.Enabled = false;
if (serialOneOfMany.IsOpen)
serialOneOfMany.Close(); // may not be necessary with Dispose?
serialOneOfMany.Dispose();
timer.Dispose();
};
if (serialOneOfMany.IsOpen)
{
string received;
try
{
lblPortNum.Content = s;
lblPortNum.Refresh();
serialOneOfMany.Write(testMessage);
byte[] rBuff = new byte[2];
int rCnt = serialOneOfMany.Read(rBuff, 0, 2);
System.Text.Encoding enc = System.Text.Encoding.ASCII;
received = enc.GetString(rBuff);
}
catch (TimeoutException e)
{
serialOneOfMany.Close();
continue;
}
if (received.Length > 0)
{
CheckIfTheMessageMatches(received, Int16.Parse(s.Remove(0, 3)));
}
}
}
}
so with this update, it just blows through the code, i can step through the code line by line, but it doesn't stop for 3 seconds at all. if i run it without any debugging breaks, it just goes through it i a fraction of a second
UPDATE 10-25-11
private void CheckPorts(string testMessage, int baudRate)
{
foreach (string s in SerialPort.GetPortNames())
{
string received = "";
testSerial = new SerialPort(s,baudRate, Parity.None, 8, StopBits.One);
lblStatus.Content = "Scanning...";
lblStatus.Refresh();
if (testSerial.IsOpen)
{
testSerial.Close();
}
else
{
testSerial.Open();
}
if (testSerial.IsOpen)
{
try
{
testSerial.NewLine = "\r";
lblPortNum.Content = s;
lblPortNum.Refresh();
testSerial.WriteTimeout= 500;
testSerial.ReadTimeout = 1000;
testSerial.WriteLine(testMessage);
System.Threading.Thread.Sleep(500);
/*THIS DOESN'T WORK
byte[] buffer = new byte[testSerial.BytesToRead];
int rCnt = testSerial.Read(buffer, 0, buffer.Length);
received = enc.GetString(buffer);*/
//received = Convert.ToString(testSerial.BaseStream.Read(buffer, 0, (int)buffer.Length));
received = testSerial.ReadLine();
int y = received.IndexOf("\r");
while (y == -1)
{
received = received + testSerial.ReadExisting();
y = received.Length;
}
if (lblInfo.Dispatcher.Thread == Thread.CurrentThread)
{
CheckIfTheMessageMatches(received, s);
received = received + lblInfo.Content;
lblInfo.Content = received;
}
else
{
lblInfo.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadCheck(threadCheck), received);
}
if (testSerial.IsOpen)
{
testSerial.Close();
}
/*I USE THIS WITH THE sPort.Read() METHOD
while (rCnt > 0)
{
if (lblInfo.Dispatcher.Thread == Thread.CurrentThread)
{
CheckIfTheMessageMatches(received, s);
rCnt = 0;
received = received + lblInfo.Content;
lblInfo.Content = received;
}
else
{
lblInfo.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadCheck(threadCheck), received);
}
}
*/
if (testSerial.IsOpen)
{
testSerial.Close();
}
}
catch (TimeoutException e)
{
testSerial.Close();
continue;
}
received = null;
}
}
lblStatus.Content = "Finished Scanning.";
lblPortNum.Content = "";
}
UPDATED CODE
here's some new code, still not working, dataeventhandler not even called once. i know it's getting messages because i have another program that works with the serial devices
private void CheckPorts(string testMessage, int baudRate)
{
foreach (string s in SerialPort.GetPortNames())
{
var serialOneOfMany = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
serialOneOfMany.ReadTimeout = 700;
serialOneOfMany.WriteTimeout = 100;
var interval = 500; // ms
var timer = new System.Timers.Timer(interval);
timer.Elapsed += (o, e) =>
{
timer.Enabled = false;
if (serialOneOfMany.IsOpen)
serialOneOfMany.Close(); // may not be necessary with Dispose?
serialOneOfMany.Dispose();
timer.Dispose();
};
timer.Enabled = true;
lblStatus.Content = "Scanning...";
lblStatus.Refresh();
if (serialOneOfMany.IsOpen)
{
serialOneOfMany.Close();
}
else
{
serialOneOfMany.Open();
}
if (serialOneOfMany.IsOpen)
{
string received;
try
{
lblPortNum.Content = s;
lblPortNum.Refresh();
serialOneOfMany.WriteLine(testMessage);
System.Threading.Thread.Sleep(400);
serialOneOfMany.DataReceived += new SerialDataReceivedEventHandler(testSerialPort_DataReceived);
}
catch (TimeoutException e)
{
serialOneOfMany.Close();
continue;
}
}
}
lblStatus.Content = "Finished Scanning.";
lblPortNum.Content = "";
}
private void testSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort receivingSerial = sender as SerialPort;
string received = receivingSerial.ReadExisting();
int y = received.IndexOf("\r");
while (y == -1)
{
received = received + receivingSerial.ReadExisting();
y = received.IndexOf("\r");
}
if (lblInfo.Dispatcher.Thread == Thread.CurrentThread)
{
string name = receivingSerial.PortName;
received = received + lblInfo.Content;
lblInfo.Content = received;
CheckIfTheMessageMatches(received, name);
}
else
{
lblInfo.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadCheck(threadCheck), received);
}
if (receivingSerial.IsOpen)
{
receivingSerial.Close();
}
}
You should be able to do these simultaneously (assuming that's ok). You would then close them as the DataReceived event is raised (extraneous code removed). Just don't close the port in CheckPorts.
private void testSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort localSerialPort = sender as SerialPort;
... // use localSerialPort instead of global/class variable
if (localSerialPort.IsOpen)
{
localSerialPort.Close();
}
}
EDIT: Responding to comment.
You can always add a timer on the fly. If you put this in the foreach loop, you'll get a timer for every serial port that will dispose its given serial port after 3 seconds. It's important here that the timer is declared within the foreach loop.
var interval = 3000; // ms
var timer = new System.Timers.Timer(interval);
timer.Elapsed += (o,e) =>
{
timer.Enabled = false;
if (testSerial.IsOpen)
testSerial.Close(); // may not be necessary with Dispose?
testSerial.Dispose();
timer.Dispose();
}
timer.Enabled = true;
EDIT: Code updated so I'll update
Scope is very important with the code I provided. You should get rid of the non-local testSerial or use an entirely different name here.
portNumber = Int32.Parse(s.Remove(0, 3));
// MUST BE LOCAL
var serialOneOfMany = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
serialOneOfMany.ReadTimeout = 2000;
serialOneOfMany.WriteTimeout = 2000;
if (serialOneOfMany.IsOpen)
{
serialOneOfMany.Close();
}
// timer must be defined _after_ serialOneOfMany
var interval = 3000; // ms
var timer = new System.Timers.Timer(interval);
timer.Elapsed += (o, e) =>
{
timer.Enabled = false;
if (serialOneOfMany.IsOpen)
serialOneOfMany.Close(); // may not be necessary with Dispose?
serialOneOfMany.Dispose();
timer.Dispose();
};
Check this info from Microsoft:
This method returns the contents of the stream and internal buffer of the SerialPort object as a string. This method does not use a time-out. Note that this method can leave trailing lead bytes in the internal buffer, which makes the BytesToRead value greater than zero.
Why don't use the usual Read method SerialPort.Read (Byte[], Int32, Int32)
Please have a look at this (I also used in an answer to a serial port related question asked by darthwillard). All the ports are opened one after another, the DataReceived events are bound (all you need to do there is to test the incoming message), but no waiting is required. The timer event handler can close all the ports or keep the one you want to use etc. I hope it helps!
private List<SerialPort> openPorts = new List<SerialPort>();
private void button3_Click(object sender, EventArgs e)
{
int baudRate = 9600;
string testMessage = "test";
txtPortName.Text = "Testing all serial ports";
foreach (string s in SerialPort.GetPortNames())
{
SerialPort newPort = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
if (!newPort.IsOpen)
{
try
{
newPort.Open();
}
catch { }
}
if (newPort.IsOpen)
{
openPorts.Add(newPort);
newPort.DataReceived += new SerialDataReceivedEventHandler(serialOneOfMany_DataReceived);
newPort.Write(testMessage);
}
else
{
newPort.Dispose();
}
}
txtPortName.Text = "Waiting for response";
tmrPortTest.Enabled = true;
}
private void serialOneOfMany_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
txtPortName.Text = ((SerialPort)sender).PortName;
}
private void tmrPortTest_Tick(object sender, EventArgs e)
{
tmrPortTest.Enabled = false;
foreach (SerialPort port in openPorts)
{
if (port.PortName != txtPortName.Text)
{
port.Close();
port.Dispose();
}
}
}
Try setting your event handler before you write to the port, and then see if it doesn't catch your break point.
You can't use Thread.Sleep. It blocks the read from the device. You need to spawn a new thread.
You may be best with BackgroundWorker. Eg:
BackgroundWorker worker=new BackgroundWorker();
worker.DoWork += (s, dwe) =>
{
// do your serial IO here
worker.RunWorkerCompleted += (s, rwe) =>
{
// check for rwe.Error and respond
};
worker.RunWorkerAsync();
open the port in public form1
just after/below the InitializeComponent(); myport.open
and close the after data is received.
worked!