domingo, 29 de junio de 2014

Crear un sistema de videovigilancia con Raspberry, Webcam y Dropbox (Parte II)

Buenas gente. ¿Acabando los exámenes, no? Os traigo la segunda parte del sistema de vigilancia. Esto también lo podéis usar para tener una copia de seguridad 'autoactualizable' de vuestra carpeta de dropbox en la Raspberry Pi y viceversa.

Si te perdistes la primera parte, aquí la tienes.

Lo primero que vamos a hacer es descargarnos una pequña aplicación de Dropbox creada por Andrea Fabrizi. Para ello nos vamos a la shell (o entramos via ssh como hago yo) y escribimos lo siguiente:
cd /usr/local/bin/

sudo wget https://raw.github.com/andreafabrizi/Dropbox-Uploader/master/dropbox_uploader.sh

sudo chmod +x dropbox_uploader.sh

Esto descargará la aplicación en /usr/local/bin/ y le dará los permisos necesarios. Si ejecutaramos dropbox_uploader.sh (escribiendolo tal cual) nos saldrían las opciones necesarias, pero aún no podemos ejecutarlo. Necesitamos explicarle con que cuenta debe comunicarse. Para ello nos vamos a https://www.dropbox.com/developers/apps y creamos una nueva app "Dropbox API app". Le decimos que trabajaremos con "files and datastores" y que necesitamos acceder también a archivos de dropbox (por si nos hiciera falta) y para lo cual hay que pulsar en el No. Especificamos como archivos fotos (y el video es opcional) y le damos un nombre a la app. No cerreis la ventana que os sale. Ahora si teneis que ejecutar el script instalado anteriormente. Os pedira una "app key" y una "app secret" las cuales son las que os dice la ultima ventana de dropbox. Si os pregunta si darle acceso full, aceptad.
Si ahora escribierais en la consola:
dropbox_uploader.sh list

Os devolvería todas las carpetas y archivos que tenéis en la raíz.

Bueno, como usaremos un solo comando para subir (en este caso) los archivos de motion a dropbox, vamos a usar un alias en vez de un script (por hacerlo más liviano).
Un alias en Linux es una palabra que tu reservas y que significará uno o varios comandos seguidos. Por ejemplo, si pusieramos como alias a='cd /' cada vez que escribieramos 'a' y pulsaramos enter nos llevaría a la carpeta raíz del disco. Bueno, nuestro comando será el siguiente:

/usr/local/bin/./dropbox_uploader.sh upload /tmp/motion/* Motion/$(date +%Y%m%d)


El cual lo que hace es ejecutar dropbox_uploader.sh con la opcion upload, la carpeta de origen de motion y como destino una carpeta en Dropbox llamada Motion (si no existe, la crea) y dentro de ella una carpeta con la fecha actual. Para crear el alias editaremos un archivo llamado bashrc. Para ello ejecutamos:

sudo nano /home/pi/,bashrc


y bajamos abajo del todo. Allí escribimos :

alias subidaMotion='/usr/local/bin/./dropbox_uploader.sh upload /tmp/motion/* Motion/$(date +%Y%m%d)'


Cerramos con Control+X, nos pregunta si guardar, escribimos 'y' y dejamos el nombre tal y como está. Deberemos reiniciar para que surta efecto el cambio. A partir de ese momento cada vez que escribais subidaMotion os copiará todas las imagenes de /tmp/motion a la carpeta de dropbox; la cual depende de la fecha.

Bueno gente, eso es todo. Ya solo os queda toquetear. Quizás en un futuro próximo me de por hacer un script que cada 5 minutos mirase el contenido de la carpeta /tmp/motion y si fuera distinto de vacío subiera automáticamente. También cabe la posibilidad de hacer que nos enviara un correo o nos avisara vía Twitter. Son cosas sencillas que iré poniendo en cuanto tenga tiempo.

Saludos y espero que os haya gustado;)

viernes, 27 de junio de 2014

Examen final Programación de Sistemas y Concurrencia. Junio 2012.

Os dejo mi solución al ejercicio de C del examen final de Programación de Sistemas y Concurrencia de la UMA de 2012.

Se desea implementar una lista enlazada circular para representar la lista de procesos que están disponibles para ejecución. El puntero externo apuntará al proceso que le corresponde la ejecución (ver figura).  

Definir la estructura de datos e implementar las siguientes operaciones: 
void Crear (LProc *lista) 
Crea una lista de procesos vacía. 
void AñadirProceso (LProc *lista, int idproc) 
Añade el proceso con identificador idproc a la lista de procesos disponibles para ejecución. Este proceso se añade como nodo anterior al nodo al que apunta ejecución. Si la lista está vacía el puntero externo apuntará a l único nodo. Dada la figura anterior, si queremos añadir el proceso 5, la lista quedaría como aparece en la figura: 


void MostrarLista( LProc lista) 
Muestra la lista de los procesos que están disponibles para la ejecución. 
void EjecutarProceso(LProc *lista) 
Simula la ejecución del proceso apuntado por ejecución, eliminándolo de la lista de procesos.  Así, partiendo de la figura anterior, la lista quedaría :


ListaCircular.h
#ifndef LISTACIRCULAR_H_
#define LISTACIRCULAR_H_


typedef struct Lista LProc;

struct Lista{
 struct TNodo * ejecucion;
};
struct TNodo{
 int valor;
 struct TNodo * sig;
};

void Crear (LProc *lista);
//Crea una lista de procesos vacía.
void AnadirProceso (LProc *lista, int idproc);
void MostrarLista( LProc lista);
//Muestra la lista de los procesos que están disponibles para la ejecución.
void EjecutarProceso(LProc *lista);

#endif /* LISTACIRCULAR_H_ */

ListaCircular.c
#include <stdio.h>
#include <stdlib.h>
#include "ListaCircular.h"

void Crear (LProc *lista){
 //Crea una lista de procesos vacía.
 (*lista).ejecucion = NULL;
}
void AnadirProceso (LProc *lista, int idproc){

 struct TNodo* nuevo = malloc(sizeof(struct TNodo));
 struct TNodo * auxiliar = (*lista).ejecucion;
 struct TNodo * anterior = NULL;
 nuevo -> valor = idproc;
 int insertado = 0;
 if((*lista).ejecucion == NULL){ //lista vacía
  nuevo ->sig = nuevo;
  (*lista).ejecucion = nuevo;
  insertado = 1;
 }
 while(!insertado ){
  if(auxiliar -> sig == (*lista).ejecucion){ // se añade detras de auxiliar
   nuevo -> sig = (*lista).ejecucion;
   auxiliar -> sig = nuevo;
   insertado = 1;
  }else{
   anterior = auxiliar;
   auxiliar = auxiliar -> sig;
  }
 }

}
void MostrarLista( LProc lista){
 //Muestra la lista de los procesos que están disponibles para la ejecución.
 struct TNodo * aux = lista.ejecucion;
 if(lista.ejecucion == NULL){
  printf("No hay procesos disponibles para la ejecución. \n");
 }else{
  printf("Procesos disponibles para la ejecución: \t");


   printf(" %d -> ",lista.ejecucion ->valor );
   lista.ejecucion = lista.ejecucion -> sig;
   while(lista.ejecucion != aux){
    printf(" %d -> ",lista.ejecucion ->valor );
    lista.ejecucion = lista.ejecucion -> sig;
   }
   printf("\n");
 }

}
void EjecutarProceso(LProc *lista){
 /*
  * Simula la ejecución del proceso apuntado por ejecución, eliminándolo de la lista de procesos.
Así, partiendo de la figura anterior, la lista quedaría :
  */
 struct TNodo * aux;
 struct TNodo * auxiliar = (*lista).ejecucion ;

 while(auxiliar -> sig != (*lista).ejecucion){
   auxiliar = auxiliar -> sig;
 }
 aux = (*lista).ejecucion ->sig;
 printf("Se ejecuta el proceso: %d \n", (*lista).ejecucion -> valor);
 free((*lista).ejecucion);
 (*lista).ejecucion = aux;
 auxiliar -> sig = (*lista).ejecucion;
}

int main(void) {
 LProc lista;
 Crear(&lista);
 MostrarLista(lista);
 AnadirProceso(&lista, 4);
 AnadirProceso(&lista, 6);
 AnadirProceso(&lista, 1);
 AnadirProceso(&lista, 8);
 AnadirProceso(&lista, 3);
 AnadirProceso(&lista, 5);
 MostrarLista(lista);
 EjecutarProceso(&lista);
 MostrarLista(lista);

 return EXIT_SUCCESS;
}


miércoles, 25 de junio de 2014

Ejercicios C++: Registros y Arrays (III)

Hace ya tiempo que no escribía una entrada de ejercicios de C++, así que aquí os dejo la tercera que vemos sobre registros y arrays, pues es lo más complicado que hemos visto hasta ahora de este lenguaje. En las (dos) próximas entradas de C++ trabajaremos con strings.


Se dispone de un array de 80 números enteros en el que al menos hay dos números que son iguales y dos que son distintos. Obtenga una función que tomando como parámetro dicho array, devuelva un elemento del array que sea mayor que el mínimo de éste.
int minimo (const TArray& array){
 int min = array[0];
 for(unsigned i = 1; i < TAMANO; i++){
  if(array[i] < min){
   min = array[i];
  }
 }
 return min;
}

int ejercicio (const TArray& array){
 int res;
 bool encontrado = false;
 unsigned i= 0;
 minimo = minimo(array);
 while(i < TAMANO && !encontrado){
  if(array[i] > minimo){
   encontrado = true;
   res = array[i];
  }
  i++;
 }

 return res;
}

Diseña un algoritmo que permita invertir el contenido de un array.
#include <iostream>
using namespace std;

const unsigned TAMANO = 10;

typedef int TArray[TAMANO];


void leerArray(TArray& array){
 for(unsigned i=0; i < TAMANO; i++){
  //cout << "v["<<i<<"]->";
  cin >> array[i];
 }
}

void invertir(const TArray& array, TArray& inv){
 unsigned j = TAMANO -1;
 for(unsigned i = 0; i < TAMANO; i++){
  inv[i] = array[j];
  j--;
 }
}



void mostrarArray(const TArray& unarray){
 for(unsigned i =0; i < TAMANO; i++){
  cout << "["<<unarray[i]<<"]";
 }
}
int main() {
 TArray array, inv;
 leerArray(array);
 invertir(array, inv);
 mostrarArray(inv);
 return 0;
}


Escriba un programa que efectúe la conversión de un número natural en base 10 a otra determinada base, sabiendo que el resultado no sobrepasará los 50 dígitos. El usuario introducirá primero el número en base 10 y después la base a la que convertirlo (el programa debe asegurarse de que la base no sea ni menor de 2 ni mayor de 9).
#include <iostream>
using namespace std;

const unsigned MAXIMO = 50;

typedef unsigned TArray[MAXIMO];

void leerDatos(unsigned &num, unsigned& base){
 cout << "Introduce el número a convertir: ";
 cin >> num;
 do{
  cout << "Introduce la base: ";
  cin >> base;
 }while(base < 2 || base > 9);
}

void convertir(unsigned num, unsigned base, TArray array, unsigned&  i){
 i=0;
 while(num != 0){
  array[i] = num%base;
  num = unsigned(num/base);
  i++;
 }
}
void invertir(const TArray& array, TArray& inv, unsigned contador){
 unsigned j = contador -1;
 for(unsigned i = 0; i < contador; i++){
  inv[i] = array[j];
  j--;
 }
}

void mostrarSolucion(const TArray& array, unsigned contador){
 cout << "El número ya convertido es ";
 for(unsigned i=0; i < contador  ; i++){
  cout << array[i];
 }
}

int main() {
 unsigned num, base, i;
 TArray primero, solucion;
 leerDatos(num, base);
 convertir(num, base, primero, i);
 invertir(primero, solucion, i);
 mostrarSolucion(solucion, i);
 return 0;
}

Dados los siguientes tipos (para una constante MAX cualquiera):

typedef char Componentes[MAX];

struct Vector {

Componentes datos; // array de caracteres

unsigned tam; // numero de celdas ocupadas

};


a) La moda de un array de caracteres es el carácter del array que se repite más frecuentemente. Si varios caracteres se repiten con la misma frecuencia máxima, entonces no hay moda. Escribe un procedimiento con tres parámetros. El primero es de entrada para recibir un registro de tipo Vector que contiene el array datos con tam caracteres. El segundo parámetro es de salida e indicará si se ha encontrado la moda en el array o no. El tercer parámetro es de salida y será el carácter que representa la moda (si es que existe).

#include <iostream>
using namespace std;

const unsigned MAX =50;

typedef char Componentes[MAX];
struct Vector {
 Componentes datos; // array de caracteres
 unsigned tam; // numero de celdas ocupadas
};

void apartadoA(const Vector& vector, bool& existeModa, char& moda){
 int apariciones;
 int mayor=0;
 char m;
 existeModa = false;
 for(unsigned i=0; i < vector.tam; i++){
  apariciones = 1;
  for(unsigned j=i+1; j< vector.tam;j++){
   if(vector.datos[i] == vector.datos[j]){
    apariciones++;
   }
  }
  if(apariciones != mayor){
   if(mayor < apariciones){
    mayor = apariciones;
       m = vector.datos[i];
       existeModa = true;
   }
  }else{
   existeModa = false;
  }
 }
 if(existeModa){
  moda = m;
 }
}

void leerVector(Vector& vector){
 vector.tam = 0;
 char a;
 cin >> a;
 while(a != '.'){
  vector.datos[vector.tam]= a;
  vector.tam++;
  cin >> a;
 }
}

int main() {
 Vector vector;
 bool existeModa;
 char moda;
 leerVector(vector);
 apartadoA(vector, existeModa, moda);
 if(existeModa){
  cout << "La moda es '"<< moda<<"'";
 }else{
  cout << "No existe la moda.";
 }
 return 0;
}

b) Diseña una función booleana que dados dos registros de tipo Vector como parámetros, devuelva TRUE si son iguales y FALSE en otro caso. Dos vectores son iguales si contienen los mismos elementos y en el mismo orden relativo, suponiendo que el primer elemento sigue al último.
#include <iostream>
using namespace std;

const unsigned MAX=50;
typedef char Componentes[MAX];
struct Vector {
 Componentes datos; // array de caracteres
 unsigned tam; // numero de celdas ocupadas
};

bool vectoresIguales(Vector& vector1, Vector& vector2){
 bool iguales = false;
 if(vector1.tam == vector2.tam){

 iguales = true;
 unsigned i=0;

 bool encontrado=false;
 unsigned j=0;
  while(j < vector2.tam && !encontrado){
   if(vector1.datos[0] == vector2.datos[j]){
    encontrado = true;
   }else{
    j++;
   }
  }

 while(i < vector1.tam && iguales){
  iguales = false;
   if(vector1.datos[i] == vector2.datos[j]){
    i++;
    if(j == vector2.tam -1){
     j = 0;
    }else{
     j++;
    }
    iguales = true;
   }

 }

 }
 return iguales;

}


void leerVector(Vector& vector){
 vector.tam = 0;
 char a;
 cout << "Introduce cadena: ";
 cin >> a;
 while(a != '.'){
  vector.datos[vector.tam]= a;
  vector.tam++;
  cin >> a;
 }
}


int main() {
 Vector v1, v2;
 leerVector(v1);
 leerVector(v2);
 if(vectoresIguales(v1,v2)){
  cout << "Los vectores son iguales.";
 }else{
  cout << "No son iguales";
 }

 return 0;
}

c) Diseña un procedimiento que tome como parámetros de entrada dos vectores con los arrays ordenados y devuelva (con un parámetro de salida) el vector resultado de realizar la mezcla ordenada de los dos arrays contenidos en los vectores de entrada.
#include <iostream>
using namespace std;

const unsigned MAX =50;

typedef char Componentes[MAX];
struct Vector {
 Componentes datos; // array de caracteres
 unsigned tam; // numero de celdas ocupadas
};

void apartadoC(const Vector& v1, const Vector& v2, Vector& res){
 unsigned i=0;
 unsigned j=0;
 unsigned cnt =0;
 res.tam = v1.tam+v2.tam;
 while(cnt < res.tam && i < v1.tam && j < v2.tam){
  if(v1.datos[i] < v2.datos[j]){
   res.datos[cnt] = v1.datos[i];
   i++;
  }else{
   res.datos[cnt] = v2.datos[j];
   j++;
  }
  cnt++;
 }
 if(i == v1.tam){
  while(cnt < res.tam){
   res.datos[cnt] = v2.datos[j];
   j++;
   cnt++;
  }
 }else if (j == v2.tam){
  while(cnt < res.tam){
     res.datos[cnt] = v1.datos[i];
     i++;
     cnt++;
  }
 }
}

void leerVector(Vector& vector){
 vector.tam = 0;
 char a;
 cout << "Introduce los datos: ";
 cin >> a;
 while(a != '.'){
  vector.datos[vector.tam]= a;
  vector.tam++;
  cin >> a;
 }
}

void mostrarArray(const Vector& res){
 for(unsigned i=0; i < res.tam; i++){
  cout <<"["<<res.datos[i]<<"]";
 }
}


int main() {
 Vector v1, v2, res;
 leerVector(v1);
 leerVector(v2);
 apartadoC(v1,v2, res);
 mostrarArray(res);
 return 0;
}

Diseña un algoritmo para calcular la suma de los elementos de la diagonal principal de una
matriz cuadrada.
include <iostream>
using namespace std;

typedef double TFilas[100];
typedef TFilas TMat[100];

struct TMatriz{
 unsigned dim;
 TMat matriz;
};

void leerDimension(TMatriz& mat){
 cout << "Introducir número de filas y columnas: ";
 cin >> mat.dim;
}

void leerMatriz(TMatriz& mat){
 cout << "Introduce los valores de la matriz."<< endl;
 for(unsigned f =0; f < mat.dim; f++){
  for(unsigned c =0; c < mat.dim; c++){
   cout << "Matriz["<<f+1<<"]["<<c+1<<"]->";
   cin >> mat.matriz[f][c];
  }
 }
}

void mostrarMatriz(const TMatriz& mat){
 for(unsigned f =0; f < mat.dim; f++){
  for(unsigned c =0; c < mat.dim; c++){
   cout << mat.matriz[f][c]<<" ";
  }
  cout << endl;
 }
}

double sumaDiagonal(const TMatriz& mat){
 double suma=0;
 for(unsigned f =0; f < mat.dim; f++){
  for(unsigned c =0; c < mat.dim; c++){
    if(c==f){
     suma = suma + mat.matriz[f][c];
    }
  }
 }
 return suma;
}

int main() {
 cout << "Suma de la diagonal de una matriz." << endl; 
 TMatriz matriz;
 leerDimension(matriz);
 leerMatriz(matriz);
 mostrarMatriz(matriz);
 cout << "La suma de la diagonal es "<< sumaDiagonal(matriz);

 return 0;
}


Una matriz tiene un punto silla en una de sus componentes, si ese componente es el mayor valor de su columna y el menor de su fila. Diseña un algoritmo que recogiendo de teclado los componentes de una matriz cuadrada de enteros de hasta un máximo de 10x10, muestre en la pantalla las coordenadas de todos sus puntos silla.
##include <iostream>
using namespace std;

typedef double TFilas[10];
typedef TFilas TMat[10];

struct TMatriz{
 unsigned dim;
 TMat matriz;
};

void leerDimension(TMatriz& mat){
 do{
 cout << "Introducir número de filas y columnas: ";
 cin >> mat.dim;
 }while(mat.dim > 10);
}

void leerMatriz(TMatriz& mat){
 cout << "Introduce los valores de la matriz."<< endl;
 for(unsigned f =0; f < mat.dim; f++){
  for(unsigned c =0; c < mat.dim; c++){
   cout << "Matriz["<<f+1<<"]["<<c+1<<"]->";
   cin >> mat.matriz[f][c];
  }
 }
}

void mostrarMatriz(const TMatriz& mat){
 for(unsigned f =0; f < mat.dim; f++){
  for(unsigned c =0; c < mat.dim; c++){
   cout << mat.matriz[f][c]<<" ";
  }
  cout << endl;
 }
}

double mayorColumna(const TMatriz& mat, unsigned columna){
 double mayor = mat.matriz[0][columna];
 for(unsigned f=1; f < mat.dim; f++){
  if(mayor < mat.matriz[f][columna]){
   mayor =  mat.matriz[f][columna];
  }
 }
 return mayor;
}

double menorFila(const TMatriz& mat, unsigned fila){
 double menor = mat.matriz[fila][0];
  for(unsigned c=1; c< mat.dim; c++){
   if(menor > mat.matriz[fila][c]){
    menor =  mat.matriz[fila][c];
   }
  }
  return menor;
}


void buscarPuntos(const TMatriz& mat){
 for(unsigned f=0; f < mat.dim; f++){
  for(unsigned c=0; c < mat.dim; c++){
   if(mat.matriz[f][c] == menorFila(mat, f) && menorFila(mat, f)==mayorColumna(mat, c)){
    cout << "El elemento de la fila "<< f+1 << " y la columna "<< c+1 << " es un punto de silla."<< endl;
   }
  }
 }
}
int main() {
 cout << "Buscar punto de silla." << endl; // prints !!!Hello World!!!
 TMatriz matriz;
  leerDimension(matriz);
  leerMatriz(matriz);
  mostrarMatriz(matriz);
  buscarPuntos(matriz);
 return 0;
}

Un tablero n-goro es un tablero con n x (n+1) casillas. Una propiedad interesante es que se pueden visitar todas sus casillas haciendo el siguiente recorrido por diagonales. Empezamos por la casilla (1,1) y recorremos la diagonal principal hacia la derecha y hacia abajo hasta llegar a la casilla (n,n). La siguiente casilla a visitar sería la (n+1,n+1) que no existe porque nos saldríamos del tablero por abajo. En estos casos siempre se pasa a la casilla equivalente en al primera fila, es decir, la (1,n+1). Ahora seguimos moviéndonos hacia la derecha y hacia abajo. Pero la siguiente casilla sería la (2,n+2) que no existe porque nos hemos salido por la derecha. En estos casos se continúa por la casilla equivalente de la primera columna, es decir, la (2,1). De nuevo nos movemos hacia la derecha y hacia abajo, hasta alcanzar la casilla (n,n-1). La siguiente casilla sería la (n+1,n), pero como nos saldríamos por abajo pasamos a la casilla equivalente de la primera fila (1,n). Si se continúa con este proceso se termina visitando todas las casillas del tablero.
Diseñe un procedimiento que dada una constante N devuelve como parámetro de salida un
tablero N-goro con sus casillas rellenas con el número correspondiente al momento en que se
visitan.
#include <iostream>
using namespace std;

typedef double TFilas[10];
typedef TFilas TMat[10];

struct TMatriz{
 unsigned nco, nfi;
 TMat matriz;
};

void leerDimension(TMatriz& mat){
 cout << "Introducir número de filas: ";
 cin >> mat.nfi;
 mat.nco= mat.nfi+1;
}
void inicializar(TMatriz& mat){
 for(unsigned i=0; i < mat.nfi; i++){
  for(unsigned j= 0; j < mat.nco; j++){
   mat.matriz[i][j]= 0;
  }
 }
}

void escribirMatriz(TMatriz& mat){
 unsigned fila = 0;
 unsigned columna =0;
 for(unsigned i = 1; i <= mat.nfi*mat.nco; i++){
  if(mat.matriz[fila][columna]== 0){
   mat.matriz[fila][columna]= i;

   fila++;
   columna++;

   if(fila >= mat.nfi){
   fila = 0;
   }
   if(columna >= mat.nco){
   columna =0;
   }
  }
 }

}


void mostrarMatriz(const TMatriz& mat){
 for(unsigned f =0; f < mat.nfi; f++){
  for(unsigned c =0; c < mat.nco; c++){
   cout << mat.matriz[f][c]<<"  ";
  }
  cout << endl;
 }
}


int main() {
 
 TMatriz matriz;
 leerDimension(matriz);
 inicializar(matriz);
 escribirMatriz(matriz);
 mostrarMatriz(matriz);
 return 0;
}



domingo, 22 de junio de 2014

Examen parcial de Programación de Sistemas y Concurrencia. Curso 2013/2014

Después de más de dos meses sin publicar ninguna entrada de lenguaje C (que sí de C++) aquí os dejo una con el parcial de Programación de Sistemas y Concurrencia de la UMA de este curso.

Os dejo el enunciado y mi solución que, como tantas veces he dicho, es una de las posibles pues siempre hay varias.

Una lista doblemente enlazada es una lista enlazada en la que cada nodo tiene dos punteros, uno que apunta al siguiente nodo de la lista y otro que apunta al nodo anterior. Además, las listas doblemente enlazadas tienen una referencia a la cabeza de la lista y a la cola (ver figura).


a) Implementar en el archivo Lista.h el tipo T_Lista que representa una lista doblemente enlazada, donde cada nodo almacena el valor de un número entero. 
NOTA: T_Lista debe tener referencias a la cabeza y a la cola de la lista.

b) Implementar en el archivo Lista.c las operaciones especificadas en el archivo Lista.h: 

void crear(T_Lista *lista) 
Crea una lista doblemente enlazada vacía. 

void destruir(T_Lista *lista) 
Destruye completamente la estructura de la lista. 

void insertar(T_Lista *lista, int valor) 
Inserta un nodo con el valor especificado como parámetro de forma ordenada 
ascendente, para ello se deberá recorrer la lista desde la cabeza y desde la cola con 
punteros auxiliares, insertándolo en el sitio correcto desde el primer puntero que lo 
encuentre. 

int eliminar(T_Lista *lista, int valor) 
Elimina un nodo con el valor especificado como parámetro, para ello se deberá recorrer 
la lista desde la cabeza y desde la cola con punteros auxiliares, eliminándolo desde el 
primer puntero que lo encuentre. En caso de que exista más de un nodo con ese valor se 
eliminará el primero encontrado, si se encuentran a la vez por la cabeza o por la cola se 
eliminará solamente uno de los dos. Si no existe en la lista el valor no se eliminará nada. 
El valor devuelto por la función indica si se ha eliminado o no el valor. 

void imprimir(T_Lista lista, int direccion) 
Escribe por pantalla la lista ordenada de forma ascendente si direccion == 0 y de 
forma descendente en caso contrario. 

Lista.h
#ifndef LISTA_H_ 
#define LISTA_H_

typedef struct Lista T_Lista;

struct Lista {
 struct TNodo * cabeza;
 struct TNodo * cola;
};

struct TNodo{
 int valor;
 struct TNodo* sig;
 struct TNodo* ant;
};

void crear(T_Lista *lista);//Crea una lista doblemente enlazada vacía.
void destruir(T_Lista *lista); //Destruye completamente la estructura de la lista.
void insertar(T_Lista *lista, int valor);
int eliminar(T_Lista *lista, int valor);
void imprimir(T_Lista lista, int direccion);

#endif /* LISTA_H_ */


Lista.c
#include <stdio.h>
#include <stdlib.h>
#include "Lista.h"



void crear(T_Lista *lista){
 //Crea una lista doblemente enlazada vacía.
 (*lista).cabeza = NULL;
 (*lista).cola = NULL;
}
void destruir(T_Lista *lista){
 //Destruye completamente la estructura de la lista.
 struct TNodo* auxiliar;
 while((*lista).cabeza != NULL){
  auxiliar = (*lista).cabeza ->sig;
  free((*lista).cabeza);
  (*lista).cabeza = auxiliar;
 }
 (*lista).cola = NULL;
}
void insertar(T_Lista *lista, int valor){
 int insertado = 0;
 struct TNodo* auxcab = (*lista).cabeza;
 struct TNodo* auxcola = (*lista).cola;
 struct TNodo * nuevo = malloc(sizeof(struct TNodo));
 nuevo ->valor = valor;

 if((*lista).cabeza == NULL && (*lista).cola == NULL){
  nuevo -> sig = NULL;
  nuevo -> ant = NULL;
  (*lista).cabeza  = nuevo;
  (*lista).cola = nuevo;
  insertado = 1;
 }

 while(!insertado){
  if(auxcab != NULL){
   if(auxcab ->valor < valor){
    auxcab = auxcab -> sig;
   }else{ // lo tengo que insertar
    if(auxcab ->ant == NULL){ // es el primero
     nuevo -> sig = auxcab;
     nuevo -> ant = NULL;
     auxcab -> ant = nuevo;
     (*lista).cabeza = nuevo;
    }else{
     nuevo -> sig = auxcab;
     nuevo -> ant = auxcab->ant;
     auxcab->ant -> sig =nuevo;
     auxcab->ant = nuevo;
    }
    insertado = 1;
   }
  }
  if(auxcola != NULL && !insertado){
   if(auxcola ->valor > valor){
    auxcola = auxcola -> ant;
   }else{ // lo tengo que insertar
    if(auxcola-> sig == NULL){ // es el ultimo
     auxcola -> sig = nuevo;
     nuevo -> sig = NULL;
     nuevo -> ant = auxcola;
     (*lista).cola = nuevo;
    }else{

     nuevo -> sig = auxcola -> sig;
     auxcola -> sig -> ant = nuevo;
     nuevo -> ant = auxcola;
     auxcola -> sig = nuevo;
    }
    insertado = 1;
   }
  }


 }

}
int eliminar(T_Lista *lista, int valor){
 struct TNodo* auxcab = (*lista).cabeza;
 struct TNodo* auxcola = (*lista).cola;
 struct TNodo * aux;

 while(auxcab != NULL || auxcola != NULL){

   if(auxcab != NULL && auxcab -> valor == valor){ // lo tengo que eliminar
    if(auxcab -> ant == NULL){ // es el primero
     aux = (*lista).cabeza -> sig;
     free((*lista).cabeza);
     (*lista).cabeza = aux;
     (*lista).cabeza -> ant = NULL;
     if(aux == NULL){
      (*lista).cola = NULL;
     }
    }else{
     auxcab -> ant -> sig = auxcab -> sig;
     auxcab -> sig -> ant = auxcab -> ant;
     free(auxcab);
    }
    return 1;

   }else if(auxcola != NULL && auxcola -> valor == valor){
    if(auxcola -> sig == NULL){ // es el ultimo
     aux = (*lista).cola -> ant;
     free((*lista).cola);
     (*lista).cola = aux;
     (*lista).cola -> sig = NULL;
    }else{
     auxcola -> ant -> sig = auxcola -> sig;
     auxcola -> sig -> ant = auxcola -> ant;
     free(auxcola);
    }
    return 1;
   }else{
    if(auxcola != NULL) auxcola = auxcola -> ant;
    if(auxcab != NULL)auxcab = auxcab -> sig;
   }
 }
 return 0;
}
void imprimir(T_Lista lista, int direccion){
 /*Escribe por pantalla la lista ordenada de forma ascendente si direccion == 0
   y de forma descendente en caso contrario. */
 struct TNodo * aux;
 if(direccion == 0){
  aux = lista.cabeza;
  while(aux != NULL){
   printf("%d, ", aux ->valor);
   aux = aux -> sig;
  }
  printf("\n");
 }else{
  aux = lista.cola;
  while(aux != NULL){
   printf("%d, ", aux ->valor);
   aux = aux ->ant;
  }
  printf("\n");
 }
}
int main(void) {
 T_Lista lista;
  crear(&lista);

  insertar(&lista, 1);

  insertar(&lista, 2);


  insertar(&lista, -1);


  insertar(&lista, 0);

  imprimir(lista, 0);
 imprimir(lista, 1);
  eliminar(&lista, 2);
  imprimir(lista, 0);
  imprimir(lista, 1);

  destruir(&lista);
  imprimir(lista, 0);
  imprimir(lista, 1);
 return EXIT_SUCCESS;
}

Pues ahí lo tenéis, podeis probar en el main con los diferentes métodos creados ;)

viernes, 20 de junio de 2014

Crear un sistema de videovigilancia con Raspberry, Webcam y Dropbox (Parte I)

Buenas gente. Hoy os traigo algo diferente, un pequeño sistema de videovigilancia que (en caso de notar movimiento) subirá fotos a tu cuenta de dropbox señalando la fecha y hora así como el lugar (dentro de la foto) donde se ha realizado el cambio.

Aquí la segunda parte del proyecto.

Para ésto vamos a usar una Raspberry Pi, una webCam (aquí os dejo un enlace con una lista de cámaras que, seguro, funcionarán).

Lo primero de todo que debéis hacer es actualizar vuestra Raspberry. Para ello:
sudo apt-get update
sudo apt-get upgrade
Lo siguiente a hacer será instalar motion. Motion será el software encargado de usar la webcam y procesar las imágenes.
sudo apt-get install motion
Ahora lo que tenemos que hacer es configurar motion. para ello hacemos lo siguiente:
sudo nano /etc/motion/motion.conf
Vamos a cambiar los siguientes valores:

#Para que se ejecute en segundo plano
Daemon = OFF ==> Daemon ON
#Lo que capturará antes y después de cada toque.
pre_capture 2
post_capture 4
#Muestra en la imagen un rectángulo con el cambio.
locate on
#Para poder acceder desde fuera:
webcam_port 9091
webcam_localhost off
#Para poder acceder al control desde fuera:
control_port 9090
control_localhost off
#Contraseña para control: Elegid la que queráis
control_authentication user:pass
Ahora nos vamos a
sudo nano /etc/default/motion
Y cambiamos el 'no' a 'yes'. Esto hará que se arranque al inicio.

Hasta aquí ya hemos instalado y configurado correctamente la cam. Podeis probarlo poniendo en la consola:
sudo service motion start
Si ahora pasárais por delante, podríais ver como aparece una carpeta llamada 'motion' en /tmp/ con imágenes. Para parar usad el comando de antes con stop. Las imágenes que guardais (al estar en la carpeta /tmp) se borrarán al apagar la raspberry, por lo que tendríamos que guardarlas. De eso nos encargaremos en la segunda mitad del tutorial, la conexión con Dropbox. Saludos y gracias ;)

viernes, 13 de junio de 2014

PROYECTO Creación de un Chat. Parte III: Petición de datos desde PHP a MySQL

Buenas gente, aquí sigo sin vida social :p. Os traigo ya los tres códigos en PHP que vamos a usar recién sacados del horno... Vereis que son similares. Todos consisten en recoger ( o no ) una variable mediante GET, enviar una instrucción SQL a la BD y mostrar por pantalla el resultado (Si lo hubiera).

Antes de nada aquí tenéis los enlaces a las otras partes:
Parte 1: Creación de la base de datos gratuita MySQL.
Parte 2: Actualización de datos en MySQL desde PHP

Os voy a recordar el código que usamos para actualizar con un nuevo nombre, el archivo userReg.php:

  <?php 
  $nick    = $_GET['nick'];
  $ip     = $_SERVER['REMOTE_ADDR'];
  $server  = 'mysql.hostinger.es';
  $usuario = 'usuario';
  $pass  = 'password';
  $BD  = 'baseDeDatos';
  
  $link = mysql_connect($server, $usuario, $pass);
  mysql_select_db($BD, $link);
  
  $inser = "INSERT INTO Usuario (Nick, IP) VALUES ('".$nick."', '".$ip."') ON DUPLICATE KEY UPDATE IP= '".$ip."'";
 
  mysql_query($inser, $link);
  mysql_close($link); // Cerramos la conexion con la base de datos
  
 ?>
Ahora vamos con uno que nos liste todos los nombres de usuario que hay (He hecho que los liste todos seguidos separados por un '&' porque era una forma sencilla de luego poder enviarselos a Java. userList.php.
 <?php 
 $server  = 'mysql.hostinger.es';
 $usuario = 'usuario';
 $pass  = 'password';
 $BD  = 'baseDeDatos';
 $link = mysql_connect($server, $usuario, $pass);
 mysql_select_db($BD, $link);
  
 $lalala = "Select Nick from Usuario";
  
 $result = mysql_query($lalala, $link);
 while($row = mysql_fetch_array($result)) {
  echo $row['Nick']."&";
 }
 mysql_close($link); // Cerramos la conexion con la base de datos 
?>
Como veis son muy similares. Solo cambia que le enviamos la instrucción y luego mostramos por pantalla lo que nos devuelve. Ya por último teneis el que va a recibir un nombre de usuario y va a devolver su IP. userBusc.php.
 <?php 
  $nick    = $_GET['nick'];
  $server  = 'mysql.hostinger.es';
  $usuario = 'usuario';
  $pass  = 'password';
  $BD  = 'baseDeDatos';
  $link = mysql_connect($server, $usuario, $pass);
  mysql_select_db($BD, $link);
  
  $lalala = "Select IP from Usuario Where Nick like '".$nick."'";
  
  $result = mysql_query($lalala, $link);
  
  $row = mysql_fetch_array($result);
  echo $row["IP"]; 
  mysql_close($link); // Cerramos la conexion con la base de datos 
  
  
 ?>
Ya podeis daros cuenta de que siempre son los mismos. Es una forma un poco fea de conectarse a una base de datos (Podríamos hacerlo orientado a objetos, con métodos y todo) pero es muy intuitiva y te puede sacar fácilmente de un apuro.

El próximo día os ensñaré a integrar esto con Java, así que estad atentos ;)

Saludos ;)

miércoles, 11 de junio de 2014

Ejercicios de ensamblador MIPS: funciones (sumatorio, factorial), media aritmética ...

Vuelvo con ejercicios de ensamblador MIPS.
En estos ejercicios ya vemos el uso de funciones, y por tanto de las instrucciones jal y jr. Recuerdo que es recomendable, aunque más bien necesario, echar un vistazo a las instrucciones para conocerlas bien y si no tenéis ni idea de ensamblador MIPS recomiendo visitar la anterior entrada en la que os dejé unos ejemplos muy sencillos para entender las instrucciones más usuales.

NOTA: Aunque en algunos ejercicios piden una función, para poder probarla añado un "main" y un segmento de datos de prueba. Pero recordad que la función debe funcionar independientemente de esos datos.

Función factorial en ensamblador MIPS.
# función factorial (argumento en $a1, valor devuelto en $v1)
# número en 'ene'
# valor factorial en 'resul'


.data
ene:      .word  5
             .space 28
resul:    .word  0        

      
.text
main:    lw $a1,ene($0)
         jal  fact          
         sw $v1,resul($0)               
         li $v0,10
         syscall
          
fact:
       addi $v1, $0, 1
       add $8, $a1, $0
      
loop:
  beq $8, $0, fin  
 mul $v1, $v1,$8
        subi $8, $8, 1
        j loop
     
fin:  jr $31


Realizad una función llamada ‘sumatorio” que sume las componentes de un vector de números enteros almacenados en memoria. Los parámetros de entrada a la función son, la dirección de comienzo del vector en $a0 y el número de componentes en $a1. La función devuelve el valor de la suma en $v0.
.data
suma: .word 0
.space 24
tamano: .word 10
datos1: .word 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
 .word 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32

.text
main:  la $a0, datos1
 lw $a1, tamano($0)
 jal sumatorio
 sw $v0, suma($0)
 li $2, 10
 syscall 

sumatorio:  add $10, $0, $0 # inicializo 10
  add $v0, $0, $0 # inicializo 8
  
etq1:  lw $9, 0($a0)
  add $v0,$v0,$9 
  addi $a0,$a0,4 
  addi $10,$10, 1 
  bne $10,$a1,etq1 
  jr $ra


A partir de la posición de memoria etiquetada como ‘A’ hay almacenada una secuencia de ‘n’ números. El valor ‘n’ se encuentra almacenado en la posición previa a ‘A’. Realizad un programa que lea dicha secuencia y la transforme en otra que se almacene a partir de la posición etiquetada como ‘B’ y obedezca la siguiente expresión de transformación:
B(0)=A(0)
B(i)= (A(i)+A(i-1))/2                i=1,2,...,n-1
Utilizad la cabecera de programa que se indica a continuación.

.data
       .space 28
       .word 5
A:     .word 2,4,5,7,4,9,6,8,9,4,5,6,7,8,9,3,4,5,9,7,2,3,4,777
B:     .word 0
      


.text  
 addi $10, $0, -4
 lw $10, A($10) # en el registro 10 tengo n
 
 beq $10, $0, fin
 
 slt $15, $10, $0
 
 bne $15, $0, fin 
 
 add $9, $0, $0
 
 
 lw $8,A($9) # En el registro 8, A
 lw $11, B($9) # En el registro 11, B
 
 sw $8, B($0) # B(0) = A(0)
 
 addi $14, $10, -1
 
loop: addi $9, $9, 4 # incrementar puntero
 
 addi $10, $9, -4
 
 lw $12, A($10)
 lw $8,A($9) 
 
 add $13, $8, $12
 
 div $13, $13, 2
 
 sw $13, B($9) 
 
 addi $14, $14, -1
 
 
 bne $14, $0, loop
 
fin: li $2, 10
 syscall
   


A partir de la posición de memoria etiquetada como ‘datos’ hay almacenada una secuencia de ‘n’ números. El valor ‘n’ se encuentra almacenado en la posición previa a ‘datos’. Realizad un programa que calcule la media aritmética de dicha secuencia y la almacene en la posición etiquetada como ‘med’. Utilizad la cabecera de programa que se indica a continuación.
.data 
 .space 28 
 .word 5
datos: .word 2,4,5,7,4,95,6,8,9,4,555,6,7,8,9,3,4,5,9,7,2,3,4,777 
med: .word 0 


.text 

addi $9, $10, -4
lw $9, datos($9) # tengo n en $9

slti $13, $9, 1


add $10, $0, $0
add $11, $0, $0
add $12, $0, $0

bne $0, $13, fin

suma:
lw $8, datos($12)
add $11, $11, $8
addi $10, $10, 1
addi $12, $12, 4
bne $10, $9, suma
div $11, $11, $9
sw $11, med($0)
fin:
li $2, 10
syscall

domingo, 8 de junio de 2014

Práctica Planificación Sistemas Inteligentes

Pues, por una petición en particular, os dejo la práctica de Planificación de la asignatura Sistemas Inteligentes de 2º de Ingeniería Informática de la UMA.
El problema del mono y los plátanos (the monkey-and-bananas problem) es aquel al que se enfrenta un mono (monkey) en un laboratorio donde hay varios plátanos colgando del techo, fuera de su alcance.
Hay una caja (box) disponible que permitirá el mono alcanzar los plátanos si se sube en ella.
Al principio el mono está en A, los plátanos en B, y la caja en C.
El mono y la caja tienen altura (height) baja (low), pero si el mono se sube a la caja tendrá altura elevada (high), la misma que tienen los plátanos.
Las acciones disponibles para el mono son:
1.  ir (go) de un lugar a otro,
2.  empujar (push) un objeto de un lugar a otro,
3.  subirse (climbUp) o
4.  bajarse (climbDown) de un objeto, y
5.  agarrar (grasp) o
6.  soltar (unGrasp) un objeto.

El resultado de grasp es que el mono obtiene el objeto si el mono y el
objeto están en el mismo lugar y a la misma altura.

Se pide hacer lo siguiente:
a) Escribir la descripción del estado inicial y final.
b) Escribir los seis esquemas de acción.
c) Crea los ficheros mb_domain.txt y mb_problem.txt para el dominio y el problema, respectivamente. A continuación suminístraselos a JavaGP (una implementación del algoritmo GRAPHPLAN) y comprueba que se encuentra un plan correcto. Guarda la salida del programa en el archivo result_trace.txt
d) OPCIONAL: Supón que el mono quiere engañar a los científicos, que han ido a tomarse un café, cogiendo los plátanos, pero dejando la caja en su lugar original. Escribe esto como un objetivo genérico (es decir, sin suponer que la caja está necesariamente en C) en el lenguaje del cálculo de situaciones. ¿Puede resolver este objetivo un sistema de planificación clásico?
Si la respuesta es no, cambia el objetivo general a un objetivo más específico que
pueda ser resuelto por JavaGP.
Crea el archivo mb_problem2.txt con el problema. Ejecute el problema en JavaGP
con mb_domain.txt y mb_problem2.txt. Almacena la salida en result_trace2.txt.

mb_domain.txt
operator go(M,O,F)
pre: monkey(M),place(F), place(O), en(M,O), bajo(M) 
post: ~en(M,O), en(M, F)

operator push(M,O,F,C) 
pre: monkey(M), box(C), place(O), place(F), en(M,O), en(C,O), bajo(C), bajo(M)
post: ~en(M,O), ~en(C,O),en(C,F), en(M,F) 

operator climbUp(M, C, P) 
pre: monkey(M), box(C), place(P), en(M,P), en(C,P), bajo(C), bajo(M) 
post: ~bajo(M) 


operator grasp(M, B, P) 
pre: monkey(M), bananas(B), place(P), en(M,P), en(B,P), ~bajo(B), ~bajo(M) 
post: agarra(M, B), ~en(B, P)

operator unGrasp(M, B,P) 
pre: monkey(M), bananas(B), place(P), en(M,P), en(B,P), agarra(M,B) 
post: ~agarra(M, B), en(B, P)


operator climbDown(M,C,P) 
pre: monkey(M), box(C), place(P), en(M,P), en(C,P), bajo(C), ~bajo(M) 
post: bajo(M)


mb_problem.txt
start(
monkey(m), 
bananas(p), 
box(caja), 
place(a),
place(b),
place(c),
~agarra(m,p),
bajo(m), 
bajo(caja),
~bajo(p),
en(m,a),
en(p, b),
en(caja, c))

goal(
agarra(m,p)
)

mb_problem2.txt
start(
monkey(m),
bananas(p), 
box(caja),
place(a),
place(b),
place(c),
en(m,a),
~bajo(p),
~agarra(m,p),
bajo(m), 
bajo(caja),
en(p, b), 
en(caja, c)) 

goal(
agarra(m,p),
en(caja,c),
en(m, a)
)
 

Para ejecutar se debe abrir la consola de comandos y, estando en el directorio en el que se encuentran tanto javagp.jar como los archivos txt, poner: java -jar javagp.jar –d mb_domain.txt  -p mb_problem.txt

Así se obtiene la salida que nos piden en los ficheros result_trace.txt, pero eso ya os lo dejo a vosotros ^^

viernes, 6 de junio de 2014

Ejercicio Concurrencia con Semáforos en Java.

Os dejo un ejercicio de concurrencia en Java usando semáforos.


En una cadena de montaje existe un robot encargado de colocar productos de 3 tipos diferentes (1, 2 o 3) en la cadena de montaje. Otros robots, retiran los productos de la cadena de montaje para realizar su empaquetado, teniendo en cuenta que están especializados en un solo tipo de producto (1, 2 o 3), ignorando los que no son de su tipo. Finalmente, se quiere llevar un control del total de productos empaquetados (independientemente de su tipo).

Modelar utilizando semáforos el sistema descrito con las siguientes indicaciones:

  • Modelar cada robot como una hebra (1 colocador y 3 empaquetadores, uno para cada tipo de producto).
  • Los productos son colocados de uno en uno en la cadena, y solamente en posiciones libres (se puede considerar que en la cadena de montaje caben un máximo N de elementos). Si no hay posiciones libres el robot colocador tendrá que esperar hasta que algún producto sea retirado de la cadena.
  • Los robots empaquetadores se especializan en un tipo de producto (1, 2 o 3) en tiempo de inicialización.
  •  Los robots empaquetadores comprueban si hay algún elemento de su tipo en la cadena ignorando los productos que no sean de su tipo. Si hay algún producto de su tipo lo retiran de la cadena (sólo 1 producto cada vez) y la posición queda libre para colocar nuevos productos, en caso contrario se quedan a la espera de que haya nuevos productos.
  • Los robots empaquetadores de distinto tipo pueden funcionar a la vez.
  • Tanto el colocador como los empaquetadores nunca acaban.
  • Cada vez que un robot empaquetador procesa un producto, la cuenta total de productos empaquetados debe aumentar y mostrarse un mensaje por pantalla.

public class Robots {
 static int tam = 10;
 static int [] cadena = new int [tam];
 
 static Semaphore coloco=new Semaphore(tam, true);;
 static Semaphore [] puedoemp =  {new Semaphore(0), new Semaphore(0), new Semaphore(0)};
 static int nemp = 0; // necesita un mutex
 static Semaphore mutex = new Semaphore(1, true);
 static Random r = new Random();
 
 public static class Colocador extends Thread{
  public void run(){
   int i, n;
   while(true){
    try {
     n = r.nextInt(3)+1;
     System.out.println("Produciendo tipo "+ n);
     sleep(r.nextInt(1000));
     coloco.acquire();
     i=0;
     while(cadena[i] != 0) i++;
     //cuando encuentra un sitio vacío, sale del bucle y puede colocar
     cadena[i]  = n;
     System.out.println("Coloco un producto "+ n + " en la posición "+ i +"\n"+ Arrays.toString(cadena));
     puedoemp[n-1].release();

    } catch (InterruptedException e) {e.printStackTrace(); }
   }
  }
 }
 
 public static class Empaquetador extends Thread {
  private int tipo;
 
  public Empaquetador (int tipo){
   this.tipo = tipo;
 
  }
  public void run(){
   int i;
   while(true){
   try {
    puedoemp[tipo-1].acquire();
    
     i =0;
    while(cadena[i] != tipo) i++;
    sleep(r.nextInt(1000));
    System.out.println("Robot de tipo "+ tipo + " empaquetando el producto de la posición "+ i);
    cadena[i] = 0; //dejo el hueco
    coloco.release();
    sleep(2000);
    mutex.acquire();  //aumentar nemp
    nemp++;
    
    System.out.println("Aumenta el número de empaquetados: "+ nemp);
    
    mutex.release(); // 
    
    
   } catch (InterruptedException e) {e.printStackTrace();}
   }
  }
 }
 public static void main(String[] args){
  System.out.println("El tamaño de la cadena será de " + tam);
  Empaquetador  empaquetador1 = new Empaquetador(1);
  Empaquetador  empaquetador2 = new Empaquetador(2);
  Empaquetador  empaquetador3 = new Empaquetador(3);
  Colocador colocador = new Colocador();
  colocador.start();
  empaquetador1.start();
  empaquetador2.start();
  empaquetador3.start();
  
 }
}

.

miércoles, 4 de junio de 2014

PROYECTO Creación de un Chat. Parte II: Actualización de Datos desde PHP

Antes de nada os mando a la primera parte por si os la habeis perdido. En ella muestro como crear el Host Gratuito y la Base de Datos:

Parte 1: Creación de Base de Datos MySQL
Parte 3: Petición de datos desde PHP a MySQL

Bueno, pues hoy tocaremos algo de PHP así por encima. Tampoco es nada demasiado complejo e intentaré explicarlo bien. Si nunca habeis programado en PHP yo os recomendaría el notepad++ que es gratuito.

Antes de nada os explicaré un pequeño detalle que me ha tenido comiendome el coco un buen rato. En PHP las Strings se pueden definir con comillas dobles ("hola") o comillas simples('hola').
La diferencia es (si me equivoco avisadme ;) ) que lo que pongas en las comillas simples va tal cual, mientras que con las comillas dobles puedes meter etiquetas como <br> (un salto de línea en  html ) o \n (un salto de linea de String). Además muchas veces uso las dobles cuando necesito usar una string que tenga comillas simples dentro (para sentencias SQL).

Ahora sí, he aquí mi código:

<html>
 <?php 
 
// Creo las variables y las defino. No es necesario poner el tipo
$Nick    = 'yo';
  $ip     = $_SERVER['REMOTE_ADDR'];
  $server  = 'mysql.hostinger.es';
  $usuario = 'nombreDeUsuarioDeMiBD';
  $pass  = 'password';
  $BD  = 'NombreDeLaBD';
  
//Conecto con la BD
$link = mysql_connect($server, $usuario, $pass);
  mysql_select_db($BD, $link);

  //Creo las sentencias que vaya a utilizar
  $inser = "Insert Into Usuario Values ('".$nick."', '".$ip."')";
  $inser2 = "INSERT INTO Usuario (Nick, IP) VALUES ('".$nick."', '".$ip."') ON DUPLICATE KEY UPDATE IP= '".$ip."'";

  //Envio la sentencia
mysql_query($inser2, $link); 
  mysql_close($link); // Cerramos la conexion con la base de datos 
  
  
 ?>
</html>
NOTAS:
-Las variables llevan siempre el símbolo $ delante.
-Para concatenar Strings en PHP se usa el punto.
-Podeis ver como en $inser y $inser2 uso las comillas dobles porque los valores en SQL deben ir entre comilas simples.
-Como veis hay dos sentencias. La primera ($inser) es la que usaba, pero si un Nick ya existía no me lo reemplazaba, por lo que busqué como solución la segunda.
-La IP se calcula directamente y mete la que estamos usando en ese momento.

Este archivo deberéis guardarlo con nombre userReg.php (en realidad el nombre es irrelevante, pero es el que yo uso y así os evitais comederos de cabeza innecesarios :p).

Os acordais en la entrada anterior que marqué un cuadro llamado 'Administrador de Archivos'? Ahí subiremos el archivo que acabamos de crear. Para ejecutarlo nos vamos a nuestro navegador y escribimos la dirección de nuestro host (la que pusimos al crear el nuevo Hosting gratuito). Supongamos que era "pruebasdephp.hol.es". En este caso iriamos al navegador y pondríamos "pruebasdephp.hol.es/userReg.php".


Esto ejecutaría nuestro php (no os dije que la única forma de ejecutar un programa php era subiendolo a un servidor? :P) y en principio nos aparecería la página en blanco (pues no le hemos dicho que nos imprima nada) pero en nuestra base de datos si nos metemos debería estar el resultado.

En mi caso tengo más entradas, pero como tienen IP verdaderas no las voy a mostrar :P

Ahora bien, os voy a enseñar a pasar datos desde el exterior a PHP. Para ello cambiaremos la definición de Nick a la siguiente:
$Nick    = $_GET['nick'];

Resubimos el nuevo archivo modificado al servidor y ahora entraremos a la url de la siguiente forma:
"pruebasdephp.hol.es/userReg.php?nick=holaa" (sin las comillas).

Os explico, al decirle a $Nick que es $_GET['nick] le estamos diciendo que su resultado lo tomará de la url. Luego en la url ponemos una interrogación (esto le dice que van a aparecer las variables) y ponemos nick=holaa, ergo el valor de nick será 'holaa' y te guardará un usuario holaa con tu IP.
Si por algún caso no saliera prueba a borrar de la tabla el que creamos antes. Puede que tengas un conflicto al ser las dos IPs las mismas (Aunque en principio no debería de dar problemas).

Al fin acabo. Esta entrada es muy espesa, tiene mucho contenido y (si no sabes nada de PHP) vas a tener que leértela  un par de veces. Cualquier duda comentadla, estaré encantado de resolverlas.

Saludos y Gracias ;)

domingo, 1 de junio de 2014

PROYECTO Creación de un Chat. Parte I: Creación de Base de Datos mySQL

Buenas gente. En estos momentos estoy liado con un 'logro personal'. Aún no está acabado pero ya tengo material suficiente para empezar a exponerlo.

Parte 2: Actualización de Datos desde PHP
Parte 3: Petición de datos desde PHP a MySQL

En principio iba a ser un chat normal, pero tras ver lo lioso que era el tener que estar metiendo la IP en el programa decidí hacer una pequeña base de datos que guardara un nombre de usuario y su IP. Hoy os voy a contar como hacer la base de datos. Yo utilizo Hostinger.
Te registras y creas un nuevo hosting gratuito (Tardan un par de diitas en que puedas entrar desde la web, no os preocupeis ;) ).

Una vez que está creado nos vamos a 'Administrar'. Hay dos puntos aquí que nos interesan:

El primero de ellos es donde subiremos nuestros archivos PHP, mientras que el segundo es donde crearemos la Base de Datos (A mi me sale un 2 porque tengo dos creadas).

No voy a entrar en detalle en toda la teoría acerca de las bases de datos, pero si tenéis alguna pregunta hacedmela saber ;)

Cuando le damos a MySQL nos saldrán unos campos a rellenar para crear la BD. Tras un ratillo esperando, nos sale abajo. Clickeamos en ella y le damos a PHPmyAdmin (el cuadro de más a la derecha).

Ahora tendremos una nueva pestaña con el gestor de la base de datos. Creamos una tabla la cual llamaremos Usuario. Solo vamos a usar dos columnas (Aunque seguramente en un futuro tendré que meter una 3ª para elegir el puerto de entrada). Una columna se llamará Nick y la otra IP (las mayúsculas importan). En mi caso Nick es un varchar de 10 mientras que IP lo será de 15. En esta imagen vereis como ponerlo exactamente:

Le damos aguardar y ya está hecha. Nos saldrá a la izquierda. Clickeamos en ella y arriba a la derecha tendremos una pestaña llamada Insertar. Ahí podremos meter algunos datos para probar.

Y hasta aquí el post de hoy. Os recomiendo que busqueis información acerca de las sentencias SQL 'Select', 'Delete' e 'Insert' pues son las que más usaremos para comunicarnos con la BD.

En la segunda parte (el miércoles 4 de Junio) hablaré de como editar una BD con PHP.
Saludos y hasta la próxima ;)