Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   .NET (https://www.clubdelphi.com/foros/forumdisplay.php?f=17)
-   -   ERROR: Referencia a objeto no establecida como instancia de un objeto. (https://www.clubdelphi.com/foros/showthread.php?t=96534)

REHome 06-12-2023 22:36:20

ERROR: Referencia a objeto no establecida como instancia de un objeto.
 
Hola:

En este código me sale un error y no se como corregirlo, ya que me interesa mostrar datos en pantalla y no me sale.

Ver error.

Código hecho hasta ahora.
Código:

using System;
using System.IO.Ports;

namespace Almacenar_byte_puerto_serie_Consola_01
{
    internal class Program
    {
        static SerialPort puertoSerie;
        static byte[] datoRecibido;
        static void Main(string[] args)
        {
            #region Configuración ventana.
            // Título de la ventana.
            Console.Title = "Almacenar byte[] desde el puerto serie.";

            // Tamaño de la ventana, x, y, o ancho y alto.
            const byte ANCHO_X = 70, ALTO_Y = 25;
            Console.SetWindowSize(ANCHO_X, ALTO_Y);

            // Color de fondo.
            Console.BackgroundColor = ConsoleColor.Black;

            // Color de las letras.
            Console.ForegroundColor = ConsoleColor.Yellow;

            // Limpiar pantalla y dejarlo todo en color de fondo.
            Console.Clear();

            // Visible el cursor.
            Console.CursorVisible = true;
            #endregion

            puertoSerie = new SerialPort()
            {
                // Configuración del puerto serie.
                PortName = "COM4",          // Nombre del puerto serie.
                BaudRate = 2400,            // Velocidad en baudios.
                Parity = Parity.None,        // Esquema para comprobar la paridad de cada byte recibido.
                StopBits = StopBits.One,    // Número de bits de parada por byte.
                DataBits = 8,                // Número de bits de datos por byte.
                Handshake = Handshake.None,  // Protocolo de establecimiento.
                DtrEnable = true,            // Línea de terminal de datos.
                RtsEnable = true,            // Línea de solicitud.

                // Establecer los tiempos de espera de lectura / escritura.
                ReadTimeout = 500,          // Tiempo de espera de escritura en ms.
                WriteTimeout = 500,          // Tiempo de espera de escritura en ms.

                // Más configuraciones.
                DiscardNull = false,        // Descartar bytes nulos recibidos.
                ParityReplace = 63,          // Reemplaza los bytes recibidos con errores de paridad.
                ReadBufferSize = 4096,      // Tamaño del búfer de lectura en bytes.
                WriteBufferSize = 2018,      // Tamaño del búfer de escritura en bytes.
                ReceivedBytesThreshold = 1  // Número de bytes que se necesitan.
            };

            puertoSerie.DataReceived += SerialPort_DataReceived;
            puertoSerie.Open();

            Console.WriteLine("Presiona cualquier tecla para detener la captura...");
            Console.ReadKey();

            puertoSerie.Close();

            Console.WriteLine("Datos recibidos:");
            MostrarBytes(datoRecibido);
        }
        static void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPort sp = (SerialPort)sender;
            int bytesToRead = sp.BytesToRead;
            byte[] buffer = new byte[bytesToRead];
            sp.Read(buffer, 0, bytesToRead);

            if (datoRecibido == null)
            {
                datoRecibido = buffer;
            }
            else
            {
                Array.Resize(ref datoRecibido, datoRecibido.Length + bytesToRead);
                Array.Copy(buffer, 0, datoRecibido, datoRecibido.Length - bytesToRead, bytesToRead);
            }
        }

        static void MostrarBytes(byte[] dato)
        {
            for (int i = 0; i < dato.Length; i++)
            {
                Console.Write($"{dato[i]} ");
            }
            Console.WriteLine();
        }
    }
}

Se trata de obtener bytes puro y duro desde el RS232 o puerto serie, almacenarlo en un array tipo byte[] y mostrarlo en pantalla.

¿Alguna idea?

Saludos.

delphi.com.ar 07-12-2023 03:13:06

Son errores distintos el del título del hilo y el de la captura de pantalla. En la captura se ve que estás accediendo a un elemento del array "numeros", por el índice almacenado en la variable "contador", y este contador esta fuera del intervalo del array. O sea es menor que el mínimo o mayor al máximo. Fíjate en la captura que "numeros" tiene una dimensión de 6, y dado que las posiciones son en base cero, estas van de 0 a 5. Por otro lado la variable "contador" tiene el valor 6, o sea está intentando acceder al séptimo elemento de un array de seis.

Por otro lado veo seguidos tus posts, y espero que no suene mal lo que digo, pero creo que debes aprender las bases del lenguaje que estás usando. Me parece que te estás peleando con lo más básico sin poder abstraer el problema. Te recomiendo ver algún curso del lenguaje online, que sin lugar a dudas te hará ahorrar mucho tiempo. Lo primero que me aparece al buscar "C# básico": https://www.youtube.com/watch?v=6W2wYwHQNT4

Saludos!

REHome 07-12-2023 23:14:03

Gracias por el vídeo.

Y... Perdón., me equivoqué de enlace sobre la imagen que en realidad es esta.



Saludos.

delphi.com.ar 08-12-2023 01:22:47

Ok, según esa imagen dato es NULL, o sea no tiene un valor asignado, por ese motivo el NullReferenceException.
No puedes acceder a propiedades de objetos no asignados, es como averiguar la edad de un perro que no existe.

El error seguramente está en la llamada a la función, y suele ser correcto que la función falle, pero deberías evaluar la asignación del parámetro y fallar con NullArgumentException detallando el parámetro no asignado.

Saludos.

tsk 08-12-2023 01:44:06

Me da la impresión de que tu función SerialPort_DataReceived no se está ejecutando. Esa función, se ejecuta en un Thread aparte, y en tu código no veo el "using System.Threading;" después de "using System.IO.Ports;"

REHome 08-12-2023 07:43:00

Buenas:

El programa se trata de que si le entra datos desde el puerto serie, que sea natural y almacenarlo en una variable tipo Byte[].

Luego mostrarlo en pantalla.

Hablando lo del Thread pongo un ejemplo en un mini chat que si lo usa, me imagino que te refieres a esto:
Código:

using System;
using System.IO;
using System.IO.Ports;
using System.Text;
using System.Threading;

namespace Chat_Consola_05
{
    class Program
    {
        static bool _continua;
        static SerialPort Puerto_serie;

        static void Main(string[] args)
        {
            const int MAXIMA_LONGITUD = 40000;
            string COM = "";
            string nombre;
            string mensaje;
            string titulo = "Mini chat C# " + typeof(string).Assembly.ImageRuntimeVersion;

            StringComparer comparaString = StringComparer.OrdinalIgnoreCase;
            Thread readThread = new Thread(Leer);

            #region Configuración ventana.
            // Título de la ventana.
            Console.Title = titulo;

            // Tamaño de la ventana, x, y, o ancho y alto.
            const byte ANCHO_X = 70, ALTO_Y = 25;
            Console.SetWindowSize(ANCHO_X, ALTO_Y);

            // Color de fondo.
            Console.BackgroundColor = ConsoleColor.Green;

            // Color de las letras.
            Console.ForegroundColor = ConsoleColor.Black;

            // Limpiar pantalla y dejarlo todo en color de fondo.
            Console.Clear();

            // Visible el cursor.
            Console.CursorVisible = true;
            #endregion

            #region Configuración puerto serie.
            // Crear un nuevo objeto SerialPort con la configuración predeterminada.
            Puerto_serie = new SerialPort();

            // Codificar a UTF-8 para que se vean bien las tildes, ñ y otros caracteres.
            Puerto_serie.Encoding = Encoding.UTF8;

            // Obtenga una lista de nombres de puertos serie.
            string[] puertos = SerialPort.GetPortNames();

            Console.WriteLine("Se encontraron los siguientes puertos series:");

            // Muestre el nombre de cada puerto en la consola.
            foreach (string puerto in puertos)
            {
                Console.WriteLine(puerto);
            }

            // Configuración.
            Console.Write(@"
Introduzca un número para seleccionar puerto COM.
Por ejemplo el 4, sería COM4.

Puerto: ");
            COM = Console.ReadLine(); // Escribir el número del puerto.
            Console.Clear(); // Limpiar pantalla.

            Puerto_serie.PortName = "COM" + COM; // Número del puerto serie.

            // Configuración del puerto serie.
            Puerto_serie.BaudRate = 2400;    // 115200
            Puerto_serie.Parity = Parity.None;
            Puerto_serie.StopBits = StopBits.One;
            Puerto_serie.DataBits = 8;
            Puerto_serie.Handshake = Handshake.None;
            Puerto_serie.DtrEnable = true;
            Puerto_serie.RtsEnable = true;

            // Establecer los tiempos de espera de lectura / escritura.
            Puerto_serie.ReadTimeout = 500; // 500 Milisegundos.
            Puerto_serie.WriteTimeout = 500; // 500

            // Más configuraciones.
            Puerto_serie.DiscardNull = false;
            Puerto_serie.ParityReplace = 63;
            Puerto_serie.ReadBufferSize = 4096;
            Puerto_serie.WriteBufferSize = 2018;
            Puerto_serie.ReceivedBytesThreshold = 1;

            // Comprueba si puede abrir el puerto serie.
            try
            {
                Puerto_serie.Open(); // Abrir el puerto serie.
            }

            // En caso que diera algún error como que no encuentra el puerto seleccionado
            // muestra una excepción.
            catch (IOException)
            {
                Console.ForegroundColor = ConsoleColor.Red; // Texto en rojo.
                Console.CursorVisible = false;
                Console.SetCursorPosition(16, 6);
                Console.WriteLine(@"El puerto " + Puerto_serie.PortName + @" no existe
                o no lo encuentra.");
                Console.ReadKey();  // Pulse cualquier tecla.
                Environment.Exit(1); // Salir de la aplicación.
            }

            // Se ha denegado el acceso al puerto.
            catch (UnauthorizedAccessException)
            {
                Console.ForegroundColor = ConsoleColor.Red; // Texto en rojo.
                Console.CursorVisible = false;
                Console.SetCursorPosition(16, 6);
                Console.WriteLine(@"Se ha denegado el acceso al puerto " + Puerto_serie.PortName +
                                "" +
                                "\nPuede estar el puerto escogido en uso.\n" +
                                "Elija un puerto diferente o desactiva el que está en uso.");
                Console.ReadKey();  // Pulse cualquier tecla.
                Environment.Exit(1); // Salir de la aplicación.
            }
            #endregion

            _continua = true;
            readThread.Start();

            // Mostrar texto Nombre y se
            Console.Write("Nombre: ");

            // guarda en la variable nombre.
            nombre = Console.ReadLine();

            // Se muestra el nombre o nick y el puerto seleccionado al final del título de la ventana.
            Console.Title = titulo + "- Nick: " + nombre + " - COM: " + COM;

            Console.WriteLine("Escriba /salir para salir.");

            while (_continua)
            {
                // Cualquier caracter recibido se guarda en la variable mensaje.
                //mensaje = Console.ReadLine();

                #region Enviar más de 255 caracteres.
                // #########################################################################
                Stream entradaDeDatos = Console.OpenStandardInput();
                byte[] buffer = new byte[MAXIMA_LONGITUD];
                int numerosBytesLeidos = entradaDeDatos.Read(buffer, 0, MAXIMA_LONGITUD);

                char[] chars = Console.InputEncoding.GetChars(buffer, 0, numerosBytesLeidos);
                mensaje = new string(chars);
                // #########################################################################
                #endregion

                // Compara /salir con el mensaje /salir si lo haz escrito igual.
                // ¿Escribiste la palabra /salir?
                if (comparaString.Equals("/salir\r\n", mensaje))
                {
                    // Sí. Entonces, pone esta variable _continue en false.
                    _continua = false;
                }
                // No. Entonces, envía por el puerto serie tu nick
                // y mensaje que haz escrito.
                else
                {
                    Puerto_serie.WriteLine(String.Format("<{0}>: {1}", nombre, mensaje));
                }
            }

            // Bloquea el subproceso.
            readThread.Join();

            // Cierra el puerto serie.
            Puerto_serie.Close();
        }

        // Lee mensaje recibido.
        public static void Leer()
        {
            // Si _continua es true se ejecuta todas las instrucciones dentro de while.
            while (_continua)
            {
                try
                {
                    // Almacena en la variable mensaje cualquier caracter o mensaje recibido.
                    string mensaje = Puerto_serie.ReadLine();

                    // Muestra en pantalla mensaje recibido.
                    Console.WriteLine(mensaje);
                }
                catch (TimeoutException) { }
            }
        }
    }
}

Saludos.

delphi.com.ar 08-12-2023 20:01:24

Insisto, debes abstraer los problemas de la lógica general del programa. Si miras el código como todo uno, no podrás llegar a desarrollar algo extenso, la programación se trata de dividir un problema, en muchos problemas pequeños.

Viendo tu código (el del inicio del post, este último no se que es), abres el puerto en la línea 58, y en la línea 66 llamas a MostrarBytes, sin ningúna garantía de que el buffer datoRecibido haya recibido datos. Simplemente si no tenías nada conectado en el puerto serie, el código producirá inevitablemente un NullReferenceException. Y si hubiera un dispositivo conectado, puede suceder una condición de carrera donde primero evalúas el buffer y luego recibes los datos.

Una simple solución puede ser tener la variable datoRecibido inicializada con un array vacío a la espera de datos, total la estas extendiendo cada vez que recibes datos. Pese a esto que te digo, lo único que lograrás es no generar excepciones, dado que el programa fallará pq no resultará lo que espera el usuario.

Saludos.

REHome 09-12-2023 09:31:16

El código del primer post no es mio en absoluto, lo saqué en Internet pero no funciona. Como nadie responde en esa Web, lo pregunto aquí.

Y si, hay que resolver partes por partes.

La idea es, que el puerto abre cuando inicio el programa, a la espera que lleguen datos por el puerto serie cuando desde el otro lado envíe tramas de bytes, se almacene en un arrray y lo muestre en pantalla.


La franja horaria es GMT +2. Ahora son las 08:05:42.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi