OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
22.01k stars 6.6k forks source link

[REQ] C++ server for esp32 and arduino #15381

Open sgawad opened 1 year ago

sgawad commented 1 year ago

My target is to use it to develop a REST api for automation on a microcontroller (although it is a pretty large one it doesn't allow pistache, restbed or qt)

so I would need to generate some generic C++ code that would be able to interface with one of those (microcontroller) servers that use very similiar architecture: https://github.com/me-no-dev/ESPAsyncWebServe (ESP32) or https://github.com/khoih-prog/Portenta_H7_AsyncWebServer. (ARM)

The dependencies should be limited to whatever may run on an microcontroller.

I see in the road map that there is a mid term goal: C++ Server, any framework (server)

What does it actually mean? Is there some way to help or someone that could help me (mentoring).

wing328 commented 1 year ago

as discussed, a few C++ server generators have been added but none of these would meet your requirement right out-of-the-box and customize the templates (e.g. -t via CLI) seem to meet your requirement so far.

sgawad commented 1 year ago

I have written some code to do the task (trying to mimic the pistache server c++) but although it compiles it seems to crash when the request is being passed to the overriding impl:

Apparently I can't attach cpp or zip so here is the code I have so far

 /*
 * ModuleApiImpl.h
 *
 * 
 */
 #ifndef MODULE_API_IMPL_H_
 #define MODULE_API_IMPL_H_
 #include <memory>
 #include <ModuleApi.h>

 namespace org::openapitools::server::api
 {
//using namespace org::openapitools::server::model;
 class  ModuleApiImpl : public org::openapitools::server::api::ModuleApi {
 public:
         //The syntax : MuduleApi(server) after the constructor parameter list is called an initializer list. It allows you to pass arguments to the constructors of base classes and member variables.
         //explicit ModuleApiImpl(const std::shared_ptr<AsyncWebServer>& server) : ModuleApi(*server), ptServer(server.get()) {}
         //explicit ModuleApiImpl(std::shared_ptr<AsyncWebServer> serverPtr);
         explicit ModuleApiImpl(std::shared_ptr<AsyncWebServer> serverPtr);
         void rotor_get_over(AsyncWebServerRequest* request) override;
 };
 } // namespace org::openapitools::server::api

 #endif
/*
* 
* ModuleApiImpl.cpp
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/

#include "ModuleApiImpl.h"

namespace org::openapitools::server::api {

    //using namespace org::openapitools::server::model;
    //ModuleApiImpl::ModuleApiImpl(const std::shared_ptr<AsyncWebServer>& server) : ModuleApi(server), serverPtr_(server) {} 
    //ModuleApiImpl::ModuleApiImpl(std::shared_ptr<AsyncWebServer> serverPtr)
    //: ModuleApi(serverPtr){}
   ModuleApiImpl::ModuleApiImpl(std::shared_ptr<AsyncWebServer> serverPtr) : ModuleApi(serverPtr){}

    void ModuleApiImpl::rotor_get_over(AsyncWebServerRequest* request){
        Serial.print("rotor_get");
        request->send(200, "text/plain", "Hello, GET: " + request->url() + "!");
    }

}
/*
 * ModuleApi.h
 *
 * 
 */

#ifndef ModuleApi_H_
#define ModuleApi_H_

#include <Portenta_H7_AsyncWebServer.h>

//#include <optional>
#include <utility>

namespace org::openapitools::server::api {

class  ModuleApi {
public:
    //explicit ModuleApi(AsyncWebServer& server);
    //void init();
    static const std::string base;
    //explicit ModuleApi(AsyncWebServer& server);
    explicit ModuleApi(std::shared_ptr<AsyncWebServer> serverPtr);
    virtual ~ModuleApi() = default;
    void init();
    virtual void rotor_get_over(AsyncWebServerRequest* request) = 0;
    void rotor_get(AsyncWebServerRequest* request);
    //void addRoute(const std::string& path, const std::string& httpMethod, std::function<void(AsyncWebServerRequest*)> callback);
    void setupRoutes();
    //void setupRoutes2();
    void rotor_get_handler(AsyncWebServerRequest* request);
    void rotor_over_get_handler(AsyncWebServerRequest* request);

protected:    
    std::shared_ptr<AsyncWebServer> serverPtr_;

};

} // namespace org::openapitools::server::api

#endif /* ModuleApi_H_ */
/**
* ModuleApi.cpp
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/

#include "ModuleApi.h"
#include "Helpers.h"
#include <ArduinoJson.h>

namespace org::openapitools::server::api {

ModuleApi::ModuleApi(std::shared_ptr<AsyncWebServer> serverPtr) : serverPtr_(serverPtr) {
    Serial.println("instance of moduleAPI object created");
    //server_ = server;
    init();
    //ptServer = &server;
}

void ModuleApi::init() {
    Serial.println("init function called");
    setupRoutes();
}
   //The third argument passed to the on function is ModuleApi::rotor_get_handler, which is a member function of the ModuleApi class. 
    //However, the on function expects a callback function that is not a member function of a class. 
    //You can fix this issue by using a lambda function or std::bind to bind the rotor_get_handler member function to an instance of the ModuleApi class 
    //and pass it as the third argument to the on function.

void ModuleApi::setupRoutes(){
    //lamda solution:
    Serial.println("defining route rotor ");
    serverPtr_->on("/rotor", HTTP_GET, [this](AsyncWebServerRequest *request){ this->rotor_get_handler(request); });
    serverPtr_->on("/rotorover", HTTP_GET, [this](AsyncWebServerRequest *request){ this->rotor_over_get_handler(request); });

    Serial.println("route rotor defined");
    //bind solution:
    //ptServer->on("/rotor", HTTP_GET, std::bind(&ModuleApi::rotor_get_handler, this, std::placeholders::_1));
}

void ModuleApi::rotor_get_handler(AsyncWebServerRequest *request){
    Serial.println("rotor_get_handler called");
    //this->rotor_get(request);
    //ModuleApiImpl* impl = dynamic_cast<ModuleApiImpl*>(this);
    this->rotor_get(request);
}

 void ModuleApi::rotor_over_get_handler(AsyncWebServerRequest *request){
    Serial.println("rotor_get_handler called");
    Serial.println(request->url());
    this->rotor_get_over(request);
    //ModuleApiImpl* impl = static_cast<ModuleApiImpl*>(this);
    //impl->rotor_get_over(request);

    //this->rotor_get(request);

    // ModuleApiImpl* impl = dynamic_cast<ModuleApiImpl*>(this);
    // if (impl != nullptr) {
    //     impl->rotor_get_over(request);
    // } else {
    //     Serial.println("Failed to cast 'this' to ModuleApiImpl*");
    // }
} 

void ModuleApi::rotor_get(AsyncWebServerRequest* request){
    Serial.println("rotor_get");
    Serial.println(request->url());
    request->send(200, "text/plain", "Hello from API, GET: " + request->url() + "!");
}
}// namespace org::openapitools::server::api

the call to build the server in main.cpp are as follow:

defined as global:

std::shared_ptr<AsyncWebServer> serverPtr = std::make_shared<AsyncWebServer>(80); in setup(){

org::openapitools::server::api::ModuleApiImpl ModuleApiImpl(serverPtr);
}

I have implemented a rotor and a rotor_over path in the api... the "rotor" callback method is within the ModuleApi.cpp and does respond in postman... although the server and microcontroller crashes not too long after. the rotor_over I tried to do the impl class and it never gets there.. As you will see in some of the comments lying around I have tried with other ways of getting the callback to respond but nothing worked so far.

So any advice or support from someone knowing about C++ callbacks in overriden classes + implementing it on a microcontroller would be very very welcome. Also since I am working on the arduino framework with PIO I can't really trace or debug which doesn't really help.