Страницы

Поиск по вопросам

понедельник, 3 декабря 2018 г.

Контроль отключения сопряженного устройства по COM порту

Имеется следующий код.
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?


Ответ

Должно же кому-то пригодиться. Судя из англоязычных форумов, сама Microsoft не даёт никаких гарантий на то, что сама реализация SerialPort() будет работать корректно. Это зависит от многих условностей: тип порта, драйвера, аппаратная реализация (какие каналы задействованы) и прочее, прочее, прочее.
А также, стоит отметить что некоторые функции, например: SerialPort.Close() могут содержать несколько инструкций, которые могут генерировать собственные исключения. Но при этом они завершают, скажем так, начатое.
Моя проблема с "залипанием" соединения на порту при "отвале" устройства (виртуальный COM порт) была решена просто:
if (_port != null) { try { _port.Close(); } catch (Exception e) { // Ignore pointless exception. } _port = null; }
Это необходимо добавлять в catch() блок где находится функция записи в порт. Иными словами, если запись в порт невозможна, то "убиваем не убитое". В итоге, порт освобождается и объект не содержит больше ничего. Что позволяет продолжать мониторинг "реальных" портов в системе и осуществлять повторное подключение по доступности.
Со стороны это выглядит как мега костыль.
P.S. Я сильно ошибался, считая Java самым убогим языком ООП. Каюсь.

Комментариев нет:

Отправить комментарий