Wait for thread to finish without blocking UI thread - c#

I am writing winForm app which have to listem SerialPort and print the data into multi line textbox in real time.
Here is the code:
using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;
using System.ComponentModel;
namespace SCPListener
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
GetAvailablePortNames();
panel1.BackColor = ColorTranslator.FromHtml("#f44147");
cbDefaultValue();
}
String[] PortNames;
bool conn = true;
String data;
public string datetime;
Form2 frm = new Form2();
void GetAvailablePortNames()
{
PortNames = SerialPort.GetPortNames();
comboBox1.Items.AddRange(PortNames);
}
void RefreshAvailablePortNames()
{
comboBox1.Items.Clear();
PortNames = SerialPort.GetPortNames();
comboBox1.Items.AddRange(PortNames);
}
public string getDataTime()
{
DateTime time = DateTime.Now;
string date = time.ToString(#"hh\:mm\:ss");
return date;
}
public void GetData()
{
string date = getDataTime();
data = serialPort1.ReadLine();
textBox1.AppendText("[" + date + "] " + "Received: " + data + "\r\n");
}
public void cbDefaultValue()
{
comboBox1.SelectedIndex = 0;
comboBox5.SelectedIndex = 0;
comboBox2.SelectedIndex = 0;
comboBox3.SelectedIndex = 0;
comboBox4.SelectedIndex = 0;
}
private void button1_Click(object sender, EventArgs e)
{
try
{
if (comboBox1.Text == "" || comboBox2.Text == "" || comboBox3.Text == "" || comboBox4.Text == "" || comboBox5.Text == "")
{
MessageBox.Show("Please port settings", "Incorrect port settings", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
DateTime time = DateTime.Now;
string date = time.ToString(#"hh\:mm\:ss");
serialPort1.PortName = comboBox1.Text;
serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);
switch (comboBox3.Text)
{
case "5":
serialPort1.DataBits = 5;
break;
case "6":
serialPort1.DataBits = 6;
break;
case "7":
serialPort1.DataBits = 7;
break;
case "8":
serialPort1.DataBits = 8;
break;
default:
serialPort1.DataBits = 5;
break;
}
switch (comboBox4.Text)
{
case "None":
serialPort1.Parity = Parity.None;
break;
case "Odd":
serialPort1.Parity = Parity.Odd;
break;
case "Even":
serialPort1.Parity = Parity.Even;
break;
case "Mark":
serialPort1.Parity = Parity.Mark;
break;
case "Space":
serialPort1.Parity = Parity.Space;
break;
}
switch (comboBox5.Text)
{
case "One":
serialPort1.StopBits = StopBits.One;
break;
case "Two":
serialPort1.StopBits = StopBits.Two;
break;
case "OnePointFive":
serialPort1.StopBits = StopBits.OnePointFive;
break;
}
serialPort1.Open();
string CurrentPornName = comboBox1.Text;
label7.Text = "Opened " + CurrentPornName;
panel1.BackColor = ColorTranslator.FromHtml("#42f477");
comboBox1.Enabled = false;
comboBox2.Enabled = false;
comboBox3.Enabled = false;
comboBox4.Enabled = false;
comboBox5.Enabled = false;
//button5.Enabled = false;
//button6.Enabled = false;
}
}
catch (Exception ex)
{
if (ex is UnauthorizedAccessException)
{
MessageBox.Show("Unauthorized Acces","Access Error",MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else if (ex is System.IO.IOException)
{
MessageBox.Show("Please plug in your device", "IO Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
private void button2_Click(object sender, EventArgs e)
{
panel1.BackColor = ColorTranslator.FromHtml("#f44147");
string CurrentPornName = comboBox1.Text;
label7.Text = "Closed " + CurrentPornName; ;
serialPort1.Close();
comboBox1.Enabled = true;
comboBox2.Enabled = true;
comboBox3.Enabled = true;
comboBox4.Enabled = true;
comboBox5.Enabled = true;
//button5.Enabled = true;
//button6.Enabled = true;
}
private void button5_Click(object sender, EventArgs e)
{
RefreshAvailablePortNames();
}
private void button3_Click(object sender, EventArgs e)
{
//backgroundWorker1.RunWorkerAsync();
Thread thread = new Thread(start: ()=>
{
try
{
datetime = getDataTime();
string date = getDataTime();
startListening:
if (serialPort1.BytesToRead > 0)
{
datetime = getDataTime();
data = serialPort1.ReadLine();
textBox2.AppendText("[" + datetime + "] " + "Received: " + data + "\r\n");
}
else
textBox2.AppendText("[" + datetime + "] " + "Error: There is no data to read" + "\r\n");
if (conn)
goto startListening;
}
catch (Exception ex)
{
DateTime time = DateTime.Now;
string date = time.ToString(#"hh\:mm\:ss");
if (ex is TimeoutException)
{
textBox1.AppendText("[" + date + "] " + "Timuot Exception" + "\r\n");
}
else if (ex is InvalidOperationException)
{
MessageBox.Show("Plese check settings", "Error: Invalid Operation", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else if (ex is System.IO.IOException)
{
MessageBox.Show("Port opened, but device unpluged..", "IO Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
});
thread.Start();
}
private void button4_Click(object sender, EventArgs e)
{
conn = false;
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
datetime = getDataTime();
string date = getDataTime();
startListening:
if (serialPort1.BytesToRead > 0)
{
datetime = getDataTime();
data = serialPort1.ReadLine();
textBox2.AppendText("[" + datetime + "] " + "Received: " + data + "\r\n");
}
else
textBox2.AppendText("[" + datetime + "] " + "Error: There is no data to read" + "\r\n");
if (conn)
goto startListening;
}
catch (Exception ex)
{
DateTime time = DateTime.Now;
string date = time.ToString(#"hh\:mm\:ss");
if (ex is TimeoutException)
{
textBox1.AppendText("[" + date + "] " + "Timuot Exception" + "\r\n");
}
else if (ex is InvalidOperationException)
{
MessageBox.Show("Plese check settings", "Error: Invalid Operation", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else if (ex is System.IO.IOException)
{
MessageBox.Show("Port opened, but device unpluged..", "IO Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
}
About Code:
comboBox1 -> where the Ports are listed
comboBox2 -> where the Baud Rates options are listed
comboBox3 -> where the Stop Bits options are listed
comboBox4 -> where the Data Bits options are listed
comboBox5 -> where the Parity options are listed
button1 -> to connect chosen port
button2 -> to disconnect port
button3 -> to start listening and print data into textBox in real time
button4 -> to stop listening
button5 -> refresh available ports
panel1-> to identify port is connected or not (if connected it is green if it is not connected it is red)
So, what i want to do is that when I click the stop button (button4), program have to stop listening. I am trying to achieve this result using backgroundWorker .
Using this code when i try to listen port (clicking start button button3 there is opening the small error window and tell please check settings (so it catching InvalidOperationException according to my code); If I wont use backgoundWorker and also remove try{...} cath{...} program start do display the received data from port but blocking the UI. Any ideas will be very helpful.

button4_click needs to call backgroundWorker1.CancelAsync() instead of setting a bool. With BackgroundWorkers, you need to specify that you want them to end and then check for it, setting and checking an outside bool will not work. The try block in your backgroundWorker1_DoWork() method should look like the following. Keep in mind, it is easier for you and others to help you if you rename your variables (either done in the UI or by right clicking the variable and selecting 'Rename'). Another note before the code, it is almost never ok to use 'goto', there is almost always a better option (a while loop in this case).
datetime = getDataTime();
string date = getDataTime();
while (!backgroundWorker1.CancellationPending)
{
if (serialPort1.BytesToRead > 0)
{
datetime = getDataTime();
data = serialPort1.ReadLine();
textBox2.AppendText($"[{datetime}] Received: {data}\r\n");
}
else
{
textBox2.AppendText($"[{datetime}] Error: There is no data to read\r\n");
}
}
Edit to say: This should get you started, you will also want to check out this link to access your UI from backgroundWorker1, the problem is your UI is on one thread and your BackgroundWorker on another, threads do not work well together without some complimenting code

Related

Pass value two textbox's from one variable value

I have an encoder connected from Siemens PLC with data block "DB1.DBD56".
My problem is, the two textbox has the same result or value.
Scenario:
Read Before 1seconds and
Read After 1seconds
I want the result like this:
[For reference]
(https://i.stack.imgur.com/HK9zU.png)
[PLC Ladder]
(https://i.stack.imgur.com/kGN62.png)
#region EVENT TO START READING THE ENCODER
private void timer_anvilState_Tick(object sender, EventArgs e)
{
try
{
Edata_convertion();
read_lastweld_result();
bool anvilState = (bool)plc_s7_1200.Read("DB1.DBX68.4");
if (plc_s7_1200.IsConnected)
{
if (anvilState == true)
{
timer_OneCycleProcess.Start(); //STATE MACHINE TIMER
}
else
{
timer_OneCycleProcess.Stop();
command = "IDLE";
}
}
}
catch (Exception ex)
{
MessageBox.Show("Please check between PC and PLC connection " + "\n" + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
#region READ BEFORE
public void Read_Before()
{
if (plc_s7_1200.IsConnected)
{
bool anvilState = (bool)plc_s7_1200.Read("DB1.DBX68.4");
if (anvilState == true)
{
string Edata = string.Format("{0:0.0}", convertTomm);
txtbefore.Text = Edata;
}
}
}
#endregion
#region READ AFTER
public void Read_After()
{
bool gen_US_on = (bool)plc_s7_1200.Read("DB1.DBX78.2");
if (gen_US_on == true)
{
string Edata = string.Format("{0:0.0}", convertTomm);
txtafterWeld.Text = Edata;
}
}
#endregion
Here's my solution.
Only revised the PLC ladder as stated my replied at comment section.
https://i.stack.imgur.com/GUBtg.png
My C# program nothing changes.
Sharing my basic state machine code
#region ONE CYCLE PROCESS
private void timer_OneCycleProcess_Tick(object sender, EventArgs e)
{
switch (command)
{
case "IDLE":
command = "BEFORE";
break;
case "BEFORE":
before();
command = "AFTER";
break;
case "AFTER":
after();
prev_result();
read_maintenanceCounter();
read_outputCounter();
command = "SAVE";
break;
case "SAVE":
command = "STOP";
save();
save_history();
break;
case "STOP":
timer_OneCycleProcess.Stop();
plc_s7_1200.Write("DB1.DBX38.0", 0);
break;
default:
break;
}
}
#endregion

How to update text of label in timer_Tick in C# WinForms

In my winforms C# project I have a NFC device which is connected to pc and works properly.I have this part of code in Timer_Tick
private void timer1_Tick(object sender, EventArgs e)
{
try
{
if (NFC.Connect())
//label6.Text = "دستگاه متصل میباشد";
label6.Text = "device is connected";
label6.Invalidate();
label6.Update();
Application.DoEvents();
}
catch (Exception exception)
{
label6.Text = "device is not connected";
//label6.Text = "دستگاه متصل نمی باشد";
label6.Invalidate();
label6.ForeColor = Color.Red;
label6.Update();
Application.DoEvents();
}
}
I want to show the device is connected or not but the problem is that label6.text does not update.
Can anyone help please?
If you're using this library; https://github.com/h4kbas/nfc-reader/blob/master/NFCReader.cs
Then perhaps you should have a code like:
try
{
if (NFC.Connect())
label6.Text = "device is connected " + DateTime.Now;
else
label6.Text = "device didn't connect " + DateTime.Now;
label6.ForeColor = Color.Black;
}
catch (Exception e)
{
label6.Text = "device error " + e.Message;
label6.ForeColor = Color.Red;
}
I'd suggest also renaming label6 to something more apt, like statusLabel. Always rename your controls
Re your comment, here is the code of Connect()
ublic bool Connect()
{
string readerName = GetReadersList()[0];
connActive = true;
retCode = Card.SCardConnect(hContext, readerName, Card.SCARD_SHARE_SHARED,
Card.SCARD_PROTOCOL_T0 | Card.SCARD_PROTOCOL_T1, ref hCard, ref Protocol);
if (retCode != Card.SCARD_S_SUCCESS)
{
connActive = false;
return false;
}
else
return true;
}
You claim "it's connected but Caius code says it's not" -> Caius' code only goes off whether the library says it is connected or not. If Caius' code says it is not connected when it is, it's not Caius' code fault.. Library is open source; go fix the code of it for others to benefit

Error HRESULT: 0x80070021 when connecting to scanner

I have the following code for scanning a document using WIA with a Kodak ScanMate i1120
public static Device InitScanner(string DeviceID)
{
var deviceManager = new DeviceManager();
// Create an empty variable to store the scanner instance
DeviceInfo firstScannerAvailable = null;
// Loop through the list of devices
for (int i = 1; i <= deviceManager.DeviceInfos.Count; i++)
{
if (deviceManager.DeviceInfos[i].DeviceID == DeviceID)
{
firstScannerAvailable = deviceManager.DeviceInfos[i];
var device = firstScannerAvailable.Connect();
return _ConnectedScanner;
}
}
return _ConnectedScanner;
}
public void ScanDocument()
{
string _DeviceID = GetDeviceID(Properties.Settings.Default.DefaultDevice);
InitScanner(_DeviceID);
if (_ConnectedScanner == null)
{
MessageBox.Show("Scanner is not turned on or connected");
Lcontinue = false;
return;
}
else
{
try
{
_item = _ConnectedScanner.Items[1] as Item;
SelectDeviceDocumentHandling(_ConnectedScanner, DeviceDocumentHandling.Feeder);
//MessageBox.Show(GetDeviceProperty(_ConnectedScanner, 3078).ToString());
_MyImage = (ImageFile)_ConnectedScanner.Items[1].Transfer();
DocCount++;
}
catch (Exception ex)
{
switch (ex.HResult)
{
case -2145320959:
Lcontinue = false;
MessageBox.Show(ex.Message + Environment.NewLine + "Unknown Error");
return;
case -2145320957:
if (DocCount == 0)
{
Lcontinue = false;
//_WaitForOperator = false;
MessageBox.Show(ex.Message + Environment.NewLine + "There is no paper in the ADF");
return;
}
else
{
Lcontinue = false;
//WaitForOperator = false;
//DocCount = 0;
}
break;
case -2145320954:
if (DocCount == 0)
{
Lcontinue = false;
//_WaitForOperator = false;
MessageBox.Show(ex.Message + Environment.NewLine + "The device is busy, Please retry");
return;
}
else
{
Lcontinue = false;
DocCount = 0;
}
break;
default:
Lcontinue = false;
MessageBox.Show("Error No : " + ex.HResult + Environment.NewLine + Environment.NewLine + ex.ToString());
return;
}
}
}
}
I have two scanners on the system, a Kyocera and the Kodak, but I only want to use the Kodak for this app.
When I try to scan with the Kodak I get error
HRESULT: 0x80070021
System.IO.FileLoadException: `The process cannot access the file because another process has locked a portion of the file.
the error happens on the following line
var device = firstScannerAvailable.Connect();
I have no other scanning applications open so I am unable to figure out what is locking the file.
Can someone see what would be causing the file lock?

Serial Communication c# terminal, changing the stop bits ruins the comunication

so I wrote a terminal in c# and I need to communicate with a kl25z microprocessor.
the application needs to support the option to choose the number of stop bits, the baud rate and the parity method.
I managed to sort it out with the baud rate but when I try to change the stop bits something goes out of sync and I cant figure out what.
the problem is that the uart is getting jibrish kind of chars instead of what I intended it to get, it does work well when I use one stop bit so I assume I just missed something in my code
I'll add the part of the code that gives me hard time:
this is only the c# part, my terminal I'm pretty sure that the problem is here
and not in the kl25z code
public Form1() {
InitializeComponent(); //init all variables
getAvailablePorts(); //fill the ports combo box with all the ports
}
void getAvailablePorts() {
String[] ports = SerialPort.GetPortNames(); //get the names of all availoable ports
comboBox1.Items.AddRange(ports); //add all the ports names to the combo box
}
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) {
try {
SerialPort sp = (SerialPort) sender;
if (serialPort1.IsOpen & sp.IsOpen) {
string indata = sp.ReadLine();
Invoke(new Action(() => textBox1.Text = indata));
if (indata.Equals("new settings been set") ||
indata.Equals("\0new settings been set") ||
indata.Equals("new settings been set??")) {
// baud rate
Invoke(new Action(() => baud = Convert.ToInt32(comboBox3.Text)));
Invoke(new Action(() => serialPort1.BaudRate = baud));
//end bits
Invoke(new Action(() => serialPort1.StopBits = end));
}
}
} catch (System.IO.IOException error) {
return;
} catch (System.InvalidOperationException error) {
return;
}
}
//here i open the port
private void button3_Click(object sender, EventArgs e) { //start port
try { // give the micro processor the info it needs about the settings
serialPort1.Handshake = Handshake.RequestToSendXOnXOff;
serialPort1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
serialPort1.PortName = comboBox1.Text; //port
serialPort1.BaudRate = baud; //baud rate current
// serialPort1.Parity= parity;//parity current
serialPort1.StopBits = end; //stopBits current
serialPort1.DataBits = bits; //current
//sort strings to send
String baudString = comboBox3.Text;
String StopString = comboBox6.Text;
String bitsString = comboBox2.Text;
String parityString = comboBox4.Text;
String startString = comboBox5.Text;
serialPort1.Open(); //open port
bitsString = "8";
//send properties to klz
serialPort1.Write("prop" + "$" + baudString + "$" + StopString + "$" + bitsString + "$" + parityString + "$" + startString + "$");
if (Convert.ToInt32(comboBox6.Text) == 1) {
end = StopBits.One;
} else {
end = StopBits.Two;
}
bits = Convert.ToInt32(comboBox2.Text);
} catch (UnauthorizedAccessException) {
textBox1.Text = "Unauthorized Access";
}
try {
progressBar1.Value = 100;
button1.Enabled = true;
button2.Enabled = true;
button3.Enabled = false;
button4.Enabled = true;
textBox2.Enabled = true;
} catch (UnauthorizedAccessException) {
textBox1.Text = "Unauthorized Access";
}
}

How do I send a Ping request three times?

I'm new to this site and I am a beginner at programming. I am trying to get this program to ping three times every time the send button is hit. I want the pingDetailsTextBox to say something similar to this: Pinging www.yahoo.com . . .
98.139.180.149 41ms
98.139.180.149 56ms
98.139.180.149 51ms
I have tried several different things to get it to work, but the code is just beyond my knowledge.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using System.Net.NetworkInformation;
using System.Globalization;
namespace Microsoft.Samples.PingClient
{
partial class PingClientForm : Form
{
Ping pingClient = new Ping();
public PingClientForm()
{
InitializeComponent();
pingClient.PingCompleted +=
new PingCompletedEventHandler(pingClient_PingCompleted);
}
private void pingClient_PingCompleted(object sender, PingCompletedEventArgs e)
{
// Check to see if an error occurred. If no error, then display
// the address used and the ping time in milliseconds.
if (e.Error == null)
{
if (e.Cancelled)
{
pingDetailsTextBox.Text += " Ping cancelled. \r\n";
}
else
{
if (e.Reply.Status == IPStatus.Success)
{
pingDetailsTextBox.Text +=
" " + e.Reply.Address.ToString() + " " +
e.Reply.RoundtripTime.ToString(
NumberFormatInfo.CurrentInfo) + "ms" + "\r\n";
}
else
{
pingDetailsTextBox.Text +=
" " + GetStatusString(e.Reply.Status) + "\r\n";
}
}
}
else
{
// Otherwise display the error.
pingDetailsTextBox.Text += " Ping error.\r\n";
MessageBox.Show(
"An error occurred while sending this ping. " +
e.Error.InnerException.Message);
}
sendButton.Enabled = true;
}
private string GetStatusString(IPStatus status)
{
switch (status)
{
case IPStatus.Success:
return "Success.";
case IPStatus.DestinationHostUnreachable:
return "Destination host unreachable.";
case IPStatus.DestinationNetworkUnreachable:
return "Destination network unreachable.";
case IPStatus.DestinationPortUnreachable:
return "Destination port unreachable.";
case IPStatus.DestinationProtocolUnreachable:
return "Destination protocol unreachable.";
case IPStatus.PacketTooBig:
return "Packet too big.";
case IPStatus.TtlExpired:
return "TTL expired.";
case IPStatus.ParameterProblem:
return "Parameter problem.";
case IPStatus.SourceQuench:
return "Source quench.";
case IPStatus.TimedOut:
return "Timed out.";
default:
return "Ping failed.";
}
}
private void sendButton_Click(object sender, EventArgs e)
{
// Select all the text in the address box.
addressTextBox.SelectAll();
if (addressTextBox.Text.Length != 0)
{
// Disable the Send button.
sendButton.Enabled = false;
pingDetailsTextBox.Text +=
"Pinging " + addressTextBox.Text + " . . .\r\n";
// Send ping request.
pingClient.SendAsync(addressTextBox.Text, null);
}
else
{
MessageBox.Show("Please enter an IP address or host name.");
}
}
private void cancelButton_Click(object sender, EventArgs e)
{
// Cancel any pending pings.
pingClient.SendAsyncCancel();
}
}
}
Okay, I checked the documentation and I think what you can do is make the following edits instead:
int pingCount = 0;
private void pingClient_PingCompleted(object sender, PingCompletedEventArgs e)
{
pingCount++;
//if error else logic etc
if(pingCount < 3) {
pingClient.SendAsync(addressTextBox.Text, null);
} else {
sendButton.Enabled = true;
}
}
private void sendButton_Click(object sender, EventArgs e)
{
pingCount = 0;
//etc
}
Why do you need to do .SendAsync() and that PingCompleted bit? I just do .Send():
public static IPStatus CheckConn(string host, ref PingReply pngReply)
{
Ping png = new Ping();
try
{
pngReply = png.Send(host);
return pngReply.Status;
}
catch (Exception ex)
{
MessageBox.Show("Exception: " + ex.Message());
}
}
Then you can just get the IPStatus returned and use ref to get the pngReply back out with this:
private void sendButton_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
btn.Enabled = false;
string host = addressTextBox.Text;
pingDetailsTextBox.Text = String.Empty;
PingReply pngReply = null;
IPStatus ipStatus;
string strStatus = String.Empty;
if (!String.IsNullOrEmpty(host))
{
for (int i=0; i < 3; i++)
{
pingDetailsTextBox.Text +=
"Pinging " + host + " . . .\r\n";
ipStatus = CheckConn(host, ref pngReply);
strStatus = GetStatusString(ipStatus);
if (ipStatus == IPStatus.Success)
{
pingDetailsTextBox.Text +=
" " + pngReply.Address.ToString() + " " +
pngReply.RoundtripTime.ToString(
NumberFormatInfo.CurrentInfo) + "ms" + "\r\n";
}
else
{
pingDetailsTextBox.Text +=
" " + strStatus + "\r\n";
}
}
}
else
MessageBox.Show("No host to ping.");
btn.Enabled = true;
}

Categories