#c_sharp
Имеется следующий код.
public static class PortHandler
{
private static ConnectionType _state;
private static SerialPort _port;
public static void init()
{
_state = ConnectionType.DETACHED;
Thread serialMonitor = new Thread(SerialMonitor);
serialMonitor.IsBackground = true;
serialMonitor.Start();
}
static void SerialMonitor()
{
while (true)
{
switch (_state)
{
case ConnectionType.DETACHED:
scanPorts();
break;
case ConnectionType.CONNECTED:
FormFunctions.writeLog("Check Port. Size: " + SerialPort.GetPortNames().Length,
MessageType.INFO);
try
{
_port.Write(new byte[1] { 0x00 }, 0, 1);
}
catch (Exception e)
{
FormFunctions.writeLog("Fault to connect to " + _port.PortName
+ ". Exception: " + e, MessageType.INFO);
_state = ConnectionType.DETACHED;
}
break;
}
Thread.Sleep(1000);
}
}
private static void scanPorts()
{
string[] ports = SerialPort.GetPortNames();
if (ports.Length == 0)
{
FormFunctions.writeLog("Port list is empty.", MessageType.INFO);
return;
}
foreach (string port in ports)
{
if (checkSerialPort(port))
{
FormFunctions.writeLog("Port connected.", MessageType.SUCCESS);
_state = ConnectionType.CONNECTED;
break;
}
}
}
private static Boolean checkSerialPort(string name)
{
_port = new SerialPort(name);
_port.BaudRate = 9600;
_port.Parity = Parity.None;
_port.StopBits = StopBits.One;
_port.DataBits = 8;
_port.Handshake = Handshake.None;
_port.PinChanged += new SerialPinChangedEventHandler(SerialPinChangedEventHandler);
_port.Open();
byte[] init = new byte[4] { 0xa1, 0xa1, 0xa1, 0xa1 };
_port.Write(init, 0, init.Length);
return true;
}
private static void SerialPinChangedEventHandler(object sender, SerialPinChangedEventArgs e)
{
FormFunctions.writeLog("Pin Changed Event: " + e, MessageType.SUCCESS);
}
}
Если закомментировать все стройчки с подключением к порту и отправкой информации,
то в логах программы регистрируются все ивенты с подключением/отключением устройства
синхронно со звуком в системе. Но стоит создать подключение, то всё работает до (!)
эмуляции отвала устройства. Т.е. если я вручную выдёргиваю девайс, то получаю IO Exception
на строке _port.Open();
Лог работы программы следующий:
12.12.2017, 11:57:11 [INFO]: Port list is empty.
12.12.2017, 11:57:12 [INFO]: Port list is empty.
12.12.2017, 11:57:13 [INFO]: Port list is empty.
12.12.2017, 11:57:14 [INFO]: Port list is empty.
12.12.2017, 11:57:15 [INFO]: Port list is empty.
12.12.2017, 11:57:16 [INFO]: Port list is empty.
12.12.2017, 11:57:17 [SUCCESS]: Port connected.
12.12.2017, 11:57:18 [INFO]: Check Port. Size: 1
12.12.2017, 11:57:19 [INFO]: Check Port. Size: 1
12.12.2017, 11:57:20 [INFO]: Check Port. Size: 1
12.12.2017, 11:57:21 [INFO]: Check Port. Size: 1
12.12.2017, 11:57:22 [INFO]: Check Port. Size: 1
12.12.2017, 11:57:22 [INFO]: Fault to connect to COM15. Exception: System.IO.IOException:
Порт 'COM15' не существует.
в System.IO.Ports.InternalResources.WinIOError(Int32 errorCode, String str)
в System.IO.Ports.SerialStream.EndWrite(IAsyncResult asyncResult)
в System.IO.Ports.SerialStream.Write(Byte[] array, Int32 offset, Int32 count,
Int32 timeout)
в System.IO.Ports.SerialPort.Write(Byte[] buffer, Int32 offset, Int32 count)
в Infrared.common.PortHandler.SerialMonitor() в C:\...\PortHandler.cs:строка 42
Пробовал использовать ивент SerialPinChangedEventHandler, но никакой реакции на него
нет. Также пробовал принудительно завершать текущее подключение, но получаю ошибку
NPE на функции _port.Close().
Подскажите, где я неправ.
P.S. Или статический класс не подходящий пример решения моей задачи, т.к. Dispose()
с ним не работает?
P.S.S. Есть ли смысл реализовать это в инстансе на Singleton?
Ответы
Ответ 1
По поводу SerialPort.Open(): Работает оно маленько не так как вы ожидаете. Если устройства не будет физические или оно в не валидном состоянии(см. MSDN), то этот метод выдаст исключение. Скорее всего этот метод вызывает внутри себя апишную CreateFile. По этому можно представить поведение SerialPort.Open(), как открытие файла и так его нет соответственно выбрасывается исключение. Но есть одно но. Если у вас порт на матиринке, то он будет виден даже когда устройство не подключено. И в этом случае SerialPort.Open() скорее всего завершится без ошибок, и возможно даже ОС даст вам слать туда данные. По поводу SerialPinChangedEventHandler Судя по документации нет ни каких гарантий, что это событие будет работать со всеми устройствами или дровами. Тут я могу ошибаться так как работать с этим не приходилось. По поводу Dispose() и Singleton Вызывать Dispose() считается хорошим тоном, даже если у вас объект живет в течении всего времени работы приложения. Но в вашем примере оно в принципе не особо нужно.Ответ 2
Должно же кому-то пригодиться. Судя из англоязычных форумов, сама Microsoft не даёт никаких гарантий на то, что сама реализация SerialPort() будет работать корректно. Это зависит от многих условностей: тип порта, драйвера, аппаратная реализация (какие каналы задействованы) и прочее, прочее, прочее. А также, стоит отметить что некоторые функции, например: SerialPort.Close() могут содержать несколько инструкций, которые могут генерировать собственные исключения. Но при этом они завершают, скажем так, начатое. Моя проблема с "залипанием" соединения на порту при "отвале" устройства (виртуальный COM порт) была решена просто: if (_port != null) { try { _port.Close(); } catch (Exception e) { // Ignore pointless exception. } _port = null; } Это необходимо добавлять в catch() блок где находится функция записи в порт. Иными словами, если запись в порт невозможна, то "убиваем не убитое". В итоге, порт освобождается и объект не содержит больше ничего. Что позволяет продолжать мониторинг "реальных" портов в системе и осуществлять повторное подключение по доступности. Со стороны это выглядит как мега костыль. P.S. Я сильно ошибался, считая Java самым убогим языком ООП. Каюсь.
Комментариев нет:
Отправить комментарий