Question

Just trying to design an architecture/class relations for my current C++ project in the IoT field.

The program itself (controller) controls various hardware via an arm-based device. I intend to use cpprestsdk for an http server implementation that provides an API for the client program that would allow to send the controller various command ("turn on this/that", "print me a log", "reconfigure" and so on..). I though about cpprestsdk because it wraps http communication, json handling and involves pplx library to handle async tasks (I've worked with C# async/await and tasks in the past). It would also provide a possibility to create a quick and simple web-interface for the device. I might be wrong, but these are my current thoughts.

Currently, I am designing a general application workflow and basic class relations.

I came up with the following:

    class App
    {
        Controller _controller; //wraps all hardware details, provides public methods to do something with the hardware
        Server _server; //wraps communication details
    };

    class Controller
    {
        void update(); //infinite loop - read inputs -> update variable -> set outputs. it runs on a background thread
        void switch(int deviceId, int state); //one of many methods to do something with the hardware
    };

    class Server
    {
    public:
        ...
    private:
        http_listener _listener; //cpprestsdk's server implementation

        handle_get(http_request request); //handles incoming requests, launches async tasks to handle these requests
        ...
    };

I thought that Server should hide all HTTP handling code from the rest of the app to make the code more clean. It would work fine, but the problem is that _listener is the one who actually launches async tasks when the request arrive.

Basically, my problem is that from a design point of view, Server should not know anything about Controller and vice-versa. It would allow to easily change their implementations. But http_listener is the one who decides to launch background threads, which means he is responsible for the control of the entire workflow (call Controller's methods, check the results, etc...) and with the current architecture Controller is out of scope of Server, let alone http_listener (actual server implementation).

I could store every request in a queue and than handle them one-by-one, but that would effectively kill all benefits of async model.

The other problem I encountered is trying to make Controller's infinite background loop and http_listener's async tasks work together. Infinite loop (scan cycle) for the hardware manages is a fine solution (every PLC works that way), but some commands may take some time, which means some async tasks(requests) will have to wait for the result more than one cycle, polling the Controller's flags to determine if the the command's execution has been finished. I think that it may lead to several problems:

  • a lot of blocking mechanisms (mutexes) that would slow down I/O scan loop execution time.
  • zombie tasks (background threads) that would wait for the result of their respective commands (poll/check - mostly do nothing), but still take the memory.

Maybe you can see some obvious mistakes in my approach? Maybe you could can provide a link/guide with the info that can answer my questions?

P.S. Now that I've finished writing this post, I have even more questions. =(

Était-ce utile?

La solution

Basically, my problem is that from a design point of view, Server should not know anything about Controller and vice-versa.

Can't you make the App (or create a new class for this) receive the http request and redirect to the appropriate Controller's method? This ends coupling between Server and Controller.

The other problem I encountered is trying to make Controller's infinite background loop and http_listener's async tasks work together.

some commands may take some time, which means some async tasks(requests) will have to wait for the result more than one cycle

You could implement the following in order to get rid of the problems you described:

Server

  • [Server] receives some request, eg: switch(id, state);
  • [App] starts the SocketServer and starts a "RunCommandThread", then finishes;
  • [RunCommandThread] calls Controller.switch(), registering into it some callback for the result;
  • [Controller switch] sends some command to the hardware, and immediately returns (does not wait for result);
  • [Controller IO scan loop] scans everything; when scanning for the specific result of the Switch, if the response is available, calls the callback registered by someone (RunCommandThread);
  • [RunCommandThread] the callback registered will write the result in the SockerServer

Client

  • sends HTTP request for "switch";
  • creates socket connection to SocketServer in the server;
  • when command finishes, result will be sent via socket
Licencié sous: CC-BY-SA avec attribution
scroll top