lunes, 1 de marzo de 2021

Cliente de Web Services con JAXWS

Para poder integrar cualquier sistema hecho en JAVA con un Web Services lo mejor que he encontrado es usando JAXWS, ya que tiene una forma muy facil de interpretar los servicios y las estructuras de datos que utilizan los mismos. Tambien te genera todo el codigo necesario para conectarte al WS desde el Wsdl del mismo. La forma de configurarlo es la siguiente:

Para ello primero debemos colocar la dependencia de maven dentro del pom.xml:

        <!-- https://mvnrepository.com/artifact/com.sun.xml.ws/jaxws-rt -->
        <dependency>
            <groupId>com.sun.xml.ws</groupId>
            <artifactId>jaxws-rt</artifactId>
            <version>2.2.10</version>
        </dependency>

Luego debemos configurar el plugin de jaxws para que se encargue de generar las clases y estructura de datos para que puedan conectarse facilmente a los WebServices.

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>jaxws-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>wsimport</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <packageName>{package}</packageName>
                    <sourceDestDir>{path_folder}</sourceDestDir>
                    <keep>true</keep>
                    <wsdlUrls>{url_ws}</wsdlUrls>
                </configuration>
            </plugin>
        </plugins>

  • En {package} es el paquete en java que va a contener las clases que autogenere el plugin una vez que se ejecutar.
  • En {path_folder} es el directorio donde va a generar el paquete con las clases, por lo general se pone src/main/java.
  • En {url_wa} es la direccion url que contiene el wsdl
  • En la propiedad keep esta en true para que se mantenga generado los archivos y no los borre luego de la ejecucion.
El proceso de maven generate-sources es el que se encarga de crear las clases que manejan los llamados y estructuras de datos de los web services.

Aqui pongo un ejemplo de como seria el llamado al web services desde java:

package com.package.client;

import java.net.URL;
import com.package.generated.ws.Informacion;
import com.package.generated.ws.WebServiceGenerated;
import com.package.generated.ws.WebServiceGeneratedSoap;

public class HelloWorldClient{
    
    public static void main(String[] args) throws Exception {
       
        String apiKey = "Esto es una prueba";
        
        WebServiceGenerated unWebServiceService = new WebServiceGenerated(new URL(urlWebServices));
        WebServiceGeneratedSoap unWebServiceCall = unWebServiceService.getWebServiceGeneratedSoap();

        Informacion result = unWebServiceCall.obtenerInformacion(apiKey);
        System.out.println(result.getReporte().size());
    }

}

En este ejemplo se utilizan dos clases autogeneradas denominadas WebServiceGenerated  WebServiceGeneratedSoap, en la ultima clase contiene los nombres de los nombres de los servicios definidos dentro del wsdl (ejemplo usado: obtenerInformacion). La clase Informacion tambien es autogenerada y es la estructura de datos armada para la respuesta del WebServices. 


viernes, 11 de septiembre de 2020

Mejorando la performance de la JVM en un contenedor

 Para poder configurar la JVM dentro de un contenedor para que este pueda escalar de manera correcta es necesario poder tomar memoria y liberar memoria de forma correcta.

Para ello tendremos que hacer cierta configuraciones que serán pasadas en parámetros al ejecutar la aplicación. 

Primer paso

A partir de la versión 10 de Java es necesario utilizar el parámetro -XX:+UseContainerSupport para indicarle a la JVM que esta ejecutándose dentro de un contenedor Docker o algún otro contenedor.

También es necesario definir que cantidad de memoria que se utiliza dentro del contenedor y para esto lo mejor es definir un porcentaje por si se le llega asignar mas memoria a la RAM, la JVM pueda tener mas memoria disponible para tomar. Para ello se utiliza el parámetro -XX:MaxRAMPercentage y se le pasa el porcentaje que toma la JVM. Ejemplo:

-XX:MaxRAMPercentage=80

Liberar memoria RAM dentro del contenedor

Una de las mejores herramientas que tienen los gestor de contenedores como (Kubernetes, etc.) es que pueden crear contenedores automáticamente en paralelo para que estos puedan tomar toda la carga tanto de trafico como de procesamiento de los Request en el caso de una API por ejemplo. Hay varias formas de escalar contenedores ya sea por superar un cierto porcentaje de la RAM y/o del uso de CPU. 

Para el caso de la RAM es necesario de que la JVM libere la memoria para que luego de que baje el trafico o el procesamiento el gestor de contenedores pueda dar de baja los contenedores que se generaron para hacer de soporte a los pedidos.

Dependiendo de que versión tengamos de la JVM utilicemos deberemos pasar parámetros o no. Si tenemos la versión 8 de Java el Garbage Collector que utilice es el ParallelGC y en Java 11 el default el Garbage Collector G1. Lo que se recomienda para liberar memoria es que utilices el default de Java 8 y si estas en Java 11 hagas el cambio de GC, con el siguiente parámetro -XX:+UseParallelGC

Ademas de configurar el margen de memoria libre dentro de la memoria Heap de Java utilizando los siguiente parámetros:

-XX:MinHeapFreeRatio=10
-XX:MaxHeapFreeRatio=10


Ejemplo

En el ejemplo siguiente se utilizo los parámetros dentro de una JVM 11:

-XX:MaxRAMPercentage=80
-XX:MinHeapFreeRatio=10
-XX:MaxHeapFreeRatio=10

Primero no se selecciono ningún GC, lo cual se utilizo el default de la JVM 11 que es el GC1 y el resultado es el siguiente 

Prueba con GC1

Como se ve la memoria Heap de Java va tomando memoria a medida que la necesita pero luego de ejecutar los requests libera memoria dentro del Heap pero no libera memoria al Sistema Operativo. 

Por otro lado vamos a agregar el parámetro -XX:+UseParallelGC para ver el comportamiento del mismo procesamiento anterior y como va tomando y liberando memoria. 

Prueba con GC Parallel

Ahora podemos observar que efectivamente a medida que no necesita RAM la va liberando del Heap y también la va liberando al Sistema Operativo. Siempre manteniendo tomada un 10% de la memoria.


Conclusión

Si llevamos a cabo las configuraciones recomendadas podemos optimizar el uso de contenedores dentro del Gestor de contenedores (Ej, Kubernetes) y liberar memoria dentro de los contenedores para avisarle al Gestor que ya puede eliminar los contenedores ya que bajo la carga de procesamiento.

lunes, 17 de agosto de 2020

Programación MotorShield Python

 Voy a explicar como fue la programacón de los movimientos del auto. El control desde la Raspberry al MotorShield y a su vez a los cuatro motores DC. 

Primero debemos tener instalado algunas de las versiones de Python 2 o 3. 

Luego es necesario descargar el siguiente codigo, ya que luego el programa va a importar el mismo:


AMSpi.py debe estar en la misma carpeta que el código que vamos a hacer.

La libreria anteriormente descargada nos va a servir mucho para tener varias herramientas para poder controlar distintos aspectos de las velocidades y direcciones de las ruedas de forma independiente sobre cada motor DC.

Y luego utilizar el siguiente código:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
   Servo Example - Example of usage ASMpi class

.. Licence MIT
.. codeauthor:: Jan Lipovský <janlipovsky@gmail.com>, janlipovsky.cz
"""

from AMSpi import AMSpi
import time
import socket
import thread
import sys

control = None

def stop(amspi,timesec):
    print("Stop")
    amspi.stop_dc_motors([amspi.DC_Motor_1, amspi.DC_Motor_2, amspi.DC_Motor_3, amspi.DC_Motor_4])

def run(amspi,timesec):
    print("Adelante")
    amspi.run_dc_motors([amspi.DC_Motor_1, amspi.DC_Motor_3])
    amspi.run_dc_motors([amspi.DC_Motor_2, amspi.DC_Motor_4], clockwise=False)

def reverse(amspi,timesec):
    print("Reversa")
    amspi.run_dc_motors([amspi.DC_Motor_1, amspi.DC_Motor_3], clockwise=False)
    amspi.run_dc_motors([amspi.DC_Motor_2, amspi.DC_Motor_4])

def izquierda(amspi,timesec):
    print("Gira izquierda")
    amspi.run_dc_motors([amspi.DC_Motor_2,amspi.DC_Motor_1], clockwise=False)
    amspi.run_dc_motors([amspi.DC_Motor_4,amspi.DC_Motor_3])

def derecha(amspi,timesec):
    print("Gira derecha")
    amspi.run_dc_motors([amspi.DC_Motor_2,amspi.DC_Motor_1])
    amspi.run_dc_motors([amspi.DC_Motor_4,amspi.DC_Motor_3],  clockwise=False)

def on_message(message):
    print('mensaje recibido '+message)

    if(message == 'AD'):
       run(control, 10)

    if(message == 'AT'):
        reverse(control,10)

    if(message == 'ST'):
        stop(control,0)

    if(message == 'DE'):
        derecha(control,10)

    if(message == 'IZ'):
        izquierda(control,10)


if __name__ == '__main__':
    # Calling AMSpi() we will use default pin numbering: BCM (use GPIO numbers)
    # if you want to use BOARD numbering do this: "with AMSpi(True) as amspi:"
    with AMSpi() as amspi:
        # Set PINs for controlling shift register (GPIO numbering)
        amspi.set_74HC595_pins(21, 20, 16)
        # Set PINs for controlling all 4 motors (GPIO numbering)
        amspi.set_L293D_pins(5, 6, 13, 19)

        control = amspi

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        server_adress = ('192.168.0.28', 55555)

        sock.bind(server_adress)

        sock.listen(1)

        while True:
            print >>sys.stderr, 'waiting for a connection'
            connection, client_adress = sock.accept()

            try:
                print >>sys.stderr, 'connection from', client_adress

                while True:
                    data= connection.recv(2)
                    print >>sys.stderr, 'received "%s"' % data
                    if data:
                        print >>sys.stderr, 'sending data back to the client'
                        on_message(data)
                    else:
                        print >>sys.stderr, 'no more data from ', client_adress
                        break

            finally:
                connection.close()



Si leen el código van a encontrar una configuración especifica que yo tengo especialmente para el auto. 
Tienen que encontrar puntualmente la que ustedes necesitan, en las funciones run, reverse, izquierda derecha, en estas funciones hay que ir configurando si el motorDC1 va hacia delante solo hay que poner. 

amspi.run_dc_motors([amspi.DC_Motor_1])

En caso de que se necesite que gira hacia el otro lado, hay que cambiar la función a:

amspi.run_dc_motors([amspi.DC_Motor_1], clockwise=False)

Para poder girar hacia la derecha hice que las ruedas de la derecha vayan em reversa y las ruedas de la izquierda hacia adelante.

RaspBerry PI - MotorShield control Motor DC

Introducción


El objetivo principal que me propuse, es poder controlar con la raspberry cuatro motores DC o sea motores comunes, así con eso poder tener tracción en las cuatro ruedas y también poder usar todas las ruedas para poder hacer giros. Ademas también poder hacer que el auto sea 100% autónomo sin cables externos.

Primero en este tutorial veremos como controlar las cuatro ruedas y poder hacer giros de las mismas. 

Para poder controlar 4 motores DC (uno en cada rueda del auto)

Motor DC conectado a la Rueda

Primero es necesario tener un controlador MotorShield, el mismo puede controlar hasta cuatro motores DC o 2 servos.

Motor Shield v1

Este modelo de Motor Shield esta diseñado para ser montado sobre un arduino, pero de todas formas se puede utilizar en la Raspberry.

Conexiones


Ahora mostrare algunas imagenes de como se conecta el MotorShield a los motores y a la Raspberry

Auto de perfil
Auto de perfil


Montado de motores DC
Montado de motores DC


Conexiones en el Motorshield


Como vieron encontré un cable unidos con 10 cables juntos, para poder utilizar los 4 motores DC solo son necesarios 2 cables por motor o sea en total 8 cables, me estarían sobrando dos que no voy a utilizar. No es necesario tener este tipo de cable, con solo tener 8 cables sean separados o unidos van a resultar útiles.



Conexiones en Raspberry


Esquema de conexiones


Si bien las imágenes muestran una posible forma de conectarse, ya que se puede elegir varios números de GPIO, el que se prefiera o quede cómodo. 
En la ultima imagen se muestra el esquema de como conectar el MotorShiled a la Raspberry indicando con cada circulo de color.

MotorShield

En cuanto al MotorShield lo único que queda agregar es que necesita una fuente adicional (En la ultima imagen se muestra donde dice Extra Power). Esta conexión puede venir desde la Raspberry desde el Pin 2 o 4 , o directamente puede ser completamente externa.

Motor DC

 
En cuanto a la conexion con el Motor DC es indistinto el orden de los cables que van al Motor DC ya que lo unico que hace es orientar la corriente del motor , o sea que gira la rueda hacia un lado o hacia el otro.

Conexiòn al Motor DC


Alimentación

Como mencione anteriormente la idea es que el auto sea completamente autónomo, para eso es necesario una bateria o algún power bank que haya que tenga un amperaje de 2 o 2,5 amp. En mi caso encontré un power bank de 2,1 amp y con carga de 20000 mhs, suficiente para poder pasar corriente a la Raspberry y al MotorShield, y con una duración de 2 o 3 horas de funcionamiento

En funcionamiento




Por ultimo el código

Como vieron en el vídeo el funcionamiento del auto es perfecto, y que muy bueno. Les paso el link donde esta el código que hace que funcione el auto en la Raspberry en Python.


Mas adelante les pasare como fue que lo hice remoto por medio de Wifi y poder controlarlo desde un celular.  




viernes, 28 de febrero de 2020

Spring JDBC Template

Por una migración y actualización de una aplicación que tengo que hacer me he encontrado con la necesidad de tener que ejecutar consultas nativas en SQL, he pensado en integrarlo con JPA pero resulta mucho trabajo como para hacer solo algunas consultas, por lo que encontré una extensión más que provee SpringBoot para hacerlo de forma más fácil.

La configuración del jdbcTemplate es la siguiente:


@Bean(name = namedParameterJdbcTemplate")
public NamedParameterJdbcTemplate namedParameterJdbcTemplate(@Qualifier("dataSource") 
                                                             DataSource dataSource) {

   return new NamedParameterJdbcTemplate(dataSource);
}


Usar NamedParameterJdbcTemplate nos permite manejar parámetros con los nombres que quisieramos, mas adelante se mostrara en el siguiente ejemplo:

MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("name", name);


Así se definen los parámetros que se van a pasar a la query, utilizando nombres (:nombre) así se identifican de una mejor forma dentro de la propia query, en este caso la consulta a la base de datos es sencilla, sera muy útil cuando tengamos consultas grandes a la base de datos.

    List users = namedParameterJdbcTemplate.query(
                      "SELECT * FROM USERS WHERE NAME = :nombre ", params, new UserRowMapper());

Se puede ver que se ejecuta una consulta a la base directamente con SQL y luego utiliza un objeto mapper para hacer la transformación al objeto que quisiéramos.
public class UserRowMapper implements RowMapper {

  @Override
  public UserRow mapRow(ResultSet resultSet, int rowNum) throws SQLException {

    UserRow row = new UserRow ();

    row.setName(resultSet.getLong("NAME"));
    row.setLastName(resultSet.getLong("LAST_NAME"));
    row.setId(resultSet.getString("USER_ID"));

    return row;

  }
}

Así se define el mapper, haciendo las conversiones correspondientes y hasta con la posibilidad de personalizar las transformaciones que prefieramos.






martes, 21 de enero de 2020

Visual Basic 6 en Windows 10

Me he encontrado con la suerte de tener que hacer una reingeniería de una aplicación que está en Visual Basic 6 y el principal problema con el que me encontré es el de poder leer el código fuente, por lo que primero probé con la utilización de un editor de textos, como Sublime Text o Visual Studio Code, pero me fue imposible interpretarlo correctamente. Por lo que se me presentó la inquietud de poder verlo como lo veía él que lo programo o sea con VB6, por lo que procedí a bajarme los instaladores de Microsoft Visual Basic 6, los mismos los encontré en los siguientes links:

https://winworldpc.com/product/microsoft-visual-bas/60

https://winworldpc.com/product/msdn/vs-60

También es necesario descargar el Service Pack 6 de VB6 desde el siguiente link:

https://www.microsoft.com/es-ar/download/details.aspx?id=9183


Primero instalar el VB6

  1. Copiar el contenido del CD de Visual Basic en una carpeta del disco local. Por ejemplo C:\VB6.
  2. Copiar el contenido de toda la carpeta c:\VB6\Setup en c:\VB6
  3. Renombrar el archivo ‘vb98ent.stf’ o ‘vs98ent.stf’ (Según sea el caso debido a que hay Visual Basic 6.0 y Visual Studio 6.0) por ‘acmsetup.stf’.
  4. Ejecutar ACMSETUP.EXE (como administrador), introducir la clave de VB6 y proceder con la instalación.Se recomienda desmarcar todo lo que está relacionado con el acceso a datos (entrar dentro y desmarcarlo todo). De esta forma no se cuelga la instalación, podrá terminar y registrase en como aplicación instalada en el panel de control.
  5. Completar la instalación. Al final nos pide reiniciar Windows.

Las opciones que seleccionó yo son: Visual Basic, ActiveX, Herramientas y características empresariales, Gráficos y herramientas.
El Sourcesafe los desmarco porque para el control de versiones ya uso Git.


Instalación del MSDN

  1. Lo mismo que la instalación de VB6. Copiamos el contenido del MSDN en una carpeta del disco. Por ejemplo c:\Temp\MSDN.
  2. Copiamos el contenido de la carpeta c:\Temp\MSDN\Setup a c:\Temp\MSDN.
  3. Renombrar ‘msdn98.stf’ que se encuentra en la carpeta donde copiamos todo por ‘acmsetup.stf’.
  4. Ejecutamos como administrador el archivo acmsetup.exe. Seleccionamos las opciones que necesitamos y listo, otra cosa instalada.


Instalación del Service Pack 6 de VB6

Es de forma similar a las instalaciones anteriores:
  1. Se descomprime el archivo Vs6sp6B del SP6 en una carpeta, por ejemplo C:\Temp\SP6.
  2. Se renombra el archivo sp698vbo.stf a acmsetup.stf (o sp698ent.stf si el SP6 que tienes es el Vs6sp6).
  3. Se ejecuta acmsetup.exe y se instala seleccionando instalación completa.

jueves, 12 de septiembre de 2019

Como instalar las versiones de los SDK y Herramientas de Android

El siguiente comando muestra la lista de versiones de SDK y distintas herramientas para instalar.


android list sdk --all


Para poder instalar una de las opciones que mostro el comando anterior se debe ejecutar el siguiente comando junto con los números que muestra el listado:

android update sdk -u -a -t <package no.>

Ejemplo:

android update sdk -u -a -t 1,2,3,4,..,n