elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Arreglado, de nuevo, el registro del warzone (wargame) de EHN


+  Foro de elhacker.net
|-+  Programación
| |-+  Programación General
| | |-+  .NET (C#, VB.NET, ASP) (Moderador: kub0x)
| | | |-+  Comunicación SerialPort concurrente
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] Ir Abajo Respuesta Imprimir
Autor Tema: Comunicación SerialPort concurrente  (Leído 2,986 veces)
K-YreX


Desconectado Desconectado

Mensajes: 1.008



Ver Perfil
Comunicación SerialPort concurrente
« en: 8 Agosto 2021, 19:44 pm »

Hola a tod@s.
Estoy con un proyecto desde hace un tiempo y aunque lo he planteado de varias formas diferentes, no logró decantarme por una. Os lo comento por si alguien quiere aportar su granito de arena:

Tengo x dispositivos que se comunican con el PC mediante un mismo puerto serie creado por un receptor. Cada dispositivo tiene un ID (2 dígitos) y para enviar un comando (aaaa) al dispositivo bb hay que escribir en el puerto serie: bbaaaa. El dispositivo responde con el mismo comando (bbaaaa) seguido de algún argumento si fuera necesario.

Como todos los dispositivos comparten el mismo puerto serie, creé algo así:
Código
  1. public class Dispositivo {
  2.  private static SerialPort puertoSerie;
  3.  
  4.  public static void AbrirConexion(string nombreCOM) {...}
  5.  
  6.  public int Id { get; }
  7.  public int NumeroSerie { get => ObtenerNumeroSerie(); }
  8.  //...
  9. }

En un principio la comunicación era comando-respuesta por lo que en los métodos utilizados implementé algo así:
Código
  1. lock(puertoSerie) {
  2.  EnviarComando(comando);
  3.  respuesta = RecibirRespuesta();
  4. }
  5. // gestion de la respuesta

De esta manera me aseguraba de que cuando una instancia de un dispositivo enviaba un comando al puerto serie, ninguno más pudiese enviar nada hasta obtener una respuesta de este (o superar el ReadTimeout del puerto serie y entonces devolver null). Esto funcionaba perfectamente mientras los comandos fuesen todos comando-respuesta.

El problema viene al intentar utilizar un comando que se envía una vez pero genera muchas respuestas seguidas durante x tiempo. Esta implementación para la comunicación ya no me servía así que opté por lo siguiente:
Código
  1. public class Dispositivo {
  2.  //...
  3.  private static readonly Dictionary<int, Dictionary<string, string>> gestorRespuestas = new Dictionary<int, Dictionary<string, string>>();
  4.  //...
  5. }

Lo que hago con esto es tener un almacén de respuestas. Cada dispositivo, tras enviar un comando se queda esperando una respuesta del puerto serie. Cuando ha obtenido una respuesta o null (si se supera el ReadTimeout) mira si esa respuesta coincide con la que estaba esperando. Si es así la gestiona y se acabó. Si no coincide, mira de qué dispositivo viene esa respuesta y a qué comando está respondiendo y lo almacena en gestorRespuestas[idEmisor][comandoRespondido], una vez hecho esto mira en gestorRespuestas[Id][comandoEnviado] si otro hilo ha almacenado una respuesta para él.

He realizado unas cuantas pruebas y funciona bastante bien excepto cuando escribo múltiples comandos en el puerto serie prácticamente al mismo tiempo que entonces alguno no llega a responderse nunca. Supongo que es porque hay que esperar un poco entre comando y comando (agregando una espera de 50 ms no he conseguido producir este problema).
La sincronización del puerto serie ahora la realizo justo para escribir en el puerto serie y para leer de él.


Me preguntaba si este enfoque es correcto o debería diseñar otra solución. Pensé también en hacer una cola de envíos y tener un único subproceso enviando comandos de esa cola cada x ms y otro subproceso únicamente para obtener las respuestas y guardarlas en el gestorRespuestas. Así cada hilo que trabaja con una instancia de dispositivo sólo tendría que acceder al gestorRespuestas y nunca al puerto serie directamente.
También he pensado en crear una clase ConcurrentSerialPort para no tener que gestionar los bloqueos desde la clase Dispositivo.

Estaré a la espera de vuestras aportaciones. :rolleyes:


En línea

Código
  1. cout << "Todos tenemos un defecto, un error en nuestro código" << endl;
Serapis
Colaborador
***
Desconectado Desconectado

Mensajes: 3.351


Ver Perfil
Re: Comunicación SerialPort concurrente
« Respuesta #1 en: 9 Agosto 2021, 15:03 pm »

Yo haría lo último que señalas... una cola de salida y otra de entrada.

Debes cuidar la cola de salida... para que no se envíen más peticiones por unidad de tiempo de las que está capacitado a recibir, ya que el puerto serie tiene sus limitaciones. Un temporizador que se  encargue de ver si tiene salidas pendientes, y si las hay enviar la primera en curso y si se espera una devolución asignarle un id y pasarlo a la cola de entrada, que cuando sea despachado del dispositivo (con el mismo id), se envía a la cola de entrada (devolución) que seguramente podrá actuar a una mejor velocidad, y una vez servida borrar de esta cola.

Con ambas colas evitas la necesidad del bloqueo que seguramente te haga perder notificaciones.

Es importante que recabes las especificaciones del puerto serie, para aprovecharlo al máximo... no saturarlo ni tampoco dejarlo inactivo y que caiga su eficiencia por debajo de sus capacidades.


En línea

Meta


Desconectado Desconectado

Mensajes: 3.438



Ver Perfil WWW
Re: Comunicación SerialPort concurrente
« Respuesta #2 en: 26 Diciembre 2021, 04:31 am »

Felices fiestas.  ;-)

¿Tienes claro que comandos envias y qué esperas recibir de él?

Puedes hacer una mini lista sobre comandos aquí para poder hacer ejemplos.

Si usas el Windows Form, añades un botón en el formulario, hablando en C# para este ejemplo, para enviar comandos es así.
Código
  1.        private void button_Activar_Click(object sender, EventArgs e)
  2.        {
  3.            byte[] mBuffer = Encoding.ASCII.GetBytes("EncenderLed"); // Comando a enviar.
  4.            serialPort1.Write(mBuffer, 0, mBuffer.Length);
  5.        }

Al recibir datos es así.
Código
  1.        // Al recibir datos.
  2.        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
  3.        {
  4.            // Acumula los caracteres recibidos a nuestro 'buffer' (string).
  5.            recibidos += serialPort1.ReadExisting();
  6.  
  7.            // Invocar o llamar al proceso de tramas.
  8.            Invoke(new EventHandler(Actualizar));
  9.        }

No olvidar que cada comando recibido, debes vaciar la variable para que no se concatenen, que luego no funciona.

Código
  1.            // Limpiar.
  2.            recibidos = "";

Ya nos contarás más información sobre ello. Que dispositivos es y si te sabes realmente los comandos y respuestas a enviar o lo haces tuyo propio o viene en documentación del dispositivo.

Saludos.  :D
En línea

Páginas: [1] Ir Arriba Respuesta Imprimir 

Ir a:  

Mensajes similares
Asunto Iniciado por Respuestas Vistas Último mensaje
C# System.IO.Port SerialPort
Hacking Mobile
hoofmen 8 23,057 Último mensaje 31 Mayo 2009, 10:33 am
por Jubjub
Configuración serialPort en C#
.NET (C#, VB.NET, ASP)
Meta 0 2,309 Último mensaje 7 Diciembre 2008, 05:32 am
por Meta
SerialPort y Modem ?
.NET (C#, VB.NET, ASP)
TrashAmbishion 0 2,028 Último mensaje 27 Enero 2013, 23:43 pm
por TrashAmbishion
Problemita usando el SerialPort ?
.NET (C#, VB.NET, ASP)
TrashAmbishion 0 1,600 Último mensaje 5 Junio 2013, 00:26 am
por TrashAmbishion
VB 2008 SerialPort
Programación Visual Basic
01munrra 2 2,446 Último mensaje 31 Julio 2019, 17:19 pm
por 01munrra
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines