905 lines
22 KiB
C++
905 lines
22 KiB
C++
#include <unistd.h>
|
||
#include <ncurses.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 <iostream>
|
||
|
||
#include <thread>
|
||
#include <atomic>
|
||
#include <queue>
|
||
#include <mutex>
|
||
#include <condition_variable>
|
||
#include <csignal>
|
||
#include <chrono>
|
||
#include <cstring>
|
||
#undef timeout
|
||
#include "mqtt/async_client.h"
|
||
|
||
using namespace std::chrono_literals;
|
||
|
||
// Constantes de fonctionnement
|
||
#define LEVEL_MIN 2
|
||
#define FLOW_PER_PUMP 150
|
||
|
||
WINDOW *window;
|
||
|
||
int etape = 10; // É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;
|
||
|
||
TemporisationRetardMontee tempo1(1500);
|
||
TemporisationRetardMontee tempo2(3000);
|
||
TemporisationRetardMontee tempo3(4000);
|
||
TemporisationRetardMontee tempo4(6000);
|
||
|
||
// Prometheus
|
||
// ************************************************************
|
||
using namespace prometheus;
|
||
|
||
std::shared_ptr<Registry> registry;
|
||
Gauge *debit_entree = nullptr;
|
||
Gauge *debit_sortie = nullptr;
|
||
|
||
Gauge *debit_p1 = nullptr;
|
||
Gauge *debit_p2 = nullptr;
|
||
Gauge *debit_p3 = nullptr;
|
||
Gauge *debit_p4 = nullptr;
|
||
Gauge *tank_gauge = nullptr;
|
||
|
||
Counter *volume_p1 = nullptr;
|
||
Counter *volume_p2 = nullptr;
|
||
Counter *volume_p3 = nullptr;
|
||
Counter *volume_p4 = nullptr;
|
||
|
||
Histogram::BucketBoundaries buckets = {
|
||
2, 5, 6, 7, 8, 9, 9.5
|
||
};
|
||
|
||
Histogram *tank_histogram = nullptr;
|
||
// ************************************************************
|
||
|
||
int main()
|
||
{
|
||
/* Initialisation */
|
||
ConsoleInit();
|
||
AffichageWindow();
|
||
|
||
InitPrometheus();
|
||
ProcessInitKeyboard();
|
||
ProcessInitIO();
|
||
ProcessInitValues();
|
||
|
||
while (1)
|
||
{
|
||
int ch = getch(); // Lit l'entrée du clavier sans bloquer
|
||
|
||
// **** Break loop if escape key (27) is pressed
|
||
if (ch == 27 || _digital[OUT_END].ivalue) {
|
||
break;
|
||
}
|
||
|
||
// **** Beep
|
||
if (_digital[OUT_BEEP].ivalue)
|
||
{
|
||
beep();
|
||
_digital[OUT_BEEP].ivalue = false;
|
||
}
|
||
|
||
Process();
|
||
|
||
LireClavier(ch);
|
||
LireEntree();
|
||
EvolutionGrafcet();
|
||
Actions();
|
||
|
||
ProcessPrometheus();
|
||
ProcessMQTT(&client);
|
||
ProcessException();
|
||
|
||
usleep(100000);
|
||
}
|
||
|
||
endwin(); // Termine ncurses et rétablit le terminal
|
||
|
||
/* Arrêt */
|
||
try {
|
||
client.unsubscribe(TOPIC)->wait();
|
||
client.stop_consuming();
|
||
client.disconnect()->wait();
|
||
} catch(const mqtt::exception &exc){
|
||
std::cerr << "Erreur déconnexion MQTT: " << exc.what() << "\n";
|
||
}
|
||
|
||
//th_machine.join();
|
||
|
||
std::cout << "[MAIN] Terminé\n";
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* Programme
|
||
*/
|
||
void LireEntree()
|
||
{
|
||
int input;
|
||
input = digitalRead(IN_KEYBOARD_A);
|
||
bp_mode_fm = (input > bp_mode);
|
||
bp_mode = input;
|
||
|
||
sensor_min = digitalRead(IN_SENSOR_MIN);
|
||
sensor_low = digitalRead(IN_SENSOR_LOW);
|
||
sensor_high = digitalRead(IN_SENSOR_HIGH);
|
||
sensor_max = digitalRead(IN_SENSOR_MAX);
|
||
}
|
||
|
||
void EvolutionGrafcet()
|
||
{
|
||
int etape_futur = etape;
|
||
|
||
if (etape < 10 && bp_mode_fm)
|
||
{
|
||
etape_futur = 10;
|
||
pompe1 = pompe2 = pompe3 = pompe4 = 0;
|
||
}
|
||
|
||
if (etape <= 2 && _digital[IN_KEYBOARD_1].raising)
|
||
{
|
||
pompe1 = !pompe1;
|
||
}
|
||
|
||
if (etape <= 2 && _digital[IN_KEYBOARD_2].raising)
|
||
{
|
||
pompe2 = !pompe2;
|
||
}
|
||
|
||
if (etape <= 2 && _digital[IN_KEYBOARD_3].raising)
|
||
{
|
||
pompe3 = !pompe3;
|
||
}
|
||
|
||
if (etape <= 2 && _digital[IN_KEYBOARD_4].raising)
|
||
{
|
||
pompe4 = !pompe4;
|
||
}
|
||
|
||
if (etape == 0 && !sensor_min)
|
||
{
|
||
etape_futur = 1;
|
||
}
|
||
|
||
if (etape == 1)
|
||
{
|
||
etape_futur = 2;
|
||
}
|
||
|
||
if (etape == 2 && sensor_min)
|
||
{
|
||
etape_futur = 0;
|
||
}
|
||
|
||
if (etape >= 10 && bp_mode_fm)
|
||
{
|
||
etape_futur = 0;
|
||
pompe1 = pompe2 = pompe3 = pompe4 = 0;
|
||
}
|
||
|
||
if (sensor_max)
|
||
{
|
||
pompe1 = pompe2 = pompe3 = pompe4 = 0;
|
||
}
|
||
|
||
/* Automatique */
|
||
if (etape == 10 && !sensor_low && !sensor_high)
|
||
{
|
||
etape_futur = 11;
|
||
}
|
||
|
||
if (etape == 11 && sensor_high)
|
||
{
|
||
etape_futur = 10;
|
||
}
|
||
|
||
if (etape == 11 && tempo1.getSortie())
|
||
{
|
||
etape_futur = 12;
|
||
}
|
||
|
||
if (etape == 12 && sensor_high)
|
||
{
|
||
etape_futur = 13;
|
||
}
|
||
|
||
if (etape == 12 && tempo2.getSortie())
|
||
{
|
||
etape_futur = 14; // Allumer le moteur 2
|
||
}
|
||
|
||
if (etape == 13 && tempo1.getSortie())
|
||
{
|
||
etape_futur = 10;
|
||
}
|
||
|
||
if (etape == 13 && !sensor_low && !sensor_high)
|
||
{
|
||
etape_futur = 12;
|
||
}
|
||
|
||
if (etape == 14 && sensor_high)
|
||
{
|
||
etape_futur = 15;
|
||
}
|
||
|
||
if (etape == 14 && tempo3.getSortie())
|
||
{
|
||
etape_futur = 16;
|
||
}
|
||
|
||
if (etape == 15 && tempo1.getSortie())
|
||
{
|
||
etape_futur = 13;
|
||
}
|
||
|
||
if (etape == 15 && !sensor_low && !sensor_high)
|
||
{
|
||
etape_futur = 14;
|
||
}
|
||
|
||
if (etape == 16 && sensor_high)
|
||
{
|
||
etape_futur = 17;
|
||
}
|
||
|
||
if (etape == 16 && tempo4.getSortie())
|
||
{
|
||
etape_futur = 18;
|
||
}
|
||
|
||
if (etape == 17 && tempo1.getSortie())
|
||
{
|
||
etape_futur = 15;
|
||
}
|
||
|
||
if (etape == 17 && !sensor_low && !sensor_high)
|
||
{
|
||
etape_futur = 16;
|
||
}
|
||
|
||
if (etape == 18 && sensor_high)
|
||
{
|
||
etape_futur = 19;
|
||
}
|
||
|
||
if (etape == 19 && tempo1.getSortie())
|
||
{
|
||
etape_futur = 17;
|
||
}
|
||
|
||
if (etape == 19 && !sensor_low && !sensor_high)
|
||
{
|
||
etape_futur = 18;
|
||
}
|
||
|
||
/* Fin de mode automatique */
|
||
|
||
if (etape != etape_futur)
|
||
{
|
||
etape = etape_futur;
|
||
}
|
||
}
|
||
|
||
void Actions()
|
||
{
|
||
digitalWrite(OUT_DISPLAY_GRAFCET, etape);
|
||
digitalWrite(OUT_DISPLAY_MODE, etape >= 10);
|
||
digitalWrite(OUT_PUMP_1, !sensor_max && (pompe1 == 1 || etape >= 12));
|
||
digitalWrite(OUT_PUMP_2, !sensor_max && (pompe2 == 1 || etape >= 14));
|
||
digitalWrite(OUT_PUMP_3, !sensor_max && (pompe3 == 1 || etape >= 16));
|
||
digitalWrite(OUT_PUMP_4, !sensor_max && (pompe4 == 1 || etape >= 18));
|
||
// digitalWrite(OUT_BEEP, etape == 1);
|
||
|
||
if (etape >= 11)
|
||
{
|
||
tempo1.activation();
|
||
tempo2.activation();
|
||
tempo3.activation();
|
||
tempo4.activation();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Process
|
||
*/
|
||
void ProcessInitKeyboard()
|
||
{
|
||
_keyboard[0].vKey = '1';
|
||
_keyboard[0].input = IN_KEYBOARD_1;
|
||
|
||
_keyboard[1].vKey = '2';
|
||
_keyboard[1].input = IN_KEYBOARD_2;
|
||
|
||
_keyboard[2].vKey = '3';
|
||
_keyboard[2].input = IN_KEYBOARD_3;
|
||
|
||
_keyboard[3].vKey = '4';
|
||
_keyboard[3].input = IN_KEYBOARD_4;
|
||
|
||
_keyboard[4].vKey = 'a';
|
||
_keyboard[4].input = IN_KEYBOARD_A;
|
||
|
||
_keyboard[5].vKey = 'x';
|
||
_keyboard[5].input = IN_KEYBOARD_X;
|
||
|
||
_keyboard[6].vKey = '6';
|
||
_keyboard[6].input = IN_KEYBOARD_7;
|
||
|
||
_keyboard[7].vKey = '7';
|
||
_keyboard[7].input = IN_KEYBOARD_8;
|
||
|
||
_keyboard[8].vKey = '8';
|
||
_keyboard[8].input = IN_KEYBOARD_9;
|
||
|
||
_keyboard[9].vKey = '9';
|
||
_keyboard[9].input = IN_KEYBOARD_0;
|
||
|
||
for (int i = 0; i < NB_KEYBOARD; i++)
|
||
{
|
||
_digital[_keyboard[i].input].mode = OP_DIGITAL_READ;
|
||
}
|
||
}
|
||
|
||
void ProcessInitIO()
|
||
{
|
||
pinMode(IN_KEYBOARD_1, IO_INPUT | DIGITAL);
|
||
pinMode(IN_KEYBOARD_2, IO_INPUT | DIGITAL);
|
||
pinMode(IN_KEYBOARD_3, IO_INPUT | DIGITAL);
|
||
pinMode(IN_KEYBOARD_4, IO_INPUT | DIGITAL);
|
||
pinMode(IN_KEYBOARD_A, IO_INPUT | DIGITAL);
|
||
|
||
pinMode(IN_KEYBOARD_7, IO_INPUT | DIGITAL);
|
||
pinMode(IN_KEYBOARD_8, IO_INPUT | DIGITAL);
|
||
pinMode(IN_KEYBOARD_9, IO_INPUT | DIGITAL);
|
||
pinMode(IN_KEYBOARD_0, IO_INPUT | DIGITAL);
|
||
pinMode(IN_KEYBOARD_X, IO_INPUT | DIGITAL);
|
||
|
||
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 > 60) {
|
||
_digital[OUT_PUMP_1].mode = 0;
|
||
digitalWrite(OUT_PUMP_1, 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;
|
||
}
|
||
|
||
// **** KEYBOARD
|
||
if (_digital[IN_KEYBOARD_X].raising)
|
||
{
|
||
_digital[IN_SENSOR_LOW].mode = _digital[IN_SENSOR_LOW].mode ^ 0x01;
|
||
}
|
||
|
||
for (int i = IN_KEYBOARD_7; i <= IN_KEYBOARD_0; i++)
|
||
{
|
||
if (_digital[i].raising)
|
||
{
|
||
unsigned char p = i + (OUT_PUMP_1 - IN_KEYBOARD_7);
|
||
_digital[p].mode ^= 0x01;
|
||
if (!(_digital[p].mode & 0x01)) {
|
||
digitalWrite(p, 0);
|
||
}
|
||
}
|
||
}
|
||
|
||
// **** 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;
|
||
}
|
||
|
||
Affichage();
|
||
|
||
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%)
|
||
|
||
mvprintw(8, 40, "(µ %.1f %% ; σ %.1f %%) ", mu * 100, sigma * 100);
|
||
|
||
// 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
|
||
*/
|
||
|
||
/**
|
||
* Initialisation, affichage des parties statiques
|
||
*/
|
||
void AffichageWindow()
|
||
{
|
||
window = subwin(stdscr, 19, 62, 0, 0);
|
||
|
||
box(window, 0, 0);
|
||
|
||
// Titre
|
||
mvwprintw(window, 1, 2, "Château d'eau (11/2024)");
|
||
|
||
// I/O
|
||
// Ligne du haut
|
||
mvwaddch(window, 2, 0, ACS_LTEE);
|
||
mvwhline(window, 2, 1, 0, 60);
|
||
mvwaddch(window, 2, 61, ACS_RTEE);
|
||
|
||
// Ligne du bas
|
||
mvwaddch(window, 7, 0, ACS_LTEE);
|
||
mvwhline(window, 7, 1, 0, 60);
|
||
mvwaddch(window, 7, 61, ACS_RTEE);
|
||
|
||
// Séparation verticale
|
||
mvwaddch(window, 2, 18, ACS_TTEE);
|
||
mvwvline(window, 3, 18, 0, 4);
|
||
mvwaddch(window, 7, 18, ACS_BTEE);
|
||
|
||
// Input : Boutons poussoirs
|
||
mvwprintw(window, 3, 2, "BP 1");
|
||
mvwprintw(window, 4, 2, "BP 2");
|
||
mvwprintw(window, 5, 2, "BP 3");
|
||
mvwprintw(window, 6, 2, "BP 4");
|
||
|
||
// Output : Moteurs pompes
|
||
mvwprintw(window, 3, 20, "Pompe 1");
|
||
mvwprintw(window, 4, 20, "Pompe 2");
|
||
mvwprintw(window, 5, 20, "Pompe 3");
|
||
mvwprintw(window, 6, 20, "Pompe 4");
|
||
|
||
// Mesures
|
||
mvwprintw(window, 8, 2, "Debit en sortie");
|
||
mvwprintw(window, 9, 2, "Debit en entrée");
|
||
mvwprintw(window, 10, 2, "Volume dans le réservoir");
|
||
|
||
// Graphe
|
||
|
||
// Ligne du haut
|
||
mvwaddch(window, 11, 0, ACS_LTEE);
|
||
mvwhline(window, 11, 1, 0, 60);
|
||
mvwaddch(window, 11, 61, ACS_RTEE);
|
||
|
||
// Ligne du bas
|
||
mvwaddch(window, 13, 0, ACS_LTEE);
|
||
mvwhline(window, 13, 1, 0, 60);
|
||
mvwaddch(window, 13, 61, ACS_RTEE);
|
||
|
||
// Graduations
|
||
for (int i = 1 ; i < 11; i++) {
|
||
mvwaddch(window, 11, 6 * i, ACS_TTEE);
|
||
mvwaddch(window, 13, 6 * i, ACS_BTEE);
|
||
}
|
||
|
||
mvwaddch(window, 13, 2 * 6, ACS_PLUS);
|
||
mvwaddch(window, 13, 6 * 6, ACS_PLUS);
|
||
mvwaddch(window, 13, 7 * 6, ACS_PLUS);
|
||
mvwaddch(window, 13, 57, ACS_TTEE); //9.5 x 6
|
||
|
||
mvwvline(window, 12, 6 * 10, 0, 1);
|
||
|
||
// Légende
|
||
mvwaddch(window, 16, 0, ACS_LTEE);
|
||
mvwhline(window, 16, 1, 0, 60);
|
||
mvwaddch(window, 16, 61, ACS_RTEE);
|
||
|
||
mvwprintw(window, 14, 2 * 6 - 1, "min");
|
||
mvwprintw(window, 14, 6 * 6 - 2, "low");
|
||
mvwprintw(window, 14, 7 * 6, "high");
|
||
mvwprintw(window, 14, 56, "max");
|
||
|
||
// Informations
|
||
mvwprintw(window, 17, 2, "Mode");
|
||
|
||
mvwaddch(window, 16, 18, ACS_TTEE);
|
||
mvwaddch(window, 18, 18, ACS_BTEE);
|
||
mvwvline(window, 17, 18, 0, 1);
|
||
mvwprintw(window, 17, 20, "Grafcet");
|
||
|
||
mvwaddch(window, 16, 31, ACS_TTEE);
|
||
mvwaddch(window, 18, 31, ACS_BTEE);
|
||
mvwvline(window, 17, 31, 0, 1);
|
||
mvwprintw(window, 17, 33, "min");
|
||
|
||
mvwprintw(window, 17, 46, "max");
|
||
|
||
wrefresh(window);
|
||
}
|
||
|
||
void Affichage()
|
||
{
|
||
mvwprintw(window, 1, 50, "%5.1f s", t_elapsed);
|
||
|
||
for (int i = OUT_PUMP_1; i <= OUT_PUMP_4; i++)
|
||
{
|
||
mvwprintw(window, (short)(i - OUT_PUMP_1 + 3), 30, " %c %5.1f s %dx", _digital[i].mode & 0x01 ? _digital[i].ivalue ? 'M' : '.' : 'X', _digital[i].duration, _digital[i].nb);
|
||
}
|
||
|
||
for (int i = IN_KEYBOARD_1; i <= IN_KEYBOARD_4; i++)
|
||
{
|
||
mvwprintw(window, (short)(i + 3), 10, _digital[_keyboard[i].input].ivalue ? "1" : "0");
|
||
}
|
||
|
||
mvwprintw(window, 8, 28, "%3d l/s", (int)_digital[IN_FLOW_OUT].dvalue);
|
||
mvwprintw(window, 9, 28, "%3d l/s", (int)_digital[IN_FLOW_IN].dvalue);
|
||
mvwprintw(window, 10, 29, "%5.2lf m3 (%+d l/s)", _digital[IN_TANK_LEVEL].dvalue, (int)_digital[IN_FLOW_DIF].dvalue);
|
||
|
||
AffichageGraphe(12, 1, _digital[IN_TANK_LEVEL].dvalue * 6);
|
||
|
||
/*
|
||
if (_digital[IN_SENSOR_LOW].mode & 0x01)
|
||
{
|
||
mvwprintw(window, 15, 1, " (%dx) (%dx) (%dx) (%dx)", _digital[IN_SENSOR_MIN].nb, _digital[IN_SENSOR_LOW].nb, _digital[IN_SENSOR_HIGH].nb, _digital[IN_SENSOR_MAX].nb);
|
||
}
|
||
else
|
||
{
|
||
mvwprintw(window, 15, 1, " (%dx) X (%dx) (%dx) (%dx)", _digital[IN_SENSOR_MIN].nb, _digital[IN_SENSOR_LOW].nb, _digital[IN_SENSOR_HIGH].nb, _digital[IN_SENSOR_MAX].nb);
|
||
}
|
||
*/
|
||
mvwprintw(window, 14, 15, "%d", _digital[IN_SENSOR_MIN].ivalue);
|
||
mvwprintw(window, 14, 38, "%d", _digital[IN_SENSOR_LOW].ivalue);
|
||
mvwprintw(window, 14, 47, "%d", _digital[IN_SENSOR_HIGH].ivalue);
|
||
mvwprintw(window, 14, 60, "%d", _digital[IN_SENSOR_MAX].ivalue);
|
||
|
||
mvwprintw(window, 15, 11, "(%dx) %s", _digital[IN_SENSOR_MIN].nb, _digital[IN_SENSOR_MIN].mode & 0x01 ? " " : "D");
|
||
mvwprintw(window, 15, 34, "(%dx) %s", _digital[IN_SENSOR_LOW].nb, _digital[IN_SENSOR_LOW].mode & 0x01 ? " " : "D");
|
||
mvwprintw(window, 15, 42, "(%dx) %s", _digital[IN_SENSOR_HIGH].nb, _digital[IN_SENSOR_HIGH].mode & 0x01 ? " " : "D");
|
||
mvwprintw(window, 15, 56, "(%dx)", _digital[IN_SENSOR_MAX].nb);
|
||
|
||
// Informations complémentaires
|
||
mvwprintw(window, 17, 2, _digital[OUT_DISPLAY_MODE].ivalue ? "Automatique" : "Manuel ");
|
||
mvwprintw(window, 17, 28, "%d", _digital[OUT_DISPLAY_GRAFCET].ivalue);
|
||
mvwprintw(window, 17, 38, "%4.2lf m3", _digital[IN_TANK_MIN].dvalue);
|
||
mvwprintw(window, 17, 51, "%4.2lf m3", _digital[IN_TANK_MAX].dvalue);
|
||
|
||
wrefresh(window);
|
||
}
|
||
|
||
void AffichageGraphe(int y, int x, double value)
|
||
{
|
||
int entier = (int)(value);
|
||
int i;
|
||
|
||
for (i = 0; i < entier; i++)
|
||
{
|
||
mvwaddwstr(window, y, x + i, L"█"); // U+2588
|
||
}
|
||
|
||
int frac = (int)((value - entier) * 4);
|
||
if (frac > 3) // 0.75 -> 0.99
|
||
{
|
||
mvwaddwstr(window, y, x + i, L"▊"); // U+258A
|
||
entier += 1;
|
||
}
|
||
if (frac > 2) // 0.5 -> 0.99
|
||
{
|
||
mvwaddwstr(window, y, x + i, L"▌"); // U+258C
|
||
entier += 1;
|
||
}
|
||
else if (frac > 1) // 0.25 -> 0.49
|
||
{
|
||
mvwaddwstr(window, y, x + i, L"▎"); //U+258E
|
||
entier += 1;
|
||
}
|
||
|
||
for (int i = entier; i < 59; i++)
|
||
{
|
||
mvwprintw(window, y, x + i, " ");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Prometheus
|
||
*/
|
||
void InitPrometheus()
|
||
{
|
||
static Exposer exposer{"0.0.0.0:8099"};
|
||
|
||
// Le registre central
|
||
registry = std::make_shared<Registry>();
|
||
|
||
exposer.RegisterCollectable(registry);
|
||
|
||
auto& gauge_volume = BuildGauge()
|
||
.Name("geii_volume")
|
||
.Help("Volume en m3")
|
||
.Register(*registry);
|
||
|
||
tank_gauge = &gauge_volume.Add({});
|
||
|
||
auto& gauge_debit = BuildGauge()
|
||
.Name("geii_debit")
|
||
.Help("Débit en l/s")
|
||
.Register(*registry);
|
||
|
||
debit_entree = &gauge_debit.Add({{"numero", "entree"}});
|
||
debit_sortie = &gauge_debit.Add({{"numero", "sortie"}});
|
||
|
||
debit_p1 = &gauge_debit.Add({{"numero", "1"}});
|
||
debit_p2 = &gauge_debit.Add({{"numero", "2"}});
|
||
debit_p3 = &gauge_debit.Add({{"numero", "3"}});
|
||
debit_p4 = &gauge_debit.Add({{"numero", "4"}});
|
||
|
||
auto& counter_debit = BuildCounter()
|
||
.Name("geii_litre")
|
||
.Help("Volume en l")
|
||
.Register(*registry);
|
||
|
||
volume_p1 = &counter_debit.Add({{"numero", "1"}});
|
||
volume_p2 = &counter_debit.Add({{"numero", "2"}});
|
||
volume_p3 = &counter_debit.Add({{"numero", "3"}});
|
||
volume_p4 = &counter_debit.Add({{"numero", "4"}});
|
||
|
||
auto& hist_volume = BuildHistogram()
|
||
.Name("geii_tank")
|
||
.Help("volume du reservoir en m3")
|
||
.Register(*registry);
|
||
|
||
tank_histogram = &hist_volume.Add({}, buckets);
|
||
}
|
||
|
||
void ProcessMQTT(mqtt::async_client* client)
|
||
{
|
||
std::string payload = R"({
|
||
"order": "STATUS",
|
||
"speed": 120,
|
||
"temperature": 36.1
|
||
})";
|
||
|
||
auto msg = mqtt::make_message("geii/telemetry", payload);
|
||
msg->set_qos(1);
|
||
client->publish(msg);
|
||
}
|
||
|
||
void ProcessPrometheus()
|
||
{
|
||
tank_gauge->Set(_digital[IN_TANK_LEVEL].dvalue);
|
||
tank_histogram->Observe(_digital[IN_TANK_LEVEL].dvalue);
|
||
|
||
debit_entree->Set(_digital[IN_FLOW_IN].dvalue);
|
||
debit_sortie->Set(_digital[IN_FLOW_OUT].dvalue);
|
||
|
||
debit_p1->Set(_digital[IN_FLOW_1].dvalue);
|
||
debit_p2->Set(_digital[IN_FLOW_2].dvalue);
|
||
debit_p3->Set(_digital[IN_FLOW_3].dvalue);
|
||
debit_p4->Set(_digital[IN_FLOW_4].dvalue);
|
||
|
||
volume_p1->Increment(_digital[IN_FLOW_1].dvalue * dt);
|
||
volume_p2->Increment(_digital[IN_FLOW_2].dvalue * dt);
|
||
volume_p3->Increment(_digital[IN_FLOW_3].dvalue * dt);
|
||
volume_p4->Increment(_digital[IN_FLOW_4].dvalue * dt);
|
||
}
|