Création du module DolibarrClient avec ses différentes routes & création du module WarehouseGUI pour le LCD M5Stack #7

Merged
Clement merged 9 commits from feat/client_dolibarr into develop 2023-11-10 15:47:41 +00:00
21 changed files with 646 additions and 4 deletions
Showing only changes of commit 1828d7a961 - Show all commits

1
.gitignore vendored
View File

@ -7,6 +7,7 @@
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
.idea/
# Aptatio/Platformio specifics
secrets.ini

View File

@ -10,6 +10,7 @@ monitor_speed = 115200
; notworthy ones:
; __PLATFORMIO_BUILD_DEBUG__ = debug mode
build_flags =
-std=c++17
Nicolas marked this conversation as resolved Outdated

vraiment utile @Nicolas ?

vraiment utile @Nicolas ?
; DO NOT TOUCH --- START
-D MONITOR_SPEED=${config.monitor_speed}
; DO NOT TOUCH --- END

22
docker-compose.yml Normal file
View File

@ -0,0 +1,22 @@
version: "3"
services:
mariadb:
image: mariadb:latest
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: dolibarr
web:
image: tuxgasy/dolibarr
environment:
DOLI_DB_HOST: mariadb
DOLI_DB_USER: root
DOLI_DB_PASSWORD: root
DOLI_DB_NAME: dolibarr
DOLI_URL_ROOT: 'http://0.0.0.0'
PHP_INI_DATE_TIMEZONE: 'Europe/Paris'
ports:
- "80:80"
links:
- mariadb

View File

@ -1,7 +1,10 @@
#ifndef PROGRAM_H
#define PROGRAM_H
#define DEFAULT_BAUD_RATE 115200
Nicolas marked this conversation as resolved Outdated

deja inclue dans les config

deja inclue dans les config
#include "Arduino.h"
#include "DolibarrClient.h"
class Program {
public:
@ -11,9 +14,11 @@ public:
Program();
/**
* Program main loop
* Program WarehouseGUI loop
*/
void loop();
private:
DolibarrClient *client;
};
#endif

View File

@ -0,0 +1,170 @@
#include "DolibarrClient.h"
#include <WiFi.h>
#include <ArduinoJson.h>
#include <iostream>
#define WAITING_WIFI_DELAY 1000
Nicolas marked this conversation as resolved Outdated

mettre en config

mettre en config
DolibarrClient::DolibarrClient(struct WifiConfig wifi_config, struct DolibarrConfig dolibarr_config) : wifi(wifi_config), dolibarr(dolibarr_config) {
#if defined(DEBUG)
Serial.println(" --- Wifi configuration --- ");
Serial.println((std::string("SSID: ") + std::string(wifi_config.ssid)).c_str());
Serial.println((std::string("Password: ") + std::string(wifi_config.password)).c_str());
Serial.println(" --- Dolibarr configuration --- ");
Serial.println((std::string("Base URL: ") + std::string(dolibarr_config.url)).c_str());
Serial.println((std::string("Token: ") + std::string(dolibarr_config.api_key)).c_str());
#endif
this->initialize_wifi();
this->initialize_http_client();
}
HTTPClient *DolibarrClient::build_url(const String& url) const {
auto *client = new HTTPClient();
String clientUrl = this->dolibarr.url + url;
client->begin(clientUrl);
client->addHeader("Content-Type", "application/json");
client->addHeader("DOLAPIKEY", this->dolibarr.api_key);
#if defined(DEBUG)
Serial.println("URL Request: " + clientUrl);
#endif
return client;
}
int DolibarrClient::login() const {
HTTPClient *client = this->build_url(LOGIN_URL);
int httpResponseCode = client->GET();
if (httpResponseCode > 0) {
StaticJsonDocument<LOGIN_JSON_SIZE> doc;
DeserializationError error = deserializeJson(doc, client->getString().c_str());
if (error) {
delete client;
return -1;
}
delete client;
return 0;
}
delete client;
return -1;
}
std::string replace_id(const char *str, const char *id) {
Nicolas marked this conversation as resolved
Review

utiliser les string d'Arduino

utiliser les string d'Arduino
std::string url(str);
url.replace(url.find("{id}"), 4, id);
return url;
}
std::vector<models::Product> *DolibarrClient::list_products() const {
HTTPClient *client = this->build_url(LIST_PRODUCT_URL);
if (client->GET() == OK_RESPONSE) {
}
return nullptr;
}
models::Product *DolibarrClient::get_product_by_id(const char* id_product) const {
HTTPClient *client = this->build_url(replace_id(GET_PRODUCT_URL, id_product).c_str());
if (client->GET() == OK_RESPONSE) {
StaticJsonDocument<GET_PRODUCT_JSON_SIZE> doc;
DeserializationError error = deserializeJson(doc, client->getString().c_str());
if (error) {
Serial.println("ERROR: ");
Serial.println(error.c_str());
delete client;
return nullptr;
}
auto *product = new models::Product();
product->date_creation = doc["date_creation"];
product->id = doc["id"];
product->entity = doc["entity"];
product->stock_reel = doc["stock_reel"];
product->label = doc["label"];
delete client;
return product;
}
delete client;
return nullptr;
}
std::vector<models::Warehouse> *DolibarrClient::list_warehouse() const {
HTTPClient *client = this->build_url(LIST_WAREHOUSE_URL);
if (client->GET() == OK_RESPONSE) {
StaticJsonDocument<LIST_WAREHOUSE_JSON_SIZE> doc;
DeserializationError error = deserializeJson(doc, client->getString().c_str());
if (error) {
Serial.println("ERROR: ");
Serial.println(error.c_str());
delete client;
return nullptr;
}
auto *warehouses = new std::vector<models::Warehouse>();
for (auto obj : doc.as<JsonArray>()) {
models::Warehouse warehouse = {};
warehouse.id = obj["id"];
warehouses->push_back(warehouse);
}
delete client;
return warehouses;
}
delete client;
return nullptr;
}
int DolibarrClient::create_movement(models::CreateProductStock &stock) const {
HTTPClient *client = this->build_url(CREATE_STOCKS_MOVEMENTS_URL);
StaticJsonDocument<CREATE_STOCKS_MOVEMENTS_JSON_SIZE> doc;
std::string result;
doc["product_id"] = stock.product_id;
doc["warehouse_id"] = stock.warehouse_id;
doc["qty"] = stock.qty;
serializeJson(doc, result);
Serial.println(result.c_str());
if (client->POST(result.c_str()) == OK_RESPONSE) {
delete client;
return 0;
}
return -1;
}
int DolibarrClient::initialize_wifi() const {
Nicolas marked this conversation as resolved
Review

faut pas faire sa dans le prog principal ?

faut pas faire sa dans le prog principal ?
WiFiClass::mode(WIFI_STA); //Optional
WiFi.setSleep(false);
WiFi.begin(this->wifi.ssid, this->wifi.password);
Serial.print("Connecting ");
while(WiFiClass::status() != WL_CONNECTED){
delay(WAITING_WIFI_DELAY);
Serial.print(".");
}
Serial.println("Connected to the WiFi network");
return 0;
}
int DolibarrClient::initialize_http_client() {
this->httpClient = new HTTPClient();
if (this->login() == 0) {
auto* product = this->get_product_by_id("1");
if (product == nullptr) {
Serial.println("Product is nullptr !");
return -1;
}
Serial.println("Product label: ");
Serial.println(product->label);
auto* warehouses = this->list_warehouse();
if (warehouses == nullptr) {
delete product;
Serial.println("Warehouse is nullptr !");
return -1;
}
Serial.println("Warehouses: ");
models::CreateProductStock product_stock = {product->id, warehouses->at(0).id, "1"};
this->create_movement(product_stock);
for (auto warehouse : *warehouses) {
Serial.println(warehouse.id);
}
delete product;
delete warehouses;
} else {
Serial.println("An Error has occurred while trying to login");
}
return 0;
}

View File

@ -0,0 +1,56 @@
#ifndef DOLIBARR_CLIENT_H
#define DOLIBARR_CLIENT_H
#include <WiFi.h>
#include <HTTPClient.h>
#include <vector>
#include "DolibarrModels.h"
constexpr u_int32_t OK_RESPONSE = 200;
constexpr u_int32_t CREATED_RESPONSE = 201;
constexpr u_int32_t NO_CONTENT_RESPONSE = 204;
constexpr u_int32_t LOGIN_JSON_SIZE = 4096;
Nicolas marked this conversation as resolved
Review

tout les constexpr les mettre dans les fichiers ini (env et config)

tout les constexpr les mettre dans les fichiers ini (env et config)
constexpr const char* LOGIN_URL = "/users/info";
constexpr u_int32_t LIST_PRODUCT_JSON_SIZE = 4096;
constexpr const char* LIST_PRODUCT_URL = "/products/";
constexpr u_int32_t GET_PRODUCT_JSON_SIZE = 4096;
constexpr const char* GET_PRODUCT_URL = "/products/{id}/";
constexpr u_int32_t GET_PRODUCT_STOCK_JSON_SIZE = 4096;
constexpr const char* GET_PRODUCT_STOCK_URL = "/products/{id}/stock/";
constexpr u_int32_t LIST_WAREHOUSE_JSON_SIZE = 4096;
constexpr const char* LIST_WAREHOUSE_URL = "/warehouses/";
constexpr u_int32_t LIST_STOCKS_MOVEMENTS_JSON_SIZE = 4096;
constexpr const char* LIST_STOCKS_MOVEMENTS_URL = "/stockmovements/?sortfield=t.rowid&sortorder=ASC&limit=100";
constexpr u_int32_t CREATE_STOCKS_MOVEMENTS_JSON_SIZE = 4096;
constexpr const char* CREATE_STOCKS_MOVEMENTS_URL = "/stockmovements/";
struct WifiConfig {
const char* ssid;
const char* password;
};
struct DolibarrConfig {
const char* url;
const char* api_key;
};
class DolibarrClient {
public:
DolibarrClient(WifiConfig wifi_config, DolibarrConfig dolibarr_config);
~DolibarrClient() = default;
int login() const;
std::vector<models::Warehouse> *list_warehouse() const;
std::vector<models::Product> *list_products() const;
models::Product *get_product_by_id(const char* id_product) const;
int create_movement(models::CreateProductStock &stock) const;
private:
HTTPClient* httpClient{};
struct WifiConfig wifi;
struct DolibarrConfig dolibarr;
int initialize_wifi() const;
int initialize_http_client();
HTTPClient *build_url(const String& url) const;
};
#endif //DOLIBARR_CLIENT_H

View File

@ -0,0 +1,5 @@
//
Nicolas marked this conversation as resolved Outdated

c'est quoi se fichier ?

c'est quoi se fichier ?
// Created by nico on 15/09/23.
//
#include "DolibarrModels.h"

View File

@ -0,0 +1,40 @@
#ifndef T_IOT_901_CONVOYOR_DOLIBARRMODELS_H
#define T_IOT_901_CONVOYOR_DOLIBARRMODELS_H
namespace models {
Nicolas marked this conversation as resolved
Review

je suis chaud que tu m'explique les namesapce j'ai jamais utiliser ;)

je suis chaud que tu m'explique les namesapce j'ai jamais utiliser ;)
struct Product {
const char* id;
const char* entity;
const char* ref;
const char* status;
const char* date_creation;
const char* date_modification;
const char* label;
const char* description;
const char* type;
const char* price;
const char* stock_reel;
const char* seuil_stock_alerte;
const char* desiredstock;
};
struct ProductStock {
const char* id;
const char* product_id;
const char* quantity;
};
struct CreateProductStock {
const char* product_id;
const char* warehouse_id;
const char* qty;
};
struct Warehouse {
const char* id;
};
}
#endif //T_IOT_901_CONVOYOR_DOLIBARRMODELS_H

View File

@ -0,0 +1,56 @@
#include <algorithm>
#include <iostream>
#include "GUIScreen.h"
// Abstract AGuiScreen definition
gui::AGUIScreen::AGUIScreen() {
std::cout << "Hello form AGUIScreen" << "\n";
this->widgets = std::vector<AGUIWidget*>();
}
int gui::AGUIScreen::update() {
std::sort(widgets.begin(), widgets.end(), [](AGUIWidget* aWidget, AGUIWidget* bWidget) {
return aWidget->getLayer() < bWidget->getLayer();
});
for (auto *widget: widgets) {
widget->update();
}
return (0);
}
int gui::AGUIScreen::addWidget(gui::AGUIWidget *widget) {
this->widgets.push_back(widget);
return 0;
}
int gui::AGUIScreen::removeWidget(gui::AGUIWidget *_widget) {
return 0;
}
int gui::AGUIScreen::removeWidget(int _index) {
return 0;
}
int gui::AGUIScreen::removeWidget(const char *_name) {
return 0;
}
std::vector<gui::AGUIWidget*> gui::AGUIScreen::getWidgets() const {
return this->widgets;
}
// DefaultGuiScreen definition
gui::DefaultGuiScreen::DefaultGuiScreen() {
std::cout << "Hello form DefaultGuiScreen" << "\n";
}
int gui::DefaultGuiScreen::setup() {
return 0;
}
const char *gui::DefaultGuiScreen::getName() const {
return "DefaultGuiScreen";
}

View File

@ -0,0 +1,35 @@
#ifndef T_IOT_901_CONVOYOR_GUICONTAINER_H
#define T_IOT_901_CONVOYOR_GUICONTAINER_H
#include <vector>
#include "GUIWidget.h"
namespace gui {
class AGUIScreen {
public:
AGUIScreen();
~AGUIScreen() = default;
virtual const char* getName() const = 0;
virtual int setup() = 0;
int update();
int addWidget(AGUIWidget* widget);
int removeWidget(AGUIWidget* widget);
int removeWidget(int index);
int removeWidget(const char* name);
std::vector<AGUIWidget*> getWidgets() const;
protected:
std::vector<AGUIWidget*> widgets;
};
class DefaultGuiScreen : public AGUIScreen {
public:
DefaultGuiScreen();
~DefaultGuiScreen() = default;
const char* getName() const override;
int setup() override;
};
}
#endif //T_IOT_901_CONVOYOR_GUICONTAINER_H

View File

@ -0,0 +1,33 @@
#include "GUIWidget.h"
gui::AGUIWidget::AGUIWidget(int x, int y, int width, int height, int layer) : layer(layer) {
this->position.x = x;
this->position.y = y;
this->size.width = width;
this->size.height = height;
}
int gui::AGUIWidget::getLayer() const {
return this->layer;
}
void gui::AGUIWidget::setLayer(int newLayer) {
this->layer = newLayer;
}
GuiWidgetPosition gui::AGUIWidget::getPosition() const {
return this->position;
}
void gui::AGUIWidget::setPosition(GuiWidgetPosition pos) {
this->position = pos;
}
GuiWidgetSize gui::AGUIWidget::getSize() const {
return this->size;
}
void gui::AGUIWidget::setSize(GuiWidgetSize newSize) {
this->size = newSize;
}

View File

@ -0,0 +1,35 @@
#ifndef T_IOT_901_CONVOYOR_GUIWIDGET_H
#define T_IOT_901_CONVOYOR_GUIWIDGET_H
struct GuiWidgetPosition {
int x;
int y;
};
struct GuiWidgetSize {
int width;
int height;
};
namespace gui {
class AGUIWidget {
public:
AGUIWidget(int x, int y, int width, int height, int layer = 0);
~AGUIWidget() = default;
virtual const char* getName() const = 0;
virtual void setup() = 0;
virtual void update() = 0;
int getLayer() const;
void setLayer(int layer);
GuiWidgetPosition getPosition() const;
void setPosition(GuiWidgetPosition position);
GuiWidgetSize getSize() const;
void setSize(GuiWidgetSize size);
private:
GuiWidgetPosition position{};
GuiWidgetSize size{};
int layer;
};
}
#endif //T_IOT_901_CONVOYOR_GUIWIDGET_H

View File

@ -0,0 +1,82 @@
#include "WarehouseGUI.h"
#include "M5Stack.h"
using namespace gui;
WarehouseGUI::WarehouseGUI() {
this->screens = std::vector<AGUIScreen*>();
}
WarehouseGUI::~WarehouseGUI() = default;
int WarehouseGUI::addScreens(gui::AGUIScreen *screen) {
this->screens.push_back(screen);
return 0;
}
int WarehouseGUI::removeScreens(gui::AGUIScreen *screen) {
for (int i = 0; i < this->screens.size(); ++i) {
if (this->screens[i] == screen) {
this->screens.erase(this->screens.begin() + i);
return 0;
}
}
return -1;
}
int WarehouseGUI::removeScreens(int index) {
if (index < 0 || index >= this->screens.size()) {
return -1;
}
this->screens.erase(this->screens.begin() + index);
return 0;
}
int WarehouseGUI::removeScreens(const char *name) {
for (int i = 0; i < this->screens.size(); ++i) {
if (this->screens[i]->getName() == name) {
this->screens.erase(this->screens.begin() + i);
return 0;
}
}
return -1;
}
int WarehouseGUI::update() {
return this->current_screen->update();
}
int WarehouseGUI::setup() {
if (this->current_screen == nullptr) {
return -1;
}
return this->current_screen->setup();
}
int WarehouseGUI::changeCurrentScreen(const char *name) {
for (auto *screen : this->screens) {
if (screen->getName() == name) {
this->current_screen = screen;
this->current_screen->setup();
return 0;
}
}
return -1;
}
int WarehouseGUI::changeCurrentScreen(int index) {
if (index < 0 || index >= this->screens.size()) {
return -1;
}
this->current_screen = this->screens[index];
this->current_screen->setup();
return 0;
}
std::vector<AGUIScreen *> gui::WarehouseGUI::getAllScreens() {
return this->screens;
}
AGUIScreen *gui::WarehouseGUI::getCurrentScreen() {
return this->current_screen;
}

View File

@ -0,0 +1,29 @@
#ifndef T_IOT_901_CONVOYOR_WAREHOUSEGUI_H
#define T_IOT_901_CONVOYOR_WAREHOUSEGUI_H
#include <vector>
#include "GUIScreen.h"
namespace gui {
class WarehouseGUI {
public:
WarehouseGUI();
~WarehouseGUI();
int addScreens(AGUIScreen* screen);
int removeScreens(AGUIScreen* screen);
int removeScreens(int index);
int removeScreens(const char *name);
int changeCurrentScreen(const char* name);
int changeCurrentScreen(int index);
std::vector<AGUIScreen*> getAllScreens();
AGUIScreen* getCurrentScreen();
int setup();
int update();
private:
std::vector<AGUIScreen*> screens;
AGUIScreen *current_screen{nullptr};
};
} // namespace gui
#endif //T_IOT_901_CONVOYOR_WAREHOUSEGUI_H

View File

@ -50,6 +50,9 @@ upload_speed = 921600
; librairies (make sure to fix versions where possible!)
lib_deps =
bblanchon/ArduinoJson@^6.21.3
Nicolas marked this conversation as resolved Outdated

mettre un commentaire sur chaque ligne pour dire l'utilité de la lib

mettre un commentaire sur chaque ligne pour dire l'utilité de la lib
m5stack/M5Stack@^0.4.5
m5stack/M5GFX@^0.1.9
; example:
; erropix/ESP32 AnalogWrite@0.2

View File

@ -4,3 +4,8 @@
[secrets]
build_flags =
-D WIFI_SSID=\"test\"
-D WIFI_PASSWORD=\"abcd1234\"
-D DOLIBARR_API_TOKEN=\"monapitokendedolibarr\"
-D DOLIBARR_URL=\"http://0.0.0.0/api/index.php\"
-D DEBUG=true

View File

@ -1,10 +1,12 @@
#include "Program.h"
#include "Arduino.h"
#include "DolibarrClient.h"
Program::Program() {
// Startup
Serial.begin(MONITOR_SPEED);
Nicolas marked this conversation as resolved Outdated

remettre le serial print comme c'était

remettre le serial print comme c'était
struct WifiConfig wifi_c = {WIFI_SSID, WIFI_PASSWORD};
struct DolibarrConfig dolibarr = {DOLIBARR_URL, DOLIBARR_API_TOKEN};
this->client = new DolibarrClient(wifi_c, dolibarr);
}
void Program::loop() {
// Loop
}

View File

@ -1,9 +1,19 @@
#include "Program.h"
#include "Arduino.h"
#include "WarehouseGUI.h"
#include <M5Stack.h>
Program* program;
void setup() {
Nicolas marked this conversation as resolved
Review

le main.cpp doit téoriquement rester par defaut

le main.cpp doit téoriquement rester par defaut
Serial.begin(DEFAULT_BAUD_RATE);
program = new Program();
#if defined(DEBUG)
Serial.println("Hello World");
#endif
}
void loop() {

10
test/dolibarr.cpp Normal file
View File

@ -0,0 +1,10 @@
#include <unity.h>
#include "DolibarrClient.h"
void test_construct_dolibarr_client() {
return;
}
void test_destroy_basic_state() {
return;
}

36
test/test.cpp Normal file
View File

@ -0,0 +1,36 @@
#include <M5Stack.h>
#include <unity.h>
#include "test.h"
void setUp(void) {
// set stuff up here
}
void tearDown(void) {
// clean stuff up here
}
int runUnityTests(void) {
UNITY_BEGIN();
RUN_TEST(test_construct_dolibarr_client);
return UNITY_END();
}
int main(void) {
return runUnityTests();
}
// For Arduino framework
void setup() {
// Wait ~2 seconds before the Unity test runner
// establishes connection with a board Serial interface
runUnityTests();
}
// For Arduino framework
void loop() {}
// For ESP-IDF framework
void app_main() {
runUnityTests();
}

6
test/test.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef T_IOT_901_CONVOYOR_TEST_H
#define T_IOT_901_CONVOYOR_TEST_H
void test_construct_dolibarr_client();
#endif //T_IOT_901_CONVOYOR_TEST_H