Line data Source code
1 : #include <spdlog/spdlog.h>
2 :
3 : #include <future>
4 : #include <nlohmann/json.hpp>
5 : #include <stdexcept>
6 :
7 : #include "Com/HTTPServer.hpp"
8 : #include "Dynamic/Demand/Demand.hpp"
9 : #include "Dynamic/Demand/UniformDemandLoader.hpp"
10 : #include "Dynamic/Env/Env.hpp"
11 : #include "Dynamic/Env/Loader.hpp"
12 : #include "Dynamic/Policy/RandomPolicy.hpp"
13 : #include "Dynamic/Policy/RewardFunction/RewardFunction.hpp"
14 : #include "Dynamic/Policy/RewardFunction/RewardFunctionGreedy.hpp"
15 : #include "GlobalState.hpp"
16 : #include "Log/ProgressLoggerJsonOStream.hpp"
17 : #include "data/SUMO/NetState.hpp"
18 : #include "utils/require_env.hpp"
19 :
20 : using namespace std;
21 : using namespace Com;
22 :
23 : using namespace utils::stringify;
24 :
25 : using json = nlohmann::json;
26 :
27 : /**yaml POST /dynamic/simulation/{id}
28 : * summary: Run dynamic simulation.
29 : * tags:
30 : * - Dynamic
31 : * consumes:
32 : * - application/json
33 : * parameters:
34 : * - name: id
35 : * in: path
36 : * required: true
37 : * description: ID of simulation
38 : * schema:
39 : * type: string
40 : * pattern: '^[\w\-]+$'
41 : * requestBody:
42 : * description: Configuration of simulation to run.
43 : * content:
44 : * application/json:
45 : * schema:
46 : * $ref: '#/components/schemas/DynamicSimulation'
47 : * responses:
48 : * '200':
49 : * description: Simulation executed successfully
50 : */
51 0 : void HTTPServer::dynamicSimulationPost(const httplib::Request &req, httplib::Response &res) {
52 0 : const string WS_HOST = utils::require_env("WS_HOST");
53 :
54 0 : json data = json::parse(req.body);
55 :
56 0 : const string &resourceID = req.matches[0];
57 :
58 0 : string netPath, tazPath, demandPath;
59 0 : Dynamic::Time beginTime, endTime;
60 0 : try {
61 0 : netPath = data.at("netPath");
62 0 : tazPath = data.at("tazPath");
63 0 : demandPath = data.at("demandPath");
64 0 : beginTime = data.at("begin");
65 0 : endTime = data.at("end");
66 :
67 0 : if(endTime < beginTime)
68 0 : throw logic_error("endTime < beginTime");
69 0 : } catch(const json::out_of_range &e) {
70 0 : res.status = 400;
71 0 : return;
72 0 : } catch(const logic_error &e) {
73 0 : res.status = 400;
74 0 : res.set_content("what(): "s + e.what(), "text/plain");
75 0 : return;
76 : }
77 :
78 0 : Dynamic::Time stepTime = 1.0;
79 0 : if(data.contains("step")) {
80 0 : stepTime = data.at("step");
81 : }
82 :
83 0 : optional<string> netstatePath;
84 0 : if(data.contains("netstatePath")) {
85 0 : netstatePath = data.at("netstatePath");
86 : }
87 :
88 0 : try {
89 0 : GlobalState::ResourceID taskID = "task://"s + resourceID;
90 :
91 0 : GlobalState::ResourceID streamID = "stream://"s + resourceID;
92 0 : utils::pipestream &ios = GlobalState::streams.create(streamID);
93 :
94 0 : GlobalState::tasks.create(
95 : taskID,
96 0 : [netPath,
97 : tazPath,
98 : demandPath,
99 : beginTime,
100 : endTime,
101 : taskID,
102 : streamID,
103 : &ios,
104 : netstatePath,
105 0 : stepTime]() -> GlobalState::TaskReturn {
106 0 : try {
107 0 : Log::ProgressLoggerJsonOStream logger(ios.o());
108 :
109 : // Supply
110 0 : shared_ptr<SUMO::Network> sumoNetwork = SUMO::Network::loadFromFile(netPath);
111 0 : SUMO::TAZs sumoTAZs = SUMO::TAZ::loadFromFile(tazPath);
112 0 : SUMO::NetworkTAZs sumo{*sumoNetwork, sumoTAZs};
113 :
114 : // clang-format off
115 0 : Dynamic::Env::Loader<
116 : const SUMO::NetworkTAZs &,
117 : Dynamic::RewardFunction &
118 0 : > loader;
119 : // clang-format on
120 :
121 0 : shared_ptr<Dynamic::Env::Env> env = loader.load(sumo, Dynamic::RewardFunctionGreedy::INSTANCE);
122 :
123 : // Demand
124 0 : VISUM::OFormatDemand oDemand = VISUM::OFormatDemand::loadFromFile(demandPath);
125 : // clang-format off
126 0 : Static::Demand::Loader<
127 : const VISUM::OFormatDemand &,
128 : const Static::SUMOAdapter &
129 : > staticDemandLoader;
130 : // clang-format on
131 0 : Static::Demand staticDemand = staticDemandLoader.load(
132 : oDemand,
133 : (Static::SUMOAdapter &)loader.adapter
134 0 : );
135 0 : Dynamic::RandomPolicy::Factory policyFactory;
136 0 : Dynamic::UniformDemandLoader demandLoader(1.0, beginTime, endTime, policyFactory);
137 0 : Dynamic::Demand demand = demandLoader.load(staticDemand, *env, loader.adapter).first;
138 :
139 : // Simulation
140 0 : env->addDemand(demand);
141 :
142 0 : Dynamic::Time delta = (endTime - beginTime) / 100;
143 0 : env->log(logger, beginTime, endTime, delta);
144 :
145 0 : optional<SUMO::NetState> netstate;
146 0 : if(netstatePath.has_value()) {
147 0 : size_t numberDumps = (size_t)((endTime - beginTime) / stepTime);
148 :
149 0 : netstate.emplace(netstatePath.value(), ios_base::out);
150 :
151 0 : env->dump(netstate.value(), loader.adapter, beginTime, endTime, numberDumps);
152 : }
153 :
154 : // TODO: run simulation
155 :
156 0 : ios.closeWrite();
157 0 : GlobalState::streams.erase(streamID);
158 :
159 0 : } catch(const exception &e) {
160 0 : spdlog::error(
161 : "Task {} aborted, what(): {}",
162 0 : taskID,
163 0 : e.what()
164 : );
165 0 : return {500, "what(): "s + e.what()};
166 : }
167 :
168 0 : spdlog::info("Task {} finished", taskID);
169 0 : return {200, ""};
170 : }
171 0 : );
172 : // clang-format on
173 :
174 : // clang-format off
175 0 : json resData = {
176 : {"log", {
177 : {"resourceID", resourceID},
178 0 : {"url", "ws://" + WS_HOST + resourceID + "/log"}
179 : }}
180 0 : };
181 : // clang-format on
182 0 : res.set_content(resData.dump(), "application/json");
183 :
184 0 : } catch(const exception &e) {
185 0 : res.status = 500;
186 0 : res.set_content("what(): "s + e.what(), "text/plain");
187 : }
188 : }
|