changing class to static blocks serialport datareceived event - c#

I am having trouble with SerialPort.DataReceived event being blocked by ShowDialog from MainWindow. Originally, the code worked as follows:
Printer connected to COM port using Windows Print Driver for operation. To check status, I must "move" windows printer COM port to COM1, and connect directly to the printer via SerialPort and send it a certain array of bytes. The printer will answer back >>LOW STOCK<< if out of paper or >>READY<< if status OK. I receive this through the serialport.datareceived event that I set up when switching the com port. If I get >>LOW STOCK<<, I continue to poll the printer over the serial port every 2 seconds.
public class PrintController
{
private PrintMonitor DefaultPrintMonitor;
public Printcontroller()
{
Initialize();
}
private void Initialize()
{
// Some code here to determine which printer type to load monitor for
DefaultPrintMonitor = new PrintMonitor();
}
}
And the Print Monitor:
public class PrintMonitor
{
private SerialPort Port;
private TimeoutManager TimeoutQueue = new TimeoutQueue();
private readonly char[] PrinterStatusCommand = new char[7] { ... The printers report status command ...};
public PrintMonitor() {}
public void StartPrintMonitor()
{
Thread thread = new Thread(new ThreadStart(RefreshPrintQueue))
{
IsBackground = true
};
thread.Start();
}
private void RefreshPrintQueue()
{
// This checks the Printer's Queue for any jobs remaining
// If the number of jobs = 0, all data has been transferred to the printer
// and we can switch the COM port so the printer can be polled for it's status
// Code omitted for brevity
if (jobs.Count() > 0)
{
// This will wait 1000ms and execute the method RefreshPrintQueue
TimeoutQueue.AddTimeout(RefreshPrintQueue, 1000);
}
else
{
TimeoutQueue.AddTimeout(GetStatus, 500);
}
}
private void GetStatus()
{
// Connect windows print driver to COM1
SetWindowsPrintDriver("COM1");
// Connect to printer using new SerialPort
ConnectSerialPrintDriver();
// Poll the printer over new SerialPort connection
QueryPrinterStatus();
}
private void ConnectSerialPrintDriver()
{
//Create new serial port to Port, and open
Port.DataReceived += Port_DataReceived;
}
private void QueryPrinterStatus()
{
byte[] send = Encoding.ASCII.GetBytes(PrinterStatusCommand);
Port.Write(send, 0, send.Length);
TimeoutQueue.AddTimeout(QueryPrinterStatus, 2000);
}
private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(100);
SerialPort p = sender as SerialPort;
string recv = p.ReadExisting().Trim();
if (recv.Contains(">>READY<<"))
{
// Stop Polling loop
TimeoutQueue.ClearAllTimeouts();
DisconnectSerialPrintDriver();
// Raises event to Main to close ShowDialog
}
else
{
// Raises event to Main to open ShowDialog
}
}
private void DisconnectSerialPrintDriver()
{
// Closes/Disconnects serial port from printer
// unsubscribes event listener
// Sets Port to null and connects windows print driver back to printers COM port
// Ready to accept print jobs again
}
Believe it or not, this works very well. When the paper is reloaded in the printer, the dialog window showing the paper out message is dismissed.
However, I recently had to make a change to the print controller to make it a static class. Now, when I open the dialog window at Main, the Port_DataReceived event stops firing. The polling of the printer continues, and the responses just seem to back up until the dialog window is manually dismissed, and then I get one more dataReceived event with the results of all the polling that went on while showing the dialog window.
Can anyone tell me what could be happening here?

Related

What is wrong about this code that causes UI to hang even after using a new thread?

I'm trying to write a program that reads the serial port using c# windows form application .I've just started to learn about threads and tasks and etc. so I'm just a little bit confused about it.So even after reading some documents, I still don't know why does the following code cause the UI to hang?So,what is wrong about this code?Do you have any suggestion?Thanks.
There is a richtextbox named txtRead which is supposed to show the data that are taken from the serial port after a button named btnReadBegin get clicked.The richtextbox get updated frequently as the serial port sends the datas,but the rest of the application hangs.
SerialPort srlPort = new
SerialPort("COM6",9600,Parity.None,8,StopBits.One);
delegate void mydel();
bool checkWhile = true;
private void BtnReadBegin_Click(object sender, EventArgs e)
{
srlPort.Open();
new Thread(() =>
{
txtRead.BeginInvoke(new mydel(ReadingSerialPortMethod));
}).Start();
}
void ReadingSerialPortMethod()
{
while (checkWhile)
{
txtRead.Text +=
Convert.ToString(Convert.ToChar(srlPort.ReadChar()));
}
}

Serial monitor, DataReceived handler misunderstanding C#, WPF

I'm developing a serial monitor for my application in WPF, programming in C#.
I have trouble managing the DataReceived event, because I want a real time monitor like HyperTerminal or TeraTerm for example (I'm not using them because I want my terminal to be a part of an ethernet communication tool, which I already developed using winPcap).
I have to read some data from my microcontroller, display it on the textBox (It just prints a menu and the list of commands available) and when it finishes the loading sequence I would like to interact with it, nothing special, just send a "flash-" command to program the fpga of the board.
My application goes in exception when I try to update the textbox.text with the data received. I tried to search everywhere but despite a lot of examples, I didn't catch something which is explaining the code properly.
Here is the code, thanks in advance
namespace WpfApplication1 {
/// <summary>
/// Interaction logic for SerialMonitor.xaml
/// </summary>
public partial class SerialMonitor : Window {
//VARIABLES
public SerialPort comPort = new SerialPort();
public SerialMonitor() {
//initialization
InitializeComponent();
scanPorts();
}
private void scanPorts() {
textBoxIndata.Clear();
string[] ports = SerialPort.GetPortNames();
foreach (string port in ports) {
comboBoxPorts.Items.Add(port);
}
}
private void openComBtn_Click(object sender , RoutedEventArgs e) {
comPort.Parity = Parity.None;
comPort.DataBits = 8;
comPort.ReadTimeout = 500;
comPort.StopBits = StopBits.One;
if (comboBoxPorts.SelectedItem != null && comboBoxPorts.SelectedItem != null) {
comPort.PortName = comboBoxPorts.SelectedItem.ToString();
comPort.BaudRate = Convert.ToInt32(comboBoxBaud.Text);
try {
//Open port and add the event handler on datareceived
comPort.Open();
comPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
}
catch (Exception ex) {
MessageBox.Show(ex.ToString());
}
}
if (comPort.IsOpen) {
label1.Content = "COM PORT OPEN";
}
}
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) {
}
//function to update the textBox, didn't manage to get it working
private void updateUI (string s) {
}
//CLOSE AND EXIT BUTTONS
private void closeComBtn_Click(object sender , RoutedEventArgs e) {
if (comPort.IsOpen) {
comPort.Close();
label1.Content = "COM PORT CLOSED";
}
}
private void exitBtn_Click(object sender , RoutedEventArgs e) {
if (comPort.IsOpen) {
comPort.Close();
}
this.Close();
}
}
}
I got now the problem that when I send my command using SerialPort.Write(string cmd), I can't read back the answer...
EDIT: Fixed everything, I will post the code if anyone is interested in programming a tool like this one
DataReceived event returns on another/secondary thread, which means you will have to marshal back to the UI thread to update your TextBox
SerialPort.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 can use Dispatcher.BeginInvoke or Dispatcher.Invoke Method to marshal back to the main thread
Exmaple
Application.Current.Dispatcher.Invoke(new Action(() => { /* Your code here */ }));
or
someControl.Dispatcher.Invoke(new Action(() => { /* Your code here */ }));
I got now the problem that when i send my command using SerialPort.Write(string cmd), I can't read back the answer...

C# SerialPort Listening Port

I am trying to do a serial communication between my micro processor and c# form application.
The problem is, data which comes from micro processor can come in 2 seconds or 5 seconds or 10 seconds. I mean there is no specific time and i would like to listen port and get data if it is come in 2 seconds if it is not, wait for the data until it comes.
I tried to this with serialport.readline(); but form hangs on while readline blocking, so i tried to do with backgroundworkers when i do this, i cant close form while backgroundworker is busy because readline command blocks the whole program.
All i am saying is, please give me some clue about listening the port while coming data time is not specific.
Thank you for your time (sorry for english it is not well)
You can use the SerialPort.DataReceived Event to get the data async. After you created an instance of the SerialPort class, you are able to add event handlers to the SerialPort. Thes event handlers are called if data was received.
mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
Inside the handler you can read the data from the input buffer and do what ever you want with it.
private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
//Do what ever you want with the data
}
This is a very common solution to get data in unregular time steps which runs your application without blocking it.
You can use the DataReceived event. It will be fired everytime new data arrives at your port. You need to register to it like this:
SerialPort port = new SerialPort(/*your specification*/);
port.DataReceived += Port_DataReceived;
In the event handler you would then read out the incoming data
private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort port = sender as SerialPort;
if (port != null)
{
var incoming_message = port.ReadExisting();
}
}
Now you just need to open the port and it will listen automatically. NOTE! the incoming data will arrive on a different thread than the main thread. So if you want to use controls of your form for display you need to use BeginInvoke
If your data is marked at the end with \n you could try using the ReadLine method:
var incoming_message = port.ReadLine();
Or you could try ReadTo
var incoming_message = port.ReadTo("\n");
EDIT:
If it is such a long time, than you should read it in batches. You could also try to handle it in a while loop.
private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort port = sender as SerialPort;
string message = "";
if (port != null)
{
while(port.BytesToRead > 0)
{
message += port.ReadExisting();
System.Threading.Thread.Sleep(500); // give the device time to send data
}
}
}
EDIT 2:
If you want to store the data declare a List<string> outside of the event handler and add the string when it is entirely read.
List<string> dataStorage = new List<string>();
private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort port = sender as SerialPort;
string message = "";
if (port != null)
{
while(port.BytesToRead > 0)
{
message += port.ReadExisting();
System.Threading.Thread.Sleep(500); // give the device time to send data
}
// add now the entire read string to the list
dataStorage(message);
}
}
Since the event handler does not know whether you have send A or B just collect the entire received messages in one list. You know the order in which you have send your commands, so later you can take out the corresponding message and use Split to get the 400 entries in an array:
string [] A_array_data = dataStorage[0].Split(" ");

receiving serial port data in a while loop within startbutton_Click() function

I have created a wpf from and in it each time I get two byte from serial port and find the difference between them and then in a while loop I show the difference by a textbox:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
SerialPort port;
private void StartButton_Click(object sender, RoutedEventArgs e)
{
port = new SerialPort("COM3");
port.BaudRate = 9600;
port.DtrEnable = true;
port.RtsEnable = true;
port.Handshake = Handshake.None;
port.Open();
try
{
if (!port.IsOpen)
throw new Exception();
}
catch (Exception)
{
Console.Out.WriteLine("port is not open!!");
}
while (port.IsOpen)
{
var b1 = port.ReadByte();
var b2 = port.ReadByte();
double t1 = b1 * 1e-9;
double t2 = b2 * 1e-9; ;
var dift = t2 - t1;
if (dift == 0)
{
this.SpeedTextBox.Text = "0";
continue;
}
this.SpeedTextBox.Text = dift;
}
}
private void StopButton_Click(object sender, RoutedEventArgs e)
{
if (port != null)
{
if (port.IsOpen) port.Close();
port.Dispose();
}
}
}
but when I execute it and click on StartButton the form will be stoped working.I know that program receives data( I tested it with another simple program) . but I don't know what goes wrong here!!
can anyone help me?
thanks in advance.
ReadByte() is a blocking method, it won't return until a byte was received. This is why SerialPort has the DataReceived event.
First use another program like Hyperterminal or Putty to test the connection and eliminate simple mistakes like bad wiring, picking the wrong port number or baud rate and getting the Parity, DataBits and StopBits settings wrong. Which you don't set so there are non-zero odds that you'll get framing errors. You must implement the ErrorReceived event to ensure these kind of errors do not go unobserved and leave you mystified why it doesn't work.
If you don't use DataReceived then it is also important that you use the ReadTimeout property to ensure your program doesn't hang forever without any way to diagnose the cause if there's something wrong with the connection. Be prepared to catch the TimeoutException.
SysInternals' PortMon is a very useful utility to compare good vs bad, it shows you exactly what's going on inside the serial port driver. Beware however that it doesn't work on a 64-bit operating system.
Hans has covered the serial port cases, but another reason why your program will lock up is that your click handler uses an infinite wait loop. The way Windows applications work is that they have a main loop that gets messages (like click events) from a queue. For each message, your event handler is called, and it is only when your event handler returns control to the main loop that it can process the next message (e.g. to redraw your window and show the new text you have set on your control). So you can't use a long loop or blocking calls in your event handler if you want your program to remain responsive to user input.

Timer not stopping when form closed

I am having an extremely bizarre bug that I cant seem to figure out. I will lay this out as best as I can.
Context: I have an application that will continually ping about a group of IP's that live in a different location. (We need to see if each device is up at a given time)
Bug: When pinging a host, and their internet is down, the application shows all devices are down (as it should). But when closing the app, the process (main app exe) is still running and our router traffic shows that the continuous ping is still running.(Same interval). So its like the timer keeps going.
But when the application is closed, and the group is up, the process closes as it should and all pings stop.
I can provide any code that you guys want to see. Ill give the basics first:
I have a class called EquipmentObj. Inside the constructor, a contiguous ping gets started (async)
*Note this is all done on a seperate form, not the mainform.
Constructor and property:
System.Timers.Timer TimerObj { get; set; }
public EquipmentObj(string ipAddress)
{
this.IP_Address = ipAddress;
this.TimerObj = new System.Timers.Timer();
this.TimerObj.Interval = 2000;
this.TimerObj.Elapsed += TimerObj_Elapsed;
this.TimerObj.AutoReset = true;
this.start();
}
Start and Elapsed Method:
private void start()
{
this._requestStop = false;
this.TimerObj.Start();
}
private void TimerObj_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if(!_requestStop)
base.start_Ping(this.Ip_Address);
}
Base Class:
public void start_Ping(string ipAddress)
{
try
{
Ping pingSender = new Ping();
// Create the event Handler
pingSender.PingCompleted += pingSender_PingCompleted;
// Create the buffer
// Reference buff size: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
byte[] packetData = Encoding.ASCII.GetBytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
// Create the pingOptions object
PingOptions packetOptions = new PingOptions();
// Parse the passed ip address to a proper ipaddress object
IPAddress ip = System.Net.IPAddress.Parse(ipAddress);
// Send async
pingSender.SendAsync(ip, 1050, packetData, ip);
}
catch (Exception ex)
{
Error_Log.write_log(ex);
}
}
public virtual void pingSender_PingCompleted(object sender, PingCompletedEventArgs e)
{
throw new NotImplementedException();
}
Implemented Ping Complete:
public override void pingSender_PingCompleted(object sender, PingCompletedEventArgs e)
{
if (e.Reply.Status == IPStatus.Success)
this.status = "On Line";
else
{
this.status = "Off Line";
this.setBounced = ++this.setBounced ?? 1;
}
this.setResponse = e.Reply.RoundtripTime.ToString();
}
Finally my cleanup which is called on the form closing:
foreach (EquipmentObj obj in this.objectListView1.Objects)
{
obj.Stop();
}
Here is the stop:
public void Stop()
{
this._requestStop = true;
this.TimerObj.Stop();
}
What I've tried:
Adding the cleanup (Above) into the closing event.
Moved my timer from a system.threading timer, to system.timer timer.
I've tried implementing idisposable.
Setting Timers to null on form closing event.
Calling dispose on the timer it self.
None of these have worked.
I hope I didn't over whelm you all with too much code and writing. Any help would be appreciated.
My best bet is you have an exception being thrown prior to the Stop being called. I will put some more try { } catch statements in there so you can handle errors.
You may have some threading issues where you are trying to access objects not created on the same thread.
I would check if you need to Invoke anytime you mess with the timer object.
void Stop()
{
if (objectListView1.InvokeRequired)
{
objectListView1.BeginInvoke(new Action(Stop));
return;
}
foreach (EquipmentObj obj in this.objectListView1.Objects)
{
obj.Stop();
}
}

Categories

Resources