una selectedrows seleccionar manipular llenar form fila datos data con como c# datagridview datatable serial-port invoke

c# - selectedrows - Los datos del puerto serial recibidos se manejaron muy despacio



manipular datos de un datagridview c# (3)

Almacena los datos en una cola y descarga el trabajo a un hilo secundario. Esto solo funciona si, en promedio, puede procesar los datos a la velocidad a la que ingresa. De lo contrario, el tamaño de la cola seguirá creciendo a medida que se atrase.

En primer lugar, comience con un contenedor alrededor de Queue<T> que permita que un hilo escriba en la cola y otro que lo lea de forma segura. Además, permite que el hilo del lector bloquee la espera de datos.

public class ThreadedQueue<T> { private readonly Queue<T> _queue = new Queue<T>(); private readonly ManualResetEvent _notEmptyEvt = new ManualResetEvent(false); public WaitHandle WaitHandle { get { return _notEmptyEvt; } } public void Enqueue(T obj) { lock (_queue) { _queue.Enqueue(obj); _notEmptyEvt.Set(); } } public T Dequeue() { _notEmptyEvt.WaitOne(Timeout.Infinite); lock (_queue) { var result = _queue.Dequeue(); if (_queue.Count == 0) _notEmptyEvt.Reset(); return result; } } }

En su controlador de puerto serie, escriba los datos en la cola:

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { string inData = serialPort1.ReadLine(); if (PauseButton.Text == "Pause" && inData.StartsWith("<")) { _queue.Enqueue(inData); } }

En el hilo secundario, lea de la cola y haga la invocación al hilo de la GUI:

private void ThreadProc() { while (true) { string inData = _queue.Dequeue(); this.Invoke(new SetGridDeleg(DoUpdate), new object[] {inData}); } }

Inicie el hilo secundario de esta manera:

Thread th = new Thread(ThreadProc); th.IsBackground = true; th.Start();

Por supuesto, deberá crear una instancia de la cola:

ThreadedQueue<string> _queue = new ThreadedQueue<string>();

Estoy leyendo datos de un arduino a una velocidad en baudios de 115200. Los datos vienen como una cadena en su propia línea en el formato: <ID,Name/Data> .

Creo que el problema con mi código es que no maneja los datos entrantes lo suficientemente rápido y los datos entrantes se ven obligados a esperar a que se procesen los datos antiguos.

La cadena entrante se divide en las tres categorías separadas (ID, Nombre, Datos) y se agrega a una tabla de datos llamada dtFromGrid que está vinculada a dataGridView1 .

¿Hay algún error o sugerencia sobre cómo mejorar el rendimiento de mi código? ¿Un hilo separado para la función de manejo funcionaría mejor que BeginInvoke ?

serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived); private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { string inData = serialPort1.ReadLine(); if (PauseButton.Text == "Pause" && inData.StartsWith("<")) { try { this.BeginInvoke(new SetGridDeleg(DoUpdate), new object[] {inData}); } catch { } } } private void DoUpdate(string inData) //inData passed in so that Serial port read only once { if (dtFromGrid == null) { dtFromGrid = new DataTable(); dtFromGrid.Columns.Add("Time", typeof(String)); dtFromGrid.Columns.Add("ID", typeof(String)); dtFromGrid.Columns.Add("Name", typeof(String)); dtFromGrid.Columns.Add("Data", typeof(String)); } DataRow dr = dtFromGrid.NewRow(); TimeSpan ts = stopWatch.Elapsed; dr["Time"] = String.Format("{0:00}:{1:00}:{2:00}.{3:000}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds); dr["ID"] = inData.Split(new char[] { ''<'', '','' })[1]; dr["Name"] = inData.Split(new char[] { '','', ''/'' })[1]; dr["Data"] = inData.Split(new char[] { ''/'', ''>'' })[1]; dtFromGrid.Rows.InsertAt(dr, 0); //Replace old data with new data if ID''s are the same to showo list of only newest data per each ID if (NewestButton.Text == "Chronological") { for (int i = 1; i < dataGridView1.Rows.Count; i++) { if (dtFromGrid.Rows[i].ItemArray[1].ToString() == dtFromGrid.Rows[0].ItemArray[1].ToString()) { dtFromGrid.Rows[i].Delete(); break; } } } //Keep a maximum of 50 rows of data if (dtFromGrid.Rows.Count == 51) { dtFromGrid.Rows[50].Delete(); } dtFromGrid.AcceptChanges(); dataGridView1.DataSource = dtFromGrid; //keep focus of dataGridView on top row dataGridView1.CurrentCell = dataGridView1.Rows[0].Cells[0]; // add newest row to a logfile if the user has set one if (logFile != "") { using (StreamWriter sw = File.AppendText(logFile)) { DataRow row = dtFromGrid.Rows[0]; object[] array = row.ItemArray; int col = 0; for (col = 0; col < array.Length - 1; col++) { sw.Write(array[col].ToString() + "/t|/t"); } sw.Write(array[col].ToString()); sw.WriteLine(); sw.Close(); } } }

Actualizar

Ahora estoy usando un hilo separado como se sugiere pero estoy teniendo errores al invocar dentro de ese hilo. Recibo múltiples errores al azar, pero el más común es "Índice fuera de rango". Mi código de invocación es el siguiente: this.Invoke((MethodInvoker) delegate { dtFromGrid.AcceptChanges(); dataGridView1.DataSource = dtFromGrid; dataGridView1.CurrentCell = dataGridView1.Rows[0].Cells[0]; });


Como dijo, su código está ralentizando la recepción de datos. puede resolver su problema poniendo en cola sus datos en una lista de cola y un proceso en segundo plano procesará esta lista uno por uno. Otro enfoque es crear un nuevo hilo en la recepción de cada lote de datos.
Ejemplo (segundo enfoque)

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { string inData = serialPort1.ReadLine(); System.Threading.Thread T = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(ProcessData)); T.Start(inData); } public void ProcessData(Object data) { .... }


Normalmente diseño una clase SerialService para administrar SerialPort. A continuación se muestra una versión simple de la clase SerialService.

El rol de la clase SerialService es leer el buffer serial lo más rápido posible. Esto borra el búfer y evita los errores del puerto serie. Estos datos brutos se pasan luego al analizador.

El truco para el rendimiento está en tu analizador. YourParser también debe ser rápido en formatear los datos sin formato en la cadena que está esperando. Una vez que se analizan sus datos, puede usar una devolución de llamada o un evento. Con una devolución de llamada o un evento, su analizador continuará analizando los nuevos datos que llegan. YourParse ahora es una clase comprobable.

Una vez que tenga sus datos correctos de la devolución de llamada del analizador, utilice BeginInvoke para enviar los datos al hilo principal donde su interfaz de usuario puede mostrarlos.

Si no está en el hilo principal de la IU e intenta actualizar la IU de otro hilo, tendrá el problema de cruce.

Buena suerte.

class Program { private static YourDataParser _parser; static void Main(string[] args) { _parser = new YourDataParser(); var serial = new SerialService("COM1"); serial.DataReceived += serial_DataReceived; } static void serial_DataReceived(object sender, DataReceivedEventArgs e) { _parser.HandleTheData(e.Data, good => { // here is your good data // This is not the main thread invoke your UI from here with the good data // Use BeginInvoke to invoke the main thread }); } } public class YourDataParser { private List<byte> _buffer = new List<byte>(); public void HandleTheData(byte[] rawdata, Action<string> goodData) { _buffer.AddRange(rawdata); foreach (var b in _buffer) { var thechar = (char) b; // handle your raw data... like look for the character ''<'' // or look for the end of line this would be CR (0x0D) LF (0x0A) // you can reference the ASCII table for the characters byte values } // and return the good data var data = "your good data after parsing it"; goodData(data); } } public class DataReceivedEventArgs : EventArgs { public DataReceivedEventArgs(byte[] data) { Data = data; } public byte[] Data { get; private set; } } class SerialService { public event EventHandler<DataReceivedEventArgs> DataReceived; private SerialPort _port; public SerialService(string comm) { _port = new SerialPort(comm) { // YOUR OTHER SETTINGS HERE... ReceivedBytesThreshold = 1 // I think is better to increase this number if you know the minimum number of bytes that will arrive at the serial port''s buffer }; // Note that the ReceivedBytesThreshold is set to 1. // That means that the port_DataReceived event will fire with a minimun of 1 byte in the serial buffer _port.DataReceived += port_DataReceived; } void port_DataReceived(object sender, SerialDataReceivedEventArgs e) { if (e.EventType != SerialData.Chars) return; while (_port.IsOpen & _port.BytesToRead != 0) { // important to get all the bytes off the buffer var size = _port.BytesToRead; var buffer = new byte[size]; var sizeRead = _port.Read(buffer, 0, size); OnDataReceived(buffer); } } protected virtual void OnDataReceived(byte[] data) { var ev = DataReceived; if (ev != null) ev(this, new DataReceivedEventArgs(data)); } }