Archivo

Entradas Etiquetadas ‘tiempo’

Publicando mensajes en Facebook sin que el usuario esté ahora mismo en la web [ modo offline ]

Viernes, 27 de Enero de 2012 Gaspar Fernández 6 comentarios

enchufe

Una de las grandes utilidades que nos da Facebook por si creamos una aplicación de escritorio, o una aplicación web que publique posts de nuestro blog automáticamente en nuestro muro, son los mensajes Offline, con este modo, no es necesario que un usuario esté identificado realmente en Facebook, aunque, debemos tener cuidado a la hora de trabajar con la aplicación, los usuarios nos han dado permiso para publicar en su nombre contenidos que nosotros generamos, por lo que debemos evitar que se pueda acceder a la publicación de contenidos directamente. Es decir, lo que voy a mostrar ahora es un mero ejemplo de cómo se haría, aunque muestro un programa que es lo más inseguro del mundo, ya que, cualquiera que entre en la dirección web especificada podrá escribir en Facebook con nuestro ID de usuario.

Creando la aplicación y practicando la publicación

Para esto, hay dos posts anteriores que te recomiendo leer si no lo has hecho ya:

Creando dos interfaces

Por un lado necesitamos la interfaz que conecta con Facebook, tal y como la hemos hecho ya en el post anterior, desde aquí se pedirá permiso para poder publicar mensajes, y para poder acceder de forma offline. Este será nuestro index.php en los ejemplos (aunque podemos cambiarlo por cualquier página que destinemos a la identificación en Facebook).

Por otro lado, necesitamos el script que realmente va a publicar texto en Facebook, este script no debe identificarse en el sistema, sólo debe enviar la publicación (será en el ejemplo publica.php)

El código fuente completo podrás verlo y descargarlo al final del post.

Pidiendo permiso

Para escribir en Facebook con el modo offline, debemos pedir el permiso, concretamente el permiso offline_access. Con este permiso la palabra clave que nos da acceso con el usuario elegido no caducará (las claves normales, sin offline access, tienen una caducidad limitada, aunque funcionarán durante un tiempo (¿minutos?) aunque el usuario haya dejado de estar activo).

Para manejar los permisos de Facebook de una mejor forma, he extendido la clase Facebook con algunos métodos que nos ayudarán a la hora de pedir permisos:

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
<?php
  /**
   *************************************************************
   * @file facebook_ext.php
   * @brief Extensión de la clase Facebook que permite manejar
   *        algunas opciones de forma más intuitiva.
   *
   * @author Gaspar Fernández <blakeyed@totaki.com>
   * @version
   * @date 14 ene 2012
   * Historial de cambios:
   *
   *
   *
   *************************************************************/


require ('facebook.php');

class FacebookExtended extends Facebook
{
  private $permissions=false;

  function __construct($config)
  {
    parent::__construct($config);
  }

  function askForPermission($ext_perm, $uid=false)
  {
    if (!$uid)
      $uid=$this->getUser();

    if (!$this->permissions)
      $this->permissions=$this->api('/'.$uid.'/permissions');
   
    if ( (isset($this->permissions['data'])) && (isset($this->permissions['data'][0])) )
      return $this->permissions['data'][0][$ext_perm];
  }

  function askForPermissions($ext_perms, $uid=false)
  {
    $permArray = explode(",", $ext_perms);

    $permissionsOk=true;

    $nElems=count($permArray);
    for ($i=0; $i< $nElems; $i++)
      {
    $ext_perm=trim($permArray[$i]);
    if ($ext_perm!='')
      {
        /* Internal debug */
        /* echo $i.'-'.$this->askForPermission($ext_perm, $uid)."<br>"; */
        $permissionsOk= ($this->askForPermission($ext_perm, $uid) && $permissionsOk);
      }
      }
    return $permissionsOk;
  }

  function loginUser($permissions)
  {
    if ($permissions)
      $loginUrl=$this->getLoginUrl(array('scope'=>$permissions));
    else
      $loginUrl=$this->getLoginUrl();

    header('Location: '.$loginUrl);
    exit;
  }

};
?>

Esta clase, se encargará de preguntar por los permisos que necesitamos de forma automática (en realidad fue como se explicó en el post anterior, aunque cuando tenemos que preguntar por varios permisos nuestro código puede crecer bastante).

Ahora, para pedir permiso haremos lo sigueinte:

1
2
3
4
$permissions = $facebook->askForPermissions('publish_stream,offline_access');
   
  if (!$permissions)
    throw new Exception('No tengo permisos suficientes', NO_PERMISSIONS);

La excepción podemos crearla como queramos, yo he seguido la técnica del post anterior, que podréis observar mejor con el código completo, lo que está claro es que el código no debe continuar pasado este punto, y debemos pedir los permisos necesarios. Además, a la hora de pedir los permisos, con la clase que hemos visto anteriormente, podemos hacer lo siguiente:

1
$facebook->loginUser('publish_stream,offline_access');

Con lo que también nos ahorramos unas líneas de código.

También, para publicar en nombre de los usuarios debemos obtener un Access Token, es una palabra de acceso única que identifica al usuario de Facebook en nuestra aplicación y nos permite interactuar, para ver esta palabra debemos hacer:

1
echo $facebook->getAccessToken();

Publicando contenido

Para publicar contenido, ahora hemos creado el archivo publica.php que sólo publicará contenido en el muro, con este archivo tenemos que tener especial cuidado, ya que en este caso, cualquiera podrá publicar contenido nada más accediendo al archivo (Código fuente de publica.php):

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
<?php

$api_key = 'Rellenar con nuestra API key';
$api_sec = 'Rellenar con nuestra API secret';

require_once('facebook_ext.php');

$facebook = new FacebookExtended(array(  
                       'appId'  => $api_key,
                       'secret' => $api_sec,
                       'cookie' => true ,
                     ));

try
{
  $destino="gaspy";     // O el nombre de usuario que queramos usar
  $public='Probando mensajes automáticos con el modo offline';
  $access_token='palabra que vimos cuando nos autorizamos, muy laarga';

  print_r( $facebook->api('/'.$destino.'/feed', 'post', array('access_token' => $access_token, 'uid' => $destino, 'message' => $public)));  
} catch (Exception $e)
{
  echo "Ocurrió un error";
}
?>

Ahora, para publicar contenido, en la llamada a la API debemos especificar:

  • uid = UID con la que queremos publicar, es decir, el usuario en nombre de quién hablamos.
  • message = Mensaje que queremos publicar
  • access_token = Palabra de acceso

Es más, podemos publicar en el muro o la página de quien queramos con esta técnica, igual que hacíamos antes, pero en este caso, lo hacemos sin que tengamos que estar identificados como ese usuario.

Código fuente de index.php

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
<?php

$api_key = 'Rellenar con nuestra API key';
$api_sec = 'Rellenar con nuestra API secret';

require_once('facebook_ext.php');

// Definimos códigos de error
define(NOT_INSTALLED,      1);
define(NO_PERMISSIONS,     2);
define(MALFORMED_ARRAY,   90);

$facebook = new FacebookExtended(array(  
                       'appId'  => $api_key,
                       'secret' => $api_sec,
                       'cookie' => true ,
                     ));

try
{
 
  $sesion = $facebook->getUser();
  if (!$sesion)
    throw new Exception('Aplicación no instalada', NOT_INSTALLED);

  echo "Estamos identificados en Facebook<br/>";
  echo "Usuario: ".$sesion."<br/>";

  $permissions = $facebook->askForPermissions('publish_stream,offline_access');
   
  if (!$permissions)
    throw new Exception('No tengo permisos suficientes', NO_PERMISSIONS);
 
  echo "No tienes que hacer nada, cierra esta ventana.";
}
catch (FacebookException $e)
{
  echo "Error de Facebook: ".$e->getCode().": ".$e->getMessage();
}
catch (Exception $e)
{
  switch ($e->getCode())
    {
    case NOT_INSTALLED:
      $facebook->loginUser();
      break;
    case NO_PERMISSIONS:
      $facebook->loginUser('publish_stream,offline_access');
      break;

    case MALFORMED_ARRAY:
      echo $e->getMessage();
      break;

    default:
      echo "Ocurrió un error no identificado";
    }
}
?>

Para descargar el código fuente: modooffline.tar.bz2 (1.7Kb)
Hay que incluir la API de Facebook para que funcione y sustituir $api_key y $api_sec por nuestra API Key y API secret respectivamente.

Foto: Samuel M. Livingston (Flickr)

Conocer uptime del servidor con PHP

Viernes, 13 de Enero de 2012 Gaspar Fernández Sin comentarios

En ocasiones, sobre todo en nuestros paneles de administración, a veces es necesario, o bonito, conocer cuánto tiempo lleva el servidor encendido, así vemos si nos han reseteado la máquina o incluso alardear de que nuestro servidor lleva encendido más tiempo que otro.

Para ello, yo uso este código:

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
/**
 ******************************************************************
 * @brief Obtiene un array con la información del uptime
 *
 * @return array('days'=> , 'hours'=> , 'minutes'=> , 'seconds'=>
 *         o falso si no se puede leer /proc/uptime
 *
 ******************************************************************/

  function getUptime()
  {
    $info = @file_get_contents("/proc/uptime");
    if (!$info)
      return false;

    $desglos = explode(" ", $info);

    $segundos = intval($desglos[0] % 60);
    $minutos = intval($desglos[0] / 60 % 60);
    $horas = intval($desglos[0] / 3600 % 24);
    $dias = intval($desglos[0] / 86400);
     
    return array('days'    => $dias,
         'hours'   => $horas,
         'minutes' => $minutos,
         'seconds' => $segundos);

  }

Donde obtenemos en un array los días, horas, minutos y segundos, directamente podemos hacer:

1
print_r(getUptime());

Si quieremos una salida bonita, podemos hacer lo siguiente:

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
/**
 ******************************************************************
 * @brief Expresa un número con su unidad. Si el número es 1, pone la unidad en singular, si es >1, lo pone el plural
 *
 * @param $numero    Número a presentar
 * @param $singular  Unidad en singular
 * @param $plural    Unidad en plural
 * @param $coma      Texto a presentar delante de la cantidad (por ejemplo, una coma)
 * @param $sicero    Texto a presentar si el número es cero
 * @param $desp      Texto a presentar después de la cantidad
 *
 * @return Texto formateado
 *
 ******************************************************************/

function cuenta($numero, $singular, $plural, $coma=false, $sicero=false, $desp=false)
{
  if ($numero>1)
    $out=$numero.$plural;
  elseif ($numero==1)
    $out=$numero.$singular;
  else
    $out=$sicero;

  if ($out)
    $out=$coma.$out.$desp;
 
  return $out;
}

function returnUptime()
{
  $o='';
  $uptime=getUptime();
  if (!$uptime)
    throw new Exception ("No se puede acceder a /proc/uptime");

  if ($uptime['days'])
    $o.=cuenta($uptime['days'],' día', ' días', ($o!='')?', ':'', '', '');

  if ($uptime['hours'])
    $o.=cuenta($uptime['hours'],' hora', ' horas', ($o!='')?', ':'', '', '');

  if ($uptime['minutes'])
    $o.=cuenta($uptime['minutes'],' minuto', ' minutos', ($o!='')?', ':'', '', '');

  if ($uptime['seconds'])
    $o.=cuenta($uptime['seconds'],' segundo', ' segundos', ($o!='')?', ':'', '', '');


  return $o;
}

echo returnUptime();

cuenta() es una función que hice hace mucho mucho tiempo y a la que le tengo mucho cariño, y me ayuda a hacer la salida más amigable para el usuario. Aunque tenéis el comentario para Doxygen encima, aquí relato sus argumentos:

  • $numero : Es el número que vamos a mostrar por pantalla, puede ser cualquier número
  • $singular : Muestra la unidad en la que está expresado el número, además, este texto se mostrará sólo si el número es 1.
  • $plural : Es igual que el anterior, pero se mostrará cuando la unidad es distinto a 1.
  • $coma : Es un texto que se representa antes de la unidad, por ejemplo una coma, si vamos a enumerar, como en este ejemplo del uptime
  • $sicero : Este texto se mostrará si $numero es 0
  • $desp : Será un texto que se muestre después de la cadena generada

La función getUptime() producirá una excepción en servidores Windows o, en sistemas donde no tengamos acceso a /proc/uptime ; por supuesto el cliente puede estar corriendo cualquier sistema operativo.

¿ Y tú qué eres ? Identificando clases heredadas en C++

Viernes, 9 de Diciembre de 2011 Gaspar Fernández Sin comentarios

blogvision_estudios_small

En ocasiones, sobre todo cuando estamos con temas de herencia entre clases, tenemos la necesidad de consultar de qué tipo es una instancia. Es decir, tenemos una clase Mascota, y clases Perro y Gato (subclases de la primera). Imaginad que en el código hay muchas funciones, que queremos que sean comunes para todas las mascotas, y por tanto, aceptarán como parámetro una instancia de Mascota, pero en su ejecución, necesitan en un momento saber de qué tipo concreto son (Perro o Gato). ¿ Cómo podemos averiguarlo ?

Lo podemos hallar con dynamic_cast. dynamic_cast forma parte de la RTTI (RunTime Type Information / Información de tipos en tiempo de ejecución), y es capaz de guardar los datos del tipo específico de una instancia y utilizarla durante la ejecución del programa (no sólo en la compilación). Esto no está en la especificación original de C++ (del 1983), sino que fue introducido en 1991, además, en algunos compiladores no viene activado por defecto (según Wikipedia, es lógico, ya que esto puede causar un coste, tanto en memoria, espacio del ejecutable y uso de memoria.)

Una forma para comprobar el tipo que estamos utilizando es la siguiente:

1
2
3
4
5
Perro *a = dynamic_cast<Perro *>(m);
  if (a!=NULL)
    {
      // Acciones con una instancia de Perro llamada a
    }

En este caso estamos comprobando que nuestra Mascota es un Perro.

A continuación vemos el ejemplo completo, en el que tenemos una serie de mascotas y vamos a llevarlas al veterinario, pero para eso, el veterinario determina qué tipo de mascota es y lo deriva al doctor adecuado (es un ejemplo). El ejemplo estará disponible para descarga al final del post.:

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
83
84
85
86
87
88
89
90
91
92
93
#include <iostream>
#include <string>

using namespace std;


class Mascota
{
public:
  Mascota(string nombre):nombre(nombre)
  {
  }
  virtual ~Mascota()
  {
  }

  string getNombre()
  {
    return nombre;
  }
private:
  string nombre;
};

class Perro : public Mascota
{
public:
  Perro(string nombre):Mascota(nombre)
  {
  }
};

class Gato : public Mascota
{
public:
  Gato(string nombre):Mascota(nombre)
  {
  }
};

class Iguana : public Mascota
{
public:
  Iguana(string nombre):Mascota(nombre)
  {
  }
};

void veterinarioPerro(Perro *p)
{
  cout << "Ponemos una inyección a tu perro " << p->getNombre() << endl;
}

void veterinarioGato(Gato *g)
{
  cout << "Ponemos una inyección a tu gato " << g->getNombre() << endl;
}

void veterinario(Mascota *m)
{
  cout << "Vas a llevar a tu mascota al veterinario..." << endl;

  Perro *a = dynamic_cast<Perro *>(m);
  if (a!=NULL)
    {
      veterinarioPerro(a);
    }
  else if (Gato *a = dynamic_cast<Gato *>(m))
    {
      veterinarioGato(a);
    }
  else
    {
      cout << "No sé qué tipo de mascota tienes" << endl;
    }
}

int main()
{
  Mascota *m;
  Mascota *m2;
  Mascota *i;

  m=new Gato("Mini");
  m2=new Perro("Rufus");
  i= new Iguana("Igor");

  veterinario(m);
  cout << endl;
  veterinario(m2);
  cout << endl;
  veterinario(i);
}

Tenemos que tener en cuenta un requisito primordial para dynamic_cast, y es que la clase sobre la que hagamos la llamada, tiene que ser una clase polimórfica, y será polimórfica cuando contenga al menos un método virtual. Si no sabemos qué método puede ser virtual en la clase, siempre podemos hacer que sea el destructor.

Aunque, por poner un ejemplo de más utilidad, imaginemos que en lugar de tener variables disponibles, cuyo control podemos llevarlo a mano, tenemos un vector de mascotas, y vamos a llevarlas a todas al veterinario. Sólo pego aquí el main(), en la descarga al final de la página estará el ejemplo completo.

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
int main()
{
  vector <Mascota *>mis_mascotas;
  Mascota *m;
 
  m=new Gato("Mini");
  mis_mascotas.push_back(m);
  m=new Perro("Rufus");
  mis_mascotas.push_back(m);
  m=new Iguana("Igor");
  mis_mascotas.push_back(m);
  m=new Perro("Ralph");
  mis_mascotas.push_back(m);
  m=new Gato("Pitu");
  mis_mascotas.push_back(m);
  m=new Perro("Pluto");
  mis_mascotas.push_back(m);
  m=new Gato("Kity");
  mis_mascotas.push_back(m);
  m=new Iguana("Motorcillo");
  mis_mascotas.push_back(m);

  for (vector<Mascota*>::iterator i=mis_mascotas.begin(); i!=mis_mascotas.end(); i++)
    {
      veterinario(*i);
      cout<<endl;
    }
}

Otra forma es utilizar typeid() para averiguar la clase a la que pertenece la instancia. Lo hacemos en la función veterinario(), es necesario incluir la biblioteca :

1
2
3
4
5
6
7
8
9
10
11
12
void veterinario(Mascota *m)
{
  cout << "Vas a llevar a tu mascota al veterinario..." << endl;

   cout << typeid(*m).name() << endl;
  if (typeid(*m)==typeid(Perro))
    veterinarioPerro((Perro*)m);
  else if (typeid(*m)==typeid(Gato))
    veterinarioGato((Gato*)m);
  else
    cout << "No sé qué tipo de mascota tienes" << endl;
}

Algo que también se puede hacer es averiguar el nombre de la clase e imprimirlo con cout, aunque intuitivamente no podremos, porque normalmente el nombre de los tipos no llegan al programa compilado, cuando se compila con soporte RTTI, tenemos la opción de almacenar estos nombres, así que cuidado al poner los nombres de las clases, que se podrá ver en el resultado final.
Para poner por pantalla los nombres de las clases, debemos hacer lo siguiente (pongo sólo la parte de main() correspondiente a recorrer el vector):

1
2
3
4
for (vector<Mascota*>::iterator i=mis_mascotas.begin(); i!=mis_mascotas.end(); i++)
    {
      cout << typeid(**i).name() << endl;
    }

typeid(instancia).name() nos devuelve en un formato textual el nombre de la clase. En el ejemplo hay ** (dos asteriscos) porque el iterador es un puntero a lo que estamos recorriendo, que son punteros a Mascota.
Hay que tener cuidado, porque esta opción no es igual en todos los compiladores, puede que algunos compiladores te devuelvan el nombre tal cual, y otros te devolverán el nombre con algún número o caracteres especiales en el caso de ser punteros, por ejemplo. El más claro ejemplo es GXX cuyo resultado es:

4Gato
5Perro
6Iguana
5Perro
4Gato
5Perro
4Gato
6Iguana

El número que precede los nombres de clase es el número de caracteres que tiene dicho nombre: Gato = 4, Perro = 5, Iguana = 6 . Si probamos con Mascota, saldrá un 7. Y no hay ninguna forma de hacerlo portable a diferentes compiladores por lo que tendremos que hallar soluciones específicas en el caso de que queramos tener el nombre de la clase en formato plano.

En GXX (g++) podemos utilizar lo siguiente, es una función que arregla el nombre para los humanos, no sólo de clases, sino más cosas. Podemos hacer lo siguiente:

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
string demangle(const char *input)
{
  int status;
  string res(abi::__cxa_demangle(input, 0, 0, &status));
  return res;
}

int main()
{
  vector <Mascota *>mis_mascotas;
  Mascota *m;
 
  m=new Gato("Mini");
  mis_mascotas.push_back(m);
  m=new Perro("Rufus");
  mis_mascotas.push_back(m);
  m=new Iguana("Igor");
  mis_mascotas.push_back(m);
  m=new Perro("Ralph");
  mis_mascotas.push_back(m);
  m=new Gato("Pitu");
  mis_mascotas.push_back(m);
  m=new Perro("Pluto");
  mis_mascotas.push_back(m);
  m=new Gato("Kity");
  mis_mascotas.push_back(m);
  m=new Iguana("Motorcillo");
  mis_mascotas.push_back(m);

  for (vector<Mascota*>::iterator i=mis_mascotas.begin(); i!=mis_mascotas.end(); i++)
    {
      cout << demangle(typeid(**i).name()) << endl;
    }
}

He utilizado una función para simplificar un poco esto, ya que __cxa_demangle(), tiene varios parámetros y nos interesa tener una función que la llame con los parámetros por defecto, para hacer la tarea más directamente. También tenemos que incluir

En G++ podemos utilizar la opción -fno-rtti si no queremos compilar nuestros programas en C++ con este soporte.

Descárgate los ejemplos: dynamic_types.tar.bz2

Luces del coche fantástico con Arduino (I)

Lunes, 29 de Agosto de 2011 Gaspar Fernández Sin comentarios

Leds con Arduino

Un pequeño proyecto con Arduino, para probar todas las salidas con posibilidad de PWM (pulse width modulation), es como una salida parecida a analógica, aunque lo que estamos haciendo es enviar pulsos cuadrados, el valor medio de la tensión será la tensión analógica que buscamos.

Os acordáis de aquella serie de los 80, El Coche Fantástico, en la que podíamos ver unas luces que se desplazaban de derecha a izquierda dejando un rastro, en eso me basé.

Montaje 1El montaje es muy sencillo, sólo tenemos que conectar diodos led a cada una de las salidas PWM de nuestro Arduino, los que utilizan ATmega328 o ATmega168 tendrán dichos pins en las salidas 3, 5, 6, 9, 10 y 11 ; las conexiones, las haremos a través de resistencias, depende de los diodos led que utilicemos, ya que la salida de cada pin de Arduino a nivel alto es de 5v, con unos 220 ó 330 ohmios basta, aunque yo he utilizado de 1K, no tenía otras a mano.fantastico_esquema

A partir de aquí nos podemos poner a introducir un pequeño programa. Aunque no es totalmente necesario, quise probar la programación orientada a objetos  (OOP) de este compilador, por lo que creé una clase LedManager. Más adelante, cuando tenga tiempo haré el código más bonito y legible, aunque por ahora, funciona.

Por otra parte, no saqué la clase como biblioteca, está todo en el mismo archivo de código fuente.

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#define MAX_LEDS 6
#define LED_CHANGE_MS 90
#define LED_FADE_MS 2

class LedManager
{
public:
  ~LedManager();
  LedManager();
  LedManager(const int pin, const int fadems);
  void addLed(const int pin);
  void setFadeMs(const int fadems);
  void setLedChangeMs(const int ledchangems);
  void begin();
  void step();
private:  
  struct LedDef
  {
    int pin;
    int value;
    int step;
  };
  LedDef leds[MAX_LEDS];
  int ledcount;
  int fadems;
  int ledchangems;
  // Para probar el control de desbordamiento
  // int time;
  // int time2;
  unsigned long time;
  unsigned long time2;
  int direction;
  int currentled;
  int changes;

  void writeLed(int num, int value);
  void writeLed(int num);
};

LedManager::~LedManager()
{
}

LedManager::LedManager():fadems(LED_FADE_MS),
             ledcount(0),
             direction(1),
             ledchangems(LED_CHANGE_MS)
{
}

void LedManager::setLedChangeMs(const int ledchangems)
{
  this->ledchangems=ledchangems;
}

void LedManager::addLed(const int pin)
{
  leds[ledcount].pin=pin;
  leds[ledcount].value=0;
  leds[ledcount].step=-1;
  ledcount++;
  pinMode(pin, OUTPUT);
  writeLed(currentled, 0);
}

void LedManager::setFadeMs(const int fadems)
{
  this->fadems=fadems;
}

void LedManager::begin()
{
  time=millis();
  time2=time;
  currentled=0;
  changes=1;
  writeLed(currentled, 255);
}

void LedManager::step()
{
  int tmp=millis();

  // Controlamos el desbordamiento
  if (tmp<time2)
    time2=tmp;

  if (tmp<time)
    time=tmp;

  if ( (tmp>time2) && ( (tmp-time2)%fadems == 0) )
    {
      for (unsigned i=0; i<ledcount; i++)
    writeLed(i);
     
      time2=tmp;
    }
 
  if ( (tmp>time) &&  ( (tmp-time)%ledchangems ==0) )
    {
      if ( ( (direction>0) && (currentled==ledcount-1) ) ||
       ( (direction<0) && (currentled==0) ) )
    direction=-direction;
     
      currentled+=direction;
      writeLed(currentled, 255);
     
      time=tmp;
    }
     
}

void LedManager::writeLed(int num, int value)
{
  leds[num].value=value;
  analogWrite(leds[num].pin, leds[num].value);
}

void LedManager::writeLed(int num)
{
  int tmp=leds[num].value+leds[num].step;
  if ((tmp>0) && (tmp<255))
    {  
      leds[num].value=tmp;
      analogWrite(leds[num].pin, leds[num].value);
    }
}


const int ledpins[MAX_LEDS]={3, 5, 6, 9, 10, 11};
LedManager ledmgr;

void setup()
{
  Serial.begin(19200);
  for (unsigned i=0; i<MAX_LEDS; ++i)
    {
      ledmgr.addLed(ledpins[i]);
    }

  ledmgr.begin();  
}

void loop()
{
  ledmgr.step();
}

Otra cosa importante, es que podemos incluir este programa (3.1K aunque se puede optimizar algo más) en cualquier proyecto y que se ejecute junto con el resto del programa. Puede que estas luces sean parte de un robot, por ejemplo; la tarea de las luces se lanzará escribiendo ledmgr.step() y podremos seguir realizando las demás tareas del robot.

Bueno, ahora veréis que con este diseño escribo un código en principio innecesario, porque poniendo un condensador en paralelo con cada led puedo conseguir un efecto parecido, y no necesito controlar los valores analógicos de las salidas ni nada, sólo mover el 1 lógico de led en led, aunque eso me lo reservo para la segunda parte de este proyecto.

Recopilación de soluciones para los retos de #tuentiContest . Challenge #6

Jueves, 23 de Junio de 2011 Gaspar Fernández 1 comentario

Últimamente he hablado acerca del I concurso de programación de Tuenti. Un concurso de programación Online que se llevó acabo durante la semana pasada (del 13 al 20 de Junio, muy mala fecha).

Podéis ver los enunciados de todos los problemas, con ejemplos sobre la entrada y salida (aunque a veces no hay que haerles mucho caso) en la web oficial del concurso, pero en Vidas Concurrentes lo encontramos todo en español.

Challenge #6 : The Clock

clockUn reto curioso y friki. Tenemos un reloj y nos preguntamos, ¿cuántos leds se encenderán desde las 00:00:00 pasados X segundos? Parece tonto, pero no lo entendí a la primera, me costó un rato, saber qué pedían, tal vez era el sueño. Es importante saber que el reloj es de 7 segmentos, y cada segundo se apagan todos los números y se vuelven a encender con la nueva hora.

Soluciones:

Si no estás en la lista y quieres plantear tu solución, deja un comentario con tu link !

Actualización 2011/06/23 13:54 : Corregido error tipográfico y añadida solución de @javipinero
Actualización 2011/06/24 06:20 : Añadida solución de @lagunex
Actualización 2011/06/28 08:23 : Añadida solución de @theom3ga
Actualización 2011/07/03 01:42 : Añadida solución de @frisco82
Actualización 2011/07/03 13:35 : Añadida solución de @Rosapolis

El método más rápido para traer un valor pasado por $_GET [ PHP ]

Miércoles, 15 de Diciembre de 2010 Gaspar Fernández 1 comentario

Tal vez por paranoia, por ganas de perder el tiempo, o por optimizar aún más el código fuente nos encontremos ante esta cuestión.

He realizado una serie de pruebas para ver cuál es el método que mejor funciona para traer un valor de $_GET basándome en el peor de los casos: cuando este valor no existe. Tal vez las pruebas no tengan excesiva validez por el método utilizado, aunque he intentado hacer todas las pruebas en las mismas condiciones.

Las medidas de tiempo han sido tomadas con el comando time de la siguiente manera:

$ time php myscript.php

Se han hecho un total de 7 pruebas, 1 de ellas de control. Las pruebas se han basado en la generación de cadenas aleatorias (que más tarde serán nuestro parámetro $_GET), además, la obtención del parámetro ha sido hecha unas 500000 veces con un bucle for. Se detallan las pruebas realizadas.
Cada prueba se ha repetido tres veces y se ha capturado el tiempo de las tres veces. Estamos usando un software complicado que hará muchas cosas mientras se realiza el test, es más, el sistema operativo puede requerir hacer una tarea que puede influir en nuestro resultado, por lo que así minimizamos el error (y si en alguna ocasión sale un dato desorbitado podemos descartarlo).

Prueba de control [TEST 0]

La generación de parámetros es lo que más tarda. Se ha utilizado uniqid() para obtener cadenas de la misma longitud y contenido arbitrario y diferente cada vez. Además, tanto la obtención del parámetro (en las siguientes pruebas) como la generación del nombre del parámetro se hace un número N de veces gracias a un bucle (que también invierte un tiempo).

Con esta prueba de control podremos ver en qué grado son significativos los valores obtenidos luego (cuando hagamos la obtención de datos), ya que vemos que el bucle y la generación tardan de media 4.493 segundos.

El código fuente del script para esta prueba es el siguiente:

1
2
3
4
5
echo "Probando 500000 peticiones GET";
for ($i=0; $i<500000; $i++)
{
$getVar=uniqid();
}

Anulando error_reporting() [TEST 1]

Lo que nos impide hacer un $variable=$_GET['parametro']; es el error_reporting, ya que si éste no existe, nos devolverá un feo error por pantalla. Así que probemos primero desactivando esto y obteniendo el valor de la variable.

1
2
3
4
5
6
7
echo "Probando 500000 peticiones GET";
error_reporting(E_NONE);
for ($i=0; $i<500000; $i++)
{
$getVar=uniqid();
$dato=$_GET[$getVar];
}

Con @$_GET[...] [TEST 2]

Otra manera de impedir que PHP muestre sus errores en pantalla, es colocando una @ delante de lo que creemos que puede fallar, y en este caso es la sentencia de $_GET[$getVar].

1
2
3
4
5
6
7
echo "Probando 500000 peticiones GET";

for ($i=0; $i<500000; $i++)
{
$getVar=uniqid();
$dato=@$_GET[$getVar];
}

if…else [TEST 3]

No es nada intuitivo, PHP es un lenguaje de scripting, así que cuanto más escribamos, peor, pero merece la pena hacer la prueba, y nos podemos llevar una sorpresa. Ahora, hacemos la obtención del dato, pero primero comprobamos que éste existe con isset().

1
2
3
4
5
6
7
8
9
echo "Probando 500000 peticiones GET";
for ($i=0; $i<500000; $i++)
{
$getVar=uniqid();
if (isset($_GET[$getVar]))
$dato=@$_GET[$getVar];
else
$dato=false;
}

Operador ternario ? [ TEST 4 ]

Tal vez vaya mejor que el if…else (TEST 3), por aquella razón de escribir menos que comentaba…

1
2
3
4
5
6
echo "Probando 500000 peticiones GET";
for ($i=0; $i<500000; $i++)
{
$getVar=uniqid();
$dato=(isset($_GET[$getVar]))?$_GET[$getVar]:false;
}

Creando una función auxiliar gval() [ TEST 5 ]

Tal vez escribimos más que con el TEST 2 pero, vamos a probar, qué tal va. Tal vez si la diferencia de tiempo no es significativa podemos utilizar este método…

1
2
3
4
5
6
7
8
9
10
11
function gval($getVar)
{
return (isset($_GET[$getVar]))?$_GET[$getVar]:false;
}

echo "Probando 500000 peticiones GET";
for ($i=0; $i<500000; $i++)
{
$getVar=uniqid();
$dato=gval($getVar);
}

Función auxiliar y parámetro por referencia [ TEST 6 ]

Una nueva prueba, ya que hemos hecho una función auxiliar, veamos cómo influye el pasar el parámetro por valor o pasarlo por referencia. En este caso podríamos hacerlo sin problema.

1
2
3
4
5
6
7
8
9
10
11
function gval(&amp;$getVar)
{
return (isset($_GET[$getVar]))?$_GET[$getVar]:false;
}

echo "Probando 500000 peticiones GET";
for ($i=0; $i<500000; $i++)
{
$getVar=uniqid();
$dato=gval($getVar);
}

Resultados obtenidos

Tipo de prueba Primer resultado Segundo resultado Tercer Resultado Media
TEST 0 4,4530 4,5960 4,4310 4,4933
TEST 1 6,7560 6,1390 5,8350 6,2433
TEST 2 7,3880 7,3350 7,3120 7,3450
TEST 3 5,5270 4,9900 5,0610 5,1927
TEST 4 5,4480 4,9820 5,1060 5,1787
TEST 5 6,2860 6,4050 5,8160 6,1690
TEST 6 5,9840 5,6290 5,7440 5,7857

Con estos resultados podemos observar que:

  • Efectivamente, el test de control es el más lento, incluye generación del nombre aleatorio del parámetro, el bucle y la carga del programa (PHP), pero más o menos partimos de que el tiempo mínimo para esta prueba son cerca de 4.5 segundos. El tiempo de obtención de dato, será más o menos la diferencia entre el tiempo total del test y los 4.5 segundos aproximadamente obtenidos aquí.
  • TEST 1: error_reporting(E_NONE) nos entorpecería en tareas de depuración (cuando realmente deseemos tenerlo en otro valor), de todas formas no es una opción, comparado con los demás. Deberíamos estar pendientes del valor de error_reporting() cuando queramos depurar el script, no ahorraríamos mucho código y tampoco es el mejor con respecto al tiempo.
  • TEST 2: Estaría bien que un carácter nos solucionara la vida, pero no es así… conseguimos el peor tiempo de todos. Se ve que a PHP le sienta mal cuando no encuentra una variable…
  • TEST 3 y TEST 4: Parece que un if…else nos puede ayudar a hacer nuestro código más rápido, un poco más elegante y funcionar mejor. También vemos que el operador ? puede ayudar. Y vemos, además, que el tiempo entre if…else y entre ? es el mismo (la diferencia es despreciable en este caso)
  • TEST5: Visto lo visto con TEST3 y TEST4 en los que escribimos más que con TEST2, vamos a crear una función auxiliar, a ver cómo se comporta con respecto a lo que tenemos. Comprobamos que el uso de una función, hace nuestro código más claro (punto positivo) y aún tarda menos que TEST1 y TEST2 aunque tarda algo más que TEST3 y TEST4. TEST5 tarda aproximadamente lo que TEST1.
  • TEST6: Parece que el hecho de no copiar el valor de una variable de verdad influye significativamente en el tiempo cerca de medio segundo; podemos seguir utilizando un método elegante con nuestra función auxiliar y al mismo tiempo aproximarnos a un tiempo mínimo.
  • Sé que 500000 iteraciones puede parecer exagerado aunque en momentos de carga de servidor y cuando esperamos que nuestra página tenga un número importante de visitas tal vez se agradezca un poco de agilidad, sobre todo en recepción de formularios (con variables $_POST). De todas formas, queda claro con estas pruebas que el hecho de poner una @ en un comando o asignación, sólo hace que PHP se trague los errores, pero es un método lento, con lo que podemos empezar a optimizar nuestro código por ahí.

Cronometrando en C

Domingo, 5 de Diciembre de 2010 Gaspar Fernández 4 comentarios

Puede que queramos hacer una comparativa de cuánto tarda nuestro código en ejecutarse, o que tengamos varios algoritmos y queramos saber cuál es el más rápido. O que estemos haciendo un programa que mida el tiempo de reacción de un usuario en una cierta tarea. Aquí vemos ejemplos con diferentes precisiones.

Minutos

Este método viene bien para ver el tiempo transcurrido en un proceso que puede durar varios minutos. Si algo tarda 20 minutos, suele darnos igual segundo arriba, segundo abajo.

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <time.h>

int main(int argc, char *argv[])
{
int anterior;
anterior=time(NULL);
sleep(1);
printf("Transcurrido: %ld\n", time(NULL)-anterior);
}

Sí, simplemente es esto, pero no voy a hacer un post sólo para esto, vamos a crear una función que llamemos cuando queremos empezar a cronometrar y volvamos a llamar cuando queramos parar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <time.h>

int cronosec(int startstop)
{
  static int pre_time;

  if (startstop)
    pre_time=time(NULL);
  else
    return time(NULL)-pre_time;

  return 0;
}

int main(int argc, char *argv[])
{
  cronosec(0);
  sleep(1);
  printf("Transcurrido: %ld\n", cronosec(1));
}

Con cronosec cuando empezamos a cronometrar, pasamos el parámetro 0, y cuando queremos ver el tiempo transcurrido llamamos al parámetro 1.

Precisión de milisegundo y microsegundo

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
#include <unistd.h>
#include <sys/time.h>

long long cronomsec(int startstop)
{
  static long long pre_time;
  struct timeval tv;

  if (startstop)
    {
      gettimeofday(&tv, NULL);      
      pre_time=tv.tv_sec*1000+tv.tv_usec/1000;
    }
  else
    {      
      gettimeofday(&tv, NULL);      
      return tv.tv_sec*1000+tv.tv_usec/1000 - pre_time;
    }
    return 0;
}

long long cronousec(int startstop)
{
  static long long pre_time;
  struct timeval tv;

  if (startstop)
    {
      gettimeofday(&tv, NULL);      
      pre_time=tv.tv_sec*1000000+tv.tv_usec;
    }
  else
    {      
      gettimeofday(&tv, NULL);      
      return tv.tv_sec*1000000+tv.tv_usec - pre_time;
    }
    return 0;
}

Las dos se usan igual que cronosec(), cronomsec() tiene precisión de milisegundos y cronousec() de microsegundos; si las observamos las dos son iguales, sólo que en una, como queremos contar milisegundos, multiplicamos los segundos por mil y dividimos microsegundos entre 1000 (struct timeval es capaz de devolvernos microsegundos también).
En cronousec sólo multiplicamos los segundos por 1000000 (un millón).

Nanosegundo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <time.h>

unsigned long long crononsec(int startstop)
{
  static unsigned long pre_time;
  static unsigned long pre_secs;
  struct timespec ts;

  if (startstop)
    {
      clock_gettime(CLOCK_MONOTONIC, &ts);
      pre_time=ts.tv_nsec;
      pre_secs=ts.tv_sec;
    }
  else
    {      
      clock_gettime(CLOCK_MONOTONIC, &ts);
      return (ts.tv_sec-pre_secs)*1000000000+ts.tv_nsec - pre_time;
    }

    return 0;
}

Con esta función, gracias a clock_gettime(), obtenemos precisión de nanosegundos. Utilizo CLOCK_MONOTONIC porque este reloj no cambia, es decir, existe la función clock_settime, y este reloj no se podrá cambiar, de hecho no queremos que nos hagan trampa en nuestras aplicaciones, cambien la hora del ordenador y nuestro programa no funcione bien. Si no, podemos usar CLOCK_REALTIME, aunque este se puede cambiar, aunque necesita privilegios.
En este último ejemplo, vemos que no multiplico los segundos desde el principio como hacía antes, ahora sólo acumulo segundos, y luego multiplico la diferencia; esto es, porque el número de segundos ya acontecidos desde 1970 (Epoch) es demasiado grande, y si lo multiplico por 1000000000 (mil millones) para saber cuántos microsegundos han pasado desde entonces, el número es demasiado grande y desborda la variable (mira que es long long y tiene 64bits), así que aplico este método, mucho más inteligente y menos costoso (dos multiplicaciones frente a una), también podía hacer todos los métodos anteriores así, aunque el precio de hacer menos operaciones es la reserva de una variable más.

Un ejemplo para probarlo todo

Una vez colocadas todas las funciones en un archivo .c y todos los includes correspondientes arriba, añadimos este main():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main(int argc, char *argv[])
{
  cronosec(1);
  cronomsec(1);
  cronousec(1);
  crononsec(1);
  usleep(4123789);      /* 4 segundos y pico*/
  printf("segundos: %d\n", cronosec(0));
  printf("milisegundos: %lld\n", cronomsec(0));
  printf("microsegundos: %lld\n", cronousec(0));
  printf("nanosegundos: %llu\n", crononsec(0));
 
  return EXIT_SUCCESS;
}

Una advertencia, las funciones que tienen más precisión de segundos son dependientes de la plataforma, además, para compilarlos necesitamos añadir la librería rt al compilar, de la siguiente forma:

$ gcc -lrt -o crono crono.c

Nos ponemos serios con PHP: Empezamos con un CRUD (la esencia no es exclusiva de PHP)

Viernes, 16 de Julio de 2010 Gaspar Fernández Sin comentarios

crudHace años tuve entre manos un gran proyecto web, en el que invertí 8 meses de mi vida y funcionó bastante bien durante un tiempo. En su desarrollo recuerdo que tuve que desarrollar cerca 50 formularios diferentes, comprobar los valores de cada uno de los campos, hacer lecturas y escrituras en base de datos con los datos obtenidos, y opcionalmente realizar alguna tarea extra una vez enviado y validado el formulario. Además, tenía que ser capaz de listar la información, modificarla y eliminarla. Por aquel entonces, aunque había partes en común y aproveché para realizar código, no lo hice de la forma más efectiva; podría haber ahorrado muchísimo trabajo.
En los últimos años, para mis proyectos web decidí fabricarme una biblioteca para hacer este tipo de tareas de forma más rápida (con menos esfuerzo, y al fin y al cabo de forma más depurada y segura). Para eso me fabriqué una biblioteca CRUD.

Es cierto que perdemos eficiencia, es más rápida por lo general una herramienta específica, que haga justo lo que se le pide en un momento dado, que una herramienta configurable y flexible (tendremos muchas más sentencias de control y a veces se ejecutarán procesos innecesarios), pero por otro lado hay que tener en cuenta que no podemos echar un mes en un proyecto que generalmente requiere unos días, y por otra parte, seguro seguro que tenemos que hacer modificaciones antes de entregarlo, y éstas no deben llevarnos otro mes más. En definitiva, nos compensa perder algo de eficiencia en favor de un tiempo de desarrollo más corto y generar un código más fácilmente mantenible.

Lo importante de un CRUD, es la posibilidad de Crear, Ver, Actualizar, Borrar (y listar información), si lo pensamos es simple, lo ideal sería tener una clase (o una función) que simplemente llamándola nos hiciera esta complicada tarea; aunque también es verdad, si lo pensamos un poco más, que podemos complicar todo esto de mil formas, y todo lo que queramos. Además, tenemos que crear una herramienta que sea lo más flexible posible para todo tipo de bases de datos y tablas, y además, sería interesante manejar los errores que puedan surgir en el proceso, para poder generar una salida acorde.

Aunque podemos encontrar bibliotecas por Internet que hacen esta tarea, yo siempre prefiero una solución DIY (Do it yourself) (Hazlo tú mismo), de esta forma, el código será 100% mío y yo controlaré el proceso por completo, además de que como materia de aprendizaje es bastante efectivo. Dejo por aquí algunas instrucciones para empezar a fabricarnos un CRUD un tanto sencillo (y que a medida que vayamos haciendo proyectos podemos ir completándolo con las necesidades que nos vayan surgiendo):

  • Listar información de base de datos: Debemos tener la posibilidad de elegir los campos a listar
  • Crear entrada: Debemos tener la posibilidad de introducir campos que concuerden con entradas en una base de datos, y también la posibilidad de generar datos automáticamente (fecha de introducción, identificadores únicos, etc):
    • Necesitaremos un generador de formularios y la posibilidad de elegir el mejor tipo de campo para cada campo de la tabla que vamos a modificar
    • Debemos tener la posibilidad de verificar, vía servidor (PHP) o cliente (Javascript), mejor hacerlo de las dos formas, que los datos estén bien formados (números máximos de caracteres, formato de introducción (por ejemplo, de una fecha, un teléfono, una dirección e-mail…)
    • Inserción en tabla, y muestra de la salida, para que el usuario vea si todo ha ido bien o mal. Sería conveniente también, que esto fuera una redirección, para que, si se refresca la página, no se reenvíen los formularios.
  • Modificar entrada:
    • Podemos aprovechar mucho código de la creación de entrada (muestra de formulario, verificación…)
    • Debemos modificar la tabla de datos, a lo mejor actualizar fechas de modificación, logs, etc
  • Eliminar entrada:
    • Idealmente, debemos presentar un diálogo tipo: “¿Está seguro?” para confirmar que no se hace por accidente, tal vez los datos no siempre interese eliminarlos, sólo hacer una marca de que no se visualicen.
  • Ver información:
    • Necesitamos hacer un sistema de visualización flexible, automático y tener la posibilidad de seleccionar los campos a mostrar (los identificadores únicos, fechas internas, IDs, no le suelen interesar al usuario), por otra parte, a veces para visualizar un dato es necesario hacer varias consultas a base de datos y tenemos que estar preparados.

Pero como dije antes, esto lo podemos complicar hasta la saciedad, aumentando el número de campos que podemos introducir (por ejemplo inserción de ficheros, Javascripts para introducir la fecha de forma fácil, o inclusión de áreas de texto HTML (como TinyMCE).

En cuanto al listado podemos implementar paginación automática (para el caso de que haya muchos elementos a listar) y ordenación de los elementos por columna (tal vez por nombre, por fecha, etc)

Por otra parte, tenemos que tener muy en cuenta el diseño, éste suele ser diferente para cada página (quitando esas páginas que parece que están hechas todas con el mismo molde), así que tenemos que hacer que nuestra salida en (x)HTML sea lo más flexible posible, que nos deje introducir código adicional, cambiar las imágenes utilizadas (iconos de insertar, eliminar…)

Y como colofón, podemos implementar soporte para relaciones, puede que echemos unos días desarrollando nuestro CRUD, pero  nos va a ahorrar mucho tiempo de desarrollo si lo utilizamos en nuestros proyectos.

Búsquedas rápidas en el historial de BASH

Jueves, 6 de Mayo de 2010 Gaspar Fernández Sin comentarios

2683642114_bba3d6383e
Cuando pasamos mucho tiempo en escribiendo en terminal, a veces tenemos la necesidad de repetir algo que escribimos en el pasado. Puede que estemos compilando algún programa, o haciendo un ./configure e instalando dependencias de un programa…
Muchas distribuciones lo tienen por defecto (por ejemplo Gentoo), pero otras muchas (Ubuntu, Mandriva, ArchLinux…) no lo tienen; se trata de activar las teclas de Avance y Retroceso de página para hacer búsquedas en nuestro historial.
Esto sirve, por ejemplo para que, si escribimos algunas letras de una orden que enviamos en el pasado, y pulsemos RePag nos encuentre aquellas órdenes que enviamos en el pasado y nos ahorre unas cuantas pulsaciones de teclado.

Lo que debemos hacer es editar el archivo /etc/inputrc, muchas distribuciones traen ciertas líneas parecidas, por lo que si vemos alguna línea que empiece por “\e[5~” o “\e[6~” deberíamos comentarla (con #) o borrarla; y tras ello introducir lo siguiente:

“\e[5~”: history-search-backward
“\e[6~”: history-search-forward

Para mí, es la forma más cómoda de trabajar.

Foto: Liamdunn (Flickr)

Monitoriza las aplicaciones con las que trabajas

Miércoles, 7 de Abril de 2010 Gaspar Fernández Sin comentarios

dtracks Lo leí hace tiempo en Genbeta pero hasta hace unos días no me decidí a probarlo (lo dejé medio olvidado en la lista de cosas por hacer), pero me ha gustado bastante.
Sirve para saber con qué programas pasas el tiempo en el ordenador, y así podemos ver lo productivos que somos, según esto mi productividad puede mejorar.
Es un programa pequeño pero muy útil. Se puede descargar el código fuente desde aquí, pero podéis probar a ver si vuestra distribución lo trae en sus repositorios.

Visita otras webs de la red