umn-drone / project / apps / transit_service / src / transit_service.cc
transit_service.cc
Raw
#include <map>
#include <chrono>
#include "WebServer.h"
#include "simulation_model.h"
#include "routing_api.h"

//--------------------  Controller ----------------------------

/// A Transit Service that communicates with a web page through web sockets.  It also acts as the controller
/// in the model view controller pattern.
class TransitService : public JsonSession, public IController {
public:
    TransitService(SimulationModel& model) : model(model), start(std::chrono::system_clock::now()), time(0.0) {
        routing::RoutingAPI api;
        routing::IGraph* graph = api.LoadFromFile("libs/routing/data/umn.osm");
        model.SetGraph(graph);
    }

    /// Handles specific commands from the web server
    void ReceiveCommand(const std::string& cmd, JsonObject& data, JsonObject& returnValue) {
        //std::cout << cmd << ": " << data << std::endl;
        if (cmd == "CreateEntity") {
            model.CreateEntity(data);
        }
        else if (cmd == "ScheduleTrip") {
            model.ScheduleTrip(data);
        }
        else if (cmd == "ping") {
            returnValue["response"] = data;
        }
        else if (cmd == "Update") {
            updateEntites.clear();

            std::chrono::time_point<std::chrono::system_clock> end = std::chrono::system_clock::now();
            std::chrono::duration<double> diff = end - start;
            double delta = diff.count() - time;
            time += delta;

            double simSpeed = data["simSpeed"];
            delta *= simSpeed;

            if (delta > 0.1) {
                for (float f = 0.0; f < delta; f+=0.01) {
                    model.Update(0.01);
                }
            }
            else {
                model.Update(delta);
            }

            for (std::map<int, const IEntity*>::iterator it = updateEntites.begin(); it != updateEntites.end(); it++) {
                // Send updated entities
                SendEntity("UpdateEntity", *it->second, false);
            }
        }
    }

    void SendEntity(const std::string& event, const IEntity& entity, bool includeDetails) {
        //JsonObject details = entity.GetDetails();
        JsonObject details;
        if (includeDetails) {
            details["details"] = entity.GetDetails();
        }
        details["id"] = entity.GetId();
        Vector3 pos_ = entity.GetPosition();
        Vector3 dir_ = entity.GetDirection();
        JsonArray pos = {pos_.x, pos_.y, pos_.z};
        JsonArray dir = {dir_.x, dir_.y, dir_.z};
        details["pos"] = pos;
        details["dir"] = dir;
        SendEventToView(event, details);
    }

    void AddEntity(const IEntity& entity) {
        SendEntity("AddEntity", entity, true);
    }
    
    void UpdateEntity(const IEntity& entity) {
        updateEntites[entity.GetId()] = &entity;
    }

    void RemoveEntity(int id) {
        JsonObject details;
        details["id"] = id;
        SendEventToView("RemoveEntity", details);
    }

    void AddPath(int id, const std::vector< std::vector<float> >& path) {
        JsonObject details;
        JsonArray array = details["path"];
        array.Resize(path.size());
        for (int i = 0; i < path.size(); i++) {
            JsonArray point = array[i];
            point.Resize(3);
            point[0] = path[i][0];
            point[1] = path[i][1];
            point[2] = path[i][2];
        }
        SendEventToView("AddPath", details);
    }

    void RemovePath(int id) {
        JsonObject details;
        details["id"] = id;
        SendEventToView("RemovePath", details);
    }

    /// Allows messages to be passed back to the view
    void SendEventToView(const std::string& event, const JsonObject& details) {
        JsonObject eventData;
        eventData["event"] = event;
        eventData["details"] = details;
        sendMessage(eventData.ToString());
    }

private:
    // Simulation Model
    SimulationModel& model;
    // Used for tracking time since last update
    std::chrono::time_point<std::chrono::system_clock> start;
    // The total time the server has been running.
    double time;
    // Current entities to update
    std::map<int, const IEntity*> updateEntites;
};


//--------------------  View / Web Server Code ----------------------------

/// The TransitWebServer holds the simulation and updates sessions.
class TransitWebServer : public WebServerBase, public IController {
public:
	TransitWebServer(int port = 8081, const std::string& webDir = ".") : WebServerBase(port, webDir), model(*this) {}
    void AddEntity(const IEntity& entity) {
        for (int i = 0; i < sessions.size(); i++) {
            static_cast<TransitService*>(sessions[i])->AddEntity(entity);
        }
    }
    
    void UpdateEntity(const IEntity& entity) {
        for (int i = 0; i < sessions.size(); i++) {
            static_cast<TransitService*>(sessions[i])->UpdateEntity(entity);
        }
    }

    void RemoveEntity(int id) {
        for (int i = 0; i < sessions.size(); i++) {
            static_cast<TransitService*>(sessions[i])->RemoveEntity(id);
        }
    }

    void AddPath(int id, const std::vector< std::vector<float> >& path) {
        for (int i = 0; i < sessions.size(); i++) {
            static_cast<TransitService*>(sessions[i])->AddPath(id, path);
        }
    }

    void RemovePath(int id) {
        for (int i = 0; i < sessions.size(); i++) {
            static_cast<TransitService*>(sessions[i])->RemovePath(id);
        }
    }

    void SendEventToView(const std::string& event, const JsonObject& details) {
        for (int i = 0; i < sessions.size(); i++) {
            static_cast<TransitService*>(sessions[i])->SendEventToView(event, details);
        }
    }

protected:
	Session* createSession() { return new TransitService(model); }
private:
    SimulationModel model;
};

/// The main program that handels starting the web sockets service.
int main(int argc, char**argv) {
    if (argc > 1) {
        int port = std::atoi(argv[1]);
        std::string webDir = std::string(argv[2]);
        TransitWebServer server(port, webDir);
        while (true) {
            server.service();
        }
    }
    else {
        std::cout << "Usage: ./build/WebApp 8081 web" << std::endl;
    }

    return 0;
}