14 #include "minijson/minijson.h" 18 #define registerWebServerControlCombine1(X, Y) X##Y 19 #define registerWebServerControlCombine(X, Y) registerWebServerControlCombine1(X, Y) 20 #define registerWebServerControlScalar(T, json_type) \ 21 static char registerWebServerControlCombine(wsc_scalar, __COUNTER__) = \ 22 WebServerControl::registerScalarType<T, json_type>(#T) 23 #define registerWebServerControlVector(T, json_type) \ 24 static char registerWebServerControlCombine(wsc_vector, __COUNTER__) = \ 25 WebServerControl::registerVectorType<T, json_type>(#T) 26 #define registerWebServerControlScalarBool(T) \ 27 registerWebServerControlScalar(T, miniJson::JsonType::kBool) 28 #define registerWebServerControlScalarNumber(T) \ 29 registerWebServerControlScalar(T, miniJson::JsonType::kNumber) 30 #define registerWebServerControlScalarString(T) \ 31 registerWebServerControlScalar(T, miniJson::JsonType::kString) 32 #define registerWebServerControlVectorNumber(T) \ 33 registerWebServerControlVector(T, miniJson::JsonType::kNumber) 34 #define registerWebServerControlVectorString(T) \ 35 registerWebServerControlVector(T, miniJson::JsonType::kString) 36 #define registerWebServerControlRealEigenMatrix() \ 37 static char registerWebServerControlCombine(wsc_matrix, __COUNTER__) = \ 38 WebServerControl::registerRealEigenMatrix() 54 params.
addClassDescription(
"Starts a webserver for sending/receiving JSON messages to get data " 55 "and control a running MOOSE calculation");
56 params.
addParam<
unsigned int>(
"port",
57 "The port to listen on; must provide either this or 'file_socket'");
60 "The path to the unix file socket to listen on; must provide either this or 'port'");
65 :
Control(parameters), _currently_waiting(false), _terminate_requested(false)
68 const auto has_file_socket =
isParamValid(
"file_socket");
69 if (!has_port && !has_file_socket)
70 mooseError(
"You must provide either the parameter 'port' or 'file_socket' to designate where " 72 if (has_port && has_file_socket)
73 paramError(
"port",
"Cannot provide both 'port' and 'file_socket'");
91 mooseAssert(
processor_id() == 0,
"Should only be started on rank 0");
92 mooseAssert(!
_server,
"Server is already started");
93 mooseAssert(!
_server_thread,
"Server thread is already listening");
96 const auto error = [](
const std::string & error)
98 miniJson::Json::_object response;
99 response[
"error"] = error;
100 return HttpResponse{400, response};
104 const auto get_string =
105 [&error](
const auto & msg,
const std::string &
name,
const std::string & description)
107 using result = std::variant<std::string, HttpResponse>;
108 const auto it = msg.find(
name);
111 error(
"The entry '" +
name +
"' is missing which should contain the " + description));
112 const auto &
value = it->second;
113 if (!
value.isString())
114 return result(error(
"The entry '" +
name +
"' which should contain the " + description));
115 return result(
value.toString());
119 const auto get_name = [&get_string](
const auto & msg,
const std::string & description)
120 {
return get_string(msg,
"name",
"name of the " + description); };
124 const auto require_waiting = [&error](
auto & control)
126 using result = std::optional<HttpResponse>;
127 if (!control.currentlyWaiting())
128 return result(error(
"This control is not currently waiting for data"));
132 const auto require_parameters = [&error](
const auto & msg,
const std::set<std::string> & params)
134 using result = std::optional<HttpResponse>;
135 for (
const auto & key_value_pair : msg)
136 if (!params.count(key_value_pair.first))
137 return result(error(
"The key '" + key_value_pair.first +
"' is unused"));
141 _server = std::make_unique<HttpServer>();
144 _server->when(
"/check")->requested([](
const HttpRequest & ) {
return HttpResponse{200}; });
152 [
this](
const HttpRequest & )
154 miniJson::Json::_object res_json;
157 res_json[
"waiting"] =
true;
158 res_json[
"execute_on_flag"] =
162 res_json[
"waiting"] =
false;
164 return HttpResponse{200, res_json};
171 _server->when(
"/get/postprocessor")
173 [
this, &error, &get_name, &require_waiting, &require_parameters](
const HttpRequest & req)
175 const auto & msg = req.json().toObject();
178 const auto name_result = get_name(msg,
"postprocessor to retrieve");
179 if (
const auto response = std::get_if<HttpResponse>(&name_result))
181 const auto &
name = std::get<std::string>(name_result);
184 if (
const auto response = require_parameters(msg, {
"name"}))
187 if (
const auto response = require_waiting(*
this))
191 return error(
"The postprocessor '" +
name +
"' was not found");
193 miniJson::Json::_object res_json;
195 return HttpResponse{200, res_json};
204 [
this, &error, &get_name, &require_waiting, &require_parameters](
const HttpRequest & req)
206 const auto & msg = req.json().toObject();
209 if (
const auto response = require_parameters(msg, {
"name"}))
212 if (
const auto response = require_waiting(*
this))
216 const auto name_result = get_name(msg,
"reporter value to retrieve");
217 if (
const auto response = std::get_if<HttpResponse>(&name_result))
219 const auto &
name = std::get<std::string>(name_result);
221 return error(
name +
" is not a valid reporter name.");
226 return error(
"The reporter value '" +
name +
"' was not found");
229 nlohmann::json njson;
231 miniJson::Json::_object res_json = {{
"value",
toMiniJson(njson)}};
233 return HttpResponse{200, res_json};
242 _server->when(
"/set/controllable")
244 [
this, &error, &get_string, &get_name, &require_waiting, &require_parameters](
245 const HttpRequest & req)
247 const auto & msg = req.json().toObject();
250 if (
const auto response = require_parameters(msg, {
"name",
"type",
"value"}))
253 if (
const auto response = require_waiting(*
this))
257 const auto type_result = get_string(msg,
"type",
"type of the parameter");
258 if (
const auto response = std::get_if<HttpResponse>(&type_result))
260 const auto &
type = std::get<std::string>(type_result);
262 return error(
"The type '" +
type +
263 "' is not registered for setting a controllable parameter");
266 const auto name_result = get_name(msg,
"name of the parameter to control");
267 if (
const auto response = std::get_if<HttpResponse>(&name_result))
269 const auto &
name = std::get<std::string>(name_result);
272 return error(
"The controllable parameter '" +
name +
"' was not found");
275 const auto value_it = msg.find(
"value");
276 if (value_it == msg.end())
278 "The entry 'value' is missing which should contain the value of the parameter");
279 const auto & json_value = value_it->second;
283 std::unique_ptr<ValueBase>
value;
291 return error(
"While parsing 'value': " + std::string(e.
what()));
296 return HttpResponse{201};
302 [
this, &error](
const HttpRequest &)
307 return HttpResponse{200};
311 return error(
"The control is not currently waiting");
317 [
this, &error](
const HttpRequest &)
323 return HttpResponse{200};
327 return error(
"The control is not currently waiting");
335 const uint16_t port = this->getParam<unsigned int>(
"port");
342 this->
mooseError(
"Failed to start the webserver; it is likely that the port ",
344 " is not available");
349 const auto & file_socket = this->getParam<FileName>(
"file_socket");
350 _server->startListening(file_socket);
365 std::vector<std::pair<std::string, std::string>> name_and_types;
368 bool terminate_solve =
false;
373 TIME_SECTION(
"execute()", 3,
"WebServerControl waiting for input")
379 std::this_thread::yield();
382 name_and_types.emplace_back(value_ptr->name(), value_ptr->type());
395 for (
const auto & [
name,
type] : name_and_types)
403 value_ptr->setControllableValue(*
this);
409 "' typed value for parameter '",
411 "'; it is likely that the parameter has a different type");
426 if (json_type == miniJson::JsonType::kNull)
428 if (json_type == miniJson::JsonType::kBool)
430 if (json_type == miniJson::JsonType::kNumber)
432 if (json_type == miniJson::JsonType::kString)
434 if (json_type == miniJson::JsonType::kArray)
436 if (json_type == miniJson::JsonType::kObject)
438 ::mooseError(
"WebServerControl::stringifyJSONType(): Unused JSON value type");
445 const auto value_str =
value.dump();
447 const auto json_value = miniJson::Json::parse(value_str, errMsg);
449 ::
mooseError(
"Failed parse value into miniJson:\n", errMsg);
454 const std::string & type)
460 const std::string &
type,
461 const miniJson::Json & json_value)
469 const auto from_json_type = json_value.getType();
470 if (from_json_type != miniJson::JsonType::kArray)
474 const auto & array_of_array_value = json_value.toArray();
475 const auto nrows = array_of_array_value.size();
477 return RealEigenMatrix::Zero(0, 0);
482 if (array_of_array_value[i].getType() != miniJson::JsonType::kArray)
484 "Element " + std::to_string(i) +
" of '" + json_value.serialize() +
"' of type " +
487 const auto & array_value = array_of_array_value[i].toArray();
489 matrix.resize(nrows, array_value.size());
490 else if (array_value.size() != (std::size_t)matrix.cols())
494 matrix(i, j) = array_value[j].toDouble();
std::string name(const ElemQuality q)
std::atomic< bool > _currently_waiting
Whether or not the Control is currently waiting.
static InputParameters validParams()
Class constructor.
void paramError(const std::string ¶m, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
static bool isRegistered(const std::string &type)
Starts a webserver that an external process can connect to in order to send JSON messages to control ...
static RealEigenMatrix getMatrixJSONValue(const miniJson::Json &json_value)
void startServer()
Internal method for starting the server.
const ExecFlagType & getCurrentExecuteOnFlag() const
Return/set the current execution flag.
RealEigenMatrixValue(const std::string &name, const std::string &type)
const Parallel::Communicator & comm() const
WebServerControl(const InputParameters ¶meters)
Common exception for parsing related errors in converting JSON to a value.
const Parallel::Communicator & _communicator
const ReporterContextBase & getReporterContextBaseByName(const ReporterName &reporter_name) const
Get the reporter context to allow non-typed operations with the data.
registerWebServerControlScalarBool(bool)
virtual bool isSolveTerminationRequested() const
Check of termination has been requested.
const std::string & name() const
Get the name of the class.
virtual void store(nlohmann::json &json) const =0
Called by JSONOutput::outputReporters to invoke storage of values for output.
Base class for a controllable value with a given type and name.
static miniJson::Json toMiniJson(const T &value)
Convert values to a miniJson::Json node.
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
std::vector< std::unique_ptr< ValueBase > > _controlled_values
The values received to control; filled on rank 0 from the server and then broadcast.
virtual void terminateSolve()
Allow objects to request clean termination of the solve.
bool hasPostprocessorByName(const PostprocessorName &name) const
Determine if the Postprocessor data exists.
registerWebServerControlScalarString(std::string)
FEProblemBase & _fe_problem
Reference to the FEProblemBase for this object.
bool hasControllableParameterByName(const std::string &name) const
const std::string & type() const
Get the type of this class.
std::mutex _controlled_values_mutex
Mutex to prevent threaded writes to _controlled_values.
Eigen::Matrix< Real, Eigen::Dynamic, Eigen::Dynamic > RealEigenMatrix
virtual const char * what() const noexcept override final
void broadcast(T &data, const unsigned int root_id=0, const bool identical_sizes=false) const
registerWebServerControlVectorNumber(Real)
static bool isValidName(const std::string &object_and_value_name)
Determines if the inputted string is convertible to a ReporterName.
registerWebServerControlRealEigenMatrix()
virtual const PostprocessorValue & getPostprocessorValueByName(const PostprocessorName &name) const
Retrieve the value of the Postprocessor.
static std::string stringifyJSONType(const miniJson::JsonType &json_type)
std::atomic< bool > _terminate_requested
std::unique_ptr< std::thread > _server_thread
The server thread.
Base class for Control objects.
IntRange< T > make_range(T beg, T end)
virtual void execute() override
Execute the control.
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type and optionally a file path to the top-level block p...
registerMooseObject("MooseApp", WebServerControl)
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
static InputParameters validParams()
processor_id_type processor_id() const
static std::unique_ptr< ValueBase > build(const std::string &type, const std::string &name)
Builds a value with the type type, name name, and a default value.
registerWebServerControlScalarNumber(Real)
auto index_range(const T &sizable)
bool hasReporterValueByName(const ReporterName &reporter_name) const
The Reporter system is comprised of objects that can contain any number of data values.
std::unique_ptr< HttpServer > _server
The server.
registerWebServerControlVectorString(std::string)