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