Line data Source code
1 : //* This file is part of the MOOSE framework 2 : //* https://mooseframework.inl.gov 3 : //* 4 : //* All rights reserved, see COPYRIGHT for full restrictions 5 : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT 6 : //* 7 : //* Licensed under LGPL 2.1, please see LICENSE for details 8 : //* https://www.gnu.org/licenses/lgpl-2.1.html 9 : 10 : #pragma once 11 : 12 : #include "Control.h" 13 : 14 : #include "WebServerControlTypeRegistry.h" 15 : 16 : #include "libmesh/parallel_eigen.h" 17 : 18 : #include "tinyhttp/http.h" 19 : 20 : #include <atomic> 21 : #include <memory> 22 : #include <thread> 23 : 24 : /** 25 : * Starts a webserver that an external process can connect to 26 : * in order to send JSON messages to control the solve 27 : */ 28 : class WebServerControl : public Control 29 : { 30 : public: 31 : static InputParameters validParams(); 32 : 33 : WebServerControl(const InputParameters & parameters); 34 : ~WebServerControl(); 35 : 36 : virtual void execute() override; 37 : 38 : /** 39 : * @return A string representation of \p json_type 40 : */ 41 : static std::string stringifyJSONType(const miniJson::JsonType & json_type); 42 : 43 : /** 44 : * @return A c++ representation of the scalar value of \p json_value with 45 : * the given expected json type 46 : */ 47 : template <typename T, miniJson::JsonType json_type> 48 : static T getScalarJSONValue(const miniJson::Json & json_value); 49 : 50 : /** 51 : * Convert values to a miniJson::Json node. 52 : * 53 : * @tparam T Data type 54 : * @param value The value to be converted. 55 : * @return miniJson::Json The miniJson::Json node of the converted value. 56 : */ 57 : template <typename T> 58 : static miniJson::Json toMiniJson(const T & value); 59 : 60 : using ValueBase = Moose::WebServerControlTypeRegistry::ValueBase; 61 : 62 : /** 63 : * Base class for a controllable value with a given type and name 64 : */ 65 : template <typename T> 66 : class TypedValueBase : public ValueBase 67 : { 68 : public: 69 62 : TypedValueBase(const std::string & name, const std::string & type) : ValueBase(name, type) {} 70 128 : TypedValueBase(const std::string & name, const std::string & type, const T & value) 71 128 : : ValueBase(name, type), _value(value) 72 : { 73 128 : } 74 : 75 : /// The underlying type of the value 76 : using value_type = T; 77 : 78 : /** 79 : * @return The underlying value 80 : */ 81 190 : const T & value() const { return _value; } 82 : 83 190 : virtual void setControllableValue(WebServerControl & control) override final 84 : { 85 190 : control.comm().broadcast(_value); 86 190 : control.setControllableValueByName<T>(name(), value()); 87 190 : } 88 : 89 : private: 90 : /// The underlying value 91 : T _value; 92 : }; 93 : 94 : /** 95 : * Class that stores a scalar controllable value to be set 96 : */ 97 : template <typename T, miniJson::JsonType json_type> 98 : class ScalarValue : public TypedValueBase<T> 99 : { 100 : public: 101 38 : ScalarValue(const std::string & name, const std::string & type) : TypedValueBase<T>(name, type) 102 : { 103 38 : } 104 70 : ScalarValue(const std::string & name, 105 : const std::string & type, 106 : const miniJson::Json & json_value) 107 70 : : TypedValueBase<T>(name, type, getScalarJSONValue<T, json_type>(json_value)) 108 : { 109 70 : } 110 : }; 111 : 112 : /** 113 : * Class that stores a vector controllable value to be set 114 : */ 115 : template <typename T, miniJson::JsonType json_type> 116 : class VectorValue : public TypedValueBase<std::vector<T>> 117 : { 118 : public: 119 18 : VectorValue(const std::string & name, const std::string & type) 120 18 : : TypedValueBase<std::vector<T>>(name, type) 121 : { 122 18 : } 123 42 : VectorValue(const std::string & name, 124 : const std::string & type, 125 : const miniJson::Json & json_value) 126 42 : : TypedValueBase<std::vector<T>>(name, type, getVectorJSONValue(json_value)) 127 : { 128 42 : } 129 : 130 42 : static std::vector<T> getVectorJSONValue(const miniJson::Json & json_value) 131 : { 132 42 : const auto from_json_type = json_value.getType(); 133 42 : if (from_json_type != miniJson::JsonType::kArray) 134 0 : throw ValueBase::Exception("The value '" + json_value.serialize() + "' of type " + 135 : stringifyJSONType(from_json_type) + " is not an array"); 136 : 137 42 : const auto & array_value = json_value.toArray(); 138 42 : std::vector<T> value(array_value.size()); 139 175 : for (const auto i : index_range(array_value)) 140 133 : value[i] = getScalarJSONValue<T, json_type>(array_value[i]); 141 84 : return value; 142 0 : } 143 : }; 144 : 145 : /** 146 : * Class that stores a RealEigenMatrix controllable value to be set 147 : */ 148 : class RealEigenMatrixValue : public TypedValueBase<RealEigenMatrix> 149 : { 150 : public: 151 : RealEigenMatrixValue(const std::string & name, const std::string & type); 152 : RealEigenMatrixValue(const std::string & name, 153 : const std::string & type, 154 : const miniJson::Json & json_value); 155 : static RealEigenMatrix getMatrixJSONValue(const miniJson::Json & json_value); 156 : }; 157 : 158 : /** 159 : * Registers a scalar parameter type to be controlled 160 : */ 161 : template <typename T, miniJson::JsonType json_type> 162 223612 : static char registerScalarType(const std::string type_name) 163 : { 164 223612 : return Moose::WebServerControlTypeRegistry().add<ScalarValue<T, json_type>>(type_name); 165 : } 166 : /** 167 : * Registers a vector parameter type to be controlled 168 : */ 169 : template <typename T, miniJson::JsonType json_type> 170 167709 : static char registerVectorType(const std::string type_name) 171 : { 172 335418 : return Moose::WebServerControlTypeRegistry().add<VectorValue<T, json_type>>("std::vector<" + 173 335418 : type_name + ">"); 174 : } 175 : 176 : /** 177 : * Registers a vector parameter type to be controlled 178 : */ 179 55903 : static char registerRealEigenMatrix() 180 : { 181 167709 : return Moose::WebServerControlTypeRegistry().add<RealEigenMatrixValue>("RealEigenMatrix"); 182 : } 183 : 184 : private: 185 : /** 186 : * Internal method for starting the server 187 : */ 188 : void startServer(); 189 : 190 : /** 191 : * @return Whether or not the server is currently waiting 192 : */ 193 284 : bool currentlyWaiting() const { return _currently_waiting.load(); } 194 : 195 : /// Whether or not the Control is currently waiting 196 : std::atomic<bool> _currently_waiting; 197 : 198 : // Whether or not the solve should be terminated in the next execute() call 199 : std::atomic<bool> _terminate_requested; 200 : 201 : /// The server 202 : std::unique_ptr<HttpServer> _server; 203 : /// The server thread 204 : std::unique_ptr<std::thread> _server_thread; 205 : 206 : /// The values received to control; filled on rank 0 from the server and then broadcast 207 : std::vector<std::unique_ptr<ValueBase>> _controlled_values; 208 : /// Mutex to prevent threaded writes to _controlled_values 209 : std::mutex _controlled_values_mutex; 210 : }; 211 : 212 : template <typename T, miniJson::JsonType json_type> 213 : T 214 203 : WebServerControl::getScalarJSONValue(const miniJson::Json & json_value) 215 : { 216 203 : const auto from_json_type = json_value.getType(); 217 203 : if (from_json_type != json_type) 218 0 : throw ValueBase::Exception("The value " + json_value.serialize() + " of JSON type " + 219 : stringifyJSONType(from_json_type) + 220 : " is not of the expected JSON type " + stringifyJSONType(json_type)); 221 : 222 : if constexpr (json_type == miniJson::JsonType::kBool) 223 56 : return json_value.toBool(); 224 : else if constexpr (json_type == miniJson::JsonType::kNumber) 225 252 : return json_value.toDouble(); 226 : else if constexpr (json_type == miniJson::JsonType::kString) 227 98 : return json_value.toString(); 228 : ::mooseError("WebServerControl::getScalarJSONValue(): Not configured for parsing type ", 229 : stringifyJSONType(from_json_type)); 230 : } 231 : 232 : // Explicit specialization 233 : template <> 234 : miniJson::Json WebServerControl::toMiniJson(const nlohmann::json & value);