SerialPort (.Net 4.0) Метод ReadTo приводит к системному таймауту - PullRequest
0 голосов
/ 15 апреля 2019

В моем сценарии есть один или два последовательных порта. Если настроен только один порт, этот порт может быть выделен для ПЛК или для шкалы.

ПЛК связывается только тогда, когда пользователь нажимает какую-либо кнопку на машине, а вместо этого шкала постоянно опрашивает взвешенное измеренное значение. Если настроен только один порт, проблем нет.

Когда два порта настроены, протокол связи немного меняется, если пользователь нажимает кнопку веса ПЛК, ПЛК отправляет запрос веса. Если этот запрос прослушивается при событии DataReceived, порт Scale открывается, и запрашиваемое значение восстанавливается.

Теперь я должен признать, что устаревший код, который я вставлю сюда, не является наилучшим возможным кодом, и это плохо, что он использует одно и то же событие DataReceived для обоих портов; но этот код работал довольно долго (около шести лет). Недавно Масштаб был изменен, и программа перестала работать должным образом (только в конфигурации с двумя портами), вызывая системное исключение тайм-аута. Я прослушал трафик и ничего не изменилось с протоколом или форматом данных.

Вот проблемный кусок кода:

private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    lock (_myLockSerialPort)
    {
        string source = "No Data" ;

        try
        {
            if (sender == null)
                return;

            SerialPort genericPort = (SerialPort)sender;

            if (genericPort.Equals(ScalePort))
            {
                if (genericPort.BytesToRead > 0)
                {
                    source = "Scale" ;
                    string datas = string.Empty;
                    datas = genericPort.ReadTo(_currentConfiguration.ReadToForPese);
                    ReadDataFromScale(genericPort, datas);
                }
            }
            else
            {
                if (genericPort.BytesToRead > 0)
                {
                    source = "PLC" ;
                    string datas = string.Empty;
                    datas = genericPort.ReadTo(_currentConfiguration.ReadToForPlc);
                    ReadDataFromPlc(genericPort, datas);
                }

            }
        }
        catch (TimeoutException timeoutException)
        {
            //Handle Timeout
        }
        catch (Exception err)
        {
            //Handle Other Errors
        }
    }
}

Вопрос в том, есть ли что-то в методе ReadTo или в интервале опроса, которое может объяснить это поведение. Я хотел бы подчеркнуть, что ничего в формате обмена данными не изменилось, поэтому символ, используемый для прекращения связи, все еще остается на месте (и присутствует в перехваченном трафике).

Ответы [ 2 ]

1 голос
/ 16 апреля 2019

НЕТ, НЕТ, НЕТ !!! Если бы у вас был текстовый файл обычно, вы бы использовали метод ReadLine () для чтения по одной строке за раз. С портами или сокетами данные поступают на вас в режиме реального времени, и программа, вероятно, работает быстрее, чем данные, поэтому вы не получите полную строку за один раз. Последовательный UART в ПК имеет только небольшой буфер чтения (может быть до 8 байт). Windows использует таймеры для перемещения данных последовательного порта в поток. Ваша программа читает поток Windows. Таким образом, фактические полученные данные представляют собой комбинацию того, насколько быстро отправитель отправляет данные, задержку UART, задержку Windows и задержку сетевой библиотеки. Это не проблема конфигурации. Это проблема в реальном времени.

0 голосов
/ 16 апреля 2019

Я бы порекомендовал использовать два SerialPort класса, один для вашей шкалы и один для ПЛК, в котором они будут иметь свои DataReceived события.Как предлагали другие, вы должны добавлять символы до тех пор, пока не увидите конечный символ, а затем проанализировать строку с вашими методами, которые вы написали.Ниже приведен пример, где перевод строки (LF) - это мой конечный символ, который я ищу.

StringBuilder sbScale = new StringBuilder();
StringBuilder sbPLC = new StringBuilder();
char LF = (char)10;

private void serialPortScale_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
    string Data = serialPortScale.ReadExisting();

    foreach (char c in Data)
    {
        if (c == LF)
        {
            sbScale.Append(c);

            CurrentLine = sbScale.ToString();
            sbScale.Clear();

            ReadDataFromScale(CurrentLine);
        }
        else
        {
            sbScale.Append(c);
        }
    }
}

private void serialPortPLC_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
    string Data = serialPortPLC.ReadExisting();

    foreach (char c in Data)
    {
        if (c == LF)
        {
            sbPLC.Append(c);

            CurrentLine = sbPLC.ToString();
            sbPLC.Clear();

            ReadDataFromPlc(CurrentLine);
        }
        else
        {
            sbPLC.Append(c);
        }
    }
}

Вы можете попытаться реализовать это в одном DataReceived событии, но оно может быть грязным.Я думаю, что выделенные классы для каждого устройства намного чище и легче для чтения, особенно если кто-то должен взять код на следующий день после вас.Также я немедленно очищаю построитель строк, потому что событие DataReceived является многопоточным, поэтому оно может сработать, пока вы уже там.

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