c# - read - using serial port
System.IO.IOException: un dispositivo conectado al sistema no funciona C#.NET 4.0 (2)
En mi experiencia, 9 de cada 10 veces esto sucede cuando otro subproceso (terminado o no) no tiene acceso exclusivo al puerto de hardware.
Intente escribir un contenedor para las operaciones del puerto, lo más importante es abrirlo / cerrarlo, usando SyncLock . https://msdn.microsoft.com/en-us/library/3a86s51t.aspx
En una nota similar, en general, consideraría que probar / detectar el control del hardware es una mala práctica, a menos que haya un adecuado manejo de excepciones.
La razón (que podría aplicarse aquí) es que, en el caso de que se produzca una excepción, el hardware se bloqueará y, lo que es peor, la excepción ocultará la verdadera causa de los errores.
En el código anterior veo salida de mensajes en el estilo.
DebugPrint(ex.Message);
sería bastante mejor hacer esto como
DebugPrint(ex.tostring());
ya que esto también exportará el seguimiento de la pila en la excepción.
Lo que haría sería implementar un registrador de excepciones que escriba esas excepciones en un archivo de texto (con marca de tiempo) en algún lugar de la computadora que esté ejecutando. Seguir los datos de excepción registrados (junto con toda la información pertinente) puede llevar a una mejor comprensión de por qué sucede exactamente esto.
He construido una aplicación C # que lee y escribe datos desde un puerto serie. El dispositivo conectado al puerto serie es un convertidor FTDI USB a serie que se comunica con el hardware a través de un módulo inalámbrico XBee. El hardware prueba los módulos de la batería para determinar la capacidad y el voltaje de estado estable, etc. Estas pruebas tardan días en completarse.
De vez en cuando, el puerto serie parece dejar de responder y emite una excepción System.IO.IOException: un dispositivo conectado al sistema no funciona correctamente.
Aquí está la traza de la pila:
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
después de que se produce este error, se System.UnauthorizedAccessException: Access to the port is denied
un System.UnauthorizedAccessException: Access to the port is denied
produce un error y este error se produce cada vez que el software intenta escribir en el puerto y nunca se recupera hasta que detenga la depuración y reinicio del software solo para lo mismo Que suceda unos días después.
¿Cómo evito que ocurra este error o hay una manera de recuperarme exitosamente de estos errores en la declaración de captura del error?
Estoy leyendo el puerto serie continuamente en un subproceso de trabajo en segundo plano y escribiendo desde otro subproceso.
También he intentado todas las partes y piezas de errores heredadas que se han sugerido en este foro, pero ninguna de ellas parece hacer ninguna diferencia. El error se produce en Windows XP Pro SP3 32bit y Windows7 Pro 32bit.
Aquí está el CommunicationClass.cs - código de transmisión en serie.
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;
}
Así es como inicializo mi puerto serial.
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);
}
Y el trabajador de fondo que maneja la lectura del puerto serie.
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 { }
}
}
Ahora reescribiré el código para leer y escribir en / desde el puerto serie desde el mismo hilo para ver si eso hace alguna diferencia.
EDITAR
Basándome en los comentarios de Jim, agregué lo siguiente a la declaración de captura de IOException:
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);
}
Esperemos que al detener el _serialPort.trabajo del trabajador de fondo, cerrar el puerto, volver a abrir el puerto, ejecutar nuevamente el trabajador de fondo e intentar escribir el mismo comando será suficiente para recuperarse exitosamente de este error. El cuadro de mensaje todavía bloquea el código para que pueda ver cuándo se produce el error y puede supervisar cómo se recupera.
No me gustan los parches de software como este, pero si funciona, entonces funciona.
Editar 2
Después de agregar el código anterior, mi software se bloqueó nuevamente pero ahora arroja una "UnauthorizedAccessException: el acceso al puerto está denegado" cuando llamo a _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:
¿Que esta pasando aqui?
Uso intensamente la clase SerialPort para comunicarme continuamente con PLC a través de adaptadores USB durante meses sin interrupción. Así que no estoy de acuerdo con quién dice que la clase SerialPort .NET no funciona. Intente insertar la creación de la clase en el hilo, aquí hay una muestra de código con BackgroundWorker.
void ThreadEngine_DoWork(object sender, DoWorkEventArgs e)
{
// Do not access the form''s 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;
}
}
El método IOScanner llama a otros métodos para comunicarse como el siguiente.
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;
}