serialPort.DataReceived running in thread not working - c#

I'm working in a thread to check the GPRS connection in CompactFramework.
The idea of the thread is simple: If the program isn't connected then I run the code to connect (this code is giving me errors), but if the connection is OK then I recheck again in 60 seconds and so on.
Now, focusing in connection code. The following code check if it's connected or not, if it isn't then I subscribe to DataReceive event.
void initFormText()
{
if (isThereConnect()) //true if it is connected
{
//enable timer to recheck if it's connected
}
else //it isn't connected
{
serialPort1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(serialPort1_DataReceived);
if (serialPort1.IsOpen)
{
serialPort1.Close();
}
serialPort1.Open();
timerStep.Enabled = true;
}
}
Now comes the issue, in the serialPort1_DataReceived I check the data and set a variable which is tested by the timerStep and it make some steps.
The problem occurs in the DataReceived event, the thing is that when I run the following code outside of a thread it works fine, it does all the job and make the connection, but in the thread it doesn't work. I test this adding some MessageBoxand I realize that the ones inside the DataReceive never appear.
void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
byte[] data = new byte[1024];
int n = serialPort1.Read(data, 0, data.Length);
string rec = Encoding.GetEncoding("windows-1252").GetString(data, 0, n);
if (string.IsNullOrEmpty(rec))
{
return;
}
if (rec.Contains("AT+CIMI") && rec.Contains("OK"))
{
MessageBox.Show("serialPort 1");
currState = 1;
}
else if (rec.Contains("READY"))
{
MessageBox.Show("serialPort 11");
currState = 1;
}
else if (rec.Contains("0,1") || rec.Contains("0,5"))
{
MessageBox.Show("serialPort 2");
currState = 2;
}
}
So by some reason the serialPort isn't receiving anything and I can't figure it out why. The fact that it works outside the thread but not in the thread is frustrating me.
I appreciate any help. Thanks in advanced!

Yes, but I think that your thread finishes before event is fired. You should create your Form in a following manner, please note that this is code for desktop but simulates what is available in CompactFramework since I don't have it installed here. First Form1 is main form and it starts thread in which is the Form2. The Form2 has a button and Click EventHandler that is working, but you need to show your Form2 with Application.Run(). Here is the sample code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(ThreadMethod));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
void ThreadMethod()
{
Form2 f = new Form2();
Application.Run(f);
}
}
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Something");
}
}
Hope it will work this way.

The event must run in the same thread (I suppose UI thread) where you have already declared serialPort1. You can execute the code from the serialPort1_DataReceived event in different thread. That thread should be started by serialPort1_DataReceived event handler. The problem is that CompactFramework doesn't have ParameterisedThreadStart so you can not effectively pass received data to the thread. You will need to set some global field using delegates.

Related

serial port myPort.Datareceived C#

Hello friends have a form in C # that reads data from a serial device connected, my problem is that I even changing form of the method myPort.DataReceived still running and receiving data. There's no way I close the connection with the serial port because the method does not stop excutar. I've tried a command to zip it when I change my form but it crashes when you try to run the myPort.Close, I believe that is why the myPort.DataReceived still running, so I removed the code and it continues myPort.Close open in another form. I think my solution would be to stop the myPort.DataReceived to then close connection, but can not find way to do this.Below is an excerpt from my code:
namespace EntradaFinalCliente
{
public partial class ConsultaSerial : Form
{
string SerialString;
private SerialPort myport;
public ConsultaSerial()
{
InitializeComponent();
abrirSerial();
lerDados();
}
public void abrirSerial()
{
myport = new SerialPort();
myport.BaudRate = 9600;
myport.PortName = SerialPort1;
myport.DataReceived += myport_DataReceived;
}
private void lerDados()
{
if (myport.IsOpen == false)
{
try
{
myport.Open();
}
catch
{
return;
}
}
}
private void myport_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(100);
SerialString = myport.ReadExisting();
this.Invoke(new EventHandler(Analisa));
}
private void Analisa(object sender, EventArgs e)
{
checarSerial();
}
And this is my closing the form button:
private void button1_Click (object sender, EventArgs e)
{
myPort.Close ();
this.Hide ();
var form1 = new Form1 ();
form1.Closed + = (s, args) => this.Close ();
Form1.Show ();
}
The issue you have it that once the event has been triggered, your application would have entered the function myport_DataReceived. The function will continue to execute regardless of whether the port has been closed. If the port has been closed, the function would execute for the last time. Waiting for 100ms makes it worse. So my advice is to remove the wait and put a try catch statement around the code to make the thread terminate cleanly.
Furthermore, it is better if you use the sender to read the incoming data than using the member myPort because the sender is the one that fires the event. It also helps to remove confusion when you open two or more ports.
It is also advised that the body of DataReceived event handler function should be kept to minimum. Only do what you need to get the data out. You can then store the data in memory and do more complicated handling somewhere else using the stored data.
private void myport_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(100); // Why do you need to wait for 100 ms? If the event reaches here, it will have the data to read. Can remove?
try
{
SerialPort sp = (SerialPort)sender;
SerialString = sp.ReadExisting();
this.Invoke(new EventHandler(Analisa));
}
catch (Exception ex)
{
// Do something else
Console.WriteLine(ex.Message);
}
}

Threading, let one thread know the others progress

Ok, well I have been at it for a while now and I decided to just use threads. I am making a syntax highlighter but I keep getting terrible performance with the file sizes that it will usually be used for. So I made two forms, the first shows the file in plain text and has a button that says "openincolor" when you click that I start a new thread as such
private void button1_Click(object sender, EventArgs e)
{
ColoringThread colorer = new ColoringThread(this.m_bruteView.Text);
Thread theThread = new Thread(new ThreadStart(colorer.OpenColorWindow));
theThread.Start();
}
public class ColoringThread
{
string text;
public ColoringThread(string initText)
{
text = initText;
}
public void OpenColorWindow()
{
Form2 form2 = new Form2(text);
form2.ShowDialog();
}
};
I want this form to send back a message each time it has complete say x lines of coloring. Then I will take that and figure out the progress and display it to the user.
How might I go about sending a message, or event(...? can I do that) to my first form to let it know of the others progress?
One very simple way to do this is with BackgroundWorker. It already provides an event to report progress.
How about something like this? This adds an event to the ColoringThread class which is subscribed to by the calling class.
private void button1_Click(object sender, EventArgs e) {
ColoringThread colorer = new ColoringThread(this.m_bruteView.Text);
colorer.HighlightProgressChanged += UpdateProgress;
Thread theThread = new Thread(new ThreadStart(colorer.OpenColorWindow));
theThread.Start();
}
private void UpdateProgress(int linesComplete) {
// update progress bar here
}
public class ColoringThread
{
string text;
public delegate void HighlightEventHandler(int linesComplete);
public event HighlightEventHandler HighlightProgressChanged;
public ColoringThread(string initText) {
text = initText;
}
public void OpenColorWindow() {
Form2 form2 = new Form2(text);
form2.ShowDialog();
int linesColored = 0;
foreach (String line in text.Split(Environment.NewLine)) {
// colorize line here
// raise event
if (HighlightProgressChanged != null)
HighlightProgressChanged(++linesColored);
}
}
};
You can pass an object as argument to the Thread.Start and share your data between the current thread and the initiating thread.
Here is a good example:
How to share data between different threads In C# using AOP?
Or you can use BackgroundWorker which has ReportProgress
What you need is System.Windows.Threading.Dispatcher's BeginInvoke method. You can't directly modify a WPF object from your background thread, however you can dispatch a delegate to do that.
In your derived Window class object you have the Property Dispatcher, so you use it as follows:
Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(status) => { StatusTextBox.Text = status },
thestatus
);
I'm sorry that I can't test that currently and I don't have the project here, where I did that. But I'm sure it will work, good luck ;)
Update: Oops, you're using Form's... I've written about WPF, sorry.

I get an error about using a different thread?

When I click my ActionButton, there is a timer that starts and after 3 seconds, it must fire a methode to change the current ContentPage to the another page.
But i get a message : The calling thread cannot access this object because a different thread owns it. I dont understand what i am doing wrong. But if i put the ChangeContent() method in the click_event, it works, but in the _tm_elapsed it doenst work?
using smartHome2011.FramePages;
using System.Timers;
public partial class AuthenticationPage : UserControl
{
private MainWindow _main;
private Storyboard _storyboard;
private Timer _tm = new Timer();
private HomeScreen _homeScreen = new HomeScreen();
public AuthenticationPage(MainWindow mainP)
{
this.InitializeComponent();
_main = mainP;
}
private void ActionButton_Click(object sender, System.EventArgs eventArgs)
{
_main.TakePicture();
identifyBox.Source = _main.source.Clone();
scanningLabel.Visibility = Visibility.Visible;
_storyboard = (Storyboard) FindResource("scanningSB");
//_storyboard.Begin();
Start();
}
private void Start()
{
_tm = new Timer(3000);
_tm.Elapsed += new ElapsedEventHandler(_tm_Elapsed);
_tm.Enabled = true;
}
private void _tm_Elapsed(object sender, ElapsedEventArgs e)
{
((Timer) sender).Enabled = false;
ChangeContent();
//MessageBox.Show("ok");
}
private void ChangeContent()
{
_main.ContentPage.Children.Clear();
_main.ContentPage.Children.Add(_homeScreen);
}
}
Description
You have to use Invoke to ensure that the UI Thread (the thread who has created your Control) will execute that.
1. If you are doing Windows Forms then do this
Sample
private void ChangeContent()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(ChangeContent));
return;
}
_main.ContentPage.Children.Clear();
_main.ContentPage.Children.Add(_homeScreen);
}
2. If you are doing WPF then do this
private void _tm_Elapsed(object sender, ElapsedEventArgs e)
{
((Timer) sender).Enabled = false;
this.Dispatcher.Invoke(new Action(ChangeContent), null);
//MessageBox.Show("ok");
}
More Information
Windows Forms
MSDN - Control.Invoke Method
MSDN - Control.InvokeRequired Property
WPF
MSDN - Dispatcher.Invoke Method
MSDN - Dispatcher Class
The logic executed in the Elapsed event of the Timer is run on a separate thread from the rest of your code. This thread cannot access objects on the main/GUI thread.
This thread should help you find out how to do it: How to update the GUI from another thread in C#?
I suspect you are using a System.Threading.Timer. You can avoid cross thread operation by just using a Windows.Forms timer:
http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx
That timer uses regular messages and the event occours on the same thread of the UI.
The event to use is no more called "Elapsed", but "Tick" read the doc here: http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.tick.aspx

BackgroundWorker does not fire the RunWorkerCompleted event

I'm creating backgroundworker not in my windows form but in the class file (BusinessLogic) that implements all the processing. From main form I first call the BL method that initializes the BGW. Then I call the method of BL which will start the BGW.
Here is more background :) on my implementation.
How to use BackGroundWorker in class file?
The DoWork event runs fine but it doesnt call the RunWorkerCompleted.
Some googling and I found out this link. I've a feeling that my problem is same as this guys.
http://www.eggheadcafe.com/software/aspnet/29191764/backgroundworker-does-not-fire-the-runworkercompleted-event.aspx
I'd appreciate any input on this issue. Thanks in advance.
Code in Main form:
private void frmMain_Load(object sender, EventArgs e)
{
Hide();
BusinessLogic.BGWInitialize();
BusinessLogic.StartBackgroundWorker();
while (!BusinessLogic.firstCycleDone)
{
Thread.Sleep(100);
}
Show();
}
Code in BusinessLogic:
public static void BGWInitialize()
{
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.ProgressChanged += new ProgressChangedEventHandler(bgWorker_ProgressChanged);
bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
bgWorker.WorkerReportsProgress = true;
}
public static void StartBackgroundWorker()
{
bgWorker.RunWorkerAsync();
}
private static void bgWorker_RunWorkerCompleted(
object sender, RunWorkerCompletedEventArgs e)
{
firstCycleDone = true;
}
The completed event is Invoked to the main thread. It is supposed to be picked up and executed by the MessagePump.
However, your Wait-and-Sleep code is blocking the message loop.
Hide();
....
while (!BusinessLogic.firstCycleDone)
{
Thread.Sleep(100);
}
Show();
The answer here is that you have no use for a Backgroundworker or another form of threading...
Just call bgWorker_DoWork() directly:
// Hide();
bgWorker_DoWork(); // rename
Show();
if you would just call Application.DoEvents(); instead of Sleep(100); your code would work, but as I said previously BackgroundWorker class is a buggy thing and I would personally use my own threads and reports
Alternatively you could sleep a bit and then call DoEvents

Question to Auto logout C# desktop application

I need to implement an auto logout feature in C#. Previously i have asked a similiar question before and i managed to implement it using the System.Windows.Forms.Timer . But right now i have a additional requirement apart from resetting the timer when the user move the mouse or enters a key i also need to reset the timer when a new message is received via the serial port ( DataReceived event handler ).
serialPort.DataReceived += port_DataRecieved;
I need to include the reset function in a portion of the port_DataRecieved function. I cannot simply add another delegate method to the serialPort.DataReceived which will perform the reset as the serialPort.DataReceived will received a lot of other messages that i am not interested in. I want to perform a reset when the message that i am interested in arrives. And i know where to put the reset feature. The issue is that the timer does not reset in port_DataRecieved method. And i cannot achieve the desired result using the System.Threading.Timer. Anyone can guide me or provide some suggestion on this issue ? Any help provided will be greatly apperciated.
public partial class Form1: Form
{
private System.Windows.Forms.Timer sessionTimer = new System.Windows.Forms.Timer();
public Form1()
{
initialiseTimer();
}
private void port_DataRecieved(object sender, SerialDataReceivedEventArgs e)
{
try
{
serialPort= (SerialPort)sender;
str = serialPort.ReadExisting();
string[] split = str.Split(Convert.ToChar(10));
for (int i = 1; i < split.Length; i++)
{
str = split[i];
if (split[i].StartsWith("+CMTI:"))
{
sessionTimer.Stop();
sessionTimer.Start();
//Other codes
}
}
}
catch (Exception)
{
MessageBox.Show("Error processing received commands !", "CONNECTION ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
sendRecPort.Close();
}
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
sessionTimer.Stop();
sessionTimer.Start();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
sessionTimer.Stop();
sessionTimer.Start();
}
private void initialiseTimer()
{
sessionTimer.Interval = (5 * 60 * 1000);
sessionTimer.Tick += new EventHandler(logOutUser);
sessionTimer.Stop();
sessionTimer.Start();
}
private void logOutUser(object sender, EventArgs e)
{
// logout the user
this.Hide();
//Open up the login Form
login.Show();
}
}
Your problem is that the the DataReceived event is being executed on a thread other than the UI thread. You're trying to modify the timer (a UI object) from a non-UI thread. This typically throws an exception, but it's possible that the method that issues the DataReceived event is swallowing that exception.
From the documentation for the DataReceived event:
The DataReceived event is raised on a
secondary thread when data is received
from the SerialPort object. Because
this event is raised on a secondary
thread, and not the main thread,
attempting to modify some elements in
the main thread, such as UI elements,
could raise a threading exception. If
it is necessary to modify elements in
the main Form or Control, post change
requests back using Invoke, which will
do the work on the proper thread.
You need to synchronize with the UI thread to set the timer.
void ResetTimer()
{
sessionTimer.Stop();
sessionTimer.Start();
}
private void port_DataRecieved(object sender, SerialDataReceivedEventArgs e)
{
//Other codes
this.Invoke((MethodInvoker)delegate { ResetTimer(); });
//Other codes
}
I need to include the reset function in a portion of the port_DataReceived function.
Ok. Gotcha.
I cannot simply add another delegate method to the serialPort.DataReceived which will perform the reset as the serialPort.DataReceived will receive a lot of other messages that I am not interested in.
Ok, but I thought you said:
I want to perform a reset when the message that I am interested in arrives.
So you either have to listen to that DataReceived method, or you won't know when that message arrives.
I'm confused. What is it you want to do? Magic?
if (dataReceived == "someValue1")
{
//action if matches "someValue1"
}
else if (dataReceived.Contains("someValue2"))
{
// action if contains "someValue2"
}
else if (dataReceived.IndexOf("someValue3") != -1 )
{
// action if contains "someValue3"
}
else if (dataReceived == "someValue4")
{
// action if matches "someValue4"
}
else
{
// default action
}

Categories