Gratis, C++Builder® 10.1 Berlin Starter Edition

Viernes, 17 de junio de 2016 Sin comentarios
Share Button

PROMOCIÓN ESPECIAL!!

Hoy (y no se si algunos días más) se puede descargar desde la web de embarcadero la versión Starter de C++ Builder 10.1 Berlín.

ClGAk_cXEAAcHbA

 

Enlace a la página.

Vídeo mostrando las características de esta versión.

Un saludo.

Share Button

Cuestión de volumen…

Martes, 14 de junio de 2016 3 comentarios
Share Button

volumen-audio-10054183Hace unos días se planteaba en el clubdelphi una cuestión acerca de la gestión de volúmenes en los dispositivos Android. Me llamó la atención porque días atrás me había planteado la cuestión de poder disponer de “perfiles de audio” en mi propio teléfono Android. Se trata de un Xiaomi con versión KitKat instalada y una ROM personalizada.

  • ¿Cómo podemos desde Delphi acceder a los volúmenes del dispositivo para las diferentes características (música, alarmas, llamadas,…)?
  • ¿Cómo podemos cambiarlos?

CONTROL DE VOLUMEN

Lo primero es saber cómo podemos acceder a esta información utilizando las clases de Android. Si revisamos la API de Android llegamos a la clase AudioManager,  que es la que se encarga de estos menesteres (y algunos más).
La descripción de la clase dice lo siguiente:

“AudioManager provides access to volume and ringer mode control.”

Si revisamos un poco las clase en una primera lectura, ya podemos ver algunas constantes interesantes como estas:

0
1
2
3
4
5
6
int    STREAM_ALARM:        The audio stream for alarms
int    STREAM_DTMF:         The audio stream for DTMF Tones
int    STREAM_MUSIC:        The audio stream for music playback
int    STREAM_NOTIFICATION: The audio stream for notifications
int    STREAM_RING:         The audio stream for the phone ring
int    STREAM_SYSTEM:       The audio stream for system sounds
int    STREAM_VOICE_CALL:   The audio stream for phone calls

Que hacen referencia a los diferentes “volúmenes” que podemos gestionar.

Si bajamos un poco más en esa página llegamos a la parte de los métodos. Podemos ver también algunos bastante explicativos como estos:

0
1
2
3
int    getStreamMaxVolume(int streamType)
  //Returns the maximum volume index for a particular stream.
int    getStreamVolume(int streamType)
  //Returns the current volume index for a particular stream.

Que permiten obtener el volumen máximo y actual para cada uno de los “volúmenes” de los que hablábamos antes.

Desde la descripción y ayuda del método ya se hace referencia a otro método que nos será de mucha utilidad, el setStreamVolume. Su descripción nos dice:

“Sets the volume index for a particular stream.”

Pues ya tenemos nuestro punto de partida.

ACCESO DESDE DELPHI

Lo siguiente sería ver si Delphi posee ya componentes/clases para acceder a estas características, como los trae implementados para acceder a muchas otras (sensores, cámara de foros, bluetooth,…)

Si buscamos en la paleta, no hay ningún componente como TLocationSensor o TBluetooth que nos permita acceder a las características de audio.
No desesperemos, seguimos teniendo más opciones…

ACCESO A CLASES DE JAVA

imagesUtilizando Delphi podemos acceder a las clases java a través de lo que Embarcadero llama “bridge files”. Es decir, una series de ficheros escritos en Delphi que hacen de “puente” entre nuestro código Delphi (nuestra aplicación) y las clases de Java.

La instalación de Delphi ya incluye bastantes de estos archivos ya generados, con lo que a tenemos el trabajo hecho. La documentación en la docWiki está accesible aquí

Tal y como se explica ahí, podemos encontrar esas clases en nuestra instalación, en la siguiente ubicación:

  • Delphi units: source\rtl\android (.pas file extension)
  • C++ header files: include\android\rtl (.hpp file extension)

¿Qué pasa si la unit “puente” que necesitamos no se encuentra en esas lista?

Aun así no debemos desesperar. Tenemos varias opciones para generarla nosotros de forma manual. No voy a entrar ahora en este tema, porque tal vez podemos tratar más extensamente en otra entrada en un futuro. Os dejo unos links, por si alguien quiere ir revisando:

En nuestro caso, hemos tenido suerte y en el directorio antes comentado de nuestra instalación tenemos el fichero Androidapi.JNI.Media.pas  que dentro incluye el interface:

0
JAudioManager = interface;//android.media.AudioManager

Recordáis las constantes que hemos comentado más arriba; Pues dentro de este este fichero podemos encontrar algo como esto (que se parece bastante…):

0
1
2
3
4
5
6
7
8
...
{class} property STREAM_ALARM: Integer read _GetSTREAM_ALARM;
{class} property STREAM_DTMF: Integer read _GetSTREAM_DTMF;
{class} property STREAM_MUSIC: Integer read _GetSTREAM_MUSIC;
{class} property STREAM_NOTIFICATION: Integer read _GetSTREAM_NOTIFICATION;
{class} property STREAM_RING: Integer read _GetSTREAM_RING;
{class} property STREAM_SYSTEM: Integer read _GetSTREAM_SYSTEM;
{class} property STREAM_VOICE_CALL: Integer read _GetSTREAM_VOICE_CALL;
...

Y un poco más abajo que están los procedimientos y funciones, algo como esto:

0
1
2
3
4
5
...
function getStreamMaxVolume(streamType: Integer): Integer; cdecl;
function getStreamVolume(streamType: Integer): Integer; cdecl;
...
procedure setStreamVolume(streamType: Integer; index: Integer; flags: Integer); cdecl;
...

Con esto se nos abre el cielo.  :-D

Una prueba sencilla, nos permitirá saber si vamos por buen camino y ver cómo podemos utilizar esta unit.

Pantalla_Ejemplo

Colocamos un formulario con varios Trackbar y un botón que nos permita recuperar el volumen actual de los diferentes elementos:

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
procedure TForm1.Button1Click(Sender: TObject);
var
  AudioObj: JObject;
  Audio: JAudioManager;
p:Double;
 
  //·····································································
  procedure InfoAudioLevel(pAudio:JAudioManager; 
         pTracBar: TTrackBar; pStreamType: integer; pEdtValue:TEdit);
  var
    p:Double;
  begin
    pTracBar.Value := pAudio.getStreamVolume(pStreamType);
    pTracBar.Max := pAudio.getStreamMaxVolume(pStreamType);
    p := ((pTracBar.Value / pTracBar.Max) * 100);
    pEdtValue.Text := IntToStr(Round(p)) + ' %';
  end;
  //······································································
 
begin
  // acceso al servicio
  AudioObj:= TAndroidHelper.Activity.getSystemService(TJActivity.JavaClass.AUDIO_SERVICE);
  // acceso a la clase
  Audio := TJAudioManager.Wrap((AudioObj as ILocalObject).GetObjectID);
  // Alarma
  InfoAudioLevel(Audio, tbAlarma, 
    TJAudioManager.JavaClass.STREAM_ALARM, edtAlarma);
  // DTMF
  InfoAudioLevel(Audio, tbDTMF, 
    TJAudioManager.JavaClass.STREAM_DTMF, edtDTMF);
  // MUsica
  InfoAudioLevel(Audio, tbMusica, 
    TJAudioManager.JavaClass.STREAM_MUSIC, edtMusica);
  // notifocaciones
  InfoAudioLevel(Audio, tbNotificaciones, 
    TJAudioManager.JavaClass.STREAM_NOTIFICATION, edtNotificaciones);
  // Ring /llamadas
  InfoAudioLevel(Audio, tbRing, 
    TJAudioManager.JavaClass.STREAM_RING, edtRing);
  // Voz
  InfoAudioLevel(Audio, tbVoz, 
    TJAudioManager.JavaClass.STREAM_VOICE_CALL, edtVoz);
end;

Habrá que añadir las siguientes units al formulario para poder utilizar las clases de Java.

0
1
2
3
4
5
uses
  Androidapi.JNI.Media,
  Androidapi.Helpers,
  Androidapi.JNIBridge,
  AndroidApi.Jni.JavaTypes,
  AndroidApi.Jni.App;

Os adjunto un vídeo de este proyecto funcionando.

Hasta aquí esta entrada.

Como siempre cualquier comentario será bienvenido.

 

Share Button

NASA, la foto del día…

Viernes, 3 de junio de 2016 4 comentarios
Share Button

Hace bastante tiempo (cuando estábamos en la facultad) con mis amigos,  nos acostumbramos a visitar la web de  “la foto del día”. Había y sigue habiendo webs de muchas temáticas, pero una de las más conocidas en aquella época era la de la NASA. Las fotos de este tipo que hoy son más habituales, entonces era difícil encontrarlas (con aquella calidad).

ImagenDelDia

Actualmente la URL para acceder a “la foto del día” es esta.

http://apod.nasa.gov/apod/astropix.html

Hoy en día la NASA provee una API para poder acceder a través de un WebService a la fotos y otros datos asociados a la foto de cada día (título, descripción,…). Utilizando la API, el acceso a la foto del día se hace a partir de esta URL:

https://api.nasa.gov/planetary/apod

Para poder acceder a la información, necesitamos pasar una fecha válida, comprendida entre Junio de 1995 y la actualidad y nuestra KEY de desarrolladores.

Continuar leyendo…

Share Button

Procesos y Threads (GLibWMI)

Lunes, 14 de marzo de 2016 Sin comentarios
Share Button

threadA partir de esta consulta en el clubdelphi, sobre procesos y threads he decidido crear esta entrada; Para ello he compilado la librería GLibWMI en Delphi Seatle. La última versión que hay en la web está compilada y comprobada para Delphi XE7 y funciona sin problemas, así que era de esperar que en Seatle lo hiciera también sin cambios.

Yo mismo la estoy usando en algún proyecto antiguo en Delphi 6/7  y en algunas de las versiones más nuevas de Delphi XE.

Volviendo al tema que nos ocupa, en este caso nos interesa obtener información de los threads asociados a un determinado proceso que está corriendo en la máquina.

Para ello seguiremos los siguientes pasos:

  1. Acceder a la información del proceso
  2. Acceder a la información de los threads asociados a ese proceso.

Me voy a basar en la librería GLibWMI, aunque se puede hacer lo mismo utilizando código, sino deseamos instalar librerías de componentes adicionales.

Dentro de los componentes de GLibWMI, hay algunos que ya nos permiten acceder a clases “destacadas” o “habituales”. Entre ellos se encuentra ProcessInfo, por ejemplo, que nos da información  de los procesos ejecutandose (lo usaremos para el primer paso), pero WMI tiene muchas clases más.

Para acceder a cualquier otra clase o a datos particulares de una concreta, hay un componente llamado WMISQL, que nos permite lanzar “sentencias SQL” (algo parecido) sobre clases de WMI. Este componente es el que nos permitirá acceder a la información de los Threads (para los que no tenemos componente específico).

INFORMACIÓN DEL PROCESO

La información de los procesos funcionando en la máquina nos la da la clase Win32_Process.

Podemos utilizar el componente ProcessInfo. Basta con “soltar” el componente en un formulario y activarlo.

Este componente nos ofrece una lista de objetos TProcessProperties, la propiedad ObjectCount que nos da el número de elementos encontrados y la propiedad ObjectIndex que marca el elemento actual.

Podemos encontrar y obtener datos de un proceso con un código similar a este:

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var
  str:string;
  i:integer;
  p:TProcessProperties; // para abreviar
begin
  // ini
  Memo1.Lines.Clear;
  // Activarlo
  ProcessInfo1.Active := True;
  // tenemos elementos?
  if (ProcessInfo1.ObjectsCount "MAYOR QUE" 0) then begin   //!!! Cambiar "MAYOR QUE" por el operador...
    // Buscamos un proceso concreto
    for i := 0 to (ProcessInfo1.ObjectsCount -1 ) do begin
      p := ProcessInfo1.ProcessProperties;
      // es el ibServer
      if (AnsiCompareText('ibserver.exe', p.Caption) = 0) then begin
        Memo1.Lines.Add('Nombre:        ' + p.Name);
        Memo1.Lines.Add('Caption:       ' + p.Caption);
        Memo1.Lines.Add('CommandLine:   ' + p.CommandLine);
        Memo1.Lines.Add('Path del EXE:  ' + p.ExecutablePath);
        Memo1.Lines.Add('Handle:        ' + p.Handle);
        Memo1.Lines.Add('Prioridad:     ' + IntToStr(p.Priority));
        Memo1.Lines.Add('Identificador: ' + IntToStr(p.ProcessId));
        Memo1.Lines.Add('Num. threads:  ' + IntToStr(p.ThreadCount));
 
        Break
      end;
      // Siguiente
      ProcessInfo1.ObjectIndex := ProcessInfo1.ObjectIndex + 1;
    end;
  end;
end;

 

Si lo ejecutamos obtendremos algo como esto:

En este caso, el componente ProcessInfo no es el más adecuado, ya que estamos obteniendo la información de TODOS los procesos, cuando sólo queremos acceder a uno sólo (ibserver, en el ejemplo anterior).

Para este caso, es más conveniente utilizar el componente WMISQL. Ya que podemos “minimizar” el tráfico de datos que solicitamos.

Para ello, este componente posee la propiedad SQL.

Aquí vemos las propiedades de este componente:

Añadiremos esta sentencia:

0
SELECT * FROM Win32_Process WHERE name = "ibserver.exe"

Si tan sólo queremos algunos datos, podemos afinar más la consulta para dejarla así:

0
1
SELECT Caption, CommandLine, ExecutablePath, Name, Handle, Handlecount, 
Priority, ProcessId, ThreadCount FROM Win32_Process WHERE name = "ibserver.exe"

 

Una vez activamos el componente, podemos acceder al resultado de la siguiente manera (vemos 2 opciones diferentes utilizando un TMemo -con la respuesta tal cual la da WMI- y un ValueListEditor -cuadro inferior-).

0
1
2
3
4
5
6
7
8
9
10
   // ini
  Memo1.Lines.Clear;
  // Activar
  WMISQL1.Active := True;
  // tenemos elementos?
  if (WMISQL1.ObjectsCount > 0) then begin
    // respuesta de WMI
    Memo1.Lines.Text := WMISQL1.AllProperties.Text;
    // Lista de propiedades
    ValueListEditor1.Strings.AddStrings(WMISQL1.Properties);
  end;

Como resultado obtendremos algo como esto:

Vemos que la propiedad ThreadCount nos devuelve 5.

 

INFORMACIÓN DE THREADS

Volviendo al tema original, una vez tenemos el identificador de nuestro proceso (en este caso el ProcessID=5848), necesitamos obtener los threads de este proceso y los datos asociados.

Para ver más información, podéis acceder a este link donde están todas las propiedades accesibles, la explicación de cada una de ellas y los diferentes valores que adoptan.

Vamos a utilizar en este caso otro componente WMISQL (o reaprovechar el mismo) y cambiaremos la consulta por esta otra:

0
SELECT * FROM Win32_Thread WHERE ProcessHandle=5848

 

Si activamos el componente en diseño (que se puede hacer) ya podemos ver que la propiedad ObjectCount es 5, por lo tanto ibserver tiene en este momento, 5 threads en marcha que dependen de él.

Con el siguiente código podemos obtener información de cada uno de ellos:

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var
  i, j:integer;
  str:string;
  h, m, s:int64;
  p:TStrings;  // para abreviar
begin
  // ini
  Memo1.Lines.Clear;
  ValueListEditor1.Strings.Clear;
  // Activar
  WMISQL1.Active := True;
  // tenemos elementos?
  if (WMISQL1.ObjectsCount > 0) then begin
    // recorrer los threads y obtener datos
    for i := 0 to (WMISQL1.ObjectsCount - 1) do begin
      p := WMISQL1.Properties;
      j := StrToIntDef(p.Values['ElapsedTime'], 0); // milisegundos
      if (j > 0) then begin
      h := (j DIV 3600000);                        // horas
      m := (j - (h * 3600000)) DIV 60000;          // minutos
      s := (j MOD 60000) DIV 1000;                 // segundos
        Memo1.Lines.Add(Format('Tiempo ejec.:   %d:%d:%d', [h, m, s]));
      end;
      Memo1.Lines.Add('Handle:         ' + p.Values['Handle']);
      Memo1.Lines.Add('Prioridad:      ' + p.Values['Priority']);
      Memo1.Lines.Add('Prioridad base: ' + p.Values['PriorityBase']);
      Memo1.Lines.Add('Id. proceso:    ' + p.Values['ProcessHandle']);
      Memo1.Lines.Add('Estado:         ' + p.Values['ThreadState']);
      Memo1.Lines.Add('··········································');
    end;
  end;
end;

 

El resultado al mostrarlo por pantalla será algo similar a esto:

Con esto ya hemos acabado. La idea es mostrar la potencia de WMI y la sencillez con la que podemos utilizar esa información accediendo desde Delphi. Como os he dicho, podéis acceder a WMI sin necesidad de componentes, utilizando código delphi.

En entradas anteriores hemos comentado cómo hacerlo.

Un saludo y hasta la próxima.

Share Button
Categories: Delphi, Ejemplos, GLibWMI, WMI Tags: , ,

Yo ya tengo el mío…

Miércoles, 2 de marzo de 2016 Sin comentarios
Share Button

Pues eso, tal y como reza el título, yo ya tengo el mío.  :-DTapa_libro

No hace muchos días, de la mano de Danysoft (partner oficial de Embarcadero en España), se publicó el último libro de Francisco Charte; “Programación de aplicaciones Delphi con acceso a Bases de Datos”.  Pues ayer ya llegó mi copia.

No puedo opiniar todavía en profundidad, pues apenas lo he ojeado por encima, pero la verdad es que tiene una pinta fantástica.

Está centrado en la librería de acceso a datos que viene actualmente con las versiones de delphi, FireDAC y que nos da acceso multiplataforma a  las Bases de Datos más utilizadas en el mercado. Más que las explicaciones que yo pueda dar, os recomiendo que reviséis el índice completo del libro que se puede descargar en PDF desde aquí.

Personalmente me han llamado la atención algunos puntos, pero que supongo que a vosotros os pueden interesar muchos otros de los que se tratan:

  • Aplicaciones con Interbase embebido
  • Bases de Datos en memoria con FireDAC
  • Bases de Datos y Unicode
  • Servicios REST
  • Todo lo refrente a multiplataforma
  • Configuración de FireDAC

Personalmente no he trabajado casi nada con FireDAC (en lo que se refiere a proyectos “en serio”) así que aunque imagino que en su mayor parte se utilizan de forma similar a otras librerías de acceso a datos, siempre está bien tener un punto de referencia para llegar un poco más allá y profundizar un poco.

Si alguien tiene opiniones y comentarios sobre el libro y desea expresarlos, pues sois libres de participar y dar vuestra opinión aquí.

Un saludo y hasta la próxima.

IMG_20160226_174910

IMG_20160226_175022

Share Button
Categories: Bases de Datos, Danysoft, Delphi Tags: ,

Hablando del tiempo… (OpenWeatherMap) 2/2

Lunes, 21 de diciembre de 2015 1 comentario
Share Button

Para complementar la entrada anterior (Hablando del tiempo… (OpenWeatherMap) 1/2)  y “acabarla” me queda publicar el desarrollo móvil correspondiente a la aplicación que vimos anteriormente.
Como ya os comenté en la entrada anterior, el código a utilizar es prácticamente el mismo que hemos utilizado en las aplicaciones para windows (VCL). La mayor diferencia que me he encontrado en el tratamiento de la respuesta JSON que obtenemos del servidor.

Para versiones antiguas de Delphi, podéis utilizar si lo necesitáis la misma librería que ya he recomendado aquí otras veces. Se trata de lkJSON que podéis encontrar en Sourceforge.
En las nuevas versiones de Delphi ya está disponible la unit System.JSON, con lo necesario para no utilizar librerías externas.

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
function TForm1.ParseTiempoCiudadesHist(AObjResp: TlkJSONobject; var ATiempoProxHoras: TTiempoProxHoras): Boolean;
var
  i, j, num, index:integer;
  oHorasList, OWList:TlkJSONlist;
  oHora, oCoord, oMain, oWind, oWeather:TlkJSONobject;
  Str:String;
begin
  // ini
  Result := False;
  // Si no está asignado salimos..
  if not Assigned(AobjResp) then begin
    Exit;
  end;
    // Si hay error no parseamos
    if IsErrorResponse(AObjResp, errCode, ErrMsg) then begin
    Exit;
  end;
 
  // proteccion para el parseo
  try
    // cod. devuelto (datos principales
    ATiempoProxHoras.Cod := errCode;
 
    num := AObjResp.IndexOfName('count');
    if (num <> -1) then begin
      num := GetAsInteger(AObjResp.Field['count'].Value);
    end;
 
    // si no hay ciudades
    if (num = 0) then begin
      MessageDlg('No hay ninguna ciudad que coincida con ese código [nombre,pais].', mtWarning, [mbOK], 0);
      Exit;
    end;
 
    // Lista de horas (Lista)
    TlkJSONBase(oHorasList) := AObjResp.Field['list'];
    // array de elementos
    SetLength(ATiempoProxHoras.TiempoHora, oHorasList.Count);
    // Quedarse con el primier elemento de la lista...
    for i := 0 to (oHorasList.Count - 1) do begin
      // datos de la primera ciudad
      TlkJSONBase(oHora) := oHorasList.Child[i];
      // datos básicos
      ATiempoProxHoras.TiempoHora[i].dt_text := GetAsString(oHora.Field['dt_txt'].Value);
      // convertir fecha-Hora
      Str := ATiempoProxHoras.TiempoHora[i].dt_text;
      ATiempoProxHoras.TiempoHora[i].dt := EncodeDateTime(
        StrToIntdef(Copy(Str, 1, 4), 0),
        StrToIntdef(Copy(Str, 6, 2), 0),
        StrToIntdef(Copy(Str, 9, 2), 0),
        StrToIntdef(Copy(Str, 12, 2), 0),
        StrToIntdef(Copy(Str, 15, 2), 0),
        StrToIntdef(Copy(Str, 18, 2), 0), 0);
 
      // Load Main
      TlkJSONBase(oMain) := oHora.Field['main'];
      ATiempoProxHoras.TiempoHora[i].Main.temp := GetAsFloat(oMain.Field['temp'].Value);
      ATiempoProxHoras.TiempoHora[i].Main.tempmin := GetAsFloat(oMain.Field['temp_min'].Value);
      ATiempoProxHoras.TiempoHora[i].Main.tempmax := GetAsFloat(oMain.Field['temp_max'].Value);
      ATiempoProxHoras.TiempoHora[i].Main.pressure := GetAsFloat(oMain.Field['pressure'].Value);
      ATiempoProxHoras.TiempoHora[i].Main.humidity := GetAsInteger(oMain.Field['humidity'].Value);
 
      // Load weather
      TlkJSONBase(OWList) := oHora.Field['weather'];
      TlkJSONBase(oWeather) := oWList.Child[0];
      ATiempoProxHoras.TiempoHora[i].Weather.id := GetAsInteger(oWeather.Field['id'].Value);
      ATiempoProxHoras.TiempoHora[i].Weather.main := GetAsString(oWeather.Field['main'].Value);
      ATiempoProxHoras.TiempoHora[i].Weather.desc := GetAsString(oWeather.Field['description'].Value);
      ATiempoProxHoras.TiempoHora[i].Weather.icon := GetAsString(oWeather.Field['icon'].Value);
    end;
 
    Result := True;
  except
    // si hay error, FALSe
    Result := False;
  end;
end;

Posíblemente las mayores diferencias (que en realidad no son tantas) se encuentran en la parte en la que accedemos a la lista de elementos que nos devuelve el WebService.

La estructura que utilizamos para almacenar estos datos, la podéis ver en el código del proyecto (todas estas definiciones están englobadas en la unit UTWeatherClass) y es la siguiente:

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// estructura para varias horas
TTiempoProxHoras = record
    Cod:Integer;			// cod. tiempo
    City:string;
    Country:string;
    TiempoHora:array of TTiempoProxHora;  // Array para cada hora
End;
// datos para una hora
TTiempoProxHora = record
    dt:TDateTime;
    dt_text:string;
    main:TMain;
    weather: TWeather;  // datos de tiempo
end;
// Main para tiempo
TMain = record
    temp:double;        // temperatura
    humidity:double;    // humedad %
    pressure:double;
    tempmin:double;
    tempmax:double;
    sealevel:Double;   // atm  presure
    grndlevel:Double;
end;
// Tiempo
TWeather = record
    id:integer;
    main: string;
    desc: string;
    icon: string;
end;

Hago un inciso.

Esta unit, es un buen ejemplo de cómo compartir código entre aplicaciones que van a ser desarrolladas para diferentes plataformas.

Delphi permite desarrollar aplicaciones multiplataforma. Eso no significa que la misma aplicación la podamos compilar y funcione en Windows, Android, iOS y OSX. En teoría si, pero en la prácira salvo alguna excepción contada, tendremos que desarrollar cosas distintas. Sin ir más lejos, es difícil que un interface para Android nos pueda ser útil para una aplicación Windows.

Eso se traduce en que al final (salvo contadas excepciones, como he dicho) tendremos que diseñar un interface “personalizado” para cada plataforma, y reaprovechar todo el código que podamos, siendo responsables en el diseño de nuestra aplicación.
No es algo nuevo de ahora, desde hace tiempo, un buen diseño aboga por la separación entre la interficie y la “lógica de negocio”; Pues esto da valor a  esa afirmación.

Volviendo al tema del tiempo, nuestra implementación utilizando la librería System.JSON, será la siguiente:

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// Parsea varias horas de tiempo para una ciudad
function TFormMain.ParseTiempoCiudadesHist(AObjResp: TJSONobject; var ATiempoProxHoras: TTiempoProxHoras): Boolean;
var
  i, j, num, index:integer;
  oCityList, oList, oArr:TJSONArray;
  oDatosHora, oCoord, oMain, oWind, oWeather:TJSONobject;
  Str:String;
  oPair, oCity:TJSONPair;
  oItem:TJSONValue;
begin
  // ini
  Result := False;
  // Si no está asignado salimos..
  if not Assigned(AobjResp) then begin
  	Exit;
  end;
  // Si hay error no parseamos
	if IsErrorResponse(AObjResp, errCode, ErrMsg) then begin
  	Exit;
  end;
 
  // proteccion para el parseo
  try
    // cod. devuelto (datos principales
    ATiempoProxHoras.Cod := errCode;
 
    // buscar la ciudad
    oCity := TJSONPair(AObjResp.Get('city'));
    ATiempoProxHoras.City := GetJSONPairItemString(oCity, 'name');
    ATiempoProxHoras.Country := GetJSONPairItemString(oCity, 'country');
 
    // Parsear lal lista de horas
    oArr := TJSONArray(AObjResp.Get('list').JsonValue);
    num := oArr.size;
    // Si no ha encontrado ninguna, salimos...
    if (num = 0) then begin
      MessageDlg('No hay ninguna ciudad que coincida con ese código [nombre,pais].', TMsgDlgType.mtWarning, [TMsgDlgBtn.mbOK], 0);
      Exit;
    end;
    // Tamaño de array que devolvemos
    Setlength(ATiempoProxHoras.TiempoHora, num);
    // Recorrido por las diferentes horas
    for i := 0 to (num - 1) do begin
      oDatosHora := TJSONObject(oArr.Get(i));
 
      // fecha/Hora
      ATiempoProxHoras.TiempoHora[i].dt_text := GetAsString(oDatosHora.GetValue('dt_txt').Value);
      Str := ATiempoProxHoras.TiempoHora[i].dt_text;
      // convertir fecha-Hora
      ATiempoProxHoras.TiempoHora[i].dt := EncodeDateTime(
        StrToIntdef(Copy(Str, 1, 4), 0),
        StrToIntdef(Copy(Str, 6, 2), 0),
        StrToIntdef(Copy(Str, 9, 2), 0),
        StrToIntdef(Copy(Str, 12, 2), 0),
        StrToIntdef(Copy(Str, 15, 2), 0),
        StrToIntdef(Copy(Str, 18, 2), 0), 0);
      // Parseo de Main
      oPair := TJSONPair(oDatosHora.Get('main'));
      // Los de main
      ATiempoProxHoras.TiempoHora[i].main.temp := GetJSONPairItemFloat(oPair, 'temp');
      // Los de main
      ATiempoProxHoras.TiempoHora[i].Main.temp := GetJSONPairItemFloat(oPair, 'temp');
      ATiempoProxHoras.TiempoHora[i].Main.tempmin := GetJSONPairItemFloat(oPair, 'temp_min');
      ATiempoProxHoras.TiempoHora[i].Main.tempmax := GetJSONPairItemFloat(oPair, 'temp_max');
      ATiempoProxHoras.TiempoHora[i].Main.pressure := GetJSONPairItemFloat(oPair, 'pressure');
      ATiempoProxHoras.TiempoHora[i].Main.sealevel := GetJSONPairItemFloat(oPair, 'sea_level');
      ATiempoProxHoras.TiempoHora[i].Main.grndlevel := GetJSONPairItemFloat(oPair, 'grnd_level');
      ATiempoProxHoras.TiempoHora[i].Main.humidity := GetJSONPairItemFloat(oPair, 'humidity');
      // weather
      oList := TJSONArray(oDatosHora.Get('weather').JsonValue);
      oWeather := TJSONObject(oList.Get(0));
      ATiempoProxHoras.TiempoHora[i].weather.id := StrToInt(oWeather.GetValue('id').Value);
      ATiempoProxHoras.TiempoHora[i].weather.main := oWeather.GetValue('main').Value;
      ATiempoProxHoras.TiempoHora[i].weather.desc := oWeather.GetValue('description').Value;
      ATiempoProxHoras.TiempoHora[i].weather.icon := oWeather.GetValue('icon').Value;
    end;
 
    Result := True;
  except
		// si hay error, FALSe
    Result := False;
  end;
end;

prox_horasAl igual que este, podeís comparar el resto de procedimientos que recuperan datos del WebService y los transforman y veréis que son bastante similares y fáciles de adaptar.

Otra cosa que podéis ver funcionando en el ejemplo son los Frames.

Hasta ahora no los había utilizado en desarrollo móvil y decidí realizar la prueba con este ejemplo, ya que se ajustaban perfectamente a la necesidad de la visualización de datos atmotféricos en la próximas horas.

Volvemos a la misma idea de antes. La creación del frame es diferentes en ambos proyectos (sólo de la parte visual), en este caso obligado puesto que uno está diseñado con la VCL y el otro con Firemonkey, pero a partir de ahí, la utilización dentro del proyecto y el código utilizado es casi idéntico.

El funcionamiento ha sido el esperado y en todas las plataformas se ha comportado correctamente.

Os dejo algunas imágenes de cómo se ve la aplicación funcionando en las diferentes plataformas.

droid@screen-1 droid@screen-4

 droid@screen-3 droid@screen-2

ios4  iOS3

iOS2  iOS1

Y un pequeño vídeo de la compilación y ejecución en los diferentes sistemas.

Os dejo también el código fuente del proyecto, que se añade a los de la anterior entrada (Hablando del tiempo… (OpenWeatherMap) 1/2).

<CODIGO_FUENTE_PROYECTO>

Actualización (21/12/2015): Estoy subiendo la aplicación a la tienda de Google, para que podáis probarla desde allí. En cuanto esté disponible os adjunto el enlace.

Actualización (21/12/2015 18:30): Ya está activa la aplicación y podeís descargarla desde el siguiente enlace:

https://play.google.com/store/apps/details?id=com.embarcadero.ComoEstaElTiempo

GooglePlay

Hasta la próxima.

Share Button
Categories: Android, Código, Delphi, iOS, JSON Tags: , , ,

Hablando del tiempo… (OpenWeatherMap) 1/2

Viernes, 4 de diciembre de 2015 8 comentarios
Share Button

clima_gdeHace unos días pensaba en un tema para una nueva entrada del blog.
No tenía ideas, así que como solemos hacer aquí cuando no tenemos de qué hablar, se me ocurrió “hablar del tiempo”… (gracias a Javi desde aquí, que me fue realmente el que me puso en la pista del tema ;-) ).

Pues ya está, decidido, hablemos del tiempo.

Tenía en mente escribir sobre algunas de las novedades de las últimas versiones de RAD Studio y utilizaremos el tema para realizar algunas pruebas y demostrar cómo funcionan. Para ellos y para hablar del tiempo, vamos a utilizar en la entrada OpenWeatherMap.

¿Qué es OpenWeatherMap?

Según la web se describe como, “un servicio que provee de datos del tiempo de más de 200.000 ciudades y de todas las ubicaciones geográficas que se encuentra disponible en la web OpenWeatherMap.org y también a través de API. La idea del servicio está inspirada en OpenStreetMap y Wikipedia que ofrecen la información gratuita y disponible para todo el mundo.”

 APY Key

OpenWeatherMap es un servicuio gratuito y para poder trabajar con él, deberemos crear una cuenta y así acceder a una API Key, que es obligatoria a la hora de realizar peticiones a la web.

Para esta entrada y los ejemplos de código he dado de alta una cuenta gratuíta y he obtenido la siguiente API Key:

278857e8dee51f914026df21d0d40c19

AVISO: Para seguir esta entrada y probar los ejemplos, podéis usar esta clave, pero si váis a programar aplicaciones o vuestros propios ejemplos, os aconsejo que déis de alta vuestra propia cuenta y obtengáis vuestra clave, pues en cualquier momento ésta puede dejar de estar activa.

La explicación completa de cómo utilizar el valor de la “API KEY” lo tenéis en este link.

Acceso a la API

Toda la documentación de la API de OpenWeatherMap está disponible en esta dirección.  Muy clara y muy bien documentada y con multitud de ejemplos que muestran cómo se comporta.

Todas o casi todas las opciones y consultas que podamos necesitar están disponibles y la cantidad de datos y detalles acerca del tiempo que podemos obtener es bastante grande.

Podemos consultar en tiempo en una ciudad o ubicación en este preciso momento, a partir de bastantes datos, pero también predicciones futuras a intervalos de horas o días. Es muy sencillo conocer, por ejemplo, el tiempo que nos espera en los próximos 7 días o la cantidad de precipitaciones o temperatura que hará en las próximas horas.
Los datos que se proporcionan incluyen diversidad de parámetros (a parte de los básicos), como pueden ser todos los referentes a lluvias, viento, nubes, nieve,…

Un ejemplo…

Podemos ver el funcionamiento con un ejemplo sencillo.

Una llamada como lo que os muestro a continuación:

http://api.openweathermap.org/data/2.5/find?&q=Barcelona,es&lang=es&units=metric&APPID=278857e8dee51f914026df21d0d40c19

Nos devolverá (según los parámetros indicados), el tiempo actual en Barcelona (ES), con los mensajes en castellano (lang=es) y en unidades del sistema métrico (units=metric –la alternativa es el Imprerial utilizado en Estados Unidos principalmente). Por defecto, como no hemos expresado nada, se devolverá en formato JSON, aunque también se puede pedir la respuesta en XML.

respuesta1

(acceder a la respuesta como texto)

Aquí podemos ver el resultado de esta consulta.

Las unidades en que están expresados los datos, así como el formato de cada uno de ellos, podéis consultarlo en la Referencia a la API, de la que ya hemos hablado antes.

Obtener tiempo en una ciudad

Podemos obtener el tiempo actual en una ciudad de una forma similar a la que hemos visto en el ejemplo anterior.  También hay otras opciones (interesantes para dispositivos móviles) como por ejemplo, obtener el tiempo en un lugar determinado a partir de las coordenadas GPS.

De esta forma, si obtenemos las coordenadas desde nuestro dispositivo, podemos realizar una llamada similar a esta:

http://api.openweathermap.org/data/2.5/weather?lat=41.390&lon=2.154&APPID=278857e8dee51f914026df21d0d40c19

En este caso la llamada se hace utilizando los valores GPS (lat=41.390&lon=2.154) y en la propia respuesta (en la parte final) podemos ver  la ciudad/país a la que pertenecen dichas coordenadas.


“sys”: {
“message”: 0.0027,
“country”: “ES”,
“sunrise”: 1446531928,
“sunset”: 1446569027
},
“id”: 3128760,
“name”: “Barcelona”,
“cod”: 200
}

Ahora de lo que se trata, es de plasmar esto en una aplicación.

img_jsonVamos a empezar de menos a más, así que la primera versión que vamos a ver está compilada con Delphi 7 (sí Delphi 7, no delphi XE 7). Y veremos que salvo la librería de tratamiento de JSON, mucho código -salvo vistas-) es el mismo que posteriormente vamos a compilar para Windows con una versión 10 Seatle y para móviles, tanto Android como iOS. Así, que ciertamente, si programáis con un poco de “vista”, el código que generamos en gran parte puede ser reutilizable en las nuevas plataformas.

Para tratamiento de JSON en verisones antiguas, he utilizado la librería ulkJSON (disponible en Sourceforge), de la que ya he hablado otras veces. Funciona muy bien y es una buena opción si posteriormente tenemos que actualizar, porque como veremos en otra entrada, el tratamiento de una respuesta es bastante similar utilizando esta librería, a realizarlo con la unit JSON que encontraremos en las versiones modernas de delphi (así que la migración luego será poco traumática).

Para una respesta como esta, que es la que obtenemos cuando solicitamos los datos sobre una ciudad:

respuesta_json

(obtener un fichero de texto con la respuesta)

Utilizaremos un código como este:

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// Parsea la respuesta de varias ciudades a una estructura que nos ha devuelto la web
function TForm1.ParseTiempoCiudades(AObjResp:TlkJSONobject; var ATiempoObj:TTiempoStruct):Boolean;
var
  i, j, num, index:integer;
  oCityList, OWList:TlkJSONlist;
  oCity, oCoord, oMain, oWind, oWeather:TlkJSONobject;
begin
  // ini
  Result := False;
  // Si no está asignado salimos..
  if not Assigned(AobjResp) then begin
    Exit;
  end;
  // Si hay error no parseamos
  if IsErrorResponse(AObjResp, errCode, ErrMsg) then begin
    Exit;
  end;
 
  // proteccion para el parseo
  try
    // cod. devuelto (datos principales
    ATiempoObj.Cod := errCode;
 
    num := AObjResp.IndexOfName('count');
    if (num &lt;&gt; -1) then begin
      num := GetAsInteger(AObjResp.Field['count'].Value);
    end;
 
    // si no hay ciudades
    if (num = 0) then begin
      MessageDlg('No hay ninguna ciudad que coincida con ese código [nombre,pais].', mtWarning, [mbOK], 0);
      Exit;
    end;
 
    // Ciudades (Lista)
    TlkJSONBase(oCityList) := AObjResp.Field['list'];
    // datos de la primera ciudad
    TlkJSONBase(oCity) := oCityList.Child[0];
    // datos básicos
    ATiempoObj.id := GetAsInteger(oCity.Field['id'].Value);
    ATiempoObj.Name := GetAsString(oCity.Field['name'].Value);
    // Parseo de coordenadas
    TlkJSONBase(oCoord)  	 	:= oCity.Field['coord'];
    ATiempoObj.Coord.Lat	 	:= GetAsFloat(oCoord.Field['lat'].Value);
    ATiempoObj.Coord.Lon	 	:= GetAsFloat(oCoord.Field['lon'].Value);
    // main
    TlkJSONBase(oMain) := oCity.Field['main'];
    ATiempoObj.Main.temp := GetAsFloat(oMain.Field['temp'].Value);
    ATiempoObj.Main.tempmin := GetAsFloat(oMain.Field['temp_min'].Value);
    ATiempoObj.Main.tempmax := GetAsFloat(oMain.Field['temp_max'].Value);
    ATiempoObj.Main.pressure := GetAsFloat(oMain.Field['pressure'].Value);
    ATiempoObj.Main.humidity := GetAsInteger(oMain.Field['humidity'].Value);
    // Viento
    TlkJSONBase(oWind) := oCity.Field['wind'];
    ATiempoObj.Wind.speed := GetAsFloat(owind.Field['speed'].Value);
    ATiempoObj.Wind.deg := GetAsInteger(oWind.Field['deg'].value);
    // Weather
    TlkJSONBase(OWList) := oCity.Field['weather'];
    // array de elementos
    SetLength(ATiempoObj.Weather, OWList.Count);
    // Recorrer la lista de objetos.
    for j := 0 to (oWList.Count - 1) do begin
      // Datos weather
      TlkJSONBase(oWeather) := oWList.Child[j];
      ATiempoObj.Weather[j].id := GetAsInteger(oWeather.Field['id'].Value);
      ATiempoObj.Weather[j].main := GetAsString(oWeather.Field['main'].Value);
      ATiempoObj.Weather[j].desc := GetAsString(oWeather.Field['description'].Value);
      ATiempoObj.Weather[j].icon := GetAsString(oWeather.Field['icon'].Value);
    end;
 
    Result := True;
  except
    // si hay error, FALSe
    Result := False;
  end;
end;

Para probar el acceso al servicio, las peticiones básicas y la librería para “parsear” la respuesta JSON, he realizado una aplicación en Delphi. Debajo podéis ver la compilación en Delphi 7  y Delphi Seatle (en el segundo caso se aplican algunos de los nuevos estilos que vienen para Windows 10). Salvo eso, el resto de código es el mismo para ambas compilaciones.

TiempoAtmosferico

Os adjunto el video de Youtube con la versión de Delphi Seatle.

Por último os dejo los links de ambos proyectos, de las librerías y de alguna cosa más relacionada.

Para la siguiente entrada dejo las aplicaciones desarrolladas para plataforma móviles y alguna cosita más.

Como siempre, cualquier comentario o sugerencia será bienvenido.

Share Button
Categories: Aplicación, Delphi, OOP, REST Tags:

Presentación de Rad Studio 10 Seatle en Barcelona

Miércoles, 25 de noviembre de 2015 3 comentarios
Share Button

Como viene siendo habitual cuando desde Embarcadero se presenta una nueva versión de Rad Studio, Danysoft realiza el “Iberian Tour” por diferentes ciudades de España para presentar las novedades del producto. Es este caso hoy día 25 se ha realizado la de Barcelona.

Podemos revisar las demos y leer algo de documentación, pero siempre es interesante asistir a una presentación en vivo. Las preguntas, las respuestas, las propias demos, el feedback y las sensaciones son diferentes a las que uno experimenta cuando está allí. Además de que como siempre he dicho, es una oportunidad para ver a diferentes amigos y excompañeros de trabajo que asisten a estas conferencias.

La presentación de hoy ha estado a cargo de Luis Navarro, Product Manager de Embarcadero.

Os adjunto algunas fotos de la presentación de hoy.

IMG_20151125_115852

IMG_20151125_115925

IMG_20151125_115908

IMG_20151125_135526b

También en este link del ClubDelphi podéis ver algunas de la presentación en Valencia (que han colgado la gente de Danysoft) y os invito a que publiquéis las de las otras ciudades si es que tenéis la oportunidad de poder asistir, así como dejar vuestras impresiones si os aperetece.

En cuanto al propio RAD Studio y en concreto a Delphi, pues qué decir. La verdad es que esta versión 10 (Seatle) tiene muy buena pinta y aunque no la he probado a fondo, sigue la misma línea ascendente de las anteriores (a mi entender). Muchas mejoras enfocadas a la estabilidad y al IDE. Y una fuerte apuesta por continuar evolucionando el desarrollo en platarformas móviles (como no podía ser de otra manera) y en seguir evolucionando la VCL para engarcharse al carro de Windows 10 (y por lo que hemos visto parece que lo ha conseguido).

Os dejo el video con las novedades de esta versión por si queréis echarle un vistazo y el link a la lista de novedades (disponible en la docwiki) de esta versión.

Un saludo y hasta la próxima.

Share Button

Intermitencias y Mudanzas…

Viernes, 25 de septiembre de 2015 4 comentarios
Share Button

Hola a todos, después de un tiempo de inactividad en este blog.

Los que os habeis intentado conectar estos últimos días a la página, habréis notado que a estado “offline” a ratos y durante otros ha funcionado pero de forma incorrecta.

Esto es debido a que en el clubdelphi, estamos realizando estos días cambios profundos. Tanto a nivel de Hardware como a nivel de Software.
Esto afecta directamente a mi web, ya que hasta ahora estaba alojada en el mismo lugar físico que el clubdelphi y además comparte dominio/subdominio.

ACTUALIZACIÓN: A día de hoy el blog ya está instalado en el hosting definitivo (en mi caso OVH, que es el mismo que utilizamos en el clubdelphi). Espero que eso sea sinónimo de un buen funcionamiento de la web. He contratado también un dominio (http://neftali-delphi.eu/) que es el que aparece en la parte superior, aunque si accedéis a la página utilizando el de siempre (http://neftali.clubdelphi.com) sigue funcionando y os redirije al nuevo.

zakaz_pereezda

 

De forma temporal y para conseguir que al menos esté accesible para consulta, he alojado la web en un servidor gratuíto. Para ello he seleccionado Hostinguer. Mientras escojo una opción definitiva ya os aviso de que esta opción afectará al normal funcionamiento de la web.

NOTA: Ya hay quien me ha comentado que algún rato ha estado caída debido a “exceso de uso de CPU”.

Esperemos que pronto pueda estar “online” de forma definitiva.

Un saludo y disculpad las posibles molestias.

Share Button
Categories: Offtopic, Otros Tags: ,

Recopilatorio sobre Delphi XE8

Miércoles, 8 de abril de 2015 6 comentarios
Share Button

A estas alturas, imagino que ya todo el mundo está al tanto de que la última versión de RAD Studio está “en la calle”.

Si estáis “online” no voy a descubrir nada nuevo con esta noticia y seguro que llego un poco tarde…  ;-D

No es esa la idea de estas líneas; Sino la de recopilar parte de la información que hay sobre XE8.

RadStudioXE8

Lo primero, es revisar un poco las novedades que nos trae esta versión.

novedadesDesde la página de Embarcadero podemos revisar las más importantes.

Aquí en la página de Danysoft, también hay una lista con una breve explicación y e imágenes mostrando algunas de las más llamativas.
AÑADO: Lista más extensa publicada desde Danysoft con los cambios agrupados por plataformas o categorías. Una organización diferente.

Continuar leyendo…

Share Button