Acerca de:

Este blog contiene los códigos, ejemplos y bases de datos que he usado cuando aprendía acerca de algún tema específico. En lugar de borrarlos (una vez dominado ya el tema), he decidido publicarlos :)

martes, 29 de noviembre de 2022

Accediendo a una base de datos en Visual Basic 6

Este tutorial es viejo viejo, de cuando recién aprendía a hacer conexiones a bases de datos con el VB6 en aquellos fines de los 90s/inicios de los 2000s. Lo dejo aquí como parte de la historia de la informatica.

La coneja no tiene nada que ver con el tutorial, pero creo que es un bonito separador :)

Empezamos:


Sucede que quienes programamos, en algún momento, tenemos que toparnos con las bases de datos queramos o no. Lo malo es que éstas son un mundo muy diferente a los punteros, al manejo de puertos, a la captura de señales o a las llamadas a Apis. Lo bueno es que una vez que se domina lo básico son lo más fácil del mundo. Y lo mejor es que los lenguajes de programación de alto nivel ya vienen con herramientas para manejas bases de datos de todo tipo.

Y eso es lo que quiero mostrar aquí: lo básico para manejar una pequeña base de datos en Access 2002 en el lenguaje de programación más fácil de entender de la Historia: Visual Basic 6.

Antes que nada hay que cargar el componente ADO (Click derecho en la barra de herramientas -> Componentes -> Microsoft ADO Data Control 6.0) y ponerlo en el formulario. Luego se guarda y se borra el componente ADO y, si se quiere, la referencia en la barra de herramientas. Esto es para que el Visual Basic reconozca los objetos Recordset y Connection.

El programa (con su código fuente) se pueden bajar de aquí. 

 


Y ahora una explicación:

Los datos a agregar o eliminar se ingresan en Text1 y Text2. La navegación se hace con los botones Anterior y Siguiente (Command3 y Command4). El botón Limpiar (Command2) es sólo para borrar lo escrito en Text1 y Text2. En el control List1 aparecen todos los registros que coinciden con la búsqueda del texto en Text3 realizada al oprimir "Buscar Todos" (Command7), en cambio el botón "Buscar" (Command5) sólo hace aparecer el primer resultado encontrado.

Para manejar la base de datos no es necesario añadir ningún control especial, basta declarar dos objetos:

Dim rs As Recordset
Dim db As Connection


"db" realiza la conexión con la base de datos y "rs" permitirá recorrer los resgistros, borrarlos, añadirlos y buscarlos. Para conextar con la base de datos (llamada "bd1.mdb") bastan estas líneas:

db.Open "PROVIDER=Microsoft.Jet.OLEDB.4.0;Data Source=" & pathBD & ";"
rs.Open "select * from Datos", db, adOpenDynamic, adLockOptimistic


bd1.mdb sólo tiene dos tablas: "Datos" y "datos2". El programa sólo trabaja con "Datos". El programador debe saber qué tablas y qué campos tiene su base de datos para poder trabajar con ella.

Es importante saber también en qué programa y en qué versión está hecha la base de datos, sino no podrá conectar. Para versiones anteriores de Access se pone:

db.Open "PROVIDER=Microsoft.Jet.OLEDB.3.51;Data Source=" & pathBD & ";"

Aquí hay que observar el primer parámetro que se le pasa a rs.Open: "select * from Datos" esto significa que rs dee recibir todos los registros de la tabla Datos de la base de datos.

"select" es un comando de los Procedimientos Almacenados de las bases de datos. Los Procedimientos Almacenados son órdenes que éstas interpretan y ejecutan y se basan en la lógica booleana. Así, si yo quisiera todos los registros cuyos Nombres coinciden con lo que se ingresa en Text3 (una búsqueda) basta poner:

rs.Close
rs.Open "select * from Datos where Nombre = '" & Text3.Text & "'", db, adOpenDynamic, adLockOptimistic


Para buscar todos los registros que coincidan con Text1 ó con Text2 (Nombre o Apellido) se pondría:

rs.Open "select * from Datos where Nombre = '" & Text1.Text & "' or Apellido = '" & Text2.Text & "'", db, adOpenDynamic, adLockOptimistic


(hay que tener cuidado de que el contenido de Text1 y Text2, o las condiciones de búsqueda si son de tipo String, estén entre comillas simples).

Como rs se vuelve a abrir, es necesario cerrarlo previamente.

Otro comando es "delete", cuando se lo usa el objeto rs se cierra después de la ejecución porllo que ya no es necesario poner rs.Close. Pero para que se pueda seguir navegando entre todos los registros de la tabla Datos se debe vovler a abrir.

rs.Open "delete * from Datos where Nombre = '" & Text1.Text & "' and Apellido = '" & Text2.Text & "'", db, adOpenDynamic, adLockOptimistic


Esta orden borra los registros cuyo Nombre y Apellido coincidan con los textos en Text1 y Text2.

Los procedimientos almacenados permiten realizar muchas cosas en la base de datos. Por ejemplo, imaginando que se tiene una tabla "Empleados" con campos "Edad" que contiene data numérica, "ID" y "Profesion". Luego de hacer las conexiones respectivas, para obtener a todos los ID y las profesiones correspondientes a mayores de edad se puede poner:

select ID, Profesion from Empleados where Edad > 18

Para agregar un registro basta una función propia de rs: AddNew y luego especificar los campos con la función "Fields" a donde se añadirán los nuevos datos. Al recorrer los registros de atrás o hacia adelante (propiedades MoveNext, MovePrevious, MoveLast y MoveFirst), o al hacer una búsqueda, "Fields" se coloca automáticamente en el registro actual.

Las propiedades de rs: EOF y BOF indican si se está al final o al principio de la tabla Datos. Mientras no se lo esté, sus valores son False. Tanto las búsquedas como los recorridos y los borrados de los registros en las tablas de la base de datos deben hacerse mientras EOF y BOF son False, si no nos "saldremos" de la base de datos y nos botará error.

Si en una búsqueda no se encuentra nada, EOF y BOF serán True.

 

miércoles, 5 de octubre de 2022

La secuencia de Van Eck en Python (y un poco acerca de la palabra reservada yield)

Buscando tutoriales ya no recuerdo de qué, me encontré con esto Van Eck Sequence Using Python - Roy Tutorials (roytuts.com)

¡La Secuencia de Van Eck! Este link también la menciona:

The Van Eck Sequence | IB Maths Resources from Intermathematics 

Y ambos la explican de una forma horrible. En serio, a partir de esas explicaciones tan cutres intenté hacer mi propio algoritmo (sin mirar los de otros) y fue horrible. Tuve que ir a esta otra web Van Eck's sequence - EverybodyWiki Bios & Wiki y jugar un poco frustrarme dos horas armando mi propio código para hallar la verdadera explicación de cómo generar la secuencia de Van Eck:

1. Empieza con cero. En este ejercicio vamos a empezar siempre de cero.

2. Coge el último número de la secuencia.

3. Si ese número no se halla al menos dos veces en la secuencia, añade cero al final. Si se halla al menos dos veces, coge las dos últimas posiciones, réstalas, y el resultado se añade al final de la secuencia.

El código que me salió es:

vanEckSeq = [0]

for _ in range(0, 20):
    valor = vanEckSeq[-1]    
   
    if vanEckSeq.count(valor) < 2:
        vanEckSeq.append(0)
    else:
        #extraer últimos dos indices
        vanEckSeq0 = [c[0] for c in enumerate(vanEckSeq) if c[1] == valor]
               
        index1 = vanEckSeq0[-1]
        index2 = vanEckSeq0[-2]
       
        vanEckSeq.append(index1 - index2)
       

print(vanEckSeq)


No está mal porque una de las condiciones que me impuse es que no podía usar bucles anidados, es decir nada de:

for _ in range(0, 20):
    for _ in range(0, whatever): 
 
Y además usa una función muy bonita de Python: enumerate que devuelve una lista de tuplas cuyos elementos son (posición del elemento en la lista, elemento de la lista).
Mi código se parece al del primer link, pero en el primer link hace algo que no me gusta: evaluar y usar todos los números naturales hasta un límite determinado, cuando en la definición de la secuencia de Van Eck se usan los valores que ya posee la secuencia.

Mi código además permite hacer cosas como esta:


def VanEck():
    vanEckSeq = [0]

    while(True):
        valor = vanEckSeq[-1]
       
        if vanEckSeq.count(valor) < 2:
            vanEckSeq.append(0)
        else:
            #extraer últimos dos indices
            vanEckSeq0 = [c[0] for c in enumerate(vanEckSeq) if c[1] == valor]
                   
            index1 = vanEckSeq0[-1]
            index2 = vanEckSeq0[-2]
           
            vanEckSeq.append(index1 - index2)
           
        yield vanEckSeq[-1]
   
   
generator = VanEck()
   
for _ in range (0, 20):
    print (next(generator))

# ... some code....
print (next(generator))
# ... some other half million lines of code....
print (next(generator))


Puedo generar una función que devuelve el último valor de la secuencia de forma indefinida. En casi todos los tutoriales que he revisado del uso de la palabra resevada yield se usa un bucle para mostrar cómo funciona. Esto no demuetra el poder de yield, el cual permite obtener un generador el cual devolverá el siguiente valor de la operación cuando se necesite, no es necesario que esté dentro de un bucle. yield permite que Python guarde el estado de todas las variables dentro de la función (que en este caso devuelve un generador), para que estén listas para la siguiente llamada next() del generador.

Codifiqué esto en Python 3.9 con Visual Code en Windows 10.

jueves, 9 de junio de 2022

Error "Clase no registrada" al usar Microsoft.Speech en Visual Studio 2019 y Windows 10

Un día quise jugar con las librerías de Microsoft Speech para hacer hablar a mi laptop. Las descargas fueron las siguientes:

Microsoft Speech Platform Runtime. Descargué el instalador de 64 y 32 bits, bastan los instaladores de la plataforma en la que se compila la aplicación, pero por el error que explicaré más abajo tuve que probar ambos:

https://www.microsoft.com/en-us/download/details.aspx?id=27225

Microsoft Speech Platform Runtime Languages:

https://www.microsoft.com/en-us/download/details.aspx?id=27224

Los paquetes para reconocimiento de audio son los que dicen TELE, los paquetes para convertir texto a voz son los que tienen nombres de personas. Yo descargué los paquetes de español México y español España. En total fueron 4 paquetes de idioma.

Lo siguiente que hice fue crear un proyecto de C# con .Net Framework. Referencié el archivo Microsoft.Speech.dll en mi proyecto:

Para 64 bits, la dll está en C:\Program Files\Microsoft SDKs\Speech\v11.0\Assembly

Para 32 bits, está en C:\Program Files (x86)\Microsoft SDKs\Speech\v11.0\Assembly

Finalmente copié y pequé un poco del código de esta web: https://docs.microsoft.com/en-us/archive/msdn-magazine/2014/december/voice-recognition-speech-recognition-with-net-desktop-applications

        static void Main(string[] args)
        {
            try
            {
                ss.SetOutputToDefaultAudioDevice();
                Console.WriteLine("\n(Speaking: I am awake)");
                ss.Speak("He despertado");
                CultureInfo ci = new CultureInfo("es-PE");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.ReadLine();
            }
        }

Y apenas empezar el debugueo me saltó este error:

Retrieving the COM class factory for component with CLSID {D941651C-44E6-4C17-BADF-C36826FC3424} failed due to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).

Luego de intentar varias soluciones: cambiar a 32 bits, cambiar a .Net Framework 4, iniciar Visual Studio como administrador, lo único que funcionó fue lo propuesto en este foro


En 64 bits el instalador está en C:\Program Files\Microsoft SDKs\Speech\v11.0\Redist

En 32 bits está en C:\Program Files (x86)\Microsoft SDKs\Speech\v11.0\Redist

Al ejecutar los instaladores se pueden ver los mensajes de que se están registrando las dlls.

Lugo volví a referenciar la dll de 64 bits, cambié a .Net Framework 4.5, puse que se trata de una aplicación de 64 bits, y probé.

Funcionó, la aplicación puede decir "He despertado" con la voz de una mujer (apodada Hilda).

Ya puedo ponerle voz a mis aplicaciones de C# :)

jueves, 28 de abril de 2022

"Curlicue Fractals" con Visual C#

Otra entrada migrada de la vieja web, la cual ha perecido con dignidad. De a pocos voy a ir copiando mis viejos tutoriales a este blog, ¡hay que tener paciencia!

Rod Stephens, autor de http://csharphelper.com/ tenía un tutorial sobre cómo dibujar estos fractales, pero él también ha tenido problemas con su web y el tutorial ya no está disponible. Voy a poner aquí lo que yo tengo, mi código está basado en el de Rod, así que los créditos van para él.

Al revisar la documentación en http://mathworld.wolfram.com/CurlicueFractal.html noté que sólo menciona dos ecuaciones cuando en el código fuente de Rod (ya no disponible) se usan cuatro:


Al seguir examinando se puede ver que las ecuaciones de la documentación de Mathworld generan ángulos, y lo que se dibujan son líneas que necesitan las coordenadas de los puntos inicial y final. 

La traducción del algoritmo es la siguiente: Se tiene una línea de longitud 1, paralela al eje x. Se calcula un número theta con la ecuación, luego, con la ecuación se calcula el nuevo ángulo de la siguiente línea de longitud 1, cuyo punto inicial será el extremo final de la línea anterior.
Al final resulta un ángulo. Su módulo es 1. Para calcular las coordenadas se multiplica por el coseno (coordenada x) y el seno (coordenada y).


Mi programa está basado en el algoritmo de Rob, pero con algunas cosas añadidas, como zoom y poder posicionar el fractal, además emplea el objeto Graphics de una forma diferente para dibujar el fractal, también añadí un número aleatorio para jugar con los colores:


El fractal se genera sólo a partir de un número irracional. Si se utiliza un número racional sólo aparecerá una línea horizontal.

La aplicación se puede descargar de aquí (para Visual C# 2008).

viernes, 25 de marzo de 2022

Dibujando arbolitos fractales con la tortuga de Python

Éste fue un pequeño ejercicio para practicar recursividad. La idea es dibujar un arbolito fractal con tres ramitas. Ya antes había revisado el código para dibujar estos árboles, pero quise tener mi propia implementación. Después de un buen rato probando, dibujando en un papel cómo debían salir las ramitas, y dónde debía poner la bifurcación recursiva, terminé con este código:

import turtle as t

def drawit(x, y, angle, lenght, n):
    if n > 0:
        t.penup()
        t.goto(x, y)
        t.pendown()
        t.seth(angle)
        t.forward(lenght)
        cx = t.xcor()
        cy = t.ycor()
        drawit(cx, cy, angle + 30, lenght / 1.3, n-1)
        drawit(cx, cy, angle - 30, lenght / 1.3, n-1)
        drawit(cx, cy, angle, lenght / 1.3, n-1)
    else:
        t.hideturtle()
        return 0


drawit(0, -100, 90, 100, 7)
t.mainloop()


la variable n indica la profundidad hasta la que se dibujarán las ramas, otra forma es dibujar las ramas hasta que llegan a una longitud mínima. Las variables x, y, angle, lenght indican las coordenadas desde dónde se dibujarán las ramas, el ángulo y la longitud respectivamente. La función seth() de la tortuga setea el ángulo en el que se empezará a dibujar. Las funciones xcor() y ycor() capturan las coordenadas actuales de la tortuga.

La tortuga primero dibuja el tronco del árbol en la posición (0, -100) en un ángulo de 90 grados. A continuación se bifurca en tres ramas, cada una dibujando un "tronco" en el ángulo y la longitud indicadas a partir de las coordenadas donde la tortuga terminó de dibujar. Para evitar que se confunda, le ordeno ir a esas coordenadas, antes de empezar a dibujar, con la función goto().

El resultado es:


En esta web también dibujan un árbol fractal, y además le ponen color, voy a usar el mismo truco para colorear mi árbol:

import turtle as t
import math

def drawit(x, y, angle, lenght, n):
    if n > 0:
        t.penup()
        t.goto(x, y)
        t.pendown()
        t.seth(angle)

        t.pensize(math.log(lenght,2)/3)
        if n < 2:
            t.color('forest green')
        else:
            t.color('gray')

        t.forward(lenght)
        cx = t.xcor()
        cy = t.ycor()
        drawit(cx, cy, angle + 30, lenght / 1.3, n-1)
        drawit(cx, cy, angle - 30, lenght / 1.3, n-1)
        drawit(cx, cy, angle, lenght / 1.3, n-1)
    else:
        t.hideturtle()
        return 0


drawit(0, -100, 90, 100, 7)
t.mainloop()

Ahora mi árbol ya no parece muerto:


Luego me encontré con este otro arbolito, cuyo código es muy parecido al mío. En lugar de un contador, usa la longitud de las ramas para ponerle un límite a la profundidad de las recursiones. No fue difícil modificar mi código para dibujar un arbolito curvo, y después de probar un poco noté que en este caso es mejor usar una longitud mínima en lugar de la variable n para detener las recursiones. Al final, mi código es el siguiente:

import turtle as t

def drawit(x, y, angle, lenght, n):
    if lenght > 2:
        t.penup()
        t.goto(x, y)
        t.pendown()
        t.seth(angle)
        t.forward(lenght)
        cx = t.xcor()
        cy = t.ycor()
        drawit(cx, cy, angle + 25, lenght * 0.9, n-1)
        drawit(cx, cy, angle - 90, lenght * 0.45, n-1)
    else:
        t.hideturtle()
        return 0


drawit(200, -200, 90, 100, 10)
t.mainloop()


El resultado es:

Necesita algunos arreglos:

import turtle as t

def drawit(x, y, angle, lenght, n):
    if lenght > 2:
        t.penup()
        t.goto(x, y)

        if lenght < 10:
            t.color('gray')
        else:
            t.color('black')

        t.pensize(lenght/15)
        t.pendown()
        t.seth(angle)
        t.forward(lenght)
        cx = t.xcor()
        cy = t.ycor()
        drawit(cx, cy, angle + 25, lenght * 0.9, n-1)
        drawit(cx, cy, angle - 90, lenght * 0.45, n-1)
    else:
        t.hideturtle()
        return 0


drawit(180, -200, 90, 100, 10)
t.mainloop()


Finalmente queda así:

miércoles, 9 de marzo de 2022

Instalando Glassfish como servicio de Windows (versiones 4.1 y 5)

Estos días han sido días de Glassfish en mi laptop. Jugando con las versiones 4.1 y 5, y rebuscando en internet, encontré que se puede instalar como un servicio de Windows. Como mis Glassfish son todos para pruebas, trasteo, más trasteo y depuración, mi dominio es el que se usa por defecto: domain1. Los puertos son también los que vienen por defecto: 4848 para la consola de administración y 8080 para la conexión http.

Dos buenos tutoriales han sido:

https://www.asesoriaensig.com.mx/instalar-glassfish-4-como-servicio-en-windows-2008/

https://www.luv2code.com/2013/11/13/install-glassfish-4-as-a-windows-service/

Lo que me sucedió es que con el comando asadmin start-domain me inicia el glassfish pero después de un rato tiende a detenerse. Este comando es para pruebas o depuración, no para producción.

Para producción en Windows, el Glassfish debe arrancarse como servicio. Para eso existe este comandito el cual debe ejecutarse como Administrador o pasará lo que se menciona aquí:

asadmin create-service

Lo que no ponen los tutoriales es lo siguiente: tengo un millón de servicios en Windows ¿cómo ubico el de Glassfish? Bueno, en Windows se crea como nombre-del-dominio Glassfish Server:

Lo malo: este servicio funciona, Glassfish 5 se ejecuta en background, pero Windows 10 no lo administra bien, reinicié mi laptop y el servicio estaba detenido así que tuve que iniciarlo de forma manual, además la información de si se está ejecutando o no no se muestra correctamente. Esto no me ocurrió con Glassfsih 4.1 corriendo en Windows Server 2013.

Volviendo a mi Glassfish 5 corriendo en Windows 10, no me permitía depurar webservices en Netbeans apuntando a ese mismo Glassfish 5. Como sólo es un Glassfish de pruebas, lo mejor en este caso es eliminar el servicio.

Deshabilité el servicio, ejecuté el Windows Powershell como administrador e intenté eliminar el servicio de glassfish con el comando:

sc domain1

No se eliminó, luego intenté con el comando:

sc delete "domain1 GlassFish Server"

Y tampoco se eliminó. Busqué en internet que estaba pasando y en dos segundos hallé la solución. El comando es:

sc.exe delete "domain1"

Lo que debe mostrar al ejecutarse es:


Luego se deben borrar los archivos que deja el servicio en esta ruta:

glassfish5\glassfish\domains\domain1\bin

Lo malo es que eliminar el servicio de glassfish no detiene los procesos de glassfish, para esto debemos ir al Task Manager (taskmgr.msc), hacer clic derecho en las cabeceras de las columnas y elegir "Línea de comandos". Luego se deben matar todos los procesos de Java que tengan "glassfish" en algún lado de su línea de comandos:

Suele haber más de uno, hay que matarlos todos. Después de hacer todo esto, ya pude debuggear mis webservices desde Netbeans usando Glassfish 5.

Por otro lado, el ejecutable asadmin está en esta ruta:

C:\glassfish5\glassfish\bin

miércoles, 9 de febrero de 2022

Arreglando el Error: "No appropriate protocol (protocol is disabled or cipher suites are inappropriate)" en Jdk 1.8.0_301

 Quise conectarme desde una aplicación chiquita de java a una base de datos en Sql Server 2017 via el driver jdbc sqljdbc4-2.0.jar. La aplicación la estaba depurando en Netbeans 11.1, corriendo en Windows 10. Todo estaba bien hasta que actualicé a la versión del Jdk a la 1.8.0_301, entonces en la línea:

Connection connection = DriverManager.getConnection(connectionString);

me arrojó esta excepción:

El controlador no pudo establecer una conexión segura con SQL Server con el cifrado de Capa de sockets seguros (SSL). Error: "No appropriate protocol (protocol is disabled or cipher suites are inappropriate)"

En inglés:

The driver could not establish a secure connection to SQL Server by using Secure Sockets Layer (SSL) encryption. Error: “No appropriate protocol (protocol is disabled or cipher suites are inappropriate)”.

Lueo de rebuscar en foros raruchos, hallé en éste la solución:


Es hora de ir a buscar ese archivo. Primero debo cerrar todas las aplicaciones de java: jars y Netbeans incluídos. En mi caso el archivo a modificar está en C:\Program Files\Java\jdk1.8.0_301\jre\lib\security\java.security. Las líneas que debo comentar son las 723, 724 y 725:


Debe quedar así:


En mi caso, basta volver a arrancar las aplicaciones de java (Netbeans incluídos) para resolver el problema y poder conectarme a mi base de datos. Puede ser que en algunos casos sea necesario reiniciar el sistema operativo.