Netduino home hardware projects downloads community

Jump to content


Photo

EventHandler vs specific thread

EventHandler thread serial loop performance

  • Please log in to reply
2 replies to this topic

#1 skyjam

skyjam

    New Member

  • Members
  • Pip
  • 8 posts

Posted 11 November 2014 - 01:59 PM

Hi forum members

 

Does anybody have some specific information or experience regarding eventhandler vs. thread?

 

Assuming I want to use multiple serial ports at the same time in my project with explicit data handling, processing and further network integration, does it make sense to use an eventhandler like this:

public static void Main()
{
	//[some instance code here]
	xSerialPort.DataReceived += xSerialPort_DataReceived;
}

private static void xSerialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
	while([whatever...])
	{
		//[some code here]
	}
}

Some of my own experiences show, that this will just create a separate thread without the need of ThreadStart().

I further guess that using an eventhadler will not fire anything, but loop as long as the while() clause will be true since there is no real check of data arriving at hardware level (in my serial case).

 

Therefore this indicates to me, that is has the same effect to use ThreadStart() and thus have full control on what my code is doing.

 

But, what about performance? Will an eventhandler be somehow faster? Will it have a higher process priority?

 

Also, does anybody know about necessary performance in a while(serial.BytesToRead > 0) loop if BytesToRead is zero? I guess it will loop at processor speed level . Maybe if the Netduino is idle, hundred times a second. Will this cause other possible processes to run slow?

 

Any thoughts?

 

Thanks for sharing!



#2 perpetualKid

perpetualKid

    Member

  • Members
  • PipPip
  • 20 posts

Posted 12 November 2014 - 05:15 AM

I think the while loop is not necessary. You will read as many bytes as available to read, and exit the eventhandler. Once there are more bytes available, the event will be raised again and you continue reading. You just need some marker in your data which marks the end of a message, typically crlf or | or so 0x00 etc.
There is no guarantee that all data arrives in a single batch in the eventhandler, at max it may come byte by byte (at least the order is kept). So you need some buffer or stream to hold already received data until the remainder is coming as well.

#3 jinzai

jinzai

    Member

  • Members
  • PipPip
  • 11 posts
  • LocationDavenport, IA

Posted 25 February 2016 - 02:18 AM

        private static void DataReceviedHandler(object sender,
                                                SerialDataReceivedEventArgs e)
        {
            try
            {
                SerialPort sp = (SerialPort)sender;
                int bytesToRead = sp.BytesToRead;

                for (int i = 0; i < bytesToRead; i++)
                {
                    byte[] theByte = new byte[1];

                    theByte[0] = (byte)sp.ReadByte();
                    if (theByte[0] == 0x0a)
                    {
                        String tempStr = BufferedString.ToString();
                        String[] NMEASentence = tempStr.Split(new char[]{','});

                        if (NMEASentence == null) { BufferedString = new StringBuilder(); break; }
                        switch (NMEASentence[0])
                        {
                            case "$GPVTG" :
                                GPVTG = tempStr.TrimEnd();
                                if (NMEASentence.Length > 8)
                                {
                                    GPS_NMEA_GPVTG.CourseTrue = Convert.ToDouble(NMEASentence[1]);
                                    GPS_NMEA_GPVTG.ReferenceTrue = Encoding.UTF8.GetBytes(NMEASentence[2])[0];
                                    if (NMEASentence[3] == "")
                                    {
                                        GPS_NMEA_GPVTG.CourseMagnetic = -0.0D;
                                    }
                                    else
                                    {
                                        GPS_NMEA_GPVTG.CourseMagnetic = Convert.ToDouble(NMEASentence[3]);
                                    }
                                    GPS_NMEA_GPVTG.ReferenceMagnetic = Encoding.UTF8.GetBytes(NMEASentence[4])[0];
                                    GPS_NMEA_GPVTG.Speed = Convert.ToDouble(NMEASentence[5]);
                                    GPS_NMEA_GPVTG.SpeedUnits = Encoding.UTF8.GetBytes(NMEASentence[6])[0];
                                    GPS_NMEA_GPVTG.Mode = (VTGMode)Convert.ToInt32(Encoding.UTF8.GetBytes(NMEASentence[7])[0].ToString());
                                    Debug.Print("Course (True) : " + GPS_NMEA_GPVTG.CourseTrue.ToString("N2") +
                                                " " + new String(Convert.ToChar(GPS_NMEA_GPVTG.ReferenceTrue), 1));
                                    Debug.Print("Course (Mag)  : " + GPS_NMEA_GPVTG.CourseMagnetic.ToString("N2") +
                                                " " + new String(Convert.ToChar(GPS_NMEA_GPVTG.ReferenceMagnetic), 1));
                                    Debug.Print("Speed : " + GPS_NMEA_GPVTG.Speed.ToString("N2") +
                                                " " + new String(Convert.ToChar(GPS_NMEA_GPVTG.SpeedUnits), 1));
                                    Debug.Print("Mode : " + GetVTGModeText(GPS_NMEA_GPVTG.Mode));
                                }
                                break;
                            case "$GPGGA":
                                GPGGA = tempStr.TrimEnd();
                                if (NMEASentence.Length > 12)
                                {
                                    GPS_NMEA_GPGGA.GPS_Time.hour = Convert.ToByte(NMEASentence[1].Substring(0, 2));
                                    GPS_NMEA_GPGGA.GPS_Time.minute = Convert.ToByte(NMEASentence[1].Substring(2, 2));
                                    GPS_NMEA_GPGGA.GPS_Time.second = Convert.ToByte(NMEASentence[1].Substring(4, 2));
                                    GPS_NMEA_GPGGA.LatitudeStr = NMEASentence[2];
                                    GPS_NMEA_GPGGA.IsLatitudeNorth = NMEASentence[3] == "N" ? true : false;
                                    GPS_NMEA_GPGGA.LongitudeStr = NMEASentence[4];
                                    GPS_NMEA_GPGGA.IsLongitudeWest = NMEASentence[5] == "W" ? true : false;
                                    GPS_NMEA_GPGGA.PositionFixIndicator = (PositionFix)Convert.ToInt32(NMEASentence[6]);
                                    GPS_NMEA_GPGGA.SatellitesUsed = Convert.ToByte(NMEASentence[7]);
                                    GPS_NMEA_GPGGA.HorizontalDilutionOfPrecision = Convert.ToDouble(NMEASentence[8]);
                                    GPS_NMEA_GPGGA.MSLAltitude = Convert.ToDouble(NMEASentence[9]);
                                    GPS_NMEA_GPGGA.MSLUnits = Encoding.UTF8.GetBytes(NMEASentence[10])[0];
                                    GPS_NMEA_GPGGA.GeoidSeparation = Convert.ToDouble(NMEASentence[11]);
                                    GPS_NMEA_GPGGA.GeoidSeparationUnits = Encoding.UTF8.GetBytes(NMEASentence[12])[0];
                                }
                                if (NMEASentence.Length > 13)
                                {
                                    //Debug.Print(NMEASentence[13]);
                                    //GPS_NMEA_GPGGA.AgeOfDifferentialCorrection = Convert.ToInt32(NMEASentence[13]);
                                    if (NMEASentence.Length >= 14)
                                    {
                                        //Debug.Print(NMEASentence[14]);
                                        //GPS_NMEA_GPGGA.DifferentialReferenceStationID = Convert.ToInt32(NMEASentence[14]);
                                    }
                                }
                                Debug.Print("GPS Time => " + GPS_NMEA_GPGGA.GPS_Time.MakeString());
                                //Debug.Print("Latitude : " + GPS_NMEA_GPGGA.Latitude().MakeString() +
                                //            (GPS_NMEA_GPGGA.IsLatitudeNorth ? "N" : "S"));
                                //Debug.Print("Longitude : " + GPS_NMEA_GPGGA.Longitude().MakeString() +
                                //            (GPS_NMEA_GPGGA.IsLongitudeWest ? "W" : "E"));
                                Debug.Print("Latitude : " + GPS_NMEA_GPGGA.LatitudeAsDecimal("N6") +
                                            (GPS_NMEA_GPGGA.IsLatitudeNorth ? "N" : "S"));
                                Debug.Print("Longitude : " + GPS_NMEA_GPGGA.LongitudeAsDecimal("N6") +
                                            (GPS_NMEA_GPGGA.IsLongitudeWest ? "W" : "E"));
                                Debug.Print("Position Fix Indicator : " + GetFixIndicatorText(GPS_NMEA_GPGGA.PositionFixIndicator));
                                Debug.Print("Satellites Used : " + GPS_NMEA_GPGGA.SatellitesUsed.ToString());
                                Debug.Print("Horizontal Dilution of Precision : " + GPS_NMEA_GPGGA.HorizontalDilutionOfPrecision.ToString("N1"));
                                Debug.Print("MSL Altitude : " + GPS_NMEA_GPGGA.MSLAltitude.ToString("N1") + " " +
                                            new String(Convert.ToChar(GPS_NMEA_GPGGA.MSLUnits), 1));
                                break;
                                //
                            case "$GPGSA":
                                GPGSA = tempStr.TrimEnd();
                                //Debug.Print(GPGSA);
                                break;
                            case "$GPRMC":
                                GPRMC = tempStr.TrimEnd();
                                if (NMEASentence.Length > 13)
                                {
                                    GPS_NMEA_GPRMC.GPSTime.hour = Convert.ToByte(NMEASentence[1].Substring(0, 2));
                                    GPS_NMEA_GPRMC.GPSTime.minute = Convert.ToByte(NMEASentence[1].Substring(2, 2));
                                    GPS_NMEA_GPRMC.GPSTime.second = Convert.ToByte(NMEASentence[1].Substring(4, 2));
                                    GPS_NMEA_GPRMC.Status = Encoding.UTF8.GetBytes(NMEASentence[2])[0];
                                    GPS_NMEA_GPRMC.LatitudeStr = NMEASentence[3];
                                    GPS_NMEA_GPRMC.NorS = Encoding.UTF8.GetBytes(NMEASentence[4])[0];
                                    GPS_NMEA_GPRMC.LatitudeStr = NMEASentence[5];
                                    GPS_NMEA_GPRMC.EorW = Encoding.UTF8.GetBytes(NMEASentence[6])[0];
                                    GPS_NMEA_GPRMC.SpeedOverGround = Convert.ToDouble(NMEASentence[7] != "" ? NMEASentence[7] : "0.0");
                                    GPS_NMEA_GPRMC.CourseOverGround = Convert.ToDouble(NMEASentence[8] != "" ? NMEASentence[8] : "0.0");
                                    GPS_NMEA_GPRMC.GPSDateStr = NMEASentence[9];
                                    Debug.Print("Speed Over Ground : " + GPS_NMEA_GPRMC.SpeedOverGround.ToString("N2") +
                                                " knots");
                                    Debug.Print("Course over ground : " + GPS_NMEA_GPRMC.CourseOverGround.ToString("N2"));
                                }
                                break;
                            default:
                                break;
                        }
                        BufferedString = new StringBuilder();
                    }
                    else
                    {
                        if (theByte[0] > 127)
                        {
                            Debug.Print("Value out of ASCII range. MSB set.");
                        }
                        else
                        {
                            try
                            {
                                BufferedString.Append(Encoding.UTF8.GetChars(theByte, 0, 1));
                            }
                            catch (Exception ex)
                            {
                                Debug.Print("Alpha Station Operations caught an exception\n" + ex.ToString());
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.Print("Alpha Station operations caught an exception : \n" + ex.Message);
                Debug.Print(ex.StackTrace);
            }
        }

It doesn't so much create a separate thread as it places your event handler in the thread that handles the com port. It is a distinction with little difference, but...you do not need any of the normal "while(true)" type of polling, here. The event gets raised by the hardware, regardless of where the main thread of execution or any other threads are in terms of their independent execution.

 

Most serial devices use text as the transport encoding and usually they terminate a transmission with a line feed, or carriage return line feed pair. (Binary information is usually length-limited, or uses a particular character to determine end of transmission. EOT/ETX). The .NET SerialPort class is quite flexible with respect to serial comms.

 

So, just write it to take one character at a time and evaluate that. If you store them off in a buffer each time and evaluate them when necessary, it will work fine and last a long time. The StringBuilder class is your friend when the comms are ASCII style. (Actually, use UTF8 encodings).

 

This is a long snippet -- I am still writing it, but -- it uses a StringBuilder to hole the received characters and evaluates each character. I am not sure, but -- I don't think that Netduino 3 can use the line oriented capablilities of .NET SerialPort. I hope I am wrong about that, actually....







Also tagged with one or more of these keywords: EventHandler, thread, serial, loop, performance

0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.