/*
	Tomasz Piekarczyk
	grupa dziekańska G1EIK
	nr albumu 200732
	temat: zastosowanie biblioteki tribool

	Zadanie programu:
		Program opisuje działanie biblioteki boost::tribool. W szczegolności tworzenia
		prostych konstrukcji logicznych na algebrze trójstanowej. Dotatkowo pokazany 
		jest sposób modyfikacji nazwy trzeciego stanu oraz korzystanie z operatora '<<'
		przy ustawionej fladze 'boolalpha'.
		
	Zasada działania programu:
		Program tworzy tablicę sensorow opisujących stan różnych parametrow fikcyjnej 
		"elektrowni jądrowej". Sensory zwracają stan parametrów w jednej z trzech 
		wartości '1' - OK; '0' - poza normą; 'indeterminate' - nie pobrano pomiaru. 
		W przypadku odpowiedniej kombinacji stanów różnych czynnikow ( np. czujnik 
		temp = 0 && czujnik radiacji = indeterminate ) generowane jest ostrzeżenie. 
*/	

#include <iostream>
#include <boost/logic/tribool.hpp>
#include <boost/logic/tribool_io.hpp>
#include <map>
#include<string>

BOOST_TRIBOOL_THIRD_STATE(notRead)	//makro umożliwiające zamianę standardowej nazwy stanu
					//nieokreślonego (indeterminate) na wybraną przez użytkownika
using namespace boost::logic;
using namespace std;    

locale g_global;
locale g_Locale( g_global, new indeterminate_name<char>("notRead") );	//dodanie do locale nowej nazwy stanu nieokreślonego

/**
*	@brief Klasa reprezentuje sensor ktory kontroluje czy obserwowany parametr ma wartość w bezpiecznym zakresie.  
*/
class Sensor
{
	private:
		
		const int m_iUpperLimit;
		const int m_iLowerLimit;
		int m_iCurrentValue;
		tribool m_tbState;
		unsigned short m_ushSamplesUnread;	//liczba próbek nieodczytanych pod rząd
		string m_sResponsibility;
		void GetSample();
	public:

		Sensor(): m_iUpperLimit(0), m_iLowerLimit(0)
		{};
		Sensor( int, int, string );
		tribool GetState();
		string MyResponsibility() const
		{ 
			return m_sResponsibility; 
		};
		unsigned short HowManyUnread() const
		{
			return m_ushSamplesUnread;
		};
};

int main(int argc, char *argv[])
{
	cout.imbue(g_Locale);			//ustawia nowo utworzony locale w ostream
	time_t m_CurrentTime = time( NULL ) ;	
	srand( m_CurrentTime );			//ustawienie ziarna dla rand;	

	const int SENSNUMBER = 5;		//liczba sensorów
	const string PRES = "Pressure"; 
	const string TEMP = "Temperature";
	const string VOLT = "Voltage";
	const string BATT = "Battery level";
	const string RADN = "Radiation level";

	map<string, tribool> l_stateTable;	//tablica hashująca (nazwa sensora,aktualny stan) 
	char l_cContinue;			//warunek zakończenia programu
	map<string, tribool>::iterator it;	
	
	Sensor l_Sensors[SENSNUMBER] = 	//tablica sensorów	
	{
		Sensor( 500,1000, PRES ),
		Sensor( 0, 80, TEMP ),
		Sensor( 200, 260, VOLT ),
		Sensor( 50, 100, BATT ),
		Sensor( 0, 50, RADN )
	};					
	

	do
	{
		//generacja kolejnego ciągu próbek
		for( int i=0; i< SENSNUMBER; i++ )	
			l_stateTable[l_Sensors[i].MyResponsibility()] = l_Sensors[i].GetState();
		
		/*
			Wypisanie całej tablicy hashującej z wykorzystaniem flagi boolalpha co 
			oznacza że standardowy wypis w postaci '0', '1', '2' zastąpiono 
			odpowiednio 'false' 'true' 'indeterminate' (lub jak w naszym przypadku 
			notRead)
		*/
		for ( it=l_stateTable.begin() ; it != l_stateTable.end(); it++ )
    			cout  << (*it).first << " => " << boolalpha << (*it).second << endl;
		cout << endl;
		
		/*
			Kiedy czujnikowi dwukrotnie nie powiodło się odczytanie wartości (stan 
			notRead) ostrzeżenie zostaje wypisane na ekran 
		*/
		for( int i=0; i< SENSNUMBER; i++ )
			if( l_Sensors[i].HowManyUnread() > 1)	
				cout << "UWAGA!!! Czujnik "<< l_Sensors[i].MyResponsibility() << " uszkodzony "<< endl;
		
		/*
			Ciąg przykładów tworzenia funkcji logicznych z użyciem tribool przy 
			opisach a - 1 argument, b - 2 argument
		*/

		//tylko kiedy a=0 and b=0
		if( !l_stateTable[TEMP] && !l_stateTable[PRES] )		
			cout << "UWAGA!!! Zwiekszone ryzyko przegrzania."<< endl;
		// tylko kiedy a=0
		if( !l_stateTable[VOLT] ) 
			cout << "UWAGA!!! Niestabilne napiecie" << endl;
		//kiedy a=notRead or b=0
		if( notRead( l_stateTable[RADN]) || !l_stateTable[RADN]  )	
			cout << "UWAGA!!! Nieznany badz przekroczony poziom radiacji" << endl;
		//kiedy a=notRead
		if( notRead(l_stateTable[BATT]) )	
			cout << "UWAGA!!! Nieznany stan generatorow" << endl;
		//kiedy a=0 and b=0
		if( !l_stateTable[BATT] && !l_stateTable[TEMP] )	
			cout << "UWAGA!!! Awaryjny system chlodzenia uszkodzony" << endl;
	
		cin.clear();
		cout << endl <<  "Kontynuacja? [t/n]" << endl; 
		cin >> l_cContinue;
		cout << endl;
	}
	//koniec programu jesli użytkownik tak zadecydował
	while( l_cContinue != 'n');	
	
	return 0;
}

/**
 * @brief Konstrukuuje sensor o zadanych limitach i nadaje nazwę sensorowi
 * @param lowLimit Poniżej tej wartości sensor ustawia wartość stanu na niepoprawną (0)
 * @param upLimit Powyżej tej wartości sensor ustawia wartość stanu na niepoprawną (0)
 * @param resp Nazwa obiektu parametru za który odpowiada sensor
 */
Sensor::Sensor( int lowLimit, int upLimit, string resp ): m_iUpperLimit(upLimit), m_iLowerLimit(lowLimit)
{
	m_tbState = true;
	m_sResponsibility = resp;
	m_ushSamplesUnread = 0;
}

/**
 * @brief Pobiera aktualny stan sensora i zwraca stan w jakim znajduje się sensor
 * @return Zwraca '0' jeśli sensor wykrył przekroczenie wartości dozwolonej, '1' jeśli wartość 
 *	   mieści się w zakresie wartości dozwolonych, 'notRead' jeśli nie powiódł się odczyt danych 
 *	   ( losowe zaburzenia )
 */
tribool Sensor::GetState()
{

	GetSample();
	return m_tbState;
}

/**
 * @brief Generuje kolejną probkę danych
 */
void Sensor::GetSample()
{
	//generacja liczby losowej
	m_iCurrentValue =   static_cast<int>(2 * m_iUpperLimit * (static_cast<double>( rand() ) / RAND_MAX ));	
	
	//losowe zaburzenie odczytu czyli stan notRead
	if(  (static_cast<double>(rand()) / RAND_MAX ) > 0.7F )	
	{
		m_tbState = notRead;
		m_ushSamplesUnread++;	
	}
	//czy aktualna probka mieści się w limitach określonych dla tego sensora
	else if( m_iCurrentValue >= m_iUpperLimit || m_iCurrentValue <= m_iLowerLimit )	
	{
		m_tbState = false;
		m_ushSamplesUnread = 0;
	}
	else
	{
		m_tbState = true;
		m_ushSamplesUnread = 0;
	}	
}
