https://mooseframework.inl.gov
Loading...
Searching...
No Matches
WebServerControl.C
Go to the documentation of this file.
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#include "WebServerControl.h"
11#include "FEProblemBase.h"
12#include "MooseApp.h"
13
14#include "minijson/minijson.h"
15#include "tinyhttp/http.h"
16
18
21{
23 params.addClassDescription("Starts a webserver for sending/receiving JSON messages to get data "
24 "and control a running MOOSE calculation");
25 params.addParam<unsigned int>("port",
26 "The port to listen on; must provide either this or 'file_socket'");
27 params.addParam<FileName>(
28 "file_socket",
29 "The path to the unix file socket to listen on; must provide either this or 'port'");
30 params.addParam<Real>("initial_client_timeout",
31 10,
32 "Time in seconds to allow the client to begin communicating on init; if "
33 "this time is surpassed the run will be killed");
34 params.addParam<Real>("client_timeout",
35 10,
36 "Time in seconds to allow the client to communicate; if this time is "
37 "surpassed the run will be killed");
38 return params;
39}
40
42 : Control(parameters),
43 _port(queryParam<unsigned int>("port")),
44 _file_socket(queryParam<FileName>("file_socket")),
45 _initial_client_timeout(getParam<Real>("initial_client_timeout")),
46 _client_timeout(getParam<Real>("client_timeout"))
47{
48 if (!_port && !_file_socket)
49 mooseError("You must provide either the parameter 'port' or 'file_socket' to designate where "
50 "to listen");
51 if (_port && _file_socket)
52 paramError("port", "Cannot provide both 'port' and 'file_socket'");
53}
54
56{
57 // Stop server if running; make sure this DOESN'T throw!
58 stopServer();
59}
60
61void
63{
64 // Only start on rank 0
65 if (processor_id() == 0)
66 {
67 mooseAssert(_server_weak_ptr.expired(), "Server is already started");
68 mooseAssert(!_server_thread_ptr, "Server thread is already listening");
69 mooseAssert(_port || _file_socket, "Neither set");
70
71 // Setup and start the server
72 {
73 // Instantiate the server but don't start it, we need
74 // to setup all of the actions
75 auto server_ptr = std::make_shared<HttpServer>();
76 // Give the control a weak_ptr to the server, as it will
77 // really be owned by the server thread
78 _server_weak_ptr = server_ptr;
79
80 // Add all of the actions
82
83 // Post message about server start
84 {
85 std::ostringstream message;
86 message << "Starting server on ";
87 if (_port)
88 message << "port " << *_port;
89 else if (_file_socket)
90 message << "file socket " << *_file_socket;
91 outputMessage(message.str());
92 }
93
94 // Start the server thread, giving the thread the server
95 // shared_ptr, as the control only has a weak_ptr to the server
96 auto console = _console;
97 _server_thread_ptr = std::make_unique<std::thread>(
98 [server_ptr, console](const auto port, const auto file_socket)
99 {
100 mooseAssert(server_ptr, "Null server");
101 auto & server = *server_ptr;
102 mooseAssert(port || file_socket, "Neither provided");
103
104 try
105 {
106 if (port)
107 server.startListening(uint16_t(*port));
108 else
109 server.startListening(*file_socket);
110 }
111 catch (std::exception & e)
112 {
113 console << "Server failed with exception: " << e.what() << std::endl;
114 }
115 },
116 _port,
118 }
119
120 // Wait for the client to call /initialize
121 {
122 // Output that we're waiting for the client
123 outputMessage("Waiting for client to initialize...");
124
125 // To output initialization time
126 const auto start = std::chrono::steady_clock::now();
127
128 while (!isClientInitialized())
129 {
130 // Kill command sent before initialize
131 if (isKillRequested())
132 {
133 stopServer();
134 mooseError("Client sent kill command; exiting");
135 }
136
137 // Make sure we haven't reached the timeout
138 const auto now = std::chrono::steady_clock::now();
139 const auto elapsed = std::chrono::duration<double>(now - start).count();
140 if (elapsed > _initial_client_timeout)
142 _initial_client_timeout, "initial_client_timeout", "during initialization"));
143
144 // Poll until the next check
145 std::this_thread::sleep_for(std::chrono::milliseconds(10));
146 }
147
148 // Assign a start time for the timeout thread, considering
149 // client initialization to be a poke
150 clientPoke();
151
152 // Output that the client has initialized
153 const auto info = getClientInfo();
155 "\"" + info.name + "\" from " + info.user + "@" + info.host + " initialized", start);
156 }
157 }
158
159 // Let the remaining ranks wait until rank 0 is done
161}
162
163void
165{
166 // If simulation is requested to terminate, do not go through this control
168 return;
169
170 // For outputting the time spent waiting
171 const auto start = std::chrono::steady_clock::now();
172
173 // Needed to broadcast all of the types and names of data that we have received on rank 0
174 // so that we can construct the same objects on the other ranks to receive the data and
175 // set the same values
176 std::vector<std::pair<std::string, std::string>> name_and_types;
177
178 // Need to also broadcast whether or not to terminate the solve on the timestep
179 bool terminate_solve = false; // Set value to avoid compiler warnings
180
181 // Wait for the server on rank 0 to be done
182 if (processor_id() == 0)
183 {
184 TIME_SECTION("execute()", 3, "WebServerControl waiting for input")
185 outputMessage("Waiting for client to continue...");
186
188
189 // While waiting, yield so the server has time to run. Check for client
190 // timeouts and a kill command.
191 while (isCurrentlyWaiting())
192 {
193 // Kill command sent
194 if (isKillRequested())
195 {
196 stopServer();
197 mooseError("Client sent kill command; exiting");
198 }
199
200 // Check client timeout
201 const auto last = std::chrono::milliseconds(_last_client_poke.load());
202 const auto now = std::chrono::steady_clock::now().time_since_epoch();
203 const auto elapsed = std::chrono::duration<double>(now - last).count();
204 if (elapsed > _client_timeout)
205 {
206 stopServer();
208 }
209
210 std::this_thread::sleep_for(std::chrono::milliseconds(10));
211 }
212
213 // Output waiting time
214 outputClientTiming("continued", start);
215
216 for (const auto & value_ptr : _controlled_values)
217 name_and_types.emplace_back(value_ptr->name(), value_ptr->type());
218
219 terminate_solve = isTerminateRequested();
221 _terminate_requested.store(false);
222 }
223
224 // All processes need to wait
226
227 // Construct the values on other processors to be received into so that
228 // they're parallel consistent
229 comm().broadcast(name_and_types);
230 if (processor_id() != 0)
231 for (const auto & [name, type] : name_and_types)
233
234 // Set all of the values
235 for (auto & value_ptr : _controlled_values)
236 {
237 try
238 {
239 value_ptr->setControllableValue(*this);
240 }
241 catch (...)
242 {
243 mooseError("Error setting '",
244 value_ptr->type(),
245 "' typed value for parameter '",
246 value_ptr->name(),
247 "'; it is likely that the parameter has a different type");
248 }
249 }
250
251 _controlled_values.clear();
252
253 // Set solve terminate on all ranks, if requested
254 _communicator.broadcast(terminate_solve);
255 if (terminate_solve)
257}
258
259template <WebServerControl::RequestMethod method>
260void
262 const std::string & path,
264 WebServerControl &)> && action,
266{
267 static_assert(method == RequestMethod::GET || method == RequestMethod::POST, "Unknown method");
268
269 auto server_ptr = _server_weak_ptr.lock();
270 if (!server_ptr || _server_thread_ptr)
271 mooseError("addServerAction(): Can only call during addServerActions()");
272 auto & server = *server_ptr;
273
274 // Capture a weak pointer to the WebServerControl so that this action
275 // knows if the control is available or not
276 const std::weak_ptr<WebServerControl> control_weak_ptr =
277 std::dynamic_pointer_cast<WebServerControl>(getSharedPtr());
278
279 // Build the action that we'll actually pass to the web server.
280 // Here, is very important that we capture everything by value
281 // so that we don't rely on state from the control.
282 auto full_action =
283 [control_weak_ptr, path, options, action](const HttpRequest & http_request) -> HttpResponse
284 {
285 // Helper for returning an error
286 const auto error = [](const std::string & error,
287 const unsigned int status_code = 400) -> HttpResponse
288 {
289 miniJson::Json::_object response;
290 response["error"] = error;
291 return HttpResponse{status_code, response};
292 };
293
294 // Only do work here if we have access to the control;
295 // if this lock fails, it means that it has been destructed
296 // and there isn't anything we can do
297 if (auto control_ptr = control_weak_ptr.lock())
298 {
299 auto & control = *control_ptr;
300
301 // Setup the request to be passed to the user function,
302 // reformatting the HttpRequest as our own request
303 // so we can change the underlying library if needed
304 Request request;
305 const auto & json_keys = options.getRequiredJSONKeys();
306 // Load JSON data if it exists into nlohmann::json
307 if (!http_request.json().isNull())
308 {
309 // JSON data exists but was not needed
310 if (json_keys.empty())
311 return error("Request should not have JSON");
312
313 // Serialize from minijson to nlohmann::json
314 const auto serialized = http_request.json().serialize();
315 const auto deserialized = nlohmann::json::parse(serialized);
316
317 // Check for required key(s)
318 for (const auto & key : json_keys)
319 if (!deserialized.contains(key))
320 return error("Missing required key '" + key + "' in JSON");
321
322 // And set in the request
323 request.setJSON(deserialized, {});
324 }
325 // JSON data does not exist but it was needed
326 else if (json_keys.size())
327 return error("Request should have JSON");
328
329 // Option requires initialization
330 if (options.getRequireInitialized() && !control.isClientInitialized())
331 return error("Client has not initialized the control");
332
333 // Option requires waiting
334 if (options.getRequireWaiting() && !control.isCurrentlyWaiting())
335 return error("Control is not currently waiting for data");
336
337 // Call the action method to act on the request
338 Response response;
339 try
340 {
341 response = action(request, control);
342 }
343 catch (const std::exception & e)
344 {
345 return error(e.what());
346 }
347
348 // Has an error, return that instead
349 if (response.hasError())
350 return error(response.getError(), response.getStatusCode());
351
352 // No JSON data, just a status code
353 if (!response.hasJSON())
354 return HttpResponse{response.getStatusCode()};
355
356 // Has JSON data, convert from nlohmann::json to miniJson
357 const auto serialized = response.getJSON().dump();
358 std::string parse_error;
359 const auto json = miniJson::Json::parse(serialized, parse_error);
360 mooseAssert(parse_error.empty(), "Should be empty");
361 return HttpResponse{response.getStatusCode(), json};
362 }
363
364 // Failed to capture weak_ptr to WebServerControl
365 return error("Control is no longer available");
366 };
367
368 auto & when = *server.when("/" + path);
369 if constexpr (method == RequestMethod::GET)
370 when.requested(full_action);
371 else
372 when.posted(full_action);
373}
374
377{
378 std::lock_guard lock(_client_info_lock);
379 if (!_client_info)
380 mooseError("WebServerControl::getClientInfo(): Client info is not set yet");
381 return *_client_info;
382}
383
384void
385WebServerControl::outputMessage(const std::string & message) const
386{
387 _console << typeAndName() << ": " << message << std::endl;
388}
389
390void
392{
393 //
394 // -- General actions ------------------------------------------------------------
395 //
396
397 // GET /check: Helper for checking if the server is running
398 // Requires waiting: no
399 // Return code: 200
400 // Return JSON data: none
401 {
402 const auto check = [](const Request &, WebServerControl &) -> Response
403 { return Response{200}; };
404
405 ServerActionOptions options;
406 options.setRequireWaiting(false, {});
407 options.setRequireInitialized(false, {});
408
409 addServerAction<RequestMethod::GET>("check", check, options);
410 }
411
412 // GET /continue: Tell the simulation to continue
413 // Requires waiting: yes
414 // Return code: 200
415 // Return JSON data:
416 // 'error': string, optional
417 // The error (only set if an error occurred)
418 {
419 const auto set_continue = [](const Request &, WebServerControl & control) -> Response
420 {
421 control.setCurrentlyWaiting(false);
422 return Response{200};
423 };
424
425 addServerAction<RequestMethod::GET>("continue", set_continue);
426 }
427
428 // GET /initialize: Initializes the communication with the client
429 // Requires waiting: no
430 // POST JSON data:
431 // 'name': string
432 // The name of the client
433 // 'host': string
434 // The name the client host
435 // 'user': string
436 // The name of the client user
437 // Return code: 200
438 // Return JSON data:
439 // 'flags': list[string]
440 // The execute on flags
441 // 'error': string, optional
442 // The error (only set if an error occurred)
443 {
444 const auto initialize = [](const Request & req, WebServerControl & control) -> Response
445 {
446 if (control.isClientInitialized())
447 return ErrorResponse("Initialize has already been called");
448
449 // Store the information received in the client info
450 const auto & json = req.getJSON();
451 ClientInfo client_info;
452 const auto name = convertJSON<std::string>(json, "name");
453 const auto host = convertJSON<std::string>(json, "host");
454 const auto user = convertJSON<std::string>(json, "user");
455 client_info.data = json;
456 control.setClientInfo(client_info);
457
458 // Capture the sorted exceute on flags
459 std::set<std::string> flags_sorted;
460 for (const auto & it : control.getParam<ExecFlagEnum>("execute_on"))
461 flags_sorted.insert(it);
462 const std::vector<std::string> flags(flags_sorted.begin(), flags_sorted.end());
463
464 // Send back context about the control
465 nlohmann::json response_json;
466 response_json["control_name"] = control.name();
467 response_json["control_type"] = control.type();
468 response_json["execute_on_flags"] = flags_sorted;
469
470 // Set initialized
471 control.setClientInitialized();
472
473 return Response{200, response_json};
474 };
475
476 ServerActionOptions options;
477 options.setRequireWaiting(false, {});
478 options.setRequireInitialized(false, {});
479 options.requireJSONKeys({"host", "name", "user"});
480
481 addServerAction<RequestMethod::POST>("initialize", initialize, options);
482 }
483
484 // GET /poke: "Poke" the server; used for checking timeouts
485 // Requires waiting: no
486 // Return code: 200
487 // Return JSON data: none
488 {
489 const auto poke = [](const Request &, WebServerControl & control) -> Response
490 {
491 control.clientPoke();
492 return Response{200};
493 };
494
495 ServerActionOptions options;
496 options.setRequireWaiting(false, {});
497
498 addServerAction<RequestMethod::GET>("poke", poke, options);
499 }
500
501 // GET /terminate: Tell FEProblemBase to terminate the solve
502 // Requires waiting: yes
503 // Return code: 200
504 // Return JSON data:
505 // 'error': string, optional
506 // The error (only set if an error occurred)
507 {
508 const auto terminate = [](const Request &, WebServerControl & control) -> Response
509 {
510 control.setTerminateRequested();
511 control.setCurrentlyWaiting(false);
512 return Response{200};
513 };
514
515 addServerAction<RequestMethod::GET>("terminate", terminate);
516 }
517
518 // GET /waiting: Check if waiting and the waiting exec flag if it exists
519 // Requires waiting: no
520 // Return code: 200
521 // Return JSON data:
522 // 'waiting': bool
523 // Whether or not the control is waiting
524 // 'execute_on_flag': string, optional
525 // Only exists if waiting=true, the execute flag that is being waited on
526 {
527 const auto waiting = [](const Request &, WebServerControl & control) -> Response
528 {
529 nlohmann::json response_json;
530 if (control.isCurrentlyWaiting())
531 {
532 response_json["waiting"] = true;
533 response_json["execute_on_flag"] =
534 static_cast<std::string>(control._fe_problem.getCurrentExecuteOnFlag());
535 }
536 else
537 response_json["waiting"] = false;
538
539 return Response{200, response_json};
540 };
541
542 ServerActionOptions options;
543 options.setRequireWaiting(false, {});
544
545 addServerAction<RequestMethod::GET>("waiting", waiting, options);
546 }
547
548 // GET /kill: Tell the client poll thread to kill
549 // Requires waiting: no
550 {
551 const auto kill = [](const Request &, WebServerControl & control) -> Response
552 {
553 control.setKillRequested();
554 return Response{200};
555 };
556
557 addServerAction<RequestMethod::GET>("kill", kill);
558 }
559
560 //
561 // -- Get actions ----------------------------------------------------------------
562 //
563
564 // GET /get/dt: Get current simulation timestep size
565 // Requires waiting: yes
566 // Return code: 200
567 // Return JSON data:
568 // 'dt': double
569 // Current timestep size
570 // Return JSON data:
571 // 'error': string, optional
572 // The error (only set if an error occurred)
573 {
574 const auto get_dt = [](const Request &, WebServerControl & control) -> Response
575 {
576 nlohmann::json response_json;
577 response_json["dt"] = control._fe_problem.dt();
578 return Response{200, response_json};
579 };
580
581 addServerAction<RequestMethod::GET>("get/dt", get_dt);
582 }
583
584 // POST /get/postprocessor: Get a postprocessor value
585 // Requires waiting: yes
586 // POST JSON data:
587 // 'name': string
588 // The name of the Postprocessor
589 // Return code: 200
590 // Return JSON data:
591 // 'value': double
592 // The postprocessor value
593 // 'error': string, optional
594 // The error (only set if an error occurred)
595 {
596 const auto get_postprocessor = [](const Request & req, WebServerControl & control) -> Response
597 {
598 // Get the postprocessor name
599 const auto name = convertJSON<std::string>(req.getJSON(), "name");
600
601 // Postprocessor should exist
602 if (!control.hasPostprocessorByName(name))
603 return ErrorResponse("Postprocessor '" + name + "' not found");
604
605 nlohmann::json response_json;
606 response_json["value"] = control.getPostprocessorValueByName(name);
607 return Response{200, response_json};
608 };
609
610 ServerActionOptions options;
611 options.requireJSONKey("name");
612
613 addServerAction<RequestMethod::POST>("get/postprocessor", get_postprocessor, options);
614 }
615
616 // POST /get/reporter: Get a Reporter value
617 // Requires waiting: yes
618 // POST JSON data:
619 // 'name': string
620 // The name of the Reporter value (object_name/value_name)
621 // Return code: 200
622 // Return JSON data:
623 // 'value': any
624 // The reporter value
625 // Return JSON data:
626 // 'error': string, optional
627 // The error (only set if an error occurred)
628 {
629 const auto get_reporter = [](const Request & req, WebServerControl & control) -> Response
630 {
631 // Get the reporter name
632 const auto name = convertJSON<std::string>(req.getJSON(), "name");
634 return ErrorResponse("Name '" + name + "' not a valid reporter value name");
635 const auto rname = ReporterName(name);
636
637 // Reporter should exist
638 if (!control.hasReporterValueByName(rname))
639 return ErrorResponse("Reporter value '" + name + "' was not found");
640
641 // Store the reporter value
642 nlohmann::json response_json;
643 control.getReporterContextBaseByName(rname).store(response_json["value"]);
644
645 return Response{200, response_json};
646 };
647
648 ServerActionOptions options;
649 options.requireJSONKey("name");
650
651 addServerAction<RequestMethod::POST>("get/reporter", get_reporter, options);
652 }
653
654 // GET /get/time: Get current simulation time
655 // Requires waiting: yes
656 // Return code: 200
657 // Return JSON data:
658 // 'time': double
659 // Current time
660 // 'error': string, optional
661 // The error (only set if an error occurred)
662 {
663 const auto get_time = [](const Request &, WebServerControl & control) -> Response
664 {
665 nlohmann::json response_json;
666 response_json["time"] = control._fe_problem.time();
667 return Response{200, response_json};
668 };
669
670 addServerAction<RequestMethod::GET>("get/time", get_time);
671 }
672
673 //
674 // -- Set actions ----------------------------------------------------------------
675 //
676
677 // POST /set/controllable: Get a controllable parameter
678 // Requires waiting: yes
679 // POST JSON data:
680 // 'name': string
681 // The path to the controllable data
682 // 'value': any
683 // The value to set
684 // 'type: string
685 // The C++ type of the controllable parameter
686 // Return code: 201
687 // Return JSON data:
688 // 'error': string, optional
689 // The error (only set if an error occurred)
690 {
691 const auto set_controllable = [](const Request & req, WebServerControl & control) -> Response
692 {
693 const auto & json = req.getJSON();
694
695 // Get the parameter type
696 const auto type = convertJSON<std::string>(json, "type");
697 const auto registered_type = Moose::WebServerControlTypeRegistry::query(type);
698 if (!registered_type)
699 return ErrorResponse("Type '" + type +
700 "' not registered for setting a controllable parameter");
701
702 // Get the parameter name
703 const auto name = convertJSON<std::string>(json, "name");
704 // Parameter should exist
705 if (!control.hasControllableParameterByName(name))
706 return ErrorResponse("Controllable parameter '" + name + "' not found");
707
708 // 'value' must exist
709 const auto value_it = json.find("value");
710 if (value_it == json.end())
711 return ErrorResponse("Missing 'value' entry");
712
713 // Build the value (also does the parsing)
714 {
715 std::unique_ptr<ControlledValueBase> value;
716 try
717 {
718 value = registered_type->build(name, *value_it);
719 }
720 catch (std::exception & e)
721 {
722 return ErrorResponse("While parsing 'value': " + std::string(e.what()));
723 }
724
725 std::lock_guard<std::mutex> lock(control._controlled_values_mutex);
726 control._controlled_values.emplace_back(std::move(value));
727 }
728
729 return Response{201};
730 };
731
732 ServerActionOptions options;
733 options.requireJSONKeys({"name", "type", "value"});
734
735 addServerAction<RequestMethod::POST>("set/controllable", set_controllable, options);
736 }
737
738 // Let derived classes add actions
740}
741
742void
744{
745 std::lock_guard lock(_client_info_lock);
746 _client_info = info;
747}
748
749void
751{
752 const int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
753 std::chrono::steady_clock::now().time_since_epoch())
754 .count();
755 _last_client_poke.store(now);
756}
757
758void
760 const std::string & message,
761 const std::chrono::time_point<std::chrono::steady_clock> & start) const
762{
763 const auto now = std::chrono::steady_clock::now();
764 const auto elapsed = std::chrono::duration<double>(now - start).count();
765
766 std::ostringstream out;
767 out << "Client " << message << " after " << std::fixed << std::setprecision(2) << elapsed
768 << " seconds";
769 outputMessage(out.str());
770}
771
772std::string
774 const Real timeout,
775 const std::string & timeout_param_name,
776 const std::optional<std::string> & suffix /* = {} */) const
777{
778 std::ostringstream oss;
779 oss << "The client timed out" << (suffix ? (" " + *suffix) : "") << "\nThe timeout is "
780 << std::fixed << std::setprecision(2) << timeout << " seconds and is set by the '"
781 << timeout_param_name << "' parameter";
782 return oss.str();
783}
784
785void
787{
788 // Only have something to do here if the server still exists
789 // and the server thread was set; make sure this doesn't
790 // throw because it is used in the destructor
791 if (auto server_ptr = _server_weak_ptr.lock(); server_ptr && _server_thread_ptr)
792 {
793 try
794 {
795 server_ptr->shutdown();
796 _server_thread_ptr->join();
797 _server_thread_ptr.reset();
798 }
799 catch (...)
800 {
801 }
802 }
803}
804
807template void WebServerControl::addServerAction<WebServerControl::RequestMethod::GET>(
808 const std::string &,
810 WebServerControl &)> &&,
812template void WebServerControl::addServerAction<WebServerControl::RequestMethod::POST>(
813 const std::string &,
815 WebServerControl &)> &&,
818
819const nlohmann::json &
821{
822 if (!hasJSON())
823 throw std::runtime_error("Request does not contain JSON when it should");
824 return *_json;
825}
826
827WebServerControl::Response::Response(const unsigned int status_code) : _status_code(status_code) {}
828
829WebServerControl::Response::Response(const unsigned int status_code, const nlohmann::json & json)
830 : _status_code(status_code), _json(json)
831{
832}
833
834const nlohmann::json &
836{
837 if (!_json)
838 throw std::runtime_error("Response does not contain JSON when it should");
839 return *_json;
840}
841
842const std::string &
844{
845 if (!_error)
846 ::mooseError("Does not have an error");
847 return *_error;
848}
849
851 const unsigned int status_code /* = 400 */)
852 : WebServerControl::Response(status_code)
853{
854 setError(error);
855}
registerMooseObject("MooseApp", WebServerControl)
void ErrorVector unsigned int
const ConsoleStream _console
An instance of helper class to write streams to the Console objects.
Base class for Control objects.
Definition Control.h:44
static InputParameters validParams()
Class constructor.
Definition Control.C:16
FEProblemBase & _fe_problem
Reference to the FEProblemBase for this object.
Definition Control.h:76
A MultiMooseEnum object to hold "execute_on" flags.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object.
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump.
const std::string & type() const
Get the type of this class.
Definition MooseBase.h:93
std::string typeAndName() const
Get the class's combined type and name; useful in error handling.
Definition MooseBase.C:57
const std::string & name() const
Get the name of the class.
Definition MooseBase.h:103
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
Definition MooseBase.h:457
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...
Definition MooseBase.h:271
std::shared_ptr< MooseObject > getSharedPtr()
Get another shared pointer to this object that has the same ownership group.
Definition MooseObject.C:70
virtual std::unique_ptr< ControlledValueBase > build(const std::string &name, const nlohmann::json &json_value) const =0
Builds a value with the given type, name name, and JSON value json_value.
static const RegisteredTypeBase & get(const std::string &type)
Get the registration for the given type, erroring if it isn't registered.
static const RegisteredTypeBase * query(const std::string &type)
Query the registration for the given type.
virtual void terminateSolve()
Allow objects to request clean termination of the solve.
Definition Problem.h:37
virtual bool isSolveTerminationRequested() const
Check of termination has been requested.
Definition Problem.h:43
The Reporter system is comprised of objects that can contain any number of data values.
static bool isValidName(const std::string &object_and_value_name)
Determines if the inputted string is convertible to a ReporterName.
void broadcast(T &data, const unsigned int root_id=0, const bool identical_sizes=false) const
Starts a webserver that an external process can connect to in order to send JSON messages to control ...
std::atomic< bool > _terminate_requested
Whether or not the solve should be terminated in the next execute() call.
const Real _client_timeout
Time in seconds to allow the client to communicate after init before timing out.
WebServerControl(const InputParameters &parameters)
void addServerAction(const std::string &path, std::function< Response(const Request &, WebServerControl &)> &&action, const ServerActionOptions &options={})
Adds an action for the server to perform at the given path.
virtual void addServerActions()
Entrypoint for controls derived from this one to add additional actions.
void addServerActionsInternal()
Adds the internal actions to the server.
virtual void execute() override final
Execute the control.
bool isKillRequested() const
Get whether or not the client sent the kill command.
static InputParameters validParams()
std::weak_ptr< HttpServer > _server_weak_ptr
Weak pointer to the server; the server itself is owned by the server thread.
std::atomic< int64_t > _last_client_poke
The most recent time we've heard from the client.
bool isTerminateRequested() const
Whether or not the client has called /terminate.
std::string clientTimeoutErrorMessage(const Real timeout, const std::string &timeout_param_name, const std::optional< std::string > &suffix={}) const
Helper for producing an error message about a client timeout.
bool isCurrentlyWaiting() const
Get whether or not the control is currently waiting.
void clientPoke()
Store a client's poke, which is a timing used to determine the client timeout.
std::mutex _client_info_lock
Lock for _client_info as it is written by the server thread.
ClientInfo getClientInfo() const
Get the information sent by the client on initialize.
const unsigned int *const _port
Port to listen on, if any.
const FileName *const _file_socket
File socket to listen on, if any.
std::unique_ptr< std::thread > _server_thread_ptr
The server thread.
std::vector< std::unique_ptr< ControlledValueBase > > _controlled_values
The values received to control; filled on rank 0 from the server and then broadcast.
void setCurrentlyWaiting(const bool value=true)
Set that the control is currently waiting; used by the server.
void stopServer()
Stop the server if it exists and is running.
void setClientInfo(const ClientInfo &info)
Set the ClientInfo object received from the client during /initialize.
void startServer(const Moose::PassKey< StartWebServerControlAction >)
Start the server.
void outputMessage(const std::string &message) const
Output a message with the prefix of this control type and name.
bool isClientInitialized() const
Whether or not the client has called /initialize.
std::optional< ClientInfo > _client_info
Client information received on /initialize by the server.
const Real _initial_client_timeout
Time in seconds to allow the client to initially communicate before timing out.
void setTerminateRequested(const bool value=true)
Set for the control to terminate the solve; used by /terminate in the server.
void outputClientTiming(const std::string &message, const std::chrono::time_point< std::chrono::steady_clock > &start) const
Output a timing message with the prefix of this control.
const Parallel::Communicator & _communicator
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
void initialize(EquationSystems &es, const std::string &system_name)
Stores the information sent by the client on initialize.
nlohmann::json data
Raw data.
Represents an error response to the client from the server.
ErrorResponse(const std::string &error, const unsigned int status_code=400)
Represents a request from the client.
std::optional< nlohmann::json > _json
The underlying JSON data, if any.
const nlohmann::json & getJSON() const
Represents a response to the client from the server.
const std::string & getError() const
const nlohmann::json & getJSON() const
void setError(const std::string &error)
Set the error message.
unsigned int getStatusCode() const
Options to be passed to addServerAction.
void requireJSONKey(const std::string &key)
Append a key to be required in JSON in the request data.
void requireJSONKeys(std::initializer_list< std::string > &&keys)
Append keys to be required in JSON in the request data.
void setRequireInitialized(const bool value, const Moose::PassKey< WebServerControl >)
Set the require initialized flag; only accessible by the WebServerControl.
const std::set< std::string > getRequiredJSONKeys() const
void setRequireWaiting(const bool value, const Moose::PassKey< WebServerControl >)
Set the require waiting flag; only accessible by the WebServerControl.