Тайм-аут последовательной связи при длительном тайм-ауте кабеля

У меня есть приложение, которое читает различные аппаратные средства через RS232. Это было проверено, и это работало отлично. Для окончательного применения мне нужно было представить кабель длиной несколько метров, что означает, что у меня есть преобразователи RS485.

Когда я запускаю свое приложение для чтения оборудования, я получаю ошибку тайм-аута для System.IO.Ports.SerialStream.Read. Я увеличил тайм-аут до 20 секунд, к сожалению, это не решило проблему

Я пробовал разные приложения для чтения оборудования, и они работали даже с частотой считывания 1сек.

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

Мой код выглядит так: сначала открытие и инициализация последовательного порта:

//get the right modbus data structure element
ModBus MB = (ModBus)s[0].sensorData;

//set up the serial port regarding the data structure's data
SerialPort sp = new SerialPort();
sp.PortName = MB.portName;
sp.BaudRate = Convert.ToInt32(MB.baudRate);
sp.DataBits = MB.dataBits;
sp.Parity = MB.parity;
sp.StopBits = MB.stopBits;
//Set time outs 20 sec for now
sp.ReadTimeout = 20000;
sp.WriteTimeout = 20000;

// добавляем порт в список, к которому может обращаться ридер portList.Add(sp); sp.Open();

Читать аппаратное обеспечение:

//get the right port for com
SerialPort sp = getRightPort();
ModBus MB = getRightModBusStructureelement();
try
   {
     //Clear in/out buffers:
     sp.DiscardOutBuffer();
     sp.DiscardInBuffer();

     //create modbus read message
     byte[] message = createReadModBusMessage();

     try
        {
         sp.Write(message, 0, message.Length);

         // FM.writeErrorLog output included for easier debug
         FM.writeErrorLog(DateTime.Now + ": ModBus Message Sent");
         FM.writeErrorLog(DateTime.Now + ": Read TimeOut = " + sp.ReadTimeout + " Write TimeOut = " + sp.WriteTimeout);

         int offset = 0, bytesRead;
         int bytesExpected = response.Length;

         FM.writeErrorLog(DateTime.Now + ": start read");

         while (bytesExpected > 0 && (bytesRead = sp.Read(response, offset, bytesExpected)) > 0)
            {
              FM.writeErrorLog(DateTime.Now + ": read - " + offset);
              offset += bytesRead;
              bytesExpected -= bytesRead;
            }
        }
        catch (Exception err)
        {
           Console.WriteLine("ERROR Modbus Message to serial port ModBus: " + err);
           FM.writeErrorLog(DateTime.Now + " - " + "ERROR Modbus Message to serial port ModBus: " + err);
         }

  }

После попытки применения я получил следующий вывод из файла ErroLog.txt:

14/01/2016 17:18:17: ModBus Message Sent
14/01/2016 17:18:17: Read TimeOut = 20000 Write TimeOut = 20000
14/01/2016 17:18:18: start read
14/01/2016 17:18:38 - ERROR Modbus Message to serial port ModBus: System.TimeoutException: The operation has timed out.
   at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count, Int32 timeout)
   at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count)
   at System.IO.Ports.SerialPort.Read(Byte[] buffer, Int32 offset, Int32 count)
   at ProbReader.SensorReader.modbusReading(List`1 mm, Int32 spCounter)
14/01/2016 17:18:38: 0
14/01/2016 17:18:38: 0

Я увеличил тайм-аут до 60 секунд на всякий случай, но та же ошибка:

15/01/2016 11:11:51: ModBus Message Sent
15/01/2016 11:11:51: Read TimeOut = 60000 Write TimeOut = 60000
15/01/2016 11:11:51: start read
15/01/2016 11:12:51 - ERROR Modbus Message to serial port ModBus: System.TimeoutException: The operation has timed out.
   at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count, Int32 timeout)
   at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count)
   at System.IO.Ports.SerialPort.Read(Byte[] buffer, Int32 offset, Int32 count)
   at ProbReader.SensorReader.modbusReading(List`1 mm, Int32 spCounter)
15/01/2016 11:12:51: 0
15/01/2016 11:12:51: 0

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

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

5 ответов

Решение

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

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

                            sp.ReadTimeout = 60000;
                            sp.WriteTimeout = 60000;

                            sp.DtrEnable = true;
                            sp.RtsEnable = true;
                            sp.Handshake = Handshake.None;

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

Если вы используете сотни метров последовательного кабеля (что само по себе является проблемой аппаратного обеспечения), тогда я настоятельно рекомендую иметь подходящий трансивер на обоих концах кабеля. Сам кабель должен быть ЭМС-экранированным и качественным. Длинные отрезки неэкранированного кабеля могут стать жертвами вызванных колебаний напряжения, которые могут повредить оборудование, не предназначенное для обработки длинных отрезков кабеля.

Даже при наличии хорошего кабеля у вас все равно будут значительные падения напряжения и индуктивные / емкостные эффекты, которые могут помешать обмену данными с более высокой скоростью передачи. Работайте с самой низкой скоростью передачи, с которой вы можете сойти.

Предполагая, что это аппаратная проблема (и я думаю, что мне пришлось решать аналогичную проблему), я бы рекомендовал заменить сотни метров последовательного кабеля сервером устройств (через Ethernet) рядом с последовательным устройством. Сервер устройств может эмулировать COM-порт на вашем компьютере и, следовательно, держать короткий кабель последовательного порта.

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

Обратите внимание, что разница между RS232 и RS485 заключается в том, что RS232 является дуплексным, а RS485 - только полудуплексным. Поскольку Modbus - это протокол типа ответа на запрос, это не ваша проблема, но важно знать.

В связи с этим RS232<->RS485 должен знать, когда включать передатчик RS485. Это может быть сделано по-разному на разных конвертерах, а также может быть конфигурируемым. Это можно сделать с помощью дополнительных линий управления, которые есть у RS232, но нет у RS485. RTS/CTS. Если неправильно настроен передатчик отвечающей стороны (ожидающий ответа), он может быть включен, и тогда он не сможет ничего получить.

Примером в руководстве является http://ftc.beijer.se/files/C125728B003AF839/992C59EC02C66E00C12579C60051484E/westermo_ug_6617-2203_mdw-45.pdf Это популярная в Швеции модель с тремя режимами работы. Он может включать / выключать передатчик только по поступающим данным, управляется выводом RTS на разъеме DB9-RS232 или всегда держать передатчик включенным (для RS422, которые имеют отдельные провода в каждом направлении) http://screencast.com/t/KrkEw13J8

Это сложно, потому что некоторые производители не распечатывают, как это на самом деле работает. Также очень легко ошибиться в проводке, потому что не ясно, что такое DCE и DTE.

Предполагая, что это не проблема аппаратного / длинного кабеля, вы могли бы сделать что-то в своем коде для обработки ошибки:

Вам нужно создать "правильный" обработчик ошибок столько, сколько вы создаете getRightPort:

SerialPort sp = getRightPort();

Предполагая, что есть Collection элементов последовательного порта внутри, и вы возвращаете правильный, в случае этого SerialPort есть ошибка, убедитесь, что вы воссоздаете ошибку SerialPortobject с такими же настройками:

catch (Exception err)
{
   Console.WriteLine("ERROR Modbus Message to serial port ModBus: " + err);
   FM.writeErrorLog(DateTime.Now + " - " + "ERROR Modbus Message to serial port ModBus: " + err);
   reinitRightPort(sp); //add this
}

И метод reinitRightPort(); может выглядеть примерно так, как вы начинаете, с небольшими отличиями, такими как:

  1. Вам не нужно добавлять это в List<SerialPort> больше не
  2. Вы не должны объявлять SerialPort в методе вы получаете его из входного аргумента.
  3. Может быть лучше ввести некоторую достоверность проверки ввода, чтобы избежать более поздних известных ошибок
  4. Возможно, вы можете закрыть предыдущее соединение, просто чтобы убедиться, что порт может использоваться новым SerialPortobject,

Что-то вроде этого:

private void reinitRightPort(SerialPort sp){ //get SerialPort from outside of method
    //Add whatever necessary here, to close all the current connections
    //Plus all the error handlings
    if (sp == null) //Read 3.
        return;
    sp.Close(); //Read 4. close the current connections

    //get the right modbus data structure element
    ModBus MB = (ModBus)s[0].sensorData;

    //set up the serial port regarding the data structure's data
    sp = new SerialPort(); //Read 2. sp is from outside the method, but use new keyword still
    sp.PortName = MB.portName;
    sp.BaudRate = Convert.ToInt32(MB.baudRate);
    sp.DataBits = MB.dataBits;
    sp.Parity = MB.parity;
    sp.StopBits = MB.stopBits;
    //Set time outs 20 sec for now
    sp.ReadTimeout = 20000;
    sp.WriteTimeout = 20000;

    //portList.Add(sp); Read 1. no need to add this! It is already there!
    sp.Open();
}

Примечание: после того, как вы это сделаете и убедитесь, что ваш порт работает нормально, по возможности вы также можете объединить reinitRightPort метод выше с вашей реальной инициализацией с небольшими изменениями. Но первое, что вы можете сделать, это заставить ваш последовательный порт работать при ошибке.

Но если источник ошибки происходит из-за проблем с оборудованием / длинным кабелем (например, из-за размещения кабеля в RS232 или RS485, или из-за падения напряжения из-за длинного кабеля, или из-за несовместимого оборудования: в общем, это никак не связано с кодированием), тогда, к сожалению, решение не может прийти и из кода. Вы должны найти реальную аппаратную проблему.

Другие вопросы по тегам