Имеется следующий код.
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 самым убогим языком ООП. Каюсь.
Комментариев нет:
Отправить комментарий