I read/write data to serial port and I want to see reading on listbox right away. I created a new thread to send command to serial port. I keep the main thread empty, so, it can update the UI and also serial port event handler wont be interrupted with something else.(I am not sure is it right approach?)
The following code works with while (!dataRecieved) { Thread.Sleep(4000); } but does not works with while (!dataRecieved) { Thread.Sleep(100); }.
The problem is if I use 100ms sleep, serial port event handler fire only once and then program stops!(If I debug with breakpoint 100ms works because I create additional time when stepping into the code.) If I wait 4000ms the program works. Also, I check the time between sending data and receiving data from serial port is 200ms. So, 100ms is reasonable.
Here is the code:
public bool dataRecieved = false;
public Form1()
{
InitializeComponent();
}
public void AppendTextBox(string value)
{
this.Invoke((MethodInvoker)delegate { richTextBox1.Text += value + "\n";});
}
private void button1_Click(object sender, EventArgs e)
{
serialPort1.Open();
Thread testThread = new Thread(() => sendThread());
testThread.Start();
}
public void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
data = serialPort1.ReadLine();
dataRecieved = true;
}
public void sendThread()
{
for(int i = 0; i<10; i++)
{
serialPort1.WriteLine("AT" + i);
// Following line creates odd situation:
// if Thread.Sleep(100), I receive only first data, then program stops(serial port handler doesnt fire!).
// if Thread.Sleep(4000), I receive all data, successfuly works.
// But I do not want to wait 4000ms, because I receive answer from device in 200ms.
while (!dataRecieved) { Thread.Sleep(100); }
AppendTextBox("Received" + "AT" + i);
dataRecieved = false;
}
}
Where I am wrong? Can you please provide a solution?
I even didn't use a new Thead for write and read on SerialPort. You just need use update control in Invoke() is ok. Below is my update on richTextBox. You can change form richTextBox to your listbox.
public void update_RichTextBox(string message)
{
Invoke(new System.Action(() =>
{
txtReceivedData.Text += message;
txtReceivedData.Refresh();
txtReceivedData.SelectionStart = txtReceivedData.Text.Length;
txtReceivedData.ScrollToCaret();
}));
}
and the way to use above void:
if (ComPort.IsOpen)
{
ComPort.Write(_inputdata + "\r");
Form1._Form1.update_RichTextBox(_inputdata + "\r");
string _receviedData = ComPort.ReadExisting();
Form1._Form1.update_RichTextBox(respond);
ComPort.DiscardInBuffer();//delete all data in device's received buffer
ComPort.DiscardOutBuffer();// delete all data in transmit buffer
}
else
{
MessageBox.Show("haven't yet open COM port");
return "FLASE";
}
I use something I call "Cross Thread Linker"
#region Cross Thread Linker
public bool ControlInvokeRequired(Control c, Action a)
{
if (c.InvokeRequired) c.Invoke(new MethodInvoker(delegate { a(); }));
else return false;
return true;
}
void Update_RichTextBox(RichTextBox rtb, string Text)
{
if (ControlInvokeRequired(rtb, () => Update_RichTextBox(rtb, Text))) return;
rtb.AppendText(Text + Environment.NewLine);
}
#endregion
Then:
Update_RichTextBox(richTextBox1, "Text to append");
Related
I was just starting to use C# last week and tried to create a simple serial monitor program. I want my program to read data from serialport continuously while displaying those data in the Form Application at the same time.
I use the code from here as reference https://github.com/ahelsayd/Serial-Lab
I use the same BeginInvoke() function. However I can not pass the variable that I want to write.
This is the original code
private void rx_data_event(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
if (mySerial.IsOpen)
{
try
{
//=====Only this part is different=======================
int dataLength = mySerial.BytesToRead;
byte[] dataReceived = new byte[dataLength];
int nbytes = mySerial.Read(dataReceived, 0, dataLength);
if (nbytes == 0) return;
//=====Only this part is different=======================
this.BeginInvoke((Action)(() =>
{
data = System.Text.Encoding.Default.GetString(dataReceived);
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1.RunWorkerAsync();
}
}));
}
catch { alert("Can't read form " + mySerial.PortName + " port it might be opennd in another program"); }
}
}
//And then update the UI
private void update_rxtextarea_event(object sender, DoWorkEventArgs e)
{
this.BeginInvoke((Action)(() =>
{
if (rx_textarea.Lines.Count() > 5000)
rx_textarea.ResetText();
rx_textarea.AppendText("[RX]> " + data);
}));
}
This code can read the Serialport and Write into the Form simultaneously. However, it does not receive all data from the serialport. So I modified the code to write the data into a buffer first until all data is received.
The modified code
private void rx_data_event(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
if (mySerial.IsOpen)
{
try
{
//=====Only this part is different=======================
string Data = mySerial.ReadExisting();
serialBuffer.Append(Data);
string bufferString = serialBuffer.ToString();
int index = -1;
do
{
index = bufferString.IndexOf(terminationSequence);
if (index > -1)
{
string message = bufferString.Substring(0, index);
bufferString = bufferString.Remove(0, index + terminationSequence.Length);
}
}
while (index > -1);
serialBuffer = new StringBuilder(bufferString);
byte[] bytes = new byte[30];
if (serialBuffer.Length == 15) {
Console.WriteLine("data:" + serialBuffer);
bytes = Encoding.ASCII.GetBytes(serialBuffer.ToString());
}
//=====Only this part is different=======================
this.BeginInvoke((Action)(() =>
{
data = Encoding.ASCII.GetString(bytes);
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1.RunWorkerAsync();
}
}));
}
catch { alert("Can't read form " + mySerial.PortName + " port it might be opennd in another program"); }
}
}
The problem is in the Form application the value of bytes is always null although when I checked by writing the output value to console window I can see the value of bytes updated.
I am very confused why the variable dataReceived value can be accessed by BeginInvoke while the variable bytes keep having null value. Is there something that I've missed that causing the value not get updated?
So I wasn't able to do it yesterday, but I promised the OP I would write something in an answer related to a comment of mine. From what I understand he/she is trying to update something in a Form based on received data from a serial port. Here's how I do it:
First off you need to declare a delegate and an event (which is basically a list of delegates):
public delegate void Data_Received_EventHandler(object sender, Data_Received_EventArgs e);
public event Data_Received_EventHandler Data_Received;
These are used to substitute the original "data received" event and event args from the serial port.
I usually define the Data_Received_EventArgs type as something based of an array of bytes, for simplicity:
public class Data_Received_EventArgs : EventArgs
{
public byte[] data;
internal Data_Received_EventArgs(int length)
{
data = new byte[length];
}
}
Then, in the original data reception event (let's say your serial port is called serialPort1):
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// check how many bytes you need to read:
int bytesToRead = serialPort1.BytesToRead;
// declare your arguments for the event based on that number of bytes (check the Data_Received_EventArgs constructor):
Data_Received_EventArgs args = new Data_Received_EventArgs(bytesToRead);
// copy the bytes from your serial port into the arguments:
for (int i = 0; i < bytesToRead; i++)
args.data[i] = (byte)serialPort1.ReadByte();
// verify if there are subscribers to the event (list not empty) and fire your event using BeginInvoke:
if (Data_Received != null)
BeginInvoke((MethodInvoker)delegate () { Data_Received(this, args); });
}
What we now have is an event which is guaranteed to execute its handlers on the UI thread.
To use it, you can subscribe to the event like so:
Data_Received += My_Data_Received_Handler;
Your handler should have the same signature as the delegate we declared in the first line of code (should be void and have specified parameters):
private void My_Data_Received_Handler(object sender, Data_Received_EventArgs e)
{
// read bytes from the arguments as you normally would from an array and do whatever e.g.:
some_Label.Text = e.data[0].ToString(); // without worrying about crossthreading
}
I know this is not the answer the OP wanted, but I hope it helps in simplifying what he/she was trying to do in the first place.
I tested this code with placing a breakpoint in Monodevelop, running on a Raspbian. This app needs to communicate with a Windows, with a C# console application.
Although the timer1_Tick method prints the incoming message everytime (and it clearly prints the new message, because there I clear the message variable everytime after an incoming message), it seems to be that the OnNewData doesn't run everytime, when there's an incoming message.
I ask new incoming message with sending the "mes" out in the timer1_Tick.
I don't process the other kind of incoming messages in that method, but for the response for "mes", this place would be the best for processing. The reason is the "mes" creates answer types ("", " type answers), what I can get anytime.
I don't know what is the problem. Any idea? How could this happen, if the textbox is being refreshed nicely after each new incoming message?
public partial class Form0 : Form
{
public static string ReadCharactersIntoString(){
char c='c';
string outputString=String.Empty;
do{
c=(char)streamReader.Read();
outputString=outputString+c;
}while(c!='>');
return outputString;
}
async void OnNewData(object sender, EventArgs e){
message = Form0.ReadCharactersIntoString ();
//work with message variable works unreliable...
//sometimes this method runs, when there's an incoming message, sometimes it's not...
//although tick method prints the message everytime...
//why? how to solve this?
reseter.Set ();
}
public void AcceptMessages(){
while (true) {
try{
if (streamReader!=null && streamReader.Peek () > 0) {
newIncomingDataEvent.Invoke(this,EventArgs.Empty);
reseter.WaitOne ();
}
}catch{
}
}
}
public static async Task Waiting(){
while (message == null || message[message.Length-1]!='>') {
}
}
public static async Task<string> CollectingAnswer(){
await Waiting();
return message;
//when the answer is completely collected, it gives me back
}
public void ConnectionEstablisher()
{
while (true)
{
if (!activeConnection)
{
try
{
socketForServer = new TcpClient(ip, port);
networkStream = socketForServer.GetStream();
streamReader = new System.IO.StreamReader(networkStream);
streamWriter = new System.IO.StreamWriter(networkStream);
streamReader.BaseStream.ReadTimeout = 5000;
streamWriter.BaseStream.ReadTimeout = 5000;
activeConnection = true;
newIncomingDataEvent+=OnNewData;
reseter = new ManualResetEvent (true);
}
catch(Exception e)
{
activeConnection = false;
}
}
}
}
private async void timer1_Tick(object sender, EventArgs e)
{
if (this.Visible && activeConnection)
{
try
{
streamWriter.Write("mes");
streamWriter.Flush();
textBoxSending.AppendText("mes" + Environment.NewLine);
collectAnswer=CollectingAnswer();
outputString=await collectAnswer;
message=null;
textBoxAccepting.AppendText(outputString + Environment.NewLine);
//this always prints the incoming response message...
I'm creating a game in which I use TCP/IP connection. The problem is that I'm using .Invoke to help me receive and send message.
The program goes like this: I'm my first window, i'm starting and connecting to the server like this :
{
TcpListener listener = new TcpListener(IPAddress.Any, this.port);
listener.Start();
try {
this.client = listener.AcceptTcpClient();
gameWindow = new GameWindow(this.client, true);
gameWindow.StartGame();
}
}
then i'm connecting to it like this:
{
IPEndPoint ipEnd = new IPEndPoint(this.serverIP, this.port);
{
try {
client.Connect(ipEnd);
if (client.Connected) {
gameWindow = new GameWindow(this.client, false);
gameWindow.StartGame();
}
}
}
The constructor for gameWindow (which is a form) looks like this:
public GameWindow(TcpClient thisClient, bool isServer)
{
InitializeComponent();
this.client = thisClient;
this.reader = new StreamReader(thisClient.GetStream());
this.writer = new StreamWriter(thisClient.GetStream());
this.writer.AutoFlush = true;
}
I must wait for the server to send a message to the client, and then start the client ( I have a function .startGame() that uses .ShowDialog() and creates some pictureBoxs)
But nowhere I can get my handle created. I've tried to put this.createHandle() (read about it here) into GameWindow_Load but still not works. If I try to send a message with:
workerSendData.RunWorkerAsync(); I get:
Additional information: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
What can I do to get my handler created? Using Thread.Sleep will sleep my whole UI, which does not work (a "solution" found on the internet)
My code for sending message :
private void workerSendData_DoWork(object sender, DoWorkEventArgs e)
{
if (client.Connected) {
this.writer.WriteLine(this.toSend); // aici trimitem datele.
// de modificat : aici vom adauga in lista noastra miscarile.
this.Invoke(new MethodInvoker(delegate () { MessageBox.Show("Me:" + this.toSend + "\n"); }));
}
else {
MessageBox.Show("Send failed");
}
workerSendData.CancelAsync();
}
My code for receiving data:
private void workerReceiveData_DoWork(object sender, DoWorkEventArgs e)
{
while (client.Connected) {
try {
this.received = this.reader.ReadLine();
this.myTurn = true;
this.Invoke(new MethodInvoker(delegate () {
MessageBox.Show("This has been received: " + this.received);
/*this.tbReceive.AppendText("You:" + this.received + "\n");*/
}));
this.received = "";
}
catch (Exception x) {
MessageBox.Show(x.Message.ToString());
}
}
}
It seems that you cannot invoke an action before the Window is fully initialized and loaded. Assuming you are working in Windows Forms, there is a solution provided by #Greg D on this question, but it doesn't be to be the safest way to go.
I would suggest that you try to find a way to start the worker only after the window is loaded (for example using the Loaded event), so that the handle is definitely ready and this situation does not occur.
I'm facing a bit of problem, it's giving error "Cross-thread operation not valid" even though I'm using Invoke method.
Here's the code snipit.
Method to update log box
private void updateStatus(String msg)
{
if (logBox.InvokeRequired)
logBox.Invoke((MethodInvoker)delegate()
{
logBox.SelectionStart = logBox.Text.Length;
logBox.Text += "\n";
logBox.Text += msg;
});
else
logBox.SelectionStart = logBox.Text.Length;
logBox.Text += "\n";
logBox.Text += msg;
}
And this Run method is being run by a thread.
private void Run()
{
int port;
try
{
port = Int32.Parse(broadcastPortTextBox.Text);
}
catch (Exception ex)
{
MetroFramework.MetroMessageBox.Show(this, ex.Message);
return;
}
updateStatus("Starting server at port: " + port.ToString());
server = new HTTPServer.HTTPServer(port);
server.Start();
} //function
It runs fine for the first time but when I click stop, it gives an exception.
private void stopButton_Click(object sender, EventArgs e)
{
updateStatus("Stoping server");
th.Abort();
updateStatus("Server stoped!");
}
I would try using the direct cast for the invoke. There's no need to check whether the invoke is required or not. If you invoke something it should always happen (in your context). Just remove the updateStatus(String msg) method so and try to cast your update like this:
void Run() {
// stuff
broadcastPortTextBox.Invoke(() => {
port = Int32.Parse(broadcastPortTextBox.Text);
});
// stuff..
logBox.Invoke(() => {
logBox.SelectionStart = logBox.Text.Length;
logBox.Text += string.Format("{0}{1}", Environment.NewLine, "Your message text..");
});
// stuff..
}
Note: If you manipulate any non thread owned element use the invoke method. Otherwise you'll end up with exceptions (see 'broadcastPortTextBox');
Edit: Accidently saved before I was done.
i'm working with ping Librarry in net 3.5 to check the presence of IP.
take a look at the code below:
public void PingIP(string IP)
{
var ping = new Ping();
ping.PingCompleted += new PingCompletedEventHandler(ping_PingCompleted); //here the event handler of ping
ping.SendAsync(IP,"a");
}
void ping_PingCompleted(object sender, PingCompletedEventArgs e)
{
if (e.Reply.Status == IPStatus.Success)
{
//On Ping Success
}
}
Then i execute the code through Thread or backgroundworker.
private void CheckSomeIP()
{
for (int a = 1; a <= 255; a++)
{
PingIP("192.168.1." + a);
}
}
System.Threading.Thread checkip = new System.Threading.Thread(CheckSomeIP);
checkip.Start();
Well, here is the problem:
If i start the thread then i would to close the application (Close with Controlbox at corner), i would get "App Crash"
although i have closed/abort the thread.
I think the problem is event handler? as they still working when i'm closing the Application so that i will get "App Crash"
What would be the best way to solve this case?
I think, on a successfull Ping, you are trying to update the interface from within the Thread, which will cause an CrossThreadingOperation exception.
Search the Web for ThreadSave / delegates:
public void PingIP(string IP)
{
var ping = new Ping();
ping.PingCompleted += new PingCompletedEventHandler(ping_PingCompleted); //here the event handler of ping
ping.SendAsync(IP,"a");
}
delegate void updateTextBoxFromThread(String Text);
void updateTextBox(String Text){
if (this.textbox1.InvokeRequired){
//textbox created by other thread.
updateTextBoxFromThread d = new updateTextBoxFromThread(updateTextBox);
this.invoke(d, new object[] {Text});
}else{
//running on same thread. - invoking the delegate will lead to this part.
this.textbox1.text = Text;
}
}
void ping_PingCompleted(object sender, PingCompletedEventArgs e)
{
if (e.Reply.Status == IPStatus.Success)
{
updateTextBox(Text);
}
}
Also on "quitting" the application, you may want to cancel al running threads. Therefore you need to keep the reference on every thread you start somewhere in your application. in the formClosing-Event of your Main-Form, you can force all (running) threads to stop.