Страницы

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

четверг, 26 декабря 2019 г.

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

#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 самым убогим языком ООП. Каюсь.

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

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