Задать вопрос
@Dxpl

System.OperationCanceledException Как правильно закрыть порт?

Пишу прогу, .Net c# WPF, принимает и обрабатывает данные по COM порту. Используется SerialPorts из System.IO.Ports.dll
Красным полупрозрачным маркером на скринах выделено то, что по моему не влияет на ошибку. Элементы wpf и другая "косметика"

Метод CloseSerial сначала переключает int portState в 0, потом закрывает порт с помощью SerialP.Close()
portState используется при для переключения открыт/закрыт порта и как bool для while внутри асинхронных методов: метод чтения GetData() и метод отправки SendData запускается сразу после открытия порта методом OpenSerial()

Если убрать GetData, то все работает нормально. Порт открывается и закрывается, при этом делая это по настоящему, данные отправляются.
Но вот при добавлении GetData при попытке закрыть порт возникает
Вызвано исключение: "System.OperationCanceledException" в System.IO.Ports.dll

Вылазит на строке RawData = SerialP.ReadLine(); То есть на чтении

При этом, судя по выводу, SerialP.Close() отрабатывает нормально и внутри GetData(); Условие if (SerialP.IsOpen == true | portState == 1) возвращает false, соотв RawData = SerialP.ReadLine(); вообще не должна выполняться. Но исключение вылазит именно на ней, VisualStudio на нее показывает по крайней мере
Кстати, если закомментировать строку RawData = SerialP.ReadLine(); исключение не возникает. И закрытие/открытие порта отрабатывает штатно
Приложение крашит, обработать исключение у меня не получилось. Инфы в инете тоже нету, только решенные вопросы по Azure, но там не мой случай. На learn.microsoft ничего толком не понял, как то слишком профессионально у них написано
Что сделал:
Ставил задержки. На случай если порт закрывается не моментально
Пробовал читать посимвольно. Результат тот же.
Пробовал много разных проверок и (опять же) обработки исключения
Подскажите пожалуйста что делаю не так
Код:
*Я вырезал из кода всякие назначения цветов для кнопок wpf, что бы не превысить колво символов
void OpenSerial()
        {
            try
            {
                SerialP.PortName = TBX_SerialPortName.Text;
                SerialP.BaudRate = 115200;
                SerialP.StopBits = StopBits.One;
                SerialP.Parity = Parity.None;
                SerialP.Open();
            }
            catch
            {
                Trace.WriteLine("Исключение. Не удается открыть порт");
            }
            finally
            {
                if(SerialP.IsOpen == true)
                {
                    portState = 1;
                    SendData();
                    GetData();
                }
            }
        }

        void CloseSerial()
        {
            Trace.WriteLine("CloseSerial");
            try
            {
                cancelTokenSource.Cancel();
                portState = 0;
                Thread.Sleep(3000);
                Trace.WriteLine("Next hop SerialP.Close()");
                SerialP.Close();
                Thread.Sleep(3000);
            }
            catch
            {
                Trace.WriteLine("Не удается закрыть порт");
            }
            finally
            {
                
                if (SerialP.IsOpen == false)
                {
                    Trace.WriteLine("IsOpen=false");
                    
                }
            }
        }

public async void GetData()
        {
            CancellationToken token = cancelTokenSource.Token;
            await Task.Run(() =>
            {


                while (portState == 1)
                {
                    if (token.IsCancellationRequested)  
                    {
                        Trace.WriteLine("Операция прервана");
                        return;     
                    }
                    if (SerialP.IsOpen == true | portState == 1)
                    {
                        Trace.WriteLine("выа");
                        NowKey = "";
                        Trace.WriteLine("р");
                        try
                        {
                                RawData = SerialP.ReadLine();
                        }
                        catch (TimeoutException) { }
                        Trace.WriteLine("OPEN");
                        if (RawData != null & RawData != "")
                        {
                            NowKey = RawData.Substring(0, 2);
                            Data = RawData.Substring(2);
                        }

                        Thread.Sleep(100);

                    }
                }
            },token);
        }

Полный код без изменений: https://ideone.com/P8ByKW
Не разобрался как поделится кодом в jsfiddle
  • Вопрос задан
  • 109 просмотров
Подписаться 1 Простой 1 комментарий
Решения вопроса 2
@cicatrix
было бы большой ошибкой думать
А не проще ловить событие порта DataReceived?

...

_serialPort.Open();
_serialPort.DiscardInBuffer();
_serialPort.DataReceived += SerialPort_DataReceived;
_serialPort.ErrorReceived += SerialPort_ErrorReceived;

...

  public static void Disconnect()
        {
            try
            {
                if (_serialPort is not null && _serialPort.IsOpen)
                {
                    _serialPort.DataReceived -= SerialPort_DataReceived;
                    _serialPort.ErrorReceived -= SerialPort_ErrorReceived;
                    _serialPort.Close();
                }
            } // try
            catch(Exception ex)
            {
                Log.Error(ex, "Error closing the serial port.");
            } // 
        } // Disconnect
...


 private static void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (_serialPort?.IsOpen != true) return;

            try
            {
                var encoding = _serialPort.Encoding;
                var bytesRead = _serialPort.Read(_buffer, 0, _serialPort.BytesToRead);
                var rawBytes = new ReadOnlyMemory<byte>(_buffer, 0, bytesRead);

                var maxCharCount = encoding.GetMaxCharCount(bytesRead);
                var charArray = ArrayPool<char>.Shared.Rent(maxCharCount);

                var charsProduced = encoding.GetChars(
                    rawBytes.Span,
                    charArray.AsSpan()
                );

                var scannedData = new ScannedData(
                    rawBytes,
                    new ReadOnlyMemory<char>(charArray, 0, charsProduced)
                );

                IncomingData.Enqueue(scannedData);
                DataReceived?.Invoke(null, new DataReceivedEventArgs(scannedData));

                ArrayPool<char>.Shared.Return(charArray);
            } // try
            catch (Exception ex)
            {
                Log.Error(ex, "Error processing barcode data");
            } // catch
        } // _serialPort_DataReceived
Ответ написан
lasalas
@lasalas
.NET Architect
Неправильный вызов async метода.
Нужно либо GetData().Wait();, либо async Task OpenSerial() { ... await GetData();
Возможно, и с SendData та же проблема.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы