ModuleHTTP.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. #include <httplib.h>
  2. #include <iostream>
  3. #include <thread>
  4. #include <boost/process.hpp>
  5. #include <filesystem>
  6. //#include <wait.h
  7. #include "ModuleBase.h"
  8. #include <httplib.h>
  9. #include <memory>
  10. #include <chrono>
  11. #include "IOptimizable.h"
  12. using namespace httplib;
  13. namespace mdd{
  14. using namespace boost::process;
  15. class ModuleHTTP : public ModuleBase {
  16. private:
  17. std::string _fname;
  18. std::string _id;
  19. int _port;
  20. std::unique_ptr<child> _child;
  21. protected:
  22. bool connect();
  23. std::string str_to_json(const std::string& input);
  24. template<class T>
  25. state updateVectorOf(std::vector<std::shared_ptr<T>>& vec, const std::string& msg);;
  26. state updateInputs();
  27. state updateOutputs();
  28. bool configureChild(const json& config) override;
  29. public:
  30. ModuleHTTP();// std::string fname, std::string id, int port);
  31. ~ModuleHTTP();
  32. state update() override;
  33. };
  34. bool ModuleHTTP::connect(){
  35. Client cli(_id, _port);
  36. std::string body;
  37. auto res = cli.Get("/status",
  38. [&](const char *data, size_t data_length) {
  39. body.append(data, data_length);
  40. return true;
  41. });
  42. if(body.empty()){
  43. return false;
  44. }
  45. body = std::string(R"()") + body;
  46. json status = json::parse(body.c_str());
  47. if (status["status"].get<std::string>() == "ready"){
  48. return true;
  49. }
  50. else{
  51. return false;
  52. }
  53. }
  54. std::string ModuleHTTP::str_to_json(const std::string& input){
  55. if(input.empty()){
  56. return input;
  57. }
  58. std::string str = input;
  59. size_t start_pos = 0;
  60. while((start_pos = str.find('[', start_pos)) != std::string::npos) {
  61. str.replace(start_pos, 1, "{");
  62. start_pos += 1;
  63. }
  64. start_pos = 0;
  65. while((start_pos = str.find(']', start_pos)) != std::string::npos) {
  66. str.replace(start_pos, 1, "}");
  67. start_pos += 1;
  68. }
  69. return str;
  70. }
  71. template<class T>
  72. state ModuleHTTP::updateVectorOf(std::vector<std::shared_ptr<T>>& vec, const std::string& msg) {
  73. json server = json::parse(msg.c_str());
  74. int diff = (int)server.size() - (int)vec.size();
  75. if (diff < 0)
  76. {
  77. vec.erase(vec.end() + diff, vec.end());
  78. }
  79. for(size_t i=0; i < vec.size(); i++){
  80. vec[i]->setName(server[i]["type"].get<std::string>());
  81. vec[i]->setAppendix(i);
  82. vec[i]->setValue() = server[i]["value"].get<std::vector<double>>();
  83. }
  84. for (size_t i = 0; i < diff; i++)
  85. {
  86. size_t length = vec.size();
  87. if (server[length]["value"].is_array())
  88. {
  89. vec.push_back(std::make_shared<T>(server[length]["type"].get<std::string>(), length, server[length]["value"].get<std::vector<double>>()));
  90. }
  91. else {
  92. vec.push_back(std::make_shared<T>(server[length]["type"].get<std::string>(), length, std::vector<double>{server[length]["value"].get<double>()}));
  93. std::cout << "Warning: Server expects single values, but mdd work with arrays!" << std::endl;
  94. }
  95. }
  96. return state::UNCHANGED;
  97. }
  98. state ModuleHTTP::updateInputs() {
  99. Client cli(_id, _port);
  100. std::string body;
  101. auto res = cli.Get("/inputs",
  102. [&](const char* data, size_t data_length) {
  103. body.append(data, data_length);
  104. return true;
  105. });
  106. assert(res->body.empty());
  107. body = std::string(R"()") + body;
  108. return updateVectorOf(inputs, body);
  109. }
  110. state ModuleHTTP::updateOutputs() {
  111. Client cli(_id, _port);
  112. std::string body;
  113. auto res = cli.Get("/outputs",
  114. [&](const char* data, size_t data_length) {
  115. body.append(data, data_length);
  116. return true;
  117. });
  118. assert(res->body.empty());
  119. body = std::string(R"()") + body;
  120. return updateVectorOf(outputs, body);
  121. }
  122. ModuleHTTP::ModuleHTTP()// std::string fname, std::string id, int port):
  123. : ModuleBase(R"JSON(
  124. {
  125. "configure":{
  126. "file":{
  127. "value": ""
  128. },
  129. "url":{
  130. "value": ""
  131. },
  132. "port":{
  133. "value": 0
  134. }
  135. },
  136. "GUI":{
  137. "name":{
  138. "value": "#HTTP"
  139. },
  140. "color":{
  141. "value": "#0257ad"
  142. }
  143. }
  144. })JSON")
  145. ,_port(0)
  146. {
  147. key = "ModuleHTTP";
  148. }
  149. bool ModuleHTTP::configureChild(const json& config) {
  150. bool changed = true;
  151. bool found = true;
  152. auto jit = config.find("url");
  153. if (jit != config.end())
  154. {
  155. _id = jit.value()["value"].get<std::string>();
  156. json& jbase = _base_config["configure"]["url"]["value"];
  157. if (_id != jbase.get<std::string>())
  158. {
  159. jbase = _id;
  160. changed = true;
  161. }
  162. found = true;
  163. }
  164. jit = config.find("port");
  165. if (jit != config.end())
  166. {
  167. json jval = jit.value()["value"];
  168. if (jval.is_string())
  169. {
  170. jval = json::parse(jval.get<std::string>());
  171. }
  172. _port = jval.get<int>();
  173. json& jbase = _base_config["configure"]["port"]["value"];
  174. if (_port != jbase.get<int>())
  175. {
  176. jbase = jval;
  177. changed = true;
  178. }
  179. found = true;
  180. }
  181. jit = config.find("file");
  182. if (jit != config.end())
  183. {
  184. _fname = jit.value()["value"].get<std::string>();
  185. json& jbase = _base_config["configure"]["file"]["value"];
  186. if (_fname != jbase.get<std::string>())
  187. {
  188. jbase = _fname;
  189. changed = true;
  190. }
  191. found = true;
  192. }
  193. if (!_fname.empty() && changed) {
  194. if (std::filesystem::exists(_fname))
  195. {
  196. _child = std::make_unique<child>("python3 " + _fname + " " + std::to_string(_port));
  197. }
  198. else {
  199. std::cout << "ERROR Couldnt find: " << _fname << std::endl;
  200. return false;
  201. }
  202. }
  203. if (changed)
  204. {
  205. if (connect())
  206. {
  207. updateInputs();
  208. updateOutputs();
  209. //_base_config = base_config.dump();
  210. }
  211. }
  212. return found;
  213. }
  214. ModuleHTTP::~ModuleHTTP()
  215. {
  216. if (_child != nullptr)
  217. {
  218. _child->terminate();
  219. _child->join();
  220. }
  221. }
  222. state ModuleHTTP::update() {
  223. Client cli(_id, _port);
  224. json new_inputs;
  225. for (int i = 0; i < inputs.size(); ++i) {
  226. json input;
  227. input["value"] = getInput(i)->getValue();
  228. //std::cout << "ModuleHTTP::update: last: " << getInput(i)->getValue().back() << std::endl;
  229. new_inputs.push_back(input);
  230. }
  231. std::string content = new_inputs.dump();
  232. while (!connect()) {
  233. std::this_thread::sleep_for(std::chrono::microseconds(500));
  234. }
  235. //std::cout << "ModuleHTTP::update: " << content << std::endl;
  236. auto res = cli.Post("/update",content.size(),
  237. [&](size_t offset, size_t length, DataSink &sink) {
  238. sink.write(content.data() + offset, length);
  239. return true; // return 'false' if you want to cancel the request.
  240. },"application/json");
  241. while(!connect()){
  242. std::this_thread::sleep_for(std::chrono::microseconds(500));
  243. }
  244. return updateOutputs();
  245. }
  246. }