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) 50 params.
addClassDescription(
"Starts a webserver for sending/receiving JSON messages to get data " 51 "and control a running MOOSE calculation");
52 params.
addParam<
unsigned int>(
"port",
53 "The port to listen on; must provide either this or 'file_socket'");
56 "The path to the unix file socket to listen on; must provide either this or 'port'");
61 :
Control(parameters), _currently_waiting(false)
64 const auto has_file_socket =
isParamValid(
"file_socket");
65 if (!has_port && !has_file_socket)
66 mooseError(
"You must provide either the parameter 'port' or 'file_socket' to designate where " 68 if (has_port && has_file_socket)
69 paramError(
"port",
"Cannot provide both 'port' and 'file_socket'");
87 mooseAssert(
processor_id() == 0,
"Should only be started on rank 0");
88 mooseAssert(!
_server,
"Server is already started");
89 mooseAssert(!
_server_thread,
"Server thread is already listening");
92 const auto error = [](
const std::string & error)
94 miniJson::Json::_object response;
95 response[
"error"] = error;
96 return HttpResponse{400, response};
100 const auto get_string =
101 [&error](
const auto & msg,
const std::string &
name,
const std::string & description)
103 using result = std::variant<std::string, HttpResponse>;
104 const auto it = msg.find(
name);
107 error(
"The entry '" +
name +
"' is missing which should contain the " + description));
108 const auto &
value = it->second;
109 if (!
value.isString())
110 return result(error(
"The entry '" +
name +
"' which should contain the " + description));
111 return result(
value.toString());
115 const auto get_name = [&get_string](
const auto & msg,
const std::string & description)
116 {
return get_string(msg,
"name",
"name of the " + description); };
120 const auto require_waiting = [&error](
auto & control)
122 using result = std::optional<HttpResponse>;
123 if (!control.currentlyWaiting())
124 return result(error(
"This control is not currently waiting for data"));
128 const auto require_parameters = [&error](
const auto & msg,
const std::set<std::string> & params)
130 using result = std::optional<HttpResponse>;
131 for (
const auto & key_value_pair : msg)
132 if (!params.count(key_value_pair.first))
133 return result(error(
"The key '" + key_value_pair.first +
"' is unused"));
137 _server = std::make_unique<HttpServer>();
140 _server->when(
"/check")->requested([](
const HttpRequest & ) {
return HttpResponse{200}; });
148 [
this](
const HttpRequest & )
150 miniJson::Json::_object res_json;
153 res_json[
"waiting"] =
true;
154 res_json[
"execute_on_flag"] =
158 res_json[
"waiting"] =
false;
160 return HttpResponse{200, res_json};
167 _server->when(
"/get/postprocessor")
169 [
this, &error, &get_name, &require_waiting, &require_parameters](
const HttpRequest & req)
171 const auto & msg = req.json().toObject();
174 const auto name_result = get_name(msg,
"postprocessor to retrieve");
175 if (
const auto response = std::get_if<HttpResponse>(&name_result))
177 const auto &
name = std::get<std::string>(name_result);
180 if (
const auto response = require_parameters(msg, {
"name"}))
183 if (
const auto response = require_waiting(*
this))
187 return error(
"The postprocessor '" +
name +
"' was not found");
189 miniJson::Json::_object res_json;
191 return HttpResponse{200, res_json};
200 _server->when(
"/set/controllable")
202 [
this, &error, &get_string, &get_name, &require_waiting, &require_parameters](
203 const HttpRequest & req)
205 const auto & msg = req.json().toObject();
208 if (
const auto response = require_parameters(msg, {
"name",
"type",
"value"}))
211 if (
const auto response = require_waiting(*
this))
215 const auto type_result = get_string(msg,
"type",
"type of the parameter");
216 if (
const auto response = std::get_if<HttpResponse>(&type_result))
218 const auto &
type = std::get<std::string>(type_result);
220 return error(
"The type '" +
type +
221 "' is not registered for setting a controllable parameter");
224 const auto name_result = get_name(msg,
"name of the parameter to control");
225 if (
const auto response = std::get_if<HttpResponse>(&name_result))
227 const auto &
name = std::get<std::string>(name_result);
230 return error(
"The controllable parameter '" +
name +
"' was not found");
233 const auto value_it = msg.find(
"value");
234 if (value_it == msg.end())
236 "The entry 'value' is missing which should contain the value of the parameter");
237 const auto & json_value = value_it->second;
241 std::unique_ptr<ValueBase>
value;
249 return error(
"While parsing 'value': " + std::string(e.
what()));
254 return HttpResponse{201};
260 [
this, &error](
const HttpRequest &)
265 return HttpResponse{200};
269 return error(
"The control is not currently waiting");
277 const uint16_t port = this->getParam<unsigned int>(
"port");
284 this->
mooseError(
"Failed to start the webserver; it is likely that the port ",
286 " is not available");
291 const auto & file_socket = this->getParam<FileName>(
"file_socket");
292 _server->startListening(file_socket);
303 std::vector<std::pair<std::string, std::string>> name_and_types;
308 TIME_SECTION(
"execute()", 3,
"WebServerControl waiting for input")
314 std::this_thread::yield();
317 name_and_types.emplace_back(value_ptr->name(), value_ptr->type());
327 for (
const auto & [
name,
type] : name_and_types)
335 value_ptr->setControllableValue(*
this);
341 "' typed value for parameter '",
343 "'; it is likely that the parameter has a different type");
353 if (json_type == miniJson::JsonType::kNull)
355 if (json_type == miniJson::JsonType::kBool)
357 if (json_type == miniJson::JsonType::kNumber)
359 if (json_type == miniJson::JsonType::kString)
361 if (json_type == miniJson::JsonType::kArray)
363 if (json_type == miniJson::JsonType::kObject)
365 ::mooseError(
"WebServerControl::stringifyJSONType(): Unused JSON value type");
std::atomic< bool > _currently_waiting
Whether or not the Control is currently waiting.
static InputParameters validParams()
Class constructor.
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 ...
void startServer()
Internal method for starting the server.
const ExecFlagType & getCurrentExecuteOnFlag() const
Return/set the current execution flag.
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
registerWebServerControlScalarBool(bool)
virtual const std::string & name() const
Get the name of the class.
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
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.
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.
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 ...
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)
virtual const PostprocessorValue & getPostprocessorValueByName(const PostprocessorName &name) const
Retrieve the value of the Postprocessor.
static std::string stringifyJSONType(const miniJson::JsonType &json_type)
std::unique_ptr< std::thread > _server_thread
The server thread.
Base class for Control objects.
virtual void execute() override
Execute the control.
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type.
registerMooseObject("MooseApp", WebServerControl)
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)
std::unique_ptr< HttpServer > _server
The server.
registerWebServerControlVectorString(std::string)