[C++11] Soucis pour endormir un thread
Répondre à la discussion
Affichage des résultats 1 à 10 sur 10

[C++11] Soucis pour endormir un thread



  1. #1
    sandrecarpe

    [C++11] Soucis pour endormir un thread


    ------

    Bonjour,
    Je suis en train de faire un système de log pour mon terrarium. Je veux collecter certaines données et les enregistrer dans un fichier toutes les 2 heures.
    Pour faire ça, je lance simplement une boucle infini dans un thread avec une pause de 2h entre chaque itération.
    Mais le delay ne se fait pas comme il devrait. Lorsque la fonction est appelée pour la première fois, la pause ligne 28 du cpp est comme ignorée et passe directement à la seconde boucle. Comportement incompréhensible. Je ne sais pas comment régler ce soucis, j'ai essayé de faire le delay avec la fonction sleep de linux mais j'ai même soucis.

    Le programme tourne sur un rasperry pi 2
    Je me suis permis de vous mettre en pièce jointe le code de la classe, elle n'est pas longue

    Merci pour votre aide

    Log.cpp :
    Code:
    #include "Log.h"
    
    
    Log::Log(const std::string filename) :
    	mFichierLog(filename, std::ios::app), 
    	mThreadLog(0),
    	mEtat(true), mTemperature(0), mHygrometrie(0), mHumiditeSol(0), mFilename(filename)
    {
    
    	mThreadLog = new std::thread(&Log::threadLog, this);
    
    }
    
    Log::~Log()
    {
    	mEtat = false;
    	mThreadLog->join();
    	delete mThreadLog;
    	std::cout << "Arret de la gestion des logs" << std::endl;
    }
    
    
    void Log::threadLog()
    {
    
    	while(mEtat)
    	{
    		std::this_thread::sleep_for(std::chrono::seconds(2*3600));
    
    
    		while(mTemperature*mHygrometrie*mHumiditeSol <= 0)
    		{
    			std::cout << "Log::threadLog() : Il n'y a pas de données Ã* enregistrer...Nouvel essai dans 10 secondes" << std::endl;
    			std::this_thread::sleep_for(std::chrono::seconds(10));
    
    		}
    
    		std::cout << "Log : nouvelles donnees" << std::endl;
    		time_t now = time(0);
    		struct tm today;
    		today = *localtime(&now);
    		std::string date = ctime(&now);
    		trim(date); // ctime créer un saut de ligne Ã* la fin de la chaine : on les supprimer
    
    		mFichierLog.open(mFilename, std::ios::app);
    		mFichierLog << date << " " << mHygrometrie << ";" << mTemperature << ";" << mHumiditeSol << std::endl;
    		mFichierLog.close();
    
    		reset();
    	}
    }
    
    void Log::setHygrometrie(float hygrometrie)
    {
    	mHygrometrie = hygrometrie;
    }
    
    
    void Log::setTemperature(float temperature)
    {
    	mTemperature = temperature;
    }
    
    void Log::setHumiditeSol(int humidite)
    {
    	mHumiditeSol = humidite;
    }
    
    
    void Log::reset()
    {
    	mTemperature = 0;
    	mHygrometrie = 0;
    	mHumiditeSol = 0;
    }
    
    void Log::trim(std::string& str)
    {
    	str.erase(str.begin(), find_if(str.begin(), str.end(), [](char& ch)->bool { return !isspace(ch); }));
    	str.erase(find_if(str.rbegin(), str.rend(), [](char& ch)->bool { return !isspace(ch); }).base(), str.end());
    }
    log.h :
    Code:
    #ifndef LOG_H 
    #define LOG_H 
    
    
    #include <string>
    #include <algorithm>
    #include <fstream>
    #include <cctype>
    #include <thread>
    #include <iostream>
    #include <thread>
    #include <chrono>
    
    class Log
    {
    	public:
    
    		Log(const std::string filename);
    		void setHygrometrie(const float hygrometrie);
    		void setTemperature(const float temperature);
    		void setHumiditeSol(const int humidite);
    		~Log();
    
    
    	private:
    		void threadLog();
    		void reset();
    		void trim(std::string &str);
    
    
    		std::fstream mFichierLog;
    		std::thread *mThreadLog;
    
    		bool mEtat;
    		float mTemperature, mHygrometrie;
    		int mHumiditeSol;
    		std::string mFilename;
    	
    };
    
    #endif

    -----
    Dernière modification par Antoane ; 06/04/2016 à 16h05. Motif: Intégration du code dans le corps du message

  2. #2
    Chanur

    Re : [C++11] Soucis pour endormir un thread

    Bonjour,
    Citation Envoyé par sandrecarpe Voir le message
    Je me suis permis de vous mettre en pièce jointe le code de la classe, elle n'est pas longue
    Et tu pourrais donner aussi le code du main() (enfin d'un main() minimal)
    Parce que j'ai ajouté celui-ci :
    Code:
    int main ()
    {
        Log ("fichier_log.txt");
        usleep (1000000);
    }
    et il plante sur la ligne qui instancie Log, en disant :
    Code:
    terminate called after throwing an instance of 'std::system_error'
      what():  Operation not permitted
    Abandon (core dumped)
    Ce qui se conçoit bien s'énonce clairement ; et les mots pour le dire arrivent aisément.

  3. #3
    Jack
    Modérateur

    Re : [C++11] Soucis pour endormir un thread

    J'ai essayé également avec ton main Chanur avec Visual Studio 2013, compilateur msvc2013 et ça fonctionne.

    Je n'ai pas non plus rencontré le problème de sandrecarpe.

    Je pourrais essayer directement avec une Rpi, mais je n'ai qu'une connexion distante, donc difficile d'utiliser un debogueur. J'ai essayé également avec le nouveau plugin de VS2015 pour linux mais de dernier fait planter VS2015 à la demande de connexion.

  4. #4
    Chanur

    Re : [C++11] Soucis pour endormir un thread

    Moi, c'est sous linux (Ubuntu) avec g++
    Si on a trois comportements différents quand on essaie dans trois contextes, je subodore un machin pas initialisé ...
    Ce qui se conçoit bien s'énonce clairement ; et les mots pour le dire arrivent aisément.

  5. A voir en vidéo sur Futura
  6. #5
    sandrecarpe

    Re : [C++11] Soucis pour endormir un thread

    Bonsoir, tout d'abord merci pour vos réponse
    L'instanciation de cette classe Log se fait dans le constructeur de la classe Terrarium, donc je vous passe cette classe. Le main, lui, se charge d'instancier l'objet Terrarium.


    Terrarium.h
    Code:
    #ifndef TERRARIUM_H 
    #define TERRARIUM_H 
    
    #include <iostream>
    #include <thread>
    #include <sstream>
    #include <wiringPi.h>
    #include <wiringSerial.h>
    #include <unistd.h>
    #include <cerrno>
    #include <cstring>
    
    #include "DHT22_library_Version_CPP/dhtObserver.h"
    #include "DHT22_library_Version_CPP/dht22.h"
    #include "LCD/LcdDisplay.h"
    #include "pins.h"
    #include "Eclairage.h"
    #include "Log.h"
    
    
    class Terrarium : public dhtObserver
    {
    	public:
    		Terrarium();
    		~Terrarium();
    
    		void onTemperatureChange(float value);
    		void onHumiditeChange(float value);
    
    		void thread_watching_dht22();
    		void thread_watching_humidite_sol();
    	
    		static void wrapper_wiringPiISR_ecl(void *function);
    		static void wrapper_wiringPiISR_brum(void *function);
    		
    		void marcheForceEclairage();
    		void marcheForceBrumisateur();
    
    		void start();
    
    		bool hasMFEclairage();
    		bool hasMFBrumisateur();
    
    
    	private:
    		Terrarium (const Terrarium &) = delete; //on supprime le constructeur de copie, en effet, on modélise UN seul et unique terrarium
    
    		bool _mf_eclairage;
    		bool _mf_brumisateur;
    		
    		dht22 _dht;
    		LcdDisplay _lcd;
    		Log _log;
    	
    };
    
    #endif


    Dans le fichier Terrarium.cpp, j'ai mis uniquement les fonctions susceptibles de manipuler l'objet Log et celles utiles à comprendre le lien avec le main (comme start())

    Terrarium.cpp
    Code:
    #include "Terrarium.h"
    
    using namespace std;
    
    
    Terrarium::Terrarium() : _mf_eclairage(false), _mf_brumisateur(false), _dht(PIN_DHT22), _lcd(0x27), _log("terrarium.log")
    {
    	_dht.setListeners(this);
    	_lcd.init();
    }
    
    
    Terrarium::~Terrarium()
    {
    	digitalWrite(PIN_ECLAIRAGE, LOW);
    	digitalWrite(PIN_BRUMISATEUR, LOW);
    	cout << "Arret du programme..." << endl;
    }
    
    
    
    
    void Terrarium::thread_watching_dht22()
    {
    
    	//la fonction read_dht22_dat() de ce thread appelles les fonctions onTemperatureChange() et onHumiditeChange() de la classe
    	cout << "lancement du thread Terrarium::thread_watching_dht22()" << endl;
    
    	float humidite = 0;
    	float temperature = 0;
    
    	while(true)
    	{
    		while (_dht.read_dht22_dat(&humidite, &temperature) != 1)
    		{
    			cout << "DHT22 : Bad data, next" << endl;
    			sleep(1);
    		}
    
    		sleep(5);
    	}
    }
    
    void Terrarium::thread_watching_humidite_sol()
    {
    	cout << "lancement du thread Terrarium::thread_watching_humidite_sol()" << endl;
    
    	int fd = serialOpen("/dev/ttyAMA0", 4800);
    	if(fd == -1)
    	{
    		cout << "Terrarium::thread_watching_humidite_sol() : " << std::strerror(errno) << endl;
    
    	}
    
    	int data[1] = {0};
    	int count = 0;
    
    	//l'information à recevoir est codée sur 2 octets.
    	//on récupère les deux octets séparemment puis un reconstruit la valeur par manipulation binaire
    	while((count = serialDataAvail(fd)) != -1)
    	{	
    		data[0] = serialGetchar(fd);
    		data[1] = serialGetchar(fd);
    		//cout << "datas : " << data[0] << " ; " << data[1] << endl;
    
    		int donnee = (data[1] << 8) | data[0];
    		if(donnee < 0 || donnee > 1023)
    		{
    			cout << "Terrarium::thread_watching_humidite_sol() : Erreur donnne < 0 ou donnee > 1023" << endl;
    			cout << "Terrarium::thread_watching_humidite_sol() : Tentative de réouverture de la liaison série..." << endl;
    			serialClose(fd);
    			fd = serialOpen("/dev/ttyAMA0", 4800);
    			if(fd == -1)
    			{
    				cout << "Impossible de réouvrir la liaison série. file descriptor : " << fd << endl;
    				break;
    			}
    			else
    				continue;
    		}
    
    		cout << "humidité sol : " << donnee << endl;
    		sleep(30);
    
    		_lcd.displayStringAtPosition("                    ", 3, 0);
    		_lcd.displayStringAtPosition(to_string(donnee).c_str(), 3, 0);
    
    		//on ajoute la nouvelle valeur au fichier log
    		_log.setHumiditeSol(donnee);
    
    
    	}
    
    	serialClose(fd);
    	thread_watching_humidite_sol();
    }
    
    void Terrarium::onTemperatureChange(float value)
    {
    	cout << "Nouvelle temperature : " << value << endl;
    
    	_lcd.displayStringAtPosition("                    ", 2, 0);
    
    	if(value < 18.0)
    	{
    		cout << "La température à atteint un faible niveau" << endl;
    		const char *str = "Temperature faible";
    		_lcd.displayStringAtPosition(str, 4, 0);
    	}
    	else
    	{
    		_lcd.displayStringAtPosition("                    ", 4, 0);
    	}
    
    
    	char degreChar = 0b11011111; //caractère "degré"
    
    	//conversion float -> string
    	std::stringstream Stream;
    	Stream << static_cast<float>(value);
    	const char* val = Stream.str().c_str();
    
    
    	string str = val;
    	str += degreChar;
    	str += "C";
    
    	//on affiche la nouvelle température sur l'écran lcd
    	_lcd.displayStringAtPosition(str.c_str(), 2, 0);
    
    
    
    	//on ajoute le nouvelle température au fichier log
    	_log.setTemperature(value);
    
    }
    
    
    void Terrarium::onHumiditeChange(float value)
    {
    	cout << "Nouvelle humidité : " << value << endl;
    
    	_lcd.displayStringAtPosition("                    ", 1, 0);
    
    
    	string str = "Humidite faible";
    	if(value < 70.0)
    	{
    		cout << "L'humidité à atteint un faible niveau" << endl;
    		
    		_lcd.displayStringAtPosition(str.c_str(), 4, 0);
    		digitalWrite(PIN_BRUMISATEUR, HIGH);
    	}
    	else
    	{
    		_lcd.displayStringAtPosition("                    ", 4, 0);
    		digitalWrite(PIN_BRUMISATEUR, LOW);
    	}
    
    
    	char degrePercent = 0b100101; // caractère "pourcent"
    
    	//conversion float -> string
    	std::stringstream Stream;
    	Stream << static_cast<float>(value);
    	const char* val = Stream.str().c_str();
    
    	str = val;
    	str += degrePercent;
    
    	//on affiche la nouvelle valeur de l'hygrométrie sur l'écran lcd
    	_lcd.displayStringAtPosition(str.c_str(), 1, 0);
    
    
    	//on ajoute la nouvelle valeur de l'hygrométrie au fichier log
    	_log.setHygrometrie(value);
    
    }
    
    void Terrarium::start()
    {
    	//on creer un thread pour recevoir les infos de la sonde dht22
    	thread threadDht22(&Terrarium::thread_watching_dht22, this);
    	threadDht22.detach();
    
    	//on creer un thread pour recevoir les infos du capteur d'humidite du sol
    	thread threadHumiditeSol(&Terrarium::thread_watching_humidite_sol, this);
    	threadHumiditeSol.detach();
    
    
    
    	//on défini les heures d'allumage et d'extinction de l'éclairage
    	time_t now = time(0);
    
    	struct tm today;
    	today = *localtime(&now);
    
    	struct tm soirStart;
    	soirStart = *localtime(&now);
    	soirStart.tm_hour = 17;
    	soirStart.tm_min = 30;
    	soirStart.tm_sec = 0;
    	if(soirStart.tm_hour < today.tm_hour)
    	{
    		soirStart.tm_mday++;
    	}
    
    	struct tm soirEnd;
    	soirEnd = *localtime(&now);
    	soirEnd.tm_hour = 21;
    	soirEnd.tm_min = 0;
    	soirEnd.tm_sec = 0;
    	if(soirEnd.tm_hour < today.tm_hour)
    	{
    		soirEnd.tm_mday++;
    	}
    
    	struct tm matinEnd;
    	matinEnd = *localtime(&now);
    	matinEnd.tm_hour = 9;
    	matinEnd.tm_min = 0;
    	matinEnd.tm_sec = 0;
    	if(matinEnd.tm_hour < today.tm_hour)
    	{
    		matinEnd.tm_mday++;
    	}
    
    	struct tm matinStart;
    	matinStart = *localtime(&now);
    	matinStart.tm_hour = 6;
    	matinStart.tm_min = 30;
    	matinStart.tm_sec = 0;
    	if(matinStart.tm_hour < today.tm_hour)
    	{
    		matinStart.tm_mday++;
    	}
    
    	//on lance le thread qui gère l'éclairage
    	Eclairage eclairage(&matinStart, &matinEnd, &soirStart, &soirEnd, this);
    	eclairage.start();
    
    }

    Et le main :
    Code:
    #include <iostream>
    #include "Terrarium.h"
    #include "LCD/LcdDisplay.h"
    #include <csignal>
    #include <ctime>
    
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    using namespace std;
    
    bool initialisation();
    void presentation();
    void onAbort(int id);
    
    
    Terrarium terrarium;
    
    int main()
    {
    
    	signal(SIGINT, onAbort);
    
    	if(!initialisation())
    	{
    		cout << "Impossible d'initialiser le programme" << endl << "fin." << endl;
    		return -1;
    	}
    	else
    	{
    		cout << "Lancement du programme" << endl;	
    	}
    
    	presentation();
    	
    	terrarium.start();
    
    
    
    	while(true); //boucle infini
    
    
    	return 0;
    }

    J'espère que ça fait pas trop lourd à lire. Si vous avez des remarques ou des suggestions sur mon code c'est toujours bon à prendre
    Merci de votre aide

  7. #6
    sandrecarpe

    Re : [C++11] Soucis pour endormir un thread

    J'ai testé la classe (enfin le minimum) sur code block sur Windows 10 et ça fonctionne sans broncher

  8. #7
    Chanur

    Re : [C++11] Soucis pour endormir un thread

    Je n'ai pas pu le compiler, faute d'avoir tous les includes. Donc ce qui suit n'est que pure supposition.

    Je trouve bizarre de déclarer Terrarium en variable globale : puisque c'est dans son constructeur que _log est instancié, ça veut dire que le thread de Log est démarré AVANT le lancement du main(). Je ne suis pas sûr qu'on ait le droit.

    Personnellement j'aurais juste déclaré en variable globale un pointeur : Terrarium * terrarium;
    Et je l'aurais instancié au début du main() : terrarium = new Terrarium ();
    en remplaçant partout terrarium.quelque_chose par terrarium->quelque_chose

    Sinon, je ne vois pas ...
    Ce qui se conçoit bien s'énonce clairement ; et les mots pour le dire arrivent aisément.

  9. #8
    sandrecarpe

    Re : [C++11] Soucis pour endormir un thread

    Salut, merci de m'aider ça fait plaisir !
    En effet le thread ce lance avant d'entrer dans le main. C'est un soucis de conception que je vais corriger, la méthode terrarium::start() ne devrait pas exister.
    Si ça ne vous dérange pas et que le soucis persiste, je vous passerai de quoi compiler entièrement le projet
    Dernière modification par sandrecarpe ; 08/04/2016 à 06h14.

  10. #9
    sandrecarpe

    Re : [C++11] Soucis pour endormir un thread

    Bonjour,
    Dans le main.cpp j'ai comme suggéré par Chanur, créé un pointeur global sur Terrarium qui j'initialise dans le main(). Il semble que le problème soit résolu, je viens de faire un essai vite fait à l'instant mais j'attends d'en faire d'autre pour vous confirmer

  11. #10
    sandrecarpe

    Re : [C++11] Soucis pour endormir un thread

    Salut ! Le problème venait bien de la variable global Terrarium dans le main. Je n'ai plus eu de soucis depuis que j'ai corrigé ça. Merci pour la piste !

Discussions similaires

  1. Pas moyen de s'endormir ...
    Par invite72c9e288 dans le forum Santé et médecine générale
    Réponses: 6
    Dernier message: 17/11/2009, 21h30
  2. Réponses: 3
    Dernier message: 01/08/2009, 20h01
  3. [Divers] Endormir des vers...
    Par invite20a9984d dans le forum Biologie
    Réponses: 5
    Dernier message: 08/08/2007, 12h20