Files
c_pompes/main.cpp
2025-12-10 07:47:04 +01:00

412 lines
10 KiB
C++

#include <iostream>
#include <iomanip>
#include <unistd.h>
#include <math.h>
#include <locale.h>
#include <array>
#include "main.hpp"
#include "AutomForArduino.cpp"
#include <prometheus/counter.h>
#include <prometheus/gauge.h>
#include <prometheus/histogram.h>
#include <prometheus/registry.h>
#include <prometheus/exposer.h>
#include <curl/curl.h>
#include <string>
#include <thread>
#include <atomic>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <csignal>
#include <chrono>
#include <cstring>
#undef timeout
#include "mqtt/async_client.h"
#include <nlohmann/json.hpp>
using json = nlohmann::json;
// Constantes de fonctionnement
#define LEVEL_MIN 2
#define FLOW_PER_PUMP 150
/* Configuration MQTT */
const std::string ADDRESS = "tcp://rabbitmq:1883";
const std::string CLIENTID = "CppClientTP";
const std::string TOPIC = "geii/ordre/#";
const int QOS = 1;
const int CYCLE_MS = 100;
int etape = 0; // Étape du grafcet : début Automatique
int bp_mode, bp_mode_fm;
unsigned short pompe1, pompe2, pompe3, pompe4; // bouton des pompes 0 (arrêt) / 1 (marche)
unsigned short pompe1_old, pompe2_old, pompe3_old, pompe4_old;
unsigned short sensor_max, sensor_high, sensor_low, sensor_min;
float TankInitalValue = 7;
// Réception des messages MQTT
// ************************************************************
class callback : public virtual mqtt::callback {
public:
void message_arrived(mqtt::const_message_ptr msg) override {
std::string payload = msg->to_string();
if (payload == "p1") {
pompe1 = !pompe1;
} else if (payload == "p2") {
pompe2 = !pompe2;
} else if (payload == "p3") {
pompe3 = !pompe3;
} else if (payload == "p4") {
pompe4 = !pompe4;
}
}
};
// ************************************************************
int main()
{
/* Initialisation */
InitPrometheus();
ProcessInitIO();
ProcessInitValues();
mqtt::async_client client(ADDRESS, CLIENTID);
callback cb;
client.set_callback(cb);
mqtt::connect_options connOpts;
connOpts.set_clean_session(true);
connOpts.set_user_name("admin");
connOpts.set_password("geii2025");
try {
client.connect(connOpts)->wait();
client.start_consuming();
client.subscribe(TOPIC, QOS)->wait();
} catch (const mqtt::exception &exc) {
std::cerr << "Erreur MQTT: " << exc.what() << "\n";
return 1;
}
while (1)
{
Process();
sensor_min = digitalRead(IN_SENSOR_MIN);
sensor_low = digitalRead(IN_SENSOR_LOW);
sensor_high = digitalRead(IN_SENSOR_HIGH);
sensor_max = digitalRead(IN_SENSOR_MAX);
digitalWrite(OUT_PUMP_1, (pompe1 == 1));
digitalWrite(OUT_PUMP_2, (pompe2 == 1));
digitalWrite(OUT_PUMP_3, (pompe3 == 1));
digitalWrite(OUT_PUMP_4, (pompe4 == 1));
ProcessPrometheus();
ProcessMQTT(&client);
ProcessException();
usleep(100000);
}
try {
client.unsubscribe(TOPIC)->wait();
client.stop_consuming();
client.disconnect()->wait();
} catch(const mqtt::exception &exc){
std::cerr << "Erreur déconnexion MQTT: " << exc.what() << std::endl;
}
std::cout << "Fin du programme" << std::endl;
return 0;
}
/**
* Process
*/
void ProcessInitIO()
{
pinMode(IN_SENSOR_MIN, IO_INPUT | DIGITAL);
pinMode(IN_SENSOR_LOW, IO_INPUT | DIGITAL);
pinMode(IN_SENSOR_HIGH, IO_INPUT | DIGITAL);
pinMode(IN_SENSOR_MAX, IO_INPUT | DIGITAL);
pinMode(IN_TANK_LEVEL, IO_INPUT | ANALOG);
pinMode(IN_FLOW_OUT, IO_INPUT | ANALOG);
pinMode(IN_FLOW_IN, IO_INPUT | ANALOG);
pinMode(IN_FLOW_DIF, IO_INPUT | ANALOG);
pinMode(IN_TANK_MIN, IO_INPUT | ANALOG);
pinMode(IN_TANK_MAX, IO_INPUT | ANALOG);
pinMode(IN_FLOW_CAP, IO_INPUT | ANALOG);
pinMode(IN_FLOW_1, IO_INPUT | ANALOG);
pinMode(IN_FLOW_2, IO_INPUT | ANALOG);
pinMode(IN_FLOW_3, IO_INPUT | ANALOG);
pinMode(IN_FLOW_4, IO_INPUT | ANALOG);
pinMode(OUT_PUMP_1, IO_OUTPUT | DIGITAL);
pinMode(OUT_PUMP_2, IO_OUTPUT | DIGITAL);
pinMode(OUT_PUMP_3, IO_OUTPUT | DIGITAL);
pinMode(OUT_PUMP_4, IO_OUTPUT | DIGITAL);
pinMode(OUT_DISPLAY_MODE, IO_OUTPUT | DIGITAL);
pinMode(OUT_DISPLAY_GRAFCET, IO_OUTPUT | DIGITAL);
pinMode(OUT_LEVEL_MIN, IO_OUTPUT | ANALOG);
pinMode(OUT_LEVEL_LOW, IO_OUTPUT | ANALOG);
pinMode(OUT_LEVEL_HIGH, IO_OUTPUT | ANALOG);
pinMode(OUT_LEVEL_MAX, IO_OUTPUT | ANALOG);
pinMode(OUT_FLOW_PER_PUMP, IO_OUTPUT | ANALOG);
pinMode(OUT_FLOW_OUT_AMPLITUDE, IO_OUTPUT | ANALOG);
pinMode(OUT_BEEP, IO_OUTPUT | DIGITAL);
_digital[OUT_PUMP_1].error = 30;
_digital[OUT_PUMP_1].efficacite = 1.0;
//_digital[OUT_PUMP_1].time = 4294967295; //UINT_MAX
_digital[OUT_PUMP_2].error = 30;
_digital[OUT_PUMP_2].efficacite = 0.72;
//_digital[OUT_PUMP_2].time = 4294967295;
_digital[OUT_PUMP_3].error = 10;
_digital[OUT_PUMP_3].efficacite = 1.0;
//_digital[OUT_PUMP_3].time = 4294967295;
_digital[OUT_PUMP_4].error = 30;
_digital[OUT_PUMP_4].efficacite = 1.0;
//_digital[OUT_PUMP_4].time = 4294967295;
}
void ProcessInitValues()
{
t_start = t_backup = millis();
srand(time(NULL));
_digital[IN_TANK_LEVEL].dvalue = _digital[IN_TANK_MAX].dvalue = _digital[IN_TANK_MIN].dvalue = TankInitalValue;
_digital[OUT_FLOW_PER_PUMP].dvalue = FLOW_PER_PUMP;
_digital[OUT_FLOW_OUT_AMPLITUDE].dvalue = 100.0;
_digital[OUT_LEVEL_MIN].dvalue = LEVEL_MIN;
_digital[OUT_LEVEL_LOW].dvalue = 6;
_digital[OUT_LEVEL_HIGH].dvalue = 7;
_digital[OUT_LEVEL_MAX].dvalue = 9.5;
_digital[IN_FLOW_OUT].dvalue = 100.0;
}
/**
* Fonctionnement des moteurs
*/
double ProcessMoteur(int i)
{
double vitesse = 1.0;
double t = _digital[i].time / 5000.0;
if (_digital[i].ivalue)
{
if (_digital[i].time < 2500)
{
vitesse = 4 * pow(t, 3.0);
}
else if (_digital[i].time < 5000)
{
vitesse = 1.0 - pow(2 - 2 * t, 3) / 2.0;
}
else
{
vitesse = 1.0 + 1.0 / (_digital[i].error * 2.0) - rand() / (double)RAND_MAX / _digital[i].error;
}
}
else
{
if (_digital[i].time < 2500)
{
vitesse = 1 - 4 * pow(t, 3.0);
}
else if (_digital[i].time < 5000)
{
vitesse = pow(2 - 2 * t, 3) / 2.0;
// vitesse = 1 - pow(t, 4.0);
}
else
{
vitesse = 0.0;
}
}
return _digital[OUT_FLOW_PER_PUMP].dvalue * _digital[i].efficacite * vitesse;
}
void ProcessException()
{
if (t_elapsed > 30) {
_digital[OUT_PUMP_1].mode = 0;
} else if (t_elapsed > 15) {
_digital[IN_SENSOR_LOW].mode = 0;
}
}
void Process()
{
// *****
unsigned long t = millis();
t_elapsed = (t - t_start) / 1000.0;
dt = (t - t_backup) / 1000.0;
// ***** FLOW OUT
if (_digital[IN_TANK_LEVEL].dvalue > 1.0)
{
//_digital[IN_FLOW_OUT].dvalue = SimulConsoSinusoidale(t);
_digital[IN_FLOW_OUT].dvalue = SimulConsoBrown(_digital[IN_FLOW_OUT].dvalue);
}
else
{
if (_digital[IN_FLOW_CAP].dvalue == 0.0) {
_digital[IN_FLOW_CAP].dvalue = _digital[IN_FLOW_OUT].dvalue;
}
_digital[IN_FLOW_OUT].dvalue = _digital[IN_FLOW_CAP].dvalue * _digital[IN_TANK_LEVEL].dvalue;
}
// ***** FLOW IN
_digital[IN_FLOW_IN].dvalue = 0;
for (int i = OUT_PUMP_1; i <= OUT_PUMP_4; i++)
{
_digital[i - 4].dvalue = ProcessMoteur(i);
_digital[IN_FLOW_IN].dvalue += _digital[i - 4].dvalue;
}
_digital[IN_FLOW_DIF].dvalue = _digital[IN_FLOW_IN].dvalue - _digital[IN_FLOW_OUT].dvalue;
// ***** TANK LEVEL
_digital[IN_TANK_LEVEL].dvalue += (_digital[IN_FLOW_IN].dvalue - _digital[IN_FLOW_OUT].dvalue) / 1000.0 * dt;
if (_digital[IN_TANK_LEVEL].dvalue > 10.0) {
_digital[IN_TANK_LEVEL].dvalue = 10.0;
}
if (_digital[IN_TANK_LEVEL].dvalue > _digital[IN_TANK_MAX].dvalue) {
_digital[IN_TANK_MAX].dvalue = _digital[IN_TANK_LEVEL].dvalue;
}
if (_digital[IN_TANK_LEVEL].dvalue < _digital[IN_TANK_MIN].dvalue) {
_digital[IN_TANK_MIN].dvalue = _digital[IN_TANK_LEVEL].dvalue;
}
// **** SENSOR
int test;
test = (_digital[IN_TANK_LEVEL].dvalue > _digital[OUT_LEVEL_MIN].dvalue);
if (_digital[IN_SENSOR_MIN].ivalue != test)
{
if (test == 0)
{
_digital[IN_SENSOR_MIN].nb += 1;
}
_digital[IN_SENSOR_MIN].ivalue = test;
}
test = _digital[IN_TANK_LEVEL].dvalue > _digital[OUT_LEVEL_LOW].dvalue && _digital[IN_SENSOR_LOW].mode & 0x01;
if (_digital[IN_SENSOR_LOW].ivalue != test)
{
if (test == 0)
{
_digital[IN_SENSOR_LOW].nb += 1;
}
_digital[IN_SENSOR_LOW].ivalue = test;
}
test = _digital[IN_TANK_LEVEL].dvalue > _digital[OUT_LEVEL_MAX].dvalue;
if (_digital[IN_SENSOR_MAX].ivalue != test)
{
if (test == 1)
{
_digital[IN_SENSOR_MAX].nb += 1;
}
_digital[IN_SENSOR_MAX].ivalue = test;
}
test = _digital[IN_TANK_LEVEL].dvalue > _digital[OUT_LEVEL_HIGH].dvalue;
if (_digital[IN_SENSOR_HIGH].ivalue != test)
{
if (test == 1)
{
_digital[IN_SENSOR_HIGH].nb += 1;
}
_digital[IN_SENSOR_HIGH].ivalue = test;
}
t_backup = t;
}
double SimulConsoSinusoidale(long t)
{
double alea = ((long)(t / 100.0) % 600) * 3 / 1800.0 * PI;
//mvprintw(18, 0, "%ld %f", (long)(t / 100.0), alea);
return 100 + cos(alea) * cos(alea) * _digital[OUT_FLOW_OUT_AMPLITUDE].dvalue;
}
// dt : Intervalle de temps
double SimulConsoBrown(double valeur_precedente)
{
float mu = 0.01 * -((((int)t_elapsed / 30) % 2) * 2 - 1); // Taux de croissance (1%)
float sigma = 0.05; // Volatilité (5%)
// Nombre aléatoire compris dans [-1 +1]
float rand_std_normal = ((double)rand() / RAND_MAX) * 2.0 - 1.0;
// Calcule la variation logarithmique pour cette étape
float drift = (mu - 0.5f * sigma * sigma) * dt;
float diffusion = sigma * sqrt(dt) * rand_std_normal;
return valeur_precedente * exp(drift + diffusion);
}
/**
* Affichage dans la console
*/
/**
* Prometheus
*/
void InitPrometheus()
{
}
void ProcessPrometheus()
{
}
void ProcessMQTT(mqtt::async_client* client)
{
json obj = {
{"entree", _digital[IN_FLOW_IN].dvalue},
{"sortie", _digital[IN_FLOW_OUT].dvalue},
{"tank", _digital[IN_TANK_LEVEL].dvalue},
{"pompe1", _digital[IN_FLOW_1].dvalue},
{"pompe2", _digital[IN_FLOW_2].dvalue},
{"pompe3", _digital[IN_FLOW_3].dvalue},
{"pompe4", _digital[IN_FLOW_4].dvalue},
{"min", _digital[IN_SENSOR_MIN].ivalue},
{"low", _digital[IN_SENSOR_LOW].ivalue},
{"high", _digital[IN_SENSOR_HIGH].ivalue},
{"max", _digital[IN_SENSOR_MAX].ivalue},
};
std::string payload = obj.dump();
auto msg = mqtt::make_message("geii/telemetry", payload);
msg->set_qos(1);
client->publish(msg);
}