Ejemplos de programación paralela



La utilización de la programación paralela puede incrementar drásticamente la velocidad de ejecución de nuestros programas, por ejemplo imaginar el caso en que tengamos dos tareas A y B, una se ejecuta en 5 segundos y otra en 10 segundos entonces:

- Si las ejecutamos secuencialmente, como se hace habitualmente, tardaríamos en terminarlas 15 segundos
- Si utilizamos WaitForAll tardaríamos 15
- Si utilizamos WaitForAny tardaríamos 5.

La Biblioteca de Programación Paralela (PPL) que incorpora Rad Studio Tokyo proporciona una clase TTask para ejecutar una o múltiples tareas en paralelo, y además añade unos métodos que se utilizan para sincronizar o para detener dichas tareas. A continuación tenéis unos ejempl
os de uso:

Utilizando un array de ITask.


procedure TFormThreading.MyButtonClick(Sender: TObject);
var
 tasks: array of ITask;
 value: Integer;
begin
 Setlength (tasks ,2);
 value := 0;

 tasks[0] := TTask.Create (procedure ()
   begin
     sleep (3000); // 3 seconds
     TInterlocked.Add (value, 3000);
   end);
 tasks[0].Start;

 tasks[1] := TTask.Create (procedure ()
   begin
     sleep (5000); // 5 seconds
     TInterlocked.Add (value, 5000);
   end);
 tasks[1].Start;

 TTask.WaitForAll(tasks);
 ShowMessage ('All done: ' + value.ToString);
end; 

Iniciar una tarea en background que no bloquea al programa principal:

procedure TFormThreading.Button1Click(Sender: TObject);
var
 aTask: ITask;
begin
 // not a thread safe snippet
 aTask := TTask.Create (procedure ()
   begin
     sleep (3000); // 3 seconds
     ShowMessage ('Hello');
   end);
 aTask.Start;
end;

Hay que tener cuidado con los interbloqueos asociados con las variables que se utilicen (se debe utilizar los métodos tInterlocked de la unit System.SyncObjs). TInterlocked implementa operaciones con el propósito de asegurar que el “thread” o el “multi-core” sea seguro y estable cuando se modifican variables que pueden ser accedidas durante la ejecución de múltiples threads. Ejemplo de utilización de tInterlocked.

var iCount: Integer;
begin
  iCount := 0;
  TParallel.&For(1, 10,
    procedure (Current: Integer)
    begin
      TInterlocked.Add(iCount, Current);
    end
  );
end;

Ejemplo de utilización de tInterlocked para hacer una suma utilizando variables globales:

 Se utilizan varios procedimientos:
 TInterlocked.Exchange(iSum, 0); copia el valor “0” a la variable iSum
TInterlocked.Add(iSum, Index); suma el valor Index a la variable iSum y lo devuelve en iSum TInterlocked.Read(iSum); Lee el valor de la variable iSum
Como véis no es obligatorio que tInterlocked se sitúe siempre dentro del bloque tTask.Run
 
VAR
iSum:integer;
procedure TFormMain.button1(Sender: TObject);
begin
//calcula iSum:=0;
  TInterlocked.Exchange(iSum, 0);
  TTask.Run(
    procedure
    begin
      TParallel.For(0, 10,
        procedure (Index: Integer)
        begin
         //calcula iSum := iSum + Index;
          TInterlocked.Add(iSum, Index);
        end);
    end);
end;
//lee cada segundo la variable global iSum en un tTimer
procedure TFormMain.Timer1Timer(Sender: TObject);
begin
 LabelSum.Text:= TInterlocked.Read(iSum);
end;

BUCLES FOR

 La PPL proporciona una función tParallel.For que permite realizar bucles For en paralelo. Equivalencia entre un “for” secuencial y un “for” en paralelo. 

EJEMPLO: BUCLE FOR SIMPLE (NO BLOQUEANTE) utilizando tThread.queue,

Internamente se espera a que la CPU esté en estado Idle).


procedure TFormMain.ButtonStartParallelForClick(Sender: TObject);
begin
  Memo1.Lines.Clear;
  TTask.Run(
    procedure
    begin
      TParallel.For(0, 9,
        procedure (Index: Integer)
        begin
          Sleep(200);
          TThread.Queue(TThread.CurrentThread,
            procedure
            begin
              Memo1.Lines.Add(Index.ToString);
            end);
        end);
    end);
end;

Ejemplo. Cálculo de los números primos menores o iguales a 5000000

uses
  System.Threading;
  System.Diagnostics;
  System.SyncObjs;


const
  Max =5000000;

procedure TForm1.btnForLoopClick(Sender: TObject);
var
  I, Tot: Integer;
  SW: TStopwatch;
begin
    // counts the prime numbers below a given value
   Tot:=0;
   SW := TStopWatch.Create;
   SW.Start;
   for I := 1 to Max do
   begin
     if IsPrime(I) then
        Inc(Tot);
   end;
   SW.Stop;
   Memo1.Lines.Add(Format('Sequential For loop. Time (in milliseconds): %d - Primes found: %d', [SW.ElapsedMilliseconds,Tot]));
end;



procedure TForm1.btnParallelForClick(Sender: TObject);
var
  Tot: Integer;
  SW: TStopwatch;
begin
     try
     // counts the prime numbers below a given value
       Tot :=0;
       SW :=TStopWatch.Create;
       SW.Start;
//el primer parámetro “2” del bucle FOR es opcional e identifica cómo 
//se agrupan los threads al ser ejecutados
       TParallel.For(2,1,Max,procedure(I:Int64)
       begin
         if IsPrime(I) then
          TInterlocked.Increment(Tot);
       end);
     SW.Stop;
      Memo1.Lines.Add(Format('Parallel For loop. Time (in milliseconds): %d - Primes found: %d', [SW.ElapsedMilliseconds,Tot]));
     except on E:EAggregateException do
      ShowMessage(E.ToString);
     end;
end;

function IsPrime(N: Integer): Boolean;
var
  Test, k: Integer;
begin
  if N <= 3 then
    IsPrime := N > 1
  else if ((N mod 2) = 0) or ((N mod 3) = 0) then
    IsPrime := False
  else
  begin
    IsPrime := True;
    k := Trunc(Sqrt(N));
    Test := 5;
    while Test <= k do
    begin
      if ((N mod Test) = 0) or ((N mod (Test + 2)) = 0) then
      begin
        IsPrime := False;
        break; { jump out of the for loop }
      end;
      Test := Test + 6;
    end;
  end;
end;

El resultado muestra que el bucle en paralelo for es mucho más eficiente que el secuencial; 
 

Ejemplo de descarga de una página web utilizando ttask:

procedure TMyForm.StartDownloadTask(lPath: string)
begin
    TTask.Create(
      procedure
      var
        lHTTP: TIdHTTP;
        IdSSL: TIdSSLIOHandlerSocketOpenSSL;
      begin
        lHTTP := TIdHTTP.Create(nil);

        TThread.Synchronize(nil,
          procedure
          begin
            Form1.Caption := 'Task Running...';
          end
        );

        try
          lHTTP.ReadTimeout := 30000;
          lHTTP.HandleRedirects := True;
          IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(lHTTP);
          IdSSL.SSLOptions.Method := sslvTLSv1;
          IdSSL.SSLOptions.Mode := sslmClient;
          lHTTP.IOHandler := IdSSL;
        Finally
          try
            lHTTP.Get('http://website.com/'+lPath, TStream(nil));
          Finally
            lHTTP.Free;
          end;
        end;

        TThread.Synchronize(nil,
          procedure
          begin
            Memo1.Lines.Add(lPath);
           end
        );

      end
    ).Start;
end;

Ejemplo que utiliza TParallel.for para detener la ejecución de todos los threads cuando se encuentra una respuesta.
Utiliza TParallel.LoopState para avisar a otros procesos que utilizan el loop paralelo. Utilizando la señal “Stop” todas las iteraciones se detienen. Las iteraciones en curso deberían chequear loopState.Stopped.


procedure Parallel3(CS: TCriticalSection);
var
  Ticks: Cardinal;
  i,ix: Integer;  // variables that are only touched once in the Parallel.For loop
begin
  i := 0;
  Ticks := TThread.GetTickCount;
  TParallel.For(1,WorkerCount,
    procedure(index:Integer; loopState: TParallel.TLoopState)
    var
      k,l,m: Integer;
    begin
      // Do something complex
      k := (1000 - index)*1000;
      for l := 0 to Pred(k) do
        m := k div 1000;
      // If criteria to stop fulfilled:
      CS.Enter;
      Try
        if loopState.Stopped then // A solution was already found
          Exit;
        loopState.Stop;  // Signal 
        Inc(i);
        ix := index;
      Finally
        CS.Leave;
      End;
    end
  );
  Ticks := TThread.GetTickCount - Ticks;
  WriteLn('Parallel time ' + Ticks.ToString + ' ticks', ' i :',i,' index:',ix);
end;

EJEMPLO: Utilización de Ttask para dibujar rectángulos, utilizando o no tSyncronize

// Author: Danny Wind
// http://dannywind.nl/wp-content/uploads/2015/10/CodeRageX_DelphiParallelProgrammingDeepDive_DannyWind.zip

procedure TFormMain.ButtonStartParallelForClick(Sender: TObject);
var
  lStride, lFrom, lTo: Integer;
begin
  ClearRectangles;
  lFrom := Low(FRectangles);
  lTo := High(FRectangles);
  lStride := Trunc(NumberBoxStride.Value);
  {TTask.Run executes the entire parallel for loop itself in parallel,
   which enables you to actually see the progress of the for-loop}
  TTask.Run(procedure
            begin
              TParallel.For(lStride, lFrom, lTo, PaintRectangle)
            end);
end;

procedure TFormMain.PaintRectangle(Index: Integer);
var
  lR,lG,lB: Byte;
  lColor:TAlphaColor;
begin
  lR := TThread.CurrentThread.ThreadID MOD High(Byte);
  lG := (2 * lR) MOD High(Byte);
  lB := (4 * lR) MOD High(Byte);
  lColor := MakeColor(lR, lG, lB, High(Byte));
  if CheckBoxSync.IsChecked then
    TThread.Synchronize(TThread.CurrentThread,
        procedure
        begin
          FRectangles[Index].Fill.Color := lColor;
          TLabel(FRectangles[Index].TagObject).Text := Index.ToString;
          FRectangles[Index].TagString := TimeToStr(Now);
          FRectangles[Index].Repaint;
        end)
  else
  begin
    FRectangles[Index].Fill.Color := lColor;
    TLabel(FRectangles[Index].TagObject).Text := Index.ToString;
    FRectangles[Index].TagString := TimeToStr(Now);
    FRectangles[Index].Repaint;
  end;
  Sleep(100);
end;

Para el cálculo de tiempo que tarda la ejecución de procedimientos os recomiendo utilizar la clase tStopWatch de la unit System.SyncObjs, de esta forma:

Var
Sw:tStopWatch;

begin


   SW := TStopWatch.Create;
   SW.Start;

   for I := 1 to Max do
   begin

   …     

   end;

   SW.Stop;
   showmessage(Format('Tiempo empleado (en milisegundos): %d', [SW.ElapsedMilliseconds]));

end;

Evitar la propagación del virus WannaCry

Mi Opinión sobre Wanacrypt0r

Desde hace unos días se está hablando del daño causado por el ransomware WannaCry en todo el mundo, recordemos que lo que hace es cifrar los archivos de vuestro PC y luego pedir un "rescate" de los mismos pagando en bitcoins (unos 300 Euros).
El dropper, con el payload, llega por email al PC y cuando se ejecuta comienza a cifrar archivos.
Hasta ahora nadie ha descubierto cómo descifrar los archivos encriptados por este ransomware, por lo que la única opción es tirar de la copia de seguridad que deberíamos tener preparada.
Todo se remonta a marzo de 2017 cuando se robaron por parte del grupo llamado "Shadow Brokers" una serie de exploits y herramientas de seguridad procedentes de la Agencia de Seguridad Nacional (NSA) no descubiertas anteriormente (EthernalBlue-DoblePulsar) y que inmediatamente se pusieron a la venta en la Deep Web, ambas permiten que un atacante envíe paquetes específicamente creados a un servidor Microsoft Server Message Block 1.0 (SMBv1) (Puerto 445)
WannaCry busca equipos que tengan expuesto SMBv1 para atacarlos y una vez dentro escanea la red interna para buscar ficheros y equipos vulnerables.

Características del virus:

Carga el código dañino en el sistema. 
Crea copias en el sistema. 
Crea entradas de persistencia en el Registro de Windows. 
Cifra todos los archivos en todas las unidades que cumplan un patrón de extensión. 
Muestra información acerca del secuestro de los archivos y solicita un rescate para su recuperación. 
Ejecuta determinados programas incluidos en el código dañino. 
Finaliza determinados procesos de bases de datos para poder cifrar sus archivos. 
Elimina las Shadow Copies del sistema.

Los sistemas afectados son los siguientes:
  • Windows 7
  • Windows 8
  • Windows 8.1
  • Windows RT 8.1
  • Windows Server 2008 SP2 y R2 SP1
  • Microsoft Windows Vista SP2 Windows 8.1
  • Windows Server 2012 y R2
  • Windows Server 2016
  • Windows XP
  • Windows Vista
  • Windows Server 2003
Microsoft lanzó un parche (MS17-010), en su momento para solucionar esta vulnerabilidad.

Un analista de MalwareTech descubrió que el virus para iniciarse buscaba un dominio en Internet (iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea.com) y en caso de no existir empezaría a cifrar archivos, por lo que la solución era registrar el anterior dominio en Internet, de esta forma el virus no se iniciaba y se paraba la infección.
En las siguientes versiones, según he leído, esto ya no sucede.

Un detalle técnico:
EthernalBlue se puede utilizar para atacar sistemas Windows 7 y Windows Server 2008 R2 sin necesidad de autenticación y DoblePulsar se utiliza para inyectar una dll y abrir un backdoor en el sistema, creando un acceso remoto al sistema.

Cómo se evita la propagación:

1) Aplicar el parche MS017-010 a los sistemas windows anteriores  https://technet.microsoft.com/en-us/library/security/ms17-010.aspx

2) Desactivar manualmente el protocolo SMB : Panel de Control – Programas y Características – Activar o desactivar características de Windows – desactivar “Compatibilidad con el protocolo para compartir archivos SMB 1.0/CIFS”.

3) Se deben bloquear las conexiones entrantes a puertos SMB (139 y 445) desde equipos externos a la red.

4) Activar el modo LOCK en todos los perfiles de usuarios.

5) Eliminar el servicio con las siguientes caracteristicas: 
Nombre: mssecsvc2.0 
Descripción: Microsoft Security Center (2.0) Service 
Ruta: %WINDIR%\mssecsvc.exe 
Comando: %s -m security 

6) Eliminar las entradas del registro:

HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /v "mzaiifkxcyb819" /t REG_SZ /d "\"C:\WINDOWS\tasksche.exe\"" /f 

HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /v “RANDOM_CHARS” /t REG_SZ /d '\%COMMON_APPDATA%\tasksche.exe\'' /f 

7) Eliminar los archivos:

@Please_Read_Me@.txt 
@WanaDecryptor@.exe

8) Por supuesto habilitar un firewall, antivirus, ...

Para comprobar si hemos sido infectados, verificar en el regedit que existen las siguientes rutas:

1) C:\Users\<...>\AppData\Local\Temp\@WanaDecryptor@.exe.lnk



2) C:\Users\<...>\AppData\Local\Temp\@WanaDecryptor@.exe



3) HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\ = “\tasksche.exe”

4) HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /v "mzaiifkxcyb819" /t REG_SZ /d "\"C:\WINDOWS\tasksche.exe\"" /f 

5) HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /v “RANDOM_CHARS” /t REG_SZ /d '\%COMMON_APPDATA%\tasksche.exe\'' /f 



6) HKLM\SOFTWARE\WanaCrypt0r\wd = “”



7) HKCU\Control Panel\Desktop\Wallpaper: “\@WanaDecryptor@.bmp”

8) Verificar si existe un servicio con las características siguientes
Nombre: mssecsvc2.0 
Descripción: Microsoft Security Center (2.0) Service 
Ruta: %WINDIR%\mssecsvc.exe 
Comando: %s -m security 

9) La existencia de archivos con la extensión “.wnry” es un indicador del compromiso del sistema. 

10) Comprobar si existen alguno de los siguientes archivos:


El CERT ha publicado una herramienta que mitiga la ejecución del virus WannaCry, que a día de hoy es de obligada instalación si queremos evitar sus efectos.










Obtener datos de los satélites que utiliza el GPS



Como sabéis las coordenadas que establece el GPS de vuestro dispositivo son el resultado de complejos cálculos matemáticos que se realizan en función de la señal de los satélites que capta, estas coordenadas pueden tener una precisión de centímetros (si se utiliza GPS diferencial) aunque lo normal es que la precisión para uso civil sea de varios metros.

Para calcular la posición se necesita como mínimo la información de 3 satélites, para ello cada uno envía su id y la hora de su reloj interno y en base a estos datos el GPS calcula el tiempo que tarda en llegar la señal; posteriormente utilizando un método matemático llamado trilateración inversa calcula la latitud y longitud de tu posición.

Android nos proporciona una serie de procedimientos que nos informan de los parámetros de cada satélite: elevación, azimut, nivel de ruido de la señal, etc...
Realmente no es tan sencillo como pudiera parecer inicialmente llegar a estos datos ya que además de utilizar el servicio del sistema "LOCATION_SERVICE" hay que configurar la clase tGPSListener que automáticamente genera eventos que nos avisan del instante en el que el GPS se activa, se desactiva, del tiempo del primer "fix", y -lo que nos interesa en este caso- cada vez que se recopile información de los satélites, que se hace mediante el disparo del evento "GPS_EVENT_SATELLITE_STATUS".

Una vez que se dispare capturaremos los datos de esta forma:
 
 satelites := FLocationManager.getGpsStatus(NIL).getSatellites;

Después recorreremos "satelites" para obtener la información asociada a cada uno de ellos tecleando lo siguiente:


satelite := tjgpssatellite.wrap((Objeto AS ILocalObject).GetObjectID);

con lo que obtendremos:

satelite.getAzimut()  : el azimut del satélite en grados
satelite.getElevation()  :  la elevacion en grados
satelite.getPrn: Un número pseudo aleatorio
satelite.getSnr: El ruido de la señal
satelite.hasAlmanac(): si tiene datos de almanaque
satelie.hasEphemeris(): si tiene datos de efemérides
satelite.usedInFix(): si se usa para fijar la posición

El procedimiento que he utilizado es el siguiente:


....

tform1 = class(tform)

   ..VARIABLES..;  

PROCEDURE onGpsStatusChanged(event: Integer);
PROCEDURE IniciarGPS;

PUBLIC
 { Public declarations }
PRIVATE
 { Private declarations }
    satelites: jIterable;
    FLocationManager: JLocationManager;
    locationListener: TLocationListener;
    LocationManagerService: JObject;
END; //tform1

// Definimos la clase TGpsListener

TGpsListener = CLASS(TJavaLocal, JGpsStatus_Listener)
  PRIVATE
    FParent: Tform1;
  PUBLIC
    CONSTRUCTOR Create(AParent: Tform1);
    PROCEDURE onGpsStatusChanged(event: Integer); CDECL;
END;

CONSTRUCTOR TGpsListener.Create(AParent: TfGPS);
BEGIN
  INHERITED Create;
  FParent := AParent;
END;

PROCEDURE TGpsListener.onGpsStatusChanged(event: Integer);
BEGIN
    FParent.onGpsStatusChanged(event);
END;
....


PROCEDURE tform1.IniciarGPS;
BEGIN
  LocationManagerService := tAndroidHelper.context.getSystemService(TJContext.JavaClass.LOCATION_SERVICE);
  FLocationManager := TJLocationManager.wrap((LocationManagerService AS ILocalObject).GetObjectID);
  GPSListener := TGpsListener.Create(self);
END;

PROCEDURE tform1.onGpsStatusChanged(event: Integer);
VAR
  satelite: jgpssatellite;
  Objeto: JObject;
  iter: jiterator;

BEGIN


  //SI SE DISPARA EL EVENTO gps_event_satellite_status...
  IF event = tjgpsstatus.JavaClass.GPS_EVENT_SATELLITE_STATUS THEN
    BEGIN

      IF assigned(FLocationManager) THEN
        BEGIN

            //OBTENGO LOS DATOS DE TODOS LOS SATELITES
            satelites := FLocationManager.getGpsStatus(NIL).getSatellites;
            iter := form1.satelites.iterator;
            
            // RECORRO LA LISTA UNO A UNO PARA OBTENER SUS CARACTERISTICAS  
            Objeto := NIL;
            WHILE iter.hasnext DO
              BEGIN
                Objeto := iter.next;
                IF assigned(Objeto) THEN
                  BEGIN
                      
                      //OBTENGO UN SATELITE DE LA LISTA

                      satelite := tjgpssatellite.wrap((Objeto AS ILocalObject).GetObjectID);
                      //Ojo, no confundir la variable satelites:JIterable con satelite: jgpssatellite
                      // LEO SUS CARACTERISTICAS
                      Label1.text := satelite.getSnr; // 'Ruido        '
                      Label2.text := satelite.getPrn;
                      Label3.text := satelite.getAzimuth.Tostring; // 'Azimuth      '
                      label4.text := satelite.getElevation.Tostring; // 'Elevación    '

                      { 
                        OTROS PARAMETROS DEL SATELITE
                        satelite.hasAlmanac
                        satelite.HasEphemeris
                        satelite.usedInFix
                      }               

                  END;

              END;
        END;
    END;
END;

En la app GPS TOTAL RUN  en el menú "Satélites" tenéis un ejemplo de como quedaría todo lo expuesto anteriormente.

Presentación de la nueva versión Delphi y C++ Builder Tokyo


Os comunico que Danysoft, distribuidor oficial de Delphi en España de los productos de Embarcadero, ha preparado unas jornadas de presentación (9 de Mayo en Lisboa, 10 de Mayo en Barcelona y 11 de Mayo en Madrid) del nuevo Delphi y C++Builder Tokyo que como sabéis actualmente es el entorno más avanzado para crear aplicaciones nativas sobre Windows, IOS, Android y Linux, en ellas un grupo de expertos analizarán las nuevas prestaciones de esta versión como son: el desarrollo para Linux Server ya que como sabéis incluye el primer compilador basado en LLVM para desarrollo empresarial certificado por Ubuntu Server y RedHat Enterprise, veremos ejemplos del nuevo RAD Server, un backend con servicios para desplegar aplicaciones, cómo publicar aplicaciones para Windows 10, nos mostrarán las nuevas mejoras en la productividad del IDE, cómo crear bases de datos con FIREDAC, etc.

La asistencia al evento es gratuita, pero dada la expectación que ha generado se necesita una inscripción previa para evitar problemas de aforo.

Aquí tenéis toda la información sobre el evento y el formulario de inscripción.

Si todavía no tenéis Delphi Tokyo podéis descargar una versión de prueba durante 30 días desde aquí.






RAD Studio 10.2 - Tokio ya está aquí



Magníficas noticias para todo los desarrolladores de Delphi y C++ Builder, la última versión de RAD Studio 10.2 Tokyo ya está disponible y viene cargada con muchísimas novedades, entre ellas la principal es la posibilidad de hacer aplicaciones en Linux (server-side), de momento está disponible para Ubuntu LTS Version 16.04 y RedHat Enterprise Versión 7, para plataformas de 64 bits y LLVM.

Se proporciona también soporte para Linux desde FireDAC para todas las DBMS, excepto para Informix; WebBroker, DataSnap y RAD Server se pueden utilizar para construir aplicaciones "multi-hilo" (para desarrollar módulos Apache) y además podremos utilizar las librerías Indy, en definitiva se nos abre un nuevo nicho de mercado a la hora de desarrollar aplicaciones.

En esta página tenéis todas las características que nos ofrece esta extraordinaria versión de RAD Studio, que os animo a que la probéis.
Desde aquí se puede descargar esta última versión para probarla durante 30 días.

Aquí tenéis los precios de lanzamiento de las tres versiones disponibles Professional, Entreprise y Architect, sólo hasta el 30 de Abril de 2017.
Embarcadero ofrece un descuento si queréis actualizaros desde la versión Starter (gratuita)


Rotaciones en 3D con RAD Studio

En este post veremos cómo crear y rotar una figura en 3D,  utilizando RAD Studio v 10.1 Berlín.

Empezamos desde File – New – Multi-Device Application y seleccionamos “3D Application




A continuación vamos añadiendo los componentes básicos:

-          tViewPort3D : será el lienzo donde se mostrará la animación en 3D.
-          tColorMaterialSource, en la propiedad “Color” selecciono “Snow”.
  tLightMaterialSource, dejamos marcadas las propiedades como se indica en la imagen: 


Ahora tenemos que indicar la orientación de la luz y la orientación del observador de la escena.

Añadimos 2 componentes más:
tLight
tCamera




Estos dos últimos componentes tienen unos botones de control que permiten moverlos en los 3 ejes del espacio.


Y otro botón más que permite cambiar el tamaño de cada una de las caras del objeto.


Por último sólo nos queda añadir los bloques de la figura que queremos crear.

En nuestro caso utilizaremos los siguientes:

tRoundcube (el bloque principal), tPlane (será la pantalla) y dos tDisk correspondientes a las cámaras

Utilizando los controles de orientación tendremos que mover cada bloque hasta conseguir lo siguiente:


Ahora sólo nos queda conseguir el movimiento de la figura
Lo haremos utilizando un tTimer y variaremos el ángulo de rotación.

PROCEDURE TForm1.Timer1Timer(Sender: TObject);
BEGIN
  WITH RoundCube1.RotationAngle DO
    Point := Point + Point3D(2, 0, 0);
END;

Activamos el Timer y  ¡¡ ya lo tenemos !!




UNIT Unit1;

INTERFACE

USES
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  System.Math.Vectors, FMX.Types3D, System.Sensors, System.Sensors.Components,
  FMX.Controls.Presentation, FMX.StdCtrls, FMX.Layouts, FMX.MaterialSources,
  FMX.Objects3D, FMX.Controls3D, FMX.Viewport3D;

TYPE
  TForm1 = CLASS(TForm)
    Viewport3D1: TViewport3D;
    Dummy1: TDummy;
    Camera1: TCamera;
    Light1: TLight;
    RoundCube1: TRoundCube;
    Plane1: TPlane;
    Disk1: TDisk;
    LightMaterialSource1: TLightMaterialSource;
    ColorMaterialSource1: TColorMaterialSource;
    Disk2: TDisk;
    Timer1: TTimer;
    PROCEDURE Timer1Timer(Sender: TObject);
  PRIVATE
    { Private declarations }
  PUBLIC
    { Public declarations }
  END;

VAR
  Form1: TForm1;

IMPLEMENTATION

{$R *.fmx}


PROCEDURE TForm1.Timer1Timer(Sender: TObject);
BEGIN

  WITH RoundCube1.RotationAngle DO
    Point := Point + Point3D(2, 0, 0);

END;

END.

¡¡ Delphi starter gratis !!

Free Starter Edition

Hace tiempo Embarcadero ofreció Delphi por tiempo limitado, ahora ha decidido ampliar en el tiempo esta oferta, desconozco hasta que fecha ofrecerá la edición Starter gratis, pero si te interesa este entorno de programación y quieres empezar a dar tus primeros pasos aquí, no lo dudes, aprovecha ahora esta oferta.
En la edición Starter no contempla los módulos de bases de datos y tiene una licencia gratuita y open source hasta que tengas unos beneficios de 1000$, ya que desde esa cantidad podrías actualizarte a la versión Profesional o Enterprise.
En un futuro es posible que se añadan más características, pero esta versión Starter nunca llegará a tener todos los módulos de la Profesional o Enterprise.


#IloveDelphi y superando el 1000000 de páginas vistas

#ILoveDelphi

El 14 de Febrero es el cumpleaños de Delphi y es un buen momento para agradecer a este magnífico lenguaje de programación (para mí el mejor) por toda la ayuda que me ha facilitado en mi carrera personal y profesional.
Recuerdo que empecé con Turbo Pascal en sus varias versiones, luego me pasé a Borland Delphi, creo que fue la primera versión que salió al mercado y desde entonces no he parado de desarrollar programas y apps para móviles con este lenguaje.
 
Todavía conservo la caja del primer Borland Delphi que salió al mercado

Es verdad que he tocado "muchos palos", Javascript, PHP, C++, Visual Basic,  ASM, pero al final me quedo con Delphi por la rapidez a la hora de programar, por lo intuitivo que es, por la capacidad de integrar diferentes bases de datos, porque te permite desarrollar una vez y ejecutar tu código en múltiples plataformas Android, IOS, Windows, etc ...
Muchas y potentes empresas han intentado hacer lenguajes parecidos, pero los que usamos Delphi ya sabemos que no hay nada igual.

También quiero agradecer a todos los lectores el que este blog haya superado el 1000000 de páginas vistas desde su creación, muchas gracias por vuestra confianza e interés.

Javier Pareja
MVP Embarcadero
blogdelphimagic@gmail.com

Soluciones a problemas comunes

Durante la programación de aplicaciones con rad studio me he encontrado con errores recurrentes.
Para solucionarlos he accedido a las páginas de ayuda, pero en otros casos me las he tenido que ingeniármelas para solventarlos.
Con el fin de ayudaros en vuestra tarea y por si os sirve de algo a continuación os iré indicando los problemas y las soluciones que apliqué.
Si me surgen más problemas los iré publicando aquí para ayudar a la comunidad de programadores Delphi.

1)  INVALID IMAGELIST INDEX



Este error me aparece inesperadamente, cuando tengo varios formularios abiertos.

SOLUCIÓN:
La única solución que he encontrado ha sido ir al menú 
- File-Save All 
- File -Close All
y volver abrir la app

2) ERROR EN EL LINKER
Se produce en algunas ocasiones, cuando vas a ejecutar tu código y se queda colgado en el proceso de linkado. Da un error en alguna librería de Android.

SOLUCIÓN: 
Abrir el Project Manager

Y sobre el nombre del proyecto (en este caso "GPSJP") hacer clic con el botón derecho del ratón y acceder en este orden a los siguientes menús: CLEAN, BUILD y COMPILE.


3) FAILED_UPDATE_INCOMPATIBLE

Se produce después de pulsar el botón RUN, la compilación no da errores y en el momento que se envía al móvil conectado por USB aparece el error.
En mi caso se producía porque había generado previamente en la fase de desarrollo una versión de la app en modo DEBUG y la que estaba enviando es una versión en modo RELEASE.

SOLUCIÓN:
Desinstalar completamente la app del móvil. Os aviso que en algunos móviles tendréis que ir a Ajustes-Aplicaciones-Forzar detención o desinstalar completamente.

4)  NO FUNCIONA LA SUBIDA DE LA APP A LA PLAY STORE .
Da un error del certificado, app no firmada, etc...
Es algo muy común que después de haber compilado vuestra app y preparado el apk para subirlo, se os olvide hacer el Deployment.

SOLUCIÓN:
Hay que ir al menú PROJECT-DEPLOYMENT
y después hacer clic donde os indico en la imagen.


5) INSTALACIÓN LIMPIA DE LA APP EN TU DISPOSITIVO
Desde el menú RUN-PARAMETERS, en la caja de texto "Parameters" escribe   -cleaninstall (con el guión)