Серийный порт отвечает на EventHandler, но не ReadExisting или ReadLine? - PullRequest
5 голосов
/ 27 сентября 2011

У меня есть программа, которая читает с последовательного порта в C #.Мне нужно быстро написать в порт, прочитать с него, а затем закрыть его.я не могу оставить это открытым.Я понимаю, что последовательные порты читаются и пишутся медленно, я попытался установить высокие свойства ReadTimeout и WriteTimeout и добавил нить. Сплю, чтобы попытаться утащить время чтения и записи для устройств.Вот немного кода:

мой метод записи в порт:

    private void CheckPorts(string testMessage)
    {

        foreach (string s in SerialPort.GetPortNames())
        {
            portNumber = Int32.Parse(s.Remove(0, 3));
            testSerial = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
            if (testSerial.IsOpen)
            {
                testSerial.Close();
            }
            testSerial.ReadTimeout = 2000;
            testSerial.WriteTimeout = 1000;
            testSerial.Open();
            if (testSerial.IsOpen)
            {
                string received;
                testSerial.DiscardInBuffer();
                try
                {
                    //testSerial.DataReceived += new SerialDataReceivedEventHandler(testSerialPort_DataReceived);

                    testSerial.Write(testMessage);
                    System.Threading.Thread.Sleep(2000);

                    received = testSerial.ReadExisting();  //EITHER I USE THIS OR EVENT HANDLER, NOT BOTH
                }
                catch (TimeoutException e)
                {
                    testSerial.Close();
                    continue;
                }

               if (received.Length > 0)
                {
                    MessageReceived(received);
                }
                testSerial.Close();
            }
       } 
 }



 private void testSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        string received = testSerial.ReadExisting();
        int y = received.IndexOf("\r");
        while (y == -1)
        {
            received = received + testSerial.ReadExisting();
            y = received.IndexOf("\r");
        }

        if (testSerial.IsOpen)
        {
            testSerial.Close();
        }

    }

Мне интересно, если мне абсолютно необходимо использовать обработчик данных, как мне сохранить последовательный порт открытымдостаточно долго, чтобы прочитать из него, но закрыть последовательный порт, прежде чем нужно будет открыть следующий порт?

видите, первый метод вызывается несколько раз, и он повторяет цикл foreach, пытаясь получить сообщение нанесколько портов, а затем пытается прочитать ответ.Итак, в какой-то момент мне нужно закрыть порты, или в следующий раз, когда он проходит через него, он не работает должным образом, потому что порт все еще открыт

ЗДЕСЬ МОЙ ОБНОВЛЕННЫЙ КОД (все еще не работает):

 private void CheckPorts(string testMessage, int baudRate)
    {

        foreach (string s in SerialPort.GetPortNames())
        {
            var interval = 3000; // ms 
            var timer = new System.Timers.Timer(interval);
            timer.Elapsed += (o, e) =>
            {
                timer.Enabled = false;

                if (testSerial.IsOpen)
                    testSerial.Close();  // may not be necessary with Dispose? 

                testSerial.Dispose();
                timer.Dispose();
            };

            portNumber = Int32.Parse(s.Remove(0, 3));
            testSerial = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
            testSerial.ReadTimeout = 2000;
            testSerial.WriteTimeout = 2000;
            if (testSerial.IsOpen)
            {
                testSerial.Close();
            }

            testSerial.Open();
            timer.Enabled = true; 

            if (testSerial.IsOpen)
            {
                string received;
                //testSerial.DiscardInBuffer();
                //autoEvent = new AutoResetEvent(false);
                try
                {
                   // testSerial.DataReceived += new SerialDataReceivedEventHandler(testSerialPort_DataReceived);

                  // autoEvent.Reset();
                    lblPortNum.Content = s;
                    lblPortNum.Refresh();

                    testSerial.Write(testMessage);
                    //System.Threading.Thread.Sleep(2000);

                    //testSerial.NewLine = "\r\n";
                    byte[] rBuff = new byte[2];
                    int rCnt = testSerial.Read(rBuff, 0, 2);
                    System.Text.Encoding enc = System.Text.Encoding.ASCII;
                    received = enc.GetString(rBuff);



                     //received = testSerial.ReadLine();
                }
                catch (TimeoutException e)
                {
                    testSerial.Close();
                    continue;
                }

               if (received.Length > 0)
               {
                    MessageReceived(received, Int16.Parse(s.Remove(0, 3)));
                }
                /*
                if (autoEvent.WaitOne(2000))
                {
                    // the port responded 
                   // testSerial.Close();
                    autoEvent.Dispose();
                    lblPortNum.Content = "HEY I RESPONDED";
                }
                else
                {
                    testSerial.Close();
                    autoEvent.Dispose();
                    continue;
                    // port did not respond within 2 seconds 
                }*/
              //testSerial.Close();
            }
        } 
     }

ВНОВЬ ОБНОВЛЕНО (все еще не работает должным образом)

private void CheckPorts(string testMessage, int baudRate)
    {

        foreach (string s in SerialPort.GetPortNames())
        {
            portNumber = Int32.Parse(s.Remove(0, 3));

            // MUST BE LOCAL 
            var serialOneOfMany = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
            serialOneOfMany.ReadTimeout = 2000;
            serialOneOfMany.WriteTimeout = 2000;
            if (serialOneOfMany.IsOpen)
            {
                serialOneOfMany.Close();
            }

            // timer must be defined _after_ serialOneOfMany 
            var interval = 3000; // ms  
            var timer = new System.Timers.Timer(interval);
            timer.Elapsed += (o, e) =>
            {
                timer.Enabled = false;

                if (serialOneOfMany.IsOpen)
                    serialOneOfMany.Close();  // may not be necessary with Dispose?  

                serialOneOfMany.Dispose();
                timer.Dispose();
            };

            if (serialOneOfMany.IsOpen)
            {
                string received;

                try
                {
                    lblPortNum.Content = s;
                    lblPortNum.Refresh();

                    serialOneOfMany.Write(testMessage);
                    byte[] rBuff = new byte[2];
                    int rCnt = serialOneOfMany.Read(rBuff, 0, 2);
                    System.Text.Encoding enc = System.Text.Encoding.ASCII;
                    received = enc.GetString(rBuff);

                }
                catch (TimeoutException e)
                {
                    serialOneOfMany.Close();
                    continue;
                }

                if (received.Length > 0)
                {
                    CheckIfTheMessageMatches(received, Int16.Parse(s.Remove(0, 3)));
                }

            }
        } 

    }

так что с этим обновлением, он просто проходит через код, я могу шаг за шагом проходить код, но этоне останавливается на 3 секунды вообще.если я запускаю его без каких-либо перерывов в отладке, он просто проходит через это в течение доли секунды

ОБНОВЛЕНИЕ 10-25-11

 private void CheckPorts(string testMessage, int baudRate)
    {
        foreach (string s in SerialPort.GetPortNames())
        {
            string received = "";
            testSerial = new SerialPort(s,baudRate, Parity.None, 8, StopBits.One);

            lblStatus.Content = "Scanning...";
            lblStatus.Refresh();

            if (testSerial.IsOpen)
            {
                testSerial.Close();
            }
            else
            {
                testSerial.Open();
            }

            if (testSerial.IsOpen)
            {
                try
                {
                    testSerial.NewLine = "\r";
                    lblPortNum.Content = s;
                    lblPortNum.Refresh();
                    testSerial.WriteTimeout= 500;
                    testSerial.ReadTimeout = 1000;
                    testSerial.WriteLine(testMessage);

                    System.Threading.Thread.Sleep(500);

                    /*THIS DOESN'T WORK
                    byte[] buffer = new byte[testSerial.BytesToRead];
                    int rCnt = testSerial.Read(buffer, 0, buffer.Length);
                    received = enc.GetString(buffer);*/

                    //received = Convert.ToString(testSerial.BaseStream.Read(buffer, 0, (int)buffer.Length));


                    received =  testSerial.ReadLine();


                   int y = received.IndexOf("\r");
                   while (y == -1)
                   {
                       received = received + testSerial.ReadExisting();
                       y = received.Length;
                   }

                   if (lblInfo.Dispatcher.Thread == Thread.CurrentThread)
                   {
                       CheckIfTheMessageMatches(received, s);
                       received = received + lblInfo.Content;
                       lblInfo.Content = received;
                   }
                   else
                   {
                       lblInfo.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadCheck(threadCheck), received);
                   }
                   if (testSerial.IsOpen)
                   {
                       testSerial.Close();
                   }

                    /*I USE THIS WITH THE sPort.Read() METHOD
                    while (rCnt > 0)
                    {
                        if (lblInfo.Dispatcher.Thread == Thread.CurrentThread)
                        {
                            CheckIfTheMessageMatches(received, s);
                            rCnt = 0;
                            received = received + lblInfo.Content;
                            lblInfo.Content = received;                                
                        }

                        else
                        {
                            lblInfo.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadCheck(threadCheck), received);
                        }
                    }
                     */

                   if (testSerial.IsOpen)
                   {
                       testSerial.Close();
                   }

                }
                catch (TimeoutException e)
                {
                    testSerial.Close();
                    continue;
                }
                received = null;
            }
        } 

        lblStatus.Content = "Finished Scanning.";
        lblPortNum.Content = "";
    }

ОБНОВЛЕННЫЙ КОД Вот некоторый новый код, все еще не работающий, dataeventhandler даже не вызывал ни разу.я знаю, что он получает сообщения, потому что у меня есть другая программа, которая работает с последовательными устройствами

private void CheckPorts(string testMessage, int baudRate)
    {
        foreach (string s in SerialPort.GetPortNames())
        {
            var serialOneOfMany = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
            serialOneOfMany.ReadTimeout = 700;
            serialOneOfMany.WriteTimeout = 100;

            var interval = 500; // ms
            var timer = new System.Timers.Timer(interval);
            timer.Elapsed += (o, e) =>
            {
                timer.Enabled = false;

                if (serialOneOfMany.IsOpen)
                    serialOneOfMany.Close();  // may not be necessary with Dispose?

                serialOneOfMany.Dispose();
                timer.Dispose();
            };
            timer.Enabled = true;

            lblStatus.Content = "Scanning...";
            lblStatus.Refresh();

            if (serialOneOfMany.IsOpen)
            {
                serialOneOfMany.Close();
            }
            else
            {
                serialOneOfMany.Open();
            }

            if (serialOneOfMany.IsOpen)
            {
                string received;

                try
                {
                    lblPortNum.Content = s;
                    lblPortNum.Refresh();

                    serialOneOfMany.WriteLine(testMessage);
                    System.Threading.Thread.Sleep(400);
                    serialOneOfMany.DataReceived += new SerialDataReceivedEventHandler(testSerialPort_DataReceived);

                }
                catch (TimeoutException e)
                {
                    serialOneOfMany.Close();
                    continue;
                }
            }
        } 

        lblStatus.Content = "Finished Scanning.";
        lblPortNum.Content = "";
    }

    private void testSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        SerialPort receivingSerial = sender as SerialPort;
        string received = receivingSerial.ReadExisting();
        int y = received.IndexOf("\r");
        while (y == -1)
        {
            received = received + receivingSerial.ReadExisting();
            y = received.IndexOf("\r");
        }

        if (lblInfo.Dispatcher.Thread == Thread.CurrentThread)
        {
            string name = receivingSerial.PortName;
            received = received + lblInfo.Content;
            lblInfo.Content = received;
            CheckIfTheMessageMatches(received, name);
        }
        else
        {
            lblInfo.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadCheck(threadCheck), received);
        } 
        if (receivingSerial.IsOpen)
        {
            receivingSerial.Close();
        }

    }

Ответы [ 7 ]

4 голосов
/ 27 сентября 2011

Вы должны быть в состоянии сделать это одновременно (при условии, что это нормально).Затем вы закроете их при возникновении события DataReceived (посторонний код удален).Только не закрывайте порт в CheckPorts.

private void testSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    SerialPort localSerialPort = sender as SerialPort;

    ... // use localSerialPort instead of global/class variable

    if (localSerialPort.IsOpen)
    {
        localSerialPort.Close();
    }
}

EDIT : ответ на комментарий.

Вы всегда можете добавить таймер на лету.Если вы поместите это в цикл foreach, вы получите таймер для каждого последовательного порта, который будет использовать заданный последовательный порт через 3 секунды.Здесь важно, чтобы таймер был объявлен в цикле foreach.

var interval = 3000; // ms
var timer = new System.Timers.Timer(interval);
timer.Elapsed += (o,e) => 
    {
        timer.Enabled = false;

        if (testSerial.IsOpen)
            testSerial.Close();  // may not be necessary with Dispose?

        testSerial.Dispose();
        timer.Dispose();
    }

timer.Enabled = true;

EDIT : код обновлен, поэтому я обновлю

Область действия очень важна для кодаЯ предоставил.Вам следует избавиться от нелокального testSerial или использовать здесь совершенно другое имя.

        portNumber = Int32.Parse(s.Remove(0, 3));

        // MUST BE LOCAL
        var serialOneOfMany = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
        serialOneOfMany.ReadTimeout = 2000;
        serialOneOfMany.WriteTimeout = 2000;
        if (serialOneOfMany.IsOpen)
        {
            serialOneOfMany.Close();
        }

        // timer must be defined _after_ serialOneOfMany
        var interval = 3000; // ms 
        var timer = new System.Timers.Timer(interval);
        timer.Elapsed += (o, e) =>
        {
            timer.Enabled = false;

            if (serialOneOfMany.IsOpen)
                serialOneOfMany.Close();  // may not be necessary with Dispose? 

            serialOneOfMany.Dispose();
            timer.Dispose();
        };     
1 голос
/ 28 октября 2011

Пожалуйста, посмотрите на это (я также использовал в ответе на вопрос, связанный с последовательным портом, заданный Дартвиллардом). Все порты открываются один за другим, события DataReceived связаны (все, что вам нужно сделать, это проверить входящее сообщение), но ожидание не требуется. Обработчик событий таймера может закрыть все порты или оставить тот, который вы хотите использовать, и т. Д. Надеюсь, это поможет!

private List<SerialPort> openPorts = new List<SerialPort>();

private void button3_Click(object sender, EventArgs e)
{
    int baudRate = 9600;
    string testMessage = "test";
    txtPortName.Text = "Testing all serial ports";
    foreach (string s in SerialPort.GetPortNames())
    {
        SerialPort newPort = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
        if (!newPort.IsOpen)
        {
            try
            {
                newPort.Open();
            }
            catch { }
        }
        if (newPort.IsOpen)
        {
            openPorts.Add(newPort);
            newPort.DataReceived += new SerialDataReceivedEventHandler(serialOneOfMany_DataReceived);
            newPort.Write(testMessage);
        }
        else
        {
            newPort.Dispose();
        }
    }
    txtPortName.Text = "Waiting for response";
    tmrPortTest.Enabled = true;
}

private void serialOneOfMany_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    txtPortName.Text = ((SerialPort)sender).PortName;
}

private void tmrPortTest_Tick(object sender, EventArgs e)
{
    tmrPortTest.Enabled = false;
    foreach (SerialPort port in openPorts)
    {
        if (port.PortName != txtPortName.Text)
        {
            port.Close();
            port.Dispose();
        }
    }
}
1 голос
/ 27 сентября 2011

Проверьте эту информацию от Microsoft:

Этот метод возвращает содержимое потока и внутреннего буфера объекта SerialPort в виде строки.Этот метод не использует тайм-аут.Обратите внимание, что этот метод может оставить конечные байты во внутреннем буфере, что делает значение BytesToRead больше нуля.

Почему бы не использовать обычный метод чтения SerialPort.Read (Byte[], Int32, Int32)

0 голосов
/ 15 ноября 2012

Вы можете быть лучшим с BackgroundWorker .Например:

        BackgroundWorker worker=new BackgroundWorker();
        worker.DoWork += (s, dwe) =>
                             {
        // do your serial IO here
        worker.RunWorkerCompleted += (s, rwe) =>
        {
            // check for rwe.Error and respond
        };
        worker.RunWorkerAsync();
0 голосов
/ 01 мая 2012

открыть порт в публичной форме1 сразу после / ниже InitializeComponent ();myport.open и закройте после получения данных.работал!

0 голосов
/ 05 ноября 2011

Вы не можете использовать Thread.Sleep.Он блокирует чтение с устройства.Вам нужно создать новую тему.

0 голосов
/ 28 октября 2011

Попробуйте установить обработчик событий перед записью в порт, а затем посмотрите, не перехватывает ли он вашу точку останова.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...