Monday, 31 August 2009

Utilización de Visprint con Delphi (Visual FingerPrint Generator)

Visprint es un programa que permite generar un fractal en función del contenido de un fichero. Ésto nos permite crear una firma para nuestros ficheros de código fuente muy interesante. He creado una pequeña aplicación, llamada Thundax Visprint, que utiliza las herramientas de Visprint, para generar fractales a partir de código delphi. Podemos utilizar cualquier fichero, pero en general lo he utilizado para ver que tipo de diagramas genera mi código. Conocí visprint gracias a una entrada que creó Fernando Acero en Kriptópolis, uno de los blogs que sigo. La verdad es que he encontrado ésta aplicación muy interesante y mediante la utilización de un checksum MD5, visprint es capaz de generar el diagrama a partir de éste número de 32 bits.

Visprint utiliza la generación de fractales IFS de Michael Barnsley, y David Johnston ha creado la última implementación de la aplicación.

Por defecto he dejado los parámetros básicos de la generación del diagrama, con la modificación de dejar la imagen en 600x600 pixels. Desde la herramienta, podremos parsear el fichero de código fuente (.pas) generando el MD5, luego ejecutando los diferentes procesos de Visprint en segundo plano y luego cargar la imagen PPM (Portable Pixmap Format) en un TImage utilizando la clase (TAEPPMGraphic), de ésta manera no tengo que estar jugando con herramientas de conversión (PPMtoBMP o PPMtoJPEG) que aún me harían más complicado el trabajo. Una vez hecho ésto, veremos cargada la imagen en la aplicación y podremos guardar el resultado en un Bitmap (de ésta manera lo hacemos más portable).

Si instalamos la aplicación y la iniciamos, obtendremos:


Ésta es la imagen de presentación (Bonita, no???), pues es la imagen del código fuente de mi Canvas personalizado del último post que hice sobre la implementación de objetos.

Los pasos a seguir son muy simples, primero hay que seleccionar el fichero, y luego ir pulsando los diferentes botones en el orden establecido. Ya veréis que irán apareciendo diferentes mensajitos indicando que la operación está acabada.

Luego, una vez hecho, podremos ver nuestro fractal generado a partir del código fuente:

Como podéis ver, cuánto más entropía hay en fichero, más bonito es:


  • Enlaces de Interés:
Conversor NetPBM.
Visor de PPM Cognaxon.
disp189a.zip (Display PPM).
Delphi Images.


Sunday, 30 August 2009

Obtener la versión de la aplicación con Delphi

En el IDE de Delphi, aparece la versión de la aplicación siempre que la tengamos marcada:


Para leer ésta versión y mostrarla en nuestra aplicación, lo podemos hacer de la siguiente manera, utilizando la propia API de Windows mediante las funciones GetFileVersionInfoSize y GetFileVersionInfo.

El código fuente es de la web de Trucomania, y lo modifiqué para mis aplicaciones:

unit ThundaxAppLib;

interface

uses
    Windows, SysUtils, Variants, Classes;

function GetAppVersion: string;

implementation

function GetAppVersion: string;
var
    Size, Size2: DWord;
    Pt, Pt2: Pointer;
begin
    Size := GetFileVersionInfoSize(PChar(ParamStr(0)), Size2);
    if Size > 0 then
    begin
        GetMem(Pt, Size);
        try
            GetFileVersionInfo(PChar(ParamStr(0)), 0, Size, Pt);
            VerQueryValue(Pt, '\', Pt2, Size2);
            with TVSFixedFileInfo(Pt2^) do
            begin
                Result := ' v' +
                    IntToStr(HiWord(dwFileVersionMS)) + '.' +
                    IntToStr(LoWord(dwFileVersionMS)) + '.' +
                    IntToStr(HiWord(dwFileVersionLS)) + ' Build ' +
                    IntToStr(LoWord(dwFileVersionLS));
            end;
        finally
            FreeMem(Pt);
        end;
    end;
end;

end.


Para su utilización, solo hay que llamar al método GetAppVersion, y devuelve un String con la cadena v1.0.0 Build 8.

Implementación de un Canvas para modificar Objetos en Delphi parte III

Continuando con la aplicación para la modificación de Objetos utilizando un Canvas, en ésta parte he implementado un modificador del z-Order, o sea, un modificador para indicar cuál de las imágenes va encima o va debajo. La implementación de mi canvas, me permite dibujar la clase TBox, y además de poder generar n-conexiones entre ellas, me permite modificar la ubicación de las cajas en el plano Z. De ésta manera puedo enviar hacia delante o hacia el fondo la caja para darle una diferente prioridad. Una vez tenga acabada correctamente la gestión de eventos, fusionaré ésta aplicación con la del post anterior y así moldear diferentes objetos con una cierta inteligencia en el Scada.

La mejora del diseño de la clase TBox es la siguiente:

Ahora dispone de diversas propiedades nuevas, así como el zOrder que indica el orden a la hora de realizar el dibujado sobre el TCanvas personalizado. Ahora solo falta unirlo al TCitectPainter para que maneje el objeto COM del IGraphicsBuilder.

Aquí os dejo la versión estable de la aplicación ThundaxBoxManager v1.0.0 Build 9.exe para que la probéis y saquéis vuestras conclusiones.

Aquí os dejo varias imágenes sobre el funcionamiento del zOrder (Send to Back):

Y (Bring to Front):

Ahora mismo, con la aplicación podríamos incluso generar grafos y calcular la ruta mínima aplicando alguno de los algoritmos típicos. Pero bueno, eso quedará para otro día, además ya hay algo implementado de eso en el blog.


Mediante ésta aplicación puedo dibujar cualquier cosa y crear diversos mapas gracias a su posibilidad de conexionado entre si. De ésta manera, podríamos llegar a crear diagramas diversos, estructuras en árbol, etc, que a veces son mejores para su entendimiento si las dibujamos, en vez de mostrar algo en texto. Se me ocurre, pues el conexionado de una red de ordenadores por ejemplo, o el viaje que realiza una factura, etc. De ésta manera podemos tener un diagrama con un poco de inteligencia y asignarle las propiedades que queramos. Solo tenemos que heredar de TBox y generar nuestro propio componente gráfico.


Thursday, 27 August 2009

Automation Object Library para Vijeo Citect con Delphi parte I


Después de éstos días de descanso y reflexión, aún estoy de vacaciones y me he parado un momento para publicar algo en lo que he estado trabajando éstos últimos días. Aquí os hago un pequeño adelanto de una de mis próximas aplicaciones utilizando todas las aplicaciones explicadas anteriormente. (sobre geometría computacional, utilización del canvas, etc.) Ésta idea bastante innovadora se utilizará para generar pantallas SCADA con mucha más facilidad utilizando las herramientas de las que dispone Vijeo Citect para la automatización de sus pantallas. Hace mucho tiempo que me dedico al sector de la Automatización Industrial y he desarrollado varios programas para la manipulación de Scada's de Vijeo Citect. Pues bien, he desarrollado un pequeño programa que permite utilizar la Automation Object Library (ctdraw32.tlb) para hacer la manipulación mediante delphi, y así aprovechar toda su potencia. Mediante la utilización de un patrón IoC y objetos COM, creo una jerarquía de clases idónea para la manipulación de ésta librería y así interaccionar con las pantallas de Citect. Ésta librería no tiene ningún secreto, es más en la ayuda de la misma aplicación aparece descrito cómo utilizarla mediante Visual basic:

Aunque es un poco ortodoxo (utilizando un lenguaje sin OO..puf!) lo bueno es poder utilizar Delphi para jugar con ésta librería gráfica. Lo primero que tenemos que hacer es importar el fichero TLB (Borland type Library) a nuestro proyecto. La instalación de éstos tipos de ficheros están explicados en algunos de mis artículos, aunque aquí os lo vuelvo a explicar:

  • Importando el componente ctdraw32.tlb:
Una vez tenemos creado nuestro proyecto, solo tenemos que ir a Component -> Import Component, y una vez dentro del asistente, tenemos que importar una Type Library:

Ésta librería viene por defecto con la instalación de Vijeo Citect 7.0, y es la Graphic Library 6.1. Yo la tengo ubicada en la ruta C:\Program Files\Citect\CitectSCADA 7\Bin para mayor comodidad por tema del sistema operativo, ya que trabajo con windows vista y aún hay alguna cosa que se le resiste. Una vez generado el fichero GraphicsBuilder_TLB.pas, lo importamos a nuestro proyecto para poder manipular sus interfaces y automatizar las pantallas del Scada.

Ahora, podemos jugar con ésta librería y diseñar las pantallas como queramos utilizando nuestro propio canvas para ir manipulando las genies del SCADA. Aquí os dejo una muestra de una pantalla de la web de Schneider Automation, para que veáis como queda una pantalla típica:


Los que ya han tocado éste SCADA, saben que la implementación de sus objetos se hace mediante genies y supergenies. Pues bien, utilizando ésta librería podemos insertar un objeto en pantalla haciendo una llamada por su nombre a una genie y emplastarla en la pantalla en la ubicación que queramos. Luego podemos ir modificando sus posiciones e incluso utilizar pipes de conexión para simular un enlace físico. Mi objetivo es utilizar la POO mediante Delphi para crear enlaces inteligentes entre los objetos insertados en la pantalla. De ésta manera la aplicación tiene un mapa mental de cómo están ubicados los objetos y de ésta manera llegar a hacer diversas cosas con ellos (ya os lo explicaré más adelante).
  • Creación de la jerarquía de clases:
Como os he explicado en el inicio de mi artículo, he utilizado un patrón IoC (Inversion Of Control) porque he creado diversas clases para manejar la creación de éstos objetos. Utilizando éste patrón consigo que la llamada de los eventos de dibujo no los haga el propio componente, sino que delego ésta responsabilidad a un gestor que se encargará de realizar la llamada del objeto y de su pintado en pantalla. Es el mismo principio que el de Hollywood (No me llames tú, ya te llamaremos nosotros).

Aquí os dejo el primer diseño de la aplicación:


Mediante ésta configuración, podremos generar los diferentes objetos (motores, válvulas, etc) y asignarles propiedades (Nombre, Identificador, tag, etc). Luego el dispatcher se encarga de envolver los componentes del TLB (hace de Wrapper class). Luego toda la gestión de los objetos la hace el Painter que es el que se encarga de hacer las llamadas a los diferentes objetos y realizar el dibujado en la pantalla del Scada.

Luego mediante la llamada de éstos objetos, podemos crear las pantallas automáticamente:

procedure TMyApp.DrawItems(Sender: TObject);
var
    MyComObject: IGraphicsBuilder2;
    myCitectPainter: TCitectPainter;
    myElement : TCitectElement;
begin
    MyComObject := TCitectDispatcher.Create;
    myCitectPainter := TCitectPainter.Create(MyComObject, 'include','standard', 'normal');

    myElement := TCitectElement.create('motor_1_east', 'motors', Point(300, 300));
    myElement.AddParam('Tag', 'Test_Tag');
    myElement.InitLine := Point(myElement.position.x + 71, myElement.position.y + 18);

    myCitectPainter.Draw(myElement);
    myCitectPainter.InsertProperties(myElement);

    FreeAndNil(myElement);

    myCitectPainter.Save();

    FreeAndNil(myCitectPainter);
    MyComObject := nil;
end;


Aquí os dejo la versión beta de la aplicación ImportCitectObjectsTHDX.exe, descargarla y probarla para que veáis lo que hace sobre la aplicación. Mediante el botón de test, veréis que realiza la inserción de 2 motores, 2 válvulas y realiza la conexión entre ellos utilizando 2 pipes:

Como podéis ver, la codificación mediante mi jerarquía de clases difiere bastante de su actual codificación utilizando visual basic:

Dim GraphicsBuilder As IGraphicsBuilder2
Set GraphicsBuilder = New GraphicsBuilder.GraphicsBuilder
With GraphicsBuilder
 .Visible = True
 .PageNew "include", "standard", "normal", 0, True, True
 .LibraryObjectPlace "include", "motors", "motor_1_east", 0,
True
 .PositionAt 300, 500
 .LibraryObjectPutProperty "Tag", "Test_Tag"
 .DrawLine 100, 100, 300, 300
 .AttributeLineColour = 120
 .PageSaveAs "Example", "TEST"
 .PageClose
 .Visible = False
End With

Set GraphicsBuilder = Nothing


Utilizando mi aplicación, podremos realizar llamadas a diversos objetos, realizar la persisténcia de éstos para la siguiente ejecución de la aplicación, parametrizar los parámetros de las genies y realizar conexiones mediante un mapa mental. La siguiente codificación nos permite dibujar lo que véis en en dibujo anterior:

uses
    CitectElement, CitectPainter, CitectDispatcher, GraphicsBuilder_TLB;

procedure TMyApp.test1(Sender: TObject);
var
    MyComObject: IGraphicsBuilder2;
    myCitectPainter: TCitectPainter;
    myElement, myElement2, myElement3, myElement4: TCitectElement;
begin
    MyComObject := TCitectDispatcher.Create;

    myElement := TCitectElement.create('motor_1_east', 'motors', Point(300, 500));
    myElement.AddParam('Tag', 'Test_Tag');
    myElement.InitLine := Point(myElement.position.x + 71,myElement.position.y + 18);

    myElement2 := TCitectElement.create('motor_1_east', 'motors', Point(400, 500));
    myElement2.AddParam('Tag', 'Test_Tag2');
    myElement2.InitLine := Point(myElement2.position.x + 71,myElement2.position.y + 18);

    myElement3 := TCitectElement.create('valve1_e', 'valves', Point(378, 561));
    myElement3.AddParam('Tag', 'Test_Tag2');
    myElement3.EndLine := Point(myElement3.position.x + 5,myElement3.position.y);

    myElement4 := TCitectElement.create('valve1_e', 'valves', Point(478, 561));
    myElement4.AddParam('Tag', 'Test_Tag2');
    myElement4.EndLine := Point(myElement4.position.x + 5,myElement4.position.y);

    myCitectPainter := TCitectPainter.Create(MyComObject, 'include','standard', 'normal');

    myCitectPainter.Draw(myElement);
    myCitectPainter.InsertProperties(myElement);

    myCitectPainter.Draw(myElement2);
    myCitectPainter.InsertProperties(myElement2);

    myCitectPainter.Draw(myElement3);
    myCitectPainter.InsertProperties(myElement3);

    myCitectPainter.Draw(myElement4);
    myCitectPainter.InsertProperties(myElement4);

    myCitectPainter.DrawPipe(myElement, myElement3);
    myCitectPainter.DrawPipe(myElement2, myElement4);

    myCitectPainter.Save();

    FreeAndNil(myElement);
    FreeAndNil(myElement2);
    FreeAndNil(myCitectPainter);
    MyComObject := nil;

end;

Ahora, solo queda trabajar e ir mejorando la aplicación para llegar a tenen una aplicación que cumpla con las necesidades descritas y que permita una escalabilidad a la hora de ir añadiendo nuevas genies.

En mi opinión, la aplicación tiene mucho futuro, y hay mucho mercado que está utilizando éste SCADA para la visualización y control en las fábricas. De ésta manera, podemos automatizar la creación de nuestras pantallas simplemente configurando los objetos sobre un lienzo y luego lanzandolos hacia citect para que los dibuje.

Friday, 21 August 2009

Implementación de un Canvas para modificar Objetos en Delphi parte II

Continuando con las mejoras de la aplicación, desde ésta se pueden dibujar diferentes conexiones entre cajas. Con la siguiente imagen veréis las cosas un poco más claras:

Ahora, podemos seleccionar una de las cajas, y realizar una conexión hacia la otra. Simplemente hay que acompañar el mouse y automáticamente generará la conexión entre centros de caja. Ahora he modificado la clase para que contenga diferentes apuntadores a otros objetos:

De ésta manera guardo un apuntador al objeto conectado y luego puedo acceder fácilmente a éste para realizar el dibujado de la línea de conexión. Ahora el sentido es único y solo admite una conexión por caja, pero bueno eso cambiará en breve. Aquí os dejo la última versión de la aplicación "Thundax Box Manager v1.0.0.6". Dentro de poco os mostraré los patrones utilizados y una versión mucho más mejorada, con mejor control de eventos que ésta versión beta en fase de desarrollo.

Pragmatic Thinking - 12 de Octubre (Confirmed)

Hoy os traigo una noticia especial, y es que el próximo día 12 de octubre tenemos confirmada la asiténcia al Seminario que da Andy Hunt en Londres llamada Pragmatic Thinking and Learning (igual que su libro), dónde aprenderemos varias cosas sobre el pensamiento y aprendizaje pragmático además de muchos ejemplos prácticos. Como ya habréis notado muchos artículos del blog hablan sobre la programación pragmática, la programación Agile, etc. Y todo ésto se lo debo a Andy Hunt, gracias a sus libros he podido descubrir una nueva perspectiva a la hora de programar. Ya os contaré como irá la experiencia publicando anécdotas y otras conversaciones que podamos tener, además de alguna que otra foto, por su puesto!. Espero que la experiencia sea provechosa y pueda difundir éstos nuevos conocimientos con vosotros mis lectores.

Editores para Iconos Online

Existen varias aplicaciones interesantes para la edición de nuestros Iconos. Yo utilizo entre otras ArtIcons Pro y MicroAngelo. Aunque las 2 son de pago, el precio es bastante bajo y son herramientas que funcionan muy bien para poder crear nuestros iconos. Hoy en día podemos encontrar aplicaciones online para casi todo, pues bien, aquí os dejo un par de enlaces para que juguéis un poco con éstos editores online:

Online Icon Maker:

Éste editor es bastante interesante, podemos subir nuestros ficheros .ico, editarlos y luego descargar la modificación.

Favicon Icon Drawing Program Online Free:

Éste editor no es tan potente como el anterior, pero permite realizar iconos simples y descargarlos. No es lo mismo que disponer de un buen editor, pero siempre va bien tener a mano aplicaciones Online, ya que nos pueden sacar de algún apuro. Espero que os sirva de ayuda!.

Thursday, 20 August 2009

Obtener la lista de feeds de tu Blog con Delphi

En éste artículo os mostraré como recuperar el fichero rss de vuestro blog y mostrar las últimas entradas de éste en un programa. Para ésto utilizaré varios ejemplos que he encontrado por la red, y unos de los que más me ha gustado y que está muy bien explicado es el de Zarco Gajic en About.com llamado Reading and manipulating XML Files (RSS) with delphi. En su ejemplo manipula su feed para descargarlo en local y mediante el TXMLDocument recorrer los nodos del documento y visualizarlo todo en un TListView.
Al ser usuario de Blogger, las cosas son un poco más complicadas aquí. Blogger dispone (que yo sepa) de suscripción a la página mediante rss y atom. Éstos dos ficheros los podemos visualizar si nos dirigimos a la dirección:
El problema que tenemos aquí es que los feeds se publican en formato Atom 1.0, y nosostros necesitamos un formato RSS 2.0 para poder leer la estructura del XML. ¿Como solucionamos ésto?. Pues bien, simplemente tenemos que utilizar el comando &alt=rss después de la URL de feed. De ésta manera tendremos un fichero con el formato deseado.

La ruta es la siguiente:
Ahora bien, desde la aplicación que ha creado Zarco Gajic y haciendo unas cuantas modificaciones, podremos leer la lista de nuestros feeds:


Podéis descargar la aplicación Thundax RSS Reader y estar a la última con mis publicaciones!

  • Enlaces de interés:
Reading and manipulating XML Files with delphi.
Display XML Feed in Delphi TreeView.
Ayudas para Blogger.


Implementación de un Canvas para modificar Objetos en Delphi parte I

Poco a poco le voy dando color a mi aplicación, y ahora he desarrollado un Canvas con el que podré modificar mis objetos gráficos. Es decir, utilizaré éste canvas especial para modificar mi clase TBox, la cuál puedo desplazar y redimensionar en tiempo de ejecución. El concepto es muy simple, utilizando un componente TImage, me apodero de su Canvas y utilizo sus métodos OnMouseUp, OnMouseMove y OnMouseDown para realizar la lógica de mi aplicación. Luego añadiendo diferentes funcionalidades a mi clase, le permito al objeto saber si estoy dentro de éste o en una de sus esquinas con el correspondiente comportamiento. Luego a medida que se realizan los desplazamientos se tienen que ir recalculando los diversos puntos de los cuales está formado mi rectángulo. Una vez me sitúo encima de el objeto, el mouse cambia su estado informando de ésto mediante el icono del cursor. La Tbox tiene unas áreas bien definidas y sa van comprobando éstas cada vez mediante los eventos que os he dicho anteriormente. La configuración básica de éste experimento es la siguiente:


Por lo tanto, desde la aplicación, podemos generar lo siguiente:


Un lienzo con la posibilidad de dibujar varias TBox. La creación es directa, apretando el botón de "Draw Rectangle", nos permite directamente ir al lienzo, y mediante 2 puntos dibujar un rectángulo. Una vez hacemos click sobre la TBox, se nos selecciona marcandose en rojo y mostrandonos los vértices. Una vez dentro de la TBox, el mouse cambia para informar que podemos generar un desplazamiento.

Además podemos modificar el tamaño del objeto, situándonos en cualquiera de sus esquinas. Aquí os dejo la versión beta de ésta aplicación "Thundax Box Manager", la cuál aún está en fase de desarrollo, pero si queréis la podéis testear. Aún tengo que modificar varias cosas y mejorar cosas del redimensionado. Os iré informando.

Wednesday, 19 August 2009

Creando un Cliente Web Service en Delphi

En éste artículo os propongo un pequeño ejercicio sobre la creación de un cliente web Service para utilizar un servicio y que nos devuelva información sobre la tabla periódica. Si consultamos la web de xMethods, veréis una lista de servicios web activos, los cuales podemos utilizar para obtener los datos remotos a través de Internet. Por lo tanto, podemos crear diversas aplicaciones y utilizar éstos servicios externos. El servicio que voy a utilizar es el siguiente: Periodic Table de Walter Vijaykumar Jones. Mediante éste servicio, podremos solicitar la información de uno de los elementos de la tabla periódica y obtener diferente información sobre su peso atómico, símbolo, etc.

Desde Delphi, crearemos una nueva VCL application, y luego seleccionamos -> New -> Other -> WSDL importer. Y añadimos el WSDL que nos devuelve la información de la SOAP.

Periodic Table : http://www.webservicex.com/periodictable.asmx?wsdl

Una vez hecho ésto, se crea un fichero PeriodicTable.pas que contiene toda la información de la SOAP.

Aquí os dejo el fichero .pas que nos ha generado el Delphi:

// ************************************************************************ //
// The types declared in this file were generated from data read from the
// WSDL File described below:
// WSDL     : http://www.webservicex.com/periodictable.asmx?wsdl
//  >Import : http://www.webservicex.com/periodictable.asmx?wsdl:0
// Encoding : utf-8
// Version  : 1.0
// (19/08/2009 16:07:16 - - $Rev: 10138 $)
// ************************************************************************ //

unit periodictable;

interface

uses InvokeRegistry, SOAPHTTPClient, Types, XSBuiltIns;

const
  IS_OPTN = $0001;
  IS_REF  = $0080;


type

  // ************************************************************************ //
  // The following types, referred to in the WSDL document are not being represented
  // in this file. They are either aliases[@] of other types represented or were referred
  // to but never[!] declared in the document. The types from the latter category
  // typically map to predefined/known XML or Borland types; however, they could also
  // indicate incorrect WSDL documents that failed to declare or import a schema type.
  // ************************************************************************ //
  // !:string          - "http://www.w3.org/2001/XMLSchema"[Gbl]



  // ************************************************************************ //
  // Namespace : http://www.webserviceX.NET
  // soapAction: http://www.webserviceX.NET/%operationName%
  // transport : http://schemas.xmlsoap.org/soap/http
  // style     : document
  // binding   : periodictableSoap
  // service   : periodictable
  // port      : periodictableSoap
  // URL       : http://www.webservicex.com/periodictable.asmx
  // ************************************************************************ //
  periodictableSoap = interface(IInvokable)
  ['{46CA9F35-B502-F19B-1A59-767555223F6A}']
    function  GetAtoms: WideString; stdcall;
    function  GetAtomicWeight(const ElementName: WideString): WideString; stdcall;
    function  GetAtomicNumber(const ElementName: WideString): WideString; stdcall;
    function  GetElementSymbol(const ElementName: WideString): WideString; stdcall;
  end;

function GetperiodictableSoap(UseWSDL: Boolean=System.False; Addr: string=''; HTTPRIO: THTTPRIO = nil): periodictableSoap;


implementation
  uses SysUtils;

function GetperiodictableSoap(UseWSDL: Boolean; Addr: string; HTTPRIO: THTTPRIO): periodictableSoap;
const
  defWSDL = 'http://www.webservicex.com/periodictable.asmx?wsdl';
  defURL  = 'http://www.webservicex.com/periodictable.asmx';
  defSvc  = 'periodictable';
  defPrt  = 'periodictableSoap';
var
  RIO: THTTPRIO;
begin
  Result := nil;
  if (Addr = '') then
  begin
    if UseWSDL then
      Addr := defWSDL
else
      Addr := defURL;
  end;
  if HTTPRIO = nil then
    RIO := THTTPRIO.Create(nil)
  else
    RIO := HTTPRIO;
  try
    Result := (RIO as periodictableSoap);
    if UseWSDL then
    begin
      RIO.WSDLLocation := Addr;
      RIO.Service := defSvc;
      RIO.Port := defPrt;
    end else
      RIO.URL := Addr;
  finally
    if (Result = nil) and (HTTPRIO = nil) then
      RIO.Free;
  end;
end;


initialization
  InvRegistry.RegisterInterface(TypeInfo(periodictableSoap), 'http://www.webserviceX.NET', 'utf-8');
  InvRegistry.RegisterDefaultSOAPAction(TypeInfo(periodictableSoap), 'http://www.webserviceX.NET/%operationName%');
  InvRegistry.RegisterInvokeOptions(TypeInfo(periodictableSoap), ioDocument);

end.



Una vez hecho ésto, ya podemos utilizar la SOAP. Para ello, tenemos que añadir un componente THTTPRIO a nuestro formulario, y añadir la información necesaria a éste:

Una vez tenemos insertado el componente, su utilización es bien simple, solo tenemos que añadir lo siguiente:

uses
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs, periodicTable, StdCtrls, InvokeRegistry, Rio, SOAPHTTPClient, StrUtils;

type
    TForm3 = class(TForm)
        Button1: TButton;
        Memo1: TMemo;
        HTTPRIO1: THTTPRIO;
        Edit1: TEdit;
        procedure Button1Click(Sender: TObject);
    private
        { Private declarations }
    public
        { Public declarations }
    end;

var
    Form3: TForm3;

implementation

{$R *.dfm}

procedure TForm3.Button1Click(Sender: TObject);
var
    wf: string;
    i: integer;
begin
    if Edit1.text = '' then
        Exit;
    memo1.Lines.clear;
    wf := (HTTPRIO1 as periodictableSoap).GetAtomicNumber(Edit1.text);
    wf := AnsiReplaceStr(wf, chr(10), chr(13) + chr(10));
    memo1.Lines.Add(wf);
    wf := (HTTPRIO1 as periodictableSoap).GetAtomicWeight(Edit1.text);
    wf := AnsiReplaceStr(wf, chr(10), chr(13) + chr(10));
    memo1.Lines.Add(wf);
    wf := (HTTPRIO1 as periodictableSoap).GetElementSymbol(Edit1.text);
    wf := AnsiReplaceStr(wf, chr(10), chr(13) + chr(10));
    memo1.Lines.Add(wf);
end;


Et voilà!, ya tenemos nuestra aplicación utilizando un servicio externo. Aquí os dejo mi aplicación Thundax Periodic Table, que utiliza el Servicio Web de Walter Vijaykumar Jones para mostrar información sobre la tabla periódica.

Aquí os dejo una imagen de mi aplicación:

Espero que os sirva de ayuda.

  • Enlaces de interés:
Creating web service client with delphi.

Implementando un Abstract Factory

Continuando con mi aplicación Thundax Geometry Link, necesito implementar un patrón Abstract Factory. Mediante éste patrón crearé diferentes familias sin especificar su clase concreta. Éste patrón es ideal cuando queremos aislar nuestra aplicación de la implementación de clases concretas. En el siguiente ejemplo podréis ver como genero la TAbstractFactory, que es la que me permitirá poder crear los objetos que yo quiero, una TBox, una TConnector y una TEllipse, pero que éstas haré que deriven de la TAbstractBox, TAbstractConnector y TAbstractEllipse, porqué quiero poder implementar varias familias por debajo de TAbstractBox sin que la factoría tenga que saberlo. Luego crearé 2 factorías, una simple y una extendida, donde configuraré la creación de mis clases. De ésta manera, en cada constructor de la factoría especificamos el objeto a devolver, sin especificar la clase concreta. En los ejemplos lo veréis más claro.

El diagrama UML de mi ejemplo es el siguiente (Esquematizado):

Como podéis comprobar, antes solo existía la clase TBox, y ahora el diseño ha cambiado un poco. Ahora dispongo de mi TAbstractBox de la cuál hago heredar para conseguir diversos tipos de caja, en éste caso la TBox genérica y la TBoxExtended, la cuál dispondrá de más opciones y propiedades. Lo mismo pasa con las 2 otras clases. Pensad que el diseño de la aplicación va cambiado día a día y que ésto puede no ser algo definitivo.

Bien, ahora para el diseño de la factoría tenemos:

Como podéis comprobar, las 2 factorías devuelven el mismo tipo de componente, pero en cada una se definen clases diferentes, aquí la poténcia de la Abstract Factory.

Mi diagrama vendría a ser algo parecido, en el diagrama UML de ejemplo que hay en la web de A&P Web Consulting:


Queda bastante detallado en el diagrama, y en la wikipedia también encontraréis más información. Mirad también en los enlaces de interés, que he dejado también algún link.

El código fuente del ejemplo es el siguiente:

TAbstractBox, TAbstractConnector y TAbstractEllipse:

type
    TAbstractBox = class(TObject)
        function ToString(): string; virtual; abstract;
    end;

    TBox = class(TAbstractBox)
        function ToString(): string; override;
    end;

    TBoxExtended = class(TAbstractBox)
        function ToString(): string; override;
    end;

    TAbstractConnector = class(TObject)
        function ToString(): string; virtual; abstract;
    end;

    TConnector = class(TAbstractConnector)
        function ToString(): string; override;
    end;

    TConnectorExtended = class(TAbstractConnector)
        function ToString(): string; override;
    end;

    TAbstractEllipse = class(TObject)
        function ToString(): string; virtual; abstract;
    end;

    TEllipse = class(TAbstractEllipse)
        function ToString(): string; override;
    end;

    TEllipseExtended = class(TAbstractEllipse)
        function ToString(): string; override;
    end;
  

{ TBox }

function TBox.ToString: string;
begin
    result := 'This is a ' + Self.ClassName;
end;

{ TConnector }

function TConnector.ToString: string;
begin
    result := 'This is a ' + Self.ClassName;
end;

{ TEllipse }

function TEllipse.ToString: string;
begin
    result := 'This is a ' + Self.ClassName;
end;

{ TEllipseExtended }

function TEllipseExtended.ToString: string;
begin
    result := 'This is a ' + Self.ClassName;
end;

{ TConnectorExtended }

function TConnectorExtended.ToString: string;
begin
    result := 'This is a ' + Self.ClassName;
end;

{ TBoxExtended }

function TBoxExtended.ToString: string;
begin
    result := 'This is a ' + Self.ClassName;
end;  

TAbstractFactory, TMySimpleFactory y TMyExtendedFactory:

type
    TAbstractFactory = class(TObject)
    public
        constructor Create;
        destructor Destroy; override;
        function GetBox(): TAbstractBox; virtual; abstract;
        function GetConnector(): TAbstractConnector; virtual; abstract;
        function GetEllipse(): TAbstractEllipse; virtual; abstract;
    end;

    TMySimpleFactory = class(TAbstractFactory)
    public
        constructor Create;
        destructor Destroy; override;
        function GetBox(): TAbstractBox; override;
        function GetConnector(): TAbstractConnector; override;
        function GetEllipse(): TAbstractEllipse; override;
    end;

    TMyExtendedFactory = class(TAbstractFactory)
    public
        constructor Create;
        destructor Destroy; override;
        function GetBox(): TAbstractBox; override;
        function GetConnector(): TAbstractConnector; override;
        function GetEllipse(): TAbstractEllipse; override;
    end;

{ TAbstractFactory }

constructor TAbstractFactory.Create;
begin

end;

destructor TAbstractFactory.Destroy;
begin

    inherited;
end;

{ TMySimpleFactory }

constructor TMySimpleFactory.Create;
begin

end;

destructor TMySimpleFactory.Destroy;
begin

    inherited;
end;

function TMySimpleFactory.GetBox: TAbstractBox;
begin
    result := TBox.Create;
end;

function TMySimpleFactory.GetConnector: TAbstractConnector;
begin
    result := TConnector.Create;
end;

function TMySimpleFactory.GetEllipse: TAbstractEllipse;
begin
    result := TEllipse.Create;
end;

{ TMyExtendedFactory }

constructor TMyExtendedFactory.Create;
begin

end;

destructor TMyExtendedFactory.Destroy;
begin

    inherited;
end;

function TMyExtendedFactory.GetBox: TAbstractBox;
begin
    result := TBoxExtended.Create;
end;

function TMyExtendedFactory.GetConnector: TAbstractConnector;
begin
    result := TConnectorExtended.Create;
end;

function TMyExtendedFactory.GetEllipse: TAbstractEllipse;
begin
    result := TEllipseExtended.Create;
end;

Ejemplo de utilización:

procedure TForm1.Button1Click(Sender: TObject);
begin
    Factory := TMySimpleFactory.Create;
    Memo1.Lines.add(Factory.GetBox.ToString);
    Memo1.Lines.add(Factory.GetConnector.ToString);
    Memo1.Lines.add(Factory.GetEllipse.ToString);

    FactoryExtended := TMyExtendedFactory.Create;
    Memo1.Lines.add(FactoryExtended.GetBox.ToString);
    Memo1.Lines.add(FactoryExtended.GetConnector.ToString);
    Memo1.Lines.add(FactoryExtended.GetEllipse.ToString);
end;

Como resultado del ejemplo obtendremos:


  • Enlaces de interés:
Abstract Factory in Delphi.

más sobre Google Insight for Search

Esta mañana he leído el post de uno de los blogs que sigo, "Apuntes, son solo apuntes", donde en una de sus entradas Fernando Bordignon, nos presenta Google Insight for Search, y la verdad es que me ha gustado tanto la aplicación, que he estado haciendo comprobaciones sobre los diferentes lenguajes de programación y la verdad es que es bastante sorprendente. Si en la búsqueda por ejemplo buscamos "Delphi", la verdad es que obtenemos lo siguiente:


Como podéis ver según las búsquedas, pierde tirada y se espera que el interés baje hasta el 13%. Los países más interesados son cuba, kazajistán y Bielorussia. La verdad es que me sorprende bastante.

Lo más sorprendente es por ejemplo que los lenguajes que más se buscan son Python, Ruby y C++.

Python:


Ruby:


C++:


Otros lenguajes como Java tienen la misma previsión que Delphi, de ir cayendo año tras año. Esperemos que la cosa vuelva a ser de interés general. Ahora solo tenéis que entrar en la web de google insight y hacer vuestras propias comprobaciones.

Tuesday, 18 August 2009

Codificar y descodificar un String en Base 64

En una de mis aplicaciones, utilicé el siguiente código adaptado por Jacob Dybala (el código original es de David Barton) donde se nos muestra un algoritmo simple de codificación y descodificación de un string en base64. Éste ejemplo funcionó muy bien para codificar una serie de passwords y luego almancenarlos en una BD. Podemos variar el algoritmo a nuestro gusto por si queremos añadir un charset diferente o mejorar alguna cosa de su implementación. Aunque en la entrada de Jacob Dybala, él mejora y optimiza bastante el tema del espacio utilizado. Para éste ejemplo desarrollé una pequeña aplicación llamada Thundax Encode/Decode String Base64, y la podréis descargar para comprobar si os gusta la salida del algoritmo. También podréis encontrar varios codificadores base64 online, aquí os dejo varios enlaces para que los probéis:
  1. base64 Online.
  2. base64 Online.
Código fuente:

unit String64;

interface

uses
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs, StdCtrls;

const
    B64Table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';

function Base64Decode(const S: string): string;
function Base64Encode(const S: string): string;

implementation

{$R *.dfm}

function Base64Encode(const S: string): string;
var
    InBuf: array[0..2] of Byte;
    OutBuf: array[0..3] of Char;
    iI, iJ: Integer;
begin
    SetLength(Result, ((Length(S) + 2) div 3) * 4);
    for iI := 1 to ((Length(S) + 2) div 3) do
    begin
        if Length(S) < (iI * 3) then
            Move(S[(iI - 1) * 3 + 1], InBuf, Length(S) - (iI - 1) * 3)
        else
            Move(S[(iI - 1) * 3 + 1], InBuf, 3);
        OutBuf[0] := B64Table[((InBuf[0] and $FC) shr 2) + 1];
        OutBuf[1] := B64Table[(((InBuf[0] and $3) shl 4) or ((InBuf[1] and $F0) shr 4)) + 1];
        OutBuf[2] := B64Table[(((InBuf[1] and $F) shl 2) or ((InBuf[2] and $C0) shr 6)) + 1];
        OutBuf[3] := B64Table[(InBuf[2] and $3F) + 1];
        Move(OutBuf, Result[(iI - 1) * 4 + 1], 4);
    end;
    if Length(S) mod 3 = 1 then
    begin
        Result[Length(Result) - 1] := '=';
        Result[Length(Result)] := '=';
    end
    else if Length(S) mod 3 = 2 then
        Result[Length(Result)] := '=';
end;

function Base64Decode(const S: string): string;
var
    OutBuf: array[0..2] of Byte;
    InBuf: array[0..3] of Byte;
    iI, iJ: Integer;
begin
    if Length(S) mod 4 <> 0 then
        raise Exception.Create('Base64: Incorrect string format');
    SetLength(Result, ((Length(S) div 4) - 1) * 3);
    for iI := 1 to (Length(S) div 4) - 1 do
    begin
        Move(S[(iI - 1) * 4 + 1], InBuf, 4);
        for iJ := 0 to 3 do
            case InBuf[iJ] of
                43: InBuf[iJ] := 62;
                48..57: Inc(InBuf[iJ], 4);
                65..90: Dec(InBuf[iJ], 65);
                97..122: Dec(InBuf[iJ], 71);
            else
                InBuf[iJ] := 63;
            end;
        OutBuf[0] := (InBuf[0] shl 2) or ((InBuf[1] shr 4) and $3);
        OutBuf[1] := (InBuf[1] shl 4) or ((InBuf[2] shr 2) and $F);
        OutBuf[2] := (InBuf[2] shl 6) or (InBuf[3] and $3F);
        Move(OutBuf, Result[(iI - 1) * 3 + 1], 3);
    end;
    if Length(S) <> 0 then
    begin
        Move(S[Length(S) - 3], InBuf, 4);
        if InBuf[2] = 61 then
        begin
            for iJ := 0 to 1 do
                case InBuf[iJ] of
                    43: InBuf[iJ] := 62;
                    48..57: Inc(InBuf[iJ], 4);
                    65..90: Dec(InBuf[iJ], 65);
                    97..122: Dec(InBuf[iJ], 71);
                else
                    InBuf[iJ] := 63;
                end;
            OutBuf[0] := (InBuf[0] shl 2) or ((InBuf[1] shr 4) and $3);
            Result := Result + Char(OutBuf[0]);
        end
        else if InBuf[3] = 61 then
        begin
            for iJ := 0 to 2 do
                case InBuf[iJ] of
                    43: InBuf[iJ] := 62;
                    48..57: Inc(InBuf[iJ], 4);
                    65..90: Dec(InBuf[iJ], 65);
                    97..122: Dec(InBuf[iJ], 71);
                else
                    InBuf[iJ] := 63;
                end;
            OutBuf[0] := (InBuf[0] shl 2) or ((InBuf[1] shr 4) and $3);
            OutBuf[1] := (InBuf[1] shl 4) or ((InBuf[2] shr 2) and $F);
            Result := Result + Char(OutBuf[0]) + Char(OutBuf[1]);
        end
        else
        begin
            for iJ := 0 to 3 do
                case InBuf[iJ] of
                    43: InBuf[iJ] := 62;
                    48..57: Inc(InBuf[iJ], 4);
                    65..90: Dec(InBuf[iJ], 65);
                    97..122: Dec(InBuf[iJ], 71);
                else
                    InBuf[iJ] := 63;
                end;
            OutBuf[0] := (InBuf[0] shl 2) or ((InBuf[1] shr 4) and $3);
            OutBuf[1] := (InBuf[1] shl 4) or ((InBuf[2] shr 2) and $F);
            OutBuf[2] := (InBuf[2] shl 6) or (InBuf[3] and $3F);
            Result := Result + Char(OutBuf[0]) + Char(OutBuf[1]) + Char(OutBuf[2]);
        end;
    end;
end;

procedure TForm3.Button1Click(Sender: TObject);
begin
    if Edit1.text = '' then
        exit;
    Edit2.text := Base64Encode(Edit1.text);
end;

procedure TForm3.Button2Click(Sender: TObject);
begin
    if Edit2.text = '' then
        exit;
    Edit1.text := Base64Decode(Edit2.text);
end;

end.


Aplicación:


Espero que os guste el algoritmo, ya que en mi opinión creo que hay que tenerlo siempre a mano.
  • Enlaces de interés:
How to Base 64 (MIME) Encode and Decode a String

Formateando código Delphi.

Mientras espero la última versión de Delphi 2010 suelo utilizar el componente Delphi Formatter de DelForExp para el IDE de Delphi. Aunque es de pago funciona bastante bien, porqué está integrado dentro del mismo IDE y con un par de teclas se formatea el código para que quede más bonito y entendible. Muchas veces si no tengo el IDE a mano, utilizo una aplicación Open Source llamada Jedi Code Format, que permite coger los ficheros .pas y formatearlos. Además podemos configurar todas las opciones que queramos para que el formato se ajuste a lo que nosotros queremos.


Con ésta aplicación podremos formatear nuestro código rápidamente simplemente configurando unos cuántos parámetros. Aquí os dejo mi fichero de configuración: JCFSettings.cfg, por si queréis probarlo, ya que de serie viene sin configurar y es un rollo tener que configurar todos los parámetros uno por uno.