Sendo os 3 pacotes iniciais, apenas apoio, e o libfreenect-dev realmente a lib necessária.
Hello World da Biblioteca
Neste primeiro projeto, pouca coisa faremos, apenas iremos compilar identificando o device.
#include <stdio.h>
#include <stdlib.h>
#include <libfreenect.h>
freenect_context *f_ctx;
freenect_device *f_dev;
int user_device_number = 0; // Normalmente 0 se você tiver apenas um Kinect
void depth_cb(freenect_device *dev, void *v_depth, uint32_t timestamp) {
// Callback para dados de profundidade - não usado neste exemplo
}
void rgb_cb(freenect_device *dev, void *rgb, uint32_t timestamp) {
// Salva uma imagem RGB capturada pelo Kinect
FILE *image = fopen("output_image.ppm", "wb");
if (image == NULL) {
printf("Erro ao abrir o arquivo para escrita\n");
return;
}
fprintf(image, "P6\n# Kinect RGB test\n640 480\n255\n");
fwrite(rgb, 640*480*3, 1, image);
fclose(image);
printf("Imagem salva como output_image.ppm\n");
// Depois de salvar a imagem, podemos sair do loop principal
freenect_stop_video(dev);
freenect_close_device(dev);
freenect_shutdown(f_ctx);
exit(0);
}
int main() {
if (freenect_init(&f_ctx, NULL) < 0) {
printf("freenect_init() falhou\n");
return 1;
}
if (freenect_open_device(f_ctx, &f_dev, user_device_number) < 0) {
printf("Não foi possível abrir o dispositivo\n");
freenect_shutdown(f_ctx);
return 1;
}
freenect_set_depth_callback(f_dev, depth_cb);
freenect_set_video_callback(f_dev, rgb_cb);
freenect_set_video_mode(f_dev, freenect_find_video_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_VIDEO_RGB));
freenect_start_video(f_dev);
while (freenect_process_events(f_ctx) >= 0) {
// Processa eventos do Kinect até que a captura de imagem seja concluída
}
return 0;
}
Neste exemplo o programa pega o kinect e tira uma foto, salvando na maquina local.
Vamos entender o código.
A freenect_init inicia a api.
A próxima função freenect_open_device, abre o device conforme o número que estiver descrito. Isso permite abrir mais de um kinect na mesma maquina.
As funções freenect_set_depth_callback e freenect_set_video_callback criam funções de callback, para controle, se voce não sabe o que é leia este artigo:
A função freenect_set_video_mode indica os parâmetros de resolução.
Por ultimo a função freenect_start_video, dá inicio ao kinect que aciona o callback quando pronto.
Perceba aqui que o pulo do gato neste makefile, é a inclusão da pasta /usr/lib/x86_64-linux-gnu que é onde a lib se encontra. Bem como a /usr/include/libfreenect que é onde o header se encontra.
Compilando o projeto
Para compilar esse projeto, é necessário apenas rodar o script na pasta src do ubuntu:
make all
Compilando o projeto
Rodando o programa
Agora iremos rodar o programa, isso é a parte mais simples.
Ao rodar, ele captura uma foto, e salva, conforme apresentado.
Daemon são programas que rodam em back ground e que são inicializados e controlados pelo sistema operacional.
Os daemon são geralmente serviços que devem ser gerenciados de forma automatica.
Iremos apresentar aqui um bom exemplo de como criar um daemon.
Para criar um daemon, primeiramente seu binário precisa ser incluído em um local que permita o acesso para qualquer usuário, por este motivo a inclusão na pasta /home/[usuário] não é uma boa pratica. Em geral incluímos em uma pasta como /usr/bin.
Os arquivos de configuração , quando houver, poderão ser incluídos na pasta /etc/[nome daemon].
A criação
A criação do daemon é bem simples.
Crie em /etc/systemd/system/ o arquivo [servido].service, tal como o exemplo do meu git:
Edite o arquivo [servico].service conforme apresentado abaixo:
[Unit]
Description=[Descreva o que faz o servico]
After=network.target
[Service]
ExecStart=/usr/bin/[servico_binario]
Restart=always
User=root
Group=root
[Install]
WantedBy=multi-user.target
Salve o serviço e como sudo dê o seguinte comando:
Temos também a variável executada, que será utilizada na marcação dos itens executados.
Sessão Crítica
O controle da sessão crítica é feita nas funções IniciaSessaoCritica e TerminaSessaoCritica, conforme fonte abaixo:
int IniciaSessaoCritica()
{
int rc;
int cont = 0;
while ( (rc = pthread_mutex_lock(&mutex))!=0)
{
usleep(100); /*Aguarda um pouco*/
cont++;
printf("Sessao critica não conseguida\n");
if (cont>3) break;
}
return rc;
}
int TerminaSessaoCritica()
{
int rc;
int cont = 0;
while ( (rc = pthread_mutex_unlock(&mutex))!=0)
{
usleep(100); /*Aguarda um pouco*/
cont++;
printf("Sessao critica não liberada\n");
if (cont>3) break;
}
return rc;
}
Ambas as funções controlam a sessão criada mutex, declarada, conforme fonte abaixo:
Para cada sessão critica, deve-se criar uma variável de controle. Como usaremos apenas a fila, como variável de troca de dados, utilizaremos uma única sessão critica.
Criando a thread da recepção
Agora iremos dar inicio a criação da thread da recepção, na qual incluiremos o inicio da thread.
Neste segmento do código, podemos acompanhar que criamos a thread com pthread_create, passando o ponteiro da função que será criada a thread threadRecepcao. Desta forma a função threadRecepcao, não mais seguirá na thread pai. O ultimo parâmetro é utilizado para passagens de argumentos, no nosso caso, não será usado.
Por fim a função da thread
/*thread function definition*/
void* threadRecepcao(void* args)
{
for(int cont = 0;cont<=MAXITEMS;cont++)
{
printf("Criando pacote nro %d\n",cont);
if(IniciaSessaoCritica()==0)
{
int Valor = (rand()% 100);
fila[cont] = Valor; /*Grava Valor na fila*/
printf("Registrou na fila[%d] = %d\n",cont,Valor);
TerminaSessaoCritica();
usleep(1000);
} else {
printf("Falha na Recepcao nro:\n",cont);
}
}
printf("Recepcao terminou atendimento\n");
}
Podemos observar que a threadRecepção requisita a sessão crítica para movimentar a fila, apenas depois alocando valor para ela. Também podemos perceber que após seu uso, o mesmo é descartado.
Criando o Controlador
O controlador, é executado, em seguida, ordenando as informações.
void ordenacao()
{
int i, x;
bool flgordenado = false;
while((!flgordenado)&&(!flgTerminou))
{
//flgordenado = true;
for (i=0; i<=MAXITEMS-1; i++)
{
if ((fila[i]!=0)&&(fila[i+1]!=0))
{
if(fila[i]>fila[i+1])
{
x = fila[i];
fila[i] = fila[i+1];
fila[i+1] = x;
flgordenado = false;
//printf("Ordenando os pacote nro %d\n",i);
}
} else {
//printf("Posicao vazia %d\n",i);
flgordenado = false;
}
}
}
}
/*thread function definition*/
void* threadControlador(void* args)
{
int oldValue, newValue;
int flgOrdenado = false;
printf("Iniciou o controlador\n");
while(!flgTerminou) /*Faz enquanto nao terminar e nao ordenao*/
{
if(IniciaSessaoCritica()==0)
{
ordenacao();
TerminaSessaoCritica();
//usleep(200);
}
}
printf("Terminou ordenação de todos os itens\n");
}
Neste bloco temos duas funções:
A thread em si é a threadControlador, que chama a função de ordenação ordenacao.
A ordenação só para quando for atendida duas condições:
Tiver terminado as demais threads, indicado pelo flag flgTerminou e quando tiver sido totalmente ordenado flgordenado.
Executor
A thread do Executor, pode ser vista conforme apresentado a seguir:
/*thread function definition*/
void* threadExecutor(void* args)
{
int oldValue, newValue;
bool flgExecutado = false;
printf("Iniciou o controlador\n");
int posicao = 0;
//while(!flgExecutado)
while((!flgExecutado)&&(!flgTerminou))
{
//printf("Pesquisando Posicao %d\n",posicao);
//printf("Entrou na sessao critica\n");
if (fila[posicao]==0) /*Fila nao foi preenchida*/
{
printf("posicao vazia %d\n",posicao);
posicao = posicao;
} else
{
executada[posicao] = fila[posicao];
printf("Executou[%d] = %d\n",posicao,fila[posicao]);
if(posicao==MAXITEMS-1)
{
printf("Chegou ao fim\n ");
flgExecutado= true; /*Finaliza executor*/
} else
{
posicao ++;
}
}
//printf("terminou while\n");
}
flgTerminouExecucao = true;
printf("Terminou ordenação de todos os itens\n");
}
Ela irá rodar até que duas condições sejam satisfeitas:
flgExecutado – Controla a execução de todas as tarefas da lista
flgTerminou – Controla o fim de todas as demais threads do sistema
Testando programa
Compilando programa
A compilação em um Raspberry PI ocorreu com sucesso.
Executando o programa
root@raspberrypi:/home/mmm/projetos/Threads-para-Raiz/c# ./threads
Bem vindo ao programa das threads
Este programa faz parte do artigo:
http://maurinsoft.com.br/index.php/2022/07/02/threads-para-raiz-parte-1/
Inicializando vetor
Iniciando Recepcao
Iniciando Controlador
Registrou na fila[0] = 80
Registrou na fila[1] = 11
Registrou na fila[2] = 76
Registrou na fila[3] = 71
Registrou na fila[4] = 27
Registrou na fila[5] = 23
Registrou na fila[6] = 33
Registrou na fila[7] = 68
Registrou na fila[8] = 32
Registrou na fila[9] = 92
Recepcao terminou atendimento
Iniciou o controlador
Iniciando Executor
Status:Iniciou o controlador
Executou[0] = 11
Executou[1] = 23
Executou[2] = 27
Executou[3] = 32
Executou[4] = 33
Executou[5] = 68
Executou[6] = 71
Executou[7] = 76
Executou[8] = 80
Executou[9] = 92
Chegou ao fim
Terminou ordenação de todos os itens
Status:Terminou ordenação de todos os itens
Fila ordenada:11 23 27 32 33 68 71 76 80 92
Fila executada:11 23 27 32 33 68 71 76 80 92
Como a execução ficou um pouco longa, resolvi jogar como código.
Conclusão
Podemos perceber que a execução de threads pode ser facilitada com uso de flags externos, que facilitam a comunicação entre os processos. Pudemos ver tambem, que a programação em threads é de fato, um estado de arte da programação C, pois envolve alem de um algoritmo bem desenvolvido, a elaboração de estratégias pensando em que uma thread não sabe exatamente quando a outra irá cumprir suas atividades.
Desta forma cada thread tem que ser pensada em aguardar e esperar as informações das threads que colaboraram com esta.
Desta forma o controle interno das informações é bem mais complicado.
Pudemos ver por ultimo o uso dos sinalizadores, que visão identificar o uso de uma sessão crítica do sistema. Que nada mais é que um recurso compartilhado.
Espero que tenham gostado do artigo.
Em caso de dúvidas ou sugestões, fico a disposição como sempre.