.NET SerialPort DataReceived взаимодействие потока событий с основным потоком - PullRequest
1 голос
/ 22 декабря 2010

Я пишу программу последовательной связи, использующую класс SerialPort в C # для взаимодействия с стриптизером, подключенным через кабель RS232.Когда я посылаю команду на машину, она отвечает несколькими байтами в зависимости от команды.Как и при отправке команды «\ D», я ожидаю загрузки данных машинной программы размером 180 байт в виде непрерывной строки.Согласно руководству для машины, в качестве лучшей практики рекомендуется отправлять нераспознанные символы, такие как запятая (,), чтобы убедиться, что машина инициализируется перед отправкой первой команды в цикле.Мой код последовательной связи выглядит следующим образом:

public class SerialHelper
{
    SerialPort commPort = null;
    string currentReceived = string.Empty;
    string receivedStr = string.Empty;
    private bool CommInitialized()
    {
        try
        {
            commPort = new SerialPort();
            commPort.PortName = "COM1";
            if (!commPort.IsOpen)
                commPort.Open();
            commPort.BaudRate = 9600;
            commPort.Parity = System.IO.Ports.Parity.None;
            commPort.StopBits = StopBits.One;
            commPort.DataBits = 8;
            commPort.RtsEnable = true;
            commPort.DtrEnable = true;

            commPort.DataReceived += new SerialDataReceivedEventHandler(commPort_DataReceived);
            return true;
        }
        catch (Exception ex)
        {
            return false;
        }
    }

    void commPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        SerialPort currentPort = (SerialPort)sender;
        currentReceived = currentPort.ReadExisting();
        receivedStr += currentReceived;
    }

    internal int CommIO(string outString, int outLen, ref string inBuffer, int inLen)
    {
        receivedStr = string.Empty;
        inBuffer = string.Empty;
        if (CommInitialized())
        {
            commPort.Write(outString);
        }

        System.Threading.Thread.Sleep(1500);

        int i = 0;
        while ((receivedStr.Length < inLen) && i < 10)
        {
            System.Threading.Thread.Sleep(500);
            i += 1;
        }

        if (!string.IsNullOrEmpty(receivedStr))
        {
            inBuffer = receivedStr;
        }
        commPort.Close();

        return inBuffer.Length;

    }
}

Я вызываю этот код из окна формы следующим образом:

len = SerialHelperObj.CommIO(",",1,ref inBuffer, 4)
len = SerialHelperObj.CommIO(",",1,ref inBuffer, 4)
If(inBuffer == "!?*O")
{
   len = SerialHelperObj.CommIO("\D",2,ref inBuffer, 180)
}

Действительное возвращаемое значение из последовательного порта выглядит следующим образом:\ D00000010000000000010 550 3250 0000256000 и т. Д. ...

Я получаю нечто вроде этого: \ D00000010D ,, 000 550 D ,, и т. Д. *

Я чувствую, что мойЗвонки комм мешают тому, когда я посылаю команды.Но я пытаюсь убедиться, что результат команды запятой затем инициирует фактическую команду.но полученный поток вставляет байты из предыдущего цикла связи.

Кто-нибудь может пролить немного света на это ...?Я потерял немного волос, просто пытаясь получить эту работу.Я не уверен, где я делаю неправильно

Ответы [ 3 ]

2 голосов
/ 22 декабря 2010

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

...

Object lockingObj = new Object();

...

void commPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    lock(lockingObj)
    {
        SerialPort currentPort = (SerialPort)sender;
        currentReceived = currentPort.ReadExisting();
        receivedStr += currentReceived;
    }
}

Оператор блокировки не позволяет более чем одному потоку одновременно выполнять код в блоке, поэтому полученныйStr получит полное содержимое одного ReadExisting до того, как другойзаписано в него.

Имейте в виду, что это НЕ гарантирует порядок исполнения;первый в победах.Итак, предположим, что поток 1 сначала идет сюда, затем поток 2 приходит через 250 мс, а поток 3 - через 250 мс.Поток 1, скорее всего, войдет первым, выполнит чтение, которое займет некоторое время, а затем выйдет.В это время потоки 2 и 3 входят в функцию и блокируются в операторе блокировки, ожидая, пока поток 1 снимет свою блокировку.Как только это произойдет, все зависит от того, какой поток сначала запланирован ядром, и это может быть поток 3, в зависимости от количества ОС и аппаратных факторов.

0 голосов
/ 22 декабря 2010

Я вижу ряд проблем с вашим кодом.

  1. Никогда не игнорируйте исключения. Возвращение false игнорирует исключение. Вы не представляете, какое исключение произошло, когда вы это делаете.
  2. В частности, ваш commPort объект, возможно, никогда не был создан. Он может быть в любом состоянии, но вы игнорируете исключения и продолжаете обращаться к потенциально недопустимому объекту.
  3. Это событие DataReceived может произойти в любое время, когда порт открыт. Он может или не может быть поднят на той же теме. Ваш код может закрывать этот порт из-под обработчика событий, что приведет к исключению, что ваш код не перехватывает.
  4. Возможно, это не имеет значения, но знаете ли вы, что объединение строк - относительно дорогая операция в .NET? Строки неизменны. receivedStr += currentReceived не добавляется к receivedStr - он создает новый строковый объект для хранения обеих частей.
0 голосов
/ 22 декабря 2010

Почему вы каждый раз заново инициализируете свой порт?Я думаю, одного достаточно.

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