Тайм-аут последовательной связи при длительном отключении кабеля
У меня есть приложение, которое считывает различные аппаратные средства через rs232. Он был протестирован, и он работал отлично. Для окончательного применения мне понадобилось ввести несколько кабелей длиной hunder m, что означает, что у меня есть преобразователи 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 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 в моем коде чтения.
Я не включил остальную часть моего кода, поскольку он истекает раньше, и я думаю, что это не имеет значения.
Ответы
Ответ 1
Как я уже упоминал в своем вопросе, я смог прочитать аппаратное обеспечение с другим программным обеспечением, чтобы оно было ошибкой программного обеспечения. После изучения всех возможных переменных, которые я мог бы манипулировать при настройке последовательного порта, я придумал идею поворота рукопожатия и позволяю ему всегда быть принятым.
Немного копаясь, дайте мне следующий код, увеличивая время записи и чтения. Он решил мою проблему:
sp.ReadTimeout = 60000;
sp.WriteTimeout = 60000;
sp.DtrEnable = true;
sp.RtsEnable = true;
sp.Handshake = Handshake.None;
Я надеюсь, что это поможет другим в будущем, и спасибо за помощь и усилия.
Ответ 2
Если вы используете сотни метров последовательного кабеля (который сам по себе является проблемой аппаратного обеспечения), я настоятельно рекомендую иметь надлежащий трансивер на обоих концах кабеля. Сам кабель должен быть экранированным и высококачественным. Длинные неэкранированные кабели могут стать жертвами наведенных колебаний напряжения, которые могут повредить оборудование, которое не предназначено для длительных проводов кабелей.
Даже с хорошим кабелем у вас все еще будут значительные падения напряжения и индуктивные/емкостные эффекты, которые могут препятствовать обмену данными при более высоких скоростях передачи. Пробег с самой низкой скоростью передачи, с которой вы можете уйти.
Ответ 3
Предполагая, что это проблема с оборудованием (и, как мне кажется, мне тоже пришлось решить аналогичную проблему), я бы рекомендовал заменить сотни метров последовательного кабеля сервером устройств (через ethernet) рядом с последовательным устройство. Сервер устройства может эмулировать COM-порт на вашем ПК и, следовательно, короткое замыкание последовательного кабеля.
К сожалению, эти серверы немного дороже, чем несколько метров кабеля.
Ответ 4
Обратите внимание, что разница между 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.
Ответ 5
Предполагая, что это не проблема с аппаратным/длинным кабелем, вы можете сделать что-то в своем коде для обработки ошибки:
Вам нужно создать "правильный" обработчик ошибок столько, сколько вы создаете getRightPort
:
SerialPort sp = getRightPort();
Предполагая, что внутри объекта последовательного порта есть Collection
, и вы возвращаете правильный, в случае возникновения этой ошибки SerialPort
убедитесь, что вы повторно создали ошибку SerialPort
object
с тем же параметры:
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();
может выглядеть так, как вы начинаете, с небольшими различиями, например:
- Вам больше не нужно добавлять это в
List<SerialPort>
- Вы не должны объявлять
SerialPort
в методе, вы получаете его из входного аргумента.
- Лучше всего ввести некоторую проверку правильности ввода, чтобы избежать более поздних известных ошибок.
- Возможно, вы можете закрыть предыдущее соединение, просто чтобы убедиться, что порт может использоваться новым
SerialPort
object
.
Что-то вроде этого:
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 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 или падение напряжения из-за длительного кабель или несовместимое оборудование: это, короче говоря, не имеет ничего общего с кодирование вообще), то, к сожалению, решение не может исходить из кода также. Вы должны найти реальную проблему с оборудованием.