1
votes

I am writing application to control robot. I have a button click method, which should open or close port.

private void cONNECTToolStripMenuItem_CheckStateChanged(
                 object sender, EventArgs e)
{
    if (cONNECTToolStripMenuItem.Checked)
    {
        cONNECTToolStripMenuItem.Text = "DISCONNECT!";
        ports.connect();
    }
    else
    {
        cONNECTToolStripMenuItem.Text = "CONNECT!";
        ports.disconnect();
    }
}

Furthermore, I have DataReceived event handler, which is called after some data from serial port is received.

private void serialPort_DataReceived(
                 object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
    SerialPort sp = (SerialPort)sender;
    string data = sp.ReadExisting();

    mainForm.addData(data);
} 

When this event handler was trying to call addData(data) function

public void addData(string data)
{
    //if (!this.Disposing || !this.IsDisposed || 
    //    !this.listBox1.Disposing || !this.listBox1.IsDisposed)
    if (this.listBox1.InvokeRequired)
    {
        SetTextCallback d = new SetTextCallback(addData);
        this.Invoke(d, new object[] { data });
    }
    else
        listBox1.Items.Add(data);         
}

there was a cross-thread error, so I wrapped function content into InvokeRequired condition.

Now there is other errors: 1. Program crashes after trying to call ports.disconnect() function

public void disconnect()
{
    serialPort.Close();
}

ports class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;

namespace server_client
{
    public class COM_Port
    {
        private Main_Form mainForm;
        public SerialPort serialPort;

        //MySQL - 1, XML File - 2, None - 0
        public int writeStatus;

        public COM_Port(Main_Form form)
        {
            mainForm = form;

            //by default niekur neirasineja nebent pasirenka
            writeStatus = 0;

            //sukuriam serialPort kintamaji
            serialPort = new SerialPort();

            //nustatom duomenis portui
            serialPort.BaudRate = 9600;
            serialPort.DataBits = 8;
            serialPort.StopBits = StopBits.One;
            serialPort.ReadTimeout = 500;
            serialPort.WriteTimeout = 500;


        }

        ~COM_Port()
        {
            disconnect();
            serialPort.Dispose();
        }

        public void connect()
        {
            if (!serialPort.IsOpen)
                if (serialPort.PortName != "") 
                    serialPort.Open();
                else
                    MessageBox.Show("Please choose COM Port in settings.");
        }

        public void disconnect()
        {
            //serialPort.DiscardInBuffer();
            //serialPort.DiscardOutBuffer();
            serialPort.Close();
        }

        public void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            string data = null;

            SerialPort sp = (SerialPort)sender;

            if (sp.IsOpen)
                data = sp.ReadExisting();
            else
                data = null;

            //perduoda duomenis atvaizdavimui i maina
            if (data != null)
            {
                mainForm.addData(data);

                switch (writeStatus)
                {
                    //neirasyti
                    case 0:
                        break;
                    //irasyti i mysql
                    case 1:
                        MySQL mysql = new MySQL();
                        break;
                    //irasyti i xml file
                    case 2:
                        File file = new File();
                        break;
                    default:
                        break;
                }
            }
        } 
    }
}

I believe, that after closing the port robot is still sending me data and DataReceived event handler tries to get that data from buffer of serial port.

Thank you for reading this love letter, for your help :).

3
Add try/catch block to your functions and event-handlers then see what exception you get.Coder
What does "crash" mean?Uwe Keim
Crash in my situation means, that the program is not responding anymore and I cant get any errors to my visual studio ;/Rokas
If the program is not responding any more, that's a "hang", not a "crash".Roger Lipscombe
@Coder Why would that be useful? If there were an unhandled exception raised, it would continue to bubble up until it found an exception handler. If you had no exception-handling code in your program, it would reach the global exception handler, which would display an error message about an "Unhandled Exception". It's useless to add try-catch blocks strictly for debugging purposes.Cody Gray

3 Answers

1
votes

I've just had this issue myself, the solution is to use BeginInvoke() rather than Invoke() in the SerialPort.DataRecieved eventhandler.

Reference: duplicate question

1
votes

This happens if you receive lots of data. The DataReceived event handler is busy all the time and tries to read the buffer. In this handler, ReadExisting() has by far the longest execution time. You cannot the close the port while the DataReceived handler is being executed. You cannot wait for ReadExisting() to finish because then during this waiting time the DataReceived event will be fired again.

So you must stop calling ReadExisting() before closing the port, for example, like this:

First construct a new SerialPort instance

 SerialComPort = new SerialPort();
 SerialComPort.DataReceived += new SerialDataReceivedEventHandler(SerialComPort_DataReceived);
 SerialComPort.ReadTimeout = 100; //[ms] Time out if Read operation does not finish
 bool SerialPortPendingClose=false;

Then the DataReceived handler can look like:

byte[] InputData = new byte[512]; 
private void SerialComPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
     if (!SerialPortPendingClose)
     { SerialComPort.Read(InputData, 0, 512); }
     //Do something with InputData
}

Finally, close the port using (for example):

SerialPortPendingClose = true;
Thread.Sleep(SerialComPort.ReadTimeout); //Wait for reading threads to finish
SerialComPort.Close();
SerialPortPendingClose = false;
1
votes

i would use the following handler

private void SerialComPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
 if (!SerialPortPendingClose)
 { SerialComPort.Read(InputData, 0, 512); }
 //Do something with InputData
 else
 { SerialComPort.DataReceived -= SerialComPort_DataReceived;} //to ensure this will be called only once
}