Compare commits

9 Commits
dev ... main

Author SHA1 Message Date
2e29769d51 Revert flow 2025-12-10 13:44:13 +01:00
a0f5055edf Prometheus 2025-12-10 11:31:20 +01:00
e5007dd657 flow 2025-12-10 10:31:47 +01:00
1b5bac2a73 Ajustement 2025-12-10 10:28:21 +01:00
6e9b0630bc flows au bon endroit 2025-12-10 09:29:59 +01:00
f0b1cf97e2 flows 2025-12-10 07:47:04 +01:00
99c7ccac16 Simplification 2025-12-09 22:13:34 +01:00
430eeca9eb grafcet manuel 2025-12-08 12:44:55 +01:00
50a3a765d2 Lib prometheus et paho.mqtt 2025-12-07 20:40:40 +01:00
10 changed files with 2108 additions and 543 deletions

View File

@@ -1,5 +1,14 @@
FROM debian:stable-slim FROM debian:stable-slim
ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=1000
RUN groupadd --gid $USER_GID $USERNAME \
&& useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \
&& apt-get update && apt-get install -y sudo \
&& echo "$USERNAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
RUN RUN set -eux; \ RUN RUN set -eux; \
apt-get update; \ apt-get update; \
apt-get install -y \ apt-get install -y \
@@ -15,20 +24,32 @@ RUN set -eux; \
libmicrohttpd-dev \ libmicrohttpd-dev \
libcurl4-openssl-dev \ libcurl4-openssl-dev \
zlib1g-dev \ zlib1g-dev \
prometheus-cpp-dev prometheus-cpp-dev \
nlohmann-json3-dev
RUN set -eux; \ RUN set -eux; \
apt-get update; \ apt-get update; \
apt-get install -y \ apt-get install -y \
libpaho-mqtt-dev libpaho-mqtt-dev
RUN set -eux; \ RUN set -eux; \
git clone https://github.com/eclipse/paho.mqtt.cpp.git; \
cd paho.mqtt.cpp; \
git submodule init; \
git submodule update; \
mkdir build && cd build; \
cmake -DPAHO_WITH_MQTT_C=ON ..; \
cmake --build . --target install; \
ldconfig;
RUN set -eux; \
apt-get update; \ apt-get update; \
apt-get install -y \ apt-get install -y \
librabbitmq4 \ librabbitmq4 \
librabbitmq-dev;\ librabbitmq-dev;\
apt-get clean apt-get clean
WORKDIR /root USER $USERNAME
WORKDIR /workspace
CMD ["sleep infinity"] CMD ["sleep infinity"]

54
.devcontainer/compose.yml Normal file
View File

@@ -0,0 +1,54 @@
services:
dev:
build:
context: .
dockerfile: Dockerfile
volumes:
- ..:/workspace:cached
command: sleep infinity
networks:
- dev_net
nodered:
build: ./nodered
container_name: nodered
ports:
- "1880:1880"
networks:
- dev_net
environment:
TZ: Europe/Paris
volumes:
- nodered:/data
rabbitmq:
image: rabbitmq:4.1.4-management
container_name: rabbitmq
environment:
RABBITMQ_DEFAULT_USER: "admin"
RABBITMQ_DEFAULT_PASS: "geii2025"
# Activation MQTT sur le port 1883
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS: >
-rabbitmq_mqtt tcp_listeners [1883]
ports:
- "5672:5672" # AMQP
- "1883:1883" # MQTT
- "15672:15672" # RabbitMQ Manager
networks:
- dev_net
volumes:
- rabbitmq:/var/lib/rabbitmq
# Activation des plugins + démarrage serveur
command: >
sh -c "rabbitmq-plugins enable --offline rabbitmq_mqtt rabbitmq_management &&
rabbitmq-server"
networks:
dev_net:
volumes:
nodered:
rabbitmq:

View File

@@ -1,13 +1,11 @@
{ {
"name": "Developpement C", "name": "Developpement C",
"build": { "dockerComposeFile": [
"dockerfile": "Dockerfile" "compose.yml"
},
"runArgs": [
"--label", "prometheus=true",
"--network=tp_net",
"--name=pompes"
], ],
"service": "dev",
"workspaceFolder": "/workspace",
"postStartCommand": "cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
"customizations": { "customizations": {
"vscode": { "vscode": {
"settings": { "settings": {
@@ -17,8 +15,7 @@
"workbench.remoteIndicator.showExtensionRecommendations": false "workbench.remoteIndicator.showExtensionRecommendations": false
}, },
"extensions": [ "extensions": [
"ms-vscode.cpptools", "ms-vscode.cpptools"
"ms-vscode.makefile-tools"
] ]
} }
} }

View File

@@ -0,0 +1,7 @@
FROM nodered/node-red:4.1
# Installer FlowFuse Dashboard
RUN npm install --unsafe-perm @flowfuse/node-red-dashboard
# Copier votre flux si nécessaire
COPY flows.json /data/flows.json

File diff suppressed because it is too large Load Diff

5
.vscode/launch.json vendored
View File

@@ -2,10 +2,10 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "Debug geii_exporter", "name": "Debug pompes",
"type": "cppdbg", "type": "cppdbg",
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/build/geii_exporter", "program": "${workspaceFolder}/build/pompes",
"args": [], "args": [],
"stopAtEntry": false, "stopAtEntry": false,
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
@@ -23,4 +23,3 @@
} }
] ]
} }

View File

@@ -409,19 +409,3 @@ void analogWrite(unsigned int p, double value)
_digital[p].dvalue = value; _digital[p].dvalue = value;
} }
/* ********************************************************
* Console *
* *
******************************************************** */
void ConsoleInit()
{
setlocale(LC_ALL, ""); // Activer le support des caractères Unicode
setlocale(LC_NUMERIC, "C");
initscr(); // Initialise ncurses
raw(); // Mode brut, sans besoin de validation par Entrée
keypad(stdscr, TRUE); // Active les touches spéciales comme ESC
nodelay(stdscr, TRUE); // Mode non-bloquant pour getch()
noecho(); // Ne pas afficher les touches appuyées
curs_set(0); // Masquer le curseur
}

View File

@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.15) cmake_minimum_required(VERSION 3.15)
project(geii_exporter LANGUAGES CXX) project(pompes LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -9,7 +9,7 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic")
# Executable principal # Executable principal
add_executable(geii_exporter add_executable(pompes
main.cpp main.cpp
) )
@@ -34,6 +34,7 @@ find_library(NCURSESW_LIB ncursesw REQUIRED)
find_library(RABBITMQ_LIB rabbitmq REQUIRED) find_library(RABBITMQ_LIB rabbitmq REQUIRED)
# Paho MQTT C client # Paho MQTT C client
find_library(PAHO_MQTTPP3_LIB paho-mqttpp3 REQUIRED)
find_library(PAHO_MQTT3C_LIB paho-mqtt3c REQUIRED) find_library(PAHO_MQTT3C_LIB paho-mqtt3c REQUIRED)
# ------------------------------- # -------------------------------
@@ -44,7 +45,7 @@ find_package(prometheus-cpp REQUIRED)
# ------------------------------- # -------------------------------
# Lien des bibliothèques # Lien des bibliothèques
# ------------------------------- # -------------------------------
target_link_libraries(geii_exporter target_link_libraries(pompes
prometheus-cpp::core prometheus-cpp::core
prometheus-cpp::pull prometheus-cpp::pull
${CURL_LIBRARIES} ${CURL_LIBRARIES}
@@ -53,5 +54,6 @@ target_link_libraries(geii_exporter
${Z_LIB} ${Z_LIB}
${NCURSESW_LIB} ${NCURSESW_LIB}
${RABBITMQ_LIB} ${RABBITMQ_LIB}
${PAHO_MQTT3C_LIB} ${PAHO_MQTT3C_LIB} # dépendance C
${PAHO_MQTTPP3_LIB} # lib C++
) )

690
main.cpp
View File

@@ -1,327 +1,180 @@
#include <iostream>
#include <iomanip>
#include <unistd.h> #include <unistd.h>
#include <ncurses.h>
#include <math.h> #include <math.h>
#include <locale.h> #include <locale.h>
#include <array> #include <array>
#include "main.hpp" #include "main.hpp"
#include "AutomForArduino.cpp" #include "AutomForArduino.cpp"
#include <prometheus/exposer.h>
#include <prometheus/registry.h>
#include <prometheus/counter.h>
#include <prometheus/gauge.h>
#include <prometheus/histogram.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 // Constantes de fonctionnement
#define LEVEL_MIN 2 #define LEVEL_MIN 2
#define FLOW_PER_PUMP 150 #define FLOW_PER_PUMP 75
WINDOW *window; /* 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 = 10; // Étape du grafcet : début Automatique int etape = 0; // Étape du grafcet : début Automatique
int bp_mode, bp_mode_fm; int bp_mode, bp_mode_fm;
unsigned short pompe1, pompe2, pompe3, pompe4; // bouton des pompes 0 (arrêt) / 1 (marche) 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 pompe1_old, pompe2_old, pompe3_old, pompe4_old;
unsigned short sensor_max, sensor_high, sensor_low, sensor_min; unsigned short sensor_max, sensor_high, sensor_low, sensor_min;
float TankInitalValue = 7; float TankInitalValue = 5;
TemporisationRetardMontee tempo1(500); using namespace prometheus;
TemporisationRetardMontee tempo2(1000); std::shared_ptr<Registry> registry;
TemporisationRetardMontee tempo3(1500); Gauge* debit_entree = nullptr;
TemporisationRetardMontee tempo4(2000); Gauge* debit_sortie = nullptr;
// Prometheus Gauge* debit_p1 = nullptr;
Gauge* debit_p2 = nullptr;
Gauge* debit_p3 = nullptr;
Gauge* debit_p4 = nullptr;
Gauge* tank_level = nullptr;
Counter* volume_p1 = nullptr;
Counter* volume_p2 = nullptr;
Counter* volume_p3 = nullptr;
Counter* volume_p4 = nullptr;
Histogram::BucketBoundaries buckets = {
1, 2, 3, 4, 5, 6, 7, 8, 9
};
Histogram *tank_level_hist = nullptr;
// 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;
}
*/
try {
json j = json::parse(payload);
// Ne rien faire si l'objet JSON est vide
if (j.empty()) return;
if (j.contains("p1")) pompe1 = j["p1"].get<int>() != 0;
if (j.contains("p2")) pompe2 = j["p2"].get<int>() != 0;
if (j.contains("p3")) pompe3 = j["p3"].get<int>() != 0;
if (j.contains("p4")) pompe4 = j["p4"].get<int>() != 0;
std::cout << "Pompes : " << pompe1 << " " << pompe2 << " " << pompe3 << " " << pompe4 << std::endl;
}
catch (const json::parse_error& e) {
std::cerr << "Erreur JSON : " << e.what() << "\n";
}
}
};
// ************************************************************ // ************************************************************
int main() int main()
{ {
/* Initialisation */ /* Initialisation */
ConsoleInit();
AffichageWindow();
InitPrometheus(); InitPrometheus();
ProcessInitKeyboard();
ProcessInitIO(); ProcessInitIO();
ProcessInitValues(); 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) 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(); Process();
LireClavier(ch);
LireEntree();
EvolutionGrafcet();
Actions();
ProcessPrometheus();
ProcessException();
usleep(100000);
}
endwin(); // Termine ncurses et rétablit le terminal
puts("Fin du programme");
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_min = digitalRead(IN_SENSOR_MIN);
sensor_low = digitalRead(IN_SENSOR_LOW); sensor_low = digitalRead(IN_SENSOR_LOW);
sensor_high = digitalRead(IN_SENSOR_HIGH); sensor_high = digitalRead(IN_SENSOR_HIGH);
sensor_max = digitalRead(IN_SENSOR_MAX); sensor_max = digitalRead(IN_SENSOR_MAX);
}
void EvolutionGrafcet() digitalWrite(OUT_PUMP_1, (pompe1 == 1));
{ digitalWrite(OUT_PUMP_2, (pompe2 == 1));
int etape_futur = etape; digitalWrite(OUT_PUMP_3, (pompe3 == 1));
digitalWrite(OUT_PUMP_4, (pompe4 == 1));
if (etape < 10 && bp_mode_fm) ProcessPrometheus();
{ ProcessMQTT(&client);
etape_futur = 10; ProcessException();
pompe1 = pompe2 = pompe3 = pompe4 = 0;
usleep(100000);
} }
if (etape <= 2 && _digital[IN_KEYBOARD_1].raising) try {
{ client.unsubscribe(TOPIC)->wait();
pompe1 = !pompe1; client.stop_consuming();
client.disconnect()->wait();
} catch(const mqtt::exception &exc){
std::cerr << "Erreur déconnexion MQTT: " << exc.what() << std::endl;
} }
if (etape <= 2 && _digital[IN_KEYBOARD_2].raising) std::cout << "Fin du programme" << std::endl;
{ return 0;
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 * 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() 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_MIN, IO_INPUT | DIGITAL);
pinMode(IN_SENSOR_LOW, IO_INPUT | DIGITAL); pinMode(IN_SENSOR_LOW, IO_INPUT | DIGITAL);
pinMode(IN_SENSOR_HIGH, IO_INPUT | DIGITAL); pinMode(IN_SENSOR_HIGH, IO_INPUT | DIGITAL);
@@ -385,8 +238,8 @@ void ProcessInitValues()
_digital[OUT_FLOW_OUT_AMPLITUDE].dvalue = 100.0; _digital[OUT_FLOW_OUT_AMPLITUDE].dvalue = 100.0;
_digital[OUT_LEVEL_MIN].dvalue = LEVEL_MIN; _digital[OUT_LEVEL_MIN].dvalue = LEVEL_MIN;
_digital[OUT_LEVEL_LOW].dvalue = 6; _digital[OUT_LEVEL_LOW].dvalue = 4.3;
_digital[OUT_LEVEL_HIGH].dvalue = 7; _digital[OUT_LEVEL_HIGH].dvalue = 6.1;
_digital[OUT_LEVEL_MAX].dvalue = 9.5; _digital[OUT_LEVEL_MAX].dvalue = 9.5;
_digital[IN_FLOW_OUT].dvalue = 100.0; _digital[IN_FLOW_OUT].dvalue = 100.0;
@@ -438,9 +291,9 @@ double ProcessMoteur(int i)
void ProcessException() void ProcessException()
{ {
if (t_elapsed > 30) { if (t_elapsed > 30) {
_digital[OUT_PUMP_1].mode = 0; //_digital[OUT_PUMP_1].mode = 0;
} else if (t_elapsed > 15) { } else if (t_elapsed > 15) {
_digital[IN_SENSOR_LOW].mode = 0; //_digital[IN_SENSOR_LOW].mode = 0;
} }
} }
@@ -454,8 +307,8 @@ void Process()
// ***** FLOW OUT // ***** FLOW OUT
if (_digital[IN_TANK_LEVEL].dvalue > 1.0) if (_digital[IN_TANK_LEVEL].dvalue > 1.0)
{ {
//_digital[IN_FLOW_OUT].dvalue = SimulConsoSinusoidale(t); _digital[IN_FLOW_OUT].dvalue = SimulConsoSinusoidale(t);
_digital[IN_FLOW_OUT].dvalue = SimulConsoBrown(_digital[IN_FLOW_OUT].dvalue); //_digital[IN_FLOW_OUT].dvalue = SimulConsoBrown(_digital[IN_FLOW_OUT].dvalue);
} }
else else
{ {
@@ -489,25 +342,6 @@ void Process()
_digital[IN_TANK_MIN].dvalue = _digital[IN_TANK_LEVEL].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)) {
_digital[p].ivalue = 0;
_digital[p].dvalue = 0.0;
}
}
}
// **** SENSOR // **** SENSOR
int test; int test;
@@ -551,8 +385,6 @@ void Process()
_digital[IN_SENSOR_HIGH].ivalue = test; _digital[IN_SENSOR_HIGH].ivalue = test;
} }
Affichage();
t_backup = t; t_backup = t;
} }
@@ -569,8 +401,6 @@ double SimulConsoBrown(double valeur_precedente)
float mu = 0.01 * -((((int)t_elapsed / 30) % 2) * 2 - 1); // Taux de croissance (1%) float mu = 0.01 * -((((int)t_elapsed / 30) % 2) * 2 - 1); // Taux de croissance (1%)
float sigma = 0.05; // Volatilité (5%) float sigma = 0.05; // Volatilité (5%)
mvprintw(8, 40, "(µ %.1f %% ; σ %.1f %%) ", mu * 100, sigma * 100);
// Nombre aléatoire compris dans [-1 +1] // Nombre aléatoire compris dans [-1 +1]
float rand_std_normal = ((double)rand() / RAND_MAX) * 2.0 - 1.0; float rand_std_normal = ((double)rand() / RAND_MAX) * 2.0 - 1.0;
@@ -585,196 +415,92 @@ double SimulConsoBrown(double valeur_precedente)
* Affichage dans la console * 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 * Prometheus
*/ */
void InitPrometheus() void InitPrometheus()
{ {
static Exposer exposer{"0.0.0.0:8099"};
// Le registre central
registry = std::make_shared<Registry>();
exposer.RegisterCollectable(registry);
auto& level_family = BuildGauge()
.Name("geii_level")
.Help("Volume en m3")
.Register(*registry);
tank_level = &level_family.Add({});
auto& debit_family = BuildGauge()
.Name("geii_debit")
.Help("Débit en l/s")
.Register(*registry);
debit_entree = &debit_family.Add({{"numero", "entree"}});
debit_sortie = &debit_family.Add({{"numero", "sortie"}});
debit_p1 = &debit_family.Add({{"numero", "1"}});
debit_p2 = &debit_family.Add({{"numero", "2"}});
debit_p3 = &debit_family.Add({{"numero", "3"}});
debit_p4 = &debit_family.Add({{"numero", "4"}});
auto& volume_family = BuildCounter()
.Name("geii_volume")
.Help("Volume en l")
.Register(*registry);
volume_p1 = &volume_family.Add({{"numero", "1"}});
volume_p2 = &volume_family.Add({{"numero", "2"}});
volume_p3 = &volume_family.Add({{"numero", "3"}});
volume_p4 = &volume_family.Add({{"numero", "4"}});
auto& hist_family = BuildHistogram()
.Name("geii_hist")
.Help("histogramme du reservoir en m3")
.Register(*registry);
tank_level_hist = &hist_family.Add({}, buckets);
} }
void ProcessPrometheus() void ProcessPrometheus()
{ {
debit_entree->Set(_digital[IN_FLOW_OUT].dvalue);
debit_sortie->Set(_digital[IN_FLOW_IN].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);
tank_level->Set(_digital[IN_TANK_LEVEL].dvalue);
tank_level_hist->Observe(_digital[IN_TANK_LEVEL].dvalue);
}
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);
} }

View File

@@ -1,12 +1,8 @@
void ConsoleInit(); #undef timeout
#include "mqtt/async_client.h"
void LireClavier(int ch);
void LireEntree();
void EvolutionGrafcet();
void Actions();
void RemiseZeroInput(); void RemiseZeroInput();
void ProcessInitKeyboard();
void ProcessInitIO(); void ProcessInitIO();
void ProcessInitValues(); void ProcessInitValues();
double ProcessMoteur(int i); double ProcessMoteur(int i);
@@ -16,27 +12,11 @@ void Process();
void InitPrometheus(); void InitPrometheus();
void ProcessPrometheus(); void ProcessPrometheus();
void ProcessMQTT(mqtt::async_client* client);
double SimulConsoSinusoidale(long t); double SimulConsoSinusoidale(long t);
double SimulConsoBrown(double valeur_precedente); double SimulConsoBrown(double valeur_precedente);
void AffichageWindow();
void Affichage();
void AffichageGraphe(int y, int x, double value);
// KEYBOARD INPUT
#define IN_KEYBOARD_1 0
#define IN_KEYBOARD_2 1
#define IN_KEYBOARD_3 2
#define IN_KEYBOARD_4 3
#define IN_KEYBOARD_A 4
#define IN_KEYBOARD_X 5
#define IN_KEYBOARD_7 6
#define IN_KEYBOARD_8 7
#define IN_KEYBOARD_9 8
#define IN_KEYBOARD_0 9
// DIGITAL INPUT // DIGITAL INPUT
#define IN_SENSOR_MIN 10 #define IN_SENSOR_MIN 10