Подтвердить что ты не робот

System.IO.IOException: подключенное к системе устройство не работает. С#.NET 4.0

Я создал приложение С#, которое считывает и записывает данные из последовательного порта. Устройство, подключенное к последовательному порту, представляет собой преобразователь FTDI USB в последовательный преобразователь, который передает аппаратное обеспечение через беспроводной модуль XBee. Аппарат проверяет аккумуляторные модули на мощность и постоянное напряжение и т.д. Эти испытания занимают несколько дней.

Кажется, что последовательный порт перестает отвечать на запросы и выдает System.IO.IOException: устройство, подключенное к системе, не работает.

Вот трассировка стека:

at system.IO.Ports.InternalResources.WinIOError
at system.IO.Ports.SerialStream.EndWrite
at system.IO.Ports.SerialStream.Write
at system.IO.Ports.SerialPort.Write
at BatteryCharger.CommunicationClass.ApiTransmission

после возникновения этой ошибки возникает ошибка System.UnauthorizedAccessException: Access to the port is denied, и эта ошибка возникает каждый раз, когда программное обеспечение пытается записать в порт, и оно никогда не восстанавливается, пока я не прекращу отладку и не перезагружу программное обеспечение только для того, дней спустя.

Как предотвратить эту ошибку или существует способ успешно восстановить эти ошибки в выводе ошибки ошибки?

Я постоянно читаю последовательный порт в фоновом рабочем потоке и пишу из другого потока.

Я также уже пробовал все предыдущие биты обработки ошибок, которые были предложены на этом форуме, но ни один из них, похоже, не имеет никакого значения. Ошибка возникает на Windows XP Pro SP3 32bit и Windows7 Pro 32bit.

Здесь приведен код обмена данными CommunicationClass.cs.

    public static bool ApiTransmission(TXpacket transmission)
    {
        //clear all previous data that may have been in the buffer before doing a transmission
        Variables.dataParserPacket_buff.Clear();
        //TXpacket xbeetransmision = new TXpacket();
        byte[] packet = transmission.GeneratePacket();

        try
        {
            if (_serialPort.IsOpen)
            {
#if Console
                Log.write("TX-Packet: " + StringHandler.listToString(packet.ToList<byte>()));
#endif
                _serialPort.Write(packet, 0, packet.Length);
                Thread.Sleep(100);
            }
            else
            {
#if Console
                Log.write("serial port is closed");
#endif
                return false;
            }
        }
        catch (UnauthorizedAccessException ex)
        {
            MessageBox.Show(ex.ToString());
            Log.write("UnauthorizedAccessException");
        }
        catch (IOException ex)
        {
            MessageBox.Show(ex.ToString());
            Log.write("IOexception");
            //_serialPort.Close();
            //Thread.Sleep(100);
            //_serialPort.Open();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
#if Console
            Log.write(ex.ToString());
#endif
        }
        return true;

    }

Вот как я инициализирую свой последовательный порт

    public CommunicationClass(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits)
    {
        _analysePacketBGW.DoWork += new DoWorkEventHandler(_analysePacketBGW_DoWork);
        _analysePacketBGW.WorkerReportsProgress = true;
        _analysePacketBGW.WorkerSupportsCancellation = true;

        _readBGW.DoWork += new DoWorkEventHandler(_readThread_DoWork);
        _readBGW.WorkerSupportsCancellation = true;
        _readBGW.WorkerReportsProgress = true;

        _parserStarterBGW.DoWork += new DoWorkEventHandler(_parserStarterThread_DoWork);
        _parserStarterBGW.WorkerSupportsCancellation = true;
        _parserStarterBGW.WorkerReportsProgress = true;
        if (_readBGW != null)
        {
            _readBGW.CancelAsync();
        }

        _serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits);

        //SerialPortFixer.Execute(portName);
        //Thread.Sleep(1000);
        //using (_serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits))
        //{
        //    //_serialPort.Open();
        //}

        _serialPort.ErrorReceived += new SerialErrorReceivedEventHandler(_serialPort_ErrorReceived);

        _dataqueuepp = new ManualResetEvent(false);

        _serialPort.Open();
        _readBGW.RunWorkerAsync();
        _parserStarterBGW.RunWorkerAsync();
        CommunicationClass.PacketReceived += new DataPacketReceived(CommunicationClass_PacketReceived);
    }

И фоновый рабочий, который обрабатывает чтение последовательного порта

    void _readThread_DoWork(object sender, DoWorkEventArgs e)
    {
#if Console
        Log.write("Read()");
#endif
        while (!_readBGW.CancellationPending)
        {
            try
            {
                int message = _serialPort.ReadByte();
                try
                {
                    Variables.dataQueue.Enqueue(message);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message + "     " + message.ToString());
                }
                _dataqueuepp.Set();
                //Console.Write(String.Format("{0:X2}", message) + " ");
            }
            catch (TimeoutException) { Log.write("read timeout"); }
            catch (IOException) { Log.write("read IOException"); }
            catch (ThreadAbortException) { Log.write("read thread aborted"); }
            catch (Exception ex) { MessageBox.Show(ex.ToString()); }
            finally { }
        }
    }

Теперь я переписал код для чтения и записи в/из последовательного порта из того же потока, чтобы убедиться, что это имеет значение.

ИЗМЕНИТЬ

Основываясь на комментариях Джима, я добавил следующее в инструкцию IOException Catch:

        catch (IOException ex)
        {
            MessageBox.Show(ex.ToString());
            Log.write("IOexception");
            _readBGW.CancelAsync();
            Thread.Sleep(100);
            _serialPort.Close();
            Thread.Sleep(100);
            _serialPort.Open();
            Thread.Sleep(100);
            _readBGW.RunWorkerAsync();
            _serialPort.Write(packet, 0, packet.Length);
        }

Будем надеяться, остановив фонового рабочего _serialPort.Read, закрыв порт, снова открыв порт, снова запустив рабочего фона и попытавшись написать ту же команду снова, чтобы успешно восстановить эту ошибку. MessageBox по-прежнему блокирует код, чтобы я мог видеть, когда возникает ошибка, и может контролировать, как он восстанавливается.

Мне не нравится такое программное обеспечение для исправления, но если оно работает, то оно работает.

РЕДАКТИРОВАТЬ 2

После добавления кода выше мое программное обеспечение снова сработало, но теперь оно выбрасывает "UnauthorizedAccessException - доступ к порту отрицается", когда я вызываю _serialPort.Close();

System.UnauthorizedAccessException was unhandled
Message=Access to the port is denied.
Source=System
StackTrace:
at System.IO.Ports.InternalResources.WinIOError(Int32 errorCode, String str)
at System.IO.Ports.InternalResources.WinIOError()
at System.IO.Ports.SerialStream.Dispose(Boolean disposing)
at System.IO.Stream.Close()
at System.IO.Ports.SerialPort.Dispose(Boolean disposing)
at System.IO.Ports.SerialPort.Close()
at BatteryCharger.CommunicationClass.ApiTransmission(TXpacket transmission) in E:\Mijn Documenten\Research\Copy of BatteryCharger_V12\BatteryCharger\CommunicationClass.cs:line 436
at BatteryCharger.CommunicationClass.tx1(TXpacket packet, String callingMethod) in E:\Mijn Documenten\Research\Copy of BatteryCharger_V12\BatteryCharger\CommunicationClass.cs:line 356
at BatteryCharger.XBee.setPin(String pinID, Byte status, XBee slave) in E:\Mijn Documenten\Research\Copy of BatteryCharger_V12\BatteryCharger\XBee.cs:line 215
at BatteryCharger.XBee.setPins(Int32 pins, XBee slave) in E:\Mijn Documenten\Research\Copy of BatteryCharger_V12\BatteryCharger\XBee.cs:line 177
at BatteryCharger.BatteryCharger.requestVoltage(Int32 block) in E:\Mijn Documenten\Research\Copy of BatteryCharger_V12\BatteryCharger\BatteryCharger.cs:line 595
at BatteryCharger.BatteryCharger.requestVoltages() in E:\Mijn Documenten\Research\Copy of BatteryCharger_V12\BatteryCharger\BatteryCharger.cs:line 612
at BatteryCharger.Form1.RunCommandOn(List`1 battList, Int32 command, Double lowerLimit, Double upperLimit) in E:\Mijn Documenten\Research\Copy of BatteryCharger_V12\BatteryCharger\Form1.cs:line 522
at BatteryCharger.Form1.chargeBlock(Int32 blockNr, Double lowerLimit, Double upperLimit) in E:\Mijn Documenten\Research\Copy of BatteryCharger_V12\BatteryCharger\Form1.cs:line 689
at BatteryCharger.Form1.<btnCheckCapacities_Click>b__13() in E:\Mijn Documenten\Research\Copy of BatteryCharger_V12\BatteryCharger\Form1.cs:line 619
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException: 

Что здесь происходит?

4b9b3361

Ответ 1

Я интенсивно использую класс SerialPort для непрерывного общения с ПЛК через USB-адаптеры в течение нескольких месяцев без перерыва. Поэтому я не могу согласиться с тем, что говорит, что класс SerialPort.NET не работает. Попробуйте вставить создание класса в поток, вот образец кода с BackgroundWorker.

    void ThreadEngine_DoWork(object sender, DoWorkEventArgs e)
    {
        // Do not access the form BackgroundWorker reference directly.
        // Instead, use the reference provided by the sender parameter.
        BackgroundWorker objBackgroundWorker = sender as BackgroundWorker;

        try
        {
            mSerialPort = new SerialPort(GetPortName(mPortName), DefaultBaudRate, Parity.Even, 7, StopBits.Two);
            mSerialPort.Open();
            objBackgroundWorker.ReportProgress(0);

            while (objBackgroundWorker.CancellationPending == false)
            {
                if (IOScanner(objBackgroundWorker, false) == true)
                {
                    ScannerStationData();
                    IsReady = true;
                    IsError = false;
                }
                else
                {
                    IsReady = false;
                    IsError = true;
                }
                Thread.Sleep(1);
            }

            // Performs last scan before thread closing
            if (objBackgroundWorker.CancellationPending == true)
            {
                IOScanner(objBackgroundWorker, true);
            }

            mSerialPort.Close();
            mSerialPort = null;
            e.Result = null;
        }
        catch (Exception objErr)
        {
            string sMessage = string.Format("PlcService.ThreadEngine_DoWork Err={0}", objErr.Message);
            mLogSysService.AddItem(sMessage);
            IsError = true;
        }
    }

Метод IOScanner вызывает другие методы для общения, например, следующие.

    protected bool WriteDMWord(int iAddress, int[] aryValues)
    {
        bool bRetValue = true;
        try
        {
            mSerialPort.NewLine = "\r";
            mSerialPort.ReadTimeout = DefaultTimeout;
            string sTxData = HostLinkProtocol.BuildWriteDMWord(iAddress, aryValues);
            mSerialPort.WriteLine(sTxData);

            string sRxData = string.Empty;
            sRxData = mSerialPort.ReadLine();
            if (HostLinkProtocol.ParseWriteDMWord(sRxData) == true)
            {
                bRetValue = true;
            }
        }
        catch (Exception objErr)
        {
            Console.WriteLine("WriteDMWord [{0}]", objErr.Message);
            bRetValue = false;
        }
        return bRetValue;
    }

Ответ 2

По моему опыту 9 раз из 10 это происходит, когда другой поток (завершенный или нет) не имеет эксклюзивного доступа к аппаратным портам.

Попробуйте написать обертку для операций с портами, а самое главное - открыть/закрыть ее, используя SyncLock. https://msdn.microsoft.com/en-us/library/3a86s51t.aspx

В аналогичной нотации я бы вообще подумал, что попытка/улавливание аппаратного обеспечения вредоносная практика, если не существует адекватной обработки исключений.

Причина (которая может применяться здесь) заключается в том, что в случае, когда создается исключение, аппаратное обеспечение блокируется, а что хуже, исключение маскирует истинную причину ошибки.

В приведенном выше коде я вижу вывод сообщений в стиле

DebugPrint(ex.Message); 

было бы лучше сделать это, например

DebugPrint(ex.tostring()); 

так как это также экспортирует трассировку стека в исключение.

Что бы я сделал, это реализовать журнал регистрации исключений, который записывает эти исключения в текстовый файл с меткой времени где-то на компьютере, на котором он запущен. Следуя за данными об исключении, зарегистрированными (вместе со всей соответствующей информацией), может привести к лучшему пониманию, почему именно это происходит.