|

PFG – Semáforo con nombre

Introducción

El programa main.c realiza una espera del tiempo indicado en el fichero vars/REFRESH_RATE usando la instrucción sleep(). El uso de esta instrucción es adecuada al no suponer una espera activa del proceso, pero no nos permite interrumpir la espera si, por ejemplo, desde la aplicación Android solicitamos una actualización inmediata de los valores de los sensores.

Para ellos podemos usar la instrucción de POSIX timedwait (implementada en ArchLinux) la cual funciona como un semáforo combinado con una instrucción sleep. Espera el tiempo indicado antes de continuar o hasta que el semáforo que tiene como parámetro se abra. Como queremos que el semáforo se pueda abrir desde la aplicación Android (mediante un script CGI escrito en C) tenemos que usar un semáforo con nombre, que se ubique en la memoria compartida del sistema operativo /dev/shm. Esta instrucción se encuentra en la librería <semaphore.h> que se debe importar.

 

Programas de prueba

Para probar el funcionamiento de la instrucción timedwait, se crea un programa de prueba que acceda al semáforo, asigne un tiempo de espera de 10 segundos y espere con la instrucción sem_wait(). Por otro lado se crea un script CGI que acceda al semáforo y lo abra con sem_post().

 

test.c

Se hace necesario, además, ejecutar la instrucción chmod sobre el semáforo con nombre ya que los permisos no se asignan correctamente al crear el semáforo.

#include <semaphore.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fcntl.h> // O_CREAT

sem_t * sem;

int main() {

    char sem_name[] = "miSemaforo";
    char shm_path[] = "/dev/shm/sem.";
    char sem_path[20];
    strcpy(sem_path, shm_path);
    strcat(sem_path, sem_name);

    sem = sem_open(sem_name, O_CREAT, 0666, 0);

    chmod(sem_path, 0666);

    static struct timespec time_to_wait = {0, 0};

    for (;;) {

        time_to_wait.tv_sec = time(NULL) + 10;

        printf("Espero...\n");
        sem_timedwait(sem, &time_to_wait);
        printf("He salido\n");  
    }

    sem_close(sem);
    sem_unlink(sem_name);

}

Se compila con el comando:

> gcc -o test test.c -lpthread

 

open_semaphore.cgi

/* Este script cgi abre un semáforo con nombre */

#include <semaphore.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>

#include <fcntl.h> // O_CREAT

sem_t * sem;

int main() {

    char myName[] = "miSemaforo";

    int valor;

    printf("Content-Type: text/html\n\n");
    
    sem = sem_open(myName, O_CREAT, 0666, 0);

    sem_getvalue(sem, &valor);
    printf("Voy a abrir...%d\n", valor);
    sem_post(sem);
    sem_getvalue(sem, &valor);
    printf("Abierto. %d\n", valor);

    sem_close(sem);

}

 

Se compila con el comando:

> gcc -o open_semaphore.cgi open_semaphore.c -lpthread

 

Comprobamos que funciona correctamente. El programa continua ejecutándose a los 10 segundos o al ejecutar el script.

semaphore

semaphore_2

 

Adaptación al programa main.c

Ahora se añade el semáforo al programa principal de la Raspberry Pi. En cada ejecución del bucle principal leemos el valor de la tasa de refresco (por si el usuario lo ha modificado desde la aplicación Android) y actualizamos el semáforo con nombre para que espere el valor indicado.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h> // O_CREAT
#include <semaphore.h>
#include "functions.h"

#define ARDUINO 0x04
#define TI 0x08

#define TAG_REFRESH_RATE "REFRESH_RATE"
#define TAG_FORECAST "FORECAST"
#define TAG_TIMESTAMP "TIMESTAMP"
#define TAG_TEMP "TEMP"
#define TAG_RAIN "RAIN"
#define TAG_HUM "HUMIDITY"

int main() {
    char * buf;

    int refresh_rate;

    char * timestamp;

    uint nBytesTAG_TEMP = sizeof(TAG_TEMP)-1; // no enviamos el caracter '\0'
    uint nBytesTAG_RAIN = sizeof(TAG_RAIN)-1;
    uint nBytesTAG_HUM = sizeof(TAG_HUM)-1;
    uint nBytesTEMP = 4;
    uint nBytesRAIN = 1;
    uint nBytesHUM = 3;

    float temperature;
    uint rain;
    uint humidity;
    int forecast;

    char temperature_char[] = {0};
    char rain_char[] = {0};
    char humidity_char[] = {0};
    char forecast_char[] = {0};

    int pid, status;

    sem_t * sem;

    char sem_name[] = "miSemaforo";
    char shm_path[] = "/dev/shm/sem.";
    char sem_path[20];
    strcpy(sem_path, shm_path);
    strcat(sem_path, sem_name);

    sem = sem_open(sem_name, O_CREAT, 0666, 0);

    chmod(sem_path, 0666);

    static struct timespec time_to_wait = {0, 0};

    // Creamos el proceso hijo y le indicamos que ejecute el script para obtener la predicción meteorológica
    pid = fork();

    if (pid == 0) {
        execlp("python", "python", "weather_forecast.py", NULL);
        exit(0);
    }

    // Proceso padre
    else {

        for(;;) {

            // Leemos la tasa de refresco
            buf = readVar(TAG_REFRESH_RATE);
            refresh_rate = atoi((char *) &buf);
            printf("%s: %d\n", TAG_REFRESH_RATE, refresh_rate);

            // Ajustamos el valor del semáforo con nombre
            time_to_wait.tv_sec = time(NULL) + refresh_rate;
        
            // Leemos los valores se los sensores
            temperature = readSensor(TI, TAG_TEMP, nBytesTAG_TEMP, nBytesTEMP) / 100.00;
            usleep(50000);
            rain = readSensor(TI, TAG_RAIN, nBytesTAG_RAIN, nBytesRAIN);
            usleep(50000);
            humidity = readSensor(TI, TAG_HUM, nBytesTAG_HUM, nBytesHUM);

            // Obtenemos el timestamp
            timestamp = getTime();

            // Leemos el pronóstico del fichero
            buf = readVar(TAG_FORECAST);
            forecast = atoi((char *) &buf);

            // Convertimos los valores a char*
            sprintf(temperature_char, "%.2f", temperature);
            sprintf(rain_char, "%d", rain);
            sprintf(humidity_char, "%d", humidity);
            sprintf(forecast_char, "%d", forecast);

            // Creamos los char** que cotienen variables y vectores
            char * variables_array[] = {TAG_TIMESTAMP, TAG_TEMP, TAG_RAIN, TAG_HUM, TAG_FORECAST, NULL};
            char * values_array[] = {timestamp, temperature_char, rain_char, humidity_char, forecast_char, NULL};

            printf("%s: %s\n", TAG_TEMP, temperature_char);
            printf("%s: %s\n", TAG_RAIN, rain_char);
            printf("%s: %s\n", TAG_HUM, humidity_char);
            printf("%s: %s\n", TAG_FORECAST, forecast_char);
            printf("%s\n", timestamp);

            // Guardamos en el log los valores
            writeVar(variables_array, values_array);

            sem_timedwait(sem, &time_to_wait);
        }

        sem_close(sem);
        sem_unlink(sem_name);
    }

    return 0;
}

 

Referencias

[1] Instrucción sem_timedwait.
[2] Instrucción sem_getvalue.
[3][4] Instrucción sem_open.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.