21 mayo 2010

El editor de informes Report Manager (1)

Pese a que no es un generador de informes muy conocido en el mundo Delphi, este editor de informes destaca por su sencillez (está en español) y que sobre todo es gratuito.

Si en algo aventaja a QuickReport es en que permite generar los informes en archivos con extensión REP para luego ser cargados desde Delphi e incluso podemos modificarlos en tiempo de ejecución. Tampoco da las explosiones que ocasiona el editor Rave cuando te equivocas, aunque hay que reconocer que es un poco raro en algunas cosas.

DESCARGANDO EL EDITOR

Puede descargarse de esta página web:

http://reportman.sourceforge.net/indexes.html

Dentro del apartado Descarga nos llevará a otra página de SourceForge donde se encuentra la versión 2.9a

Lo que ocurre es que solo nos bajamos el editor y también necesitamos los componentes. Para ello pulsamos en la misma página web el botón View all files y descargamos este archivo repporman_delphi_builder_2_9.zip:


INSTALANDO EL EDITOR

La ventana de instalación permite elegir el idioma español y solo hay que ir pulsando el botón Siguiente:


INSTALANDO LOS COMPONENTES


Creamos una carpeta llamada ReportManager dentro de la carpeta:

\CodeGear\RAD Studio\5.0\Componentes\

Y descomprimimos dentro de la misma el archivo que hemos descargado:

Repporman_delphi_builder_2_9.zip

Nos vamos a Delphi y abrimos el archivo rppack2007.groupproj:

Veremos estos paquetes en la parte derecha de Delphi:

Pulsamos el paquete rppack_del2007.bpl con el botón derecho del ratón y seleccionamos Install:

Con solo hacer esto se instalará todo. Si todo ha ido correctamente veremos esto en la paleta de componentes:

Pasemos a ver el entorno de trabajo de este editor.

EL ASPECTO DEL EDITOR

Al ejecutar por primera vez el editor de informes vemos que aparece completamente pelado:

Pulsamos el botón Nuevo y mostrará este aspecto:

Lo mejor de todo es que se encuentra en nuestro idioma. Dentro del editor podemos distinguir estas partes:

Barra de herramientas superior

Contiene las típicas opciones de abrir o guardar archivos, mostrar la vista previa, etc. Y abajo tenemos los iconos para insertar componentes: etiquetas, campos vinculados a tablas, formas geométricas, dibujos y códigos de barras. Ya iremos viendo detenidamente cada uno de ellos según los necesitemos.

Estructura del informe

Aquí vemos las bandas de las que se compone el informe. Si seleccionamos por ejemplo el Detalle mostrará abajo el inspector de objetos con sus propiedades. A su lado tenemos la pestaña Datos donde veremos las variables globales que podemos utilizar:


La zona de trabajo

Aquí es donde iremos colocando componentes para crear el informe. No os dejéis engañar por la simpleza de este editor, ya que como veremos más adelante se pueden crear informes avanzados por programación interceptando en tiempo real los componentes al igual que hacemos con QuickReport.

CREANDO UN INFORME

Para probar que todo ha ido bien, vamos a crear un pequeño informe a partir de una base de datos de Interbase que contiene una tabla de CONTACTOS. Lo primero que tenemos que hacer es establecer la fuente de donde van a venir los datos. La vinculación con el origen de datos es otra característica algo rara que confunde por sus múltiples pantallas.

Seleccionamos en el menú superior las opciones Informe - Configuración de datos:

Y aparecerá esta ventana:

Seleccionamos Interbase Express que equivale realmente a una conexión DBExpress al pulsar el botón Configurar:

Aquí trae como ejemplo la configuración a una base de datos Interbase cuya conexión DBExpress está configurada dentro de Delphi 7, pero lo que vamos a hacer es crear una nueva conexión pulsando este botón:

En el nombre de la conexión le ponemos AGENDA:

Entonces volverá a la pantalla anterior y configuramos donde se encuentra la base de datos:

No hay que olvidar poner la propiedad SQL Dialect a 3. Probamos la conexión pulsando el botón:

A continuación nos pedirá identificarnos:


Y si todo ha ido bien nos dirá esto:

Volvemos a la primera pantalla y seleccionamos la conexión que hemos creado:

Lo añadirá a la lista de conexiones disponibles:


Ahora nos vamos a la pestaña Conjunto de datos del informe para elegir las tablas de la que queremos sacar la información:


Pulsamos el botón Nuevo conjunto de datos y lo llamamos CONTACTOS:

La ventana cambiará a esta otra:

Escribimos la sentencia SQL de los datos que nos vamos a traer:

Pulsamos Aceptar y al volver al editor veremos en la pestaña Datos la tabla que hemos vinculado:

Para no perder la configuración guardamos el informe con Archivo – Guardar como – Contactos.rep. Como lo que quiero diseñar es un listado, tenemos el problema de que solo tenemos la banda de detalle. Para insertar la banda de la cabecera seleccionamos Informe – Añadir – Cabecera de página. Ya tenemos las dos bandas:

Vamos a pulsar este icono para añadir una etiqueta al informe:

Una vez tenemos la etiqueta seleccionada nos vamos a la izquierda al inspector de objetos y en la propiedad texto escribimos LISTADO DE CONTACTOS:

Una de las cosas raras que hace este editor es que al insertar una etiqueta le da un anchura y altura muy pequeña. Tenemos que estirarla siempre. Así creamos el resto de etiquetas:

Para copiar etiquetas no podemos utilizar la combinación de teclas CTRL + C y CTRL + V, hay que hacerlo con ALT + C y ALT + V. Esta es también una característica algo rara.

Ahora insertamos los campos arrastrándolos desde la pestaña de datos a la banda del detalle:

Así quedaría después de insertarlos:

Pulsamos el botón de vista previa para ver el informe:

Aquí encontramos el primer inconveniente:

Para que se listen todos los registros tenemos que vincular la tabla CONTACTOS al informe. Para ello nos vamos a la pestaña Estructura y en la propiedad Con. D. Principal seleccionamos CONTACTOS:


Ahora si sale bien:

En el próximo artículo veremos como cargar este informe desde nuestro proyecto de Delphi así como exportarlo a PDF y otros formatos. También veremos otros formatos más avanzados como una factura.

Pruebas realizadas en RAD Studio 2007.

07 mayo 2010

La librería Synapse (y 5)

Vamos a terminar de ver las propiedades de esta librería, fijándonos en otras funciones que incorpora que pueden facilitarnos la vida a la hora de crear nuestro software orientado a redes o Internet.

COMPROBAR SI UNA DIRECCIÓN DE CORREO ES VÁLIDA

Debemos añadir la unidad mailchck en el formulario donde vamos a realizar la comprobación y llamamos a esta función:

function mailcheck(email: string): integer;

Y estos son los valores que devuelve:

0 - La dirección existe
1 - La dirección puede existir
2 - Tus direcciones DNS no son válidas
3 - Tus direcciones DNS no están definidas
4 - No puede conectar con los servidores MX
5 - El dominio no tiene registro MX
6 - La dirección no existe
7 - La dirección es incorrecta

ESCANEAR DIRECCIONES IP

Vamos a crear un pequeño programa que recorra un rango de direcciones IP y nos diga si existen o no. Es como si hiciésemos un Ping a cada dirección. Para ello creamos un nuevo proyecto con este formulario:

Para introducir la dirección inicial y final he añadido dos componentes TEdit llamados DirIni y DirFin, de modo que cada vez que encuentre una IP activa lo guarde en un componente TListBox llamado Lista.

Para no reinventar la rueda, vamos a copiar las unidades IPUtils.pas y IPThread.pas de la carpeta:

\CodeGear\RAD Studio\5.0\Componentes\Synapse\source\demo\scan

a la carpeta donde tenemos este proyecto y las vinculamos a nuestro formulario:

uses
..., IPUtils, PingThread;

Y este sería el código para recorrer todas las direcciones:

procedure TFEscanear.BEscanearClick(Sender: TObject);
var
i, j: Cardinal;
Ping: Array of TPingResult;
ContadorPing, DirInicial, DirFinal: Cardinal;
Puffer: String;
Hilos: Array of TPingThread;
bHilosCompletados: Boolean;
begin
// Pasamos las direcciones IP de texto a entero
DirInicial := IPToCardinal(StrToIP(DirIni.Text));
DirFinal := IPToCardinal(StrToIP(DirFin.Text));

// Contamos el nº de direcciones a escanear
ContadorPing := (DirFinal - DirInicial) + 1;

// Mostramos el nº de direcciones a escanear
Lista.Items.Add('Escaneando ' + IntToStr(ContadorPing) + ' direcciones...');
Application.ProcessMessages;

// Inicializamos el array dinámico
SetLength(Ping, ContadorPing);
SetLength(Hilos, ContadorPing);

// Recorremos las direcciones y las metemos en el array
j := 0;
for i := DirInicial to DirFinal do
begin
Ping[j].IPAdress := IPToStr(CardinalToIP(i));
Ping[j].Exists := False;
Inc(j);
end;

// Creamos un hilo para cada Ping
for i := 0 to ContadorPing - 1 do
Hilos[i] := TPingThread.Create(Ping[i]);

Lista.Items.Add(' ');

// Esperamos a que los hilos sean ejecutados
repeat
bHilosCompletados := True;
Sleep(1000);
for i := 0 to ContadorPing - 1 do
begin
if not Hilos[i].Ready then
begin
bHilosCompletados := False;
Break;
end;
end;
until bHilosCompletados;

// Volcamos los resultados a la lista
for i := 0 to ContadorPing - 1 do
begin
if Hilos[i].PingResult.Exists then
begin
Puffer := IntToStr(i + 1) + ' ' + Hilos[i].PingResult.IPAdress;
Lista.Items.Add(Puffer);
end;
end;

// Liberamos los hilos de ejecución
for i := 0 to ContadorPing - 1 do
Hilos[i].Free;
end;

Lo que hace es lanzar un hilo de ejecución por cada dirección y cuando terminen todos los muestra por pantalla. Ejecutamos el programa, pulsamos el botón Escanear y esperamos a que termine:

Si en algo destaca esta librería es en su rapidez, ya que se ventila las 250 direcciones en cuestión de segundos.

SINCRONIZAR LA HORA DE VARIOS ORDENADORES

El protocolo NTP (Network Time Procotol) se creó para sincronizar los relojes de cada PC a través de redes, algo ideal para sistemas con bases de datos distribuidas.

Vamos a crear un pequeño programa que lea la hora de un servidor SNTP o NTP:

Contiene un campo TEdit llamado URL donde metemos la dirección del servidor y una etiqueta llamada EMensaje donde volcamos la hora leída o un mensaje de error. Al pulsar el botón Leer hora ejecutamos este código:

procedure TFClienteSNTP.BLeerClick(Sender: TObject);
var
SNTP: TSntpSend;
begin
SNTP := TSntpSend.Create;
try
SNTP.TargetHost := URL.Text;

if SNTP.GetSNTP then
EMensaje.Caption :=
DatetimeToStr(SNTP.NTPTime) + ' UTC'
else
EMensaje.Caption := 'No conectado.';
finally
SNTP.Free;
end;
end;

Para poder utilizar la clase TSntpSend necesitamos añadir la unidad SNTPsend al formulario. Al ejecutar el programa, podemos probarlo con nosotros mismos ya que Windows incorpora un servidor SNTP:

La hora que devuelve es UTC, por lo que debemos incrementar más o menos horas según el país donde estemos. Lo ideal es utilizar un servidor público como este:

De este modo, todos los equipos de nuestra red estarán sincronizados incluso si se encuentran en distintas localizaciones. Aquí podéis encontrar servidores públicos:

http://www.pool.ntp.org/zone/europe


Como funciona sobre UDP, es muy rápido y puede ser de mucha utilidad para que comiencen a la vez las copias de seguridad de distintos centros o para realizar una comunicación directa por TCP a una cierta hora del día.

ENVIAR DATOS POR EL PUERTO SERIE

El ejemplo que trae Synapse sobre las comunicación por el puerto serie no funciona, ya que han cambiado los parámetros de la función Connect y los han desviado a la función Config. Para hacer una prueba hice este formulario:

Contiene los componentes TEdit llamados Puerto, Baudios y Comando que son los parámetros del puerto serie y los mensajes de salida irán a parar a un TMemo llamado Salida. Al pulsar el botón Enviar ejecutamos:

procedure TFPuetoSerie.BEnviarClick(Sender: TObject);
var
Serie: TBlockSerial;
begin
Serie := TBlockSerial.Create;
Serie.RaiseExcept := True;

try
Serie.Config(StrToIntDef(Baudios.Text, 9600), 8, 'N', 0, False, False);
Serie.Connect(Puerto.Text);
Salida.Lines.Text := Serie.ATCommand(Comando.Text);
finally
Serie.Free;
end;
end;

También debemos añadir la unidad SynaSer al mismo. Por más que lo he intentado siempre me salta esta excepción:

Y mira que he probado a enviarlo por COM1 o COM2, incluso me he instalado el emulador de puertos COM llamado com0com que tiene esta pantalla de configuración:

Este emulador se instala perfectamente en Windows simulando todos los puertos que queramos, pero yo con estos temas ya estoy algo obsoleto, ya que desde que pasamos a la ADSL y el resto de dispositivos van por USB (impresoras de tickets, autómatas, etc.), mi experiencia actualmente con el puerto serie es nula. Si alguien tiene ganas de investigar el asunto y tiene MODEM que pruebe a ver que tal.

CONCLUSIONES

A parte de todo lo que hemos visto, esta librería incorpora otros ejemplos como un servidor de ECHO o rutinas para codificar en MIME, algo utilizado para enviar datos por correo a pelo, pero creo que en ese sentido los componentes Indy le superan.

Por lo demás, solo queda decir que aunque no es una librería demasiado extensa, si funciona muy bien, es rápida y permite aprender como se programan algunos protocolos TCP/IP a bajo nivel, algo que puede gustarle a los programadores de troyanos y virus por su bajo consumo de recursos.

Pruebas realizadas en RAD Studio 2007.

23 abril 2010

La librería Synapse (4)

El protocolo SNMP (Simple Network Management Protocol) facilita el intercambio de información entre distintos dispositivos de red a la hora de mandarse información sobre su configuración. Este protocolo utiliza UDP para solicitar la información al servidor.

Por ejemplo, podemos utilizar este protocolo para que los clientes de un programa le pidan al servidor que puerto se utiliza para atacar al motor de bases de datos, aunque generalmente se suele utilizar para configuraciones de routers, impresoras, servidores, etc.

Para que un cliente le pueda pedir algún dato al servidor se utiliza un identificador de objeto llamado MIB que viene definido en este árbol (clic para ampliar):

En el ejemplo que trae la librería Synapse utiliza el identificador 1.3.6.1.2.1.1.1.0, lo que quiere decir que es un objeto ISO, que pertenece a una organización identificada, DOD, Internet, etc. Es decir, vamos siguiendo las ramas del árbol según los dispositivos que van a comunicarse. Para más información sobre SNMP:

http://es.wikipedia.org/wiki/Simple_Network_Management_Protocol

CREAR UN SERVIDOR SNMP

Nuestro servidor va a tener un solo formulario con un componente Memo llamado Mensajes donde escribiremos lo que pasa en el servidor:

Para poder vincularnos con la librería de Synapse entonces debemos añadir al formulario las unidades:

blcksock, winsock, Synautil, SNMPSend, asn1util;

Al igual que los servidores que implementamos anteriormente, nuestro servidor será un hilo de ejecución que escucha por el puerto 161 las peticiones de valores de los clientes:

THiloSNMP = class(TThread)
private
Sock: TUDPBlockSocket;
SNMPRec: TSNMPRec;
sMensaje: string;
public
constructor Create;
destructor Destroy; override;
procedure Execute; override;
procedure ProcessSnmpRequest(PDU: integer; var sOID, sValor: string;
var iTipoValor: Integer);
procedure Monitorizar;
end;

Al crear el constructor creamos el Sock de conexión y el objeto SNMPRec que recogerá las peticiones de los clientes:

constructor THiloSNMP.Create;
begin
Sock := TUDPBlockSocket.Create;
SNMPRec := TSNMPRec.Create;
FreeOnTerminate := True;
inherited Create(False);
end;

No debemos olvidar de destruirlo todo:

Destructor THiloSNMP.Destroy;
begin
Sock.Free;
SNMPREC.Free;
inherited Destroy;
end;

Al poner en marcha el servidor, queda indefinidamente esperando peticiones de valores de los clientes hasta que lo cerremos:

procedure THiloSNMP.Execute;
var
sBuffer: string;
n: integer;
mib: TSNMPMib;
sOID, sValor: string;
iTipoValor: Integer;
begin
with Sock do
begin
Bind('0.0.0.0', '161');

if Sock.LastError <> 0 then
Exit;

repeat
if Terminated then
Break;

sBuffer := Sock.RecvPacket(1000);

if Sock.LastError = 0 then
begin
SNMPRec.Clear;
SNMPRec.DecodeBuf(sBuffer);

for n := 0 to SNMPRec.MIBCount - 1 do
begin
mib := SNMPRec.MIBByIndex(n);

if mib <> nil then
begin
sOID := mib.OID;
sValor := mib.Value;
iTipoValor := mib.ValueType;
ProcessSnmpRequest(SNMPRec.PDUType, sOID, sValor, iTipoValor);
mib.OID := sOID;
mib.Value := sValor;
mib.ValueType := iTipoValor;
end;
end;

SNMPRec.PDUType := PDUGetResponse;
SNMPRec.ErrorStatus := 0;
sBuffer := SNMPRec.EncodeBuf;
Sock.SendString(sBuffer);
end;

until False;
end;
end;

Cuando el servidor recibe el objeto identificador del cliente MIB, lee el identificador del valor OID y genera una respuesta con el método ProcessSnmpRequest:

procedure THiloSNMP.ProcessSnmpRequest(PDU: integer; var sOID, sValor: string;
var iTipoValor: Integer);
begin
if PDU = PDUGetRequest then
begin
sMensaje := 'Solicitan: ' + sOID;
Synchronize(Monitorizar);

// Si no tenemos respuesta, devolvemos una predeterminada
sValor := 'No lo se';

if sOID = '1.3.6.1.2.1.1.1.0' then
begin
sValor := 'Valor devuelto por el servidor';
iTipoValor := ASN1_OCTSTR;
end;

sMensaje := 'Respondiendo: ' + sValor;
Synchronize(Monitorizar);
end;
end;

Para poder sacar por pantalla los mensajes del servidor utilizamos el método Monitorizar para poder sincronizarnos con el formulario principal a través del hilo de ejecución:

procedure THiloSNMP.Monitorizar;
begin
FServidorSNMP.Mensajes.Lines.Add(sMensaje);
end;

Por último, en creamos y destruimos el servidor SNMP al crear o destruir el formulario:

procedure TFServidorSNMP.FormCreate(Sender: TObject);
begin
Mensajes.Lines.Add('Arrando servidor SNMP por el puerto 161...');
HiloSNMP := THiloSNMP.Create;
end;

procedure TFServidorSNMP.FormDestroy(Sender: TObject);
begin
HiloSNMP.Terminate;
end;

Ahora vamos a crear el cliente que le pide los datos.

CREAR UN CLIENTE SNMP

El cliente de SNMP es mucho más fácil de crear que el servidor. Tenemos que añadir la unidad SNMPSend al siguiente formulario:

Se compone de los componentes TEdit llamados IP y OID para solicitar el valor al servidor y la respuesta irá a parar al componente TMemo llamado Respuesta. El campo Comunidad lo necesita la función SNMPGet como parámetro:

procedure TFClienteSNMP.BPedirClick(Sender: TObject);
var
sRespuesta: string;
begin
SNMPget(OID.Text, Comunidad.Text, IP.Text, sRespuesta);
Respuesta.Lines.Text := sRespuesta;
end;

Ahora ponemos en marcha el servidor:

Ejecutamos el cliente, escribimos la dirección 127.0.0.1 y pulsamos el botón Pedir:

El servidor nos mostrará la petición realizada:

Esta claro que este protocolo se creó originalmente para comunicar dispositivos de hardware a través de la red, por lo que si necesitamos realizar llamadas y respuestas entre software de distintas máquinas, lo mejor sería que se comunicasen por TCP y UDP por su cuenta. Esta claro que este protocolo esta diseñado específicamente para administradores de red.

En el siguiente artículo veremos otros pequeños protocolos que abarca esta librería.

Pruebas realizadas en RAD Studio 2007.

Publicidad