Bogusław Kempny

Rejestrator Czasu Pracy

Autor adres
Początek HC-SR04 LCD Kamera fork() sms strfry() GPIO impulsy Klawiatura Brama GPIO PWM SG90 RFID Grafolog RCP ....















Zdarza się, że musimy kontrolować godziny pracy naszych pracowników wykonujących swoje obowiązki poza macierzystym zakładem. Albo firma zatrudnia niewielu pracowników, na przykład 20.

Klasyczny system Rejestracji Czasu Pracy w takim przypadku jest mało przydatny, stacjonarne czytniki, okablowanie, sterownik, serwer… Trochę za dużo. Wyobraźmy sobie instalację takiego systemu dla 30 osób pracujących na oddalonej budowie przez 20 dni, potem 30 dni na innej budowie, potem w innym trochę składzie znów w innym miejscu.

Rozwiązaniem takiego problemu może być system MiniRCP, zawierający wszystkie te elementy w jednej niewielkiej obudowie 14x10x6 cm.

Kabelek zasilający, przycisk przełączający pomiędzy rejestracją wejścia i wyjścia z pracy, czytnik kart zbliżeniowych RFID, kamera robiąca zdjęcie osobie rejestrującej kartę, sygnalizator odczytania karty, wyświetlacz.

Na wyświetlaczu zawsze dokładny czas, synchronizowany z serwerami czasu w sieci, aktualny stan rejestratora (rejestracja wejścia czy wyjścia), po zbliżeniu karty imię i nazwisko osoby, do której karta należy. Kamera zapisuje zdjęcia w formacie 640x480 pikseli, około 25 kB, dla oszczędności miejsca na dysku, można jednak ustawić inną rozdzielczość, aż do 5 Mpx. Dobrze sobie radzi nawet przy bardzo słabym oświetleniu.

Komunikacja z siecią internet poprzez port ethernet lub modem GSM. Dostęp do zarejestrowanych danych (wejścia/wyjścia, zdjęcia, raporty i zestawienia, administrowanie danymi pracowników itp.) z dowolnej przeglądarki, protokołem HTTPS. Dostęp możliwy wyłącznie w przypadku posiadania klucza kryptograficznego.

Co mamy w środku?

Najważniejszy element – RaspberryPI. Steruje wyświetlaczem czytnikiem kart zbliżeniowych, robi zdjęcia (na fotografii odpięta taśma do kamery), komunikuje się z internetem poprzez ethernet lub modem GSM, zapewnia dostęp do urządzenia poprzez serwer Apache.

Dane pracowników i odczyty rejestracji czasu pracy przechowywane w bazie danym MariaDB (MySQL).

I tu uwaga, RaspberryPI jako dysku używa karty MicroSD, nośniki tego typu niezbyt nadają się do częstego zapisywania danych, każdy zapis trochę je niszczy, dlatego pliki bazy danych umieszczone są na pendrive. Dla bezpieczeństwa codziennie robione są kopie danych na karcie SD na pendrive, a danych z pendrive na kartę SD.

Co jeszcze? Zasilacz 5V, malutki buzzer i to wszystko.

Jak to połączyć?

Zgodnie z tym schematem

Kamerę podpinamy po prosty do gniazda „CAMERA” Rapberry.

Jak to ożywić? Obsługę wyświetlacza, kamery, czytnika RFID opisałem już wcześniej, zobacz:

LCD

Kamera

RFID

Program do obsługi rejestratora, dostępny w tym miejscu skompilować musimy z bibliotekami MariaDB, WiringPI, a więc:

cc pomiar.c -o pomiar -L/usr/lib/mysql -lmariadbclient  -L/usr/local/lib -lwiringPi -lwiringPiDev

Ograniczymy się w tym miejscu do omówienia fragmentów, które nie były objaśnione przy okazji programików do obsługi wyświetlacza, kamery czy czytnika RFID.

Po pierwsze wyświetlanie.

Niby nic ciekawego, opisałem to już przy okazji wyświetlacza LCD

Jednak w tym wypadku nie jest to takie trywialne.

Po zidentyfikowaniu karty pracownika funkcją fork() uruchamiany jest skrypt /home/minircp/pstryk robiący zdjęcie, co może potrwać kilka sekund. W tym czasie główny proces działa dalej, pisze do bazy danych, wyświetla co sekundę aktualny czas.

Po zrobieniu zdjęcia nasz główny proces dostaje sygnał SIGCHLD, obsługiwany przez procedurę clean_up_child_process(int signal_num).

Gdybyśmy tego sygnału nie obsłużyli, każde uruchomienie skryptu pstryk() kończyłoby się pozostawieniem procesu potomnego w stanie „zombie”.

Ale procedura ta wyświetla jeszcze napis „OK” i uruchamia wątek lateok, który po sekundzie gasi napis „OK”. I tu może się pojawić problem, główny proces co sekundę wyświetla aktualny czas, Jeśli przerwanie SIGCHLD zostanie obsłużone w tym czasie, kiedy główny proces wyświetla czas, na ekranie wyświetlacza pojawią się śmieci.

Aby tego uniknąć, komunikaty nie są wyświetlane bezpośrednio na wyświetlaczu, ale pisane procedurą wyswbuf() do struktury bufwysw. Co sekundę główny proces (przy okazji wyświetlania aktualnego czasu) odczytuje ewentualne komunikaty z tej struktury i wyświetla je procedurą wyswietl().

Struktura bufwysw działa jako kolejka FIFO, o pojemności PIPESIZE.

To fragmenty kodu, które omówiliśmy:
#include 

…….

// tablica na fifo do wyswietlacza
struct {
  int x;
  int y;
  char tresc[30];
} bufwysw[PIPESIZE];

int wyswp;
int wyswk;

…….

PI_THREAD(lateok);

…….

void clean_up_child_process(int signal_num)
{
/* remove child process */
int status, k;
wait (&status);
wyswbuf(9,2,"OK");
piThreadCreate (lateok) ;
}
…….

void main (argc, argv)
int argc;
char *argv[];
{

…….

wyswp =0;
wyswk =0;

…….

/* handle SIGCHLD by calling clean_up_child_process */
struct sigaction sigchild_action;
memset(&sigchild_action, 0, sizeof(sigchild_action));
sigchild_action.sa_handler = &clean_up_child_process;
sigaction(SIGCHLD, &sigchild_action, NULL);

…….

  if (czas2!= czas1)
    {
      loctime = localtime (&czas1);
      strftime (buffer, SIZE, "%Y-%m-%d  %H:%M:%S", loctime);
      wyswietl(0,3,buffer);
      czas2=czas1;
      if(wyswp != wyswk)
       {
         wyswietl(bufwysw[wyswk].x, bufwysw[wyswk].y, bufwysw[wyswk].tresc);
         wyswk ++;
         if (wyswk == PIPESIZE) wyswk = 0;
       }
    }

…….

lcdClear(lcdHandle);
wyswietl(6,1,"WEJSCIE");

…….

    if (! (m = fork()) )
    execl("/home/minircp/pstryk", "/home/minircp/pstryk", nrzdjchr, (char *)0);

…….

}                               // od main

//*****************************************
void wyswietl(int x, int y, char* tresc)
{
  lcdPosition (lcdHandle, x, y) ; 
  strncpy(buf1, tresc, 20);
  lcdPuts (lcdHandle, buf1) ;
}

//*****************************************
void wyswbuf(int x, int y, char* tresc)
{
  bufwysw[wyswp].x = x;
  bufwysw[wyswp].y = y;
  strncpy(bufwysw[wyswp].tresc, tresc, 20);
  wyswp++;
  if (wyswp == PIPESIZE) wyswp = 0;
fflush(0);
}


//*************************************
PI_THREAD(lateok)
{
  sleep(1);
  wyswbuf(9,2,"  ");
  wyswbuf(0,0,"                    ");
}
I po drugie, baza danych.

Właściwie nic odkrywczego, standardowe C API MySQL/MariaDB.

Na dwie rzeczy zwrócic trzeba jednak uwagę. Karta SD nie lubi częstych zapisów, taka jej natura. Dlatego pliki bazy danych umieściliśmy na pendrive wpiętym do portu USB.

Nie jest to może super szybki interfejs, ale szybkość zapisów/odczytów nie jest tu krytyczna. W przypadku uszkodzenia pamięci na pendrive nie tracimy jednak całego systemu operacyjnego, a codziennie (cronem) robiona jest kopia karty SD na pendrive i kopia baz z pendrive na kartę SD.
#! /bin/sh
cd /home/kopie_rcp
NAZWA=minircp_mysql.tgz
tar -czvf $NAZWA  /var/lib/mysql  --exclude="/home/minircp/zdjecia/*" --exclude=/home/minircp/kopie_system/* /home/minircp
cd /home/minircp/kopie_system
NAZWA=system_html.tgz
tar -czvf $NAZWA  /etc /var/www/html
I druga istotna sprawa. MySQL/MariaDB operuje na kopiach tablic w RAM. Oznacza to, że w przypadku zaniku zasilania, zapisy z pewnego okresu (może nawet kilku godzin) znikną. Aby tego uniknąć, po każdym zapisie wymuszamy zapis na dysku tablic, które zostały zmodyfikowane:
        sprintf (buf_sql,
            "flush tables czytnik, czytnik_rob, zdjecia");
        if (mysql_query (&mysql, buf_sql))
         {
…..
I jeszcze wyłączanie urządzenia. RaspberryPI nie posiada przycisku wyłączenia, po prostu trzeba odłączyć zasilanie.

System operacyjny i aplikacje nie koniecznie lubią takie brutalne traktowanie. Dlatego podczas startu systemu uruchamiany jest proces reset (skompilowany program reset.c). Monitoruje on stan przycisku wejście/wyjście. W przypadku naciśnięcia go i przytrzymania przez 6 sekund inicjowana jest procedura zamykania systemu, informacje o tym pojawiają cię na wyświetlaczu LCD.

Jeśli dotarłaś/dotarłeś aż do tego miejsca, gratuluję wytrwałości i czekam na ewentualne pytania, najprościej na mój adres e-mail.