Serial Communcation DataRecievedHandler and WPF UI Not In Sync - c#

I am writing a WPF/C# program that reads data from a weather station and will eventually put it in a MySQL database. The weather station's input to the computer is through a serial port so I am using System.IO.Ports to facilitate the communication.
This is set up so far where I click a button on the UI and it sends the commands to the serial port to have the weather station send data. The issue I am running into involves using the DataRecievedHandler method to handle when data is sent through the serial port to my program. When I get a response back, I get it in the form of a data packet containing 99 bytes, which I read into an array. The issue I am having is that when I try to send that array somewhere else to parse it out, it seems like the array doesn't get completely filled until I press another button on the UI... almost like it waits before finishing filling up the array.
This is the code from the button press in the UI: It sends a wakeup signal "\n"
waits for a bit then sends the signal to get data "LPS 2 1\n"
private void button_testSerial_Click(object sender, RoutedEventArgs e)
{
try
{
wxSerialPort.WriteLine("\n");
//printArrayList();
Thread.Sleep(3000);
wxSerialPort.Write("LPS 2 1\n");
}
catch (Exception C)
{
MessageBox.Show("Please ensure both DB connection and Serial Connections are set up and connected \n \n" + "Error text: \n" + C.ToString(), "Connection Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
When I send the wakeup I get first a response of [10], [13] which is correct, this signals the station is awake and returns a buffer with those values.
When I send the next one (LPS 2 1\n)
I can see data come in and if I run a for loop on the array it looks like it has data while the method is running from the button click. The issue is, if I try to send the array from the DataRecievedHandler method to another method or class or such it seems like it doesn't get filled or sent properly. So I end up running into null values in my Byte[] array.
This is my DataRecievedHandler method
public void DataRecievedHandler(object sender, SerialDataReceivedEventArgs e)
{
byte[] buffer = new byte[wxSerialPort.BytesToRead];
wxSerialPort.Read(buffer, 0, buffer.Length);
Console.WriteLine("Buffer length: " + buffer.Length); //test to see if data comes in
if (buffer.Length == 2) //wake up response is 2 values [10][13]
{
//Console.WriteLine("Wakeup Response");
return;
}
if (buffer.Length == 0)
{
//do nothing and skip
return;
}
else
{
string debugtext = "";
int counter = 0;
for (int i = 0; i < buffer.Length; i++) //loop prints out contents for testing
{
counter++;
Console.WriteLine("Buffer Contents " + buffer[i]);
// Console.WriteLine("ADDING To ArrayList Data: " + buffer[i] + " at position: " + i);
wxPacketData = buffer; //this is where I assign the Byte[] buffer to an array that can be sent off and parsed
}
counter = 0;
return;
}
}
Finally I send the wxPacketData array (byte array created when the DataReceivedHandler method runs) to another class to be parsed, but this only works if it is clicked from another button, this doesn't work when I have it under the same button as shown above:
private void button_parsePacket_Click(object sender, RoutedEventArgs e)
{
wxPacketParser wxPacket = new wxPacketParser(wxPacketData);
wxPacket.parseOutsideHumidity();
wxPacket.parseOutsideTemperature();
wxPacket.parseBarometricPressure();
wxPacket.parseDewPoint();
}
The question is, what am I doing wrong so that I have to run the parsing code under a different button click for this to work properly?
I am relatively new to this so if more information is needed please let me know. All suggestions or guidance are very welcome and I appreciate everyone's help in advance. I feel like I'm missing something very simple or basic, but probably need a push to see what that issue is.

Related

TCP GetStreatm().BeginRead() on Timer not updating C# Windows Form

I have a timer event that executes the following code. I'm trying to read from a TCP connection for a specific string, but it doesn't seem like the buffer gets updated on each passing timer tick event. The source that I'm getting this data from will send 4 different types of strings in a byte array depending on the current state of the system. They are sent to me on a continuous basis. What appears to be happening is that I read just once and then not again for some reason. I've verified that the source I'm receiving data from is indeed sending me different messages, but I don't seem to be able to "read" them. Just the first time only apparently. I tried using the Array.Clear() method, but I still only seem to get one buffer of data and not something that is continuously updating itself. The point of this timer event is to continuously update a C# Windows Form app to alert someone of a fault. I created the "PartnerClient TCPClient at the top of my program.
I'm hopeful that someone has some advice. Perhaps I need an EndRead, but I have tried this approach. Any advice would help
public void FaultDataTimer_Tick(object sender, EventArgs e)
{
byte[] mRx = new byte[9];
byte[] statusBytes = new byte[9];
string strRecv;
string[] values = { "ULI_Fault", "DynoFault", "ULI_AOkay", "DynoAOkay" };
if (PartnerClient.Connected == true)
{
try
{
PartnerClient.GetStream().BeginRead(mRx, 0, mRx.Length, null, PartnerClient);
}
catch (Exception exc)
{
MessageBox.Show(exc.Message);
}
for (int i = 0; i < 9; i++)
{
statusBytes[i] = mRx[i];
}
strRecv = Encoding.ASCII.GetString(statusBytes);
if (values.Any(strRecv.Contains) || values.Any(strRecv.Contains))
{
if (strRecv == "ULI_Fault")
{
uliPanel.BackColor = Color.Red;
}
else if (strRecv == "DynoFault")
{
dynoPanel.BackColor = Color.Red;
}
else if (strRecv == "ULI_AOkay")
{
uliPanel.BackColor = greenColor;
}
else if (strRecv == "DynoAOkay")
{
dynoPanel.BackColor = greenColor;
}
}
}
Array.Clear(mRx, 0, mRx.Length);
}

Read and write data to/from Serial Port

I am building a console application which will be scheduled in Task Scheduler of Windows to run my code every day at a set hour. Summing up, this application will read and write through SerialPort. When I send something to the Arduino, I need to receive something from it to finish up what I've sent and execute the command.
In other words, I will send something so see if the door is opened, if it is the application will run a code to close it. If the door is already closed I will send a bunch of characters to be displayed into the Arduino Led Display.
So I've developed a code but I am not sure if it's totally correct, if possible help me improve it. There is any changes I could make?
static void Main(string[] args)
{
SerialPort comport = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
comport.Open();
string start = "?";
string carriageReturn = "\r";
string text = string.Empty;
string mensage = "#" + "r" + "\r";
string mensage2 = "#" + "{" + texto + "\r";
try
{
while (true)
{
//Send to the Arduino
comport.Write(start+ "*" + carriageReturn);
//If the serial port have bytes to read
if (comport.BytesToRead > 0)
{
//Buffer with data
byte[] dados = HexStringToByteArray(mensage);
//Handle data
comport.Read(dados, 0, dados.Length);
//Send again to execute the the command
comport.Write(start + "*" + carriageReturn);
}
if (comport.BytesToRead > 0)
{
comport.Write(start + "*" + carriageReturn);
byte[] dados2 = HexStringToByteArray(mensage2);
comport.Read(dados2, 0, dados2.Length);
comport.Write(text);
}
comport.Close();
}
}
catch(Exception ex)
{
}
}
private static byte[] HexStringToByteArray(string s)
{
s = s.Replace(" ", "");
byte[] buffer = new byte[s.Length / 2];
for (int i = 0; i < s.Length; i += 2)
buffer[i / 2] = (byte)Convert.ToByte(s.Substring(i, 2), 16);
return buffer;
}
EDIT:
My basic input output mapping/relationship is:
I will send ?*\r to the Arduino, then I will wait for a answer.
If the answer from Arduino is #r\r I will send the ?*\r again.
If the answer from the Arduino is #{/r I will send a string to him.
Here is a first attempt of my revision.
You should consider to use the DataReceived event and have an Exit-Strategy. Up to now your program never exits.
static void Main(string[] args)
{
SerialPort port = new SerialPort("COM1", 9600, Parity.Odd, 7, StopBits.One);
// register the event
port.DataReceived += Port_DataReceived;
//open the port
port.Open();
try
{
// start the communication
port.Write("?*\r");
Console.WriteLine("Waiting for response");
// Your manual exit strategy. Hit a kKeyboard-key to end this game
while (!Console.KeyAvailable)
{
}
}
catch (Exception ex)
{
Console.WriteLine("Writing failed! \nError: " + ex.Message);
}
}
The DataReceived event will be fired as soon as the Arduino sends you something. If it doesn't you can just quit your program manually.
Inside the event you can do your entire logic. As you already posted the input-output mapping you can just hack it into solid code as it is written in your post. If it is only 2 cases you could also use a switch/case construct:
private static void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort port = sender as SerialPort;
// read input
string incoming = port.ReadExisting();
switch (incoming)
{
case "#r\r":
// send the message back
port.Write("?*\r");
break;
case #"#{/r":
port.Write("Display this!");
break;
default:
Console.WriteLine("Unknown command from Arduino!\n Command: " + incoming);
break;
}
}
I hope it helps
If I understand your description properly (closing the door will be done by arduino, right?), this is the task:
A task scheduled console app triggers an arduino via Serial to check a status and execute something (if status tells thats necessary)
Arduino reports back status (and eventually that task was performed)
You could do it very simple:
"?" --> PC sends a trigger: Check door status and behave accordingly
Then PC waits for a response:
"1" <-- Arduino response ( door was already closed )
"2" <-- or alternate response ( I closed the door )
Arduino could send anything else to indicate an error. (feel free to invent as much as adequate)
Additionally, it's possible that arduino does not respond at all. So what would your scheduled console app do then?
You do not need to send/receive more than a byte, to have it simpler for Arduino and your little Serial PC code.
The PC code does not have a user interface, so there's no need for Arduino to respond immediately, if closing the door should take a while.

Why server app receives 50% of client messages?

Pre:
Client open socket to send data to the server:
private void Form1_Load(object sender, EventArgs e)
{
client = new TcpClient();
client.BeginConnect("127.0.0.1", 995, new AsyncCallback(ConnectCallback), client);
}
private void ConnectCallback(IAsyncResult _result) // it will send hello message from client
{
string data;
byte[] remdata = { };
IAsyncResult inResult = _result;
currentProcess = Process.GetCurrentProcess();
string currentProcessAsText = currentProcess.Id.ToString();
SetControlPropertyThreadSafe(proccessIdLabel, "Text", currentProcessAsText);
try {
sock = client.Client;
// send hello message
data = "Client with proccess id " + currentProcessAsText + " is connecting";
sock.Send(Encoding.ASCII.GetBytes(data));
SetControlPropertyThreadSafe(answersTextBox, "Text", answersTextBox.Text + "\n"+ GetCurrentTime() + " Connection established");
}
catch
{
SetControlPropertyThreadSafe(answersTextBox, "Text", answersTextBox.Text + "\n" + GetCurrentTime() + " Can't connect");
}
}
After that I have handler for click on some button (to send messages):
private void SendButton_Click(object sender, EventArgs e)
{
try
{
string data;
sock = client.Client;
data = "Some text";
sock.Send(Encoding.ASCII.GetBytes(data));
}
catch
{
SetControlPropertyThreadSafe(answersTextBox, "Text", "Can't connect");
}
}
Also handler for form close to send server exit command, so he will stop thread for this client:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
try
{
sock.Send(Encoding.ASCII.GetBytes("exit"));
sock.Close();
}
catch
{
}
}
Server listening port and handles messages:
private void Listeners()
{
Socket socketForClient = Listener.AcceptSocket();
string data;
int i = 0;
if (socketForClient.Connected)
{
string remoteHost = socketForClient.RemoteEndPoint.ToString();
Console.WriteLine(Message("Client:" + remoteHost + " now connected to server."));
while (true)
{
// буфер данных
byte[] buf = new byte[1024];
try
{
int messageLength = socketForClient.Receive(buf);
if (messageLength > 0)
{
byte[] cldata = new byte[messageLength];
socketForClient.Receive(cldata);
data = "";
data = Encoding.ASCII.GetString(cldata).Trim();
if (data.Contains("exit"))
{
socketForClient.Close();
string message = Message("Client:" + remoteHost + " is disconnected from the server (client wish).");
Console.WriteLine(message);
return;
}
else
{
Console.WriteLine(Message("Recevied message from client " + remoteHost + ":\n"));
Console.WriteLine(data);
Console.WriteLine("\nEOF\n");
}
}
}
catch
{
string message = Message("Client:" + remoteHost + " is disconnected from the server (forced close).");
Console.WriteLine(message);
socketForClient.Close();
return;
}
}
}
}
private void ServStart()
{
Listener = new TcpListener(LocalPort);
Listener.Start(); // начали слушать
Console.WriteLine("Waiting connections [" + Convert.ToString(LocalPort) + "]...");
for (int i = 0; i < 1000; i++)
{
Thread newThread = new Thread(new ThreadStart(Listeners));
newThread.Start();
}
}
So on server start it creates 1000 threads, which are listens clients messages.
Problems:
I will describe some situation:
Start server
Server starts threads and ready to accept clients connections
Start client
Connection is establishing. Server says that client connected on some port. Client send "hello" message. Server doesn't handle this hello message.
Push the button, so client will send Some text to the server. Server handles this message.
Push the button. Client sends "some text" again. Server doesn't handles that message.
Push the button. Client sends "some text" again. Server handles that message.
If I will push again, it will not handle it obviously....
Server logs:
Why server receives/client sends only 1 of 2 messages? What can cause it?
Also I have problems with sending exit message to the server, when client form is closing. I send exit message on this action.
So situation:
I just pushed the button and server handled it (so server will not handle next message).
I close the form, message is sended, but either client sends wrong message or server receives wrong message.
Situation in console:
You can see, that when form was closed and client sended exit, server handled empty message. Why?
Situation when client exit command passed by server normally:
.....
Client sends data, server doesn't handled it
Now, server will handle client, so we try to close form:
Console:
So in 2nd item client had sended hello message and server failed to handle it. In 3rd item client sends exit command and server passed it correctly.
Main question: why server handles only 1 of 2 messages from client?
Another point: also I found, that when client send exit data, server receives exit\0\0\0\0\0\0\0\0\ (or more, or less \0 symbols). Why?
Good news I think, that server receives or not receives messages constantly. 1 message is received, 1 message is not. That says about my lack of knowledges, but not random error.
So many bugs. :(
That said, the biggest one I noticed was this one:
int messageLength = socketForClient.Receive(buf);
if (messageLength > 0)
{
byte[] cldata = new byte[messageLength];
socketForClient.Receive(cldata);
data = "";
data = Encoding.ASCII.GetString(cldata).Trim();
First, understand that in TCP, you have no guarantees about the number of bytes any given receive operation will receive. No matter how the remote endpoint sends the data, you could receive all of the data at once, or only parts of it, in separate receive operations. TCP guarantees the bytes will be received in the same order in which they were sent, and nothing more.
But the above code not only fails to take that into account, it's just completely wrong. The number of bytes received in the first operation is how many bytes were received in that operation. But you are using that number as if it would tell you something about the number of bytes received in the next call to Receive(). It does nothing of the sort. At the same time, you ignore the data you received in the first operation.
Instead, your code should look more like this:
int messageLength = socketForClient.Receive(buf);
if (messageLength > 0)
{
data = Encoding.ASCII.GetString(buf, 0, messageLength).Trim();
That's still not quite right, because of course you could receive just a partial message in the call to Receive(), or even more than one message concatenated together. But at least you're likely to see all of the text.
That change will address the specific question you've asked about. If you have trouble figuring out how to address the other bugs, please feel free to post concise, specific questions and code examples to ask for help on those. See https://stackoverflow.com/help/mcve and https://stackoverflow.com/help/how-to-ask for advice on better ways to present your question.

Serial Port Polling and Data handling

I am trying to read from several serial ports from sensors through microcontrollers. Each serial port will receive more than 2000 measurements (each measurement is 7 bytes, all in hex). And they are firing at the same time. Right now I am polling from 4 serial ports. Also, I translate each measurement into String and append it to a Stringbuilder. When I finish receiving data, they will be ouput in to a file. The problem is the CPU consumption is very high, ranging from 80% to 100%.
I went though some articles and put Thread.Sleep(100) at the end. It reduces CPU time when there is no data coming. I also put Thread.Sleep at the end of each polling when the BytesToRead is smaller than 100. It only helps to a certain extent.
Can someone suggest a solution to poll from serial port and handle data that I get? Maybe appending every time I get something causes the problem?
//I use separate threads for all sensors
private void SensorThread(SerialPort mySerialPort, int bytesPerMeasurement, TextBox textBox, StringBuilder data)
{
textBox.BeginInvoke(new MethodInvoker(delegate() { textBox.Text = ""; }));
int bytesRead;
int t;
Byte[] dataIn;
while (mySerialPort.IsOpen)
{
try
{
if (mySerialPort.BytesToRead != 0)
{
//trying to read a fix number of bytes
bytesRead = 0;
t = 0;
dataIn = new Byte[bytesPerMeasurement];
t = mySerialPort.Read(dataIn, 0, bytesPerMeasurement);
bytesRead += t;
while (bytesRead != bytesPerMeasurement)
{
t = mySerialPort.Read(dataIn, bytesRead, bytesPerMeasurement - bytesRead);
bytesRead += t;
}
//convert them into hex string
StringBuilder s = new StringBuilder();
foreach (Byte b in dataIn) { s.Append(b.ToString("X") + ","); }
var line = s.ToString();
var lineString = string.Format("{0} ---- {2}",
line,
mySerialPort.BytesToRead);
data.Append(lineString + "\r\n");//append a measurement to a huge Stringbuilder...Need a solution for this.
////use delegate to change UI thread...
textBox.BeginInvoke(new MethodInvoker(delegate() { textBox.Text = line; }));
if (mySerialPort.BytesToRead <= 100) { Thread.Sleep(100); }
}
else{Thread.Sleep(100);}
}
catch (Exception ex)
{
//MessageBox.Show(ex.ToString());
}
}
}
this is not a good way to do it, it far better to work on the DataReceived event.
basically with serial ports there's a 3 stage process that works well.
Receiving the Data from the serial port
Waiting till you have a relevant chunk of data
Interpreting the data
so something like
class DataCollector
{
private readonly Action<List<byte>> _processMeasurement;
private readonly string _port;
private SerialPort _serialPort;
private const int SizeOfMeasurement = 4;
List<byte> Data = new List<byte>();
public DataCollector(string port, Action<List<byte>> processMeasurement)
{
_processMeasurement = processMeasurement;
_serialPort = new SerialPort(port);
_serialPort.DataReceived +=SerialPortDataReceived;
}
private void SerialPortDataReceived(object sender, SerialDataReceivedEventArgs e)
{
while(_serialPort.BytesToRead > 0)
{
var count = _serialPort.BytesToRead;
var bytes = new byte[count];
_serialPort.Read(bytes, 0, count);
AddBytes(bytes);
}
}
private void AddBytes(byte[] bytes)
{
Data.AddRange(bytes);
while(Data.Count > SizeOfMeasurement)
{
var measurementData = Data.GetRange(0, SizeOfMeasurement);
Data.RemoveRange(0, SizeOfMeasurement);
if (_processMeasurement != null) _processMeasurement(measurementData);
}
}
}
Note: Add Bytes keeps collecting data till you have enough to count as a measurement, or if you get a burst of data, splits it up into seperate measurements.... so you can get 1 byte one time, 2 the next, and 1 more the next, and it will then take that an turn it into a measurement. Most of the time if your micro sends it in a burst, it will come in as one, but sometimes it will get split into 2.
then somewhere you can do
var collector = new DataCollector("COM1", ProcessMeasurement);
and
private void ProcessMeasurement(List<byte> bytes)
{
// this will get called for every measurement, so then
// put stuff into a text box.... or do whatever
}
First of all consider reading Using Stopwatches and Timers in .NET. You can break down any performance issue with this and tell exactly which part of Your code is causing the problem.
Use SerialPort.DataReceived Event to trigger data receiving process.
Separate receiving process and data manipulation process. Store Your data first then process.
Do not edit UI from reading loop.
I guess what you should be doing is adding an event handler to process incoming data:
mySerialPort.DataReceived += new SerialDataReceivedEventHandler(mySerialPort_DataReceived);
This eliminates the need to run a separate thread for each serial port you listen to. Also, each DataReceived handler will be called precisely when there is data available and will consume only as much CPU time as is necessary to process the data, then yield to the application/OS.
If that doesn't solve the CPU usage problem, it means you're doing too much processing. But unless you've got some very fast serial ports I can't imagine the code you've got there will pose a problem.

c# NetworkStream BeginRead seemingly overwriting byte

For some program, I want to send data over from a python program to a c# program. I put all of my data into a list in python and send it over after converting to bytes (packing them as doubles, so I have 8 bytes per number I am sending over). Having some understanding of how sockets and TCP streams work, the very first number in the list is the amount of bytes that the rest of the list takes up. Hence, the first 8 bytes of my stream tell me how many bytes I need to read to get all other data.
However, when I call BeginRead and it calls the callback, it has read 8 bytes less than I asked it to read. For instance, those first 8 bytes tell me there is 116432 bytes to read, but when I call EndRead it returns 116424.
Okay, so what? There's eight bytes missing, this amounts to one double being lost. This is a problem in and of itself, but I even figured out where this double has gone.
In python, at a specific point (while it is still doubles) in my data, I see I am sending this: "...,1961.0, 0.0128, 2033.0, 0.0442, 2034.0,..." when I inspect that same point in c# (after converting my bytes back to doubles), I see this: "..,1961.0, 2033.0002, 0.0442,2034.0,...".
To me, it seems clear that somehow these 8 bytes got mashed together into one, fusing the two number (bit-wise maybe?) together somehow. I have also noticed that the index of where this occurs in the byte data is roughly at the 64k-65k mark. So I'm suspecting that with 64kbytes being the max packet size of TCP packets, the stream has some kind of hiccup there and overwrites one part of my buffer without clearing it, leading to some literal mix up? Does anybody have any idea how I could fix this problem or what mistake I made that is causing this to happen?
I will paste the two relevant functions here.
private void Listen(int port)
{
try
{
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
var client = _server.AcceptTcpClient();
// Get a stream object for reading and writing
var stream = client.GetStream();
var pLength = new byte[8];
// Loop to receive all the data sent by the client.
while(_running)
{
if(!stream.DataAvailable && stream.Read(pLength, 0, 8) <= 0)
continue;
var nOfBytes = (int) BitConverter.ToDouble(pLength, 0);
pLength = new byte[8];
if (nOfBytes <= 0)
{
continue;
}
var localBytes = new byte[nOfBytes];
var scriptData = new ScriptData(stream, localBytes);
stream.BeginRead(localBytes, 0, nOfBytes, new AsyncCallback(GotAllBytes), scriptData);
}
// Shutdown and end connection
client.Close();
}
catch (SocketException e)
{
_errorBool = true;
_errorString = "Port: " + port + "\n" + e.Message + e.StackTrace;
}
finally
{
// Stop listening for new clients.
_server.Stop();
}
}
private void GotAllBytes(IAsyncResult result)
{
var scriptData = (ScriptData)result.AsyncState;
if (OnlinePaused)
{
scriptData.Stream.EndRead(result);
return;
}
var bytesRead = scriptData.Stream.EndRead(result);
_rawDataQueue.Enqueue(scriptData.Buffer.ToList());
}
Thanks for reading, I hope you can help out.
Thanks to Jeroen Mostert in the comments of the original question, this problem has been resolved by not working async but instead implementing a BinaryReader.
private void Listen(int port)
{
try
{
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
var client = _server.AcceptTcpClient();
// Get a stream object for reading and writing
var stream = client.GetStream();
var pLength = new byte[8];
// Loop to receive all the data sent by the client.
while(_running)
{
if(!stream.DataAvailable && stream.Read(pLength, 0, 8) <= 0)
continue;
var nOfBytes = (int) BitConverter.ToDouble(pLength, 0);
pLength = new byte[8];
if (nOfBytes <= 0)
{
continue;
}
var localBytes = new byte[nOfBytes];
var reader = new BinaryReader(stream);
ProcessData(reader);
}
// Shutdown and end connection
client.Close();
}
catch (SocketException e)
{
_errorBool = true;
_errorString = "Port: " + port + "\n" + e.Message + e.StackTrace;
}
finally
{
// Stop listening for new clients.
_server.Stop();
}
}
Thank you for everybody who was trying to help.

Categories

Resources